@quillmark/svelte 0.26.2 → 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.
Files changed (97) hide show
  1. package/NOTICE +4 -4
  2. package/README.md +26 -8
  3. package/THEMING.md +40 -4
  4. package/dist/core/address.d.ts +35 -64
  5. package/dist/core/address.js +35 -55
  6. package/dist/core/bloom.d.ts +4 -10
  7. package/dist/core/bloom.js +14 -27
  8. package/dist/core/codec/blocks.d.ts +41 -43
  9. package/dist/core/codec/blocks.js +97 -130
  10. package/dist/core/codec/code.d.ts +7 -10
  11. package/dist/core/codec/code.js +37 -46
  12. package/dist/core/codec/decode.d.ts +19 -23
  13. package/dist/core/codec/decode.js +76 -111
  14. package/dist/core/codec/encode.d.ts +11 -15
  15. package/dist/core/codec/encode.js +60 -67
  16. package/dist/core/codec/field.d.ts +62 -95
  17. package/dist/core/codec/field.js +107 -179
  18. package/dist/core/codec/islands.d.ts +33 -41
  19. package/dist/core/codec/islands.js +76 -86
  20. package/dist/core/codec/lists.d.ts +17 -26
  21. package/dist/core/codec/lists.js +117 -157
  22. package/dist/core/codec/marks.d.ts +4 -3
  23. package/dist/core/codec/marks.js +8 -3
  24. package/dist/core/codec/prose.css +84 -46
  25. package/dist/core/codec/schema.d.ts +18 -23
  26. package/dist/core/codec/schema.js +84 -76
  27. package/dist/core/codec/slash.d.ts +42 -48
  28. package/dist/core/codec/slash.js +0 -0
  29. package/dist/core/codec/table-copy.d.ts +4 -3
  30. package/dist/core/codec/table-copy.js +25 -7
  31. package/dist/core/codec/table-view.d.ts +12 -12
  32. package/dist/core/codec/table-view.js +460 -383
  33. package/dist/core/codec/table.d.ts +94 -61
  34. package/dist/core/codec/table.js +331 -83
  35. package/dist/core/codec/tasks.d.ts +6 -0
  36. package/dist/core/codec/tasks.js +30 -3
  37. package/dist/core/errors.d.ts +15 -30
  38. package/dist/core/errors.js +2 -6
  39. package/dist/core/index.js +7 -12
  40. package/dist/core/lifecycle.d.ts +7 -16
  41. package/dist/core/lifecycle.js +10 -21
  42. package/dist/core/rebind.svelte.d.ts +10 -20
  43. package/dist/core/rebind.svelte.js +15 -27
  44. package/dist/core/rungs.d.ts +14 -11
  45. package/dist/core/rungs.js +17 -19
  46. package/dist/core/teardown.d.ts +4 -6
  47. package/dist/core/teardown.js +7 -10
  48. package/dist/core/theme.css +74 -54
  49. package/dist/index.js +3 -8
  50. package/dist/preset/app.css +71 -0
  51. package/dist/preset/index.css +8 -7
  52. package/dist/preset/recipes.css +19 -164
  53. package/dist/preset/scale.css +28 -18
  54. package/dist/preset/shell.css +157 -0
  55. package/dist/preview/Preview.svelte +8 -0
  56. package/dist/preview/bridge.d.ts +18 -22
  57. package/dist/preview/bridge.js +29 -58
  58. package/dist/preview/controller.d.ts +21 -29
  59. package/dist/preview/controller.js +14 -39
  60. package/dist/preview/geometry.d.ts +13 -34
  61. package/dist/preview/geometry.js +13 -34
  62. package/dist/preview/index.js +4 -16
  63. package/dist/preview/paint.d.ts +3 -3
  64. package/dist/preview/paint.js +28 -68
  65. package/dist/preview/strings.d.ts +1 -1
  66. package/dist/preview/strings.js +2 -14
  67. package/dist/preview/viewport.js +20 -15
  68. package/dist/preview/zoom.d.ts +21 -22
  69. package/dist/preview/zoom.js +53 -30
  70. package/dist/visual/ArrayField.svelte +77 -16
  71. package/dist/visual/BooleanField.svelte +28 -15
  72. package/dist/visual/Card.svelte +66 -51
  73. package/dist/visual/CardControls.svelte +5 -11
  74. package/dist/visual/DateField.svelte +20 -4
  75. package/dist/visual/EnumField.svelte +3 -0
  76. package/dist/visual/FieldHint.svelte +2 -1
  77. package/dist/visual/FormatPopover.svelte +82 -15
  78. package/dist/visual/FormatPopover.svelte.d.ts +4 -0
  79. package/dist/visual/MatrixAdd.svelte +30 -12
  80. package/dist/visual/MatrixField.svelte +24 -16
  81. package/dist/visual/NumberField.svelte +7 -0
  82. package/dist/visual/ObjectField.svelte +4 -4
  83. package/dist/visual/ProseField.svelte +0 -5
  84. package/dist/visual/TipsCard.svelte +11 -10
  85. package/dist/visual/VariantField.svelte +3 -1
  86. package/dist/visual/VisualEditorInner.svelte +19 -3
  87. package/dist/visual/controls.css +7 -2
  88. package/dist/visual/fonts.d.ts +18 -0
  89. package/dist/visual/fonts.js +59 -0
  90. package/dist/visual/icons/nodes.d.ts +0 -5
  91. package/dist/visual/icons/nodes.js +0 -4
  92. package/dist/visual/props.d.ts +14 -1
  93. package/dist/visual/strings.d.ts +7 -0
  94. package/dist/visual/strings.js +5 -2
  95. package/dist/visual/structure.d.ts +43 -87
  96. package/dist/visual/structure.js +42 -83
  97. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  import type { DocPath } from './address.js';
