@quillmark/svelte 0.20.1 → 0.22.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 (85) hide show
  1. package/NOTICE +6 -7
  2. package/README.md +1 -1
  3. package/THEMING.md +1 -1
  4. package/dist/core/address.d.ts +6 -1
  5. package/dist/core/address.js +12 -3
  6. package/dist/core/bloom.d.ts +9 -4
  7. package/dist/core/bloom.js +18 -49
  8. package/dist/core/codec/atoms.d.ts +1 -1
  9. package/dist/core/codec/atoms.js +26 -11
  10. package/dist/core/codec/blocks.d.ts +5 -5
  11. package/dist/core/codec/blocks.js +27 -8
  12. package/dist/core/codec/code.d.ts +4 -4
  13. package/dist/core/codec/code.js +52 -22
  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 -15
  18. package/dist/core/codec/field.js +66 -26
  19. package/dist/core/codec/index.d.ts +1 -1
  20. package/dist/core/codec/index.js +2 -2
  21. package/dist/core/codec/inputrules.d.ts +4 -0
  22. package/dist/core/codec/inputrules.js +44 -6
  23. package/dist/core/codec/link-paste.d.ts +11 -0
  24. package/dist/core/codec/link-paste.js +32 -0
  25. package/dist/core/codec/lists.d.ts +5 -4
  26. package/dist/core/codec/lists.js +87 -31
  27. package/dist/core/codec/nesting.d.ts +20 -0
  28. package/dist/core/codec/nesting.js +62 -0
  29. package/dist/core/codec/prose.css +145 -63
  30. package/dist/core/codec/schema.d.ts +2 -2
  31. package/dist/core/codec/schema.js +71 -18
  32. package/dist/core/codec/slash.js +0 -0
  33. package/dist/core/codec/table-copy.d.ts +17 -0
  34. package/dist/core/codec/table-copy.js +84 -0
  35. package/dist/core/codec/table-view.d.ts +6 -7
  36. package/dist/core/codec/table-view.js +281 -201
  37. package/dist/core/codec/table.d.ts +2 -9
  38. package/dist/core/codec/table.js +10 -19
  39. package/dist/core/codec/tasks.d.ts +19 -0
  40. package/dist/core/codec/tasks.js +140 -0
  41. package/dist/core/errors.d.ts +2 -2
  42. package/dist/core/rungs.d.ts +6 -0
  43. package/dist/core/rungs.js +11 -0
  44. package/dist/core/theme.css +24 -12
  45. package/dist/visual/ArrayField.svelte +236 -78
  46. package/dist/visual/ArrayField.svelte.d.ts +0 -2
  47. package/dist/visual/BooleanField.svelte +12 -1
  48. package/dist/visual/Card.svelte +41 -23
  49. package/dist/visual/Card.svelte.d.ts +3 -2
  50. package/dist/visual/CardControls.svelte +15 -15
  51. package/dist/visual/DateField.svelte +93 -64
  52. package/dist/visual/EnumField.svelte +9 -4
  53. package/dist/visual/Field.svelte +20 -16
  54. package/dist/visual/Field.svelte.d.ts +4 -2
  55. package/dist/visual/FieldHint.svelte +22 -5
  56. package/dist/visual/FieldLabel.svelte +5 -33
  57. package/dist/visual/FieldLabel.svelte.d.ts +0 -2
  58. package/dist/visual/FormatPopover.svelte +78 -81
  59. package/dist/visual/MatrixField.svelte +28 -19
  60. package/dist/visual/NumberField.svelte +32 -3
  61. package/dist/visual/ObjectField.svelte +40 -24
  62. package/dist/visual/ObjectField.svelte.d.ts +6 -0
  63. package/dist/visual/ProseField.svelte +10 -13
  64. package/dist/visual/ProseField.svelte.d.ts +5 -2
  65. package/dist/visual/ProseValue.svelte +25 -19
  66. package/dist/visual/ProseValue.svelte.d.ts +4 -4
  67. package/dist/visual/TextField.svelte +3 -2
  68. package/dist/visual/TipsCard.svelte +7 -7
  69. package/dist/visual/VariantField.svelte +1 -1
  70. package/dist/visual/VariantField.svelte.d.ts +1 -1
  71. package/dist/visual/VisualEditorInner.svelte +121 -39
  72. package/dist/visual/controls.css +21 -2
  73. package/dist/visual/icons/Icon.svelte +3 -2
  74. package/dist/visual/icons/Icon.svelte.d.ts +3 -2
  75. package/dist/visual/icons/nodes.d.ts +25 -21
  76. package/dist/visual/icons/nodes.js +9 -7
  77. package/dist/visual/leaves.d.ts +12 -9
  78. package/dist/visual/leaves.js +19 -0
  79. package/dist/visual/motion.d.ts +25 -3
  80. package/dist/visual/motion.js +58 -9
  81. package/dist/visual/strings.d.ts +15 -9
  82. package/dist/visual/strings.js +19 -12
  83. package/dist/visual/structure.d.ts +21 -31
  84. package/dist/visual/structure.js +54 -51
  85. package/package.json +3 -3
