@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.
- package/dist/{chunk-5KLB6BEZ.js → chunk-BWLNEJJW.js} +8 -8
- package/dist/{chunk-76NMTLYL.js → chunk-CTZQOM5Z.js} +1 -1
- package/dist/{chunk-TQ4H357Q.js → chunk-DWMQYHNC.js} +82 -13
- package/dist/{chunk-TPNADFNN.js → chunk-GTQRCY6W.js} +56 -26
- package/dist/{chunk-WEQRBXT2.mjs → chunk-HVJHXIKW.mjs} +54 -24
- package/dist/{chunk-SB3SCWLT.mjs → chunk-SSOP3F5F.mjs} +1 -1
- package/dist/{chunk-B3Z3JEJH.mjs → chunk-STWPTWO4.mjs} +1 -1
- package/dist/{chunk-P6MDWIEX.mjs → chunk-YVN3PJRC.mjs} +80 -11
- package/dist/components/SplineEntity.d.ts +5 -0
- package/dist/components/TextEntity.d.ts +6 -1
- package/dist/index.js +326 -134
- package/dist/index.mjs +217 -25
- package/dist/layout/LayoutWorkerSource.d.ts +1 -1
- package/dist/layout.js +3 -3
- package/dist/layout.mjs +2 -2
- package/dist/renderer/CanvasRenderer.d.ts +12 -1
- package/dist/renderer/IRenderer.d.ts +7 -0
- package/dist/renderer/url.d.ts +1 -1
- package/dist/renderer.js +2 -2
- package/dist/renderer.mjs +1 -1
- package/dist/text/MSDFTextEntity.d.ts +15 -1
- package/dist/text.js +3 -3
- package/dist/text.mjs +2 -2
- package/dist/tree/Entity.d.ts +31 -0
- package/dist/tree/Scene.d.ts +26 -3
- package/package.json +5 -3
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
LayoutResultBuffer,
|
|
4
4
|
computeLineSegments,
|
|
5
5
|
createCanvasMeasurer
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SSOP3F5F.mjs";
|
|
7
7
|
import {
|
|
8
8
|
CanvasRenderer,
|
|
9
9
|
SVGRenderer,
|
|
@@ -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,12 +24,12 @@ 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,
|
|
31
31
|
LayoutWorkerManager
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-STWPTWO4.mjs";
|
|
33
33
|
|
|
34
34
|
// src/tree/ComputeParticleEntity.ts
|
|
35
35
|
var PARTICLE_STRIDE_FLOATS = 8;
|
|
@@ -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;
|
|
@@ -386,6 +394,8 @@ var Scene = class _Scene {
|
|
|
386
394
|
initializingWebGPU = false;
|
|
387
395
|
gpuCanvas = null;
|
|
388
396
|
gpuContext = null;
|
|
397
|
+
/** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
|
|
398
|
+
gpuHasContent = false;
|
|
389
399
|
mouseX = -9999;
|
|
390
400
|
mouseY = -9999;
|
|
391
401
|
pointerMoveListener = null;
|
|
@@ -396,8 +406,10 @@ var Scene = class _Scene {
|
|
|
396
406
|
this.debugA11y = options.debugA11y ?? false;
|
|
397
407
|
this.disableWindowResize = options.disableWindowResize ?? false;
|
|
398
408
|
if (this.disableWindowResize) {
|
|
399
|
-
|
|
400
|
-
|
|
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);
|
|
401
413
|
} else {
|
|
402
414
|
this.width = typeof window !== "undefined" ? window.innerWidth : canvas.clientWidth || canvas.width || 800;
|
|
403
415
|
this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
|
|
@@ -409,6 +421,7 @@ var Scene = class _Scene {
|
|
|
409
421
|
this.autoThrottle = options.autoThrottle ?? true;
|
|
410
422
|
this.particleBackend = options.particleBackend ?? "auto";
|
|
411
423
|
this.a11ySyncInterval = options.a11ySyncInterval ?? 0;
|
|
424
|
+
this.contentProjectionEnabled = options.contentProjection ?? true;
|
|
412
425
|
this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
|
|
413
426
|
this.root = new class RootEntity extends Entity {
|
|
414
427
|
isPointInside() {
|
|
@@ -430,7 +443,10 @@ var Scene = class _Scene {
|
|
|
430
443
|
if (options.renderer) {
|
|
431
444
|
this.renderer = options.renderer;
|
|
432
445
|
} else {
|
|
433
|
-
this.renderer = new CanvasRenderer(
|
|
446
|
+
this.renderer = new CanvasRenderer(
|
|
447
|
+
canvas,
|
|
448
|
+
this.disableWindowResize ? { width: this.width, height: this.height } : void 0
|
|
449
|
+
);
|
|
434
450
|
}
|
|
435
451
|
if (typeof document !== "undefined") {
|
|
436
452
|
this.a11yRoot = document.createElement("div");
|
|
@@ -559,6 +575,11 @@ var Scene = class _Scene {
|
|
|
559
575
|
* @param entity - The subtree whose shadow nodes should be removed.
|
|
560
576
|
*/
|
|
561
577
|
detachA11y(entity) {
|
|
578
|
+
const contentEl = this.contentElements.get(entity.id);
|
|
579
|
+
if (contentEl) {
|
|
580
|
+
contentEl.remove();
|
|
581
|
+
this.contentElements.delete(entity.id);
|
|
582
|
+
}
|
|
562
583
|
this.removeA11yRecursively(entity);
|
|
563
584
|
}
|
|
564
585
|
/**
|
|
@@ -605,6 +626,8 @@ var Scene = class _Scene {
|
|
|
605
626
|
this.a11yRoot?.remove();
|
|
606
627
|
this.portalRoot?.remove();
|
|
607
628
|
this.a11yElements.clear();
|
|
629
|
+
for (const el of this.contentElements.values()) el.remove();
|
|
630
|
+
this.contentElements.clear();
|
|
608
631
|
this.pointRenderer?.destroy();
|
|
609
632
|
this.renderer.dispose?.();
|
|
610
633
|
this.glCanvas?.remove();
|
|
@@ -619,6 +642,10 @@ var Scene = class _Scene {
|
|
|
619
642
|
this.manager.destroy();
|
|
620
643
|
this.manager = null;
|
|
621
644
|
}
|
|
645
|
+
if (this.device) {
|
|
646
|
+
this.device.destroy?.();
|
|
647
|
+
this.device = null;
|
|
648
|
+
}
|
|
622
649
|
}
|
|
623
650
|
setupEvents() {
|
|
624
651
|
if (typeof window !== "undefined" && !this.disableWindowResize) {
|
|
@@ -963,11 +990,96 @@ var Scene = class _Scene {
|
|
|
963
990
|
el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
|
|
964
991
|
}
|
|
965
992
|
}
|
|
993
|
+
this.syncContentProjection(node);
|
|
966
994
|
for (const child of node.children) this.syncA11y(child);
|
|
967
995
|
if (node === this.root) {
|
|
968
996
|
for (const overlay of this.overlayRoot.children) this.syncA11y(overlay);
|
|
969
997
|
}
|
|
970
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
|
+
}
|
|
971
1083
|
enforceA11yDomOrder() {
|
|
972
1084
|
if (!this.a11yRoot) return;
|
|
973
1085
|
this.fullViewportElements.length = 0;
|
|
@@ -1164,8 +1276,8 @@ var Scene = class _Scene {
|
|
|
1164
1276
|
loop(time) {
|
|
1165
1277
|
if (!this.isRunning) return;
|
|
1166
1278
|
let cap = this.effectiveMaxFPS();
|
|
1167
|
-
const
|
|
1168
|
-
if (
|
|
1279
|
+
const isIdle = !this.dirty && !this.hasAnyPendingAnimation(this.root) && !this.hasAnyPendingAnimation(this.overlayRoot);
|
|
1280
|
+
if (isIdle && this.autoThrottle && this.renderMode === "always" && this.maxFPS > 0) {
|
|
1169
1281
|
cap = Math.min(cap, 2);
|
|
1170
1282
|
}
|
|
1171
1283
|
if (cap > 0 && time - this.lastTime < 1e3 / cap - 1) {
|
|
@@ -1174,17 +1286,19 @@ var Scene = class _Scene {
|
|
|
1174
1286
|
}
|
|
1175
1287
|
const dt = time - this.lastTime;
|
|
1176
1288
|
this.lastTime = time;
|
|
1177
|
-
if (this.renderMode === "onDemand" &&
|
|
1289
|
+
if (this.renderMode === "onDemand" && isIdle) {
|
|
1178
1290
|
this.scheduleFrame();
|
|
1179
1291
|
return;
|
|
1180
1292
|
}
|
|
1293
|
+
this.dirty = false;
|
|
1181
1294
|
this.render(this.renderer, dt, time);
|
|
1182
1295
|
const hasActiveAnimation = this.hasAnyPendingAnimation(this.root) || this.hasAnyPendingAnimation(this.overlayRoot);
|
|
1183
1296
|
const hasInteractive = this.hasAnyInteractive(this.root) || this.hasAnyInteractive(this.overlayRoot);
|
|
1297
|
+
const wantsContentSync = this.contentProjectionEnabled;
|
|
1184
1298
|
const shouldSyncInterval = this.a11ySyncInterval <= 0 || time - this.lastA11ySync >= this.a11ySyncInterval;
|
|
1185
|
-
if ((hasInteractive || this.a11yElements.size > 0) && (shouldSyncInterval || this.a11yPendingSyncAfterAnimation)) {
|
|
1299
|
+
if ((hasInteractive || this.a11yElements.size > 0 || wantsContentSync) && (shouldSyncInterval || this.a11yPendingSyncAfterAnimation)) {
|
|
1186
1300
|
this.lastA11ySync = time;
|
|
1187
|
-
if (hasInteractive) {
|
|
1301
|
+
if (hasInteractive || wantsContentSync) {
|
|
1188
1302
|
this.syncA11y(this.root);
|
|
1189
1303
|
}
|
|
1190
1304
|
this.enforceA11yDomOrder();
|
|
@@ -1192,7 +1306,6 @@ var Scene = class _Scene {
|
|
|
1192
1306
|
} else if (hasActiveAnimation) {
|
|
1193
1307
|
this.a11yPendingSyncAfterAnimation = true;
|
|
1194
1308
|
}
|
|
1195
|
-
this.dirty = false;
|
|
1196
1309
|
this.scheduleFrame();
|
|
1197
1310
|
}
|
|
1198
1311
|
/**
|
|
@@ -1304,6 +1417,7 @@ var Scene = class _Scene {
|
|
|
1304
1417
|
renderPass.end();
|
|
1305
1418
|
}
|
|
1306
1419
|
this.device.queue.submit([commandEncoder.finish()]);
|
|
1420
|
+
if (this.gpuContext) this.gpuHasContent = true;
|
|
1307
1421
|
} catch (e) {
|
|
1308
1422
|
console.error("WebGPU frame execution failed. Falling back.", e);
|
|
1309
1423
|
this.deviceLost = true;
|
|
@@ -1312,9 +1426,23 @@ var Scene = class _Scene {
|
|
|
1312
1426
|
}
|
|
1313
1427
|
} else if (isMainRenderPath) {
|
|
1314
1428
|
for (const entity of computeEntities) {
|
|
1315
|
-
|
|
1429
|
+
let mx = this.mouseX;
|
|
1430
|
+
let my = this.mouseY;
|
|
1431
|
+
if (mx > -9e3 && my > -9e3) {
|
|
1432
|
+
const local = entity.worldToLocal(mx, my);
|
|
1433
|
+
if (local) {
|
|
1434
|
+
mx = local.x;
|
|
1435
|
+
my = local.y;
|
|
1436
|
+
} else {
|
|
1437
|
+
mx = -9999;
|
|
1438
|
+
my = -9999;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
entity.updateCPU(dt / 1e3, mx, my, this.width, this.height);
|
|
1316
1442
|
}
|
|
1317
1443
|
}
|
|
1444
|
+
} else if (isMainRenderer) {
|
|
1445
|
+
this.clearGPUCanvasIfStale();
|
|
1318
1446
|
}
|
|
1319
1447
|
renderer.clear();
|
|
1320
1448
|
if (isMainRenderer) {
|
|
@@ -1412,7 +1540,19 @@ var Scene = class _Scene {
|
|
|
1412
1540
|
if (visible) {
|
|
1413
1541
|
if (node instanceof ComputeParticleEntity) {
|
|
1414
1542
|
if (this.deviceLost || this.webgpuDisabled || !this.device || !this.manager) {
|
|
1415
|
-
this.renderCPUParticles(
|
|
1543
|
+
this.renderCPUParticles(
|
|
1544
|
+
renderer,
|
|
1545
|
+
node,
|
|
1546
|
+
worldOpacity,
|
|
1547
|
+
a,
|
|
1548
|
+
b,
|
|
1549
|
+
c,
|
|
1550
|
+
d,
|
|
1551
|
+
te,
|
|
1552
|
+
tf,
|
|
1553
|
+
worldScaleX,
|
|
1554
|
+
isSimilarityTransform
|
|
1555
|
+
);
|
|
1416
1556
|
}
|
|
1417
1557
|
} else {
|
|
1418
1558
|
node.render(renderer);
|
|
@@ -1436,6 +1576,7 @@ var Scene = class _Scene {
|
|
|
1436
1576
|
if (isMainRenderer) {
|
|
1437
1577
|
this.pointRenderer?.flush();
|
|
1438
1578
|
}
|
|
1579
|
+
renderer.present?.();
|
|
1439
1580
|
}
|
|
1440
1581
|
/**
|
|
1441
1582
|
* Export the current scene state to a lightweight, flat SVG XML string.
|
|
@@ -1455,6 +1596,10 @@ var Scene = class _Scene {
|
|
|
1455
1596
|
this.renderer.resize(width, height);
|
|
1456
1597
|
}
|
|
1457
1598
|
this.pointRenderer?.resize(width, height);
|
|
1599
|
+
if (this.gpuCanvas) {
|
|
1600
|
+
this.gpuCanvas.width = width;
|
|
1601
|
+
this.gpuCanvas.height = height;
|
|
1602
|
+
}
|
|
1458
1603
|
this.markDirty();
|
|
1459
1604
|
}
|
|
1460
1605
|
/**
|
|
@@ -1477,6 +1622,27 @@ var Scene = class _Scene {
|
|
|
1477
1622
|
if (overlayHit) return overlayHit;
|
|
1478
1623
|
return this.findHitRecursively(this.root, x, y);
|
|
1479
1624
|
}
|
|
1625
|
+
/** Submit one transparent clear pass when particle content lingers on the GPU canvas. */
|
|
1626
|
+
clearGPUCanvasIfStale() {
|
|
1627
|
+
if (!this.gpuHasContent || !this.device || !this.gpuContext || this.deviceLost) return;
|
|
1628
|
+
try {
|
|
1629
|
+
const encoder = this.device.createCommandEncoder();
|
|
1630
|
+
const pass = encoder.beginRenderPass({
|
|
1631
|
+
colorAttachments: [
|
|
1632
|
+
{
|
|
1633
|
+
view: this.gpuContext.getCurrentTexture().createView(),
|
|
1634
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
1635
|
+
loadOp: "clear",
|
|
1636
|
+
storeOp: "store"
|
|
1637
|
+
}
|
|
1638
|
+
]
|
|
1639
|
+
});
|
|
1640
|
+
pass.end();
|
|
1641
|
+
this.device.queue.submit([encoder.finish()]);
|
|
1642
|
+
} catch {
|
|
1643
|
+
}
|
|
1644
|
+
this.gpuHasContent = false;
|
|
1645
|
+
}
|
|
1480
1646
|
async initWebGPUContext(entities) {
|
|
1481
1647
|
if (!navigator.gpu) {
|
|
1482
1648
|
throw new Error("WebGPU not supported on this platform.");
|
|
@@ -1569,10 +1735,10 @@ var Scene = class _Scene {
|
|
|
1569
1735
|
}).catch(() => this.recreateWebGPUDeviceWithRetry(entities, attempt + 1));
|
|
1570
1736
|
}, backoff);
|
|
1571
1737
|
}
|
|
1572
|
-
renderCPUParticles(renderer, entity, worldOpacity) {
|
|
1738
|
+
renderCPUParticles(renderer, entity, worldOpacity, a, b, c, d, e, f, worldScale, isSimilarityTransform) {
|
|
1573
1739
|
const data = entity.particleData;
|
|
1574
1740
|
const size = entity.maxParticles;
|
|
1575
|
-
const
|
|
1741
|
+
const useGL = renderer === this.renderer && !!this.pointRenderer && isSimilarityTransform;
|
|
1576
1742
|
for (let i = 0; i < size; i++) {
|
|
1577
1743
|
const idx = i * 8;
|
|
1578
1744
|
const x = data[idx];
|
|
@@ -1582,8 +1748,14 @@ var Scene = class _Scene {
|
|
|
1582
1748
|
if (life === 0) continue;
|
|
1583
1749
|
const opacity = life < 0 ? worldOpacity : worldOpacity * Math.min(1, life);
|
|
1584
1750
|
const scale = life >= 0 ? Math.min(1, life) : 1;
|
|
1585
|
-
if (
|
|
1586
|
-
this.pointRenderer.addCircle(
|
|
1751
|
+
if (useGL) {
|
|
1752
|
+
this.pointRenderer.addCircle(
|
|
1753
|
+
a * x + c * y + e,
|
|
1754
|
+
b * x + d * y + f,
|
|
1755
|
+
pSize * scale * worldScale,
|
|
1756
|
+
entity.baseColor,
|
|
1757
|
+
opacity
|
|
1758
|
+
);
|
|
1587
1759
|
} else {
|
|
1588
1760
|
renderer.fillCircle(x, y, pSize * scale, entity.baseColor, opacity);
|
|
1589
1761
|
}
|
|
@@ -1631,6 +1803,14 @@ var TextEntity = class extends Entity {
|
|
|
1631
1803
|
this.on("hover", () => this.isHovered = true);
|
|
1632
1804
|
this.on("pointerleave", () => this.isHovered = false);
|
|
1633
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
|
+
}
|
|
1634
1814
|
/**
|
|
1635
1815
|
* Replace the text content. Runs the **cold** measurement pass (re-segment +
|
|
1636
1816
|
* re-measure) since the glyphs changed, then re-lays out.
|
|
@@ -1809,6 +1989,11 @@ var SplineEntity = class extends Entity {
|
|
|
1809
1989
|
bounds;
|
|
1810
1990
|
offscreen = null;
|
|
1811
1991
|
baked = false;
|
|
1992
|
+
/** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
|
|
1993
|
+
bakedWidth = 0;
|
|
1994
|
+
bakedHeight = 0;
|
|
1995
|
+
/** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
|
|
1996
|
+
containsGradient;
|
|
1812
1997
|
/** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
|
|
1813
1998
|
polylines = null;
|
|
1814
1999
|
/**
|
|
@@ -1828,6 +2013,8 @@ var SplineEntity = class extends Entity {
|
|
|
1828
2013
|
this.bounds = this.computeBounds();
|
|
1829
2014
|
this.width = this.bounds.width;
|
|
1830
2015
|
this.height = this.bounds.height;
|
|
2016
|
+
const isGradient = (c) => c !== null && !Array.isArray(c);
|
|
2017
|
+
this.containsGradient = (this.doc.equations?.some((eq) => isGradient(eq.color_rgb)) ?? false) || (this.doc.paths?.some((p) => isGradient(p.color_rgb)) ?? false);
|
|
1831
2018
|
this.interactive = true;
|
|
1832
2019
|
}
|
|
1833
2020
|
computeBounds() {
|
|
@@ -1987,18 +2174,22 @@ var SplineEntity = class extends Entity {
|
|
|
1987
2174
|
const pad = this.lineWidth + 2;
|
|
1988
2175
|
const w = Math.max(1, Math.ceil(this.bounds.width) + pad * 2);
|
|
1989
2176
|
const h = Math.max(1, Math.ceil(this.bounds.height) + pad * 2);
|
|
2177
|
+
this.bakedWidth = w;
|
|
2178
|
+
this.bakedHeight = h;
|
|
2179
|
+
const dpr = typeof window !== "undefined" && typeof window.devicePixelRatio === "number" ? window.devicePixelRatio || 1 : 1;
|
|
1990
2180
|
let canvas;
|
|
1991
2181
|
if (typeof OffscreenCanvas !== "undefined") {
|
|
1992
|
-
canvas = new OffscreenCanvas(w, h);
|
|
2182
|
+
canvas = new OffscreenCanvas(w * dpr, h * dpr);
|
|
1993
2183
|
} else if (typeof document !== "undefined") {
|
|
1994
2184
|
canvas = document.createElement("canvas");
|
|
1995
|
-
canvas.width = w;
|
|
1996
|
-
canvas.height = h;
|
|
2185
|
+
canvas.width = w * dpr;
|
|
2186
|
+
canvas.height = h * dpr;
|
|
1997
2187
|
} else {
|
|
1998
2188
|
return;
|
|
1999
2189
|
}
|
|
2000
2190
|
const ctx = canvas.getContext("2d");
|
|
2001
2191
|
if (!ctx) return;
|
|
2192
|
+
ctx.scale(dpr, dpr);
|
|
2002
2193
|
ctx.translate(pad - this.bounds.x, pad - this.bounds.y);
|
|
2003
2194
|
ctx.lineWidth = this.lineWidth;
|
|
2004
2195
|
ctx.lineCap = "round";
|
|
@@ -2032,7 +2223,7 @@ var SplineEntity = class extends Entity {
|
|
|
2032
2223
|
}
|
|
2033
2224
|
render(r) {
|
|
2034
2225
|
let rendered = false;
|
|
2035
|
-
if (this.cache) {
|
|
2226
|
+
if (this.cache && !this.containsGradient) {
|
|
2036
2227
|
if (!this.baked) this.bake();
|
|
2037
2228
|
if (this.offscreen) {
|
|
2038
2229
|
const pad = this.lineWidth + 2;
|
|
@@ -2040,8 +2231,9 @@ var SplineEntity = class extends Entity {
|
|
|
2040
2231
|
this.offscreen,
|
|
2041
2232
|
this.bounds.x - pad,
|
|
2042
2233
|
this.bounds.y - pad,
|
|
2043
|
-
|
|
2044
|
-
this.
|
|
2234
|
+
// Logical size, not the (DPR-scaled) bitmap size.
|
|
2235
|
+
this.bakedWidth,
|
|
2236
|
+
this.bakedHeight
|
|
2045
2237
|
);
|
|
2046
2238
|
rendered = true;
|
|
2047
2239
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const WORKER_SOURCE_STRING = "\"use strict\";(()=>{var
|
|
1
|
+
export declare const WORKER_SOURCE_STRING = "\"use strict\";(()=>{var C=new Map;function f(t){return typeof t==\"number\"&&Number.isFinite(t)}function U(t){return t.origin?t.origin===self.location.origin:!0}function j(t){if(!t||typeof t!=\"object\")return!1;let e=t;return typeof e.id==\"string\"&&f(e.seqId)&&typeof e.text==\"string\"&&typeof e.fontId==\"string\"&&(e.fontData===void 0||typeof e.fontData==\"object\")&&f(e.maxWidth)&&f(e.maxHeight)&&f(e.fontSize)&&(e.lineHeight===void 0||f(e.lineHeight))&&(e.letterSpacing===void 0||f(e.letterSpacing))}self.onmessage=t=>{if(!U(t)||!j(t.data))return;let{id:e,seqId:D,text:L,fontId:k,fontData:S,maxWidth:H,maxHeight:z,fontSize:d,lineHeight:I,letterSpacing:M}=t.data;S&&C.set(k,S);let g=C.get(k);if(!g)return;let m=[],i=[],h=[],w=[],n=0,c=0,r=0,o=-1,p=g.metrics?.ascender??.8,R=g.metrics?.descender??-.2,b=I??d*(p-R),P=M??0,F=new Map;for(let s of g.glyphs??[])F.set(s.unicode,s.advance);let x=()=>{n>r&&(r=n),n=0,c++,o=-1},A=Array.from(L);for(let s=0;s<A.length;s++){let a=A[s].codePointAt(0);if(a===10){x();continue}let W=(F.get(a)??1)*d,q=a>=11904;if(q&&(o=-1),n+W>H&&n>0){if(a===32){x();continue}if(o>=0&&i[o]>0){let l=i[o];l>r&&(r=l),c++;let v=c*b+p*d;for(let y=o;y<i.length;y++)i[y]-=l,h[y]=v;n-=l}else x()}a===32?o=-1:o===-1&&!q&&(o=m.length),m.push(a),i.push(n),h.push(c*b+p*d),w.push(-256),n+=W+P}n>r&&(r=n);let u={id:e,seqId:D,width:r,height:(c+1)*b,codePoints:new Uint32Array(m),xCoords:new Float32Array(i),yCoords:new Float32Array(h),packedStyles:new Uint32Array(w)};self.postMessage(u,[u.codePoints.buffer,u.xCoords.buffer,u.yCoords.buffer,u.packedStyles.buffer])};})();\n";
|
package/dist/layout.js
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
var
|
|
6
|
+
var _chunkBWLNEJJWjs = require('./chunk-BWLNEJJW.js');
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var _chunkCTZQOM5Zjs = require('./chunk-CTZQOM5Z.js');
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
exports.LayoutEngine =
|
|
16
|
+
exports.LayoutEngine = _chunkBWLNEJJWjs.LayoutEngine; exports.LayoutResultBuffer = _chunkBWLNEJJWjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunkCTZQOM5Zjs.LayoutWorkerManager; exports.computeLineSegments = _chunkBWLNEJJWjs.computeLineSegments; exports.createCanvasMeasurer = _chunkBWLNEJJWjs.createCanvasMeasurer;
|
package/dist/layout.mjs
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
LayoutResultBuffer,
|
|
4
4
|
computeLineSegments,
|
|
5
5
|
createCanvasMeasurer
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SSOP3F5F.mjs";
|
|
7
7
|
import {
|
|
8
8
|
LayoutWorkerManager
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-STWPTWO4.mjs";
|
|
10
10
|
export {
|
|
11
11
|
LayoutEngine,
|
|
12
12
|
LayoutResultBuffer,
|
|
@@ -14,7 +14,18 @@ export declare class CanvasRenderer implements IRenderer {
|
|
|
14
14
|
private batchColor;
|
|
15
15
|
private batchAlpha;
|
|
16
16
|
private batchCount;
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* @param canvas - The target canvas. Its backing store is resized to the
|
|
19
|
+
* logical size × devicePixelRatio.
|
|
20
|
+
* @param size - Explicit logical size. Without it the renderer assumes a
|
|
21
|
+
* fullscreen canvas and sizes to the window — pass this for embedded /
|
|
22
|
+
* custom-container canvases (the Scene does when `disableWindowResize` is
|
|
23
|
+
* set) so the canvas's own dimensions aren't clobbered by the window's.
|
|
24
|
+
*/
|
|
25
|
+
constructor(canvas: HTMLCanvasElement, size?: {
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
});
|
|
18
29
|
/**
|
|
19
30
|
* Expose the underlying `CanvasRenderingContext2D` for operations not
|
|
20
31
|
* covered by the {@link IRenderer} interface.
|
|
@@ -175,6 +175,13 @@ export interface IRenderer {
|
|
|
175
175
|
stop: number;
|
|
176
176
|
color: string;
|
|
177
177
|
}[]): any;
|
|
178
|
+
/**
|
|
179
|
+
* Present the completed frame, called by {@link Scene} exactly once at the
|
|
180
|
+
* end of each render pass (after the final {@link flush}). Retained-scene
|
|
181
|
+
* backends (e.g. `@vectojs/three`) do their single real GL render here;
|
|
182
|
+
* immediate-mode backends (Canvas2D, SVG) don't need it. Optional.
|
|
183
|
+
*/
|
|
184
|
+
present?(): void;
|
|
178
185
|
/**
|
|
179
186
|
* Release any backend-owned GPU textures / GL contexts / caches.
|
|
180
187
|
*
|
package/dist/renderer/url.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* returns it verbatim — relative navigation is never script-injectable.
|
|
19
19
|
* 4. If the URL parses with a scheme NOT in {@link SAFE_SCHEMES}, returns `'#'`
|
|
20
20
|
* to keep the link non-empty but inert.
|
|
21
|
-
* 5. Otherwise returns the
|
|
21
|
+
* 5. Otherwise returns the trimmed input unchanged (no canonicalization).
|
|
22
22
|
*
|
|
23
23
|
* The function never throws; malformed input falls back to `'#'`.
|
|
24
24
|
*/
|
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;
|
|
@@ -8,6 +8,10 @@ export interface MSDFTextEntityOptions {
|
|
|
8
8
|
color?: string;
|
|
9
9
|
lineHeight?: number;
|
|
10
10
|
letterSpacing?: number;
|
|
11
|
+
/** Wrap boundary in logical pixels. Defaults to 1000. */
|
|
12
|
+
maxWidth?: number;
|
|
13
|
+
/** Layout height limit in logical pixels. Defaults to 1000. */
|
|
14
|
+
maxHeight?: number;
|
|
11
15
|
}
|
|
12
16
|
export declare class MSDFTextEntity extends Entity {
|
|
13
17
|
private font;
|
|
@@ -17,13 +21,23 @@ export declare class MSDFTextEntity extends Entity {
|
|
|
17
21
|
color: string;
|
|
18
22
|
private letterSpacing;
|
|
19
23
|
private lineHeight?;
|
|
24
|
+
private maxWidth;
|
|
25
|
+
private maxHeight;
|
|
20
26
|
private text;
|
|
21
27
|
private lastRenderedSeqId;
|
|
22
28
|
private rgbColorCache;
|
|
23
29
|
private fontStringCache;
|
|
24
30
|
private layoutResult;
|
|
25
31
|
constructor(text: string, options: MSDFTextEntityOptions);
|
|
32
|
+
/** Change the wrap boundary and re-run layout for the current text. */
|
|
33
|
+
setMaxWidth(maxWidth: number): void;
|
|
26
34
|
setText(text: string): void;
|
|
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;
|
|
27
41
|
isPointInside(globalX: number, globalY: number): boolean;
|
|
28
42
|
render(renderer: any): void;
|
|
29
43
|
destroy(): void;
|
package/dist/text.js
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunkDWMQYHNCjs = require('./chunk-DWMQYHNC.js');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var _chunkCTZQOM5Zjs = require('./chunk-CTZQOM5Z.js');
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
exports.ArabicShaper =
|
|
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
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
MSDFFont,
|
|
3
3
|
MSDFTextEntity,
|
|
4
4
|
SVGEntity
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-YVN3PJRC.mjs";
|
|
6
6
|
import {
|
|
7
7
|
ArabicShaper,
|
|
8
8
|
BidiResolver
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-STWPTWO4.mjs";
|
|
10
10
|
export {
|
|
11
11
|
ArabicShaper,
|
|
12
12
|
BidiResolver,
|