@quillmark/svelte 0.2.0 → 0.3.1
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/NOTICE +6 -6
- package/README.md +3 -3
- package/THEMING.md +12 -8
- package/dist/core/address.d.ts +7 -7
- package/dist/core/address.js +6 -6
- package/dist/core/bloom.d.ts +3 -32
- package/dist/core/bloom.js +19 -51
- 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 +10 -10
- package/dist/core/codec/field.d.ts +21 -26
- 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 +235 -221
- 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 +70 -26
- package/dist/core/errors.d.ts +5 -5
- package/dist/core/index.js +2 -2
- package/dist/core/lifecycle.js +1 -1
- package/dist/core/rebind.svelte.d.ts +2 -2
- package/dist/core/rebind.svelte.js +4 -4
- package/dist/core/teardown.d.ts +1 -1
- package/dist/core/teardown.js +1 -1
- package/dist/core/theme.css +170 -64
- package/dist/preset/index.css +1 -1
- package/dist/preset/recipes.css +31 -29
- package/dist/preset/scale.css +37 -26
- package/dist/preview/Preview.svelte +17 -27
- package/dist/preview/Preview.svelte.d.ts +4 -5
- package/dist/preview/bridge.js +6 -6
- package/dist/preview/controller.d.ts +8 -10
- package/dist/preview/controller.js +19 -44
- package/dist/preview/geometry.d.ts +8 -10
- package/dist/preview/geometry.js +8 -10
- package/dist/preview/index.js +4 -4
- package/dist/preview/paint.d.ts +1 -1
- package/dist/preview/paint.js +17 -19
- package/dist/visual/ArrayField.svelte +80 -66
- package/dist/visual/ArrayField.svelte.d.ts +1 -1
- package/dist/visual/BooleanField.svelte +8 -6
- package/dist/visual/BooleanField.svelte.d.ts +2 -2
- package/dist/visual/Card.svelte +160 -329
- package/dist/visual/Card.svelte.d.ts +2 -7
- 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 +1 -1
- package/dist/visual/EnumField.svelte +16 -16
- package/dist/visual/EnumField.svelte.d.ts +2 -2
- package/dist/visual/Field.svelte +128 -115
- package/dist/visual/Field.svelte.d.ts +1 -1
- package/dist/visual/FieldHint.svelte +24 -42
- package/dist/visual/FieldHint.svelte.d.ts +1 -1
- package/dist/visual/FieldLabel.svelte +8 -8
- package/dist/visual/FormatPopover.svelte +80 -35
- package/dist/visual/NumberField.svelte +5 -5
- package/dist/visual/NumberField.svelte.d.ts +1 -1
- package/dist/visual/ObjectField.svelte +9 -9
- package/dist/visual/ObjectField.svelte.d.ts +1 -1
- package/dist/visual/ProseArrayElement.svelte +11 -13
- package/dist/visual/ProseArrayElement.svelte.d.ts +1 -1
- package/dist/visual/ProseField.svelte +53 -87
- package/dist/visual/ProseField.svelte.d.ts +13 -19
- 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 +68 -56
- package/dist/visual/VisualEditor.svelte +6 -6
- package/dist/visual/VisualEditorInner.svelte +203 -290
- package/dist/visual/controls.css +74 -30
- package/dist/visual/diagnostics.d.ts +5 -5
- package/dist/visual/diagnostics.js +3 -3
- 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 +19 -0
- package/dist/visual/icons/nodes.js +13 -1
- package/dist/visual/index.js +4 -4
- package/dist/visual/leaves.d.ts +3 -3
- package/dist/visual/leaves.js +1 -1
- package/dist/visual/links.d.ts +24 -0
- package/dist/visual/links.js +67 -0
- package/dist/visual/props.d.ts +8 -8
- 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 +2 -2
- package/dist/visual/tips.js +3 -3
- package/package.json +2 -2
- package/dist/preview/overlay.d.ts +0 -11
- package/dist/preview/overlay.js +0 -97
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,14 +12,14 @@ 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. The gate is read
|
|
22
|
+
* chrome never breaks the editor. The gate is read outside that catch — an
|
|
23
23
|
* uninitialized core is not a tip that failed to render, and would otherwise be
|
|
24
24
|
* reported as one.
|
|
25
25
|
*/
|
package/dist/visual/tips.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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
|
//
|
|
@@ -25,14 +25,14 @@ 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. The gate is read
|
|
35
|
+
* chrome never breaks the editor. The gate is read outside that catch — an
|
|
36
36
|
* uninitialized core is not a tip that failed to render, and would otherwise be
|
|
37
37
|
* reported as one.
|
|
38
38
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quillmark/svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
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": ">=
|
|
26
|
+
"node": ">=24"
|
|
27
27
|
},
|
|
28
28
|
"svelte": "./dist/index.js",
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { LiveSession } from '@quillmark/wasm';
|
|
2
|
-
import type { PageSlot } from './paint.js';
|
|
3
|
-
export interface OverlayController {
|
|
4
|
-
/** Re-read geometry and rebuild every box; call after a layout-affecting refresh. */
|
|
5
|
-
refresh(): void;
|
|
6
|
-
/** Bloom the boxes at `field` AND under it: transient, and a no-op when `field`
|
|
7
|
-
* is already the marked one. */
|
|
8
|
-
flashField(field: string): void;
|
|
9
|
-
destroy(): void;
|
|
10
|
-
}
|
|
11
|
-
export declare function createOverlay(session: LiveSession, slots: readonly PageSlot[]): OverlayController;
|
package/dist/preview/overlay.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { bloom, bloomTiming, primeWash } from '../core/bloom.js';
|
|
2
|
-
import { boxesForField, rectToPercent, applyPercentRect } from './geometry.js';
|
|
3
|
-
const FIELD_ATTR = 'data-qm-field';
|
|
4
|
-
export function createOverlay(session, slots) {
|
|
5
|
-
const layers = slots.map((slot) => {
|
|
6
|
-
const layer = document.createElement('div');
|
|
7
|
-
layer.className = 'qm-overlay-layer';
|
|
8
|
-
Object.assign(layer.style, { position: 'absolute', inset: '0', pointerEvents: 'none' });
|
|
9
|
-
slot.el.appendChild(layer);
|
|
10
|
-
return layer;
|
|
11
|
-
});
|
|
12
|
-
// The marked address, and with it the bloom's start. ONE variable does both jobs:
|
|
13
|
-
// the change guard has to outlive the decay (a caret move after the wash ended
|
|
14
|
-
// must not re-mark the same field), and `bloom` refuses an `elapsed` past the
|
|
15
|
-
// decay, so a spent flash is inert on every later rebuild and never needs
|
|
16
|
-
// clearing. The editor→preview signal is continuous (every caret move, so every
|
|
17
|
-
// keystroke), and only a change of address is an event; marking the field being
|
|
18
|
-
// typed into is noise the recompile already answers, since the changed text
|
|
19
|
-
// repaints under the caret 120ms later.
|
|
20
|
-
let flash;
|
|
21
|
-
/** field → its boxes, rebuilt with them. The bloom's only lookup. */
|
|
22
|
-
let byField = new Map();
|
|
23
|
-
// The boxes a flash covers: the address's own, plus every one UNDER it, since the
|
|
24
|
-
// boxes are keyed as `regions()` names them (`main.references.0`) and an
|
|
25
|
-
// editor-side signal names the declared field (`main.references`). Resolved per
|
|
26
|
-
// apply rather than at `flashField`, so a rebuild picks up boxes the recompile
|
|
27
|
-
// moved or added.
|
|
28
|
-
function flashed(field) {
|
|
29
|
-
const els = [];
|
|
30
|
-
for (const [key, group] of byField) {
|
|
31
|
-
if (key === field || key.startsWith(`${field}.`))
|
|
32
|
-
els.push(...group);
|
|
33
|
-
}
|
|
34
|
-
return els;
|
|
35
|
-
}
|
|
36
|
-
// Start or RESUME the bloom on whatever boxes exist for it now: one path, because
|
|
37
|
-
// starting is resuming at ~0. A field surfaces several boxes and they share
|
|
38
|
-
// `startedAt`, so they bloom in step rather than shimmering unevenly.
|
|
39
|
-
function applyFlash() {
|
|
40
|
-
if (!flash)
|
|
41
|
-
return;
|
|
42
|
-
const els = flashed(flash.field);
|
|
43
|
-
if (!els.length)
|
|
44
|
-
return;
|
|
45
|
-
const elapsed = performance.now() - flash.startedAt;
|
|
46
|
-
const timing = bloomTiming(els[0]);
|
|
47
|
-
for (const el of els)
|
|
48
|
-
bloom(el, elapsed, timing);
|
|
49
|
-
}
|
|
50
|
-
// Field names come from `regions()` (the only session query that enumerates them;
|
|
51
|
-
// Preview carries no schema); the rects from `boxesForField`, whose fallback is
|
|
52
|
-
// what makes every address `regions()` names draw.
|
|
53
|
-
function build() {
|
|
54
|
-
for (const layer of layers)
|
|
55
|
-
layer.replaceChildren();
|
|
56
|
-
byField = new Map();
|
|
57
|
-
const regions = session.regions();
|
|
58
|
-
const fields = new Set();
|
|
59
|
-
for (const region of regions)
|
|
60
|
-
fields.add(region.field);
|
|
61
|
-
for (const field of fields) {
|
|
62
|
-
const els = [];
|
|
63
|
-
for (const box of boxesForField(field, session.fieldBoxes(field), regions)) {
|
|
64
|
-
const layer = layers[box.page];
|
|
65
|
-
const slot = slots[box.page];
|
|
66
|
-
if (!layer || !slot)
|
|
67
|
-
continue;
|
|
68
|
-
const pct = rectToPercent(box.rect, slot.size);
|
|
69
|
-
const el = document.createElement('div');
|
|
70
|
-
el.className = 'qm-field-box';
|
|
71
|
-
el.setAttribute(FIELD_ATTR, field);
|
|
72
|
-
applyPercentRect(el, pct);
|
|
73
|
-
el.style.borderRadius = 'var(--_qm-radius-inner)';
|
|
74
|
-
primeWash(el);
|
|
75
|
-
layer.appendChild(el);
|
|
76
|
-
els.push(el);
|
|
77
|
-
}
|
|
78
|
-
if (els.length)
|
|
79
|
-
byField.set(field, els);
|
|
80
|
-
}
|
|
81
|
-
applyFlash();
|
|
82
|
-
}
|
|
83
|
-
build();
|
|
84
|
-
return {
|
|
85
|
-
refresh: build,
|
|
86
|
-
flashField(field) {
|
|
87
|
-
if (field === flash?.field)
|
|
88
|
-
return;
|
|
89
|
-
flash = { field, startedAt: performance.now() };
|
|
90
|
-
applyFlash();
|
|
91
|
-
},
|
|
92
|
-
destroy() {
|
|
93
|
-
for (const layer of layers)
|
|
94
|
-
layer.remove();
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
}
|