@glissade/backend-dom 0.22.0-pre.3 → 0.22.0-pre.5

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 CHANGED
@@ -108,11 +108,17 @@ base bundle is absent or a different version (never a cryptic `undefined`).
108
108
 
109
109
  ### Documented divergences (preview/non-parity)
110
110
 
111
- - **Text line-breaking** is the browser's layout engine, **not** the canvas/Skia
112
- rasterizer — so line breaks can differ from `gs render`. Intended.
113
111
  - **Text line-breaking** uses the browser's layout engine, so it can differ from
114
- the canvas/Skia rasterizer; the measuring span is mounted in the live document
115
- so wrapping reflects the real font (a detached host would measure 0).
112
+ the canvas/Skia rasterizer (intended). The measuring span is mounted in the live
113
+ document so wrapping reflects the real font (a detached host would measure 0).
114
+ **Web fonts load async**, so text first measured before its font loads wraps on
115
+ the fallback estimate. Pass **`onReflow`** and re-render in it — the backend
116
+ fires it when `document.fonts` becomes ready (and on later `@font-face` batches),
117
+ so the host re-evaluates and text re-wraps with the loaded font:
118
+
119
+ ```js
120
+ const backend = new DomBackend(stage, { onReflow: () => frame(currentTime) });
121
+ ```
116
122
  - **`measureText`** measures `width` via a hidden DOM element (matching what this
117
123
  backend draws). `ascent`/`descent` are **estimates** (`0.8`/`0.2 × fontSize`),
118
124
  not real font metrics — fine for layout composition, not for exact vertical
package/dist/index.d.ts CHANGED
@@ -2,7 +2,18 @@ import { BackendCaps, DisplayList, FontSpec, RenderBackend, TextMetricsLite, Vid
2
2
  import { NodeIdStream } from "@glissade/scene/identity";
3
3
 
4
4
  //#region src/index.d.ts
5
-
5
+ /** Construction options for {@link DomBackend}. */
6
+ interface DomBackendOptions {
7
+ /**
8
+ * Called when web fonts finish loading (and on later lazy `@font-face`
9
+ * batches). **Re-render in this callback** so text re-wraps with the loaded
10
+ * fonts — wrapping is computed upstream in the scene from this backend's
11
+ * `measureText`, so a caption measured before its font loaded can render
12
+ * unwrapped at first paint. Typically `() => drive(currentTime)` in a host's
13
+ * draw loop. No-op where `document.fonts` is absent (e.g. jsdom).
14
+ */
15
+ onReflow?: () => void;
16
+ }
6
17
  /**
7
18
  * A DOM/SVG `RenderBackend`. Construct with a host element (renders into it) or a
8
19
  * bare `Document` (builds a detached `root` you read off `backend.root`). Each
@@ -13,7 +24,7 @@ import { NodeIdStream } from "@glissade/scene/identity";
13
24
  declare class DomBackend implements RenderBackend {
14
25
  #private;
15
26
  readonly root: HTMLElement;
16
- constructor(target: HTMLElement | Document);
27
+ constructor(target: HTMLElement | Document, opts?: DomBackendOptions);
17
28
  readonly caps: BackendCaps;
18
29
  /** Supply the out-of-band id stream (S1 `emitWithIds().ids`) the next
19
30
  * `render()` stamps as `data-node-id`. Positional by command index. */
@@ -26,4 +37,4 @@ declare class DomBackend implements RenderBackend {
26
37
  dispose(): void;
27
38
  }
28
39
  //#endregion
29
- export { DomBackend };
40
+ export { DomBackend, DomBackendOptions };
package/dist/index.js CHANGED
@@ -157,13 +157,14 @@ var DomBackend = class {
157
157
  #recon = /* @__PURE__ */ new WeakMap();
158
158
  #owned = /* @__PURE__ */ new WeakMap();
159
159
  #ids = [];
160
+ #onReflow;
160
161
  #measureSpan = null;
161
162
  #warnedMeasure = false;
162
163
  #warnedMesh = false;
163
164
  #warnedGradientInterp = false;
164
165
  #warnedShader = false;
165
166
  #warnedUnbalanced = false;
166
- constructor(target) {
167
+ constructor(target, opts = {}) {
167
168
  const isDoc = target.nodeType === 9;
168
169
  this.#doc = isDoc ? target : target.ownerDocument ?? target;
169
170
  this.#host = isDoc ? null : target;
@@ -172,6 +173,26 @@ var DomBackend = class {
172
173
  this.root.style.position = "relative";
173
174
  this.root.style.overflow = "hidden";
174
175
  if (this.#host) this.#host.appendChild(this.root);
176
+ this.#onReflow = opts.onReflow;
177
+ this.#wireFontReflow();
178
+ }
179
+ /**
180
+ * When web fonts finish loading, fire `onReflow` so the HOST re-renders. Text
181
+ * wrapping is computed UPSTREAM in the scene (from this backend's `measureText`),
182
+ * so a caption measured before its font loaded wraps on the fallback-font
183
+ * estimate and can render unwrapped at first paint. The backend can't re-wrap
184
+ * alone — the line breaks already live in the DisplayList the scene produced —
185
+ * so per the passive-sink contract it SIGNALS, and the host re-evaluates with
186
+ * the now-loaded fonts. No-op when no `onReflow` is given or the environment
187
+ * has no `document.fonts` (e.g. jsdom).
188
+ */
189
+ #wireFontReflow() {
190
+ const reflow = this.#onReflow;
191
+ if (!reflow) return;
192
+ const fonts = this.#doc.fonts;
193
+ if (!fonts) return;
194
+ fonts.ready?.then?.(() => reflow())?.catch?.(() => {});
195
+ fonts.addEventListener?.("loadingdone", () => reflow());
175
196
  }
176
197
  caps = {
177
198
  filters: ALL_FILTER_KINDS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/backend-dom",
3
- "version": "0.22.0-pre.3",
3
+ "version": "0.22.0-pre.5",
4
4
  "description": "glissade DOM render backend: DisplayList -> HTML/SVG elements. A preview / non-parity realtime tier (accessibility, selectable text, CSS-native embedding) — NOT a Skia-export twin.",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
@@ -18,8 +18,8 @@
18
18
  "dist"
19
19
  ],
20
20
  "dependencies": {
21
- "@glissade/core": "0.22.0-pre.3",
22
- "@glissade/scene": "0.22.0-pre.3"
21
+ "@glissade/core": "0.22.0-pre.5",
22
+ "@glissade/scene": "0.22.0-pre.5"
23
23
  },
24
24
  "repository": {
25
25
  "type": "git",