|
|
|
|
@ -1,295 +1,243 @@
|
|
|
|
|
# Continue tomorrow
|
|
|
|
|
|
|
|
|
|
Fecha de corte: **2026-05-27**. Rama: `active-uix`. Working tree con cambios
|
|
|
|
|
sin commitear listos para push.
|
|
|
|
|
Fecha de corte: **2026-05-28**. Rama: `active-uix`. 7 commits por
|
|
|
|
|
delante de `gita/active-uix` (push pendiente al cerrar la sesión).
|
|
|
|
|
|
|
|
|
|
## 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' }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
Sesión doble — primera mitad **arquitectónica** (toggle-group
|
|
|
|
|
estructural), segunda mitad **UX del editor Words** (silencio del
|
|
|
|
|
canvas, rail visual, scroll interno, toolbar mínima, popovers, drag
|
|
|
|
|
sobre listas/tablas).
|
|
|
|
|
|
|
|
|
|
### Parte A — toggle-group structural identity
|
|
|
|
|
|
|
|
|
|
Cierra la deuda del hand-off 2026-05-27 #5: el item de toggle-group
|
|
|
|
|
ahora DECLARA `data-toggle` en su morfo y la receta de Toggle pinta
|
|
|
|
|
end-to-end. Cero duplicación de derivaciones.
|
|
|
|
|
|
|
|
|
|
- `morfo/components/toggle-group.ts`: item part añade
|
|
|
|
|
`{ attr: 'data-toggle', value: v.literal(''), severity: 'required' }`.
|
|
|
|
|
- `eidos/components/toggle-group/context.ts` (nuevo): contexto
|
|
|
|
|
Svelte tipado con getters reactivos para `variant` + `size`.
|
|
|
|
|
- `eidos/.../toggle-group.svelte`: setea el contexto en el root.
|
|
|
|
|
- `eidos/.../toggle-group-item.svelte`: lee el contexto y escribe
|
|
|
|
|
`data-variant` + `data-size` en el button del item.
|
|
|
|
|
- `eidos/.../toggle-group.css`: ~150 líneas borradas (derivaciones,
|
|
|
|
|
variant cascades, size cascade, focus/disabled/icon-only
|
|
|
|
|
duplicados). Solo queda layout/orientation/attached/block/focus
|
|
|
|
|
z-index/group-disabled.
|
|
|
|
|
- TSC v2.2 composition (palette overrides en `[data-toggle-group]
|
|
|
|
|
[data-color='X'] [data-toggle-group-item]`) sigue activa — palette
|
|
|
|
|
+ toggle's derivation chain hace el trabajo.
|
|
|
|
|
|
|
|
|
|
Verificación: 12 combinaciones (4 colores × 3 variants × 2 estados)
|
|
|
|
|
con computed values bit-a-bit idénticos al baseline pre-refactor.
|
|
|
|
|
|
|
|
|
|
### Parte B — auditoría npm check verde
|
|
|
|
|
|
|
|
|
|
`npm run check` pasó de 16 errores a **0 errors** en esta sesión.
|
|
|
|
|
|
|
|
|
|
- 4 errores `orientation` drift en `soma/components/words-toolbar*`
|
|
|
|
|
(deuda mía del sprint anterior).
|
|
|
|
|
- 12 errores en el sprint Words activo:
|
|
|
|
|
- `onUploadImage` propagado en soma `Words.Provider`.
|
|
|
|
|
- `'insert-image'` añadido a 2 Records `WordsToolbarButtonCommandName`.
|
|
|
|
|
- `leafItem` snippet hoisted FUERA de `<SomaWords.Provider>` (en
|
|
|
|
|
Svelte 5, snippets dentro de un component element se pasan como
|
|
|
|
|
props del componente — el snippet era helper local, debe estar
|
|
|
|
|
a nivel root de template).
|
|
|
|
|
- 5 arrays inline `{id: ...}` en `words-drawer.svelte` extraídos
|
|
|
|
|
a constantes typed `as const satisfies readonly { id:
|
|
|
|
|
WordsCommandName; ... }[]` (FORMAT_MARKS / TEXT_ALIGN_ACTIONS /
|
|
|
|
|
CELL_TONE_ACTIONS / CELL_VERTICAL_ACTIONS / CELL_TEXT_ALIGN_ACTIONS).
|
|
|
|
|
|
|
|
|
|
### Parte C — Words editor: fixes UX en cascada (commits EV-*)
|
|
|
|
|
|
|
|
|
|
Orden en el que el usuario fue pidiendo:
|
|
|
|
|
|
|
|
|
|
**EV-A — spam de eventos**: `isInsideWordsTool` extendido a drawer,
|
|
|
|
|
block-handle, block-handle-menu, block-inserter, image-float-bar.
|
|
|
|
|
`contact-focus` target movido de `content` a `provider` en morfo +
|
|
|
|
|
sema cascade. (Más tarde EV-F1 lo desactivó del todo).
|
|
|
|
|
|
|
|
|
|
**EV-B — toolbar slim**: presets reducidos a `[history-menu,
|
|
|
|
|
insert-menu, link-menu, tools-menu, find-replace]` quitando
|
|
|
|
|
text/block/list/align/table que ya viven en el drawer.
|
|
|
|
|
|
|
|
|
|
**EV-C — drag handle UX**: borrado `setDragImage(hoverBlockEl)`
|
|
|
|
|
para que el ghost sea solo el grip (no el bloque entero). Nuevo
|
|
|
|
|
`data-dragging` + CSS fade del ancla estático.
|
|
|
|
|
|
|
|
|
|
**EV-D — inserter al bottom edge**: `seam.y = a.bottom` en vez del
|
|
|
|
|
midpoint. Half-open interval `[top, bottom)` para que el píxel
|
|
|
|
|
exacto del bottom pertenezca al seam (no al bloque).
|
|
|
|
|
|
|
|
|
|
**EV-E — scroll interno**: nuevo token `content-max-block-size`
|
|
|
|
|
(50/60/70vh) + `overflow-y: auto` en `[data-words-content]`.
|
|
|
|
|
Listeners de scroll en handle/inserter/float-bar pasados a
|
|
|
|
|
**capture phase** (scroll no burbujea — para captar scroll dentro
|
|
|
|
|
del content).
|
|
|
|
|
|
|
|
|
|
**EV-F (6 fixes en uno)**:
|
|
|
|
|
1. Cascade sema de `contact-focus` REMOVIDA → canvas mudo en focus.
|
|
|
|
|
2. Rail bg → silver flat (#d4d4d4) + borde derecho darker (#9a9a9a),
|
|
|
|
|
sin dot pattern. **Tokens** `words.rail-bg/rail-border` (fixed
|
|
|
|
|
tone, NOT theme-aware — emulan margen de papel).
|
|
|
|
|
3. Toolbar: `undo` + `redo` DIRECTOS (no popover); `insert-menu`
|
|
|
|
|
eliminado. Resultado: 5 items (undo, redo, Links, Tools,
|
|
|
|
|
Find/replace).
|
|
|
|
|
4. Family-panel base 12.5 → 16rem · tools 17rem · link 20rem.
|
|
|
|
|
5. Link popover scroll: consecuencia del #4, ya no aparece.
|
|
|
|
|
6. Engine `insertParagraph` con guarda sobre bloque vacío:
|
|
|
|
|
paragraph → no-op, heading → demote a paragraph.
|
|
|
|
|
|
|
|
|
|
**EV-G (final pass, parcial)**:
|
|
|
|
|
- Sound STILL aparecía después de EV-F1. Causa real: `contact` family
|
|
|
|
|
tiene BASE signature en SEMA_MAP que suena aunque no haya cascade
|
|
|
|
|
per-component. **Solución**: comentar el `runtime.trigger
|
|
|
|
|
('contact-focus')` en soma. Event no se emite. Telemetry vacía
|
|
|
|
|
(aceptado).
|
|
|
|
|
- Listas + tablas no mostraban drag handle. Selectores en
|
|
|
|
|
block-handle + block-inserter ampliados de
|
|
|
|
|
`[data-words-node="block"][data-words-path]` a también
|
|
|
|
|
`[data-words-node="list"]` y `[data-words-node="table"]`.
|
|
|
|
|
- Hover removido del inserter button (CSS rule borrada) y del
|
|
|
|
|
block-handle (también removed). Gutter overlays son ahora
|
|
|
|
|
AMBIENT — no se iluminan en hover.
|
|
|
|
|
|
|
|
|
|
## Lo que QUEDÓ PENDIENTE para mañana
|
|
|
|
|
|
|
|
|
|
Reportado por el usuario al final, no resuelto en el commit
|
|
|
|
|
final por presión de cierre:
|
|
|
|
|
|
|
|
|
|
### P1 — Drag handle aparece FUERA del rail sobre bloques `<code>`
|
|
|
|
|
|
|
|
|
|
El usuario reporta que el grip se va fuera de la rail cuando hovers
|
|
|
|
|
un code block. Posibles causas:
|
|
|
|
|
|
|
|
|
|
- `<pre>` tiene `border: var(--words-border-width)` + padding propio
|
|
|
|
|
(1.25rem) + `overflow-x: auto`. El `rect.left` debería seguir
|
|
|
|
|
siendo el outer edge del `<pre>` (igual que cualquier `<p>`), pero
|
|
|
|
|
podría haber un shift inesperado.
|
|
|
|
|
- Sospecha alterna: el code block tiene un margen-inline negativo
|
|
|
|
|
(no encontrado en mi grep, pero vale la pena reverificar).
|
|
|
|
|
- Lugar: `src/uix/eidos/components/words/words-block-handle.svelte
|
|
|
|
|
> measureFromEl()` líneas 85-108.
|
|
|
|
|
|
|
|
|
|
Pasos para mañana:
|
|
|
|
|
1. Ir a `/uix/components/words`, hover sobre el code block,
|
|
|
|
|
inspector → log de `block.getBoundingClientRect()` para `<pre>`
|
|
|
|
|
vs `<p>`. Comparar `rect.left`.
|
|
|
|
|
2. Si difieren, hay un shift en CSS — buscar margen/padding/
|
|
|
|
|
transform en `[data-words-content] :where(pre)` (línea 1970 de
|
|
|
|
|
`words.css`).
|
|
|
|
|
3. Si NO difieren pero el handle igual sale mal, el bug es en la
|
|
|
|
|
resta de `cssToPx` o en `rememberRailMetrics` cuando el host
|
|
|
|
|
tiene padding por overflow.
|
|
|
|
|
|
|
|
|
|
### P1 — Heading inline level change (h1 ↔ h2 ↔ h3)
|
|
|
|
|
|
|
|
|
|
El usuario quiere que al editar un heading pueda cambiar de nivel
|
|
|
|
|
INLINE (sin abrir drawer/menu). Opciones:
|
|
|
|
|
|
|
|
|
|
- A. **Drawer's Block panel** ya tiene un HeadingPicker; verificar
|
|
|
|
|
que se muestra cuando `currentBlock === 'heading'`. Si no, añadir.
|
|
|
|
|
- B. **Block-handle menu** podría tener un submenu "Transform to..."
|
|
|
|
|
con h1/h2/h3/paragraph/quote. Patrón Notion.
|
|
|
|
|
- C. **Floating menu** sobre el heading (similar al image float bar)
|
|
|
|
|
con 3 chips H1/H2/H3.
|
|
|
|
|
|
|
|
|
|
Recomendación: empezar por A (menor scope), evaluar UX.
|
|
|
|
|
|
|
|
|
|
### P2 — Heredados antes de hoy
|
|
|
|
|
|
|
|
|
|
- **POLISH-1b** (#145): Slider para image radius + toggle switches
|
|
|
|
|
en la float bar. REQUIRE extender `WordsImageBlock` con `radius` +
|
|
|
|
|
campos boolean (`shadow`, `bordered`). Toca engine + serializers +
|
|
|
|
|
render + CSS + float bar — sprint propio.
|
|
|
|
|
- **POLISH-3** (#148): Topbar del demo (breadcrumb, Edit/Preview,
|
|
|
|
|
Saved indicator).
|
|
|
|
|
- **WORDS-F2.3-F2.6** (#99-102): Migración table + code-block a
|
|
|
|
|
`extensions/*`. Sprint multi-sesión documentado.
|
|
|
|
|
- **WORDS-DRAGDROP-SOMA** (#151): Migrar drag-drop a soma
|
|
|
|
|
DragDrop.Provider en vez de HTML5 nativo. Requiere restructurar el
|
|
|
|
|
engine render (intento previo falló).
|
|
|
|
|
|
|
|
|
|
## Estado de los gates
|
|
|
|
|
|
|
|
|
|
| Gate | Status | Notas |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| `npm run check` | **0 errors** | 26 warnings (pre-existentes en demos) |
|
|
|
|
|
| `morfo-check` | exit 0 | |
|
|
|
|
|
| Tests morfo + sema + eidos + soma/words | **467/467 pass** | |
|
|
|
|
|
| Tests engine (`soma/words/engine`) | **85/85 pass** | |
|
|
|
|
|
| `npm run morfo:vocabulary` | no corrido en última verif | rerun mañana |
|
|
|
|
|
| `translations:check` | no corrido | rerun mañana |
|
|
|
|
|
|
|
|
|
|
## Commits de hoy (7 ahead)
|
|
|
|
|
|
|
|
|
|
### 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);
|
|
|
|
|
// }
|
|
|
|
|
125c62cc fix(words): silence canvas, silver rail, direct undo/redo, popover widths, Enter empty
|
|
|
|
|
ce954732 feat(words): internal scroll for the content area + capture-phase scroll sync
|
|
|
|
|
c4be6dbe fix(words): silence body event spam + slim top toolbar + reposition handle/inserter
|
|
|
|
|
c250f7d7 fix(words): close 12 type-drift errors from F2/F3/COLOR sprints
|
|
|
|
|
d2f184da refactor(toggle-group): item is structurally a Toggle (zero derivation duplication)
|
|
|
|
|
+ commit EV-G (en curso al cierre, contiene: silenciar contact-focus trigger, drag para list/table, eliminar hovers de overlays)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Único consumer hoy: `toggle-group`. Pattern reutilizable para futuros
|
|
|
|
|
wrappers compositivos (button-group, nav-menu, etc.).
|
|
|
|
|
## Decisiones arquitectónicas (afectan al ecosistema)
|
|
|
|
|
|
|
|
|
|
### Private tokens (`_` prefix)
|
|
|
|
|
### 1. Wrappers visuales declaran su DNA con el morfo del componente reutilizado
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
**Doctrina nueva** (toggle-group sprint): cuando un wrapper componente
|
|
|
|
|
reusa visualmente otro (como toggle-group reusa Toggle), la respuesta
|
|
|
|
|
canónica NO es duplicar el cascade ni extender TSC con un tercer
|
|
|
|
|
feature. Es DECLARAR LA IDENTIDAD ESTRUCTURAL en el morfo del wrapper
|
|
|
|
|
añadiendo el atributo del componente reusado.
|
|
|
|
|
|
|
|
|
|
### Inline derivation cuando el ancestor scope no aplica
|
|
|
|
|
Ejemplo: el `Item` de toggle-group declara `data-toggle` porque ES un
|
|
|
|
|
toggle estructuralmente. La receta de Toggle entonces lo pinta entero.
|
|
|
|
|
|
|
|
|
|
**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.
|
|
|
|
|
Patrón aplicable si emerge: button-group, link-group, nav-menu, etc.
|
|
|
|
|
|
|
|
|
|
**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.
|
|
|
|
|
Documentado en CLAUDE.md hand-off 2026-05-28.
|
|
|
|
|
|
|
|
|
|
## Trabajo acumulado SIN commitear
|
|
|
|
|
### 2. Words: tokens de la rail son FIXED-TONE (no theme-aware)
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
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)
|
|
|
|
|
```
|
|
|
|
|
Las decisiones de visual del editor para la gutter rail son: silver
|
|
|
|
|
plano `#d4d4d4` + borde `#9a9a9a`. Estos NO son theme-aware — la
|
|
|
|
|
intención es emular un margen físico de cuaderno, que debe verse
|
|
|
|
|
igual en dark/light theme.
|
|
|
|
|
|
|
|
|
|
### 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
|
|
|
|
|
|
|
|
|
|
```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
|
|
|
|
|
```
|
|
|
|
|
Tokens declarados en `lib/recipes/base.ts > words.rail-bg / rail-border`.
|
|
|
|
|
|
|
|
|
|
### 3. Words: canvas perceptualmente silencioso
|
|
|
|
|
|
|
|
|
|
`contact-focus` event ya NO se dispara desde soma. Telemetry para
|
|
|
|
|
focus está vacía (aceptado por el usuario: "no puede emitir eventos
|
|
|
|
|
es demencial"). Si en el futuro un consumidor necesita el evento,
|
|
|
|
|
descomentar `void this.runtime.trigger('contact-focus', ...)` en
|
|
|
|
|
`words-provider.svelte.ts` (cerca de `onfocus`).
|
|
|
|
|
|
|
|
|
|
### 4. Words: gutter overlays son AMBIENT (sin hover)
|
|
|
|
|
|
|
|
|
|
block-handle y block-inserter button NO tienen `:hover` styles.
|
|
|
|
|
Estado visual cambia solo en data attrs (`data-open`, `data-grabbed`,
|
|
|
|
|
`data-dragging`). Los overlays son afordancias ambientes, no botones
|
|
|
|
|
interactivos que se iluminen.
|
|
|
|
|
|
|
|
|
|
### 5. Words: drag handle + inserter cubren listas y tablas
|
|
|
|
|
|
|
|
|
|
Los selectores de top-level blocks en block-handle y block-inserter
|
|
|
|
|
ahora aceptan `data-words-node="block" | "list" | "table"`. Listas
|
|
|
|
|
ordenadas/desordenadas/check y tablas tienen drag handle + inserter
|
|
|
|
|
seam por debajo.
|
|
|
|
|
|
|
|
|
|
### 6. EnterParagraph: bloque vacío
|
|
|
|
|
|
|
|
|
|
## 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".
|
|
|
|
|
Engine `insertParagraph` con guarda explícita:
|
|
|
|
|
- paragraph vacío + Enter → no-op (no crea otro bloque vacío)
|
|
|
|
|
- heading vacío + Enter → demote a paragraph (canonical Notion UX)
|
|
|
|
|
- otros tipos vacíos: sin cambio (mantiene comportamiento existente
|
|
|
|
|
documentado en `operations.ts` para code/image/table/list/quote)
|
|
|
|
|
|