@quillmark/svelte 0.5.0 → 0.6.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 (56) hide show
  1. package/README.md +8 -2
  2. package/THEMING.md +22 -27
  3. package/dist/core/address.d.ts +17 -3
  4. package/dist/core/address.js +20 -3
  5. package/dist/core/codec/blocks.d.ts +37 -0
  6. package/dist/core/codec/blocks.js +163 -0
  7. package/dist/core/codec/field.js +3 -5
  8. package/dist/core/codec/index.d.ts +1 -1
  9. package/dist/core/codec/index.js +3 -2
  10. package/dist/core/codec/inputrules.js +24 -144
  11. package/dist/core/codec/prose.css +14 -25
  12. package/dist/core/codec/schema.d.ts +8 -0
  13. package/dist/core/codec/schema.js +33 -1
  14. package/dist/core/codec/slash.d.ts +18 -16
  15. package/dist/core/codec/slash.js +0 -0
  16. package/dist/core/codec/table-view.js +127 -157
  17. package/dist/core/codec/table.d.ts +0 -10
  18. package/dist/core/codec/table.js +0 -15
  19. package/dist/core/theme.css +350 -347
  20. package/dist/preset/recipes.css +10 -21
  21. package/dist/preset/scale.css +6 -7
  22. package/dist/preview/Preview.svelte +4 -2
  23. package/dist/preview/geometry.d.ts +10 -9
  24. package/dist/preview/geometry.js +11 -13
  25. package/dist/visual/ArrayField.svelte +242 -63
  26. package/dist/visual/ArrayField.svelte.d.ts +5 -1
  27. package/dist/visual/Card.svelte +21 -2
  28. package/dist/visual/DiagnosticList.svelte +6 -11
  29. package/dist/visual/EnumField.svelte +33 -1
  30. package/dist/visual/Field.svelte +29 -5
  31. package/dist/visual/FieldLabel.svelte +2 -2
  32. package/dist/visual/FormatPopover.svelte +13 -1
  33. package/dist/visual/ObjectField.svelte +157 -35
  34. package/dist/visual/ObjectField.svelte.d.ts +13 -4
  35. package/dist/visual/ProseArrayElement.svelte +22 -2
  36. package/dist/visual/ProseArrayElement.svelte.d.ts +1 -0
  37. package/dist/visual/TipsCard.svelte +33 -13
  38. package/dist/visual/VariantField.svelte +98 -0
  39. package/dist/visual/VariantField.svelte.d.ts +21 -0
  40. package/dist/visual/VisualEditorInner.svelte +18 -15
  41. package/dist/visual/controls.css +282 -283
  42. package/dist/visual/diagnostics.d.ts +15 -24
  43. package/dist/visual/diagnostics.js +14 -30
  44. package/dist/visual/domid.d.ts +13 -0
  45. package/dist/visual/domid.js +16 -0
  46. package/dist/visual/leaves.d.ts +9 -7
  47. package/dist/visual/links.d.ts +5 -2
  48. package/dist/visual/links.js +9 -3
  49. package/dist/visual/props.d.ts +1 -1
  50. package/dist/visual/strings.d.ts +21 -6
  51. package/dist/visual/strings.js +3 -0
  52. package/dist/visual/structure.d.ts +32 -7
  53. package/dist/visual/structure.js +49 -3
  54. package/package.json +2 -2
  55. package/dist/core/codec/clipboard.d.ts +0 -59
  56. package/dist/core/codec/clipboard.js +0 -202
@@ -97,27 +97,34 @@
97
97
  paints over the rest. The two read as one block, which is what document-level
98
98
  guidance is: the main card's, not a card of its own. Still in-flow, no lift.
99
99
 
100
- It takes the card's hairline, and the tuck makes the two outlines one: the slip's top
101
- stroke and the tops of its sides run under `main`, so what shows starts at the card's
102
- bottom corners.
100
+ No stroke of its own. A tint below the card reads as a slip; the same tint inside an
101
+ outline reads as a second card, which is the one thing the tuck exists to deny. What
102
+ shows below the seam is fill, and `main`'s hairline is the pair's only edge.
103
103
 
104
104
  The bottom corners take the inner rung where the card above takes the outer: a slip
105
105
  is cut crisper than the thing it is tucked under, at every setting of the dial.
106
106
 
