@vectojs/core 1.35.0 → 1.35.2
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-IFSFHYF7.mjs → chunk-25PS5K7R.mjs} +35 -15
- package/dist/{chunk-TV4ARAC5.mjs → chunk-CHDJEHML.mjs} +2 -0
- package/dist/{chunk-M73XB4ZK.js → chunk-MO24PVGT.js} +42 -22
- package/dist/{chunk-XXU56ZNA.js → chunk-XHPUCU5P.js} +32 -30
- package/dist/components/SplineEntity.d.ts +6 -0
- package/dist/index.js +214 -116
- package/dist/index.mjs +161 -63
- package/dist/renderer/WebGLPointRenderer.d.ts +3 -3
- package/dist/renderer/colorParse.d.ts +2 -2
- package/dist/renderer/url.d.ts +4 -2
- package/dist/renderer.js +2 -2
- package/dist/renderer.mjs +1 -1
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Entity.d.ts +15 -0
- package/dist/tree/Scene.d.ts +14 -13
- package/dist/wasm/anim-backend.d.ts +13 -3
- package/dist/wasm/backend.d.ts +13 -0
- package/dist/wasm/hit-backend.d.ts +11 -4
- package/dist/wasm/particle-backend.d.ts +6 -0
- package/dist/wasm/vectojs_core.wasm +0 -0
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
parseColorToRGBA,
|
|
12
12
|
sanitizeUrl,
|
|
13
13
|
setRendererDevMode
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-25PS5K7R.mjs";
|
|
15
15
|
import {
|
|
16
16
|
Entity,
|
|
17
17
|
MSDFTextEntity,
|
|
18
18
|
SVGEntity,
|
|
19
19
|
VectoJSEvent,
|
|
20
20
|
contentLineInHint
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-CHDJEHML.mjs";
|
|
22
22
|
|
|
23
23
|
// src/tree/ComputeParticleEntity.ts
|
|
24
24
|
var PARTICLE_STRIDE_FLOATS = 8;
|
|
@@ -316,6 +316,7 @@ var ComputeParticleEntity = class extends Entity {
|
|
|
316
316
|
stepWithBackend(backend, dt, mouseX, mouseY, width, height) {
|
|
317
317
|
const count = this.maxParticles;
|
|
318
318
|
backend.ensure(count);
|
|
319
|
+
backend.revalidateViews();
|
|
319
320
|
backend.gather(this.particleData, count, true);
|
|
320
321
|
const pending = backend.step(count, {
|
|
321
322
|
dt,
|
|
@@ -1325,6 +1326,7 @@ var DriverTicker = class {
|
|
|
1325
1326
|
tP.length = tweenCount;
|
|
1326
1327
|
tD.length = tweenCount;
|
|
1327
1328
|
backend.ensure(springCount, tweenCount);
|
|
1329
|
+
backend.revalidateViews();
|
|
1328
1330
|
if (springCount > 0) {
|
|
1329
1331
|
const sv = backend.springView();
|
|
1330
1332
|
for (let i = 0; i < springCount; i++) {
|
|
@@ -1593,6 +1595,7 @@ var HitTester = class {
|
|
|
1593
1595
|
gathered = gatherHitAABBs(this.root, frame);
|
|
1594
1596
|
}
|
|
1595
1597
|
backend.ensure(gathered.count, width, height, 64);
|
|
1598
|
+
backend.revalidateViews();
|
|
1596
1599
|
const view = backend.inputView();
|
|
1597
1600
|
view.minx.set(gathered.minx.subarray(0, gathered.count));
|
|
1598
1601
|
view.miny.set(gathered.miny.subarray(0, gathered.count));
|
|
@@ -2123,10 +2126,15 @@ var ContentProjectionManager = class {
|
|
|
2123
2126
|
}
|
|
2124
2127
|
if (this.phases.enabled) this.phases.record("calibScan", performance.now() - scanStart);
|
|
2125
2128
|
if (measurements.length === 0) {
|
|
2126
|
-
el.dataset.vectoGridCalibration = calibrationKey;
|
|
2127
|
-
el.dataset.vectoGridReady = "true";
|
|
2128
2129
|
el.dataset.vectoGridCalibrationSamples = "0";
|
|
2129
2130
|
delete el.dataset.vectoGridCalibrationPending;
|
|
2131
|
+
const readyFrame = requestAnimationFrame(() => {
|
|
2132
|
+
this.calibrationFrameHandles.delete(entityId);
|
|
2133
|
+
if (!el.isConnected) return;
|
|
2134
|
+
el.dataset.vectoGridCalibration = calibrationKey;
|
|
2135
|
+
el.dataset.vectoGridReady = "true";
|
|
2136
|
+
});
|
|
2137
|
+
this.calibrationFrameHandles.set(entityId, readyFrame);
|
|
2130
2138
|
return;
|
|
2131
2139
|
}
|
|
2132
2140
|
const appendStart = this.phases.enabled ? performance.now() : 0;
|
|
@@ -2369,8 +2377,15 @@ var WASM_STATUS = {
|
|
|
2369
2377
|
/** A kernel ran before `init`. */
|
|
2370
2378
|
UNINITIALIZED: 2,
|
|
2371
2379
|
/** A sibling run addressed a slot or parent outside the store. */
|
|
2372
|
-
BAD_RUN: 3
|
|
2380
|
+
BAD_RUN: 3,
|
|
2381
|
+
/** A requested capacity's size arithmetic overflowed; the previous store
|
|
2382
|
+
* was left untouched. */
|
|
2383
|
+
OVERFLOW: 4
|
|
2373
2384
|
};
|
|
2385
|
+
function viewsStale(cap, probe, memory) {
|
|
2386
|
+
if (cap === 0) return false;
|
|
2387
|
+
return probe.byteLength === 0 || probe.buffer !== memory.buffer;
|
|
2388
|
+
}
|
|
2374
2389
|
var PAD2 = 8;
|
|
2375
2390
|
var WasmTransformBackend = class {
|
|
2376
2391
|
available = true;
|
|
@@ -2560,9 +2575,15 @@ var WasmTransformBackend = class {
|
|
|
2560
2575
|
}
|
|
2561
2576
|
ensure(count, runCount) {
|
|
2562
2577
|
if (count + PAD2 <= this.cap && runCount <= this.runCap) return;
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
this.ex.init(count,
|
|
2578
|
+
const cap = count + PAD2;
|
|
2579
|
+
const runCap = Math.max(runCount, count, 1);
|
|
2580
|
+
const status = this.ex.init(count, runCap);
|
|
2581
|
+
if (status !== WASM_STATUS.OK) {
|
|
2582
|
+
this.lastStatus = status;
|
|
2583
|
+
return;
|
|
2584
|
+
}
|
|
2585
|
+
this.cap = cap;
|
|
2586
|
+
this.runCap = runCap;
|
|
2566
2587
|
this.refreshViews();
|
|
2567
2588
|
}
|
|
2568
2589
|
/** Rebuild typed-array views after an init() (which may have grown, and thus
|
|
@@ -2630,11 +2651,26 @@ var AnimBackend = class {
|
|
|
2630
2651
|
*/
|
|
2631
2652
|
ensure(springCount, tweenCount) {
|
|
2632
2653
|
if (springCount + PAD3 <= this.springCap && tweenCount + PAD3 <= this.tweenCap) return;
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
this.ex.anim_init(
|
|
2654
|
+
const springCap = springCount + PAD3;
|
|
2655
|
+
const tweenCap = tweenCount + PAD3;
|
|
2656
|
+
const status = this.ex.anim_init(springCap, tweenCap);
|
|
2657
|
+
if (status !== WASM_STATUS.OK) {
|
|
2658
|
+
this.lastStatus = status;
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
this.springCap = springCap;
|
|
2662
|
+
this.tweenCap = tweenCap;
|
|
2636
2663
|
this.refreshViews();
|
|
2637
2664
|
}
|
|
2665
|
+
/**
|
|
2666
|
+
* Re-create the typed-array views if another backend's allocation grew the
|
|
2667
|
+
* shared linear memory and detached them (see {@link viewsStale}). Call after
|
|
2668
|
+
* {@link ensure} and before writing into {@link springView}/{@link tweenView}.
|
|
2669
|
+
*/
|
|
2670
|
+
revalidateViews() {
|
|
2671
|
+
if (this.springCap === 0) return;
|
|
2672
|
+
if (viewsStale(this.springCap, this.sv.val, this.ex.memory)) this.refreshViews();
|
|
2673
|
+
}
|
|
2638
2674
|
/**
|
|
2639
2675
|
* Advance `count` springs (from index 0) by `dtMs` milliseconds, in place.
|
|
2640
2676
|
* Returns `true` when the kernel ran. `false` means it rejected the call
|
|
@@ -2728,15 +2764,26 @@ var HitTestBackend = class {
|
|
|
2728
2764
|
this.gridH = gh;
|
|
2729
2765
|
this.cellSize = cellSize;
|
|
2730
2766
|
}
|
|
2767
|
+
/**
|
|
2768
|
+
* Re-create the typed-array views if another backend's allocation grew the
|
|
2769
|
+
* shared linear memory and detached them (see {@link viewsStale}). Call after
|
|
2770
|
+
* {@link ensure} and before writing into {@link inputView}.
|
|
2771
|
+
*/
|
|
2772
|
+
revalidateViews() {
|
|
2773
|
+
if (this.entityCap === 0) return;
|
|
2774
|
+
if (viewsStale(this.entityCap, this.vminx, this.ex.memory)) this.refreshViews();
|
|
2775
|
+
}
|
|
2731
2776
|
/**
|
|
2732
2777
|
* Run the kernel's bucketing over whatever is currently resident in
|
|
2733
2778
|
* {@link inputView} (write the AABBs there, and call {@link ensure} first).
|
|
2734
|
-
* Returns `false` if the build
|
|
2735
|
-
* not trust {@link candidatesAt} results
|
|
2736
|
-
* to the JS walk instead (never return a
|
|
2779
|
+
* Returns `false` if the build was rejected (no `hit_init` yet) or overflowed
|
|
2780
|
+
* its item budget — the caller must not trust {@link candidatesAt} results
|
|
2781
|
+
* for this build and should fall back to the JS walk instead (never return a
|
|
2782
|
+
* wrong hit).
|
|
2737
2783
|
*/
|
|
2738
2784
|
runBuild(count, vw, vh, cellSize) {
|
|
2739
|
-
this.ex.hit_build(count, vw, vh, cellSize);
|
|
2785
|
+
const status = this.ex.hit_build(count, vw, vh, cellSize);
|
|
2786
|
+
if (status !== WASM_STATUS.OK) return false;
|
|
2740
2787
|
return this.ex.hit_overflow() === 0;
|
|
2741
2788
|
}
|
|
2742
2789
|
/**
|
|
@@ -2762,10 +2809,16 @@ var HitTestBackend = class {
|
|
|
2762
2809
|
if (count + PAD4 <= this.entityCap && cellCount <= this.cellCap && itemCount <= this.itemCap) {
|
|
2763
2810
|
return;
|
|
2764
2811
|
}
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
this.ex.hit_init(count,
|
|
2812
|
+
const entityCap = count + PAD4;
|
|
2813
|
+
const cellCap = Math.max(cellCount, 1);
|
|
2814
|
+
const itemCap = Math.max(itemCount, count, 1);
|
|
2815
|
+
const status = this.ex.hit_init(count, cellCap, itemCap);
|
|
2816
|
+
if (status !== WASM_STATUS.OK) {
|
|
2817
|
+
return;
|
|
2818
|
+
}
|
|
2819
|
+
this.entityCap = entityCap;
|
|
2820
|
+
this.cellCap = cellCap;
|
|
2821
|
+
this.itemCap = itemCap;
|
|
2769
2822
|
this.refreshViews();
|
|
2770
2823
|
}
|
|
2771
2824
|
/** Rebuild typed-array views after a growing `hit_init` (which detaches the
|
|
@@ -2804,10 +2857,24 @@ var ParticleBackend = class {
|
|
|
2804
2857
|
*/
|
|
2805
2858
|
ensure(count) {
|
|
2806
2859
|
if (count + PAD5 <= this.cap) return;
|
|
2807
|
-
|
|
2808
|
-
this.ex.particle_init(
|
|
2860
|
+
const cap = count + PAD5;
|
|
2861
|
+
const status = this.ex.particle_init(cap);
|
|
2862
|
+
if (status !== WASM_STATUS.OK) {
|
|
2863
|
+
this.lastStatus = status;
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
this.cap = cap;
|
|
2809
2867
|
this.refreshViews();
|
|
2810
2868
|
}
|
|
2869
|
+
/**
|
|
2870
|
+
* Re-create the typed-array views if another backend's allocation grew the
|
|
2871
|
+
* shared linear memory and detached them (see {@link viewsStale}). Call after
|
|
2872
|
+
* {@link ensure} and before {@link gather}/{@link scatter}.
|
|
2873
|
+
*/
|
|
2874
|
+
revalidateViews() {
|
|
2875
|
+
if (this.cap === 0) return;
|
|
2876
|
+
if (viewsStale(this.cap, this.view.px, this.ex.memory)) this.refreshViews();
|
|
2877
|
+
}
|
|
2811
2878
|
/**
|
|
2812
2879
|
* Advance `count` particles one step in place. Returns `true` when at least
|
|
2813
2880
|
* one live particle is still moving or off-origin beyond epsilon (the fused
|
|
@@ -3074,9 +3141,17 @@ var WasmBackendFacade = class {
|
|
|
3074
3141
|
* main render walk onto it; passing `null` reverts to the JS path.
|
|
3075
3142
|
*/
|
|
3076
3143
|
setTransform(backend) {
|
|
3144
|
+
const backendChanged = backend !== this._transform && this._transform !== null;
|
|
3077
3145
|
this._transform = backend;
|
|
3078
3146
|
this._mode = backend ? "wasm" : "js";
|
|
3079
3147
|
this._uploadRejections = 0;
|
|
3148
|
+
if (backendChanged) {
|
|
3149
|
+
this._treeStore = null;
|
|
3150
|
+
this._storeStructureVersion = -1;
|
|
3151
|
+
this._inputs = null;
|
|
3152
|
+
this._world = null;
|
|
3153
|
+
this._aabbsFresh = false;
|
|
3154
|
+
}
|
|
3080
3155
|
}
|
|
3081
3156
|
// ── The shared runtime ──────────────────────────────────────────────────────
|
|
3082
3157
|
/**
|
|
@@ -3244,6 +3319,7 @@ var WasmBackendFacade = class {
|
|
|
3244
3319
|
}
|
|
3245
3320
|
if (!backend.uploadRuns(built.store)) {
|
|
3246
3321
|
this.transformReason = "rejected";
|
|
3322
|
+
this._aabbsFresh = false;
|
|
3247
3323
|
this._uploadRejections++;
|
|
3248
3324
|
if (this._uploadRejections >= WASM_UPLOAD_REJECT_LIMIT) {
|
|
3249
3325
|
this._mode = "js";
|
|
@@ -3283,6 +3359,7 @@ var WasmBackendFacade = class {
|
|
|
3283
3359
|
}
|
|
3284
3360
|
if (backend.runKernel("simd") !== WASM_STATUS.OK) {
|
|
3285
3361
|
this.transformReason = "rejected";
|
|
3362
|
+
this._aabbsFresh = false;
|
|
3286
3363
|
return null;
|
|
3287
3364
|
}
|
|
3288
3365
|
this._aabbsFresh = false;
|
|
@@ -4468,23 +4545,23 @@ var Scene = class _Scene {
|
|
|
4468
4545
|
}
|
|
4469
4546
|
/**
|
|
4470
4547
|
* Drop `entity` and its whole subtree from the batched-driver candidate set.
|
|
4471
|
-
* Called by
|
|
4472
|
-
* removed-but-still-animating entity stays pinned in
|
|
4473
|
-
* drivers keep ticking every frame even though it is
|
|
4474
|
-
* re-added, {@link
|
|
4475
|
-
* still has live drivers, so the motion resumes.
|
|
4548
|
+
* Called by `Entity.remove` on detach (which `remove`/`hideOverlay` route
|
|
4549
|
+
* through): without this a removed-but-still-animating entity stays pinned in
|
|
4550
|
+
* the Set (a leak) and its drivers keep ticking every frame even though it is
|
|
4551
|
+
* off-tree. If it is later re-added, {@link _registerActiveDriverSubtree}
|
|
4552
|
+
* re-registers any node that still has live drivers, so the motion resumes.
|
|
4476
4553
|
*/
|
|
4477
|
-
|
|
4554
|
+
_unregisterActiveDriverSubtree(entity) {
|
|
4478
4555
|
this._driverTicker.unregisterSubtree(entity);
|
|
4479
4556
|
}
|
|
4480
4557
|
/**
|
|
4481
4558
|
* Re-register every node in `entity`'s subtree that still has live property
|
|
4482
|
-
* drivers. Called by
|
|
4483
|
-
*
|
|
4484
|
-
*
|
|
4485
|
-
*
|
|
4559
|
+
* drivers. Called by `Entity._addOne` so re-attaching a subtree that was
|
|
4560
|
+
* removed mid-animation resumes its batched drivers (they were dropped from
|
|
4561
|
+
* the candidate set on removal, but the driver state still lives on each
|
|
4562
|
+
* entity).
|
|
4486
4563
|
*/
|
|
4487
|
-
|
|
4564
|
+
_registerActiveDriverSubtree(entity) {
|
|
4488
4565
|
this._driverTicker.registerSubtree(entity);
|
|
4489
4566
|
}
|
|
4490
4567
|
// --- domain: render-scheduler — batched driver tick (extraction 6), reads the anim backend ---
|
|
@@ -5021,7 +5098,6 @@ var Scene = class _Scene {
|
|
|
5021
5098
|
*/
|
|
5022
5099
|
add(entity) {
|
|
5023
5100
|
this.root.add(entity);
|
|
5024
|
-
this.registerActiveDriverSubtree(entity);
|
|
5025
5101
|
return this;
|
|
5026
5102
|
}
|
|
5027
5103
|
// --- domain: a11y-projection — subtree pruning, focus preservation on removal ---
|
|
@@ -5100,7 +5176,6 @@ var Scene = class _Scene {
|
|
|
5100
5176
|
remove(entity) {
|
|
5101
5177
|
this.root.remove(entity);
|
|
5102
5178
|
this.removeA11yRecursively(entity);
|
|
5103
|
-
this.unregisterActiveDriverSubtree(entity);
|
|
5104
5179
|
return this;
|
|
5105
5180
|
}
|
|
5106
5181
|
/**
|
|
@@ -5132,7 +5207,6 @@ var Scene = class _Scene {
|
|
|
5132
5207
|
*/
|
|
5133
5208
|
showOverlay(overlay) {
|
|
5134
5209
|
this.overlayRoot.add(overlay);
|
|
5135
|
-
this.registerActiveDriverSubtree(overlay);
|
|
5136
5210
|
this.markDirty();
|
|
5137
5211
|
}
|
|
5138
5212
|
/**
|
|
@@ -5141,7 +5215,6 @@ var Scene = class _Scene {
|
|
|
5141
5215
|
hideOverlay(overlay) {
|
|
5142
5216
|
this.overlayRoot.remove(overlay);
|
|
5143
5217
|
this.removeA11yRecursively(overlay);
|
|
5144
|
-
this.unregisterActiveDriverSubtree(overlay);
|
|
5145
5218
|
this.markDirty();
|
|
5146
5219
|
}
|
|
5147
5220
|
destroyEntitySubtree(entity) {
|
|
@@ -5385,8 +5458,9 @@ var Scene = class _Scene {
|
|
|
5385
5458
|
* workload rendered ~1.0 frames per content change. To measure anything about
|
|
5386
5459
|
* scheduling, use {@link start} and let `requestAnimationFrame` drive.
|
|
5387
5460
|
*
|
|
5388
|
-
* @param dt
|
|
5389
|
-
*
|
|
5461
|
+
* @param dt Milliseconds to advance (same unit as the rAF timestamps `loop`
|
|
5462
|
+
* feeds `render`). Not clamped by `MAX_FRAME_DT` — the caller chooses the
|
|
5463
|
+
* step, since determinism is the point.
|
|
5390
5464
|
*/
|
|
5391
5465
|
step(dt) {
|
|
5392
5466
|
const time = this.lastTime + dt;
|
|
@@ -6329,26 +6403,43 @@ var Scene = class _Scene {
|
|
|
6329
6403
|
if (mctx) {
|
|
6330
6404
|
mctx.font = lineFont;
|
|
6331
6405
|
const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6406
|
+
if (line.shapedPaint) {
|
|
6407
|
+
const segments = [...segmenter.segment(line.text)];
|
|
6408
|
+
let runningX = 0;
|
|
6409
|
+
for (let segIdx = 0; segIdx < segments.length; segIdx++) {
|
|
6410
|
+
const { segment, index: index2 } = segments[segIdx];
|
|
6411
|
+
const nextIndex = segIdx + 1 < segments.length ? segments[segIdx + 1].index : line.text.length;
|
|
6412
|
+
const canvasX = mctx.measureText(line.text.slice(0, index2)).width;
|
|
6413
|
+
const canvasWidth = mctx.measureText(line.text.slice(0, nextIndex)).width - canvasX;
|
|
6414
|
+
const carrierEl = document.createElement("span");
|
|
6415
|
+
carrierEl.textContent = segment;
|
|
6416
|
+
carrierEl.style.position = "relative";
|
|
6417
|
+
carrierEl.style.display = "inline-block";
|
|
6418
|
+
carrierEl.style.left = `${canvasX - runningX}px`;
|
|
6419
|
+
carrierEl.style.width = `${canvasWidth}px`;
|
|
6420
|
+
carrierEl.style.boxSizing = "border-box";
|
|
6421
|
+
carrierEl.style.lineHeight = `${lineHeight2}px`;
|
|
6422
|
+
carrierEl.style.whiteSpace = "pre";
|
|
6423
|
+
carrierEl.style.verticalAlign = "top";
|
|
6424
|
+
carrierEl.style.unicodeBidi = "isolate";
|
|
6425
|
+
lineElement.appendChild(carrierEl);
|
|
6426
|
+
runningX += canvasWidth;
|
|
6427
|
+
}
|
|
6428
|
+
} else {
|
|
6429
|
+
for (const { segment } of segmenter.segment(line.text)) {
|
|
6430
|
+
const canvasWidth = mctx.measureText(segment).width;
|
|
6431
|
+
const carrierEl = document.createElement("span");
|
|
6432
|
+
carrierEl.textContent = segment;
|
|
6433
|
+
carrierEl.style.position = "relative";
|
|
6434
|
+
carrierEl.style.display = "inline-block";
|
|
6435
|
+
carrierEl.style.width = `${canvasWidth}px`;
|
|
6436
|
+
carrierEl.style.boxSizing = "border-box";
|
|
6437
|
+
carrierEl.style.lineHeight = `${lineHeight2}px`;
|
|
6438
|
+
carrierEl.style.whiteSpace = "pre";
|
|
6439
|
+
carrierEl.style.verticalAlign = "top";
|
|
6440
|
+
carrierEl.style.unicodeBidi = "isolate";
|
|
6441
|
+
lineElement.appendChild(carrierEl);
|
|
6442
|
+
}
|
|
6352
6443
|
}
|
|
6353
6444
|
} else {
|
|
6354
6445
|
lineElement.textContent = line.text;
|
|
@@ -6684,7 +6775,7 @@ var Scene = class _Scene {
|
|
|
6684
6775
|
const hasInteractive = this.frameHadInteractive;
|
|
6685
6776
|
const wantsContentSync = this.contentProjectionEnabled;
|
|
6686
6777
|
const shouldSyncInterval = this.a11ySyncInterval <= 0 || time - this.lastA11ySync >= this.a11ySyncInterval;
|
|
6687
|
-
if ((hasInteractive || this.a11yElements.size > 0 || wantsContentSync) && (shouldSyncInterval || this.a11yPendingSyncAfterAnimation)) {
|
|
6778
|
+
if ((hasInteractive || this.a11yElements.size > 0 || wantsContentSync) && (shouldSyncInterval || !hasActiveAnimation && this.a11yPendingSyncAfterAnimation)) {
|
|
6688
6779
|
this.lastA11ySync = time;
|
|
6689
6780
|
if (hasInteractive || wantsContentSync) {
|
|
6690
6781
|
const userTiming = this.phases.userTiming ? beginVectoUserTiming(VECTO_USER_TIMING.scene.a11ySync) : null;
|
|
@@ -7544,7 +7635,7 @@ var GridTextEntity = class extends Entity {
|
|
|
7544
7635
|
updateGrid(ascii) {
|
|
7545
7636
|
this.grid = ascii;
|
|
7546
7637
|
this.rows = ascii.length;
|
|
7547
|
-
this.cols = ascii
|
|
7638
|
+
this.cols = ascii.reduce((widest, row) => Math.max(widest, row?.length ?? 0), 0);
|
|
7548
7639
|
}
|
|
7549
7640
|
isPointInside(_globalX, _globalY) {
|
|
7550
7641
|
return false;
|
|
@@ -7632,6 +7723,12 @@ var SplineEntity = class extends Entity {
|
|
|
7632
7723
|
/** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
|
|
7633
7724
|
bakedWidth = 0;
|
|
7634
7725
|
bakedHeight = 0;
|
|
7726
|
+
/**
|
|
7727
|
+
* Device-pixel ratio the bitmap was rasterized at. Tracked so a DPR change
|
|
7728
|
+
* (browser zoom, a monitor move, a renderer whose clamp changed) re-bakes
|
|
7729
|
+
* instead of blitting a bitmap at the wrong device density forever.
|
|
7730
|
+
*/
|
|
7731
|
+
bakedDPR = 0;
|
|
7635
7732
|
/**
|
|
7636
7733
|
* Gradient strokes can't be baked to a solid-color bitmap; they render
|
|
7637
7734
|
* per-frame. Derived from `_doc`, so it is NOT readonly — assigning a new
|
|
@@ -7855,14 +7952,14 @@ var SplineEntity = class extends Entity {
|
|
|
7855
7952
|
}
|
|
7856
7953
|
}
|
|
7857
7954
|
/** Bake all equations into an OffscreenCanvas once (when available). */
|
|
7858
|
-
bake() {
|
|
7955
|
+
bake(dpr) {
|
|
7859
7956
|
this.baked = true;
|
|
7860
7957
|
const pad = this.lineWidth + 2;
|
|
7861
7958
|
const w = Math.max(1, Math.ceil(this.bounds.width) + pad * 2);
|
|
7862
7959
|
const h = Math.max(1, Math.ceil(this.bounds.height) + pad * 2);
|
|
7863
7960
|
this.bakedWidth = w;
|
|
7864
7961
|
this.bakedHeight = h;
|
|
7865
|
-
|
|
7962
|
+
this.bakedDPR = dpr;
|
|
7866
7963
|
let canvas;
|
|
7867
7964
|
if (typeof OffscreenCanvas !== "undefined") {
|
|
7868
7965
|
canvas = new OffscreenCanvas(w * dpr, h * dpr);
|
|
@@ -7910,7 +8007,8 @@ var SplineEntity = class extends Entity {
|
|
|
7910
8007
|
render(r) {
|
|
7911
8008
|
let rendered = false;
|
|
7912
8009
|
if (this.cache && !this.containsGradient) {
|
|
7913
|
-
|
|
8010
|
+
const dpr = typeof r.pixelRatio === "number" && r.pixelRatio > 0 ? r.pixelRatio : typeof window !== "undefined" && typeof window.devicePixelRatio === "number" ? window.devicePixelRatio || 1 : 1;
|
|
8011
|
+
if (!this.baked || this.bakedDPR !== dpr) this.bake(dpr);
|
|
7914
8012
|
if (this.offscreen) {
|
|
7915
8013
|
const pad = this.lineWidth + 2;
|
|
7916
8014
|
r.drawImage(
|
|
@@ -49,9 +49,9 @@ export interface PointRenderer {
|
|
|
49
49
|
*
|
|
50
50
|
* Batching here is by primitive type — one draw per active type — so draw calls
|
|
51
51
|
* and batches are the same number, and both are bounded at five plus one per
|
|
52
|
-
* mid-frame MSDF
|
|
53
|
-
* only thing worth watching:
|
|
54
|
-
* previous
|
|
52
|
+
* mid-frame atlas switch (MSDF or sprite). Those switches are the only
|
|
53
|
+
* variable term and the only thing worth watching: each forces a commit of
|
|
54
|
+
* glyphs/sprites batched against the previous atlas.
|
|
55
55
|
*/
|
|
56
56
|
stats?(): WebGLDrawStats;
|
|
57
57
|
/** Add one circle in world (CSS-pixel) coordinates; `alpha` multiplies the color's. */
|
|
@@ -8,8 +8,8 @@ export type RGBA = [number, number, number, number];
|
|
|
8
8
|
* Fast paths handle `#rgb`/`#rgba`/`#rrggbb`/`#rrggbbaa` and `rgb()`/`rgba()`;
|
|
9
9
|
* other forms (named colors, `hsl()`, …) resolve via a cached 1×1 canvas when a
|
|
10
10
|
* DOM is available. Results are cached per input string and shared by identity,
|
|
11
|
-
* so callers must treat the returned array as read-only. Unparseable input
|
|
12
|
-
*
|
|
11
|
+
* so callers must treat the returned array as read-only. Unparseable input
|
|
12
|
+
* yields opaque black `[0, 0, 0, 1]` regardless of DOM availability.
|
|
13
13
|
*
|
|
14
14
|
* @param css - A CSS color string, e.g. `'#38bdf8'` or `'rgba(0,0,0,.5)'`.
|
|
15
15
|
* @returns The cached `[r, g, b, a]` tuple in `[0, 1]`.
|
package/dist/renderer/url.d.ts
CHANGED
|
@@ -16,8 +16,10 @@
|
|
|
16
16
|
* 2. Trims leading whitespace (browsers do this before scheme resolution).
|
|
17
17
|
* 3. If the URL is relative (no scheme, or starts with `#`, `?`, `/`, `./`),
|
|
18
18
|
* returns it verbatim — relative navigation is never script-injectable.
|
|
19
|
-
* 4. If the URL parses with a scheme NOT in {@link SAFE_SCHEMES}
|
|
20
|
-
*
|
|
19
|
+
* 4. If the URL parses with a scheme NOT in {@link SAFE_SCHEMES} — after HTML
|
|
20
|
+
* character references are decoded, so an entity-encoded payload cannot
|
|
21
|
+
* smuggle a scheme past the check — returns `'#'` to keep the link non-empty
|
|
22
|
+
* but inert.
|
|
21
23
|
* 5. Otherwise returns the trimmed input unchanged (no canonicalization).
|
|
22
24
|
*
|
|
23
25
|
* The function never throws; malformed input falls back to `'#'`.
|
package/dist/renderer.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
var
|
|
13
|
+
var _chunkMO24PVGTjs = require('./chunk-MO24PVGT.js');
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
@@ -22,4 +22,4 @@ var _chunkM73XB4ZKjs = require('./chunk-M73XB4ZK.js');
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
exports.CanvasRenderer =
|
|
25
|
+
exports.CanvasRenderer = _chunkMO24PVGTjs.CanvasRenderer; exports.GlyphRasterAtlas = _chunkMO24PVGTjs.GlyphRasterAtlas; exports.SVGRenderer = _chunkMO24PVGTjs.SVGRenderer; exports.TextRasterCache = _chunkMO24PVGTjs.TextRasterCache; exports.WebGPUParticleSystemManager = _chunkMO24PVGTjs.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkMO24PVGTjs.createWebGLPointRenderer; exports.installRendererDevTraps = _chunkMO24PVGTjs.installRendererDevTraps; exports.isRendererDevMode = _chunkMO24PVGTjs.isRendererDevMode; exports.parseColorToRGBA = _chunkMO24PVGTjs.parseColorToRGBA; exports.setRendererDevMode = _chunkMO24PVGTjs.setRendererDevMode;
|
package/dist/renderer.mjs
CHANGED
package/dist/text.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunkXHPUCU5Pjs = require('./chunk-XHPUCU5P.js');
|
|
6
6
|
|
|
7
7
|
// src/text/index.ts
|
|
8
8
|
var _text = require('@vectojs/text'); _createStarExport(_text);
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
exports.MSDFTextEntity =
|
|
12
|
+
exports.MSDFTextEntity = _chunkXHPUCU5Pjs.MSDFTextEntity; exports.SVGEntity = _chunkXHPUCU5Pjs.SVGEntity;
|
package/dist/text.mjs
CHANGED
package/dist/tree/Entity.d.ts
CHANGED
|
@@ -116,6 +116,21 @@ export interface ContentProjectionLine {
|
|
|
116
116
|
* hit-mapping when DOM order != visual order (PR #146 revert).
|
|
117
117
|
*/
|
|
118
118
|
perGraphemeCarriers?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* The canvas painted this line's ink as ONE shaped `fillText(line.text)`, so
|
|
121
|
+
* the ink includes the browser's kerning and ligatures. Per-grapheme
|
|
122
|
+
* carriers must then be measured as shaped PREFIX DIFFERENCES of the whole
|
|
123
|
+
* line — isolated grapheme advances would omit exactly the kerning/ligature
|
|
124
|
+
* contraction the paint applied, widening the DOM line past the ink.
|
|
125
|
+
*
|
|
126
|
+
* Leave unset for per-glyph painters (glyphs placed at summed isolated
|
|
127
|
+
* advances, e.g. RichText and TextEntity): there the ink is unkerned, and
|
|
128
|
+
* shaped prefixes would drift the carriers ahead of it by the accumulated
|
|
129
|
+
* kerning delta instead.
|
|
130
|
+
*
|
|
131
|
+
* Only meaningful alongside {@link perGraphemeCarriers}.
|
|
132
|
+
*/
|
|
133
|
+
shapedPaint?: boolean;
|
|
119
134
|
}
|
|
120
135
|
/**
|
|
121
136
|
* Advice from the {@link Scene} about which part of an entity is worth
|
package/dist/tree/Scene.d.ts
CHANGED
|
@@ -936,21 +936,21 @@ export declare class Scene {
|
|
|
936
936
|
_registerActiveDriverEntity(entity: Entity): void;
|
|
937
937
|
/**
|
|
938
938
|
* Drop `entity` and its whole subtree from the batched-driver candidate set.
|
|
939
|
-
* Called by
|
|
940
|
-
* removed-but-still-animating entity stays pinned in
|
|
941
|
-
* drivers keep ticking every frame even though it is
|
|
942
|
-
* re-added, {@link
|
|
943
|
-
* still has live drivers, so the motion resumes.
|
|
939
|
+
* Called by `Entity.remove` on detach (which `remove`/`hideOverlay` route
|
|
940
|
+
* through): without this a removed-but-still-animating entity stays pinned in
|
|
941
|
+
* the Set (a leak) and its drivers keep ticking every frame even though it is
|
|
942
|
+
* off-tree. If it is later re-added, {@link _registerActiveDriverSubtree}
|
|
943
|
+
* re-registers any node that still has live drivers, so the motion resumes.
|
|
944
944
|
*/
|
|
945
|
-
|
|
945
|
+
_unregisterActiveDriverSubtree(entity: Entity): void;
|
|
946
946
|
/**
|
|
947
947
|
* Re-register every node in `entity`'s subtree that still has live property
|
|
948
|
-
* drivers. Called by
|
|
949
|
-
*
|
|
950
|
-
*
|
|
951
|
-
*
|
|
948
|
+
* drivers. Called by `Entity._addOne` so re-attaching a subtree that was
|
|
949
|
+
* removed mid-animation resumes its batched drivers (they were dropped from
|
|
950
|
+
* the candidate set on removal, but the driver state still lives on each
|
|
951
|
+
* entity).
|
|
952
952
|
*/
|
|
953
|
-
|
|
953
|
+
_registerActiveDriverSubtree(entity: Entity): void;
|
|
954
954
|
/**
|
|
955
955
|
* Advance every registered entity's active drivers for this frame, batching
|
|
956
956
|
* whichever are batchable (`SpringDriver`; `TweenDriver` with a named
|
|
@@ -1247,8 +1247,9 @@ export declare class Scene {
|
|
|
1247
1247
|
* workload rendered ~1.0 frames per content change. To measure anything about
|
|
1248
1248
|
* scheduling, use {@link start} and let `requestAnimationFrame` drive.
|
|
1249
1249
|
*
|
|
1250
|
-
* @param dt
|
|
1251
|
-
*
|
|
1250
|
+
* @param dt Milliseconds to advance (same unit as the rAF timestamps `loop`
|
|
1251
|
+
* feeds `render`). Not clamped by `MAX_FRAME_DT` — the caller chooses the
|
|
1252
|
+
* step, since determinism is the point.
|
|
1252
1253
|
*/
|
|
1253
1254
|
step(dt: number): void;
|
|
1254
1255
|
/**
|
|
@@ -7,9 +7,13 @@
|
|
|
7
7
|
* (see `Scene._tickBatchedDrivers`), simply keeps using it.
|
|
8
8
|
*
|
|
9
9
|
* The kernel (`crates/vectojs-core-rs/src/anim.rs`) is bit-identical to
|
|
10
|
-
* `SpringPhysics.update` for springs
|
|
11
|
-
* `
|
|
12
|
-
*
|
|
10
|
+
* `SpringPhysics.update` for springs, and to the JS tweens too since the
|
|
11
|
+
* explicit-multiplication rewrite: `easing.ts` and the kernel's `ease()` both
|
|
12
|
+
* express integer powers as explicit multiplication (no `Math.pow`/`powi`,
|
|
13
|
+
* neither of which is correctly rounded), so the earlier ~1e-12 ULP gap is
|
|
14
|
+
* closed. Batched state still needs re-seeding per frame — `elapsed` is
|
|
15
|
+
* kernel-side state, so every gather re-writes it from the driver rather than
|
|
16
|
+
* letting the kernel's copy drift.
|
|
13
17
|
*
|
|
14
18
|
* Unlike the transform/hit-test stores, this backend holds no cross-frame
|
|
15
19
|
* residency: every qualifying frame re-gathers ALL currently-active batchable
|
|
@@ -56,6 +60,12 @@ export declare class AnimBackend {
|
|
|
56
60
|
* writing first and sizing after would write into a stale buffer.
|
|
57
61
|
*/
|
|
58
62
|
ensure(springCount: number, tweenCount: number): void;
|
|
63
|
+
/**
|
|
64
|
+
* Re-create the typed-array views if another backend's allocation grew the
|
|
65
|
+
* shared linear memory and detached them (see {@link viewsStale}). Call after
|
|
66
|
+
* {@link ensure} and before writing into {@link springView}/{@link tweenView}.
|
|
67
|
+
*/
|
|
68
|
+
revalidateViews(): void;
|
|
59
69
|
/**
|
|
60
70
|
* Advance `count` springs (from index 0) by `dtMs` milliseconds, in place.
|
|
61
71
|
* Returns `true` when the kernel ran. `false` means it rejected the call
|
package/dist/wasm/backend.d.ts
CHANGED
|
@@ -27,7 +27,20 @@ export declare const WASM_STATUS: {
|
|
|
27
27
|
readonly UNINITIALIZED: 2;
|
|
28
28
|
/** A sibling run addressed a slot or parent outside the store. */
|
|
29
29
|
readonly BAD_RUN: 3;
|
|
30
|
+
/** A requested capacity's size arithmetic overflowed; the previous store
|
|
31
|
+
* was left untouched. */
|
|
32
|
+
readonly OVERFLOW: 4;
|
|
30
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* True when a backend's resident typed-array views must be rebuilt: the shared
|
|
36
|
+
* `WebAssembly.Memory` buffer they were constructed over has been detached
|
|
37
|
+
* (reads as `byteLength === 0`) or replaced. Every backend of a Scene shares one
|
|
38
|
+
* instance and therefore one linear memory, so ANY backend's `*_init` can grow
|
|
39
|
+
* it and detach the views of every other backend. The transform backend keeps
|
|
40
|
+
* this check inline in {@link WasmTransformBackend.revalidateViews}; the anim/
|
|
41
|
+
* hit/particle backends reuse this helper.
|
|
42
|
+
*/
|
|
43
|
+
export declare function viewsStale(cap: number, probe: ArrayBufferView, memory: WebAssembly.Memory): boolean;
|
|
31
44
|
/** Which kernel to run. `simd` is the default; `scalar` exists for A/B and for
|
|
32
45
|
* the (theoretical) case of a build without simd128. Both are f64 and
|
|
33
46
|
* bit-identical to {@link composeJS}. */
|