2
2
  /**
3
- * What failed. Stable strings rather than an enum: a consumer switches on them,
4
- * and a new surface adds a code without moving the ones an app already handles.
3
+ * What failed. Stable strings rather than an enum: a consumer switches on them, and a new surface
4
+ * adds a code without moving the ones an app handles.
5
5
  */
6
6
  export type EditorErrorCode =
7
7
  /** A scalar/array/object write the boundary refused. Also pinned as a
@@ -27,37 +27,26 @@ export type EditorErrorCode =
27
27
  /** A tip's markdown did not render: the tip shows as literal text. */
28
28
  | 'tip-render-failed'
29
29
  /**
30
- * A prop a surface binds once was swapped in place, and the surface neither
31
- * re-keyed on it nor observed it. The mounted surface still names the previous
32
- * handle. `dev` throughout: the fix is a remount at the call site, so this is
33
- * aimed at whoever wired it and never at a running app's telemetry.
34
- *
35
- * The one code that is also thrown, in a dev build, once reported: a swapped
36
- * `onError` is itself a rebind, so the handler this arrives at may be the one the
37
- * consumer just replaced (`rebind.svelte.ts`).
30
+ * A prop a surface binds once was swapped in place, and the surface neither re-keyed on it nor
31
+ * observed it. `dev`: the fix is a remount at the call site. Also thrown, in a dev build, once
32
+ * reported; a swapped `onError` is itself a rebind, so the handler this arrives at may be the
33
+ * one just replaced (`rebind.svelte.ts`).
38
34
  */
39
35
  | 'rebind-ignored'
40
36
  /**
41
- * A landing named a card or a field the surface does not hold: a `cardId` from a
42
- * previous session or an already-removed card, or a `DocPath` naming no declared
43
- * field — an array element path included, where the array itself is undeclared or
44
- * unmounted. The call is a no-op and the document is untouched.
45
- *
46
- * `dev`, because the fix is at the call site: a host driving the instance verbs
47
- * from outside, or a preview pick the editor has no field to route to. Every
48
- * mounted field is a target, prose leaf and form control alike, and an array's
49
- * elements are targets through it, so this is not how a scalar or an element
50
- * reports. The preview never reports it at all: a field it places nothing for is
51
- * `scrollToField` returning `false`.
37
+ * A landing named a card or a field the surface does not hold: a stale `cardId`, or a `DocPath`
38
+ * naming no declared field (an array element path included where the array is undeclared or
39
+ * unmounted). The call is a no-op. `dev`: the fix is at the call site, a host driving the
40
+ * instance verbs or a preview pick the editor has no field to route to. The preview never
41
+ * reports it: a field it places nothing for is `scrollToField` returning `false`.
52
42
  */
