@nmmty/lazycanvas 1.0.0-dev.4 → 1.0.0-dev.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/core/Scene.d.ts +4 -6
  2. package/dist/core/Scene.js +6 -22
  3. package/dist/jsx-runtime.js +1 -1
  4. package/dist/structures/LazyCanvas.d.ts +2 -0
  5. package/dist/structures/LazyCanvas.js +2 -0
  6. package/dist/structures/components/BaseLayer.d.ts +40 -28
  7. package/dist/structures/components/BaseLayer.js +45 -12
  8. package/dist/structures/components/BezierLayer.d.ts +10 -2
  9. package/dist/structures/components/BezierLayer.js +3 -3
  10. package/dist/structures/components/Div.d.ts +10 -7
  11. package/dist/structures/components/Div.js +18 -3
  12. package/dist/structures/components/ImageLayer.js +2 -2
  13. package/dist/structures/components/LineLayer.d.ts +10 -2
  14. package/dist/structures/components/LineLayer.js +3 -3
  15. package/dist/structures/components/MorphLayer.d.ts +1 -1
  16. package/dist/structures/components/MorphLayer.js +5 -5
  17. package/dist/structures/components/Path2DLayer.d.ts +4 -4
  18. package/dist/structures/components/Path2DLayer.js +10 -12
  19. package/dist/structures/components/PolygonLayer.d.ts +1 -1
  20. package/dist/structures/components/PolygonLayer.js +5 -5
  21. package/dist/structures/components/QuadraticLayer.d.ts +10 -2
  22. package/dist/structures/components/QuadraticLayer.js +3 -3
  23. package/dist/structures/components/TextLayer.d.ts +1 -1
  24. package/dist/structures/components/TextLayer.js +33 -12
  25. package/dist/structures/helpers/readers/JSONReader.js +13 -13
  26. package/dist/structures/managers/LayoutManager.d.ts +23 -0
  27. package/dist/structures/managers/LayoutManager.js +409 -0
  28. package/dist/structures/managers/RenderManager.d.ts +1 -0
  29. package/dist/structures/managers/RenderManager.js +35 -2
  30. package/dist/types/types.d.ts +25 -0
  31. package/dist/utils/utils.js +11 -7
  32. package/package.json +3 -2
  33. package/biome.json +0 -41
