@vectojs/core 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -9
- package/dist/{chunk-RW6NC4RB.js → chunk-6I53LI3Z.js} +55 -13
- package/dist/{chunk-H3QIE77O.mjs → chunk-ISGOYXPF.mjs} +177 -52
- package/dist/{chunk-LA3FJLP2.js → chunk-KPWVPGHR.js} +187 -62
- package/dist/{chunk-T456DL4P.mjs → chunk-LIOJ37MH.mjs} +1 -1
- package/dist/{chunk-2Y45S4JK.mjs → chunk-NKOQV3RM.mjs} +84 -5
- package/dist/{chunk-LIX7DJTI.js → chunk-TXA3LZDM.js} +85 -6
- package/dist/{chunk-72WVPMSJ.js → chunk-VVOQNUBK.js} +8 -8
- package/dist/{chunk-YA2J5ZH7.mjs → chunk-YSS44ADQ.mjs} +49 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +184 -119
- package/dist/index.mjs +146 -81
- package/dist/layout/LayoutWorkerManager.d.ts +4 -0
- package/dist/layout.js +3 -3
- package/dist/layout.mjs +2 -2
- package/dist/renderer/CanvasRenderer.d.ts +6 -0
- package/dist/renderer/IRenderer.d.ts +9 -0
- package/dist/renderer/SVGRenderer.d.ts +14 -0
- package/dist/renderer/url.d.ts +31 -0
- package/dist/renderer.js +2 -2
- package/dist/renderer.mjs +1 -1
- package/dist/text.js +3 -3
- package/dist/text.mjs +2 -2
- package/dist/tree/DOMPortalEntity.d.ts +2 -1
- package/dist/tree/Entity.d.ts +48 -7
- package/dist/tree/Scene.d.ts +9 -1
- package/package.json +2 -4
|
@@ -185,8 +185,50 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
185
185
|
}
|
|
186
186
|
return grad;
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Canvas2D drawing contexts are automatically released when their
|
|
190
|
+
* `<canvas>` element is GC'd, so there's no explicit GPU handle to free.
|
|
191
|
+
* This method clears our internal batch state and is idempotent.
|
|
192
|
+
*/
|
|
193
|
+
dispose() {
|
|
194
|
+
this.batchCount = 0;
|
|
195
|
+
this.batchColor = "";
|
|
196
|
+
this.batchAlpha = 1;
|
|
197
|
+
this.batchActive = false;
|
|
198
|
+
}
|
|
188
199
|
}, _class.__initStatic(), _class);
|
|
189
200
|
|
|
201
|
+
// src/renderer/url.ts
|
|
202
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:", "ftp:"]);
|
|
203
|
+
var SCHEME_PATTERN = /^[a-z][a-z\d+.-]*$/i;
|
|
204
|
+
function normalizeSchemeCandidate(value) {
|
|
205
|
+
let result = "";
|
|
206
|
+
for (const character of value) {
|
|
207
|
+
const code = character.charCodeAt(0);
|
|
208
|
+
if (code > 32 && (code < 127 || code > 159)) result += character;
|
|
209
|
+
}
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
function hasSafeSchemeOrIsRelative(url) {
|
|
213
|
+
const colonIndex = url.indexOf(":");
|
|
214
|
+
if (colonIndex < 0) return true;
|
|
215
|
+
const candidate = normalizeSchemeCandidate(url.slice(0, colonIndex));
|
|
216
|
+
if (!SCHEME_PATTERN.test(candidate)) return true;
|
|
217
|
+
return SAFE_SCHEMES.has(`${candidate.toLowerCase()}:`);
|
|
218
|
+
}
|
|
219
|
+
function sanitizeUrl(href) {
|
|
220
|
+
if (typeof href !== "string") return "";
|
|
221
|
+
const trimmed = href.trim();
|
|
222
|
+
if (trimmed === "") return "";
|
|
223
|
+
return hasSafeSchemeOrIsRelative(trimmed) ? trimmed : "#";
|
|
224
|
+
}
|
|
225
|
+
function isSafeUrl(urlStr) {
|
|
226
|
+
if (typeof urlStr !== "string") return false;
|
|
227
|
+
const trimmed = urlStr.trim();
|
|
228
|
+
if (trimmed === "") return false;
|
|
229
|
+
return hasSafeSchemeOrIsRelative(trimmed);
|
|
230
|
+
}
|
|
231
|
+
|
|
190
232
|
// src/renderer/SVGRenderer.ts
|
|
191
233
|
var SVGRenderer = (_class2 = class {
|
|
192
234
|
|
|
@@ -395,7 +437,7 @@ var SVGRenderer = (_class2 = class {
|
|
|
395
437
|
}
|
|
396
438
|
fill(colorOrGradient) {
|
|
397
439
|
this.flush();
|
|
398
|
-
const fillVal = this.resolveGradient(colorOrGradient);
|
|
440
|
+
const fillVal = this.escapeXML(this.resolveGradient(colorOrGradient));
|
|
399
441
|
const dStr = this.currentPath.join(" ");
|
|
400
442
|
const transformStr = `matrix(${this.ma},${this.mb},${this.mc},${this.md},${this.me},${this.mf})`;
|
|
401
443
|
this.buffer.push(
|
|
@@ -404,7 +446,7 @@ var SVGRenderer = (_class2 = class {
|
|
|
404
446
|
}
|
|
405
447
|
stroke(colorOrGradient, lineWidth = 1) {
|
|
406
448
|
this.flush();
|
|
407
|
-
const strokeVal = this.resolveGradient(colorOrGradient);
|
|
449
|
+
const strokeVal = this.escapeXML(this.resolveGradient(colorOrGradient));
|
|
408
450
|
const dStr = this.currentPath.join(" ");
|
|
409
451
|
const transformStr = `matrix(${this.ma},${this.mb},${this.mc},${this.md},${this.me},${this.mf})`;
|
|
410
452
|
this.buffer.push(
|
|
@@ -424,7 +466,7 @@ var SVGRenderer = (_class2 = class {
|
|
|
424
466
|
const fontWeight = fontWeightMatch ? fontWeightMatch[1].toLowerCase() : "normal";
|
|
425
467
|
const cleanFont = font.replace(/\d+(?:\.\d+)?(px|em|rem)(?:\/\d+(?:\.\d+)?(?:px|em|rem|%)?)?/, "").trim();
|
|
426
468
|
const fontFamily = cleanFont.replace(/(bold|italic|normal|600|500|400|300|100)\s+/gi, "").trim() || "sans-serif";
|
|
427
|
-
const fillVal = this.resolveGradient(color);
|
|
469
|
+
const fillVal = this.escapeXML(this.resolveGradient(color));
|
|
428
470
|
const transformStr = `matrix(${this.ma},${this.mb},${this.mc},${this.md},${this.me},${this.mf})`;
|
|
429
471
|
this.buffer.push(
|
|
430
472
|
`<g transform="${transformStr}"><text x="${x}" y="${y}" font-size="${fontSize}" font-weight="${fontWeight}" font-style="${fontStyle}" font-family="${this.escapeXML(fontFamily)}" fill="${fillVal}" opacity="${this.globalAlpha}">${this.escapeXML(text)}</text></g>`
|
|
@@ -446,7 +488,9 @@ var SVGRenderer = (_class2 = class {
|
|
|
446
488
|
}
|
|
447
489
|
drawImage(source, dx, dy, dw, dh) {
|
|
448
490
|
this.flush();
|
|
449
|
-
const
|
|
491
|
+
const fromCanvas2 = typeof _optionalChain([source, 'optionalAccess', _ => _.toDataURL]) === "function";
|
|
492
|
+
const rawHref = fromCanvas2 ? source.toDataURL() : _optionalChain([source, 'optionalAccess', _2 => _2.src]) || "";
|
|
493
|
+
const href = fromCanvas2 && this.isSafeRasterDataUrl(rawHref) ? rawHref : sanitizeUrl(String(rawHref));
|
|
450
494
|
const transformStr = `matrix(${this.ma},${this.mb},${this.mc},${this.md},${this.me},${this.mf})`;
|
|
451
495
|
if (href) {
|
|
452
496
|
this.buffer.push(
|
|
@@ -459,6 +503,21 @@ var SVGRenderer = (_class2 = class {
|
|
|
459
503
|
console.warn("drawImage source fallback triggered");
|
|
460
504
|
}
|
|
461
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* Embed SVG markup as an isolated nested image.
|
|
508
|
+
*
|
|
509
|
+
* This explicit path is used by `SVGEntity` during vector export. General
|
|
510
|
+
* `drawImage()` input remains subject to the URL policy and therefore still
|
|
511
|
+
* rejects caller-provided SVG data URLs.
|
|
512
|
+
*/
|
|
513
|
+
drawSVG(source, dx, dy, dw, dh) {
|
|
514
|
+
this.flush();
|
|
515
|
+
const href = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(source)}`;
|
|
516
|
+
const transformStr = `matrix(${this.ma},${this.mb},${this.mc},${this.md},${this.me},${this.mf})`;
|
|
517
|
+
this.buffer.push(
|
|
518
|
+
`<image href="${this.escapeXML(href)}" x="${dx}" y="${dy}" width="${dw}" height="${dh}" transform="${transformStr}" />`
|
|
519
|
+
);
|
|
520
|
+
}
|
|
462
521
|
flush() {
|
|
463
522
|
if (!this.batchActive || this.batchCircles.length === 0) return;
|
|
464
523
|
let d = "";
|
|
@@ -566,6 +625,17 @@ var SVGRenderer = (_class2 = class {
|
|
|
566
625
|
}
|
|
567
626
|
});
|
|
568
627
|
}
|
|
628
|
+
isSafeRasterDataUrl(value) {
|
|
629
|
+
return typeof value === "string" && /^data:image\/(?:png|jpeg|gif|webp);base64,[a-z\d+/=\s]*$/i.test(value);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* SVGRenderer accumulates strings in memory; nothing external is allocated.
|
|
633
|
+
* Drop the buffers for GC and become idempotent.
|
|
634
|
+
*/
|
|
635
|
+
dispose() {
|
|
636
|
+
this.clear();
|
|
637
|
+
this.gradientCache.clear();
|
|
638
|
+
}
|
|
569
639
|
}, _class2);
|
|
570
640
|
|
|
571
641
|
// src/renderer/colorParse.ts
|
|
@@ -831,6 +901,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
831
901
|
let logicalW = 0;
|
|
832
902
|
let logicalH = 0;
|
|
833
903
|
let dpr = 1;
|
|
904
|
+
let destroyed = false;
|
|
834
905
|
return {
|
|
835
906
|
resize(width, height) {
|
|
836
907
|
logicalW = width;
|
|
@@ -1037,6 +1108,8 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1037
1108
|
gl.bindVertexArray(null);
|
|
1038
1109
|
},
|
|
1039
1110
|
destroy() {
|
|
1111
|
+
if (destroyed) return;
|
|
1112
|
+
destroyed = true;
|
|
1040
1113
|
gl.deleteBuffer(pointBuffer);
|
|
1041
1114
|
gl.deleteBuffer(rectBuffer);
|
|
1042
1115
|
gl.deleteBuffer(spriteBuffer);
|
|
@@ -1326,10 +1399,14 @@ var WebGPUParticleSystemManager = (_class3 = class {
|
|
|
1326
1399
|
if (!this.computePipeline || !entity.computeBindGroup) return;
|
|
1327
1400
|
const uniformArray = new Float32Array(20);
|
|
1328
1401
|
const color = parseColorToRGBA(entity.baseColor);
|
|
1402
|
+
let opacity = 1;
|
|
1403
|
+
for (let current = entity; current; current = current.parent) {
|
|
1404
|
+
opacity *= current.opacity;
|
|
1405
|
+
}
|
|
1329
1406
|
uniformArray[0] = color[0];
|
|
1330
1407
|
uniformArray[1] = color[1];
|
|
1331
1408
|
uniformArray[2] = color[2];
|
|
1332
|
-
uniformArray[3] = color[3];
|
|
1409
|
+
uniformArray[3] = color[3] * opacity;
|
|
1333
1410
|
const mActive = !isNaN(mouseX) && !isNaN(mouseY) && mouseX > -9e3 && mouseY > -9e3;
|
|
1334
1411
|
uniformArray[4] = mActive ? mouseX : -9999;
|
|
1335
1412
|
uniformArray[5] = mActive ? mouseY : -9999;
|
|
@@ -1377,4 +1454,6 @@ var WebGPUParticleSystemManager = (_class3 = class {
|
|
|
1377
1454
|
|
|
1378
1455
|
|
|
1379
1456
|
|
|
1380
|
-
|
|
1457
|
+
|
|
1458
|
+
|
|
1459
|
+
exports.CanvasRenderer = CanvasRenderer; exports.sanitizeUrl = sanitizeUrl; exports.isSafeUrl = isSafeUrl; exports.SVGRenderer = SVGRenderer; exports.parseColorToRGBA = parseColorToRGBA; exports.createWebGLPointRenderer = createWebGLPointRenderer; exports.WebGPUParticleSystemManager = WebGPUParticleSystemManager;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunk6I53LI3Zjs = require('./chunk-6I53LI3Z.js');
|
|
5
5
|
|
|
6
6
|
// src/layout/LayoutEngine.ts
|
|
7
7
|
function computeLineSegments(top, bottom, maxWidth, exclusions) {
|
|
@@ -142,8 +142,8 @@ var LayoutEngine = (_class = class {
|
|
|
142
142
|
offset += paragraph.length + 1;
|
|
143
143
|
continue;
|
|
144
144
|
}
|
|
145
|
-
const { shapedText, indexMap } =
|
|
146
|
-
const levels =
|
|
145
|
+
const { shapedText, indexMap } = _chunk6I53LI3Zjs.ArabicShaper.shapeArabic(paragraph);
|
|
146
|
+
const levels = _chunk6I53LI3Zjs.BidiResolver.resolveLevels(shapedText);
|
|
147
147
|
const words = [];
|
|
148
148
|
let shapedCharIdx = 0;
|
|
149
149
|
let pFallback = false;
|
|
@@ -192,7 +192,7 @@ var LayoutEngine = (_class = class {
|
|
|
192
192
|
words,
|
|
193
193
|
isEmpty: false,
|
|
194
194
|
fallbackToCanvas: pFallback || void 0,
|
|
195
|
-
baseLevel:
|
|
195
|
+
baseLevel: _chunk6I53LI3Zjs.BidiResolver.getBaseLevel(shapedText)
|
|
196
196
|
};
|
|
197
197
|
if (this.paragraphCache.size > 1e3) this.paragraphCache.clear();
|
|
198
198
|
this.paragraphCache.set(key, prepared);
|
|
@@ -264,8 +264,8 @@ var LayoutEngine = (_class = class {
|
|
|
264
264
|
offset += paragraph.length + 1;
|
|
265
265
|
continue;
|
|
266
266
|
}
|
|
267
|
-
const { shapedText, indexMap } =
|
|
268
|
-
const levels =
|
|
267
|
+
const { shapedText, indexMap } = _chunk6I53LI3Zjs.ArabicShaper.shapeArabic(paragraph);
|
|
268
|
+
const levels = _chunk6I53LI3Zjs.BidiResolver.resolveLevels(shapedText);
|
|
269
269
|
const words = [];
|
|
270
270
|
let shapedCharIdx = 0;
|
|
271
271
|
let pFallback = false;
|
|
@@ -317,7 +317,7 @@ var LayoutEngine = (_class = class {
|
|
|
317
317
|
words,
|
|
318
318
|
isEmpty: false,
|
|
319
319
|
fallbackToCanvas: pFallback || void 0,
|
|
320
|
-
baseLevel:
|
|
320
|
+
baseLevel: _chunk6I53LI3Zjs.BidiResolver.getBaseLevel(shapedText)
|
|
321
321
|
};
|
|
322
322
|
if (this.richParagraphCache.size > 1e3) this.richParagraphCache.clear();
|
|
323
323
|
this.richParagraphCache.set(key, prepared);
|
|
@@ -372,7 +372,7 @@ var LayoutEngine = (_class = class {
|
|
|
372
372
|
}
|
|
373
373
|
for (const run of runs) {
|
|
374
374
|
const runStartX = run[0].x;
|
|
375
|
-
|
|
375
|
+
_chunk6I53LI3Zjs.BidiResolver.reorderVisual(run, paragraphBaseLevel);
|
|
376
376
|
let x = runStartX;
|
|
377
377
|
for (const node of run) {
|
|
378
378
|
node.x = x;
|
|
@@ -301,24 +301,57 @@ var WORKER_SOURCE_STRING = '"use strict";(()=>{var b=new Map;self.onmessage=x=>{
|
|
|
301
301
|
// src/layout/LayoutWorkerManager.ts
|
|
302
302
|
var LayoutWorkerManager = class _LayoutWorkerManager {
|
|
303
303
|
static instance;
|
|
304
|
-
worker;
|
|
304
|
+
worker = null;
|
|
305
305
|
registeredFonts = /* @__PURE__ */ new Set();
|
|
306
306
|
pendingCallbacks = /* @__PURE__ */ new Map();
|
|
307
307
|
seqIdCounter = /* @__PURE__ */ new Map();
|
|
308
308
|
debounceTimers = /* @__PURE__ */ new Map();
|
|
309
309
|
constructor() {
|
|
310
|
+
this.worker = this.createWorker();
|
|
311
|
+
}
|
|
312
|
+
createWorker() {
|
|
310
313
|
const workerBlob = new Blob([WORKER_SOURCE_STRING], { type: "application/javascript" });
|
|
311
314
|
const workerURL = URL.createObjectURL(workerBlob);
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
+
let worker;
|
|
316
|
+
try {
|
|
317
|
+
worker = new Worker(workerURL);
|
|
318
|
+
} finally {
|
|
319
|
+
URL.revokeObjectURL(workerURL);
|
|
320
|
+
}
|
|
321
|
+
worker.onmessage = (e) => {
|
|
322
|
+
if (this.worker !== worker) return;
|
|
315
323
|
const response = e.data;
|
|
316
|
-
const
|
|
324
|
+
const key = `${response.id}-${response.seqId}`;
|
|
325
|
+
const callback = this.pendingCallbacks.get(key);
|
|
317
326
|
if (callback) {
|
|
327
|
+
this.pendingCallbacks.delete(key);
|
|
318
328
|
callback(response);
|
|
319
|
-
this.pendingCallbacks.delete(`${response.id}-${response.seqId}`);
|
|
320
329
|
}
|
|
321
330
|
};
|
|
331
|
+
worker.onerror = () => this.handleWorkerFailure(worker);
|
|
332
|
+
worker.onmessageerror = () => this.handleWorkerFailure(worker);
|
|
333
|
+
return worker;
|
|
334
|
+
}
|
|
335
|
+
ensureWorker() {
|
|
336
|
+
if (!this.worker) this.worker = this.createWorker();
|
|
337
|
+
return this.worker;
|
|
338
|
+
}
|
|
339
|
+
handleWorkerFailure(worker) {
|
|
340
|
+
if (this.worker !== worker) return;
|
|
341
|
+
worker.terminate();
|
|
342
|
+
this.worker = null;
|
|
343
|
+
this.pendingCallbacks.clear();
|
|
344
|
+
this.registeredFonts.clear();
|
|
345
|
+
}
|
|
346
|
+
destroy() {
|
|
347
|
+
for (const timer of this.debounceTimers.values()) clearTimeout(timer);
|
|
348
|
+
this.debounceTimers.clear();
|
|
349
|
+
this.pendingCallbacks.clear();
|
|
350
|
+
this.seqIdCounter.clear();
|
|
351
|
+
this.registeredFonts.clear();
|
|
352
|
+
this.worker?.terminate();
|
|
353
|
+
this.worker = null;
|
|
354
|
+
if (_LayoutWorkerManager.instance === this) _LayoutWorkerManager.instance = void 0;
|
|
322
355
|
}
|
|
323
356
|
static getInstance() {
|
|
324
357
|
if (!_LayoutWorkerManager.instance) {
|
|
@@ -332,6 +365,7 @@ var LayoutWorkerManager = class _LayoutWorkerManager {
|
|
|
332
365
|
clearTimeout(existingTimer);
|
|
333
366
|
}
|
|
334
367
|
const runLayout = () => {
|
|
368
|
+
this.debounceTimers.delete(entityId);
|
|
335
369
|
const nextSeqId = (this.seqIdCounter.get(entityId) ?? 0) + 1;
|
|
336
370
|
this.seqIdCounter.set(entityId, nextSeqId);
|
|
337
371
|
const request = {
|
|
@@ -350,7 +384,12 @@ var LayoutWorkerManager = class _LayoutWorkerManager {
|
|
|
350
384
|
this.registeredFonts.add(options.fontId);
|
|
351
385
|
}
|
|
352
386
|
this.pendingCallbacks.set(`${entityId}-${nextSeqId}`, options.callback);
|
|
353
|
-
|
|
387
|
+
try {
|
|
388
|
+
this.ensureWorker().postMessage(request);
|
|
389
|
+
} catch {
|
|
390
|
+
const worker = this.worker;
|
|
391
|
+
if (worker) this.handleWorkerFailure(worker);
|
|
392
|
+
}
|
|
354
393
|
};
|
|
355
394
|
if (!this.seqIdCounter.has(entityId)) {
|
|
356
395
|
runLayout();
|
|
@@ -365,6 +404,9 @@ var LayoutWorkerManager = class _LayoutWorkerManager {
|
|
|
365
404
|
clearTimeout(existingTimer);
|
|
366
405
|
this.debounceTimers.delete(entityId);
|
|
367
406
|
}
|
|
407
|
+
for (const key of this.pendingCallbacks.keys()) {
|
|
408
|
+
if (key.startsWith(`${entityId}-`)) this.pendingCallbacks.delete(key);
|
|
409
|
+
}
|
|
368
410
|
this.seqIdCounter.delete(entityId);
|
|
369
411
|
}
|
|
370
412
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from './renderer/SVGRenderer';
|
|
|
4
4
|
export * from './renderer/WebGLPointRenderer';
|
|
5
5
|
export * from './renderer/WebGPUParticleSystemManager';
|
|
6
6
|
export * from './renderer/colorParse';
|
|
7
|
+
export * from './renderer/url';
|
|
7
8
|
export * from './tree/Entity';
|
|
8
9
|
export * from './tree/Scene';
|
|
9
10
|
export * from './components/TextEntity';
|