@vectojs/core 1.13.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,9 +1,3 @@
1
- import {
2
- LayoutEngine,
3
- LayoutResultBuffer,
4
- computeLineSegments,
5
- createCanvasMeasurer
6
- } from "./chunk-X7I465AQ.mjs";
7
1
  import {
8
2
  CanvasRenderer,
9
3
  SVGRenderer,
@@ -15,25 +9,11 @@ import {
15
9
  sanitizeUrl
16
10
  } from "./chunk-L5BCKFQE.mjs";
17
11
  import {
18
- Easing,
19
12
  Entity,
20
- MSDFFont,
21
13
  MSDFTextEntity,
22
14
  SVGEntity,
23
- SpringDriver,
24
- SpringPhysics,
25
- TweenDriver,
26
- VectoJSEvent,
27
- clearCssLineBoxMetrics,
28
- cssLineBoxBaseline,
29
- isTweenConfig,
30
- prepareContentGrid
31
- } from "./chunk-XIEQHSBB.mjs";
32
- import {
33
- ArabicShaper,
34
- BidiResolver,
35
- LayoutWorkerManager
36
- } from "./chunk-IESDTEJ4.mjs";
15
+ VectoJSEvent
16
+ } from "./chunk-64UFEHOJ.mjs";
37
17
 
38
18
  // src/tree/ComputeParticleEntity.ts
39
19
  var PARTICLE_STRIDE_FLOATS = 8;
@@ -330,6 +310,7 @@ var ComputeParticleEntity = class extends Entity {
330
310
  };
331
311
 
332
312
  // src/tree/Scene.ts