@@ -1,9 +1,9 @@
1
- // The card stack's reorder trip: a slot's position ↔ position in a
2
- // keyed list, the one row of the mechanism table no CSS rule can serve, because the
3
- // two frames belong to different elements. Svelte's `animate:` supplies the halves CSS
4
- // cannot see (the rects either side of the reconciliation) and WAAPI paints the trip,
5
- // the same seam `core/bloom.ts` takes to keep a duration in the derivation while a
6
- // script runs the animation.
1
+ // The card stack's trips: a slot's position ↔ position in a keyed list, the one row of
2
+ // the mechanism table no CSS rule can serve, because the two frames belong to different
3
+ // elements, and a slot entering or leaving the list. Svelte's `animate:` and `in:`/`out:`
4
+ // supply the halves CSS cannot see (the rects either side of the reconciliation, a node
5
+ // the list no longer holds) and WAAPI paints the trip, the same seam `core/bloom.ts` takes
6
+ // to keep a duration in the derivation while a script runs the animation.
7
7
  import { rungMs, rungStyle } from '../core/rungs.js';
8
8
  /** Marks this module's runs, so a second reorder cancels the first rather than layering
9
9
  * a second transform over it. */
@@ -34,9 +34,11 @@ export function reorderArm() {
34
34
  * `armed` is why this is the reorder and not every reconcile: `animate:` fires wherever
35
35
  * a slot's rect moved, and a card growing under the caret moves every slot below it,
36
36
  * which is a layout change with no trip in it and one that would draw those slots over
37
- * the content that displaced them for the length of the run. So the reorder command
38
- * arms the gesture and the frame it lands in disarms it, which is the admission test
39
- * read off the endpoints: two positions in the list, not two positions on the page.
37
+ * the content that displaced them for the length of the run. So a command that moves
38
+ * slots in the list arms the gesture and the frame it lands in disarms it, which is the
39
+ * admission test read off the endpoints: two positions in the list, not two positions on
40
+ * the page. A move is one; so, in the card stack, are an insert and a removal, which move
41
+ * every slot past them by one.
40
42
  *
41
43
  * Both endpoints are rest states, so the curve is `--_qm-ease-reverse` (a second click
42
44
  * lands mid-slide) and the rung is `slow` (the trip moves the stack around it).
@@ -61,3 +63,50 @@ export function reorder(node, { from, to }, armed) {
61
63
  ], { duration, easing, id: RUN_ID });
62
64
  return {};
63
65
  }
