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/src/uix/soma/components/words/engine/operations/delete-ops.test.ts

318 lines
12 KiB

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
/**
* R4A.4 — deleteBackward + deleteForward tests.
*/
import { describe, expect, it } from 'vitest';
import { deleteBackward, deleteForward } from './delete-ops';
import { createState } from './text';
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
import { createCollapsedSelection } from '../selection';
import { resolveTextNode } from './helpers';
import { WORDS_VERSION, type WordsDocument } from '../types';
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
function doc(...children: WordsDocument['children']): WordsDocument {
return { version: WORDS_VERSION, children };
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
}
function rangeSelection(
startPath: readonly number[],
startOffset: number,
endPath: readonly number[],
endOffset: number
) {
return {
anchor: { path: startPath, offset: startOffset },
focus: { path: endPath, offset: endOffset }
};
}
// ── deleteBackward — within text ─────────────────────────────────────────
describe('deleteBackward — within text', () => {
it('deletes the char to the left of the caret', () => {
const d = doc({ type: 'paragraph', children: [{ type: 'text', text: 'hello' }] });
const sel = createCollapsedSelection([0, 0], 5);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(true);
expect(resolveTextNode(r.state.document, [0, 0])!.text).toBe('hell');
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
expect(r.state.selection?.focus.offset).toBe(4);
});
it('non-collapsed → delegates to deleteRange', () => {
const d = doc({ type: 'paragraph', children: [{ type: 'text', text: 'hello' }] });
const sel = rangeSelection([0, 0], 1, [0, 0], 4);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(true);
expect(resolveTextNode(r.state.document, [0, 0])!.text).toBe('ho');
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
});
it('caret at start of first paragraph in doc → no-op', () => {
const d = doc({ type: 'paragraph', children: [{ type: 'text', text: 'hi' }] });
const sel = createCollapsedSelection([0, 0], 0);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(false);
});
});
// ── deleteBackward — merge with previous block ───────────────────────────
describe('deleteBackward — merge previous top-level block', () => {
it('merges two paragraphs when at start of second', () => {
const d = doc(
{ type: 'paragraph', children: [{ type: 'text', text: 'hello' }] },
{ type: 'paragraph', children: [{ type: 'text', text: ' world' }] }
);
const sel = createCollapsedSelection([1, 0], 0);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(true);
expect(r.state.document.children).toHaveLength(1);
expect(resolveTextNode(r.state.document, [0, 0])!.text).toBe('hello world');
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
// Caret lands at the merge boundary.
expect(r.state.selection?.focus.offset).toBe(5);
});
it('merges paragraph into heading: result keeps heading type', () => {
const d = doc(
{ type: 'heading', level: 2, children: [{ type: 'text', text: 'Title' }] },
{ type: 'paragraph', children: [{ type: 'text', text: ' body' }] }
);
const sel = createCollapsedSelection([1, 0], 0);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(true);
expect(r.state.document.children).toHaveLength(1);
const block = r.state.document.children[0];
expect(block.type).toBe('heading');
if (block.type !== 'heading') return;
expect(block.children[0]).toMatchObject({ type: 'text', text: 'Title body' });
});
it('no-op when previous block is atomic (image)', () => {
const d = doc(
{ type: 'image', src: 'x' },
{ type: 'paragraph', children: [{ type: 'text', text: 'hi' }] }
);
const sel = createCollapsedSelection([1, 0], 0);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(false);
});
});
// ── deleteBackward — list ────────────────────────────────────────────────
describe('deleteBackward — list', () => {
it('item index > 0: merges into previous item', () => {
const d = doc({
type: 'list',
kind: 'unordered',
items: [
{ children: [{ type: 'text', text: 'first' }] },
{ children: [{ type: 'text', text: 'second' }] }
]
});
const sel = createCollapsedSelection([0, 1, 0], 0);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(true);
const list = r.state.document.children[0];
if (list.type !== 'list') throw new Error('expected list');
expect(list.items).toHaveLength(1);
expect(list.items[0].children[0]).toMatchObject({ text: 'firstsecond' });
// Caret at merge boundary.
expect(r.state.selection?.focus.path).toEqual([0, 0, 0]);
expect(r.state.selection?.focus.offset).toBe(5);
});
it('item index 0: exits the list (first item becomes paragraph)', () => {
const d = doc({
type: 'list',
kind: 'unordered',
items: [
{ children: [{ type: 'text', text: 'first' }] },
{ children: [{ type: 'text', text: 'second' }] }
]
});
const sel = createCollapsedSelection([0, 0, 0], 0);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(true);
// Now: [paragraph(first), list(second)]
expect(r.state.document.children).toHaveLength(2);
expect(r.state.document.children[0].type).toBe('paragraph');
expect(r.state.document.children[1].type).toBe('list');
});
it('item index 0 + only one item: becomes a single paragraph', () => {
const d = doc({
type: 'list',
kind: 'unordered',
items: [{ children: [{ type: 'text', text: 'lone' }] }]
});
const sel = createCollapsedSelection([0, 0, 0], 0);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(true);
expect(r.state.document.children).toHaveLength(1);
expect(r.state.document.children[0].type).toBe('paragraph');
});
});
// ── deleteBackward — table cell ──────────────────────────────────────────
describe('deleteBackward — table cell', () => {
feat(words): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
it('cell col > 0: backspace at cell start does NOT merge across cells', () => {
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
const d = doc({
type: 'table',
rows: [
{
cells: [
feat(words): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
{ children: [{ type: 'paragraph', children: [{ type: 'text', text: 'A' }] }] },
{ children: [{ type: 'paragraph', children: [{ type: 'text', text: 'B' }] }] }
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): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
// Caret at the start of the SECOND cell's first block. Cells are
// fixed grid positions — backspace here is a no-op (no cross-cell merge).
const sel = createCollapsedSelection([0, 0, 1, 0, 0], 0);
const r = deleteBackward(createState(d, sel));
feat(words): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
expect(r.changed).toBe(false);
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
const table = r.state.document.children[0];
if (table.type !== 'table') throw new Error('expected table');
feat(words): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
expect(table.rows[0].cells).toHaveLength(2);
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
});
it('cell col 0: no-op (does NOT merge across rows)', () => {
const d = doc({
type: 'table',
rows: [
feat(words): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
{
cells: [{ children: [{ type: 'paragraph', children: [{ type: 'text', text: 'r0c0' }] }] }]
},
{
cells: [{ children: [{ type: 'paragraph', children: [{ type: 'text', text: 'r1c0' }] }] }]
}
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
]
});
const sel = createCollapsedSelection([0, 1, 0, 0], 0);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(false);
});
});
// ── deleteBackward — callout ─────────────────────────────────────────────
describe('deleteBackward — callout', () => {
it('merges two inner paragraphs when at start of second', () => {
const d = doc({
type: 'callout',
intent: 'risk',
children: [
{ type: 'paragraph', children: [{ type: 'text', text: 'first' }] },
{ type: 'paragraph', children: [{ type: 'text', text: 'second' }] }
]
});
const sel = createCollapsedSelection([0, 1, 0], 0);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(true);
const callout = r.state.document.children[0];
if (callout.type !== 'callout') throw new Error('expected callout');
expect(callout.children).toHaveLength(1);
const inner = callout.children[0];
if (inner.type !== 'paragraph') throw new Error('expected paragraph');
expect(inner.children[0]).toMatchObject({ text: 'firstsecond' });
});
it('first inner block at offset 0: no-op (does NOT exit callout)', () => {
const d = doc({
type: 'callout',
intent: 'risk',
children: [{ type: 'paragraph', children: [{ type: 'text', text: 'inside' }] }]
});
const sel = createCollapsedSelection([0, 0, 0], 0);
const r = deleteBackward(createState(d, sel));
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
expect(r.changed).toBe(false);
});
});
// ── deleteForward — within text ──────────────────────────────────────────
describe('deleteForward — within text', () => {
it('deletes the char to the right of the caret', () => {
const d = doc({ type: 'paragraph', children: [{ type: 'text', text: 'hello' }] });
const sel = createCollapsedSelection([0, 0], 0);
const r = deleteForward(createState(d, sel));
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
expect(r.changed).toBe(true);
expect(resolveTextNode(r.state.document, [0, 0])!.text).toBe('ello');
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
expect(r.state.selection?.focus.offset).toBe(0);
});
it('non-collapsed → delegates to deleteRange', () => {
const d = doc({ type: 'paragraph', children: [{ type: 'text', text: 'hello' }] });
const sel = rangeSelection([0, 0], 1, [0, 0], 4);
const r = deleteForward(createState(d, sel));
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
expect(r.changed).toBe(true);
expect(resolveTextNode(r.state.document, [0, 0])!.text).toBe('ho');
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
});
});
// ── deleteForward — merge with next block ────────────────────────────────
describe('deleteForward — merge next block', () => {
it('merges next paragraph into current', () => {
const d = doc(
{ type: 'paragraph', children: [{ type: 'text', text: 'hello' }] },
{ type: 'paragraph', children: [{ type: 'text', text: ' world' }] }
);
const sel = createCollapsedSelection([0, 0], 5);
const r = deleteForward(createState(d, sel));
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
expect(r.changed).toBe(true);
expect(r.state.document.children).toHaveLength(1);
expect(resolveTextNode(r.state.document, [0, 0])!.text).toBe('hello world');
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
});
it('no-op at end of last paragraph', () => {
const d = doc({ type: 'paragraph', children: [{ type: 'text', text: 'hello' }] });
const sel = createCollapsedSelection([0, 0], 5);
const r = deleteForward(createState(d, sel));
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
expect(r.changed).toBe(false);
});
});
describe('deleteForward — list / table', () => {
it('list: merges with next item when at end of current', () => {
const d = doc({
type: 'list',
kind: 'unordered',
items: [
{ children: [{ type: 'text', text: 'first' }] },
{ children: [{ type: 'text', text: 'second' }] }
]
});
const sel = createCollapsedSelection([0, 0, 0], 5);
const r = deleteForward(createState(d, sel));
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
expect(r.changed).toBe(true);
const list = r.state.document.children[0];
if (list.type !== 'list') throw new Error('expected list');
expect(list.items).toHaveLength(1);
expect(list.items[0].children[0]).toMatchObject({ text: 'firstsecond' });
});
feat(words): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
it('table cell: deleteForward at cell end does NOT merge across cells', () => {
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
const d = doc({
type: 'table',
rows: [
{
cells: [
feat(words): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
{ children: [{ type: 'paragraph', children: [{ type: 'text', text: 'A' }] }] },
{ children: [{ type: 'paragraph', children: [{ type: 'text', text: 'B' }] }] }
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): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
// Caret at the end of the FIRST cell's first block. Cells are fixed
// grid positions — deleteForward here is a no-op (no cross-cell merge).
const sel = createCollapsedSelection([0, 0, 0, 0, 0], 1);
const r = deleteForward(createState(d, sel));
feat(words): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
expect(r.changed).toBe(false);
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
const table = r.state.document.children[0];
if (table.type !== 'table') throw new Error('expected table');
feat(words): table cells hold blocks (TableCell.children → WordsBlock[]) P5m. A table cell now holds full BLOCKS (paragraphs, lists, images, nested tables) like Callout / Column, not just inline text. A fresh cell seeds one empty paragraph; the existing generic block machinery (path walker, render, validate, normalize) handles cells the same way it handles callout / column. Atomic model change across the engine: - types: TableCell.children: WordsInline[] -> WordsBlock[]. - factories / table-ops / extra-ops: cells seed [paragraph], not [text]. - normalize: normalizeTableCells recurses each child block (mirrors callout); empty cell seeds a paragraph. - built-ins table spec: render -> renderBlock per child; validate -> validateBlock per child; HTML -> blockToHtml per child (<td><p>...</p></td>); Markdown -> flatten blocks to inline joined with <br> (GFM cells are inline-only; lossy, consistent with markdown being export-only). - serialize-text + selection-walkers: cell text recurses block children. - delete-ops: inline cross-cell merge (mergeTablePrev/Next) replaced by recurseIntoTableMerge — treats the cell's blocks as a sub-document, so backspace merges blocks WITHIN a cell; at a cell's first block it is a no-op (cells never merge across the fixed grid — matches mainstream editors). Caret paths into a cell are one level deeper now ([t,r,c,blockIdx,...]). All table test fixtures wrapped (inline -> paragraph) and 13 assertions updated to the new behavior (<p> wrappers, deeper paths, no cross-cell merge). Browser-verified: cells render block content (path 14.0.0.0), table renders clean. npm run check: 0 words errors (1 pre-existing grafito baseline). Words tests: 452/452. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
expect(table.rows[0].cells).toHaveLength(2);
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
});
});

Powered by TurnKey Linux.