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

270 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Continue tomorrow
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 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.
## Sprint Words V2 architecture — R1 cerrado
Tras la discusión de arquitectura (separación contenido/diseño, P1-P8 + decisiones D-SEM-1/2/3 + Q1-Q6), R1 está cerrado.
**Doctrina firmada** (full text en `src/uix/soma/components/words/ARCHITECTURE_PROPOSAL.md`):
- P1-P8 principios firmados (incluido P6 Single Source of Truth + P7 Vocabulario evaluativo único + P8 reglas para qué entra al modelo)
- D-SEM-2 → `CalloutBlock.intent` usa `SemaIntent` canónico (no `warning/info/success/danger`)
- D-Q1-Q6 → id opcional autogenerado, MD lossy total, no presets en core, engine package en R5 (opcional)
**Implementado en R1**:
- `engine/types-v2.ts` (~300 líneas): WordsDocumentV2 v2.0.0, BlockBase con `id` + `visual` opcionales, BlockVisual RAW (números px + hex), per-type `Pick<>` (ParagraphVisual/HeadingVisual/QuoteVisual/CodeVisual/ListVisual/TableVisual/ImageVisual/DividerVisual/CalloutVisual), 9 bloques (paragraph/heading/quote/code/list/table/image + **divider** y **callout** nuevos), marks structured (`{type:'color'|'background', value:'#hex'}` en vez de template literal V1), `WordsEvalIntent` declarado localmente para evitar dependency a `$uix/sema` (con plan de runtime parity assertion).
- `engine/validate-v2.ts` (~500 líneas): validador puro `validateWordsDocument(input) → ValidationResult`. 7 reglas duras + 11 códigos de error. Path JSON-pointer en cada error.
- `engine/validate-v2.test.ts` (50 tests): cubre topo, todos los block types, marks (boolean + structured + rechazo del format V1 + rechazo de tokens), visual con whitelist per-type (P8), id uniqueness, callout SemaIntent + rechazo de "warning"/"info"/etc., divider, quote.cite, link nesting + URL parseability, table headerRow/headerCol/per-cell+per-row visual.
**Estado tras R1**: 135/135 engine tests (85 V1 + 50 V2) · `npm run check` 0 errors. V1 sigue intacto, V2 en paralelo.
## Roadmap V2 pendiente
| Sprint | Estado | Qué hace |
|---|---|---|
| **R1 — Audit + tipos + validador** | ✅ Cerrado | types-v2.ts + validate-v2.ts + 50 tests |
| **R2 — Engine refactor (no breaking)** | ⏳ Pendiente | Migrator v1→v2 (drop image.status / cell-tone / table-striped / table-compact; structured marks; autogenerar ids). Runtime sidecar para image upload status (Map<blockId, status>). Tests del migrator. |
| **R3 — Visual sidecar implementation** | ⏳ Pendiente | Render emite inline styles desde block.visual. POLISH-1b (image radius/shadow/border) implementable bajo este modelo. Drawer image panel con controles. |
| **R4 — Sweep tokens del contenido** | ⏳ Pendiente | Eliminar referencias `cell-tone` / `table-striped` / `table-compact` del rest del codebase (provider, render, eidos, serializers). Drawer panels actualizados. Audit script: 0 referencias a tokens en engine/extensions. |
| **R5 — Engine as package (opcional)** | ⏳ Diferible | Mover engine/ a paquete propio. Eidos consume vía path normal. |
## 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)
```
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)
```
## Decisiones arquitectónicas (afectan al ecosistema)
### 1. Wrappers visuales declaran su DNA con el morfo del componente reutilizado
**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.
Ejemplo: el `Item` de toggle-group declara `data-toggle` porque ES un
toggle estructuralmente. La receta de Toggle entonces lo pinta entero.
Patrón aplicable si emerge: button-group, link-group, nav-menu, etc.
Documentado en CLAUDE.md hand-off 2026-05-28.
### 2. Words: tokens de la rail son FIXED-TONE (no theme-aware)
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.
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
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)

Powered by TurnKey Linux.