66
+ /**
67
+ * A slot entering or leaving the list fades where it stands, while the slots it displaces
68
+ * take the reorder trip: the stack is what slides, and a card is what appears or goes, under
69
+ * the slots crossing it. Opacity alone, because a leaving slot is held at its place by a
70
+ * transform `animate:` writes, which a keyframed transform would override.
71
+ *
72
+ * The returned duration is what keeps a leaving slot mounted until its fade has run, and
73
+ * the fill what keeps it from painting whole in the frame between the two.
74
+ */
75
+ export function slot(node, _params, { direction }) {
76
+ if (typeof node.animate !== 'function')
77
+ return {};
78
+ const style = rungStyle(node);
79
+ const duration = rungMs(style, '--_qm-duration-slow');
80
+ const easing = style.getPropertyValue('--_qm-ease-reverse').trim();
81
+ if (duration === undefined || !easing)
82
+ return {};
83
+ const opacity = direction === 'out' ? [1, 0] : [0, 1];
84
+ node.animate({ opacity }, { duration, easing, fill: direction === 'out' ? 'forwards' : 'none' });
85
+ return { duration };
86
+ }
87
+ /** Frames nothing may scroll in before a scroll counts as over. More than one, because a
88
+ * smooth scroll asked for in this frame takes its first step in a later one. */
89
+ const STILL_FRAMES = 3;
90
+ /** The wait's ceiling, in frames: a scroller a host keeps moving is not a trip that ends. */
91
+ const MAX_FRAMES = 60;
92
+ /**
93
+ * Resolves once nothing in the document has scrolled for {@link STILL_FRAMES} frames
94
+ * running. A smooth scroll moves its scroller on every frame until it lands, so stillness
95
+ * is its end on every engine, whether or not it reports one; a scroll with nowhere to go
96
+ * is still from the first frame.
97
+ */
98
+ export function scrollSettled() {
99
+ return new Promise((resolve) => {
100
+ let still = 0;
101
+ let frames = 0;
102
+ const moved = () => void (still = 0);
103
+ document.addEventListener('scroll', moved, { capture: true, passive: true });
104
+ const frame = () => {
105
+ if (++still < STILL_FRAMES && ++frames < MAX_FRAMES)
106
+ return void requestAnimationFrame(frame);
107
+ document.removeEventListener('scroll', moved, { capture: true });
108
+ resolve();
109
+ };
110
+ requestAnimationFrame(frame);
111
+ });
112
+ }
@@ -15,15 +15,14 @@ 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 reorder pair on an `object` row, accessible names: the buttons are glyphs. */
25
- arrayMoveUp: string;
26
- arrayMoveDown: string;
24
+ /** The drag handle on an `object` row: its tooltip. */
25
+ arrayDrag: string;
27
26
  /**
28
27
  * A row control's accessible name, the action and the row it acts on: `Remove
29
28
  * Authors 2`. The action alone stays the tooltip. Parametric, so word order stays
@@ -42,9 +41,11 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
42
41
  * word order stays the translator's.
43
42
  */
44
43
  elementUntitled: (label: string, index: number) => string;
45
- /** The tag on the unset sentinel's row, which is what tells it from the member it
46
- * ghosts ({@link EnumField}). */
44
+ /** The tag on the unset sentinel's row where it ghosts a member, which is what tells
45
+ * it from that member ({@link EnumField}). */
47
46
  enumUnsetTag: string;
47
+ /** The same row's tag where it ghosts no member: no `default:`, or a blank one. */
48
+ enumUnsetTagNone: string;
48
49
  /** The count in the matrix's label row: how many of its items are held. */
49
50
  matrixHeld: (held: number, total: number) => string;
50
51
  /** An open matrix's add box at rest, naming the list it adds to: `Add to Staff…`. The box
@@ -58,8 +59,6 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
58
59
  /** The date field's toggle, pressed while the field holds `today`: the render
59
60
  * date rather than a written one. */
60
61
  dateToday: string;
61
- /** The required marker's accessible name; the glyph itself is a `*`. */
62
- fieldRequired: string;
63
62
  /** What an unset optional cell (`t?`) ghosts at rest: the `none` it prints
64
63
  * unanswered, in the product's words. */
65
64
  optionalGhost: string;
@@ -74,7 +73,8 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
74
73
  formatStrikethrough: string;
75
74
  formatCode: string;
76
75
  formatLink: string;
