@vectojs/core 0.2.6 → 0.2.7
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-4BO5XBJL.js → chunk-DWMQYHNC.js} +25 -0
- package/dist/{chunk-GADBN53Z.js → chunk-GTQRCY6W.js} +4 -0
- package/dist/{chunk-PK5EYWIT.mjs → chunk-HVJHXIKW.mjs} +4 -0
- package/dist/{chunk-SL654OMF.mjs → chunk-YVN3PJRC.mjs} +25 -0
- package/dist/components/TextEntity.d.ts +6 -1
- package/dist/index.js +233 -121
- package/dist/index.mjs +118 -6
- package/dist/renderer.js +2 -2
- package/dist/renderer.mjs +1 -1
- package/dist/text/MSDFTextEntity.d.ts +6 -1
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Entity.d.ts +31 -0
- package/dist/tree/Scene.d.ts +19 -0
- package/package.json +5 -3
|
@@ -892,6 +892,19 @@ var Entity = (_class4 = class {
|
|
|
892
892
|
getBatchRect() {
|
|
893
893
|
return null;
|
|
894
894
|
}
|
|
895
|
+
/**
|
|
896
|
+
* Opt into DOM content projection for entities that render static text.
|
|
897
|
+
* The {@link Scene} mirrors the returned text as a transparent DOM node
|
|
898
|
+
* positioned over the drawn glyphs, making canvas text findable (Ctrl+F),
|
|
899
|
+
* readable by screen readers and crawlers, translatable, and — when
|
|
900
|
+
* `selectable` is set — natively selectable. Returns `null` by default.
|
|
901
|
+
* Read on the a11y sync cadence, so text changes propagate automatically.
|
|
902
|
+
*
|
|
903
|
+
* @returns The projection descriptor, or `null` to project nothing.
|
|
904
|
+
*/
|
|
905
|
+
getContentProjection() {
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
895
908
|
/**
|
|
896
909
|
* Whether this entity still has a queued/running tween animation, or an
|
|
897
910
|
* active {@link setTransition}/{@link animateTo}/{@link springTo} property
|
|
@@ -1067,6 +1080,18 @@ var MSDFTextEntity = (_class6 = class extends Entity {
|
|
|
1067
1080
|
}
|
|
1068
1081
|
});
|
|
1069
1082
|
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Mirror the rendered text into the DOM content layer: find-in-page, screen
|
|
1085
|
+
* readers, crawlers, and translation see the same string the canvas draws.
|
|
1086
|
+
*/
|
|
1087
|
+
getContentProjection() {
|
|
1088
|
+
if (!this.text) return null;
|
|
1089
|
+
return {
|
|
1090
|
+
text: this.text,
|
|
1091
|
+
font: `${this.fontSize}px ${this.fallbackFont}`,
|
|
1092
|
+
lineHeight: this.lineHeight
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1070
1095
|
isPointInside(globalX, globalY) {
|
|
1071
1096
|
if (!this.layoutResult) return false;
|
|
1072
1097
|
const local = this.worldToLocal(globalX, globalY);
|
|
@@ -34,6 +34,10 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
34
34
|
this.height = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _2 => _2.height]), () => ( (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0)));
|
|
35
35
|
canvas.width = this.width * dpr;
|
|
36
36
|
canvas.height = this.height * dpr;
|
|
37
|
+
if (canvas.style) {
|
|
38
|
+
canvas.style.width = `${this.width}px`;
|
|
39
|
+
canvas.style.height = `${this.height}px`;
|
|
40
|
+
}
|
|
37
41
|
const ctx = canvas.getContext("2d");
|
|
38
42
|
this.ctx = ctx;
|
|
39
43
|
if (ctx) ctx.scale(dpr, dpr);
|
|
@@ -34,6 +34,10 @@ var CanvasRenderer = class _CanvasRenderer {
|
|
|
34
34
|
this.height = size?.height ?? (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0);
|
|
35
35
|
canvas.width = this.width * dpr;
|
|
36
36
|
canvas.height = this.height * dpr;
|
|
37
|
+
if (canvas.style) {
|
|
38
|
+
canvas.style.width = `${this.width}px`;
|
|
39
|
+
canvas.style.height = `${this.height}px`;
|
|
40
|
+
}
|
|
37
41
|
const ctx = canvas.getContext("2d");
|
|
38
42
|
this.ctx = ctx;
|
|
39
43
|
if (ctx) ctx.scale(dpr, dpr);
|
|
@@ -892,6 +892,19 @@ var Entity = class {
|
|
|
892
892
|
getBatchRect() {
|
|
893
893
|
return null;
|
|
894
894
|
}
|
|
895
|
+
/**
|
|
896
|
+
* Opt into DOM content projection for entities that render static text.
|
|
897
|
+
* The {@link Scene} mirrors the returned text as a transparent DOM node
|
|
898
|
+
* positioned over the drawn glyphs, making canvas text findable (Ctrl+F),
|
|
899
|
+
* readable by screen readers and crawlers, translatable, and — when
|
|
900
|
+
* `selectable` is set — natively selectable. Returns `null` by default.
|
|
901
|
+
* Read on the a11y sync cadence, so text changes propagate automatically.
|
|
902
|
+
*
|
|
903
|
+
* @returns The projection descriptor, or `null` to project nothing.
|
|
904
|
+
*/
|
|
905
|
+
getContentProjection() {
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
895
908
|
/**
|
|
896
909
|
* Whether this entity still has a queued/running tween animation, or an
|
|
897
910
|
* active {@link setTransition}/{@link animateTo}/{@link springTo} property
|
|
@@ -1067,6 +1080,18 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1067
1080
|
}
|
|
1068
1081
|
});
|
|
1069
1082
|
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Mirror the rendered text into the DOM content layer: find-in-page, screen
|
|
1085
|
+
* readers, crawlers, and translation see the same string the canvas draws.
|
|
1086
|
+
*/
|
|
1087
|
+
getContentProjection() {
|
|
1088
|
+
if (!this.text) return null;
|
|
1089
|
+
return {
|
|
1090
|
+
text: this.text,
|
|
1091
|
+
font: `${this.fontSize}px ${this.fallbackFont}`,
|
|
1092
|
+
lineHeight: this.lineHeight
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1070
1095
|
isPointInside(globalX, globalY) {
|
|
1071
1096
|
if (!this.layoutResult) return false;
|
|
1072
1097
|
const local = this.worldToLocal(globalX, globalY);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Entity } from '../tree/Entity';
|
|
1
|
+
import { Entity, type ContentProjection } from '../tree/Entity';
|
|
2
2
|
import { IRenderer } from '../renderer/IRenderer';
|
|
3
3
|
export declare class TextEntity extends Entity {
|
|
4
4
|
text: string;
|
|
@@ -13,6 +13,11 @@ export declare class TextEntity extends Entity {
|
|
|
13
13
|
lineWidth: number;
|
|
14
14
|
private isHovered;
|
|
15
15
|
constructor(text: string, atlas: any, maxWidth: number, fontSize?: number);
|
|
16
|
+
/**
|
|
17
|
+
* Mirror the rendered text into the DOM content layer: find-in-page, screen
|
|
18
|
+
* readers, crawlers, and translation see the same string the canvas draws.
|
|
19
|
+
*/
|
|
20
|
+
getContentProjection(): ContentProjection | null;
|
|
16
21
|
/**
|
|
17
22
|
* Replace the text content. Runs the **cold** measurement pass (re-segment +
|
|
18
23
|
* re-measure) since the glyphs changed, then re-lays out.
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ var _chunkBWLNEJJWjs = require('./chunk-BWLNEJJW.js');
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
var
|
|
15
|
+
var _chunkGTQRCY6Wjs = require('./chunk-GTQRCY6W.js');
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
|
|
@@ -24,7 +24,7 @@ var _chunkGADBN53Zjs = require('./chunk-GADBN53Z.js');
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
var
|
|
27
|
+
var _chunkDWMQYHNCjs = require('./chunk-DWMQYHNC.js');
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
|
|
@@ -41,7 +41,7 @@ var PARTICLE_OFFSET_ORIGIN_X = 4;
|
|
|
41
41
|
var PARTICLE_OFFSET_ORIGIN_Y = 5;
|
|
42
42
|
var PARTICLE_OFFSET_SIZE = 6;
|
|
43
43
|
var PARTICLE_OFFSET_LIFE = 7;
|
|
44
|
-
var ComputeParticleEntity = (_class = class extends
|
|
44
|
+
var ComputeParticleEntity = (_class = class extends _chunkDWMQYHNCjs.Entity {
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
|
|
@@ -296,6 +296,11 @@ var ComputeParticleEntity = (_class = class extends _chunk4BO5XBJLjs.Entity {
|
|
|
296
296
|
|
|
297
297
|
// src/tree/Scene.ts
|
|
298
298
|
var REDUCED_MOTION_FPS = 30;
|
|
299
|
+
function parseInlinePx(value) {
|
|
300
|
+
if (!value || !value.endsWith("px")) return null;
|
|
301
|
+
const n = parseFloat(value);
|
|
302
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
303
|
+
}
|
|
299
304
|
var Scene = (_class2 = class _Scene {
|
|
300
305
|
static __initStatic() {this.webglCreator = null}
|
|
301
306
|
static __initStatic2() {this.webgpuManagerClass = null}
|
|
@@ -350,69 +355,75 @@ var Scene = (_class2 = class _Scene {
|
|
|
350
355
|
// server-side (e.g. headless layout / vector export) without jsdom.
|
|
351
356
|
|
|
352
357
|
__init18() {this.a11yElements = /* @__PURE__ */ new Map()}
|
|
358
|
+
/** DOM nodes mirroring static text content, keyed by entity id. */
|
|
359
|
+
__init19() {this.contentElements = /* @__PURE__ */ new Map()}
|
|
360
|
+
__init20() {this.contentProjectionEnabled = true}
|
|
353
361
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
362
|
+
__init21() {this.focusedA11yElement = null}
|
|
363
|
+
__init22() {this.caretBlinkTimer = null}
|
|
364
|
+
__init23() {this.a11yNeedsReorder = true}
|
|
365
|
+
__init24() {this.portalRoot = null}
|
|
366
|
+
__init25() {this.fullViewportElements = []}
|
|
367
|
+
__init26() {this.normalElements = []}
|
|
368
|
+
__init27() {this.activeIds = /* @__PURE__ */ new Set()}
|
|
369
|
+
__init28() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
|
|
370
|
+
__init29() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
|
|
371
|
+
__init30() {this.portalEntities = /* @__PURE__ */ new Map()}
|
|
372
|
+
__init31() {this.renderOrderCounter = 0}
|
|
365
373
|
// Optional WebGL point-cloud layer (see SceneOptions.pointBackend).
|
|
366
|
-
|
|
367
|
-
|
|
374
|
+
__init32() {this.pointRenderer = null}
|
|
375
|
+
__init33() {this.glCanvas = null}
|
|
368
376
|
|
|
369
377
|
|
|
370
378
|
|
|
371
|
-
|
|
379
|
+
__init34() {this.disableWindowResize = false}
|
|
372
380
|
// WebGPU properties
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
381
|
+
__init35() {this.destroyed = false}
|
|
382
|
+
__init36() {this.device = null}
|
|
383
|
+
__init37() {this.deviceLost = false}
|
|
384
|
+
__init38() {this.particleBackend = "auto"}
|
|
385
|
+
__init39() {this._webgpuDisabled = false}
|
|
378
386
|
get webgpuDisabled() {
|
|
379
387
|
return this._webgpuDisabled || this.particleBackend === "cpu";
|
|
380
388
|
}
|
|
381
389
|
set webgpuDisabled(value) {
|
|
382
390
|
this._webgpuDisabled = value;
|
|
383
391
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
392
|
+
__init40() {this.recoveryTimerId = null}
|
|
393
|
+
__init41() {this.manager = null}
|
|
394
|
+
__init42() {this.initializingWebGPU = false}
|
|
395
|
+
__init43() {this.gpuCanvas = null}
|
|
396
|
+
__init44() {this.gpuContext = null}
|
|
389
397
|
/** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
constructor(canvas, options = {}) {;_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);_class2.prototype.__init45.call(this);_class2.prototype.__init46.call(this);_class2.prototype.__init47.call(this);_class2.prototype.__init48.call(this);
|
|
398
|
+
__init45() {this.gpuHasContent = false}
|
|
399
|
+
__init46() {this.mouseX = -9999}
|
|
400
|
+
__init47() {this.mouseY = -9999}
|
|
401
|
+
__init48() {this.pointerMoveListener = null}
|
|
402
|
+
__init49() {this.pointerLeaveListener = null}
|
|
403
|
+
__init50() {this.hasWarnedZeroSize = false}
|
|
404
|
+
constructor(canvas, options = {}) {;_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);_class2.prototype.__init45.call(this);_class2.prototype.__init46.call(this);_class2.prototype.__init47.call(this);_class2.prototype.__init48.call(this);_class2.prototype.__init49.call(this);_class2.prototype.__init50.call(this);
|
|
397
405
|
this.canvas = canvas;
|
|
398
406
|
this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
|
|
399
407
|
this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
|
|
400
408
|
if (this.disableWindowResize) {
|
|
401
|
-
|
|
402
|
-
|
|
409
|
+
const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _15 => _15.style, 'optionalAccess', _16 => _16.width]));
|
|
410
|
+
const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _17 => _17.style, 'optionalAccess', _18 => _18.height]));
|
|
411
|
+
this.width = _nullishCoalesce(styleWidth, () => ( (canvas.width || canvas.clientWidth || 0)));
|
|
412
|
+
this.height = _nullishCoalesce(styleHeight, () => ( (canvas.height || canvas.clientHeight || 0)));
|
|
403
413
|
} else {
|
|
404
414
|
this.width = typeof window !== "undefined" ? window.innerWidth : canvas.clientWidth || canvas.width || 800;
|
|
405
415
|
this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
|
|
406
416
|
}
|
|
407
417
|
const globalProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
408
|
-
const isTest = globalProcess && (_optionalChain([globalProcess, 'access',
|
|
418
|
+
const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _19 => _19.env, 'optionalAccess', _20 => _20.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _21 => _21.env, 'optionalAccess', _22 => _22.VITEST]) === "true");
|
|
409
419
|
this.maxFPS = _nullishCoalesce(options.maxFPS, () => ( (isTest ? 0 : 60)));
|
|
410
420
|
this.respectReducedMotion = _nullishCoalesce(options.respectReducedMotion, () => ( true));
|
|
411
421
|
this.autoThrottle = _nullishCoalesce(options.autoThrottle, () => ( true));
|
|
412
422
|
this.particleBackend = _nullishCoalesce(options.particleBackend, () => ( "auto"));
|
|
413
423
|
this.a11ySyncInterval = _nullishCoalesce(options.a11ySyncInterval, () => ( 0));
|
|
424
|
+
this.contentProjectionEnabled = _nullishCoalesce(options.contentProjection, () => ( true));
|
|
414
425
|
this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
|
|
415
|
-
this.root = new class RootEntity extends
|
|
426
|
+
this.root = new class RootEntity extends _chunkDWMQYHNCjs.Entity {
|
|
416
427
|
isPointInside() {
|
|
417
428
|
return false;
|
|
418
429
|
}
|
|
@@ -421,7 +432,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
421
432
|
}
|
|
422
433
|
}("root");
|
|
423
434
|
this.root._scene = this;
|
|
424
|
-
this.overlayRoot = new class OverlayRoot extends
|
|
435
|
+
this.overlayRoot = new class OverlayRoot extends _chunkDWMQYHNCjs.Entity {
|
|
425
436
|
isPointInside() {
|
|
426
437
|
return false;
|
|
427
438
|
}
|
|
@@ -432,7 +443,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
432
443
|
if (options.renderer) {
|
|
433
444
|
this.renderer = options.renderer;
|
|
434
445
|
} else {
|
|
435
|
-
this.renderer = new (0,
|
|
446
|
+
this.renderer = new (0, _chunkGTQRCY6Wjs.CanvasRenderer)(
|
|
436
447
|
canvas,
|
|
437
448
|
this.disableWindowResize ? { width: this.width, height: this.height } : void 0
|
|
438
449
|
);
|
|
@@ -498,7 +509,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
498
509
|
}
|
|
499
510
|
/** Convert browser viewport coordinates into this Scene's logical coordinates. */
|
|
500
511
|
clientToScene(clientX, clientY) {
|
|
501
|
-
const rect = _optionalChain([this, 'access',
|
|
512
|
+
const rect = _optionalChain([this, 'access', _23 => _23.canvas, 'access', _24 => _24.getBoundingClientRect, 'optionalCall', _25 => _25()]);
|
|
502
513
|
if (!rect) return { x: clientX, y: clientY };
|
|
503
514
|
const cssWidth = rect.width || this.canvas.clientWidth || this.width;
|
|
504
515
|
const cssHeight = rect.height || this.canvas.clientHeight || this.height;
|
|
@@ -564,6 +575,11 @@ var Scene = (_class2 = class _Scene {
|
|
|
564
575
|
* @param entity - The subtree whose shadow nodes should be removed.
|
|
565
576
|
*/
|
|
566
577
|
detachA11y(entity) {
|
|
578
|
+
const contentEl = this.contentElements.get(entity.id);
|
|
579
|
+
if (contentEl) {
|
|
580
|
+
contentEl.remove();
|
|
581
|
+
this.contentElements.delete(entity.id);
|
|
582
|
+
}
|
|
567
583
|
this.removeA11yRecursively(entity);
|
|
568
584
|
}
|
|
569
585
|
/**
|
|
@@ -607,13 +623,15 @@ var Scene = (_class2 = class _Scene {
|
|
|
607
623
|
this.canvas.removeEventListener("pointerleave", this.pointerLeaveListener);
|
|
608
624
|
}
|
|
609
625
|
}
|
|
610
|
-
_optionalChain([this, 'access',
|
|
611
|
-
_optionalChain([this, 'access',
|
|
626
|
+
_optionalChain([this, 'access', _26 => _26.a11yRoot, 'optionalAccess', _27 => _27.remove, 'call', _28 => _28()]);
|
|
627
|
+
_optionalChain([this, 'access', _29 => _29.portalRoot, 'optionalAccess', _30 => _30.remove, 'call', _31 => _31()]);
|
|
612
628
|
this.a11yElements.clear();
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
_optionalChain([this, 'access',
|
|
616
|
-
_optionalChain([this, 'access',
|
|
629
|
+
for (const el of this.contentElements.values()) el.remove();
|
|
630
|
+
this.contentElements.clear();
|
|
631
|
+
_optionalChain([this, 'access', _32 => _32.pointRenderer, 'optionalAccess', _33 => _33.destroy, 'call', _34 => _34()]);
|
|
632
|
+
_optionalChain([this, 'access', _35 => _35.renderer, 'access', _36 => _36.dispose, 'optionalCall', _37 => _37()]);
|
|
633
|
+
_optionalChain([this, 'access', _38 => _38.glCanvas, 'optionalAccess', _39 => _39.remove, 'call', _40 => _40()]);
|
|
634
|
+
_optionalChain([this, 'access', _41 => _41.gpuCanvas, 'optionalAccess', _42 => _42.remove, 'call', _43 => _43()]);
|
|
617
635
|
this.gpuCanvas = null;
|
|
618
636
|
this.gpuContext = null;
|
|
619
637
|
if (this.recoveryTimerId) {
|
|
@@ -625,7 +643,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
625
643
|
this.manager = null;
|
|
626
644
|
}
|
|
627
645
|
if (this.device) {
|
|
628
|
-
_optionalChain([this, 'access',
|
|
646
|
+
_optionalChain([this, 'access', _44 => _44.device, 'access', _45 => _45.destroy, 'optionalCall', _46 => _46()]);
|
|
629
647
|
this.device = null;
|
|
630
648
|
}
|
|
631
649
|
}
|
|
@@ -769,42 +787,42 @@ var Scene = (_class2 = class _Scene {
|
|
|
769
787
|
el.style.background = "transparent";
|
|
770
788
|
}
|
|
771
789
|
el.addEventListener("click", (e) => {
|
|
772
|
-
node.dispatchEvent(new (0,
|
|
790
|
+
node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("click", node, e));
|
|
773
791
|
});
|
|
774
792
|
el.addEventListener("mouseenter", (e) => {
|
|
775
793
|
if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.2)";
|
|
776
|
-
node.dispatchEvent(new (0,
|
|
794
|
+
node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("hover", node, e, false));
|
|
777
795
|
});
|
|
778
796
|
el.addEventListener("mouseleave", (e) => {
|
|
779
797
|
if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.05)";
|
|
780
|
-
node.dispatchEvent(new (0,
|
|
798
|
+
node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("pointerleave", node, e, false));
|
|
781
799
|
});
|
|
782
800
|
const capEl = el;
|
|
783
801
|
el.addEventListener("pointerdown", (e) => {
|
|
784
802
|
if (typeof capEl.setPointerCapture === "function") capEl.setPointerCapture(e.pointerId);
|
|
785
|
-
node.dispatchEvent(new (0,
|
|
803
|
+
node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("pointerdown", node, e));
|
|
786
804
|
});
|
|
787
805
|
el.addEventListener("pointerup", (e) => {
|
|
788
806
|
if (typeof capEl.releasePointerCapture === "function")
|
|
789
807
|
capEl.releasePointerCapture(e.pointerId);
|
|
790
|
-
node.dispatchEvent(new (0,
|
|
808
|
+
node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("pointerup", node, e));
|
|
791
809
|
});
|
|
792
810
|
el.addEventListener(
|
|
793
811
|
"pointermove",
|
|
794
|
-
(e) => node.dispatchEvent(new (0,
|
|
812
|
+
(e) => node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("pointermove", node, e))
|
|
795
813
|
);
|
|
796
814
|
el.addEventListener(
|
|
797
815
|
"wheel",
|
|
798
816
|
(e) => {
|
|
799
|
-
node.dispatchEvent(new (0,
|
|
817
|
+
node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("wheel", node, e));
|
|
800
818
|
},
|
|
801
819
|
{ passive: false }
|
|
802
820
|
);
|
|
803
821
|
el.addEventListener("keydown", (e) => {
|
|
804
|
-
node.dispatchEvent(new (0,
|
|
822
|
+
node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("keydown", node, e));
|
|
805
823
|
});
|
|
806
824
|
el.addEventListener("keyup", (e) => {
|
|
807
|
-
node.dispatchEvent(new (0,
|
|
825
|
+
node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("keyup", node, e));
|
|
808
826
|
});
|
|
809
827
|
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
|
|
810
828
|
const input = el;
|
|
@@ -831,7 +849,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
831
849
|
});
|
|
832
850
|
el.addEventListener("compositionupdate", (e) => {
|
|
833
851
|
const data = _nullishCoalesce(e.data, () => ( ""));
|
|
834
|
-
composition = { start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess',
|
|
852
|
+
composition = { start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _47 => _47.start]), () => ( 0)), length: data.length };
|
|
835
853
|
forward();
|
|
836
854
|
});
|
|
837
855
|
el.addEventListener("compositionend", () => {
|
|
@@ -877,7 +895,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
877
895
|
el.addEventListener("keydown", (e) => {
|
|
878
896
|
if (e.key === "Enter" || e.key === " ") {
|
|
879
897
|
e.preventDefault();
|
|
880
|
-
node.dispatchEvent(new (0,
|
|
898
|
+
node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("click", node, e));
|
|
881
899
|
}
|
|
882
900
|
});
|
|
883
901
|
}
|
|
@@ -902,7 +920,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
902
920
|
if (el.placeholder !== attrs.placeholder) el.placeholder = attrs.placeholder;
|
|
903
921
|
}
|
|
904
922
|
if (attrs.href !== void 0 && el instanceof HTMLAnchorElement) {
|
|
905
|
-
const safeHref =
|
|
923
|
+
const safeHref = _chunkGTQRCY6Wjs.sanitizeUrl.call(void 0, attrs.href);
|
|
906
924
|
if (el.getAttribute("href") !== safeHref) el.setAttribute("href", safeHref);
|
|
907
925
|
}
|
|
908
926
|
if (el instanceof HTMLImageElement) {
|
|
@@ -972,11 +990,96 @@ var Scene = (_class2 = class _Scene {
|
|
|
972
990
|
el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
|
|
973
991
|
}
|
|
974
992
|
}
|
|
993
|
+
this.syncContentProjection(node);
|
|
975
994
|
for (const child of node.children) this.syncA11y(child);
|
|
976
995
|
if (node === this.root) {
|
|
977
996
|
for (const overlay of this.overlayRoot.children) this.syncA11y(overlay);
|
|
978
997
|
}
|
|
979
998
|
}
|
|
999
|
+
/**
|
|
1000
|
+
* Mirror one entity's static text ({@link Entity.getContentProjection}) as a
|
|
1001
|
+
* transparent DOM node positioned over the drawn glyphs. Runs on the a11y
|
|
1002
|
+
* sync cadence; all writes are dirty-checked. Off-viewport projections are
|
|
1003
|
+
* hidden (`display: none`) so text-heavy scenes only materialize what is
|
|
1004
|
+
* visible to the browser's text machinery anyway.
|
|
1005
|
+
*/
|
|
1006
|
+
syncContentProjection(node) {
|
|
1007
|
+
if (!this.contentProjectionEnabled || !this.a11yRoot) return;
|
|
1008
|
+
const projection = node.getContentProjection();
|
|
1009
|
+
let el = this.contentElements.get(node.id);
|
|
1010
|
+
if (!projection || !projection.text) {
|
|
1011
|
+
if (el) {
|
|
1012
|
+
el.remove();
|
|
1013
|
+
this.contentElements.delete(node.id);
|
|
1014
|
+
}
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
if (!el) {
|
|
1018
|
+
el = document.createElement("div");
|
|
1019
|
+
el.setAttribute("data-vecto-content", node.id);
|
|
1020
|
+
const s = el.style;
|
|
1021
|
+
s.position = "absolute";
|
|
1022
|
+
s.transformOrigin = "0 0";
|
|
1023
|
+
s.margin = "0";
|
|
1024
|
+
s.padding = "0";
|
|
1025
|
+
s.color = "transparent";
|
|
1026
|
+
s.whiteSpace = "pre-wrap";
|
|
1027
|
+
s.overflow = "hidden";
|
|
1028
|
+
s.zIndex = "0";
|
|
1029
|
+
el.addEventListener(
|
|
1030
|
+
"wheel",
|
|
1031
|
+
(e2) => {
|
|
1032
|
+
node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("wheel", node, e2));
|
|
1033
|
+
},
|
|
1034
|
+
{ passive: false }
|
|
1035
|
+
);
|
|
1036
|
+
this.a11yRoot.appendChild(el);
|
|
1037
|
+
this.contentElements.set(node.id, el);
|
|
1038
|
+
}
|
|
1039
|
+
if (el.textContent !== projection.text) el.textContent = projection.text;
|
|
1040
|
+
const font = _nullishCoalesce(projection.font, () => ( ""));
|
|
1041
|
+
if (el.style.font !== font) el.style.font = font;
|
|
1042
|
+
const lineHeight = projection.lineHeight !== void 0 ? `${projection.lineHeight}px` : "";
|
|
1043
|
+
if (el.style.lineHeight !== lineHeight) el.style.lineHeight = lineHeight;
|
|
1044
|
+
const hidden = node.interactive ? "true" : null;
|
|
1045
|
+
if (el.getAttribute("aria-hidden") !== hidden) {
|
|
1046
|
+
if (hidden) el.setAttribute("aria-hidden", hidden);
|
|
1047
|
+
else el.removeAttribute("aria-hidden");
|
|
1048
|
+
}
|
|
1049
|
+
const selectable = projection.selectable === true;
|
|
1050
|
+
const pointerEvents = selectable ? "auto" : "none";
|
|
1051
|
+
if (el.style.pointerEvents !== pointerEvents) {
|
|
1052
|
+
el.style.pointerEvents = pointerEvents;
|
|
1053
|
+
el.style.userSelect = selectable ? "text" : "none";
|
|
1054
|
+
el.style.cursor = selectable ? "text" : "";
|
|
1055
|
+
}
|
|
1056
|
+
const { a, b, c, d, e, f } = node.getWorldTransform();
|
|
1057
|
+
el.style.left = `${e}px`;
|
|
1058
|
+
el.style.top = `${f}px`;
|
|
1059
|
+
if (node.width > 0) el.style.width = `${node.width}px`;
|
|
1060
|
+
if (node.height > 0) el.style.height = `${node.height}px`;
|
|
1061
|
+
el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
|
|
1062
|
+
let visible = true;
|
|
1063
|
+
if (node.width > 0 && node.height > 0) {
|
|
1064
|
+
let minX = Infinity;
|
|
1065
|
+
let minY = Infinity;
|
|
1066
|
+
let maxX = -Infinity;
|
|
1067
|
+
let maxY = -Infinity;
|
|
1068
|
+
for (let i = 0; i < 4; i++) {
|
|
1069
|
+
const lx = i & 1 ? node.width : 0;
|
|
1070
|
+
const ly = i & 2 ? node.height : 0;
|
|
1071
|
+
const wx = a * lx + c * ly + e;
|
|
1072
|
+
const wy = b * lx + d * ly + f;
|
|
1073
|
+
if (wx < minX) minX = wx;
|
|
1074
|
+
if (wx > maxX) maxX = wx;
|
|
1075
|
+
if (wy < minY) minY = wy;
|
|
1076
|
+
if (wy > maxY) maxY = wy;
|
|
1077
|
+
}
|
|
1078
|
+
visible = maxX >= 0 && minX <= this.width && maxY >= 0 && minY <= this.height;
|
|
1079
|
+
}
|
|
1080
|
+
const display = visible ? "" : "none";
|
|
1081
|
+
if (el.style.display !== display) el.style.display = display;
|
|
1082
|
+
}
|
|
980
1083
|
enforceA11yDomOrder() {
|
|
981
1084
|
if (!this.a11yRoot) return;
|
|
982
1085
|
this.fullViewportElements.length = 0;
|
|
@@ -1035,12 +1138,12 @@ var Scene = (_class2 = class _Scene {
|
|
|
1035
1138
|
syncOverlayGeometry() {
|
|
1036
1139
|
const parent = this.canvas.parentElement;
|
|
1037
1140
|
if (!parent) return;
|
|
1038
|
-
const canvasRect = _optionalChain([this, 'access',
|
|
1039
|
-
const parentRect = _optionalChain([parent, 'access',
|
|
1040
|
-
const cssWidth = _optionalChain([canvasRect, 'optionalAccess',
|
|
1041
|
-
const cssHeight = _optionalChain([canvasRect, 'optionalAccess',
|
|
1042
|
-
const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess',
|
|
1043
|
-
const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess',
|
|
1141
|
+
const canvasRect = _optionalChain([this, 'access', _48 => _48.canvas, 'access', _49 => _49.getBoundingClientRect, 'optionalCall', _50 => _50()]);
|
|
1142
|
+
const parentRect = _optionalChain([parent, 'access', _51 => _51.getBoundingClientRect, 'optionalCall', _52 => _52()]);
|
|
1143
|
+
const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _53 => _53.width]) || this.canvas.clientWidth || this.width;
|
|
1144
|
+
const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _54 => _54.height]) || this.canvas.clientHeight || this.height;
|
|
1145
|
+
const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _55 => _55.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _56 => _56.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
|
|
1146
|
+
const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _57 => _57.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _58 => _58.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
|
|
1044
1147
|
const scaleX = this.width > 0 ? cssWidth / this.width : 1;
|
|
1045
1148
|
const scaleY = this.height > 0 ? cssHeight / this.height : 1;
|
|
1046
1149
|
for (const root of [this.a11yRoot, this.portalRoot]) {
|
|
@@ -1165,7 +1268,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
1165
1268
|
* (and {@link respectReducedMotion} is on). `0` means uncapped.
|
|
1166
1269
|
*/
|
|
1167
1270
|
effectiveMaxFPS() {
|
|
1168
|
-
const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access',
|
|
1271
|
+
const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _59 => _59.reducedMotionQuery, 'optionalAccess', _60 => _60.matches]);
|
|
1169
1272
|
if (reduced)
|
|
1170
1273
|
return this.maxFPS > 0 ? Math.min(this.maxFPS, REDUCED_MOTION_FPS) : REDUCED_MOTION_FPS;
|
|
1171
1274
|
return this.maxFPS;
|
|
@@ -1191,10 +1294,11 @@ var Scene = (_class2 = class _Scene {
|
|
|
1191
1294
|
this.render(this.renderer, dt, time);
|
|
1192
1295
|
const hasActiveAnimation = this.hasAnyPendingAnimation(this.root) || this.hasAnyPendingAnimation(this.overlayRoot);
|
|
1193
1296
|
const hasInteractive = this.hasAnyInteractive(this.root) || this.hasAnyInteractive(this.overlayRoot);
|
|
1297
|
+
const wantsContentSync = this.contentProjectionEnabled;
|
|
1194
1298
|
const shouldSyncInterval = this.a11ySyncInterval <= 0 || time - this.lastA11ySync >= this.a11ySyncInterval;
|
|
1195
|
-
if ((hasInteractive || this.a11yElements.size > 0) && (shouldSyncInterval || this.a11yPendingSyncAfterAnimation)) {
|
|
1299
|
+
if ((hasInteractive || this.a11yElements.size > 0 || wantsContentSync) && (shouldSyncInterval || this.a11yPendingSyncAfterAnimation)) {
|
|
1196
1300
|
this.lastA11ySync = time;
|
|
1197
|
-
if (hasInteractive) {
|
|
1301
|
+
if (hasInteractive || wantsContentSync) {
|
|
1198
1302
|
this.syncA11y(this.root);
|
|
1199
1303
|
}
|
|
1200
1304
|
this.enforceA11yDomOrder();
|
|
@@ -1342,7 +1446,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
1342
1446
|
}
|
|
1343
1447
|
renderer.clear();
|
|
1344
1448
|
if (isMainRenderer) {
|
|
1345
|
-
_optionalChain([this, 'access',
|
|
1449
|
+
_optionalChain([this, 'access', _61 => _61.pointRenderer, 'optionalAccess', _62 => _62.begin, 'call', _63 => _63()]);
|
|
1346
1450
|
}
|
|
1347
1451
|
const vw = this.width;
|
|
1348
1452
|
const vh = this.height;
|
|
@@ -1470,15 +1574,15 @@ var Scene = (_class2 = class _Scene {
|
|
|
1470
1574
|
if (isMainRenderer) this.reconcilePortals();
|
|
1471
1575
|
renderer.flush();
|
|
1472
1576
|
if (isMainRenderer) {
|
|
1473
|
-
_optionalChain([this, 'access',
|
|
1577
|
+
_optionalChain([this, 'access', _64 => _64.pointRenderer, 'optionalAccess', _65 => _65.flush, 'call', _66 => _66()]);
|
|
1474
1578
|
}
|
|
1475
|
-
_optionalChain([renderer, 'access',
|
|
1579
|
+
_optionalChain([renderer, 'access', _67 => _67.present, 'optionalCall', _68 => _68()]);
|
|
1476
1580
|
}
|
|
1477
1581
|
/**
|
|
1478
1582
|
* Export the current scene state to a lightweight, flat SVG XML string.
|
|
1479
1583
|
*/
|
|
1480
1584
|
toSVG() {
|
|
1481
|
-
const renderer = new (0,
|
|
1585
|
+
const renderer = new (0, _chunkGTQRCY6Wjs.SVGRenderer)(this.width, this.height);
|
|
1482
1586
|
this.render(renderer, 0, 0);
|
|
1483
1587
|
return renderer.toXMLString();
|
|
1484
1588
|
}
|
|
@@ -1491,7 +1595,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
1491
1595
|
if (typeof this.renderer.resize === "function") {
|
|
1492
1596
|
this.renderer.resize(width, height);
|
|
1493
1597
|
}
|
|
1494
|
-
_optionalChain([this, 'access',
|
|
1598
|
+
_optionalChain([this, 'access', _69 => _69.pointRenderer, 'optionalAccess', _70 => _70.resize, 'call', _71 => _71(width, height)]);
|
|
1495
1599
|
if (this.gpuCanvas) {
|
|
1496
1600
|
this.gpuCanvas.width = width;
|
|
1497
1601
|
this.gpuCanvas.height = height;
|
|
@@ -1535,7 +1639,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
1535
1639
|
});
|
|
1536
1640
|
pass.end();
|
|
1537
1641
|
this.device.queue.submit([encoder.finish()]);
|
|
1538
|
-
} catch (
|
|
1642
|
+
} catch (e3) {
|
|
1539
1643
|
}
|
|
1540
1644
|
this.gpuHasContent = false;
|
|
1541
1645
|
}
|
|
@@ -1675,20 +1779,20 @@ function defaultMeasurer() {
|
|
|
1675
1779
|
if (sharedMeasurer === void 0) sharedMeasurer = _chunkBWLNEJJWjs.createCanvasMeasurer.call(void 0, "sans-serif");
|
|
1676
1780
|
return sharedMeasurer;
|
|
1677
1781
|
}
|
|
1678
|
-
var TextEntity = (_class3 = class extends
|
|
1782
|
+
var TextEntity = (_class3 = class extends _chunkDWMQYHNCjs.Entity {
|
|
1679
1783
|
|
|
1680
1784
|
|
|
1681
1785
|
|
|
1682
1786
|
|
|
1683
|
-
|
|
1787
|
+
__init51() {this.nodes = []}
|
|
1684
1788
|
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1789
|
+
__init52() {this.fillStyle = "#94a3b8"}
|
|
1790
|
+
__init53() {this.strokeStyle = null}
|
|
1791
|
+
__init54() {this.hoveredFillStyle = "#ffffff"}
|
|
1792
|
+
__init55() {this.lineWidth = 1}
|
|
1793
|
+
__init56() {this.isHovered = false}
|
|
1690
1794
|
constructor(text, atlas, maxWidth, fontSize = 24) {
|
|
1691
|
-
super();_class3.prototype.
|
|
1795
|
+
super();_class3.prototype.__init51.call(this);_class3.prototype.__init52.call(this);_class3.prototype.__init53.call(this);_class3.prototype.__init54.call(this);_class3.prototype.__init55.call(this);_class3.prototype.__init56.call(this);;
|
|
1692
1796
|
this.text = text;
|
|
1693
1797
|
this.atlas = atlas;
|
|
1694
1798
|
this.fontSize = fontSize;
|
|
@@ -1699,6 +1803,14 @@ var TextEntity = (_class3 = class extends _chunk4BO5XBJLjs.Entity {
|
|
|
1699
1803
|
this.on("hover", () => this.isHovered = true);
|
|
1700
1804
|
this.on("pointerleave", () => this.isHovered = false);
|
|
1701
1805
|
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Mirror the rendered text into the DOM content layer: find-in-page, screen
|
|
1808
|
+
* readers, crawlers, and translation see the same string the canvas draws.
|
|
1809
|
+
*/
|
|
1810
|
+
getContentProjection() {
|
|
1811
|
+
if (!this.text) return null;
|
|
1812
|
+
return { text: this.text, font: `${this.fontSize}px sans-serif` };
|
|
1813
|
+
}
|
|
1702
1814
|
/**
|
|
1703
1815
|
* Replace the text content. Runs the **cold** measurement pass (re-segment +
|
|
1704
1816
|
* re-measure) since the glyphs changed, then re-lays out.
|
|
@@ -1773,17 +1885,17 @@ var TextEntity = (_class3 = class extends _chunk4BO5XBJLjs.Entity {
|
|
|
1773
1885
|
}, _class3);
|
|
1774
1886
|
|
|
1775
1887
|
// src/components/GridTextEntity.ts
|
|
1776
|
-
var GridTextEntity = (_class4 = class extends
|
|
1888
|
+
var GridTextEntity = (_class4 = class extends _chunkDWMQYHNCjs.Entity {
|
|
1777
1889
|
|
|
1778
|
-
|
|
1779
|
-
|
|
1890
|
+
__init57() {this.fillStyle = "#ffffff"}
|
|
1891
|
+
__init58() {this.grid = []}
|
|
1780
1892
|
// Array of rows
|
|
1781
|
-
|
|
1782
|
-
|
|
1893
|
+
__init59() {this.cols = 0}
|
|
1894
|
+
__init60() {this.rows = 0}
|
|
1783
1895
|
|
|
1784
1896
|
|
|
1785
1897
|
constructor(_atlas, fontSize = 10) {
|
|
1786
|
-
super();_class4.prototype.
|
|
1898
|
+
super();_class4.prototype.__init57.call(this);_class4.prototype.__init58.call(this);_class4.prototype.__init59.call(this);_class4.prototype.__init60.call(this);;
|
|
1787
1899
|
this.fontSize = fontSize;
|
|
1788
1900
|
this.charWidth = fontSize * 1;
|
|
1789
1901
|
this.charHeight = fontSize * 1.1;
|
|
@@ -1792,7 +1904,7 @@ var GridTextEntity = (_class4 = class extends _chunk4BO5XBJLjs.Entity {
|
|
|
1792
1904
|
updateGrid(ascii) {
|
|
1793
1905
|
this.grid = ascii;
|
|
1794
1906
|
this.rows = ascii.length;
|
|
1795
|
-
this.cols = _optionalChain([ascii, 'access',
|
|
1907
|
+
this.cols = _optionalChain([ascii, 'access', _72 => _72[0], 'optionalAccess', _73 => _73.length]) || 0;
|
|
1796
1908
|
}
|
|
1797
1909
|
isPointInside(_globalX, _globalY) {
|
|
1798
1910
|
return false;
|
|
@@ -1867,7 +1979,7 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
|
|
|
1867
1979
|
const ey = py - cy;
|
|
1868
1980
|
return ex * ex + ey * ey;
|
|
1869
1981
|
}
|
|
1870
|
-
var SplineEntity = (_class5 = class extends
|
|
1982
|
+
var SplineEntity = (_class5 = class extends _chunkDWMQYHNCjs.Entity {
|
|
1871
1983
|
|
|
1872
1984
|
|
|
1873
1985
|
|
|
@@ -1875,23 +1987,23 @@ var SplineEntity = (_class5 = class extends _chunk4BO5XBJLjs.Entity {
|
|
|
1875
1987
|
|
|
1876
1988
|
|
|
1877
1989
|
|
|
1878
|
-
|
|
1879
|
-
|
|
1990
|
+
__init61() {this.offscreen = null}
|
|
1991
|
+
__init62() {this.baked = false}
|
|
1880
1992
|
/** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
|
|
1881
|
-
|
|
1882
|
-
|
|
1993
|
+
__init63() {this.bakedWidth = 0}
|
|
1994
|
+
__init64() {this.bakedHeight = 0}
|
|
1883
1995
|
/** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
|
|
1884
1996
|
|
|
1885
1997
|
/** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
|
|
1886
|
-
|
|
1998
|
+
__init65() {this.polylines = null}
|
|
1887
1999
|
/**
|
|
1888
2000
|
* When `true`, the renderer draws a rounded-rect outline of the entity's
|
|
1889
2001
|
* local bounds after painting the curves. Useful for drag feedback and
|
|
1890
2002
|
* debugging hit areas. Defaults to `false`.
|
|
1891
2003
|
*/
|
|
1892
|
-
|
|
2004
|
+
__init66() {this.showBounds = false}
|
|
1893
2005
|
constructor(doc, opts = {}) {
|
|
1894
|
-
super();_class5.prototype.
|
|
2006
|
+
super();_class5.prototype.__init61.call(this);_class5.prototype.__init62.call(this);_class5.prototype.__init63.call(this);_class5.prototype.__init64.call(this);_class5.prototype.__init65.call(this);_class5.prototype.__init66.call(this);;
|
|
1895
2007
|
this.doc = doc;
|
|
1896
2008
|
this.lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
|
|
1897
2009
|
this.cache = _nullishCoalesce(opts.cache, () => ( true));
|
|
@@ -1902,7 +2014,7 @@ var SplineEntity = (_class5 = class extends _chunk4BO5XBJLjs.Entity {
|
|
|
1902
2014
|
this.width = this.bounds.width;
|
|
1903
2015
|
this.height = this.bounds.height;
|
|
1904
2016
|
const isGradient = (c) => c !== null && !Array.isArray(c);
|
|
1905
|
-
this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access',
|
|
2017
|
+
this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _74 => _74.doc, 'access', _75 => _75.equations, 'optionalAccess', _76 => _76.some, 'call', _77 => _77((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _78 => _78.doc, 'access', _79 => _79.paths, 'optionalAccess', _80 => _80.some, 'call', _81 => _81((p) => isGradient(p.color_rgb))]), () => ( false)));
|
|
1906
2018
|
this.interactive = true;
|
|
1907
2019
|
}
|
|
1908
2020
|
computeBounds() {
|
|
@@ -2144,9 +2256,9 @@ async function loadSpline(url) {
|
|
|
2144
2256
|
// src/math/SpatialHashGrid.ts
|
|
2145
2257
|
var SpatialHashGrid = (_class6 = class {
|
|
2146
2258
|
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
constructor(cellSize = 64) {;_class6.prototype.
|
|
2259
|
+
__init67() {this.grid = /* @__PURE__ */ new Map()}
|
|
2260
|
+
__init68() {this.entityCells = /* @__PURE__ */ new Map()}
|
|
2261
|
+
constructor(cellSize = 64) {;_class6.prototype.__init67.call(this);_class6.prototype.__init68.call(this);
|
|
2150
2262
|
this.cellSize = cellSize;
|
|
2151
2263
|
}
|
|
2152
2264
|
hash(cx, cy) {
|
|
@@ -2200,7 +2312,7 @@ var SpatialHashGrid = (_class6 = class {
|
|
|
2200
2312
|
const keys = this.entityCells.get(id);
|
|
2201
2313
|
if (!keys) return;
|
|
2202
2314
|
for (const key of keys) {
|
|
2203
|
-
_optionalChain([this, 'access',
|
|
2315
|
+
_optionalChain([this, 'access', _82 => _82.grid, 'access', _83 => _83.get, 'call', _84 => _84(key), 'optionalAccess', _85 => _85.delete, 'call', _86 => _86(id)]);
|
|
2204
2316
|
}
|
|
2205
2317
|
this.entityCells.delete(id);
|
|
2206
2318
|
}
|
|
@@ -2236,20 +2348,20 @@ var SpatialHashGrid = (_class6 = class {
|
|
|
2236
2348
|
}, _class6);
|
|
2237
2349
|
|
|
2238
2350
|
// src/tree/DOMPortalEntity.ts
|
|
2239
|
-
var DOMPortalEntity = (_class7 = class extends
|
|
2351
|
+
var DOMPortalEntity = (_class7 = class extends _chunkDWMQYHNCjs.Entity {
|
|
2240
2352
|
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2353
|
+
__init69() {this.isDOMPortal = true}
|
|
2354
|
+
__init70() {this.domListeners = []}
|
|
2355
|
+
__init71() {this.resizeObserver = null}
|
|
2356
|
+
__init72() {this.cachedWidth = 100}
|
|
2357
|
+
__init73() {this.cachedHeight = 100}
|
|
2358
|
+
__init74() {this.lastWidth = ""}
|
|
2359
|
+
__init75() {this.lastHeight = ""}
|
|
2360
|
+
__init76() {this.lastTransform = ""}
|
|
2361
|
+
__init77() {this.lastZIndex = ""}
|
|
2362
|
+
__init78() {this.lastOpacity = ""}
|
|
2251
2363
|
constructor(domElement, width, height, id) {
|
|
2252
|
-
super(id);_class7.prototype.
|
|
2364
|
+
super(id);_class7.prototype.__init69.call(this);_class7.prototype.__init70.call(this);_class7.prototype.__init71.call(this);_class7.prototype.__init72.call(this);_class7.prototype.__init73.call(this);_class7.prototype.__init74.call(this);_class7.prototype.__init75.call(this);_class7.prototype.__init76.call(this);_class7.prototype.__init77.call(this);_class7.prototype.__init78.call(this);;
|
|
2253
2365
|
this.domElement = domElement;
|
|
2254
2366
|
this.width = _nullishCoalesce(width, () => ( 0));
|
|
2255
2367
|
this.height = _nullishCoalesce(height, () => ( 0));
|
|
@@ -2271,7 +2383,7 @@ var DOMPortalEntity = (_class7 = class extends _chunk4BO5XBJLjs.Entity {
|
|
|
2271
2383
|
const events = ["click", "pointerdown", "pointerup", "pointermove", "wheel"];
|
|
2272
2384
|
for (const type of events) {
|
|
2273
2385
|
const handler = (e) => {
|
|
2274
|
-
this.dispatchEvent(new (0,
|
|
2386
|
+
this.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)(type, this, e));
|
|
2275
2387
|
};
|
|
2276
2388
|
this.domElement.addEventListener(type, handler);
|
|
2277
2389
|
this.domListeners.push({ type, handler, capture: false });
|
|
@@ -2282,7 +2394,7 @@ var DOMPortalEntity = (_class7 = class extends _chunk4BO5XBJLjs.Entity {
|
|
|
2282
2394
|
];
|
|
2283
2395
|
for (const { native, vecto } of hoverEvents) {
|
|
2284
2396
|
const handler = (e) => {
|
|
2285
|
-
this.dispatchEvent(new (0,
|
|
2397
|
+
this.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)(vecto, this, e, false));
|
|
2286
2398
|
};
|
|
2287
2399
|
this.domElement.addEventListener(native, handler);
|
|
2288
2400
|
this.domListeners.push({ type: native, handler, capture: false });
|
|
@@ -2290,7 +2402,7 @@ var DOMPortalEntity = (_class7 = class extends _chunk4BO5XBJLjs.Entity {
|
|
|
2290
2402
|
const focusEvents = ["focus", "blur"];
|
|
2291
2403
|
for (const type of focusEvents) {
|
|
2292
2404
|
const handler = (e) => {
|
|
2293
|
-
this.dispatchEvent(new (0,
|
|
2405
|
+
this.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)(type, this, e, true));
|
|
2294
2406
|
};
|
|
2295
2407
|
this.domElement.addEventListener(type, handler, true);
|
|
2296
2408
|
this.domListeners.push({ type, handler, capture: true });
|
|
@@ -2329,8 +2441,8 @@ var DOMPortalEntity = (_class7 = class extends _chunk4BO5XBJLjs.Entity {
|
|
|
2329
2441
|
}, _class7);
|
|
2330
2442
|
|
|
2331
2443
|
// src/index.ts
|
|
2332
|
-
Scene.registerWebGLPointRendererCreator(
|
|
2333
|
-
Scene.registerWebGPUParticleSystemManager(
|
|
2444
|
+
Scene.registerWebGLPointRendererCreator(_chunkGTQRCY6Wjs.createWebGLPointRenderer);
|
|
2445
|
+
Scene.registerWebGPUParticleSystemManager(_chunkGTQRCY6Wjs.WebGPUParticleSystemManager);
|
|
2334
2446
|
|
|
2335
2447
|
|
|
2336
2448
|
|
|
@@ -2375,4 +2487,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkGADBN53Zjs.WebGPUParticleSystemM
|
|
|
2375
2487
|
|
|
2376
2488
|
|
|
2377
2489
|
|
|
2378
|
-
exports.ArabicShaper = _chunkCTZQOM5Zjs.ArabicShaper; exports.BidiResolver = _chunkCTZQOM5Zjs.BidiResolver; exports.CanvasRenderer =
|
|
2490
|
+
exports.ArabicShaper = _chunkCTZQOM5Zjs.ArabicShaper; exports.BidiResolver = _chunkCTZQOM5Zjs.BidiResolver; exports.CanvasRenderer = _chunkGTQRCY6Wjs.CanvasRenderer; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Easing = _chunkDWMQYHNCjs.Easing; exports.Entity = _chunkDWMQYHNCjs.Entity; exports.GridTextEntity = GridTextEntity; exports.LayoutEngine = _chunkBWLNEJJWjs.LayoutEngine; exports.LayoutResultBuffer = _chunkBWLNEJJWjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunkCTZQOM5Zjs.LayoutWorkerManager; exports.MSDFFont = _chunkDWMQYHNCjs.MSDFFont; exports.MSDFTextEntity = _chunkDWMQYHNCjs.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.SVGEntity = _chunkDWMQYHNCjs.SVGEntity; exports.SVGRenderer = _chunkGTQRCY6Wjs.SVGRenderer; exports.Scene = Scene; exports.SpatialHashGrid = SpatialHashGrid; exports.SplineEntity = SplineEntity; exports.SpringDriver = _chunkDWMQYHNCjs.SpringDriver; exports.SpringPhysics = _chunkDWMQYHNCjs.SpringPhysics; exports.TextEntity = TextEntity; exports.TweenDriver = _chunkDWMQYHNCjs.TweenDriver; exports.VectoJSEvent = _chunkDWMQYHNCjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkGTQRCY6Wjs.WebGPUParticleSystemManager; exports.computeLineSegments = _chunkBWLNEJJWjs.computeLineSegments; exports.createCanvasMeasurer = _chunkBWLNEJJWjs.createCanvasMeasurer; exports.createWebGLPointRenderer = _chunkGTQRCY6Wjs.createWebGLPointRenderer; exports.isSafeUrl = _chunkGTQRCY6Wjs.isSafeUrl; exports.isTweenConfig = _chunkDWMQYHNCjs.isTweenConfig; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkGTQRCY6Wjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkGTQRCY6Wjs.sanitizeUrl;
|
package/dist/index.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
isSafeUrl,
|
|
13
13
|
parseColorToRGBA,
|
|
14
14
|
sanitizeUrl
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-HVJHXIKW.mjs";
|
|
16
16
|
import {
|
|
17
17
|
Easing,
|
|
18
18
|
Entity,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
TweenDriver,
|
|
25
25
|
VectoJSEvent,
|
|
26
26
|
isTweenConfig
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-YVN3PJRC.mjs";
|
|
28
28
|
import {
|
|
29
29
|
ArabicShaper,
|
|
30
30
|
BidiResolver,
|
|
@@ -296,6 +296,11 @@ var ComputeParticleEntity = class extends Entity {
|
|
|
296
296
|
|
|
297
297
|
// src/tree/Scene.ts
|
|
298
298
|
var REDUCED_MOTION_FPS = 30;
|
|
299
|
+
function parseInlinePx(value) {
|
|
300
|
+
if (!value || !value.endsWith("px")) return null;
|
|
301
|
+
const n = parseFloat(value);
|
|
302
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
303
|
+
}
|
|
299
304
|
var Scene = class _Scene {
|
|
300
305
|
static webglCreator = null;
|
|
301
306
|
static webgpuManagerClass = null;
|
|
@@ -350,6 +355,9 @@ var Scene = class _Scene {
|
|
|
350
355
|
// server-side (e.g. headless layout / vector export) without jsdom.
|
|
351
356
|
a11yRoot;
|
|
352
357
|
a11yElements = /* @__PURE__ */ new Map();
|
|
358
|
+
/** DOM nodes mirroring static text content, keyed by entity id. */
|
|
359
|
+
contentElements = /* @__PURE__ */ new Map();
|
|
360
|
+
contentProjectionEnabled = true;
|
|
353
361
|
resizeHandler;
|
|
354
362
|
focusedA11yElement = null;
|
|
355
363
|
caretBlinkTimer = null;
|
|
@@ -398,8 +406,10 @@ var Scene = class _Scene {
|
|
|
398
406
|
this.debugA11y = options.debugA11y ?? false;
|
|
399
407
|
this.disableWindowResize = options.disableWindowResize ?? false;
|
|
400
408
|
if (this.disableWindowResize) {
|
|
401
|
-
|
|
402
|
-
|
|
409
|
+
const styleWidth = parseInlinePx(canvas.style?.width);
|
|
410
|
+
const styleHeight = parseInlinePx(canvas.style?.height);
|
|
411
|
+
this.width = styleWidth ?? (canvas.width || canvas.clientWidth || 0);
|
|
412
|
+
this.height = styleHeight ?? (canvas.height || canvas.clientHeight || 0);
|
|
403
413
|
} else {
|
|
404
414
|
this.width = typeof window !== "undefined" ? window.innerWidth : canvas.clientWidth || canvas.width || 800;
|
|
405
415
|
this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
|
|
@@ -411,6 +421,7 @@ var Scene = class _Scene {
|
|
|
411
421
|
this.autoThrottle = options.autoThrottle ?? true;
|
|
412
422
|
this.particleBackend = options.particleBackend ?? "auto";
|
|
413
423
|
this.a11ySyncInterval = options.a11ySyncInterval ?? 0;
|
|
424
|
+
this.contentProjectionEnabled = options.contentProjection ?? true;
|
|
414
425
|
this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
|
|
415
426
|
this.root = new class RootEntity extends Entity {
|
|
416
427
|
isPointInside() {
|
|
@@ -564,6 +575,11 @@ var Scene = class _Scene {
|
|
|
564
575
|
* @param entity - The subtree whose shadow nodes should be removed.
|
|
565
576
|
*/
|
|
566
577
|
detachA11y(entity) {
|
|
578
|
+
const contentEl = this.contentElements.get(entity.id);
|
|
579
|
+
if (contentEl) {
|
|
580
|
+
contentEl.remove();
|
|
581
|
+
this.contentElements.delete(entity.id);
|
|
582
|
+
}
|
|
567
583
|
this.removeA11yRecursively(entity);
|
|
568
584
|
}
|
|
569
585
|
/**
|
|
@@ -610,6 +626,8 @@ var Scene = class _Scene {
|
|
|
610
626
|
this.a11yRoot?.remove();
|
|
611
627
|
this.portalRoot?.remove();
|
|
612
628
|
this.a11yElements.clear();
|
|
629
|
+
for (const el of this.contentElements.values()) el.remove();
|
|
630
|
+
this.contentElements.clear();
|
|
613
631
|
this.pointRenderer?.destroy();
|
|
614
632
|
this.renderer.dispose?.();
|
|
615
633
|
this.glCanvas?.remove();
|
|
@@ -972,11 +990,96 @@ var Scene = class _Scene {
|
|
|
972
990
|
el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
|
|
973
991
|
}
|
|
974
992
|
}
|
|
993
|
+
this.syncContentProjection(node);
|
|
975
994
|
for (const child of node.children) this.syncA11y(child);
|
|
976
995
|
if (node === this.root) {
|
|
977
996
|
for (const overlay of this.overlayRoot.children) this.syncA11y(overlay);
|
|
978
997
|
}
|
|
979
998
|
}
|
|
999
|
+
/**
|
|
1000
|
+
* Mirror one entity's static text ({@link Entity.getContentProjection}) as a
|
|
1001
|
+
* transparent DOM node positioned over the drawn glyphs. Runs on the a11y
|
|
1002
|
+
* sync cadence; all writes are dirty-checked. Off-viewport projections are
|
|
1003
|
+
* hidden (`display: none`) so text-heavy scenes only materialize what is
|
|
1004
|
+
* visible to the browser's text machinery anyway.
|
|
1005
|
+
*/
|
|
1006
|
+
syncContentProjection(node) {
|
|
1007
|
+
if (!this.contentProjectionEnabled || !this.a11yRoot) return;
|
|
1008
|
+
const projection = node.getContentProjection();
|
|
1009
|
+
let el = this.contentElements.get(node.id);
|
|
1010
|
+
if (!projection || !projection.text) {
|
|
1011
|
+
if (el) {
|
|
1012
|
+
el.remove();
|
|
1013
|
+
this.contentElements.delete(node.id);
|
|
1014
|
+
}
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
if (!el) {
|
|
1018
|
+
el = document.createElement("div");
|
|
1019
|
+
el.setAttribute("data-vecto-content", node.id);
|
|
1020
|
+
const s = el.style;
|
|
1021
|
+
s.position = "absolute";
|
|
1022
|
+
s.transformOrigin = "0 0";
|
|
1023
|
+
s.margin = "0";
|
|
1024
|
+
s.padding = "0";
|
|
1025
|
+
s.color = "transparent";
|
|
1026
|
+
s.whiteSpace = "pre-wrap";
|
|
1027
|
+
s.overflow = "hidden";
|
|
1028
|
+
s.zIndex = "0";
|
|
1029
|
+
el.addEventListener(
|
|
1030
|
+
"wheel",
|
|
1031
|
+
(e2) => {
|
|
1032
|
+
node.dispatchEvent(new VectoJSEvent("wheel", node, e2));
|
|
1033
|
+
},
|
|
1034
|
+
{ passive: false }
|
|
1035
|
+
);
|
|
1036
|
+
this.a11yRoot.appendChild(el);
|
|
1037
|
+
this.contentElements.set(node.id, el);
|
|
1038
|
+
}
|
|
1039
|
+
if (el.textContent !== projection.text) el.textContent = projection.text;
|
|
1040
|
+
const font = projection.font ?? "";
|
|
1041
|
+
if (el.style.font !== font) el.style.font = font;
|
|
1042
|
+
const lineHeight = projection.lineHeight !== void 0 ? `${projection.lineHeight}px` : "";
|
|
1043
|
+
if (el.style.lineHeight !== lineHeight) el.style.lineHeight = lineHeight;
|
|
1044
|
+
const hidden = node.interactive ? "true" : null;
|
|
1045
|
+
if (el.getAttribute("aria-hidden") !== hidden) {
|
|
1046
|
+
if (hidden) el.setAttribute("aria-hidden", hidden);
|
|
1047
|
+
else el.removeAttribute("aria-hidden");
|
|
1048
|
+
}
|
|
1049
|
+
const selectable = projection.selectable === true;
|
|
1050
|
+
const pointerEvents = selectable ? "auto" : "none";
|
|
1051
|
+
if (el.style.pointerEvents !== pointerEvents) {
|
|
1052
|
+
el.style.pointerEvents = pointerEvents;
|
|
1053
|
+
el.style.userSelect = selectable ? "text" : "none";
|
|
1054
|
+
el.style.cursor = selectable ? "text" : "";
|
|
1055
|
+
}
|
|
1056
|
+
const { a, b, c, d, e, f } = node.getWorldTransform();
|
|
1057
|
+
el.style.left = `${e}px`;
|
|
1058
|
+
el.style.top = `${f}px`;
|
|
1059
|
+
if (node.width > 0) el.style.width = `${node.width}px`;
|
|
1060
|
+
if (node.height > 0) el.style.height = `${node.height}px`;
|
|
1061
|
+
el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
|
|
1062
|
+
let visible = true;
|
|
1063
|
+
if (node.width > 0 && node.height > 0) {
|
|
1064
|
+
let minX = Infinity;
|
|
1065
|
+
let minY = Infinity;
|
|
1066
|
+
let maxX = -Infinity;
|
|
1067
|
+
let maxY = -Infinity;
|
|
1068
|
+
for (let i = 0; i < 4; i++) {
|
|
1069
|
+
const lx = i & 1 ? node.width : 0;
|
|
1070
|
+
const ly = i & 2 ? node.height : 0;
|
|
1071
|
+
const wx = a * lx + c * ly + e;
|
|
1072
|
+
const wy = b * lx + d * ly + f;
|
|
1073
|
+
if (wx < minX) minX = wx;
|
|
1074
|
+
if (wx > maxX) maxX = wx;
|
|
1075
|
+
if (wy < minY) minY = wy;
|
|
1076
|
+
if (wy > maxY) maxY = wy;
|
|
1077
|
+
}
|
|
1078
|
+
visible = maxX >= 0 && minX <= this.width && maxY >= 0 && minY <= this.height;
|
|
1079
|
+
}
|
|
1080
|
+
const display = visible ? "" : "none";
|
|
1081
|
+
if (el.style.display !== display) el.style.display = display;
|
|
1082
|
+
}
|
|
980
1083
|
enforceA11yDomOrder() {
|
|
981
1084
|
if (!this.a11yRoot) return;
|
|
982
1085
|
this.fullViewportElements.length = 0;
|
|
@@ -1191,10 +1294,11 @@ var Scene = class _Scene {
|
|
|
1191
1294
|
this.render(this.renderer, dt, time);
|
|
1192
1295
|
const hasActiveAnimation = this.hasAnyPendingAnimation(this.root) || this.hasAnyPendingAnimation(this.overlayRoot);
|
|
1193
1296
|
const hasInteractive = this.hasAnyInteractive(this.root) || this.hasAnyInteractive(this.overlayRoot);
|
|
1297
|
+
const wantsContentSync = this.contentProjectionEnabled;
|
|
1194
1298
|
const shouldSyncInterval = this.a11ySyncInterval <= 0 || time - this.lastA11ySync >= this.a11ySyncInterval;
|
|
1195
|
-
if ((hasInteractive || this.a11yElements.size > 0) && (shouldSyncInterval || this.a11yPendingSyncAfterAnimation)) {
|
|
1299
|
+
if ((hasInteractive || this.a11yElements.size > 0 || wantsContentSync) && (shouldSyncInterval || this.a11yPendingSyncAfterAnimation)) {
|
|
1196
1300
|
this.lastA11ySync = time;
|
|
1197
|
-
if (hasInteractive) {
|
|
1301
|
+
if (hasInteractive || wantsContentSync) {
|
|
1198
1302
|
this.syncA11y(this.root);
|
|
1199
1303
|
}
|
|
1200
1304
|
this.enforceA11yDomOrder();
|
|
@@ -1699,6 +1803,14 @@ var TextEntity = class extends Entity {
|
|
|
1699
1803
|
this.on("hover", () => this.isHovered = true);
|
|
1700
1804
|
this.on("pointerleave", () => this.isHovered = false);
|
|
1701
1805
|
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Mirror the rendered text into the DOM content layer: find-in-page, screen
|
|
1808
|
+
* readers, crawlers, and translation see the same string the canvas draws.
|
|
1809
|
+
*/
|
|
1810
|
+
getContentProjection() {
|
|
1811
|
+
if (!this.text) return null;
|
|
1812
|
+
return { text: this.text, font: `${this.fontSize}px sans-serif` };
|
|
1813
|
+
}
|
|
1702
1814
|
/**
|
|
1703
1815
|
* Replace the text content. Runs the **cold** measurement pass (re-segment +
|
|
1704
1816
|
* re-measure) since the glyphs changed, then re-lays out.
|
package/dist/renderer.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var _chunkGTQRCY6Wjs = require('./chunk-GTQRCY6W.js');
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
exports.CanvasRenderer =
|
|
14
|
+
exports.CanvasRenderer = _chunkGTQRCY6Wjs.CanvasRenderer; exports.SVGRenderer = _chunkGTQRCY6Wjs.SVGRenderer; exports.WebGPUParticleSystemManager = _chunkGTQRCY6Wjs.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkGTQRCY6Wjs.createWebGLPointRenderer; exports.parseColorToRGBA = _chunkGTQRCY6Wjs.parseColorToRGBA;
|
package/dist/renderer.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Entity } from '../tree/Entity';
|
|
1
|
+
import { Entity, type ContentProjection } from '../tree/Entity';
|
|
2
2
|
import { MSDFFont } from './MSDFFont';
|
|
3
3
|
export interface MSDFTextEntityOptions {
|
|
4
4
|
font: MSDFFont;
|
|
@@ -33,6 +33,11 @@ export declare class MSDFTextEntity extends Entity {
|
|
|
33
33
|
setMaxWidth(maxWidth: number): void;
|
|
34
34
|
setText(text: string): void;
|
|
35
35
|
private queueLayout;
|
|
36
|
+
/**
|
|
37
|
+
* Mirror the rendered text into the DOM content layer: find-in-page, screen
|
|
38
|
+
* readers, crawlers, and translation see the same string the canvas draws.
|
|
39
|
+
*/
|
|
40
|
+
getContentProjection(): ContentProjection | null;
|
|
36
41
|
isPointInside(globalX: number, globalY: number): boolean;
|
|
37
42
|
render(renderer: any): void;
|
|
38
43
|
destroy(): void;
|
package/dist/text.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunkDWMQYHNCjs = require('./chunk-DWMQYHNC.js');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
@@ -13,4 +13,4 @@ var _chunkCTZQOM5Zjs = require('./chunk-CTZQOM5Z.js');
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
exports.ArabicShaper = _chunkCTZQOM5Zjs.ArabicShaper; exports.BidiResolver = _chunkCTZQOM5Zjs.BidiResolver; exports.MSDFFont =
|
|
16
|
+
exports.ArabicShaper = _chunkCTZQOM5Zjs.ArabicShaper; exports.BidiResolver = _chunkCTZQOM5Zjs.BidiResolver; exports.MSDFFont = _chunkDWMQYHNCjs.MSDFFont; exports.MSDFTextEntity = _chunkDWMQYHNCjs.MSDFTextEntity; exports.SVGEntity = _chunkDWMQYHNCjs.SVGEntity;
|
package/dist/text.mjs
CHANGED
package/dist/tree/Entity.d.ts
CHANGED
|
@@ -52,6 +52,26 @@ export interface BatchRect {
|
|
|
52
52
|
/** CSS fill color. */
|
|
53
53
|
color: string;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Static text an {@link Entity} exposes for DOM content projection, returned
|
|
57
|
+
* from {@link Entity.getContentProjection}. The Scene mirrors it as a
|
|
58
|
+
* transparent, position-synced DOM node so browser-native text machinery —
|
|
59
|
+
* find-in-page, screen readers, SEO crawlers, translation, `#:~:text=`
|
|
60
|
+
* fragments — operates on canvas-rendered text.
|
|
61
|
+
*/
|
|
62
|
+
export interface ContentProjection {
|
|
63
|
+
/** The plain text content as rendered (line breaks as `\n`). */
|
|
64
|
+
text: string;
|
|
65
|
+
/** CSS font shorthand matching the drawn glyphs, e.g. `'24px sans-serif'`. */
|
|
66
|
+
font?: string;
|
|
67
|
+
/** Line height in px, when it differs from the font's default. */
|
|
68
|
+
lineHeight?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Allow native mouse selection on the projected text. Off by default so the
|
|
71
|
+
* projection never intercepts pointer input meant for the canvas.
|
|
72
|
+
*/
|
|
73
|
+
selectable?: boolean;
|
|
74
|
+
}
|
|
55
75
|
/**
|
|
56
76
|
* Semantic attributes an {@link Entity} can project into the accessibility /
|
|
57
77
|
* automation shadow layer maintained by {@link Scene}.
|
|
@@ -453,6 +473,17 @@ export declare abstract class Entity {
|
|
|
453
473
|
* @returns The rectangle to batch, or `null` for the normal render path.
|
|
454
474
|
*/
|
|
455
475
|
getBatchRect(): BatchRect | null;
|
|
476
|
+
/**
|
|
477
|
+
* Opt into DOM content projection for entities that render static text.
|
|
478
|
+
* The {@link Scene} mirrors the returned text as a transparent DOM node
|
|
479
|
+
* positioned over the drawn glyphs, making canvas text findable (Ctrl+F),
|
|
480
|
+
* readable by screen readers and crawlers, translatable, and — when
|
|
481
|
+
* `selectable` is set — natively selectable. Returns `null` by default.
|
|
482
|
+
* Read on the a11y sync cadence, so text changes propagate automatically.
|
|
483
|
+
*
|
|
484
|
+
* @returns The projection descriptor, or `null` to project nothing.
|
|
485
|
+
*/
|
|
486
|
+
getContentProjection(): ContentProjection | null;
|
|
456
487
|
/**
|
|
457
488
|
* Whether this entity still has a queued/running tween animation, or an
|
|
458
489
|
* active {@link setTransition}/{@link animateTo}/{@link springTo} property
|
package/dist/tree/Scene.d.ts
CHANGED
|
@@ -75,6 +75,14 @@ export interface SceneOptions {
|
|
|
75
75
|
* and not marked dirty) to save power/CPU. Default is `true`.
|
|
76
76
|
*/
|
|
77
77
|
autoThrottle?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Mirror static text from entities implementing
|
|
80
|
+
* {@link Entity.getContentProjection} as transparent, position-synced DOM
|
|
81
|
+
* nodes, so find-in-page, screen readers, crawlers, and translation work on
|
|
82
|
+
* canvas-rendered text. Default is `true`; disable for purely decorative
|
|
83
|
+
* scenes to skip the sync walk.
|
|
84
|
+
*/
|
|
85
|
+
contentProjection?: boolean;
|
|
78
86
|
}
|
|
79
87
|
/** Frame-rate the loop is capped to when the OS requests reduced motion. */
|
|
80
88
|
export declare const REDUCED_MOTION_FPS = 30;
|
|
@@ -147,6 +155,9 @@ export declare class Scene {
|
|
|
147
155
|
private a11yPendingSyncAfterAnimation;
|
|
148
156
|
private a11yRoot;
|
|
149
157
|
private a11yElements;
|
|
158
|
+
/** DOM nodes mirroring static text content, keyed by entity id. */
|
|
159
|
+
private contentElements;
|
|
160
|
+
private contentProjectionEnabled;
|
|
150
161
|
private resizeHandler;
|
|
151
162
|
private focusedA11yElement;
|
|
152
163
|
private caretBlinkTimer;
|
|
@@ -269,6 +280,14 @@ export declare class Scene {
|
|
|
269
280
|
/** True when any node in the subtree is interactive (drives a11y sync). */
|
|
270
281
|
private hasAnyInteractive;
|
|
271
282
|
private syncA11y;
|
|
283
|
+
/**
|
|
284
|
+
* Mirror one entity's static text ({@link Entity.getContentProjection}) as a
|
|
285
|
+
* transparent DOM node positioned over the drawn glyphs. Runs on the a11y
|
|
286
|
+
* sync cadence; all writes are dirty-checked. Off-viewport projections are
|
|
287
|
+
* hidden (`display: none`) so text-heavy scenes only materialize what is
|
|
288
|
+
* visible to the browser's text machinery anyway.
|
|
289
|
+
*/
|
|
290
|
+
private syncContentProjection;
|
|
272
291
|
private enforceA11yDomOrder;
|
|
273
292
|
/** Keep DOM/WebGL overlay layers aligned with the canvas's CSS box. */
|
|
274
293
|
private syncOverlayGeometry;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vectojs/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -53,7 +53,8 @@
|
|
|
53
53
|
],
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "node scripts/build-worker.js && tsup && tsc -p tsconfig.build.json",
|
|
56
|
-
"test": "vitest run"
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:e2e": "bun e2e/hidpi.e2e.ts"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {},
|
|
59
60
|
"devDependencies": {
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
"esbuild": "^0.21.5",
|
|
62
63
|
"jsdom": "^29.1.1",
|
|
63
64
|
"tsup": "^8.3.5",
|
|
64
|
-
"vitest": "^4.1.9"
|
|
65
|
+
"vitest": "^4.1.9",
|
|
66
|
+
"puppeteer-core": "^22.12.1"
|
|
65
67
|
}
|
|
66
68
|
}
|