@vectojs/core 1.39.0 → 1.40.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/README.md +43 -135
- package/dist/{chunk-RUM2KEDI.mjs → chunk-AISSDI6U.mjs} +15 -8
- package/dist/{chunk-KRUDTGNR.js → chunk-BKXERWAD.js} +15 -8
- package/dist/{chunk-7PYD5UDX.mjs → chunk-CIRZ3S2Z.mjs} +48 -3
- package/dist/{chunk-POPXUPKR.js → chunk-HC5EZOZJ.js} +80 -35
- package/dist/components/VirtualizedSetAggregate.d.ts +110 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1130 -216
- package/dist/index.mjs +927 -13
- 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 +59 -1
- package/dist/tree/Scene.d.ts +212 -1
- package/dist/tree/scene/HitResult.d.ts +50 -0
- package/dist/tree/scene/HitTester.d.ts +30 -0
- package/dist/tree/scene/ProjectionBackend.d.ts +64 -0
- package/dist/tree/scene/ProjectionPolicy.d.ts +162 -0
- package/dist/tree/scene/SemanticProjectionPolicy.d.ts +119 -0
- package/dist/wasm/vectojs_core.wasm +0 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
# @vectojs/core
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
transforms, render scheduling, layout, text flow, spatial hit-testing, event propagation, renderer
|
|
11
|
-
backends, and the accessibility/automation projection layer.
|
|
12
|
-
|
|
13
|
-
[Core guide](https://vectojs.org/learn/core-scene/) ·
|
|
14
|
-
[API reference](https://vectojs.org/reference/core-api/) ·
|
|
15
|
-
[Main repository](https://github.com/vectojs/vectojs)
|
|
3
|
+
`@vectojs/core` is the runtime at the root of the VectoJS dependency graph: a retained
|
|
4
|
+
`Scene`/`Entity` scene graph (the Virtual Math Tree) that renders to a single canvas, with
|
|
5
|
+
transforms, render scheduling, spatial hit-testing, DOM-like event propagation, layout and text
|
|
6
|
+
engines, Canvas/SVG/WebGL/WebGPU renderer backends, and the accessibility/automation projection
|
|
7
|
+
layer. It is also the composition point of the framework — it depends on and re-exports
|
|
8
|
+
`@vectojs/layout`, `@vectojs/text`, `@vectojs/math`, and `@vectojs/animation`, so `@vectojs/ui`
|
|
9
|
+
and every higher-level package build directly on it.
|
|
16
10
|
|
|
17
11
|
## Install
|
|
18
12
|
|
|
@@ -20,7 +14,7 @@ backends, and the accessibility/automation projection layer.
|
|
|
20
14
|
bun add @vectojs/core
|
|
21
15
|
```
|
|
22
16
|
|
|
23
|
-
##
|
|
17
|
+
## Usage
|
|
24
18
|
|
|
25
19
|
```ts
|
|
26
20
|
import { Entity, type IRenderer, Scene } from '@vectojs/core';
|
|
@@ -57,124 +51,38 @@ scene.add(new Dot().setPosition(80, 80));
|
|
|
57
51
|
scene.start();
|
|
58
52
|
```
|
|
59
53
|
|
|
60
|
-
##
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
scene.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
import { Scene, Entity } from '@vectojs/core';
|
|
98
|
-
import { LayoutEngine } from '@vectojs/core/layout';
|
|
99
|
-
import type { IRenderer } from '@vectojs/core/renderer';
|
|
100
|
-
import { TextEntity } from '@vectojs/core/text';
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Both ESM and CommonJS outputs are published with TypeScript declarations.
|
|
104
|
-
|
|
105
|
-
## Accessibility and automation
|
|
106
|
-
|
|
107
|
-
Canvas pixels have no semantics. Interactive entities can implement `getA11yAttributes()`; the
|
|
108
|
-
Scene projects transparent DOM nodes over their world-space bounds and forwards native input back
|
|
109
|
-
into the VectoJS event system.
|
|
110
|
-
|
|
111
|
-
This projection is intentionally thin. Applications still own accessible names, keyboard behavior,
|
|
112
|
-
focus order, contrast, and correct control semantics. See the
|
|
113
|
-
[accessibility guide](https://vectojs.org/learn/accessibility/).
|
|
114
|
-
|
|
115
|
-
Non-control workspaces that own keyboard shortcuts can opt into focus order
|
|
116
|
-
explicitly: return `{ role: 'region', label: 'Canvas workspace', tabIndex: 0 }`.
|
|
117
|
-
The Scene refreshes the projected `tabindex` when attributes change. Keep native
|
|
118
|
-
text inputs and editable content in charge of their own editing shortcuts.
|
|
119
|
-
|
|
120
|
-
Interactive projected nodes capture the active pointer on `pointerdown` and route
|
|
121
|
-
`pointermove`, `pointerup`, and `pointercancel` through normal VMT capture/bubble propagation.
|
|
122
|
-
Treat `pointercancel` as rollback: discard transient gesture state and do not create durable
|
|
123
|
-
history. Pointer capture is released safely on both completion paths.
|
|
124
|
-
|
|
125
|
-
## Static content projection
|
|
126
|
-
|
|
127
|
-
Canvas-rendered text can opt into browser-native find, translation, selection, and copy without
|
|
128
|
-
turning the application into a DOM layout. Override `getContentProjection()` on the owning Entity;
|
|
129
|
-
the Scene creates a transparent, position-synchronized mirror while the VMT remains authoritative:
|
|
130
|
-
|
|
131
|
-
```ts
|
|
132
|
-
class SelectableLabel extends Entity {
|
|
133
|
-
getContentProjection() {
|
|
134
|
-
return {
|
|
135
|
-
text: 'Selectable canvas text',
|
|
136
|
-
font: '18px Inter',
|
|
137
|
-
lineHeight: 24,
|
|
138
|
-
selectable: true,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
`selectable` controls whether the mirror receives pointer input. The Scene keeps projection order
|
|
145
|
-
aligned with VMT order, removes descendant mirrors with their subtree, and hides mirrors fully
|
|
146
|
-
outside the viewport or a `clipChildren` ancestor. Tooling can inspect the currently materialized
|
|
147
|
-
node with `scene.getContentElement(entityId)`. Virtualized or non-materialized off-viewport text is
|
|
148
|
-
not searchable until the application brings it into the active scene.
|
|
149
|
-
|
|
150
|
-
Code-like renderers can compile their logical source once with `prepareContentGrid()` and return the
|
|
151
|
-
same immutable plan as `ContentProjection.grid`. The plan retains UTF-16 source ranges, legal
|
|
152
|
-
grapheme carets, CR/LF ownership, tab stops, wide CJK/emoji advances, Arabic shaping, and Unicode
|
|
153
|
-
bidi positions. Scene projects those cells in logical source order, performs font calibration in a
|
|
154
|
-
cold offscreen batch, and uses the plan's local geometry for pointer selection even when the entity
|
|
155
|
-
is rotated, scaled, or the page is zoomed.
|
|
156
|
-
|
|
157
|
-
Selection routing preserves forward/reverse drag direction, Shift extension, word and line
|
|
158
|
-
selection, and exact clipboard source. Projection rebuild/removal/destroy paths release active
|
|
159
|
-
selection and pending calibration ownership before replacing DOM carriers.
|
|
160
|
-
|
|
161
|
-
## Performance model
|
|
162
|
-
|
|
163
|
-
Useful levers include on-demand rendering, viewport culling, spatial hashing, prepared text layout,
|
|
164
|
-
typed reusable buffers, batched WebGL points, and optional WebGPU particle compute. None makes every
|
|
165
|
-
workload allocation-free or GPU-bound; profile the renderer and entity types used by your app.
|
|
166
|
-
|
|
167
|
-
Run the repository benchmarks with `bun run benchmark`, `bun run compare:dom`, and
|
|
168
|
-
`bun run compare`. Prepared-grid scaling can be measured independently with
|
|
169
|
-
`bun run --cwd packages/core benchmark:grid`; benchmark timing is release evidence rather than a
|
|
170
|
-
wall-clock CI gate.
|
|
171
|
-
|
|
172
|
-
## Related packages
|
|
173
|
-
|
|
174
|
-
- [`@vectojs/ui`](https://github.com/vectojs/vectojs/tree/main/packages/ui) — high-level accessible components
|
|
175
|
-
- [`@vectojs/three`](https://github.com/vectojs/vectojs/tree/main/packages/three) — Three.js/WebXR projection and raycast routing
|
|
176
|
-
- [`@vectojs/video-exporter`](https://github.com/vectojs/vectojs/tree/main/packages/video-exporter) — deterministic H.264 capture
|
|
177
|
-
|
|
178
|
-
## License
|
|
179
|
-
|
|
180
|
-
[MIT](https://github.com/vectojs/vectojs/blob/main/LICENSE) © 2026 Xuepoo
|
|
54
|
+
## Highlights
|
|
55
|
+
|
|
56
|
+
- Retained `Scene`/`Entity` tree with affine transforms, capture/bubble event dispatch,
|
|
57
|
+
viewport culling, and dirty-flag `renderMode: 'onDemand'` rendering; `scene.step(dt)` drives a
|
|
58
|
+
deterministic frame for tests and video export.
|
|
59
|
+
- Backend-neutral `IRenderer` drawing contract with modular backends: `CanvasRenderer`,
|
|
60
|
+
`SVGRenderer`, batched WebGL points (`WebGLPointRenderer`), and WebGPU particle compute
|
|
61
|
+
(`WebGPUParticleSystemManager`) — registered on load via `Scene.register*`, selected through
|
|
62
|
+
`pointBackend` / `particleBackend` options.
|
|
63
|
+
- Optional Rust WASM kernels (`crates/vectojs-core-rs`) hot-swapped per subsystem with
|
|
64
|
+
`scene.enableWasmTransforms / enableWasmHitTest / enableWasmAnimBatching / enableWasmParticles`;
|
|
65
|
+
fallible exports return `WASM_STATUS` codes (`OK`/`CAPACITY`/`UNINITIALIZED`/`BAD_RUN`/
|
|
66
|
+
`OVERFLOW`) so any rejected batch degrades to the JS path instead of rendering half-written state.
|
|
67
|
+
- Semantic accessibility projection: entities implementing `getA11yAttributes()` get transparent,
|
|
68
|
+
position-synchronized DOM mirrors for screen readers, keyboard users, Playwright, and AI agents;
|
|
69
|
+
static text opts into browser-native selection/find/copy through `getContentProjection()` and
|
|
70
|
+
Core's prepared content grid (`prepareContentGrid()`).
|
|
71
|
+
- Entity-based text renderers stay here because they extend `Entity`: `TextEntity`,
|
|
72
|
+
`GridTextEntity`, GPU-resolved `MSDFTextEntity` (off-thread layout via `LayoutWorkerManager`),
|
|
73
|
+
`SVGEntity`, and `DOMPortalEntity`.
|
|
74
|
+
- The standalone engines are re-exported from this barrel and remain available as subpaths, so
|
|
75
|
+
existing imports keep working:
|
|
76
|
+
`@vectojs/core/layout`, `@vectojs/core/text`, `@vectojs/core/renderer`.
|
|
77
|
+
- Lifecycle ownership is explicit: a `Scene` owns renderers, workers, observers, and projected DOM
|
|
78
|
+
nodes; `scene.destroy()` releases all of them.
|
|
79
|
+
|
|
80
|
+
> Documents @vectojs/core@1.39.0.
|
|
81
|
+
|
|
82
|
+
## Documentation
|
|
83
|
+
|
|
84
|
+
- [Core Scene architecture](https://vectojs.org/learn/core-scene/)
|
|
85
|
+
- [`@vectojs/core` API reference](https://vectojs.org/reference/core-api/)
|
|
86
|
+
- [`Entity` reference](https://vectojs.org/reference/core-entity/)
|
|
87
|
+
- [Renderers reference](https://vectojs.org/reference/core-renderer/)
|
|
88
|
+
- [Accessibility & automation guide](https://vectojs.org/learn/accessibility/)
|
|
@@ -63,7 +63,8 @@ function emptyDrawCounters() {
|
|
|
63
63
|
}
|
|
64
64
|
var TWO_PI = Math.PI * 2;
|
|
65
65
|
function getDevicePixelRatio() {
|
|
66
|
-
|
|
66
|
+
const raw = typeof window !== "undefined" ? window.devicePixelRatio : 1;
|
|
67
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 1;
|
|
67
68
|
}
|
|
68
69
|
var CanvasRenderer = class _CanvasRenderer {
|
|
69
70
|
ctx;
|
|
@@ -209,7 +210,9 @@ var CanvasRenderer = class _CanvasRenderer {
|
|
|
209
210
|
/** Real `devicePixelRatio`, clamped to {@link maxDPR} when set. */
|
|
210
211
|
effectiveDPR() {
|
|
211
212
|
const real = getDevicePixelRatio();
|
|
212
|
-
|
|
213
|
+
if (this.maxDPR === void 0) return real;
|
|
214
|
+
if (!Number.isFinite(this.maxDPR) || this.maxDPR <= 0) return real;
|
|
215
|
+
return Math.min(real, this.maxDPR);
|
|
213
216
|
}
|
|
214
217
|
/**
|
|
215
218
|
* @inheritdoc
|
|
@@ -240,14 +243,18 @@ var CanvasRenderer = class _CanvasRenderer {
|
|
|
240
243
|
*/
|
|
241
244
|
resize(width, height) {
|
|
242
245
|
const dpr = this.effectiveDPR();
|
|
246
|
+
const safeWidth = Number.isFinite(width) && width >= 0 ? width : this.width;
|
|
247
|
+
const safeHeight = Number.isFinite(height) && height >= 0 ? height : this.height;
|
|
248
|
+
const backingW = Number.isFinite(safeWidth * dpr) ? Math.max(1, Math.round(safeWidth * dpr)) : 1;
|
|
249
|
+
const backingH = Number.isFinite(safeHeight * dpr) ? Math.max(1, Math.round(safeHeight * dpr)) : 1;
|
|
243
250
|
this.appliedDPR = dpr;
|
|
244
|
-
this.width =
|
|
245
|
-
this.height =
|
|
246
|
-
this.ctx.canvas.width =
|
|
247
|
-
this.ctx.canvas.height =
|
|
251
|
+
this.width = safeWidth;
|
|
252
|
+
this.height = safeHeight;
|
|
253
|
+
this.ctx.canvas.width = backingW;
|
|
254
|
+
this.ctx.canvas.height = backingH;
|
|
248
255
|
if (this.ctx.canvas.style) {
|
|
249
|
-
this.ctx.canvas.style.width = `${
|
|
250
|
-
this.ctx.canvas.style.height = `${
|
|
256
|
+
this.ctx.canvas.style.width = `${safeWidth}px`;
|
|
257
|
+
this.ctx.canvas.style.height = `${safeHeight}px`;
|
|
251
258
|
}
|
|
252
259
|
this.ctx.scale(dpr, dpr);
|
|
253
260
|
this._cachedFont = "";
|
|
@@ -65,7 +65,8 @@ function emptyDrawCounters() {
|
|
|
65
65
|
}
|
|
66
66
|
var TWO_PI = Math.PI * 2;
|
|
67
67
|
function getDevicePixelRatio() {
|
|
68
|
-
|
|
68
|
+
const raw = typeof window !== "undefined" ? window.devicePixelRatio : 1;
|
|
69
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 1;
|
|
69
70
|
}
|
|
70
71
|
var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
71
72
|
|
|
@@ -211,7 +212,9 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
211
212
|
/** Real `devicePixelRatio`, clamped to {@link maxDPR} when set. */
|
|
212
213
|
effectiveDPR() {
|
|
213
214
|
const real = getDevicePixelRatio();
|
|
214
|
-
|
|
215
|
+
if (this.maxDPR === void 0) return real;
|
|
216
|
+
if (!Number.isFinite(this.maxDPR) || this.maxDPR <= 0) return real;
|
|
217
|
+
return Math.min(real, this.maxDPR);
|
|
215
218
|
}
|
|
216
219
|
/**
|
|
217
220
|
* @inheritdoc
|
|
@@ -242,14 +245,18 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
242
245
|
*/
|
|
243
246
|
resize(width, height) {
|
|
244
247
|
const dpr = this.effectiveDPR();
|
|
248
|
+
const safeWidth = Number.isFinite(width) && width >= 0 ? width : this.width;
|
|
249
|
+
const safeHeight = Number.isFinite(height) && height >= 0 ? height : this.height;
|
|
250
|
+
const backingW = Number.isFinite(safeWidth * dpr) ? Math.max(1, Math.round(safeWidth * dpr)) : 1;
|
|
251
|
+
const backingH = Number.isFinite(safeHeight * dpr) ? Math.max(1, Math.round(safeHeight * dpr)) : 1;
|
|
245
252
|
this.appliedDPR = dpr;
|
|
246
|
-
this.width =
|
|
247
|
-
this.height =
|
|
248
|
-
this.ctx.canvas.width =
|
|
249
|
-
this.ctx.canvas.height =
|
|
253
|
+
this.width = safeWidth;
|
|
254
|
+
this.height = safeHeight;
|
|
255
|
+
this.ctx.canvas.width = backingW;
|
|
256
|
+
this.ctx.canvas.height = backingH;
|
|
250
257
|
if (this.ctx.canvas.style) {
|
|
251
|
-
this.ctx.canvas.style.width = `${
|
|
252
|
-
this.ctx.canvas.style.height = `${
|
|
258
|
+
this.ctx.canvas.style.width = `${safeWidth}px`;
|
|
259
|
+
this.ctx.canvas.style.height = `${safeHeight}px`;
|
|
253
260
|
}
|
|
254
261
|
this.ctx.scale(dpr, dpr);
|
|
255
262
|
this._cachedFont = "";
|
|
@@ -27,16 +27,22 @@ var VectoJSEvent = class {
|
|
|
27
27
|
nativeEvent;
|
|
28
28
|
/** Whether the event bubbles past its target (capture always runs). */
|
|
29
29
|
bubbles;
|
|
30
|
+
/**
|
|
31
|
+
* Which transport delivered this event ({@link VectoEventSource}).
|
|
32
|
+
* `undefined` for unattributed legacy/synthetic dispatches.
|
|
33
|
+
*/
|
|
34
|
+
source;
|
|
30
35
|
explicitScenePoint;
|
|
31
36
|
stopped = false;
|
|
32
37
|
stoppedImmediate = false;
|
|
33
|
-
constructor(type, target, nativeEvent, bubbles = true, scenePoint) {
|
|
38
|
+
constructor(type, target, nativeEvent, bubbles = true, scenePoint, source) {
|
|
34
39
|
this.type = type;
|
|
35
40
|
this.target = target;
|
|
36
41
|
this.currentTarget = target;
|
|
37
42
|
this.nativeEvent = nativeEvent;
|
|
38
43
|
this.bubbles = bubbles;
|
|
39
44
|
this.explicitScenePoint = scenePoint;
|
|
45
|
+
this.source = source;
|
|
40
46
|
}
|
|
41
47
|
/** Stop the event from reaching the next node in the propagation path. */
|
|
42
48
|
stopPropagation() {
|
|
@@ -330,6 +336,41 @@ var Entity = class {
|
|
|
330
336
|
* faint-but-live control.
|
|
331
337
|
*/
|
|
332
338
|
a11yHidden = false;
|
|
339
|
+
/**
|
|
340
|
+
* Opt-in to the DOM visual projection (RFC2, implemented by `@vectojs/dom`).
|
|
341
|
+
* Plain data only — core never materializes an element from this; a
|
|
342
|
+
* registered `ProjectionBackend` does.
|
|
343
|
+
*
|
|
344
|
+
* Name mapping: this field IS the RFC4 §2 `projection` policy
|
|
345
|
+
* (`ProjectionPolicy`: `'canvas' | 'dom' | 'auto'` in
|
|
346
|
+
* `tree/scene/ProjectionPolicy.ts`) — predates the RFC, so the field keeps
|
|
347
|
+
* its name and the RFC spelling lives on the value type.
|
|
348
|
+
*
|
|
349
|
+
* - `'canvas'` (default): today's rendering. Zero behavior change.
|
|
350
|
+
* - `'dom'`: the node materializes as a live `HTMLElement` positioned by its
|
|
351
|
+
* world matrix. The live element replaces the transparent a11y mirror
|
|
352
|
+
* (same single-delivery reasoning as `DOMPortalEntity`), so opting in
|
|
353
|
+
* requires a DOM backend and a DOM environment for the AT representation.
|
|
354
|
+
* - `'auto'`: negotiated per node per frame by `Scene.resolveProjectionFor`
|
|
355
|
+
* (RFC4 §3: explicit beats automatic, fallbacks reported with reasons,
|
|
356
|
+
* hysteresis against flip-flop, no-DOM short-circuits to canvas).
|
|
357
|
+
*/
|
|
358
|
+
domPolicy = "canvas";
|
|
359
|
+
/**
|
|
360
|
+
* Backend-side creation hint for a `'dom'`-policy node (tag/content mapping
|
|
361
|
+
* key, e.g. `'text' | 'button' | 'input' | 'container' | 'transform'`). Plain
|
|
362
|
+
* string so custom nodes need no DOM types in core; `@vectojs/dom` owns the
|
|
363
|
+
* registry. `''` means a plain positioned `div` with no content sync.
|
|
364
|
+
*/
|
|
365
|
+
domKind = "";
|
|
366
|
+
/**
|
|
367
|
+
* Internal cache owned by the DOM backend: true while the node has a live
|
|
368
|
+
* projected element. The render walk reads it to skip canvas paint for
|
|
369
|
+
* DOM-resident nodes (so a mounted node is never double-drawn) and falls
|
|
370
|
+
* through to canvas while false (SSR / no backend registered). Do not set
|
|
371
|
+
* by hand — the backend sets it on mount and clears it on unmount.
|
|
372
|
+
*/
|
|
373
|
+
domResident = false;
|
|
333
374
|
/**
|
|
334
375
|
* Clip this node's children to its local box (`[0,0]–[width,height]`) while
|
|
335
376
|
* rendering. Combined with translating a content child, this is how
|
|
@@ -631,8 +672,7 @@ var Entity = class {
|
|
|
631
672
|
(e) => new Promise((resolve) => {
|
|
632
673
|
this._spawnDriver(e[0], e[1], cfg);
|
|
633
674
|
const d = this._drivers?.get(e[0]);
|
|
634
|
-
if (!d)
|
|
635
|
-
resolve();
|
|
675
|
+
if (!d) resolve();
|
|
636
676
|
else d.onDone = resolve;
|
|
637
677
|
})
|
|
638
678
|
)
|
|
@@ -822,6 +862,7 @@ var Entity = class {
|
|
|
822
862
|
el.focus();
|
|
823
863
|
return;
|
|
824
864
|
}
|
|
865
|
+
if (typeof requestAnimationFrame === "undefined") return;
|
|
825
866
|
requestAnimationFrame(() => {
|
|
826
867
|
const retry = this.scene?.getA11yElement(this.id);
|
|
827
868
|
if (retry) retry.focus();
|
|
@@ -1188,6 +1229,10 @@ var Entity = class {
|
|
|
1188
1229
|
* `selectable` is set — natively selectable. Returns `null` by default.
|
|
1189
1230
|
* Read on the a11y sync cadence, so text changes propagate automatically.
|
|
1190
1231
|
*
|
|
1232
|
+
* This is the descriptor half of the `ContentProjection` row of
|
|
1233
|
+
* `ProjectionBackend` (`tree/scene/ProjectionBackend.ts`): the entity
|
|
1234
|
+
* describes *what* to project, the scene owns *how* it is materialized.
|
|
1235
|
+
*
|
|
1191
1236
|
* @param hint - Optional advice about which part of the entity is worth
|
|
1192
1237
|
* describing. Purely an optimization: ignoring it is always correct, which
|
|
1193
1238
|
* is why it is a parameter rather than a required contract change. See
|
|
@@ -29,16 +29,22 @@ var VectoJSEvent = (_class = class {
|
|
|
29
29
|
|
|
30
30
|
/** Whether the event bubbles past its target (capture always runs). */
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Which transport delivered this event ({@link VectoEventSource}).
|
|
34
|
+
* `undefined` for unattributed legacy/synthetic dispatches.
|
|
35
|
+
*/
|
|
36
|
+
|
|
32
37
|
|
|
33
38
|
__init() {this.stopped = false}
|
|
34
39
|
__init2() {this.stoppedImmediate = false}
|
|
35
|
-
constructor(type, target, nativeEvent, bubbles = true, scenePoint) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);
|
|
40
|
+
constructor(type, target, nativeEvent, bubbles = true, scenePoint, source) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);
|
|
36
41
|
this.type = type;
|
|
37
42
|
this.target = target;
|
|
38
43
|
this.currentTarget = target;
|
|
39
44
|
this.nativeEvent = nativeEvent;
|
|
40
45
|
this.bubbles = bubbles;
|
|
41
46
|
this.explicitScenePoint = scenePoint;
|
|
47
|
+
this.source = source;
|
|
42
48
|
}
|
|
43
49
|
/** Stop the event from reaching the next node in the propagation path. */
|
|
44
50
|
stopPropagation() {
|
|
@@ -332,13 +338,48 @@ var Entity = (_class2 = class {
|
|
|
332
338
|
* faint-but-live control.
|
|
333
339
|
*/
|
|
334
340
|
__init36() {this.a11yHidden = false}
|
|
341
|
+
/**
|
|
342
|
+
* Opt-in to the DOM visual projection (RFC2, implemented by `@vectojs/dom`).
|
|
343
|
+
* Plain data only — core never materializes an element from this; a
|
|
344
|
+
* registered `ProjectionBackend` does.
|
|
345
|
+
*
|
|
346
|
+
* Name mapping: this field IS the RFC4 §2 `projection` policy
|
|
347
|
+
* (`ProjectionPolicy`: `'canvas' | 'dom' | 'auto'` in
|
|
348
|
+
* `tree/scene/ProjectionPolicy.ts`) — predates the RFC, so the field keeps
|
|
349
|
+
* its name and the RFC spelling lives on the value type.
|
|
350
|
+
*
|
|
351
|
+
* - `'canvas'` (default): today's rendering. Zero behavior change.
|
|
352
|
+
* - `'dom'`: the node materializes as a live `HTMLElement` positioned by its
|
|
353
|
+
* world matrix. The live element replaces the transparent a11y mirror
|
|
354
|
+
* (same single-delivery reasoning as `DOMPortalEntity`), so opting in
|
|
355
|
+
* requires a DOM backend and a DOM environment for the AT representation.
|
|
356
|
+
* - `'auto'`: negotiated per node per frame by `Scene.resolveProjectionFor`
|
|
357
|
+
* (RFC4 §3: explicit beats automatic, fallbacks reported with reasons,
|
|
358
|
+
* hysteresis against flip-flop, no-DOM short-circuits to canvas).
|
|
359
|
+
*/
|
|
360
|
+
__init37() {this.domPolicy = "canvas"}
|
|
361
|
+
/**
|
|
362
|
+
* Backend-side creation hint for a `'dom'`-policy node (tag/content mapping
|
|
363
|
+
* key, e.g. `'text' | 'button' | 'input' | 'container' | 'transform'`). Plain
|
|
364
|
+
* string so custom nodes need no DOM types in core; `@vectojs/dom` owns the
|
|
365
|
+
* registry. `''` means a plain positioned `div` with no content sync.
|
|
366
|
+
*/
|
|
367
|
+
__init38() {this.domKind = ""}
|
|
368
|
+
/**
|
|
369
|
+
* Internal cache owned by the DOM backend: true while the node has a live
|
|
370
|
+
* projected element. The render walk reads it to skip canvas paint for
|
|
371
|
+
* DOM-resident nodes (so a mounted node is never double-drawn) and falls
|
|
372
|
+
* through to canvas while false (SSR / no backend registered). Do not set
|
|
373
|
+
* by hand — the backend sets it on mount and clears it on unmount.
|
|
374
|
+
*/
|
|
375
|
+
__init39() {this.domResident = false}
|
|
335
376
|
/**
|
|
336
377
|
* Clip this node's children to its local box (`[0,0]–[width,height]`) while
|
|
337
378
|
* rendering. Combined with translating a content child, this is how
|
|
338
379
|
* scroll/overflow containers (e.g. `ScrollView`) keep their content inside a
|
|
339
380
|
* fixed viewport. Off by default (children render unclipped). Canvas2D only.
|
|
340
381
|
*/
|
|
341
|
-
|
|
382
|
+
__init40() {this.clipChildren = false}
|
|
342
383
|
/**
|
|
343
384
|
* Group this subtree's projected text into its own accessibility **region**,
|
|
344
385
|
* without clipping anything.
|
|
@@ -365,14 +406,14 @@ var Entity = (_class2 = class {
|
|
|
365
406
|
*
|
|
366
407
|
* Off by default. Regions nest: the nearest enclosing region wins.
|
|
367
408
|
*/
|
|
368
|
-
|
|
409
|
+
__init41() {this.a11yRegion = false}
|
|
369
410
|
// Lazily allocated (see _drivers above). Most entities never register a
|
|
370
411
|
// listener or an imperative animate() tween.
|
|
371
|
-
|
|
412
|
+
__init42() {this.listeners = null}
|
|
372
413
|
/** Capture-phase listeners (fired root→target before bubble). */
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
constructor(id) {;_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);_class2.prototype.__init39.call(this);_class2.prototype.__init40.call(this);_class2.prototype.__init41.call(this);
|
|
414
|
+
__init43() {this.captureListeners = null}
|
|
415
|
+
__init44() {this.animations = null}
|
|
416
|
+
constructor(id) {;_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);_class2.prototype.__init39.call(this);_class2.prototype.__init40.call(this);_class2.prototype.__init41.call(this);_class2.prototype.__init42.call(this);_class2.prototype.__init43.call(this);_class2.prototype.__init44.call(this);
|
|
376
417
|
this.id = id || `entity_${Math.random().toString(36).substring(2, 9)}`;
|
|
377
418
|
}
|
|
378
419
|
/**
|
|
@@ -633,8 +674,7 @@ var Entity = (_class2 = class {
|
|
|
633
674
|
(e) => new Promise((resolve) => {
|
|
634
675
|
this._spawnDriver(e[0], e[1], cfg);
|
|
635
676
|
const d = _optionalChain([this, 'access', _84 => _84._drivers, 'optionalAccess', _85 => _85.get, 'call', _86 => _86(e[0])]);
|
|
636
|
-
if (!d)
|
|
637
|
-
resolve();
|
|
677
|
+
if (!d) resolve();
|
|
638
678
|
else d.onDone = resolve;
|
|
639
679
|
})
|
|
640
680
|
)
|
|
@@ -824,6 +864,7 @@ var Entity = (_class2 = class {
|
|
|
824
864
|
el.focus();
|
|
825
865
|
return;
|
|
826
866
|
}
|
|
867
|
+
if (typeof requestAnimationFrame === "undefined") return;
|
|
827
868
|
requestAnimationFrame(() => {
|
|
828
869
|
const retry = _optionalChain([this, 'access', _109 => _109.scene, 'optionalAccess', _110 => _110.getA11yElement, 'call', _111 => _111(this.id)]);
|
|
829
870
|
if (retry) retry.focus();
|
|
@@ -1190,6 +1231,10 @@ var Entity = (_class2 = class {
|
|
|
1190
1231
|
* `selectable` is set — natively selectable. Returns `null` by default.
|
|
1191
1232
|
* Read on the a11y sync cadence, so text changes propagate automatically.
|
|
1192
1233
|
*
|
|
1234
|
+
* This is the descriptor half of the `ContentProjection` row of
|
|
1235
|
+
* `ProjectionBackend` (`tree/scene/ProjectionBackend.ts`): the entity
|
|
1236
|
+
* describes *what* to project, the scene owns *how* it is materialized.
|
|
1237
|
+
*
|
|
1193
1238
|
* @param hint - Optional advice about which part of the entity is worth
|
|
1194
1239
|
* describing. Purely an optimization: ignoring it is always correct, which
|
|
1195
1240
|
* is why it is a parameter rather than a required contract change. See
|
|
@@ -1267,27 +1312,27 @@ var MSDFTextEntity = (_class3 = class extends Entity {
|
|
|
1267
1312
|
// joined by soft hyphens (U+00AD); the worker then treats those as break
|
|
1268
1313
|
// opportunities. `text` keeps the original string for a11y/content
|
|
1269
1314
|
// projection; `layoutText` is the soft-hyphen-annotated string sent to layout.
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1315
|
+
__init45() {this.hyphenator = null}
|
|
1316
|
+
__init46() {this.layoutText = ""}
|
|
1317
|
+
__init47() {this.text = ""}
|
|
1318
|
+
__init48() {this.lastRenderedSeqId = 0}
|
|
1274
1319
|
/** Bumped by {@link queueLayout}; read by `Scene` to skip an unchanged sync. */
|
|
1275
|
-
|
|
1320
|
+
__init49() {this.contentEpoch = 0}
|
|
1276
1321
|
// Atlas-decode subscription (see watchAtlasDecode). Held so `destroy()` can
|
|
1277
1322
|
// release it: the handler closes over `this`, so leaving it attached to a
|
|
1278
1323
|
// long-lived shared atlas image would retain the whole entity.
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1324
|
+
__init50() {this.atlasDecodeTarget = null}
|
|
1325
|
+
__init51() {this.atlasDecodeHandler = null}
|
|
1326
|
+
__init52() {this.fontStringCache = []}
|
|
1327
|
+
__init53() {this.layoutResult = null}
|
|
1283
1328
|
/**
|
|
1284
1329
|
* Visual rows rebuilt from {@link layoutResult} (see
|
|
1285
1330
|
* {@link rebuildProjectionLines}). Empty until a layout reply lands and the
|
|
1286
1331
|
* reply's shaped glyphs can be mapped back to the source text 1:1.
|
|
1287
1332
|
*/
|
|
1288
|
-
|
|
1333
|
+
__init54() {this.projectionLines = []}
|
|
1289
1334
|
constructor(text, options) {
|
|
1290
|
-
super();_class3.prototype.
|
|
1335
|
+
super();_class3.prototype.__init45.call(this);_class3.prototype.__init46.call(this);_class3.prototype.__init47.call(this);_class3.prototype.__init48.call(this);_class3.prototype.__init49.call(this);_class3.prototype.__init50.call(this);_class3.prototype.__init51.call(this);_class3.prototype.__init52.call(this);_class3.prototype.__init53.call(this);_class3.prototype.__init54.call(this);;
|
|
1291
1336
|
this.font = options.font;
|
|
1292
1337
|
this.texture = options.texture;
|
|
1293
1338
|
this.fallbackFont = _nullishCoalesce(options.fallbackFont, () => ( "sans-serif"));
|
|
@@ -1671,23 +1716,23 @@ var SVGEntity = (_class4 = class extends Entity {
|
|
|
1671
1716
|
* rasterized. Set to `'transparent'` to opt out and keep the box empty.
|
|
1672
1717
|
* Default `'rgba(248,113,113,0.9)'`.
|
|
1673
1718
|
*/
|
|
1674
|
-
|
|
1719
|
+
__init55() {this.fallbackStroke = "rgba(248,113,113,0.9)"}
|
|
1675
1720
|
/** Fill behind the fallback marker. Default `'rgba(248,113,113,0.12)'`. */
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1721
|
+
__init56() {this.fallbackFill = "rgba(248,113,113,0.12)"}
|
|
1722
|
+
__init57() {this.svgSource = ""}
|
|
1723
|
+
__init58() {this.imageBitmap = null}
|
|
1724
|
+
__init59() {this.imageElement = null}
|
|
1725
|
+
__init60() {this.blobURL = null}
|
|
1726
|
+
__init61() {this.currentImg = null}
|
|
1727
|
+
__init62() {this.lodTimeout = null}
|
|
1728
|
+
__init63() {this.rasterFailed = false}
|
|
1729
|
+
__init64() {this.cachedDoc = null}
|
|
1730
|
+
__init65() {this.baseWidth = 100}
|
|
1731
|
+
__init66() {this.baseHeight = 100}
|
|
1732
|
+
__init67() {this.lastRasterizedScale = 1}
|
|
1733
|
+
__init68() {this.targetScale = 1}
|
|
1689
1734
|
constructor(svgSource, id) {
|
|
1690
|
-
super(id);_class4.prototype.
|
|
1735
|
+
super(id);_class4.prototype.__init55.call(this);_class4.prototype.__init56.call(this);_class4.prototype.__init57.call(this);_class4.prototype.__init58.call(this);_class4.prototype.__init59.call(this);_class4.prototype.__init60.call(this);_class4.prototype.__init61.call(this);_class4.prototype.__init62.call(this);_class4.prototype.__init63.call(this);_class4.prototype.__init64.call(this);_class4.prototype.__init65.call(this);_class4.prototype.__init66.call(this);_class4.prototype.__init67.call(this);_class4.prototype.__init68.call(this);;
|
|
1691
1736
|
this.setSVGSource(svgSource);
|
|
1692
1737
|
}
|
|
1693
1738
|
setSVGSource(svgSource) {
|