|
|
# 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:
|
|
|
|
|
|
```ts
|
|
|
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
|
|
|
|
|
|
```ts
|
|
|
'_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
|
|
|
|
|
|
```ts
|
|
|
'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
|
|
|
|
|
|
```bash
|
|
|
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".
|