53
43
  | 'target-unknown';
54
44
  /** A failure a surface recovered from. */
55
45
  export interface EditorError {
56
46
  code: EditorErrorCode;
57
47
  /**
58
- * `error` is a runtime failure an app routes to telemetry; `dev` is a contract
59
- * violation aimed at whoever is building against the package. Without the
60
- * split every consumer's sink filters the dev codes by hand.
48
+ * `error` is a runtime failure an app routes to telemetry; `dev` a contract violation aimed at
49
+ * whoever builds against the package.
61
50
  */
62
51
  severity: 'error' | 'dev';
63
52
  /** English, for a log. Not for display: nothing here has a place on screen. */
@@ -72,12 +61,8 @@ export interface EditorError {
72
61
  /** The `onError` hook every surface takes. */
73
62
  export type EditorErrorHandler = (err: EditorError) => void;
74
63
  /**
75
- * Report `err`, or log it when nothing is listening: a failure the package
76
- * recovered from is worth seeing in a console.
77
- *
78
- * A throwing handler is the consumer's bug and must not become the package's: it
79
- * is caught, because every call site sits on a recovery path where a second throw
80
- * would undo the recovery.
64
+ * Report `err`, or log it when nothing is listening. A throwing handler is caught: every call site
65
+ * sits on a recovery path where a second throw would undo the recovery.
81
66
  */
82
67
  export declare function reportError(handler: EditorErrorHandler | undefined, err: EditorError): void;
83
68
  /** A thrown value's message, for the `message` field. */
@@ -1,10 +1,6 @@
1
1
  /**
2
- * Report `err`, or log it when nothing is listening: a failure the package
3
- * recovered from is worth seeing in a console.
4
- *
5
- * A throwing handler is the consumer's bug and must not become the package's: it
6
- * is caught, because every call site sits on a recovery path where a second throw
7
- * would undo the recovery.
2
+ * Report `err`, or log it when nothing is listening. A throwing handler is caught: every call site
3
+ * sits on a recovery path where a second throw would undo the recovery.
8
4
  */
9
5
  export function reportError(handler, err) {
10
6
  if (!handler) {
@@ -1,17 +1,12 @@
1
1
  // `@quillmark/svelte/core`: what this package owns at the substrate seam.
2
2
  //
3
- // The `@quillmark/wasm` API is not re-exported: import it straight from the peer
4
- // dependency, the single source of truth (quiver states the same rule). The
5
- // handles cross this package's surfaces untouched (no wrapper types, per
6
- // DOCUMENT_MODEL §What the editor owns), so the boundary needs no second door
7
- // here. What earns a line is what more than one surface speaks and no other
8
- // package declares: the address vocabulary and the error channel, plus the WASM
9
- // init gate.
3
+ // The `@quillmark/wasm` API is not re-exported: import it from the peer dependency. What earns a
4
+ // line here is what more than one surface speaks and no other package declares: the address
5
+ // vocabulary, the error channel, and the WASM init gate.
10
6
  //
11
- // No stylesheet: a host reaches this entry where there is no document (a worker,
12
- // a server), and a dev server serves a side-effect sheet as a module that writes a
13
- // `<style>` into one. The surface barrels carry the theme; `check:deps` holds it.
7
+ // No stylesheet: a host reaches this entry where there is no document (a worker, a server), and a
8
+ // dev server serves a side-effect sheet as a module that writes a `<style>` into one. The surface
9
+ // barrels carry the theme; `check:deps` holds it.
14
10
  export { init } from './lifecycle.js';
15
- // The hop between that vocabulary and the `Addr` the document verbs take, both
16
- // directions: a host handed a path by a hook and holding a mutator needs it.
11
+ // The hop between that vocabulary and the `Addr` the document verbs take.
17
12
  export { fieldPathForAddr, addrForFieldPath } from './address.js';
@@ -1,23 +1,14 @@
1
1
  import { type CoreSurface } from '@quillmark/wasm';
2
2
  /**
3
- * Instantiate the core WASM build and resolve to its surface. Await once before
4
- * any other boundary verb, from any surface's setup path; the promise is memoized,
5
- * so concurrent callers share one instantiation and repeat calls are free. A failed
6
- * init clears the memo, leaving a retry possible.
7
- *
8
- * The surface is the artifact's own, handed back unchanged: `const { Quill, Document }
9
- * = await init()` is the whole of a host's access to the classes, and this package
10
- * re-declares none of it.
3
+ * Instantiate the core WASM build and resolve to its surface. Await once before any other boundary
4
+ * verb; the promise is memoized, so concurrent callers share one instantiation, and a failed init
5
+ * clears the memo so a retry is possible. The surface is the artifact's own, handed back unchanged.
11
6
  */
12
7
  export declare function init(): Promise<CoreSurface>;
13
8
  /**
14
- * The surface synchronously, for this package's pure verbs: the `DocPath` formatter
15
- * on the public API and the codec inside a ProseMirror transaction, neither of which
16
- * can await. Package-internal, and read at the call rather than destructured at
17
- * module scope, so no module's import graph carries an ordering constraint.
18
- *
19
- * Reaching one before {@link init} resolves throws, naming the fix. This is the seam
20
- * that turns an awaited surface back into sync verbs, so it is the one place the
21
- * precondition the gate made structural has to be stated again.
9
+ * The surface synchronously, for this package's pure verbs (the `DocPath` formatter and the codec
10
+ * inside a ProseMirror transaction), which cannot await. Package-internal, and read at the call
11
+ * rather than destructured at module scope. Reaching it before {@link init} resolves throws, naming
12
+ * the fix.
22
13
  */
23
14
  export declare function core(): CoreSurface;
@@ -1,21 +1,14 @@
1
- // `@quillmark/wasm` ships wasm-bindgen's web target: the core build is
2
- // instantiated by an explicit `await init()`, which resolves to the classes and
3
- // free functions — the artifact exports none of them statically, so awaiting is
4
- // the only way to hold one. Instantiation carries the panic hook on its start
5
- // section, so a Rust panic reaches JS as a stack trace.
1
+ // `@quillmark/wasm` ships wasm-bindgen's web target: the core build is instantiated by an explicit
2
+ // `await init()`, which resolves to the classes and free functions, none of which the artifact
3
+ // exports statically. Instantiation installs the panic hook on its start section.
6
4
  import { init as initWasm } from '@quillmark/wasm';
7
5
  let started;
8
6
  /** The resolved surface, for the sync verbs below that cannot await one. */
9
7
  let latched;
10
8
  /**
11
- * Instantiate the core WASM build and resolve to its surface. Await once before
12
- * any other boundary verb, from any surface's setup path; the promise is memoized,
13
- * so concurrent callers share one instantiation and repeat calls are free. A failed
14
- * init clears the memo, leaving a retry possible.
15
- *
16
- * The surface is the artifact's own, handed back unchanged: `const { Quill, Document }
17
- * = await init()` is the whole of a host's access to the classes, and this package
18
- * re-declares none of it.
9
+ * Instantiate the core WASM build and resolve to its surface. Await once before any other boundary
10
+ * verb; the promise is memoized, so concurrent callers share one instantiation, and a failed init
11
+ * clears the memo so a retry is possible. The surface is the artifact's own, handed back unchanged.
19
12
  */
20
13
  export function init() {
21
14
  if (started)
@@ -30,14 +23,10 @@ export function init() {
30
23
  return started;
31
24
  }
32
25
  /**
33
- * The surface synchronously, for this package's pure verbs: the `DocPath` formatter
34
- * on the public API and the codec inside a ProseMirror transaction, neither of which
35
- * can await. Package-internal, and read at the call rather than destructured at
36
- * module scope, so no module's import graph carries an ordering constraint.
37
- *
38
- * Reaching one before {@link init} resolves throws, naming the fix. This is the seam
39
- * that turns an awaited surface back into sync verbs, so it is the one place the
40
- * precondition the gate made structural has to be stated again.
26
+ * The surface synchronously, for this package's pure verbs (the `DocPath` formatter and the codec
27
+ * inside a ProseMirror transaction), which cannot await. Package-internal, and read at the call
28
+ * rather than destructured at module scope. Reaching it before {@link init} resolves throws, naming
29
+ * the fix.
41
30
  */
42
31
  export function core() {
43
32
  if (!latched) {
@@ -4,29 +4,19 @@ export type Bound = Record<string, unknown> & {
4
4
  onError?: EditorErrorHandler;
5
5
  };
6
6
  /**
7
- * Report the violation, then throw it in a dev build. Both surfaces raise it here; the
8
- * guards themselves stay separate, since the preview snapshots its props once and the
9
- * editor re-reads its pair on every re-key.
7
+ * Report the violation, then throw it in a dev build. Both surfaces raise it here; their guards
8
+ * stay separate, since the preview snapshots its props once and the editor re-reads its pair on
9
+ * every re-key.
10
10
  *
11
- * The report alone reaches whoever bound `onError` at mount, and a consumer swapping
12
- * that handler beside the handle it belongs to is the case this exists for: they are
13
- * told through the sink they just abandoned. A throw needs no handler to arrive.
14
- *
15
- * It costs the surface nothing: an effect that throws neither unmounts its tree nor
16
- * stops the ones beside it, so the surface goes on serving what it bound, which is the
17
- * very state being reported.
11
+ * The report alone reaches whoever bound `onError` at mount, so a consumer swapping that handler
12
+ * beside its handle is told through the sink they just abandoned; a throw needs no handler. The
13
+ * throw costs nothing: an effect that throws neither unmounts its tree nor stops its neighbours.
18
14
  */
19
15
  export declare function reportRebindIgnored(onError: EditorErrorHandler | undefined, message: string): void;
20
16
  /**
21
- * Report `rebind-ignored` once, at `dev`, when any prop `snapshot` names is swapped
22
- * in place, and in a dev build throw it after. `remount` closes the message with the
23
- * surface's own remount spelling (`Remount the preview ({#key session}) to rebind.`).
24
- *
25
- * Mounted identities are recorded on the effect's first run, the flush the surface
26
- * binds in; every later comparison reads them through the same `snapshot`.
27
- *
28
- * The report goes to the snapshot's `onError`: every other error these surfaces
29
- * report reaches the handler they bound, and a swapped `onError` is itself one of
30
- * the reports.
17
+ * Report `rebind-ignored` once, at `dev`, when any prop `snapshot` names is swapped in place, and
18
+ * in a dev build throw it after. `remount` closes the message with the surface's own remount
19
+ * spelling. Mounted identities are recorded on the effect's first run. The report goes to the
20
+ * snapshot's `onError`, which a swap is itself one of the reports for.
31
21
  */
32
22
  export declare function guardRebind(snapshot: () => Bound, remount: string): void;
@@ -1,25 +1,19 @@
1
- // The remount contract, enforced. `Preview` binds its props once, when its container
2
- // mounts, and observes nothing after: the vanilla core below it owns scroll position,
3
- // mounted slots and observer sets that a remount would discard on every apply. So a
4
- // prop swapped in place is reported, not dropped in silence, and thrown in a dev build.
1
+ // The remount contract, enforced. `Preview` binds its props once, at mount, and observes nothing
2
+ // after: the vanilla core below it owns scroll position, mounted slots and observer sets that a
3
+ // remount would discard. So a prop swapped in place is reported, and thrown in a dev build.
5
4
  //
6
- // A guard over one prop of a set is worse than none: a consumer who learns the
7
- // surface complains when it ignores a prop reads silence on the rest as reactivity.
8
- // The snapshot is every once-bound prop, and one report names whichever went stale.
5
+ // A guard over part of a set teaches a consumer to read silence on the rest as reactivity, so the
6
+ // snapshot is every once-bound prop and one report names whichever went stale.
9
7
  import { DEV } from 'esm-env';
10
8
  import { reportError } from './errors.js';
11
9
  /**
12
- * Report the violation, then throw it in a dev build. Both surfaces raise it here; the
13
- * guards themselves stay separate, since the preview snapshots its props once and the
14
- * editor re-reads its pair on every re-key.
10
+ * Report the violation, then throw it in a dev build. Both surfaces raise it here; their guards
11
+ * stay separate, since the preview snapshots its props once and the editor re-reads its pair on
12
+ * every re-key.
15
13
  *
16
- * The report alone reaches whoever bound `onError` at mount, and a consumer swapping
17
- * that handler beside the handle it belongs to is the case this exists for: they are
18
- * told through the sink they just abandoned. A throw needs no handler to arrive.
19
- *
20
- * It costs the surface nothing: an effect that throws neither unmounts its tree nor
21
- * stops the ones beside it, so the surface goes on serving what it bound, which is the
22
- * very state being reported.
14
+ * The report alone reaches whoever bound `onError` at mount, so a consumer swapping that handler
15
+ * beside its handle is told through the sink they just abandoned; a throw needs no handler. The
16
+ * throw costs nothing: an effect that throws neither unmounts its tree nor stops its neighbours.
23
17
  */
24
18
  export function reportRebindIgnored(onError, message) {
25
19
  reportError(onError, { code: 'rebind-ignored', severity: 'dev', message });
@@ -27,16 +21,10 @@ export function reportRebindIgnored(onError, message) {
27
21
  throw new Error(`[quillmark/ui] rebind-ignored: ${message}`);
28
22
  }
29
23
  /**
30
- * Report `rebind-ignored` once, at `dev`, when any prop `snapshot` names is swapped
31
- * in place, and in a dev build throw it after. `remount` closes the message with the
32
- * surface's own remount spelling (`Remount the preview ({#key session}) to rebind.`).
33
- *
34
- * Mounted identities are recorded on the effect's first run, the flush the surface
35
- * binds in; every later comparison reads them through the same `snapshot`.
36
- *
37
- * The report goes to the snapshot's `onError`: every other error these surfaces
38
- * report reaches the handler they bound, and a swapped `onError` is itself one of
39
- * the reports.
24
+ * Report `rebind-ignored` once, at `dev`, when any prop `snapshot` names is swapped in place, and
25
+ * in a dev build throw it after. `remount` closes the message with the surface's own remount
26
+ * spelling. Mounted identities are recorded on the effect's first run. The report goes to the
27
+ * snapshot's `onError`, which a swap is itself one of the reports for.
40
28
  */
41
29
  export function guardRebind(snapshot, remount) {
42
30
  let mounted;
@@ -1,14 +1,17 @@
1
- /** The element the scale is declared on, for `node`: its root, or `node` where it stands
2
- * outside one (a detached surface, a consumer's own mount). */
1
+ /**
2
+ * The style of the element the scale is declared on for `node`: its root, or `node` where it stands
3
+ * outside one.
4
+ */
3
5
  export declare function rungStyle(node: Element): CSSStyleDeclaration;
4
- /** A duration rung off `style`, in ms; `undefined` where the derivation is out of reach
5
- * (an unstyled root, or jsdom, where `getComputedStyle` reports custom properties as
6
- * empty). No rung, no motion: a fallback here would be the scale restated in the one
7
- * place `check:style` cannot read. */
6
+ /**
7
+ * A duration rung off `style`, in ms; `undefined` where the derivation is out of reach (an unstyled
8
+ * root, or jsdom). No rung, no motion: a fallback would restate the scale where `check:style`
9
+ * cannot read it.
10
+ */
8
11
  export declare function rungMs(style: CSSStyleDeclaration, rung: string): number | undefined;
9
- /** `steps` of the space rung off `node`'s root, in px: a floating surface's offset from
10
- * its anchor. `--_qm-space` is registered, so it computes to px where the rungs derived
11
- * from it compute to their expressions; hence the multiple rather than a read of
12
- * `--_qm-space-2`. `undefined` where the derivation is out of reach, as for
13
- * {@link rungMs}. */
12
+ /**
13
+ * `steps` of the space rung off `node`'s root, in px: a floating surface's offset from its anchor.
14
+ * `--_qm-space` is registered so it computes to px, unlike the rungs derived from it. `undefined`
15
+ * where the derivation is out of reach, as for {@link rungMs}.
16
+ */
14
17
  export declare function spacePx(node: Element | undefined, steps?: number): number | undefined;
@@ -1,20 +1,18 @@
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). */
1
+ // Where a script reads the scale. The derivation declares every `--_qm-*` rung on `[data-qm-root]`
2
+ // and nowhere else (`core/theme.css`), so the root is the element to ask. Resolving off the root
3
+ // also costs less: `getComputedStyle` compounds with nesting depth under jsdom.
4
+ /**
5
+ * The style of the element the scale is declared on for `node`: its root, or `node` where it stands
6
+ * outside one.
7
+ */
11
8
  export function rungStyle(node) {
12
9
  return getComputedStyle(node.closest('[data-qm-root]') ?? node);
13
10
  }
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. */
11
+ /**
12
+ * A duration rung off `style`, in ms; `undefined` where the derivation is out of reach (an unstyled
13
+ * root, or jsdom). No rung, no motion: a fallback would restate the scale where `check:style`
14
+ * cannot read it.
15
+ */
18
16
  export function rungMs(style, rung) {
19
17
  const raw = style.getPropertyValue(rung).trim();
20
18
  const n = Number.parseFloat(raw);
@@ -22,11 +20,11 @@ export function rungMs(style, rung) {
22
20
  return undefined;
23
21
  return raw.endsWith('ms') ? n : n * 1000;
24
22
  }
25
- /** `steps` of the space rung off `node`'s root, in px: a floating surface's offset from
26
- * its anchor. `--_qm-space` is registered, so it computes to px where the rungs derived
27
- * from it compute to their expressions; hence the multiple rather than a read of
28
- * `--_qm-space-2`. `undefined` where the derivation is out of reach, as for
29
- * {@link rungMs}. */
23
+ /**
24
+ * `steps` of the space rung off `node`'s root, in px: a floating surface's offset from its anchor.
25
+ * `--_qm-space` is registered so it computes to px, unlike the rungs derived from it. `undefined`
26
+ * where the derivation is out of reach, as for {@link rungMs}.
27
+ */
30
28
  export function spacePx(node, steps = 1) {
31
29
  if (!node)
32
30
  return undefined;
@@ -3,15 +3,13 @@ export interface Lifespan {
3
3
  /** False once the span has ended. */
4
4
  readonly alive: boolean;
5
5
  /**
6
- * Register work to undo at the end: a registry entry to drop, a timer or frame
7
- * to cancel, a handle to free. Cancellers run in registration order, which is
8
- * where the order lives. Registering on an ended span runs the canceller at
9
- * once: there is no later run to hold it for.
6
+ * Register work to undo at the end (a registry entry, a timer, a handle), run in registration
7
+ * order. On an ended span the canceller runs at once.
10
8
  */
11
9
  onEnd(cancel: () => void): void;
12
10
  /**
13
- * Await `pending`, then report whether the surface survived the window. `false`
14
- * is a continuation to drop: what it captured before the await is gone.
11
+ * Await `pending`, then report whether the surface survived the window; `false` is a
12
+ * continuation to drop.
15
13
  */
16
14
  resumes(pending: Promise<unknown>): Promise<boolean>;
17
15
  /** Run the cancellers once and end the span. Idempotent. */
@@ -1,12 +1,10 @@
1
- // The teardown order every surface holds: unregister, cancel, then free.
1
+ // The teardown order every surface holds: unregister, cancel, then free. An order rather than a
2
+ // list, because work is deferred: freeing first leaves a scheduled continuation holding what nobody
3
+ // owns, and cancelling while still registered still takes the next event.
2
4
  //
3
- // An order rather than a list, because work is deferred: a surface that frees
4
- // first leaves a scheduled continuation holding what nobody owns, and one that
5
- // cancels first but stays registered still takes the next event.
6
- //
7
- // `Lifespan` carries it. A surface builds one, registers its cancellers in the
8
- // order they must run, and ends the span once; continuations that cross an await
9
- // ask `resumes()` rather than a flag they captured before it.
5
+ // A surface builds a `Lifespan`, registers its cancellers in the order they must run, and ends the
6
+ // span once; continuations that cross an await ask `resumes()` rather than a flag captured before
7
+ // it.
10
8
  export function createLifespan() {
11
9
  let alive = true;
12
10
  const cancels = [];
@@ -26,8 +24,7 @@ export function createLifespan() {
26
24
  end() {
27
25
  if (!alive)
28
26
  return;
29
- // Dead before the cancellers run: one of them may synchronously reach
30
- // something that asks, and a span mid-teardown is not alive.
27
+ // Dead before the cancellers run: one may synchronously ask.
31
28
  alive = false;
32
29
  for (const cancel of cancels)
33
30
  cancel();