@quillmark/svelte 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +2 -2
  2. package/THEMING.md +11 -7
  3. package/dist/core/address.d.ts +7 -7
  4. package/dist/core/address.js +6 -6
  5. package/dist/core/bloom.d.ts +5 -5
  6. package/dist/core/bloom.js +8 -8
  7. package/dist/core/codec/anchor.d.ts +1 -1
  8. package/dist/core/codec/anchor.js +1 -1
  9. package/dist/core/codec/atoms.d.ts +10 -0
  10. package/dist/core/codec/atoms.js +78 -0
  11. package/dist/core/codec/clipboard.d.ts +59 -0
  12. package/dist/core/codec/clipboard.js +202 -0
  13. package/dist/core/codec/code.d.ts +1 -1
  14. package/dist/core/codec/code.js +4 -4
  15. package/dist/core/codec/decode.d.ts +1 -1
  16. package/dist/core/codec/decode.js +8 -8
  17. package/dist/core/codec/encode.d.ts +4 -4
  18. package/dist/core/codec/encode.js +10 -10
  19. package/dist/core/codec/field.d.ts +21 -26
  20. package/dist/core/codec/field.js +33 -32
  21. package/dist/core/codec/index.js +5 -8
  22. package/dist/core/codec/inputrules.js +56 -18
  23. package/dist/core/codec/islands.d.ts +11 -7
  24. package/dist/core/codec/islands.js +16 -10
  25. package/dist/core/codec/keymap.d.ts +5 -6
  26. package/dist/core/codec/keymap.js +8 -7
  27. package/dist/core/codec/lists.d.ts +3 -3
  28. package/dist/core/codec/lists.js +32 -13
  29. package/dist/core/codec/marks.d.ts +1 -1
  30. package/dist/core/codec/marks.js +1 -1
  31. package/dist/core/codec/positions.js +1 -1
  32. package/dist/core/codec/prose.css +230 -215
  33. package/dist/core/codec/schema.js +4 -4
  34. package/dist/core/codec/slash.d.ts +39 -41
  35. package/dist/core/codec/slash.js +0 -0
  36. package/dist/core/codec/table-view.d.ts +12 -16
  37. package/dist/core/codec/table-view.js +488 -292
  38. package/dist/core/codec/table.d.ts +42 -15
  39. package/dist/core/codec/table.js +70 -26
  40. package/dist/core/errors.d.ts +5 -5
  41. package/dist/core/index.js +2 -2
  42. package/dist/core/lifecycle.js +1 -1
  43. package/dist/core/rebind.svelte.d.ts +2 -2
  44. package/dist/core/rebind.svelte.js +4 -4
  45. package/dist/core/teardown.d.ts +1 -1
  46. package/dist/core/teardown.js +1 -1
  47. package/dist/core/theme.css +144 -54
  48. package/dist/preset/index.css +1 -1
  49. package/dist/preset/recipes.css +31 -29
  50. package/dist/preset/scale.css +36 -25
  51. package/dist/preview/Preview.svelte +12 -10
  52. package/dist/preview/Preview.svelte.d.ts +3 -3
  53. package/dist/preview/bridge.js +4 -4
  54. package/dist/preview/controller.js +3 -3
  55. package/dist/preview/geometry.d.ts +2 -2
  56. package/dist/preview/geometry.js +2 -2
  57. package/dist/preview/index.js +3 -3
  58. package/dist/preview/overlay.d.ts +1 -1
  59. package/dist/preview/overlay.js +3 -3
  60. package/dist/preview/paint.js +15 -17
  61. package/dist/visual/ArrayField.svelte +80 -66
  62. package/dist/visual/ArrayField.svelte.d.ts +1 -1
  63. package/dist/visual/BooleanField.svelte +8 -6
  64. package/dist/visual/BooleanField.svelte.d.ts +2 -2
  65. package/dist/visual/Card.svelte +151 -325
  66. package/dist/visual/Card.svelte.d.ts +2 -6
  67. package/dist/visual/CardControls.svelte +1 -1
  68. package/dist/visual/DateField.svelte +19 -19
  69. package/dist/visual/DateField.svelte.d.ts +3 -3
  70. package/dist/visual/DiagnosticList.svelte +1 -1
  71. package/dist/visual/EnumField.svelte +16 -16
  72. package/dist/visual/EnumField.svelte.d.ts +2 -2
  73. package/dist/visual/Field.svelte +128 -115
  74. package/dist/visual/Field.svelte.d.ts +1 -1
  75. package/dist/visual/FieldHint.svelte +24 -42
  76. package/dist/visual/FieldHint.svelte.d.ts +1 -1
  77. package/dist/visual/FieldLabel.svelte +8 -8
  78. package/dist/visual/FormatPopover.svelte +80 -35
  79. package/dist/visual/NumberField.svelte +5 -5
  80. package/dist/visual/NumberField.svelte.d.ts +1 -1
  81. package/dist/visual/ObjectField.svelte +9 -9
  82. package/dist/visual/ObjectField.svelte.d.ts +1 -1
  83. package/dist/visual/ProseArrayElement.svelte +11 -13
  84. package/dist/visual/ProseArrayElement.svelte.d.ts +1 -1
  85. package/dist/visual/ProseField.svelte +53 -84
  86. package/dist/visual/ProseField.svelte.d.ts +12 -17
  87. package/dist/visual/SlashMenu.svelte +42 -16
  88. package/dist/visual/TextField.svelte +5 -5
  89. package/dist/visual/TextField.svelte.d.ts +1 -1
  90. package/dist/visual/TipsCard.svelte +43 -28
  91. package/dist/visual/VisualEditor.svelte +6 -6
  92. package/dist/visual/VisualEditorInner.svelte +122 -129
  93. package/dist/visual/controls.css +74 -30
  94. package/dist/visual/diagnostics.d.ts +5 -5
  95. package/dist/visual/diagnostics.js +3 -3
  96. package/dist/visual/domid.js +3 -3
  97. package/dist/visual/ext.d.ts +1 -1
  98. package/dist/visual/ext.js +1 -1
  99. package/dist/visual/icons/nodes.d.ts +3 -0
  100. package/dist/visual/icons/nodes.js +2 -1
  101. package/dist/visual/index.js +4 -4
  102. package/dist/visual/leaves.d.ts +3 -3
  103. package/dist/visual/leaves.js +1 -1
  104. package/dist/visual/links.d.ts +24 -0
  105. package/dist/visual/links.js +67 -0
  106. package/dist/visual/props.d.ts +8 -8
  107. package/dist/visual/signals.d.ts +6 -6
  108. package/dist/visual/strings.d.ts +14 -19
  109. package/dist/visual/strings.js +5 -7
  110. package/dist/visual/structure.d.ts +24 -17
  111. package/dist/visual/structure.js +8 -8
  112. package/dist/visual/synced.svelte.d.ts +1 -1
  113. package/dist/visual/synced.svelte.js +4 -4
  114. package/dist/visual/tips.d.ts +2 -2
  115. package/dist/visual/tips.js +3 -3
  116. package/package.json +1 -1
