@quillmark/svelte 0.27.0 → 0.28.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/NOTICE +4 -4
- package/README.md +26 -8
- package/THEMING.md +10 -8
- package/dist/core/address.d.ts +35 -64
- package/dist/core/address.js +35 -55
- package/dist/core/bloom.d.ts +4 -10
- package/dist/core/bloom.js +14 -27
- package/dist/core/codec/blocks.d.ts +41 -43
- package/dist/core/codec/blocks.js +97 -130
- package/dist/core/codec/code.d.ts +7 -10
- package/dist/core/codec/code.js +37 -46
- package/dist/core/codec/decode.d.ts +19 -23
- package/dist/core/codec/decode.js +76 -111
- package/dist/core/codec/encode.d.ts +11 -15
- package/dist/core/codec/encode.js +60 -67
- package/dist/core/codec/field.d.ts +62 -95
- package/dist/core/codec/field.js +107 -179
- package/dist/core/codec/islands.d.ts +33 -41
- package/dist/core/codec/islands.js +76 -86
- package/dist/core/codec/lists.d.ts +17 -26
- package/dist/core/codec/lists.js +117 -157
- package/dist/core/codec/marks.d.ts +4 -3
- package/dist/core/codec/marks.js +8 -3
- package/dist/core/codec/prose.css +84 -46
- package/dist/core/codec/schema.d.ts +18 -23
- package/dist/core/codec/schema.js +84 -76
- package/dist/core/codec/slash.d.ts +42 -48
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-copy.d.ts +4 -3
- package/dist/core/codec/table-copy.js +25 -7
- package/dist/core/codec/table-view.d.ts +12 -12
- package/dist/core/codec/table-view.js +460 -383
- package/dist/core/codec/table.d.ts +94 -61
- package/dist/core/codec/table.js +331 -83
- package/dist/core/codec/tasks.d.ts +6 -0
- package/dist/core/codec/tasks.js +30 -3
- package/dist/core/errors.d.ts +15 -30
- package/dist/core/errors.js +2 -6
- package/dist/core/index.js +7 -12
- package/dist/core/lifecycle.d.ts +7 -16
- package/dist/core/lifecycle.js +10 -21
- package/dist/core/rebind.svelte.d.ts +10 -20
- package/dist/core/rebind.svelte.js +15 -27
- package/dist/core/rungs.d.ts +14 -11
- package/dist/core/rungs.js +17 -19
- package/dist/core/teardown.d.ts +4 -6
- package/dist/core/teardown.js +7 -10
- package/dist/core/theme.css +74 -54
- package/dist/index.js +3 -8
- package/dist/preset/app.css +3 -1
- package/dist/preset/index.css +6 -8
- package/dist/preset/recipes.css +12 -15
- package/dist/preset/scale.css +18 -17
- package/dist/preset/shell.css +10 -0
- package/dist/preview/bridge.d.ts +18 -22
- package/dist/preview/bridge.js +29 -58
- package/dist/preview/controller.d.ts +21 -29
- package/dist/preview/controller.js +14 -39
- package/dist/preview/geometry.d.ts +13 -34
- package/dist/preview/geometry.js +13 -34
- package/dist/preview/index.js +4 -16
- package/dist/preview/paint.d.ts +3 -3
- package/dist/preview/paint.js +28 -68
- package/dist/preview/strings.d.ts +1 -1
- package/dist/preview/strings.js +2 -14
- package/dist/preview/zoom.d.ts +18 -23
- package/dist/preview/zoom.js +28 -27
- package/dist/visual/ArrayField.svelte +77 -16
- package/dist/visual/BooleanField.svelte +27 -14
- package/dist/visual/Card.svelte +61 -48
- package/dist/visual/CardControls.svelte +5 -11
- package/dist/visual/DateField.svelte +20 -4
- package/dist/visual/EnumField.svelte +3 -0
- package/dist/visual/FieldHint.svelte +2 -1
- package/dist/visual/FormatPopover.svelte +82 -15
- package/dist/visual/FormatPopover.svelte.d.ts +4 -0
- package/dist/visual/MatrixAdd.svelte +30 -12
- package/dist/visual/MatrixField.svelte +24 -16
- package/dist/visual/NumberField.svelte +7 -0
- package/dist/visual/ObjectField.svelte +4 -4
- package/dist/visual/ProseField.svelte +0 -5
- package/dist/visual/TipsCard.svelte +11 -10
- package/dist/visual/VariantField.svelte +3 -1
- package/dist/visual/VisualEditorInner.svelte +19 -3
- package/dist/visual/controls.css +7 -2
- package/dist/visual/fonts.d.ts +18 -0
- package/dist/visual/fonts.js +59 -0
- package/dist/visual/icons/nodes.d.ts +0 -5
- package/dist/visual/icons/nodes.js +0 -4
- package/dist/visual/props.d.ts +14 -1
- package/dist/visual/strings.d.ts +7 -0
- package/dist/visual/strings.js +5 -2
- package/dist/visual/structure.d.ts +43 -87
- package/dist/visual/structure.js +42 -83
- package/package.json +2 -2
package/dist/core/theme.css
CHANGED
|
@@ -101,11 +101,10 @@
|
|
|
101
101
|
--_qm-font-mono: var(--qm-font-mono, ui-monospace, monospace);
|
|
102
102
|
|
|
103
103
|
/* ── Colour ──────────────────────────────────────────────────────────────
|
|
104
|
-
Ink steps fg → bg
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
for (17% ≈ #d4d4d4, 35% ≈ #555).
|
|
104
|
+
Ink steps fg → bg and the hover rung steps bg → fg, both mixed in oklab;
|
|
105
|
+
sRGB muddies the mid-tones and the label rung is exactly mid-tone. So
|
|
106
|
+
inverting the poles inverts that half of the scale. The percentages are
|
|
107
|
+
calibrated to the light-mode literals they stand in for (35% ≈ #555).
|
|
109
108
|
|
|
110
109
|
Planes are the half that does not read the ink pole, because what makes a
|
|
111
110
|
card read as a card is that it is the brighter plane, in both schemes
|
|
@@ -115,57 +114,68 @@
|
|
|
115
114
|
light, and the base plane — a card, the preview's paper — is the pole
|
|
116
115
|
itself.
|
|
117
116
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
117
|
+
One plane sinks, in two roles. `well` is the hole cut into a card: a field,
|
|
118
|
+
the one native select, neither of which draws an edge, so the step is the
|
|
119
|
+
whole of what says a value is typed there. `sunken` is the ground — the
|
|
120
|
+
column behind the cards, the desk behind the sheets — and it is the same
|
|
121
|
+
tone: a card draws no edge either, so the step off the ground is the whole
|
|
122
|
+
of where one card stops and the next starts, and the well's step is one
|
|
123
|
+
that carries that at the light pole. The order is card, then ground
|
|
124
|
+
and well together, at both poles.
|
|
126
125
|
|
|
127
|
-
How far
|
|
126
|
+
How far it sinks is a function of the card's own L, not of the scheme the
|
|
128
127
|
host declared: a dark surface has to spend more of what little it has
|
|
129
128
|
before the card above it reads as lifted, so the two calibrated distances
|
|
130
|
-
— 0.
|
|
129
|
+
— 0.04 off white, 0.0547 off the dark default — interpolate linearly
|
|
131
130
|
between. A card pinned at neither pole takes a proportionate step whatever
|
|
132
131
|
`color-scheme` says, which is the half of the derivation that reads a
|
|
133
132
|
colour rather than a declaration.
|
|
134
133
|
|
|
135
|
-
|
|
134
|
+
It sinks by subtraction where every other rung mixes: a mix toward black
|
|
136
135
|
is a ratio, so it scales what it takes and pulls the result toward
|
|
137
136
|
neutral, and a ground is a fixed distance. `a b` pass through, so the
|
|
138
137
|
plane keeps the pole's hue and a turned `--qm-bg` tints it. The defaults
|
|
139
|
-
land within a channel of `#
|
|
140
|
-
|
|
138
|
+
land within a channel of `#f2f2f2` and `#1a1a1c`. Below L ≈ 0.06 the
|
|
139
|
+
plane clamps to black, which is where a near-black card leaves no room
|
|
141
140
|
for a ground of its own. */
|
|
142
141
|
--_qm-surface: var(--_qm-bg);
|
|
143
|
-
--_qm-surface-sunken: oklab(from var(--_qm-bg) calc(l - (0.0214 + 0.0467 * (1 - l))) a b);
|
|
144
142
|
--_qm-surface-well: oklab(from var(--_qm-bg) calc(l - (0.04 + 0.0202 * (1 - l))) a b);
|
|
143
|
+
--_qm-surface-sunken: var(--_qm-surface-well);
|
|
145
144
|
/* `raised` is the same ramp spelled as a mix, its step 0.08 × (1 − l): the light
|
|
146
145
|
arm is zero, a white card having nowhere further to go, so a floating surface
|
|
147
146
|
separates on its hairline and `--_qm-lift` there and the step joins them in
|
|
148
147
|
dark. */
|
|
149
148
|
--_qm-surface-raised: color-mix(in oklab, var(--_qm-bg), #fff 8%);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
--_qm-surface-
|
|
155
|
-
/*
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
149
|
+
/* A step toward the ink laid over whatever plane is under it, rather than a tone off
|
|
150
|
+
the card: a hover lands on the card, a well, a band and a floating surface, and a
|
|
151
|
+
fixed tone stepped off the first sits level with `raised` in dark. Translucent, as
|
|
152
|
+
the accent's tint is, so the step is the same over each. */
|
|
153
|
+
--_qm-surface-hover: color-mix(in oklab, var(--_qm-fg) 8%, transparent);
|
|
154
|
+
/* A band laid on the card rather than cut into it: a table's header, the pill a
|
|
155
|
+
grip draws, the off switch's track and the unticked box. A resting fill, where
|
|
156
|
+
`hover` above is a state, so the two have to be told apart on a card that shows
|
|
157
|
+
both at once: each arm stands one ground step past the hover step at its pole,
|
|
158
|
+
which lands the band two steps off the card in dark and a little over in light.
|
|
159
|
+
It steps toward the ink at both poles, a shade under white in light and a shade
|
|
160
|
+
over the card in dark, where `well` sinks to a black stripe. */
|
|
161
|
+
--_qm-surface-band: light-dark(
|
|
162
|
+
color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 12%),
|
|
163
|
+
color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 18%)
|
|
164
|
+
);
|
|
165
|
+
/* The one stroke: a card's two horizontals, a section's and a subform's verticals,
|
|
166
|
+
a table's frame and the lines between its cells, a floating surface's edge, the
|
|
167
|
+
popover's divider. One and a half ground steps off the card at both poles, the
|
|
168
|
+
same derivation the well takes, so a hairline stands off the card by the same
|
|
169
|
+
measure a card stands off the ground whatever the scheme. Toward the ink, which
|
|
170
|
+
is the half of the step `light-dark()` carries: the well always sinks, a stroke
|
|
171
|
+
darkens a light card and lightens a dark one. On the defaults the arms land near
|
|
172
|
+
#ececec and #3b3c3e. The multiple is the whole calibration: at it a stroke reads
|
|
173
|
+
in light without outweighing the type beside it, and reads in dark without being
|
|
174
|
+
the loudest line on the card. */
|
|
160
175
|
--_qm-border: light-dark(
|
|
161
|
-
|
|
162
|
-
|
|
176
|
+
oklab(from var(--_qm-bg) calc(l - 1.5 * (0.04 + 0.0202 * (1 - l))) a b),
|
|
177
|
+
oklab(from var(--_qm-bg) calc(l + 1.5 * (0.04 + 0.0202 * (1 - l))) a b)
|
|
163
178
|
);
|
|
164
|
-
/* A stroke ruled through content rather than round it: a table's frame and every
|
|
165
|
-
line between its cells. The light arm of `border` is a card's edge against the
|
|
166
|
-
ground, and drawn between lines of type it vanishes, so a grid takes one ratio at
|
|
167
|
-
both poles, which lands within a few points of `border` in dark. */
|
|
168
|
-
--_qm-rule: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 14%);
|
|
169
179
|
/* Three rungs, and nothing under the last: a step below `label` lands under 3:1 on
|
|
170
180
|
the well a field draws it against, and what would take it — an unset value's word,
|
|
171
181
|
an invitation — is content a reader has to read (WCAG 1.4.3), not decoration.
|
|
@@ -259,7 +269,7 @@
|
|
|
259
269
|
only up to the point where it is assembled. One width: focus is the only
|
|
260
270
|
state that draws a ring anywhere, so there is no held-back tier under it.
|
|
261
271
|
Sub-pixel, because at two it reads as a second border on a card whose every
|
|
262
|
-
other
|
|
272
|
+
other stroke is a hairline. */
|
|
263
273
|
--_qm-ring-width: 1.5px;
|
|
264
274
|
/* Every ring the surface draws, focus and selection alike, which is why the name
|
|
265
275
|
carries neither. Assembled here because the parts spelled out at a use site pass
|
|
@@ -296,17 +306,17 @@
|
|
|
296
306
|
|
|
297
307
|
/* ── Geometry ──────────────────────────────────────────────────────────*/
|
|
298
308
|
/* One stroke width for every edge the chrome draws and every rule it ranks
|
|
299
|
-
with: a
|
|
300
|
-
rather than a rhythm choice, like the tap floor: a hairline is a
|
|
301
|
-
any `--qm-space`, so neither derives from a dial. Both exist as rungs because
|
|
309
|
+
with: a control's, a floating surface's, the two closing a card's payload. A
|
|
310
|
+
threshold rather than a rhythm choice, like the tap floor: a hairline is a
|
|
311
|
+
hairline at any `--qm-space`, so neither derives from a dial. Both exist as rungs because
|
|
302
312
|
`check:style`'s border-width axis can only hold a stroke against a token. */
|
|
303
313
|
--_qm-border-width: 1px;
|
|
304
314
|
/* Half a step over it, for the verticals alone — a section's, a subform's, a
|
|
305
315
|
blockquote's. What a vertical states is depth, and depth is the one thing a
|
|
306
|
-
reader has to find in a dense card; the strokes that
|
|
307
|
-
stay at the hairline, so the ladder is what carries the extra
|
|
308
|
-
because a whole step reads as a different kind of line rather
|
|
309
|
-
one doing more work. */
|
|
316
|
+
reader has to find in a dense card; the strokes that rank two or edge a
|
|
317
|
+
floating surface stay at the hairline, so the ladder is what carries the extra
|
|
318
|
+
weight. Half, because a whole step reads as a different kind of line rather
|
|
319
|
+
than the same one doing more work. */
|
|
310
320
|
--_qm-vertical-width: 1.5px;
|
|
311
321
|
--_qm-radius: var(--qm-radius, 8px);
|
|
312
322
|
/* The interior corner, and half is the relationship rather than the number: a
|
|
@@ -342,6 +352,15 @@
|
|
|
342
352
|
`check:style`'s rhythm axis sees each of them read a rung, never that they read
|
|
343
353
|
the same one — the case `--_qm-inset-control` above is cut for. */
|
|
344
354
|
--_qm-nest: var(--_qm-space-3);
|
|
355
|
+
/* The numerals a list marker is given room for: two digits, so a tenth item's number
|
|
356
|
+
stands inside the leaf as the first's does. In `ch`, so it tracks the face the
|
|
357
|
+
numbers are set in. */
|
|
358
|
+
--_qm-marker-digits: 2ch;
|
|
359
|
+
/* The text column, measured in from a block's own inline inset: a section's vertical
|
|
360
|
+
plus the rung its heading stands off it, so a heading riding that vertical and a
|
|
361
|
+
line beside it start on one edge. A rung for the reason the nesting step is: the
|
|
362
|
+
card and the tip tucked under `main` are two components holding one edge. */
|
|
363
|
+
--_qm-column: calc(var(--_qm-space) + var(--_qm-vertical-width));
|
|
345
364
|
/* The scroll tail: dead space under the last card, so it can be read at the
|
|
346
365
|
middle of a pane rather than at its bottom edge. A fraction of the viewport
|
|
347
366
|
rather than a step on the ramp, because what it answers is how far the eye
|
|
@@ -358,6 +377,10 @@
|
|
|
358
377
|
rather than through the icon's `size`, so a stylesheet beats the SVG presentation
|
|
359
378
|
attribute and one rung is the whole of what a control draws. */
|
|
360
379
|
--_qm-glyph-control: 14px;
|
|
380
|
+
/* The stroke a control glyph draws, in screen length: the frame's 2 units of 24 at
|
|
381
|
+
the control rung. A glyph drawn at another edge reads it under
|
|
382
|
+
`vector-effect: non-scaling-stroke`, so every glyph strokes at one weight. */
|
|
383
|
+
--_qm-glyph-stroke: calc(var(--_qm-glyph-control) / 12);
|
|
361
384
|
/* The narrowest track a control that fills its width stays usable in: a table's
|
|
362
385
|
column floors at it, rises no further than its content, and a row of them scrolls
|
|
363
386
|
sideways rather than crushing a cell. A column drawing a mark takes no floor — a
|
|
@@ -374,22 +397,19 @@
|
|
|
374
397
|
--_qm-track-prose: calc(var(--_qm-track-min) * 2);
|
|
375
398
|
|
|
376
399
|
/* ── Type ────────────────────────────────────────────────────────────────
|
|
377
|
-
`--qm-font-size` anchors the chrome rung
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
already is. */
|
|
400
|
+
`--qm-font-size` anchors the chrome rung and `label` stands one 1.125 step under
|
|
401
|
+
it. The ratio and the weight are fixed conventions, not dials: no consumer tunes
|
|
402
|
+
a typographic ratio, and a dial nobody turns is a name every reader still holds.
|
|
403
|
+
Two chrome rungs: nothing the chrome names ranks above the value it names, a card
|
|
404
|
+
title being body at the mid weight, and a second step below `label` lands near
|
|
405
|
+
11px, under a size anything here wants to be read at. */
|
|
384
406
|
--_qm-text-body: var(--qm-font-size, 0.875rem);
|
|
385
|
-
--_qm-text-title: calc(var(--_qm-text-body) * 1.125);
|
|
386
407
|
--_qm-text-label: calc(var(--_qm-text-body) / 1.125);
|
|
387
408
|
/* Paper: the document body answers to the preview's paper where every other
|
|
388
409
|
surface here answers to the control beside it, so a rung shared with the chrome
|
|
389
410
|
makes a consumer asking for larger prose take larger inputs with it. 16/15 where
|
|
390
411
|
the ramp steps at 9/8: small enough to read as a change of register rather than
|
|
391
|
-
a rung of hierarchy
|
|
392
|
-
exactly. */
|
|
412
|
+
a rung of hierarchy. */
|
|
393
413
|
--_qm-text-paper: calc(var(--_qm-text-body) * 16 / 15);
|
|
394
414
|
/* Two weights over the regular, and only one of them is chrome. `mid` names
|
|
395
415
|
everything the surface names — a card title, a section header, a field
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
// `@quillmark/svelte` root: the substrate, free of any UI framework.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// `/visual`) so a bundler pulls only what an entry reaches; the root
|
|
5
|
-
// re-exports `/core` (the address vocabulary, the error channel, `init`) for a
|
|
6
|
-
// consumer that wants one import for the substrate. It carries no framework code.
|
|
7
|
-
// The `@quillmark/wasm` API is imported from the peer dependency directly, never
|
|
8
|
-
// from here.
|
|
1
|
+
// `@quillmark/svelte` root: the substrate, free of any UI framework. The headline surfaces mount
|
|
2
|
+
// from their own subpaths (`/preview`, `/visual`); the root re-exports `/core` for a consumer that
|
|
3
|
+
// wants one import for the substrate.
|
|
9
4
|
export * from './core/index.js';
|
package/dist/preset/app.css
CHANGED
|
@@ -27,9 +27,11 @@
|
|
|
27
27
|
overflow-wrap: anywhere;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
/* The foot: a line of meta type under a hairline, its links in the
|
|
30
|
+
/* The foot: a line of meta type on the head's plate under a hairline, its links in the
|
|
31
|
+
line's own ink. The two bands framing the panes stand on one plate. */
|
|
31
32
|
:where(.qm-app-footer) {
|
|
32
33
|
padding: var(--qmh-space-2) var(--qmh-space-4);
|
|
34
|
+
background: var(--qmh-surface);
|
|
33
35
|
border-block-start: var(--qmh-border-width) solid var(--qmh-border);
|
|
34
36
|
font-size: var(--qmh-text-meta);
|
|
35
37
|
line-height: var(--qmh-leading-tight);
|
package/dist/preset/index.css
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
reading them. `recipes.css` ranks at ordinary specificity, `shell.css` and `app.css`
|
|
8
|
-
at zero. */
|
|
1
|
+
/*
|
|
2
|
+
* `@quillmark/svelte/preset`, the package's one CSS entry point: the endorsed look for the page a
|
|
3
|
+
* surface is mounted on. Opt-in: the surface barrels pull `core/theme.css` and never this, because
|
|
4
|
+
* this styles the document. `scale.css` is where the literals are minted; the rest read them.
|
|
5
|
+
* `recipes.css` ranks at ordinary specificity, `shell.css` and `app.css` at zero.
|
|
6
|
+
*/
|
|
9
7
|
|
|
10
8
|
@import './scale.css';
|
|
11
9
|
@import './recipes.css';
|
package/dist/preset/recipes.css
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
and unlayered a consumer's rules win over these by ordinary precedence. Ordinary
|
|
14
|
-
specificity too, not the surfaces' `:where()` zero: a consumer takes these classes on
|
|
15
|
-
purpose, so a rule of their own beside one is aimed rather than incidental. */
|
|
1
|
+
/*
|
|
2
|
+
* The endorsed look: the rules a host draws around a mounted surface, as classes it puts on its
|
|
3
|
+
* own elements. The shell is `shell.css` and an app's chrome `app.css`; the rungs in `scale.css`
|
|
4
|
+
* are the invisible half. This file mints nothing; `check:style` runs its axes here.
|
|
5
|
+
*
|
|
6
|
+
* These are host classes: they land on the consumer's DOM, never on a mounted root, so they freeze
|
|
7
|
+
* nothing about the surfaces' interior.
|
|
8
|
+
*
|
|
9
|
+
* Unlayered, at ordinary specificity (not the surfaces' `:where()` zero): a consumer takes these
|
|
10
|
+
* classes on purpose, so a rule of their own beside one is aimed rather than incidental, and a
|
|
11
|
+
* layer of the preset's own would put a name in the contract.
|
|
12
|
+
*/
|
|
16
13
|
|
|
17
14
|
/* ── The page ──────────────────────────────────────────────────────────────── */
|
|
18
15
|
|
package/dist/preset/scale.css
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
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, the
|
|
5
|
-
ring's width, the status hue at both poles) and the
|
|
4
|
+
figures here are the figures there (the poles, the ground step's ramp, 8% hover, the
|
|
5
|
+
label ink's 35%, the 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
7
|
tone at both poles and a host page meets an editor's own column exactly. The border
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
is calibrated for its own plane rather than shared: a page's controls are boxed and
|
|
9
|
+
read against one ground, a card's strokes at several weights against the rungs
|
|
10
|
+
under it, so the mixes are not the same figure. The well
|
|
11
11
|
under the ground has no counterpart here: a host's own controls are boxed and carry a
|
|
12
12
|
hairline, so nothing on a page needs the step an edgeless field does.
|
|
13
13
|
|
|
@@ -20,9 +20,8 @@
|
|
|
20
20
|
Literals live here and nowhere else in the preset: `check:style` runs its axes over
|
|
21
21
|
the sheets beside it, with this file exempt as the place the defaults are minted.
|
|
22
22
|
|
|
23
|
-
The poles fall back to the
|
|
24
|
-
|
|
25
|
-
painted a page with, which is what a plate should be. The dials sit in front, so
|
|
23
|
+
The poles fall back to the surfaces' own pair, so a host that turns no dial lands on
|
|
24
|
+
the plane an editor mounted in it paints at both poles. The dials sit in front, so
|
|
26
25
|
`--qm-bg`/`--qm-fg` on an ancestor retunes page and surface together. */
|
|
27
26
|
|
|
28
27
|
:root {
|
|
@@ -32,21 +31,20 @@
|
|
|
32
31
|
the page sinks from it by the same ramp. That is what puts a host page and
|
|
33
32
|
an editor's own column on one tone rather than a plane apart. Planes step by
|
|
34
33
|
light and everything else by ink, which is the package's own split
|
|
35
|
-
(ARCHITECTURE §Styling); the figures are its figures.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
--qmh-
|
|
39
|
-
--qmh-page: oklab(from var(--qmh-surface) calc(l - (0.0214 + 0.0467 * (1 - l))) a b);
|
|
40
|
-
--qmh-ink: var(--qm-fg, CanvasText);
|
|
34
|
+
(ARCHITECTURE §Styling); the figures are its figures. */
|
|
35
|
+
--qmh-surface: var(--qm-bg, light-dark(#fff, #272729));
|
|
36
|
+
--qmh-page: oklab(from var(--qmh-surface) calc(l - (0.04 + 0.0202 * (1 - l))) a b);
|
|
37
|
+
--qmh-ink: var(--qm-fg, light-dark(#1c1c1c, #d7dadc));
|
|
41
38
|
/* One step off the ink and no second: a page's chrome type is small, tracked and
|
|
42
39
|
uppercase, so a rung below this reads under 4.5:1 against the plate and the
|
|
43
40
|
label and status lines that would take it are text (WCAG 1.4.3). */
|
|
44
|
-
--qmh-ink-meta: color-mix(in oklab, var(--qmh-ink), var(--qmh-surface)
|
|
41
|
+
--qmh-ink-meta: color-mix(in oklab, var(--qmh-ink), var(--qmh-surface) 35%);
|
|
45
42
|
--qmh-surface-hover: color-mix(in oklab, var(--qmh-surface), var(--qmh-ink) 8%);
|
|
46
43
|
--qmh-border: color-mix(in oklab, var(--qmh-surface), var(--qmh-ink) 17%);
|
|
47
44
|
/* The edge that says "engaged" where the hairline says "edge": a pressed switch,
|
|
48
|
-
the underline under a link.
|
|
49
|
-
|
|
45
|
+
the underline under a link. Half way to the ink, which clears WCAG 1.4.11's 3:1
|
|
46
|
+
against the page and the plate at both poles. */
|
|
47
|
+
--qmh-border-strong: color-mix(in oklab, var(--qmh-surface), var(--qmh-ink) 50%);
|
|
50
48
|
|
|
51
49
|
/* The one hue a host's page spends on purpose: the primary action wears it. The
|
|
52
50
|
figures are the surfaces' own default for the same dial, so a page that turns no
|
|
@@ -62,6 +60,9 @@
|
|
|
62
60
|
on a dark page. The dial sits in front of the pair, as every pole's does, so
|
|
63
61
|
a host that turns one owns both schemes. */
|
|
64
62
|
--qmh-alert: var(--qm-danger, light-dark(#9e1518, #f9786c));
|
|
63
|
+
/* What a modal's backdrop lays over the page: the ground held translucent, so the
|
|
64
|
+
page recedes under the dialog without a second tone to read. */
|
|
65
|
+
--qmh-scrim: color-mix(in srgb, var(--qmh-page) 80%, transparent);
|
|
65
66
|
|
|
66
67
|
/* ── Rhythm ──────────────────────────────────────────────────────────────── */
|
|
67
68
|
/* One base and its multiples, the same four steps the surfaces derive. A host
|
package/dist/preset/shell.css
CHANGED
|
@@ -145,3 +145,13 @@
|
|
|
145
145
|
border-radius: var(--qmh-radius);
|
|
146
146
|
overflow: hidden;
|
|
147
147
|
}
|
|
148
|
+
|
|
149
|
+
/* One track is the whole width under the threshold, so a frame's sides and corners would
|
|
150
|
+
frame the screen rather than a pane. It keeps the rules above and below it, which part
|
|
151
|
+
it from the bands. After the frame, since both rank zero and the later one wins. */
|
|
152
|
+
@media (width < 50rem), (height < 30rem) {
|
|
153
|
+
:where(.qm-frame) {
|
|
154
|
+
border-inline: none;
|
|
155
|
+
border-radius: 0;
|
|
156
|
+
}
|
|
157
|
+
}
|
package/dist/preview/bridge.d.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
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
|
-
/**
|
|
5
|
-
*
|
|
4
|
+
/**
|
|
5
|
+
* Both trips are held while the pane has no box to measure against; the last one held runs when it
|
|
6
|
+
* has one again.
|
|
7
|
+
*/
|
|
6
8
|
export interface BridgeController {
|
|
7
|
-
/**
|
|
8
|
-
*
|
|
9
|
+
/**
|
|
10
|
+
* Scroll `field`'s first box to centre; `false` when this compile places none for that address.
|
|
11
|
+
*/
|
|
9
12
|
scrollToField(field: string): boolean;
|
|
10
13
|
/**
|
|
11
|
-
* Bring the caret rect at `field`/`pos` into view
|
|
12
|
-
*
|
|
14
|
+
* Bring the caret rect at `field`/`pos` into view; a no-op off-content or when already clear of
|
|
15
|
+
* the fold.
|
|
13
16
|
*/
|
|
14
17
|
focusPosition(field: string, pos: number): void;
|
|
15
18
|
destroy(): void;
|
|
@@ -21,24 +24,17 @@ export interface VerticalSpan {
|
|
|
21
24
|
height: number;
|
|
22
25
|
}
|
|
23
26
|
/**
|
|
24
|
-
* Whether `target` sits clear of `port`'s edges by the fold's clearance, which is the
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
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.
|
|
27
|
+
* Whether `target` sits clear of `port`'s edges by the fold's clearance, which is the target's own
|
|
28
|
+
* height, so it scales with zoom as the caret does. The floor is {@link MIN_CLEARANCE}, for a
|
|
29
|
+
* zero-height rect. The cap is a third of the room the port has to spare, so a target too tall for
|
|
30
|
+
* its own clearance passes once centred rather than being re-centred by every keystroke. A target
|
|
31
|
+
* taller than the port is clear while it covers the port.
|
|
36
32
|
*/
|
|
37
33
|
export declare function clearOfTheFold(target: VerticalSpan, port: VerticalSpan): boolean;
|
|
38
|
-
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
34
|
+
/**
|
|
35
|
+
* The last trip a pane with no box could not run. A cell so a rebuilt bridge inherits it
|
|
36
|
+
* (`controller.ts`).
|
|
37
|
+
*/
|
|
42
38
|
export interface HeldTrip {
|
|
43
39
|
run?: () => void;
|
|
44
40
|
}
|
package/dist/preview/bridge.js
CHANGED
|
@@ -1,32 +1,24 @@
|
|
|
1
1
|
import { boxesForField, rectToPercent, clickToPdfPt, pxToPt, applyPercentRect } from './geometry.js';
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
|
|
2
|
+
/**
|
|
3
|
+
* A pane hidden by the shell's narrow switch (`preset/shell.css`) measures 0×0, as does every box
|
|
4
|
+
* on it, which the clearance arithmetic would read as a caret clear of both edges.
|
|
5
|
+
*/
|
|
6
6
|
function hasBox(port) {
|
|
7
7
|
return port.width > 0 && port.height > 0;
|
|
8
8
|
}
|
|
9
|
-
/** What a rect reporting no height clears an edge by, in CSS px
|
|
10
|
-
* smallest zoom a page still reads at. */
|
|
9
|
+
/** What a rect reporting no height clears an edge by, in CSS px. */
|
|
11
10
|
const MIN_CLEARANCE = 8;
|
|
12
|
-
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
|
|
11
|
+
/**
|
|
12
|
+
* What a click may miss a field's ink by and still land on it, in CSS px of the page as drawn
|
|
13
|
+
* (PREVIEW.md §"Click bridge").
|
|
14
|
+
*/
|
|
16
15
|
const CLICK_SLACK_PX = 2;
|
|
17
16
|
/**
|
|
18
|
-
* Whether `target` sits clear of `port`'s edges by the fold's clearance, which is the
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* The floor is what a zero-height rect gets, which bare intersection would score as
|
|
25
|
-
* clear at either edge. The cap is a third of the room the port has to spare, so a
|
|
26
|
-
* target too tall for its own height of clearance — a located line at zoom, a short
|
|
27
|
-
* split track — passes once centred and holds a band around it, rather than being
|
|
28
|
-
* re-centred by every keystroke. A target taller than the port is clear while it covers
|
|
29
|
-
* the port: no scroll shows more of it.
|
|
17
|
+
* Whether `target` sits clear of `port`'s edges by the fold's clearance, which is the target's own
|
|
18
|
+
* height, so it scales with zoom as the caret does. The floor is {@link MIN_CLEARANCE}, for a
|
|
19
|
+
* zero-height rect. The cap is a third of the room the port has to spare, so a target too tall for
|
|
20
|
+
* its own clearance passes once centred rather than being re-centred by every keystroke. A target
|
|
21
|
+
* taller than the port is clear while it covers the port.
|
|
30
22
|
*/
|
|
31
23
|
export function clearOfTheFold(target, port) {
|
|
32
24
|
if (target.height >= port.height)
|
|
@@ -36,19 +28,13 @@ export function clearOfTheFold(target, port) {
|
|
|
36
28
|
}
|
|
37
29
|
export function createBridge(session, container, slots, onPick, held = {}) {
|
|
38
30
|
const unlisten = [];
|
|
39
|
-
// Two rungs: `positionAt` over span-tracked content, `fieldAt` over every
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
// being tracked, and the pick carries no `pos` there: a fabricated `0` would be an
|
|
43
|
-
// invented offset wearing a real one's type. No third rung hit-tests `regions()`,
|
|
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.
|
|
31
|
+
// Two rungs: `positionAt` over span-tracked content, then `fieldAt` over every tracked
|
|
32
|
+
// placement (PREVIEW.md §"Click bridge"). The second carries no `pos`: a fabricated `0` would
|
|
33
|
+
// be an invented offset.
|
|
46
34
|
if (onPick) {
|
|
47
35
|
for (const { page, el } of slots) {
|
|
48
36
|
const handleClick = (ev) => {
|
|
49
|
-
// Re-read through the live array: a
|
|
50
|
-
// slot objects to re-cache `size`, and the click math must use the
|
|
51
|
-
// current compile's PageSize, not the one captured at bridge build.
|
|
37
|
+
// Re-read through the live array: a refresh swaps slot objects to re-cache `size`.
|
|
52
38
|
const slot = slots[page];
|
|
53
39
|
if (!slot)
|
|
54
40
|
return;
|
|
@@ -68,9 +54,8 @@ export function createBridge(session, container, slots, onPick, held = {}) {
|
|
|
68
54
|
unlisten.push(() => el.removeEventListener('click', handleClick));
|
|
69
55
|
}
|
|
70
56
|
}
|
|
71
|
-
// Where the % rect `pct` on `slot` currently sits
|
|
72
|
-
//
|
|
73
|
-
// so it displaces nothing and no layout survives the read.
|
|
57
|
+
// Where the % rect `pct` on `slot` currently sits: a throwaway absolute marker, measured and
|
|
58
|
+
// removed.
|
|
74
59
|
function measure(slot, pct) {
|
|
75
60
|
const marker = document.createElement('div');
|
|
76
61
|
applyPercentRect(marker, pct);
|
|
@@ -79,11 +64,7 @@ export function createBridge(session, container, slots, onPick, held = {}) {
|
|
|
79
64
|
marker.remove();
|
|
80
65
|
return rect;
|
|
81
66
|
}
|
|
82
|
-
// Centre `target` vertically in `port
|
|
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.
|
|
67
|
+
// Centre `target` vertically in `port`; the cross axis takes the minimum trip.
|
|
87
68
|
function centre(target, port) {
|
|
88
69
|
container.scrollTop += target.top + target.height / 2 - (port.top + port.height / 2);
|
|
89
70
|
reveal(target, port);
|
|
@@ -94,10 +75,8 @@ export function createBridge(session, container, slots, onPick, held = {}) {
|
|
|
94
75
|
else if (target.right > port.right)
|
|
95
76
|
container.scrollLeft += target.right - port.right;
|
|
96
77
|
}
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
// would open where the caret was several edits ago. Guarded for jsdom, which ships no
|
|
100
|
-
// ResizeObserver and lays out nothing for one to report.
|
|
78
|
+
// Runs the held trip once the pane has a box again. Guarded for jsdom, which has no
|
|
79
|
+
// ResizeObserver.
|
|
101
80
|
if (typeof ResizeObserver !== 'undefined') {
|
|
102
81
|
const observer = new ResizeObserver(() => {
|
|
103
82
|
if (!held.run || !hasBox(container.getBoundingClientRect()))
|
|
@@ -117,14 +96,12 @@ export function createBridge(session, container, slots, onPick, held = {}) {
|
|
|
117
96
|
if (!slot)
|
|
118
97
|
return false;
|
|
119
98
|
const port = container.getBoundingClientRect();
|
|
120
|
-
//
|
|
121
|
-
// with no box yet is not a second no.
|
|
99
|
+
// Placed, so `true`; a pane with no box yet is not a no.
|
|
122
100
|
if (!hasBox(port)) {
|
|
123
101
|
held.run = () => void scrollToField(field);
|
|
124
102
|
return true;
|
|
125
103
|
}
|
|
126
|
-
// A discrete act
|
|
127
|
-
// the continuous hop below.
|
|
104
|
+
// A discrete act, so it centres every time.
|
|
128
105
|
centre(measure(slot, rectToPercent(box.rect, slot.size)), port);
|
|
129
106
|
return true;
|
|
130
107
|
}
|
|
@@ -135,21 +112,15 @@ export function createBridge(session, container, slots, onPick, held = {}) {
|
|
|
135
112
|
const slot = slots[region.page];
|
|
136
113
|
if (!slot)
|
|
137
114
|
return;
|
|
138
|
-
// Held before the
|
|
139
|
-
//
|
|
140
|
-
// a track is hidden. What is held is the request, not the trip, so it re-locates
|
|
141
|
-
// against whatever compile is current when the pane comes back.
|
|
115
|
+
// Held before the fold test, which would call a zero-sized pane clear. The request is held,
|
|
116
|
+
// so it re-locates against the compile current on return.
|
|
142
117
|
const port = container.getBoundingClientRect();
|
|
143
118
|
if (!hasBox(port)) {
|
|
144
119
|
held.run = () => focusPosition(field, pos);
|
|
145
120
|
return;
|
|
146
121
|
}
|
|
147
|
-
//
|
|
148
|
-
// the
|
|
149
|
-
// takes the scrollport back from the user on every one of them, including
|
|
150
|
-
// the ones that changed nothing about where the caret already was.
|
|
151
|
-
// Across, a page zoomed wider than the pane has a caret to keep in view too, and
|
|
152
|
-
// the least trip does it without moving the line the reader is on.
|
|
122
|
+
// Called per keystroke, so it moves the pane only when the caret has left the fold; across,
|
|
123
|
+
// the least trip.
|
|
153
124
|
const target = measure(slot, rectToPercent(region.rect, slot.size));
|
|
154
125
|
if (clearOfTheFold(target, port))
|
|
155
126
|
reveal(target, port);
|