@quillmark/svelte 0.1.1 → 0.2.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 +15 -11
- package/THEMING.md +42 -23
- package/dist/core/address.d.ts +39 -4
- package/dist/core/address.js +47 -19
- package/dist/core/codec/encode.js +4 -1
- package/dist/core/codec/field.d.ts +1 -1
- package/dist/core/codec/prose.css +41 -18
- package/dist/core/codec/table.js +3 -1
- package/dist/core/errors.d.ts +15 -5
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +5 -4
- package/dist/core/lifecycle.d.ts +21 -5
- package/dist/core/lifecycle.js +34 -8
- package/dist/core/rebind.svelte.d.ts +16 -2
- package/dist/core/rebind.svelte.js +26 -8
- package/dist/core/theme.css +43 -18
- package/dist/preset/recipes.css +55 -8
- package/dist/preset/scale.css +5 -4
- package/dist/preview/Preview.svelte +10 -9
- package/dist/preview/Preview.svelte.d.ts +5 -5
- package/dist/preview/bridge.d.ts +6 -4
- package/dist/preview/bridge.js +17 -7
- package/dist/preview/controller.d.ts +13 -6
- package/dist/preview/controller.js +5 -2
- package/dist/preview/geometry.d.ts +16 -1
- package/dist/preview/geometry.js +20 -0
- package/dist/preview/index.d.ts +1 -0
- package/dist/preview/index.js +7 -0
- package/dist/preview/overlay.d.ts +2 -1
- package/dist/preview/overlay.js +24 -9
- package/dist/preview/paint.js +4 -0
- package/dist/visual/ArrayField.svelte +26 -20
- package/dist/visual/ArrayField.svelte.d.ts +4 -3
- package/dist/visual/Card.svelte +21 -19
- package/dist/visual/Card.svelte.d.ts +8 -3
- package/dist/visual/DiagnosticList.svelte +1 -1
- package/dist/visual/EnumField.svelte +34 -14
- package/dist/visual/EnumField.svelte.d.ts +4 -3
- package/dist/visual/Field.svelte +87 -25
- package/dist/visual/Field.svelte.d.ts +11 -7
- package/dist/visual/FieldHint.svelte +59 -10
- package/dist/visual/FormatPopover.svelte +3 -1
- package/dist/visual/ObjectField.svelte +21 -2
- package/dist/visual/ObjectField.svelte.d.ts +3 -1
- package/dist/visual/ProseArrayElement.svelte +1 -6
- package/dist/visual/ProseArrayElement.svelte.d.ts +0 -1
- package/dist/visual/ProseField.svelte +20 -12
- package/dist/visual/ProseField.svelte.d.ts +5 -3
- package/dist/visual/TipsCard.svelte +1 -1
- package/dist/visual/VisualEditor.svelte +12 -13
- package/dist/visual/VisualEditor.svelte.d.ts +2 -3
- package/dist/visual/VisualEditorInner.svelte +140 -66
- package/dist/visual/VisualEditorInner.svelte.d.ts +2 -3
- package/dist/visual/controls.css +14 -11
- package/dist/visual/diagnostics.d.ts +12 -20
- package/dist/visual/diagnostics.js +6 -18
- package/dist/visual/index.d.ts +1 -0
- package/dist/visual/index.js +7 -0
- package/dist/visual/leaves.d.ts +47 -0
- package/dist/visual/leaves.js +31 -0
- package/dist/visual/props.d.ts +27 -6
- package/dist/visual/tips.d.ts +3 -1
- package/dist/visual/tips.js +5 -2
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Document, Quill, Addr, Diagnostic, ResolvedField } from '@quillmark/wasm';
|
|
2
2
|
import type { EditorErrorHandler } from '../core/errors.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { LeafRegistry } from './leaves.js';
|
|
4
4
|
import type { FieldModel, FieldSpan } from './structure.js';
|
|
5
5
|
import type { FieldDomIds } from './domid.js';
|
|
6
6
|
interface Props {
|
|
@@ -14,22 +14,26 @@ interface Props {
|
|
|
14
14
|
doc: Document;
|
|
15
15
|
/** The schema a prose leaf reads its content through (`ProseField`). */
|
|
16
16
|
quill: Quill;
|
|
17
|
-
/** LIVE
|
|
18
|
-
|
|
17
|
+
/** This field's LIVE address (getter-`card`, so a card reorder re-targets in
|
|
18
|
+
* place): the prose leaf commits to it, and a focus reports it. */
|
|
19
|
+
addr: Addr;
|
|
19
20
|
leafKey: string;
|
|
20
21
|
/** This field's three DOM names, derived from `leafKey` (see `domid.ts`): how
|
|
21
22
|
* the label and the control find each other. */
|
|
22
23
|
domIds: FieldDomIds;
|
|
23
24
|
onCommitScalar: (value: unknown) => void;
|
|
24
|
-
/** Enum-option policy: `false`
|
|
25
|
-
* control reads
|
|
25
|
+
/** Enum-option policy: `false` marks that option unavailable. Only the enum
|
|
26
|
+
* control reads this pair. */
|
|
26
27
|
optionAllowed?: (value: string) => boolean;
|
|
28
|
+
/** How a refused option draws: greyed (default) or left out. */
|
|
29
|
+
enumDisallowed?: 'hide' | 'disable';
|
|
27
30
|
onFocus?: (addr: Addr) => void;
|
|
28
31
|
onCaretMove?: (addr: Addr, pos: number) => void;
|
|
29
32
|
onChange?: (addr: Addr) => void;
|
|
30
33
|
onError?: EditorErrorHandler;
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
/** The editor's leaf registry (`leaves.ts`): a form control registers its
|
|
35
|
+
* landing handle here, a prose leaf its controller from inside `ProseField`. */
|
|
36
|
+
leaves?: LeafRegistry;
|
|
33
37
|
diagnostics?: Diagnostic[];
|
|
34
38
|
}
|
|
35
39
|
declare const Field: import("svelte").Component<Props, {}, "">;
|
|
@@ -45,6 +45,39 @@
|
|
|
45
45
|
/** The root to portal INTO: `document.body` would escape the consumer's dials
|
|
46
46
|
* along with the editor's subtree. `undefined` falls back to bits-ui's default. */
|
|
47
47
|
const portalTarget = $derived(triggerEl?.closest<HTMLElement>('[data-qm-root]') ?? undefined);
|
|
48
|
+
|
|
49
|
+
/** Placement, re-measured on each opening: the surface rides the label rung beside
|
|
50
|
+
* the glyph, or stands over the field. */
|
|
51
|
+
let beside = $state(false);
|
|
52
|
+
|
|
53
|
+
/** Whether this description sets one line. Off a probe rather than the surface
|
|
54
|
+
* itself, since the side is a prop the surface is positioned FROM, and a side chosen
|
|
55
|
+
* after the fact moves a surface the reader is already looking at. The probe wears
|
|
56
|
+
* the surface's own class inside the portal target, so it wraps to the same measure
|
|
57
|
+
* under the same dials; `width: max-content` keeps that measure the class's rather
|
|
58
|
+
* than the containing block's. Line boxes are counted off a range over the text
|
|
59
|
+
* rather than derived from a height, which would need the line rung and the inset
|
|
60
|
+
* restated here as numbers. A DOM with no layout yields none, which stands the
|
|
61
|
+
* surface over the field. */
|
|
62
|
+
function ridesTheRung(): boolean {
|
|
63
|
+
const host = portalTarget ?? document.body;
|
|
64
|
+
const probe = document.createElement('div');
|
|
65
|
+
probe.className = 'qm-hint-popover';
|
|
66
|
+
probe.style.cssText =
|
|
67
|
+
'position:absolute;width:max-content;visibility:hidden;pointer-events:none';
|
|
68
|
+
probe.textContent = description;
|
|
69
|
+
host.appendChild(probe);
|
|
70
|
+
const range = document.createRange();
|
|
71
|
+
range.selectNodeContents(probe);
|
|
72
|
+
const lines = range.getClientRects().length;
|
|
73
|
+
probe.remove();
|
|
74
|
+
return lines === 1;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function raise(): void {
|
|
78
|
+
beside = ridesTheRung();
|
|
79
|
+
open = true;
|
|
80
|
+
}
|
|
48
81
|
</script>
|
|
49
82
|
|
|
50
83
|
<!-- The trigger is OURS, not `Popover.Trigger`, and the surface anchors to it:
|
|
@@ -60,7 +93,7 @@
|
|
|
60
93
|
aria-expanded={open}
|
|
61
94
|
aria-describedby={describedBy}
|
|
62
95
|
onpointerenter={(e) => {
|
|
63
|
-
if (e.pointerType !== 'touch')
|
|
96
|
+
if (e.pointerType !== 'touch') raise();
|
|
64
97
|
}}
|
|
65
98
|
onpointerleave={(e) => {
|
|
66
99
|
if (e.pointerType !== 'touch') open = false;
|
|
@@ -69,13 +102,16 @@
|
|
|
69
102
|
// Touch only, and a TOGGLE: the one modality with no hover to leave and no
|
|
70
103
|
// blur on the way out. A mouse press falls through to hover, which already
|
|
71
104
|
// has it open and will close it on the way off.
|
|
72
|
-
if (e.pointerType === 'touch')
|
|
105
|
+
if (e.pointerType === 'touch') {
|
|
106
|
+
if (open) open = false;
|
|
107
|
+
else raise();
|
|
108
|
+
}
|
|
73
109
|
}}
|
|
74
110
|
onfocus={(e) => {
|
|
75
111
|
// Keyboard arrival only. A pointer press focuses the button too, and opening
|
|
76
112
|
// on that would re-open what the tap just toggled shut; `:focus-visible` is
|
|
77
113
|
// the UA's own answer to which arrival this was.
|
|
78
|
-
if (e.currentTarget.matches(':focus-visible'))
|
|
114
|
+
if (e.currentTarget.matches(':focus-visible')) raise();
|
|
79
115
|
}}
|
|
80
116
|
onblur={() => (open = false)}
|
|
81
117
|
onkeydown={(e) => {
|
|
@@ -88,12 +124,21 @@
|
|
|
88
124
|
}}><Icon name="info" size={13} /></button
|
|
89
125
|
>
|
|
90
126
|
|
|
127
|
+
<!-- PLACEMENT IS THE DESCRIPTION'S HEIGHT. One line rides the label rung beside its own
|
|
128
|
+
glyph, where the overhang past the rung's line box falls in the label-to-control gaps.
|
|
129
|
+
Anything taller stands over the field: beside the glyph it would hang onto THE CONTROL
|
|
130
|
+
IT DESCRIBES, the one overlap a reader cannot clear by reaching for what is hidden,
|
|
131
|
+
since a pointer leaving the trigger closes the surface. Over the field it covers a
|
|
132
|
+
neighbour's control instead, which reaching for it dismisses. -->
|
|
133
|
+
<!-- `align="center"` for the rung, `"start"` over the field: centred, a surface taller
|
|
134
|
+
than the glyph's line box would hang half its height onto the field's own control. -->
|
|
135
|
+
|
|
91
136
|
<Popover.Root bind:open>
|
|
92
137
|
<Popover.Portal to={portalTarget}>
|
|
93
138
|
<Popover.Content
|
|
94
139
|
customAnchor={triggerEl ?? null}
|
|
95
|
-
side=
|
|
96
|
-
align=
|
|
140
|
+
side={beside ? 'right' : 'top'}
|
|
141
|
+
align={beside ? 'center' : 'start'}
|
|
97
142
|
sideOffset={6}
|
|
98
143
|
trapFocus={false}
|
|
99
144
|
onOpenAutoFocus={(e: Event) => e.preventDefault()}
|
|
@@ -168,18 +213,22 @@
|
|
|
168
213
|
}
|
|
169
214
|
.qm-field-hint:hover,
|
|
170
215
|
.qm-field-hint:focus-visible {
|
|
171
|
-
color: var(--_qm-ink-
|
|
216
|
+
color: var(--_qm-ink-label);
|
|
172
217
|
}
|
|
173
218
|
/* The lift, the translucency, the blur and the scale-in come from
|
|
174
219
|
`.qm-popover-surface` (controls.css). This surface's own is a MEASURE: guidance
|
|
175
220
|
is prose, and prose past ~40 characters a line stops being scannable; plus the
|
|
176
221
|
inset and the meta type rung that keep it a note about a field rather than a
|
|
177
|
-
second field.
|
|
178
|
-
|
|
222
|
+
second field.
|
|
223
|
+
|
|
224
|
+
Unscoped, because the placement probe wears this class on a node Svelte never
|
|
225
|
+
compiled: a scoped rule would leave the probe wrapping to a different measure
|
|
226
|
+
than the surface it stands in for, which is the one thing the probe must not do. */
|
|
227
|
+
:global(.qm-hint-popover) {
|
|
179
228
|
max-width: 22rem;
|
|
180
229
|
padding: var(--_qm-space) var(--_qm-space-2);
|
|
181
|
-
font-size: var(--_qm-text-
|
|
230
|
+
font-size: var(--_qm-text-label);
|
|
182
231
|
line-height: var(--_qm-leading-body);
|
|
183
|
-
color: var(--_qm-ink-
|
|
232
|
+
color: var(--_qm-ink-label);
|
|
184
233
|
}
|
|
185
234
|
</style>
|
|
@@ -385,10 +385,12 @@
|
|
|
385
385
|
gap: var(--_qm-space);
|
|
386
386
|
align-items: center;
|
|
387
387
|
}
|
|
388
|
+
/* The box's recipe without `.qm-input`, which adds the `width: 100%` that would
|
|
389
|
+
stretch this to the popover. */
|
|
388
390
|
.qm-link-input {
|
|
389
391
|
font: inherit;
|
|
390
392
|
font-size: var(--_qm-text-body);
|
|
391
|
-
padding: var(--_qm-
|
|
393
|
+
padding: var(--_qm-inset-control);
|
|
392
394
|
border: var(--_qm-border-width) solid var(--_qm-border);
|
|
393
395
|
border-radius: var(--_qm-radius-inner);
|
|
394
396
|
min-width: 12rem;
|
|
@@ -37,6 +37,19 @@
|
|
|
37
37
|
const entries = $derived(Object.entries(properties ?? {}));
|
|
38
38
|
const obj = $derived((value ?? {}) as Record<string, unknown>);
|
|
39
39
|
|
|
40
|
+
/** Take the caret: the FIRST property's control, a subform having no single control
|
|
41
|
+
* of its own to land on. Resolved off the DOM rather than a ref per property: every
|
|
42
|
+
* property is a scalar control the DOM already knows how to focus (the date field's
|
|
43
|
+
* segments carry `tabindex`, the `literal` separators between them do not), so a
|
|
44
|
+
* handle each would be five refs restating document order. An empty or wholly
|
|
45
|
+
* unsupported subform lands nothing. */
|
|
46
|
+
let rootEl = $state<HTMLElement | undefined>();
|
|
47
|
+
export function focus(): void {
|
|
48
|
+
rootEl
|
|
49
|
+
?.querySelector<HTMLElement>('input, select, button, [tabindex]:not([tabindex="-1"])')
|
|
50
|
+
?.focus();
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
function commitProp(key: string, v: unknown): void {
|
|
41
54
|
if (v === undefined) {
|
|
42
55
|
// A nested control cleared (the unset rung): drop the key so the property
|
|
@@ -51,7 +64,13 @@
|
|
|
51
64
|
}
|
|
52
65
|
</script>
|
|
53
66
|
|
|
54
|
-
<div
|
|
67
|
+
<div
|
|
68
|
+
bind:this={rootEl}
|
|
69
|
+
class="qm-object"
|
|
70
|
+
role="group"
|
|
71
|
+
aria-labelledby={labelledBy}
|
|
72
|
+
aria-describedby={describedBy}
|
|
73
|
+
>
|
|
55
74
|
{#each entries as [key, sub] (key)}
|
|
56
75
|
{@const kind = controlKind(sub)}
|
|
57
76
|
{@const propLabel = `${label != null ? `${label} ` : ''}${sub.ui?.title ?? humanize(key)}`}
|
|
@@ -117,7 +136,7 @@
|
|
|
117
136
|
font-size: var(--_qm-text-label);
|
|
118
137
|
/* Soft (500), a rung under the top-level field label's 600: a nested object
|
|
119
138
|
prop reads as secondary. Resolves the label-weight disagreement. */
|
|
120
|
-
font-weight: var(--_qm-weight-
|
|
139
|
+
font-weight: var(--_qm-weight-label);
|
|
121
140
|
/* A label is a line, not a passage, so it overrides the root's reading
|
|
122
141
|
rhythm the way every other label rung does. */
|
|
123
142
|
line-height: var(--_qm-leading-tight);
|
|
@@ -12,6 +12,8 @@ interface Props {
|
|
|
12
12
|
describedBy?: string;
|
|
13
13
|
onCommit: (obj: Record<string, unknown>) => void;
|
|
14
14
|
}
|
|
15
|
-
declare const ObjectField: import("svelte").Component<Props, {
|
|
15
|
+
declare const ObjectField: import("svelte").Component<Props, {
|
|
16
|
+
focus: () => void;
|
|
17
|
+
}, "">;
|
|
16
18
|
type ObjectField = ReturnType<typeof ObjectField>;
|
|
17
19
|
export default ObjectField;
|
|
@@ -27,9 +27,8 @@
|
|
|
27
27
|
* array repeater's Enter/Backspace (`ArrayField`). Fires BEFORE the view's own
|
|
28
28
|
* keymap, so the state it reads is the one this keystroke has yet to change. */
|
|
29
29
|
onKey?: (e: KeyboardEvent) => void;
|
|
30
|
-
onFocusEl?: () => void;
|
|
31
30
|
}
|
|
32
|
-
let { value, plaintext, label, onChange, onKey
|
|
31
|
+
let { value, plaintext, label, onChange, onKey }: Props = $props();
|
|
33
32
|
|
|
34
33
|
let containerEl: HTMLDivElement | undefined = $state();
|
|
35
34
|
let view: EditorView | undefined;
|
|
@@ -62,10 +61,6 @@
|
|
|
62
61
|
if (tr.docChanged) onChange(pmToContent(next.doc));
|
|
63
62
|
},
|
|
64
63
|
handleDOMEvents: {
|
|
65
|
-
focus: () => {
|
|
66
|
-
onFocusEl?.();
|
|
67
|
-
return false;
|
|
68
|
-
},
|
|
69
64
|
keydown: (_v, e) => {
|
|
70
65
|
onKey?.(e);
|
|
71
66
|
return false;
|
|
@@ -10,7 +10,6 @@ interface Props {
|
|
|
10
10
|
* array repeater's Enter/Backspace (`ArrayField`). Fires BEFORE the view's own
|
|
11
11
|
* keymap, so the state it reads is the one this keystroke has yet to change. */
|
|
12
12
|
onKey?: (e: KeyboardEvent) => void;
|
|
13
|
-
onFocusEl?: () => void;
|
|
14
13
|
}
|
|
15
14
|
declare const ProseArrayElement: import("svelte").Component<Props, {
|
|
16
15
|
focus: () => void;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<script lang="ts">
|
|
11
11
|
import { onMount } from 'svelte';
|
|
12
12
|
import { createField, type FieldController, type SlashState } from '../core/codec/index.js';
|
|
13
|
+
import type { LeafRegistry } from './leaves.js';
|
|
13
14
|
import SlashMenu from './SlashMenu.svelte';
|
|
14
15
|
import { wording } from './strings.js';
|
|
15
16
|
import './controls.css';
|
|
@@ -54,8 +55,10 @@
|
|
|
54
55
|
/** A commit landed on this leaf: the prose change lane. */
|
|
55
56
|
onChange?: (addr: Addr) => void;
|
|
56
57
|
onError?: EditorErrorHandler;
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
/** The editor's leaf registry (`leaves.ts`). A prose leaf registers its
|
|
59
|
+
* CONTROLLER: the landing handle plus the codec seam a form control has no half
|
|
60
|
+
* of, which is what makes this leaf a caret target and not merely a focus one. */
|
|
61
|
+
leaves?: LeafRegistry;
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
let {
|
|
@@ -74,8 +77,7 @@
|
|
|
74
77
|
onCaretMove,
|
|
75
78
|
onChange,
|
|
76
79
|
onError,
|
|
77
|
-
|
|
78
|
-
unregister
|
|
80
|
+
leaves
|
|
79
81
|
}: Props = $props();
|
|
80
82
|
|
|
81
83
|
let containerEl: HTMLDivElement | undefined = $state();
|
|
@@ -134,9 +136,9 @@
|
|
|
134
136
|
onChange,
|
|
135
137
|
onError
|
|
136
138
|
});
|
|
137
|
-
|
|
139
|
+
leaves?.registerProse(leafKey, controller);
|
|
138
140
|
return () => {
|
|
139
|
-
|
|
141
|
+
leaves?.unregisterProse(leafKey);
|
|
140
142
|
controller?.destroy();
|
|
141
143
|
controller = undefined;
|
|
142
144
|
};
|
|
@@ -172,16 +174,22 @@
|
|
|
172
174
|
own surface. */
|
|
173
175
|
position: relative;
|
|
174
176
|
}
|
|
175
|
-
/* A leaf the inline schema does NOT constrain
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
It moves with the type ramp and with nothing else. Three is what an EMPTY body
|
|
177
|
+
/* A leaf the inline schema does NOT constrain is prose rather than a cell in a row
|
|
178
|
+
of controls, so it opens at a few lines and grows. The floor is three LINE BOXES,
|
|
179
|
+
not a length: `1em` rather than a size rung because which rung this leaf reads is
|
|
180
|
+
the rule below's, and a second naming of it drifts. Three is what an EMPTY body
|
|
180
181
|
costs, which is the only state the floor is ever the height: the ghost takes the
|
|
181
182
|
first line, so the opening reads as an invitation with room under it rather than
|
|
182
183
|
as a drop. */
|
|
183
184
|
.qm-prose-block {
|
|
184
|
-
min-height: calc(
|
|
185
|
+
min-height: calc(1em * var(--_qm-leading-body) * 3);
|
|
186
|
+
}
|
|
187
|
+
/* PAPER, and the box is what says so. Not `block` alone: `inline` is the quill's to
|
|
188
|
+
declare, so a schema omitting it on a richtext field puts a block leaf in a field
|
|
189
|
+
row, where the paper rung would break the height agreement `controls.css` builds by
|
|
190
|
+
construction. */
|
|
191
|
+
.qm-prose-block:not(.qm-control-box) {
|
|
192
|
+
font-size: var(--_qm-text-paper);
|
|
185
193
|
}
|
|
186
194
|
/* The caret is the prose leaf's focus indicator, not a ring: a ring around a
|
|
187
195
|
contenteditable reads as form chrome rather than as paper. So the outline is
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { LeafRegistry } from './leaves.js';
|
|
2
2
|
import './controls.css';
|
|
3
3
|
import type { Document, Quill, Addr } from '@quillmark/wasm';
|
|
4
4
|
import type { EditorErrorHandler } from '../core/errors.js';
|
|
@@ -40,8 +40,10 @@ interface Props {
|
|
|
40
40
|
/** A commit landed on this leaf: the prose change lane. */
|
|
41
41
|
onChange?: (addr: Addr) => void;
|
|
42
42
|
onError?: EditorErrorHandler;
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
/** The editor's leaf registry (`leaves.ts`). A prose leaf registers its
|
|
44
|
+
* CONTROLLER: the landing handle plus the codec seam a form control has no half
|
|
45
|
+
* of, which is what makes this leaf a caret target and not merely a focus one. */
|
|
46
|
+
leaves?: LeafRegistry;
|
|
45
47
|
}
|
|
46
48
|
declare const ProseField: import("svelte").Component<Props, {
|
|
47
49
|
focus: () => void;
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
button carries a label and so widens its inset and takes the meta type rung. */
|
|
186
186
|
.qm-tips-action {
|
|
187
187
|
padding: var(--_qm-space-half) var(--_qm-space-2);
|
|
188
|
-
font-size: var(--_qm-text-
|
|
188
|
+
font-size: var(--_qm-text-label);
|
|
189
189
|
color: var(--_qm-ink-label);
|
|
190
190
|
}
|
|
191
191
|
</style>
|
|
@@ -24,9 +24,8 @@
|
|
|
24
24
|
-->
|
|
25
25
|
<script lang="ts">
|
|
26
26
|
import { untrack } from 'svelte';
|
|
27
|
-
import {
|
|
28
|
-
import type {
|
|
29
|
-
import type { DocPath } from '../core/address.js';
|
|
27
|
+
import { reportRebindIgnored } from '../core/rebind.svelte.js';
|
|
28
|
+
import type { DocPath, Landing } from '../core/address.js';
|
|
30
29
|
import type { CardId } from './signals.js';
|
|
31
30
|
import type { VisualEditorProps } from './props.js';
|
|
32
31
|
import VisualEditorInner from './VisualEditorInner.svelte';
|
|
@@ -54,12 +53,10 @@
|
|
|
54
53
|
}
|
|
55
54
|
if (quill === boundQuill || reported) return;
|
|
56
55
|
reported = true;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
'quill swapped in place; the mounted leaves still commit against the quill this doc mounted with. Swap the doc with it, or remount the editor, to rebind.'
|
|
62
|
-
});
|
|
56
|
+
reportRebindIgnored(
|
|
57
|
+
props.onError,
|
|
58
|
+
'quill swapped in place; the mounted leaves still commit against the quill this doc mounted with. Swap the doc with it, or remount the editor, to rebind.'
|
|
59
|
+
);
|
|
63
60
|
});
|
|
64
61
|
});
|
|
65
62
|
|
|
@@ -67,11 +64,13 @@
|
|
|
67
64
|
// landing between a swap and the incoming mount is a no-op rather than a throw,
|
|
68
65
|
// which is what lets a host hold ONE `bind:this` across two documents.
|
|
69
66
|
|
|
70
|
-
/**
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
/** Land a preview pick: a caret where the payload carries one and the leaf can
|
|
68
|
+
* spend it, an array element where the address names one, a focus otherwise. */
|
|
69
|
+
export async function setCaret(at: Landing): Promise<void> {
|
|
70
|
+
await inner?.setCaret(at);
|
|
73
71
|
}
|
|
74
|
-
/** Reveal and focus the
|
|
72
|
+
/** Reveal and focus the field at `field` — any mounted one — without placing a
|
|
73
|
+
* caret inside it. */
|
|
75
74
|
export async function focusField(field: DocPath): Promise<void> {
|
|
76
75
|
await inner?.focusField(field);
|
|
77
76
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { DocPath } from '../core/address.js';
|
|
1
|
+
import type { DocPath, Landing } from '../core/address.js';
|
|
3
2
|
import type { CardId } from './signals.js';
|
|
4
3
|
import type { VisualEditorProps } from './props.js';
|
|
5
4
|
declare const VisualEditor: import("svelte").Component<VisualEditorProps, {
|
|
6
|
-
setCaret: (
|
|
5
|
+
setCaret: (at: Landing) => Promise<void>;
|
|
7
6
|
focusField: (field: DocPath) => Promise<void>;
|
|
8
7
|
insertCard: (kind: string, at?: number) => CardId | undefined;
|
|
9
8
|
removeCard: (cardId: CardId) => void;
|