@quillmark/svelte 0.0.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/LICENSE +201 -0
- package/NOTICE +58 -0
- package/README.md +241 -0
- package/THEMING.md +153 -0
- package/dist/core/address.d.ts +56 -0
- package/dist/core/address.js +95 -0
- package/dist/core/bloom.d.ts +37 -0
- package/dist/core/bloom.js +116 -0
- package/dist/core/codec/anchor.d.ts +19 -0
- package/dist/core/codec/anchor.js +36 -0
- package/dist/core/codec/code.d.ts +12 -0
- package/dist/core/codec/code.js +118 -0
- package/dist/core/codec/decode.d.ts +24 -0
- package/dist/core/codec/decode.js +270 -0
- package/dist/core/codec/encode.d.ts +71 -0
- package/dist/core/codec/encode.js +480 -0
- package/dist/core/codec/field.d.ts +171 -0
- package/dist/core/codec/field.js +493 -0
- package/dist/core/codec/index.d.ts +22 -0
- package/dist/core/codec/index.js +43 -0
- package/dist/core/codec/inputrules.d.ts +7 -0
- package/dist/core/codec/inputrules.js +168 -0
- package/dist/core/codec/islands.d.ts +36 -0
- package/dist/core/codec/islands.js +82 -0
- package/dist/core/codec/keymap.d.ts +13 -0
- package/dist/core/codec/keymap.js +29 -0
- package/dist/core/codec/lists.d.ts +22 -0
- package/dist/core/codec/lists.js +104 -0
- package/dist/core/codec/marks.d.ts +37 -0
- package/dist/core/codec/marks.js +74 -0
- package/dist/core/codec/positions.d.ts +17 -0
- package/dist/core/codec/positions.js +108 -0
- package/dist/core/codec/prose.css +458 -0
- package/dist/core/codec/reconcile.d.ts +26 -0
- package/dist/core/codec/reconcile.js +64 -0
- package/dist/core/codec/schema.d.ts +13 -0
- package/dist/core/codec/schema.js +155 -0
- package/dist/core/codec/slash.d.ts +79 -0
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-view.d.ts +50 -0
- package/dist/core/codec/table-view.js +928 -0
- package/dist/core/codec/table.d.ts +85 -0
- package/dist/core/codec/table.js +224 -0
- package/dist/core/errors.d.ts +73 -0
- package/dist/core/errors.js +24 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +19 -0
- package/dist/core/lifecycle.d.ts +7 -0
- package/dist/core/lifecycle.js +21 -0
- package/dist/core/rebind.svelte.d.ts +18 -0
- package/dist/core/rebind.svelte.js +44 -0
- package/dist/core/teardown.d.ts +20 -0
- package/dist/core/teardown.js +37 -0
- package/dist/core/theme.css +276 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +9 -0
- package/dist/preset/index.css +10 -0
- package/dist/preset/recipes.css +188 -0
- package/dist/preset/scale.css +97 -0
- package/dist/preview/Preview.svelte +118 -0
- package/dist/preview/Preview.svelte.d.ts +39 -0
- package/dist/preview/bridge.d.ts +13 -0
- package/dist/preview/bridge.js +88 -0
- package/dist/preview/controller.d.ts +40 -0
- package/dist/preview/controller.js +141 -0
- package/dist/preview/geometry.d.ts +37 -0
- package/dist/preview/geometry.js +45 -0
- package/dist/preview/index.d.ts +5 -0
- package/dist/preview/index.js +14 -0
- package/dist/preview/overlay.d.ts +10 -0
- package/dist/preview/overlay.js +82 -0
- package/dist/preview/paint.d.ts +17 -0
- package/dist/preview/paint.js +262 -0
- package/dist/preview/strings.d.ts +14 -0
- package/dist/preview/strings.js +22 -0
- package/dist/visual/ArrayField.svelte +362 -0
- package/dist/visual/ArrayField.svelte.d.ts +24 -0
- package/dist/visual/BooleanField.svelte +88 -0
- package/dist/visual/BooleanField.svelte.d.ts +20 -0
- package/dist/visual/Card.svelte +849 -0
- package/dist/visual/Card.svelte.d.ts +43 -0
- package/dist/visual/CardControls.svelte +92 -0
- package/dist/visual/CardControls.svelte.d.ts +11 -0
- package/dist/visual/DateField.svelte +203 -0
- package/dist/visual/DateField.svelte.d.ts +28 -0
- package/dist/visual/DiagnosticList.svelte +50 -0
- package/dist/visual/DiagnosticList.svelte.d.ts +7 -0
- package/dist/visual/EnumField.svelte +169 -0
- package/dist/visual/EnumField.svelte.d.ts +25 -0
- package/dist/visual/Field.svelte +270 -0
- package/dist/visual/Field.svelte.d.ts +37 -0
- package/dist/visual/FieldHint.svelte +185 -0
- package/dist/visual/FieldHint.svelte.d.ts +13 -0
- package/dist/visual/FieldLabel.svelte +141 -0
- package/dist/visual/FieldLabel.svelte.d.ts +19 -0
- package/dist/visual/FormatPopover.svelte +396 -0
- package/dist/visual/FormatPopover.svelte.d.ts +9 -0
- package/dist/visual/NumberField.svelte +72 -0
- package/dist/visual/NumberField.svelte.d.ts +19 -0
- package/dist/visual/ObjectField.svelte +131 -0
- package/dist/visual/ObjectField.svelte.d.ts +17 -0
- package/dist/visual/ProseArrayElement.svelte +98 -0
- package/dist/visual/ProseArrayElement.svelte.d.ts +19 -0
- package/dist/visual/ProseField.svelte +213 -0
- package/dist/visual/ProseField.svelte.d.ts +50 -0
- package/dist/visual/SlashMenu.svelte +126 -0
- package/dist/visual/SlashMenu.svelte.d.ts +14 -0
- package/dist/visual/TextField.svelte +63 -0
- package/dist/visual/TextField.svelte.d.ts +23 -0
- package/dist/visual/TipsCard.svelte +191 -0
- package/dist/visual/TipsCard.svelte.d.ts +13 -0
- package/dist/visual/VisualEditor.svelte +96 -0
- package/dist/visual/VisualEditor.svelte.d.ts +14 -0
- package/dist/visual/VisualEditorInner.svelte +1032 -0
- package/dist/visual/VisualEditorInner.svelte.d.ts +17 -0
- package/dist/visual/controls.css +272 -0
- package/dist/visual/diagnostics.d.ts +52 -0
- package/dist/visual/diagnostics.js +83 -0
- package/dist/visual/domid.d.ts +21 -0
- package/dist/visual/domid.js +36 -0
- package/dist/visual/ext.d.ts +19 -0
- package/dist/visual/ext.js +29 -0
- package/dist/visual/icons/Icon.svelte +39 -0
- package/dist/visual/icons/Icon.svelte.d.ts +12 -0
- package/dist/visual/icons/nodes.d.ts +103 -0
- package/dist/visual/icons/nodes.js +55 -0
- package/dist/visual/index.d.ts +9 -0
- package/dist/visual/index.js +24 -0
- package/dist/visual/motion.d.ts +18 -0
- package/dist/visual/motion.js +53 -0
- package/dist/visual/props.d.ts +77 -0
- package/dist/visual/props.js +1 -0
- package/dist/visual/signals.d.ts +59 -0
- package/dist/visual/signals.js +1 -0
- package/dist/visual/strings.d.ts +123 -0
- package/dist/visual/strings.js +80 -0
- package/dist/visual/structure.d.ts +224 -0
- package/dist/visual/structure.js +270 -0
- package/dist/visual/synced.svelte.d.ts +15 -0
- package/dist/visual/synced.svelte.js +34 -0
- package/dist/visual/tips.d.ts +24 -0
- package/dist/visual/tips.js +50 -0
- package/package.json +102 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/** One `<svg>` child: the element to make, and the attributes to set on it. */
|
|
2
|
+
export type IconNode = readonly (readonly [string, Record<string, string>])[];
|
|
3
|
+
export declare const ICONS: {
|
|
4
|
+
readonly 'chevron-right': readonly [readonly ["path", {
|
|
5
|
+
readonly d: "m9 18 6-6-6-6";
|
|
6
|
+
}]];
|
|
7
|
+
readonly 'chevron-down': readonly [readonly ["path", {
|
|
8
|
+
readonly d: "m6 9 6 6 6-6";
|
|
9
|
+
}]];
|
|
10
|
+
readonly 'chevron-up': readonly [readonly ["path", {
|
|
11
|
+
readonly d: "m18 15-6-6-6 6";
|
|
12
|
+
}]];
|
|
13
|
+
readonly bold: readonly [readonly ["path", {
|
|
14
|
+
readonly d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8";
|
|
15
|
+
}]];
|
|
16
|
+
readonly italic: readonly [readonly ["line", {
|
|
17
|
+
readonly x1: "19";
|
|
18
|
+
readonly x2: "10";
|
|
19
|
+
readonly y1: "4";
|
|
20
|
+
readonly y2: "4";
|
|
21
|
+
}], readonly ["line", {
|
|
22
|
+
readonly x1: "14";
|
|
23
|
+
readonly x2: "5";
|
|
24
|
+
readonly y1: "20";
|
|
25
|
+
readonly y2: "20";
|
|
26
|
+
}], readonly ["line", {
|
|
27
|
+
readonly x1: "15";
|
|
28
|
+
readonly x2: "9";
|
|
29
|
+
readonly y1: "4";
|
|
30
|
+
readonly y2: "20";
|
|
31
|
+
}]];
|
|
32
|
+
readonly underline: readonly [readonly ["path", {
|
|
33
|
+
readonly d: "M6 4v6a6 6 0 0 0 12 0V4";
|
|
34
|
+
}], readonly ["line", {
|
|
35
|
+
readonly x1: "4";
|
|
36
|
+
readonly x2: "20";
|
|
37
|
+
readonly y1: "20";
|
|
38
|
+
readonly y2: "20";
|
|
39
|
+
}]];
|
|
40
|
+
readonly strikethrough: readonly [readonly ["path", {
|
|
41
|
+
readonly d: "M16 4H9a3 3 0 0 0-2.83 4";
|
|
42
|
+
}], readonly ["path", {
|
|
43
|
+
readonly d: "M14 12a4 4 0 0 1 0 8H6";
|
|
44
|
+
}], readonly ["line", {
|
|
45
|
+
readonly x1: "4";
|
|
46
|
+
readonly x2: "20";
|
|
47
|
+
readonly y1: "12";
|
|
48
|
+
readonly y2: "12";
|
|
49
|
+
}]];
|
|
50
|
+
readonly code: readonly [readonly ["path", {
|
|
51
|
+
readonly d: "m16 18 6-6-6-6";
|
|
52
|
+
}], readonly ["path", {
|
|
53
|
+
readonly d: "m8 6-6 6 6 6";
|
|
54
|
+
}]];
|
|
55
|
+
readonly link: readonly [readonly ["path", {
|
|
56
|
+
readonly d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71";
|
|
57
|
+
}], readonly ["path", {
|
|
58
|
+
readonly d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71";
|
|
59
|
+
}]];
|
|
60
|
+
readonly hash: readonly [readonly ["line", {
|
|
61
|
+
readonly x1: "4";
|
|
62
|
+
readonly x2: "20";
|
|
63
|
+
readonly y1: "9";
|
|
64
|
+
readonly y2: "9";
|
|
65
|
+
}], readonly ["line", {
|
|
66
|
+
readonly x1: "4";
|
|
67
|
+
readonly x2: "20";
|
|
68
|
+
readonly y1: "15";
|
|
69
|
+
readonly y2: "15";
|
|
70
|
+
}], readonly ["line", {
|
|
71
|
+
readonly x1: "10";
|
|
72
|
+
readonly x2: "8";
|
|
73
|
+
readonly y1: "3";
|
|
74
|
+
readonly y2: "21";
|
|
75
|
+
}], readonly ["line", {
|
|
76
|
+
readonly x1: "16";
|
|
77
|
+
readonly x2: "14";
|
|
78
|
+
readonly y1: "3";
|
|
79
|
+
readonly y2: "21";
|
|
80
|
+
}]];
|
|
81
|
+
readonly info: readonly [readonly ["circle", {
|
|
82
|
+
readonly cx: "12";
|
|
83
|
+
readonly cy: "12";
|
|
84
|
+
readonly r: "10";
|
|
85
|
+
}], readonly ["path", {
|
|
86
|
+
readonly d: "M12 16v-4";
|
|
87
|
+
}], readonly ["path", {
|
|
88
|
+
readonly d: "M12 8h.01";
|
|
89
|
+
}]];
|
|
90
|
+
readonly plus: readonly [readonly ["path", {
|
|
91
|
+
readonly d: "M5 12h14";
|
|
92
|
+
}], readonly ["path", {
|
|
93
|
+
readonly d: "M12 5v14";
|
|
94
|
+
}]];
|
|
95
|
+
readonly x: readonly [readonly ["path", {
|
|
96
|
+
readonly d: "M18 6 6 18";
|
|
97
|
+
}], readonly ["path", {
|
|
98
|
+
readonly d: "m6 6 12 12";
|
|
99
|
+
}]];
|
|
100
|
+
};
|
|
101
|
+
/** The closed set. A name that is not here is a compile error at the call site,
|
|
102
|
+
* which is what a string key buys over thirteen imported components. */
|
|
103
|
+
export type IconName = keyof typeof ICONS;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// The chrome's glyph geometry, as the element tuples an `<svg>` carries: a tag and
|
|
2
|
+
// its attributes, against the 24×24 frame `Icon.svelte` draws. Every glyph the
|
|
3
|
+
// Svelte chrome shows is here; `core/codec/table-view.ts` holds its own set, that
|
|
4
|
+
// being the one place chrome is built without Svelte.
|
|
5
|
+
//
|
|
6
|
+
// Verbatim from lucide 1.28.0 — the version is stated because a glyph that drifts
|
|
7
|
+
// from upstream is otherwise invisible, and because the notices in `NOTICE` are the
|
|
8
|
+
// ones that release carries (ISC, and MIT for the Feather-derived ten).
|
|
9
|
+
export const ICONS = {
|
|
10
|
+
'chevron-right': [['path', { d: 'm9 18 6-6-6-6' }]],
|
|
11
|
+
'chevron-down': [['path', { d: 'm6 9 6 6 6-6' }]],
|
|
12
|
+
'chevron-up': [['path', { d: 'm18 15-6-6-6 6' }]],
|
|
13
|
+
bold: [['path', { d: 'M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8' }]],
|
|
14
|
+
italic: [
|
|
15
|
+
['line', { x1: '19', x2: '10', y1: '4', y2: '4' }],
|
|
16
|
+
['line', { x1: '14', x2: '5', y1: '20', y2: '20' }],
|
|
17
|
+
['line', { x1: '15', x2: '9', y1: '4', y2: '20' }]
|
|
18
|
+
],
|
|
19
|
+
underline: [
|
|
20
|
+
['path', { d: 'M6 4v6a6 6 0 0 0 12 0V4' }],
|
|
21
|
+
['line', { x1: '4', x2: '20', y1: '20', y2: '20' }]
|
|
22
|
+
],
|
|
23
|
+
strikethrough: [
|
|
24
|
+
['path', { d: 'M16 4H9a3 3 0 0 0-2.83 4' }],
|
|
25
|
+
['path', { d: 'M14 12a4 4 0 0 1 0 8H6' }],
|
|
26
|
+
['line', { x1: '4', x2: '20', y1: '12', y2: '12' }]
|
|
27
|
+
],
|
|
28
|
+
code: [
|
|
29
|
+
['path', { d: 'm16 18 6-6-6-6' }],
|
|
30
|
+
['path', { d: 'm8 6-6 6 6 6' }]
|
|
31
|
+
],
|
|
32
|
+
link: [
|
|
33
|
+
['path', { d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71' }],
|
|
34
|
+
['path', { d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71' }]
|
|
35
|
+
],
|
|
36
|
+
hash: [
|
|
37
|
+
['line', { x1: '4', x2: '20', y1: '9', y2: '9' }],
|
|
38
|
+
['line', { x1: '4', x2: '20', y1: '15', y2: '15' }],
|
|
39
|
+
['line', { x1: '10', x2: '8', y1: '3', y2: '21' }],
|
|
40
|
+
['line', { x1: '16', x2: '14', y1: '3', y2: '21' }]
|
|
41
|
+
],
|
|
42
|
+
info: [
|
|
43
|
+
['circle', { cx: '12', cy: '12', r: '10' }],
|
|
44
|
+
['path', { d: 'M12 16v-4' }],
|
|
45
|
+
['path', { d: 'M12 8h.01' }]
|
|
46
|
+
],
|
|
47
|
+
plus: [
|
|
48
|
+
['path', { d: 'M5 12h14' }],
|
|
49
|
+
['path', { d: 'M12 5v14' }]
|
|
50
|
+
],
|
|
51
|
+
x: [
|
|
52
|
+
['path', { d: 'M18 6 6 18' }],
|
|
53
|
+
['path', { d: 'm6 6 12 12' }]
|
|
54
|
+
]
|
|
55
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as VisualEditor } from './VisualEditor.svelte';
|
|
2
|
+
export type { VisualEditorProps } from './props.js';
|
|
3
|
+
export { createField } from '../core/codec/index.js';
|
|
4
|
+
export type { CreateFieldOpts, FieldController } from '../core/codec/index.js';
|
|
5
|
+
export { DEFAULT_VISUAL_STRINGS } from './strings.js';
|
|
6
|
+
export type { VisualStrings, VisualStringsInput, FormatDiagnostic } from './strings.js';
|
|
7
|
+
export type { BodyPlaceholder, BodyPlaceholderContext } from './structure.js';
|
|
8
|
+
export { patchEditorExt } from './ext.js';
|
|
9
|
+
export type { ActiveLeaf, CardId, ChangeSource, EditorChange } from './signals.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// `@quillmark/svelte/visual`: the WYSIWYG surface.
|
|
2
|
+
//
|
|
3
|
+
// The federated VisualEditor: a thin Svelte composition over many small editors.
|
|
4
|
+
// Each content leaf is the codec's `createField` prose leaf; scalar fields are
|
|
5
|
+
// form controls; structure/cards are the editor's own. Depends on `/core` and the
|
|
6
|
+
// codec. RELATIVE imports (not `$lib`): svelte-package ships this as-is.
|
|
7
|
+
//
|
|
8
|
+
// The barrel carries what a consumer story wants, and nothing else: a symbol
|
|
9
|
+
// earns its line when a caller outside the package needs it, and grows one when
|
|
10
|
+
// that caller arrives, not before.
|
|
11
|
+
export { default as VisualEditor } from './VisualEditor.svelte';
|
|
12
|
+
// The prose leaf: the vanilla mount API a non-Svelte consumer (or a custom
|
|
13
|
+
// surface) builds on.
|
|
14
|
+
export { createField } from '../core/codec/index.js';
|
|
15
|
+
// What the surface SAYS, and how it words a diagnostic. `strings` is keyed and
|
|
16
|
+
// partial (unset keys take the package's English) and `DEFAULT_VISUAL_STRINGS` is
|
|
17
|
+
// that English, exported so a consumer can compose against it rather than restate
|
|
18
|
+
// it. The empty body's per-kind hook is the `bodyPlaceholder` key inside it.
|
|
19
|
+
export { DEFAULT_VISUAL_STRINGS } from './strings.js';
|
|
20
|
+
// The `$ext.editor` write unit. A consumer seeding editor-side chrome
|
|
21
|
+
// state (a card title, the tips channel) goes through this rather than
|
|
22
|
+
// `storeExtNamespace`, which replaces the namespace and takes the sibling keys with
|
|
23
|
+
// it.
|
|
24
|
+
export { patchEditorExt } from './ext.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AnimationConfig } from 'svelte/animate';
|
|
2
|
+
/**
|
|
3
|
+
* Slide a keyed slot from where it was to where it now is.
|
|
4
|
+
*
|
|
5
|
+
* `armed` is why this is the reorder and not every reconcile: `animate:` fires wherever
|
|
6
|
+
* a slot's rect moved, and a card growing under the caret moves every slot below it,
|
|
7
|
+
* which is a layout change with no trip in it and one that would draw those slots over
|
|
8
|
+
* the content that displaced them for the length of the run. So the reorder command
|
|
9
|
+
* arms the gesture and the frame it lands in disarms it, which is the admission test
|
|
10
|
+
* read off the endpoints: two positions in the list, not two positions on the page.
|
|
11
|
+
*
|
|
12
|
+
* Both endpoints are rest states, so the curve is `--_qm-ease-reverse` (a second click
|
|
13
|
+
* lands mid-slide) and the rung is `slow` (the trip moves the stack around it).
|
|
14
|
+
*/
|
|
15
|
+
export declare function reorder(node: HTMLElement, { from, to }: {
|
|
16
|
+
from: DOMRect;
|
|
17
|
+
to: DOMRect;
|
|
18
|
+
}, armed: () => boolean): AnimationConfig;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// The card stack's reorder trip: a slot's position ↔ position in a
|
|
2
|
+
// keyed list, the one row of the mechanism table no CSS rule can serve, because the
|
|
3
|
+
// two frames belong to different elements. Svelte's `animate:` supplies the halves CSS
|
|
4
|
+
// cannot see (the rects either side of the reconciliation) and WAAPI paints the trip,
|
|
5
|
+
// the same seam `core/bloom.ts` takes to keep a duration in the derivation while a
|
|
6
|
+
// script runs the animation.
|
|
7
|
+
/** Marks this module's runs, so a second reorder cancels the first rather than layering
|
|
8
|
+
* a second transform over it. */
|
|
9
|
+
const RUN_ID = 'qm-reorder';
|
|
10
|
+
/** A duration rung off `style`, in ms; `undefined` where the derivation is out of reach
|
|
11
|
+
* (an unstyled root, or jsdom, where `getComputedStyle` reports custom properties as
|
|
12
|
+
* empty). No rung, no motion: a fallback here would be the scale restated in the one
|
|
13
|
+
* place `check:style` cannot read. */
|
|
14
|
+
function rungMs(style, rung) {
|
|
15
|
+
const raw = style.getPropertyValue(rung).trim();
|
|
16
|
+
const n = Number.parseFloat(raw);
|
|
17
|
+
if (!Number.isFinite(n))
|
|
18
|
+
return undefined;
|
|
19
|
+
return raw.endsWith('ms') ? n : n * 1000;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Slide a keyed slot from where it was to where it now is.
|
|
23
|
+
*
|
|
24
|
+
* `armed` is why this is the reorder and not every reconcile: `animate:` fires wherever
|
|
25
|
+
* a slot's rect moved, and a card growing under the caret moves every slot below it,
|
|
26
|
+
* which is a layout change with no trip in it and one that would draw those slots over
|
|
27
|
+
* the content that displaced them for the length of the run. So the reorder command
|
|
28
|
+
* arms the gesture and the frame it lands in disarms it, which is the admission test
|
|
29
|
+
* read off the endpoints: two positions in the list, not two positions on the page.
|
|
30
|
+
*
|
|
31
|
+
* Both endpoints are rest states, so the curve is `--_qm-ease-reverse` (a second click
|
|
32
|
+
* lands mid-slide) and the rung is `slow` (the trip moves the stack around it).
|
|
33
|
+
*/
|
|
34
|
+
export function reorder(node, { from, to }, armed) {
|
|
35
|
+
// The returned config is empty by design, in both exits and the run below: Svelte
|
|
36
|
+
// samples a config's keyframes through a JS easing function, and this curve is a
|
|
37
|
+
// rung the derivation mints and `check:style` holds.
|
|
38
|
+
if (!armed() || typeof node.animate !== 'function')
|
|
39
|
+
return {};
|
|
40
|
+
const style = getComputedStyle(node);
|
|
41
|
+
const duration = rungMs(style, '--_qm-duration-slow');
|
|
42
|
+
const easing = style.getPropertyValue('--_qm-ease-reverse').trim();
|
|
43
|
+
if (duration === undefined || !easing)
|
|
44
|
+
return {};
|
|
45
|
+
for (const running of node.getAnimations())
|
|
46
|
+
if (running.id === RUN_ID)
|
|
47
|
+
running.cancel();
|
|
48
|
+
node.animate([
|
|
49
|
+
{ transform: `translate(${from.left - to.left}px, ${from.top - to.top}px)` },
|
|
50
|
+
{ transform: 'none' }
|
|
51
|
+
], { duration, easing, id: RUN_ID });
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Document, Quill, Addr, Diagnostic } from '@quillmark/wasm';
|
|
2
|
+
import type { Place } from '../core/address.js';
|
|
3
|
+
import type { EditorErrorHandler } from '../core/errors.js';
|
|
4
|
+
import type { ActiveLeaf, EditorChange } from './signals.js';
|
|
5
|
+
import type { FormatDiagnostic, VisualStringsInput } from './strings.js';
|
|
6
|
+
export interface VisualEditorProps {
|
|
7
|
+
doc: Document;
|
|
8
|
+
quill: Quill;
|
|
9
|
+
/**
|
|
10
|
+
* The active leaf: its canonical `DocPath` and the session key of the card holding
|
|
11
|
+
* it, which is what a host tracking the active card keeps across a reorder
|
|
12
|
+
* (`CardId`).
|
|
13
|
+
*/
|
|
14
|
+
onActiveLeafChange?: (active: ActiveLeaf) => void;
|
|
15
|
+
/**
|
|
16
|
+
* A caret move in the active leaf, carrying the canonical `DocPath` the
|
|
17
|
+
* preview also speaks: `onCaretMove={preview.focusPosition}` is the whole
|
|
18
|
+
* editor→preview bridge, no translation and no `doc.cards` read on the
|
|
19
|
+
* keystroke path (the editor mints the path off its derived card tree, which
|
|
20
|
+
* already holds every kind).
|
|
21
|
+
*
|
|
22
|
+
* A SELECTION signal, not a change signal: an arrow key fires it and commits
|
|
23
|
+
* nothing. One keystroke fires `onChange` first and this second, so the edit
|
|
24
|
+
* has landed by the time the caret names where it is.
|
|
25
|
+
*
|
|
26
|
+
* A PLACE, so consecutive duplicates do not arrive: landing the caret where it
|
|
27
|
+
* already sits reports nothing, and a place left and returned to reports twice.
|
|
28
|
+
*/
|
|
29
|
+
onCaretMove?: (at: Place) => void;
|
|
30
|
+
/**
|
|
31
|
+
* EVERY edit that lands on the document: a prose commit, a scalar/array/object
|
|
32
|
+
* write, a card operation. The signal a host recompiles off; `source` is what
|
|
33
|
+
* moved, for a host that wants a structure op to recompile at once and a
|
|
34
|
+
* keystroke to wait for the burst to settle.
|
|
35
|
+
*/
|
|
36
|
+
onChange?: (change: EditorChange) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Failures the editor RECOVERED from: a commit the boundary refused (also
|
|
39
|
+
* pinned as a diagnostic on its field), a card operation that threw, a
|
|
40
|
+
* `validate`/`resolve` that threw, a prose commit that fell back. None of them
|
|
41
|
+
* stop editing; without this hook each is a `console.error` an app cannot
|
|
42
|
+
* route. Reaches the prose leaves too, so one handler covers the surface.
|
|
43
|
+
*/
|
|
44
|
+
onError?: EditorErrorHandler;
|
|
45
|
+
/**
|
|
46
|
+
* External diagnostics, routed by `.path` and merged with `quill.validate`
|
|
47
|
+
* and local commit errors (VISUAL_EDITOR §Diagnostics).
|
|
48
|
+
*/
|
|
49
|
+
diagnostics?: Diagnostic[];
|
|
50
|
+
/**
|
|
51
|
+
* Consumer policy hook: given a field `addr` and an enum option,
|
|
52
|
+
* return `false` to mark that option unavailable. A disallowed option renders
|
|
53
|
+
* DISABLED (never stripped), so an already-authored value stays visible and its
|
|
54
|
+
* stored payload is untouched: the schema is unchanged, this is runtime policy.
|
|
55
|
+
* Absent → every schema option is offered (the default, zero behavior change).
|
|
56
|
+
*/
|
|
57
|
+
enumOptionAllowed?: (addr: Addr, value: string) => boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Every word the surface says, keyed and partial: unset keys take the package's
|
|
60
|
+
* English. Several are ACCESSIBLE NAMES rather than decoration, so this is what
|
|
61
|
+
* a product shipping in another language replaces. The empty body's per-kind
|
|
62
|
+
* wording is the `bodyPlaceholder` key here rather than a prop of its own.
|
|
63
|
+
*/
|
|
64
|
+
strings?: VisualStringsInput;
|
|
65
|
+
/**
|
|
66
|
+
* A boundary `Diagnostic` → the text shown under its field. Returning
|
|
67
|
+
* `undefined` takes the diagnostic's own message, which is the arm the parse and
|
|
68
|
+
* render lanes need: their parameters exist only inside that English string.
|
|
69
|
+
*/
|
|
70
|
+
formatDiagnostic?: FormatDiagnostic;
|
|
71
|
+
/** Appended to the root's own class: the surface is a mounted element the
|
|
72
|
+
* consumer positions, so it needs a handle for layout it owns. */
|
|
73
|
+
class?: string;
|
|
74
|
+
/** Merged onto the root. Free because the derivation moved off this attribute
|
|
75
|
+
* and onto `data-qm-root` (core/theme.css). */
|
|
76
|
+
style?: string;
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { DocPath } from '../core/address.js';
|
|
2
|
+
/**
|
|
3
|
+
* Which lane an edit came down. The three differ in COST, not in meaning: prose
|
|
4
|
+
* arrives per keystroke and wants a debounce, a structure op arrives once per
|
|
5
|
+
* gesture and does not. A host that recompiles the same way for all three never
|
|
6
|
+
* reads it.
|
|
7
|
+
*/
|
|
8
|
+
export type ChangeSource =
|
|
9
|
+
/** A content commit from a prose leaf's own edit. */
|
|
10
|
+
'prose'
|
|
11
|
+
/** A scalar/array/object write through the typed writer, or a field cleared. */
|
|
12
|
+
| 'field'
|
|
13
|
+
/** A card operation: add, move, remove, retype, rename, tips dismissal. */
|
|
14
|
+
| 'structure';
|
|
15
|
+
/**
|
|
16
|
+
* A card's SESSION key: the editor's own identity for a card instance, `'main'` for
|
|
17
|
+
* the main card and an opaque `IdSeq` id (`structure.ts`) for a composable one. It
|
|
18
|
+
* rides beside every address the surface emits, because no address survives a
|
|
19
|
+
* reorder: `Addr` and `DocPath` are both positional, so a host holding
|
|
20
|
+
* `cards.indorsement[2].from` names a different card after one `moveCard`, and
|
|
21
|
+
* silently. The key does not move.
|
|
22
|
+
*
|
|
23
|
+
* SESSION-SCOPED BY CONSTRUCTION. Nothing document-side backs it (the document model
|
|
24
|
+
* carries no card handle): it is minted at mount and dies with the surface, so it
|
|
25
|
+
* does not survive a reload and a host persisting one is persisting a session key.
|
|
26
|
+
*
|
|
27
|
+
* THE EDITOR OWNS STRUCTURAL MUTATION for the session. The keys track the inserts,
|
|
28
|
+
* moves and removals the editor performs; a host that mutates the card array behind
|
|
29
|
+
* it (its own `doc.insertCard`) desyncs them, and the answer is to re-seed the
|
|
30
|
+
* surface rather than expect the keys to follow. There is nothing document-side to
|
|
31
|
+
* reconcile against, so this is a contract rather than a mechanism.
|
|
32
|
+
*/
|
|
33
|
+
export type CardId = string;
|
|
34
|
+
/** An edit that LANDED on the document, and which lane it came down. */
|
|
35
|
+
export interface EditorChange {
|
|
36
|
+
source: ChangeSource;
|
|
37
|
+
/**
|
|
38
|
+
* Where it landed, in the canonical `DocPath` the preview and every diagnostic
|
|
39
|
+
* already speak: a FIELD for the prose and field lanes, the CARD itself
|
|
40
|
+
* (`cards.<kind>[i]`) for a structure op, where the change is the card rather than
|
|
41
|
+
* anything inside it. Absent for a card removal and a tips dismissal, which are
|
|
42
|
+
* the stack's change rather than a leaf's.
|
|
43
|
+
*/
|
|
44
|
+
path?: DocPath;
|
|
45
|
+
/**
|
|
46
|
+
* Which card it landed in ({@link CardId}). Present for a removal, whose `path`
|
|
47
|
+
* is not: the index is meaningless once the card is gone, and the key is the only
|
|
48
|
+
* handle a host tracking cards has left to drop. Absent only for the tips
|
|
49
|
+
* dismissal, which is document-level chrome and names no card.
|
|
50
|
+
*/
|
|
51
|
+
cardId?: CardId;
|
|
52
|
+
}
|
|
53
|
+
/** The leaf that has focus: where it sits, and which card holds it. */
|
|
54
|
+
export interface ActiveLeaf {
|
|
55
|
+
/** The leaf's own address, the same `DocPath` a `Place`, a `ContentHit` and a
|
|
56
|
+
* `Diagnostic` name a field with. */
|
|
57
|
+
field: DocPath;
|
|
58
|
+
cardId: CardId;
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { Diagnostic } from '@quillmark/wasm';
|
|
2
|
+
import { type SlashStrings, type TableChromeStrings } from '../core/codec/index.js';
|
|
3
|
+
import type { BodyPlaceholder } from './structure.js';
|
|
4
|
+
/**
|
|
5
|
+
* The island and the insert menu's keys come from the CODEC, which owns both models
|
|
6
|
+
* (a NodeView inside the leaf, and the trigger the leaf's keymap drives). Extending
|
|
7
|
+
* rather than restating them is what keeps one English list: a consumer overrides an
|
|
8
|
+
* island handle's name beside the card controls' and the codec still has a default
|
|
9
|
+
* for a leaf mounted directly.
|
|
10
|
+
*/
|
|
11
|
+
export interface VisualStrings extends TableChromeStrings, SlashStrings {
|
|
12
|
+
cardMoveUp: string;
|
|
13
|
+
cardMoveDown: string;
|
|
14
|
+
cardDelete: string;
|
|
15
|
+
/** The card-title input's accessible name. */
|
|
16
|
+
cardTitle: string;
|
|
17
|
+
/** The multi-kind trigger, which names no kind because it opens a menu of them. */
|
|
18
|
+
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
|
+
arrayAdd: string;
|
|
28
|
+
arrayRemove: string;
|
|
29
|
+
/** The required marker's accessible name; the glyph itself is a `*`. */
|
|
30
|
+
fieldRequired: string;
|
|
31
|
+
formatGroup: string;
|
|
32
|
+
formatBold: string;
|
|
33
|
+
formatEmphasis: string;
|
|
34
|
+
formatUnderline: string;
|
|
35
|
+
formatStrikethrough: string;
|
|
36
|
+
formatCode: string;
|
|
37
|
+
formatLink: string;
|
|
38
|
+
/** The anchor button's accessible name. */
|
|
39
|
+
formatAnchor: string;
|
|
40
|
+
/** Its hover title, which says what an anchor IS; the name above only says what. */
|
|
41
|
+
formatAnchorTitle: string;
|
|
42
|
+
linkPlaceholder: string;
|
|
43
|
+
linkApply: string;
|
|
44
|
+
linkCancel: string;
|
|
45
|
+
tipsLabel: string;
|
|
46
|
+
tipsDismiss: string;
|
|
47
|
+
tipsNext: string;
|
|
48
|
+
tipsDot: (index: number, count: number) => string;
|
|
49
|
+
/** The whole sentence, since a translator orders the kind within it. */
|
|
50
|
+
unknownKind: (kind: string) => string;
|
|
51
|
+
retypeLabel: string;
|
|
52
|
+
retypePlaceholder: string;
|
|
53
|
+
noCardKinds: string;
|
|
54
|
+
/**
|
|
55
|
+
* The flat built-in invitation: the package's own words, kept to the one thing
|
|
56
|
+
* true of every body so it claims nothing about the card it sits in.
|
|
57
|
+
*/
|
|
58
|
+
bodyGhost: string;
|
|
59
|
+
/**
|
|
60
|
+
* Per-KIND wording, in place of {@link VisualStrings.bodyGhost}; `undefined`
|
|
61
|
+
* takes it. Consulted once per kind per session and cached by the editor, so a
|
|
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.
|
|
64
|
+
*/
|
|
65
|
+
bodyPlaceholder: BodyPlaceholder;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The hook that turns a boundary `Diagnostic` into displayed text.
|
|
69
|
+
*
|
|
70
|
+
* A formatter reads the whole `Diagnostic`, including `args`: the facts `message`
|
|
71
|
+
* interpolates, keyed by name. What it can re-word tracks what that type carries per
|
|
72
|
+
* lane (VISUAL_EDITOR §Diagnostics for why each lands where it does):
|
|
73
|
+
*
|
|
74
|
+
* - **`validation::enum_violation`, `type_mismatch`, `format_violation`,
|
|
75
|
+
* `must_fill`**: the CONSTRAINT re-words from the quill's schema at `path`, the
|
|
76
|
+
* offending VALUE from `args` (`value` / `sourceToken` / `actual`, the engine
|
|
77
|
+
* testifying to what it saw) and never from the document at `path`: validation
|
|
78
|
+
* runs post-coercion, so the validator read a value the document does not hold and
|
|
79
|
+
* a sentence built from `path` names a spelling the user never typed.
|
|
80
|
+
* - **`edit::field_coercion_failed`**: re-words from the app's own control state, the
|
|
81
|
+
* refused value being in neither document (unchanged on throw) nor schema.
|
|
82
|
+
* - **`parse::yaml_error_with_location`, `invalid_structure`**: does not re-word. No
|
|
83
|
+
* `path`, and `args` carries the LOCATION (`blockIndex`, `line`) and nothing else,
|
|
84
|
+
* engine prose riding under no key: the parser's own text, its column and its caret
|
|
85
|
+
* snippet exist only inside `message`.
|
|
86
|
+
* - **`LiveSession.warnings`**: does not re-word. Backend text, an external feed.
|
|
87
|
+
*
|
|
88
|
+
* The last two are the boundary's shape, not a gap in it, so the fallback arm is
|
|
89
|
+
* permanent: returning `undefined` renders `d.message` unchanged. `code` is optional
|
|
90
|
+
* at this pin, so that arm is reachable by TYPE and not only in principle.
|
|
91
|
+
*
|
|
92
|
+
* Displayed text is that message or this replacement and nothing beside it: `hint` is
|
|
93
|
+
* the tail of the message it accompanies, so rendering it too ships a two-language
|
|
94
|
+
* diagnostic.
|
|
95
|
+
*/
|
|
96
|
+
export type FormatDiagnostic = (d: Diagnostic) => string | undefined;
|
|
97
|
+
/** The package's English. Every key, so the merge below is total. */
|
|
98
|
+
export declare const DEFAULT_VISUAL_STRINGS: VisualStrings;
|
|
99
|
+
/** A consumer's wording: any subset, the rest the package's. */
|
|
100
|
+
export type VisualStringsInput = Partial<VisualStrings>;
|
|
101
|
+
/** Fill a partial override to the whole key set. */
|
|
102
|
+
export declare function mergeStrings(custom: VisualStringsInput | undefined): VisualStrings;
|
|
103
|
+
/**
|
|
104
|
+
* Everything the surface says, as the tree reads it. Getters rather than values,
|
|
105
|
+
* so a consumer swapping locale mid-session re-renders rather than freezing the
|
|
106
|
+
* wording at mount. The two seams travel together because a product needs both or
|
|
107
|
+
* neither: wording it cannot apply to the diagnostics beside it is half a
|
|
108
|
+
* translation.
|
|
109
|
+
*/
|
|
110
|
+
export interface Wording {
|
|
111
|
+
readonly strings: VisualStrings;
|
|
112
|
+
readonly formatDiagnostic: FormatDiagnostic | undefined;
|
|
113
|
+
}
|
|
114
|
+
export declare function setWording(channel: Wording): void;
|
|
115
|
+
/**
|
|
116
|
+
* The wording for a component under a mounted editor. Falls to the package's
|
|
117
|
+
* English off-tree, so a component rendered outside the editor (a test, a consumer
|
|
118
|
+
* composing one directly) still has every key.
|
|
119
|
+
*/
|
|
120
|
+
export declare function wording(): Wording;
|
|
121
|
+
/** A diagnostic's displayed text: the consumer's formatter, or its message. The
|
|
122
|
+
* fallback arm is permanent, not a courtesy ({@link FormatDiagnostic}). */
|
|
123
|
+
export declare function diagnosticText(d: Diagnostic, format: FormatDiagnostic | undefined): string;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Every word the visual surface says, in one place, so a product shipping in
|
|
2
|
+
// another language can replace them. Keyed and PARTIAL: a consumer sets the keys
|
|
3
|
+
// it has translations for and the rest take the package's English, which is what
|
|
4
|
+
// makes wording an override rather than a fork.
|
|
5
|
+
//
|
|
6
|
+
// Several of these are ACCESSIBLE NAMES rather than decoration — the card
|
|
7
|
+
// controls, the add trigger, the required marker — so an untranslated surface is
|
|
8
|
+
// not merely inconsistent, it reads the wrong language to a screen reader. That is
|
|
9
|
+
// why this exists as a seam and not as a list of literals to grep for.
|
|
10
|
+
//
|
|
11
|
+
// A key is a plain string where the text is fixed and a function where it is not:
|
|
12
|
+
// a parametric string cannot be assembled from fragments and stay translatable,
|
|
13
|
+
// since the word order is the translator's to choose.
|
|
14
|
+
//
|
|
15
|
+
// The strings reach the tree through CONTEXT, not props. They are ambient,
|
|
16
|
+
// read-only, and wanted eight components deep (a card's controls, a field's
|
|
17
|
+
// required marker, the formatting popover, the tips card); threading them by hand
|
|
18
|
+
// would put a `strings` prop on every component between the root and each leaf,
|
|
19
|
+
// which is a prop that means nothing to any of them.
|
|
20
|
+
import { getContext, setContext } from 'svelte';
|
|
21
|
+
import { DEFAULT_SLASH_STRINGS, DEFAULT_TABLE_STRINGS } from '../core/codec/index.js';
|
|
22
|
+
/** The package's English. Every key, so the merge below is total. */
|
|
23
|
+
export const DEFAULT_VISUAL_STRINGS = {
|
|
24
|
+
...DEFAULT_TABLE_STRINGS,
|
|
25
|
+
...DEFAULT_SLASH_STRINGS,
|
|
26
|
+
cardMoveUp: 'Move up',
|
|
27
|
+
cardMoveDown: 'Move down',
|
|
28
|
+
cardDelete: 'Delete card',
|
|
29
|
+
cardTitle: 'Card title',
|
|
30
|
+
addCard: 'Add card',
|
|
31
|
+
addCardOfKind: (kind) => `Add ${kind}`,
|
|
32
|
+
addCardMark: (kind) => `+ Add ${kind}`,
|
|
33
|
+
addCardMarkAny: '+ Add Card',
|
|
34
|
+
arrayAdd: '+ Add',
|
|
35
|
+
arrayRemove: 'Remove',
|
|
36
|
+
fieldRequired: 'required',
|
|
37
|
+
formatGroup: 'Formatting',
|
|
38
|
+
formatBold: 'Bold (Mod-B)',
|
|
39
|
+
formatEmphasis: 'Emphasis (Mod-I)',
|
|
40
|
+
formatUnderline: 'Underline (Mod-U)',
|
|
41
|
+
formatStrikethrough: 'Strikethrough',
|
|
42
|
+
formatCode: 'Code',
|
|
43
|
+
formatLink: 'Link',
|
|
44
|
+
formatAnchor: 'Anchor',
|
|
45
|
+
formatAnchorTitle: 'Anchor — an identity handle over the selection',
|
|
46
|
+
linkPlaceholder: 'https://…',
|
|
47
|
+
linkApply: 'Apply',
|
|
48
|
+
linkCancel: 'Cancel',
|
|
49
|
+
tipsLabel: 'Editor tips',
|
|
50
|
+
tipsDismiss: 'Dismiss',
|
|
51
|
+
tipsNext: 'Next',
|
|
52
|
+
tipsDot: (index, count) => `Tip ${index} of ${count}`,
|
|
53
|
+
unknownKind: (kind) => `Unrecognized card type ${kind}. Its content is preserved.`,
|
|
54
|
+
retypeLabel: 'Change to',
|
|
55
|
+
retypePlaceholder: 'Choose a type…',
|
|
56
|
+
noCardKinds: 'This document declares no card types — delete this card to remove it.',
|
|
57
|
+
bodyGhost: 'Write…',
|
|
58
|
+
bodyPlaceholder: () => undefined
|
|
59
|
+
};
|
|
60
|
+
/** Fill a partial override to the whole key set. */
|
|
61
|
+
export function mergeStrings(custom) {
|
|
62
|
+
return custom ? { ...DEFAULT_VISUAL_STRINGS, ...custom } : DEFAULT_VISUAL_STRINGS;
|
|
63
|
+
}
|
|
64
|
+
const KEY = Symbol('qm.wording');
|
|
65
|
+
export function setWording(channel) {
|
|
66
|
+
setContext(KEY, channel);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The wording for a component under a mounted editor. Falls to the package's
|
|
70
|
+
* English off-tree, so a component rendered outside the editor (a test, a consumer
|
|
71
|
+
* composing one directly) still has every key.
|
|
72
|
+
*/
|
|
73
|
+
export function wording() {
|
|
74
|
+
return (getContext(KEY) ?? { strings: DEFAULT_VISUAL_STRINGS, formatDiagnostic: undefined });
|
|
75
|
+
}
|
|
76
|
+
/** A diagnostic's displayed text: the consumer's formatter, or its message. The
|
|
77
|
+
* fallback arm is permanent, not a courtesy ({@link FormatDiagnostic}). */
|
|
78
|
+
export function diagnosticText(d, format) {
|
|
79
|
+
return (format ? format(d) : undefined) ?? d.message;
|
|
80
|
+
}
|