You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
5.8 KiB
99 lines
5.8 KiB
# CONTINUE — F1.8 `nav-tree` (handoff 2026-07-22) · **CERRADO**
|
|
|
|
> **F1.8 HECHA (2026-07-22).** Este handoff queda como registro de la tanda:
|
|
> demo, verificación en navegador real, review adversarial (10 hallazgos
|
|
> confirmados, todos arreglados) y cierre están completos. El registro
|
|
> definitivo vive en `docs/process/PLAN-blocks.md` §7. Lo que sigue es el
|
|
> estado tal como quedó al PARAR aquella sesión (histórico).
|
|
|
|
Estado al parar: **componente COMPLETO y en verde en todos los gates
|
|
estáticos; falta demo + verificación en navegador + review adversarial +
|
|
commit final + registro**. Plan maestro: `docs/process/PLAN-blocks.md` §F1.8.
|
|
|
|
## Qué es (decisiones ya tomadas y ratificadas en fase 0)
|
|
|
|
Árbol de navegación **data-driven** (E-1) para docs-shell: el app pasa
|
|
`nodes` (datos) + `activeHref` (su URL); el componente resuelve el nodo activo,
|
|
deriva el trail de ancestros y **auto-expande** el trail. APG **Disclosure
|
|
Navigation** (landmark `<nav>` + links nativos + botones disclosure), NO
|
|
`tree-view` (que es un WIDGET de selección `role=tree`).
|
|
|
|
**Frontera vs `tree-view`**: rol + interacción + modelo de datos distintos →
|
|
componente separado, no extensión. (Scope-approval cerrado.)
|
|
|
|
**Decisiones de diseño** (todas implementadas):
|
|
- **Disclosure propio** (no compone Collapsible): el estado `open` es un getter
|
|
de soma (el expand-set) que alimenta `aria-expanded`/`data-state`; el toggle
|
|
dispara `emerge-expand`/`emerge-collapse` vía `runtime.trigger` (sema +
|
|
`setExpanded`). NO hay máquina de eventos que sostenga el estado.
|
|
- **`emerge` + pack de sema**: corregido en fase 5 — el audit A-3.1 exige
|
|
eventos en un componente interactivo; el toggle de disclosure ES un `emerge`
|
|
canónico (espeja collapsible/tree-view). scope = `['soma','sema','eidos']`.
|
|
- **Filas propias** (`<a>`/`<button>` estilizados por el recipe), NO `Link`/
|
|
`Button` compuestos — como anchor-nav; el contrato a11y es de nav-tree.
|
|
- **`isExpanded` es query PURA** (sin `$effect` que escriba estado): abierto si
|
|
no-colapsado-por-usuario Y (expandido-por-usuario O en-trail O `defaultOpen`).
|
|
- **Profundidad = CSS var** `--_nav-tree-depth` (no attr por nivel).
|
|
- **Nodo padre navegable** = link + chevron (patrón Fumadocs); sin href = botón.
|
|
|
|
## Ficheros (todos escritos, type-clean)
|
|
|
|
- Morfo: `src/uix/morfo/components/nav-tree.ts` (6 partes; eventos emerge;
|
|
`expression:'pack'`).
|
|
- Soma: `src/uix/soma/components/nav-tree/` — `nav-tree-provider.svelte.ts`
|
|
(`NavTreeProvider` raíz + `NavTreeItemProvider` por nodo), `types.ts`,
|
|
`langs.ts`, `components/nav-tree.svelte` (Provider), `components/nav-tree-node.svelte`
|
|
(recursivo), `exports.ts`, `index.ts`, `README.md`.
|
|
- Eidos: `src/uix/eidos/components/nav-tree/` — `nav-tree.svelte` (forward),
|
|
`nav-tree.css` (recipe), `types.ts`, `index.ts`, `README.md`.
|
|
- Sema pack: `src/uix/sema/components/nav-tree.ts`.
|
|
- Langs: `src/uix/langs/components/nav-tree.ts`.
|
|
- Registros: `langs/components/index.ts`, `soma/components/index.ts`,
|
|
`sema/components/index.ts`, `eidos/lib/recipes/base.ts` (token `indent`),
|
|
`eidos/generated/base.css` (generado), `web/routes/uix/+layout@.svelte`
|
|
(pack `navTreeSema` registrado).
|
|
|
|
## Gates que YA pasan
|
|
|
|
- `npx tsx scripts/component-audit.ts --only nav-tree` → **PASS**.
|
|
- `npx tsx scripts/eidos-lint.ts nav-tree` → 20 morfo-backed, 0 invalid.
|
|
- `npx svelte-check` → 0 errores en nav-tree + pack.
|
|
- `npx vitest run src/uix/contracts.test.ts` → mis partes limpias.
|
|
⚠️ Quedan **2 fallos AJENOS** (menubar-provider DOM-write · radio-group
|
|
`data-ready`) de sesiones paralelas SIN commitear — NO son de nav-tree.
|
|
|
|
## Qué FALTA (orden para mañana)
|
|
|
|
1. **Demo** `web/routes/uix/components/nav-tree/+page.svelte` — canónica, misma
|
|
profundidad que anchor-nav (9 pestañas: live/system/motion/sema/services/
|
|
api/morfo/recipe/a11y; harness `SystemAxes`/`MotionPanel`/`SemaPanel`/
|
|
`DemoTrace`; `type Tab` en UNA línea con `// prettier-ignore`). Contenido
|
|
live: un árbol REAL de ~40 nodos y 3 niveles (p. ej. el mapa de `docs/`),
|
|
control de `activeHref` (select o los propios links moviéndolo), trail vivo.
|
|
El pack de sema YA está registrado en el layout.
|
|
2. **Entrada de nav**: añadir `nav-tree` a la lista de componentes en
|
|
`web/routes/uix/+layout@.svelte` (donde se añadió `anchor-nav`).
|
|
3. **Verificar en NAVEGADOR** (obligatorio, es UI): árbol renderiza; trail
|
|
auto-expandido al cambiar `activeHref`; chevron rota en `data-state=open`;
|
|
link activo toma color primary + peso; grupo colapsado = `hidden`;
|
|
`aria-current="page"` + `aria-expanded`/`aria-controls` correctos; DARK
|
|
(`colorScheme:'dark'` vía Playwright) y RTL. ⚠️ El Browser pane SUSPENDIDO
|
|
da `getComputedStyle` de color OBSOLETO (lección anchor-nav) — si el color
|
|
activo se ve mal, verifícalo en chromium foreground, NO es bug.
|
|
4. **Review adversarial** (workflow, como sticky/anchor-nav): dimensiones
|
|
correctness-scrollspy/trail · reactividad-lifecycle · morfo-a11y ·
|
|
css-recipe. Verificador escéptico por hallazgo. Arregla confirmados.
|
|
5. **Commit final + registro** en `PLAN-blocks.md` §7 (F1.8 HECHA) + memoria
|
|
`project_blocks_tier_2026-07-21.md` (F1 = 7/8) + `MEMORY.md`.
|
|
|
|
## Gotchas / lecciones de esta tanda
|
|
|
|
- **Correr `contracts.test.ts`**, no solo `npm run check` — destapa READMEs de
|
|
soma faltantes (le pasó a anchor-nav) y attrs/DOM-writes hardcodeados.
|
|
- `aria-expanded` necesita `stateRef` (da `'true'/'false'`); un `propRef`
|
|
booleano NO (el resolver solo convierte booleanos en aria para `stateRef`).
|
|
- El estado de un part se puede alimentar con un getter (`states:{open:()=>…}`)
|
|
sin evento; los eventos emerge son para la sema (espeja collapsible).
|
|
- `resolveProps(bindings)` es puro pero NO incluye el marker/id; el patrón
|
|
data-driven blessed es **providers per-nodo con `renderProps()`** (menubar).
|