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

11 KiB

Continue tomorrow

Fecha de corte: 2026-05-29. Rama: active-uix.

TL;DR de la sesión

V1 del editor Words ELIMINADO. V2 es ahora el único motor.

La sesión arrancó con una auditoría del componente Words (codex-full-audit.md en la raíz) y la pregunta del usuario: "elimina v1 y continua v2, ¿qué sentido tiene v1?". Decisión firmada y ejecutada de cabo a rabo (F1 → F6). Ya no hay coexistencia V1/V2 ni migrador: el modelo de documento público es WordsDocumentV2 y punto.

Decisiones firmadas (vinculantes)

  • D1 = "V2 puro, sin legacy": borrados engine/document.ts + engine/migrate-v2.ts. SIN importador de retrocompatibilidad. La API pública pasa de WordsDocument → WordsDocumentV2. Los docs persistidos en V1 dejan de cargar (ruptura aceptada por el usuario).
  • D2 = "Aceptar el drop": eliminados por completo los controles / props / commands / atributos morfo de tabla striped, compact y cell-tone. NO se re-mapean ni se re-portan.

Qué se borró (V1) y a dónde se movió (V2)

Borrado — motor V1 (engine/*.ts): commands.ts, document.ts, dom-selection.ts, engine.test.ts, find-replace.ts, history.ts, index.ts (barrel V1), input.ts, migrate-v2.ts (+test), operations.ts, render.ts, serialize-html.ts, serialize-json.ts, serialize-markdown.ts, serialize-text.ts.

Borrado — árbol extensions/ entero: extension-registry (+test), extension-types, image/*, table/*. El modelo V2 no usa el sistema de extensiones; tabla/imagen/divider/callout son bloques nativos del union WordsBlockV2.

El motor V2 vive SOLO en engine/operations-v2/ + un puñado de .ts en la raíz de engine/ (types-v2, validate-v2, normalize, render-v2, serialize-html-v2, serialize-markdown-v2, sema-parity, code-highlight, path, selection, dom). Esta tanda portó al namespace V2 lo que faltaba de V1: commands, history, input, find-replace, render-dom, serialize-text, extra-ops, insert-block-types, markdown (input-rule shortcuts), factories, dom-helpers, dom-selection — además de cerrar R4A.2→R4A.8 (text / delete / marks-ops / paragraph-ops / delete-ops / block-format / list-ops / link-ops / table-ops / selection-walkers / inline-split).

NO existe parser HTML / Markdown / JSON-schema en V2. Las serializaciones HTML y Markdown son export-only (lossy → sin inverso fiel). El import sólo acepta JSON (JSON.parse + validateWordsDocument) y texto plano (parseWordsPlainText).

Superficie pública V2 (lo que consume cualquier app)

  • Tipos: WordsDocumentV2, WordsBlockV2, WordsInlineV2, WordsSelection, WordsExportFormat, WordsImportFormat — todos re-exportados desde $soma/components/words.
  • WORDS_DOCUMENT_VERSION_V2 = '2.0.0' desde engine/types-v2.
  • Provider (words-provider.svelte.ts): importContent (text + json), exportContent (json / text / html / markdown), plainText derivado vía renderWordsPlainText.
  • Serializers: serializeHtmlV2(doc), serializeMarkdownV2(doc), renderWordsPlainText(doc), renderWordsDocumentV2, renderWordsDomHtml / Pretty.
  • Comando central: applyWordsCommand(state, WordsCommandV2) en operations-v2/commands.ts.

Forma de los contenedores V2 (recordatorio — NO hay discriminador type en sub-nodos)

  • Tabla: { type:'table', rows, headerRow?, headerCol? }; fila { cells, visual? }; celda { children, align?, verticalAlign?, colspan?, rowspan?, visual? }. Celda usa align, no textAlign.
  • Lista: { type:'list', kind, items }; item { children, checked?, indent? }. items, no children. Sin type:'list-item'.
  • Bloques con alineación: paragraph/heading/quote llevan textAlign? (a nivel bloque).

⚠️ Decisión consecuente NO firmada — export HTML

serializeHtmlV2 emite HTML semántico limpio, no un formato roundtrip-lossless. Como V2 no tiene parser HTML, HTML y Markdown son export-only. Si el usuario quería export HTML roundtrip-friendly (re- importable), es un encargo aparte. Pendiente de confirmar con él.

Verificación al cierre (todo verde)

Gate Status Notas
npm run check 0 errors / 5168 files 26 warnings pre-existentes, ninguno en archivos tocados
npx vitest run src/uix/soma/components/words 455/456 el único fallo es el flake de sema-parity por timeout 5s bajo carga paralela
sema-parity aislado 6/6 en 362ms confirma que el flake es de infra, no regresión
npm run morfo:vocabulary exit 0 sólo WARN suaves (vocabularios noveles legítimos)
Browser smoke /uix/components/words OK mount limpio, 0 console errors

Smoke detallado: el doc sample V2 renderiza (tabla 2×2 con headerRow, 4 list-items incl. el indentado); export markdown emite tabla GFM válida; import texto round-trip ("imported text"); import JSON válido round-trip a través del gate ("imported json"); import JSON inválido (versión mala) → "invalid json" y deja el doc intacto (la razón de añadir el gate validateWordsDocument: un doc malo asignado al value bindeado rompería los $derived).

Consumidores re-cableados a V2

  • words-provider.svelte.ts + words-provider.svelte.test.ts
  • exports.ts + types.ts (superficie pública)
  • morfo/components/words.ts (drop de atributos striped/compact/tone)
  • eidos: words.svelte, words-drawer.svelte, words-block-handle, words-block-inserter, words-command-icon, words-heading-picker, words-toolbar-family-menu, words.css, types.ts
  • soma components/words.svelte
  • test/words-content-harness.svelte (importaba el barrel V1 borrado; reescrito a operations-v2 + types-v2 + WordsDocumentV2)
  • web/routes/uix/components/words/+page.svelte (último consumidor; imports V2, importFormats → ['json','text'], docs sample/empty/long a forma V2, walkers readPlainText/blockToPlainText/readInlineText sobre el union V2, exportWordsDocument + importDraftDocument reescritos, prosa de la API-tab limpiada de striped/tone)

Cerrado en la continuación (2026-05-29)

✅ Limpieza de huérfanos del drop D2

Borrados los selectores CSS muertos de words.css ([data-command='table-cell-tone-*'], table[data-words-table-striped], [data-words-table-compact], [data-words-cell-tone] + focus-within, y los [data-words-cell-header] huérfanos — los headers se renderizan como <th>). README actualizado a la realidad V2 (tabla lista solo formato vivo; HTML/MD export-only; import JSON + texto). ARCHITECTURE_PROPOSAL.md intacto: documenta la decisión de eliminarlos, no features stale. Verificado en browser (tabla 2×2 renderiza, <th> conserva su tinte vía :where(th)). Commit 4a0661a5.

✅ R5 — POLISH-1b (image visuals)

El panel de imagen del drawer (eidos/components/words/words-drawer.svelte > imagePanel) ya tenía radius slider + shadow/border toggles. Añadido lo que faltaba: shadow blur slider, shadow color picker, border color picker y background color picker, vía un snippet reutilizable imageColorField (presets + <input type=color> + clear opcional), mismo patrón que el color de texto del panel format. Todos despachan setBlockVisual → block.visual.* → visualToStyle → inline style en el <figure>. Verificado en browser: el panel renderiza los 4 controles nuevos; click en swatch de Background actualiza el figure (background-color:#22c55e) preservando radius/border/shadow (shallow-merge correcto). npm run check 0 errors.

✅ P1 — Drag handle sobre <code> (NO reproduce)

Investigado: NO reproduce en el código actual. El "rail" al que se refería el bug fue ELIMINADO en el rediseño de gutter (commit 5450d5f7, "rail out"), posterior al sprint EV donde se reportó. measureFromEl (words-block-handle.svelte) ancla el handle a contentRect.left + 8 — block-type-agnostic. Verificado en browser: sobre code y sobre paragraph el gutter cae en left: 350.333 idéntico (solo difiere top). No hay nada que arreglar; bug obsoleto.

✅ P1 — Heading inline level change (h1 ↔ h2 ↔ h3)

Añadido al Block panel del drawer (eidos/components/words/words-drawer.svelte > blockPanel): cuando currentBlock === 'heading', un ToggleGroup H1/H2/H3 (value = currentHeadingLevel) despacha applyCommand({ type: 'setBlock', block: 'heading', level }). runCommand / WordsCommandName no tiene id por-nivel (heading-1/2/3 son ids de slash / insert-menu), por eso se usa el comando estructurado setBlock con el nivel numérico (as const mantiene cada level como literal 1|2|3). Verificado en browser: caret en heading → el panel muestra "Heading level"; el toggle refleja el nivel actual; el click convierte bidireccional (H1→H3→H2) y el toggle se actualiza. npm run check 0 errors.

Rework del editor Words (2026-05-29 #2 — redirect del usuario)

El usuario redirigió a un nuevo bloque de trabajo sobre el editor. Plan en 3 pasos (acordado: overlays a eidos POR FASES, con revisión entre cada una):

  • Paso 1 — gutter izquierdo (✅ hecho): quitado el botón "+" del gutter; "insertar bloque" integrado en el menú del drag handle como "Insert below" (vía act('insertBelow')). Gutter convertido en banda silver estrecha: --_words-gutter-size 3.5rem→2.5rem, fondo --words-rail-bg #ededed (muy suave) + borde derecho 1px --words-rail-border #cfcfcf (un poco más fuerte), pintado como linear-gradient en [data-words-content] (solo background, no toca el contenteditable). Tokens del recipe actualizados + generate:eidos-css. El inserter de SEAM (words-block-inserter.svelte, el "+" entre bloques) NO se tocó — es otro overlay; preguntar si también debe irse.
  • Paso 2 — props de bloque (#3, pendiente): añadir color (texto) a BlockVisual + per-type subsets + visualToStyle + validador; y sección común (margen/espaciado/fondo/color-texto) en el block panel. Construir esos color controls con eidos ColorPicker y migrar los <input type=color> nativos existentes (foreground/background marks + panel de imagen) a ColorPicker. (Cubre #3 + la parte "color pickers → eidos" de #4-fase1.)
  • Paso 3 — menú del drag → eidos (#4-fase1 resto) + #4-fase2: migrar el menú del block-handle a eidos Menu/Popover; luego link editor / find / status a Popover+Tooltip.

Pendiente para mañana

P2 — Heredados

  • POLISH-3: topbar del demo (breadcrumb, Edit/Preview, Saved).
  • WORDS-DRAGDROP-SOMA: migrar drag-drop a soma DragDrop.Provider en vez de HTML5 nativo (intento previo falló — requiere reestructurar el render del engine).

Notas de infra

  • sema-parity.test.ts flakea por timeout de 5s sólo bajo carga paralela (full-suite). Aislado pasa 6/6 en <500ms. Si molesta, subir el testTimeout de ese archivo; no es regresión.
  • .claude/scheduled_tasks.lock es un artefacto transitorio — NO commitear.

Powered by TurnKey Linux.