@@ -0,0 +1,409 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LayoutManager = void 0;
4
+ const components_1 = require("../components");
5
+ const types_1 = require("../../types");
6
+ const LazyUtil_1 = require("../../utils/LazyUtil");
7
+ const fs_1 = require("fs");
8
+ class LayoutManager {
9
+ constructor(opts) {
10
+ this.yoga = null;
11
+ this.debug = opts?.debug || false;
12
+ this.ready = this.init();
13
+ }
14
+ async init() {
15
+ try {
16
+ // Initialize Yoga
17
+ // Use dynamic import to avoid issues with CJS/ESM interop and TLA
18
+ const yogaPkg = await import("yoga-layout");
19
+ const loadYoga = yogaPkg.default || yogaPkg;
20
+ if (typeof loadYoga === "function") {
21
+ this.yoga = await loadYoga((0, fs_1.readFileSync)(require.resolve("yoga-layout/dist/yoga.wasm")));
22
+ }
23
+ else {
24
+ this.yoga = loadYoga;
25
+ }
26
+ }
27
+ catch (e) {
28
+ // Fallback
29
+ try {
30
+ const yoga = require("yoga-layout");
31
+ this.yoga = yoga;
32
+ }
33
+ catch (e2) {
34
+ console.error("Failed to initialize Yoga Layout", e, e2);
35
+ }
36
+ }
37
+ }
38
+ calculateLayout(root, width, height, ctx, canvas) {
39
+ if (!this.yoga) {
40
+ if (this.debug)
41
+ LazyUtil_1.LazyLog.log("warn", "LayoutManager: Yoga not initialized yet");
42
+ return;
43
+ }
44
+ const rootNode = this.createNode(root, ctx, canvas);
45
+ if (!rootNode)
46
+ return;
47
+ rootNode.setWidth(width);
48
+ rootNode.setHeight(height);
49
+ rootNode.calculateLayout(width, height, this.yoga.DIRECTION_LTR);
50
+ this.applyLayout(rootNode, root);
51
+ // Clean up
52
+ this.freeNode(rootNode);
53
+ }
54
+ createNode(layer, ctx, canvas) {
55
+ if (!this.yoga)
56
+ return null;
57
+ const node = this.yoga.Node.create();
58
+ const layout = layer.props?.layout || {};
59
+ const size = layer.props?.size || {};
60
+ // Apply explicit layout properties first
61
+ if (layout.width !== undefined) {
62
+ this.setDimension(node, "width", layout.width);
63
+ }
64
+ else if (size.width !== undefined && layer.type !== types_1.LayerType.Text) {
65
+ // For TextLayer, skip size.width to allow measureFunc to work
66
+ this.setDimension(node, "width", size.width);
67
+ }
68
+ else if ((layer instanceof components_1.Div || layer.type === "group") && !layout.flexDirection) {
69
+ // For Div without explicit width and not a flex container, stretch to parent
70
+ // Flex containers should shrink-wrap their content by default
71
+ node.setWidthPercent(100);
72
+ }
73
+ if (layout.height !== undefined) {
74
+ this.setDimension(node, "height", layout.height);
75
+ }
76
+ else if (size.height !== undefined && layer.type !== types_1.LayerType.Text) {
77
+ // For TextLayer, skip size.height to allow measureFunc to work
78
+ this.setDimension(node, "height", size.height);
79
+ }
80
+ else if ((layer instanceof components_1.Div || layer.type === "group") && !layout.flexDirection) {
81
+ // For Div without explicit height and not a flex container, stretch to parent
82
+ // Flex containers should shrink-wrap their content by default
83
+ node.setHeightPercent(100);
84
+ }
85
+ if (layout.flexDirection)
86
+ node.setFlexDirection(this.getFlexDirection(layout.flexDirection));
87
+ if (layout.justifyContent)
88
+ node.setJustifyContent(this.getJustifyContent(layout.justifyContent));
89
+ if (layout.alignItems)
90
+ node.setAlignItems(this.getAlignItems(layout.alignItems));
91
+ if (layout.flexGrow !== undefined)
92
+ node.setFlexGrow(layout.flexGrow);
93
+ if (layout.flexShrink !== undefined)
94
+ node.setFlexShrink(layout.flexShrink);
95
+ if (layout.flexBasis !== undefined)
96
+ node.setFlexBasis(layout.flexBasis);
97
+ if (layout.padding)
98
+ this.setPadding(node, layout.padding);
99
+ if (layout.margin)
100
+ this.setMargin(node, layout.margin);
101
+ if (layout.gap !== undefined)
102
+ node.setGap(this.yoga.GUTTER_ALL, layout.gap);
103
+ // Position type (relative/absolute)
104
+ const isAbsolute = layout.position === "absolute";
105
+ if (isAbsolute) {
106
+ node.setPositionType(this.yoga.POSITION_TYPE_ABSOLUTE);
107
+ // Position values (top, left, right, bottom) - only for absolute positioning
108
+ if (layout.top !== undefined)
109
+ node.setPosition(this.yoga.EDGE_TOP, layout.top);
110
+ if (layout.left !== undefined)
111
+ node.setPosition(this.yoga.EDGE_LEFT, layout.left);
112
+ if (layout.right !== undefined)
113
+ node.setPosition(this.yoga.EDGE_RIGHT, layout.right);
114
+ if (layout.bottom !== undefined)
115
+ node.setPosition(this.yoga.EDGE_BOTTOM, layout.bottom);
116
+ }
117
+ // If not absolute, ignore top/left/right/bottom as they break flexbox layout
118
+ // Handle TextLayer measurement
119
+ if (layer.type === types_1.LayerType.Text && ctx && canvas) {
120
+ node.setMeasureFunc((width, widthMode, height, heightMode) => {
121
+ const textLayer = layer;
122
+ // Save original align/baseline for accurate measurement
123
+ const originalAlign = textLayer.props.align;
124
+ const originalBaseline = textLayer.props.baseline;
125
+ // Set to top-left for measurement (Yoga expects top-left coordinates)
126
+ textLayer.props.align = "left";
127
+ textLayer.props.baseline = "top";
128
+ // Temporarily disable multiline and width to measure natural size
129
+ const originalSize = textLayer.props.size ? { ...textLayer.props.size } : undefined;
130
+ const originalMultiline = textLayer.props.multiline
131
+ ? { ...textLayer.props.multiline }
132
+ : undefined;
133
+ // Disable multiline for measurement
134
+ if (textLayer.props.multiline) {
135
+ textLayer.props.multiline.enabled = false;
136
+ }
137
+ // Don't set width constraint for natural measurement
138
+ if (textLayer.props.size) {
139
+ textLayer.props.size.width = undefined;
140
+ }
141
+ const size = textLayer.measureText(ctx, canvas);
142
+ // Restore original props
143
+ if (originalSize)
144
+ textLayer.props.size = originalSize;
145
+ if (originalMultiline)
146
+ textLayer.props.multiline = originalMultiline;
147
+ textLayer.props.align = originalAlign;
148
+ textLayer.props.baseline = originalBaseline;
149
+ return { width: Math.ceil(size.width), height: Math.ceil(size.height) };
150
+ });
151
+ }
152
+ // Handle children
153
+ if (layer instanceof components_1.Div || (layer.type === "group" && "layers" in layer)) {
154
+ const children = layer.layers;
155
+ let childIndex = 0;
156
+ children.forEach((child) => {
157
+ const childLayout = child.props?.layout;
158
+ const childPosition = child.props?.position;
159
+ // IMPORTANT: Logic for deciding if child participates in Yoga layout:
160
+ // 1. Div/containers always participate (they manage their own children)
161
+ // 2. If layer has explicit position prop (x or y set), skip Yoga - use position-based positioning
162
+ // 3. If layer has explicit layout prop, use Yoga layout
163
+ // 4. Otherwise (no position, no layout), use Yoga layout by default for proper flow
164
+ const isContainer = child instanceof components_1.Div || child.type === "group";
165
+ const hasExplicitPosition = childPosition && (childPosition.x !== undefined || childPosition.y !== undefined);
166
+ const hasExplicitLayout = childLayout !== undefined && Object.keys(childLayout).length > 0;
167
+ // Skip Yoga layout if:
168
+ // - Not a container AND has explicit position set (user wants manual positioning)
169
+ if (!isContainer && hasExplicitPosition && !hasExplicitLayout) {
170
+ return; // Skip this child - it will use position-based positioning
171
+ }
172
+ const childNode = this.createNode(child, ctx, canvas);
173
+ if (childNode) {
174
+ node.insertChild(childNode, childIndex++);
175
+ }
176
+ });
177
+ }
178
+ else if ("children" in layer && Array.isArray(layer.children)) {
179
+ const children = layer.children;
180
+ let childIndex = 0;
181
+ children.forEach((child) => {
182
+ const childLayout = child.props?.layout;
183
+ const childPosition = child.props?.position;
184
+ const isContainer = child instanceof components_1.Div || child.type === "group";
185
+ const hasExplicitPosition = childPosition && (childPosition.x !== undefined || childPosition.y !== undefined);
186
+ const hasExplicitLayout = childLayout !== undefined && Object.keys(childLayout).length > 0;
187
+ if (!isContainer && hasExplicitPosition && !hasExplicitLayout) {
188
+ return; // Skip this child - it will use position-based positioning
189
+ }
190
+ const childNode = this.createNode(child, ctx, canvas);
191
+ if (childNode) {
192
+ node.insertChild(childNode, childIndex++);
193
+ }
194
+ });
195
+ }
196
+ return node;
197
+ }
198
+ applyLayout(node, layer) {
199
+ const layout = node.getComputedLayout();
200
+ // Debug logging if enabled
201
+ if (this.debug) {
202
+ console.log(`[Layout] ${layer.id}: left=${layout.left}, top=${layout.top}, width=${layout.width}, height=${layout.height}`);
203
+ }
204
+ // Apply computed layout to layer props
205
+ // We need to be careful not to overwrite original props if we want to recalculate
206
+ // But for rendering, we need the absolute positions.
207
+ // Maybe we should store computed layout separately or update position?
208
+ // For now, let's update position and size if they are not fixed?
209
+ // Or better, update a specific 'computedLayout' property if we added one.
210
+ // Since we didn't add 'computedLayout', let's update position.
211
+ // Note: Yoga calculates relative positions. We might need to convert to absolute if the renderer expects absolute.
212
+ // But if the renderer handles hierarchy (Div draws children), relative is fine.
213
+ if (!layer.props)
214
+ layer.props = {};
215
+ if (!layer.props.position)
216
+ layer.props.position = { x: 0, y: 0 };
217
+ // Mark that this layer has computed layout from Yoga
218
+ // This will be used by TextLayer to know it should use top-left alignment
219
+ layer.props._computedLayout = true;
220
+ // Update position
221
+ layer.props.position.x = layout.left;
222
+ layer.props.position.y = layout.top;
223
+ // If layout is applied, we should probably force centring to top-left (start-top)
224
+ // to match Yoga's coordinate system
225
+ if ("centring" in layer.props) {
226
+ layer.props.centring = "start-top"; // or "none" depending on implementation
227
+ }
228
+ // Update size if applicable (e.g. Div or layers that support size)
229
+ if ("size" in layer.props) {
230
+ // @ts-ignore
231
+ const currentSize = layer.props.size;
232
+ // @ts-ignore
233
+ layer.props.size = { ...currentSize, width: layout.width, height: layout.height };
234
+ }
235
+ else if (layer instanceof components_1.Div) {
236
+ // Div doesn't have size prop usually, but maybe it should?
237
+ }
238
+ // Recursively apply to children
239
+ if (layer instanceof components_1.Div || (layer.type === "group" && "layers" in layer)) {
240
+ const children = layer.layers;
241
+ let yogaChildIndex = 0;
242
+ for (let i = 0; i < children.length; i++) {
243
+ const child = children[i];
244
+ // Check if this child was added to Yoga tree
245
+ // Must match the logic in createNode
246
+ const childLayout = child.props?.layout;
247
+ const childPosition = child.props?.position;
248
+ const isContainer = child instanceof components_1.Div || child.type === "group";
249
+ const hasExplicitPosition = childPosition && (childPosition.x !== undefined || childPosition.y !== undefined);
250
+ const hasExplicitLayout = childLayout !== undefined && Object.keys(childLayout).length > 0;
251
+ // Skip if this child wasn't added to Yoga tree
252
+ if (!isContainer && hasExplicitPosition && !hasExplicitLayout) {
253
+ continue;
254
+ }
255
+ const childNode = node.getChild(yogaChildIndex++);
256
+ if (childNode) {
257
+ this.applyLayout(childNode, child);
258
+ }
259
+ }
260
+ }
261
+ else if ("children" in layer && Array.isArray(layer.children)) {
262
+ const children = layer.children;
263
+ let yogaChildIndex = 0;
264
+ for (let i = 0; i < children.length; i++) {
265
+ const child = children[i];
266
+ const childLayout = child.props?.layout;
267
+ const childPosition = child.props?.position;
268
+ const isContainer = child instanceof components_1.Div || child.type === "group";
269
+ const hasExplicitPosition = childPosition && (childPosition.x !== undefined || childPosition.y !== undefined);
270
+ const hasExplicitLayout = childLayout !== undefined && Object.keys(childLayout).length > 0;
271
+ if (!isContainer && hasExplicitPosition && !hasExplicitLayout) {
272
+ continue;
273
+ }
274
+ const childNode = node.getChild(yogaChildIndex++);
275
+ if (childNode) {
276
+ this.applyLayout(childNode, child);
277
+ }
278
+ }
279
+ }
280
+ }
281
+ freeNode(node) {
282
+ // Recursively free nodes? Yoga might handle this if we free root?
283
+ // Yoga JS usually requires manual freeing.
284
+ // node.freeRecursive(); // if available
285
+ if (node.freeRecursive) {
286
+ node.freeRecursive();
287
+ }
288
+ else {
289
+ node.free();
290
+ }
291
+ }
292
+ // Helpers for Yoga enums
293
+ getFlexDirection(dir) {
294
+ switch (dir) {
295
+ case "row":
296
+ return this.yoga.FLEX_DIRECTION_ROW;
297
+ case "column":
298
+ return this.yoga.FLEX_DIRECTION_COLUMN;
299
+ case "row-reverse":
300
+ return this.yoga.FLEX_DIRECTION_ROW_REVERSE;
301
+ case "column-reverse":
302
+ return this.yoga.FLEX_DIRECTION_COLUMN_REVERSE;
303
+ default:
304
+ return this.yoga.FLEX_DIRECTION_ROW;
305
+ }
306
+ }
307
+ getJustifyContent(justify) {
308
+ switch (justify) {
309
+ case "flex-start":
310
+ return this.yoga.JUSTIFY_FLEX_START;
311
+ case "center":
312
+ return this.yoga.JUSTIFY_CENTER;
313
+ case "flex-end":
314
+ return this.yoga.JUSTIFY_FLEX_END;
315
+ case "space-between":
316
+ return this.yoga.JUSTIFY_SPACE_BETWEEN;
317
+ case "space-around":
318
+ return this.yoga.JUSTIFY_SPACE_AROUND;
319
+ case "space-evenly":
320
+ return this.yoga.JUSTIFY_SPACE_EVENLY;
321
+ default:
322
+ return this.yoga.JUSTIFY_FLEX_START;
323
+ }
324
+ }
325
+ getAlignItems(align) {
326
+ switch (align) {
327
+ case "flex-start":
328
+ return this.yoga.ALIGN_FLEX_START;
329
+ case "center":
330
+ return this.yoga.ALIGN_CENTER;
331
+ case "flex-end":
332
+ return this.yoga.ALIGN_FLEX_END;
333
+ case "stretch":
334
+ return this.yoga.ALIGN_STRETCH;
335
+ case "baseline":
336
+ return this.yoga.ALIGN_BASELINE;
337
+ default:
338
+ return this.yoga.ALIGN_STRETCH;
339
+ }
340
+ }
341
+ getPositionType(position) {
342
+ if (position === "absolute")
343
+ return this.yoga.POSITION_TYPE_ABSOLUTE;
344
+ return this.yoga.POSITION_TYPE_RELATIVE;
345
+ }
346
+ setDimension(node, prop, value) {
347
+ if (typeof value === "number") {
348
+ if (prop === "width")
349
+ node.setWidth(value);
350
+ else
351
+ node.setHeight(value);
352
+ }
353
+ else if (typeof value === "string") {
354
+ if (value.endsWith("%")) {
355
+ const val = parseFloat(value);
356
+ if (prop === "width")
357
+ node.setWidthPercent(val);
358
+ else
359
+ node.setHeightPercent(val);
360
+ }
361
+ else if (value === "auto") {
362
+ if (prop === "width")
363
+ node.setWidthAuto();
364
+ else
365
+ node.setHeightAuto();
366
+ }
367
+ }
368
+ }
369
+ setPadding(node, padding) {
370
+ if (typeof padding === "number") {
371
+ node.setPadding(this.yoga.EDGE_ALL, padding);
372
+ }
373
+ else if (Array.isArray(padding)) {
374
+ // CSS order: top, right, bottom, left
375
+ if (padding.length === 1)
376
+ node.setPadding(this.yoga.EDGE_ALL, padding[0]);
377
+ else if (padding.length === 2) {
378
+ node.setPadding(this.yoga.EDGE_VERTICAL, padding[0]);
379
+ node.setPadding(this.yoga.EDGE_HORIZONTAL, padding[1]);
380
+ }
381
+ else if (padding.length === 4) {
382
+ node.setPadding(this.yoga.EDGE_TOP, padding[0]);
383
+ node.setPadding(this.yoga.EDGE_RIGHT, padding[1]);
384
+ node.setPadding(this.yoga.EDGE_BOTTOM, padding[2]);
385
+ node.setPadding(this.yoga.EDGE_LEFT, padding[3]);
386
+ }
387
+ }
388
+ }
389
+ setMargin(node, margin) {
390
+ if (typeof margin === "number") {
391
+ node.setMargin(this.yoga.EDGE_ALL, margin);
392
+ }
393
+ else if (Array.isArray(margin)) {
394
+ if (margin.length === 1)
395
+ node.setMargin(this.yoga.EDGE_ALL, margin[0]);
396
+ else if (margin.length === 2) {
397
+ node.setMargin(this.yoga.EDGE_VERTICAL, margin[0]);
398
+ node.setMargin(this.yoga.EDGE_HORIZONTAL, margin[1]);
399
+ }
400
+ else if (margin.length === 4) {
401
+ node.setMargin(this.yoga.EDGE_TOP, margin[0]);
402
+ node.setMargin(this.yoga.EDGE_RIGHT, margin[1]);
403
+ node.setMargin(this.yoga.EDGE_BOTTOM, margin[2]);
404
+ node.setMargin(this.yoga.EDGE_LEFT, margin[3]);
405
+ }
406
+ }
407
+ }
408
+ }
409
+ exports.LayoutManager = LayoutManager;
@@ -1,6 +1,7 @@
1
1
  import { AnyExport } from "../../types";
