@quillmark/svelte 0.2.0 → 0.3.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 +2 -2
- package/THEMING.md +11 -7
- package/dist/core/address.d.ts +7 -7
- package/dist/core/address.js +6 -6
- package/dist/core/bloom.d.ts +5 -5
- package/dist/core/bloom.js +8 -8
- package/dist/core/codec/anchor.d.ts +1 -1
- package/dist/core/codec/anchor.js +1 -1
- package/dist/core/codec/atoms.d.ts +10 -0
- package/dist/core/codec/atoms.js +78 -0
- package/dist/core/codec/clipboard.d.ts +59 -0
- package/dist/core/codec/clipboard.js +202 -0
- package/dist/core/codec/code.d.ts +1 -1
- package/dist/core/codec/code.js +4 -4
- package/dist/core/codec/decode.d.ts +1 -1
- package/dist/core/codec/decode.js +8 -8
- package/dist/core/codec/encode.d.ts +4 -4
- package/dist/core/codec/encode.js +10 -10
- package/dist/core/codec/field.d.ts +21 -26
- package/dist/core/codec/field.js +33 -32
- package/dist/core/codec/index.js +5 -8
- package/dist/core/codec/inputrules.js +56 -18
- package/dist/core/codec/islands.d.ts +11 -7
- package/dist/core/codec/islands.js +16 -10
- package/dist/core/codec/keymap.d.ts +5 -6
- package/dist/core/codec/keymap.js +8 -7
- package/dist/core/codec/lists.d.ts +3 -3
- package/dist/core/codec/lists.js +32 -13
- package/dist/core/codec/marks.d.ts +1 -1
- package/dist/core/codec/marks.js +1 -1
- package/dist/core/codec/positions.js +1 -1
- package/dist/core/codec/prose.css +230 -215
- package/dist/core/codec/schema.js +4 -4
- package/dist/core/codec/slash.d.ts +39 -41
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-view.d.ts +12 -16
- package/dist/core/codec/table-view.js +488 -292
- package/dist/core/codec/table.d.ts +42 -15
- package/dist/core/codec/table.js +70 -26
- package/dist/core/errors.d.ts +5 -5
- package/dist/core/index.js +2 -2
- package/dist/core/lifecycle.js +1 -1
- package/dist/core/rebind.svelte.d.ts +2 -2
- package/dist/core/rebind.svelte.js +4 -4
- package/dist/core/teardown.d.ts +1 -1
- package/dist/core/teardown.js +1 -1
- package/dist/core/theme.css +144 -54
- package/dist/preset/index.css +1 -1
- package/dist/preset/recipes.css +31 -29
- package/dist/preset/scale.css +36 -25
- package/dist/preview/Preview.svelte +12 -10
- package/dist/preview/Preview.svelte.d.ts +3 -3
- package/dist/preview/bridge.js +4 -4
- package/dist/preview/controller.js +3 -3
- package/dist/preview/geometry.d.ts +2 -2
- package/dist/preview/geometry.js +2 -2
- package/dist/preview/index.js +3 -3
- package/dist/preview/overlay.d.ts +1 -1
- package/dist/preview/overlay.js +3 -3
- package/dist/preview/paint.js +15 -17
- package/dist/visual/ArrayField.svelte +80 -66
- package/dist/visual/ArrayField.svelte.d.ts +1 -1
- package/dist/visual/BooleanField.svelte +8 -6
- package/dist/visual/BooleanField.svelte.d.ts +2 -2
- package/dist/visual/Card.svelte +151 -325
- package/dist/visual/Card.svelte.d.ts +2 -6
- package/dist/visual/CardControls.svelte +1 -1
- package/dist/visual/DateField.svelte +19 -19
- package/dist/visual/DateField.svelte.d.ts +3 -3
- package/dist/visual/DiagnosticList.svelte +1 -1
- package/dist/visual/EnumField.svelte +16 -16
- package/dist/visual/EnumField.svelte.d.ts +2 -2
- package/dist/visual/Field.svelte +128 -115
- package/dist/visual/Field.svelte.d.ts +1 -1
- package/dist/visual/FieldHint.svelte +24 -42
- package/dist/visual/FieldHint.svelte.d.ts +1 -1
- package/dist/visual/FieldLabel.svelte +8 -8
- package/dist/visual/FormatPopover.svelte +80 -35
- package/dist/visual/NumberField.svelte +5 -5
- package/dist/visual/NumberField.svelte.d.ts +1 -1
- package/dist/visual/ObjectField.svelte +9 -9
- package/dist/visual/ObjectField.svelte.d.ts +1 -1
- package/dist/visual/ProseArrayElement.svelte +11 -13
- package/dist/visual/ProseArrayElement.svelte.d.ts +1 -1
- package/dist/visual/ProseField.svelte +53 -84
- package/dist/visual/ProseField.svelte.d.ts +12 -17
- package/dist/visual/SlashMenu.svelte +42 -16
- package/dist/visual/TextField.svelte +5 -5
- package/dist/visual/TextField.svelte.d.ts +1 -1
- package/dist/visual/TipsCard.svelte +43 -28
- package/dist/visual/VisualEditor.svelte +6 -6
- package/dist/visual/VisualEditorInner.svelte +122 -129
- package/dist/visual/controls.css +74 -30
- package/dist/visual/diagnostics.d.ts +5 -5
- package/dist/visual/diagnostics.js +3 -3
- package/dist/visual/domid.js +3 -3
- package/dist/visual/ext.d.ts +1 -1
- package/dist/visual/ext.js +1 -1
- package/dist/visual/icons/nodes.d.ts +3 -0
- package/dist/visual/icons/nodes.js +2 -1
- package/dist/visual/index.js +4 -4
- package/dist/visual/leaves.d.ts +3 -3
- package/dist/visual/leaves.js +1 -1
- package/dist/visual/links.d.ts +24 -0
- package/dist/visual/links.js +67 -0
- package/dist/visual/props.d.ts +8 -8
- package/dist/visual/signals.d.ts +6 -6
- package/dist/visual/strings.d.ts +14 -19
- package/dist/visual/strings.js +5 -7
- package/dist/visual/structure.d.ts +24 -17
- package/dist/visual/structure.js +8 -8
- package/dist/visual/synced.svelte.d.ts +1 -1
- package/dist/visual/synced.svelte.js +4 -4
- package/dist/visual/tips.d.ts +2 -2
- package/dist/visual/tips.js +3 -3
- package/package.json +1 -1
package/dist/preset/scale.css
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
/*
|
|
2
|
-
page
|
|
3
|
-
(dials in, `--_qm-*` out), and the two are calibrated against each other: the
|
|
4
|
-
here are the
|
|
5
|
-
|
|
6
|
-
on the same tone at both poles
|
|
1
|
+
/* The host'S derivation: the same public dials in, a closed `--qmh-*` scale for the
|
|
2
|
+
page around the surfaces out. `core/theme.css` is the same shape one level down
|
|
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 two arms, 8% hover, 17%
|
|
5
|
+
border, 45% and 60% ink) and the dial means the same plane in both, so a host plate
|
|
6
|
+
and a card on it land on the same tone at both poles and a host page meets an
|
|
7
|
+
editor's own column exactly. The well under the ground has no counterpart here: a
|
|
8
|
+
host's own controls are boxed and carry a hairline, so nothing on a page needs the
|
|
9
|
+
step an edgeless field does.
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
declared
|
|
11
|
+
Two scales rather than one because they answer to different roots. `--_qm-*` is
|
|
12
|
+
declared on each mounted root, out of reach from an ancestor, so a surface holds its
|
|
10
13
|
scale wherever it is dropped. A host scale has the opposite job: it is the document,
|
|
11
14
|
so it is declared at `:root` and everything on the page reads it. A single namespace
|
|
12
15
|
could not be both.
|
|
@@ -14,26 +17,34 @@
|
|
|
14
17
|
Literals live here and nowhere else in the preset: `check:style` runs its axes over
|
|
15
18
|
`recipes.css` beside it, with this file exempt as the place the defaults are minted.
|
|
16
19
|
|
|
17
|
-
The poles fall back to the
|
|
18
|
-
where its own unstyled text does
|
|
19
|
-
|
|
20
|
+
The poles fall back to the system colours, so a host that turns no dial still lands
|
|
21
|
+
where its own unstyled text does — `Canvas` being the plane the UA would have
|
|
22
|
+
painted a page with, which is what a plate should be. The dials sit in front, so
|
|
23
|
+
`--qm-bg`/`--qm-fg` on an ancestor retunes page and surface together. */
|
|
20
24
|
|
|
21
25
|
:root {
|
|
22
26
|
/* ── Colour ──────────────────────────────────────────────────────────────── */
|
|
23
|
-
|
|
27
|
+
/* The dial is the plate, exactly as it is the card one level down: a plate beside
|
|
28
|
+
a mounted editor is the same plane as a card in it, so both read the pole and
|
|
29
|
+
the page sinks from it by the same two arms. That is what puts a host page and
|
|
30
|
+
an editor's own column on one tone rather than a plane apart. Planes step by
|
|
31
|
+
light and everything else by ink, which is the package's own split
|
|
32
|
+
(ARCHITECTURE §Styling); the figures are its figures. */
|
|
33
|
+
--qmh-surface: var(--qm-bg, Canvas);
|
|
34
|
+
--qmh-page: light-dark(
|
|
35
|
+
oklab(from var(--qmh-surface) calc(l - 0.0214) calc(a - 0.00065) calc(b - 0.00158)),
|
|
36
|
+
oklab(from var(--qmh-surface) calc(l - 0.0553) calc(a - 0.00048) calc(b + 0.00165))
|
|
37
|
+
);
|
|
24
38
|
--qmh-ink: var(--qm-fg, CanvasText);
|
|
25
|
-
--qmh-ink-meta: color-mix(in oklab, var(--qmh-ink), var(--qmh-
|
|
26
|
-
--qmh-ghost: color-mix(in oklab, var(--qmh-ink), var(--qmh-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
--qmh-surface: color-mix(in oklab, var(--qmh-page), var(--qmh-ink) 4%);
|
|
30
|
-
--qmh-surface-hover: color-mix(in oklab, var(--qmh-page), var(--qmh-ink) 8%);
|
|
31
|
-
--qmh-border: color-mix(in oklab, var(--qmh-page), var(--qmh-ink) 17%);
|
|
39
|
+
--qmh-ink-meta: color-mix(in oklab, var(--qmh-ink), var(--qmh-surface) 45%);
|
|
40
|
+
--qmh-ghost: color-mix(in oklab, var(--qmh-ink), var(--qmh-surface) 60%);
|
|
41
|
+
--qmh-surface-hover: color-mix(in oklab, var(--qmh-surface), var(--qmh-ink) 8%);
|
|
42
|
+
--qmh-border: color-mix(in oklab, var(--qmh-surface), var(--qmh-ink) 17%);
|
|
32
43
|
/* The edge that says "engaged" where the hairline says "edge": a pressed switch,
|
|
33
44
|
the underline under a link. */
|
|
34
|
-
--qmh-border-strong: color-mix(in oklab, var(--qmh-
|
|
45
|
+
--qmh-border-strong: color-mix(in oklab, var(--qmh-surface), var(--qmh-ink) 40%);
|
|
35
46
|
|
|
36
|
-
/* The two hues a host spends, and it spends them on a message. Both read the
|
|
47
|
+
/* The two hues a host spends, and it spends them on a message. Both read the dial,
|
|
37
48
|
so a page carries one colour meaning error and one meaning warning rather than a
|
|
38
49
|
second pair beside the surfaces'. The dark half is a lift the dials do not carry:
|
|
39
50
|
a hue calibrated against white reads as a stain on a dark page. */
|
|
@@ -42,7 +53,7 @@
|
|
|
42
53
|
|
|
43
54
|
/* ── Rhythm ──────────────────────────────────────────────────────────────── */
|
|
44
55
|
/* One base and its multiples, the same four steps the surfaces derive. A host
|
|
45
|
-
separating
|
|
56
|
+
separating sections down a page wants steps above these; it derives them from
|
|
46
57
|
this base rather than minting them, which is what keeps its ramp one ramp. */
|
|
47
58
|
--qmh-space: var(--qm-space, 0.25rem);
|
|
48
59
|
--qmh-space-half: calc(var(--qmh-space) / 2);
|
|
@@ -58,7 +69,7 @@
|
|
|
58
69
|
so it does not derive from the spacing dial. */
|
|
59
70
|
--qmh-measure: 34rem;
|
|
60
71
|
|
|
61
|
-
/* The floor under a control a
|
|
72
|
+
/* The floor under a control a thumb lands on. Hand geometry rather than rhythm, which
|
|
62
73
|
is why it does not derive from the spacing dial either: 44px is where both platform
|
|
63
74
|
guidelines and WCAG put a touch target, and a control padded to a pointer's size is
|
|
64
75
|
under it whatever rung the padding reads. */
|
|
@@ -73,7 +84,7 @@
|
|
|
73
84
|
--qmh-font: var(--qm-font, ui-sans-serif, system-ui, sans-serif);
|
|
74
85
|
--qmh-font-mono: var(--qm-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
75
86
|
|
|
76
|
-
/* The page's own ramp, and it does
|
|
87
|
+
/* The page's own ramp, and it does not read `--qm-font-size`: that dial anchors the
|
|
77
88
|
surfaces' ramp, which is chrome inside a card, and a page's body text is a
|
|
78
89
|
different job at a different size. A host that wants them locked sets both. */
|
|
79
90
|
--qmh-text: 1rem;
|
|
@@ -94,7 +105,7 @@
|
|
|
94
105
|
--qmh-ease-reverse: cubic-bezier(0.4, 0, 0.6, 1);
|
|
95
106
|
}
|
|
96
107
|
|
|
97
|
-
/* Collapse the
|
|
108
|
+
/* Collapse the scale, not each transition: every animated recipe reads the rung, so one
|
|
98
109
|
override reaches all of them with no `*` rule touching mounted package elements. */
|
|
99
110
|
@media (prefers-reduced-motion: reduce) {
|
|
100
111
|
:root {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
paint.ts/overlay.ts/bridge.ts/controller.ts own the behavior. Exposes the
|
|
5
5
|
`PreviewController` verbs as instance methods (`bind:this`) for a consumer
|
|
6
6
|
that drives `refresh`/`scrollToField`/`focusPosition`/`setZoom` imperatively
|
|
7
|
-
(e.g. after `session.apply` elsewhere)
|
|
7
|
+
(e.g. after `session.apply` elsewhere).
|
|
8
8
|
-->
|
|
9
9
|
<script lang="ts">
|
|
10
10
|
import { onMount } from 'svelte';
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
import type { EditorErrorHandler } from '../core/errors.js';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Remount contract. `createPreview` binds once in `onMount`; a later change to any
|
|
20
20
|
* prop it closed over (`session`, `margin`, `overlays`, `onPick`, `onError`,
|
|
21
|
-
* `strings`) is
|
|
22
|
-
* the session by
|
|
21
|
+
* `strings`) is not observed, and each reports `rebind-ignored` when swapped. Swap
|
|
22
|
+
* the session by remounting (`{#key session}`, as the playground does); drive
|
|
23
23
|
* in-place edits through the `refresh(change)` method, not a prop change.
|
|
24
24
|
*
|
|
25
25
|
* `onError` is itself once-bound, so a swapped handler means the report of its own
|
|
@@ -97,15 +97,17 @@
|
|
|
97
97
|
<div bind:this={containerEl} class="qm-preview {className ?? ''}" {style} data-qm-root></div>
|
|
98
98
|
|
|
99
99
|
<style>
|
|
100
|
-
/* A
|
|
100
|
+
/* A detached root: the preview is not a descendant of the editor, so it carries
|
|
101
101
|
`data-qm-root` for the page/overlay rungs paint.ts and overlay.ts read
|
|
102
102
|
(core/theme.css).
|
|
103
103
|
|
|
104
|
-
The
|
|
105
|
-
tone behind it is
|
|
106
|
-
in.
|
|
104
|
+
The desk as well as the paper: a sheet is at `--_qm-surface` (paint.ts), so the
|
|
105
|
+
tone behind it is the sunken rung and the gutter is the margin the sheet floats
|
|
106
|
+
in. Paper is the brighter plane at both poles, exactly as a card is over the
|
|
107
|
+
editor's own column, so the two panes step the same way. Both are
|
|
108
|
+
the surface's own, so a bare `<div>` is a mounting site. The
|
|
107
109
|
padding is safe against the paint loop: a slot is a `width: 100%` child and
|
|
108
|
-
measures the
|
|
110
|
+
measures the content box, and the container's own `clientWidth` is read only as
|
|
109
111
|
a change detector, which a constant inset does not disturb. */
|
|
110
112
|
.qm-preview {
|
|
111
113
|
box-sizing: border-box;
|
|
@@ -114,6 +116,6 @@
|
|
|
114
116
|
height: 100%;
|
|
115
117
|
overflow-y: auto;
|
|
116
118
|
padding: var(--_qm-space-4);
|
|
117
|
-
background: var(--_qm-surface-
|
|
119
|
+
background: var(--_qm-surface-sunken);
|
|
118
120
|
}
|
|
119
121
|
</style>
|
|
@@ -3,10 +3,10 @@ import type { LiveSession, ChangeSet } from '@quillmark/wasm';
|
|
|
3
3
|
import type { DocPath, Landing, Place } from '../core/address.js';
|
|
4
4
|
import type { EditorErrorHandler } from '../core/errors.js';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Remount contract. `createPreview` binds once in `onMount`; a later change to any
|
|
7
7
|
* prop it closed over (`session`, `margin`, `overlays`, `onPick`, `onError`,
|
|
8
|
-
* `strings`) is
|
|
9
|
-
* the session by
|
|
8
|
+
* `strings`) is not observed, and each reports `rebind-ignored` when swapped. Swap
|
|
9
|
+
* the session by remounting (`{#key session}`, as the playground does); drive
|
|
10
10
|
* in-place edits through the `refresh(change)` method, not a prop change.
|
|
11
11
|
*
|
|
12
12
|
* `onError` is itself once-bound, so a swapped handler means the report of its own
|
package/dist/preview/bridge.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { boxesForField, rectToPercent, clickToPdfPt, applyPercentRect } from './geometry.js';
|
|
2
2
|
export function createBridge(session, container, slots, onPick) {
|
|
3
3
|
const unlisten = [];
|
|
4
|
-
//
|
|
4
|
+
// Two rungs: `positionAt` over span-tracked content, `fieldAt` over every placement
|
|
5
5
|
// the compile tracks, a strict superset of it (PREVIEW.md §"Click bridge" carries
|
|
6
6
|
// the measurement). The second fires where a field is placed without its content
|
|
7
|
-
// being tracked, and the pick carries
|
|
7
|
+
// being tracked, and the pick carries no `pos` there: a fabricated `0` would be an
|
|
8
8
|
// invented offset wearing a real one's type. No third rung hit-tests `regions()`,
|
|
9
9
|
// whose rects bound ink the field does not fill.
|
|
10
10
|
if (onPick) {
|
|
@@ -53,7 +53,7 @@ export function createBridge(session, container, slots, onPick) {
|
|
|
53
53
|
else if (target.right > port.right)
|
|
54
54
|
container.scrollLeft += target.right - port.right;
|
|
55
55
|
}
|
|
56
|
-
// Clear of the fold is the target's
|
|
56
|
+
// Clear of the fold is the target's own height of clearance at each edge, not bare
|
|
57
57
|
// intersection: a caret rect flush against one is visible and unusable, and the
|
|
58
58
|
// next line typed lands past it. Derived from the target rather than a margin
|
|
59
59
|
// dial, so it scales with zoom exactly as the caret does.
|
|
@@ -81,7 +81,7 @@ export function createBridge(session, container, slots, onPick) {
|
|
|
81
81
|
const slot = slots[region.page];
|
|
82
82
|
if (!slot)
|
|
83
83
|
return;
|
|
84
|
-
// The
|
|
84
|
+
// The continuous hop: one call per keystroke and per arrow key, so it moves
|
|
85
85
|
// the pane only when the caret has left the fold. Centring unconditionally
|
|
86
86
|
// takes the scrollport back from the user on every one of them, including
|
|
87
87
|
// the ones that changed nothing about where the caret already was.
|
|
@@ -83,8 +83,8 @@ export function createPreview(session, opts) {
|
|
|
83
83
|
showMessage(pageCount === 0 ? t.noPages : t.unsupported, pageCount === 0 ? EMPTY_CLASS : UNSUPPORTED_CLASS);
|
|
84
84
|
return;
|
|
85
85
|
}
|
|
86
|
-
// Read the count against the current slots
|
|
87
|
-
// clear any prior message
|
|
86
|
+
// Read the count against the current slots before reconcile moves it, and
|
|
87
|
+
// clear any prior message before painting so a paint that throws mid-refresh
|
|
88
88
|
// leaves its error surfaced rather than have `hideMessage` clobber it.
|
|
89
89
|
const countChanged = pageCount !== paintLoop.slots.length;
|
|
90
90
|
hideMessage();
|
|
@@ -104,7 +104,7 @@ export function createPreview(session, opts) {
|
|
|
104
104
|
}
|
|
105
105
|
render(session.pageCount, []);
|
|
106
106
|
// The last place the editor put the caret, re-located after every recompile.
|
|
107
|
-
// `session.locate` answers against the
|
|
107
|
+
// `session.locate` answers against the last compiled layout and a consumer
|
|
108
108
|
// debounces `update`, so a caret typed past that layout's content is off-content
|
|
109
109
|
// for the whole burst: `focusPosition` no-ops and the pane sits still, and a
|
|
110
110
|
// caret event is otherwise the only thing that asks again. The re-locate is the
|
|
@@ -15,7 +15,7 @@ export interface PdfPoint {
|
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* An address's boxes: `fieldBoxes`'s union, else its own `regions()` rects, else the
|
|
18
|
-
* rects of everything
|
|
18
|
+
* rects of everything under it. The one rule the overlay and the scroll both read, so
|
|
19
19
|
* an address `regions()` names never draws nothing (PREVIEW.md §"Three
|
|
20
20
|
* responsibilities").
|
|
21
21
|
*
|
|
@@ -43,7 +43,7 @@ export declare function rectToPercent(rect: PdfRect, pageSize: PageSize): Percen
|
|
|
43
43
|
export declare function applyPercentRect(el: HTMLElement, pct: PercentRect): void;
|
|
44
44
|
/**
|
|
45
45
|
* Inverse: a click at CSS px `(px, py)` within a page element of CSS size
|
|
46
|
-
* `(cssW, cssH)` -> PDF-pt. The
|
|
46
|
+
* `(cssW, cssH)` -> PDF-pt. The exact inverse of {@link rectToPercent}: the
|
|
47
47
|
* fraction `rectToPercent` would place a point's box corner at is the same
|
|
48
48
|
* fraction this reads back from `px/cssW`, `py/cssH`; DPR-independent, since
|
|
49
49
|
* the page element's CSS size (not its canvas backing store) is the only input.
|
package/dist/preview/geometry.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* An address's boxes: `fieldBoxes`'s union, else its own `regions()` rects, else the
|
|
3
|
-
* rects of everything
|
|
3
|
+
* rects of everything under it. The one rule the overlay and the scroll both read, so
|
|
4
4
|
* an address `regions()` names never draws nothing (PREVIEW.md §"Three
|
|
5
5
|
* responsibilities").
|
|
6
6
|
*
|
|
@@ -50,7 +50,7 @@ export function applyPercentRect(el, pct) {
|
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* Inverse: a click at CSS px `(px, py)` within a page element of CSS size
|
|
53
|
-
* `(cssW, cssH)` -> PDF-pt. The
|
|
53
|
+
* `(cssW, cssH)` -> PDF-pt. The exact inverse of {@link rectToPercent}: the
|
|
54
54
|
* fraction `rectToPercent` would place a point's box corner at is the same
|
|
55
55
|
* fraction this reads back from `px/cssW`, `py/cssH`; DPR-independent, since
|
|
56
56
|
* the page element's CSS size (not its canvas backing store) is the only input.
|
package/dist/preview/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// `@quillmark/svelte/preview`: the live-preview surface.
|
|
2
2
|
//
|
|
3
3
|
// `createPreview(session)` (vanilla-TS core) + a thin `<Preview>` Svelte wrapper
|
|
4
|
-
// land here.
|
|
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
7
|
// nothing else. Enforced by `check:deps`, which walks the subpath's import graph.
|
|
8
8
|
// The theme derivation, which the page slots and the overlay read through `var()`.
|
|
9
|
-
// Imported at the
|
|
9
|
+
// Imported at the barrel because a subpath is what a consumer gets: this surface
|
|
10
10
|
// reaches modules inside `core/` and never its entry, so a sheet hanging off that
|
|
11
11
|
// entry arrives only for a consumer importing `/core` for some other reason. The
|
|
12
12
|
// sheet rather than the entry, because the barrel needs the derivation and not
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import '../core/theme.css';
|
|
15
15
|
export { createPreview } from './controller.js';
|
|
16
16
|
export { default as Preview } from './Preview.svelte';
|
|
17
|
-
// What the preview
|
|
17
|
+
// What the preview says when there is nothing to paint. Keyed and partial; the
|
|
18
18
|
// English is exported so a consumer composes against it rather than restating it.
|
|
19
19
|
// The merge is the surface's own: a consumer hands `strings` to a prop, never
|
|
20
20
|
// merges one.
|
|
@@ -3,7 +3,7 @@ import type { PageSlot } from './paint.js';
|
|
|
3
3
|
export interface OverlayController {
|
|
4
4
|
/** Re-read geometry and rebuild every box; call after a layout-affecting refresh. */
|
|
5
5
|
refresh(): void;
|
|
6
|
-
/** Bloom the boxes at `field`
|
|
6
|
+
/** Bloom the boxes at `field` and under it: transient, and a no-op when `field`
|
|
7
7
|
* is already the marked one. */
|
|
8
8
|
flashField(field: string): void;
|
|
9
9
|
destroy(): void;
|
package/dist/preview/overlay.js
CHANGED
|
@@ -9,7 +9,7 @@ export function createOverlay(session, slots) {
|
|
|
9
9
|
slot.el.appendChild(layer);
|
|
10
10
|
return layer;
|
|
11
11
|
});
|
|
12
|
-
// The marked address, and with it the bloom's start.
|
|
12
|
+
// The marked address, and with it the bloom's start. One variable does both jobs:
|
|
13
13
|
// the change guard has to outlive the decay (a caret move after the wash ended
|
|
14
14
|
// must not re-mark the same field), and `bloom` refuses an `elapsed` past the
|
|
15
15
|
// decay, so a spent flash is inert on every later rebuild and never needs
|
|
@@ -20,7 +20,7 @@ export function createOverlay(session, slots) {
|
|
|
20
20
|
let flash;
|
|
21
21
|
/** field → its boxes, rebuilt with them. The bloom's only lookup. */
|
|
22
22
|
let byField = new Map();
|
|
23
|
-
// The boxes a flash covers: the address's own, plus every one
|
|
23
|
+
// The boxes a flash covers: the address's own, plus every one under it, since the
|
|
24
24
|
// boxes are keyed as `regions()` names them (`main.references.0`) and an
|
|
25
25
|
// editor-side signal names the declared field (`main.references`). Resolved per
|
|
26
26
|
// apply rather than at `flashField`, so a rebuild picks up boxes the recompile
|
|
@@ -33,7 +33,7 @@ export function createOverlay(session, slots) {
|
|
|
33
33
|
}
|
|
34
34
|
return els;
|
|
35
35
|
}
|
|
36
|
-
// Start or
|
|
36
|
+
// Start or resume the bloom on whatever boxes exist for it now: one path, because
|
|
37
37
|
// starting is resuming at ~0. A field surfaces several boxes and they share
|
|
38
38
|
// `startedAt`, so they bloom in step rather than shimmering unevenly.
|
|
39
39
|
function applyFlash() {
|
package/dist/preview/paint.js
CHANGED
|
@@ -33,14 +33,13 @@ onPaintError) {
|
|
|
33
33
|
}, { root: container, threshold: 0 });
|
|
34
34
|
function makeSlot(page) {
|
|
35
35
|
const size = session.pageSize(page);
|
|
36
|
-
// The sheet
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
// boundary
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
// the paper.
|
|
36
|
+
// The sheet is a plane and the desk is the one under it: paper sits at
|
|
37
|
+
// `--_qm-surface` and the container paints `--_qm-surface-sunken`, so the
|
|
38
|
+
// edge is where two tones meet and no stroke is drawn around it
|
|
39
|
+
// (ARCHITECTURE §Styling). It leaves the overlay's no-ink-at-rest rule
|
|
40
|
+
// untouched, since a boundary that is not painted is not a mark on the page
|
|
41
|
+
// (PREVIEW §Overlay). `border-box` costs nothing here and keeps a slot a
|
|
42
|
+
// consumer insets from measuring wider than its paper.
|
|
44
43
|
const el = document.createElement('div');
|
|
45
44
|
el.className = 'qm-page';
|
|
46
45
|
// The page's index, in the DOM. `pageByEl` is this module's and a consumer
|
|
@@ -52,8 +51,7 @@ onPaintError) {
|
|
|
52
51
|
boxSizing: 'border-box',
|
|
53
52
|
width: '100%',
|
|
54
53
|
aspectRatio: `${size.widthPt} / ${size.heightPt}`,
|
|
55
|
-
background: 'var(--_qm-surface)'
|
|
56
|
-
border: 'var(--_qm-border-width) solid var(--_qm-border)'
|
|
54
|
+
background: 'var(--_qm-surface)'
|
|
57
55
|
});
|
|
58
56
|
if (page > 0)
|
|
59
57
|
el.style.marginTop = PAGE_GAP;
|
|
@@ -63,7 +61,7 @@ onPaintError) {
|
|
|
63
61
|
return { page, size, el };
|
|
64
62
|
}
|
|
65
63
|
// Rasterize `slot` into its canvas (creating the canvas if this is its first
|
|
66
|
-
// paint since mounting). `layoutScale` is derived from the slot's
|
|
64
|
+
// paint since mounting). `layoutScale` is derived from the slot's own current
|
|
67
65
|
// CSS width, so layout width tracks the container per the zoom settled
|
|
68
66
|
// decision; `canvas.style.*` is then set from the authoritative PaintResult,
|
|
69
67
|
// never guessed.
|
|
@@ -74,7 +72,7 @@ onPaintError) {
|
|
|
74
72
|
canvas.className = 'qm-page-canvas';
|
|
75
73
|
Object.assign(canvas.style, { position: 'absolute', inset: '0', display: 'block' });
|
|
76
74
|
}
|
|
77
|
-
// Contextless canvas: bail
|
|
75
|
+
// Contextless canvas: bail before registering, so `updateBand` still sees
|
|
78
76
|
// the page as unmounted and retries instead of keeping a blank page.
|
|
79
77
|
const ctx = canvas.getContext('2d');
|
|
80
78
|
if (!ctx)
|
|
@@ -108,7 +106,7 @@ onPaintError) {
|
|
|
108
106
|
canvases.delete(page);
|
|
109
107
|
}
|
|
110
108
|
// Mount pages newly inside [min(visible)-margin, max(visible)+margin], unmount
|
|
111
|
-
// pages that fell outside it. Only touches pages
|
|
109
|
+
// pages that fell outside it. Only touches pages changing band membership;
|
|
112
110
|
// an already-mounted page in the band is left alone (paint contract: an idle
|
|
113
111
|
// canvas keeps its pixels for free; repainting it here would be pure waste).
|
|
114
112
|
function updateBand() {
|
|
@@ -158,13 +156,13 @@ onPaintError) {
|
|
|
158
156
|
}
|
|
159
157
|
}
|
|
160
158
|
// Slots are built on the first `refresh` (the controller calls it immediately),
|
|
161
|
-
//
|
|
159
|
+
// not here: reconcile → `session.pageSize` must run after the controller's
|
|
162
160
|
// `supportsCanvas`/`pageCount` gate, since a `supportsCanvas: false` compile with
|
|
163
161
|
// pages would throw on `pageSize` (runtime.d.ts: it succeeds iff `supportsCanvas`)
|
|
164
162
|
// before the controller can surface its "unsupported" message.
|
|
165
163
|
// ── Keep mounted rasters in step with the display ───────────────────────────
|
|
166
|
-
// Every paint freezes `canvas.style.width/height` to the box width
|
|
167
|
-
//
|
|
164
|
+
// Every paint freezes `canvas.style.width/height` to the box width at that
|
|
165
|
+
// paint (paintSlot). A page that stays mounted while the container's CSS width
|
|
168
166
|
// or `devicePixelRatio` shifts would otherwise keep a stale raster the %-space
|
|
169
167
|
// overlay/click math silently drifts off of: the box tracks the container, the
|
|
170
168
|
// ink does not. Two observers close the gap by repainting mounted pages from
|
|
@@ -212,7 +210,7 @@ onPaintError) {
|
|
|
212
210
|
resizeObserver?.observe(container);
|
|
213
211
|
// `devicePixelRatio` changes (window dragged to a different-DPI monitor, browser
|
|
214
212
|
// zoom) leave CSS-px width untouched, so the ResizeObserver never sees them;
|
|
215
|
-
// watch DPR directly. A `(resolution: …dppx)` query pins the
|
|
213
|
+
// watch DPR directly. A `(resolution: …dppx)` query pins the current ratio and
|
|
216
214
|
// is therefore one-shot: re-arm it against the new ratio on each change. Guarded
|
|
217
215
|
// for environments (jsdom) without `matchMedia`.
|
|
218
216
|
let dprQuery;
|