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.
svelte-kit-vice/continue.md

14 KiB

Continue tomorrow

Fecha de corte: 2026-05-27. Rama: active-uix. Working tree con cambios sin commitear listos para push.

TL;DR de la sesión

Sesión maratón del Token Scope Contract — del bug del toggle (ya conocido) hasta cobertura universal sin excepciones arquitectónicas:

  1. Diagnóstico inicial: usuario reporta "el toggle no funciona, el color del intent no cambia". Eager-resolution de CSS custom properties en el cascade del toggle.
  2. TSC v1 → v2 → v2.1 → v2.2: 4 iteraciones del Token Scope Contract. Cada una cerrando un gap (scope explícito, álgebra de cobertura, private tokens, multi-part + composition).
  3. Universal migration: 18 componentes con data-color migrados a TSC (15 con TSC v2.1, + 3 con extensiones v2.2 de hoy).
  4. Bug crítico post-composition: la toggle-group composition correctamente override --toggle-palette-* pero el cascade colapsaba downstream porque los derived tokens (--toggle-solid-on-bg etc.) viven en scope [data-toggle] (sibling, no ancestor, de [data-toggle-group-item]). Fix: inlined derivations en toggle-group.css con palette-direct refs.
  5. Bundle JIT purge (scripts/eidos-purge.ts): tool standalone que tree-shakes el generated/base.css por componente, var() ref, data-attr y component import. Reduce ~50% el bundle final.
  6. Docs: THEMING.md (~1700 líneas) como referencia canónica del theming. CLAUDE.md con 5 hand-offs del 2026-05-27. Eidos README actualizado.

Tareas completadas hoy (resumen)

