@quillmark/svelte 0.26.2 → 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 (97) hide show
  1. package/NOTICE +4 -4
  2. package/README.md +26 -8
  3. package/THEMING.md +40 -4
  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 +71 -0
  51. package/dist/preset/index.css +8 -7
  52. package/dist/preset/recipes.css +19 -164
  53. package/dist/preset/scale.css +28 -18
  54. package/dist/preset/shell.css +157 -0
  55. package/dist/preview/Preview.svelte +8 -0
  56. package/dist/preview/bridge.d.ts +18 -22
  57. package/dist/preview/bridge.js +29 -58
  58. package/dist/preview/controller.d.ts +21 -29
  59. package/dist/preview/controller.js +14 -39
  60. package/dist/preview/geometry.d.ts +13 -34
  61. package/dist/preview/geometry.js +13 -34
  62. package/dist/preview/index.js +4 -16
  63. package/dist/preview/paint.d.ts +3 -3
  64. package/dist/preview/paint.js +28 -68
  65. package/dist/preview/strings.d.ts +1 -1
  66. package/dist/preview/strings.js +2 -14
  67. package/dist/preview/viewport.js +20 -15
  68. package/dist/preview/zoom.d.ts +21 -22
  69. package/dist/preview/zoom.js +53 -30
  70. package/dist/visual/ArrayField.svelte +77 -16
  71. package/dist/visual/BooleanField.svelte +28 -15
  72. package/dist/visual/Card.svelte +66 -51
  73. package/dist/visual/CardControls.svelte +5 -11
  74. package/dist/visual/DateField.svelte +20 -4
  75. package/dist/visual/EnumField.svelte +3 -0
  76. package/dist/visual/FieldHint.svelte +2 -1
  77. package/dist/visual/FormatPopover.svelte +82 -15
  78. package/dist/visual/FormatPopover.svelte.d.ts +4 -0
  79. package/dist/visual/MatrixAdd.svelte +30 -12
  80. package/dist/visual/MatrixField.svelte +24 -16
  81. package/dist/visual/NumberField.svelte +7 -0
  82. package/dist/visual/ObjectField.svelte +4 -4
  83. package/dist/visual/ProseField.svelte +0 -5
  84. package/dist/visual/TipsCard.svelte +11 -10
  85. package/dist/visual/VariantField.svelte +3 -1
  86. package/dist/visual/VisualEditorInner.svelte +19 -3
  87. package/dist/visual/controls.css +7 -2
  88. package/dist/visual/fonts.d.ts +18 -0
  89. package/dist/visual/fonts.js +59 -0
  90. package/dist/visual/icons/nodes.d.ts +0 -5
  91. package/dist/visual/icons/nodes.js +0 -4
  92. package/dist/visual/props.d.ts +14 -1
  93. package/dist/visual/strings.d.ts +7 -0
  94. package/dist/visual/strings.js +5 -2
  95. package/dist/visual/structure.d.ts +43 -87
  96. package/dist/visual/structure.js +42 -83
  97. package/package.json +2 -2
@@ -1,9 +1,5 @@
1
- // The schema × payload join, done by the editor (VISUAL_EDITOR §Structure). Pure
2
- // functions only (no runes, no Document reads) so the ordering, control
3
- // dispatch, group layout, titles, ghosts, and session-identity bookkeeping
4
- // are unit-testable in isolation (tests/visual/structure.test.ts). The reactive
5
- // orchestration (revision counter, live doc reads) lives in VisualEditor.svelte;
6
- // this module is the projection math it feeds.
1
+ // The schema × payload join, as pure functions (VISUAL_EDITOR §Structure). The reactive
2
+ // orchestration lives in VisualEditor.svelte.
7
3
  import { VARIANT_DISCRIMINANT_KEY } from '@quillmark/wasm';
8
4
  import { core } from '../core/lifecycle.js';
9
5
  /** The container controls: the ones a nested address walks into and a deep
@@ -11,25 +7,15 @@ import { core } from '../core/lifecycle.js';
11
7
  export function isContainer(kind) {
12
8
  return kind === 'array' || kind === 'object' || kind === 'variant' || kind === 'matrix';
13
9
  }
14
- /**
15
- * A card's resolved rows keyed by field name: the provenance channel parallel to
16
- * {@link CardModel.values}. `resolve` returns rows in declaration order; the
17
- * editor keys them the same way `values` keys its payload walk, so a field's value
18
- * and its provenance resolve under one name.
19
- */
10
+ /** A card's resolved rows keyed by field name, parallel to {@link CardModel.values}. */
20
11
  export function provenanceMap(fields) {
21
12
  return Object.fromEntries(fields.map((f) => [f.name, f]));
22
13
  }
