@quillmark/svelte 0.21.0 → 0.23.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 (69) hide show
  1. package/NOTICE +6 -7
  2. package/README.md +1 -1
  3. package/dist/core/address.d.ts +6 -1
  4. package/dist/core/address.js +12 -3
  5. package/dist/core/bloom.d.ts +9 -4
  6. package/dist/core/bloom.js +18 -49
  7. package/dist/core/codec/atoms.d.ts +1 -1
  8. package/dist/core/codec/atoms.js +1 -1
  9. package/dist/core/codec/blocks.js +4 -2
  10. package/dist/core/codec/cap.d.ts +21 -0
  11. package/dist/core/codec/cap.js +63 -0
  12. package/dist/core/codec/code.d.ts +1 -1
  13. package/dist/core/codec/code.js +1 -1
  14. package/dist/core/codec/decode.d.ts +5 -5
  15. package/dist/core/codec/decode.js +18 -12
  16. package/dist/core/codec/encode.js +6 -1
  17. package/dist/core/codec/field.d.ts +22 -10
  18. package/dist/core/codec/field.js +54 -18
  19. package/dist/core/codec/index.d.ts +3 -1
  20. package/dist/core/codec/index.js +3 -2
  21. package/dist/core/codec/inputrules.js +1 -1
  22. package/dist/core/codec/lists.d.ts +3 -2
  23. package/dist/core/codec/lists.js +21 -5
  24. package/dist/core/codec/nesting.d.ts +20 -0
  25. package/dist/core/codec/nesting.js +62 -0
  26. package/dist/core/codec/prose.css +74 -20
  27. package/dist/core/codec/schema.d.ts +2 -2
  28. package/dist/core/codec/schema.js +16 -7
  29. package/dist/core/codec/table-copy.d.ts +17 -0
  30. package/dist/core/codec/table-copy.js +84 -0
  31. package/dist/core/codec/table-view.d.ts +1 -7
  32. package/dist/core/codec/table-view.js +70 -119
  33. package/dist/core/codec/table.d.ts +2 -9
  34. package/dist/core/codec/table.js +10 -19
  35. package/dist/core/codec/tasks.d.ts +19 -0
  36. package/dist/core/codec/tasks.js +140 -0
  37. package/dist/core/errors.d.ts +2 -2
  38. package/dist/visual/ArrayField.svelte +103 -63
  39. package/dist/visual/ArrayField.svelte.d.ts +0 -2
  40. package/dist/visual/Card.svelte +9 -3
  41. package/dist/visual/Card.svelte.d.ts +2 -2
  42. package/dist/visual/Field.svelte +33 -17
  43. package/dist/visual/Field.svelte.d.ts +4 -2
  44. package/dist/visual/FieldLabel.svelte +19 -33
  45. package/dist/visual/FieldLabel.svelte.d.ts +4 -2
  46. package/dist/visual/FormatPopover.svelte +54 -73
  47. package/dist/visual/MatrixField.svelte +18 -12
  48. package/dist/visual/NumberField.svelte +32 -3
  49. package/dist/visual/ObjectField.svelte +50 -22
  50. package/dist/visual/ObjectField.svelte.d.ts +6 -0
  51. package/dist/visual/ProseField.svelte +20 -14
  52. package/dist/visual/ProseField.svelte.d.ts +8 -2
  53. package/dist/visual/ProseValue.svelte +30 -19
  54. package/dist/visual/ProseValue.svelte.d.ts +7 -4
  55. package/dist/visual/TextField.svelte +3 -2
  56. package/dist/visual/TipsCard.svelte +5 -2
  57. package/dist/visual/VariantField.svelte +1 -1
  58. package/dist/visual/VariantField.svelte.d.ts +1 -1
  59. package/dist/visual/VisualEditorInner.svelte +13 -13
  60. package/dist/visual/controls.css +19 -1
  61. package/dist/visual/icons/nodes.d.ts +0 -21
  62. package/dist/visual/icons/nodes.js +1 -7
  63. package/dist/visual/leaves.d.ts +12 -9
  64. package/dist/visual/leaves.js +19 -0
  65. package/dist/visual/strings.d.ts +16 -5
  66. package/dist/visual/strings.js +17 -9
  67. package/dist/visual/structure.d.ts +26 -32
  68. package/dist/visual/structure.js +71 -53
  69. package/package.json +3 -3
