@vectojs/core 0.2.5 → 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.
@@ -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
@@ -41,9 +41,9 @@ export interface SceneOptions {
41
41
  debugA11y?: boolean;
42
42
  /**
43
43
  * Cap the render loop to at most this many frames per second (power saving —
44
- * e.g. a quieter fan in a library). `0` (default) means uncapped (native
45
- * refresh rate). Continuous animations still run, just less often. Also
46
- * settable later via {@link Scene.maxFPS}.
44
+ * e.g. a quieter fan in a library). `0` means uncapped (native refresh
45
+ * rate). Defaults to `60` (`0` under test runners). Continuous animations
46
+ * still run, just less often. Also settable later via {@link Scene.maxFPS}.
47
47
  */
48
48
  maxFPS?: number;
49
49
  /**
@@ -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;
@@ -177,6 +188,8 @@ export declare class Scene {
177
188
  private initializingWebGPU;
178
189
  private gpuCanvas;
179
190
  private gpuContext;
191
+ /** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
192
+ private gpuHasContent;
180
193
  private mouseX;
181
194
  private mouseY;
182
195
  private pointerMoveListener;
@@ -267,6 +280,14 @@ export declare class Scene {
267
280
  /** True when any node in the subtree is interactive (drives a11y sync). */
268
281
  private hasAnyInteractive;
269
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;
270
291
  private enforceA11yDomOrder;
271
292
  /** Keep DOM/WebGL overlay layers aligned with the canvas's CSS box. */
272
293
  private syncOverlayGeometry;
@@ -308,6 +329,8 @@ export declare class Scene {
308
329
  * Finds the topmost interactive entity at the given coordinates.
309
330
  */
310
331
  findEntityAt(x: number, y: number): Entity | null;
332
+ /** Submit one transparent clear pass when particle content lingers on the GPU canvas. */
333
+ private clearGPUCanvasIfStale;
311
334
  private initWebGPUContext;
312
335
  private setupDeviceLostHandler;
313
336
  private recreateWebGPUDeviceWithRetry;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/core",
3
- "version": "0.2.5",
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
  }