@quillmark/svelte 0.27.0 → 0.28.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 (95) hide show
  1. package/NOTICE +4 -4
  2. package/README.md +26 -8
  3. package/THEMING.md +10 -8
  4. package/dist/core/address.d.ts +35 -64
  5. package/dist/core/address.js +35 -55
  6. package/dist/core/bloom.d.ts +4 -10
  7. package/dist/core/bloom.js +14 -27
  8. package/dist/core/codec/blocks.d.ts +41 -43
  9. package/dist/core/codec/blocks.js +97 -130
  10. package/dist/core/codec/code.d.ts +7 -10
  11. package/dist/core/codec/code.js +37 -46
  12. package/dist/core/codec/decode.d.ts +19 -23
  13. package/dist/core/codec/decode.js +76 -111
  14. package/dist/core/codec/encode.d.ts +11 -15
  15. package/dist/core/codec/encode.js +60 -67
  16. package/dist/core/codec/field.d.ts +62 -95
  17. package/dist/core/codec/field.js +107 -179
  18. package/dist/core/codec/islands.d.ts +33 -41
  19. package/dist/core/codec/islands.js +76 -86
  20. package/dist/core/codec/lists.d.ts +17 -26
  21. package/dist/core/codec/lists.js +117 -157
  22. package/dist/core/codec/marks.d.ts +4 -3
  23. package/dist/core/codec/marks.js +8 -3
  24. package/dist/core/codec/prose.css +84 -46
  25. package/dist/core/codec/schema.d.ts +18 -23
  26. package/dist/core/codec/schema.js +84 -76
  27. package/dist/core/codec/slash.d.ts +42 -48
  28. package/dist/core/codec/slash.js +0 -0
  29. package/dist/core/codec/table-copy.d.ts +4 -3
  30. package/dist/core/codec/table-copy.js +25 -7
  31. package/dist/core/codec/table-view.d.ts +12 -12
  32. package/dist/core/codec/table-view.js +460 -383
  33. package/dist/core/codec/table.d.ts +94 -61
  34. package/dist/core/codec/table.js +331 -83
  35. package/dist/core/codec/tasks.d.ts +6 -0
  36. package/dist/core/codec/tasks.js +30 -3
  37. package/dist/core/errors.d.ts +15 -30
  38. package/dist/core/errors.js +2 -6
  39. package/dist/core/index.js +7 -12
  40. package/dist/core/lifecycle.d.ts +7 -16
  41. package/dist/core/lifecycle.js +10 -21
  42. package/dist/core/rebind.svelte.d.ts +10 -20
  43. package/dist/core/rebind.svelte.js +15 -27
  44. package/dist/core/rungs.d.ts +14 -11
  45. package/dist/core/rungs.js +17 -19
  46. package/dist/core/teardown.d.ts +4 -6
  47. package/dist/core/teardown.js +7 -10
  48. package/dist/core/theme.css +74 -54
  49. package/dist/index.js +3 -8
  50. package/dist/preset/app.css +3 -1
  51. package/dist/preset/index.css +6 -8
  52. package/dist/preset/recipes.css +12 -15
  53. package/dist/preset/scale.css +18 -17
  54. package/dist/preset/shell.css +10 -0
  55. package/dist/preview/bridge.d.ts +18 -22
  56. package/dist/preview/bridge.js +29 -58
  57. package/dist/preview/controller.d.ts +21 -29
  58. package/dist/preview/controller.js +14 -39
  59. package/dist/preview/geometry.d.ts +13 -34
  60. package/dist/preview/geometry.js +13 -34
  61. package/dist/preview/index.js +4 -16
  62. package/dist/preview/paint.d.ts +3 -3
  63. package/dist/preview/paint.js +28 -68
  64. package/dist/preview/strings.d.ts +1 -1
  65. package/dist/preview/strings.js +2 -14
  66. package/dist/preview/zoom.d.ts +18 -23
  67. package/dist/preview/zoom.js +28 -27
  68. package/dist/visual/ArrayField.svelte +77 -16
  69. package/dist/visual/BooleanField.svelte +27 -14
  70. package/dist/visual/Card.svelte +61 -48
  71. package/dist/visual/CardControls.svelte +5 -11
  72. package/dist/visual/DateField.svelte +20 -4
  73. package/dist/visual/EnumField.svelte +3 -0
  74. package/dist/visual/FieldHint.svelte +2 -1
  75. package/dist/visual/FormatPopover.svelte +82 -15
  76. package/dist/visual/FormatPopover.svelte.d.ts +4 -0
  77. package/dist/visual/MatrixAdd.svelte +30 -12
  78. package/dist/visual/MatrixField.svelte +24 -16
  79. package/dist/visual/NumberField.svelte +7 -0
  80. package/dist/visual/ObjectField.svelte +4 -4
  81. package/dist/visual/ProseField.svelte +0 -5
  82. package/dist/visual/TipsCard.svelte +11 -10
  83. package/dist/visual/VariantField.svelte +3 -1
  84. package/dist/visual/VisualEditorInner.svelte +19 -3
  85. package/dist/visual/controls.css +7 -2
  86. package/dist/visual/fonts.d.ts +18 -0
  87. package/dist/visual/fonts.js +59 -0
  88. package/dist/visual/icons/nodes.d.ts +0 -5
  89. package/dist/visual/icons/nodes.js +0 -4
  90. package/dist/visual/props.d.ts +14 -1
  91. package/dist/visual/strings.d.ts +7 -0
  92. package/dist/visual/strings.js +5 -2
  93. package/dist/visual/structure.d.ts +43 -87
  94. package/dist/visual/structure.js +42 -83
  95. package/package.json +2 -2