@@ -31,7 +31,7 @@
31
31
  type Landing
32
32
  } from '../core/address.js';
33
33
  import { errorMessage, reportError } from '../core/errors.js';
34
- import { bloomInside } from '../core/bloom.js';
34
+ import { bloom } from '../core/bloom.js';
35
35
  import { createLifespan } from '../core/teardown.js';
36
36
  import { spacePx } from '../core/rungs.js';
37
37
  import type {
@@ -220,7 +220,7 @@
220
220
  const card = addr.card;
221
221
  return card != null ? { card, field: addr.field } : { field: addr.field };
222
222
  }
223
- function handleFocus(addr: Addr): void {
223
+ function handleFocus(addr: Addr, steps: readonly PathStep[] = []): void {
224
224
  const plain = normalize(addr);
225
225
  activeAddr = plain;
226
226
  activeCardId = cardIdOf(plain);
@@ -228,7 +228,7 @@
228
228
  // to at the offset it still names has to report again.
229
229
  lastCaretField = undefined;
230
230
  lastCaretPos = undefined;
231
- const field = pathFor(plain);
231
+ const field = pathFor(plain, steps);
232
232
  // Both halves or neither: a stale addr names no path and no live card.
233
233
  if (field != null && activeCardId != null)
234
234
  onActiveLeafChange?.({ field, cardId: activeCardId });
@@ -245,11 +245,11 @@
245
245
  return model.cards.map((c) => c.kind);
246
246
  }
247
247
  /**
248
- * A leaf's canonical `DocPath`. `undefined` for an address outside the live card
249
- * array (a stale addr: drop rather than mis-target).
248
+ * A leaf's canonical `DocPath`, `steps` down from the field. `undefined` for an
249
+ * address outside the live card array (a stale addr: drop rather than mis-target).
250
250
  */
251
- function pathFor(addr: Addr): DocPath | undefined {
252
- return fieldPathForAddr(addr, addr.card == null ? NO_KINDS : liveKinds());
251
+ function pathFor(addr: Addr, steps: readonly PathStep[] = []): DocPath | undefined {
252
+ return fieldPathForAddr(addr, addr.card == null ? NO_KINDS : liveKinds(), steps);
253
253
  }
254
254
  // The last place reported, so a caret that did not move is not news. A leaf fires
255
255
  // one caret signal per transaction, and a transaction need not have moved the
@@ -736,10 +736,10 @@
736
736
  // (where landing a caret changes nothing on screen) or one off-screen, where the
737
737
  // browser's focus-scroll moves the page and leaves the caret to be hunted for.
738
738
  const box = await land(found, at.granularity === 'segment' ? undefined : at.pos);
739
- if (span.alive) bloomInside(box);
739
+ if (span.alive) bloom(box);
740
740
  }
741
741
  /** The active prose leaf: the formatting popover's observation seam. The active
742
- * field's controller where it registered one, else the by-value leaf focused last
742
+ * field's own leaf where it registered one, else the by-value leaf focused last
743
743
  * under it (`leaves.ts`); `undefined` for a form control, which holds no marks to
744
744
  * toggle. */
745
745
  export function getActiveLeaf(): ActiveProse | undefined {
@@ -747,7 +747,7 @@
747
747
  const card = activeAddr.card != null ? activeCardId : undefined;
748
748
  const key = fieldKeyToString({ card, field: activeAddr.field });
749
749
  const controller = leaves.prose(key);
750
- if (controller) return { views: controller as FieldController & LeafViews, controller };
750
+ if (controller) return { views: controller as FieldController & LeafViews };
751
751
  const views = leaves.value(key);
752
752
  return views && { views };
753
753
  }
@@ -826,7 +826,7 @@
826
826
  const found = await revealLeaf(field);
827
827
  if (!found) return missed(`no mounted field at ${field}`, field);
828
828
  const box = await land(found, undefined);
829
- if (span.alive) bloomInside(box);
829
+ if (span.alive) bloom(box);
830
830
  }