107
- The insets are asymmetric so the ink is not: the top takes the tuck back, and the
108
- bottom gives up a rung to the foot's control, whose box stands on the tap floor
109
- rather than on its own one-line label. The horizontal is the card's inset plus the
110
- body's margin (ProseField), so the tip starts under the prose rather than under the
111
- fields. */
107
+ Down, the insets are the document's own rhythm and they are measured to the ink, not
108
+ to the boxes. The top is a block gap below the seam, what two of the body's paragraphs
109
+ take, so the tip reads as the block after the prose; the bottom is the card's inset
110
+ rung; and the foot sits at half of either, being the message's own foot rather than
111
+ the next thing after it. The control gives its tap floor back as margin for that to
112
+ hold: a box standing on the floor rather than on its one-line label carries a rung of
113
+ slack at each edge, and spent inside the gap that is enough to make the tightest space
114
+ in the card measure as the widest.
115
+
116
+ Across, the inset is `main`'s stroke and the two nested insets under it, the card's
117
+ own and the prose control's, so the tip starts on the prose's edge rather than short
118
+ of it. */
112
119
  .qm-tips {
113
120
  margin-top: calc(var(--_qm-radius) * -1);
114
- border: var(--_qm-border-width) solid var(--_qm-border);
115
121
  border-radius: 0 0 var(--_qm-radius-inner) var(--_qm-radius-inner);
116
- padding: calc(var(--_qm-space-3) + var(--_qm-radius)) var(--_qm-space-5) var(--_qm-space-2);
122
+ padding: calc(var(--_qm-space-3) + var(--_qm-radius))
123
+ calc(var(--_qm-border-width) + var(--_qm-space-3) * 2) var(--_qm-space-3);
117
124
  background: var(--_qm-tips-surface);
118
125
  display: flex;
119
126
  flex-direction: column;
120
- gap: var(--_qm-space-2);
127
+ gap: var(--_qm-space);
121
128
  font-family: var(--_qm-tips-font);
122
129
  font-size: var(--_qm-text-body);
123
130
  line-height: var(--_qm-leading-body);
@@ -168,7 +175,11 @@
168
175
  the spacing one — the foot's button clears the floor and walks the whole set from
169
176
  the tip the card opens on, so no tip is reachable only by a dot. State is three
170
177
  steps of one ink on the recede ladder, never a second hue and never a size: a dot
171
- that grew would move the row it sits in. */
178
+ that grew would move the row it sits in.
179
+
180
+ `color` is restated because a button takes the UA's and not its parent's, so
181
+ `currentColor` is the card's ink only once it is inherited; left to the UA it is
182
+ `buttontext`, which is the one neutral on a card that carries no neutrals. */
172
183
  .qm-tips-dot {
173
184
  box-sizing: content-box;
174
185
  width: 5px;
@@ -176,6 +187,7 @@
176
187
  padding: var(--_qm-space-half);
177
188
  border: none;
178
189
  border-radius: var(--_qm-radius-pill);
190
+ color: inherit;
179
191
  background: currentColor;
180
192
  background-clip: content-box;
181
193
  opacity: var(--_qm-opacity-idle);
@@ -192,8 +204,16 @@
192
204
  overrides is everything the family sets for a glyph among callers of its own
193
205
  colour, a labelled control on a warm card wanting none of it. The hover included:
194
206
  the family's neutral step would read as lent from the document above. Unlayered,
195
- so it beats the base rule without out-specifying it. */
207
+ so it beats the base rule without out-specifying it.
208
+
209
+ The box is the tap target and the label is what the rhythm measures, so both axes
210
+ give the difference back as margin: the inline rung lands the label on the body's
211
+ edge above, and the block rung takes the floor's slack out of the foot's height.
212
+ The box, and the hover it paints, still reaches past the label the way a control
213
+ does. */
196
214
  .qm-tips-action {
215
+ margin-block: calc(var(--_qm-space) * -1);
216
+ margin-right: calc(var(--_qm-space-2) * -1);
197
217
  padding: var(--_qm-space-half) var(--_qm-space-2);
198
218
  font-family: inherit;
199
219
  font-size: inherit;
@@ -0,0 +1,98 @@
1
+ <!--
2
+ An `enum` declaring `variants:` → the discriminant's select, and under it the cells
3
+ the chosen world brings into play (VISUAL_EDITOR §"Enum variants"). The field rests as
4
+ a container, `{value: <member>, …that member's fields}`, and commits whole: it is one
5
+ cell to `Addr`, so there is no per-cell write address.
6
+
7
+ The cells are {@link ObjectField}, which already draws a scalar field set keyed by
8
+ name over a container its parent commits by value. It takes the whole container as its
9
+ `value` and the world's declaration as its `properties`, so what it hands back is the
10
+ container with one cell written — and cells outside the drawn world ride through
11
+ untouched rather than needing to be merged back.
12
+ -->
13
+ <script lang="ts">
14
+ import type { QuillFieldSchema } from '@quillmark/wasm';
15
+ import EnumField from './EnumField.svelte';
16
+ import ObjectField from './ObjectField.svelte';
17
+ import {
18
+ VARIANT_DISCRIMINANT,
19
+ commitDiscriminant,
20
+ variantCells,
21
+ variantMember
22
+ } from './structure.js';
23
+
24
+ interface Props {
25
+ /** The stored container, or undefined while the field is unset. */
26
+ value: Record<string, unknown> | undefined;
27
+ schema: QuillFieldSchema;
28
+ /** The resolved `default:`'s member: what an unset field renders as. */
29
+ ghostMember: string | undefined;
30
+ /** Field label, the naming prefix the cells' controls compose with. */
31
+ label: string;
32
+ /** `<label for>` target: the discriminant trigger, this control's one labelable
33
+ * element. The cells are named by their own composed labels. */
34
+ id?: string;
35
+ labelledBy?: string;
36
+ describedBy?: string;
37
+ onCommit: (v: Record<string, unknown> | undefined) => void;
38
+ optionAllowed?: (value: string) => boolean;
39
+ enumDisallowed?: 'hide' | 'disable';
40
+ }
41
+ let {
42
+ value,
43
+ schema,
44
+ ghostMember,
45
+ label,
46
+ id,
47
+ labelledBy,
48
+ describedBy,
49
+ onCommit,
50
+ optionAllowed,
51
+ enumDisallowed
52
+ }: Props = $props();
53
+
54
+ const member = $derived(variantMember(value, ghostMember));
55
+ const cells = $derived(variantCells(schema, member));
56
+ const discriminant = $derived(value?.[VARIANT_DISCRIMINANT] as string | undefined);
57
+ </script>
58
+
59
+ <div class="qm-variant">
60
+ <EnumField
61
+ value={discriminant}
62
+ values={schema.values ?? []}
63
+ fallback={ghostMember}
64
+ {id}
65
+ {describedBy}
66
+ onCommit={(v) => onCommit(commitDiscriminant(value, v))}
67
+ {optionAllowed}
68
+ {enumDisallowed}
69
+ />
70
+ <!-- Keyed on the member so a flip remounts the cells rather than re-targeting
71
+ them: two worlds' cells are different fields that happen to occupy one place,
72
+ and a control carrying local state (an unreconciled pick, a caret) across the
73
+ flip would carry it between them. -->
74
+ {#if cells}
75
+ {#key member}
76
+ <!-- `close`: the discriminant's box directly above IS the cells' top boundary,
77
+ so the band's opening stroke would state it twice. -->
78
+ <ObjectField
79
+ value={value ?? {}}
80
+ properties={cells}
81
+ {label}
82
+ idBase={id}
83
+ {labelledBy}
84
+ {describedBy}
85
+ edges="close"
86
+ onCommit={(obj) => onCommit(obj)}
87
+ />
88
+ {/key}
89
+ {/if}
90
+ </div>
91
+
92
+ <style>
93
+ .qm-variant {
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: var(--_qm-space-2);
97
+ }
98
+ </style>
@@ -0,0 +1,21 @@
1
+ import type { QuillFieldSchema } from '@quillmark/wasm';
2
+ interface Props {
3
+ /** The stored container, or undefined while the field is unset. */
4
+ value: Record<string, unknown> | undefined;
5
+ schema: QuillFieldSchema;
6
+ /** The resolved `default:`'s member: what an unset field renders as. */
7
+ ghostMember: string | undefined;
8
+ /** Field label, the naming prefix the cells' controls compose with. */
9
+ label: string;
10
+ /** `<label for>` target: the discriminant trigger, this control's one labelable
11
+ * element. The cells are named by their own composed labels. */
12
+ id?: string;
13
+ labelledBy?: string;
14
+ describedBy?: string;
15
+ onCommit: (v: Record<string, unknown> | undefined) => void;
16
+ optionAllowed?: (value: string) => boolean;
17
+ enumDisallowed?: 'hide' | 'disable';
18
+ }
19
+ declare const VariantField: import("svelte").Component<Props, {}, "">;
20
+ type VariantField = ReturnType<typeof VariantField>;
21
+ export default VariantField;
@@ -58,7 +58,6 @@
58
58
  } from './structure.js';
59
59
  import {
60
60
  fieldKeyToString,
61
- inlineShown,
62
61
  resolveCardKey,
63
62
  routeAndResolve,
64
63
  mergeDiagnostics,
@@ -487,11 +486,10 @@
487
486
 
488
487
  // All three producers. The two positional ones resolve their `.path` to the live
489
488
  // stable-id keying; commit errors are id-keyed already. Nothing is dropped for
490
- // being unwelcome — diagnostics never gate — but the obligation warning is
491
- // withheld from the boundary's lane, the label's `*` being where the surface
492
- // states that (`inlineShown`).
489
+ // being unwelcome — diagnostics never gate — but warnings do not draw: only
490
+ // errors reach a control (`routeAndResolve`).
493
491
  const diagByKey = $derived.by(() => {
494
- const fromValidate = routeAndResolve(validation.filter(inlineShown), cardIds);
492
+ const fromValidate = routeAndResolve(validation, cardIds);
495
493
  const fromExternal = routeAndResolve(diagnostics, cardIds);
496
494
  return mergeDiagnostics(fromValidate, fromExternal, [...commitErrors.values()]);
497
495
  });
@@ -670,14 +668,15 @@
670
668
 
671
669
  /**
672
670
  * Put the caret in a revealed target, at the finest grain it can take: a USV offset
673
- * in a prose leaf, a bare focus everywhere else. An array element is no
674
- * `createField` leaf, and a form control has no coordinate to spend an offset in (an
675
- * `<input type="number">` refuses a selection outright), which is also the whole of
676
- * what a click on plate-placed ink can mean.
671
+ * in a prose leaf or an array element, a bare focus everywhere else. A form control
672
+ * has no coordinate to spend an offset in (an `<input type="number">` refuses a
673
+ * selection outright), which is also the whole of what a click on plate-placed ink
674
+ * can mean. An element is no `createField` leaf, so the offset goes down the element
675
+ * lane instead, where what it means is the row control's (`leaves.ts`).
677
676
  */
678
677
  function land(found: Landed, pos: number | undefined): void {
679
678
  if (found.element != null && found.control.focusElement) {
680
- return found.control.focusElement(found.element);
679
+ return found.control.focusElement(found.element, pos);
681
680
  }
682
681
  if (pos != null) {
683
682
  const prose = leaves.prose(found.key);
@@ -900,9 +899,13 @@
900
899
 
901
900
  The column, not only the cards in it: the gutter the stack sits in and the tone
902
901
  behind it are the surface's own, so a bare `<div>` is a mounting site and nothing
903
- is owed before the editor looks right. Consumer CSS is unlayered and beats all of
904
- it, which is how a host that wants the column back takes it. `border-box`, so a
905
- height from the caller is the height this draws.
902
+ is owed before the editor looks right. `border-box`, so a height from the caller is
903
+ the height this draws.
904
+
905
+ `:global(:where(…))` puts a promised class at zero rank, and both halves are
906
+ load-bearing: `:global` drops the scoping class, `:where` drops the rest
907
+ (ARCHITECTURE §Styling). The class is the surface's root wherever it mounts, so
908
+ leaving the component's scope costs nothing.
906
909
 
907
910
  The sunken rung, which is what makes a card an island: the cards are the base
908
911
  plane and this is the one they float on, so the gutter reads as ground rather
@@ -910,7 +913,7 @@
910
913
  ramp, which is what gives the ground enough of itself to read as ground: at a
911
914
  narrower one the stack meets whatever frames the surface and the islands are
912
915
  back to being one block clipped at its edges. */
913
- .qm-editor {
916
+ :global(:where(.qm-editor)) {
914
917
  box-sizing: border-box;
915
918
  display: flex;
916
919
  flex-direction: column;
@@ -935,7 +938,7 @@
935
938
  site with no height of its own, which is the page form kept. The cards do not
936
939
  shrink to fit: a flex item's automatic minimum floors each at its own height, so
937
940
  the stack overflows and scrolls rather than squashing. */
938
- .qm-editor.qm-pane {
941
+ :global(:where(.qm-editor.qm-pane)) {
939
942
  height: 100%;
940
943
  overflow: auto;
941
944
  overscroll-behavior: contain;