@meonode/canvas 2.0.5 → 3.0.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/CONTRIBUTING.md +9 -9
- package/README.md +9 -21
- package/dist/cjs/canvas/canvas.helper.d.ts +1 -1
- package/dist/cjs/canvas/canvas.type.d.ts +9 -4
- package/dist/cjs/canvas/canvas.type.d.ts.map +1 -1
- package/dist/cjs/canvas/chart.canvas.d.ts +7 -3
- package/dist/cjs/canvas/chart.canvas.d.ts.map +1 -1
- package/dist/cjs/canvas/chart.canvas.js +3 -4
- package/dist/cjs/canvas/chart.canvas.js.map +1 -1
- package/dist/cjs/canvas/grid.canvas.d.ts +2 -2
- package/dist/cjs/canvas/grid.canvas.d.ts.map +1 -1
- package/dist/cjs/canvas/grid.canvas.js +2 -8
- package/dist/cjs/canvas/grid.canvas.js.map +1 -1
- package/dist/cjs/canvas/image.canvas.d.ts +2 -2
- package/dist/cjs/canvas/image.canvas.js.map +1 -1
- package/dist/cjs/canvas/layout.canvas.d.ts +5 -1
- package/dist/cjs/canvas/layout.canvas.d.ts.map +1 -1
- package/dist/cjs/canvas/layout.canvas.js +59 -68
- package/dist/cjs/canvas/layout.canvas.js.map +1 -1
- package/dist/cjs/canvas/root.canvas.d.ts +9 -16
- package/dist/cjs/canvas/root.canvas.d.ts.map +1 -1
- package/dist/cjs/canvas/root.canvas.js +56 -43
- package/dist/cjs/canvas/root.canvas.js.map +1 -1
- package/dist/cjs/canvas/text.canvas.d.ts +7 -3
- package/dist/cjs/canvas/text.canvas.d.ts.map +1 -1
- package/dist/cjs/canvas/text.canvas.js +25 -85
- package/dist/cjs/canvas/text.canvas.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/util/disk.cache.d.ts +5 -0
- package/dist/cjs/util/disk.cache.d.ts.map +1 -1
- package/dist/cjs/util/disk.cache.js +23 -8
- package/dist/cjs/util/disk.cache.js.map +1 -1
- package/dist/cjs/worker/comlink.pool.d.ts +1 -1
- package/dist/cjs/worker/comlink.pool.d.ts.map +1 -1
- package/dist/cjs/worker/comlink.pool.js +3 -0
- package/dist/cjs/worker/comlink.pool.js.map +1 -1
- package/dist/cjs/worker/comlink.setup.js +0 -1
- package/dist/cjs/worker/comlink.setup.js.map +1 -1
- package/dist/esm/canvas/canvas.helper.d.ts +1 -1
- package/dist/esm/canvas/canvas.type.d.ts +9 -4
- package/dist/esm/canvas/canvas.type.d.ts.map +1 -1
- package/dist/esm/canvas/chart.canvas.d.ts +7 -3
- package/dist/esm/canvas/chart.canvas.d.ts.map +1 -1
- package/dist/esm/canvas/chart.canvas.js +3 -4
- package/dist/esm/canvas/grid.canvas.d.ts +2 -2
- package/dist/esm/canvas/grid.canvas.d.ts.map +1 -1
- package/dist/esm/canvas/grid.canvas.js +1 -7
- package/dist/esm/canvas/image.canvas.d.ts +2 -2
- package/dist/esm/canvas/layout.canvas.d.ts +5 -1
- package/dist/esm/canvas/layout.canvas.d.ts.map +1 -1
- package/dist/esm/canvas/layout.canvas.js +59 -69
- package/dist/esm/canvas/root.canvas.d.ts +9 -16
- package/dist/esm/canvas/root.canvas.d.ts.map +1 -1
- package/dist/esm/canvas/root.canvas.js +57 -43
- package/dist/esm/canvas/text.canvas.d.ts +7 -3
- package/dist/esm/canvas/text.canvas.d.ts.map +1 -1
- package/dist/esm/canvas/text.canvas.js +25 -85
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/util/disk.cache.d.ts +5 -0
- package/dist/esm/util/disk.cache.d.ts.map +1 -1
- package/dist/esm/util/disk.cache.js +23 -9
- package/dist/esm/worker/comlink.pool.d.ts +1 -1
- package/dist/esm/worker/comlink.pool.d.ts.map +1 -1
- package/dist/esm/worker/comlink.pool.js +3 -0
- package/package.json +9 -16
|
@@ -12,6 +12,7 @@ import { cpus } from 'node:os';
|
|
|
12
12
|
|
|
13
13
|
/** Registry to track fonts that have already been loaded */
|
|
14
14
|
const registeredFonts = new Map();
|
|
15
|
+
let _fontRegistrationLock = null;
|
|
15
16
|
/**
|
|
16
17
|
* FinalizationRegistry to clean up WorkerCanvas instances that were not explicitly released.
|
|
17
18
|
* This is a safety net — users should still call .release() explicitly.
|
|
@@ -24,25 +25,10 @@ const canvasRegistry = new FinalizationRegistry(heldValue => {
|
|
|
24
25
|
// Worker already gone — nothing to clean up
|
|
25
26
|
}
|
|
26
27
|
});
|
|
27
|
-
/** Engine configuration — legacy support for configure() */
|
|
28
|
-
let _defaultWorkerMode = true;
|
|
29
|
-
let _defaultWorkerPoolSize = Math.max(1, cpus().length - 1);
|
|
30
28
|
let _workerPool = null;
|
|
31
|
-
/**
|
|
32
|
-
* Configure the canvas rendering engine.
|
|
33
|
-
* Call this once at application startup before rendering.
|
|
34
|
-
* @deprecated Pass workerMode and workers directly to Root() props instead.
|
|
35
|
-
*/
|
|
36
|
-
function configure(options) {
|
|
37
|
-
if (options.workerMode !== undefined)
|
|
38
|
-
_defaultWorkerMode = options.workerMode;
|
|
39
|
-
if (options.workers !== undefined)
|
|
40
|
-
_defaultWorkerPoolSize = options.workers;
|
|
41
|
-
}
|
|
42
29
|
/**
|
|
43
30
|
* Terminate all worker pools and free worker thread resources.
|
|
44
31
|
* Call this when shutting down a long-running server to clean up immediately.
|
|
45
|
-
* After calling, you must call configure() again before rendering.
|
|
46
32
|
*/
|
|
47
33
|
function terminate() {
|
|
48
34
|
if (_workerPool) {
|
|
@@ -159,6 +145,8 @@ class RootNode extends ColumnNode {
|
|
|
159
145
|
targetHeight;
|
|
160
146
|
/** Scale factor for rendering (e.g. 2 for 2x resolution) */
|
|
161
147
|
scale;
|
|
148
|
+
/** Max concurrent image fetches during render (default: 5) */
|
|
149
|
+
imageConcurrency;
|
|
162
150
|
/**
|
|
163
151
|
* Creates a new root node for canvas rendering
|
|
164
152
|
* @param props Configuration properties for the root node
|
|
@@ -172,36 +160,22 @@ class RootNode extends ColumnNode {
|
|
|
172
160
|
if (!props.width) {
|
|
173
161
|
throw new Error('Width and height are required for Root');
|
|
174
162
|
}
|
|
175
|
-
// Register provided fonts with caching
|
|
176
|
-
if (props.fonts?.length) {
|
|
177
|
-
for (const font of props.fonts) {
|
|
178
|
-
const family = font.family;
|
|
179
|
-
const paths = font.paths.map(p => path.resolve(p));
|
|
180
|
-
if (!registeredFonts.has(family)) {
|
|
181
|
-
registeredFonts.set(family, new Set());
|
|
182
|
-
}
|
|
183
|
-
const cachedPaths = registeredFonts.get(family);
|
|
184
|
-
const newPaths = paths.filter(p => !cachedPaths.has(p) && fs.existsSync(p));
|
|
185
|
-
if (newPaths.length > 0) {
|
|
186
|
-
FontLibrary.use({ [family]: newPaths });
|
|
187
|
-
newPaths.forEach(p => cachedPaths.add(p));
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
163
|
// Set up scale and width
|
|
192
164
|
this.scale = props.scale || 1;
|
|
193
165
|
this.targetWidth = props.width;
|
|
194
166
|
this.targetHeight = props.height;
|
|
167
|
+
this.imageConcurrency = props.imageConcurrency ?? 5;
|
|
195
168
|
this.node.setWidth(this.targetWidth);
|
|
196
169
|
// Convert any CanvasElement children to actual BoxNode instances
|
|
197
170
|
if (this.props.children) {
|
|
198
171
|
const childArray = Array.isArray(this.props.children) ? this.props.children : [this.props.children];
|
|
199
|
-
|
|
172
|
+
const converted = childArray.map(child => {
|
|
200
173
|
if (child && typeof child === 'object' && '__type' in child) {
|
|
201
174
|
return buildTree(child);
|
|
202
175
|
}
|
|
203
176
|
return child;
|
|
204
177
|
});
|
|
178
|
+
this.props.children = converted;
|
|
205
179
|
}
|
|
206
180
|
// Initialize children nodes
|
|
207
181
|
this.processInitialChildren();
|
|
@@ -213,8 +187,9 @@ class RootNode extends ColumnNode {
|
|
|
213
187
|
findAllImageNodes() {
|
|
214
188
|
const imageNodes = [];
|
|
215
189
|
const queue = [this];
|
|
216
|
-
|
|
217
|
-
|
|
190
|
+
let head = 0;
|
|
191
|
+
while (head < queue.length) {
|
|
192
|
+
const node = queue[head++];
|
|
218
193
|
if (node instanceof ImageNode) {
|
|
219
194
|
imageNodes.push(node);
|
|
220
195
|
}
|
|
@@ -222,12 +197,46 @@ class RootNode extends ColumnNode {
|
|
|
222
197
|
}
|
|
223
198
|
return imageNodes;
|
|
224
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Registers fonts with serialization to prevent duplicate FontLibrary.use() calls
|
|
202
|
+
* when multiple Root() instances are created concurrently.
|
|
203
|
+
*/
|
|
204
|
+
async _registerFonts() {
|
|
205
|
+
if (!this.props.fonts?.length)
|
|
206
|
+
return;
|
|
207
|
+
// Wait for any in-flight registration to complete
|
|
208
|
+
if (_fontRegistrationLock)
|
|
209
|
+
await _fontRegistrationLock;
|
|
210
|
+
_fontRegistrationLock = (async () => {
|
|
211
|
+
try {
|
|
212
|
+
for (const font of this.props.fonts) {
|
|
213
|
+
const family = font.family;
|
|
214
|
+
const paths = font.paths.map(p => path.resolve(p));
|
|
215
|
+
if (!registeredFonts.has(family)) {
|
|
216
|
+
registeredFonts.set(family, new Set());
|
|
217
|
+
}
|
|
218
|
+
const cachedPaths = registeredFonts.get(family);
|
|
219
|
+
const newPaths = paths.filter(p => !cachedPaths.has(p) && fs.existsSync(p));
|
|
220
|
+
if (newPaths.length > 0) {
|
|
221
|
+
FontLibrary.use({ [family]: newPaths });
|
|
222
|
+
newPaths.forEach(p => cachedPaths.add(p));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
_fontRegistrationLock = null;
|
|
228
|
+
}
|
|
229
|
+
})();
|
|
230
|
+
await _fontRegistrationLock;
|
|
231
|
+
}
|
|
225
232
|
async render(ctx, offsetX = 0, offsetY = 0) {
|
|
226
233
|
// If ctx is provided, delegate to parent render (used when called as a child node)
|
|
227
234
|
if (ctx) {
|
|
228
235
|
await super.render(ctx, offsetX, offsetY);
|
|
229
236
|
return;
|
|
230
237
|
}
|
|
238
|
+
// Register fonts with serialization to prevent duplicate FontLibrary.use() across concurrent Root() calls
|
|
239
|
+
await this._registerFonts();
|
|
231
240
|
const diskCacheKeys = this.props.useDiskCache ? new Set() : undefined;
|
|
232
241
|
try {
|
|
233
242
|
// Step 1: Load all images with a concurrency limit to avoid overwhelming remote sources.
|
|
@@ -235,15 +244,20 @@ class RootNode extends ColumnNode {
|
|
|
235
244
|
const imageNodes = this.findAllImageNodes();
|
|
236
245
|
if (imageNodes.length > 0) {
|
|
237
246
|
const imageCache = new Map();
|
|
238
|
-
const CONCURRENCY = 5;
|
|
239
247
|
const queue = [...imageNodes];
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
248
|
+
let qIdx = 0;
|
|
249
|
+
const workers = Array.from({ length: Math.min(this.imageConcurrency, queue.length) }, async () => {
|
|
250
|
+
while (qIdx < queue.length) {
|
|
251
|
+
const node = queue[qIdx++];
|
|
243
252
|
await node.load(imageCache, diskCacheKeys);
|
|
244
253
|
}
|
|
245
254
|
});
|
|
246
|
-
await Promise.allSettled(workers)
|
|
255
|
+
await Promise.allSettled(workers).then(results => {
|
|
256
|
+
results.forEach(r => {
|
|
257
|
+
if (r.status === 'rejected')
|
|
258
|
+
console.warn('[RootNode] Image load worker failed:', r.reason);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
247
261
|
}
|
|
248
262
|
// Step 2: Calculate initial layout
|
|
249
263
|
this.node.calculateLayout(this.targetWidth, undefined, Style.Direction.LTR);
|
|
@@ -275,9 +289,9 @@ class RootNode extends ColumnNode {
|
|
|
275
289
|
}
|
|
276
290
|
}
|
|
277
291
|
async function Root(props) {
|
|
278
|
-
// Determine worker mode
|
|
279
|
-
const workerMode = props.workerMode ??
|
|
280
|
-
const workerPoolSize = props.workers ??
|
|
292
|
+
// Determine worker mode
|
|
293
|
+
const workerMode = props.workerMode ?? true;
|
|
294
|
+
const workerPoolSize = props.workers ?? Math.max(1, cpus().length - 1);
|
|
281
295
|
if (workerMode) {
|
|
282
296
|
// Lazy initialize worker pool — dynamic import to avoid loading Comlink in non-worker contexts
|
|
283
297
|
if (!_workerPool) {
|
|
@@ -291,4 +305,4 @@ async function Root(props) {
|
|
|
291
305
|
return new RootNode(props).render();
|
|
292
306
|
}
|
|
293
307
|
|
|
294
|
-
export { Root, RootNode, buildTree,
|
|
308
|
+
export { Root, RootNode, buildTree, terminate };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { TextProps, CanvasElement } from '
|
|
1
|
+
import type { TextProps, CanvasElement } from './canvas.type.js';
|
|
2
2
|
import { type CanvasRenderingContext2D } from 'skia-canvas';
|
|
3
|
-
import { BoxNode } from '
|
|
3
|
+
import { BoxNode } from './layout.canvas.js';
|
|
4
4
|
/**
|
|
5
5
|
* Node for rendering text content with rich text styling support
|
|
6
6
|
* Supports color and weight variations through HTML-like tags
|
|
@@ -140,7 +140,11 @@ export declare class TextNode extends BoxNode {
|
|
|
140
140
|
*/
|
|
141
141
|
private measureSpaceWidth;
|
|
142
142
|
/**
|
|
143
|
-
*
|
|
143
|
+
* Applies this.props.fontVariant to the context, or resets to 'normal'.
|
|
144
|
+
* Centralizes the type guard + warn pattern repeated across measure/render paths.
|
|
145
|
+
*/
|
|
146
|
+
private _applyFontVariant;
|
|
147
|
+
/**
|
|
144
148
|
*
|
|
145
149
|
* Core features:
|
|
146
150
|
* - Dynamic line heights with leading/spacing controls
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.canvas.d.ts","sourceRoot":"","sources":["../../../src/canvas/text.canvas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACpF,OAAO,EAAU,KAAK,wBAAwB,EAA2B,MAAM,aAAa,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAGnD;;;GAGG;AACH,qBAAa,QAAS,SAAQ,OAAO;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAwC;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,kBAAkB,CAAe;IAEjC,KAAK,EAAE,SAAS,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;gBAElC,IAAI,GAAE,MAAM,GAAG,MAAW,EAAE,KAAK,GAAE,SAAc;IAuB7D;;;;;;;;OAQG;WACW,gBAAgB,CAC5B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,MAAM,EACZ,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,GAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;QACpC,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;QAClC,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,wBAAwB,CAAC,WAAW,CAAC,CAAA;QACjD,YAAY,CAAC,EAAE,wBAAwB,CAAC,cAAc,CAAC,CAAA;KACnD;cAwBW,aAAa,IAAI,IAAI;IAoDxC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,sBAAsB;IA8B9B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,aAAa;IA+ErB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,gBAAgB;IAyBxB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAM7B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IAiCrB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAQjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,WAAW;
|
|
1
|
+
{"version":3,"file":"text.canvas.d.ts","sourceRoot":"","sources":["../../../src/canvas/text.canvas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACpF,OAAO,EAAU,KAAK,wBAAwB,EAA2B,MAAM,aAAa,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAGnD;;;GAGG;AACH,qBAAa,QAAS,SAAQ,OAAO;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAwC;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,kBAAkB,CAAe;IAEjC,KAAK,EAAE,SAAS,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;gBAElC,IAAI,GAAE,MAAM,GAAG,MAAW,EAAE,KAAK,GAAE,SAAc;IAuB7D;;;;;;;;OAQG;WACW,gBAAgB,CAC5B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,MAAM,EACZ,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,GAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;QACpC,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;QAClC,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,wBAAwB,CAAC,WAAW,CAAC,CAAA;QACjD,YAAY,CAAC,EAAE,wBAAwB,CAAC,cAAc,CAAC,CAAA;KACnD;cAwBW,aAAa,IAAI,IAAI;IAoDxC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,sBAAsB;IA8B9B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,aAAa;IA+ErB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,gBAAgB;IAyBxB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAM7B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IAiCrB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAQjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,WAAW;IA4LnB;;;;;;;;;OASG;IACH,OAAO,CAAC,YAAY;IA6KpB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAmErB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;;;;;;;;;;;;;OAeG;cACsB,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAoW3H;AAED;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,GAAG,MAAM,EAAE,QAAQ,SAAS,KAAG,aAI9D,CAAA"}
|
|
@@ -352,18 +352,7 @@ class TextNode extends BoxNode {
|
|
|
352
352
|
// Pre-measure each text segment width with its specific styling
|
|
353
353
|
for (const segment of this.segments) {
|
|
354
354
|
ctx.font = this.getFontString(segment);
|
|
355
|
-
|
|
356
|
-
ctx.fontVariant = this.props.fontVariant;
|
|
357
|
-
}
|
|
358
|
-
else if (this.props.fontVariant !== undefined) {
|
|
359
|
-
console.warn(`[TextNode ${this.key || ''}] Invalid fontVariant prop type in measureText (segment width):`, this.props.fontVariant);
|
|
360
|
-
if (ctx.fontVariant !== 'normal')
|
|
361
|
-
ctx.fontVariant = 'normal';
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
if (ctx.fontVariant !== 'normal')
|
|
365
|
-
ctx.fontVariant = 'normal';
|
|
366
|
-
}
|
|
355
|
+
this._applyFontVariant(ctx, 'measureText (segment width)');
|
|
367
356
|
segment.width = this.addLetterSpacingExtra(segment.text, ctx.measureText(segment.text).width, parsedLetterSpacingPx);
|
|
368
357
|
}
|
|
369
358
|
// Calculate available layout width
|
|
@@ -387,18 +376,7 @@ class TextNode extends BoxNode {
|
|
|
387
376
|
// Handle empty line metrics
|
|
388
377
|
if (line.length === 0) {
|
|
389
378
|
ctx.font = this.getFontString();
|
|
390
|
-
|
|
391
|
-
ctx.fontVariant = this.props.fontVariant;
|
|
392
|
-
}
|
|
393
|
-
else if (this.props.fontVariant !== undefined) {
|
|
394
|
-
console.warn(`[TextNode ${this.key || ''}] Invalid fontVariant prop type in measureText (empty line):`, this.props.fontVariant);
|
|
395
|
-
if (ctx.fontVariant !== 'normal')
|
|
396
|
-
ctx.fontVariant = 'normal';
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
if (ctx.fontVariant !== 'normal')
|
|
400
|
-
ctx.fontVariant = 'normal';
|
|
401
|
-
}
|
|
379
|
+
this._applyFontVariant(ctx, 'measureText (empty line)');
|
|
402
380
|
const metrics = ctx.measureText(this.metricsString);
|
|
403
381
|
maxAscent = metrics.actualBoundingBoxAscent ?? baseFontSize * 0.8;
|
|
404
382
|
maxDescent = metrics.actualBoundingBoxDescent ?? baseFontSize * 0.2;
|
|
@@ -412,18 +390,7 @@ class TextNode extends BoxNode {
|
|
|
412
390
|
const segmentSize = segment.size || baseFontSize;
|
|
413
391
|
maxFontSizeOnLine = Math.max(maxFontSizeOnLine, segmentSize);
|
|
414
392
|
ctx.font = this.getFontString(segment);
|
|
415
|
-
|
|
416
|
-
ctx.fontVariant = this.props.fontVariant;
|
|
417
|
-
}
|
|
418
|
-
else if (this.props.fontVariant !== undefined) {
|
|
419
|
-
console.warn(`[TextNode ${this.key || ''}] Invalid fontVariant prop type in measureText (segment height):`, this.props.fontVariant);
|
|
420
|
-
if (ctx.fontVariant !== 'normal')
|
|
421
|
-
ctx.fontVariant = 'normal';
|
|
422
|
-
}
|
|
423
|
-
else {
|
|
424
|
-
if (ctx.fontVariant !== 'normal')
|
|
425
|
-
ctx.fontVariant = 'normal';
|
|
426
|
-
}
|
|
393
|
+
this._applyFontVariant(ctx, 'measureText (segment height)');
|
|
427
394
|
const metrics = ctx.measureText(this.metricsString);
|
|
428
395
|
const ascent = metrics.actualBoundingBoxAscent ?? segmentSize * 0.8;
|
|
429
396
|
const descent = metrics.actualBoundingBoxDescent ?? segmentSize * 0.2;
|
|
@@ -434,18 +401,7 @@ class TextNode extends BoxNode {
|
|
|
434
401
|
// Fallback metrics for lines with only whitespace
|
|
435
402
|
if (maxAscent === 0 && maxDescent === 0 && line.length > 0) {
|
|
436
403
|
ctx.font = this.getFontString();
|
|
437
|
-
|
|
438
|
-
ctx.fontVariant = this.props.fontVariant;
|
|
439
|
-
}
|
|
440
|
-
else if (this.props.fontVariant !== undefined) {
|
|
441
|
-
console.warn(`[TextNode ${this.key || ''}] Invalid fontVariant prop type in measureText (fallback):`, this.props.fontVariant);
|
|
442
|
-
if (ctx.fontVariant !== 'normal')
|
|
443
|
-
ctx.fontVariant = 'normal';
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
446
|
-
if (ctx.fontVariant !== 'normal')
|
|
447
|
-
ctx.fontVariant = 'normal';
|
|
448
|
-
}
|
|
404
|
+
this._applyFontVariant(ctx, 'measureText (fallback)');
|
|
449
405
|
const metrics = ctx.measureText(this.metricsString);
|
|
450
406
|
maxAscent = metrics.actualBoundingBoxAscent ?? baseFontSize * 0.8;
|
|
451
407
|
maxDescent = metrics.actualBoundingBoxDescent ?? baseFontSize * 0.2;
|
|
@@ -478,18 +434,7 @@ class TextNode extends BoxNode {
|
|
|
478
434
|
if (/^\s+$/.test(word))
|
|
479
435
|
continue;
|
|
480
436
|
ctx.font = this.getFontString(segment);
|
|
481
|
-
|
|
482
|
-
ctx.fontVariant = this.props.fontVariant;
|
|
483
|
-
}
|
|
484
|
-
else if (this.props.fontVariant !== undefined) {
|
|
485
|
-
console.warn(`[TextNode ${this.key || ''}] Invalid fontVariant prop type in measureText (single line width):`, this.props.fontVariant);
|
|
486
|
-
if (ctx.fontVariant !== 'normal')
|
|
487
|
-
ctx.fontVariant = 'normal';
|
|
488
|
-
}
|
|
489
|
-
else {
|
|
490
|
-
if (ctx.fontVariant !== 'normal')
|
|
491
|
-
ctx.fontVariant = 'normal';
|
|
492
|
-
}
|
|
437
|
+
this._applyFontVariant(ctx, 'measureText (single line width)');
|
|
493
438
|
const wordWidth = this.addLetterSpacingExtra(word, ctx.measureText(word).width, parsedLetterSpacingPx);
|
|
494
439
|
if (!firstWordInSingleLine) {
|
|
495
440
|
singleLineWidth += spaceWidth + parsedWordSpacingPx;
|
|
@@ -800,7 +745,24 @@ class TextNode extends BoxNode {
|
|
|
800
745
|
return width > 0 ? width : (this.props.fontSize || 16) * 0.3;
|
|
801
746
|
}
|
|
802
747
|
/**
|
|
803
|
-
*
|
|
748
|
+
* Applies this.props.fontVariant to the context, or resets to 'normal'.
|
|
749
|
+
* Centralizes the type guard + warn pattern repeated across measure/render paths.
|
|
750
|
+
*/
|
|
751
|
+
_applyFontVariant(ctx, context) {
|
|
752
|
+
if (typeof this.props.fontVariant === 'string') {
|
|
753
|
+
ctx.fontVariant = this.props.fontVariant;
|
|
754
|
+
}
|
|
755
|
+
else if (this.props.fontVariant !== undefined) {
|
|
756
|
+
console.warn(`[TextNode ${this.key || ''}] Invalid fontVariant prop type in ${context}:`, this.props.fontVariant);
|
|
757
|
+
if (ctx.fontVariant !== 'normal')
|
|
758
|
+
ctx.fontVariant = 'normal';
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
if (ctx.fontVariant !== 'normal')
|
|
762
|
+
ctx.fontVariant = 'normal';
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
804
766
|
*
|
|
805
767
|
* Core features:
|
|
806
768
|
* - Dynamic line heights with leading/spacing controls
|
|
@@ -1040,18 +1002,7 @@ class TextNode extends BoxNode {
|
|
|
1040
1002
|
// Apply segment styles
|
|
1041
1003
|
ctx.font = this.getFontString(segment);
|
|
1042
1004
|
ctx.fillStyle = segment.color || this.props.color || 'black';
|
|
1043
|
-
|
|
1044
|
-
ctx.fontVariant = this.props.fontVariant;
|
|
1045
|
-
}
|
|
1046
|
-
else if (this.props.fontVariant !== undefined) {
|
|
1047
|
-
console.warn(`[TextNode ${this.key || ''}] Invalid fontVariant prop type in _renderContent (segment render):`, this.props.fontVariant);
|
|
1048
|
-
if (ctx.fontVariant !== 'normal')
|
|
1049
|
-
ctx.fontVariant = 'normal';
|
|
1050
|
-
}
|
|
1051
|
-
else {
|
|
1052
|
-
if (ctx.fontVariant !== 'normal')
|
|
1053
|
-
ctx.fontVariant = 'normal';
|
|
1054
|
-
}
|
|
1005
|
+
this._applyFontVariant(ctx, '_renderContent (segment render)');
|
|
1055
1006
|
// Handle text truncation and ellipsis
|
|
1056
1007
|
let textToDraw = segment.text;
|
|
1057
1008
|
let currentSegmentRenderWidth = segmentWidth;
|
|
@@ -1119,18 +1070,7 @@ class TextNode extends BoxNode {
|
|
|
1119
1070
|
if (ellipsisRemainingWidth >= ellipsisWidth) {
|
|
1120
1071
|
ctx.save();
|
|
1121
1072
|
ctx.font = this.getFontString(ellipsisStyle);
|
|
1122
|
-
|
|
1123
|
-
ctx.fontVariant = this.props.fontVariant;
|
|
1124
|
-
}
|
|
1125
|
-
else if (this.props.fontVariant !== undefined) {
|
|
1126
|
-
console.warn(`[TextNode ${this.key || ''}] Invalid fontVariant prop type in _renderContent (ellipsis draw):`, this.props.fontVariant);
|
|
1127
|
-
if (ctx.fontVariant !== 'normal')
|
|
1128
|
-
ctx.fontVariant = 'normal';
|
|
1129
|
-
}
|
|
1130
|
-
else {
|
|
1131
|
-
if (ctx.fontVariant !== 'normal')
|
|
1132
|
-
ctx.fontVariant = 'normal';
|
|
1133
|
-
}
|
|
1073
|
+
this._applyFontVariant(ctx, '_renderContent (ellipsis draw)');
|
|
1134
1074
|
ctx.fillStyle = ellipsisStyle?.color || this.props.color || 'black';
|
|
1135
1075
|
ctx.fillText(ellipsisChar, currentX, lineY, Math.max(0, ellipsisRemainingWidth + 1));
|
|
1136
1076
|
ctx.restore();
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -3,9 +3,9 @@ export * from './canvas/canvas.type.js';
|
|
|
3
3
|
export { Box, Column, Row, type BoxNode } from './canvas/layout.canvas.js';
|
|
4
4
|
export { Image } from './canvas/image.canvas.js';
|
|
5
5
|
export { Text } from './canvas/text.canvas.js';
|
|
6
|
-
export { Root,
|
|
6
|
+
export { Root, terminate } from './canvas/root.canvas.js';
|
|
7
7
|
export { GridItem } from './canvas/grid.canvas.js';
|
|
8
8
|
export { Grid } from './canvas/grid.canvas.js';
|
|
9
9
|
export { Chart } from './canvas/chart.canvas.js';
|
|
10
|
-
export { clearDiskCache } from './util/disk.cache.js';
|
|
10
|
+
export { clearDiskCache, setDiskCacheDir } from './util/disk.cache.js';
|
|
11
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AAC9C,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AAC9C,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/esm/index.js
CHANGED
|
@@ -2,8 +2,8 @@ export { Border, Style } from './constant/common.const.js';
|
|
|
2
2
|
export { Box, Column, Row } from './canvas/layout.canvas.js';
|
|
3
3
|
export { Image } from './canvas/image.canvas.js';
|
|
4
4
|
export { Text } from './canvas/text.canvas.js';
|
|
5
|
-
export { Root,
|
|
5
|
+
export { Root, terminate } from './canvas/root.canvas.js';
|
|
6
6
|
export { Grid, GridItem } from './canvas/grid.canvas.js';
|
|
7
7
|
export { Chart } from './canvas/chart.canvas.js';
|
|
8
|
-
export { clearDiskCache } from './util/disk.cache.js';
|
|
8
|
+
export { clearDiskCache, setDiskCacheDir } from './util/disk.cache.js';
|
|
9
9
|
export * from 'yoga-layout';
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Override the default disk cache directory.
|
|
3
|
+
* Must be called before any cache read/write operations.
|
|
4
|
+
*/
|
|
5
|
+
export declare function setDiskCacheDir(dir: string): void;
|
|
1
6
|
export declare function hashBuffer(buf: Buffer): string;
|
|
2
7
|
export declare function readDiskCache(key: string): Promise<Buffer | null>;
|
|
3
8
|
export declare function writeDiskCache(key: string, data: Buffer): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"disk.cache.d.ts","sourceRoot":"","sources":["../../../src/util/disk.cache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"disk.cache.d.ts","sourceRoot":"","sources":["../../../src/util/disk.cache.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGjD;AAQD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOvE;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7E;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAShE;AAED;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAUpD"}
|
|
@@ -2,12 +2,20 @@ import { createHash } from 'crypto';
|
|
|
2
2
|
import { promises } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
let _cacheDir = join(process.cwd(), '.cache', 'files');
|
|
6
6
|
let _dirEnsured = false;
|
|
7
|
+
/**
|
|
8
|
+
* Override the default disk cache directory.
|
|
9
|
+
* Must be called before any cache read/write operations.
|
|
10
|
+
*/
|
|
11
|
+
function setDiskCacheDir(dir) {
|
|
12
|
+
_cacheDir = dir;
|
|
13
|
+
_dirEnsured = false;
|
|
14
|
+
}
|
|
7
15
|
async function ensureDir() {
|
|
8
16
|
if (_dirEnsured)
|
|
9
17
|
return;
|
|
10
|
-
await promises.mkdir(
|
|
18
|
+
await promises.mkdir(_cacheDir, { recursive: true });
|
|
11
19
|
_dirEnsured = true;
|
|
12
20
|
}
|
|
13
21
|
function hashBuffer(buf) {
|
|
@@ -16,7 +24,7 @@ function hashBuffer(buf) {
|
|
|
16
24
|
async function readDiskCache(key) {
|
|
17
25
|
try {
|
|
18
26
|
await ensureDir();
|
|
19
|
-
return await promises.readFile(join(
|
|
27
|
+
return await promises.readFile(join(_cacheDir, key));
|
|
20
28
|
}
|
|
21
29
|
catch {
|
|
22
30
|
return null;
|
|
@@ -25,7 +33,7 @@ async function readDiskCache(key) {
|
|
|
25
33
|
async function writeDiskCache(key, data) {
|
|
26
34
|
try {
|
|
27
35
|
await ensureDir();
|
|
28
|
-
await promises.writeFile(join(
|
|
36
|
+
await promises.writeFile(join(_cacheDir, key), data);
|
|
29
37
|
}
|
|
30
38
|
catch {
|
|
31
39
|
// best-effort — cache write failures are non-fatal
|
|
@@ -33,10 +41,13 @@ async function writeDiskCache(key, data) {
|
|
|
33
41
|
}
|
|
34
42
|
async function deleteDiskCache(key) {
|
|
35
43
|
try {
|
|
36
|
-
await promises.unlink(join(
|
|
44
|
+
await promises.unlink(join(_cacheDir, key));
|
|
37
45
|
}
|
|
38
|
-
catch {
|
|
46
|
+
catch (err) {
|
|
39
47
|
// non-fatal — file may not exist if write failed earlier
|
|
48
|
+
if (err.code !== 'ENOENT') {
|
|
49
|
+
console.warn(`[disk.cache] Failed to delete cache entry "${key}":`, err.message);
|
|
50
|
+
}
|
|
40
51
|
}
|
|
41
52
|
}
|
|
42
53
|
/**
|
|
@@ -46,10 +57,13 @@ async function deleteDiskCache(key) {
|
|
|
46
57
|
async function clearDiskCache() {
|
|
47
58
|
_dirEnsured = false;
|
|
48
59
|
try {
|
|
49
|
-
await promises.rm(
|
|
60
|
+
await promises.rm(_cacheDir, { recursive: true, force: true });
|
|
50
61
|
}
|
|
51
|
-
catch {
|
|
62
|
+
catch (err) {
|
|
52
63
|
// non-fatal — directory may not exist
|
|
64
|
+
if (err.code !== 'ENOENT') {
|
|
65
|
+
console.warn('[disk.cache] Failed to clear cache directory:', err.message);
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
68
|
}
|
|
55
69
|
// Clean up disk cache on process exit to handle crashes mid-render.
|
|
@@ -69,4 +83,4 @@ const cleanupOnExit = () => {
|
|
|
69
83
|
process.on('SIGINT', cleanupOnExit);
|
|
70
84
|
process.on('SIGTERM', cleanupOnExit);
|
|
71
85
|
|
|
72
|
-
export { clearDiskCache, deleteDiskCache, hashBuffer, readDiskCache, writeDiskCache };
|
|
86
|
+
export { clearDiskCache, deleteDiskCache, hashBuffer, readDiskCache, setDiskCacheDir, writeDiskCache };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comlink.pool.d.ts","sourceRoot":"","sources":["../../../src/worker/comlink.pool.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAa,YAAY,EAAU,MAAM,0BAA0B,CAAA;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAExD,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,SAAS,EAAE,MAAM,CAAA;CAClB;AASD,oFAAoF;AACpF,eAAO,MAAM,SAAS,kBAAkB,CAAA;AAExC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,EAAE,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,CAAC,CAuB7H;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAuB3G;AAED;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,IAAI,CAAe;IAC3B,OAAO,CAAC,KAAK,CAAmB;gBAEpB,IAAI,EAAE,MAAM;IAYxB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,KAAK;YAQC,aAAa;IAiBrB,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"comlink.pool.d.ts","sourceRoot":"","sources":["../../../src/worker/comlink.pool.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAa,YAAY,EAAU,MAAM,0BAA0B,CAAA;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAExD,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,SAAS,EAAE,MAAM,CAAA;CAClB;AASD,oFAAoF;AACpF,eAAO,MAAM,SAAS,kBAAkB,CAAA;AAExC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,EAAE,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,CAAC,CAuB7H;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAuB3G;AAED;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,IAAI,CAAe;IAC3B,OAAO,CAAC,KAAK,CAAmB;gBAEpB,IAAI,EAAE,MAAM;IAYxB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,KAAK;YAQC,aAAa;IAiBrB,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2DzD,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAInH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIxD,SAAS;CAOV"}
|
|
@@ -114,6 +114,9 @@ class ComlinkPool {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
async render(props) {
|
|
117
|
+
if (this.endpoints.length === 0) {
|
|
118
|
+
throw new Error('[ComlinkPool] Pool has been terminated');
|
|
119
|
+
}
|
|
117
120
|
// Extract functions from props, replacing them with serializable sentinels.
|
|
118
121
|
// A single Comlink.proxy() callback is created at the top level so Comlink
|
|
119
122
|
// can correctly transfer it via its proxy transfer handler.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meonode/canvas",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "A declarative, component-based library for server-side canvas image generation. Write complex visuals with simple functions, similar to the composition style of @meonode/ui.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"canvas",
|
|
@@ -37,9 +37,10 @@
|
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
|
-
"build": "
|
|
41
|
-
"test": "
|
|
42
|
-
"
|
|
40
|
+
"build": "bun -e \"fs.rmSync('./dist', {recursive:true, force:true})\" && rollup -c --bundleConfigAsCjs && bun scripts/rewrite-dts-aliases.ts",
|
|
41
|
+
"test": "vitest run --coverage",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"lint": "eslint . --ext .ts,.tsx,.js,.jsx,.mjs --fix && bun run typecheck",
|
|
43
44
|
"format": "prettier --write .",
|
|
44
45
|
"generate:samples": "npx tsx scripts/generate_sample_charts.ts && npx tsx scripts/generate_sample_grids.ts && npx tsx scripts/generate_sample_nested_grids.ts",
|
|
45
46
|
"check:memory": "npx tsx --expose-gc scripts/check_memory.ts",
|
|
@@ -47,7 +48,6 @@
|
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"@eslint/js": "^9.39.4",
|
|
50
|
-
"@jest/globals": "^30.3.0",
|
|
51
51
|
"@rollup/plugin-commonjs": "^29.0.2",
|
|
52
52
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
53
53
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
@@ -56,37 +56,30 @@
|
|
|
56
56
|
"@semantic-release/gitlab": "^13.3.2",
|
|
57
57
|
"@semantic-release/npm": "^13.1.5",
|
|
58
58
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
59
|
-
"@types/jest": "^30.0.0",
|
|
60
|
-
"@types/lodash-es": "^4.17.12",
|
|
61
59
|
"@types/node": "^25.5.0",
|
|
62
60
|
"@types/sharp": "^0.32.0",
|
|
63
|
-
"@types/tinycolor2": "^1.4.6",
|
|
64
61
|
"@typescript-eslint/eslint-plugin": "^8.57.2",
|
|
65
62
|
"@typescript-eslint/parser": "^8.57.2",
|
|
63
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
66
64
|
"eslint": "^9.39.4",
|
|
67
65
|
"eslint-config-prettier": "^10.1.8",
|
|
68
66
|
"eslint-plugin-jsdoc": "^62.8.1",
|
|
69
67
|
"eslint-plugin-prettier": "^5.5.5",
|
|
70
68
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
71
69
|
"husky": "^9.1.7",
|
|
72
|
-
"jest": "^30.3.0",
|
|
73
70
|
"prettier": "^3.8.1",
|
|
74
71
|
"rollup": "^4.60.0",
|
|
75
72
|
"rollup-plugin-tsconfig-paths": "^1.5.2",
|
|
76
|
-
"semantic-release": "
|
|
77
|
-
"ts-jest": "^29.4.6",
|
|
78
|
-
"tsc-alias": "^1.8.16",
|
|
73
|
+
"semantic-release": "24.2.9",
|
|
79
74
|
"typescript": "^6.0.2",
|
|
80
|
-
"typescript-eslint": "^8.57.2"
|
|
75
|
+
"typescript-eslint": "^8.57.2",
|
|
76
|
+
"vitest": "^3.2.4"
|
|
81
77
|
},
|
|
82
|
-
"packageManager": "yarn@4.11.0",
|
|
83
78
|
"dependencies": {
|
|
84
79
|
"comlink": "^4.4.2",
|
|
85
80
|
"file-type": "^22.0.0",
|
|
86
|
-
"lodash-es": "^4.17.23",
|
|
87
81
|
"sharp": "^0.34.5",
|
|
88
82
|
"skia-canvas": "^3.0.8",
|
|
89
|
-
"tinycolor2": "^1.6.0",
|
|
90
83
|
"tslib": "^2.8.1",
|
|
91
84
|
"yoga-layout": "^3.2.1"
|
|
92
85
|
},
|