@meonode/canvas 2.0.4 → 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.
Files changed (79) hide show
  1. package/CONTRIBUTING.md +9 -9
  2. package/README.md +9 -21
  3. package/dist/cjs/canvas/canvas.helper.d.ts +1 -1
  4. package/dist/cjs/canvas/canvas.type.d.ts +9 -4
  5. package/dist/cjs/canvas/canvas.type.d.ts.map +1 -1
  6. package/dist/cjs/canvas/chart.canvas.d.ts +7 -3
  7. package/dist/cjs/canvas/chart.canvas.d.ts.map +1 -1
  8. package/dist/cjs/canvas/chart.canvas.js +3 -4
  9. package/dist/cjs/canvas/chart.canvas.js.map +1 -1
  10. package/dist/cjs/canvas/grid.canvas.d.ts +2 -2
  11. package/dist/cjs/canvas/grid.canvas.d.ts.map +1 -1
  12. package/dist/cjs/canvas/grid.canvas.js +2 -8
  13. package/dist/cjs/canvas/grid.canvas.js.map +1 -1
  14. package/dist/cjs/canvas/image.canvas.d.ts +2 -2
  15. package/dist/cjs/canvas/image.canvas.js.map +1 -1
  16. package/dist/cjs/canvas/layout.canvas.d.ts +5 -1
  17. package/dist/cjs/canvas/layout.canvas.d.ts.map +1 -1
  18. package/dist/cjs/canvas/layout.canvas.js +59 -68
  19. package/dist/cjs/canvas/layout.canvas.js.map +1 -1
  20. package/dist/cjs/canvas/root.canvas.d.ts +9 -16
  21. package/dist/cjs/canvas/root.canvas.d.ts.map +1 -1
  22. package/dist/cjs/canvas/root.canvas.js +56 -43
  23. package/dist/cjs/canvas/root.canvas.js.map +1 -1
  24. package/dist/cjs/canvas/text.canvas.d.ts +7 -3
  25. package/dist/cjs/canvas/text.canvas.d.ts.map +1 -1
  26. package/dist/cjs/canvas/text.canvas.js +25 -85
  27. package/dist/cjs/canvas/text.canvas.js.map +1 -1
  28. package/dist/cjs/index.d.ts +2 -2
  29. package/dist/cjs/index.d.ts.map +1 -1
  30. package/dist/cjs/index.js +1 -1
  31. package/dist/cjs/util/disk.cache.d.ts +5 -0
  32. package/dist/cjs/util/disk.cache.d.ts.map +1 -1
  33. package/dist/cjs/util/disk.cache.js +23 -8
  34. package/dist/cjs/util/disk.cache.js.map +1 -1
  35. package/dist/cjs/worker/comlink.pool.d.ts +15 -4
  36. package/dist/cjs/worker/comlink.pool.d.ts.map +1 -1
  37. package/dist/cjs/worker/comlink.pool.js +69 -18
  38. package/dist/cjs/worker/comlink.pool.js.map +1 -1
  39. package/dist/cjs/worker/comlink.setup.d.ts.map +1 -1
  40. package/dist/cjs/worker/comlink.setup.js +1 -2
  41. package/dist/cjs/worker/comlink.setup.js.map +1 -1
  42. package/dist/cjs/worker/render.worker.js +4 -2
  43. package/dist/cjs/worker/render.worker.js.map +1 -1
  44. package/dist/cjs/worker/worker.types.d.ts +2 -1
  45. package/dist/cjs/worker/worker.types.d.ts.map +1 -1
  46. package/dist/esm/canvas/canvas.helper.d.ts +1 -1
  47. package/dist/esm/canvas/canvas.type.d.ts +9 -4
  48. package/dist/esm/canvas/canvas.type.d.ts.map +1 -1
  49. package/dist/esm/canvas/chart.canvas.d.ts +7 -3
  50. package/dist/esm/canvas/chart.canvas.d.ts.map +1 -1
  51. package/dist/esm/canvas/chart.canvas.js +3 -4
  52. package/dist/esm/canvas/grid.canvas.d.ts +2 -2
  53. package/dist/esm/canvas/grid.canvas.d.ts.map +1 -1
  54. package/dist/esm/canvas/grid.canvas.js +1 -7
  55. package/dist/esm/canvas/image.canvas.d.ts +2 -2
  56. package/dist/esm/canvas/layout.canvas.d.ts +5 -1
  57. package/dist/esm/canvas/layout.canvas.d.ts.map +1 -1
  58. package/dist/esm/canvas/layout.canvas.js +59 -69
  59. package/dist/esm/canvas/root.canvas.d.ts +9 -16
  60. package/dist/esm/canvas/root.canvas.d.ts.map +1 -1
  61. package/dist/esm/canvas/root.canvas.js +57 -43
  62. package/dist/esm/canvas/text.canvas.d.ts +7 -3
  63. package/dist/esm/canvas/text.canvas.d.ts.map +1 -1
  64. package/dist/esm/canvas/text.canvas.js +25 -85
  65. package/dist/esm/index.d.ts +2 -2
  66. package/dist/esm/index.d.ts.map +1 -1
  67. package/dist/esm/index.js +2 -2
  68. package/dist/esm/util/disk.cache.d.ts +5 -0
  69. package/dist/esm/util/disk.cache.d.ts.map +1 -1
  70. package/dist/esm/util/disk.cache.js +23 -9
  71. package/dist/esm/worker/comlink.pool.d.ts +15 -4
  72. package/dist/esm/worker/comlink.pool.d.ts.map +1 -1
  73. package/dist/esm/worker/comlink.pool.js +67 -18
  74. package/dist/esm/worker/comlink.setup.d.ts.map +1 -1
  75. package/dist/esm/worker/comlink.setup.js +1 -1
  76. package/dist/esm/worker/render.worker.js +4 -2
  77. package/dist/esm/worker/worker.types.d.ts +2 -1
  78. package/dist/esm/worker/worker.types.d.ts.map +1 -1
  79. package/package.json +9 -16
