@meonode/canvas 1.5.6 → 1.6.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/canvas/canvas.type.d.ts +34 -1
- package/dist/cjs/canvas/canvas.type.d.ts.map +1 -1
- package/dist/cjs/canvas/chart.canvas.util.d.ts +2 -2
- package/dist/cjs/canvas/chart.canvas.util.d.ts.map +1 -1
- package/dist/cjs/canvas/chart.canvas.util.js +5 -2
- package/dist/cjs/canvas/chart.canvas.util.js.map +1 -1
- package/dist/cjs/canvas/grid.canvas.util.d.ts +3 -3
- package/dist/cjs/canvas/grid.canvas.util.d.ts.map +1 -1
- package/dist/cjs/canvas/grid.canvas.util.js +34 -1
- package/dist/cjs/canvas/grid.canvas.util.js.map +1 -1
- package/dist/cjs/canvas/image.canvas.util.d.ts +16 -8
- package/dist/cjs/canvas/image.canvas.util.d.ts.map +1 -1
- package/dist/cjs/canvas/image.canvas.util.js +82 -72
- package/dist/cjs/canvas/image.canvas.util.js.map +1 -1
- package/dist/cjs/canvas/layout.canvas.util.d.ts +4 -4
- package/dist/cjs/canvas/layout.canvas.util.d.ts.map +1 -1
- package/dist/cjs/canvas/layout.canvas.util.js +24 -3
- package/dist/cjs/canvas/layout.canvas.util.js.map +1 -1
- package/dist/cjs/canvas/root.canvas.util.d.ts +23 -4
- package/dist/cjs/canvas/root.canvas.util.d.ts.map +1 -1
- package/dist/cjs/canvas/root.canvas.util.js +228 -5
- package/dist/cjs/canvas/root.canvas.util.js.map +1 -1
- package/dist/cjs/canvas/text.canvas.util.d.ts +2 -2
- package/dist/cjs/canvas/text.canvas.util.d.ts.map +1 -1
- package/dist/cjs/canvas/text.canvas.util.js +5 -1
- package/dist/cjs/canvas/text.canvas.util.js.map +1 -1
- package/dist/cjs/canvas/worker.types.d.ts +76 -0
- package/dist/cjs/canvas/worker.types.d.ts.map +1 -0
- package/dist/cjs/index.d.ts +2 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/render.worker.d.ts +2 -0
- package/dist/cjs/render.worker.d.ts.map +1 -0
- package/dist/cjs/render.worker.js +73 -0
- package/dist/cjs/render.worker.js.map +1 -0
- package/dist/esm/canvas/canvas.type.d.ts +34 -1
- package/dist/esm/canvas/canvas.type.d.ts.map +1 -1
- package/dist/esm/canvas/chart.canvas.util.d.ts +2 -2
- package/dist/esm/canvas/chart.canvas.util.d.ts.map +1 -1
- package/dist/esm/canvas/chart.canvas.util.js +6 -3
- package/dist/esm/canvas/grid.canvas.util.d.ts +3 -3
- package/dist/esm/canvas/grid.canvas.util.d.ts.map +1 -1
- package/dist/esm/canvas/grid.canvas.util.js +34 -3
- package/dist/esm/canvas/image.canvas.util.d.ts +16 -8
- package/dist/esm/canvas/image.canvas.util.d.ts.map +1 -1
- package/dist/esm/canvas/image.canvas.util.js +82 -72
- package/dist/esm/canvas/layout.canvas.util.d.ts +4 -4
- package/dist/esm/canvas/layout.canvas.util.d.ts.map +1 -1
- package/dist/esm/canvas/layout.canvas.util.js +24 -3
- package/dist/esm/canvas/root.canvas.util.d.ts +23 -4
- package/dist/esm/canvas/root.canvas.util.d.ts.map +1 -1
- package/dist/esm/canvas/root.canvas.util.js +227 -7
- package/dist/esm/canvas/text.canvas.util.d.ts +2 -2
- package/dist/esm/canvas/text.canvas.util.d.ts.map +1 -1
- package/dist/esm/canvas/text.canvas.util.js +5 -1
- package/dist/esm/canvas/worker.types.d.ts +76 -0
- package/dist/esm/canvas/worker.types.d.ts.map +1 -0
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/render.worker.d.ts +2 -0
- package/dist/esm/render.worker.d.ts.map +1 -0
- package/dist/esm/render.worker.js +70 -0
- package/package.json +1 -1
|
@@ -39,23 +39,82 @@ class ImageNode extends BoxNode {
|
|
|
39
39
|
...props,
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
-
load() {
|
|
42
|
+
load(cache) {
|
|
43
43
|
if (!this.loadingPromise) {
|
|
44
|
-
this.loadingPromise = this._loadImage();
|
|
44
|
+
this.loadingPromise = this._loadImage(cache);
|
|
45
45
|
}
|
|
46
46
|
return this.loadingPromise;
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* @returns Promise that resolves when image loading completes
|
|
52
|
-
* @throws Error if image loading fails
|
|
49
|
+
* Fetches and processes the image source into a CanvasImage.
|
|
50
|
+
* Does not touch node state — pure fetch logic.
|
|
53
51
|
*/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
async _fetchCanvasImage() {
|
|
53
|
+
const { fileTypeFromBuffer, fileTypeFromFile } = await import('file-type');
|
|
54
|
+
let finalSource = this.props.src;
|
|
55
|
+
let isSvg = false;
|
|
56
|
+
let contentBuffer = null;
|
|
57
|
+
let detectedMime;
|
|
58
|
+
if (typeof this.props.src === 'string') {
|
|
59
|
+
if (this.props.src.startsWith('http')) {
|
|
60
|
+
const response = await fetch(this.props.src);
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
throw new Error(`HTTP error ${response.status} fetching image: ${this.props.src}`);
|
|
63
|
+
}
|
|
64
|
+
const imageArrayBuffer = await response.arrayBuffer();
|
|
65
|
+
contentBuffer = Buffer.from(imageArrayBuffer);
|
|
66
|
+
finalSource = contentBuffer;
|
|
67
|
+
const fileTypeResult = await fileTypeFromBuffer(contentBuffer);
|
|
68
|
+
detectedMime = fileTypeResult?.mime;
|
|
69
|
+
isSvg = detectedMime === 'image/svg+xml';
|
|
70
|
+
if ((!detectedMime || detectedMime === 'application/xml') && contentBuffer.toString('utf-8').includes('<svg')) {
|
|
71
|
+
isSvg = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
finalSource = this.props.src;
|
|
76
|
+
const filePath = this.props.src;
|
|
77
|
+
try {
|
|
78
|
+
const fileTypeResult = await fileTypeFromFile(filePath);
|
|
79
|
+
detectedMime = fileTypeResult?.mime;
|
|
80
|
+
isSvg = detectedMime === 'image/svg+xml';
|
|
81
|
+
if ((!detectedMime || detectedMime === 'application/xml') && filePath.toLowerCase().endsWith('.svg')) {
|
|
82
|
+
isSvg = true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
isSvg = filePath.toLowerCase().endsWith('.svg');
|
|
87
|
+
}
|
|
88
|
+
if (isSvg && this.props.color) {
|
|
89
|
+
try {
|
|
90
|
+
contentBuffer = await promises.readFile(filePath);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
isSvg = false;
|
|
94
|
+
contentBuffer = null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
contentBuffer = this.props.src;
|
|
101
|
+
finalSource = contentBuffer;
|
|
102
|
+
const fileTypeResult = await fileTypeFromBuffer(contentBuffer);
|
|
103
|
+
detectedMime = fileTypeResult?.mime;
|
|
104
|
+
isSvg = detectedMime === 'image/svg+xml';
|
|
105
|
+
}
|
|
106
|
+
if (isSvg && this.props.color && contentBuffer) {
|
|
107
|
+
const svgString = contentBuffer.toString('utf-8');
|
|
108
|
+
const modifiedSvgString = svgString.replace(/fill="[^"]*"/g, `fill="${this.props.color}"`);
|
|
109
|
+
finalSource = modifiedSvgString !== svgString ? Buffer.from(modifiedSvgString) : contentBuffer;
|
|
110
|
+
}
|
|
111
|
+
return loadImage(finalSource);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Loads and processes an image, using the render-scoped cache to avoid
|
|
115
|
+
* re-fetching the same source within a single render pass.
|
|
116
|
+
*/
|
|
117
|
+
_loadImage(cache) {
|
|
59
118
|
if (!this.props.src) {
|
|
60
119
|
const aspectRatioFromProps = typeof this.props.aspectRatio === 'number' && this.props.aspectRatio > 0 ? this.props.aspectRatio : undefined;
|
|
61
120
|
this.node.setAspectRatio(aspectRatioFromProps);
|
|
@@ -65,71 +124,19 @@ class ImageNode extends BoxNode {
|
|
|
65
124
|
}
|
|
66
125
|
return new Promise(resolve => {
|
|
67
126
|
const load = async () => {
|
|
68
|
-
const { fileTypeFromBuffer, fileTypeFromFile } = await import('file-type');
|
|
69
|
-
let finalSource = this.props.src;
|
|
70
|
-
let isSvg = false;
|
|
71
|
-
let contentBuffer = null;
|
|
72
|
-
let detectedMime;
|
|
73
127
|
try {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
const imageArrayBuffer = await response.arrayBuffer();
|
|
81
|
-
contentBuffer = Buffer.from(imageArrayBuffer);
|
|
82
|
-
finalSource = contentBuffer;
|
|
83
|
-
const fileTypeResult = await fileTypeFromBuffer(contentBuffer);
|
|
84
|
-
detectedMime = fileTypeResult?.mime;
|
|
85
|
-
isSvg = detectedMime === 'image/svg+xml';
|
|
86
|
-
if ((!detectedMime || detectedMime === 'application/xml') && contentBuffer.toString('utf-8').includes('<svg')) {
|
|
87
|
-
isSvg = true;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
finalSource = this.props.src;
|
|
92
|
-
const filePath = this.props.src;
|
|
93
|
-
try {
|
|
94
|
-
const fileTypeResult = await fileTypeFromFile(filePath);
|
|
95
|
-
detectedMime = fileTypeResult?.mime;
|
|
96
|
-
isSvg = detectedMime === 'image/svg+xml';
|
|
97
|
-
if ((!detectedMime || detectedMime === 'application/xml') && filePath.toLowerCase().endsWith('.svg')) {
|
|
98
|
-
isSvg = true;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
isSvg = filePath.toLowerCase().endsWith('.svg');
|
|
103
|
-
}
|
|
104
|
-
if (isSvg && this.props.color) {
|
|
105
|
-
try {
|
|
106
|
-
contentBuffer = await promises.readFile(filePath);
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
isSvg = false;
|
|
110
|
-
contentBuffer = null;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
128
|
+
let imagePromise;
|
|
129
|
+
if (cache && typeof this.props.src === 'string') {
|
|
130
|
+
const cacheKey = this.props.color ? `${this.props.src}|${this.props.color}` : this.props.src;
|
|
131
|
+
if (!cache.has(cacheKey)) {
|
|
132
|
+
cache.set(cacheKey, this._fetchCanvasImage());
|
|
113
133
|
}
|
|
134
|
+
imagePromise = cache.get(cacheKey);
|
|
114
135
|
}
|
|
115
136
|
else {
|
|
116
|
-
|
|
117
|
-
finalSource = contentBuffer;
|
|
118
|
-
const fileTypeResult = await fileTypeFromBuffer(contentBuffer);
|
|
119
|
-
detectedMime = fileTypeResult?.mime;
|
|
120
|
-
isSvg = detectedMime === 'image/svg+xml';
|
|
121
|
-
}
|
|
122
|
-
if (isSvg && this.props.color && contentBuffer) {
|
|
123
|
-
const svgString = contentBuffer.toString('utf-8');
|
|
124
|
-
const modifiedSvgString = svgString.replace(/fill="[^"]*"/g, `fill="${this.props.color}"`);
|
|
125
|
-
if (modifiedSvgString !== svgString) {
|
|
126
|
-
finalSource = Buffer.from(modifiedSvgString);
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
finalSource = contentBuffer;
|
|
130
|
-
}
|
|
137
|
+
imagePromise = this._fetchCanvasImage();
|
|
131
138
|
}
|
|
132
|
-
const img = await
|
|
139
|
+
const img = await imagePromise;
|
|
133
140
|
this.loadedImage = img;
|
|
134
141
|
this.naturalWidth = img.width;
|
|
135
142
|
this.naturalHeight = img.height;
|
|
@@ -305,6 +312,9 @@ class ImageNode extends BoxNode {
|
|
|
305
312
|
/**
|
|
306
313
|
* Factory function to create ImageNode instances
|
|
307
314
|
*/
|
|
308
|
-
const Image = (props) =>
|
|
315
|
+
const Image = (props) => ({
|
|
316
|
+
__type: 'Image',
|
|
317
|
+
props: props,
|
|
318
|
+
});
|
|
309
319
|
|
|
310
320
|
export { Image, ImageNode };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CanvasRenderingContext2D } from 'skia-canvas';
|
|
2
|
-
import type { BaseProps, BoxProps } from '../canvas/canvas.type.js';
|
|
2
|
+
import type { BaseProps, BoxProps, NodeDescriptor } from '../canvas/canvas.type.js';
|
|
3
3
|
import { Node } from '../constant/common.const.js';
|
|
4
4
|
/**
|
|
5
5
|
* @class BoxNode
|
|
@@ -92,7 +92,7 @@ export declare class BoxNode {
|
|
|
92
92
|
* @param {BoxProps} props Box properties and configuration.
|
|
93
93
|
* @returns {BoxNode} New BoxNode instance.
|
|
94
94
|
*/
|
|
95
|
-
export declare const Box: (
|
|
95
|
+
export declare const Box: ({ children, ...rest }: BoxProps) => NodeDescriptor;
|
|
96
96
|
/**
|
|
97
97
|
* @class ColumnNode
|
|
98
98
|
* Node class for vertical column layout
|
|
@@ -105,7 +105,7 @@ export declare class ColumnNode extends BoxNode {
|
|
|
105
105
|
* @param {BoxProps} props Column properties and configuration.
|
|
106
106
|
* @returns {ColumnNode} New ColumnNode instance.
|
|
107
107
|
*/
|
|
108
|
-
export declare const Column: (
|
|
108
|
+
export declare const Column: ({ children, ...rest }: BoxProps) => NodeDescriptor;
|
|
109
109
|
/**
|
|
110
110
|
* @class RowNode
|
|
111
111
|
* @classdesc Node class for horizontal row layout.
|
|
@@ -118,5 +118,5 @@ export declare class RowNode extends BoxNode {
|
|
|
118
118
|
* @param {BoxProps} props Row properties and configuration.
|
|
119
119
|
* @returns {RowNode} New RowNode instance.
|
|
120
120
|
*/
|
|
121
|
-
export declare const Row: (
|
|
121
|
+
export declare const Row: ({ children, ...rest }: BoxProps) => NodeDescriptor;
|
|
122
122
|
//# sourceMappingURL=layout.canvas.util.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/layout.canvas.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAEnE,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAkB,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"layout.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/layout.canvas.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAEnE,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAkB,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAGlG,OAAa,EAAS,IAAI,EAAE,MAAM,4BAA4B,CAAA;AAE9D;;;;GAIG;AACH,qBAAa,OAAO;IAClB;;OAEG;IACH,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IAE/B;;OAEG;IACH,IAAI,EAAE,IAAI,CAAA;IAEV;;OAEG;IACH,QAAQ,EAAE,OAAO,EAAE,CAAA;IAEnB;;OAEG;IACH,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAA;IAE3B;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IAEtB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ;;;OAGG;gBACS,KAAK,GAAE,QAAQ,GAAG,SAAc;IAqB5C;;OAEG;IACI,sBAAsB;IAW7B;;;OAGG;IACH,SAAS,CAAC,sBAAsB,CAAC,WAAW,EAAE,QAAQ,GAAG,SAAS;IAkClE;;OAEG;IACH,SAAS,CAAC,aAAa,IAAI,IAAI;IAI/B;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM;IAanD;;;OAGG;IACI,cAAc,IAAI,OAAO;IAiBhC;;OAEG;IACH,SAAS,CAAC,+BAA+B;IAIzC;;;OAGG;IACH,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ;IAqInC;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,wBAAwB,EAAE,OAAO,GAAE,MAAU,EAAE,OAAO,GAAE,MAAU;IA+J9E;;;;;;;;OAQG;IACH,SAAS,CAAC,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAoR5G;AAWD;;;;GAIG;AACH,eAAO,MAAM,GAAG,GAAI,uBAAuB,QAAQ,KAAG,cAIpD,CAAA;AAEF;;;GAGG;AACH,qBAAa,UAAW,SAAQ,OAAO;gBACzB,KAAK,GAAE,QAAQ,GAAG,SAAc;CAS7C;AAED;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,uBAAuB,QAAQ,KAAG,cAIvD,CAAA;AAEF;;;GAGG;AACH,qBAAa,OAAQ,SAAQ,OAAO;gBACtB,KAAK,GAAE,QAAQ,GAAG,SAAc;CAS7C;AAED;;;;GAIG;AACH,eAAO,MAAM,GAAG,GAAI,uBAAuB,QAAQ,KAAG,cAIpD,CAAA"}
|
|
@@ -718,12 +718,25 @@ class BoxNode {
|
|
|
718
718
|
});
|
|
719
719
|
}
|
|
720
720
|
}
|
|
721
|
+
/**
|
|
722
|
+
* Normalizes children into a flat NodeDescriptor array, filtering falsy values.
|
|
723
|
+
*/
|
|
724
|
+
function normalizeDescriptorChildren(children) {
|
|
725
|
+
if (children === undefined || children === null || children === false)
|
|
726
|
+
return undefined;
|
|
727
|
+
const arr = (Array.isArray(children) ? children : [children]).filter(Boolean);
|
|
728
|
+
return arr.length > 0 ? arr : undefined;
|
|
729
|
+
}
|
|
721
730
|
/**
|
|
722
731
|
* Creates a new BoxNode instance.
|
|
723
732
|
* @param {BoxProps} props Box properties and configuration.
|
|
724
733
|
* @returns {BoxNode} New BoxNode instance.
|
|
725
734
|
*/
|
|
726
|
-
const Box = (
|
|
735
|
+
const Box = ({ children, ...rest }) => ({
|
|
736
|
+
__type: 'Box',
|
|
737
|
+
props: rest,
|
|
738
|
+
children: normalizeDescriptorChildren(children),
|
|
739
|
+
});
|
|
727
740
|
/**
|
|
728
741
|
* @class ColumnNode
|
|
729
742
|
* Node class for vertical column layout
|
|
@@ -744,7 +757,11 @@ class ColumnNode extends BoxNode {
|
|
|
744
757
|
* @param {BoxProps} props Column properties and configuration.
|
|
745
758
|
* @returns {ColumnNode} New ColumnNode instance.
|
|
746
759
|
*/
|
|
747
|
-
const Column = (
|
|
760
|
+
const Column = ({ children, ...rest }) => ({
|
|
761
|
+
__type: 'Column',
|
|
762
|
+
props: rest,
|
|
763
|
+
children: normalizeDescriptorChildren(children),
|
|
764
|
+
});
|
|
748
765
|
/**
|
|
749
766
|
* @class RowNode
|
|
750
767
|
* @classdesc Node class for horizontal row layout.
|
|
@@ -765,6 +782,10 @@ class RowNode extends BoxNode {
|
|
|
765
782
|
* @param {BoxProps} props Row properties and configuration.
|
|
766
783
|
* @returns {RowNode} New RowNode instance.
|
|
767
784
|
*/
|
|
768
|
-
const Row = (
|
|
785
|
+
const Row = ({ children, ...rest }) => ({
|
|
786
|
+
__type: 'Row',
|
|
787
|
+
props: rest,
|
|
788
|
+
children: normalizeDescriptorChildren(children),
|
|
789
|
+
});
|
|
769
790
|
|
|
770
791
|
export { Box, BoxNode, Column, ColumnNode, Row, RowNode };
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import { Canvas } from 'skia-canvas';
|
|
2
|
-
import { ColumnNode } from '../canvas/layout.canvas.util.js';
|
|
3
|
-
import type { BaseProps, RootProps } from '../canvas/canvas.type.js';
|
|
2
|
+
import { ColumnNode, BoxNode } from '../canvas/layout.canvas.util.js';
|
|
3
|
+
import type { BaseProps, RootProps, NodeDescriptor } from '../canvas/canvas.type.js';
|
|
4
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
|
+
*/
|
|
15
|
+
export declare function configure(options: CanvasEngineConfig): void;
|
|
16
|
+
/**
|
|
17
|
+
* Converts a NodeDescriptor tree into actual BoxNode instances.
|
|
18
|
+
* Used both for non-worker rendering (inline tree building) and inside
|
|
19
|
+
* the render worker (reconstructing the tree from serialized descriptors).
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildTree(descriptor: NodeDescriptor): BoxNode;
|
|
5
22
|
/**
|
|
6
23
|
* Root node that manages the canvas rendering context and coordinates overall layout and drawing.
|
|
7
24
|
* Inherits from ColumnNode to provide vertical layout capabilities.
|
|
@@ -36,9 +53,11 @@ export declare class RootNode extends ColumnNode {
|
|
|
36
53
|
render(): Promise<Canvas>;
|
|
37
54
|
}
|
|
38
55
|
/**
|
|
39
|
-
* Creates and renders a new root node with the given properties
|
|
56
|
+
* Creates and renders a new root node with the given properties.
|
|
57
|
+
* When worker mode is enabled via configure(), rendering runs in a worker thread
|
|
58
|
+
* and the returned object implements the same toBuffer/toBufferSync interface.
|
|
40
59
|
* @param props Configuration properties for the root node
|
|
41
|
-
* @returns Promise resolving to the rendered Canvas
|
|
60
|
+
* @returns Promise resolving to the rendered Canvas (or WorkerCanvas in worker mode)
|
|
42
61
|
*/
|
|
43
62
|
export declare const Root: (props: RootProps) => Promise<Canvas>;
|
|
44
63
|
//# sourceMappingURL=root.canvas.util.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/root.canvas.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA8C,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"root.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/root.canvas.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA8C,MAAM,aAAa,CAAA;AAEhF,OAAO,EAAE,UAAU,EAAE,OAAO,EAAW,MAAM,gCAAgC,CAAA;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAiBnF,eAAO,MAAM,qBAAqB,YAEjC,CAAA;AAOD,MAAM,WAAW,kBAAkB;IACjC,uFAAuF;IACvF,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,kBAAkB,QAMpD;AA2LD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,cAAc,GAAG,OAAO,CAmB7D;AAED;;;GAGG;AACH,qBAAa,QAAS,SAAQ,UAAU;IACtC,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,CAAQ;IACrC,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAE9B;;;;OAIG;gBACS,KAAK,EAAE,SAAS,GAAG,SAAS;IAoDxC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAazB;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;CA6ChC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,GAAU,OAAO,SAAS,KAAG,OAAO,CAAC,MAAM,CAS3D,CAAA"}
|
|
@@ -1,12 +1,209 @@
|
|
|
1
1
|
import { FontLibrary, Canvas } from 'skia-canvas';
|
|
2
|
-
import { ColumnNode } from './layout.canvas.util.js';
|
|
2
|
+
import { ColumnNode, RowNode, BoxNode } from './layout.canvas.util.js';
|
|
3
3
|
import { ImageNode } from './image.canvas.util.js';
|
|
4
|
+
import { TextNode } from './text.canvas.util.js';
|
|
5
|
+
import { ChartNode } from './chart.canvas.util.js';
|
|
6
|
+
import { GridItemNode, GridNode } from './grid.canvas.util.js';
|
|
4
7
|
import { Style } from '../constant/common.const.js';
|
|
5
8
|
import * as path from 'node:path';
|
|
6
9
|
import * as fs from 'node:fs';
|
|
10
|
+
import { cpus } from 'node:os';
|
|
11
|
+
import { Worker } from 'node:worker_threads';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
7
13
|
|
|
8
14
|
/** Registry to track fonts that have already been loaded */
|
|
9
15
|
const registeredFonts = new Map();
|
|
16
|
+
/** Engine configuration */
|
|
17
|
+
let _workerMode = true;
|
|
18
|
+
let _workerPoolSize = Math.max(1, cpus().length - 1);
|
|
19
|
+
let _workerPool = null;
|
|
20
|
+
/**
|
|
21
|
+
* Configure the canvas rendering engine.
|
|
22
|
+
* Call this once at application startup before rendering.
|
|
23
|
+
*/
|
|
24
|
+
function configure(options) {
|
|
25
|
+
if (options.workerMode !== undefined)
|
|
26
|
+
_workerMode = options.workerMode;
|
|
27
|
+
if (options.workers !== undefined)
|
|
28
|
+
_workerPoolSize = options.workers;
|
|
29
|
+
if (_workerMode) {
|
|
30
|
+
_workerPool = new WorkerPool(_workerPoolSize);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Proxies all skia-canvas Canvas APIs to a Canvas instance living inside a worker thread.
|
|
35
|
+
* Sync methods (toBufferSync, toURLSync) return from a pre-encoded PNG buffer.
|
|
36
|
+
* Async methods (toBuffer, toURL, toFile, getters) delegate to the worker.
|
|
37
|
+
*/
|
|
38
|
+
class WorkerCanvas {
|
|
39
|
+
width;
|
|
40
|
+
height;
|
|
41
|
+
_buffer; // pre-encoded PNG for sync use
|
|
42
|
+
_pool;
|
|
43
|
+
_workerIdx;
|
|
44
|
+
_canvasId;
|
|
45
|
+
constructor(opts) {
|
|
46
|
+
this._buffer = opts.buffer;
|
|
47
|
+
this.width = opts.width;
|
|
48
|
+
this.height = opts.height;
|
|
49
|
+
this._pool = opts.pool;
|
|
50
|
+
this._workerIdx = opts.workerIdx;
|
|
51
|
+
this._canvasId = opts.canvasId;
|
|
52
|
+
}
|
|
53
|
+
_call(method, ...args) {
|
|
54
|
+
return this._pool.callOnCanvas(this._workerIdx, this._canvasId, method, args);
|
|
55
|
+
}
|
|
56
|
+
// --- Sync methods: return from pre-encoded PNG buffer ---
|
|
57
|
+
toBufferSync(_format, _options) {
|
|
58
|
+
return this._buffer;
|
|
59
|
+
}
|
|
60
|
+
toURLSync(_format, _options) {
|
|
61
|
+
return `data:image/png;base64,${this._buffer.toString('base64')}`;
|
|
62
|
+
}
|
|
63
|
+
// --- Async methods: delegate to worker ---
|
|
64
|
+
toBuffer(format, options) {
|
|
65
|
+
return this._call('toBuffer', format, options);
|
|
66
|
+
}
|
|
67
|
+
toURL(format, options) {
|
|
68
|
+
return this._call('toURL', format, options);
|
|
69
|
+
}
|
|
70
|
+
toFile(filename, options) {
|
|
71
|
+
return this._call('toFile', filename, options);
|
|
72
|
+
}
|
|
73
|
+
/** Returns a Buffer (Sharp instance cannot be transferred across threads) */
|
|
74
|
+
toSharp(options) {
|
|
75
|
+
return this._call('toSharp', options);
|
|
76
|
+
}
|
|
77
|
+
toSharpSync(_options) {
|
|
78
|
+
throw new Error('[canvas] toSharpSync() is not available in worker mode — use toSharp() instead');
|
|
79
|
+
}
|
|
80
|
+
// --- Async convenience getters ---
|
|
81
|
+
get png() {
|
|
82
|
+
return this._call('toBuffer', 'png');
|
|
83
|
+
}
|
|
84
|
+
get webp() {
|
|
85
|
+
return this._call('toBuffer', 'webp');
|
|
86
|
+
}
|
|
87
|
+
get jpg() {
|
|
88
|
+
return this._call('toBuffer', 'jpg');
|
|
89
|
+
}
|
|
90
|
+
get svg() {
|
|
91
|
+
return this._call('toBuffer', 'svg');
|
|
92
|
+
}
|
|
93
|
+
get pdf() {
|
|
94
|
+
return this._call('toBuffer', 'pdf');
|
|
95
|
+
}
|
|
96
|
+
get raw() {
|
|
97
|
+
return this._call('toBuffer', 'raw');
|
|
98
|
+
}
|
|
99
|
+
/** Release the Canvas from worker memory. Call when done with this object. */
|
|
100
|
+
release() {
|
|
101
|
+
this._pool.releaseCanvas(this._workerIdx, this._canvasId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Worker thread pool — routes render and canvas-call messages */
|
|
105
|
+
class WorkerPool {
|
|
106
|
+
workers = [];
|
|
107
|
+
idle = [];
|
|
108
|
+
queue = [];
|
|
109
|
+
pending = new Map();
|
|
110
|
+
nextId = 0;
|
|
111
|
+
constructor(size) {
|
|
112
|
+
this.init(size);
|
|
113
|
+
}
|
|
114
|
+
init(size) {
|
|
115
|
+
const workerFile = path.join(path.dirname(fileURLToPath(import.meta.url)), '../render.worker.js');
|
|
116
|
+
for (let i = 0; i < size; i++) {
|
|
117
|
+
const workerIdx = i;
|
|
118
|
+
const worker = new Worker(workerFile);
|
|
119
|
+
worker.on('message', (msg) => {
|
|
120
|
+
const task = this.pending.get(msg.taskId);
|
|
121
|
+
if (!task)
|
|
122
|
+
return;
|
|
123
|
+
this.pending.delete(msg.taskId);
|
|
124
|
+
if ('error' in msg) {
|
|
125
|
+
task.reject(new Error(msg.error));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if ('canvasId' in msg) {
|
|
129
|
+
// Render complete — put worker back to idle
|
|
130
|
+
this.idle.push(worker);
|
|
131
|
+
this.drain();
|
|
132
|
+
const result = { buffer: msg.buffer, canvasId: msg.canvasId, workerIdx, width: msg.width, height: msg.height };
|
|
133
|
+
task.resolve(result);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// Canvas method call complete
|
|
137
|
+
task.resolve(msg.result);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
this.workers.push(worker);
|
|
141
|
+
this.idle.push(worker);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
drain() {
|
|
145
|
+
while (this.queue.length > 0 && this.idle.length > 0) {
|
|
146
|
+
const task = this.queue.shift();
|
|
147
|
+
const worker = this.idle.pop();
|
|
148
|
+
const request = { type: 'render', taskId: task.id, props: task.props };
|
|
149
|
+
worker.postMessage(request);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
render(props) {
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
const id = this.nextId++;
|
|
155
|
+
this.pending.set(id, { resolve: resolve, reject });
|
|
156
|
+
if (this.idle.length > 0) {
|
|
157
|
+
const worker = this.idle.pop();
|
|
158
|
+
const request = { type: 'render', taskId: id, props };
|
|
159
|
+
worker.postMessage(request);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
this.queue.push({ id, props });
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
callOnCanvas(workerIdx, canvasId, method, args) {
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
const id = this.nextId++;
|
|
169
|
+
this.pending.set(id, { resolve: resolve, reject });
|
|
170
|
+
const request = { type: 'call', taskId: id, canvasId, method, args };
|
|
171
|
+
this.workers[workerIdx].postMessage(request);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
releaseCanvas(workerIdx, canvasId) {
|
|
175
|
+
const request = { type: 'release', canvasId };
|
|
176
|
+
this.workers[workerIdx]?.postMessage(request);
|
|
177
|
+
}
|
|
178
|
+
terminate() {
|
|
179
|
+
this.workers.forEach(w => w.terminate());
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Converts a NodeDescriptor tree into actual BoxNode instances.
|
|
184
|
+
* Used both for non-worker rendering (inline tree building) and inside
|
|
185
|
+
* the render worker (reconstructing the tree from serialized descriptors).
|
|
186
|
+
*/
|
|
187
|
+
function buildTree(descriptor) {
|
|
188
|
+
switch (descriptor.__type) {
|
|
189
|
+
case 'Box':
|
|
190
|
+
return new BoxNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) });
|
|
191
|
+
case 'Column':
|
|
192
|
+
return new ColumnNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) });
|
|
193
|
+
case 'Row':
|
|
194
|
+
return new RowNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) });
|
|
195
|
+
case 'Grid':
|
|
196
|
+
return new GridNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) });
|
|
197
|
+
case 'GridItem':
|
|
198
|
+
return new GridItemNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) });
|
|
199
|
+
case 'Image':
|
|
200
|
+
return new ImageNode(descriptor.props);
|
|
201
|
+
case 'Text':
|
|
202
|
+
return new TextNode(descriptor.text, descriptor.props);
|
|
203
|
+
case 'Chart':
|
|
204
|
+
return new ChartNode(descriptor.props);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
10
207
|
/**
|
|
11
208
|
* Root node that manages the canvas rendering context and coordinates overall layout and drawing.
|
|
12
209
|
* Inherits from ColumnNode to provide vertical layout capabilities.
|
|
@@ -56,6 +253,16 @@ class RootNode extends ColumnNode {
|
|
|
56
253
|
this.targetWidth = props.width;
|
|
57
254
|
this.targetHeight = props.height;
|
|
58
255
|
this.node.setWidth(this.targetWidth);
|
|
256
|
+
// Convert any NodeDescriptor children to actual BoxNode instances
|
|
257
|
+
if (this.props.children) {
|
|
258
|
+
const childArray = Array.isArray(this.props.children) ? this.props.children : [this.props.children];
|
|
259
|
+
this.props.children = childArray.map(child => {
|
|
260
|
+
if (child && typeof child === 'object' && '__type' in child) {
|
|
261
|
+
return buildTree(child);
|
|
262
|
+
}
|
|
263
|
+
return child;
|
|
264
|
+
});
|
|
265
|
+
}
|
|
59
266
|
// Initialize children nodes
|
|
60
267
|
this.processInitialChildren();
|
|
61
268
|
}
|
|
@@ -81,15 +288,17 @@ class RootNode extends ColumnNode {
|
|
|
81
288
|
* @returns Promise resolving to the rendered Canvas instance
|
|
82
289
|
*/
|
|
83
290
|
async render() {
|
|
84
|
-
// Step 1: Load all images with a concurrency limit to avoid overwhelming remote sources
|
|
291
|
+
// Step 1: Load all images with a concurrency limit to avoid overwhelming remote sources.
|
|
292
|
+
// A per-render cache deduplicates identical src+color combinations within this render pass.
|
|
85
293
|
const imageNodes = this.findAllImageNodes();
|
|
86
294
|
if (imageNodes.length > 0) {
|
|
295
|
+
const imageCache = new Map();
|
|
87
296
|
const CONCURRENCY = 5;
|
|
88
297
|
const queue = [...imageNodes];
|
|
89
298
|
const workers = Array.from({ length: Math.min(CONCURRENCY, queue.length) }, async () => {
|
|
90
299
|
while (queue.length > 0) {
|
|
91
300
|
const node = queue.shift();
|
|
92
|
-
await node.load();
|
|
301
|
+
await node.load(imageCache);
|
|
93
302
|
}
|
|
94
303
|
});
|
|
95
304
|
await Promise.allSettled(workers);
|
|
@@ -118,10 +327,21 @@ class RootNode extends ColumnNode {
|
|
|
118
327
|
}
|
|
119
328
|
}
|
|
120
329
|
/**
|
|
121
|
-
* Creates and renders a new root node with the given properties
|
|
330
|
+
* Creates and renders a new root node with the given properties.
|
|
331
|
+
* When worker mode is enabled via configure(), rendering runs in a worker thread
|
|
332
|
+
* and the returned object implements the same toBuffer/toBufferSync interface.
|
|
122
333
|
* @param props Configuration properties for the root node
|
|
123
|
-
* @returns Promise resolving to the rendered Canvas
|
|
334
|
+
* @returns Promise resolving to the rendered Canvas (or WorkerCanvas in worker mode)
|
|
124
335
|
*/
|
|
125
|
-
const Root = async (props) =>
|
|
336
|
+
const Root = async (props) => {
|
|
337
|
+
if (_workerMode) {
|
|
338
|
+
if (!_workerPool) {
|
|
339
|
+
_workerPool = new WorkerPool(_workerPoolSize);
|
|
340
|
+
}
|
|
341
|
+
const result = await _workerPool.render(props);
|
|
342
|
+
return new WorkerCanvas({ ...result, pool: _workerPool });
|
|
343
|
+
}
|
|
344
|
+
return new RootNode(props).render();
|
|
345
|
+
};
|
|
126
346
|
|
|
127
|
-
export { Root, RootNode };
|
|
347
|
+
export { Root, RootNode, buildTree, configure };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TextProps } from '../canvas/canvas.type.js';
|
|
1
|
+
import type { TextProps, NodeDescriptor } from '../canvas/canvas.type.js';
|
|
2
2
|
import { type CanvasRenderingContext2D } from 'skia-canvas';
|
|
3
3
|
import { BoxNode } from '../canvas/layout.canvas.util.js';
|
|
4
4
|
/**
|
|
@@ -161,5 +161,5 @@ export declare class TextNode extends BoxNode {
|
|
|
161
161
|
/**
|
|
162
162
|
* Creates a new TextNode instance with rich text support
|
|
163
163
|
*/
|
|
164
|
-
export declare const Text: (text: number | string, props?: TextProps) =>
|
|
164
|
+
export declare const Text: (text: number | string, props?: TextProps) => NodeDescriptor;
|
|
165
165
|
//# sourceMappingURL=text.canvas.util.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/text.canvas.util.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"text.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/text.canvas.util.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAe,cAAc,EAAE,MAAM,yBAAyB,CAAA;AACrF,OAAO,EAAU,KAAK,wBAAwB,EAA2B,MAAM,aAAa,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AAGxD;;;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;cACgB,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAkXrH;AAED;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,GAAG,MAAM,EAAE,QAAQ,SAAS,KAAG,cAI9D,CAAA"}
|
|
@@ -1150,6 +1150,10 @@ class TextNode extends BoxNode {
|
|
|
1150
1150
|
/**
|
|
1151
1151
|
* Creates a new TextNode instance with rich text support
|
|
1152
1152
|
*/
|
|
1153
|
-
const Text = (text, props) =>
|
|
1153
|
+
const Text = (text, props) => ({
|
|
1154
|
+
__type: 'Text',
|
|
1155
|
+
text,
|
|
1156
|
+
props,
|
|
1157
|
+
});
|
|
1154
1158
|
|
|
1155
1159
|
export { Text, TextNode };
|