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

15 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 (sesiones 2026-05-27, 6 checkpoint commits + 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.3i (stub) ✅ — tableExtension: WordsExtension con nodeTypes + factories + commandNames (12) publicado en extensions/table/table-extension.ts. Engine NO consume registry todavía — la stub publica el shape estable que F2.3f-h irán rellenando con hooks. 6 smoke tests + 102 tests en el scope extensions+engine en verde.

F2.3 pendiente — los 3 sub-pasos restantes son el refactor difícil (la stub F2.3i existe ya como receptor de hooks):

  • 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 (full) — wire-up al engine constructor. Crear el patrón de instanciación del engine que (a) construye una WordsExtensionRegistry, (b) registra tableExtension por defecto, (c) hace que el dispatcher (applyWordsCommand), el render, el path, el normalize y los serializers consulten primero la registry y fallback al switch hardcodeado. Hoy tableExtension está como stub publicada — el engine la ignora.

  • F2.3j — verify final.

Recomendación de orden para próxima sesión (confirmado tras F2.3a-e + F2.3i-stub):

  1. F2.3h primero (operations) — la stub tableExtension ya es receptor válido. Mover los ~20 reducers (insertTable*, deleteTable*, toggle-table-*, setTableCell*, moveTableCell) a extensions/table/operations.ts, llenar tableExtension.commands con sus key=opType. Cabledar el dispatcher central (applyWordsCommand) para consultar registry.getCommand(opType) antes del switch. Decisión clave: las utilities compartidas (tableCellOptions, tableOptions, replaceAt, currentTableCellPath) van a extensions/table/utils.ts o se re-exportan desde engine; la opción a re-exportar las dos primeras (engine NO depende de ellas fuera del table-branch) y mover el resto.
  2. F2.3f y F2.3g al final, donde el engine ya consulta registry para todo el resto. Diseñar visitor pattern en path.ts + normalize.ts para que el dispatcher pase el callback "is this a node my extension owns?" al walker.

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 + tableExtension stub ✅ (F2.3a-e + F2.3i-stub)
  • Engine NO consulta registry aún (importa funciones directamente de extension; tableExtension publicada pero engine no la ve). El cambio a "engine consulta registry para tipo X" llegará en F2.3h.

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.