@quillmark/svelte 0.1.1 → 0.3.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.
- package/README.md +16 -12
- package/THEMING.md +48 -25
- package/dist/core/address.d.ts +42 -7
- package/dist/core/address.js +49 -21
- package/dist/core/bloom.d.ts +5 -5
- package/dist/core/bloom.js +8 -8
- package/dist/core/codec/anchor.d.ts +1 -1
- package/dist/core/codec/anchor.js +1 -1
- package/dist/core/codec/atoms.d.ts +10 -0
- package/dist/core/codec/atoms.js +78 -0
- package/dist/core/codec/clipboard.d.ts +59 -0
- package/dist/core/codec/clipboard.js +202 -0
- package/dist/core/codec/code.d.ts +1 -1
- package/dist/core/codec/code.js +4 -4
- package/dist/core/codec/decode.d.ts +1 -1
- package/dist/core/codec/decode.js +8 -8
- package/dist/core/codec/encode.d.ts +4 -4
- package/dist/core/codec/encode.js +14 -11
- package/dist/core/codec/field.d.ts +22 -27
- package/dist/core/codec/field.js +33 -32
- package/dist/core/codec/index.js +5 -8
- package/dist/core/codec/inputrules.js +56 -18
- package/dist/core/codec/islands.d.ts +11 -7
- package/dist/core/codec/islands.js +16 -10
- package/dist/core/codec/keymap.d.ts +5 -6
- package/dist/core/codec/keymap.js +8 -7
- package/dist/core/codec/lists.d.ts +3 -3
- package/dist/core/codec/lists.js +32 -13
- package/dist/core/codec/marks.d.ts +1 -1
- package/dist/core/codec/marks.js +1 -1
- package/dist/core/codec/positions.js +1 -1
- package/dist/core/codec/prose.css +266 -228
- package/dist/core/codec/schema.js +4 -4
- package/dist/core/codec/slash.d.ts +39 -41
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-view.d.ts +12 -16
- package/dist/core/codec/table-view.js +488 -292
- package/dist/core/codec/table.d.ts +42 -15
- package/dist/core/codec/table.js +73 -27
- package/dist/core/errors.d.ts +17 -7
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +6 -5
- package/dist/core/lifecycle.d.ts +21 -5
- package/dist/core/lifecycle.js +34 -8
- package/dist/core/rebind.svelte.d.ts +18 -4
- package/dist/core/rebind.svelte.js +30 -12
- package/dist/core/teardown.d.ts +1 -1
- package/dist/core/teardown.js +1 -1
- package/dist/core/theme.css +173 -58
- package/dist/preset/index.css +1 -1
- package/dist/preset/recipes.css +79 -30
- package/dist/preset/scale.css +40 -28
- package/dist/preview/Preview.svelte +22 -19
- package/dist/preview/Preview.svelte.d.ts +8 -8
- package/dist/preview/bridge.d.ts +6 -4
- package/dist/preview/bridge.js +19 -9
- package/dist/preview/controller.d.ts +13 -6
- package/dist/preview/controller.js +8 -5
- package/dist/preview/geometry.d.ts +17 -2
- package/dist/preview/geometry.js +21 -1
- package/dist/preview/index.d.ts +1 -0
- package/dist/preview/index.js +9 -2
- package/dist/preview/overlay.d.ts +2 -1
- package/dist/preview/overlay.js +26 -11
- package/dist/preview/paint.js +19 -17
- package/dist/visual/ArrayField.svelte +103 -83
- package/dist/visual/ArrayField.svelte.d.ts +5 -4
- package/dist/visual/BooleanField.svelte +8 -6
- package/dist/visual/BooleanField.svelte.d.ts +2 -2
- package/dist/visual/Card.svelte +161 -333
- package/dist/visual/Card.svelte.d.ts +5 -4
- package/dist/visual/CardControls.svelte +1 -1
- package/dist/visual/DateField.svelte +19 -19
- package/dist/visual/DateField.svelte.d.ts +3 -3
- package/dist/visual/DiagnosticList.svelte +2 -2
- package/dist/visual/EnumField.svelte +48 -28
- package/dist/visual/EnumField.svelte.d.ts +5 -4
- package/dist/visual/Field.svelte +207 -132
- package/dist/visual/Field.svelte.d.ts +11 -7
- package/dist/visual/FieldHint.svelte +79 -48
- package/dist/visual/FieldHint.svelte.d.ts +1 -1
- package/dist/visual/FieldLabel.svelte +8 -8
- package/dist/visual/FormatPopover.svelte +82 -35
- package/dist/visual/NumberField.svelte +5 -5
- package/dist/visual/NumberField.svelte.d.ts +1 -1
- package/dist/visual/ObjectField.svelte +28 -9
- package/dist/visual/ObjectField.svelte.d.ts +4 -2
- package/dist/visual/ProseArrayElement.svelte +12 -19
- package/dist/visual/ProseArrayElement.svelte.d.ts +1 -2
- package/dist/visual/ProseField.svelte +62 -85
- package/dist/visual/ProseField.svelte.d.ts +14 -17
- package/dist/visual/SlashMenu.svelte +42 -16
- package/dist/visual/TextField.svelte +5 -5
- package/dist/visual/TextField.svelte.d.ts +1 -1
- package/dist/visual/TipsCard.svelte +43 -28
- package/dist/visual/VisualEditor.svelte +18 -19
- package/dist/visual/VisualEditor.svelte.d.ts +2 -3
- package/dist/visual/VisualEditorInner.svelte +251 -184
- package/dist/visual/VisualEditorInner.svelte.d.ts +2 -3
- package/dist/visual/controls.css +85 -38
- package/dist/visual/diagnostics.d.ts +13 -21
- package/dist/visual/diagnostics.js +7 -19
- package/dist/visual/domid.js +3 -3
- package/dist/visual/ext.d.ts +1 -1
- package/dist/visual/ext.js +1 -1
- package/dist/visual/icons/nodes.d.ts +3 -0
- package/dist/visual/icons/nodes.js +2 -1
- package/dist/visual/index.d.ts +1 -0
- package/dist/visual/index.js +10 -3
- package/dist/visual/leaves.d.ts +47 -0
- package/dist/visual/leaves.js +31 -0
- package/dist/visual/links.d.ts +24 -0
- package/dist/visual/links.js +67 -0
- package/dist/visual/props.d.ts +33 -12
- package/dist/visual/signals.d.ts +6 -6
- package/dist/visual/strings.d.ts +14 -19
- package/dist/visual/strings.js +5 -7
- package/dist/visual/structure.d.ts +24 -17
- package/dist/visual/structure.js +8 -8
- package/dist/visual/synced.svelte.d.ts +1 -1
- package/dist/visual/synced.svelte.js +4 -4
- package/dist/visual/tips.d.ts +4 -2
- package/dist/visual/tips.js +7 -4
- package/package.json +2 -2
package/dist/visual/props.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ import type { ActiveLeaf, EditorChange } from './signals.js';
|
|
|
5
5
|
import type { FormatDiagnostic, VisualStringsInput } from './strings.js';
|
|
6
6
|
export interface VisualEditorProps {
|
|
7
7
|
doc: Document;
|
|
8
|
+
/**
|
|
9
|
+
* The schema this document is edited against. Borrowed for the surface's lifetime,
|
|
10
|
+
* whoever handed it over: a quill from `@quillmark/quiver`'s `getQuill` is the
|
|
11
|
+
* quiver's, shared with every other caller for that ref, and freeing it hands the
|
|
12
|
+
* next caller a freed handle. A host wanting one of its own mints it from
|
|
13
|
+
* `.toTree()`. The editor frees nothing it is passed.
|
|
14
|
+
*/
|
|
8
15
|
quill: Quill;
|
|
9
16
|
/**
|
|
10
17
|
* The active leaf: its canonical `DocPath` and the session key of the card holding
|
|
@@ -19,23 +26,28 @@ export interface VisualEditorProps {
|
|
|
19
26
|
* keystroke path (the editor mints the path off its derived card tree, which
|
|
20
27
|
* already holds every kind).
|
|
21
28
|
*
|
|
22
|
-
* A
|
|
29
|
+
* A selection signal, not a change signal: an arrow key fires it and commits
|
|
23
30
|
* nothing. One keystroke fires `onChange` first and this second, so the edit
|
|
24
31
|
* has landed by the time the caret names where it is.
|
|
25
32
|
*
|
|
26
|
-
* A
|
|
33
|
+
* A place, so consecutive duplicates do not arrive: landing the caret where it
|
|
27
34
|
* already sits reports nothing, and a place left and returned to reports twice.
|
|
28
35
|
*/
|
|
29
36
|
onCaretMove?: (at: Place) => void;
|
|
30
37
|
/**
|
|
31
|
-
*
|
|
38
|
+
* Every edit that lands on the document: a prose commit, a scalar/array/object
|
|
32
39
|
* write, a card operation. The signal a host recompiles off; `source` is what
|
|
33
|
-
* moved
|
|
34
|
-
*
|
|
40
|
+
* moved.
|
|
41
|
+
*
|
|
42
|
+
* **The lane split is the default, not a tuning exercise.** `'structure'` arrives
|
|
43
|
+
* once per gesture and recompiles at once; `'prose'` and `'field'` arrive per
|
|
44
|
+
* keystroke and wait for the burst to settle. Recompiling structure on the debounce
|
|
45
|
+
* makes a card insert look dropped, and recompiling prose at once compiles the
|
|
46
|
+
* document on every character.
|
|
35
47
|
*/
|
|
36
48
|
onChange?: (change: EditorChange) => void;
|
|
37
49
|
/**
|
|
38
|
-
* Failures the editor
|
|
50
|
+
* Failures the editor recovered from: a commit the boundary refused (also
|
|
39
51
|
* pinned as a diagnostic on its field), a card operation that threw, a
|
|
40
52
|
* `validate`/`resolve` that threw, a prose commit that fell back. None of them
|
|
41
53
|
* stop editing; without this hook each is a `console.error` an app cannot
|
|
@@ -49,16 +61,25 @@ export interface VisualEditorProps {
|
|
|
49
61
|
diagnostics?: Diagnostic[];
|
|
50
62
|
/**
|
|
51
63
|
* Consumer policy hook: given a field `addr` and an enum option,
|
|
52
|
-
* return `false` to mark that option unavailable
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
64
|
+
* return `false` to mark that option unavailable; `enumDisallowed` decides how
|
|
65
|
+
* the control then draws it. The stored payload is untouched either way: the
|
|
66
|
+
* schema is unchanged, this is runtime policy. Absent → every schema option is
|
|
67
|
+
* offered. It reaches a field's own enum control; an enum inside an object
|
|
68
|
+
* subform has no `Addr` to pass and is offered whole.
|
|
56
69
|
*/
|
|
57
70
|
enumOptionAllowed?: (addr: Addr, value: string) => boolean;
|
|
71
|
+
/**
|
|
72
|
+
* How an `enumOptionAllowed: false` option draws: `'disable'` (the default) greys
|
|
73
|
+
* it in place, `'hide'` leaves it out of the list. One policy for the surface, not
|
|
74
|
+
* a per-option verdict, and the selected value is drawn under either (disabled
|
|
75
|
+
* when refused), so the control always shows what the document says
|
|
76
|
+
* (VISUAL_EDITOR §"Enum policy").
|
|
77
|
+
*/
|
|
78
|
+
enumDisallowed?: 'hide' | 'disable';
|
|
58
79
|
/**
|
|
59
80
|
* Every word the surface says, keyed and partial: unset keys take the package's
|
|
60
|
-
* English. Several are
|
|
61
|
-
* a product shipping in another language replaces. The empty body's per-
|
|
81
|
+
* English. Several are accessible names rather than decoration, so this is what
|
|
82
|
+
* a product shipping in another language replaces. The empty body's per-card
|
|
62
83
|
* wording is the `bodyPlaceholder` key here rather than a prop of its own.
|
|
63
84
|
*/
|
|
64
85
|
strings?: VisualStringsInput;
|
package/dist/visual/signals.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DocPath } from '../core/address.js';
|
|
2
2
|
/**
|
|
3
|
-
* Which lane an edit came down. The three differ in
|
|
3
|
+
* Which lane an edit came down. The three differ in cost, not in meaning: prose
|
|
4
4
|
* arrives per keystroke and wants a debounce, a structure op arrives once per
|
|
5
5
|
* gesture and does not. A host that recompiles the same way for all three never
|
|
6
6
|
* reads it.
|
|
@@ -13,30 +13,30 @@ export type ChangeSource =
|
|
|
13
13
|
/** A card operation: add, move, remove, retype, rename, tips dismissal. */
|
|
14
14
|
| 'structure';
|
|
15
15
|
/**
|
|
16
|
-
* A card's
|
|
16
|
+
* A card's session key: the editor's own identity for a card instance, `'main'` for
|
|
17
17
|
* the main card and an opaque `IdSeq` id (`structure.ts`) for a composable one. It
|
|
18
18
|
* rides beside every address the surface emits, because no address survives a
|
|
19
19
|
* reorder: `Addr` and `DocPath` are both positional, so a host holding
|
|
20
20
|
* `cards.indorsement[2].from` names a different card after one `moveCard`, and
|
|
21
21
|
* silently. The key does not move.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
23
|
+
* Session-scoped by construction. Nothing document-side backs it (the document model
|
|
24
24
|
* carries no card handle): it is minted at mount and dies with the surface, so it
|
|
25
25
|
* does not survive a reload and a host persisting one is persisting a session key.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
27
|
+
* The editor owns structural mutation for the session. The keys track the inserts,
|
|
28
28
|
* moves and removals the editor performs; a host that mutates the card array behind
|
|
29
29
|
* it (its own `doc.insertCard`) desyncs them, and the answer is to re-seed the
|
|
30
30
|
* surface rather than expect the keys to follow. There is nothing document-side to
|
|
31
31
|
* reconcile against, so this is a contract rather than a mechanism.
|
|
32
32
|
*/
|
|
33
33
|
export type CardId = string;
|
|
34
|
-
/** An edit that
|
|
34
|
+
/** An edit that landed on the document, and which lane it came down. */
|
|
35
35
|
export interface EditorChange {
|
|
36
36
|
source: ChangeSource;
|
|
37
37
|
/**
|
|
38
38
|
* Where it landed, in the canonical `DocPath` the preview and every diagnostic
|
|
39
|
-
* already speak: a
|
|
39
|
+
* already speak: a field for the prose and field lanes, the card itself
|
|
40
40
|
* (`cards.<kind>[i]`) for a structure op, where the change is the card rather than
|
|
41
41
|
* anything inside it. Absent for a card removal and a tips dismissal, which are
|
|
42
42
|
* the stack's change rather than a leaf's.
|
package/dist/visual/strings.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Diagnostic } from '@quillmark/wasm';
|
|
|
2
2
|
import { type SlashStrings, type TableChromeStrings } from '../core/codec/index.js';
|
|
3
3
|
import type { BodyPlaceholder } from './structure.js';
|
|
4
4
|
/**
|
|
5
|
-
* The island and the insert menu's keys come from the
|
|
5
|
+
* The island and the insert menu's keys come from the codec, which owns both models
|
|
6
6
|
* (a NodeView inside the leaf, and the trigger the leaf's keymap drives). Extending
|
|
7
7
|
* rather than restating them is what keeps one English list: a consumer overrides an
|
|
8
8
|
* island handle's name beside the card controls' and the codec still has a default
|
|
@@ -14,16 +14,11 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
|
|
|
14
14
|
cardDelete: string;
|
|
15
15
|
/** The card-title input's accessible name. */
|
|
16
16
|
cardTitle: string;
|
|
17
|
-
/** The
|
|
17
|
+
/** The trailing strip's visible words, which are also every strip's accessible
|
|
18
|
+
* name; the glyph rides the wording, as the array foot's does. One string over
|
|
19
|
+
* one kind and many: the trigger seeds A card, and which kind is the menu's word
|
|
20
|
+
* to say or the schema's to have already settled. */
|
|
18
21
|
addCard: string;
|
|
19
|
-
/** The single-kind trigger, whose accessible name IS the kind it seeds. */
|
|
20
|
-
addCardOfKind: (kind: string) => string;
|
|
21
|
-
/** The trailing strip's VISIBLE words, which are also its accessible name; the
|
|
22
|
-
* glyph rides the wording, as the array foot's does. A pair of their own rather
|
|
23
|
-
* than a prefix over the two above, because a language reading the mark as one
|
|
24
|
-
* phrase has nothing to prefix. */
|
|
25
|
-
addCardMark: (kind: string) => string;
|
|
26
|
-
addCardMarkAny: string;
|
|
27
22
|
arrayAdd: string;
|
|
28
23
|
arrayRemove: string;
|
|
29
24
|
/** The required marker's accessible name; the glyph itself is a `*`. */
|
|
@@ -37,10 +32,12 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
|
|
|
37
32
|
formatLink: string;
|
|
38
33
|
/** The anchor button's accessible name. */
|
|
39
34
|
formatAnchor: string;
|
|
40
|
-
/** Its hover title, which says what an anchor
|
|
35
|
+
/** Its hover title, which says what an anchor is; the name above only says what. */
|
|
41
36
|
formatAnchorTitle: string;
|
|
42
37
|
linkPlaceholder: string;
|
|
43
38
|
linkApply: string;
|
|
39
|
+
/** Drawn only where the selection carries a link. */
|
|
40
|
+
linkRemove: string;
|
|
44
41
|
linkCancel: string;
|
|
45
42
|
tipsLabel: string;
|
|
46
43
|
tipsDismiss: string;
|
|
@@ -57,10 +54,8 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
|
|
|
57
54
|
*/
|
|
58
55
|
bodyGhost: string;
|
|
59
56
|
/**
|
|
60
|
-
* Per-
|
|
61
|
-
* takes it.
|
|
62
|
-
* hook that samples a set at random still reads as one deliberate string.
|
|
63
|
-
* Impurity is expected, and the cache is what contains it.
|
|
57
|
+
* Per-CARD wording, in place of {@link VisualStrings.bodyGhost}; `undefined`
|
|
58
|
+
* takes it. Pure and uncached ({@link BodyPlaceholder}).
|
|
64
59
|
*/
|
|
65
60
|
bodyPlaceholder: BodyPlaceholder;
|
|
66
61
|
}
|
|
@@ -72,22 +67,22 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
|
|
|
72
67
|
* lane (VISUAL_EDITOR §Diagnostics for why each lands where it does):
|
|
73
68
|
*
|
|
74
69
|
* - **`validation::enum_violation`, `type_mismatch`, `format_violation`,
|
|
75
|
-
* `must_fill`**: the
|
|
76
|
-
* offending
|
|
70
|
+
* `must_fill`**: the constraint re-words from the quill's schema at `path`, the
|
|
71
|
+
* offending value from `args` (`value` / `sourceToken` / `actual`, the engine
|
|
77
72
|
* testifying to what it saw) and never from the document at `path`: validation
|
|
78
73
|
* runs post-coercion, so the validator read a value the document does not hold and
|
|
79
74
|
* a sentence built from `path` names a spelling the user never typed.
|
|
80
75
|
* - **`edit::field_coercion_failed`**: re-words from the app's own control state, the
|
|
81
76
|
* refused value being in neither document (unchanged on throw) nor schema.
|
|
82
77
|
* - **`parse::yaml_error_with_location`, `invalid_structure`**: does not re-word. No
|
|
83
|
-
* `path`, and `args` carries the
|
|
78
|
+
* `path`, and `args` carries the location (`blockIndex`, `line`) and nothing else,
|
|
84
79
|
* engine prose riding under no key: the parser's own text, its column and its caret
|
|
85
80
|
* snippet exist only inside `message`.
|
|
86
81
|
* - **`LiveSession.warnings`**: does not re-word. Backend text, an external feed.
|
|
87
82
|
*
|
|
88
83
|
* The last two are the boundary's shape, not a gap in it, so the fallback arm is
|
|
89
84
|
* permanent: returning `undefined` renders `d.message` unchanged. `code` is optional
|
|
90
|
-
* at this pin, so that arm is reachable by
|
|
85
|
+
* at this pin, so that arm is reachable by type and not only in principle.
|
|
91
86
|
*
|
|
92
87
|
* Displayed text is that message or this replacement and nothing beside it: `hint` is
|
|
93
88
|
* the tail of the message it accompanies, so rendering it too ships a two-language
|
package/dist/visual/strings.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Every word the visual surface says, in one place, so a product shipping in
|
|
2
|
-
// another language can replace them. Keyed and
|
|
2
|
+
// another language can replace them. Keyed and partial: a consumer sets the keys
|
|
3
3
|
// it has translations for and the rest take the package's English, which is what
|
|
4
4
|
// makes wording an override rather than a fork.
|
|
5
5
|
//
|
|
6
|
-
// Several of these are
|
|
6
|
+
// Several of these are accessible names rather than decoration — the card
|
|
7
7
|
// controls, the add trigger, the required marker — 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.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// a parametric string cannot be assembled from fragments and stay translatable,
|
|
13
13
|
// since the word order is the translator's to choose.
|
|
14
14
|
//
|
|
15
|
-
// The strings reach the tree through
|
|
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
17
|
// required marker, 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,
|
|
@@ -27,10 +27,7 @@ export const DEFAULT_VISUAL_STRINGS = {
|
|
|
27
27
|
cardMoveDown: 'Move down',
|
|
28
28
|
cardDelete: 'Delete card',
|
|
29
29
|
cardTitle: 'Card title',
|
|
30
|
-
addCard: 'Add
|
|
31
|
-
addCardOfKind: (kind) => `Add ${kind}`,
|
|
32
|
-
addCardMark: (kind) => `+ Add ${kind}`,
|
|
33
|
-
addCardMarkAny: '+ Add Card',
|
|
30
|
+
addCard: '+ Add Card',
|
|
34
31
|
arrayAdd: '+ Add',
|
|
35
32
|
arrayRemove: 'Remove',
|
|
36
33
|
fieldRequired: 'required',
|
|
@@ -45,6 +42,7 @@ export const DEFAULT_VISUAL_STRINGS = {
|
|
|
45
42
|
formatAnchorTitle: 'Anchor — an identity handle over the selection',
|
|
46
43
|
linkPlaceholder: 'https://…',
|
|
47
44
|
linkApply: 'Apply',
|
|
45
|
+
linkRemove: 'Remove',
|
|
48
46
|
linkCancel: 'Cancel',
|
|
49
47
|
tipsLabel: 'Editor tips',
|
|
50
48
|
tipsDismiss: 'Dismiss',
|
|
@@ -17,7 +17,7 @@ export interface FieldModel {
|
|
|
17
17
|
* undefined when the field declares none. Chrome-only; never gates. */
|
|
18
18
|
description: string | undefined;
|
|
19
19
|
/**
|
|
20
|
-
* Required-ness: a field with
|
|
20
|
+
* Required-ness: a field with no `default:` is "Unendorsed": its
|
|
21
21
|
* seed carries a `!must_fill` marker (DOCUMENT_MODEL: there is no separate
|
|
22
22
|
* `required` axis). Drives a persistent label `*`, complementary to the ghosted
|
|
23
23
|
* `default:` (a required field has no default to ghost). Persistent (schema-
|
|
@@ -38,7 +38,7 @@ export interface GroupSection {
|
|
|
38
38
|
/**
|
|
39
39
|
* One card instance projected for rendering: the schema × payload join result
|
|
40
40
|
* the VisualEditor's `$derived` produces and hands to `<Card>`. Positional
|
|
41
|
-
* identity is carried by `id` (a session key),
|
|
41
|
+
* identity is carried by `id` (a session key), not by array index.
|
|
42
42
|
*/
|
|
43
43
|
export interface CardModel {
|
|
44
44
|
/** Stable session id (`'main'` for the main card). */
|
|
@@ -48,7 +48,7 @@ export interface CardModel {
|
|
|
48
48
|
/**
|
|
49
49
|
* The card's `kind` has no projectable schema: a foreign kind under
|
|
50
50
|
* a schema that declares others, or a card under a schema with no `card_kinds` at
|
|
51
|
-
* all. Such a card renders a
|
|
51
|
+
* all. Such a card renders a recovery shell (humanized title + retype + delete)
|
|
52
52
|
* instead of a field list, so its content is never dropped or trapped: retyping
|
|
53
53
|
* to a declared kind re-projects it, delete removes it. Always `false` for `main`.
|
|
54
54
|
*/
|
|
@@ -62,7 +62,7 @@ export interface CardModel {
|
|
|
62
62
|
/**
|
|
63
63
|
* Field name → its resolved provenance row (`{ value, source }`), parallel to
|
|
64
64
|
* `values` (FIELD_PROVENANCE). The channel that feeds chrome (the ghosted
|
|
65
|
-
* `default:` and any authored/default/zero affordance),
|
|
65
|
+
* `default:` and any authored/default/zero affordance), never the control
|
|
66
66
|
* value. Empty when `quill.resolve` is unavailable.
|
|
67
67
|
*/
|
|
68
68
|
provenance: Record<string, ResolvedField>;
|
|
@@ -73,7 +73,7 @@ export interface CardModel {
|
|
|
73
73
|
* empty for a card that does: the resolved body `default:` when there is one,
|
|
74
74
|
* else the consumer's wording, else the built-in invitation
|
|
75
75
|
* ({@link resolveBodyGhost}). A body leaf therefore always has something in it
|
|
76
|
-
* to write into, which the inline fields deliberately do not: their ghost
|
|
76
|
+
* to write into, which the inline fields deliberately do not: their ghost is
|
|
77
77
|
* the resolved default, and an invented one would read as a value.
|
|
78
78
|
*/
|
|
79
79
|
bodyGhost?: string;
|
|
@@ -114,25 +114,32 @@ export declare function ghostDefault(row: ResolvedField | undefined): unknown;
|
|
|
114
114
|
* thing to display as a placeholder (FIELD_PROVENANCE). */
|
|
115
115
|
export declare function stringifyGhost(ghost: unknown): string | undefined;
|
|
116
116
|
/** What a {@link BodyPlaceholder} is told about the body it words. The card's
|
|
117
|
-
*
|
|
118
|
-
* anything richer, and a renamed card must not
|
|
117
|
+
* identity, not its chrome: `cardId` is what per-card wording keys off, `kind` keys
|
|
118
|
+
* the consumer's own `quill.schema` for anything richer, and a renamed card must not
|
|
119
|
+
* shift its ghost. */
|
|
119
120
|
export interface BodyPlaceholderContext {
|
|
121
|
+
/**
|
|
122
|
+
* The card's session key (`CardId`), `'main'` for the main card. Follows its card
|
|
123
|
+
* across a reorder and a retype, and is re-minted when the surface re-keys on a new
|
|
124
|
+
* document handle. Opaque: wording keyed off it hashes it rather than reads it.
|
|
125
|
+
*/
|
|
126
|
+
cardId: string;
|
|
120
127
|
/** The card's kind; `'main'` for the main card. */
|
|
121
128
|
kind: string;
|
|
122
129
|
/** The main card: whose `kind` is not a `card_kinds` key. */
|
|
123
130
|
isMain: boolean;
|
|
124
131
|
}
|
|
125
132
|
/**
|
|
126
|
-
* Consumer wording for an empty body,
|
|
127
|
-
*
|
|
128
|
-
* cached
|
|
129
|
-
*
|
|
130
|
-
*
|
|
133
|
+
* Consumer wording for an empty body, in place of the flat `bodyGhost` string;
|
|
134
|
+
* returning `undefined` takes it. Consulted per card on every derive and never
|
|
135
|
+
* cached, so it must be pure: a hook sampling a set at random re-rolls on every
|
|
136
|
+
* keystroke. Per-card wording is a function of `cardId`, per-kind wording a function
|
|
137
|
+
* of `kind`, and either is stable because the function is.
|
|
131
138
|
*/
|
|
132
139
|
export type BodyPlaceholder = (ctx: BodyPlaceholderContext) => string | undefined;
|
|
133
140
|
/**
|
|
134
|
-
* The empty body's ghost: the resolved body `default:`, else
|
|
135
|
-
* wording, else the flat built-in. The `default:`
|
|
141
|
+
* The empty body's ghost: the resolved body `default:`, else the consumer's
|
|
142
|
+
* wording, else the flat built-in. The `default:` wins because it is the only one
|
|
136
143
|
* of the three that describes the render: it promises what prints if nothing is
|
|
137
144
|
* written, and wording placed over it would make that promise unreadable. The
|
|
138
145
|
* other two are invitations, and an invitation belongs only where there is no
|
|
@@ -140,7 +147,7 @@ export type BodyPlaceholder = (ctx: BodyPlaceholderContext) => string | undefine
|
|
|
140
147
|
*/
|
|
141
148
|
export declare function resolveBodyGhost(resolvedDefault: string | undefined, custom: string | undefined, builtIn: string): string;
|
|
142
149
|
/** Map a field schema to its control (precedence: prose › enum › text › …).
|
|
143
|
-
* An array's
|
|
150
|
+
* An array's element control is this over `items`: a missing `items` is a text
|
|
144
151
|
* element. */
|
|
145
152
|
export declare function controlKind(f: QuillFieldSchema): ControlKind;
|
|
146
153
|
/** The closed value domain of an enum/enum-string field, or undefined. */
|
|
@@ -151,7 +158,7 @@ export declare function humanize(name: string): string;
|
|
|
151
158
|
export declare function fieldModels(cardSchema: QuillCardSchema): FieldModel[];
|
|
152
159
|
/**
|
|
153
160
|
* Group-section order (VISUAL_EDITOR §"Structure mirrors the schema"): the schema's `ui.groups` registry
|
|
154
|
-
*
|
|
161
|
+
* key order when present: `QuillCardUi.groups` is a typed `Record` at the
|
|
155
162
|
* boundary, so this reads it uncast: else the first-appearance order of each
|
|
156
163
|
* field's `ui.group`.
|
|
157
164
|
*/
|
|
@@ -195,7 +202,7 @@ export interface PlacedField {
|
|
|
195
202
|
* Assign each field its span in the section grid. Consecutive packable fields are
|
|
196
203
|
* `cell`s the grid auto-places: capacity and wrapping are the container query's
|
|
197
204
|
* business, so a trailing orphan keeps its column width rather than growing to fill.
|
|
198
|
-
* A packable run of
|
|
205
|
+
* A packable run of one is `lone`: with no row above to align to, one column reads as
|
|
199
206
|
* truncated, so it takes half the capacity from column 1. Everything else is `full`.
|
|
200
207
|
*
|
|
201
208
|
* Pure, and stays pure: no width, no measurement, nothing to re-derive on resize.
|
package/dist/visual/structure.js
CHANGED
|
@@ -36,8 +36,8 @@ export function stringifyGhost(ghost) {
|
|
|
36
36
|
return ghost != null && typeof ghost !== 'object' ? String(ghost) : undefined;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* The empty body's ghost: the resolved body `default:`, else
|
|
40
|
-
* wording, else the flat built-in. The `default:`
|
|
39
|
+
* The empty body's ghost: the resolved body `default:`, else the consumer's
|
|
40
|
+
* wording, else the flat built-in. The `default:` wins because it is the only one
|
|
41
41
|
* of the three that describes the render: it promises what prints if nothing is
|
|
42
42
|
* written, and wording placed over it would make that promise unreadable. The
|
|
43
43
|
* other two are invitations, and an invitation belongs only where there is no
|
|
@@ -47,7 +47,7 @@ export function resolveBodyGhost(resolvedDefault, custom, builtIn) {
|
|
|
47
47
|
return resolvedDefault || custom || builtIn;
|
|
48
48
|
}
|
|
49
49
|
/** Map a field schema to its control (precedence: prose › enum › text › …).
|
|
50
|
-
* An array's
|
|
50
|
+
* An array's element control is this over `items`: a missing `items` is a text
|
|
51
51
|
* element. */
|
|
52
52
|
export function controlKind(f) {
|
|
53
53
|
switch (f.type) {
|
|
@@ -102,7 +102,7 @@ export function fieldModels(cardSchema) {
|
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
104
|
* Group-section order (VISUAL_EDITOR §"Structure mirrors the schema"): the schema's `ui.groups` registry
|
|
105
|
-
*
|
|
105
|
+
* key order when present: `QuillCardUi.groups` is a typed `Record` at the
|
|
106
106
|
* boundary, so this reads it uncast: else the first-appearance order of each
|
|
107
107
|
* field's `ui.group`.
|
|
108
108
|
*/
|
|
@@ -174,13 +174,13 @@ export function initialExpandedGroup(sections) {
|
|
|
174
174
|
}
|
|
175
175
|
/**
|
|
176
176
|
* Whether a field can share a row. `ui.compact` asks; a shape declines when the
|
|
177
|
-
*
|
|
177
|
+
* document sets its height, because a row is as tall as its tallest cell and the cell
|
|
178
178
|
* beside it does not grow in step: an object nests a whole field set, block richtext
|
|
179
179
|
* (`inline` absent) holds paragraphs, and an array holds however many elements the
|
|
180
180
|
* document carries. Any of them stands its neighbour in a column of whitespace. An
|
|
181
181
|
* inline prose leaf is one line tall and packs like any scalar.
|
|
182
182
|
*
|
|
183
|
-
* An array declines
|
|
183
|
+
* An array declines whatever its items are: one-line elements make a one-line step,
|
|
184
184
|
* but nothing holds two arrays to the same number of them, so the shorter of a packed
|
|
185
185
|
* pair pays a cell of whitespace for every element the taller one has past it, and
|
|
186
186
|
* pays more of it as the document is filled.
|
|
@@ -196,7 +196,7 @@ function packable(f) {
|
|
|
196
196
|
* Assign each field its span in the section grid. Consecutive packable fields are
|
|
197
197
|
* `cell`s the grid auto-places: capacity and wrapping are the container query's
|
|
198
198
|
* business, so a trailing orphan keeps its column width rather than growing to fill.
|
|
199
|
-
* A packable run of
|
|
199
|
+
* A packable run of one is `lone`: with no row above to align to, one column reads as
|
|
200
200
|
* truncated, so it takes half the capacity from column 1. Everything else is `full`.
|
|
201
201
|
*
|
|
202
202
|
* Pure, and stays pure: no width, no measurement, nothing to re-derive on resize.
|
|
@@ -250,7 +250,7 @@ export function bodyEnabled(cardSchema) {
|
|
|
250
250
|
return cardSchema?.body?.enabled !== false;
|
|
251
251
|
}
|
|
252
252
|
// ── Session identity (VISUAL_EDITOR §"The address is the spine") ─────────────
|
|
253
|
-
// Cards are
|
|
253
|
+
// Cards are positional in the content and `doc.cards` re-allocates on each read,
|
|
254
254
|
// so a stable card-instance key cannot be the card object (a fresh object every
|
|
255
255
|
// derive): it is a session id held in a parallel array, reordered in lockstep
|
|
256
256
|
// with the structure ops and resolved to an index only at the mutation boundary.
|
|
@@ -4,7 +4,7 @@ export interface SyncedLocal<T> {
|
|
|
4
4
|
value: T;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Seed a local from `project` and reconcile only an
|
|
7
|
+
* Seed a local from `project` and reconcile only an external change into it.
|
|
8
8
|
* `project` reads the reactive source (a prop) and maps it to the local's type:
|
|
9
9
|
* ` => value ?? ''`, ` => (value != null ? String(value) : '')`, etc. The
|
|
10
10
|
* seed is `untrack`ed (the ongoing sync is the `$effect`, not the initializer),
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// The controlled-local reconcile shared by the scalar controls (TextField /
|
|
2
2
|
// NumberField / DateField / BooleanField / EnumField). Each seeds a local from a
|
|
3
|
-
// projection of its `value` prop and reconciles
|
|
3
|
+
// projection of its `value` prop and reconciles only an external change back into
|
|
4
4
|
// the local: own edits stay `untrack`ed, so typing never re-runs the sync and
|
|
5
|
-
// resets the caret.
|
|
6
|
-
//
|
|
5
|
+
// resets the caret. One copy for all five, rather than a character-identical block
|
|
6
|
+
// in each (VISUAL_EDITOR §Surface).
|
|
7
7
|
import { untrack } from 'svelte';
|
|
8
8
|
/**
|
|
9
|
-
* Seed a local from `project` and reconcile only an
|
|
9
|
+
* Seed a local from `project` and reconcile only an external change into it.
|
|
10
10
|
* `project` reads the reactive source (a prop) and maps it to the local's type:
|
|
11
11
|
* ` => value ?? ''`, ` => (value != null ? String(value) : '')`, etc. The
|
|
12
12
|
* seed is `untrack`ed (the ongoing sync is the `$effect`, not the initializer),
|
package/dist/visual/tips.d.ts
CHANGED
|
@@ -12,13 +12,15 @@ export declare function tipsChannel(raw: unknown): string[];
|
|
|
12
12
|
* (`strong`/`emph`/`code`/`link`) rather than by a second markdown renderer that
|
|
13
13
|
* would drift from it.
|
|
14
14
|
*
|
|
15
|
-
* The
|
|
15
|
+
* The inline schema, not the block one: a tip is a one-line hint, and
|
|
16
16
|
* `decodeInline` folds stray lines to a space and drops island slots, so any string
|
|
17
17
|
* produces one paragraph: no tip can change the card's block structure as the
|
|
18
18
|
* cursor advances.
|
|
19
19
|
*
|
|
20
20
|
* Raw HTML does not survive the round-trip, so this is not the injection seam an
|
|
21
21
|
* `{@html}` of the same string would be. A throw degrades to the literal text:
|
|
22
|
-
* chrome never breaks the editor.
|
|
22
|
+
* chrome never breaks the editor. The gate is read outside that catch — an
|
|
23
|
+
* uninitialized core is not a tip that failed to render, and would otherwise be
|
|
24
|
+
* reported as one.
|
|
23
25
|
*/
|
|
24
26
|
export declare function renderTip(markdown: string, onError?: EditorErrorHandler): Node;
|
package/dist/visual/tips.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// The tips channel: `$ext.editor.tips`, a list of authoring hints a
|
|
2
|
-
// quill or consumer seeds, which the editor only ever
|
|
2
|
+
// quill or consumer seeds, which the editor only ever renders and clears.
|
|
3
3
|
// Editor-only chrome; never reaches the render backend, never gates, absent when
|
|
4
4
|
// the channel is empty (VISUAL_EDITOR §"Card operations").
|
|
5
5
|
//
|
|
6
6
|
// The channel narrowing the derive reads and the render the card paints. The write
|
|
7
7
|
// is `patchEditorExt` (ext.ts), the one door into the namespace.
|
|
8
|
-
import {
|
|
8
|
+
import { core } from '../core/lifecycle.js';
|
|
9
9
|
import { reportError, errorMessage } from '../core/errors.js';
|
|
10
10
|
import { renderContent, inlineSchema } from '../core/codec/index.js';
|
|
11
11
|
/**
|
|
@@ -25,16 +25,19 @@ export function tipsChannel(raw) {
|
|
|
25
25
|
* (`strong`/`emph`/`code`/`link`) rather than by a second markdown renderer that
|
|
26
26
|
* would drift from it.
|
|
27
27
|
*
|
|
28
|
-
* The
|
|
28
|
+
* The inline schema, not the block one: a tip is a one-line hint, and
|
|
29
29
|
* `decodeInline` folds stray lines to a space and drops island slots, so any string
|
|
30
30
|
* produces one paragraph: no tip can change the card's block structure as the
|
|
31
31
|
* cursor advances.
|
|
32
32
|
*
|
|
33
33
|
* Raw HTML does not survive the round-trip, so this is not the injection seam an
|
|
34
34
|
* `{@html}` of the same string would be. A throw degrades to the literal text:
|
|
35
|
-
* chrome never breaks the editor.
|
|
35
|
+
* chrome never breaks the editor. The gate is read outside that catch — an
|
|
36
|
+
* uninitialized core is not a tip that failed to render, and would otherwise be
|
|
37
|
+
* reported as one.
|
|
36
38
|
*/
|
|
37
39
|
export function renderTip(markdown, onError) {
|
|
40
|
+
const { importMarkdown } = core();
|
|
38
41
|
try {
|
|
39
42
|
return renderContent(importMarkdown(markdown), inlineSchema);
|
|
40
43
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quillmark/svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Editor, live-preview and source surfaces over a Quillmark WASM session.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"check": "svelte-check --tsconfig ./tsconfig.json"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@quillmark/wasm": ">=0.
|
|
69
|
+
"@quillmark/wasm": ">=0.103.0-0",
|
|
70
70
|
"svelte": "^5.0.0"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|