77
- /** The anchor button's accessible name. */
76
+ /** The anchor button's accessible name. The popover withholds the button, so
77
+ * nothing draws this or the title below. */
78
78
  formatAnchor: string;
79
79
  /** Its hover title, which says what an anchor is; the name above only says what. */
80
80
  formatAnchorTitle: string;
@@ -137,6 +137,12 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
137
137
  * diagnostic.
138
138
  */
139
139
  export type FormatDiagnostic = (d: Diagnostic) => string | undefined;
140
+ /**
141
+ * A `Mod` chord as the writer's platform prints it: `⌘B` on Apple's, `Ctrl+B`
142
+ * elsewhere. The platform test is the one `prosemirror-keymap` resolves `Mod` by, so a
143
+ * tooltip names the chord the keymap binds.
144
+ */
145
+ export declare function chord(key: string, platform?: string): string;
140
146
  /** The package's English. Every key, so the merge below is total. */
141
147
  export declare const DEFAULT_VISUAL_STRINGS: VisualStrings;
142
148
  /** 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,30 +35,29 @@ 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
- arrayMoveUp: 'Move up',
34
- arrayMoveDown: 'Move down',
41
+ arrayDrag: 'Drag to move',
35
42
  arrayRowAction: (action, row) => `${action} ${row}`,
36
43
  arrayCount: (count, max) => `${count} / ${max}`,
37
44
  elementUntitled: (label, index) => `${label} ${index}`,
38
- enumUnsetTag: 'default',
45
+ enumUnsetTag: 'Default',
46
+ enumUnsetTagNone: 'Unset',
39
47
  matrixHeld: (held, total) => `${held} of ${total} held`,
40
48
  matrixAdd: (list) => (list ? `Add to ${list}…` : 'Add…'),
41
49
  matrixItemTitle: (list, index) => `${list ? `${list} item` : 'Item'} ${index}`,
42
50
  matrixRemove: (title) => `Remove ${title}`,
43
51
  dateToday: 'Today',
44
- fieldRequired: 'required',
45
52
  optionalGhost: 'None',
46
53
  proseHeld: 'Holds more than this field can edit here.',
47
54
  formatGroup: 'Formatting',
48
- formatBold: 'Bold (Mod-B)',
49
- formatEmphasis: 'Emphasis (Mod-I)',
50
- formatUnderline: 'Underline (Mod-U)',
55
+ formatBold: `Bold (${chord('B')})`,
56
+ formatEmphasis: `Italic (${chord('I')})`,
57
+ formatUnderline: `Underline (${chord('U')})`,
51
58
  formatStrikethrough: 'Strikethrough',
52
59
  formatCode: 'Code',
53
- formatLink: 'Link',
60
+ formatLink: `Link (${chord('K')})`,
54
61
  formatAnchor: 'Anchor',
55
62
  formatAnchorTitle: 'Anchor — an identity handle over the selection',
56
63
  linkPlaceholder: 'https://…',
@@ -61,7 +68,7 @@ export const DEFAULT_VISUAL_STRINGS = {
61
68
  tipsDismiss: 'Dismiss',
62
69
  tipsNext: 'Next',
63
70
  tipsDot: (index, count) => `Tip ${index} of ${count}`,
64
- unknownKind: (kind) => `Unrecognized card type ${kind}. Its content is preserved.`,
71
+ unknownKind: (kind) => `Unknown card type ${kind}. Its content is kept.`,
65
72
  retypeLabel: 'Change to',
66
73
  retypePlaceholder: 'Choose a type…',
67
74
  noCardKinds: 'This document declares no card types — delete this card to remove it.',
@@ -11,22 +11,17 @@ export interface FieldModel {
11
11
  control: ControlKind;
12
12
  /** `ui.group` (undefined = ungrouped). */