@@ -9,9 +9,8 @@ function appendText(acc, s) {
9
9
  acc.usvEnd += usvLength(s);
10
10
  }
11
11
  /**
12
- * Walk a PM document once, producing the content projection and the position-map
13
- * runs (they share the traversal so they can never disagree). This is the single
14
- * source both `pmToContent` and the position map draw from.
12
+ * Walk a PM document once, producing the content projection and the position-map runs; sharing the
13
+ * traversal means they cannot disagree.
15
14
  */
16
15
  export function scanDoc(doc) {
17
16
  const acc = {
@@ -28,8 +27,10 @@ export function scanDoc(doc) {
28
27
  acc.marks = mergeMarks(acc.rawMarks);
29
28
  return acc;
30
29
  }
31
- /** The content projection a scan carries (drops the position runs): what a caller
32
- * holding a `Scan` reads instead of walking the document a second time. */
30
+ /**
31
+ * The content projection a scan carries, for a caller holding a `Scan` that would otherwise walk
32
+ * again.
33
+ */
33
34
  export function scanContent(s) {
34
35
  const { text, lines, marks, islands } = s;
35
36
  return { text, lines, marks, islands };
@@ -38,11 +39,12 @@ export function scanContent(s) {
38
39
  export function pmToContent(doc) {
39
40
  return scanContent(scanDoc(doc));
40
41
  }
41
- /** Walk a block-content fragment; `contentStart` is the PM pos before its first child.
42
- * Two container siblings of one run shape read as a single container — contiguity plus
43
- * an equal path is what the store means by "the same one" — so each alternates its
44
- * `instance` against the sibling before it. Sibling adjacency is the whole scope: a
45
- * container one level in has a differing path above it, which already tells it apart. */
42
+ /**
43
+ * Walk a block-content fragment; `contentStart` is the PM pos before its first child. Two container
44
+ * siblings of one run shape read as a single container (contiguity plus an equal path is the
45
+ * store's "same one"), so each alternates its `instance` against the sibling before it; a container
46
+ * one level in already differs in path above it.
47
+ */
46
48
  function scanBlocks(acc, parent, contentStart, containers) {
47
49
  let pm = contentStart;
48
50
  let prevShape = null;
@@ -55,9 +57,10 @@ function scanBlocks(acc, parent, contentStart, containers) {
55
57
  pm += child.nodeSize;
56
58
  });
57
59
  }
58
- /** A block's container run shape, `ordinal` and `instance` aside; null where it opens
59
- * no container. A list's `start` is not in it — the store welds two runs differing only
60
- * there. */
60
+ /**
61
+ * A block's container run shape, `ordinal` and `instance` aside; null where it opens no container.
62
+ * A list's `start` is not in it: the store welds two runs differing only there.
63
+ */
61
64
  function runShape(node) {
62
65
  switch (node.type.name) {
63
66
  case 'blockquote':
@@ -145,9 +148,10 @@ function textblockKind(node) {
145
148
  return { kind: 'heading', attrs: { level: node.attrs.level } };
146
149
  return { kind: 'para' };
147
150
  }
148
- /** A container with its run `instance`, spelled only where it is not the zero a
149
- * canonical read omits: the projection is compared against such a read
150
- * (`lineMetaEqual`), so the two have to spell one container one way. */
151
+ /**
152
+ * A container with its run `instance`, spelled only where it is not the zero a canonical read
153
+ * omits, so the projection and `lineMetaEqual` spell one container one way.
154
+ */
151
155
  function withInstance(container, instance) {
152
156
  return instance === 0 ? container : { ...container, instance };
153
157
  }
@@ -171,11 +175,9 @@ function scanInline(acc, block, contentStart, containers, kind) {
171
175
  if (child.isText) {
172
176
  const text = child.text ?? '';
173
177
  emitText(acc, text, pm, child.marks);
174
- // A `\n` in a textblock that is not `whitespace: 'pre'` is the same line
175
- // boundary a hard break is: `paragraph` is `inline*`, so the schema admits
176
- // one, and `joinTextblocksAround` — a bare `replaceStep` running neither
177
- // `clearIncompatible` nor `replaceNewlines` — leaves one. It rides the text
178
- // run, where PM and USV advance together.
178
+ // A `\n` in a textblock that is not `whitespace: 'pre'` is the same line boundary a
179
+ // hard break is: `joinTextblocksAround` (a bare `replaceStep`) leaves one. It rides the
180
+ // text run, where PM and USV advance together.
179
181
  const breaks = text.split('\n').length - 1;
180
182
  for (let i = 0; i < breaks; i++)
181
183
  acc.lines.push({ containers, continues: true, ...kind });
@@ -206,10 +208,8 @@ function emitText(acc, s, pmStart, marks) {
206
208
  }
207
209
  }
208
210
  /**
209
- * Merge adjacent/overlapping same-descriptor marks into maximal ranges: the
210
- * per-text-node marks the walk emits, folded into the content's normalized form.
211
- * Same two primitives the mark diff runs on (`groupFormatting` + `union`), so the
212
- * projection and the diff group and merge by one rule.
211
+ * Merge adjacent/overlapping same-descriptor marks into maximal ranges, by the same primitives the
212
+ * mark diff runs on (`groupFormatting` + `union`).
213
213
  */
214
214
  function mergeMarks(raw) {
215
215
  const out = [];
@@ -271,12 +271,10 @@ function diffText(oldText, newText) {
271
271
  return { ops };
272
272
  }
273
273
  /**
274
- * Whether the splice moves a line boundary — a `\n` deleted, or one inserted. Where
275
- * it does not, every line keeps the metadata it had and equal metadata on both sides
276
- * is the whole story. Where it does, the store's split/join inheritance decides what
277
- * each line ends up carrying, and an array that still matches position for position
278
- * says nothing about it: one splice that both joins a boundary and opens another
279
- * leaves the line count and the metadata array unchanged while the kinds slide.
274
+ * Whether the splice moves a line boundary (a `\n` deleted or inserted). Where it does not, equal
275
+ * metadata on both sides is the whole story. Where it does, the store's split/join inheritance
276
+ * decides what each line carries: one splice that joins a boundary and opens another leaves the
277
+ * line count and metadata array unchanged while the kinds slide.
280
278
  */
281
279
  function movesBoundary(oldText, delta) {
282
280
  if (!delta)
@@ -305,11 +303,8 @@ function diffLines(oldRt, newRt, delta) {
305
303
  if (!movesBoundary(oldRt.text, delta) && lineMetaEqual(oldRt.lines, newRt.lines))
306
304
  return [];
307
305
  const ops = [];
308
- // Force every new line's metadata. Redundant ops are safe no-ops (verified),
309
- // so this is correct regardless of how the delta's split/join inheritance left
310
- // the intermediate metadata. `continues` is a boundary property of the `\n`
311
- // preceding a line: line 0 has no predecessor, never carries it, and the op
312
- // rejects it there, so it's set only for lines ≥ 1.
306
+ // Force every new line's metadata; redundant ops are safe no-ops. `continues` is a property of
307
+ // the `\n` preceding a line, so line 0 never carries it and it is set only for lines ≥ 1.
313
308
  for (let i = 0; i < newRt.lines.length; i++) {
314
309
  const l = newRt.lines[i];
315
310
  ops.push({ op: 'setContainers', line: i, containers: l.containers });
@@ -319,9 +314,10 @@ function diffLines(oldRt, newRt, delta) {
319
314
  }
320
315
  return ops;
321
316
  }
322
- /** A line minus its `containers` / `continues` envelope: exactly `setKind`'s
323
- * payload, which the boundary names `ContentLineKind`. Lifting it whole is what keeps
324
- * this arm-agnostic, so an arm added upstream reaches the op by construction. */
317
+ /**
318
+ * A line minus its `containers` / `continues` envelope: exactly `setKind`'s payload
319
+ * (`ContentLineKind`). Lifted whole, so an arm added upstream reaches the op by construction.
320
+ */
325
321
  function kindPart(l) {
326
322
  const { containers: _containers, continues: _continues, ...kind } = l;
327
323
  return kind;
@@ -342,28 +338,23 @@ function lineMetaEqual(a, b) {
342
338
  }
343
339
  return true;
344
340
  }
345
- /** A line kind's comparison key: key-order-insensitive at every depth, because the two
346
- * sides come from different producers (a WASM read and this scan) and a kind carries a
347
- * nested `attrs` bag. */
341
+ /**
342
+ * A line kind's comparison key, key-order-insensitive at every depth: the sides come from different
343
+ * producers (a WASM read and this scan) and a kind carries a nested `attrs` bag.
344
+ */
348
345
  function kindKey(l) {
349
346
  return canonicalJson(kindPart(l));
350
347
  }
351
348
  /**
352
- * Split the edit's islands off the text splice. `applyChange` throws
353
- * `IslandSlotInInsert` on a `delta` that types a slot, so a slot never rides the
354
- * delta: every slot inside the splice's inserted region is stripped from it and
355
- * placed by `{ op: 'insert', at }`, which carries the backing entry in the same
356
- * op. Every other island kept its slot, so a changed payload is `{ op: 'set' }`,
357
- * addressed by the id both sides carry. A deleted island needs no op at all: the
358
- * delta that removes its slot drops the entry.
359
- *
360
- * `at` is the slot's position in the final text: inserts apply left to right, so
361
- * by the time the k-th lands, the k slots before it are back in place.
349
+ * Split the edit's islands off the text splice. `applyChange` throws `IslandSlotInInsert` on a
350
+ * `delta` that types a slot, so every slot inside the splice's inserted region is stripped from it
351
+ * and placed by `{ op: 'insert', at }`, which carries the backing entry. Every other island kept
352
+ * its slot, so a changed payload is `{ op: 'set' }` by the id both sides carry. A deleted island
353
+ * needs no op: the delta that removes its slot drops the entry.
362
354
  *
363
- * The pairing is positional for `insert` and by id for `set`, which is what each
364
- * addresses by. An id the store does not carry emits nothing rather than a `set`
365
- * that would throw `UnknownIslandId`: nothing mints or renames an island id, so
366
- * it is unreachable.
355
+ * `at` is the slot's position in the final text: inserts apply left to right, so by the k-th, the k
356
+ * slots before it are in place. An id the store does not carry emits nothing (it would throw
357
+ * `UnknownIslandId`); nothing mints or renames an island id, so that is unreachable.
367
358
  */
368
359
  function splitIslands(edit) {
369
360
  const { oldRt, newRt, delta } = edit;
@@ -386,8 +377,7 @@ function splitIslands(edit) {
386
377
  });
387
378
  return { delta: inserts && delta ? withoutSlots(delta) : delta, islandOps };
388
379
  }
389
- /** The USV positions of `text`'s island slots, in document order: the i-th is the
390
- * i-th island's, since the scan appends an entry per slot it writes. */
380
+ /** The USV positions of `text`'s island slots, in document order: the i-th is the i-th island's. */
391
381
  function slotPositions(text) {
392
382
  const out = [];
393
383
  let usv = 0;
@@ -398,9 +388,10 @@ function slotPositions(text) {
398
388
  }
399
389
  return out;
400
390
  }
401
- /** The splice's inserted region `[start, end)` in final coords. `diffText` emits
402
- * one `[retain?][delete?][insert?][retain?]`, so the leading retain is where the
403
- * insert lands and its length is how far it reaches. */
391
+ /**
392
+ * The splice's inserted region `[start, end)` in final coords: `diffText` emits one
393
+ * `[retain?][delete?][insert?][retain?]`, so the leading retain is where the insert lands.
394
+ */
404
395
  function insertedRegion(delta) {
405
396
  if (!delta)
406
397
  return { start: 0, end: 0 };
@@ -409,9 +400,10 @@ function insertedRegion(delta) {
409
400
  const insert = delta.ops.find((op) => 'insert' in op);
410
401
  return { start, end: start + (insert ? usvLength(insert.insert) : 0) };
411
402
  }
412
- /** The delta with every island slot removed from its insert, or `undefined` when
413
- * that leaves nothing but retains. Retain/delete arithmetic reads the old text,
414
- * so dropping inserted characters leaves the rest of the splice intact. */
403
+ /**
404
+ * The delta with every island slot removed from its insert, or `undefined` when only retains
405
+ * remain.
406
+ */
415
407
  function withoutSlots(delta) {
416
408
  const ops = [];
417
409
  let splices = false;
@@ -431,9 +423,10 @@ function withoutSlots(delta) {
431
423
  }
432
424
  return splices ? { ops } : undefined;
433
425
  }
434
- /** Formatting marks → add/remove ops; anchors → add/removeAnchor by id. A `MarkOp`'s
435
- * `add` / `remove` is a `ContentMark` under an op, so a descriptor spreads in whole and
436
- * the wire's `attrs` spelling is the one a checker admits. */
426
+ /**
427
+ * Formatting marks → add/remove ops; anchors → add/removeAnchor by id. A descriptor spreads in
428
+ * whole, so the wire's `attrs` spelling is the one a checker admits.
429
+ */
437
430
  function diffMarks(oldRt, newRt, moved, opts) {
438
431
  const ops = [];
439
432
  // The store's answer for where `moved`'s text channels leave the field's marks;
@@ -9,9 +9,10 @@ import { type SlashState, type SlashStrings } from './slash.js';
9
9
  /** Options for {@link createField}. */
10
10
  export interface CreateFieldOpts {
11
11
  doc: Document;
12
- /** The schema the leaf reads through: `quill.reader(doc).getContent` decodes by
13
- * declared type, which is the only thing that says whether a stored string is
14
- * markdown or literal text. */
12
+ /**
13
+ * The schema the leaf reads through: the declared type decides whether a stored string is
14
+ * markdown or literal text.
15
+ */
15
16
  quill: Quill;
16
17
  addr: Addr;
17
18
  container: HTMLElement;
@@ -24,53 +25,44 @@ export interface CreateFieldOpts {
24
25
  * `aria-describedby` while it holds. */
25
26
  heldNoteId?: string;
26
27
  /**
27
- * Fired when the leaf holds or releases, and at mount when it mounts held. A one-line
28
- * leaf, or a `text` one, is held over a value its schema cannot
29
- * hold (`fitsLeaf`, upstream's `isInline` and `isPlain`): the decode would join lines
30
- * and drop containers and islands for the first commit to store. Any leaf is held
31
- * over what is read and not edited: an element, a definition list, a footnote and an
32
- * equation (CODEC §Decode). A held leaf draws
33
- * its content on the block schema, read-only, and commits nothing. Judged of the value
34
- * each mount and re-hydrate reads, so {@link FieldController.applyExternal} is what
35
- * re-evaluates it.
28
+ * Fired when the leaf holds or releases, and at mount when it mounts held. A one-line or `text`
29
+ * leaf is held over a value its schema cannot hold (`fitsLeaf`): the decode would join lines
30
+ * and drop containers and islands. Any leaf is held over what is read and not edited: a font
31
+ * family, a footnote, an equation (CODEC §Decode). A held leaf draws on the block schema,
32
+ * read-only, and commits nothing. Judged on each mount and re-hydrate ({@link
33
+ * FieldController.applyExternal}).
36
34
  */
37
35
  onHold?(held: boolean): void;
38
36
  /** Suppress the markdown-shorthand input rules. */
39
37
  noInputRules?: boolean;
40
- /** The island chrome's wording, read live (per render) so a consumer swapping
41
- * locale mid-session re-renders rather than freezing it at mount. Absent leaves
42
- * the package's English; an inline leaf never asks (it holds no island). */
38
+ /**
39
+ * The island chrome's wording, read live so a locale swap re-renders. Absent leaves the
40
+ * package's English.
41
+ */
43
42
  tableStrings?: () => TableChromeStrings;
44
43
  /**
45
- * The slash menu's live state, or `undefined` when it is closed: the channel the
46
- * chrome draws from (`visual/SlashMenu.svelte`).
47
- *
48
- * Its presence is what mounts the menu at all. The trigger and its keys are the
49
- * leaf's, but a surface only a keyboard can reach (claiming Enter, Escape and the
50
- * arrows with nothing on screen) is worse than no surface, so the door exists
51
- * exactly where something can draw it. A constrained inline leaf never has one:
52
- * it holds no island and no block to convert.
44
+ * The slash menu's live state, or `undefined` when it is closed: the channel the chrome draws
45
+ * from (`visual/SlashMenu.svelte`). Its presence is what mounts the menu, so the keys never
46
+ * claim Enter, Escape and the arrows with nothing on screen. A constrained inline leaf never
47
+ * has one.
53
48
  */
54
49
  onSlash?(state: SlashState | undefined): void;
55
- /** The slash surface's wording, read per decoration pass. Absent leaves the
56
- * package's English; read only beside {@link onSlash}, the hint being the menu's. */
50
+ /** The slash surface's wording, read per decoration pass; read only beside {@link onSlash}. */
57
51
  slashStrings?: () => SlashStrings;
58
- /** Accessible name → `aria-label` on the `contenteditable`. For a leaf nothing
59
- * else names: an array element (the field label plus its 1-based index), the
60
- * card body (no visible label at all). A leaf with a field label takes
61
- * `labelledBy` instead: `for` cannot reach a `contenteditable`, which is not a
62
- * labelable element, so the association runs the other way. */
52
+ /**
53
+ * Accessible name → `aria-label` on the `contenteditable`, for a leaf nothing else names (an
54
+ * array element, the card body). A leaf with a field label takes `labelledBy` instead: `for`
55
+ * cannot reach a `contenteditable`.
56
+ */
63
57
  label?: string;
64
58
  /** The field label's own id → `aria-labelledby`, which supersedes `label`. */
65
59
  labelledBy?: string;
66
60
  /** The parked `description` → `aria-describedby`; announced after the name. */
67
61
  describedBy?: string;
68
62
  /**
69
- * The resolved `default:` where it prints: the content an unset field's leaf holds,
70
- * marked `data-default` on the editable for the default rung. Nothing is written
71
- * until an edit, which commits the whole leaf as authored: the default taken, with
72
- * the edit in it. The initial value; {@link FieldController.setFallback} moves it
73
- * after mount.
63
+ * The resolved `default:` where it prints: the content an unset field's leaf holds, marked
64
+ * `data-default`. Nothing is written until an edit, which commits the whole leaf with the edit
65
+ * in it. Initial value; {@link FieldController.setFallback} moves it.
74
66
  */
75
67
  fallback?: Content;
76
68
  /** Ghost text shown on the empty leaf: the `none` an unset optional field prints, or
@@ -91,15 +83,10 @@ export interface CreateFieldOpts {
91
83
  /** Fired with the new USV caret after an edit or a selection move. */
92
84
  onCaretMove?(addr: Addr, pos: number): void;
93
85
  /**
94
- * Fired after an edit committed to the document: the leaf's change signal, and
95
- * the reason it is not `onCaretMove`. That one also fires on a bare selection
96
- * move, so a host driving a recompile off it recompiles on every arrow key.
97
- *
98
- * Fires for every commit regardless of which branch it took (`applyChange`, the
99
- * first-edit `overwrite`, the fallback `overwrite`, a `text` leaf's typed write)
100
- * and for an anchor mutation,
101
- * which changes no text. A commit that failed outright (`commit-lost`) does not
102
- * fire it: nothing landed.
86
+ * Fired after an edit committed to the document: the leaf's change signal. Unlike `onCaretMove`
87
+ * it does not fire on a bare selection move. Fires for every commit branch and for an anchor
88
+ * mutation (which changes no text); a commit that failed outright (`commit-lost`) does not fire
89
+ * it.
103
90
  */
104
91
  onChange?(addr: Addr): void;
105
92
  /** Recovered failures on the commit path ({@link EditorErrorHandler}). */
@@ -116,12 +103,9 @@ export interface FieldController {
116
103
  * re-evaluate its hold against the value now stored. */
117
104
  applyExternal(): void;
118
105
  /**
119
- * Move the empty-leaf ghost after mount: a card retyped to another kind takes
120
- * its new kind's wording without remounting, which it must not do (the leaf key
121
- * is the card's session id, so a remount would cost the caret).
122
- *
123
- * Refreshes the decoration without a transaction: a placeholder is chrome, and
124
- * a transaction would fire `onCaretMove` at a moment the caret did not move.
106
+ * Move the empty-leaf ghost after mount (a card retyped to another kind takes its new wording;
107
+ * a remount would cost the caret). Refreshes the decoration without a transaction, which would
108
+ * fire `onCaretMove` with no caret move.
125
109
  */
126
110
  setPlaceholder(text: string | undefined): void;
127
111
  /** Move the default an unset leaf holds after mount: a retype or a quill swap
@@ -131,11 +115,9 @@ export interface FieldController {
131
115
  /** The current stored content for this addr (for tests / reconcile). */
132
116
  getContent(): Content;
133
117
  /**
134
- * Insert an identity anchor `id` at USV `pos`: the seam that gives
135
- * `anchor` the toggle the six formatting marks have. Zero-width; it folds into the
136
- * plugin's position set and commits through the mark-diff `anchor` op, so it
137
- * survives later edits like any anchor. The id is caller-supplied and must be
138
- * unique + invariant (the 0.97 anchor-id policy); a duplicate id is a no-op.
118
+ * Insert an identity anchor `id` at USV `pos`. Zero-width; it folds into the plugin's position
119
+ * set and commits through the mark-diff `anchor` op. The id is caller-supplied and must be
120
+ * unique and invariant; a duplicate id is a no-op.
139
121
  */
140
122
  insertAnchor(id: string, pos: number): void;
141
123
  /** Remove the identity anchor `id` (the inverse of {@link insertAnchor}); a no-op if absent. */
@@ -143,9 +125,8 @@ export interface FieldController {
143
125
  /** Ids of the anchors within USV range `[from, to]`: a selection's anchor state. */
144
126
  anchorsInRange(from: number, to: number): string[];
145
127
  /**
146
- * Move the slash menu's cursor onto the command `name`: what a pointer entering an
147
- * item calls. The keyboard cursor and the pointer's highlight are one state, so the
148
- * chrome moves the one the keys already drive rather than painting a second.
128
+ * Move the slash menu's cursor onto the command `name` (a pointer entering an item): the
129
+ * keyboard cursor and the pointer highlight are one state.
149
130
  */
150
131
  slashFocus(name: string): void;
151
132
  /** Run the command `name` (a click on its item): the same path Enter takes. A no-op
@@ -159,13 +140,11 @@ export interface FieldController {
159
140
  destroy(): void;
160
141
  }
161
142
  /**
162
- * The ProseMirror handles a chrome surface reaches a mounted leaf through. Not part
163
- * of {@link FieldController}: they are PM internals, and a consumer's contract is the
164
- * controller's verbs. The format popover is the one caller and it needs both:
165
- * `view` to name the leaf's own document, `focusedView` to act on wherever the caret
166
- * actually is, which inside a table island is a nested cell view (`table-view.ts`).
167
- * The two being different is exactly what tells the popover to withhold its
168
- * `anchor` button: an anchor is the field's coordinate space, and a cell is not in it.
143
+ * The ProseMirror handles a chrome surface reaches a mounted leaf through; not part of {@link
144
+ * FieldController}. The format popover needs both: `view` names the leaf's own document,
145
+ * `focusedView` acts wherever the caret is, which inside a table island is a nested cell view
146
+ * (`table-view.ts`). Their differing is what tells the popover to withhold its `anchor` button: a
147
+ * cell is not in the field's coordinate space.
169
148
  */
170
149
  export interface LeafViews {
171
150
  view: EditorView;
@@ -173,17 +152,15 @@ export interface LeafViews {
173
152
  /** Every view nested inside this leaf, in mount order: one per table cell. */
174
153
  nestedViews(): EditorView[];
175
154
  }
176
- /** The canonical empty `Content`: one empty `para` line. The empty a prose
177
- * leaf (or an array's prose element) seeds from. */
155
+ /** The canonical empty `Content`: one empty `para` line. */
178
156
  export declare function emptyContent(): Content;
179
157
  /** Whether `rt` is the empty leaf: {@link emptyContent}, nothing typed and no shape left. */
180
158
  export declare function isEmptyContent(rt: Content): boolean;
181
- /** The naming attributes for the `contenteditable`, shared by every prose leaf
182
- * (`ProseValue` takes it too, so an addressed leaf and a by-value one name their
183
- * regions by one rule). `aria-labelledby` supersedes `aria-label`: a leaf carrying
184
- * both is the ambiguity where implementations disagree about which wins, and
185
- * `undefined` is returned whole when a leaf takes neither, since ProseMirror reads
186
- * the absence, not an empty object. */
159
+ /**
160
+ * The naming attributes for the `contenteditable`, shared with `ProseValue`. `aria-labelledby`
161
+ * supersedes `aria-label`, and `undefined` is returned whole when a leaf takes neither, since
162
+ * ProseMirror reads the absence.
163
+ */
187
164
  export declare function proseAttributes(opts: {
188
165
  label?: string;
189
166
  labelledBy?: string;
@@ -198,36 +175,26 @@ export declare function heldAttributes(opts: {
198
175
  }, noteId: string | undefined): Record<string, string>;
199
176
  export declare function createField(opts: CreateFieldOpts): FieldController;
200
177
  /**
201
- * The prose-leaf plugin stack (VISUAL_EDITOR §Surface): shared by
202
- * {@link createField} and the by-value editor (`ProseValue`), so the two
203
- * never fork the keymap/plugin ordering. History first, with the plugin that starts an
204
- * undo step at each block-moving edit (`undo.ts`), then any leaf-specific
205
- * plugins (`afterHistory`: the addressed leaf passes its anchor-position plugin;
206
- * a by-value leaf passes none), then {@link linebreakPlugin}, which normalizes what
207
- * the rest of the stack leaves, then the markdown-shorthand input rules and the
208
- * Backspace that takes one back, the field keymap over the base keymap, the URL paste
209
- * that links a selection (`link-paste.ts`), and last the ones that answer to a block
210
- * leaf's own shapes: the gap cursor, {@link pastAtomPlugin}, the island paste pass
211
- * (`islands.ts`), the table paste (`table-paste.ts`), the markdown paste
212
- * (`markdown-paste.ts`) and the task item's checkbox (`tasks.ts`). A one-line leaf's
213
- * paste joins its lines (`line-paste.ts`).
178
+ * The prose-leaf plugin stack (VISUAL_EDITOR §Surface), shared by {@link createField} and
179
+ * `ProseValue` so the two never fork the plugin ordering. History first with the step-splitting
180
+ * plugin (`undo.ts`), then `afterHistory` plugins (the addressed leaf's anchor plugin), {@link
181
+ * linebreakPlugin}, the markdown-shorthand input rules and their Backspace, the field keymap over
182
+ * the base keymap, the URL paste that links a selection (`link-paste.ts`), and last the block
183
+ * leaf's own shapes: the gap cursor, {@link pastAtomPlugin}, the island paste (`islands.ts`), table
184
+ * paste (`table-paste.ts`), markdown paste (`markdown-paste.ts`) and the task checkbox
185
+ * (`tasks.ts`). A one-line leaf's paste joins its lines (`line-paste.ts`).
214
186
  *
215
- * Every mark-shaped plugin reads the schema rather than a flag: over either mark-free
216
- * schema the shorthand rules build nothing (each is guarded on its mark type), the
217
- * toggles bind no key and a pasted URL stays text, so a `**bold**` keeps the delimiters
218
- * its author typed without a second rule saying so.
187
+ * Mark-shaped plugins read the schema rather than a flag: over a mark-free schema the shorthand
188
+ * rules build nothing, the toggles bind no key and a pasted URL stays text.
219
189
  */
220
190
  export declare function proseLeafPlugins(schema: Schema, opts: {
221
191
  inline: boolean;
222
- /** The slash menu's report channel; absent mounts no menu and leaves
223
- * Enter/Escape/the arrows to the links below (`keymap.ts`). */
192
+ /** The slash menu's report channel; absent mounts no menu (`keymap.ts`). */
224
193
  slash?: (state: SlashState | undefined) => void;
225
194
  /** The empty line's hint, read live; drawn only beside `slash`. */
226
195
  slashHint?: () => string | undefined;
227
196
  noInputRules?: boolean;
228
- /** Read live, not captured: the ghost can move after mount
229
- * ({@link FieldController.setPlaceholder}), and a re-hydration rebuilds this
230
- * stack, so the plugin must ask rather than hold. */
197
+ /** Read live: the ghost can move after mount and a re-hydration rebuilds this stack. */
231
198
  placeholder?: () => string | undefined;
232
199
  /** {@link CreateFieldOpts.placeholderUntilEdit}. */
233
200
  placeholderUntilEdit?: boolean;