Total: 87 tareas TaskList. Sprint TSC ocupa #50–#87. Highlights:

  • TSC v1 (#50–#56): primera versión — scope/depends explícitos, generator agrupando por scope.
  • TSC v2 (#57–#62): álgebra de scope (ScopeSet covers), cross-axis collision detection, var() auto-inferred deps, color:* rename.
  • P0–P2 housekeeping (#63–#68): rename --{c}-color-{role}-{slot} → --{c}-{role}-{slot}, JIT purge script.
  • CONSOL + GAP (#69–#73): consolidación de docs, universal anti-eager- resolution guard test.
  • UNIV (#75–#79): private tokens (_ prefix) + migración universal de 13 componentes.
  • EXT (#80–#86): TSC v2.2 — multi-part parts + cross-recipe composition + 3 componentes restantes migrados (select, toggle-group, avatar) + docs.
  • EXT-FIX (#87): bug del toggle-group color cascade tras la composition migration.

Patrones canon nuevos en esta sesión

Token Scope Contract (TSC) — la fuente de verdad

Cada recipe token tiene un scope explícito que materializa el selector CSS donde se emite. El generator infiere deps de los var() y valida álgebra de cobertura. Imposible introducir el bug eager-resolution si declaras tokens en TSC — el validator lo rechaza antes de generar CSS.

Formas:

recipes.toggle = {
  'height-md': '32px',                                    // shorthand → root
  'solid-on-bg': {                                        // single decl
    value: 'var(--toggle-palette-solid)', scope: 'host'
  },
  'palette-solid': {                                      // multi-decl
    declarations: [
      { value: 'var(--toggle-neutral-solid)', scope: 'host' },
      { value: 'var(--toggle-affirm-solid)',  scope: 'color:affirm' }
    ]
  }
}

TSC v2.2 — parts: [...] para multi-part scope

'_accent-track': {
  parts: ['trigger', 'content'],
  declarations: [
    { value: 'var(--select-primary-track)', scope: 'host' },
    { value: 'var(--select-affirm-track)',  scope: 'color:affirm' }
  ]
}
// → emits:
// [data-select-trigger], [data-select-content] { --_select-accent-track: ... }
// [data-select-trigger][data-color='affirm'], [data-select-content][data-color='affirm'] { ... }

Único consumer hoy: select (Trigger + Content). Cualquier componente que tenga atributos cascadeados per-part (no en root) usa este patrón.

TSC v2.2 — composition: { ... } para cross-recipe override

'toggle-group': {
  gap: 'var(--space-1)',
  composition: {
    toggle: {                                  // foreign recipe
      targetSelector: '[data-toggle-group-item]',
      tokens: {
        'palette-solid': {
          declarations: [
            { value: 'var(--toggle-affirm-solid)', scope: 'color:affirm' },
            { value: 'var(--toggle-risk-solid)',   scope: 'color:risk' }
          ]
        }
      }
    }
  }
}
// → emits:
// [data-toggle-group][data-color='affirm'] [data-toggle-group-item] {
//   --toggle-palette-solid: var(--toggle-affirm-solid);
// }

Único consumer hoy: toggle-group. Pattern reutilizable para futuros wrappers compositivos (button-group, nav-menu, etc.).

Private tokens (_ prefix)

Recipe key _palette-solid → CSS var --_toggle-palette-solid (con underscore prefix). NO aparece en el public CSS contract. Solo accesible desde la propia CSS recipe del componente. Convención: usar para slots intermedios de derivation que no quieres exponer como theming knobs.

Inline derivation cuando el ancestor scope no aplica

Anti-pattern: leer var(--{foreign-component}-derived-token) desde un elemento que NO es descendant del scope donde se declaró ese token. Resultado: undefined → cascade colapsa.

Pattern correcto: inline la derivation expression localmente. Ejemplo en toggle-group.css para [data-toggle-group-item] — replica las expressions de recipes/base.ts > toggle.{solid,outline,ghost}-* directamente. Duplicación documentada y aceptada como trade-off.

Trabajo acumulado SIN commitear

Modificados (eidos + soma touched durante TSC sprint):
 M src/uix/eidos/components/avatar/avatar.css
 M src/uix/eidos/components/badge/badge.css
 M src/uix/eidos/components/button/button.css
 M src/uix/eidos/components/card/card.css
 M src/uix/eidos/components/checkbox/checkbox.css
 M src/uix/eidos/components/editable/editable.css
 M src/uix/eidos/components/file-upload/file-upload.css
 M src/uix/eidos/components/radio-group/radio-group.css
 M src/uix/eidos/components/select/select.css
 M src/uix/eidos/components/stepper/stepper.css
 M src/uix/eidos/components/switch/switch.css
 M src/uix/eidos/components/tag-group/tag-group.css
 M src/uix/eidos/components/tags-input/tags-input.css
 M src/uix/eidos/components/toggle-group/toggle-group.css
 M src/uix/eidos/components/toggle/toggle.css
 M src/uix/eidos/README.md
 M CLAUDE.md
 M package.json   (npm script para eidos:purge añadido)

Modificados (words component sprint en paralelo — no este chat):
 M src/uix/eidos/components/words/*
 M src/uix/soma/components/words/*
 M src/uix/words/README.md
 M web/routes/uix/components/words/*

Eliminado:
 D src/docs/libro_semantica_completo.txt   (reemplazado por .docx)

Untracked (TSC v2.2 + auxiliares):
?? scripts/eidos-purge.ts
?? scripts/probe-*.ts          (5 probe scripts del debug session)
?? src/docs/Disenando_lo_que_ocurre_manuscrito_completo_revisado_v2.docx
?? src/uix/eidos/THEMING.md
?? src/uix/eidos/THEMING_AUDIT_2026-05-27.md
?? src/uix/eidos/components/words/*  (slash menu, family menu, code lang picker)
?? src/uix/soma/components/words/components/words-slash-menu.svelte
?? src/uix/soma/components/words/engine/code-highlight.ts
?? src/uix/words/references/*  (4 audit docs + 1 screenshot)

Variants son canon — NO theme-extensibles

Decisión arquitectónica documentada hoy en THEMING.md §19. Los variants (solid, outline, ghost, soft, surface, etc.) son fijos a nivel del framework — paralelos a las 8 sema families. El theme solo cambia palette/shadows. La fuente de verdad es la const EIDOS_VARIANTS en lib/types.ts con 5 archetypes (control / selection / chip / marker / tabs); los unions TS se derivan de ella via [number] indexed access.

Variants component-specific (Banner inline/overlay/persistent, Spinner bars/dots/ring, Button 'plain') viven en cada components/{c}/types.ts. El lint recipe-css-contract.test.ts > variant CSS selectors per component match the declared type union valida bidireccionalmente que CSS selectors y type unions coincidan.

Words extension system — estado y siguiente sprint

Hecho (commits 1805b081 skeleton + 088d31ec F2.3a):

  • F1.1 fix evento contact-focus molesto (transition-only + popover scope)
  • F1.2 declarar 9 data-* hardcoded en morfo
  • F1.3 i18n keys kebab-case
  • F1.4 canonizar 9 event names a forma {family}-{verb}-{variant}
  • F1.5 toolbarLayout grouped|inline con ResponsiveProp
  • F2.1 audit + design de WordsExtension interface (9 hooks)
  • F2.2 skeleton: extension-types.ts + extension-registry.ts con 14 tests verdes
  • F2.3a tipos de table movidos a extensions/table/types.ts con re-export en engine/document.ts

F2.3 hecho (sesión de hoy, 5 checkpoint commits + 143/143 tests verdes después de cada uno):

  • F2.3a ✅ — table types → extensions/table/types.ts (commit 088d31ec)
  • F2.3b ✅ — table factories + predicates + value-set constants → extensions/table/factories.ts (commit a0afa44d)
  • F2.3d ✅ — table markdown serializer (serialize + parse + buildTableFromMarkdownRows) → extensions/table/serialize-markdown.ts (commit d6f0af48)
  • F2.3c ✅ — table HTML serializer (serializeTableHtml + parseTableHtml + cell helpers) → extensions/table/serialize-html.ts (commit 5797e69b)
  • F2.3e ✅ — table render (renderTable + renderTablePlainText) → extensions/table/render.ts (commit d8392735)

F2.3 pendiente — los 4 sub-pasos restantes son el refactor difícil:

  • F2.3f — path.ts navigation. Las refs de table están en BRANCHES dentro de funciones grandes (resolvePath, updateNode, inferContainerKind), NO en helpers aislados. Extracción requiere:

    • Diseñar callback adapters para recursión (engine → extension → engine)
    • O bien: el engine consulta registry sólo para "is this a node my extension owns?" y el resto del walk queda en engine
    • Reflexión: ¿cabe redibujar el dispatcher al estilo visitor pattern? Sería más limpio.
  • F2.3g — normalize.ts. Mismo patrón: branches dentro de normalizeBlock / normalizeNode. Extracción requiere mismo enfoque que F2.3f.

  • F2.3h — operations.ts (2620 LoC, ~400-500 LoC table). Las funciones insertTable*, deleteTable*, toggle-table-* son funciones standalone — extraíbles. Pero comparten utilities (tableCellOptions, tableOptions, replaceAt) que viven en el engine. Extraer requiere:

    • Mover utilities a extensions/table/utils.ts o re-exportar desde engine
    • Mover los ~20 reducers a extensions/table/operations.ts
    • El dispatcher central (applyWordsCommand) consulta registry.getCommand(opType) y fallback al switch existente
  • F2.3i — registry wire-up. Crear tableExtension: WordsExtension que cabledea TODOS los hooks (commands, render, serialize, etc.) al registry. El engine constructor registra tableExtension por defecto para backward compatibility. Después el dispatcher reescrito consulta registry.

  • F2.3j — verify final.

Recomendación de orden para próxima sesión (lo que dije y se ha confirmado al hacer F2.3a-e):

  1. Antes de F2.3f/g/h, hacer F2.3i con stub vacía: construir tableExtension: WordsExtension que registra sólo nodeTypes ['table','table-row','table-cell'], commandNames y events. El engine al boot lo registra. Después en h-i se mueve la lógica gradualmente hacia los hooks.
  2. F2.3h primero (operations) usando el stub. Cada reducer extraído pasa de case 'insertTable': en switch a entrada en extension.commands. El switch del engine consulta registry.getCommand() antes del fallback.
  3. F2.3f y F2.3g al final, donde el engine ya consulta registry para todo el resto.

Estado del extension system después de hoy:

  • WordsExtension interface ✅ (F2.2)
  • WordsExtensionRegistry con 14 tests verdes ✅ (F2.2)
  • extensions/table/ con types + factories + 2 serializers + render ✅ (F2.3a-e)
  • Engine NO consulta registry aún (importa funciones directamente de extension). El cambio a "engine consulta registry para tipo X" llegará en F2.3i.

Después F2.4 (code-block, plan similar), F2.5 (docs EXTENSIONS.md), F2.6 (verify final).

Cómo retomar mañana

  1. Probar toggle-group en navegador:

    • El bug del cascade fue corregido inlining derivations en toggle-group.css. Pero el fix no se probó en navegador. Si el cascade sigue roto en algún caso, revisar las inlined expressions contra recipes/base.ts > toggle.{solid,outline,ghost}-*.
  2. Avatar regenerado: la migración TSC v2.2 produce un orden de selector ligeramente distinto al original ([data-color][data-variant] en lugar de [data-variant][data-color]). Mismo CSS efectivo — misma especificidad. Si algún test snapshot rompe, regenerar.

  3. Próximo trabajo natural en TSC (NO urgent):

    • Si más componentes necesitan multi-part scope o composition, ya está todo soportado — solo añadir las recipes.
    • Considerar radius:/ring-color: como AtomicScope adicionales si el avatar ring-color cascade emerge como pattern recurrente.

Comandos útiles

cd G:/dev/svelte/vicen
npm run check                               # svelte-check
npm run test                                # full vitest run
npx vitest run src/uix/eidos                # eidos-only (100 tests)
npm run generate:eidos-css                  # regen src/uix/eidos/generated/base.css
npx tsx scripts/eidos-purge.ts <route>      # JIT purge para una ruta

Referencias canónicas tras la sesión

  • Theming completo: src/uix/eidos/THEMING.md (1700+ líneas, §18 reescrito hoy como "Cobertura universal de TSC", §7 ampliado con multi-part + composition).
  • Hand-offs: CLAUDE.md §"Session hand-off — 2026-05-27 #5" (TSC v2.2 + cobertura universal de theming).
  • Recipe authoring guide: THEMING.md §7 (TSC) + §8 (añadir componente nuevo).
  • Anti-eager-resolution guard: src/uix/eidos/recipe-css-contract.test.ts test "forbids palette-derived tokens at :root scope".

Powered by TurnKey Linux.