@vectojs/core 1.19.0 → 1.21.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/{chunk-IA3KW4CG.js → chunk-AGP4VLF4.js} +75 -25
- package/dist/{chunk-XWBVBXFZ.mjs → chunk-FRMLD4PP.mjs} +50 -0
- package/dist/{chunk-L4SWVP2H.js → chunk-GKSCJ6AF.js} +201 -9
- package/dist/{chunk-QS3CUV7H.mjs → chunk-RTENOAYT.mjs} +192 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +542 -228
- package/dist/index.mjs +335 -21
- package/dist/renderer/CanvasRenderer.d.ts +2 -0
- package/dist/renderer/GlyphRasterAtlas.d.ts +186 -0
- package/dist/renderer/IRenderer.d.ts +31 -0
- package/dist/renderer/index.d.ts +1 -0
- package/dist/renderer.js +4 -2
- package/dist/renderer.mjs +3 -1
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Entity.d.ts +144 -0
- package/dist/tree/Scene.d.ts +179 -0
- package/package.json +1 -1
package/dist/tree/Scene.d.ts
CHANGED
|
@@ -17,6 +17,61 @@ import { type CoreWasmRuntime } from '../wasm/runtime';
|
|
|
17
17
|
import { type HitModuleSource, type HitTestBackend } from '../wasm/hit-backend';
|
|
18
18
|
import { type AnimModuleSource, type AnimBackend } from '../wasm/anim-backend';
|
|
19
19
|
import { type ParticleModuleSource, type ParticleBackend } from '../wasm/particle-backend';
|
|
20
|
+
/**
|
|
21
|
+
* A timed phase of a frame.
|
|
22
|
+
*
|
|
23
|
+
* `render` is the ENCLOSING phase — it contains `transform`, `drawWalk` and
|
|
24
|
+
* `flush` — so it is reported without a share to avoid double-counting.
|
|
25
|
+
* `a11ySync` and `a11yOrder` run after `render` in the frame loop, so they are
|
|
26
|
+
* siblings of it, not children.
|
|
27
|
+
*/
|
|
28
|
+
export type RenderPhase = 'render' | 'transform' | 'drawWalk' | 'flush' | 'a11ySync'
|
|
29
|
+
/**
|
|
30
|
+
* Time inside {@link Scene.syncContentGridProjection} materializing DOM
|
|
31
|
+
* carriers, nested inside `a11ySync`.
|
|
32
|
+
*
|
|
33
|
+
* Split out because `a11ySync` for a streaming code block measured 1661-1875 ms
|
|
34
|
+
* against a 210-671 ms render, and attributing that to grid materialization was
|
|
35
|
+
* an assumption. Nothing should be optimised here on the strength of the parent
|
|
36
|
+
* phase alone.
|
|
37
|
+
*/
|
|
38
|
+
| 'gridMaterialize'
|
|
39
|
+
/**
|
|
40
|
+
* Whole of {@link Scene.syncContentProjection}, nested inside `a11ySync`.
|
|
41
|
+
*
|
|
42
|
+
* Measured at 99.8-99.9% of `a11ySync` for a streaming code block, so per-node
|
|
43
|
+
* a11y attribute and geometry work is not where that phase's cost lives.
|
|
44
|
+
*/
|
|
45
|
+
| 'contentProjection'
|
|
46
|
+
/** Per-node a11y attribute/geometry work, excluding content projection and descendants. */
|
|
47
|
+
| 'a11yNodes'
|
|
48
|
+
/** Whole of `syncContentGridProjection`, of which `gridMaterialize` is one part. */
|
|
49
|
+
| 'gridSync'
|
|
50
|
+
/**
|
|
51
|
+
* Synchronous part of `scheduleContentGridCalibration` — building the probe DOM.
|
|
52
|
+
*
|
|
53
|
+
* The measurement itself is deferred to a rAF, but the probe is constructed
|
|
54
|
+
* here. Measured at 77-80% of `gridSync` on Chrome (3.7-4.5 ms per frame, i.e.
|
|
55
|
+
* the entire 240Hz budget) against about 1 ms on Firefox, making it the largest
|
|
56
|
+
* remaining cost of projecting a streaming code block once carrier reuse landed.
|
|
57
|
+
*/
|
|
58
|
+
| 'gridCalibrateSchedule'
|
|
59
|
+
/** The `querySelectorAll` + per-cell scan inside calibration scheduling. */
|
|
60
|
+
| 'calibScan'
|
|
61
|
+
/** Probe DOM construction and insertion inside calibration scheduling. */
|
|
62
|
+
| 'calibProbeBuild' | 'a11yOrder'
|
|
63
|
+
/** Sum of every entity's own render(), nested inside drawWalk. */
|
|
64
|
+
| 'entityPaint';
|
|
65
|
+
export interface RenderPhaseEntry {
|
|
66
|
+
phase: RenderPhase;
|
|
67
|
+
totalMs: number;
|
|
68
|
+
calls: number;
|
|
69
|
+
avgMs: number;
|
|
70
|
+
/** Worst single sample — a spiky phase is a different problem from a slow one. */
|
|
71
|
+
maxMs: number;
|
|
72
|
+
/** Percent of the measured total, or `null` for the enclosing `render` phase. */
|
|
73
|
+
share: number | null;
|
|
74
|
+
}
|
|
20
75
|
/**
|
|
21
76
|
* Who marked the scene dirty, and why.
|
|
22
77
|
*
|
|
@@ -230,6 +285,43 @@ export declare class Scene {
|
|
|
230
285
|
renderMode: 'always' | 'onDemand';
|
|
231
286
|
/** Cap on distinct recorded dirty reasons (see `recordDirtyReason`). */
|
|
232
287
|
private static readonly MAX_DIRTY_REASONS;
|
|
288
|
+
private _phaseTiming;
|
|
289
|
+
private _phaseTotals;
|
|
290
|
+
/**
|
|
291
|
+
* Start or stop per-phase render timing.
|
|
292
|
+
*
|
|
293
|
+
* Off by default, and the probes compile to a single boolean test when off:
|
|
294
|
+
* these sit on the frame path, so the disabled cost has to be nothing. Enable,
|
|
295
|
+
* run the scene, then read {@link renderPhases}.
|
|
296
|
+
*
|
|
297
|
+
* Exists because a frame total cannot tell you where the time went. The
|
|
298
|
+
* markdown streaming benchmark put render at 85-99% of an append's cost, and
|
|
299
|
+
* there was no way to decompose that number further — which is exactly the
|
|
300
|
+
* position that led to two wrong optimisation guesses earlier
|
|
301
|
+
* (`CodeBlock` reuse, hit-grid fusion), both of which measured as no change.
|
|
302
|
+
*/
|
|
303
|
+
setPhaseTiming(enabled: boolean): void;
|
|
304
|
+
/** Whether per-phase render timing is being recorded. */
|
|
305
|
+
get phaseTiming(): boolean;
|
|
306
|
+
/**
|
|
307
|
+
* Accumulate one phase sample.
|
|
308
|
+
*
|
|
309
|
+
* Totals rather than a per-frame log: the question is always "which phase owns
|
|
310
|
+
* the frame", and a log of thousands of samples answers it less directly while
|
|
311
|
+
* costing far more memory. `maxMs` is kept because a phase that is cheap on
|
|
312
|
+
* average but spikes is a different problem from one that is uniformly slow.
|
|
313
|
+
*/
|
|
314
|
+
private _recordPhase;
|
|
315
|
+
/**
|
|
316
|
+
* Recorded phase timings, most expensive first, with each phase's share of the
|
|
317
|
+
* measured total.
|
|
318
|
+
*
|
|
319
|
+
* `share` is the number that matters: a phase at 4% cannot be worth optimising
|
|
320
|
+
* however inefficient it looks in isolation.
|
|
321
|
+
*/
|
|
322
|
+
get renderPhases(): RenderPhaseEntry[];
|
|
323
|
+
/** Drop recorded phase timings, keeping timing enabled. */
|
|
324
|
+
clearRenderPhases(): void;
|
|
233
325
|
private _dirtyTracking;
|
|
234
326
|
private _dirtyReasons;
|
|
235
327
|
private dirty;
|
|
@@ -300,6 +392,29 @@ export declare class Scene {
|
|
|
300
392
|
private contentGridCalibrationFrames;
|
|
301
393
|
/** Detached, untransformed font probes used by the cold calibration pass. */
|
|
302
394
|
private contentGridCalibrationProbes;
|
|
395
|
+
/**
|
|
396
|
+
* Monotonic stamp identifying the conditions grid cells were calibrated under.
|
|
397
|
+
*
|
|
398
|
+
* Calibration measures the difference between the advance the canvas grid assigns
|
|
399
|
+
* a cluster and the width the browser lays it out at, then writes a per-cell
|
|
400
|
+
* `scaleX`. That result stays valid until the font or the page scale changes, and
|
|
401
|
+
* it lives on the cell element — so a cell carrying this stamp needs no further
|
|
402
|
+
* work.
|
|
403
|
+
*
|
|
404
|
+
* The scan that feeds calibration was O(cells) on every revision bump: for a
|
|
405
|
+
* streaming code block it re-derived a measurement key for every cell in the
|
|
406
|
+
* block each frame in order to produce only ~20 distinct keys, costing about
|
|
407
|
+
* 2.5 ms/frame after the `style.font` fix and still over half of `a11ySync`. Since
|
|
408
|
+
* carrier reuse (#244) leaves untouched lines — and therefore their calibrated
|
|
409
|
+
* transforms — in place, cells stamped with the current generation can simply be
|
|
410
|
+
* skipped, making the scan O(new cells) instead.
|
|
411
|
+
*
|
|
412
|
+
* A plain incrementing integer rather than the descriptive calibration key,
|
|
413
|
+
* because it goes into an attribute selector and must not need escaping.
|
|
414
|
+
*/
|
|
415
|
+
private contentGridCalibrationGeneration;
|
|
416
|
+
/** The `(fontEpoch, pageScale)` pair the current generation corresponds to. */
|
|
417
|
+
private contentGridCalibrationStamp;
|
|
303
418
|
/** Invalidates grid font calibration after browser font availability changes. */
|
|
304
419
|
private contentFontEpoch;
|
|
305
420
|
/** Cached Canvas-to-client scale for the current font/viewport epoch. */
|
|
@@ -742,6 +857,15 @@ export declare class Scene {
|
|
|
742
857
|
*/
|
|
743
858
|
private setupGLContextRecovery;
|
|
744
859
|
private endContentSelectionDrag;
|
|
860
|
+
/**
|
|
861
|
+
* Index of the carrier line currently holding a selection inside `el`, or
|
|
862
|
+
* `null`.
|
|
863
|
+
*
|
|
864
|
+
* Lets a partial re-materialization decide whether the user's selection is even
|
|
865
|
+
* affected. Checks the tracked anchor first (it survives a drag) and falls back
|
|
866
|
+
* to the live DOM selection.
|
|
867
|
+
*/
|
|
868
|
+
private contentGridSelectionLine;
|
|
745
869
|
private releaseContentSelectionForRebuild;
|
|
746
870
|
/**
|
|
747
871
|
* Rebuild a content-projection element's DOM (`rebuild`) while preserving a
|
|
@@ -778,7 +902,26 @@ export declare class Scene {
|
|
|
778
902
|
* @example scene.add(new CircleEntity());
|
|
779
903
|
*/
|
|
780
904
|
add(entity: Entity): this;
|
|
905
|
+
/**
|
|
906
|
+
* Reset per-grid calibration and bookkeeping before a (re)materialization.
|
|
907
|
+
*
|
|
908
|
+
* @param entityId - Owning entity, keyed into the calibration maps.
|
|
909
|
+
* @param el - The projection element.
|
|
910
|
+
* @param releaseSelection - Whether to drop a selection this element owns.
|
|
911
|
+
* Pass `false` when carrier lines are being reused: the selection's DOM nodes
|
|
912
|
+
* survive the pass, so tearing it down would wipe a user's selection on every
|
|
913
|
+
* streamed chunk — the exact bug `preserveContentSelectionAcrossRebuild`
|
|
914
|
+
* exists to prevent on the non-grid path.
|
|
915
|
+
*/
|
|
781
916
|
private clearContentGridState;
|
|
917
|
+
/**
|
|
918
|
+
* Drop any projected elements under `node` without touching the entity tree.
|
|
919
|
+
*
|
|
920
|
+
* Used when the walk reaches an invisible subtree: the entities stay put (a
|
|
921
|
+
* later `show()` re-projects them), but nothing under here may remain
|
|
922
|
+
* focusable or announced while hidden.
|
|
923
|
+
*/
|
|
924
|
+
private pruneA11ySubtree;
|
|
782
925
|
private removeA11yRecursively;
|
|
783
926
|
/**
|
|
784
927
|
* If `el` is about to be removed from the DOM while it holds browser focus,
|
|
@@ -867,6 +1010,26 @@ export declare class Scene {
|
|
|
867
1010
|
get rootEntity(): Entity;
|
|
868
1011
|
/** The overlay layer root (see {@link showOverlay}), read-only for tooling. */
|
|
869
1012
|
get overlayRootEntity(): Entity;
|
|
1013
|
+
/**
|
|
1014
|
+
* Advance and render exactly one frame, synchronously.
|
|
1015
|
+
*
|
|
1016
|
+
* This renders UNCONDITIONALLY: it consults neither {@link renderMode} nor
|
|
1017
|
+
* {@link dirty}, and it does not apply the `always`-mode idle auto-throttle.
|
|
1018
|
+
* That is deliberate — a deterministic driver (video export, a test, a
|
|
1019
|
+
* fixed-step benchmark) asks for a frame because it wants that frame, not a
|
|
1020
|
+
* scheduler opinion about whether it is needed.
|
|
1021
|
+
*
|
|
1022
|
+
* The consequence is a measurement footgun worth stating explicitly: a
|
|
1023
|
+
* benchmark that drives frames through `step()` CANNOT observe frame skipping,
|
|
1024
|
+
* so `always` and `onDemand` produce byte-identical draw counts through this
|
|
1025
|
+
* path. An investigation into whether `onDemand` skips redundant repaints once
|
|
1026
|
+
* concluded "it does not" on exactly that basis; on the real rAF loop the same
|
|
1027
|
+
* workload rendered ~1.0 frames per content change. To measure anything about
|
|
1028
|
+
* scheduling, use {@link start} and let `requestAnimationFrame` drive.
|
|
1029
|
+
*
|
|
1030
|
+
* @param dt Seconds to advance. Not clamped by `MAX_FRAME_DT` — the caller
|
|
1031
|
+
* chooses the step, since determinism is the point.
|
|
1032
|
+
*/
|
|
870
1033
|
step(dt: number): void;
|
|
871
1034
|
/**
|
|
872
1035
|
* Mark the scene as needing a redraw on the next frame.
|
|
@@ -875,6 +1038,22 @@ export declare class Scene {
|
|
|
875
1038
|
* entity state outside of {@link Entity.animate} so the change is rendered.
|
|
876
1039
|
*/
|
|
877
1040
|
markDirty(source?: DirtySource): void;
|
|
1041
|
+
/**
|
|
1042
|
+
* Increments whenever the tree's shape changes: add, remove or reparent.
|
|
1043
|
+
*
|
|
1044
|
+
* Already maintained for the resident WASM transform store (see
|
|
1045
|
+
* {@link markStructureChanged}, called from `Entity.add`/`remove`), and exposed
|
|
1046
|
+
* here because a cache of the tree's shape — a DevTools tree model, a serialized
|
|
1047
|
+
* snapshot — is valid exactly as long as this value is unchanged. Comparing it is
|
|
1048
|
+
* O(1) against re-walking the tree, which is what it replaces: DevTools rebuilt
|
|
1049
|
+
* both trees on a fixed 500 ms interval, a constant cost proportional to entity
|
|
1050
|
+
* count, purely because it had no way to ask whether the shape had changed.
|
|
1051
|
+
*
|
|
1052
|
+
* Property changes do NOT bump it. Moving or restyling an entity leaves the
|
|
1053
|
+
* shape intact, so a consumer that also cares about values must read those
|
|
1054
|
+
* directly rather than rebuilding a tree.
|
|
1055
|
+
*/
|
|
1056
|
+
get structureVersion(): number;
|
|
878
1057
|
/**
|
|
879
1058
|
* Record who marked the scene dirty and why.
|
|
880
1059
|
*
|