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-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.