@@ -1,10 +1,9 @@
1
1
  import { Canvas } from 'skia-canvas';
2
2
  import { parsePercentage, parseBorderRadius, drawRoundedRectPath, drawBorders } from './canvas.helper.js';
3
- import { omit } from 'lodash-es';
4
- import tinycolor from 'tinycolor2';
5
3
  import { Style } from '../constant/common.const.js';
6
4
  import YogaTypes__default from 'yoga-layout';
7
5
 
6
+ const _HEX_ALPHA_RE = /^#([0-9a-fA-F]{8})$/;
8
7
  /**
9
8
  * @class BoxNode
10
9
  * @classdesc Base node class for rendering rectangular boxes with layout, styling, and children.
@@ -123,7 +122,8 @@ class BoxNode {
123
122
  console.warn('Attempted to append an invalid child node.', child);
124
123
  return;
125
124
  }
126
- child.resolveInheritedStyles(omit(this.props, 'children'));
125
+ const { children: _c, ...inheritedProps } = this.props;
126
+ child.resolveInheritedStyles(inheritedProps);
127
127
  child.applyDefaults();
128
128
  this.children.push(child);
129
129
  this.node.insertChild(child.node, index);
@@ -212,69 +212,12 @@ class BoxNode {
212
212
  }
213
213
  }
214
214
  }
215
- if (gap) {
216
- if (typeof gap === 'number') {
217
- this.node.setGap(Style.Gutter.All, gap);
218
- }
219
- else if (typeof gap === 'string' && gap.endsWith('%')) {
220
- this.node.setGapPercent(Style.Gutter.All, parseFloat(gap));
221
- }
222
- else {
223
- for (const [gutter, value] of Object.entries(gap)) {
224
- if (gutter in Style.Gutter) {
225
- const gutterKey = gutter;
226
- if (typeof value === 'string' && value.endsWith('%')) {
227
- this.node.setGapPercent(Style.Gutter[gutterKey], parseFloat(value));
228
- }
229
- else {
230
- this.node.setGap(Style.Gutter[gutterKey], value);
231
- }
232
- }
233
- }
234
- }
235
- }
236
- if (margin) {
237
- if (typeof margin === 'number' || margin === 'auto') {
238
- this.node.setMargin(Style.Edge.All, margin);
239
- }
240
- else if (typeof margin === 'string' && margin.endsWith('%')) {
241
- this.node.setMarginPercent(Style.Edge.All, parseFloat(margin));
242
- }
243
- else {
244
- for (const [edge, value] of Object.entries(margin)) {
245
- if (edge in Style.Edge) {
246
- const edgeKey = edge;
247
- if (typeof value === 'string' && value.endsWith('%')) {
248
- this.node.setMarginPercent(Style.Edge[edgeKey], parseFloat(value));
249
- }
250
- else {
251
- this.node.setMargin(Style.Edge[edgeKey], value);
252
- }
253
- }
254
- }
255
- }
256
- }
257
- if (padding) {
258
- if (typeof padding === 'number') {
259
- this.node.setPadding(Style.Edge.All, padding);
260
- }
261
- else if (typeof padding === 'string' && padding.endsWith('%')) {
262
- this.node.setPaddingPercent(Style.Edge.All, parseFloat(padding));
263
- }
264
- else {
265
- for (const [edge, value] of Object.entries(padding)) {
266
- if (edge in Style.Edge) {
267
- const edgeKey = edge;
268
- if (typeof value === 'string' && value.endsWith('%')) {
269
- this.node.setPaddingPercent(Style.Edge[edgeKey], parseFloat(value));
270
- }
271
- else {
272
- this.node.setPadding(Style.Edge[edgeKey], value);
273
- }
274
- }
275
- }
276
- }
277
- }
215
+ if (gap)
216
+ _setEdgeValues(gap, Style.Gutter, (e, v) => this.node.setGap(e, v), (e, v) => this.node.setGapPercent(e, v));
217
+ if (margin)
218
+ _setEdgeValues(margin, Style.Edge, (e, v) => this.node.setMargin(e, v), (e, v) => this.node.setMarginPercent(e, v), true);
219
+ if (padding)
220
+ _setEdgeValues(padding, Style.Edge, (e, v) => this.node.setPadding(e, v), (e, v) => this.node.setPaddingPercent(e, v));
278
221
  if (border) {
279
222
  if (typeof border === 'number') {
280
223
  this.node.setBorder(Style.Edge.All, border);
@@ -495,8 +438,7 @@ class BoxNode {
495
438
  const backgroundColor = this.props.backgroundColor;
496
439
  let isOpaque = false;
497
440
  if (backgroundColor && !this.props.gradient) {
498
- const rgba = tinycolor(backgroundColor).toRgb();
499
- isOpaque = rgba && rgba.a === 1;
441
+ isOpaque = _isColorOpaque(backgroundColor);
500
442
  }
501
443
  // Render outset shadows if present
502
444
  if (outsetShadows.length > 0) {
@@ -734,6 +676,54 @@ function normalizeDescriptorChildren(children) {
734
676
  const arr = (Array.isArray(children) ? children : [children]).filter(Boolean);
735
677
  return arr.length > 0 ? arr : undefined;
736
678
  }
679
+ /**
680
+ * Generic helper to set gap/margin/padding edge values on a Yoga node.
681
+ * Handles scalar (number | string), percent strings, and per-edge object notation.
682
+ */
683
+ function _setEdgeValues(value, keys, setFn, percentFn, allowAuto = false) {
684
+ if (typeof value === 'number' || (allowAuto && value === 'auto')) {
685
+ setFn(keys.All, value);
686
+ }
687
+ else if (typeof value === 'string' && percentFn && value.endsWith('%')) {
688
+ percentFn(keys.All, parseFloat(value));
689
+ }
690
+ else if (typeof value === 'object') {
691
+ for (const [key, val] of Object.entries(value)) {
692
+ if (key in keys) {
693
+ const edgeKey = keys[key];
694
+ if (typeof val === 'string' && percentFn && val.endsWith('%')) {
695
+ percentFn(edgeKey, parseFloat(val));
696
+ }
697
+ else {
698
+ setFn(edgeKey, val);
699
+ }
700
+ }
701
+ }
702
+ }
703
+ }
704
+ /**
705
+ * Checks if a CSS color string represents a fully opaque color (alpha = 1).
706
+ * Handles hex (#RGB, #RRGGBB, #RRGGBBAA), rgb()/rgba(), and transparent.
707
+ */
708
+ function _isColorOpaque(color) {
709
+ if (color === 'transparent')
710
+ return false;
711
+ const hexAlpha = _HEX_ALPHA_RE.exec(color);
712
+ if (hexAlpha) {
713
+ return parseInt(hexAlpha[1].slice(6), 16) === 255;
714
+ }
715
+ // #RGB or #RRGGBB are always opaque
716
+ if (color.startsWith('#'))
717
+ return true;
718
+ // rgba(r, g, b, a)
719
+ const rgba = /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)/.exec(color);
720
+ if (rgba) {
721
+ const a = rgba[4] !== undefined ? parseFloat(rgba[4]) : 1;
722
+ return a === 1;
723
+ }
724
+ // Unknown format — assume opaque (covers named colors like 'black', 'white', etc.)
725
+ return true;
726
+ }
737
727
  /**
738
728
  * Creates a new BoxNode instance.
739
729
  * @param {BoxProps} props Box properties and configuration.
@@ -795,4 +785,4 @@ const Row = ({ children, ...rest }) => ({
795
785
  children: normalizeDescriptorChildren(children),
796
786
  });
797
787
 
798
- export { Box, BoxNode, Column, ColumnNode, Row, RowNode };
788
+ export { Box, BoxNode, Column, ColumnNode, Row, RowNode, normalizeDescriptorChildren };
@@ -1,23 +1,9 @@
1
1
  import { Canvas, type CanvasRenderingContext2D } from 'skia-canvas';
2
- import { ColumnNode, BoxNode } from '../canvas/layout.canvas.js';
3
- import type { BaseProps, RootProps, CanvasElement, RootPropsWithWorker, RootPropsWithoutWorker } from '../canvas/canvas.type.js';
4
- export declare const _clearRegisteredFonts: () => void;
5
- export interface CanvasEngineConfig {
6
- /** Run rendering in worker threads to avoid blocking the event loop (default: true) */
7
- workerMode?: boolean;
8
- /** Number of worker threads in the pool (default: os.cpus().length - 1) */
9
- workers?: number;
10
- }
11
- /**
12
- * Configure the canvas rendering engine.
13
- * Call this once at application startup before rendering.
14
- * @deprecated Pass workerMode and workers directly to Root() props instead.
15
- */
16
- export declare function configure(options: CanvasEngineConfig): void;
2
+ import { ColumnNode, BoxNode } from './layout.canvas.js';
3
+ import type { BaseProps, RootProps, CanvasElement, RootPropsWithWorker, RootPropsWithoutWorker } from './canvas.type.js';
17
4
  /**
18
5
  * Terminate all worker pools and free worker thread resources.
19
6
  * Call this when shutting down a long-running server to clean up immediately.
20
- * After calling, you must call configure() again before rendering.
21
7
  */
