@quillmark/svelte 0.5.1 → 0.7.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 +6 -0
- package/THEMING.md +13 -17
- package/dist/core/address.d.ts +17 -3
- package/dist/core/address.js +20 -3
- package/dist/core/codec/blocks.d.ts +37 -0
- package/dist/core/codec/blocks.js +163 -0
- package/dist/core/codec/field.js +5 -6
- package/dist/core/codec/index.d.ts +1 -1
- package/dist/core/codec/index.js +3 -2
- package/dist/core/codec/inputrules.js +24 -144
- package/dist/core/codec/prose.css +14 -25
- package/dist/core/codec/schema.d.ts +8 -0
- package/dist/core/codec/schema.js +33 -1
- package/dist/core/codec/slash.d.ts +18 -16
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-view.d.ts +4 -0
- package/dist/core/codec/table-view.js +137 -158
- package/dist/core/codec/table.d.ts +0 -10
- package/dist/core/codec/table.js +0 -15
- package/dist/core/theme.css +357 -346
- package/dist/preset/recipes.css +25 -21
- package/dist/preview/Preview.svelte +4 -2
- package/dist/preview/bridge.d.ts +23 -0
- package/dist/preview/bridge.js +92 -35
- package/dist/preview/controller.d.ts +6 -5
- package/dist/preview/controller.js +13 -7
- package/dist/preview/geometry.d.ts +10 -9
- package/dist/preview/geometry.js +11 -13
- package/dist/preview/index.js +3 -2
- package/dist/preview/paint.js +9 -1
- package/dist/visual/ArrayField.svelte +246 -63
- package/dist/visual/ArrayField.svelte.d.ts +5 -1
- package/dist/visual/Card.svelte +84 -50
- package/dist/visual/EnumField.svelte +37 -3
- package/dist/visual/Field.svelte +28 -4
- package/dist/visual/FieldLabel.svelte +2 -2
- package/dist/visual/FormatPopover.svelte +13 -1
- package/dist/visual/ObjectField.svelte +128 -35
- package/dist/visual/ObjectField.svelte.d.ts +11 -4
- package/dist/visual/ProseArrayElement.svelte +22 -2
- package/dist/visual/ProseArrayElement.svelte.d.ts +1 -0
- package/dist/visual/SlashMenu.svelte +7 -0
- package/dist/visual/TipsCard.svelte +33 -13
- package/dist/visual/VariantField.svelte +95 -0
- package/dist/visual/VariantField.svelte.d.ts +21 -0
- package/dist/visual/VisualEditorInner.svelte +44 -21
- package/dist/visual/controls.css +282 -283
- package/dist/visual/diagnostics.d.ts +13 -11
- package/dist/visual/diagnostics.js +11 -9
- package/dist/visual/domid.d.ts +13 -0
- package/dist/visual/domid.js +16 -0
- package/dist/visual/hold.d.ts +17 -0
- package/dist/visual/hold.js +39 -0
- package/dist/visual/index.js +1 -1
- package/dist/visual/leaves.d.ts +9 -7
- package/dist/visual/links.d.ts +5 -2
- package/dist/visual/links.js +9 -3
- package/dist/visual/motion.d.ts +9 -0
- package/dist/visual/motion.js +10 -2
- package/dist/visual/strings.d.ts +15 -0
- package/dist/visual/strings.js +3 -0
- package/dist/visual/structure.d.ts +32 -6
- package/dist/visual/structure.js +49 -3
- package/package.json +2 -2
- package/dist/core/codec/clipboard.d.ts +0 -59
- package/dist/core/codec/clipboard.js +0 -202
package/dist/preset/recipes.css
CHANGED
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
they freeze nothing about the surfaces' interior: the reason the package withholds
|
|
9
9
|
its own internal classes does not reach them.
|
|
10
10
|
|
|
11
|
-
Unlayered, deliberately
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
precedence and the focus rule works with nothing declared. */
|
|
11
|
+
Unlayered, deliberately: a layer of the preset's own would put a name in the contract,
|
|
12
|
+
and unlayered a consumer's rules win over these by ordinary precedence. Ordinary
|
|
13
|
+
specificity too, not the surfaces' `:where()` zero: a consumer takes these classes on
|
|
14
|
+
purpose, so a rule of their own beside one is aimed rather than incidental. */
|
|
16
15
|
|
|
17
16
|
/* ── The page ──────────────────────────────────────────────────────────────── */
|
|
18
17
|
|
|
@@ -51,11 +50,19 @@
|
|
|
51
50
|
root scroller counting the overflow its panes clip, and a wheel over the chrome drags
|
|
52
51
|
the whole app off the top. Three bands (a head, the body, an optional foot), and the
|
|
53
52
|
body takes what is left and may shrink below its content, so the panes in it own
|
|
54
|
-
their overflow and hold still while their contents move.
|
|
53
|
+
their overflow and hold still while their contents move.
|
|
54
|
+
|
|
55
|
+
The one column is stated for the same reason the rows are: an implicit track is `auto`,
|
|
56
|
+
which is sized by what a band holds, and a `.qm-split` under one contributes its items'
|
|
57
|
+
max-content through its `fr` tracks. The shell would take the width of the widest thing
|
|
58
|
+
mounted in it instead of the viewport's, and every band would be that wide with the
|
|
59
|
+
second pane past the edge. A host holding its content to a maximum never sees it; one
|
|
60
|
+
spending the whole screen sees it on the first surface it mounts. */
|
|
55
61
|
.qm-workspace {
|
|
56
62
|
position: fixed;
|
|
57
63
|
inset: 0;
|
|
58
64
|
display: grid;
|
|
65
|
+
grid-template-columns: minmax(0, 1fr);
|
|
59
66
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
60
67
|
overflow: hidden;
|
|
61
68
|
}
|
|
@@ -116,9 +123,12 @@ html:has(.qm-workspace) {
|
|
|
116
123
|
pane needs no `inert` and the host needs no breakpoint in JS. Both stay mounted (a
|
|
117
124
|
preview binds its handle once, an editor holds a caret, a history and a scroll), so
|
|
118
125
|
the switch is presentational, never conditional. A pane hidden this way keeps the
|
|
119
|
-
pixels it had
|
|
120
|
-
|
|
121
|
-
|
|
126
|
+
pixels it had: the paint loop's band sweep no-ops on an empty visible set, and its
|
|
127
|
+
resize observer holds the last real width rather than rastering at the 0×0 box a hidden
|
|
128
|
+
pane reports, so what comes back at the width it left needs no paint at all. The
|
|
129
|
+
preview's caret follow spans the same window — a trip asked for against a pane with no
|
|
130
|
+
box is held and run when it has one (`preview/bridge.ts`), the pane opening at the
|
|
131
|
+
caret rather than several edits behind it.
|
|
122
132
|
|
|
123
133
|
Height is the second clause because a landscape phone passes the first one: it clears
|
|
124
134
|
the width with room for two tracks and has no body left to put them in. A desktop window
|
|
@@ -308,18 +318,12 @@ pre.qm-readout {
|
|
|
308
318
|
|
|
309
319
|
/* One visible indicator for the whole host, since none of these controls draws a ring
|
|
310
320
|
of its own. It reads the ink rung rather than a hue: the page is monochrome, and a
|
|
311
|
-
focus ring is a position, not a destination.
|
|
312
|
-
|
|
321
|
+
focus ring is a position, not a destination.
|
|
322
|
+
|
|
323
|
+
The mounted surfaces are cut out of it: they draw their own ring, and a blanket would
|
|
324
|
+
repaint the controls that already have one. `:not(:where(…))` contributes no
|
|
325
|
+
specificity, so the cut-out leaves this rule's own rank where it was. */
|
|
326
|
+
:focus-visible:not(:where([data-qm-root], [data-qm-root] *)) {
|
|
313
327
|
outline: var(--qmh-ring-width) solid var(--qmh-ink);
|
|
314
328
|
outline-offset: var(--qmh-space-half);
|
|
315
329
|
}
|
|
316
|
-
|
|
317
|
-
/* The mounted surfaces are the exception, and the rule above is why: an unlayered host
|
|
318
|
-
declaration beats `@layer qm.chrome` by design, so a ring written for controls that
|
|
319
|
-
draw none repaints the ones that do. `revert-layer` hands each control back to the
|
|
320
|
-
layer under this one, which is the package's own ring at its own offset, and is
|
|
321
|
-
what makes this file's own lack of a layer load-bearing rather than a preference. */
|
|
322
|
-
[data-qm-root] :focus-visible {
|
|
323
|
-
outline: revert-layer;
|
|
324
|
-
outline-offset: revert-layer;
|
|
325
|
-
}
|
|
@@ -101,8 +101,10 @@
|
|
|
101
101
|
the surface's own, so a bare `<div>` is a mounting site. The
|
|
102
102
|
padding is safe against the paint loop: a slot is a `width: 100%` child and
|
|
103
103
|
measures the content box, and the container's own `clientWidth` is read only as
|
|
104
|
-
a change detector, which a constant inset does not disturb.
|
|
105
|
-
|
|
104
|
+
a change detector, which a constant inset does not disturb.
|
|
105
|
+
|
|
106
|
+
`:global(:where(…))` puts this promised class at zero rank (ARCHITECTURE §Styling). */
|
|
107
|
+
:global(:where(.qm-preview)) {
|
|
106
108
|
box-sizing: border-box;
|
|
107
109
|
position: relative;
|
|
108
110
|
width: 100%;
|
package/dist/preview/bridge.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { LiveSession } from '@quillmark/wasm';
|
|
2
2
|
import type { Landing } from '../core/address.js';
|
|
3
3
|
import type { PageSlot } from './paint.js';
|
|
4
|
+
/** Both trips are held while the pane has no box to measure against, and the last one
|
|
5
|
+
* held runs when it has one again. */
|
|
4
6
|
export interface BridgeController {
|
|
5
7
|
/** Scroll `field`'s first box to centre; `false` when this compile places none
|
|
6
8
|
* for that address, which is the whole of what the preview can say about it. */
|
|
@@ -12,4 +14,25 @@ export interface BridgeController {
|
|
|
12
14
|
focusPosition(field: string, pos: number): void;
|
|
13
15
|
destroy(): void;
|
|
14
16
|
}
|
|
17
|
+
/** The vertical extent the fold test reads; a `DOMRect` is one. */
|
|
18
|
+
export interface VerticalSpan {
|
|
19
|
+
top: number;
|
|
20
|
+
bottom: number;
|
|
21
|
+
height: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Whether `target` sits clear of `port`'s edges by the fold's clearance, which is the
|
|
25
|
+
* target's own height: a caret rect flush against an edge is visible and unusable, and
|
|
26
|
+
* the next line typed lands past it. Derived from the target rather than a margin dial,
|
|
27
|
+
* so it scales with zoom exactly as the caret does, and bounded at both ends so the
|
|
28
|
+
* answer is reachable across the whole range of targets a compile locates.
|
|
29
|
+
*
|
|
30
|
+
* The floor is what a zero-height rect gets, which bare intersection would score as
|
|
31
|
+
* clear at either edge. The cap is a third of the room the port has to spare, so a
|
|
32
|
+
* target too tall for its own height of clearance — a located line at zoom, a short
|
|
33
|
+
* split track — passes once centred and holds a band around it, rather than being
|
|
34
|
+
* re-centred by every keystroke. A target taller than the port is clear while it covers
|
|
35
|
+
* the port: no scroll shows more of it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function clearOfTheFold(target: VerticalSpan, port: VerticalSpan): boolean;
|
|
15
38
|
export declare function createBridge(session: LiveSession, container: HTMLElement, slots: readonly PageSlot[], onPick: ((at: Landing) => void) | undefined): BridgeController;
|
package/dist/preview/bridge.js
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
import { boxesForField, rectToPercent, clickToPdfPt, applyPercentRect } from './geometry.js';
|
|
2
|
+
/** A pane hidden by the shell's narrow switch (`preset/recipes.css`) measures 0×0, and so
|
|
3
|
+
* does every box on it: the clearance arithmetic reads that as a caret clear of both
|
|
4
|
+
* edges, and a centring computes its trip out of zeros. Neither is an answer about where
|
|
5
|
+
* the caret is. */
|
|
6
|
+
function hasBox(port) {
|
|
7
|
+
return port.width > 0 && port.height > 0;
|
|
8
|
+
}
|
|
9
|
+
/** What a rect reporting no height clears an edge by, in CSS px: a caret's own at the
|
|
10
|
+
* smallest zoom a page still reads at. */
|
|
11
|
+
const MIN_CLEARANCE = 8;
|
|
12
|
+
/**
|
|
13
|
+
* Whether `target` sits clear of `port`'s edges by the fold's clearance, which is the
|
|
14
|
+
* target's own height: a caret rect flush against an edge is visible and unusable, and
|
|
15
|
+
* the next line typed lands past it. Derived from the target rather than a margin dial,
|
|
16
|
+
* so it scales with zoom exactly as the caret does, and bounded at both ends so the
|
|
17
|
+
* answer is reachable across the whole range of targets a compile locates.
|
|
18
|
+
*
|
|
19
|
+
* The floor is what a zero-height rect gets, which bare intersection would score as
|
|
20
|
+
* clear at either edge. The cap is a third of the room the port has to spare, so a
|
|
21
|
+
* target too tall for its own height of clearance — a located line at zoom, a short
|
|
22
|
+
* split track — passes once centred and holds a band around it, rather than being
|
|
23
|
+
* re-centred by every keystroke. A target taller than the port is clear while it covers
|
|
24
|
+
* the port: no scroll shows more of it.
|
|
25
|
+
*/
|
|
26
|
+
export function clearOfTheFold(target, port) {
|
|
27
|
+
if (target.height >= port.height)
|
|
28
|
+
return target.top <= port.top && target.bottom >= port.bottom;
|
|
29
|
+
const clearance = Math.min(Math.max(target.height, MIN_CLEARANCE), (port.height - target.height) / 3);
|
|
30
|
+
return target.top - clearance >= port.top && target.bottom + clearance <= port.bottom;
|
|
31
|
+
}
|
|
2
32
|
export function createBridge(session, container, slots, onPick) {
|
|
3
33
|
const unlisten = [];
|
|
4
34
|
// Two rungs: `positionAt` over span-tracked content, `fieldAt` over every placement
|
|
@@ -53,43 +83,70 @@ export function createBridge(session, container, slots, onPick) {
|
|
|
53
83
|
else if (target.right > port.right)
|
|
54
84
|
container.scrollLeft += target.right - port.right;
|
|
55
85
|
}
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
86
|
+
// The trip a pane with no box could not run, held for the box coming back. One, and
|
|
87
|
+
// the last one asked for: a caret hop supersedes the hop before it on a showing pane
|
|
88
|
+
// too.
|
|
89
|
+
let held;
|
|
90
|
+
// The re-assert. Nothing else asks on the way back: `refresh` re-asks only on a
|
|
91
|
+
// recompile, and the reader who has just tapped Preview is not typing, so the pane
|
|
92
|
+
// would open where the caret was several edits ago. Guarded for jsdom, which ships no
|
|
93
|
+
// ResizeObserver and lays out nothing for one to report.
|
|
94
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
95
|
+
const observer = new ResizeObserver(() => {
|
|
96
|
+
if (!held || !hasBox(container.getBoundingClientRect()))
|
|
97
|
+
return;
|
|
98
|
+
const trip = held;
|
|
99
|
+
held = undefined;
|
|
100
|
+
trip();
|
|
101
|
+
});
|
|
102
|
+
observer.observe(container);
|
|
103
|
+
unlisten.push(() => observer.disconnect());
|
|
63
104
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
105
|
+
function scrollToField(field) {
|
|
106
|
+
const box = boxesForField(field, session.fieldBoxes(field), session.regions())[0];
|
|
107
|
+
if (!box)
|
|
108
|
+
return false;
|
|
109
|
+
const slot = slots[box.page];
|
|
110
|
+
if (!slot)
|
|
111
|
+
return false;
|
|
112
|
+
// The address is placed, which is the whole of what the `boolean` answers; a pane
|
|
113
|
+
// with no box yet is not a second no.
|
|
114
|
+
if (!hasBox(container.getBoundingClientRect())) {
|
|
115
|
+
held = () => void scrollToField(field);
|
|
75
116
|
return true;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
117
|
+
}
|
|
118
|
+
// A discrete act ("show me this field"), so it centres every time, unlike
|
|
119
|
+
// the continuous hop below.
|
|
120
|
+
centre(measure(slot, rectToPercent(box.rect, slot.size)));
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
function focusPosition(field, pos) {
|
|
124
|
+
const region = session.locate(field, pos);
|
|
125
|
+
if (!region)
|
|
126
|
+
return;
|
|
127
|
+
const slot = slots[region.page];
|
|
128
|
+
if (!slot)
|
|
129
|
+
return;
|
|
130
|
+
// Held before the guard below, which a pane measuring zero answers "already clear"
|
|
131
|
+
// for every keystroke of (`hasBox`): the follow would die silently for as long as
|
|
132
|
+
// a track is hidden. What is held is the request, not the trip, so it re-locates
|
|
133
|
+
// against whatever compile is current when the pane comes back.
|
|
134
|
+
if (!hasBox(container.getBoundingClientRect())) {
|
|
135
|
+
held = () => focusPosition(field, pos);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// The continuous hop: one call per keystroke and per arrow key, so it moves
|
|
139
|
+
// the pane only when the caret has left the fold. Centring unconditionally
|
|
140
|
+
// takes the scrollport back from the user on every one of them, including
|
|
141
|
+
// the ones that changed nothing about where the caret already was.
|
|
142
|
+
const target = measure(slot, rectToPercent(region.rect, slot.size));
|
|
143
|
+
if (clearOfTheFold(target, container.getBoundingClientRect()))
|
|
144
|
+
return;
|
|
145
|
+
centre(target);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
scrollToField,
|
|
149
|
+
focusPosition,
|
|
93
150
|
destroy() {
|
|
94
151
|
for (const off of unlisten)
|
|
95
152
|
off();
|
|
@@ -23,10 +23,11 @@ export interface PreviewController {
|
|
|
23
23
|
* apply-driven hop. */
|
|
24
24
|
refresh(change: ChangeSet): void;
|
|
25
25
|
/**
|
|
26
|
-
* Scroll `field`'s first box into view
|
|
27
|
-
* at that address — an answer, not a failure: the
|
|
28
|
-
* track, and the preview carries no schema to tell
|
|
29
|
-
* misnamed. The editor's `focusField` is what
|
|
26
|
+
* Scroll `field`'s first box into view, and end any follow it outranks. `false`
|
|
27
|
+
* when this compile places nothing at that address — an answer, not a failure: the
|
|
28
|
+
* plate places plenty it does not track, and the preview carries no schema to tell
|
|
29
|
+
* that from a field the host misnamed. The editor's `focusField` is what
|
|
30
|
+
* distinguishes them.
|
|
30
31
|
*/
|
|
31
32
|
scrollToField(field: DocPath): boolean;
|
|
32
33
|
/**
|
|
@@ -40,7 +41,7 @@ export interface PreviewController {
|
|
|
40
41
|
focusPosition(at: Place): void;
|
|
41
42
|
/**
|
|
42
43
|
* End the follow: the pane stays where the user left it until the next
|
|
43
|
-
* `focusPosition`.
|
|
44
|
+
* `focusPosition`. A `scrollToField` that placed its target ends one too.
|
|
44
45
|
*
|
|
45
46
|
* The editor's `onActiveLeafChange` is the signal, firing for a form control as for
|
|
46
47
|
* a prose leaf: `onActiveLeafChange={preview.endFollow}`. A control has no caret
|
|
@@ -84,12 +84,12 @@ export function createPreview(session, opts) {
|
|
|
84
84
|
}
|
|
85
85
|
render(session.pageCount, []);
|
|
86
86
|
// The last place the editor put the caret, re-located after every recompile and
|
|
87
|
-
// dropped on a focus change. `session.locate` answers against the
|
|
88
|
-
// layout and a consumer debounces `update`, so a caret typed past that
|
|
89
|
-
// content is off-content for the whole burst: `focusPosition` no-ops and
|
|
90
|
-
// sits still, and a caret event is otherwise the only thing that asks
|
|
91
|
-
// re-locate is the preview's, not the consumer's: the staleness sits
|
|
92
|
-
// session queries this module owns both ends of.
|
|
87
|
+
// dropped on a focus change or a discrete hop. `session.locate` answers against the
|
|
88
|
+
// last compiled layout and a consumer debounces `update`, so a caret typed past that
|
|
89
|
+
// layout's content is off-content for the whole burst: `focusPosition` no-ops and
|
|
90
|
+
// the pane sits still, and a caret event is otherwise the only thing that asks
|
|
91
|
+
// again. The re-locate is the preview's, not the consumer's: the staleness sits
|
|
92
|
+
// between two session queries this module owns both ends of.
|
|
93
93
|
let followed;
|
|
94
94
|
return {
|
|
95
95
|
refresh(change) {
|
|
@@ -100,7 +100,13 @@ export function createPreview(session, opts) {
|
|
|
100
100
|
bridge?.focusPosition(followed.field, followed.pos);
|
|
101
101
|
},
|
|
102
102
|
scrollToField(field) {
|
|
103
|
-
|
|
103
|
+
const placed = bridge?.scrollToField(field) ?? false;
|
|
104
|
+
// The discrete hop outranks the follow: a caret re-asserted at the next
|
|
105
|
+
// recompile would pull the pane straight off the field a host just asked
|
|
106
|
+
// for. A hop that placed nothing moved nothing, so the follow stands.
|
|
107
|
+
if (placed)
|
|
108
|
+
followed = undefined;
|
|
109
|
+
return placed;
|
|
104
110
|
},
|
|
105
111
|
focusPosition(at) {
|
|
106
112
|
followed = at;
|
|
@@ -14,17 +14,18 @@ export interface PdfPoint {
|
|
|
14
14
|
y: number;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* An address's boxes: `fieldBoxes`'s union, else
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* An address's boxes: `fieldBoxes`'s union, else every `regions()` rect at or under it.
|
|
18
|
+
* What the scroll reads, so an address `regions()` names is never one the preview
|
|
19
|
+
* cannot place (PREVIEW.md §"Two responsibilities").
|
|
20
20
|
*
|
|
21
21
|
* `fieldBoxes` answers `[]` for a scalar reference the plate places without tracking
|
|
22
|
-
* its content and for
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
22
|
+
* its content and for an array itself; runtime.d.ts says what such a field's box is, a
|
|
23
|
+
* single `regions()` rect. The second rung takes an address and its descendants
|
|
24
|
+
* together, since a container holds both: a plate composing an array's presentation
|
|
25
|
+
* regions the array on the ink it composed — a separator, a heading — where each
|
|
26
|
+
* element regions the ink it carries itself, and the field is placed at all of it. The
|
|
27
|
+
* boundary character (`.` for a nested field, `[` for an array element) keeps the
|
|
28
|
+
* prefix a path boundary: `main.keywords` matches `main.keywords[0]`, not
|
|
28
29
|
* `main.keywords_note`.
|
|
29
30
|
*/
|
|
30
31
|
export declare function boxesForField(field: string, boxes: readonly FieldRegion[], regions: readonly FieldRegion[]): readonly FieldRegion[];
|
package/dist/preview/geometry.js
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* An address's boxes: `fieldBoxes`'s union, else
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* An address's boxes: `fieldBoxes`'s union, else every `regions()` rect at or under it.
|
|
3
|
+
* What the scroll reads, so an address `regions()` names is never one the preview
|
|
4
|
+
* cannot place (PREVIEW.md §"Two responsibilities").
|
|
5
5
|
*
|
|
6
6
|
* `fieldBoxes` answers `[]` for a scalar reference the plate places without tracking
|
|
7
|
-
* its content and for
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* its content and for an array itself; runtime.d.ts says what such a field's box is, a
|
|
8
|
+
* single `regions()` rect. The second rung takes an address and its descendants
|
|
9
|
+
* together, since a container holds both: a plate composing an array's presentation
|
|
10
|
+
* regions the array on the ink it composed — a separator, a heading — where each
|
|
11
|
+
* element regions the ink it carries itself, and the field is placed at all of it. The
|
|
12
|
+
* boundary character (`.` for a nested field, `[` for an array element) keeps the
|
|
13
|
+
* prefix a path boundary: `main.keywords` matches `main.keywords[0]`, not
|
|
13
14
|
* `main.keywords_note`.
|
|
14
15
|
*/
|
|
15
16
|
export function boxesForField(field, boxes, regions) {
|
|
16
17
|
if (boxes.length)
|
|
17
18
|
return boxes;
|
|
18
|
-
|
|
19
|
-
if (own.length)
|
|
20
|
-
return own;
|
|
21
|
-
return regions.filter((r) => r.field.startsWith(`${field}.`) || r.field.startsWith(`${field}[`));
|
|
19
|
+
return regions.filter((r) => r.field === field || r.field.startsWith(`${field}.`) || r.field.startsWith(`${field}[`));
|
|
22
20
|
}
|
|
23
21
|
/**
|
|
24
22
|
* Forward: a PDF-pt rect -> CSS % of the page box. Percent positioning tracks
|
package/dist/preview/index.js
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
// land here. Reserved-package invariant: this subpath imports no editor-side code
|
|
5
5
|
// (the codec, the VisualEditor) so a `@quillmark/preview` promotion stays a
|
|
6
6
|
// re-export, not a refactor: it may reach `/core` (the shared WASM boundary) and
|
|
7
|
-
// nothing else. Enforced by `check:deps`,
|
|
7
|
+
// nothing else. Enforced by `check:deps`, at the subpath's edge: a relative hop past a
|
|
8
|
+
// module directly in `core/` is what pulls the codec, and all of ProseMirror with it.
|
|
8
9
|
// The theme derivation, which the page slots read through `var()`.
|
|
9
10
|
// Imported at the barrel because a subpath is what a consumer gets: this surface
|
|
10
11
|
// reaches modules inside `core/` and never its entry, so a sheet hanging off that
|
|
11
12
|
// entry arrives only for a consumer importing `/core` for some other reason. The
|
|
12
13
|
// sheet rather than the entry, because the barrel needs the derivation and not
|
|
13
|
-
// `init`. `check:deps`
|
|
14
|
+
// `init`. `check:deps` holds the reach.
|
|
14
15
|
import '../core/theme.css';
|
|
15
16
|
export { createPreview } from './controller.js';
|
|
16
17
|
export { default as Preview } from './Preview.svelte';
|
package/dist/preview/paint.js
CHANGED
|
@@ -192,9 +192,17 @@ onPaintError) {
|
|
|
192
192
|
return;
|
|
193
193
|
rafId = requestAnimationFrame(() => {
|
|
194
194
|
rafId = 0;
|
|
195
|
+
const width = container.clientWidth;
|
|
196
|
+
// A pane the shell has hidden reports a 0×0 box, which is a width to paint
|
|
197
|
+
// nothing at rather than a width that moved: `paintSlot` would take its
|
|
198
|
+
// `clientWidth || widthPt` fallback and freeze every mounted canvas at the 1×
|
|
199
|
+
// raster. The last real width and any pending force are held instead — the
|
|
200
|
+
// observer fires again when the pane has a box, and a return at that same
|
|
201
|
+
// width needs no raster, the pixels being the ones it was hidden with.
|
|
202
|
+
if (width === 0)
|
|
203
|
+
return;
|
|
195
204
|
const forced = forcePending;
|
|
196
205
|
forcePending = false;
|
|
197
|
-
const width = container.clientWidth;
|
|
198
206
|
if (!forced && width === lastWidth)
|
|
199
207
|
return;
|
|
200
208
|
lastWidth = width;
|