23
- /** The empty rows a card with no resolve entry reads: a shared constant so the
24
- * per-card miss allocates nothing. */
14
+ /** Shared so a per-card miss allocates nothing. */
25
15
  export const NO_RESOLVED_ROWS = { fields: [], body: null };
26
16
  /**
27
- * Composable cards' resolved rows keyed by document index (`ResolvedCard.index`,
28
- * not array position, so it holds whatever order the resolve view returns): built
29
- * once per derive, so the per-card provenance join is O(1). Both channels ride one
30
- * entry: a card reads its fields and its body from a single lookup. Empty map when
31
- * `resolved` is absent (a `resolve` failure degrades to no ghosts, never a blank
32
- * form).
17
+ * Resolved rows keyed by `ResolvedCard.index`, not array position. Empty when `resolved` is absent:
18
+ * a `resolve` failure degrades to no ghosts, never a blank form.
33
19
  */
34
20
  export function resolvedByCardIndex(resolved) {
35
21
  return new Map((resolved?.cards ?? []).map((c) => [c.index, { fields: c.fields, body: c.body }]));
@@ -40,9 +26,10 @@ export function resolvedByCardIndex(resolved) {
40
26
  export function ghostDefault(row) {
41
27
  return row?.source === 'default' ? row.value : undefined;
42
28
  }
43
- /** A stored `boolean` cell as the render reads it: a boolean, `true` or `false` in any
44
- * case, or a number by whether it is non-zero; undefined where the render refuses the
45
- * value. Quillmark's coercion, restated, so the suite holds it to `reader.resolve()`. */
29
+ /**
30
+ * A stored `boolean` cell as the render reads it (Quillmark's coercion, held to `reader.resolve()`
31
+ * by the suite); undefined where the render refuses it.
32
+ */
46
33
  export function renderedBoolean(value) {
47
34
  if (typeof value === 'boolean')
48
35
  return value;
@@ -52,10 +39,10 @@ export function renderedBoolean(value) {
52
39
  return value.toLowerCase() === 'true';
53
40
  return undefined;
54
41
  }
55
- /** A ghost value's string form, or undefined for null/object (only text ghosts
56
- * render a placeholder). A scalar's resolved default reads through it; a content
57
- * field's resolves as `Content`, which a prose leaf reads by its text instead
58
- * ({@link titleText}). */
42
+ /**
43
+ * A ghost value's string form, or undefined for null/object. A content field's default resolves as
44
+ * `Content` and is read by its text ({@link titleText}).
45
+ */
59
46
  export function stringifyGhost(ghost) {
60
47
  return ghost != null && typeof ghost !== 'object' ? String(ghost) : undefined;
61
48
  }
@@ -74,8 +61,10 @@ export function resolveBodyGhost(resolvedDefault, custom, builtIn) {
74
61
  export function optionalCell(f) {
75
62
  return f.type.endsWith('?');
76
63
  }
77
- /** The type a cell's control reads: the declared one, an optional cell's `?` stripped.
78
- * The `?` moves the render floor and nothing else, so every read of a type is this. */
64
+ /**
65
+ * The declared type with an optional cell's `?` stripped; the `?` moves the render floor and
66
+ * nothing else.
67
+ */
79
68
  export function baseType(f) {
80
69
  return (optionalCell(f) ? f.type.slice(0, -1) : f.type);
81
70
  }
@@ -84,7 +73,7 @@ export function baseType(f) {
84
73
  export function oneLine(f) {
85
74
  return f.multiline !== true;
86
75
  }
87
- /** Whether a content field's codec is the literal one, its value text with no marks: `text`. */
76
+ /** Whether a content field's codec is the literal one: `text`. */
88
77
  export function literalText(f) {
89
78
  return baseType(f) === 'text';
90
79
  }
@@ -143,8 +132,7 @@ export function mountKey(name, f) {
143
132
  items?.max ?? ''
144
133
  ].join('\u0000');
145
134
  }
146
- /** Map a field schema to its control. An array's element control is this over `items`;
147
- * a type this build does not know takes the text input. */
135
+ /** An array's element control is this over `items`; an unknown type takes the text input. */
148
136
  export function controlKind(f) {
149
137
  switch (baseType(f)) {
150
138
  case 'richtext':
@@ -260,7 +248,6 @@ export function memberWrite(columns, title) {
260
248
  export function matrixId(id) {
261
249
  return /^[a-z][a-z0-9_]*$/.test(id);
262
250
  }
263
- /** Whether `id` is on the roster. */
264
251
  export function onRoster(matrix, id) {
265
252
  return matrix.members != null && Object.hasOwn(matrix.members, id);
266
253
  }
@@ -377,8 +364,9 @@ function stepInto(schema, step) {
377
364
  }
378
365
  }
379
366
  // ── Row summaries and layouts ────────────────────────────────────────────────
380
- /** Whether a cell is one line tall by declaration: the shape a table column and a row
381
- * summary can hold. A prose cell is one where it is {@link oneLine}. */
367
+ /**
368
+ * Whether a cell is one line tall by declaration: what a table column and a row summary can hold.
369
+ */
382
370
  export function shortCell(sub) {
383
371
  const kind = controlKind(sub);
384
372
  if (kind === 'prose')
@@ -386,11 +374,8 @@ export function shortCell(sub) {
386
374
  return (kind === 'text' || kind === 'enum' || kind === 'number' || kind === 'boolean' || kind === 'date');
387
375
  }
388
376
  /**
389
- * The first short text cell's words — the first one-line `text` or `richtext`, in
390
- * declaration order — read through {@link titleText}; `undefined` where
391
- * there is no such cell or it is empty. The cell is picked by the schema alone, so an
392
- * instance is named by one cell whatever the others hold. What names an instance from
393
- * its own values: a collapsed row, and a card whose kind declares no `title`.
377
+ * The words of the first one-line `text` or `richtext` cell in declaration order, picked by schema
378
+ * alone so one cell names an instance whatever the others hold.
394
379
  */
395
380
  function firstShortText(cells, values) {
396
381
  const named = Object.entries(cells ?? {}).find(([, sub]) => {
@@ -401,8 +386,7 @@ function firstShortText(cells, values) {
401
386
  return undefined;
402
387
  return titleText(ownValue(values, named[0])).trim() || undefined;
403
388
  }
404
- /** A collapsed row's own words ({@link firstShortText}); `undefined` while the row has
405
- * nothing to say for itself. */
389
+ /** A collapsed row's own words ({@link firstShortText}). */
406
390
  export function rowSummary(items, row) {
407
391
  return firstShortText(items?.properties, (row ?? {}));
408
392
  }
@@ -451,7 +435,7 @@ export function commitDiscriminant(value, member) {
451
435
  delete next[VARIANT_DISCRIMINANT];
452
436
  return Object.keys(next).length ? next : undefined;
453
437
  }
454
- /** `foo_bar` → `Foo bar`: the label fallback when a field declares no `title`. */
438
+ /** `foo_bar` → `Foo bar`. */
455
439
  export function humanize(name) {
456
440
  const spaced = name.replace(/_/g, ' ').trim();
457
441
  return spaced ? spaced[0].toUpperCase() + spaced.slice(1) : spaced;
@@ -483,10 +467,8 @@ export function fieldModels(cardSchema) {
483
467
  });
484
468
  }
485
469
  /**
486
- * Group-section order (VISUAL_EDITOR §"Structure mirrors the schema"): the schema's `ui.groups` registry
487
- * key order when present: `QuillCardUi.groups` is a typed `Record` at the
488
- * boundary, so this reads it uncast: else the first-appearance order of each
489
- * field's `ui.group`.
470
+ * Group-section order (VISUAL_EDITOR §"Structure mirrors the schema"): the `ui.groups` key order,
471
+ * else first appearance of each field's `ui.group`.
490
472
  */
491
473
  export function groupOrder(cardSchema) {
492
474
  const groups = cardSchema.ui?.groups;
@@ -500,20 +482,13 @@ export function groupOrder(cardSchema) {
500
482
  }
501
483
  return order;
502
484
  }
503
- /**
504
- * A group's display label: the `ui.groups[g].title` override when the registry
505
- * declares one, else the humanized id (`memo_for` → "Memo for"): the same
506
- * id-derives-the-label rule a field's key follows.
507
- */
485
+ /** A group's `ui.groups[g].title`, else the humanized id. */
508
486
  export function groupLabel(cardSchema, group) {
509
487
  return cardSchema.ui?.groups?.[group]?.title ?? humanize(group);
510
488
  }
511
489
  /**
512
- * Sort field models into ordered group sections. Declared groups first (in
513
- * `order`), each carrying its fields in declaration order; then any remaining
514
- * groups (including the ungrouped bucket) in first-appearance order. `labelFor`
515
- * resolves a group id to its display label: always {@link groupLabel} bound to
516
- * the card schema, which falls back to {@link humanize} itself.
490
+ * Declared groups first (in `order`), then any remaining groups, including the ungrouped bucket, in
491
+ * first-appearance order.
517
492
  */
518
493
  export function groupSections(fields, order, labelFor) {
519
494
  const sections = [];
@@ -568,18 +543,9 @@ export function cardSections(cardSchema) {
568
543
  return [...panels, ...groupSections(grouped, order, (g) => groupLabel(cardSchema, g))];
569
544
  }
570
545
  /**
571
- * The group section a card's accordion opens on first mount: the first in
572
- * order, or `null` when the card declares none. Ungrouped fields render outside
573
- * the accordion, so only `group != null` sections count.
574
- *
575
- * A card opens on fields. All-collapsed paints as a stack of chevrons that name
576
- * their sections and disclose nothing of what is behind them, and a body leaf is
577
- * no substitute: on a card carrying both, the fields are what the card is for.
578
- * The first section is the one `ui.groups` already ranks highest, and it is the
579
- * same section on every mount: a rule keyed on document state would move the
580
- * opening under a user as they fill.
581
- *
582
- * State is ephemeral session state the Card owns; this only seeds it.
546
+ * The group section a card's accordion opens on first mount, or `null` when the card declares none.
547
+ * All-collapsed would disclose nothing of what is behind the chevrons, and a rule keyed on document
548
+ * state would move the opening under a user as they fill.
583
549
  */
584
550
  export function initialExpandedGroup(sections) {
585
551
  return sections.find((s) => s.group != null)?.group ?? null;
@@ -613,15 +579,9 @@ function packable(f) {
613
579
  return f.control !== 'prose' || f.inline;
614
580
  }
615
581
  /**
616
- * Assign each field its span in the section grid. Consecutive packable fields are
617
- * `cell`s the grid auto-places: capacity and wrapping are the container query's
618
- * business, so a trailing orphan keeps its column width rather than growing to fill.
619
- * A run of one is stranded — nothing packed beside it, and no row above to align to —
620
- * so it is `full` like the shapes that declined: a lone narrow track reads as truncated
621
- * against the width it leaves empty, and sharing was the whole of what asked for it.
622
- * Everything else is `full`.
623
- *
624
- * Pure, and stays pure: no width, no measurement, nothing to re-derive on resize.
582
+ * Consecutive packable fields are `cell`s the grid auto-places; a trailing orphan keeps its column
583
+ * width. A run of one is `full`: a lone narrow track reads as truncated against the width it leaves
584
+ * empty. Everything else is `full`. No width is measured.
625
585
  */
626
586
  export function placeFields(fields) {
627
587
  const out = [];
@@ -693,7 +653,6 @@ export function cardTitle(cardSchema, kind, values, extTitle) {
693
653
  return t;
694
654
  return firstShortText(cardSchema?.fields, values) ?? humanize(kind);
695
655
  }
696
- /** Whether a card kind renders a body leaf: gated by `body.enabled !== false`. */
697
656
  export function bodyEnabled(cardSchema) {
698
657
  return cardSchema?.body?.enabled !== false;
699
658
  }
@@ -702,10 +661,10 @@ export function bodyEnabled(cardSchema) {
702
661
  // so a stable card-instance key cannot be the card object (a fresh object every
703
662
  // derive): it is a session id held in a parallel array, reordered in lockstep
704
663
  // with the structure ops and resolved to an index only at the mutation boundary.
705
- /** A monotonic per-session id source. Ids are opaque strings, stable for the
706
- * session; a fresh array for N existing instances is `seq.take(N)`, and an id
707
- * resolves to its current index by `ids.indexOf(id)` (-1 once the instance is
708
- * gone): read at the mutation boundary, never cached. */
664
+ /**
665
+ * A monotonic per-session id source of opaque strings. An id resolves to its index by
666
+ * `ids.indexOf(id)` at the mutation boundary, never cached.
667
+ */
709
668
  export class IdSeq {
710
669
  #n = 0;
711
670
  next() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quillmark/svelte",
3
- "version": "0.26.2",
3
+ "version": "0.28.0",
4
4
  "description": "Editor, live-preview and source surfaces over a Quillmark WASM session.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -69,7 +69,7 @@
69
69
  "check": "svelte-check --tsconfig ./tsconfig.json --fail-on-warnings"
70
70
  },
71
71
  "peerDependencies": {
72
- "@quillmark/wasm": ">=0.129.0-0",
72
+ "@quillmark/wasm": ">=0.133.0-0",
73
73
  "svelte": "^5.33.0"
74
74
  },
75
75
  "dependencies": {