22
8
  export declare function terminate(): void;
23
9
  /**
@@ -42,6 +28,8 @@ export declare class RootNode extends ColumnNode {
42
28
  private readonly targetHeight;
43
29
  /** Scale factor for rendering (e.g. 2 for 2x resolution) */
44
30
  private readonly scale;
31
+ /** Max concurrent image fetches during render (default: 5) */
32
+ private readonly imageConcurrency;
45
33
  /**
46
34
  * Creates a new root node for canvas rendering
47
35
  * @param props Configuration properties for the root node
@@ -53,6 +41,11 @@ export declare class RootNode extends ColumnNode {
53
41
  * @returns Array of all ImageNode instances found in the tree
54
42
  */
55
43
  private findAllImageNodes;
44
+ /**
45
+ * Registers fonts with serialization to prevent duplicate FontLibrary.use() calls
46
+ * when multiple Root() instances are created concurrently.
47
+ */
48
+ private _registerFonts;
56
49
  /**
57
50
  * Renders the entire node tree to a canvas, handling image loading, layout calculation,
58
51
  * and final drawing
@@ -1 +1 @@
1
- {"version":3,"file":"root.canvas.d.ts","sourceRoot":"","sources":["../../../src/canvas/root.canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAEhF,OAAO,EAAE,UAAU,EAAE,OAAO,EAAW,MAAM,2BAA2B,CAAA;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AAgB/H,eAAO,MAAM,qBAAqB,YAEjC,CAAA;AAmBD,MAAM,WAAW,kBAAkB;IACjC,uFAAuF;IACvF,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,kBAAkB,QAGpD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,SAKxB;AAqFD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,aAAa,GAAG,OAAO,CAmB5D;AAED;;;GAGG;AACH,qBAAa,QAAS,SAAQ,UAAU;IAC9B,KAAK,EAAE,SAAS,GAAG,SAAS,CAAA;IACpC,6CAA6C;IAC7C,OAAO,CAAC,MAAM,CAAoB;IAClC,8CAA8C;IAC9C,OAAO,CAAC,GAAG,CAAwC;IACnD,4CAA4C;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,6CAA6C;IAC7C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAE9B;;;;OAIG;gBACS,KAAK,EAAE,SAAS,GAAG,SAAS;IAoDxC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAazB;;;;OAIG;IACY,MAAM,CAAC,GAAG,EAAE,wBAAwB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACjG,MAAM,CAAC,GAAG,CAAC,EAAE,wBAAwB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CA4DlG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,GAAG;IAAE,OAAO,IAAI,IAAI,CAAA;CAAE,CAAC,CAAA;AACvF,wBAAgB,IAAI,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA"}
1
+ {"version":3,"file":"root.canvas.d.ts","sourceRoot":"","sources":["../../../src/canvas/root.canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAEhF,OAAO,EAAE,UAAU,EAAE,OAAO,EAAW,MAAM,2BAA2B,CAAA;AACxE,OAAO,KAAK,EACV,SAAS,EACT,SAAS,EACT,aAAa,EACb,mBAAmB,EACnB,sBAAsB,EAKvB,MAAM,yBAAyB,CAAA;AAoChC;;;GAGG;AACH,wBAAgB,SAAS,SAKxB;AAqFD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,aAAa,GAAG,OAAO,CAmB5D;AAED;;;GAGG;AACH,qBAAa,QAAS,SAAQ,UAAU;IAC9B,KAAK,EAAE,SAAS,GAAG,SAAS,CAAA;IACpC,6CAA6C;IAC7C,OAAO,CAAC,MAAM,CAAoB;IAClC,8CAA8C;IAC9C,OAAO,CAAC,GAAG,CAAwC;IACnD,4CAA4C;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,6CAA6C;IAC7C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,8DAA8D;IAC9D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IAEzC;;;;OAIG;gBACS,KAAK,EAAE,SAAS,GAAG,SAAS;IAkCxC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAczB;;;OAGG;YACW,cAAc;IAgC5B;;;;OAIG;IACY,MAAM,CAAC,GAAG,EAAE,wBAAwB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACjG,MAAM,CAAC,GAAG,CAAC,EAAE,wBAAwB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAmElG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,GAAG;IAAE,OAAO,IAAI,IAAI,CAAA;CAAE,CAAC,CAAA;AACvF,wBAAgB,IAAI,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA"}
@@ -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
- this.props.children = childArray.map(child => {
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
- while (queue.length > 0) {
217
- const node = queue.shift();
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
- const workers = Array.from({ length: Math.min(CONCURRENCY, queue.length) }, async () => {
241
- while (queue.length > 0) {
242
- const node = queue.shift();
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: props override legacy configure()
279
- const workerMode = props.workerMode ?? _defaultWorkerMode;
280
- const workerPoolSize = props.workers ?? _defaultWorkerPoolSize;
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, configure, terminate };
308
+ export { Root, RootNode, buildTree, terminate };
@@ -1,6 +1,6 @@
1
- import type { TextProps, CanvasElement } from '../canvas/canvas.type.js';
1
+ import type { TextProps, CanvasElement } from './canvas.type.js';
2
2
  import { type CanvasRenderingContext2D } from 'skia-canvas';
3
- import { BoxNode } from '../canvas/layout.canvas.js';
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
- * Renders multi-line text content with rich text styling and layout features
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;IA+NnB;;;;;;;;;OASG;IACH,OAAO,CAAC,YAAY;IA6KpB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAmErB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;;;;;;;;;;;;;;;OAgBG;cACsB,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAkX3H;AAED;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,GAAG,MAAM,EAAE,QAAQ,SAAS,KAAG,aAI9D,CAAA"}
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
- if (typeof this.props.fontVariant === 'string') {
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
- if (typeof this.props.fontVariant === 'string') {
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
- if (typeof this.props.fontVariant === 'string') {
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
- if (typeof this.props.fontVariant === 'string') {
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
- if (typeof this.props.fontVariant === 'string') {
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
- * Renders multi-line text content with rich text styling and layout features
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
- if (typeof this.props.fontVariant === 'string') {
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
- if (typeof this.props.fontVariant === 'string') {
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();
@@ -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, configure, terminate } from './canvas/root.canvas.js';
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
@@ -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,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACpE,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,MAAM,sBAAsB,CAAA"}
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, configure, terminate } from './canvas/root.canvas.js';
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":"AAaA,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,CAMhE;AAED;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAOpD"}
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"}