313
+ import { clearCssLineBoxMetrics, cssLineBoxBaseline } from "@vectojs/text";
333
314
  var INTERACTIVE_A11Y_ROLES = /* @__PURE__ */ new Set([
334
315
  "button",
335
316
  "switch",
@@ -720,6 +701,9 @@ var Scene = class _Scene {
720
701
  contentMetricScaleEpoch = -1;
721
702
  contentMetricScaleX = 1;
722
703
  contentProjectionEnabled = true;
704
+ // Virtualization margin (px) for content projection; `undefined` → one
705
+ // viewport height, resolved at sync time. `Infinity` = materialize everything.
706
+ contentProjectionMargin = void 0;
723
707
  /**
724
708
  * True while a text-selection drag that started on a projection's blank
725
709
  * region (no text node under the press) is being driven manually — the
@@ -870,6 +854,7 @@ var Scene = class _Scene {
870
854
  this.particleBackend = options.particleBackend ?? "auto";
871
855
  this.a11ySyncInterval = options.a11ySyncInterval ?? 0;
872
856
  this.contentProjectionEnabled = options.contentProjection ?? true;
857
+ this.contentProjectionMargin = options.contentProjectionMargin;
873
858
  this._devActive = _Scene._devModeDetected();
874
859
  this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
875
860
  this.root = new class RootEntity extends Entity {
@@ -1661,6 +1646,56 @@ var Scene = class _Scene {
1661
1646
  * hidden (`display: none`) so text-heavy scenes only materialize what is
1662
1647
  * visible to the browser's text machinery anyway.
1663
1648
  */
1649
+ /**
1650
+ * Whether `node`'s world-space box, expanded by `margin` px on every side,
1651
+ * overlaps the scene viewport AND every `clipChildren` ancestor's box. Used
1652
+ * both to virtualize content projection (materialize only near-viewport text,
1653
+ * at `margin = contentProjectionMargin`) and for the exact `display:none`
1654
+ * visibility test (`margin = 0`). Boundless nodes (width/height 0) opt out of
1655
+ * culling and always count as visible, matching the legacy behavior.
1656
+ */
1657
+ projectionBoxVisible(node, tf, margin) {
1658
+ if (!(node.width > 0 && node.height > 0)) return true;
1659
+ const { a, b, c, d, e, f } = tf;
1660
+ const worldCorners = [];
1661
+ let minX = Infinity;
1662
+ let minY = Infinity;
1663
+ let maxX = -Infinity;
1664
+ let maxY = -Infinity;
1665
+ for (let i = 0; i < 4; i++) {
1666
+ const lx = i & 1 ? node.width : 0;
1667
+ const ly = i & 2 ? node.height : 0;
1668
+ const wx = a * lx + c * ly + e;
1669
+ const wy = b * lx + d * ly + f;
1670
+ worldCorners.push({ x: wx, y: wy });
1671
+ if (wx < minX) minX = wx;
1672
+ if (wx > maxX) maxX = wx;
1673
+ if (wy < minY) minY = wy;
1674
+ if (wy > maxY) maxY = wy;
1675
+ }
1676
+ if (!(maxX >= -margin && minX <= this.width + margin && maxY >= -margin && minY <= this.height + margin)) {
1677
+ return false;
1678
+ }
1679
+ for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
1680
+ if (!ancestor.clipChildren || ancestor.width <= 0 || ancestor.height <= 0) continue;
1681
+ let localMinX = Infinity;
1682
+ let localMinY = Infinity;
1683
+ let localMaxX = -Infinity;
1684
+ let localMaxY = -Infinity;
1685
+ for (const corner of worldCorners) {
1686
+ const local = ancestor.worldToLocal(corner.x, corner.y);
1687
+ if (!local) continue;
1688
+ localMinX = Math.min(localMinX, local.x);
1689
+ localMinY = Math.min(localMinY, local.y);
1690
+ localMaxX = Math.max(localMaxX, local.x);
1691
+ localMaxY = Math.max(localMaxY, local.y);
1692
+ }
1693
+ if (!(localMaxX >= -margin && localMinX <= ancestor.width + margin && localMaxY >= -margin && localMinY <= ancestor.height + margin)) {
1694
+ return false;
1695
+ }
1696
+ }
1697
+ return true;
1698
+ }
1664
1699
  syncContentProjection(node) {
1665
1700
  if (!this.contentProjectionEnabled || !this.a11yRoot) return;
1666
1701
  const projection = node.getContentProjection();
@@ -1674,6 +1709,17 @@ var Scene = class _Scene {
1674
1709
  }
1675
1710
  return;
1676
1711
  }
1712
+ const worldTf = node.getWorldTransform();
1713
+ const margin = this.contentProjectionMargin ?? this.height;
1714
+ if (Number.isFinite(margin) && !this.projectionBoxVisible(node, worldTf, margin)) {
1715
+ if (el) {
1716
+ this.clearContentGridState(node.id, el);
1717
+ el.remove();
1718
+ this.contentElements.delete(node.id);
1719
+ this.a11yNeedsReorder = true;
1720
+ }
1721
+ return;
1722
+ }
1677
1723
  if (!el) {
1678
1724
  el = document.createElement("div");
1679
1725
  el.setAttribute("data-vecto-content", node.id);
@@ -1770,7 +1816,7 @@ var Scene = class _Scene {
1770
1816
  el.style.userSelect = selectable ? "text" : "none";
1771
1817
  el.style.cursor = selectable ? "text" : "";
1772
1818
  }
1773
- const { a, b, c, d, e, f } = node.getWorldTransform();
1819
+ const { a, b, c, d, e, f } = worldTf;
1774
1820
  const contentX = projection.contentX ?? 0;
1775
1821
  const contentY = projection.contentY ?? 0;
1776
1822
  const baselineOffset = lines && lines.length > 0 ? 0 : projection.baseline === void 0 ? 0 : projection.baseline - cssLineBoxBaseline(font, projection.lineHeight ?? 16);
@@ -1780,42 +1826,7 @@ var Scene = class _Scene {
1780
1826
  if (node.width > 0) el.style.width = `${node.width}px`;
1781
1827
  if (node.height > 0) el.style.height = `${node.height}px`;
1782
1828
  el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
1783
- let visible = true;
1784
- if (node.width > 0 && node.height > 0) {
1785
- const worldCorners = [];
1786
- let minX = Infinity;
1787
- let minY = Infinity;
1788
- let maxX = -Infinity;
1789
- let maxY = -Infinity;
1790
- for (let i = 0; i < 4; i++) {
1791
- const lx = i & 1 ? node.width : 0;
1792
- const ly = i & 2 ? node.height : 0;
1793
- const wx = a * lx + c * ly + e;
1794
- const wy = b * lx + d * ly + f;
1795
- worldCorners.push({ x: wx, y: wy });
1796
- if (wx < minX) minX = wx;
1797
- if (wx > maxX) maxX = wx;
1798
- if (wy < minY) minY = wy;
1799
- if (wy > maxY) maxY = wy;
1800
- }
1801
- visible = maxX >= 0 && minX <= this.width && maxY >= 0 && minY <= this.height;
1802
- for (let ancestor = node.parent; visible && ancestor; ancestor = ancestor.parent) {
1803
- if (!ancestor.clipChildren || ancestor.width <= 0 || ancestor.height <= 0) continue;
1804
- let localMinX = Infinity;
1805
- let localMinY = Infinity;
1806
- let localMaxX = -Infinity;
1807
- let localMaxY = -Infinity;
1808
- for (const corner of worldCorners) {
1809
- const local = ancestor.worldToLocal(corner.x, corner.y);
1810
- if (!local) continue;
1811
- localMinX = Math.min(localMinX, local.x);
1812
- localMinY = Math.min(localMinY, local.y);
1813
- localMaxX = Math.max(localMaxX, local.x);
1814
- localMaxY = Math.max(localMaxY, local.y);
1815
- }
1816
- visible = localMaxX >= 0 && localMinX <= ancestor.width && localMaxY >= 0 && localMinY <= ancestor.height;
1817
- }
1818
- }
1829
+ const visible = this.projectionBoxVisible(node, worldTf, 0);
1819
1830
  const display = visible ? "" : "none";
1820
1831
  if (el.style.display !== display) el.style.display = display;
1821
1832
  }
@@ -2808,6 +2819,10 @@ var Scene = class _Scene {
2808
2819
  };
2809
2820
 
2810
2821
  // src/components/TextEntity.ts
2822
+ import {
2823
+ LayoutEngine,
2824
+ createCanvasMeasurer
2825
+ } from "@vectojs/layout";
2811
2826
  var sharedMeasurer;
2812
2827
  function defaultMeasurer() {
2813
2828
  if (sharedMeasurer === void 0) sharedMeasurer = createCanvasMeasurer("sans-serif");
@@ -3429,99 +3444,11 @@ var Group = class extends Entity {
3429
3444
  }
3430
3445
  };
3431
3446
 
3432
- // src/math/SpatialHashGrid.ts
3433
- var SpatialHashGrid = class {
3434
- cellSize;
3435
- grid = /* @__PURE__ */ new Map();
3436
- entityCells = /* @__PURE__ */ new Map();
3437
- constructor(cellSize = 64) {
3438
- this.cellSize = cellSize;
3439
- }
3440
- hash(cx, cy) {
3441
- const x = cx < 0 ? -2 * cx - 1 : 2 * cx;
3442
- const y = cy < 0 ? -2 * cy - 1 : 2 * cy;
3443
- return (x + y) * (x + y + 1) / 2 + y;
3444
- }
3445
- cellsForAABB(x, y, w, h) {
3446
- const minCx = Math.floor(x / this.cellSize);
3447
- const minCy = Math.floor(y / this.cellSize);
3448
- const maxCx = Math.floor((x + w) / this.cellSize);
3449
- const maxCy = Math.floor((y + h) / this.cellSize);
3450
- const keys = [];
3451
- for (let cx = minCx; cx <= maxCx; cx++) {
3452
- for (let cy = minCy; cy <= maxCy; cy++) {
3453
- keys.push(this.hash(cx, cy));
3454
- }
3455
- }
3456
- return keys;
3457
- }
3458
- /**
3459
- * Insert or update an entity's axis-aligned bounding box in the grid.
3460
- *
3461
- * If the entity is already registered its old cell memberships are removed
3462
- * before the new ones are computed, so this method is safe to call every
3463
- * frame.
3464
- *
3465
- * @param id - Unique string identifier for the entity.
3466
- * @param x - Left edge of the AABB in world space.
3467
- * @param y - Top edge of the AABB in world space.
3468
- * @param w - Width of the AABB.
3469
- * @param h - Height of the AABB.
3470
- */
3471
- insert(id, x, y, w, h) {
3472
- this.remove(id);
3473
- const keys = this.cellsForAABB(x, y, w, h);
3474
- this.entityCells.set(id, keys);
3475
- for (const key of keys) {
3476
- if (!this.grid.has(key)) this.grid.set(key, /* @__PURE__ */ new Set());
3477
- this.grid.get(key).add(id);
3478
- }
3479
- }
3480
- /**
3481
- * Remove an entity from all grid cells it currently occupies.
3482
- *
3483
- * Silently does nothing if the entity is not registered.
3484
- *
3485
- * @param id - Unique string identifier of the entity to remove.
3486
- */
3487
- remove(id) {
3488
- const keys = this.entityCells.get(id);
3489
- if (!keys) return;
3490
- for (const key of keys) {
3491
- this.grid.get(key)?.delete(id);
3492
- }
3493
- this.entityCells.delete(id);
3494
- }
3495
- /**
3496
- * Return all entity IDs whose grid cells overlap the given AABB.
3497
- *
3498
- * Time complexity: O(k) where k is the number of cells the query AABB spans
3499
- * plus the number of results — O(1) average for small, similarly-sized entities.
3500
- *
3501
- * @param x - Left edge of the query AABB.
3502
- * @param y - Top edge of the query AABB.
3503
- * @param w - Width of the query AABB.
3504
- * @param h - Height of the query AABB.
3505
- * @returns A `Set` of entity ID strings whose cells intersect the query region.
3506
- */
3507
- query(x, y, w, h) {
3508
- const result = /* @__PURE__ */ new Set();
3509
- for (const key of this.cellsForAABB(x, y, w, h)) {
3510
- const cell = this.grid.get(key);
3511
- if (cell) for (const id of cell) result.add(id);
3512
- }
3513
- return result;
3514
- }
3515
- /**
3516
- * Clear all cells and entity registrations, resetting the grid to an empty state.
3517
- *
3518
- * Call once per frame before re-inserting all dynamic entities.
3519
- */
3520
- clear() {
3521
- this.grid.clear();
3522
- this.entityCells.clear();
3523
- }
3524
- };
3447
+ // src/index.ts
3448
+ export * from "@vectojs/layout";
3449
+ export * from "@vectojs/text";
3450
+ export * from "@vectojs/math";
3451
+ export * from "@vectojs/animation";
3525
3452
 
3526
3453
  // src/tree/DOMPortalEntity.ts
3527
3454
  var DOMPortalEntity = class extends Entity {
@@ -3627,20 +3554,13 @@ var DOMPortalEntity = class extends Entity {
3627
3554
  Scene.registerWebGLPointRendererCreator(createWebGLPointRenderer);
3628
3555
  Scene.registerWebGPUParticleSystemManager(WebGPUParticleSystemManager);
3629
3556
  export {
3630
- ArabicShaper,
3631
- BidiResolver,
3632
3557
  CanvasRenderer,
3633
3558
  Circle,
3634
3559
  ComputeParticleEntity,
3635
3560
  DOMPortalEntity,
3636
- Easing,
3637
3561
  Entity,
3638
3562
  GridTextEntity,
3639
3563
  Group,
3640
- LayoutEngine,
3641
- LayoutResultBuffer,
3642
- LayoutWorkerManager,
3643
- MSDFFont,
3644
3564
  MSDFTextEntity,
3645
3565
  PARTICLE_OFFSET_LIFE,
3646
3566
  PARTICLE_OFFSET_ORIGIN_X,
@@ -3656,25 +3576,15 @@ export {
3656
3576
  SVGEntity,
3657
3577
  SVGRenderer,
3658
3578
  Scene,
3659
- SpatialHashGrid,
3660
3579
  SplineEntity,
3661
- SpringDriver,
3662
- SpringPhysics,
3663
3580
  TextEntity,
3664
3581
  TextRasterCache,
3665
- TweenDriver,
3666
3582
  VectoJSEvent,
3667
3583
  WebGPUParticleSystemManager,
3668
- clearCssLineBoxMetrics,
3669
- computeLineSegments,
3670
- createCanvasMeasurer,
3671
3584
  createWebGLPointRenderer,
3672
- cssLineBoxBaseline,
3673
3585
  isSafeUrl,
3674
- isTweenConfig,
3675
3586
  loadSpline,
3676
3587
  parseColorToRGBA,
3677
3588
  polySegmentToBezier,
3678
- prepareContentGrid,
3679
3589
  sanitizeUrl
3680
3590
  };
@@ -1,3 +1 @@
1
- export * from './LayoutEngine';
2
- export * from './LayoutWorkerManager';
3
- export * from './measure';
1
+ export * from '@vectojs/layout';
package/dist/layout.js CHANGED
@@ -1,16 +1,2 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
-
3
-
4
-
5
-
6
- var _chunkBA5HUUDFjs = require('./chunk-BA5HUUDF.js');
7
-
8
-
9
- var _chunk4AR425ARjs = require('./chunk-4AR425AR.js');
10
-
11
-
12
-
13
-
14
-
15
-
16
- exports.LayoutEngine = _chunkBA5HUUDFjs.LayoutEngine; exports.LayoutResultBuffer = _chunkBA5HUUDFjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunk4AR425ARjs.LayoutWorkerManager; exports.computeLineSegments = _chunkBA5HUUDFjs.computeLineSegments; exports.createCanvasMeasurer = _chunkBA5HUUDFjs.createCanvasMeasurer;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _createStarExport(obj) { Object.keys(obj) .filter((key) => key !== "default" && key !== "__esModule") .forEach((key) => { if (exports.hasOwnProperty(key)) { return; } Object.defineProperty(exports, key, {enumerable: true, configurable: true, get: () => obj[key]}); }); }// src/layout/index.ts
2
+ var _layout = require('@vectojs/layout'); _createStarExport(_layout);
package/dist/layout.mjs CHANGED
@@ -1,16 +1,2 @@
1
- import {
2
- LayoutEngine,
3
- LayoutResultBuffer,
4
- computeLineSegments,
5
- createCanvasMeasurer
6
- } from "./chunk-X7I465AQ.mjs";
7
- import {
8
- LayoutWorkerManager
9
- } from "./chunk-IESDTEJ4.mjs";
10
- export {
11
- LayoutEngine,
12
- LayoutResultBuffer,
13
- LayoutWorkerManager,
14
- computeLineSegments,
15
- createCanvasMeasurer
16
- };
1
+ // src/layout/index.ts
2
+ export * from "@vectojs/layout";
@@ -1,5 +1,5 @@
1
1
  import { Entity, type ContentProjection } from '../tree/Entity';
2
- import { MSDFFont } from './MSDFFont';
2
+ import { MSDFFont } from '@vectojs/text';
3
3
  export interface MSDFTextEntityOptions {
4
4
  font: MSDFFont;
5
5
  texture: TexImageSource;
@@ -1,7 +1,3 @@
1
- export * from './ArabicShaper';
2
- export * from './BidiResolver';
3
- export * from './PreparedContentGrid';
4
- export * from './MSDFFont';
1
+ export * from '@vectojs/text';
5
2
  export * from './MSDFTextEntity';
6
3
  export * from './SVGEntity';
7
- export * from './Typography';
package/dist/text.js CHANGED
@@ -1,22 +1,11 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _createStarExport(obj) { Object.keys(obj) .filter((key) => key !== "default" && key !== "__esModule") .forEach((key) => { if (exports.hasOwnProperty(key)) { return; } Object.defineProperty(exports, key, {enumerable: true, configurable: true, get: () => obj[key]}); }); }
2
2
 
3
3
 
4
+ var _chunkJ6NHSFIEjs = require('./chunk-J6NHSFIE.js');
4
5
 
6
+ // src/text/index.ts
7
+ var _text = require('@vectojs/text'); _createStarExport(_text);
5
8
 
6
9
 
7
10
 
8
- var _chunk2Z23LTH3js = require('./chunk-2Z23LTH3.js');
9
-
10
-
11
-
12
- var _chunk4AR425ARjs = require('./chunk-4AR425AR.js');
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
- exports.ArabicShaper = _chunk4AR425ARjs.ArabicShaper; exports.BidiResolver = _chunk4AR425ARjs.BidiResolver; exports.MSDFFont = _chunk2Z23LTH3js.MSDFFont; exports.MSDFTextEntity = _chunk2Z23LTH3js.MSDFTextEntity; exports.SVGEntity = _chunk2Z23LTH3js.SVGEntity; exports.clearCssLineBoxMetrics = _chunk2Z23LTH3js.clearCssLineBoxMetrics; exports.cssLineBoxBaseline = _chunk2Z23LTH3js.cssLineBoxBaseline; exports.prepareContentGrid = _chunk2Z23LTH3js.prepareContentGrid;
11
+ exports.MSDFTextEntity = _chunkJ6NHSFIEjs.MSDFTextEntity; exports.SVGEntity = _chunkJ6NHSFIEjs.SVGEntity;
package/dist/text.mjs CHANGED
@@ -1,22 +1,11 @@
1
1
  import {
2
- MSDFFont,
3
2
  MSDFTextEntity,
4
- SVGEntity,
5
- clearCssLineBoxMetrics,
6
- cssLineBoxBaseline,
7
- prepareContentGrid
8
- } from "./chunk-XIEQHSBB.mjs";
9
- import {
10
- ArabicShaper,
11
- BidiResolver
12
- } from "./chunk-IESDTEJ4.mjs";
3
+ SVGEntity
4
+ } from "./chunk-64UFEHOJ.mjs";
5
+
6
+ // src/text/index.ts
7
+ export * from "@vectojs/text";
13
8
  export {
14
- ArabicShaper,
15
- BidiResolver,
16
- MSDFFont,
17
9
  MSDFTextEntity,
18
- SVGEntity,
19
- clearCssLineBoxMetrics,
20
- cssLineBoxBaseline,
21
- prepareContentGrid
10
+ SVGEntity
22
11
  };
@@ -1,5 +1,5 @@
1
- import { type MotionConfig, type TweenConfig, type SpringConfig } from '../animation/drivers';
2
- import type { PreparedContentGrid } from '../text/PreparedContentGrid';
1
+ import { type MotionConfig, type TweenConfig, type SpringConfig } from '@vectojs/animation';
2
+ import type { PreparedContentGrid } from '@vectojs/text';
3
3
  /** A numeric transform/visual property that participates in the animation system. */
4
4
  export type AnimatableProp = 'x' | 'y' | 'scaleX' | 'scaleY' | 'rotation' | 'opacity';
5
5
  /**
@@ -98,6 +98,18 @@ export interface SceneOptions {
98
98
  * scenes to skip the sync walk.
99
99
  */
100
100
  contentProjection?: boolean;
101
+ /**
102
+ * How far outside the viewport (in CSS px, each side) content projections are
103
+ * materialized as DOM. Projections whose box is farther than this are not
104
+ * created — and are removed when they scroll past it — so a document taller
105
+ * than the viewport keeps only a bounded, near-viewport set of DOM nodes
106
+ * instead of one element (plus a `<span>` per line) per block for the whole
107
+ * document. A larger margin keeps more off-screen text ready for native
108
+ * find-in-page / selection at the cost of more DOM; `Infinity` restores the
109
+ * legacy "materialize the entire document" behavior. Default: one viewport
110
+ * height (`undefined` → resolved to `Scene.height` at sync time).
111
+ */
112
+ contentProjectionMargin?: number;
101
113
  }
102
114
  /** Frame-rate the loop is capped to when the OS requests reduced motion. */
103
115
  export declare const REDUCED_MOTION_FPS = 30;
@@ -216,6 +228,7 @@ export declare class Scene {
216
228
  private contentMetricScaleEpoch;
217
229
  private contentMetricScaleX;
218
230
  private contentProjectionEnabled;
231
+ private contentProjectionMargin;
219
232
  /**
220
233
  * True while a text-selection drag that started on a projection's blank
221
234
  * region (no text node under the press) is being driven manually — the
@@ -402,6 +415,15 @@ export declare class Scene {
402
415
  * hidden (`display: none`) so text-heavy scenes only materialize what is
403
416
  * visible to the browser's text machinery anyway.
404
417
  */
418
+ /**
419
+ * Whether `node`'s world-space box, expanded by `margin` px on every side,
420
+ * overlaps the scene viewport AND every `clipChildren` ancestor's box. Used
421
+ * both to virtualize content projection (materialize only near-viewport text,
422
+ * at `margin = contentProjectionMargin`) and for the exact `display:none`
423
+ * visibility test (`margin = 0`). Boundless nodes (width/height 0) opt out of
424
+ * culling and always count as visible, matching the legacy behavior.
425
+ */
426
+ private projectionBoxVisible;
405
427
  private syncContentProjection;
406
428
  /**
407
429
  * Materialize a prepared grid in logical source order while positioning each
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/core",
3
- "version": "1.13.0",
3
+ "version": "1.15.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -53,16 +53,18 @@
53
53
  "THIRD_PARTY_NOTICES.md"
54
54
  ],
55
55
  "scripts": {
56
- "benchmark:grid": "vitest bench benchmark/PreparedContentGrid.bench.ts --run",
57
- "build": "node scripts/build-worker.js && tsup && tsc -p tsconfig.build.json",
56
+ "build": "(cd ../math && bun run build) && (cd ../text && bun run build) && (cd ../layout && bun run build) && (cd ../animation && bun run build) && tsup && tsc -p tsconfig.build.json",
58
57
  "test": "vitest run",
59
58
  "test:e2e": "bun e2e/hidpi.e2e.ts && bun e2e/text-projection.e2e.ts"
60
59
  },
61
- "dependencies": {},
60
+ "dependencies": {
61
+ "@vectojs/animation": "^0.1.0",
62
+ "@vectojs/layout": "^0.1.0",
63
+ "@vectojs/math": "^0.1.0",
64
+ "@vectojs/text": "^0.1.0"
65
+ },
62
66
  "devDependencies": {
63
67
  "@vitest/coverage-v8": "^4.1.10",
64
- "bidi-js": "^1.0.3",
65
- "esbuild": "^0.28.1",
66
68
  "jsdom": "^29.1.1",
67
69
  "tsup": "^8.3.5",
68
70
  "vitest": "^4.1.10",
@@ -1,48 +0,0 @@
1
- import { type EasingFn, type EasingName } from './easing';
2
- export interface SpringConfig {
3
- stiffness?: number;
4
- damping?: number;
5
- mass?: number;
6
- }
7
- export interface TweenConfig {
8
- duration: number;
9
- easing?: EasingName | EasingFn;
10
- delay?: number;
11
- }
12
- /** A motion config. Presence of `duration` selects a tween; otherwise a spring. */
13
- export type MotionConfig = 'spring' | SpringConfig | TweenConfig;
14
- export declare function isTweenConfig(c: MotionConfig): c is TweenConfig;
15
- /** Backs one animating property. Ticked in ms; writes `value`. */
16
- export interface PropertyDriver {
17
- value: number;
18
- /** The current destination — applied exactly when the animation completes, so a
19
- * finished spring lands on target rather than within its rest epsilon. */
20
- readonly target: number;
21
- /** Change the destination. Spring keeps velocity; tween restarts from current value. */
22
- retarget(to: number): void;
23
- tick(dtMs: number): void;
24
- isDone(): boolean;
25
- }
26
- export declare class TweenDriver implements PropertyDriver {
27
- value: number;
28
- private from;
29
- private to;
30
- private elapsed;
31
- private readonly duration;
32
- private readonly delay;
33
- private readonly ease;
34
- constructor(from: number, to: number, cfg: TweenConfig);
35
- get target(): number;
36
- retarget(to: number): void;
37
- tick(dtMs: number): void;
38
- isDone(): boolean;
39
- }
40
- export declare class SpringDriver implements PropertyDriver {
41
- private spring;
42
- constructor(from: number, to: number, cfg: SpringConfig);
43
- get value(): number;
44
- get target(): number;
45
- retarget(to: number): void;
46
- tick(dtMs: number): void;
47
- isDone(): boolean;
48
- }
@@ -1,16 +0,0 @@
1
- /** An easing function: maps normalized time t in [0,1] to eased progress. */
2
- export type EasingFn = (t: number) => number;
3
- /** Curated easing set. Add sparingly — every entry must map f(0)=0, f(1)=1. */
4
- export declare const Easing: {
5
- linear: (t: number) => number;
6
- easeInQuad: (t: number) => number;
7
- easeOutQuad: (t: number) => number;
8
- easeInOutQuad: (t: number) => number;
9
- easeInCubic: (t: number) => number;
10
- easeOutCubic: (t: number) => number;
11
- easeInOutCubic: (t: number) => number;
12
- easeOutBack: (t: number) => number;
13
- easeInOutBack: (t: number) => number;
14
- };
15
- /** Name of a built-in easing curve. */
16
- export type EasingName = keyof typeof Easing;