|
|
# 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.
|
|
|
|
|
|
## Pendiente para mañana
|
|
|
|
|
|
### P1 — Drag handle FUERA del rail sobre bloques `<code>` (heredado)
|
|
|
|
|
|
El grip se va fuera de la rail al hacer hover sobre un code block.
|
|
|
Sin diagnosticar. Lugar: `eidos/components/words/words-block-handle.svelte
|
|
|
> measureFromEl()`. Pasos: comparar `getBoundingClientRect().left` de
|
|
|
`<pre>` vs `<p>`; si difieren, buscar padding/margin/transform en
|
|
|
`[data-words-content] :where(pre)` en `words.css`.
|
|
|
|
|
|
### P1 — Heading inline level change (h1 ↔ h2 ↔ h3) (heredado)
|
|
|
|
|
|
Cambiar nivel de heading inline sin abrir drawer. Recomendación:
|
|
|
empezar por el Block panel del drawer (verificar que el HeadingPicker
|
|
|
se muestra cuando `currentBlock === 'heading'`).
|
|
|
|
|
|
### 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.
|