13
13
  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`). */
14
+ /** Asks to share a row with adjacent compacts: `ui.compact` when declared, else
15
+ * {@link packsByDefault}. A request, not a guarantee: `placeFields` declines it for
16
+ * the shapes that grow (see `packable`). */
16
17
  compact: boolean;
17
18
  /** Display label: `title` when set, else the humanized field name. */
18
19
  label: string;
19
20
  /** Schema `description`: authoring help rendered beside the label,
20
21
  * undefined when the field declares none. Chrome-only; never gates. */
21
22
  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). */
23
+ /** Prose-leaf flags (only meaningful when `control === 'prose'` / array items):
24
+ * one line (`oneLine`), and the literal codec (`literalText`). */
30
25
  inline: boolean;
31
26
  plaintext: boolean;
32
27
  }
@@ -161,6 +156,11 @@ export declare function optionalCell(f: QuillFieldSchema): boolean;
161
156
  /** The type a cell's control reads: the declared one, an optional cell's `?` stripped.
162
157
  * The `?` moves the render floor and nothing else, so every read of a type is this. */
163
158
  export declare function baseType(f: QuillFieldSchema): QuillFieldType;
159
+ /** Whether a content field is one line: one paragraph, no container, no island. A `text`
160
+ * or `richtext` field is one line unless it declares `multiline: true`. */
161
+ export declare function oneLine(f: QuillFieldSchema): boolean;
162
+ /** Whether a content field's codec is the literal one, its value text with no marks: `text`. */
163
+ export declare function literalText(f: QuillFieldSchema): boolean;
164
164
  /**
165
165
  * A scalar `default:` as the text an unset control holds, where it prints: as
166
166
  * declared, `undefined` for none, a blank or a non-scalar. A default of spaces prints
@@ -169,19 +169,18 @@ export declare function baseType(f: QuillFieldSchema): QuillFieldType;
169
169
  export declare function printedText(v: unknown): string | undefined;
170
170
  /**
171
171
  * 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
172
+ * imported, a `text` one's text taken literally, one paragraph whose later lines
173
173
  * continue it, as the literal codec reads a stored one. What a cell's static
174
174
  * `default:` stands in its leaf as, where no resolved row reaches it. `undefined` for
175
175
  * a blank or a non-scalar.
176
176
  */
177
177
  export declare function declaredContent(v: unknown, markdown: boolean): Content | undefined;
178
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. */
179
+ * {@link oneLine} and its elements'. A retype or a quill swap that changes any of them
180
+ * under a name remounts the field, where a kept leaf would commit on the old schema. */
181
181
  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. */
182
+ /** Map a field schema to its control. An array's element control is this over `items`;
183
+ * a type this build does not know takes the text input. */
185
184
  export declare function controlKind(f: QuillFieldSchema): ControlKind;
186
185
  /** The discriminant cell of a variant container, off the boundary's own constant: it
187
186
  * crosses inside untyped container data with no type to read it off, so a literal here
@@ -254,8 +253,8 @@ export declare function addedItems(matrix: QuillFieldSchema, map: Record<string,
254
253
  */
255
254
  export declare function addedId(title: string, taken: (id: string) => boolean): string;
256
255
  /** 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. */
256
+ * added item's `title`, a one-line `text` cell, beside them. Composed here because the
257
+ * boundary serializes the roster and the columns alone. */
259
258
  export declare function matrixMemberSchema(matrix: QuillFieldSchema, id: string): QuillFieldSchema;
260
259
  /**
261
260
  * The schema at `steps` under `field`, or `undefined` where the schema declares no such
@@ -267,8 +266,7 @@ export declare function matrixMemberSchema(matrix: QuillFieldSchema, id: string)
267
266
  */
268
267
  export declare function schemaAt(field: QuillFieldSchema, steps: readonly PathStep[]): QuillFieldSchema | undefined;
269
268
  /** 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. */
269
+ * summary can hold. A prose cell is one where it is {@link oneLine}. */
272
270
  export declare function shortCell(sub: QuillFieldSchema): boolean;
273
271
  /** A collapsed row's own words ({@link firstShortText}); `undefined` while the row has
274
272
  * nothing to say for itself. */
@@ -301,19 +299,11 @@ export declare function variantCells(schema: QuillFieldSchema, member: string |
301
299
  * `undefined`.
302
300
  */
303
301
  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
302
  /** `foo_bar` → `Foo bar`: the label fallback when a field declares no `title`. */
314
303
  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[];
304
+ /** Project a card schema's `fields` map (declaration = key order) into models. A
305
+ * card-level object's `properties` project the same way. */
306
+ export declare function fieldModels(cardSchema: Pick<QuillCardSchema, 'fields'>): FieldModel[];
317
307
  /**
318
308
  * Group-section order (VISUAL_EDITOR §"Structure mirrors the schema"): the schema's `ui.groups` registry
319
309
  * key order when present: `QuillCardUi.groups` is a typed `Record` at the
@@ -84,6 +84,15 @@ export function optionalCell(f) {
84
84
  export function baseType(f) {
85
85
  return (optionalCell(f) ? f.type.slice(0, -1) : f.type);
86
86
  }
87
+ /** Whether a content field is one line: one paragraph, no container, no island. A `text`
88
+ * or `richtext` field is one line unless it declares `multiline: true`. */
89
+ export function oneLine(f) {
90
+ return f.multiline !== true;
91
+ }
92
+ /** Whether a content field's codec is the literal one, its value text with no marks: `text`. */
93
+ export function literalText(f) {
94
+ return baseType(f) === 'text';
95
+ }
87
96
  /**
88
97
  * A scalar `default:` as the text an unset control holds, where it prints: as
89
98
  * declared, `undefined` for none, a blank or a non-scalar. A default of spaces prints
@@ -95,7 +104,7 @@ export function printedText(v) {
95
104
  }
96
105
  /**
97
106
  * A declared content literal as the `Content` it holds: a `richtext` one's markdown
98
- * imported, a `plaintext` one's text taken literally, one paragraph whose later lines
107
+ * imported, a `text` one's text taken literally, one paragraph whose later lines
99
108
  * continue it, as the literal codec reads a stored one. What a cell's static
100
109
  * `default:` stands in its leaf as, where no resolved row reaches it. `undefined` for
101
110
  * a blank or a non-scalar.
@@ -118,25 +127,23 @@ export function declaredContent(v, markdown) {
118
127
  };
119
128
  }
120
129
  /** A field's `{#each}` key: its name and what a mounted leaf reads once, its type and
121
- * `inline` and its elements'. A retype or a quill swap that changes any of them under a
122
- * name remounts the field, where a kept leaf would commit on the old schema. */
130
+ * {@link oneLine} and its elements'. A retype or a quill swap that changes any of them
131
+ * under a name remounts the field, where a kept leaf would commit on the old schema. */
123
132
  export function mountKey(name, f) {
124
- return `${name}\u0000${f.type}\u0000${f.inline === true}\u0000${f.items?.type ?? ''}\u0000${f.items?.inline === true}`;
133
+ const items = f.items;
134
+ return `${name}\u0000${f.type}\u0000${oneLine(f)}\u0000${items?.type ?? ''}\u0000${items ? oneLine(items) : ''}`;
125
135
  }
126
- /** Map a field schema to its control (precedence: prose › enum › text › …).
127
- * An array's element control is this over `items`: a missing `items` is a text
128
- * element. */
136
+ /** Map a field schema to its control. An array's element control is this over `items`;
137
+ * a type this build does not know takes the text input. */
129
138
  export function controlKind(f) {
130
139
  switch (baseType(f)) {
131
140
  case 'richtext':
132
- case 'plaintext':
141
+ case 'text':
133
142
  return 'prose';
134
143
  case 'enum':
135
144
  // `variants:` is the one key that changes a field's resting shape (canon
136
145
  // `SCHEMAS.md`): with it the field rests as a container, without it a bare string.
137
146
  return f.variants ? 'variant' : 'enum';
138
- case 'string':
139
- return 'text';
140
147
  case 'number':
141
148
  case 'integer':
142
149
  return 'number';
@@ -300,13 +307,13 @@ export function addedId(title, taken) {
300
307
  return `${base}_${n}`;
301
308
  }
302
309
  /** The `object` a matrix member is, for the schema walk: the declared columns, and an
303
- * added item's `title` beside them. Composed here because the boundary serializes the
304
- * roster and the columns alone. */
310
+ * added item's `title`, a one-line `text` cell, beside them. Composed here because the
311
+ * boundary serializes the roster and the columns alone. */
305
312
  export function matrixMemberSchema(matrix, id) {
306
313
  const columns = matrix.properties ?? {};
307
314
  return {
308
315
  type: 'object',
309
- properties: onRoster(matrix, id) ? columns : { [MATRIX_TITLE]: { type: 'string' }, ...columns }
316
+ properties: onRoster(matrix, id) ? columns : { [MATRIX_TITLE]: { type: 'text' }, ...columns }
310
317
  };
311
318
  }
312
319
  // ── The schema walk ──────────────────────────────────────────────────────────
@@ -330,7 +337,7 @@ export function schemaAt(field, steps) {
330
337
  /** What an array declaring no `items` holds, the walk's copy of the element control
331
338
  * {@link controlKind} names for one: a text element, so an index into such an array is
332
339
  * a place and not a refusal. */
333
- const UNDECLARED_ELEMENT = { type: 'string' };
340
+ const UNDECLARED_ELEMENT = { type: 'text' };
334
341
  function stepInto(schema, step) {
335
342
  switch (controlKind(schema)) {
336
343
  case 'array':
@@ -361,17 +368,16 @@ function stepInto(schema, step) {
361
368
  }
362
369
  // ── Row summaries and layouts ────────────────────────────────────────────────
363
370
  /** Whether a cell is one line tall by declaration: the shape a table column and a row
364
- * summary can hold. A prose cell is one where it declares `inline`, `richtext` or
365
- * `plaintext` alike. */
371
+ * summary can hold. A prose cell is one where it is {@link oneLine}. */
366
372
  export function shortCell(sub) {
367
373
  const kind = controlKind(sub);
368
374
  if (kind === 'prose')
369
- return !!sub.inline;
375
+ return oneLine(sub);
370
376
  return (kind === 'text' || kind === 'enum' || kind === 'number' || kind === 'boolean' || kind === 'date');
371
377
  }
372
378
  /**
373
- * The first short text cell's words — the first `string`, or inline `richtext` /
374
- * `plaintext`, in declaration order — read through {@link titleText}; `undefined` where
379
+ * The first short text cell's words — the first one-line `text` or `richtext`, in
380
+ * declaration order — read through {@link titleText}; `undefined` where
375
381
  * there is no such cell or it is empty. The cell is picked by the schema alone, so an
376
382
  * instance is named by one cell whatever the others hold. What names an instance from
377
383
  * its own values: a collapsed row, and a card whose kind declares no `title`.
@@ -435,39 +441,36 @@ export function commitDiscriminant(value, member) {
435
441
  delete next[VARIANT_DISCRIMINANT];
436
442
  return Object.keys(next).length ? next : undefined;
437
443
  }
438
- /**
439
- * Whether the schema obliges a cell: `default:`'s absence, which is the whole of the
440
- * obligation (DOCUMENT_MODEL). Exempt are the cells whose absence of one says nothing:
441
- * a typed dictionary, since a namespace declares no `default:` at all and `validate`
442
- * anchors obligation on the leaves under it; a matrix, whose blank is nothing held, a
443
- * whole answer; and an optional cell, which can declare none, its unanswered render
444
- * being `none`.
445
- */
446
- export function obliged(schema) {
447
- return (schema.type !== 'object' &&
448
- schema.type !== 'matrix' &&
449
- !optionalCell(schema) &&
450
- schema.default === undefined);
451
- }
452
444
  /** `foo_bar` → `Foo bar`: the label fallback when a field declares no `title`. */
453
445
  export function humanize(name) {
454
446
  const spaced = name.replace(/_/g, ' ').trim();
455
447
  return spaced ? spaced[0].toUpperCase() + spaced.slice(1) : spaced;
456
448
  }
457
- /** Project a card schema's `fields` map (declaration = key order) into models. */
449
+ /**
450
+ * Whether a field asks to share a row when its `ui.compact` is undeclared: a control
451
+ * whose type bounds its width. Text never does, its type saying nothing of whether a
452
+ * value is a rank or a subject line, so a short text field asks with `compact: true`.
453
+ */
454
+ function packsByDefault(control) {
455
+ return control === 'date' || control === 'enum' || control === 'number' || control === 'boolean';
456
+ }
457
+ /** Project a card schema's `fields` map (declaration = key order) into models. A
458
+ * card-level object's `properties` project the same way. */
458
459
  export function fieldModels(cardSchema) {
459
- return Object.entries(cardSchema.fields).map(([name, schema]) => ({
460
- name,
461
- schema,
462
- control: controlKind(schema),
463
- group: schema.ui?.group,
464
- compact: !!schema.ui?.compact,
465
- label: schema.title ?? humanize(name),
466
- description: schema.description,
467
- required: obliged(schema),
468
- inline: !!schema.inline,
469
- plaintext: baseType(schema) === 'plaintext'
470
- }));
460
+ return Object.entries(cardSchema.fields).map(([name, schema]) => {
461
+ const control = controlKind(schema);
462
+ return {
463
+ name,
464
+ schema,
465
+ control,
466
+ group: schema.ui?.group,
467
+ compact: schema.ui?.compact ?? packsByDefault(control),
468
+ label: schema.title ?? humanize(name),
469
+ description: schema.description,
470
+ inline: oneLine(schema),
471
+ plaintext: literalText(schema)
472
+ };
473
+ });
471
474
  }
472
475
  /**
473
476
  * Group-section order (VISUAL_EDITOR §"Structure mirrors the schema"): the schema's `ui.groups` registry
@@ -543,12 +546,12 @@ export function initialExpandedGroup(sections) {
543
546
  return sections.find((s) => s.group != null)?.group ?? null;
544
547
  }
545
548
  /**
546
- * Whether a field can share a row. `ui.compact` asks; a shape declines when the
549
+ * Whether a field can share a row. `compact` asks; a shape declines when the
547
550
  * document sets its height, because a row is as tall as its tallest cell and the cell
548
- * beside it does not grow in step: an object nests a whole field set, block richtext
549
- * (`inline` absent) holds paragraphs, and an array holds however many elements the
550
- * document carries. Any of them stands its neighbour in a column of whitespace. An
551
- * inline prose leaf is one line tall and packs like any scalar.
551
+ * beside it does not grow in step: an object nests a whole field set, a multiline prose
552
+ * leaf holds paragraphs or lines, and an array holds however many elements the
553
+ * document carries. Any of them stands its neighbour in a column of whitespace. A
554
+ * one-line prose leaf packs like any scalar.
552
555
  *
553
556
  * A variant declines for the same reason one step further in: its height is the live
554
557
  * world's cell count, so it is set by a pick rather than only by the document, and a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quillmark/svelte",
3
- "version": "0.20.1",
3
+ "version": "0.22.0",
4
4
  "description": "Editor, live-preview and source surfaces over a Quillmark WASM session.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,7 +23,7 @@
23
23
  "author": "Quillmark Contributors",
24
24
  "type": "module",
25
25
  "engines": {
26
- "node": ">=24"
26
+ "node": ">=22"
27
27
  },
28
28
  "svelte": "./dist/index.js",
29
29
  "types": "./dist/index.d.ts",
@@ -69,7 +69,7 @@
69
69
  "check": "svelte-check --tsconfig ./tsconfig.json --fail-on-warnings"
70
70
  },
71
71
  "peerDependencies": {
72
- "@quillmark/wasm": ">=0.124.0-0",
72
+ "@quillmark/wasm": ">=0.126.0-0",
73
73
  "svelte": "^5.33.0"
74
74
  },
75
75
  "dependencies": {