@@ -6,7 +6,7 @@ import { type Command } from 'prosemirror-state';
6
6
  *
7
7
  * `Enter` is upstream's `newlineInCode`, and it is load-bearing rather than
8
8
  * cosmetic: inside a `list_item > code_block` the list link's `splitListItem`
9
- * otherwise splits the ITEM in two, each half holding a code block. Every command
9
+ * otherwise splits the item in two, each half holding a code block. Every command
10
10
  * here declines outside a code block, so the list links keep the keys elsewhere.
11
11
  */
12
12
  export declare function codeKeymap(schema: Schema): Record<string, Command>;
@@ -16,7 +16,7 @@ function codeBlockAt(state) {
16
16
  /**
17
17
  * Start offsets of the lines the range `[f, t)` covers, in document order.
18
18
  *
19
- * A line whose start IS `t` is excluded on a non-empty range: a selection ending at
19
+ * A line whose start is `t` is excluded on a non-empty range: a selection ending at
20
20
  * a line start does not reach into that line, the editor convention (else selecting
21
21
  * whole lines by dragging to the next line's start indents one line too many).
22
22
  */
@@ -44,7 +44,7 @@ function outdentWidth(text, s) {
44
44
  }
45
45
  /**
46
46
  * Tab in a code block: insert one indent unit, or indent every covered line when the
47
- * selection spans a newline. A single-line selection is REPLACED by the unit: Tab
47
+ * selection spans a newline. A single-line selection is replaced by the unit: Tab
48
48
  * types, the way any other key does.
49
49
  */
50
50
  const indentInCode = (state, dispatch) => {
@@ -75,7 +75,7 @@ const indentInCode = (state, dispatch) => {
75
75
  /**
76
76
  * Shift-Tab in a code block: remove one indent level from every covered line.
77
77
  *
78
- * Declines when no covered line carries an indent, so the key is NOT swallowed: it
78
+ * Declines when no covered line carries an indent, so the key is not swallowed: it
79
79
  * falls through to the list link and then out of the leaf, which is the body's
80
80
  * keyboard exit from inside a code block (VISUAL_EDITOR §Chrome).
81
81
  */
@@ -104,7 +104,7 @@ const outdentInCode = (state, dispatch) => {
104
104
  *
105
105
  * `Enter` is upstream's `newlineInCode`, and it is load-bearing rather than
106
106
  * cosmetic: inside a `list_item > code_block` the list link's `splitListItem`
107
- * otherwise splits the ITEM in two, each half holding a code block. Every command
107
+ * otherwise splits the item in two, each half holding a code block. Every command
108
108
  * here declines outside a code block, so the list links keep the keys elsewhere.
109
109
  */
110
110
  export function codeKeymap(schema) {
@@ -9,7 +9,7 @@ interface DecodeOpts {
9
9
  plaintext?: boolean;
10
10
  }
11
11
  /**
12
- * A `Content` as READ-ONLY DOM: decode under `schema`, then the nodes' own `toDOM`.
12
+ * A `Content` as read-only DOM: decode under `schema`, then the nodes' own `toDOM`.
13
13
  * The rendering half of the codec's job with no editing attached (no PM view, no
14
14
  * plugins, no `contenteditable`) for chrome that must show content in the same
15
15
  * mark vocabulary a leaf edits it in (the tips card). A second renderer over the
@@ -4,7 +4,7 @@
4
4
  // prefix (`list_item`/`quote` → lists/blockquote), select the block node by
5
5
  // `kind`, apply marks over their `[start,end)` USV ranges (PM splits inline nodes
6
6
  // at mark boundaries), and lower island slots to leaf nodes (inline in a `para`
7
- // line, block on an `island` line). Anchors are NOT applied here; they are
7
+ // line, block on an `island` line). Anchors are not applied here; they are
8
8
  // decorations (field.ts). Positions throughout are USV; `Array.from` iterates by
9
9
  // code point so an astral char is one unit, never a surrogate half.
10
10
  import { DOMSerializer } from 'prosemirror-model';
@@ -24,7 +24,7 @@ export function usvLength(s) {
24
24
  return n;
25
25
  }
26
26
  /**
27
- * A `Content` as READ-ONLY DOM: decode under `schema`, then the nodes' own `toDOM`.
27
+ * A `Content` as read-only DOM: decode under `schema`, then the nodes' own `toDOM`.
28
28
  * The rendering half of the codec's job with no editing attached (no PM view, no
29
29
  * plugins, no `contenteditable`) for chrome that must show content in the same
30
30
  * mark vocabulary a leaf edits it in (the tips card). A second renderer over the
@@ -92,7 +92,7 @@ function groupBlocks(schema, leaves, depth, marks, cursor) {
92
92
  continue;
93
93
  }
94
94
  const here = path[depth];
95
- // `ContentContainer` is an OPEN set: a bare `here.container === 'x'` does not
95
+ // `ContentContainer` is an open set: a bare `here.container === 'x'` does not
96
96
  // narrow, so the two arms this schema builds are claimed by the boundary's
97
97
  // guard and a literal check, and everything else is an unknown the inert
98
98
  // wrapper carries (CODEC §Open sets); no branch degrades silently.
@@ -106,7 +106,7 @@ function groupBlocks(schema, leaves, depth, marks, cursor) {
106
106
  const c = atDepth(leaves[j], depth);
107
107
  if (!c || !isListItemContainer(c) || c.ordered !== ordered || c.start !== start)
108
108
  break;
109
- // An ordinal reset is an ADJACENT SIBLING list (content preserves the
109
+ // An ordinal reset is an adjacent sibling list (content preserves the
110
110
  // shape): merging it here would re-encode `[0,1,0]` as `[0,1,2]`,
111
111
  // breaking the decode round-trip on the first edit.
112
112
  if (c.ordinal < prevOrd)
@@ -131,7 +131,7 @@ function groupBlocks(schema, leaves, depth, marks, cursor) {
131
131
  continue;
132
132
  }
133
133
  // quote and every unknown container share one shape: a wrapper over the run
134
- // of leaves carrying the IDENTICAL container here (identity by `containerKey`,
134
+ // of leaves carrying the identical container here (identity by `containerKey`,
135
135
  // so two adjacent unknowns differing only in `attrs` stay two wrappers).
136
136
  const key = containerKey(here);
137
137
  let j = i + 1;
@@ -165,7 +165,7 @@ function containerKey(c) {
165
165
  return 'attrs' in c ? `${c.container}\u0000${JSON.stringify(c.attrs)}` : c.container;
166
166
  }
167
167
  /** A single leaf block node (para/heading/code/rule/island, or an unknown kind
168
- * carried on a paragraph) from its segments. `kind` is an OPEN set, so the two
168
+ * carried on a paragraph) from its segments. `kind` is an open set, so the two
169
169
  * payload-carrying arms read through the boundary's guards; the payload-free arms
170
170
  * compare literally, and anything left is unknown. */
171
171
  function makeLeaf(schema, leaf, marks, cursor) {
@@ -191,13 +191,13 @@ function makeLeaf(schema, leaf, marks, cursor) {
191
191
  if (isHeadingLine(line)) {
192
192
  return schema.nodes.heading.create({ level: line.level }, inline);
193
193
  }
194
- // An unknown kind renders as a paragraph AND rides on it, so it re-encodes
194
+ // An unknown kind renders as a paragraph and rides on it, so it re-encodes
195
195
  // verbatim rather than flattening to `para` on the field's first edit.
196
196
  const unknown = line.kind === 'para' ? null : { kind: line.kind, attrs: 'attrs' in line ? line.attrs : null };
197
197
  return schema.nodes.paragraph.create({ unknown }, inline);
198
198
  }
199
199
  /** Consume the next island entry as PM node attrs (text-order matched to slots):
200
- * the WHOLE entry, `loss` included, which an island edit has to write back
200
+ * the whole entry, `loss` included, which an island edit has to write back
201
201
  * (`islands.ts`). A slot with no entry behind it is a malformed content; the empty
202
202
  * attrs keep the decode total rather than throwing on a read. */
203
203
  function islandAttrs(cursor) {
@@ -27,7 +27,7 @@ export interface Scan {
27
27
  usvEnd: number;
28
28
  }
29
29
  /**
30
- * Walk a PM document once, producing the content projection AND the position-map
30
+ * Walk a PM document once, producing the content projection and the position-map
31
31
  * runs (they share the traversal so they can never disagree). This is the single
32
32
  * source both `pmToContent` and the position map draw from.
33
33
  */
@@ -40,14 +40,14 @@ interface LowerOpts {
40
40
  id: string;
41
41
  pos: number;
42
42
  }[];
43
- /** Anchor decoration positions after the edit, in FINAL (new) USV coords. */
43
+ /** Anchor decoration positions after the edit, in final (new) USV coords. */
44
44
  newAnchors?: {
45
45
  id: string;
46
46
  pos: number;
47
47
  }[];
48
48
  }
49
49
  /**
50
- * One old→new content edit with the text splice it implies. BOTH sides are content,
50
+ * One old→new content edit with the text splice it implies. Both sides are content,
51
51
  * never a PM doc: the projection of the new doc (`pmToContent`) is the caller's to
52
52
  * hold, because a doc-taking overload re-walks the whole tree, once per keystroke,
53
53
  * for a value the caller has in hand.
@@ -56,7 +56,7 @@ interface LowerOpts {
56
56
  * and the commit path holds the edit across the lowering. `contentEdit` being its
57
57
  * only constructor is what keeps a `delta` from disagreeing with the contents it
58
58
  * was diffed from; `lower` is where a slot inside that splice moves to the island
59
- * channel, so this `delta` is the RAW splice, not always the one committed.
59
+ * channel, so this `delta` is the raw splice, not always the one committed.
60
60
  */
61
61
  export interface ContentEdit {
62
62
  readonly oldRt: Content;
@@ -10,7 +10,7 @@ function appendText(acc, s) {
10
10
  acc.usvEnd += usvLength(s);
11
11
  }
12
12
  /**
13
- * Walk a PM document once, producing the content projection AND the position-map
13
+ * Walk a PM document once, producing the content projection and the position-map
14
14
  * runs (they share the traversal so they can never disagree). This is the single
15
15
  * source both `pmToContent` and the position map draw from.
16
16
  */
@@ -50,7 +50,7 @@ function scanBlock(acc, node, nodePos, containers) {
50
50
  case 'paragraph':
51
51
  case 'heading': {
52
52
  // One kind value per block: the opening line and its hard-break
53
- // continuations read the SAME object, so they cannot disagree.
53
+ // continuations read the same object, so they cannot disagree.
54
54
  const kind = textblockKind(node);
55
55
  beginLine(acc, contentStart, containers, kind);
56
56
  scanInline(acc, node, contentStart, containers, kind);
@@ -256,7 +256,7 @@ function diffLines(oldRt, newRt) {
256
256
  }
257
257
  /** A line minus its `containers` / `continues` envelope: exactly `setKind`'s
258
258
  * payload, which the boundary names `ContentLineKind`. Lifting it whole is what
259
- * keeps this arm-agnostic: `kind` is an OPEN set, so an arm-by-arm switch would
259
+ * keeps this arm-agnostic: `kind` is an open set, so an arm-by-arm switch would
260
260
  * have to guess at the unknown arm's payload and would drift on every arm
261
261
  * upstream adds. */
262
262
  function kindPart(l) {
@@ -286,14 +286,14 @@ function kindKey(l) {
286
286
  }
287
287
  /**
288
288
  * Split the edit's islands off the text splice. `applyChange` throws
289
- * `IslandSlotInInsert` on a `delta` that types a slot, so a slot NEVER rides the
289
+ * `IslandSlotInInsert` on a `delta` that types a slot, so a slot never rides the
290
290
  * delta: every slot inside the splice's inserted region is stripped from it and
291
291
  * placed by `{ op: 'insert', at }`, which carries the backing entry in the same
292
292
  * op. Every other island kept its slot, so a changed payload is `{ op: 'set' }`,
293
- * addressed by the id both sides carry. A DELETED island needs no op at all: the
293
+ * addressed by the id both sides carry. A deleted island needs no op at all: the
294
294
  * delta that removes its slot drops the entry.
295
295
  *
296
- * `at` is the slot's position in the FINAL text: inserts apply left to right, so
296
+ * `at` is the slot's position in the final text: inserts apply left to right, so
297
297
  * by the time the k-th lands, the k slots before it are back in place.
298
298
  *
299
299
  * The pairing is positional for `insert` and by id for `set`, which is what each
@@ -338,7 +338,7 @@ function slotPositions(text) {
338
338
  }
339
339
  return out;
340
340
  }
341
- /** The splice's inserted region `[start, end)` in FINAL coords. `diffText` emits
341
+ /** The splice's inserted region `[start, end)` in final coords. `diffText` emits
342
342
  * one `[retain?][delete?][insert?][retain?]`, so the leading retain is where the
343
343
  * insert lands and its length is how far it reaches. */
344
344
  function insertedRegion(delta) {
@@ -350,7 +350,7 @@ function insertedRegion(delta) {
350
350
  return { start, end: start + (insert ? usvLength(insert.insert) : 0) };
351
351
  }
352
352
  /** The delta with every island slot removed from its insert, or `undefined` when
353
- * that leaves nothing but retains. Retain/delete arithmetic reads the OLD text,
353
+ * that leaves nothing but retains. Retain/delete arithmetic reads the old text,
354
354
  * so dropping inserted characters leaves the rest of the splice intact. */
355
355
  function withoutSlots(delta) {
356
356
  const ops = [];
@@ -375,7 +375,7 @@ function withoutSlots(delta) {
375
375
  * A post-delta position carried through the bundle's island inserts, which move
376
376
  * text exactly as the delta does and rebase marks by the same rule (start-assoc
377
377
  * `after`, end-assoc `before`; an anchor is a point and holds `before`). Ascending
378
- * `at`s apply left to right, so each comparison reads the RUNNING position.
378
+ * `at`s apply left to right, so each comparison reads the running position.
379
379
  */
380
380
  function shiftPastIslands(pos, insertedAt, assoc) {
381
381
  let p = pos;
@@ -389,7 +389,7 @@ function diffMarks(oldRt, newRt, delta, insertedAt, opts) {
389
389
  const ops = [];
390
390
  // Read the gate once per diff rather than once per mark.
391
391
  const { mapPos } = core();
392
- // Rebase old marks through BOTH text-moving channels to final coords, exactly
392
+ // Rebase old marks through both text-moving channels to final coords, exactly
393
393
  // as `applyChange` does internally (start assoc `after`, end assoc `before`).
394
394
  const rebase = (pos, assoc) => shiftPastIslands(delta ? mapPos(delta, pos, assoc) : pos, insertedAt, assoc);
395
395
  // Group both sides by descriptor key (excluding anchors).
@@ -4,7 +4,7 @@ import { EditorView } from 'prosemirror-view';
4
4
  import type { Document, Content, Addr, Quill } from '@quillmark/wasm';
5
5
  import type { EditorErrorHandler } from '../errors.js';
6
6
  import { type TableChromeStrings } from './table-view.js';
7
- import { type SlashItem, type SlashState, type SlashStrings } from './slash.js';
7
+ import { type SlashState } from './slash.js';
8
8
  /** Options for {@link createField}. */
9
9
  export interface CreateFieldOpts {
10
10
  doc: Document;
@@ -20,24 +20,22 @@ export interface CreateFieldOpts {
20
20
  plaintext?: boolean;
21
21
  /** Suppress the markdown-shorthand input rules. */
22
22
  noInputRules?: boolean;
23
- /** The island chrome's wording, read LIVE (per render) so a consumer swapping
23
+ /** The island chrome's wording, read live (per render) so a consumer swapping
24
24
  * locale mid-session re-renders rather than freezing it at mount. Absent leaves
25
25
  * the package's English; an inline leaf never asks (it holds no island). */
26
26
  tableStrings?: () => TableChromeStrings;
27
- /** The slash menu's wording, read live for the same reason. */
28
- slashStrings?: () => SlashStrings;
29
27
  /**
30
28
  * The slash menu's live state, or `undefined` when it is closed: the channel the
31
29
  * chrome draws from (`visual/SlashMenu.svelte`).
32
30
  *
33
- * Its PRESENCE is what mounts the menu at all. The trigger and its keys are the
31
+ * Its presence is what mounts the menu at all. The trigger and its keys are the
34
32
  * leaf's, but a surface only a keyboard can reach (claiming Enter, Escape and the
35
33
  * arrows with nothing on screen) is worse than no surface, so the door exists
36
34
  * exactly where something can draw it. A constrained inline leaf never has one:
37
35
  * it holds no island and no block to convert.
38
36
  */
39
37
  onSlash?(state: SlashState | undefined): void;
40
- /** Accessible name → `aria-label` on the `contenteditable`. For a leaf NOTHING
38
+ /** Accessible name → `aria-label` on the `contenteditable`. For a leaf nothing
41
39
  * else names: an array element (the field label plus its 1-based index), the
42
40
  * card body (no visible label at all). A leaf with a field label takes
43
41
  * `labelledBy` instead: `for` cannot reach a `contenteditable`, which is not a
@@ -48,7 +46,7 @@ export interface CreateFieldOpts {
48
46
  /** The parked `description` → `aria-describedby`; announced after the name. */
49
47
  describedBy?: string;
50
48
  /** Ghost text shown on the empty leaf: the resolved `default:` a field ghosts,
51
- * or a body's invitation. The INITIAL value; {@link
49
+ * or a body's invitation. The initial value; {@link
52
50
  * FieldController.setPlaceholder} moves it after mount. Empty/absent
53
51
  * shows no ghost. */
54
52
  placeholder?: string;
@@ -56,7 +54,7 @@ export interface CreateFieldOpts {
56
54
  /** Fired with the new USV caret after an edit or a selection move. */
57
55
  onCaretMove?(addr: Addr, pos: number): void;
58
56
  /**
59
- * Fired after an edit COMMITTED to the document: the leaf's change signal, and
57
+ * Fired after an edit committed to the document: the leaf's change signal, and
60
58
  * the reason it is not `onCaretMove`. That one also fires on a bare selection
61
59
  * move, so a host driving a recompile off it recompiles on every arrow key.
62
60
  *
@@ -83,7 +81,7 @@ export interface FieldController {
83
81
  * its new kind's wording without remounting, which it must not do (the leaf key
84
82
  * is the card's session id, so a remount would cost the caret).
85
83
  *
86
- * Refreshes the decoration WITHOUT a transaction: a placeholder is chrome, and
84
+ * Refreshes the decoration without a transaction: a placeholder is chrome, and
87
85
  * a transaction would fire `onCaretMove` at a moment the caret did not move.
88
86
  */
89
87
  setPlaceholder(text: string | undefined): void;
@@ -103,14 +101,14 @@ export interface FieldController {
103
101
  /** Ids of the anchors within USV range `[from, to]`: a selection's anchor state. */
104
102
  anchorsInRange(from: number, to: number): string[];
105
103
  /**
106
- * Move the slash menu's cursor onto item `id`: what a POINTER entering an item
107
- * calls. The keyboard cursor and the pointer's highlight are one state, so the
104
+ * Move the slash menu's cursor onto the command `name`: what a pointer entering an
105
+ * item calls. The keyboard cursor and the pointer's highlight are one state, so the
108
106
  * chrome moves the one the keys already drive rather than painting a second.
109
107
  */
110
- slashFocus(id: string): void;
111
- /** Run slash item `id` (a click on one): the same path Enter takes. A no-op with
112
- * no menu open, which is the state a stale click lands in. */
113
- slashPick(id: string): void;
108
+ slashFocus(name: string): void;
109
+ /** Run the command `name` (a click on its item): the same path Enter takes. A no-op
110
+ * with no menu open, which is the state a stale click lands in. */
111
+ slashPick(name: string): void;
114
112
  /** The current PM selection as a USV `{ from, to }` range (the boundary currency). */
115
113
  selectionRange(): {
116
114
  from: number;
@@ -123,9 +121,9 @@ export interface FieldController {
123
121
  * of {@link FieldController}: they are PM internals, and a consumer's contract is the
124
122
  * controller's verbs. The format popover is the one caller and it needs both:
125
123
  * `view` to name the leaf's own document, `focusedView` to act on wherever the caret
126
- * actually is, which inside a table island is a NESTED cell view (`table-view.ts`).
124
+ * actually is, which inside a table island is a nested cell view (`table-view.ts`).
127
125
  * The two being different is exactly what tells the popover to withhold its
128
- * `anchor` button: an anchor is the FIELD's coordinate space, and a cell is not in it.
126
+ * `anchor` button: an anchor is the field's coordinate space, and a cell is not in it.
129
127
  */
130
128
  export interface LeafViews {
131
129
  view: EditorView;
@@ -144,24 +142,21 @@ export declare function createField(opts: CreateFieldOpts): FieldController;
144
142
  * leaf-specific plugins (`afterHistory`: the addressed leaf passes its
145
143
  * anchor-position plugin; a by-value array element passes none), the
146
144
  * markdown-shorthand input rules, then the field keymap over the base keymap, and
147
- * last the two that answer to a caret or a selection BESIDE a block: the gap cursor
145
+ * last the two that answer to a caret or a selection beside a block: the gap cursor
148
146
  * and {@link pastAtomPlugin}.
149
147
  *
150
148
  * A `plaintext` field carries no marks (decode strips them, the keymap suppresses
151
149
  * Mod-b/i/u), so it also skips the input rules: `inlineSchema` still declares the
152
- * mark types, so a `**bold**` rule would apply a strong mark AND eat the literal
153
- * delimiters. The inline schema has no block nodes, so those rules are the ONLY
150
+ * mark types, so a `**bold**` rule would apply a strong mark and eat the literal
151
+ * delimiters. The inline schema has no block nodes, so those rules are the only
154
152
  * ones it would add: skip all.
155
153
  */
156
154
  export declare function proseLeafPlugins(schema: Schema, opts: {
157
155
  inline: boolean;
158
156
  plaintext: boolean;
159
- /** The slash menu's model and its report channel; absent mounts no menu and
160
- * leaves Enter/Escape/the arrows to the links below (`keymap.ts`). */
161
- slash?: {
162
- items: () => SlashItem[];
163
- onState: (state: SlashState | undefined) => void;
164
- };
157
+ /** The slash menu's report channel; absent mounts no menu and leaves
158
+ * Enter/Escape/the arrows to the links below (`keymap.ts`). */
159
+ slash?: (state: SlashState | undefined) => void;
165
160
  noInputRules?: boolean;
166
161
  /** Read live, not captured: the ghost can move after mount
167
162
  * ({@link FieldController.setPlaceholder}), and a re-hydration rebuilds this
@@ -6,11 +6,11 @@
6
6
  // (b) lower the tr to a `ChangeBundle` (or `overwrite` for a field not yet at
7
7
  // content rest, which has nothing to splice),
8
8
  // (c) commit via `doc.applyChange(addr, bundle)`,
9
- // (d) fire `onChange` when the transaction COMMITTED, then `onCaretMove` with
9
+ // (d) fire `onChange` when the transaction committed, then `onCaretMove` with
10
10
  // the new USV caret, which a bare selection move fires on its own.
11
11
  // On an `applyChange` throw the optimistic PM state stays and the failure reports
12
12
  // through `onError`: never a crash. Caret continuity across own-edits is the PM
13
- // `StepMap`; an EXTERNAL content change re-hydrates through `applyExternal`, gated
13
+ // `StepMap`; an external content change re-hydrates through `applyExternal`, gated
14
14
  // by `reconcile`.
15
15
  import { baseKeymap, toggleMark } from 'prosemirror-commands';
16
16
  import { gapCursor } from 'prosemirror-gapcursor';
@@ -19,6 +19,7 @@ import { keymap } from 'prosemirror-keymap';
19
19
  import { EditorState, NodeSelection, Plugin, PluginKey, Selection, TextSelection } from 'prosemirror-state';
20
20
  import { Decoration, DecorationSet, EditorView } from 'prosemirror-view';
21
21
  import { reportError, errorMessage } from '../errors.js';
22
+ import { tableClipboardPlugin } from './clipboard.js';
22
23
  import { decode } from './decode.js';
23
24
  import { usvToPM, pmToUsv, buildLineIndex } from './positions.js';
24
25
  import { lower, pmToContent, contentEdit } from './encode.js';
@@ -28,7 +29,7 @@ import { inputRulesPlugin } from './inputrules.js';
28
29
  import { bodyKeymap } from './keymap.js';
29
30
  import { blockSchema, inlineSchema } from './schema.js';
30
31
  import { DEFAULT_TABLE_STRINGS, tableNodeView } from './table-view.js';
31
- import { DEFAULT_SLASH_STRINGS, focusSlashItem, runSlashItem, slashItems, slashPlugin } from './slash.js';
32
+ import { focusSlashItem, runSlashItem, slashPlugin } from './slash.js';
32
33
  const anchorKey = new PluginKey('quill-anchors');
33
34
  /** Plugin-held anchor positions (PM coords), mapped through every edit's `StepMap`
34
35
  * and mutated by an {@link AnchorEdit} meta (an insert/remove at a selection). */
@@ -62,8 +63,8 @@ function anchorPlugin(seed) {
62
63
  *
63
64
  * Bound rather than verbatim, because the storage form is not the leaf's business
64
65
  * and the verbatim read cannot answer it alone. A field the editor committed rests
65
- * as `Content`; a field a markdown parse left rests as the authored STRING, and
66
- * what that string MEANS is the declared type's to say: `richtext` is markdown,
66
+ * as `Content`; a field a markdown parse left rests as the authored string, and
67
+ * what that string means is the declared type's to say: `richtext` is markdown,
67
68
  * `plaintext` is literal text, and reading one as the other silently eats every
68
69
  * `*` the author typed. */
69
70
  function readLeaf(reader, addr) {
@@ -77,9 +78,9 @@ export function emptyContent() {
77
78
  /** Whether ops may commit here: the stored value is a `Content` object, so
78
79
  * `applyChange` splices exactly the content the leaf read and PM is showing.
79
80
  *
80
- * The two other rest forms both take `overwrite` instead. An UNSET field (`undefined`;
81
+ * The two other rest forms both take `overwrite` instead. An unset field (`undefined`;
81
82
  * a default-only richtext field before its first edit) has nothing to splice.
82
- * An AUTHORED STRING is the trap: `applyChange` reads it as markdown whatever the
83
+ * An authored string is the trap: `applyChange` reads it as markdown whatever the
83
84
  * declared type is, so on a `plaintext` field its pre-image is not the content the
84
85
  * leaf read and a delta computed against that content lands at the wrong offsets.
85
86
  * Installing over it costs nothing either way, since content-only marks do not
@@ -119,15 +120,14 @@ export function createField(opts) {
119
120
  // The ghost's live cell: the placeholder plugin reads it per pass, so moving it
120
121
  // is an assignment plus a re-render rather than a rebuilt plugin stack.
121
122
  let placeholderText = opts.placeholder;
122
- // The views nested INSIDE this leaf: one per table cell (`table-view.ts`). The
123
+ // The views nested inside this leaf: one per table cell (`table-view.ts`). The
123
124
  // leaf holds the set because chrome asks the leaf, not the island, which view
124
125
  // holds the caret: the format popover raises over a cell exactly as over the
125
126
  // body, and only the leaf knows both.
126
127
  const nested = new Set();
127
- // The menu's offers, derived from the wording per read: the chrome names an item
128
- // and the codec runs it, so a locale swap changes the labels and not the picks.
129
- const menuItems = () => slashItems(opts.slashStrings?.() ?? DEFAULT_SLASH_STRINGS);
130
- const slash = !inline && opts.onSlash ? { items: menuItems, onState: opts.onSlash } : undefined;
128
+ // The menu's report channel, and the whole of what a leaf holds of it: the
129
+ // vocabulary is the codec's own constant (`slash.ts`), not a wording to derive.
130
+ const slash = inline ? undefined : opts.onSlash;
131
131
  const state = buildState(reconciler.last);
132
132
  index = buildLineIndex(state.doc);
133
133
  view = new EditorView(container, {
@@ -154,7 +154,7 @@ export function createField(opts) {
154
154
  const next = view.state.apply(tr);
155
155
  view.updateState(next); // (a) optimistic
156
156
  index = buildLineIndex(next.doc);
157
- // Commit a content edit OR an anchor mutation. An anchor insert/remove is
157
+ // Commit a content edit or an anchor mutation. An anchor insert/remove is
158
158
  // zero-width, so `docChanged` is false: the `anchorKey` meta is what
159
159
  // routes it through the same commit path (the diff emits the anchor op).
160
160
  if (tr.docChanged || tr.getMeta(anchorKey)) {
@@ -203,7 +203,7 @@ export function createField(opts) {
203
203
  // Returns whether anything landed: the caller's change signal, which must not
204
204
  // be a property of which branch the commit took.
205
205
  function commitEdit(oldRt, newDoc) {
206
- // The projection and its text splice, each computed ONCE per keystroke: the gate
206
+ // The projection and its text splice, each computed once per keystroke: the gate
207
207
  // below, both overwrite fallbacks, and `lower` all read this one `edit`.
208
208
  const edit = contentEdit(oldRt, pmToContent(newDoc));
209
209
  try {
@@ -226,7 +226,7 @@ export function createField(opts) {
226
226
  return true;
227
227
  }
228
228
  catch (e) {
229
- // Bound the damage: an op path the gates missed leaves the store STALE
229
+ // Bound the damage: an op path the gates missed leaves the store stale
230
230
  // while PM keeps the edit; and because the reconciler then re-diffs
231
231
  // from that stale content, every later edit re-throws and the field
232
232
  // silently stops persisting. Install the full projection instead
@@ -256,7 +256,7 @@ export function createField(opts) {
256
256
  }
257
257
  /** The anchors the plugin currently holds (PM coords); `[]` before the first apply. */
258
258
  const heldAnchors = () => anchorKey.getState(view.state) ?? [];
259
- /** Anchor positions in the CURRENT (new) doc, as USV. */
259
+ /** Anchor positions in the current (new) doc, as USV. */
260
260
  function readAnchorsUsv(newDoc) {
261
261
  return heldAnchors().map((a) => ({ id: a.id, pos: pmToUsv(index, a.pos) }));
262
262
  }
@@ -326,13 +326,13 @@ export function createField(opts) {
326
326
  })
327
327
  .map((a) => a.id);
328
328
  },
329
- slashFocus(id) {
329
+ slashFocus(name) {
330
330
  if (slash)
331
- focusSlashItem(view, id, slash.items());
331
+ focusSlashItem(view, name);
332
332
  },
333
- slashPick(id) {
333
+ slashPick(name) {
334
334
  if (slash)
335
- runSlashItem(view, id);
335
+ runSlashItem(view, name);
336
336
  },
337
337
  selectionRange() {
338
338
  const { from, to } = view.state.selection;
@@ -365,13 +365,13 @@ export function createField(opts) {
365
365
  * leaf-specific plugins (`afterHistory`: the addressed leaf passes its
366
366
  * anchor-position plugin; a by-value array element passes none), the
367
367
  * markdown-shorthand input rules, then the field keymap over the base keymap, and
368
- * last the two that answer to a caret or a selection BESIDE a block: the gap cursor
368
+ * last the two that answer to a caret or a selection beside a block: the gap cursor
369
369
  * and {@link pastAtomPlugin}.
370
370
  *
371
371
  * A `plaintext` field carries no marks (decode strips them, the keymap suppresses
372
372
  * Mod-b/i/u), so it also skips the input rules: `inlineSchema` still declares the
373
- * mark types, so a `**bold**` rule would apply a strong mark AND eat the literal
374
- * delimiters. The inline schema has no block nodes, so those rules are the ONLY
373
+ * mark types, so a `**bold**` rule would apply a strong mark and eat the literal
374
+ * delimiters. The inline schema has no block nodes, so those rules are the only
375
375
  * ones it would add: skip all.
376
376
  */
377
377
  export function proseLeafPlugins(schema, opts) {
@@ -380,21 +380,22 @@ export function proseLeafPlugins(schema, opts) {
380
380
  list.push(slashPlugin(opts.slash));
381
381
  if (!opts.noInputRules && !opts.plaintext)
382
382
  list.push(inputRulesPlugin(schema));
383
- list.push(keymap(editorKeymap(schema, opts.inline, opts.plaintext, opts.slash?.items)));
383
+ list.push(keymap(editorKeymap(schema, opts.inline, opts.plaintext, !!opts.slash)));
384
384
  list.push(keymap(baseKeymap));
385
- // Both answer to a selection over an ATOM, which only a block leaf holds: an
386
- // inline leaf is one textblock with no island and no gap to put a cursor in.
385
+ // All three answer to something only a block leaf holds: an inline leaf is one
386
+ // textblock with no island, no gap to put a cursor in, and nowhere for a pasted
387
+ // table to land.
387
388
  if (!opts.inline)
388
- list.push(gapCursor(), pastAtomPlugin());
389
+ list.push(gapCursor(), pastAtomPlugin(), tableClipboardPlugin(schema));
389
390
  if (opts.placeholder)
390
391
  list.push(placeholderPlugin(opts.placeholder));
391
392
  return list;
392
393
  }
393
394
  /**
394
- * A printable key over a selected atom writes PAST it. A selection is the subject
395
+ * A printable key over a selected atom writes past it. A selection is the subject
395
396
  * of the next command, never a thing armed for
396
397
  * replacement: Backspace deletes it, Mod-C copies it, and a character lands beside
397
- * it. A BLOCK island takes a new paragraph after it, an INLINE one the caret after
398
+ * it. A block island takes a new paragraph after it, an inline one the caret after
398
399
  * the image in the same line: one rule, both node types.
399
400
  *
400
401
  * PM routes text input over a non-`TextSelection` through `handleTextInput` before
@@ -461,13 +462,13 @@ function placeholderPlugin(read) {
461
462
  * The field's keymap: history, mark toggles, and the body's structural keys; Enter
462
463
  * suppressed inline.
463
464
  *
464
- * Tab forks on the leaf's ROLE, not on the caret's position. An inline/plaintext
465
+ * Tab forks on the leaf's role, not on the caret's position. An inline/plaintext
465
466
  * leaf is a form field: Tab stays unbound, so field navigation is open for a shell
466
467
  * keymap (VISUAL_EDITOR §Settled and open). A block-schema body is a document: Tab
467
468
  * is structural, a chain each nested surface prepends to (`keymap.ts`). One key
468
469
  * never means two things within one surface.
469
470
  */
470
- function editorKeymap(schema, inline, plaintext, slashItems) {
471
+ function editorKeymap(schema, inline, plaintext, slash) {
471
472
  const map = {
472
473
  'Mod-z': undo,
473
474
  'Mod-y': redo,
@@ -487,7 +488,7 @@ function editorKeymap(schema, inline, plaintext, slashItems) {
487
488
  }
488
489
  else {
489
490
  // Each binding falls through to `baseKeymap` when no link claims the key.
490
- Object.assign(map, bodyKeymap(schema, slashItems));
491
+ Object.assign(map, bodyKeymap(schema, slash));
491
492
  }
492
493
  return map;
493
494
  }
@@ -1,9 +1,9 @@
1
1
  // The codec barrel: the bidirectional bridge between one content field (`Content`)
2
- // and one ProseMirror document (CODEC). Decode (content → PM), lower (PM tr → a
2
+ // and one ProseMirror document (CODEC.md). Decode (content → PM), lower (PM tr → a
3
3
  // `ChangeBundle` for `applyChange`), the USV↔PM position map, and the `createField`
4
4
  // prose leaf. Consumed by the VisualEditor and the codec test suite.
5
5
  // ProseMirror's structural base styles and the gap cursor's, then the retint of the
6
- // hues they mint. Imported HERE because `createField` is what mounts a view: a
6
+ // hues they mint. Imported here because `createField` is what mounts a view: a
7
7
  // consumer reaching the codec directly gets them with it, and the order is what makes
8
8
  // the retint win.
9
9
  import 'prosemirror-view/style/prosemirror.css';
@@ -24,12 +24,9 @@ export { DEFAULT_SLASH_STRINGS } from './slash.js';
24
24
  export { blockSchema, inlineSchema } from './schema.js';
25
25
  // Decode / encode / positions (tests + VisualEditor).
26
26
  //
27
- // THE BARREL CARRIES WHAT HAS AN OFF-BARREL CALLER, and nothing else. The mark
28
- // algebra, the island bridge, the schema predicate, the `diff*` helpers,
29
- // `scanDoc`, `codePoints`: every one of them is reached by RELATIVE import
30
- // within `codec/`, so none of them is here. An export nothing imports is surface
31
- // that still has to stay honest; a symbol earns its line when a caller outside
32
- // this folder wants it.
27
+ // The barrel carries what has an off-barrel caller, and nothing else. A symbol
28
+ // reached only by relative import within `codec/` stays off it: an export nothing
29
+ // imports is surface that still has to stay honest.
33
30
  export { decode, renderContent, usvLength } from './decode.js';
34
31
  export { pmToContent, contentEdit, lower } from './encode.js';
35
32
  export { usvToPM, pmToUsv, buildLineIndex } from './positions.js';