@glissade/scene 0.58.1 → 0.59.0-pre.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/dist/describe.d.ts +27 -0
- package/dist/describe.js +44 -7
- package/dist/diagnostics.d.ts +110 -3
- package/dist/diagnostics.js +215 -3
- package/dist/identity.d.ts +1 -1
- package/dist/identity.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/layoutCtors.d.ts +1 -1
- package/dist/layoutCtors.js +1 -1
- package/dist/motion.js +1 -1
- package/dist/nodes.d.ts +16 -1
- package/dist/nodes.js +21 -2
- package/dist/orient.js +1 -1
- package/dist/scene.d.ts +14 -3
- package/dist/scene.js +6 -3
- package/dist/tokens.js +1 -1
- package/dist/type.d.ts +15 -2
- package/dist/type.js +4 -4
- package/dist/typewriter.js +1 -1
- package/package.json +2 -2
package/dist/describe.d.ts
CHANGED
|
@@ -46,6 +46,14 @@ interface DescribedProp {
|
|
|
46
46
|
arity?: number;
|
|
47
47
|
/** Construction-only props: `true` when the constructor REQUIRES it (e.g. Image/Video `assetId`). */
|
|
48
48
|
required?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* 0.59 F "manifest conventions": the physical UNIT of the value, when one
|
|
51
|
+
* applies — e.g. `'degrees'` for rotation, `'seconds'` for a Video time offset.
|
|
52
|
+
* ADDITIVE + curated (a small per-prop table, like `positionAnchor`), present
|
|
53
|
+
* ONLY where a unit is meaningful; absent for unitless/px props. Lets a
|
|
54
|
+
* consumer stop guessing whether `rotation` is degrees or radians.
|
|
55
|
+
*/
|
|
56
|
+
unit?: string;
|
|
49
57
|
}
|
|
50
58
|
interface DescribedNode {
|
|
51
59
|
props: {
|
|
@@ -65,6 +73,17 @@ interface DescribedNode {
|
|
|
65
73
|
* shape-vs-Text anchor mismatch by pixel-measuring.
|
|
66
74
|
*/
|
|
67
75
|
positionAnchor: string;
|
|
76
|
+
/**
|
|
77
|
+
* 0.59 F ride-along "type-level bindable discovery aid": the prop names on this
|
|
78
|
+
* node a Track CAN drive (i.e. the animatable ones) — a flat, at-a-glance list
|
|
79
|
+
* so a consumer sees the bindable surface without filtering `props`. GENERATED
|
|
80
|
+
* from the same `listTargets()` the animatable props are, so it can't drift.
|
|
81
|
+
* This is the TYPE-level "can be animated" aid; the INSTANCE-level "is CURRENTLY
|
|
82
|
+
* bound on THIS node" truth (the anti-false-conclusion guard) is
|
|
83
|
+
* `instanceProps(node).bound` on `@glissade/scene/diagnostics`. Optional so a
|
|
84
|
+
* manifest captured before 0.59 (no `bindable`) still type-checks.
|
|
85
|
+
*/
|
|
86
|
+
bindable?: string[];
|
|
68
87
|
/**
|
|
69
88
|
* The tree-shakeable subpath this node is imported from, when not the base
|
|
70
89
|
* `@glissade/scene` index (e.g. the Layout family lives on
|
|
@@ -113,6 +132,14 @@ interface DescribedHelper {
|
|
|
113
132
|
usage: string;
|
|
114
133
|
/** Runnable example snippets — see {@link DescribedNode.examples}. */
|
|
115
134
|
examples?: readonly string[];
|
|
135
|
+
/**
|
|
136
|
+
* 0.59 F/E "manifest conventions": `true` when this helper needs a real text
|
|
137
|
+
* MEASURER for correct geometry (splitText/fitText/…). Without one it degrades
|
|
138
|
+
* to a rough per-character estimate (or, with `{ requireMeasurer: true }`,
|
|
139
|
+
* throws) — so a consumer knows to pass `{ measurer }` / call setTextMeasurer()
|
|
140
|
+
* first. Absent (⇒ not measurer-dependent) for every other helper.
|
|
141
|
+
*/
|
|
142
|
+
requiresMeasurer?: boolean;
|
|
116
143
|
}
|
|
117
144
|
/**
|
|
118
145
|
* One entry in the {@link ApiManifest.surface} taxonomy (0.47 "verifiable
|
package/dist/describe.js
CHANGED
|
@@ -23,7 +23,7 @@ import { easings, listValueTypes } from "@glissade/core";
|
|
|
23
23
|
* never pulled onto the base embed path — a scene that never calls `describe()`
|
|
24
24
|
* pays zero bytes for it.
|
|
25
25
|
*/
|
|
26
|
-
const RAW_VERSION = "0.
|
|
26
|
+
const RAW_VERSION = "0.59.0-pre.0";
|
|
27
27
|
const PACKAGE_VERSION = RAW_VERSION.includes("GLISSADE_".concat("VERSION")) ? "0.0.0-dev" : RAW_VERSION;
|
|
28
28
|
/**
|
|
29
29
|
* Parse the documented positional-arg count from a helper `usage` string — the
|
|
@@ -336,6 +336,33 @@ const BASE_CONSTRUCTION_PROP_META = {
|
|
|
336
336
|
* coords. Surfaced per node so a consumer aligning a card + its label stops
|
|
337
337
|
* pixel-measuring the mismatch — and knows `anchor` (above) overrides it.
|
|
338
338
|
*/
|
|
339
|
+
/**
|
|
340
|
+
* 0.59 F "manifest conventions" — the curated per-prop UNIT table (the
|
|
341
|
+
* POSITION_ANCHOR precedent applied to units). Keyed by prop NAME; a prop absent
|
|
342
|
+
* here carries no `unit` (unitless or px). Deliberately minimal: rotation is the
|
|
343
|
+
* classic degrees-vs-radians ambiguity, Video's time offsets are seconds.
|
|
344
|
+
*/
|
|
345
|
+
const PROP_UNITS = {
|
|
346
|
+
rotation: "degrees",
|
|
347
|
+
at: "seconds",
|
|
348
|
+
trimStart: "seconds",
|
|
349
|
+
clipDuration: "seconds",
|
|
350
|
+
sourceFps: "fps"
|
|
351
|
+
};
|
|
352
|
+
/**
|
|
353
|
+
* 0.59 F/E — helpers that need a real text measurer for correct geometry (they
|
|
354
|
+
* snapshot part/fit geometry through it). Surfaced as `requiresMeasurer:true`.
|
|
355
|
+
*/
|
|
356
|
+
const MEASURER_HELPERS = new Set([
|
|
357
|
+
"measureWrappedText",
|
|
358
|
+
"splitText",
|
|
359
|
+
"fitText",
|
|
360
|
+
"fitTextSize",
|
|
361
|
+
"fitTextGroup",
|
|
362
|
+
"revealWords",
|
|
363
|
+
"revealLines",
|
|
364
|
+
"emphasizeWords"
|
|
365
|
+
]);
|
|
339
366
|
const POSITION_ANCHOR = {
|
|
340
367
|
Rect: "center",
|
|
341
368
|
Circle: "center",
|
|
@@ -358,7 +385,8 @@ function describeNode(node, typeName) {
|
|
|
358
385
|
type,
|
|
359
386
|
animatable: true,
|
|
360
387
|
target: `<id>/${path}`,
|
|
361
|
-
...arity !== void 0 ? { arity } : {}
|
|
388
|
+
...arity !== void 0 ? { arity } : {},
|
|
389
|
+
...PROP_UNITS[path] !== void 0 ? { unit: PROP_UNITS[path] } : {}
|
|
362
390
|
};
|
|
363
391
|
}
|
|
364
392
|
const ownNames = NODE_CONSTRUCTION_PROP_NAMES[typeName] ?? [];
|
|
@@ -372,14 +400,21 @@ function describeNode(node, typeName) {
|
|
|
372
400
|
props[prop] = {
|
|
373
401
|
type: spec.type,
|
|
374
402
|
animatable: false,
|
|
375
|
-
...spec.required ? { required: true } : {}
|
|
403
|
+
...spec.required ? { required: true } : {},
|
|
404
|
+
...PROP_UNITS[prop] !== void 0 ? { unit: PROP_UNITS[prop] } : {}
|
|
376
405
|
};
|
|
377
406
|
}
|
|
378
407
|
return {
|
|
379
408
|
props,
|
|
380
|
-
positionAnchor: POSITION_ANCHOR[typeName] ?? "center"
|
|
409
|
+
positionAnchor: POSITION_ANCHOR[typeName] ?? "center",
|
|
410
|
+
bindable: bindableProps(props)
|
|
381
411
|
};
|
|
382
412
|
}
|
|
413
|
+
/** The animatable (Track-drivable) prop names of a manifest — the generated
|
|
414
|
+
* 0.59 `DescribedNode.bindable` discovery aid (can't drift from `props`). */
|
|
415
|
+
function bindableProps(props) {
|
|
416
|
+
return Object.entries(props).filter(([, p]) => p.animatable).map(([name]) => name);
|
|
417
|
+
}
|
|
383
418
|
/**
|
|
384
419
|
* The Layout family — `Layout` and its `Stack`/`Row`/`Column` ergonomic
|
|
385
420
|
* factories — lives on the budgeted `@glissade/scene/layout` entry (it ships
|
|
@@ -436,6 +471,7 @@ function describeLayoutNode() {
|
|
|
436
471
|
return {
|
|
437
472
|
props,
|
|
438
473
|
positionAnchor: "top-left",
|
|
474
|
+
bindable: bindableProps(props),
|
|
439
475
|
subpath: LAYOUT_SUBPATH
|
|
440
476
|
};
|
|
441
477
|
}
|
|
@@ -844,10 +880,11 @@ function describe(opts = {}) {
|
|
|
844
880
|
...m,
|
|
845
881
|
...ex(m.name)
|
|
846
882
|
})) : BUILDER_METHODS },
|
|
847
|
-
helpers:
|
|
883
|
+
helpers: HELPERS.map((h) => ({
|
|
848
884
|
...h,
|
|
849
|
-
...
|
|
850
|
-
|
|
885
|
+
...MEASURER_HELPERS.has(h.name) ? { requiresMeasurer: true } : {},
|
|
886
|
+
...withEx ? ex(h.name) : void 0
|
|
887
|
+
})),
|
|
851
888
|
components: listComponents().map((c) => ({
|
|
852
889
|
name: c.name,
|
|
853
890
|
props: mapComponentProps(c.props)
|
package/dist/diagnostics.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { i as DrawCommand, m as Resource, n as DisplayList } from "./displayList.js";
|
|
2
|
+
import { B as Node } from "./nodes.js";
|
|
2
3
|
import { t as collapseReplacer } from "./collapseReplacer.js";
|
|
3
|
-
import {
|
|
4
|
-
import { CoverageReport, FontMode, FontUsage, Timeline } from "@glissade/core";
|
|
4
|
+
import { i as Scene } from "./scene.js";
|
|
5
|
+
import { CoverageReport, FontMode, FontUsage, Timeline, ValueTypeId } from "@glissade/core";
|
|
5
6
|
|
|
6
7
|
//#region src/displayDiff.d.ts
|
|
7
8
|
|
|
@@ -142,4 +143,110 @@ interface ValidateSceneFontsOptions {
|
|
|
142
143
|
*/
|
|
143
144
|
declare function validateSceneFonts(scene: Scene, doc: Timeline, loadBytes: FontByteLoader, options?: ValidateSceneFontsOptions): Promise<CoverageReport>;
|
|
144
145
|
//#endregion
|
|
145
|
-
|
|
146
|
+
//#region src/validate.d.ts
|
|
147
|
+
/**
|
|
148
|
+
* Bumped ONLY on a breaking change to the diagnostic shape. New CODES and new
|
|
149
|
+
* OPTIONAL fields are additive and do NOT bump it — a consumer keys on `code`
|
|
150
|
+
* and tolerates unknown ones.
|
|
151
|
+
*/
|
|
152
|
+
declare const DIAGNOSTIC_SCHEMA_VERSION: 1;
|
|
153
|
+
/** Closed severity ladder. `error` = a build error (unbound target); `warning`
|
|
154
|
+
* = a probable-mistake (position of a flow child); `info` = a valid-but-notable
|
|
155
|
+
* observation (off-canvas, estimating measurer). */
|
|
156
|
+
type DiagnosticSeverity = 'error' | 'warning' | 'info';
|
|
157
|
+
/**
|
|
158
|
+
* Stable, ADDITIVE-ONLY diagnostic codes (never renamed/removed — the wire
|
|
159
|
+
* contract). Chosen with BOTH `validateScene` and the future
|
|
160
|
+
* `gs parity --semantic` surface in mind.
|
|
161
|
+
* - `UNKNOWN_TARGET` — a track targets an id/prop that resolves to no signal.
|
|
162
|
+
* - `ID_COLLISION` — reserved: a duplicate node id (a built Scene rejects these
|
|
163
|
+
* at assembly, so it is unreachable here today; kept for the shared contract).
|
|
164
|
+
* - `OFF_CANVAS` — a node's static position places its box fully outside the
|
|
165
|
+
* viewport (valid, but usually a mistake).
|
|
166
|
+
* - `YOGA_CHILD_POSITION` — a track drives `position`/`position.*` of a FLOWABLE
|
|
167
|
+
* child of a Layout, whose flex slot overrides/confounds that position.
|
|
168
|
+
* - `MEASURER_FALLBACK` — the scene carries Text but no real measurer is
|
|
169
|
+
* injected, so layout uses the rough per-character estimate.
|
|
170
|
+
*/
|
|
171
|
+
type DiagnosticCode = 'UNKNOWN_TARGET' | 'ID_COLLISION' | 'OFF_CANVAS' | 'YOGA_CHILD_POSITION' | 'MEASURER_FALLBACK';
|
|
172
|
+
/** One diagnostic. The `{schemaVersion, code, severity, message, node?, track?}`
|
|
173
|
+
* core shape is PINNED; future fields are ADDITIVE only. */
|
|
174
|
+
interface SceneDiagnostic {
|
|
175
|
+
schemaVersion: typeof DIAGNOSTIC_SCHEMA_VERSION;
|
|
176
|
+
code: DiagnosticCode;
|
|
177
|
+
severity: DiagnosticSeverity;
|
|
178
|
+
message: string;
|
|
179
|
+
/** The node id the diagnostic concerns, when applicable. */
|
|
180
|
+
node?: string;
|
|
181
|
+
/** The track target string the diagnostic concerns, when applicable. */
|
|
182
|
+
track?: string;
|
|
183
|
+
}
|
|
184
|
+
/** `validateScene` result — the CLI-lint `{ hasErrors, diagnostics }` shape,
|
|
185
|
+
* plus a top-level `schemaVersion`. */
|
|
186
|
+
interface ValidateSceneResult {
|
|
187
|
+
schemaVersion: typeof DIAGNOSTIC_SCHEMA_VERSION;
|
|
188
|
+
/** true iff any diagnostic has severity `error`. */
|
|
189
|
+
hasErrors: boolean;
|
|
190
|
+
/** Every diagnostic found — AGGREGATED, never throw-on-first, stable order. */
|
|
191
|
+
diagnostics: SceneDiagnostic[];
|
|
192
|
+
}
|
|
193
|
+
/** Classic edit distance (iterative two-row DP). Small strings only (ids). */
|
|
194
|
+
declare function levenshtein(a: string, b: string): number;
|
|
195
|
+
/**
|
|
196
|
+
* The nearest candidate to `name` within a reasonable edit budget (≤ 2, or a
|
|
197
|
+
* third of the length for longer names), or undefined if none is close enough —
|
|
198
|
+
* so a wildly-different typo doesn't get a misleading "did you mean" tail.
|
|
199
|
+
*/
|
|
200
|
+
declare function nearestId(name: string, candidates: Iterable<string>): string | undefined;
|
|
201
|
+
/**
|
|
202
|
+
* Eagerly validate a scene (+ optional timeline) and AGGREGATE every problem —
|
|
203
|
+
* the static belt that surfaces at the AUTHORING site what the render-time
|
|
204
|
+
* `UnboundTargetError` backstop only shows one-at-a-time from deep in the render
|
|
205
|
+
* loop. Pure read (see the module header): calling it never changes a subsequent
|
|
206
|
+
* render's bytes.
|
|
207
|
+
*
|
|
208
|
+
* With a `doc`, every track target is walked through the existing
|
|
209
|
+
* `scene.resolveTarget`; an unresolved one becomes an `UNKNOWN_TARGET` error
|
|
210
|
+
* with a Levenshtein nearest-id / nearest-prop suggestion, and a
|
|
211
|
+
* `position`/`position.*` track on a flowable Layout child becomes a
|
|
212
|
+
* `YOGA_CHILD_POSITION` warning. Scene-only checks (off-canvas, measurer
|
|
213
|
+
* fallback) run regardless.
|
|
214
|
+
*/
|
|
215
|
+
declare function validateScene(scene: Scene, doc?: Timeline): ValidateSceneResult;
|
|
216
|
+
/**
|
|
217
|
+
* Read a node's RESOLVED prop value at time `t` — the always-truthful read, the
|
|
218
|
+
* anti-false-conclusion primitive for inspection tooling (and load-bearing for
|
|
219
|
+
* 0.60 `critique()`). A BOUND prop returns its REAL bound value at `t` (not the
|
|
220
|
+
* misleading static default); an unbound prop returns its static value at any
|
|
221
|
+
* `t`; an unresolvable target returns `undefined`.
|
|
222
|
+
*
|
|
223
|
+
* Thin wrapper over the existing `scene.resolveTarget` + core's `evaluateAt`
|
|
224
|
+
* (read inside a read phase). NOTE: the scene must be BOUND (`bindScene`/
|
|
225
|
+
* `evaluate` already ran for the doc) for a track-driven value to appear —
|
|
226
|
+
* `resolveAt` reads the live signal, it does not itself bind. Render-neutral:
|
|
227
|
+
* the scene playhead is restored after the read.
|
|
228
|
+
*/
|
|
229
|
+
declare function resolveAt(scene: Scene, target: string, t: number): unknown;
|
|
230
|
+
/** One prop's live binding state on a SPECIFIC node instance. */
|
|
231
|
+
interface InstancePropState {
|
|
232
|
+
/** The track-target path (e.g. `position`, `opacity`). */
|
|
233
|
+
path: string;
|
|
234
|
+
/** The §2.2 value type(s) the prop accepts. */
|
|
235
|
+
expects: ValueTypeId | readonly ValueTypeId[] | undefined;
|
|
236
|
+
/**
|
|
237
|
+
* TRUE when THIS instance's signal currently has a bound source (a timeline
|
|
238
|
+
* track OR a computed `() => …` initializer) — so a static read of it is a
|
|
239
|
+
* LIE; use `resolveAt` to read its real value over time. This is the
|
|
240
|
+
* anti-false-conclusion guard (the cursorFill trap): type-level "bindable"
|
|
241
|
+
* says the prop CAN be animated; this says it currently IS.
|
|
242
|
+
*/
|
|
243
|
+
bound: boolean;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Announce which props are CURRENTLY bound on THIS node instance (not just
|
|
247
|
+
* type-level bindable). Reads `signal.isBound` per registered target — a pure
|
|
248
|
+
* inspection read. Pair with `resolveAt` to read a bound prop's real value.
|
|
249
|
+
*/
|
|
250
|
+
declare function instanceProps(node: Node): InstancePropState[];
|
|
251
|
+
//#endregion
|
|
252
|
+
export { type CacheColdResult, type CommandDelta, DIAGNOSTIC_SCHEMA_VERSION, DL_SNAPSHOT_VERSION, type DiagnosticCode, type DiagnosticSeverity, type DisplayDiff, type DlSnapshot, DlSnapshotError, type FieldChange, type FontByteLoader, type InstancePropState, type SceneDiagnostic, type ValidateSceneFontsOptions, type ValidateSceneResult, auditCacheCold, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, diffDisplayLists, formatDisplayDiff, instanceProps, levenshtein, nearestId, parseDisplaySnapshot, resolveAt, serializeDisplayList, validateScene, validateSceneFonts };
|
package/dist/diagnostics.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { I as isEstimatingMeasurer, J as collapseReplacer, P as estimatingMeasurer, W as createDisplayListBuilder, r as Group, s as Text } from "./nodes.js";
|
|
2
2
|
import { a as evaluate } from "./scene.js";
|
|
3
|
-
import { buildFontRegistry, parseCmap, validateFonts } from "@glissade/core";
|
|
3
|
+
import { buildFontRegistry, evaluateAt, parseCmap, untracked, validateFonts } from "@glissade/core";
|
|
4
4
|
//#region src/displayDiff.ts
|
|
5
5
|
/** A flat, stable JSON value for one command with its resource ids INLINED to content. */
|
|
6
6
|
function commandView(cmd, resources) {
|
|
@@ -272,4 +272,216 @@ async function validateSceneFonts(scene, doc, loadBytes, options = {}) {
|
|
|
272
272
|
return validateFonts(usages, registry, cmaps, mode, { ...options.osFamilies !== void 0 ? { osFamilies: options.osFamilies } : {} });
|
|
273
273
|
}
|
|
274
274
|
//#endregion
|
|
275
|
-
|
|
275
|
+
//#region src/validate.ts
|
|
276
|
+
/**
|
|
277
|
+
* 0.59 "fail-loud ground floor" — the eager, render-NEUTRAL scene validator plus
|
|
278
|
+
* the truthful read primitive (`resolveAt`) and the instance-level bound
|
|
279
|
+
* indicator (`instanceProps`). All DIAGNOSTIC surface: it lives on the
|
|
280
|
+
* tree-shakeable `@glissade/scene/diagnostics` subpath (re-exported from
|
|
281
|
+
* `diagnostics.ts`), NEVER on the base scene index — the base embed pays zero
|
|
282
|
+
* bytes for it, exactly like the diff/audit/fontUsage cluster.
|
|
283
|
+
*
|
|
284
|
+
* THE THREE INVARIANTS THIS MODULE UPHOLDS:
|
|
285
|
+
* - `validateScene(scene, doc)` is a PURE READ: it walks track targets through
|
|
286
|
+
* the EXISTING `scene.resolveTarget` (no new resolution machinery), reads node
|
|
287
|
+
* bounds/flow-flags, and reports. It NEVER draws RNG, warms a signal memo,
|
|
288
|
+
* populates a measurer/font cache, or mutates a node — so `render(scene)` is
|
|
289
|
+
* byte-identical whether or not `validateScene` ran first. (Flowable-ness is
|
|
290
|
+
* probed with the STATELESS estimating measurer, not the scene's injected one,
|
|
291
|
+
* so no backend font cache is touched.)
|
|
292
|
+
* - It AGGREGATES every failure (never throw-on-first) and returns them.
|
|
293
|
+
* - The schema is PINNED: a closed `severity` enum + stable, additive-only
|
|
294
|
+
* string `code`s + a `schemaVersion` — the shared contract with the CLI lint
|
|
295
|
+
* JSON shape and the future `gs parity --semantic`.
|
|
296
|
+
*/
|
|
297
|
+
/**
|
|
298
|
+
* Bumped ONLY on a breaking change to the diagnostic shape. New CODES and new
|
|
299
|
+
* OPTIONAL fields are additive and do NOT bump it — a consumer keys on `code`
|
|
300
|
+
* and tolerates unknown ones.
|
|
301
|
+
*/
|
|
302
|
+
const DIAGNOSTIC_SCHEMA_VERSION = 1;
|
|
303
|
+
/** Classic edit distance (iterative two-row DP). Small strings only (ids). */
|
|
304
|
+
function levenshtein(a, b) {
|
|
305
|
+
if (a === b) return 0;
|
|
306
|
+
if (a.length === 0) return b.length;
|
|
307
|
+
if (b.length === 0) return a.length;
|
|
308
|
+
let prev = new Array(b.length + 1);
|
|
309
|
+
let curr = new Array(b.length + 1);
|
|
310
|
+
for (let j = 0; j <= b.length; j++) prev[j] = j;
|
|
311
|
+
for (let i = 1; i <= a.length; i++) {
|
|
312
|
+
curr[0] = i;
|
|
313
|
+
for (let j = 1; j <= b.length; j++) {
|
|
314
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
315
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
316
|
+
}
|
|
317
|
+
[prev, curr] = [curr, prev];
|
|
318
|
+
}
|
|
319
|
+
return prev[b.length];
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* The nearest candidate to `name` within a reasonable edit budget (≤ 2, or a
|
|
323
|
+
* third of the length for longer names), or undefined if none is close enough —
|
|
324
|
+
* so a wildly-different typo doesn't get a misleading "did you mean" tail.
|
|
325
|
+
*/
|
|
326
|
+
function nearestId(name, candidates) {
|
|
327
|
+
const budget = Math.max(2, Math.floor(name.length / 3));
|
|
328
|
+
let best;
|
|
329
|
+
let bestD = Infinity;
|
|
330
|
+
for (const c of candidates) {
|
|
331
|
+
const d = levenshtein(name, c);
|
|
332
|
+
if (d < bestD && d <= budget) {
|
|
333
|
+
bestD = d;
|
|
334
|
+
best = c;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return best;
|
|
338
|
+
}
|
|
339
|
+
/** Resolve the OWNING node + remaining prop path for a `'<id>/<prop>'` target,
|
|
340
|
+
* by the same longest-registered-id-prefix walk `scene.resolveTarget` uses. */
|
|
341
|
+
function owningNode(scene, target) {
|
|
342
|
+
for (let slash = target.lastIndexOf("/"); slash > 0; slash = target.lastIndexOf("/", slash - 1)) {
|
|
343
|
+
const node = scene.nodes.get(target.slice(0, slash));
|
|
344
|
+
if (node) return {
|
|
345
|
+
node,
|
|
346
|
+
prop: target.slice(slash + 1)
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/** True when `node` is a FLOWABLE child of a Layout — i.e. its parent is a
|
|
351
|
+
* Layout (duck-typed via the `isLayoutNode` static marker, so this stays off
|
|
352
|
+
* the Yoga import) AND it has an intrinsic box (Layout flow-positions exactly
|
|
353
|
+
* these; non-flowable children emit absolutely, untouched). Flowable-ness is
|
|
354
|
+
* probed with the STATELESS estimating measurer — never the scene's injected
|
|
355
|
+
* one — so no backend font cache is warmed (render-neutrality). */
|
|
356
|
+
function isFlowableLayoutChild(node) {
|
|
357
|
+
const parent = node.parent;
|
|
358
|
+
if (!parent) return false;
|
|
359
|
+
if (parent.constructor?.isLayoutNode !== true) return false;
|
|
360
|
+
return node.intrinsicSize(estimatingMeasurer) !== null;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Eagerly validate a scene (+ optional timeline) and AGGREGATE every problem —
|
|
364
|
+
* the static belt that surfaces at the AUTHORING site what the render-time
|
|
365
|
+
* `UnboundTargetError` backstop only shows one-at-a-time from deep in the render
|
|
366
|
+
* loop. Pure read (see the module header): calling it never changes a subsequent
|
|
367
|
+
* render's bytes.
|
|
368
|
+
*
|
|
369
|
+
* With a `doc`, every track target is walked through the existing
|
|
370
|
+
* `scene.resolveTarget`; an unresolved one becomes an `UNKNOWN_TARGET` error
|
|
371
|
+
* with a Levenshtein nearest-id / nearest-prop suggestion, and a
|
|
372
|
+
* `position`/`position.*` track on a flowable Layout child becomes a
|
|
373
|
+
* `YOGA_CHILD_POSITION` warning. Scene-only checks (off-canvas, measurer
|
|
374
|
+
* fallback) run regardless.
|
|
375
|
+
*/
|
|
376
|
+
function validateScene(scene, doc) {
|
|
377
|
+
const diagnostics = [];
|
|
378
|
+
const push = (code, severity, message, extra) => {
|
|
379
|
+
diagnostics.push({
|
|
380
|
+
schemaVersion: 1,
|
|
381
|
+
code,
|
|
382
|
+
severity,
|
|
383
|
+
message,
|
|
384
|
+
...extra?.node !== void 0 ? { node: extra.node } : {},
|
|
385
|
+
...extra?.track !== void 0 ? { track: extra.track } : {}
|
|
386
|
+
});
|
|
387
|
+
};
|
|
388
|
+
untracked(() => {
|
|
389
|
+
if (doc) for (const tr of doc.tracks) {
|
|
390
|
+
const target = tr.target;
|
|
391
|
+
const owner = owningNode(scene, target);
|
|
392
|
+
if (!scene.resolveTarget(target)) {
|
|
393
|
+
push("UNKNOWN_TARGET", "error", unknownTargetMessage(scene, target, owner), {
|
|
394
|
+
track: target,
|
|
395
|
+
...owner?.node.id !== void 0 ? { node: owner.node.id } : {}
|
|
396
|
+
});
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (owner && /^position(\.[xy])?$/.test(owner.prop) && isFlowableLayoutChild(owner.node)) push("YOGA_CHILD_POSITION", "warning", `track '${target}' drives the position of a flowable child of a Layout — the flex slot overrides it, so the keyframes are confounded. Animate the Layout (gap/padding/width) or wrap the child in a Group and drive THAT, or make the child absolute (non-flowable).`, {
|
|
400
|
+
track: target,
|
|
401
|
+
...owner.node.id !== void 0 ? { node: owner.node.id } : {}
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
const animatedPos = /* @__PURE__ */ new Set();
|
|
405
|
+
if (doc) for (const tr of doc.tracks) {
|
|
406
|
+
const m = /^(.+)\/position(?:\.[xy])?$/.exec(tr.target);
|
|
407
|
+
if (m) animatedPos.add(m[1]);
|
|
408
|
+
}
|
|
409
|
+
const { w, h } = scene.size;
|
|
410
|
+
let sawText = false;
|
|
411
|
+
const visit = (node) => {
|
|
412
|
+
if (isTextNode(node)) sawText = true;
|
|
413
|
+
const pos = node.position;
|
|
414
|
+
if (node.id !== void 0 && !animatedPos.has(node.id) && typeof pos === "function") {
|
|
415
|
+
const [px, py] = pos();
|
|
416
|
+
if (Number.isFinite(px) && Number.isFinite(py) && (px < 0 || py < 0 || px > w || py > h)) push("OFF_CANVAS", "info", `node '${node.id}' has a static position [${px}, ${py}] outside the ${w}×${h} viewport — it may not be visible (fine if intentional, e.g. an off-screen start).`, { node: node.id });
|
|
417
|
+
}
|
|
418
|
+
const children = node.children;
|
|
419
|
+
if (Array.isArray(children)) for (const c of children) visit(c);
|
|
420
|
+
};
|
|
421
|
+
visit(scene.root);
|
|
422
|
+
if (sawText && isEstimatingMeasurer(scene.textMeasurer)) push("MEASURER_FALLBACK", "info", "the scene contains Text but no real text measurer is injected — line breaking uses a rough per-character estimate. Call setTextMeasurer(backend) / setDefaultMeasurer(...) for exact layout.");
|
|
423
|
+
});
|
|
424
|
+
return {
|
|
425
|
+
schemaVersion: 1,
|
|
426
|
+
hasErrors: diagnostics.some((d) => d.severity === "error"),
|
|
427
|
+
diagnostics
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
/** Build the friendliest UNKNOWN_TARGET message: nearest-PROP when the node
|
|
431
|
+
* exists (a typo'd prop), else nearest node-ID (a typo'd id). */
|
|
432
|
+
function unknownTargetMessage(scene, target, owner) {
|
|
433
|
+
if (owner) {
|
|
434
|
+
const props = owner.node.listTargets().map((t) => t.path);
|
|
435
|
+
const near = nearestId(owner.prop, props);
|
|
436
|
+
return `track targets '${target}' but node '${owner.node.id}' has no animatable prop '${owner.prop}'` + (near ? ` — did you mean '${owner.node.id}/${near}'?` : "");
|
|
437
|
+
}
|
|
438
|
+
const slash = target.indexOf("/");
|
|
439
|
+
const idPart = slash >= 0 ? target.slice(0, slash) : target;
|
|
440
|
+
const near = nearestId(idPart, scene.nodes.keys());
|
|
441
|
+
return `track targets '${target}' but no node '${idPart}' exists in the scene` + (near ? ` — did you mean '${near}'?` : "");
|
|
442
|
+
}
|
|
443
|
+
/** Duck-typed Text detection (avoids importing nodes.ts / dragging its
|
|
444
|
+
* construction surface onto the diagnostics bundle): Text overrides
|
|
445
|
+
* `describeType` to `'Text'`. */
|
|
446
|
+
function isTextNode(node) {
|
|
447
|
+
return node.describeType === "Text";
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Read a node's RESOLVED prop value at time `t` — the always-truthful read, the
|
|
451
|
+
* anti-false-conclusion primitive for inspection tooling (and load-bearing for
|
|
452
|
+
* 0.60 `critique()`). A BOUND prop returns its REAL bound value at `t` (not the
|
|
453
|
+
* misleading static default); an unbound prop returns its static value at any
|
|
454
|
+
* `t`; an unresolvable target returns `undefined`.
|
|
455
|
+
*
|
|
456
|
+
* Thin wrapper over the existing `scene.resolveTarget` + core's `evaluateAt`
|
|
457
|
+
* (read inside a read phase). NOTE: the scene must be BOUND (`bindScene`/
|
|
458
|
+
* `evaluate` already ran for the doc) for a track-driven value to appear —
|
|
459
|
+
* `resolveAt` reads the live signal, it does not itself bind. Render-neutral:
|
|
460
|
+
* the scene playhead is restored after the read.
|
|
461
|
+
*/
|
|
462
|
+
function resolveAt(scene, target, t) {
|
|
463
|
+
const sig = scene.resolveTarget(target);
|
|
464
|
+
if (typeof sig !== "function") return void 0;
|
|
465
|
+
const prev = scene.playhead.peek();
|
|
466
|
+
try {
|
|
467
|
+
return evaluateAt(scene.playhead, t, () => sig());
|
|
468
|
+
} finally {
|
|
469
|
+
scene.playhead.forceSet(prev);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Announce which props are CURRENTLY bound on THIS node instance (not just
|
|
474
|
+
* type-level bindable). Reads `signal.isBound` per registered target — a pure
|
|
475
|
+
* inspection read. Pair with `resolveAt` to read a bound prop's real value.
|
|
476
|
+
*/
|
|
477
|
+
function instanceProps(node) {
|
|
478
|
+
return node.listTargets().map(({ path, expects }) => {
|
|
479
|
+
return {
|
|
480
|
+
path,
|
|
481
|
+
expects,
|
|
482
|
+
bound: node.resolveTarget(path)?.isBound === true
|
|
483
|
+
};
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
//#endregion
|
|
487
|
+
export { DIAGNOSTIC_SCHEMA_VERSION, DL_SNAPSHOT_VERSION, DlSnapshotError, auditCacheCold, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, diffDisplayLists, formatDisplayDiff, instanceProps, levenshtein, nearestId, parseDisplaySnapshot, resolveAt, serializeDisplayList, validateScene, validateSceneFonts };
|
package/dist/identity.d.ts
CHANGED
package/dist/identity.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { C as Mat2x3, D as matEquals, E as invert, O as multiply, S as IDENTITY, T as fromTRS, _ as StrokeStyle, a as FilterSpec, b as glow, c as MeshInterpolation, d as Paint, f as PathSeg, g as ShaderRef, h as ResourceId, i as DrawCommand, l as MeshPaint, m as Resource, n as DisplayList, o as FilterValidationError, p as Rect$1, r as DisplayListBuilder, s as FontSpec, t as BlendMode, u as MeshPoint, v as createDisplayListBuilder, w as applyToPoint, x as validateFilters, y as filtersToCanvasFilter } from "./displayList.js";
|
|
2
|
-
import { $ as
|
|
2
|
+
import { $ as estimatingMeasurer, A as hachureLines, B as Node, C as HachureSpec, D as SketchValidationError, E as SketchStyle, F as validateSketch, G as MEASURE_QUANTUM_PX, H as NodeProps, I as AnchorSpec, J as TextMetricsLite, L as BindablePropTarget, M as roughen, N as sketchStrokes, O as arcLength, P as validateHachure, Q as breakLines, R as EvalContext, S as roundedRectSegs, T as ResolvedSketch, U as PropInit, V as NodeConstructionError, W as resolveAnchor, X as __resetEstimateWarnings, Y as WrappedTextMetrics, Z as assertFiniteFontSize, _ as VideoProps, a as Group, at as setDefaultMeasurer, b as pathFromSegs, c as LineBox, d as Rect, et as isEstimatingMeasurer, f as RevealMark, g as Video, h as TextProps, i as GraphemeBox, it as segmentWords, j as resolveSketch, k as flatten, l as Path, m as Text, n as ClipRegion, nt as quantize, o as ImageNode, p as ShapeProps, q as TextMeasurer, r as Custom, rt as segmentGraphemes, s as ImageProps, t as Circle, tt as measureWrappedText, u as PathProps, v as WordBox, w as Polyline, x as revealSchedule, y as coercePathData, z as HitArea } from "./nodes.js";
|
|
3
3
|
import { t as collapseReplacer } from "./collapseReplacer.js";
|
|
4
|
-
import { a as
|
|
4
|
+
import { a as SceneInit, c as createScene, i as Scene, l as evaluate, n as DuplicateNodeIdError, o as SceneModule, r as ReservedNodeIdError, s as bindScene, t as BindSceneOptions } from "./scene.js";
|
|
5
5
|
import { a as typewriter, c as textCursor, i as TypewriterResult, n as StepMark, o as TextCursor, r as TypeEdit, s as TextCursorProps, t as EditMark } from "./typewriter.js";
|
|
6
6
|
import { a as EachLayout, c as EachResult, i as EachError, l as Place, n as EachContext, o as EachMotion, r as EachDistribute, s as EachOpts, t as EachBox, u as each } from "./each.js";
|
|
7
7
|
import { a as LayoutEngineMissingError, c as requireLayoutEngine, i as LayoutEngine, l as setLayoutEngine, n as LayoutChildSpec, r as LayoutContainerSpec, s as getLayoutEngine, t as LayoutBox } from "./layoutEngine.js";
|
|
@@ -493,4 +493,4 @@ declare function meshRasterSize(bw: number, bh: number): {
|
|
|
493
493
|
h: number;
|
|
494
494
|
};
|
|
495
495
|
//#endregion
|
|
496
|
-
export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type Bounds, type CanvasLike, Circle, type ClipRegion, ColdAssetError, type Ctx2DLike, Custom, DeterminismViolationError, type DisplayList, type DisplayListBuilder, type DrawCommand, type DrawOnEachOptions, type DrawOnOptions, DuplicateNodeIdError, type EachBox, type EachContext, type EachDistribute, EachError, type EachLayout, type EachMotion, type EachOpts, type EachResult, Echo, type EchoProps, type EditMark, type EvalContext, type FilterKind, type FilterSpec, FilterValidationError, type FontSpec, type GraphemeBox, Group, type GuardMode, type HachureSpec, Highlight, type HighlightProps, type HitArea, IDENTITY, ImageNode as Image, ImageNode, type ImageDataLike, type ImageHandle, type ImageProps, type LayerCacheEntry, type LayerStore, type LayoutBox, type LayoutChildSpec, type LayoutContainerSpec, type LayoutEngine, LayoutEngineMissingError, type LineBox, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, type Mat2x3, type MeshInterpolation, type MeshPaint, type MeshPoint, MotionBlur, type MotionBlurProps, NODE_TAXONOMY, Node, NodeConstructionError, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSeg, type Place, type Polyline, type PropInit, Raster2D, type Raster2DHost, Rect, type Rect$1 as RectShape, type RenderBackend, ReservedNodeIdError, type ResolvedSketch, type Resource, type ResourceId, type RevealMark, type Scene, type SceneInit, type SceneModule, type ShaderCaps, ShaderEffect, type ShaderEffectProps, type ShaderRef, type ShapeProps, type SketchStyle, SketchValidationError, type StepMark, type StrokeStyle, Text, TextCursor, type TextCursorProps, type TextMeasurer, type TextMetricsLite, type TextProps, TrackMatte, type TrackMatteProps, type TypeEdit, type TypewriterResult, Video, type VideoFrameSource, type VideoProps, type WordBox, type WrappedTextMetrics, __resetEstimateWarnings, applyToPoint, arcLength, assertFiniteFontSize, bindScene, breakLines, coercePathData, collapseReplacer, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, echo, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, motionBlur, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, trackMatte, typewriter, validateFilters, validateHachure, validateSketch, withDeterminismGuards };
|
|
496
|
+
export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindSceneOptions, type BindablePropTarget, type BlendMode, type Bounds, type CanvasLike, Circle, type ClipRegion, ColdAssetError, type Ctx2DLike, Custom, DeterminismViolationError, type DisplayList, type DisplayListBuilder, type DrawCommand, type DrawOnEachOptions, type DrawOnOptions, DuplicateNodeIdError, type EachBox, type EachContext, type EachDistribute, EachError, type EachLayout, type EachMotion, type EachOpts, type EachResult, Echo, type EchoProps, type EditMark, type EvalContext, type FilterKind, type FilterSpec, FilterValidationError, type FontSpec, type GraphemeBox, Group, type GuardMode, type HachureSpec, Highlight, type HighlightProps, type HitArea, IDENTITY, ImageNode as Image, ImageNode, type ImageDataLike, type ImageHandle, type ImageProps, type LayerCacheEntry, type LayerStore, type LayoutBox, type LayoutChildSpec, type LayoutContainerSpec, type LayoutEngine, LayoutEngineMissingError, type LineBox, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, type Mat2x3, type MeshInterpolation, type MeshPaint, type MeshPoint, MotionBlur, type MotionBlurProps, NODE_TAXONOMY, Node, NodeConstructionError, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSeg, type Place, type Polyline, type PropInit, Raster2D, type Raster2DHost, Rect, type Rect$1 as RectShape, type RenderBackend, ReservedNodeIdError, type ResolvedSketch, type Resource, type ResourceId, type RevealMark, type Scene, type SceneInit, type SceneModule, type ShaderCaps, ShaderEffect, type ShaderEffectProps, type ShaderRef, type ShapeProps, type SketchStyle, SketchValidationError, type StepMark, type StrokeStyle, Text, TextCursor, type TextCursorProps, type TextMeasurer, type TextMetricsLite, type TextProps, TrackMatte, type TrackMatteProps, type TypeEdit, type TypewriterResult, Video, type VideoFrameSource, type VideoProps, type WordBox, type WrappedTextMetrics, __resetEstimateWarnings, applyToPoint, arcLength, assertFiniteFontSize, bindScene, breakLines, coercePathData, collapseReplacer, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, echo, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, motionBlur, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, trackMatte, typewriter, validateFilters, validateHachure, validateSketch, withDeterminismGuards };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as matEquals, B as segmentWords, C as Node, G as filtersToCanvasFilter, I as isEstimatingMeasurer, J as collapseReplacer, K as glow, L as measureWrappedText, M as assertFiniteFontSize, N as breakLines, P as estimatingMeasurer, Q as invert, R as quantize, S as validateSketch, T as resolveAnchor, U as FilterValidationError, V as setDefaultMeasurer, W as createDisplayListBuilder, X as applyToPoint, Y as IDENTITY, Z as fromTRS, a as Path, b as sketchStrokes, c as Video, d as revealSchedule, et as multiply, f as roundedRectSegs, g as hachureLines, h as flatten, i as ImageNode, j as __resetEstimateWarnings, k as MEASURE_QUANTUM_PX, l as coercePathData, m as arcLength, n as Custom, o as Rect, p as SketchValidationError, q as validateFilters, r as Group, s as Text, t as Circle, u as pathFromSegs, v as resolveSketch, w as NodeConstructionError, x as validateHachure, y as roughen, z as segmentGraphemes } from "./nodes.js";
|
|
2
2
|
import { a as evaluate, i as createScene, n as ReservedNodeIdError, r as bindScene, t as DuplicateNodeIdError } from "./scene.js";
|
|
3
3
|
import { i as setLayoutEngine, n as getLayoutEngine, r as requireLayoutEngine, t as LayoutEngineMissingError } from "./layoutEngine.js";
|
|
4
4
|
import { n as TextCursor, r as textCursor, t as typewriter } from "./typewriter.js";
|
package/dist/layoutCtors.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as DisplayListBuilder } from "./displayList.js";
|
|
2
|
-
import { B as Node, H as NodeProps,
|
|
2
|
+
import { B as Node, H as NodeProps, R as EvalContext, U as PropInit, a as Group, q as TextMeasurer } from "./nodes.js";
|
|
3
3
|
import { r as LayoutContainerSpec } from "./layoutEngine.js";
|
|
4
4
|
import { BindableSignal } from "@glissade/core";
|
|
5
5
|
|
package/dist/layoutCtors.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { F as fallbackMeasurer, r as Group } from "./nodes.js";
|
|
2
2
|
import { r as requireLayoutEngine } from "./layoutEngine.js";
|
|
3
3
|
import { computed, signal } from "@glissade/core";
|
|
4
4
|
//#region src/layoutCtors.ts
|
package/dist/motion.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as Node, Z as fromTRS, _ as hashStr, et as multiply, r as Group, s as Text, t as Circle } from "./nodes.js";
|
|
2
2
|
import { a as FollowPath, c as pathLength, i as orientToPath, l as pointAtLength, n as OrientToPath, o as followPath, r as lookAt, s as motionPath, t as LookAt } from "./orient.js";
|
|
3
3
|
import { n as each } from "./each.js";
|
|
4
4
|
import { bake, signal, valueNoise, vec2Signal } from "@glissade/core";
|
package/dist/nodes.d.ts
CHANGED
|
@@ -51,11 +51,26 @@ declare const estimatingMeasurer: TextMeasurer;
|
|
|
51
51
|
* registered measurer never trips it.
|
|
52
52
|
*/
|
|
53
53
|
declare function isEstimatingMeasurer(m: TextMeasurer): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Thrown by a measurer-requiring helper (`splitText`/`fitText`) when it resolved
|
|
56
|
+
* to the estimating fallback AND the caller opted into fail-loud
|
|
57
|
+
* (`requireMeasurer: true`, 0.59 measurer fail-loud). The default stays
|
|
58
|
+
* warn-once (below) — this is the opt-in that turns the silent geometry
|
|
59
|
+
* DEGRADATION into a hard error for pipelines that need exact layout.
|
|
60
|
+
*/
|
|
61
|
+
declare class MeasurerRequiredError extends Error {
|
|
62
|
+
constructor(site: string);
|
|
63
|
+
}
|
|
54
64
|
/**
|
|
55
65
|
* One-shot dev-warning when a build-time geometry getter resolved its measurer
|
|
56
66
|
* to the rough per-character estimate (no backend, no `setDefaultMeasurer`).
|
|
57
67
|
* `site` keys the de-dupe so each distinct caller warns at most once. Silent
|
|
58
68
|
* when a real measurer is in play — the estimate is the only footgun here.
|
|
69
|
+
*
|
|
70
|
+
* 0.59: pass `strict` to make the same fallback FAIL LOUD ({@link
|
|
71
|
+
* MeasurerRequiredError}) instead of warn-once — the `requireMeasurer` opt-in on
|
|
72
|
+
* splitText/fitText. Default (`strict` falsy) preserves the exact prior
|
|
73
|
+
* warn-once behavior (byte/behavior-neutral).
|
|
59
74
|
*/
|
|
60
75
|
|
|
61
76
|
/**
|
|
@@ -961,4 +976,4 @@ interface RevealMark {
|
|
|
961
976
|
*/
|
|
962
977
|
declare function revealSchedule(text: Text, reveal: Track<number>, measurer?: TextMeasurer): RevealMark[];
|
|
963
978
|
//#endregion
|
|
964
|
-
export {
|
|
979
|
+
export { estimatingMeasurer as $, hachureLines as A, Node as B, HachureSpec as C, SketchValidationError as D, SketchStyle as E, validateSketch as F, MEASURE_QUANTUM_PX as G, NodeProps as H, AnchorSpec as I, TextMetricsLite as J, MeasurerRequiredError as K, BindablePropTarget as L, roughen as M, sketchStrokes as N, arcLength as O, validateHachure as P, breakLines as Q, EvalContext as R, roundedRectSegs as S, ResolvedSketch as T, PropInit as U, NodeConstructionError as V, resolveAnchor as W, __resetEstimateWarnings as X, WrappedTextMetrics as Y, assertFiniteFontSize as Z, VideoProps as _, Group as a, setDefaultMeasurer as at, pathFromSegs as b, LineBox as c, Rect as d, isEstimatingMeasurer as et, RevealMark as f, Video as g, TextProps as h, GraphemeBox as i, segmentWords as it, resolveSketch as j, flatten as k, Path as l, Text as m, ClipRegion as n, quantize as nt, ImageNode as o, ShapeProps as p, TextMeasurer as q, Custom as r, segmentGraphemes as rt, ImageProps as s, Circle as t, measureWrappedText as tt, PathProps as u, WordBox as v, Polyline as w, revealSchedule as x, coercePathData as y, HitArea as z };
|
package/dist/nodes.js
CHANGED
|
@@ -324,13 +324,32 @@ function isEstimatingMeasurer(m) {
|
|
|
324
324
|
}
|
|
325
325
|
const warnedEstimate = /* @__PURE__ */ new Set();
|
|
326
326
|
/**
|
|
327
|
+
* Thrown by a measurer-requiring helper (`splitText`/`fitText`) when it resolved
|
|
328
|
+
* to the estimating fallback AND the caller opted into fail-loud
|
|
329
|
+
* (`requireMeasurer: true`, 0.59 measurer fail-loud). The default stays
|
|
330
|
+
* warn-once (below) — this is the opt-in that turns the silent geometry
|
|
331
|
+
* DEGRADATION into a hard error for pipelines that need exact layout.
|
|
332
|
+
*/
|
|
333
|
+
var MeasurerRequiredError = class extends Error {
|
|
334
|
+
constructor(site) {
|
|
335
|
+
super(`${site}: no real text measurer available and { requireMeasurer: true } was set — part geometry would use a rough per-character estimate. Pass { measurer } or call setTextMeasurer()/setDefaultMeasurer() first.`);
|
|
336
|
+
this.name = "MeasurerRequiredError";
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
/**
|
|
327
340
|
* One-shot dev-warning when a build-time geometry getter resolved its measurer
|
|
328
341
|
* to the rough per-character estimate (no backend, no `setDefaultMeasurer`).
|
|
329
342
|
* `site` keys the de-dupe so each distinct caller warns at most once. Silent
|
|
330
343
|
* when a real measurer is in play — the estimate is the only footgun here.
|
|
344
|
+
*
|
|
345
|
+
* 0.59: pass `strict` to make the same fallback FAIL LOUD ({@link
|
|
346
|
+
* MeasurerRequiredError}) instead of warn-once — the `requireMeasurer` opt-in on
|
|
347
|
+
* splitText/fitText. Default (`strict` falsy) preserves the exact prior
|
|
348
|
+
* warn-once behavior (byte/behavior-neutral).
|
|
331
349
|
*/
|
|
332
|
-
function warnIfEstimating(m, site) {
|
|
350
|
+
function warnIfEstimating(m, site, strict = false) {
|
|
333
351
|
if (!isEstimatingMeasurer(m)) return;
|
|
352
|
+
if (strict) throw new MeasurerRequiredError(site);
|
|
334
353
|
if (warnedEstimate.has(site)) return;
|
|
335
354
|
warnedEstimate.add(site);
|
|
336
355
|
emitDevWarning(`${site}: no text measurer available — using a rough per-character estimate; pass { measurer } or call after setTextMeasurer()/setDefaultMeasurer() for exact layout.`);
|
|
@@ -2358,4 +2377,4 @@ function revealSchedule(text, reveal, measurer) {
|
|
|
2358
2377
|
return marks;
|
|
2359
2378
|
}
|
|
2360
2379
|
//#endregion
|
|
2361
|
-
export {
|
|
2380
|
+
export { matEquals as $, MeasurerRequiredError as A, segmentWords as B, Node as C, NODE_CONSTRUCTION_PROP_NAMES as D, BASE_CONSTRUCTION_PROP_NAMES as E, fallbackMeasurer as F, filtersToCanvasFilter as G, warnIfEstimating as H, isEstimatingMeasurer as I, collapseReplacer as J, glow as K, measureWrappedText as L, assertFiniteFontSize as M, breakLines as N, isConstructionProp as O, estimatingMeasurer as P, invert as Q, quantize as R, validateSketch as S, resolveAnchor as T, FilterValidationError as U, setDefaultMeasurer as V, createDisplayListBuilder as W, applyToPoint as X, IDENTITY as Y, fromTRS as Z, hashStr as _, Path as a, sketchStrokes as b, Video as c, revealSchedule as d, multiply as et, roundedRectSegs as f, hachureLines as g, flatten as h, ImageNode as i, __resetEstimateWarnings as j, MEASURE_QUANTUM_PX as k, coercePathData as l, arcLength as m, Custom as n, Rect as o, SketchValidationError as p, validateFilters as q, Group as r, Text as s, Circle as t, pathFromSegs as u, resolveSketch as v, NodeConstructionError as w, validateHachure as x, roughen as y, segmentGraphemes as z };
|
package/dist/orient.js
CHANGED
package/dist/scene.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as DisplayList, s as FontSpec } from "./displayList.js";
|
|
2
|
-
import { B as Node,
|
|
2
|
+
import { B as Node, L as BindablePropTarget, Y as WrappedTextMetrics, a as Group, q as TextMeasurer } from "./nodes.js";
|
|
3
3
|
import { BoundTimeline, CompiledTimeline, Playhead, Timeline } from "@glissade/core";
|
|
4
4
|
|
|
5
5
|
//#region src/scene.d.ts
|
|
@@ -55,7 +55,18 @@ interface BindingCacheEntry {
|
|
|
55
55
|
compiled: CompiledTimeline;
|
|
56
56
|
bound: BoundTimeline;
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
/** Options for {@link bindScene}. */
|
|
59
|
+
interface BindSceneOptions {
|
|
60
|
+
/**
|
|
61
|
+
* 0.59 mode gate (threaded to {@link BindOptions.onUnbound}): `'throw'`
|
|
62
|
+
* (default, loud) vs `'warn'` (prod embeds — downgrade an unresolved target to
|
|
63
|
+
* a dev-warning and skip the track). `mount({ production: true })` passes
|
|
64
|
+
* `'warn'` here on its first (memo-warming) bind, so the whole render loop runs
|
|
65
|
+
* in the chosen mode. Byte-neutral for every valid scene (see BindOptions).
|
|
66
|
+
*/
|
|
67
|
+
onUnbound?: 'throw' | 'warn';
|
|
68
|
+
}
|
|
69
|
+
declare function bindScene(scene: Scene, doc: Timeline, opts?: BindSceneOptions): BindingCacheEntry;
|
|
59
70
|
/**
|
|
60
71
|
* The non-negotiable contract (§2.5): same (scene, timeline, t) → identical
|
|
61
72
|
* DisplayList, in any call order. Never awaits; asset readiness is the
|
|
@@ -76,4 +87,4 @@ declare function evaluate(scene: Scene, doc: Timeline, t: number): DisplayList;
|
|
|
76
87
|
*/
|
|
77
88
|
declare function evaluate(scene: Scene): DisplayList;
|
|
78
89
|
//#endregion
|
|
79
|
-
export {
|
|
90
|
+
export { SceneInit as a, createScene as c, Scene as i, evaluate as l, DuplicateNodeIdError as n, SceneModule as o, ReservedNodeIdError as r, bindScene as s, BindSceneOptions as t };
|
package/dist/scene.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { F as fallbackMeasurer, L as measureWrappedText, O as isConstructionProp, W as createDisplayListBuilder, r as Group } from "./nodes.js";
|
|
2
2
|
import { bindTimeline, compileTimeline, createPlayhead, evaluateAt, signal } from "@glissade/core";
|
|
3
3
|
//#region src/scene.ts
|
|
4
4
|
/**
|
|
@@ -75,7 +75,7 @@ function constructionPropMessage(nodes, target) {
|
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
function bindScene(scene, doc) {
|
|
78
|
+
function bindScene(scene, doc, opts = {}) {
|
|
79
79
|
let perScene = bindings.get(scene);
|
|
80
80
|
if (!perScene) {
|
|
81
81
|
perScene = /* @__PURE__ */ new WeakMap();
|
|
@@ -86,7 +86,10 @@ function bindScene(scene, doc) {
|
|
|
86
86
|
const compiled = compileTimeline(doc);
|
|
87
87
|
entry = {
|
|
88
88
|
compiled,
|
|
89
|
-
bound: bindTimeline(compiled, scene.resolveTarget, scene.playhead, {
|
|
89
|
+
bound: bindTimeline(compiled, scene.resolveTarget, scene.playhead, {
|
|
90
|
+
unboundMessage: (target) => constructionPropMessage(scene.nodes, target),
|
|
91
|
+
...opts.onUnbound !== void 0 ? { onUnbound: opts.onUnbound } : {}
|
|
92
|
+
})
|
|
90
93
|
};
|
|
91
94
|
perScene.set(doc, entry);
|
|
92
95
|
}
|
package/dist/tokens.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as matEquals, C as Node, Y as IDENTITY, f as roundedRectSegs } from "./nodes.js";
|
|
2
2
|
import { signal, vec2Signal } from "@glissade/core";
|
|
3
3
|
//#region src/tokenHighlight.ts
|
|
4
4
|
/**
|
package/dist/type.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { K as
|
|
1
|
+
import { K as MeasurerRequiredError, a as Group, c as LineBox, h as TextProps, i as GraphemeBox, m as Text, q as TextMeasurer, v as WordBox } from "./nodes.js";
|
|
2
2
|
import { o as TextCursor, s as TextCursorProps, t as EditMark } from "./typewriter.js";
|
|
3
3
|
import { EaseSpec, Track } from "@glissade/core";
|
|
4
4
|
|
|
@@ -20,6 +20,14 @@ interface SplitTextOpts {
|
|
|
20
20
|
* Text geometry getters use.
|
|
21
21
|
*/
|
|
22
22
|
measurer?: TextMeasurer;
|
|
23
|
+
/**
|
|
24
|
+
* 0.59 measurer fail-loud OPT-IN. By default a split with no real measurer
|
|
25
|
+
* warns once and degrades to the rough per-character estimate. Set
|
|
26
|
+
* `requireMeasurer: true` to instead THROW `MeasurerRequiredError` — for a
|
|
27
|
+
* pipeline that must not silently ship estimated geometry. Default false
|
|
28
|
+
* (warn) — behavior-neutral.
|
|
29
|
+
*/
|
|
30
|
+
requireMeasurer?: boolean;
|
|
23
31
|
}
|
|
24
32
|
/** One part of a split, in the source Text's draw space (group-local coords). */
|
|
25
33
|
interface SplitPart {
|
|
@@ -89,6 +97,11 @@ interface FitTextOpts {
|
|
|
89
97
|
/** measurer for exact fit — pass one (or call setTextMeasurer first), else the
|
|
90
98
|
* estimating fallback is used with a one-time dev warning (the splitText footgun). */
|
|
91
99
|
measurer?: TextMeasurer;
|
|
100
|
+
/**
|
|
101
|
+
* 0.59 measurer fail-loud OPT-IN (as splitText): `true` THROWs
|
|
102
|
+
* `MeasurerRequiredError` when no real measurer is available instead of
|
|
103
|
+
* warn-once + estimate. Default false. */
|
|
104
|
+
requireMeasurer?: boolean;
|
|
92
105
|
}
|
|
93
106
|
/**
|
|
94
107
|
* The largest integer-px fontSize ≤ the text's current size at which it fits the
|
|
@@ -238,4 +251,4 @@ interface EmphasizeOpts {
|
|
|
238
251
|
*/
|
|
239
252
|
declare function emphasizeWords(source: Text | TextProps, indices: readonly number[], opts?: EmphasizeOpts): RevealResult;
|
|
240
253
|
//#endregion
|
|
241
|
-
export { EmphasizeOpts, FitTextOpts, type GraphemeBox, KineticTypeError, type LineBox, RevealFrom, RevealOpts, RevealResult, SplitBy, SplitPart, SplitTextError, SplitTextOpts, SplitTextResult, TypeOnOpts, TypeOnResult, type WordBox, emphasizeWords, fitText, fitTextGroup, fitTextSize, revealLines, revealWords, splitText, typeOn };
|
|
254
|
+
export { EmphasizeOpts, FitTextOpts, type GraphemeBox, KineticTypeError, type LineBox, MeasurerRequiredError, RevealFrom, RevealOpts, RevealResult, SplitBy, SplitPart, SplitTextError, SplitTextOpts, SplitTextResult, TypeOnOpts, TypeOnResult, type WordBox, emphasizeWords, fitText, fitTextGroup, fitTextSize, revealLines, revealWords, splitText, typeOn };
|
package/dist/type.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as MeasurerRequiredError, F as fallbackMeasurer, H as warnIfEstimating, L as measureWrappedText, R as quantize, r as Group, s as Text, z as segmentGraphemes } from "./nodes.js";
|
|
2
2
|
import { r as textCursor, t as typewriter } from "./typewriter.js";
|
|
3
3
|
import { key, timeline, track } from "@glissade/core";
|
|
4
4
|
//#region src/type.ts
|
|
@@ -54,7 +54,7 @@ function splitText(source, opts = {}) {
|
|
|
54
54
|
"grapheme"
|
|
55
55
|
].includes(by)) throw new SplitTextError(`splitText() got an unknown { by: ${JSON.stringify(by)} } — valid values are 'word', 'line', 'grapheme'.`);
|
|
56
56
|
const m = opts.measurer ?? text.measurerSource?.() ?? fallbackMeasurer();
|
|
57
|
-
warnIfEstimating(m, "splitText");
|
|
57
|
+
warnIfEstimating(m, "splitText", opts.requireMeasurer === true);
|
|
58
58
|
const font = {
|
|
59
59
|
fontFamily: text.fontFamily,
|
|
60
60
|
fontSize: text.fontSize(),
|
|
@@ -160,7 +160,7 @@ function fits(text, size, opts, m) {
|
|
|
160
160
|
*/
|
|
161
161
|
function fitTextSize(text, opts) {
|
|
162
162
|
const m = opts.measurer ?? text.measurerSource?.() ?? fallbackMeasurer();
|
|
163
|
-
warnIfEstimating(m, "fitText");
|
|
163
|
+
warnIfEstimating(m, "fitText", opts.requireMeasurer === true);
|
|
164
164
|
const minPx = opts.minPx ?? 6;
|
|
165
165
|
const hi = Math.max(minPx, Math.floor(text.fontSize()));
|
|
166
166
|
if (fits(text, hi, opts, m)) return hi;
|
|
@@ -357,4 +357,4 @@ function emphasizeWords(source, indices, opts = {}) {
|
|
|
357
357
|
};
|
|
358
358
|
}
|
|
359
359
|
//#endregion
|
|
360
|
-
export { KineticTypeError, SplitTextError, emphasizeWords, fitText, fitTextGroup, fitTextSize, revealLines, revealWords, splitText, typeOn };
|
|
360
|
+
export { KineticTypeError, MeasurerRequiredError, SplitTextError, emphasizeWords, fitText, fitTextGroup, fitTextSize, revealLines, revealWords, splitText, typeOn };
|
package/dist/typewriter.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as matEquals, C as Node, Y as IDENTITY, f as roundedRectSegs, z as segmentGraphemes } from "./nodes.js";
|
|
2
2
|
import { key, signal, track } from "@glissade/core";
|
|
3
3
|
//#region src/textCursor.ts
|
|
4
4
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glissade/scene",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0-pre.0",
|
|
4
4
|
"description": "glissade scene graph: nodes, transforms, DisplayList emission. Renderer-agnostic; zero DOM/Node dependencies.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"engines": {
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
],
|
|
82
82
|
"dependencies": {
|
|
83
83
|
"yoga-layout": "^3.2.1",
|
|
84
|
-
"@glissade/core": "0.
|
|
84
|
+
"@glissade/core": "0.59.0-pre.0"
|
|
85
85
|
},
|
|
86
86
|
"repository": {
|
|
87
87
|
"type": "git",
|