2
2
  import { LazyCanvas } from "../LazyCanvas";
3
3
  import { Canvas, SKRSContext2D, SvgCanvas } from "@napi-rs/canvas";
4
+ import { Buffer } from "buffer";
4
5
  /**
5
6
  * Interface representing the RenderManager.
6
7
  */
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RenderManager = void 0;
4
4
  const types_1 = require("../../types");
5
+ const components_1 = require("../components");
5
6
  const LazyUtil_1 = require("../../utils/LazyUtil");
6
7
  /**
7
8
  * Class responsible for managing rendering operations, including static and animated exports.
@@ -28,6 +29,32 @@ class RenderManager {
28
29
  if (layer.visible) {
29
30
  this.lazyCanvas.ctx.globalCompositeOperation = layer.props?.globalComposite || "source-over";
30
31
  await layer.draw(this.lazyCanvas.ctx, this.lazyCanvas.canvas, this.lazyCanvas.manager.layers, this.debug);
32
+ // Draw children if any (and not a Div, as Div handles its own children)
33
+ // Actually, if we want to support children on any layer, we should handle it here.
34
+ // But Div.draw already handles children.
35
+ // If we handle it here for Div, we get double rendering.
36
+ // So we skip Div.
37
+ const children = layer.children;
38
+ if (!(layer instanceof components_1.Div) && children && Array.isArray(children) && children.length > 0) {
39
+ const ctx = this.lazyCanvas.ctx;
40
+ ctx.save();
41
+ // Apply parent position offset
42
+ // LayoutManager sets position relative to parent.
43
+ // We need to translate context to parent's position so children are drawn relative to it.
44
+ // However, layer.draw() might have already drawn the layer at that position.
45
+ // And layer.draw() usually restores context.
46
+ // So we are back at parent's parent coordinate system.
47
+ // We need to translate to layer's position.
48
+ if (layer.props.position) {
49
+ const x = typeof layer.props.position.x === "number" ? layer.props.position.x : 0;
50
+ const y = typeof layer.props.position.y === "number" ? layer.props.position.y : 0;
51
+ ctx.translate(x, y);
52
+ }
53
+ for (const child of children) {
54
+ await this.renderLayer(child);
55
+ }
56
+ ctx.restore();
57
+ }
31
58
  this.lazyCanvas.ctx.shadowColor = "transparent";
32
59
  }
33
60
  return this.lazyCanvas.ctx;
@@ -40,7 +67,13 @@ class RenderManager {
40
67
  async renderStatic(exportType) {
41
68
  if (this.debug)
42
69
  LazyUtil_1.LazyLog.log("info", `Rendering static...`);
43
- for (const layer of this.lazyCanvas.manager.layers.toArray()) {
70
+ // Wait for layout engine to be ready
71
+ await this.lazyCanvas.manager.layout.ready;
72
+ const rootLayers = this.lazyCanvas.manager.layers.toArray().filter((l) => !l.parent);
73
+ for (const layer of rootLayers) {
74
+ this.lazyCanvas.manager.layout.calculateLayout(layer, this.lazyCanvas.options.width, this.lazyCanvas.options.height, this.lazyCanvas.ctx, this.lazyCanvas.canvas);
75
+ }
76
+ for (const layer of rootLayers) {
44
77
  await this.renderLayer(layer);
45
78
  }
46
79
  switch (exportType) {
@@ -74,7 +107,7 @@ class RenderManager {
74
107
  return await this.renderStatic(types_1.Export.BUFFER);
75
108
  case types_1.Export.CTX:
76
109
  case "ctx":
77
- return this.lazyCanvas.ctx;
110
+ return await this.renderStatic(types_1.Export.CTX);
78
111
  case types_1.Export.SVG:
79
112
  case "svg":
80
113
  return await this.renderStatic(types_1.Export.SVG);
@@ -230,3 +230,28 @@ export type StrokeOptions = {
230
230
  dash?: number[];
231
231
  miterLimit?: number;
232
232
  };
233
+
234
+ export interface ILayoutProps {
235
+ width?: ScaleType;
236
+ height?: ScaleType;
237
+ flexDirection?: "row" | "column" | "row-reverse" | "column-reverse";
238
+ justifyContent?:
239
+ | "flex-start"
240
+ | "center"
241
+ | "flex-end"
242
+ | "space-between"
243
+ | "space-around"
244
+ | "space-evenly";
245
+ alignItems?: "flex-start" | "center" | "flex-end" | "stretch" | "baseline";
246
+ flexGrow?: number;
247
+ flexShrink?: number;
248
+ flexBasis?: number | string;
249
+ padding?: number | number[];
250
+ margin?: number | number[];
251
+ gap?: number;
252
+ position?: "absolute" | "relative";
253
+ top?: number | string;
254
+ left?: number | string;
255
+ right?: number | string;
256
+ bottom?: number | string;
257
+ }
@@ -98,9 +98,9 @@ function parseToNormal(v, ctx, canvas, layer = { width: 0, height: 0 }, options
98
98
  case "link-h":
99
99
  return getLayerHeight(anyLayer, ctx, canvas, manager, parserInstance) + additionalSpacing;
100
100
  case "link-x":
101
- return parserInstance.parse(anyLayer.props.position.x) + additionalSpacing;
101
+ return parserInstance.parse(anyLayer.props.position?.x || 0) + additionalSpacing;
102
102
  case "link-y":
103
- return parserInstance.parse(anyLayer.props.position.y) + additionalSpacing;
103
+ return parserInstance.parse(anyLayer.props.position?.y || 0) + additionalSpacing;
104
104
  }
105
105
  }
106
106
  }
@@ -118,9 +118,9 @@ function parseToNormal(v, ctx, canvas, layer = { width: 0, height: 0 }, options
118
118
  case types_1.LinkType.Height:
119
119
  return getLayerHeight(anyLayer, ctx, canvas, manager, parserInstance) + additionalSpacing;
120
120
  case types_1.LinkType.X:
121
- return parserInstance.parse(anyLayer.props.position.x) + additionalSpacing;
121
+ return parserInstance.parse(anyLayer.props.position?.x || 0) + additionalSpacing;
122
122
  case types_1.LinkType.Y:
123
- return parserInstance.parse(anyLayer.props.position.y) + additionalSpacing;
123
+ return parserInstance.parse(anyLayer.props.position?.y || 0) + additionalSpacing;
124
124
  }
125
125
  }
126
126
  return 0;
@@ -436,8 +436,10 @@ function resizeLayers(layers, ratio) {
436
436
  if (layers.length > 0) {
437
437
  for (const layer of layers) {
438
438
  if (!(layer instanceof components_1.Div || layer instanceof components_1.Path2DLayer)) {
439
- layer.props.position.x = resize(layer.props.position.x, ratio);
440
- layer.props.position.y = resize(layer.props.position.y, ratio);
439
+ if (layer.props.position) {
440
+ layer.props.position.x = resize(layer.props.position.x, ratio);
441
+ layer.props.position.y = resize(layer.props.position.y, ratio);
442
+ }
441
443
  if ("size" in layer.props && layer.props.size) {
442
444
  layer.props.size.width = resize(layer.props.size.width, ratio);
443
445
  layer.props.size.height = resize(layer.props.size.height, ratio);
@@ -458,7 +460,9 @@ function resizeLayers(layers, ratio) {
458
460
  if ("stroke" in layer.props && layer.props.stroke) {
459
461
  layer.props.stroke.width = resize(layer.props.stroke.width, ratio);
460
462
  }
461
- if ("endX" in layer.props.position && "endY" in layer.props.position) {
463
+ if (layer.props.position &&
464
+ "endX" in layer.props.position &&
465
+ "endY" in layer.props.position) {
462
466
  layer.props.position.endX = resize(layer.props.position.endX, ratio);
463
467
  layer.props.position.endY = resize(layer.props.position.endY, ratio);
464
468
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nmmty/lazycanvas",
3
- "version": "1.0.0-dev.4",
3
+ "version": "1.0.0-dev.7",
4
4
  "description": "A simple way to interact with @napi-rs/canvas in an advanced way!",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -25,7 +25,8 @@
25
25
  "@napi-rs/canvas": "^0.1.88",
26
26
  "gifenc": "^1.0.3",
27
27
  "js-yaml": "^4.1.0",
28
- "svgson": "^5.3.1"
28
+ "svgson": "^5.3.1",
29
+ "yoga-layout": "^3.2.1"
29
30
  },
30
31
  "devDependencies": {
31
32
  "@biomejs/biome": "1.9.4",
package/biome.json DELETED
@@ -1,41 +0,0 @@
1
- {
2
- "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3
- "vcs": {
4
- "enabled": false,
5
- "clientKind": "git",
6
- "useIgnoreFile": false
7
- },
8
- "files": {
9
- "ignoreUnknown": false,
10
- "ignore": [
11
- "node_modules/",
12
- "dist/",
13
- "biome.json",
14
- ".biome/",
15
- ".git/",
16
- ".hg/",
17
- ".svn/",
18
- "public/",
19
- "docs/",
20
- "resources/"
21
- ]
22
- },
23
- "formatter": {
24
- "enabled": true,
25
- "indentStyle": "space",
26
- "indentWidth": 2,
27
- "lineWidth": 100
28
- },
29
- "organizeImports": {
30
- "enabled": true
31
- },
32
- "linter": {
33
- "enabled": true,
34
- "rules": { "recommended": true }
35
- },
36
- "javascript": {
37
- "formatter": {
38
- "quoteStyle": "double"
39
- }
40
- }
41
- }