@quillmark/svelte 0.10.1 → 0.12.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 +1 -1
- package/THEMING.md +2 -2
- package/dist/core/address.d.ts +17 -10
- package/dist/core/address.js +26 -12
- package/dist/core/bloom.js +4 -3
- package/dist/core/codec/blocks.js +21 -1
- package/dist/core/codec/breaks.d.ts +10 -2
- package/dist/core/codec/breaks.js +65 -9
- package/dist/core/codec/code.d.ts +6 -4
- package/dist/core/codec/code.js +7 -4
- package/dist/core/codec/decode.js +103 -68
- package/dist/core/codec/encode.d.ts +5 -8
- package/dist/core/codec/encode.js +56 -86
- package/dist/core/codec/field.js +43 -24
- package/dist/core/codec/index.d.ts +2 -1
- package/dist/core/codec/index.js +6 -3
- package/dist/core/codec/islands.d.ts +16 -11
- package/dist/core/codec/islands.js +70 -59
- package/dist/core/codec/keymap.js +2 -1
- package/dist/core/codec/marks.d.ts +17 -13
- package/dist/core/codec/marks.js +39 -42
- package/dist/core/codec/positions.d.ts +4 -1
- package/dist/core/codec/positions.js +5 -1
- package/dist/core/codec/reconcile.d.ts +6 -0
- package/dist/core/codec/reconcile.js +19 -0
- package/dist/core/codec/schema.d.ts +5 -8
- package/dist/core/codec/schema.js +30 -156
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-view.js +77 -26
- package/dist/core/codec/table.d.ts +6 -5
- package/dist/core/codec/table.js +14 -31
- package/dist/core/codec/urls.d.ts +20 -0
- package/dist/core/codec/urls.js +45 -0
- package/dist/core/rungs.d.ts +8 -0
- package/dist/core/rungs.js +24 -0
- package/dist/core/theme.css +10 -0
- package/dist/preset/recipes.css +1 -1
- package/dist/preset/scale.css +7 -4
- package/dist/preview/bridge.d.ts +8 -1
- package/dist/preview/bridge.js +31 -25
- package/dist/preview/controller.js +13 -13
- package/dist/preview/geometry.d.ts +12 -1
- package/dist/preview/geometry.js +14 -1
- package/dist/preview/paint.js +6 -4
- package/dist/preview/strings.d.ts +0 -2
- package/dist/preview/strings.js +5 -6
- package/dist/visual/ArrayField.svelte +600 -173
- package/dist/visual/ArrayField.svelte.d.ts +14 -1
- package/dist/visual/BooleanField.svelte +10 -7
- package/dist/visual/Card.svelte +8 -30
- package/dist/visual/DateField.svelte +2 -2
- package/dist/visual/Field.svelte +76 -32
- package/dist/visual/FieldHint.svelte +1 -1
- package/dist/visual/MatrixField.svelte +352 -0
- package/dist/visual/MatrixField.svelte.d.ts +36 -0
- package/dist/visual/ObjectField.svelte +356 -133
- package/dist/visual/ObjectField.svelte.d.ts +17 -2
- package/dist/visual/VariantField.svelte +55 -2
- package/dist/visual/VariantField.svelte.d.ts +10 -1
- package/dist/visual/VisualEditor.svelte +1 -1
- package/dist/visual/VisualEditorInner.svelte +118 -67
- package/dist/visual/controls.css +39 -9
- package/dist/visual/diagnostics.d.ts +26 -2
- package/dist/visual/diagnostics.js +52 -5
- package/dist/visual/ext.d.ts +12 -13
- package/dist/visual/ext.js +22 -19
- package/dist/visual/hold.js +2 -2
- package/dist/visual/icons/nodes.d.ts +3 -0
- package/dist/visual/icons/nodes.js +2 -1
- package/dist/visual/index.js +2 -3
- package/dist/visual/leaves.d.ts +25 -12
- package/dist/visual/links.d.ts +4 -0
- package/dist/visual/links.js +15 -4
- package/dist/visual/motion.d.ts +14 -5
- package/dist/visual/motion.js +13 -11
- package/dist/visual/strings.d.ts +26 -13
- package/dist/visual/strings.js +7 -2
- package/dist/visual/structure.d.ts +101 -8
- package/dist/visual/structure.js +252 -13
- package/package.json +2 -2
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Where a script reads the scale. The derivation declares every `--_qm-*` rung on
|
|
2
|
+
// `[data-qm-root]` and nowhere else (`core/theme.css`), so a descendant's value is that
|
|
3
|
+
// one inherited and the root is the element the question is about: asking a leaf three
|
|
4
|
+
// containers deep is asking an arbitrary element what the surface is set to.
|
|
5
|
+
//
|
|
6
|
+
// The read is also the cost. `getComputedStyle` compounds with nesting depth under jsdom,
|
|
7
|
+
// so a suite that resolves rungs off landed elements pays the depth of whatever it landed
|
|
8
|
+
// on; resolving them off the root pays the root's.
|
|
9
|
+
/** The element the scale is declared on, for `node`: its root, or `node` where it stands
|
|
10
|
+
* outside one (a detached surface, a consumer's own mount). */
|
|
11
|
+
export function rungStyle(node) {
|
|
12
|
+
return getComputedStyle(node.closest('[data-qm-root]') ?? node);
|
|
13
|
+
}
|
|
14
|
+
/** A duration rung off `style`, in ms; `undefined` where the derivation is out of reach
|
|
15
|
+
* (an unstyled root, or jsdom, where `getComputedStyle` reports custom properties as
|
|
16
|
+
* empty). No rung, no motion: a fallback here would be the scale restated in the one
|
|
17
|
+
* place `check:style` cannot read. */
|
|
18
|
+
export function rungMs(style, rung) {
|
|
19
|
+
const raw = style.getPropertyValue(rung).trim();
|
|
20
|
+
const n = Number.parseFloat(raw);
|
|
21
|
+
if (!Number.isFinite(n))
|
|
22
|
+
return undefined;
|
|
23
|
+
return raw.endsWith('ms') ? n : n * 1000;
|
|
24
|
+
}
|
package/dist/core/theme.css
CHANGED
|
@@ -336,6 +336,16 @@
|
|
|
336
336
|
through the icon's `size`, so a stylesheet beats the SVG presentation attribute
|
|
337
337
|
and one rung is the whole of what a control draws. */
|
|
338
338
|
--_qm-glyph-control: 14px;
|
|
339
|
+
/* The narrowest track a control that fills its width stays usable in: a table's
|
|
340
|
+
column floors at it, rises no further than its content, and a row of them scrolls
|
|
341
|
+
sideways rather than crushing a cell. A column drawing a mark takes no floor — a
|
|
342
|
+
switch is one width at every width, so there is no width for it to be usable in
|
|
343
|
+
(`ArrayField`). What the field has spare stands after the last column, an inner
|
|
344
|
+
grid carrying its own rhythm rather than the card's. Under the field minimum the
|
|
345
|
+
capacity ladder steps at (`.qm-tracks`), since a cell in a row has the header to
|
|
346
|
+
name it and no label of its own to fit. A threshold rather than a rhythm choice,
|
|
347
|
+
so it does not move with `--qm-space`. */
|
|
348
|
+
--_qm-track-min: 7rem;
|
|
339
349
|
|
|
340
350
|
/* ── Type ────────────────────────────────────────────────────────────────
|
|
341
351
|
`--qm-font-size` anchors the chrome rung; 1.125 is the ratio between adjacent
|
package/dist/preset/recipes.css
CHANGED
|
@@ -112,7 +112,7 @@ html:has(.qm-workspace) {
|
|
|
112
112
|
display: none;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
/* One mount at
|
|
115
|
+
/* One mount at a time under the threshold, rather than two of them abreast. Half a narrow
|
|
116
116
|
viewport is under the width either surface reads at, so the split stops being one and
|
|
117
117
|
the tracks become views of the same room: the host says which with `data-qm-show`, and
|
|
118
118
|
the attribute names the track rather than what is in it, so nothing here knows an editor
|
package/dist/preset/scale.css
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
/* The host'
|
|
1
|
+
/* The host's derivation: the same public dials in, a closed `--qmh-*` scale for the
|
|
2
2
|
page around the surfaces out. `core/theme.css` is the same shape one level down
|
|
3
3
|
(dials in, `--_qm-*` out), and the two are calibrated against each other: the
|
|
4
|
-
figures here are the figures there (the ground step's ramp, 8% hover,
|
|
5
|
-
|
|
4
|
+
figures here are the figures there (the ground step's ramp, 8% hover, the
|
|
5
|
+
ring's width, the status hue at both poles) and the
|
|
6
6
|
dial means the same plane in both, so a host plate and a card on it land on the same
|
|
7
|
-
tone at both poles and a host page meets an editor's own column exactly. The
|
|
7
|
+
tone at both poles and a host page meets an editor's own column exactly. The border
|
|
8
|
+
and ink rungs are each calibrated for their own plane rather than shared: a page's
|
|
9
|
+
chrome is read at one size against one ground, a card's interior at several against
|
|
10
|
+
the rungs under it, so the mixes are not the same figure. The well
|
|
8
11
|
under the ground has no counterpart here: a host's own controls are boxed and carry a
|
|
9
12
|
hairline, so nothing on a page needs the step an edgeless field does.
|
|
10
13
|
|
package/dist/preview/bridge.d.ts
CHANGED
|
@@ -35,4 +35,11 @@ export interface VerticalSpan {
|
|
|
35
35
|
* the port: no scroll shows more of it.
|
|
36
36
|
*/
|
|
37
37
|
export declare function clearOfTheFold(target: VerticalSpan, port: VerticalSpan): boolean;
|
|
38
|
-
|
|
38
|
+
/** The trip a pane with no box could not run, held for the box coming back. One, and the
|
|
39
|
+
* last one asked for: a caret hop supersedes the hop before it on a showing pane too.
|
|
40
|
+
* A cell rather than closure state, so a bridge rebuilt under a changed page count
|
|
41
|
+
* inherits what the one it replaced was holding (`controller.ts`). */
|
|
42
|
+
export interface HeldTrip {
|
|
43
|
+
run?: () => void;
|
|
44
|
+
}
|
|
45
|
+
export declare function createBridge(session: LiveSession, container: HTMLElement, slots: readonly PageSlot[], onPick: ((at: Landing) => void) | undefined, held?: HeldTrip): BridgeController;
|
package/dist/preview/bridge.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { boxesForField, rectToPercent, clickToPdfPt, applyPercentRect } from './geometry.js';
|
|
1
|
+
import { boxesForField, rectToPercent, clickToPdfPt, pxToPt, applyPercentRect } from './geometry.js';
|
|
2
2
|
/** A pane hidden by the shell's narrow switch (`preset/recipes.css`) measures 0×0, and so
|
|
3
3
|
* does every box on it: the clearance arithmetic reads that as a caret clear of both
|
|
4
4
|
* edges, and a centring computes its trip out of zeros. Neither is an answer about where
|
|
@@ -9,6 +9,11 @@ function hasBox(port) {
|
|
|
9
9
|
/** What a rect reporting no height clears an edge by, in CSS px: a caret's own at the
|
|
10
10
|
* smallest zoom a page still reads at. */
|
|
11
11
|
const MIN_CLEARANCE = 8;
|
|
12
|
+
/** What a click may miss a field's ink by and still land on it, in CSS px of the page as
|
|
13
|
+
* drawn, so it converts per click at the scale the slot is showing. Sized as the
|
|
14
|
+
* pointer's imprecision and not as the leading it partly closes, which is a trade
|
|
15
|
+
* PREVIEW.md §"Click bridge" carries. */
|
|
16
|
+
const CLICK_SLACK_PX = 2;
|
|
12
17
|
/**
|
|
13
18
|
* Whether `target` sits clear of `port`'s edges by the fold's clearance, which is the
|
|
14
19
|
* target's own height: a caret rect flush against an edge is visible and unusable, and
|
|
@@ -29,14 +34,15 @@ export function clearOfTheFold(target, port) {
|
|
|
29
34
|
const clearance = Math.min(Math.max(target.height, MIN_CLEARANCE), (port.height - target.height) / 3);
|
|
30
35
|
return target.top - clearance >= port.top && target.bottom + clearance <= port.bottom;
|
|
31
36
|
}
|
|
32
|
-
export function createBridge(session, container, slots, onPick) {
|
|
37
|
+
export function createBridge(session, container, slots, onPick, held = {}) {
|
|
33
38
|
const unlisten = [];
|
|
34
39
|
// Two rungs: `positionAt` over span-tracked content, `fieldAt` over every placement
|
|
35
40
|
// the compile tracks, a strict superset of it (PREVIEW.md §"Click bridge" carries
|
|
36
41
|
// the measurement). The second fires where a field is placed without its content
|
|
37
42
|
// being tracked, and the pick carries no `pos` there: a fabricated `0` would be an
|
|
38
43
|
// invented offset wearing a real one's type. No third rung hit-tests `regions()`,
|
|
39
|
-
// whose rects bound ink the field does not fill
|
|
44
|
+
// whose rects bound ink the field does not fill; the slack both rungs carry is what
|
|
45
|
+
// that rung would have been reached for.
|
|
40
46
|
if (onPick) {
|
|
41
47
|
for (const { page, el } of slots) {
|
|
42
48
|
const handleClick = (ev) => {
|
|
@@ -50,10 +56,11 @@ export function createBridge(session, container, slots, onPick) {
|
|
|
50
56
|
const px = ev.clientX - box.left;
|
|
51
57
|
const py = ev.clientY - box.top;
|
|
52
58
|
const pt = clickToPdfPt(px, py, box.width, box.height, slot.size);
|
|
53
|
-
const
|
|
59
|
+
const tol = pxToPt(CLICK_SLACK_PX, box.width, slot.size);
|
|
60
|
+
const hit = session.positionAt(slot.page, pt.x, pt.y, tol);
|
|
54
61
|
if (hit)
|
|
55
62
|
return void onPick(hit);
|
|
56
|
-
const field = session.fieldAt(slot.page, pt.x, pt.y);
|
|
63
|
+
const field = session.fieldAt(slot.page, pt.x, pt.y, tol);
|
|
57
64
|
if (field)
|
|
58
65
|
onPick({ field });
|
|
59
66
|
};
|
|
@@ -72,52 +79,50 @@ export function createBridge(session, container, slots, onPick) {
|
|
|
72
79
|
marker.remove();
|
|
73
80
|
return rect;
|
|
74
81
|
}
|
|
75
|
-
// Centre `target` vertically in the scrollport; the cross axis takes the
|
|
76
|
-
// trip instead, since the container is a vertical scroller and only a page
|
|
77
|
-
// than the port has anything to move there.
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
// Centre `target` vertically in `port`, the scrollport's box; the cross axis takes the
|
|
83
|
+
// minimum trip instead, since the container is a vertical scroller and only a page
|
|
84
|
+
// wider than the port has anything to move there. `port` is the caller's own read: the
|
|
85
|
+
// marker `measure` places is absolute inside the slot, so nothing between that read
|
|
86
|
+
// and this one moves the container's box, and one forced layout serves the whole hop.
|
|
87
|
+
function centre(target, port) {
|
|
80
88
|
container.scrollTop += target.top + target.height / 2 - (port.top + port.height / 2);
|
|
81
89
|
if (target.left < port.left)
|
|
82
90
|
container.scrollLeft += target.left - port.left;
|
|
83
91
|
else if (target.right > port.right)
|
|
84
92
|
container.scrollLeft += target.right - port.right;
|
|
85
93
|
}
|
|
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
94
|
// The re-assert. Nothing else asks on the way back: `refresh` re-asks only on a
|
|
91
95
|
// recompile, and the reader who has just tapped Preview is not typing, so the pane
|
|
92
96
|
// would open where the caret was several edits ago. Guarded for jsdom, which ships no
|
|
93
97
|
// ResizeObserver and lays out nothing for one to report.
|
|
94
98
|
if (typeof ResizeObserver !== 'undefined') {
|
|
95
99
|
const observer = new ResizeObserver(() => {
|
|
96
|
-
if (!held || !hasBox(container.getBoundingClientRect()))
|
|
100
|
+
if (!held.run || !hasBox(container.getBoundingClientRect()))
|
|
97
101
|
return;
|
|
98
|
-
const trip = held;
|
|
99
|
-
held = undefined;
|
|
102
|
+
const trip = held.run;
|
|
103
|
+
held.run = undefined;
|
|
100
104
|
trip();
|
|
101
105
|
});
|
|
102
106
|
observer.observe(container);
|
|
103
107
|
unlisten.push(() => observer.disconnect());
|
|
104
108
|
}
|
|
105
109
|
function scrollToField(field) {
|
|
106
|
-
const box = boxesForField(field, session.fieldBoxes(field), session.regions())[0];
|
|
110
|
+
const box = boxesForField(field, session.fieldBoxes(field), () => session.regions())[0];
|
|
107
111
|
if (!box)
|
|
108
112
|
return false;
|
|
109
113
|
const slot = slots[box.page];
|
|
110
114
|
if (!slot)
|
|
111
115
|
return false;
|
|
116
|
+
const port = container.getBoundingClientRect();
|
|
112
117
|
// The address is placed, which is the whole of what the `boolean` answers; a pane
|
|
113
118
|
// with no box yet is not a second no.
|
|
114
|
-
if (!hasBox(
|
|
115
|
-
held = () => void scrollToField(field);
|
|
119
|
+
if (!hasBox(port)) {
|
|
120
|
+
held.run = () => void scrollToField(field);
|
|
116
121
|
return true;
|
|
117
122
|
}
|
|
118
123
|
// A discrete act ("show me this field"), so it centres every time, unlike
|
|
119
124
|
// the continuous hop below.
|
|
120
|
-
centre(measure(slot, rectToPercent(box.rect, slot.size)));
|
|
125
|
+
centre(measure(slot, rectToPercent(box.rect, slot.size)), port);
|
|
121
126
|
return true;
|
|
122
127
|
}
|
|
123
128
|
function focusPosition(field, pos) {
|
|
@@ -131,8 +136,9 @@ export function createBridge(session, container, slots, onPick) {
|
|
|
131
136
|
// for every keystroke of (`hasBox`): the follow would die silently for as long as
|
|
132
137
|
// a track is hidden. What is held is the request, not the trip, so it re-locates
|
|
133
138
|
// against whatever compile is current when the pane comes back.
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
const port = container.getBoundingClientRect();
|
|
140
|
+
if (!hasBox(port)) {
|
|
141
|
+
held.run = () => focusPosition(field, pos);
|
|
136
142
|
return;
|
|
137
143
|
}
|
|
138
144
|
// The continuous hop: one call per keystroke and per arrow key, so it moves
|
|
@@ -140,9 +146,9 @@ export function createBridge(session, container, slots, onPick) {
|
|
|
140
146
|
// takes the scrollport back from the user on every one of them, including
|
|
141
147
|
// the ones that changed nothing about where the caret already was.
|
|
142
148
|
const target = measure(slot, rectToPercent(region.rect, slot.size));
|
|
143
|
-
if (clearOfTheFold(target,
|
|
149
|
+
if (clearOfTheFold(target, port))
|
|
144
150
|
return;
|
|
145
|
-
centre(target);
|
|
151
|
+
centre(target, port);
|
|
146
152
|
}
|
|
147
153
|
return {
|
|
148
154
|
scrollToField,
|
|
@@ -7,14 +7,13 @@ const CONTAINER_CLASS = 'qm-preview';
|
|
|
7
7
|
// class, the hooks a consumer and the tests target.
|
|
8
8
|
const MESSAGE_CLASS = 'qm-preview-message';
|
|
9
9
|
const EMPTY_CLASS = 'qm-preview-empty';
|
|
10
|
-
const UNSUPPORTED_CLASS = 'qm-preview-unsupported';
|
|
11
10
|
const ERROR_CLASS = 'qm-preview-error';
|
|
12
11
|
export function createPreview(session, opts) {
|
|
13
12
|
const container = opts.container;
|
|
14
13
|
const margin = opts.margin ?? 1;
|
|
15
14
|
const t = mergePreviewStrings(opts.strings);
|
|
16
15
|
container.classList.add(CONTAINER_CLASS);
|
|
17
|
-
// One element, restamped: the non-paint states (empty,
|
|
16
|
+
// One element, restamped: the non-paint states (empty, a paint that
|
|
18
17
|
// threw) are mutually exclusive.
|
|
19
18
|
let message;
|
|
20
19
|
function showMessage(text, state) {
|
|
@@ -33,8 +32,8 @@ export function createPreview(session, opts) {
|
|
|
33
32
|
// slots and never calls the `paint`/`pageSize` verbs the boundary refuses
|
|
34
33
|
// there. A paint that unexpectedly throws is caught per-slot and surfaced
|
|
35
34
|
// through the shared message rather than aborting the observer callback
|
|
36
|
-
// mid-sweep (
|
|
37
|
-
//
|
|
35
|
+
// mid-sweep (a paint is the one surface that can still refuse a counted page;
|
|
36
|
+
// brittle to leave uncaught).
|
|
38
37
|
const paintLoop = createPaintLoop(session, container, margin, (page, err) => {
|
|
39
38
|
reportError(opts.onError, {
|
|
40
39
|
code: 'paint-failed',
|
|
@@ -50,24 +49,25 @@ export function createPreview(session, opts) {
|
|
|
50
49
|
// slots array is a stable identity but its members are swapped on reconcile, and
|
|
51
50
|
// the listeners are per slot.
|
|
52
51
|
let bridge;
|
|
52
|
+
// Outlives that rebuild, so a trip held for a pane with no box crosses it. Only the
|
|
53
|
+
// follow is re-asserted after a recompile, and a `scrollToField` the pane could not
|
|
54
|
+
// run has nothing else that would ask again for it.
|
|
55
|
+
const held = {};
|
|
53
56
|
function attach() {
|
|
54
|
-
bridge = createBridge(session, container, paintLoop.slots, opts.onPick);
|
|
57
|
+
bridge = createBridge(session, container, paintLoop.slots, opts.onPick, held);
|
|
55
58
|
}
|
|
56
59
|
function detach() {
|
|
57
60
|
bridge?.destroy();
|
|
58
61
|
bridge = undefined;
|
|
59
62
|
}
|
|
60
|
-
// Called at construction and after every `apply`, so
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
// capability generally, not just the page count.
|
|
63
|
+
// Called at construction and after every `apply`, so a 0-page compile that later
|
|
64
|
+
// gains pages recovers. A counted page is the whole condition: every backend
|
|
65
|
+
// paints, and `paint`/`pageSize` refuse only a page the count excludes.
|
|
64
66
|
function render(pageCount, dirtyPages) {
|
|
65
|
-
if (
|
|
66
|
-
// A 0-page compile is a recoverable empty; pages the boundary cannot
|
|
67
|
-
// raster are a genuine unsupported.
|
|
67
|
+
if (pageCount === 0) {
|
|
68
68
|
paintLoop.refresh([], 0);
|
|
69
69
|
detach();
|
|
70
|
-
showMessage(
|
|
70
|
+
showMessage(t.noPages, EMPTY_CLASS);
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
// Read the count against the current slots before reconcile moves it, and
|
|
@@ -27,8 +27,11 @@ export interface PdfPoint {
|
|
|
27
27
|
* boundary character (`.` for a nested field, `[` for an array element) keeps the
|
|
28
28
|
* prefix a path boundary: `main.keywords` matches `main.keywords[0]`, not
|
|
29
29
|
* `main.keywords_note`.
|
|
30
|
+
*
|
|
31
|
+
* `regions` is a thunk because the first rung answers without it, and taking it
|
|
32
|
+
* marshals every placement in the compile across the WASM boundary.
|
|
30
33
|
*/
|
|
31
|
-
export declare function boxesForField(field: string, boxes: readonly FieldRegion[], regions: readonly FieldRegion[]): readonly FieldRegion[];
|
|
34
|
+
export declare function boxesForField(field: string, boxes: readonly FieldRegion[], regions: () => readonly FieldRegion[]): readonly FieldRegion[];
|
|
32
35
|
/**
|
|
33
36
|
* Forward: a PDF-pt rect -> CSS % of the page box. Percent positioning tracks
|
|
34
37
|
* the page's displayed size across DPR and container resize for free, so the
|
|
@@ -41,6 +44,14 @@ export declare function rectToPercent(rect: PdfRect, pageSize: PageSize): Percen
|
|
|
41
44
|
* the transform that produces it rather than at the caller placing a marker.
|
|
42
45
|
*/
|
|
43
46
|
export declare function applyPercentRect(el: HTMLElement, pct: PercentRect): void;
|
|
47
|
+
/**
|
|
48
|
+
* A CSS-px length on a page element of CSS width `cssW` -> the PDF-pt length it spans.
|
|
49
|
+
* Width alone, because that is the scale the slot is drawn at: `paint.ts` sizes a slot
|
|
50
|
+
* by width under a locked aspect ratio, so {@link clickToPdfPt}'s two ratios are one
|
|
51
|
+
* number and a length has one to convert through. What the click bridge reads its
|
|
52
|
+
* pointer slack in points through.
|
|
53
|
+
*/
|
|
54
|
+
export declare function pxToPt(px: number, cssW: number, pageSize: PageSize): number;
|
|
44
55
|
/**
|
|
45
56
|
* Inverse: a click at CSS px `(px, py)` within a page element of CSS size
|
|
46
57
|
* `(cssW, cssH)` -> PDF-pt. The exact inverse of {@link rectToPercent}: the
|
package/dist/preview/geometry.js
CHANGED
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
* boundary character (`.` for a nested field, `[` for an array element) keeps the
|
|
13
13
|
* prefix a path boundary: `main.keywords` matches `main.keywords[0]`, not
|
|
14
14
|
* `main.keywords_note`.
|
|
15
|
+
*
|
|
16
|
+
* `regions` is a thunk because the first rung answers without it, and taking it
|
|
17
|
+
* marshals every placement in the compile across the WASM boundary.
|
|
15
18
|
*/
|
|
16
19
|
export function boxesForField(field, boxes, regions) {
|
|
17
20
|
if (boxes.length)
|
|
18
21
|
return boxes;
|
|
19
|
-
return regions.filter((r) => r.field === field || r.field.startsWith(`${field}.`) || r.field.startsWith(`${field}[`));
|
|
22
|
+
return regions().filter((r) => r.field === field || r.field.startsWith(`${field}.`) || r.field.startsWith(`${field}[`));
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
22
25
|
* Forward: a PDF-pt rect -> CSS % of the page box. Percent positioning tracks
|
|
@@ -47,6 +50,16 @@ export function applyPercentRect(el, pct) {
|
|
|
47
50
|
height: `${pct.height}%`
|
|
48
51
|
});
|
|
49
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* A CSS-px length on a page element of CSS width `cssW` -> the PDF-pt length it spans.
|
|
55
|
+
* Width alone, because that is the scale the slot is drawn at: `paint.ts` sizes a slot
|
|
56
|
+
* by width under a locked aspect ratio, so {@link clickToPdfPt}'s two ratios are one
|
|
57
|
+
* number and a length has one to convert through. What the click bridge reads its
|
|
58
|
+
* pointer slack in points through.
|
|
59
|
+
*/
|
|
60
|
+
export function pxToPt(px, cssW, pageSize) {
|
|
61
|
+
return (px / cssW) * pageSize.widthPt;
|
|
62
|
+
}
|
|
50
63
|
/**
|
|
51
64
|
* Inverse: a click at CSS px `(px, py)` within a page element of CSS size
|
|
52
65
|
* `(cssW, cssH)` -> PDF-pt. The exact inverse of {@link rectToPercent}: the
|
package/dist/preview/paint.js
CHANGED
|
@@ -41,7 +41,11 @@ onPaintError) {
|
|
|
41
41
|
// responsibilities"). `border-box` costs nothing here and keeps a slot a
|
|
42
42
|
// consumer insets from measuring wider than its paper.
|
|
43
43
|
const el = document.createElement('div');
|
|
44
|
-
|
|
44
|
+
// Not `.qm-page`: that name is the preset's page baseline, an unlayered rule at
|
|
45
|
+
// ordinary specificity a host puts on `<body>`, and a slot wearing it takes that
|
|
46
|
+
// rule's `color-scheme` inside a mounted root, which the derivation leaves to the
|
|
47
|
+
// host (`core/theme.css`).
|
|
48
|
+
el.className = 'qm-page-slot';
|
|
45
49
|
// The page's index, in the DOM. `pageByEl` is this module's and a consumer
|
|
46
50
|
// drawing an overlay of its own needs the number: position among siblings is
|
|
47
51
|
// the only handle without it, which is right today and is not a contract.
|
|
@@ -157,9 +161,7 @@ onPaintError) {
|
|
|
157
161
|
}
|
|
158
162
|
// Slots are built on the first `refresh` (the controller calls it immediately),
|
|
159
163
|
// not here: reconcile → `session.pageSize` must run after the controller's
|
|
160
|
-
// `
|
|
161
|
-
// pages would throw on `pageSize` (runtime.d.ts: it succeeds iff `supportsCanvas`)
|
|
162
|
-
// before the controller can surface its "unsupported" message.
|
|
164
|
+
// `pageCount` gate, since `pageSize` throws for a page the count excludes.
|
|
163
165
|
// ── Keep mounted rasters in step with the display ───────────────────────────
|
|
164
166
|
// Every paint freezes `canvas.style.width/height` to the box width at that
|
|
165
167
|
// paint (paintSlot). A page that stays mounted while the container's CSS width
|
|
@@ -3,8 +3,6 @@ export interface PreviewStrings {
|
|
|
3
3
|
renderFailed: string;
|
|
4
4
|
/** A compile with zero pages: recoverable, and the commonest state of a fresh seed. */
|
|
5
5
|
noPages: string;
|
|
6
|
-
/** A compile whose pages this backend cannot raster at all. */
|
|
7
|
-
unsupported: string;
|
|
8
6
|
}
|
|
9
7
|
/** The package's English. Every key, so the merge below is total. */
|
|
10
8
|
export declare const DEFAULT_PREVIEW_STRINGS: PreviewStrings;
|
package/dist/preview/strings.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Every word the preview says
|
|
2
|
-
//
|
|
1
|
+
// Every word the preview says: the surface is a canvas, and its only prose is the
|
|
2
|
+
// message states that stand in when there is nothing to paint.
|
|
3
3
|
//
|
|
4
4
|
// Separate from the visual surface's set rather than pooled with it: `/preview`
|
|
5
5
|
// reaches `/core` and nothing editor-side, which is what lets it promote to its own
|
|
@@ -8,13 +8,12 @@
|
|
|
8
8
|
// twice rather than learning two idioms.
|
|
9
9
|
//
|
|
10
10
|
// A plain options field rather than a context channel, unlike the visual surface's:
|
|
11
|
-
//
|
|
12
|
-
//
|
|
11
|
+
// they are read in one module by one function, so there is no tree to make them
|
|
12
|
+
// ambient for.
|
|
13
13
|
/** The package's English. Every key, so the merge below is total. */
|
|
14
14
|
export const DEFAULT_PREVIEW_STRINGS = {
|
|
15
15
|
renderFailed: 'Preview failed to render.',
|
|
16
|
-
noPages: 'No pages to preview.'
|
|
17
|
-
unsupported: 'Preview is not available for this document.'
|
|
16
|
+
noPages: 'No pages to preview.'
|
|
18
17
|
};
|
|
19
18
|
/** Fill a partial override to the whole key set. */
|
|
20
19
|
export function mergePreviewStrings(custom) {
|