831
831
  /** Seed a card of `kind` and insert it at `at` (default: the end). Returns the new
832
832
  * card's session key, or `undefined` when the quill seeds no card of that kind. */
@@ -995,7 +995,7 @@
995
995
  {#if kinds.length === 1}
996
996
  <button
997
997
  type="button"
998
- class="qm-add-btn qm-add-affordance qm-tap-floor"
998
+ class="qm-add-btn qm-add-affordance qm-tap-floor qm-focus-ring"
999
999
  aria-label={marked ? undefined : merged.addCard}
1000
1000
  onclick={() => addCard(atIndex, kinds[0], true)}
1001
1001
  >{#if marked}{merged.addCard}{:else}<Icon name="plus" class="qm-add-glyph" />{/if}</button
@@ -1008,7 +1008,7 @@
1008
1008
  rules below reach it through `:global`. -->
1009
1009
  <DropdownMenu.Root>
1010
1010
  <DropdownMenu.Trigger
1011
- class="qm-add-btn qm-add-affordance qm-tap-floor"
1011
+ class="qm-add-btn qm-add-affordance qm-tap-floor qm-focus-ring"
1012
1012
  aria-label={marked ? undefined : merged.addCard}
1013
1013
  >{#if marked}{merged.addCard}{:else}<Icon
1014
1014
  name="plus"
@@ -267,6 +267,22 @@
267
267
  transform: translate(-50%, -50%);
268
268
  }
269
269
 
270
+ /* The arrival wash: a plane over the host's box at rest, which a landing plays
271
+ (`core/bloom.ts`). On the host from mount, because WebKit resizes a laid-out subgrid's
272
+ tracks when an out-of-flow box is added to it. */
273
+ :where(.qm-wash) {
274
+ position: relative;
275
+ }
276
+ :where(.qm-wash)::after {
277
+ content: '';
278
+ position: absolute;
279
+ inset: 0;
280
+ border-radius: inherit;
281
+ background: var(--_qm-accent-wash);
282
+ opacity: 0;
283
+ pointer-events: none;
284
+ }
285
+
270
286
  /* The capacity ladder: how many tracks a grid of fields steps to, off the width of
271
287
  the query container it stands in. One recipe, because three grids read it — a section's
272
288
  fields (`Card.svelte`), a subform's properties (`ObjectField`), each over its own
@@ -311,7 +327,8 @@
311
327
  highlight the keyboard cannot reach. It fills with the accent held rather than
312
328
  the hover rung: the mark is a cursor a keyboard drives, so it has to read at a
313
329
  glance from across the list, where a hover only has to confirm the pointer
314
- already under it.
330
+ already under it. So the mark is the item's whole focus indicator, and the
331
+ outline a focused item would draw beside it is taken off.
315
332
 
316
333
  The resting fill is the recipe's too: an item drawn on a `<button>` carries a UA
317
334
  fill to take back, and a scoped block outranks this file, so a caller taking it
@@ -338,6 +355,7 @@
338
355
  background: transparent;
339
356
  cursor: pointer;
340
357
  user-select: none;
358
+ outline: none;
341
359
  }
342
360
  :where(.qm-menu-item[data-highlighted]) {
343
361
  background: var(--_qm-accent-tint);
@@ -87,27 +87,6 @@ export declare const ICONS: {
87
87
  }], readonly ["path", {
88
88
  readonly d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71";
89
89
  }]];
90
- readonly hash: readonly [readonly ["line", {
91
- readonly x1: "4";
92
- readonly x2: "20";
93
- readonly y1: "9";
94
- readonly y2: "9";
95
- }], readonly ["line", {
96
- readonly x1: "4";
97
- readonly x2: "20";
98
- readonly y1: "15";
99
- readonly y2: "15";
100
- }], readonly ["line", {
101
- readonly x1: "10";
102
- readonly x2: "8";
103
- readonly y1: "3";
104
- readonly y2: "21";
105
- }], readonly ["line", {
106
- readonly x1: "16";
107
- readonly x2: "14";
108
- readonly y1: "3";
109
- readonly y2: "21";
110
- }]];
111
90
  readonly info: readonly [readonly ["circle", {
112
91
  readonly cx: "12";
113
92
  readonly cy: "12";
@@ -5,7 +5,7 @@
5
5
  //
6
6
  // Verbatim from lucide 1.28.0 — the version is stated because a glyph that drifts
7
7
  // from upstream is otherwise invisible, and because the notices in `NOTICE` are the
8
- // ones that release carries (ISC, and MIT for the Feather-derived ten).
8
+ // ones that release carries (ISC, and MIT for the Feather-derived glyphs).
9
9
  export const ICONS = {
10
10
  'chevron-right': [['path', { d: 'm9 18 6-6-6-6' }]],
11
11
  'arrow-right': [
@@ -45,12 +45,6 @@ export const ICONS = {
45
45
  ['path', { d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71' }],
46
46
  ['path', { d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71' }]
47
47
  ],
48
- hash: [
49
- ['line', { x1: '4', x2: '20', y1: '9', y2: '9' }],
50
- ['line', { x1: '4', x2: '20', y1: '15', y2: '15' }],
51
- ['line', { x1: '10', x2: '8', y1: '3', y2: '21' }],
52
- ['line', { x1: '16', x2: '14', y1: '3', y2: '21' }]
53
- ],
54
48
  info: [
55
49
  ['circle', { cx: '12', cy: '12', r: '10' }],
56
50
  ['path', { d: 'M12 16v-4' }],
@@ -8,8 +8,8 @@ import type { FieldController, LeafViews } from '../core/codec/index.js';
8
8
  */
9
9
  export type LandingBox = HTMLElement | undefined | Promise<HTMLElement | undefined>;
10
10
  /**
11
- * A mounted field's landing handle: focus it, and the element the arrival wash blooms
12
- * inside ({@link import('../core/bloom.js').bloomInside}).
11
+ * A mounted field's landing handle: focus it, and the `.qm-wash` box the arrival wash
12
+ * plays over ({@link import('../core/bloom.js').bloom}).
13
13
  *
14
14
  * A `FieldController` is one, which is what lets the two lanes share a lookup; `Field`
15
15
  * builds the same shape over a form control off the handoff a label click already
@@ -71,16 +71,19 @@ export interface LeafRegistry {
71
71
  clear(): void;
72
72
  }
73
73
  export declare function createLeafRegistry(): LeafRegistry;
74
- /**
75
- * The active prose leaf as the formatting popover reaches it: the views a selection is
76
- * read from and a mark toggled at, and the controller where the leaf is a field's. A
77
- * by-value leaf has none, its anchors being dropped on the parent's value write, so the
78
- * anchor verbs the controller carries have nothing to act on there.
79
- */
74
+ /** The active prose leaf as the formatting popover reaches it: the views a selection is
75
+ * read from and a mark toggled at. */
80
76
  export interface ActiveProse {
81
77
  views: LeafViews;
82
- controller?: FieldController;
83
78
  }
79
+ /**
80
+ * The steps from a field's control box `root` down to the cell holding `target`, the
81
+ * reverse of {@link FieldControl.focusPath}: read off the marker each container rung
82
+ * stamps on the cell it draws, `data-qm-index` on an array's row and `data-qm-prop` on a
83
+ * key step (an object's property, a variant's cell, a matrix's member and an added
84
+ * item's title). `[]` for a target on the field's own control.
85
+ */
86
+ export declare function stepsTo(target: EventTarget | null, root: Element): PathStep[];
84
87
  /** The by-value lane with a field's key bound: what a leaf under that field announces
85
88
  * itself through, knowing no key of its own. */
86
89
  export interface ValueLeafSeat {
@@ -66,6 +66,25 @@ export function createLeafRegistry() {
66
66
  }
67
67
  };
68
68
  }
69
+ /**
70
+ * The steps from a field's control box `root` down to the cell holding `target`, the
71
+ * reverse of {@link FieldControl.focusPath}: read off the marker each container rung
72
+ * stamps on the cell it draws, `data-qm-index` on an array's row and `data-qm-prop` on a
73
+ * key step (an object's property, a variant's cell, a matrix's member and an added
74
+ * item's title). `[]` for a target on the field's own control.
75
+ */
76
+ export function stepsTo(target, root) {
77
+ const steps = [];
78
+ for (let el = target instanceof Element ? target : null; el && el !== root; el = el.parentElement) {
79
+ const index = el.getAttribute('data-qm-index');
80
+ const key = el.getAttribute('data-qm-prop');
81
+ if (index != null)
82
+ steps.push(Number(index));
83
+ else if (key != null)
84
+ steps.push(key);
85
+ }
86
+ return steps.reverse();
87
+ }
69
88
  const SEAT = Symbol('qm-value-leaf');
70
89
  /** Seat every by-value leaf under the calling field, at any depth of its containers. */
71
90
  export function seatValueLeaves(seat) {
@@ -15,13 +15,13 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
15
15
  /** The card-title input's accessible name. */
16
16
  cardTitle: string;
17
17
  /** The trailing strip's visible words, which are also every strip's accessible
18
- * name; the plus rides the wording. One string over
18
+ * name. One string over
19
19
  * one kind and many: the trigger seeds a card, and which kind is the menu's word
20
20
  * to say or the schema's to have already settled. */
21
21
  addCard: string;
22
22
  arrayAdd: string;
23
23
  arrayRemove: string;
24
- /** The drag handle on an `object` row: its tooltip and, with the row, its name. */
24
+ /** The drag handle on an `object` row: its tooltip. */
25
25
  arrayDrag: string;
26
26
  /**
27
27
  * A row control's accessible name, the action and the row it acts on: `Remove
@@ -35,6 +35,12 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
35
35
  * spells the separator.
36
36
  */
37
37
  arrayCount: (count: number, max: number) => string;
38
+ /**
39
+ * The count beside the label of a `text` or `richtext` field declaring `max:`, the
40
+ * carrier of a keystroke the cap refuses: `12 / 40`. Parametric, so a translator
41
+ * spells the separator.
42
+ */
43
+ textCount: (count: number, max: number) => string;
38
44
  /**
39
45
  * A collapsed array element with nothing to summarize itself by: the label and the
40
46
  * 1-based index, which is what its accessible name already says. Parametric, so
@@ -59,8 +65,6 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
59
65
  /** The date field's toggle, pressed while the field holds `today`: the render
60
66
  * date rather than a written one. */
61
67
  dateToday: string;
62
- /** The required marker's accessible name; the glyph itself is a `*`. */
63
- fieldRequired: string;
64
68
  /** What an unset optional cell (`t?`) ghosts at rest: the `none` it prints
65
69
  * unanswered, in the product's words. */
66
70
  optionalGhost: string;
@@ -75,7 +79,8 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
75
79
  formatStrikethrough: string;
76
80
  formatCode: string;
77
81
  formatLink: string;
78
- /** The anchor button's accessible name. */
82
+ /** The anchor button's accessible name. The popover withholds the button, so
83
+ * nothing draws this or the title below. */
79
84
  formatAnchor: string;
80
85
  /** Its hover title, which says what an anchor is; the name above only says what. */
81
86
  formatAnchorTitle: string;
@@ -138,6 +143,12 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
138
143
  * diagnostic.
139
144
  */
140
145
  export type FormatDiagnostic = (d: Diagnostic) => string | undefined;
146
+ /**
147
+ * A `Mod` chord as the writer's platform prints it: `⌘B` on Apple's, `Ctrl+B`
148
+ * elsewhere. The platform test is the one `prosemirror-keymap` resolves `Mod` by, so a
149
+ * tooltip names the chord the keymap binds.
150
+ */
151
+ export declare function chord(key: string, platform?: string): string;
141
152
  /** The package's English. Every key, so the merge below is total. */
142
153
  export declare const DEFAULT_VISUAL_STRINGS: VisualStrings;
143
154
  /** A consumer's wording: any subset, the rest the package's. */
@@ -4,7 +4,7 @@
4
4
  // makes wording an override rather than a fork.
5
5
  //
6
6
  // Several of these are accessible names rather than decoration — the card
7
- // controls, the add trigger, the required marker — so an untranslated surface is
7
+ // controls, the add trigger — so an untranslated surface is
8
8
  // not merely inconsistent, it reads the wrong language to a screen reader. That is
9
9
  // why this exists as a seam and not as a list of literals to grep for.
10
10
  //
@@ -14,11 +14,19 @@
14
14
  //
15
15
  // The strings reach the tree through context, not props. They are ambient,
16
16
  // read-only, and wanted eight components deep (a card's controls, a field's
17
- // required marker, the formatting popover, the tips card); threading them by hand
17
+ // optional ghost, the formatting popover, the tips card); threading them by hand
18
18
  // would put a `strings` prop on every component between the root and each leaf,
19
19
  // which is a prop that means nothing to any of them.
20
20
  import { getContext, setContext } from 'svelte';
21
21
  import { DEFAULT_SLASH_STRINGS, DEFAULT_TABLE_STRINGS } from '../core/codec/index.js';
22
+ /**
23
+ * A `Mod` chord as the writer's platform prints it: `⌘B` on Apple's, `Ctrl+B`
24
+ * elsewhere. The platform test is the one `prosemirror-keymap` resolves `Mod` by, so a
25
+ * tooltip names the chord the keymap binds.
26
+ */
27
+ export function chord(key, platform = typeof navigator === 'undefined' ? '' : navigator.platform) {
28
+ return /Mac|iP(hone|[oa]d)/.test(platform) ? `⌘${key}` : `Ctrl+${key}`;
29
+ }
22
30
  /** The package's English. Every key, so the merge below is total. */
23
31
  export const DEFAULT_VISUAL_STRINGS = {
24
32
  ...DEFAULT_TABLE_STRINGS,
@@ -27,12 +35,13 @@ export const DEFAULT_VISUAL_STRINGS = {
27
35
  cardMoveDown: 'Move down',
28
36
  cardDelete: 'Delete card',
29
37
  cardTitle: 'Card title',
30
- addCard: '+ Add Card',
38
+ addCard: 'Add card',
31
39
  arrayAdd: '+ Add',
32
40
  arrayRemove: 'Remove',
33
41
  arrayDrag: 'Drag to move',
34
42
  arrayRowAction: (action, row) => `${action} ${row}`,
35
43
  arrayCount: (count, max) => `${count} / ${max}`,
44
+ textCount: (count, max) => `${count} / ${max}`,
36
45
  elementUntitled: (label, index) => `${label} ${index}`,
37
46
  enumUnsetTag: 'Default',
38
47
  enumUnsetTagNone: 'Unset',
@@ -41,16 +50,15 @@ export const DEFAULT_VISUAL_STRINGS = {
41
50
  matrixItemTitle: (list, index) => `${list ? `${list} item` : 'Item'} ${index}`,
42
51
  matrixRemove: (title) => `Remove ${title}`,
43
52
  dateToday: 'Today',
44
- fieldRequired: 'required',
45
53
  optionalGhost: 'None',
46
54
  proseHeld: 'Holds more than this field can edit here.',
47
55
  formatGroup: 'Formatting',
48
- formatBold: 'Bold (Mod-B)',
49
- formatEmphasis: 'Emphasis (Mod-I)',
50
- formatUnderline: 'Underline (Mod-U)',
56
+ formatBold: `Bold (${chord('B')})`,
57
+ formatEmphasis: `Italic (${chord('I')})`,
58
+ formatUnderline: `Underline (${chord('U')})`,
51
59
  formatStrikethrough: 'Strikethrough',
52
60
  formatCode: 'Code',
53
- formatLink: 'Link',
61
+ formatLink: `Link (${chord('K')})`,
54
62
  formatAnchor: 'Anchor',
55
63
  formatAnchorTitle: 'Anchor — an identity handle over the selection',
56
64
  linkPlaceholder: 'https://…',
@@ -61,7 +69,7 @@ export const DEFAULT_VISUAL_STRINGS = {
61
69
  tipsDismiss: 'Dismiss',
62
70
  tipsNext: 'Next',
63
71
  tipsDot: (index, count) => `Tip ${index} of ${count}`,
64
- unknownKind: (kind) => `Unrecognized card type ${kind}. Its content is preserved.`,
72
+ unknownKind: (kind) => `Unknown card type ${kind}. Its content is kept.`,
65
73
  retypeLabel: 'Change to',
66
74
  retypePlaceholder: 'Choose a type…',
67
75
  noCardKinds: 'This document declares no card types — delete this card to remove it.',
@@ -1,4 +1,5 @@
1
1
  import { type Content, type PathStep, type PayloadItem, type QuillCardSchema, type QuillFieldSchema, type QuillFieldType, type ResolvedField, type Resolved } from '@quillmark/wasm';
2
+ import type { TextCap } from '../core/codec/index.js';
2
3
  /** The control a field type maps to (VISUAL_EDITOR §"Structure mirrors the schema"). */
3
4
  export type ControlKind = 'prose' | 'text' | 'enum' | 'variant' | 'number' | 'boolean' | 'date' | 'array' | 'object' | 'matrix';
4
5
  /** The container controls: the ones a nested address walks into and a deep
@@ -11,22 +12,17 @@ export interface FieldModel {
11
12
  control: ControlKind;
12
13
  /** `ui.group` (undefined = ungrouped). */
13
14
  group: string | undefined;
14
- /** `ui.compact`: asks to share a row with adjacent compacts. A request, not a
15
- * guarantee: `placeFields` declines it for the shapes that grow (see `packable`). */
15
+ /** Asks to share a row with adjacent compacts: `ui.compact` when declared, else
16
+ * {@link packsByDefault}. A request, not a guarantee: `placeFields` declines it for
17
+ * the shapes that grow (see `packable`). */
16
18
  compact: boolean;
17
19
  /** Display label: `title` when set, else the humanized field name. */
18
20
  label: string;
19
21
  /** Schema `description`: authoring help rendered beside the label,
20
22
  * undefined when the field declares none. Chrome-only; never gates. */
21
23
  description: string | undefined;
22
- /**
23
- * Required-ness: {@link obliged}. Drives a persistent label `*`; a field never
24
- * carries both it and a ghosted `default:`, that default being the whole of what
25
- * retires the obligation. Persistent (schema-derived, survives filling). Label
26
- * chrome only; never gates.
27
- */
28
- required: boolean;
29
- /** Prose-leaf flags (only meaningful when `control === 'prose'` / array items). */
24
+ /** Prose-leaf flags (only meaningful when `control === 'prose'` / array items):
25
+ * one line (`oneLine`), and the literal codec (`literalText`). */
30
26
  inline: boolean;
31
27
  plaintext: boolean;
32
28
  }
@@ -161,6 +157,13 @@ export declare function optionalCell(f: QuillFieldSchema): boolean;
161
157
  /** The type a cell's control reads: the declared one, an optional cell's `?` stripped.
162
158
  * The `?` moves the render floor and nothing else, so every read of a type is this. */
163
159
  export declare function baseType(f: QuillFieldSchema): QuillFieldType;
160
+ /** Whether a content field is one line: one paragraph, no container, no island. A `text`
161
+ * or `richtext` field is one line unless it declares `multiline: true`. */
162
+ export declare function oneLine(f: QuillFieldSchema): boolean;
163
+ /** Whether a content field's codec is the literal one, its value text with no marks: `text`. */
164
+ export declare function literalText(f: QuillFieldSchema): boolean;
165
+ /** The character cap a content field's `max:` declares, `undefined` on any other. */
166
+ export declare function textCap(f: QuillFieldSchema | undefined): TextCap | undefined;
164
167
  /**
165
168
  * A scalar `default:` as the text an unset control holds, where it prints: as
166
169
  * declared, `undefined` for none, a blank or a non-scalar. A default of spaces prints
@@ -169,19 +172,19 @@ export declare function baseType(f: QuillFieldSchema): QuillFieldType;
169
172
  export declare function printedText(v: unknown): string | undefined;
170
173
  /**
171
174
  * A declared content literal as the `Content` it holds: a `richtext` one's markdown
172
- * imported, a `plaintext` one's text taken literally, one paragraph whose later lines
175
+ * imported, a `text` one's text taken literally, one paragraph whose later lines
173
176
  * continue it, as the literal codec reads a stored one. What a cell's static
174
177
  * `default:` stands in its leaf as, where no resolved row reaches it. `undefined` for
175
178
  * a blank or a non-scalar.
176
179
  */
177
180
  export declare function declaredContent(v: unknown, markdown: boolean): Content | undefined;
178
- /** A field's `{#each}` key: its name and what a mounted leaf reads once, its type and
179
- * `inline` and its elements'. A retype or a quill swap that changes any of them under a
180
- * name remounts the field, where a kept leaf would commit on the old schema. */
181
+ /** A field's `{#each}` key: its name and what a mounted leaf reads once, its type,
182
+ * {@link oneLine} and `max:`, and its elements'. A retype or a quill swap that changes
183
+ * any of them under a name remounts the field, where a kept leaf would commit on the old
184
+ * schema. */
181
185
  export declare function mountKey(name: string, f: QuillFieldSchema): string;
182
- /** Map a field schema to its control (precedence: prose › enum › text › …).
183
- * An array's element control is this over `items`: a missing `items` is a text
184
- * element. */
186
+ /** Map a field schema to its control. An array's element control is this over `items`;
187
+ * a type this build does not know takes the text input. */
185
188
  export declare function controlKind(f: QuillFieldSchema): ControlKind;
186
189
  /** The discriminant cell of a variant container, off the boundary's own constant: it
187
190
  * crosses inside untyped container data with no type to read it off, so a literal here
@@ -254,8 +257,8 @@ export declare function addedItems(matrix: QuillFieldSchema, map: Record<string,
254
257
  */
255
258
  export declare function addedId(title: string, taken: (id: string) => boolean): string;
256
259
  /** The `object` a matrix member is, for the schema walk: the declared columns, and an
257
- * added item's `title` beside them. Composed here because the boundary serializes the
258
- * roster and the columns alone. */
260
+ * added item's `title`, a one-line `text` cell, beside them. Composed here because the
261
+ * boundary serializes the roster and the columns alone. */
259
262
  export declare function matrixMemberSchema(matrix: QuillFieldSchema, id: string): QuillFieldSchema;
260
263
  /**
261
264
  * The schema at `steps` under `field`, or `undefined` where the schema declares no such
@@ -267,8 +270,7 @@ export declare function matrixMemberSchema(matrix: QuillFieldSchema, id: string)
267
270
  */
268
271
  export declare function schemaAt(field: QuillFieldSchema, steps: readonly PathStep[]): QuillFieldSchema | undefined;
269
272
  /** Whether a cell is one line tall by declaration: the shape a table column and a row
270
- * summary can hold. A prose cell is one where it declares `inline`, `richtext` or
271
- * `plaintext` alike. */
273
+ * summary can hold. A prose cell is one where it is {@link oneLine}. */
272
274
  export declare function shortCell(sub: QuillFieldSchema): boolean;
273
275
  /** A collapsed row's own words ({@link firstShortText}); `undefined` while the row has
274
276
  * nothing to say for itself. */
@@ -301,19 +303,11 @@ export declare function variantCells(schema: QuillFieldSchema, member: string |
301
303
  * `undefined`.
302
304
  */
303
305
  export declare function commitDiscriminant(value: Record<string, unknown> | undefined, member: string | undefined): Record<string, unknown> | undefined;
304
- /**
305
- * Whether the schema obliges a cell: `default:`'s absence, which is the whole of the
306
- * obligation (DOCUMENT_MODEL). Exempt are the cells whose absence of one says nothing:
307
- * a typed dictionary, since a namespace declares no `default:` at all and `validate`
308
- * anchors obligation on the leaves under it; a matrix, whose blank is nothing held, a
309
- * whole answer; and an optional cell, which can declare none, its unanswered render
310
- * being `none`.
311
- */
312
- export declare function obliged(schema: QuillFieldSchema): boolean;
313
306
  /** `foo_bar` → `Foo bar`: the label fallback when a field declares no `title`. */
314
307
  export declare function humanize(name: string): string;
315
- /** Project a card schema's `fields` map (declaration = key order) into models. */
316
- export declare function fieldModels(cardSchema: QuillCardSchema): FieldModel[];
308
+ /** Project a card schema's `fields` map (declaration = key order) into models. A
309
+ * card-level object's `properties` project the same way. */
310
+ export declare function fieldModels(cardSchema: Pick<QuillCardSchema, 'fields'>): FieldModel[];
317
311
  /**
318
312
  * Group-section order (VISUAL_EDITOR §"Structure mirrors the schema"): the schema's `ui.groups` registry
319
313
  * key order when present: `QuillCardUi.groups` is a typed `Record` at the