|
|
|
|
|
# Continue tomorrow
|
|
|
|
|
|
|
feat(words): inspector UX overhaul — id-based active block, controls, chrome
Active-block addressing by materialized-path id (words-activate.svelte +
engine/block-tree.ts): click sets the active block explicitly; inspector
follows it, immune to selection churn. Breadcrumb navigates ancestry.
Inspector controls:
- ToggleGroups (font / weight / align / border) always keep one option
selected; the default value stores as undefined to keep the model clean.
- Border gains a `none` chip that clears the border (BorderChipValue).
- Image full-width is a Switch; image src + column width/gap validate on
commit (sanitizeWordsUrl, CSS-length/flex checks) with a [data-invalid] ring.
- Corner-radius + border-width are slider-only (WordsNumRow `sliderOnly`):
the paired numeric stepper was redundant.
- Numeric fields clamp to their range (line-height ≤ 2.5, font-size 12-40).
Inspector chrome:
- Unified field frame + single accent focus ring (no inset outline over text,
no ghost/black-border mix).
- Section dividers + uppercase accordion headers; styled breadcrumb row.
- Depth-tied accent (primary/affirm/loss/risk) shared by header mark and the
canvas active-block ring.
- Selection ring is an ::after pseudo-element with a fixed radius, so a block
the user rounded no longer rounds its selection indicator.
- Raw hex/rgba replaced by --color-*-solid tokens (theme-aware, portal-safe).
Columns: empty on insert, gap control, per-column width; Add-block removed
from the Columns BLOCK panel (lives in the column inspector). Shift+Enter
soft break renders via white-space: pre-wrap.
npm run check: 1 pre-existing baseline error (visual.ts:147). Words soma
452/452. Verified end-to-end in the browser.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
Fecha de corte: **2026-06-01**. Rama: `active-uix`.
|
|
|
|
|
|
|
|
|
|
|
|
## Sesión 2026-06-01 — Words inspector UX (sprint en curso)
|
|
|
|
|
|
|
|
|
|
|
|
Sprint de pulido del **inspector del editor Words** (sidebar derecha)
|
|
|
|
|
|
por feedback iterativo del usuario. Todo verificado en navegador real
|
|
|
|
|
|
(`localhost:5173/uix/components/words`, Chrome tab). `npm run check`: 1
|
|
|
|
|
|
error pre-existente (`engine/operations/visual.ts:147`, baseline, NO
|
|
|
|
|
|
mío). Words soma 452/452. Contract test `keeps raw color values out of
|
|
|
|
|
|
component CSS` pasa.
|
|
|
|
|
|
|
|
|
|
|
|
### Lo cerrado y verificado este sprint
|
|
|
|
|
|
|
|
|
|
|
|
**Ronda 1 — 7 ítems (todos DONE + verificados):**
|
|
|
|
|
|
1. Quitado el dropdown "Add block" del panel de BLOQUE Columns (vive solo
|
|
|
|
|
|
en el inspector específico de columna). `dropdownsInColumnsBlockPanel: 0`.
|
|
|
|
|
|
2. Validación de ancho de columna (`isValidColumnWidth` + `commitColumnWidth`
|
|
|
|
|
|
en `words-block-panel.svelte`): nº flex / longitud CSS / fr / keywords;
|
|
|
|
|
|
garbage → `[data-invalid]`, no commit.
|
|
|
|
|
|
3. Validación de URL de imagen vía `sanitizeWordsUrl` (export añadido al
|
|
|
|
|
|
barrel `soma/components/words/exports.ts`): `javascript:` rechazado.
|
|
|
|
|
|
4. Shift+Enter soft break: ya funcionaba (`\n` + `white-space: pre-wrap`);
|
|
|
|
|
|
verificado con keystrokes reales `MARKA⇧⏎MARKB` → mismo bloque.
|
|
|
|
|
|
5+7. Rediseño cabecera inspector: mark de acento por profundidad
|
|
|
|
|
|
(primary/affirm/loss/risk) + wash + divisor + breadcrumb estilado
|
|
|
|
|
|
(`[data-words-inspector-breadcrumb]`, single-row scroll).
|
|
|
|
|
|
6. Botones add/delete normalizados a icon-only (Trash2 delete, flechas
|
|
|
|
|
|
Arrow{Up,Down,Left,Right} para table-ops, Plus para add).
|
|
|
|
|
|
|
|
|
|
|
|
**Ronda 2 — chrome systémico (DONE + verificado):**
|
|
|
|
|
|
- Inputs unificados: un solo frame bordeado (text inputs + NumberFields),
|
|
|
|
|
|
un solo focus-ring de acento (NO más `outline` inset sobre el texto, NO
|
|
|
|
|
|
más mezcla ghost/borde-negro). Specificity `[data-number-field][data-size]`
|
|
|
|
|
|
(0,3,0) gana al recipe sin tocar el componente.
|
|
|
|
|
|
- NumberField rowhead ensanchado a 6.75rem (decimales visibles); inline
|
|
|
|
|
|
rows pinneados a 6.75rem (Ancho/Alto misma anchura).
|
|
|
|
|
|
- Clamp de formato: `WordsNumRow` reenvía `max` → line-height clampa a 2.5,
|
|
|
|
|
|
font-size 12–40. Adiós "Interlineado 191".
|
|
|
|
|
|
- Secciones separadas: divisor hairline + headers uppercase en el
|
|
|
|
|
|
acordeón (`[data-accordion-item] + [data-accordion-item]`).
|
|
|
|
|
|
- Spacing link sincroniza al vincular (adopta el mayor de los dos lados).
|
|
|
|
|
|
- Colores raw → tokens `--color-*-solid` (theme-aware, sobreviven al portal
|
|
|
|
|
|
del popover).
|
|
|
|
|
|
|
|
|
|
|
|
**Ronda 3 — defectos de controles (DONE + verificado):**
|
|
|
|
|
|
- ToggleGroups con **valor por defecto seleccionado** (uno siempre activo):
|
|
|
|
|
|
Fuente=`default`, Grosor=`400`/Normal, Alinear=`left`, Borde=`none`. El
|
|
|
|
|
|
default se almacena como `undefined` en el modelo (limpio). Verificado:
|
|
|
|
|
|
`Predeterminada/Normal/Izquierda/Ninguno` seleccionados al activar bloque.
|
|
|
|
|
|
- Borde gana opción **`none`** (chip que limpia `border: undefined`). Nuevo
|
|
|
|
|
|
type `BorderChipValue = 'none' | BorderStyleValue`. Langs `BORDER_NONE`
|
|
|
|
|
|
+ bundle `border.none` ({en:'None', es:'Ninguno'}).
|
|
|
|
|
|
- Image "Ancho total" → **Switch** (era Toggle con texto on/off).
|
|
|
|
|
|
- **Bug del radio de esquina arreglado**: el ring de selección era un
|
|
|
|
|
|
`outline`/`box-shadow` que HEREDA el `border-radius` del bloque, así que
|
|
|
|
|
|
redondear el bloque a 40px redondeaba el ring. Ahora el ring es un
|
|
|
|
|
|
**pseudo-elemento `::after`** con radio FIJO (4px), `inset: -3px`,
|
|
|
|
|
|
`pointer-events: none`. Verificado: `blockRadius=40px, ringRadius=4px,
|
|
|
|
|
|
decoupled=true`.
|
|
|
|
|
|
- **Slider-only** para "Grosor del borde" + "Radio de esquina": nuevo prop
|
|
|
|
|
|
`sliderOnly` en `WordsNumRow` que oculta el NumberField y muestra un chip
|
|
|
|
|
|
de valor read-only (el slider + input era redundante). Verificado:
|
|
|
|
|
|
`hasNumberField:false, hasValueChip:true, hasSlider:true`.
|
|
|
|
|
|
|
|
|
|
|
|
### Archivos tocados este sprint
|
|
|
|
|
|
|
|
|
|
|
|
- `src/uix/eidos/components/words/words-inspector.svelte` — header markup,
|
|
|
|
|
|
4 ToggleGroups con default + border `none`, sliderOnly en 2 numrows.
|
|
|
|
|
|
- `src/uix/eidos/components/words/words-block-panel.svelte` — validaciones
|
|
|
|
|
|
(width/gap/src), table-ops icon-only, image full-width → Switch.
|
|
|
|
|
|
- `src/uix/eidos/components/words/words-num-row.svelte` — `max` forward +
|
|
|
|
|
|
prop `sliderOnly`.
|
|
|
|
|
|
- `src/uix/eidos/components/words/words-spacing-row.svelte` — link sincroniza.
|
|
|
|
|
|
- `src/uix/eidos/components/words/words.css` — chrome unificado, breadcrumb,
|
|
|
|
|
|
ring pseudo-elemento, tokens de color, anchuras inline.
|
|
|
|
|
|
- `src/uix/eidos/components/words/langs-inspector.ts` — `BORDER_NONE` + bundle.
|
|
|
|
|
|
- `src/uix/soma/components/words/exports.ts` — export `sanitizeWordsUrl`.
|
|
|
|
|
|
- (sesión previa, ya en el árbol) `words-activate.svelte`,
|
|
|
|
|
|
`engine/block-tree.ts` (active-block por id materializado).
|
|
|
|
|
|
|
|
|
|
|
|
### Pendiente / próximos pasos (donde retomar)
|
|
|
|
|
|
|
|
|
|
|
|
- El usuario sigue iterando sobre el diseño del inspector — esperar su
|
|
|
|
|
|
siguiente ronda de feedback visual. El patrón establecido: leer el
|
|
|
|
|
|
estado EXACTO del control antes de editar (los Edit fallan por
|
|
|
|
|
|
reformateo de prettier entre llamadas; usar substrings únicos cortos).
|
|
|
|
|
|
- Defecto conocido NO abordado: nada pendiente reportado al cierre.
|
|
|
|
|
|
- `npm run check` baseline error en `visual.ts:147` (cast WordsNode →
|
|
|
|
|
|
Record) sigue ahí — no es de este sprint.
|
|
|
|
|
|
|
|
|
|
|
|
### Nota de proceso
|
|
|
|
|
|
|
|
|
|
|
|
El harness de tools devolvió output vacío/corrupto varias veces esta
|
|
|
|
|
|
sesión (batches paralelos grandes se cancelan). Mitigación: editar de
|
|
|
|
|
|
uno en uno, verificar con `npm run check` (verdad de tipos) + navegador
|
|
|
|
|
|
(verdad de runtime) cuando un Read/Bash dé resultado dudoso.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Sesión 2026-05-29 (anterior)
|
|
|
|
|
|
|
refactor(words)!: remove V1 engine — V2 is now the sole engine
Deletes the V1 rich-text engine and the extensions/ tree. The public
document model flips from WordsDocument to WordsDocumentV2 (D1: V2 puro,
sin legacy — no backward-compat importer, V1-persisted docs no longer
load). Per D2, table striped/compact/cell-tone are dropped entirely
(props, commands, morfo attrs).
- Delete engine/{document,operations,commands,history,input,render,
find-replace,serialize-{html,json,markdown,text},dom-selection,index}
and the whole extensions/ tree (registry + image + table).
- Complete the V2 ops under operations-v2/: port commands, history,
input, find-replace, render-dom, serialize-text, extra-ops,
insert-block-types, markdown shortcuts, factories, dom-selection;
land R4A.2-R4A.8 (text/delete/marks-ops/paragraph-ops/delete-ops/
block-format/list-ops/link-ops/table-ops/selection-walkers).
- Rewire provider, exports, types, morfo/words.ts, eidos word
components, soma words.svelte, the content-test harness, and the
/uix/components/words demo to V2.
- Import accepts JSON (JSON.parse + validateWordsDocument gate) and
plain text only; HTML and Markdown remain export-only (lossy, no
faithful inverse parser).
Verify: npm run check 0 errors; words scope 455/456 (lone failure is the
sema-parity 5s timeout flake under parallel load — 6/6 isolated);
morfo:vocabulary exit 0; browser smoke of /uix/components/words green
(sample renders, text + JSON import round-trip, invalid JSON rejected
without mutating the doc).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4 months ago
|
|
|
|
Fecha de corte: **2026-05-29**. Rama: `active-uix`.
|
|
|
|
|
|
|
Build TextArea component + fix locale propagation bug across soma providers
Major work in this session:
1. NumberField demo refactored to expose Intl.NumberFormatOptions live
- 9 canonical presets (currency/unit/percent/compact/plain)
- Locale/currency/unit lists derived from $libs/langs, $libs/currency,
$format/units (no more hardcoded arrays)
- Default locale from uix.format.numbers.getLocale()
2. Bug fix: locale prop ignored when soma.format service is active
- soma/NumberField: 5 bypass points via new `hasOwnLocale` getter
(decimalSeparator, groupSeparator, formatValue, formatEditValue, parseValue)
- soma/Calendar: dateOrder respects locale prop (matches DateField precedence)
- soma/RangeCalendar: same dateOrder fix
- When the consumer passes `locale` prop, soma.nums/dates is bypassed
and Intl is used directly with the prop locale
3. TextArea — new component, full 4-layer stack
- morfo: 3 parts (provider/input/count), 3 events
(commit-input/commit-submit/shift-count-overflow), APG textbox
- soma: TextAreaProvider with autosize (scrollHeight measurement),
submitOn modes (false|enter|shift+enter|mod+enter), maxLength soft clamp,
Field integration (OR-merge), counter overflow tracking
- eidos: 5 sizes × 3 variants × 8 colors, autosize suppresses CSS resize
- sema: commit-submit + shift-count-overflow perceptual cues
- demo: canonical 6-tab depth, all props exposed as live controls
- PrimitiveTextareaAttributes added to soma/types/html.ts
4. Also includes accumulated unreleased work from prior sessions:
- Eidos ports: announce, clipboard, drag-drop, feed, table,
tree-grid, tree-view, virtual-grid, virtual-list, words
- Their canonical demos under web/routes/uix/components/
- Semantic events defined for utility morfos
(announce/clipboard/virtual-list/virtual-grid)
- Tree indent guides fixed (TreeView ::before, TreeGrid stacked gradients)
- Sidebar reorganized with Data + Utilities groups
- Eidos words content layer and primitives
Reference library survey performed before TextArea API design (Mantine,
Radix Themes, Chakra v3, React Aria, Ark UI, Bits UI, shadcn, MUI, Prime).
Decision documented: no separate CurrencyField/UnitField — single
NumberField with formatOptions is the canon (matches all surveyed
headless libs except PrimeReact's mode enum, which still uses 1 component).
svelte-check: 0 errors in modified/created files.
See CONTINUE.md for full session notes and next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
## TL;DR de la sesión
|
|
|
|
|
|
|
refactor(words)!: remove V1 engine — V2 is now the sole engine
Deletes the V1 rich-text engine and the extensions/ tree. The public
document model flips from WordsDocument to WordsDocumentV2 (D1: V2 puro,
sin legacy — no backward-compat importer, V1-persisted docs no longer
load). Per D2, table striped/compact/cell-tone are dropped entirely
(props, commands, morfo attrs).
- Delete engine/{document,operations,commands,history,input,render,
find-replace,serialize-{html,json,markdown,text},dom-selection,index}
and the whole extensions/ tree (registry + image + table).
- Complete the V2 ops under operations-v2/: port commands, history,
input, find-replace, render-dom, serialize-text, extra-ops,
insert-block-types, markdown shortcuts, factories, dom-selection;
land R4A.2-R4A.8 (text/delete/marks-ops/paragraph-ops/delete-ops/
block-format/list-ops/link-ops/table-ops/selection-walkers).
- Rewire provider, exports, types, morfo/words.ts, eidos word
components, soma words.svelte, the content-test harness, and the
/uix/components/words demo to V2.
- Import accepts JSON (JSON.parse + validateWordsDocument gate) and
plain text only; HTML and Markdown remain export-only (lossy, no
faithful inverse parser).
Verify: npm run check 0 errors; words scope 455/456 (lone failure is the
sema-parity 5s timeout flake under parallel load — 6/6 isolated);
morfo:vocabulary exit 0; browser smoke of /uix/components/words green
(sample renders, text + JSON import round-trip, invalid JSON rejected
without mutating the doc).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4 months ago
|
|
|
|
**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)
|
fix(words): silence canvas truly, drag for list/table, no overlay hovers + day-end docs
EV-G final pass + EOD documentation.
Words editor:
- Stop emitting `contact-focus` from soma. Removing the per-component
cascade rule earlier didn't fully silence the editor — the `contact`
family carries a BASE sound signature in SEMA_MAP (pitch 800, gain
0.25) that fires even without a cascade rule. The trigger call is
commented out (event no longer emitted; telemetry empty). Per spec:
"sigue apareciendo el puto sonido, el ring".
- Drag handle + inserter now cover lists AND tables. Selectors
expanded from `[data-words-node="block"][data-words-path]` to also
match `[data-words-node="list"]` and `[data-words-node="table"]`.
Top-level lists / tables get the grip + the "+" seam under them.
- Removed :hover from `[data-words-block-handle]` and
`[data-words-block-inserter-button]`. Gutter overlays are AMBIENT —
visual state via data-attrs only (`data-open`, `data-grabbed`,
`data-dragging`). Per spec: "quítale el hover al botón de añadir
bloque, si el handle no tiene este tampoco".
- Rail tokens hoisted to recipe: `words.rail-bg = #d4d4d4`,
`words.rail-border = #9a9a9a`. Fixed-tone (not theme-aware) — the
rail emulates the physical margin of a notebook; same look in
light/dark. Recipe-css-contract test was rejecting raw hex in
words.css.
Docs:
- CLAUDE.md hand-off 2026-05-28 #2 documenting the 6 EV-* Words
sprints + 5 architectural decisions with lasting effect (canvas
silent, fixed-tone tokens for physical metaphors, drag predicate
covers list/table, ambient overlays, insertParagraph empty-block
guard).
- continue.md (CONTINUE.md) rewritten for tomorrow's pickup:
TL;DR of the two halves (toggle-group structural + Words UX),
remaining bugs (drag handle outside rail on code blocks, heading
inline level change), and the heredados (POLISH-1b, F2.3-F2.6,
DRAGDROP-SOMA).
Verification: 467/467 morfo + sema + eidos + soma/words tests pass.
85/85 engine pass. `npm run check`: 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
|
|
|
|
|
|
| Gate | Status | Notas |
|
|
|
|
|
|
|---|---|---|
|
refactor(words)!: remove V1 engine — V2 is now the sole engine
Deletes the V1 rich-text engine and the extensions/ tree. The public
document model flips from WordsDocument to WordsDocumentV2 (D1: V2 puro,
sin legacy — no backward-compat importer, V1-persisted docs no longer
load). Per D2, table striped/compact/cell-tone are dropped entirely
(props, commands, morfo attrs).
- Delete engine/{document,operations,commands,history,input,render,
find-replace,serialize-{html,json,markdown,text},dom-selection,index}
and the whole extensions/ tree (registry + image + table).
- Complete the V2 ops under operations-v2/: port commands, history,
input, find-replace, render-dom, serialize-text, extra-ops,
insert-block-types, markdown shortcuts, factories, dom-selection;
land R4A.2-R4A.8 (text/delete/marks-ops/paragraph-ops/delete-ops/
block-format/list-ops/link-ops/table-ops/selection-walkers).
- Rewire provider, exports, types, morfo/words.ts, eidos word
components, soma words.svelte, the content-test harness, and the
/uix/components/words demo to V2.
- Import accepts JSON (JSON.parse + validateWordsDocument gate) and
plain text only; HTML and Markdown remain export-only (lossy, no
faithful inverse parser).
Verify: npm run check 0 errors; words scope 455/456 (lone failure is the
sema-parity 5s timeout flake under parallel load — 6/6 isolated);
morfo:vocabulary exit 0; browser smoke of /uix/components/words green
(sample renders, text + JSON import round-trip, invalid JSON rejected
without mutating the doc).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4 months ago
|
|
|
|
| `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.
|
refactor(words)!: remove V1 engine — V2 is now the sole engine
Deletes the V1 rich-text engine and the extensions/ tree. The public
document model flips from WordsDocument to WordsDocumentV2 (D1: V2 puro,
sin legacy — no backward-compat importer, V1-persisted docs no longer
load). Per D2, table striped/compact/cell-tone are dropped entirely
(props, commands, morfo attrs).
- Delete engine/{document,operations,commands,history,input,render,
find-replace,serialize-{html,json,markdown,text},dom-selection,index}
and the whole extensions/ tree (registry + image + table).
- Complete the V2 ops under operations-v2/: port commands, history,
input, find-replace, render-dom, serialize-text, extra-ops,
insert-block-types, markdown shortcuts, factories, dom-selection;
land R4A.2-R4A.8 (text/delete/marks-ops/paragraph-ops/delete-ops/
block-format/list-ops/link-ops/table-ops/selection-walkers).
- Rewire provider, exports, types, morfo/words.ts, eidos word
components, soma words.svelte, the content-test harness, and the
/uix/components/words demo to V2.
- Import accepts JSON (JSON.parse + validateWordsDocument gate) and
plain text only; HTML and Markdown remain export-only (lossy, no
faithful inverse parser).
Verify: npm run check 0 errors; words scope 455/456 (lone failure is the
sema-parity 5s timeout flake under parallel load — 6/6 isolated);
morfo:vocabulary exit 0; browser smoke of /uix/components/words green
(sample renders, text + JSON import round-trip, invalid JSON rejected
without mutating the doc).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4 months ago
|
|
|
|
|
|
|
|
|
|
### ✅ 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.
|
refactor(words)!: remove V1 engine — V2 is now the sole engine
Deletes the V1 rich-text engine and the extensions/ tree. The public
document model flips from WordsDocument to WordsDocumentV2 (D1: V2 puro,
sin legacy — no backward-compat importer, V1-persisted docs no longer
load). Per D2, table striped/compact/cell-tone are dropped entirely
(props, commands, morfo attrs).
- Delete engine/{document,operations,commands,history,input,render,
find-replace,serialize-{html,json,markdown,text},dom-selection,index}
and the whole extensions/ tree (registry + image + table).
- Complete the V2 ops under operations-v2/: port commands, history,
input, find-replace, render-dom, serialize-text, extra-ops,
insert-block-types, markdown shortcuts, factories, dom-selection;
land R4A.2-R4A.8 (text/delete/marks-ops/paragraph-ops/delete-ops/
block-format/list-ops/link-ops/table-ops/selection-walkers).
- Rewire provider, exports, types, morfo/words.ts, eidos word
components, soma words.svelte, the content-test harness, and the
/uix/components/words demo to V2.
- Import accepts JSON (JSON.parse + validateWordsDocument gate) and
plain text only; HTML and Markdown remain export-only (lossy, no
faithful inverse parser).
Verify: npm run check 0 errors; words scope 455/456 (lone failure is the
sema-parity 5s timeout flake under parallel load — 6/6 isolated);
morfo:vocabulary exit 0; browser smoke of /uix/components/words green
(sample renders, text + JSON import round-trip, invalid JSON rejected
without mutating the doc).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4 months ago
|
|
|
|
|
|
|
|
|
|
## 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
|
refactor(words)!: remove V1 engine — V2 is now the sole engine
Deletes the V1 rich-text engine and the extensions/ tree. The public
document model flips from WordsDocument to WordsDocumentV2 (D1: V2 puro,
sin legacy — no backward-compat importer, V1-persisted docs no longer
load). Per D2, table striped/compact/cell-tone are dropped entirely
(props, commands, morfo attrs).
- Delete engine/{document,operations,commands,history,input,render,
find-replace,serialize-{html,json,markdown,text},dom-selection,index}
and the whole extensions/ tree (registry + image + table).
- Complete the V2 ops under operations-v2/: port commands, history,
input, find-replace, render-dom, serialize-text, extra-ops,
insert-block-types, markdown shortcuts, factories, dom-selection;
land R4A.2-R4A.8 (text/delete/marks-ops/paragraph-ops/delete-ops/
block-format/list-ops/link-ops/table-ops/selection-walkers).
- Rewire provider, exports, types, morfo/words.ts, eidos word
components, soma words.svelte, the content-test harness, and the
/uix/components/words demo to V2.
- Import accepts JSON (JSON.parse + validateWordsDocument gate) and
plain text only; HTML and Markdown remain export-only (lossy, no
faithful inverse parser).
Verify: npm run check 0 errors; words scope 455/456 (lone failure is the
sema-parity 5s timeout flake under parallel load — 6/6 isolated);
morfo:vocabulary exit 0; browser smoke of /uix/components/words green
(sample renders, text + JSON import round-trip, invalid JSON rejected
without mutating the doc).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4 months ago
|
|
|
|
|
feat(words): inspector UX overhaul — id-based active block, controls, chrome
Active-block addressing by materialized-path id (words-activate.svelte +
engine/block-tree.ts): click sets the active block explicitly; inspector
follows it, immune to selection churn. Breadcrumb navigates ancestry.
Inspector controls:
- ToggleGroups (font / weight / align / border) always keep one option
selected; the default value stores as undefined to keep the model clean.
- Border gains a `none` chip that clears the border (BorderChipValue).
- Image full-width is a Switch; image src + column width/gap validate on
commit (sanitizeWordsUrl, CSS-length/flex checks) with a [data-invalid] ring.
- Corner-radius + border-width are slider-only (WordsNumRow `sliderOnly`):
the paired numeric stepper was redundant.
- Numeric fields clamp to their range (line-height ≤ 2.5, font-size 12-40).
Inspector chrome:
- Unified field frame + single accent focus ring (no inset outline over text,
no ghost/black-border mix).
- Section dividers + uppercase accordion headers; styled breadcrumb row.
- Depth-tied accent (primary/affirm/loss/risk) shared by header mark and the
canvas active-block ring.
- Selection ring is an ::after pseudo-element with a fixed radius, so a block
the user rounded no longer rounds its selection indicator.
- Raw hex/rgba replaced by --color-*-solid tokens (theme-aware, portal-safe).
Columns: empty on insert, gap control, per-column width; Add-block removed
from the Columns BLOCK panel (lives in the column inspector). Shift+Enter
soft break renders via white-space: pre-wrap.
npm run check: 1 pre-existing baseline error (visual.ts:147). Words soma
452/452. Verified end-to-end in the browser.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
### P0 — RESUELTO 2026-05-31 (column inserter end-to-end + path encoding)
|
|
|
|
|
|
|
|
|
|
|
|
Lo que ayer documenté como "selection sync con paths nested" resultó
|
|
|
|
|
|
ser TRES bugs distintos, todos cerrados:
|
|
|
|
|
|
|
|
|
|
|
|
1. **Path 5-segmentos en `insertBlockInColumn`** — mi op pasaba
|
|
|
|
|
|
`[colsIdx, colIdx, innerIdx, 0]` (= el text node) a
|
|
|
|
|
|
`pointFromInlineTextOffset`, que espera la ruta al CONTENEDOR.
|
|
|
|
|
|
La función añade su propio inline-index al final → producía
|
|
|
|
|
|
`[12, 0, 0, 0, 0]` (5 segmentos) en vez del canónico `[12, 0, 0, 0]`.
|
|
|
|
|
|
Fix: pasar el container path (`[12, 0, 0]` para heading, `[12, 0, 0, 0]`
|
|
|
|
|
|
para list-item) — la función ya añade el `, inlineIdx` al final.
|
|
|
|
|
|
2. **Selectionchange echo sobrescribía el modelo** — `api.focus()`
|
|
|
|
|
|
movía el caret DOM a inicio del doc (default browser behavior),
|
|
|
|
|
|
disparaba selectionchange → syncSelectionFromDom → updateSelection
|
|
|
|
|
|
sobrescribía la selección que el op había puesto. Fix: capturar
|
|
|
|
|
|
`intended = api.selection` inmediatamente después del op y
|
|
|
|
|
|
re-aplicarla vía `setTimeout(0)` AFTER del echo handler.
|
|
|
|
|
|
3. **DropdownMenu focus trap interceptaba `api.focus()`** — Fix:
|
|
|
|
|
|
`trapFocus={false}` en `DropdownMenu.Content` desactiva el
|
|
|
|
|
|
`focusin` capture-listener.
|
|
|
|
|
|
|
|
|
|
|
|
Edición DOM↔model con paths nested SÍ funciona correctamente — el
|
|
|
|
|
|
typing dentro de columnas, callouts, listas en columnas, etc., todo
|
|
|
|
|
|
ok desde el primer click. El problema era exclusivamente el flow
|
|
|
|
|
|
imperativo del inserter (focus + insert + selección).
|
|
|
|
|
|
|
|
|
|
|
|
**Lo que se verificó end-to-end en navegador (Chrome):**
|
|
|
|
|
|
- Click `+` en columna vacía → dropdown abre, primer-item auto-focus.
|
|
|
|
|
|
- Pick "Heading" → "Title" stub aparece + se selecciona.
|
|
|
|
|
|
- Tipear inmediatamente → "Title" se reemplaza con el texto tecleado
|
|
|
|
|
|
(Notion-style replace-stub-on-first-keystroke).
|
|
|
|
|
|
- Inspector cambia automáticamente al panel correcto del nuevo bloque.
|
|
|
|
|
|
- Doc title y resto del doc intactos — typing va al lugar correcto.
|
|
|
|
|
|
|
|
|
|
|
|
**Pieces en disco:**
|
|
|
|
|
|
- `engine/operations/insert-block-types.ts:381` —
|
|
|
|
|
|
`insertBlockInColumn` op con container-path correcto.
|
|
|
|
|
|
- `engine/operations/commands.ts` — command + dispatcher.
|
|
|
|
|
|
- `words-provider.svelte.ts:700-733` — `applyCommandWithOptions`
|
|
|
|
|
|
dispara restoreDomSelection tras cambio de selección.
|
|
|
|
|
|
- `words-provider.svelte.ts:1274` — `api.restoreCaret()` expuesto.
|
|
|
|
|
|
- `soma/components/words/types.ts` — `restoreCaret` añadido al
|
|
|
|
|
|
snippet API type.
|
|
|
|
|
|
- `words-column-inserter.svelte` — flow `close → tick → focus →
|
|
|
|
|
|
applyCommand → setTimeout(setSelection(intended))`.
|
|
|
|
|
|
|
|
|
|
|
|
### Lo que SÍ quedó cabledado en columns (sesión 2026-05-31)
|
|
|
|
|
|
|
|
|
|
|
|
- **`insertBlockInColumn` engine op** (`insert-block-types.ts`) —
|
|
|
|
|
|
inserta un bloque en una columna desde un trigger sin caret (el
|
|
|
|
|
|
`+` overlay). Devuelve `{document, selection, activeMarks}` en una
|
|
|
|
|
|
transacción. Verbatim de la doctrina Tiptap `chain().focus()
|
|
|
|
|
|
.insertContent().run()` adaptada a nuestro stack.
|
|
|
|
|
|
- **Command `insertBlockInColumn`** + dispatcher en
|
|
|
|
|
|
`engine/operations/commands.ts`.
|
|
|
|
|
|
- **`applyCommandWithOptions` ahora dispara `restoreDomSelection`**
|
|
|
|
|
|
cuando la selección cambia (excepto batches de typing). Era el
|
|
|
|
|
|
hueco: comandos invocados desde fuera del editor (overlay buttons,
|
|
|
|
|
|
drag-drop, column inserter) actualizaban el modelo pero el caret
|
|
|
|
|
|
DOM se quedaba donde el usuario clickeó por última vez. Fix en
|
|
|
|
|
|
`words-provider.svelte.ts:700-733`.
|
|
|
|
|
|
- **`WordsProviderSnippetProps` sincronizado** con la implementación:
|
|
|
|
|
|
`selectedBlockPath`, `selectAtomicBlock(idx, blockPath?)`,
|
|
|
|
|
|
`setSelection`. Antes faltaban y rompían el typecheck del eidos.
|
|
|
|
|
|
- **`words-column-inserter.svelte` simplificado** — patrón:
|
|
|
|
|
|
`applyCommand` + close dropdown + `onCloseAutoFocus preventDefault`
|
|
|
|
|
|
(bloquea el return-to-trigger que producía Space-repeat). Safety
|
|
|
|
|
|
timeout 250ms para el `busy` lock — el botón nunca se queda muerto
|
|
|
|
|
|
por una excepción intermedia.
|
|
|
|
|
|
- **`web/routes/uix/components/words/+page.svelte`** — Columns block
|
|
|
|
|
|
añadido al doc inicial para que el test fixture esté siempre a mano.
|
|
|
|
|
|
|
|
|
|
|
|
**Lo que NO consigué cerrar:** el typing dentro de columnas. Ese es
|
|
|
|
|
|
el P0 de arriba — el inserter está listo para cuando el path encoding
|
|
|
|
|
|
nested funcione.
|
|
|
|
|
|
|
refactor(words)!: remove V1 engine — V2 is now the sole engine
Deletes the V1 rich-text engine and the extensions/ tree. The public
document model flips from WordsDocument to WordsDocumentV2 (D1: V2 puro,
sin legacy — no backward-compat importer, V1-persisted docs no longer
load). Per D2, table striped/compact/cell-tone are dropped entirely
(props, commands, morfo attrs).
- Delete engine/{document,operations,commands,history,input,render,
find-replace,serialize-{html,json,markdown,text},dom-selection,index}
and the whole extensions/ tree (registry + image + table).
- Complete the V2 ops under operations-v2/: port commands, history,
input, find-replace, render-dom, serialize-text, extra-ops,
insert-block-types, markdown shortcuts, factories, dom-selection;
land R4A.2-R4A.8 (text/delete/marks-ops/paragraph-ops/delete-ops/
block-format/list-ops/link-ops/table-ops/selection-walkers).
- Rewire provider, exports, types, morfo/words.ts, eidos word
components, soma words.svelte, the content-test harness, and the
/uix/components/words demo to V2.
- Import accepts JSON (JSON.parse + validateWordsDocument gate) and
plain text only; HTML and Markdown remain export-only (lossy, no
faithful inverse parser).
Verify: npm run check 0 errors; words scope 455/456 (lone failure is the
sema-parity 5s timeout flake under parallel load — 6/6 isolated);
morfo:vocabulary exit 0; browser smoke of /uix/components/words green
(sample renders, text + JSON import round-trip, invalid JSON rejected
without mutating the doc).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4 months ago
|
|
|
|
### 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.
|