labellife-design-tool 0.1.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 (85) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +128 -0
  3. package/dist/lib/CanvasEditor.d.ts +8 -0
  4. package/dist/lib/CanvasEditor.d.ts.map +1 -0
  5. package/dist/lib/components/Header.d.ts +17 -0
  6. package/dist/lib/components/Header.d.ts.map +1 -0
  7. package/dist/lib/components/LeftMenu.d.ts +13 -0
  8. package/dist/lib/components/LeftMenu.d.ts.map +1 -0
  9. package/dist/lib/components/TemplateInputModal.d.ts +10 -0
  10. package/dist/lib/components/TemplateInputModal.d.ts.map +1 -0
  11. package/dist/lib/components/header/AppName.d.ts +4 -0
  12. package/dist/lib/components/header/AppName.d.ts.map +1 -0
  13. package/dist/lib/components/header/ExportButton.d.ts +8 -0
  14. package/dist/lib/components/header/ExportButton.d.ts.map +1 -0
  15. package/dist/lib/components/header/HistoryControls.d.ts +10 -0
  16. package/dist/lib/components/header/HistoryControls.d.ts.map +1 -0
  17. package/dist/lib/components/header/ZoomControls.d.ts +10 -0
  18. package/dist/lib/components/header/ZoomControls.d.ts.map +1 -0
  19. package/dist/lib/components/sidebar/RightSidebar.d.ts +16 -0
  20. package/dist/lib/components/sidebar/RightSidebar.d.ts.map +1 -0
  21. package/dist/lib/config.d.ts +11 -0
  22. package/dist/lib/config.d.ts.map +1 -0
  23. package/dist/lib/constants/CanvasPresets.d.ts +6 -0
  24. package/dist/lib/constants/CanvasPresets.d.ts.map +1 -0
  25. package/dist/lib/constants/DefaultColors.d.ts +2 -0
  26. package/dist/lib/constants/DefaultColors.d.ts.map +1 -0
  27. package/dist/lib/constants/FontFamilies.d.ts +2 -0
  28. package/dist/lib/constants/FontFamilies.d.ts.map +1 -0
  29. package/dist/lib/constants/index.d.ts +4 -0
  30. package/dist/lib/constants/index.d.ts.map +1 -0
  31. package/dist/lib/elements/EditableTextElement.d.ts +10 -0
  32. package/dist/lib/elements/EditableTextElement.d.ts.map +1 -0
  33. package/dist/lib/elements/ShapeElement.d.ts +10 -0
  34. package/dist/lib/elements/ShapeElement.d.ts.map +1 -0
  35. package/dist/lib/elements/UrlImageElement.d.ts +10 -0
  36. package/dist/lib/elements/UrlImageElement.d.ts.map +1 -0
  37. package/dist/lib/elements/index.d.ts +4 -0
  38. package/dist/lib/elements/index.d.ts.map +1 -0
  39. package/dist/lib/index.css +1109 -0
  40. package/dist/lib/index.js +4052 -0
  41. package/dist/lib/lib/index.d.ts +11 -0
  42. package/dist/lib/lib/index.d.ts.map +1 -0
  43. package/dist/lib/panels/BackgroundPanel.d.ts +11 -0
  44. package/dist/lib/panels/BackgroundPanel.d.ts.map +1 -0
  45. package/dist/lib/panels/DesignPanel.d.ts +15 -0
  46. package/dist/lib/panels/DesignPanel.d.ts.map +1 -0
  47. package/dist/lib/panels/ElementPanel.d.ts +8 -0
  48. package/dist/lib/panels/ElementPanel.d.ts.map +1 -0
  49. package/dist/lib/panels/ExportPanel.d.ts +10 -0
  50. package/dist/lib/panels/ExportPanel.d.ts.map +1 -0
  51. package/dist/lib/panels/ImagePanel.d.ts +13 -0
  52. package/dist/lib/panels/ImagePanel.d.ts.map +1 -0
  53. package/dist/lib/panels/TextPanel.d.ts +11 -0
  54. package/dist/lib/panels/TextPanel.d.ts.map +1 -0
  55. package/dist/lib/panels/VariablesPanel.d.ts +11 -0
  56. package/dist/lib/panels/VariablesPanel.d.ts.map +1 -0
  57. package/dist/lib/types/CanvasDesign.d.ts +13 -0
  58. package/dist/lib/types/CanvasDesign.d.ts.map +1 -0
  59. package/dist/lib/types/CanvasEditor.d.ts +6 -0
  60. package/dist/lib/types/CanvasEditor.d.ts.map +1 -0
  61. package/dist/lib/types/CanvasElement.d.ts +80 -0
  62. package/dist/lib/types/CanvasElement.d.ts.map +1 -0
  63. package/dist/lib/types/Config.d.ts +19 -0
  64. package/dist/lib/types/Config.d.ts.map +1 -0
  65. package/dist/lib/types/Feature.d.ts +4 -0
  66. package/dist/lib/types/Feature.d.ts.map +1 -0
  67. package/dist/lib/types/MenuElement.d.ts +7 -0
  68. package/dist/lib/types/MenuElement.d.ts.map +1 -0
  69. package/dist/lib/types/Page.d.ts +9 -0
  70. package/dist/lib/types/Page.d.ts.map +1 -0
  71. package/dist/lib/types/Panel.d.ts +17 -0
  72. package/dist/lib/types/Panel.d.ts.map +1 -0
  73. package/dist/lib/types/ShapeType.d.ts +2 -0
  74. package/dist/lib/types/ShapeType.d.ts.map +1 -0
  75. package/dist/lib/types/ToolType.d.ts +3 -0
  76. package/dist/lib/types/ToolType.d.ts.map +1 -0
  77. package/dist/lib/types/UnsplashSearchResult.d.ts +11 -0
  78. package/dist/lib/types/UnsplashSearchResult.d.ts.map +1 -0
  79. package/dist/lib/types/UserInput.d.ts +21 -0
  80. package/dist/lib/types/UserInput.d.ts.map +1 -0
  81. package/dist/lib/types/index.d.ts +12 -0
  82. package/dist/lib/types/index.d.ts.map +1 -0
  83. package/dist/lib/utils/exportImportUtils.d.ts +24 -0
  84. package/dist/lib/utils/exportImportUtils.d.ts.map +1 -0
  85. package/package.json +91 -0
@@ -0,0 +1,4052 @@
1
+ // src/CanvasEditor.tsx
2
+ import { useState as useState3, useRef as useRef5, useEffect as useEffect4, useCallback as useCallback2 } from "react";
3
+ import { Stage, Layer, Rect as Rect2 } from "react-konva";
4
+ import {
5
+ PlusCircle
6
+ } from "lucide-react";
7
+
8
+ // src/elements/EditableTextElement.tsx
9
+ import { useRef, useEffect } from "react";
10
+ import { Text, Transformer } from "react-konva";
11
+ import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
12
+ var applyTextCase = (text, textCase) => {
13
+ if (!text)
14
+ return;
15
+ switch (textCase) {
16
+ case "uppercase":
17
+ return text.toUpperCase();
18
+ case "lowercase":
19
+ return text.toLowerCase();
20
+ case "capitalize":
21
+ return text.replace(/\b\w/g, (char) => char.toUpperCase());
22
+ case "none":
23
+ default:
24
+ return text;
25
+ }
26
+ };
27
+ var EditableTextElement = ({ element, isSelected, onSelect, onChange }) => {
28
+ const textRef = useRef(null);
29
+ const trRef = useRef(null);
30
+ useEffect(() => {
31
+ if (isSelected && trRef.current && textRef.current && !element.locked) {
32
+ trRef.current.nodes([textRef.current]);
33
+ trRef.current.getLayer()?.batchDraw();
34
+ }
35
+ }, [isSelected, element.locked]);
36
+ const handleDragEnd = (e) => {
37
+ onChange({
38
+ x: e.target.x(),
39
+ y: e.target.y()
40
+ });
41
+ };
42
+ const handleTransformEnd = () => {
43
+ const node = textRef.current;
44
+ if (node) {
45
+ const scaleX = node.scaleX();
46
+ node.scaleX(1);
47
+ onChange({
48
+ x: node.x(),
49
+ y: node.y(),
50
+ rotation: node.rotation(),
51
+ fontSize: (element.fontSize || 16) * scaleX
52
+ });
53
+ }
54
+ };
55
+ const handleDblClick = () => {
56
+ if (element.locked)
57
+ return;
58
+ const textNode = textRef.current;
59
+ if (!textNode)
60
+ return;
61
+ textNode.hide();
62
+ trRef.current?.hide();
63
+ const textPosition = textNode.absolutePosition();
64
+ const stageBox = textNode.getStage().container().getBoundingClientRect();
65
+ const areaPosition = {
66
+ x: stageBox.left + textPosition.x,
67
+ y: stageBox.top + textPosition.y
68
+ };
69
+ const textarea = document.createElement("textarea");
70
+ document.body.appendChild(textarea);
71
+ textarea.value = element.text || "";
72
+ textarea.style.position = "absolute";
73
+ textarea.style.top = areaPosition.y + "px";
74
+ textarea.style.left = areaPosition.x + "px";
75
+ textarea.style.width = textNode.width() - textNode.padding() * 2 + "px";
76
+ textarea.style.fontSize = element.fontSize + "px";
77
+ textarea.style.border = "2px solid #4299e1";
78
+ textarea.style.padding = "4px";
79
+ textarea.style.margin = "0px";
80
+ textarea.style.overflow = "hidden";
81
+ textarea.style.background = "white";
82
+ textarea.style.outline = "none";
83
+ textarea.style.resize = "none";
84
+ textarea.style.lineHeight = String(element.lineHeight || 1.2);
85
+ textarea.style.fontFamily = element.fontFamily || "Arial";
86
+ textarea.style.transformOrigin = "left top";
87
+ textarea.style.textAlign = element.align || "left";
88
+ textarea.style.color = element.fill || "#000";
89
+ textarea.style.fontWeight = element.fontStyle?.includes("bold") ? "bold" : "normal";
90
+ textarea.style.fontStyle = element.fontStyle?.includes("italic") ? "italic" : "normal";
91
+ let textDecorationStyle = "";
92
+ if (element.textDecoration?.includes("underline")) {
93
+ textDecorationStyle += "underline ";
94
+ }
95
+ if (element.strikethrough) {
96
+ textDecorationStyle += "line-through";
97
+ }
98
+ textarea.style.textDecoration = textDecorationStyle.trim() || "none";
99
+ switch (element.textCase) {
100
+ case "uppercase":
101
+ textarea.style.textTransform = "uppercase";
102
+ break;
103
+ case "lowercase":
104
+ textarea.style.textTransform = "lowercase";
105
+ break;
106
+ case "capitalize":
107
+ textarea.style.textTransform = "capitalize";
108
+ break;
109
+ default:
110
+ textarea.style.textTransform = "none";
111
+ }
112
+ textarea.style.letterSpacing = `${element.letterSpacing || 0}px`;
113
+ const rotation = textNode.rotation();
114
+ let transform = "";
115
+ if (rotation) {
116
+ transform += "rotateZ(" + rotation + "deg)";
117
+ }
118
+ textarea.style.transform = transform;
119
+ textarea.style.height = "auto";
120
+ textarea.style.height = textarea.scrollHeight + 3 + "px";
121
+ textarea.focus();
122
+ textarea.select();
123
+ const removeTextarea = () => {
124
+ textarea.parentNode?.removeChild(textarea);
125
+ window.removeEventListener("click", handleOutsideClick);
126
+ textNode.show();
127
+ trRef.current?.show();
128
+ };
129
+ textarea.addEventListener("keydown", (e) => {
130
+ if (e.keyCode === 13 && !e.shiftKey) {
131
+ onChange({ text: textarea.value });
132
+ removeTextarea();
133
+ }
134
+ if (e.keyCode === 27) {
135
+ removeTextarea();
136
+ }
137
+ });
138
+ textarea.addEventListener("input", () => {
139
+ textarea.style.height = "auto";
140
+ textarea.style.height = textarea.scrollHeight + 3 + "px";
141
+ });
142
+ const handleOutsideClick = (e) => {
143
+ if (e.target !== textarea) {
144
+ onChange({ text: textarea.value });
145
+ removeTextarea();
146
+ }
147
+ };
148
+ setTimeout(() => {
149
+ window.addEventListener("click", handleOutsideClick);
150
+ });
151
+ };
152
+ if (!element.visible)
153
+ return null;
154
+ return /* @__PURE__ */ jsxDEV(Fragment, {
155
+ children: [
156
+ /* @__PURE__ */ jsxDEV(Text, {
157
+ ref: textRef,
158
+ text: applyTextCase(element.text || "Double-click to edit", element.textCase),
159
+ x: element.x,
160
+ y: element.y,
161
+ width: element.textWidth,
162
+ height: element.textHeight,
163
+ fontSize: element.fontSize || 16,
164
+ fontFamily: element.fontFamily || "Arial",
165
+ fontStyle: element.fontStyle,
166
+ textDecoration: (element.textDecoration?.includes("underline") ? "underline " : "") + (element.strikethrough ? "line-through" : ""),
167
+ fill: element.fill || "#000000",
168
+ stroke: element.stroke,
169
+ strokeWidth: element.strokeWidth || 0,
170
+ align: element.align || "left",
171
+ verticalAlign: element.verticalAlign || "top",
172
+ lineHeight: element.lineHeight || 1.2,
173
+ letterSpacing: element.letterSpacing || 0,
174
+ rotation: element.rotation,
175
+ opacity: element.opacity,
176
+ draggable: !element.locked,
177
+ onClick: onSelect,
178
+ onTap: onSelect,
179
+ onDblClick: handleDblClick,
180
+ onDblTap: handleDblClick,
181
+ onDragEnd: handleDragEnd,
182
+ onTransformEnd: handleTransformEnd
183
+ }, undefined, false, undefined, this),
184
+ isSelected && !element.locked && /* @__PURE__ */ jsxDEV(Transformer, {
185
+ ref: trRef,
186
+ enabledAnchors: [
187
+ "top-left",
188
+ "top-right",
189
+ "bottom-left",
190
+ "bottom-right"
191
+ ],
192
+ boundBoxFunc: (oldBox, newBox) => {
193
+ if (newBox.width < 5) {
194
+ return oldBox;
195
+ }
196
+ return newBox;
197
+ }
198
+ }, undefined, false, undefined, this)
199
+ ]
200
+ }, undefined, true, undefined, this);
201
+ };
202
+ // src/elements/ShapeElement.tsx
203
+ import { useRef as useRef2, useEffect as useEffect2 } from "react";
204
+ import {
205
+ Rect,
206
+ Transformer as Transformer2,
207
+ Circle,
208
+ RegularPolygon,
209
+ Star,
210
+ Line,
211
+ Arrow,
212
+ Path
213
+ } from "react-konva";
214
+ import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime";
215
+ var ShapeElement = ({ element, isSelected, onSelect, onChange }) => {
216
+ const shapeRef = useRef2(null);
217
+ const trRef = useRef2(null);
218
+ useEffect2(() => {
219
+ if (isSelected && trRef.current && shapeRef.current && !element.locked) {
220
+ trRef.current.nodes([shapeRef.current]);
221
+ trRef.current.getLayer()?.batchDraw();
222
+ }
223
+ }, [isSelected, element.locked]);
224
+ const handleDragEnd = (e) => {
225
+ onChange({
226
+ x: e.target.x(),
227
+ y: e.target.y()
228
+ });
229
+ };
230
+ const handleTransformEnd = () => {
231
+ const node = shapeRef.current;
232
+ if (node) {
233
+ const scaleX = node.scaleX();
234
+ const scaleY = node.scaleY();
235
+ let newWidth = element.width || 100;
236
+ let newHeight = element.height || 100;
237
+ if (element.shapeType === "custom-path" && element.pathNaturalWidth && element.pathNaturalHeight) {
238
+ newWidth = Math.max(5, (element.width || element.pathNaturalWidth) * scaleX);
239
+ newHeight = Math.max(5, (element.height || element.pathNaturalHeight) * scaleY);
240
+ node.scaleX(1);
241
+ node.scaleY(1);
242
+ } else if (element.shapeType === "circle") {
243
+ const currentRadius = (element.width || 100) / 2;
244
+ const newRadius = Math.max(2.5, currentRadius * Math.max(scaleX, scaleY));
245
+ newWidth = newRadius * 2;
246
+ newHeight = newRadius * 2;
247
+ node.scaleX(1);
248
+ node.scaleY(1);
249
+ } else if (element.shapeType === "star") {
250
+ const currentOuterRadius = element.outerRadius || (element.width || 140) / 2;
251
+ const newOuterRadius = Math.max(2.5, currentOuterRadius * Math.max(scaleX, scaleY));
252
+ const newInnerRadius = (element.innerRadius || currentOuterRadius * 0.4) * (newOuterRadius / currentOuterRadius);
253
+ onChange({
254
+ x: node.x(),
255
+ y: node.y(),
256
+ rotation: node.rotation(),
257
+ outerRadius: newOuterRadius,
258
+ innerRadius: newInnerRadius,
259
+ width: newOuterRadius * 2,
260
+ height: newOuterRadius * 2
261
+ });
262
+ node.scaleX(1);
263
+ node.scaleY(1);
264
+ return;
265
+ } else {
266
+ newWidth = Math.max(5, (element.width || 100) * scaleX);
267
+ newHeight = Math.max(5, (element.height || 100) * scaleY);
268
+ node.scaleX(1);
269
+ node.scaleY(1);
270
+ }
271
+ onChange({
272
+ x: node.x(),
273
+ y: node.y(),
274
+ rotation: node.rotation(),
275
+ width: newWidth,
276
+ height: newHeight
277
+ });
278
+ }
279
+ };
280
+ if (!element.visible)
281
+ return null;
282
+ const commonProps = {
283
+ ref: shapeRef,
284
+ x: element.x,
285
+ y: element.y,
286
+ rotation: element.rotation,
287
+ opacity: element.opacity,
288
+ fill: element.fill || "#BFBFBF",
289
+ stroke: element.stroke || "#0C0C0C",
290
+ strokeWidth: element.strokeWidth || 0,
291
+ draggable: !element.locked,
292
+ onClick: onSelect,
293
+ onTap: onSelect,
294
+ onDragEnd: handleDragEnd,
295
+ onTransformEnd: handleTransformEnd
296
+ };
297
+ let shapeNode = null;
298
+ switch (element.shapeType) {
299
+ case "rect":
300
+ shapeNode = /* @__PURE__ */ jsxDEV2(Rect, {
301
+ ...commonProps,
302
+ width: element.width || 100,
303
+ height: element.height || 100,
304
+ cornerRadius: element.cornerRadius || 0
305
+ }, undefined, false, undefined, this);
306
+ break;
307
+ case "octagon":
308
+ shapeNode = /* @__PURE__ */ jsxDEV2(RegularPolygon, {
309
+ ...commonProps,
310
+ sides: 8,
311
+ radius: (element.width || 100) / 2
312
+ }, undefined, false, undefined, this);
313
+ break;
314
+ case "nonagon":
315
+ shapeNode = /* @__PURE__ */ jsxDEV2(RegularPolygon, {
316
+ ...commonProps,
317
+ sides: 9,
318
+ radius: (element.width || 100) / 2
319
+ }, undefined, false, undefined, this);
320
+ break;
321
+ case "decagon":
322
+ shapeNode = /* @__PURE__ */ jsxDEV2(RegularPolygon, {
323
+ ...commonProps,
324
+ sides: 10,
325
+ radius: (element.width || 100) / 2
326
+ }, undefined, false, undefined, this);
327
+ break;
328
+ case "hendecagon":
329
+ shapeNode = /* @__PURE__ */ jsxDEV2(RegularPolygon, {
330
+ ...commonProps,
331
+ sides: 11,
332
+ radius: (element.width || 100) / 2
333
+ }, undefined, false, undefined, this);
334
+ break;
335
+ case "dodecagon":
336
+ shapeNode = /* @__PURE__ */ jsxDEV2(RegularPolygon, {
337
+ ...commonProps,
338
+ sides: 12,
339
+ radius: (element.width || 100) / 2
340
+ }, undefined, false, undefined, this);
341
+ break;
342
+ case "right-triangle":
343
+ shapeNode = /* @__PURE__ */ jsxDEV2(Line, {
344
+ ...commonProps,
345
+ points: [0, 0, 0, element.height || 100, element.width || 100, element.height || 100],
346
+ closed: true
347
+ }, undefined, false, undefined, this);
348
+ break;
349
+ case "isosceles-triangle":
350
+ shapeNode = /* @__PURE__ */ jsxDEV2(Line, {
351
+ ...commonProps,
352
+ points: [(element.width || 2) / 2 || 50, 0, 0, element.height || 100, element.width || 100, element.height || 100],
353
+ closed: true
354
+ }, undefined, false, undefined, this);
355
+ break;
356
+ case "scalene-triangle":
357
+ shapeNode = /* @__PURE__ */ jsxDEV2(Line, {
358
+ ...commonProps,
359
+ points: [element.width || 3 / 3 || 33, 0, 0, element.height || 100, element.width || 100, element.height || 100],
360
+ closed: true
361
+ }, undefined, false, undefined, this);
362
+ break;
363
+ case "pentagon":
364
+ shapeNode = /* @__PURE__ */ jsxDEV2(RegularPolygon, {
365
+ ...commonProps,
366
+ sides: 5,
367
+ radius: (element.width || 100) / 2
368
+ }, undefined, false, undefined, this);
369
+ break;
370
+ case "circle":
371
+ shapeNode = /* @__PURE__ */ jsxDEV2(Circle, {
372
+ ...commonProps,
373
+ radius: (element.width || 100) / 2
374
+ }, undefined, false, undefined, this);
375
+ break;
376
+ case "triangle":
377
+ shapeNode = /* @__PURE__ */ jsxDEV2(RegularPolygon, {
378
+ ...commonProps,
379
+ sides: 3,
380
+ radius: (element.width || 100) / 2
381
+ }, undefined, false, undefined, this);
382
+ break;
383
+ case "star":
384
+ shapeNode = /* @__PURE__ */ jsxDEV2(Star, {
385
+ ...commonProps,
386
+ numPoints: element.numPoints || 5,
387
+ innerRadius: element.innerRadius || (element.outerRadius || 70) * 0.4,
388
+ outerRadius: element.outerRadius || 70
389
+ }, undefined, false, undefined, this);
390
+ break;
391
+ case "polygon":
392
+ shapeNode = /* @__PURE__ */ jsxDEV2(RegularPolygon, {
393
+ ...commonProps,
394
+ sides: element.sides || 6,
395
+ radius: (element.width || 100) / 2
396
+ }, undefined, false, undefined, this);
397
+ break;
398
+ case "line":
399
+ shapeNode = /* @__PURE__ */ jsxDEV2(Line, {
400
+ ...commonProps,
401
+ points: [0, 0, element.width || 100, 0],
402
+ stroke: element.stroke || "#C0BFBF",
403
+ strokeWidth: element.strokeWidth || 4,
404
+ fillEnabled: false
405
+ }, undefined, false, undefined, this);
406
+ break;
407
+ case "arrow":
408
+ shapeNode = /* @__PURE__ */ jsxDEV2(Arrow, {
409
+ ...commonProps,
410
+ points: [0, 0, element.width || 100, 0],
411
+ pointerLength: element.pointerLength || 10,
412
+ pointerWidth: element.pointerWidth || 10,
413
+ fill: element.stroke || "#C0BFBF",
414
+ stroke: element.stroke || "#C0BFBF",
415
+ strokeWidth: element.strokeWidth || 4
416
+ }, undefined, false, undefined, this);
417
+ break;
418
+ case "dashed-line":
419
+ shapeNode = /* @__PURE__ */ jsxDEV2(Line, {
420
+ ...commonProps,
421
+ points: [0, 0, element.width || 100, 0],
422
+ dash: element.dash || [4, 2],
423
+ stroke: element.stroke || "#C0BFBF",
424
+ strokeWidth: element.strokeWidth || 4,
425
+ fillEnabled: false
426
+ }, undefined, false, undefined, this);
427
+ break;
428
+ case "dotted-line":
429
+ shapeNode = /* @__PURE__ */ jsxDEV2(Line, {
430
+ ...commonProps,
431
+ points: [0, 0, element.width || 100, 0],
432
+ dash: element.dash || [
433
+ element.strokeWidth || 2,
434
+ (element.strokeWidth || 2) * 1.5
435
+ ],
436
+ stroke: element.stroke || "#C0BFBF",
437
+ strokeWidth: element.strokeWidth || 4,
438
+ fillEnabled: false
439
+ }, undefined, false, undefined, this);
440
+ break;
441
+ case "custom-path":
442
+ if (element.pathData && element.pathNaturalWidth && element.pathNaturalHeight) {
443
+ const scaleX = (element.width || element.pathNaturalWidth) / element.pathNaturalWidth;
444
+ const scaleY = (element.height || element.pathNaturalHeight) / element.pathNaturalHeight;
445
+ shapeNode = /* @__PURE__ */ jsxDEV2(Path, {
446
+ ...commonProps,
447
+ data: element.pathData,
448
+ scaleX,
449
+ scaleY,
450
+ strokeWidth: element.strokeWidth === undefined && commonProps.fill ? 0 : element.strokeWidth || 0
451
+ }, undefined, false, undefined, this);
452
+ } else {
453
+ console.warn("Custom path element is missing pathData or natural dimensions:", element.id);
454
+ shapeNode = /* @__PURE__ */ jsxDEV2(Rect, {
455
+ ...commonProps,
456
+ width: element.width || 20,
457
+ height: element.height || 20,
458
+ fill: "red"
459
+ }, undefined, false, undefined, this);
460
+ }
461
+ break;
462
+ default:
463
+ console.warn("Unsupported shapeType:", element.shapeType);
464
+ shapeNode = /* @__PURE__ */ jsxDEV2(Rect, {
465
+ ...commonProps,
466
+ width: element.width || 50,
467
+ height: element.height || 50,
468
+ fill: "gray"
469
+ }, undefined, false, undefined, this);
470
+ }
471
+ return /* @__PURE__ */ jsxDEV2(Fragment2, {
472
+ children: [
473
+ shapeNode,
474
+ isSelected && !element.locked && /* @__PURE__ */ jsxDEV2(Transformer2, {
475
+ ref: trRef,
476
+ boundBoxFunc: (oldBox, newBox) => {
477
+ const minSize = 5;
478
+ if (newBox.width < minSize || newBox.height < minSize) {
479
+ if (element.shapeType === "star" || element.shapeType === "circle" || element.shapeType === "triangle" || element.shapeType === "polygon") {
480
+ if (newBox.width < minSize && newBox.height < minSize)
481
+ return oldBox;
482
+ if (newBox.width < minSize)
483
+ return { ...oldBox, height: newBox.height };
484
+ if (newBox.height < minSize)
485
+ return { ...oldBox, width: newBox.width };
486
+ }
487
+ return oldBox;
488
+ }
489
+ return newBox;
490
+ },
491
+ enabledAnchors: element.shapeType === "circle" ? ["top-left", "top-right", "bottom-left", "bottom-right"] : undefined,
492
+ keepRatio: element.shapeType === "circle"
493
+ }, undefined, false, undefined, this)
494
+ ]
495
+ }, undefined, true, undefined, this);
496
+ };
497
+ // src/elements/UrlImageElement.tsx
498
+ import { useState, useRef as useRef3, useEffect as useEffect3, useCallback } from "react";
499
+ import { Image, Transformer as Transformer3, Group } from "react-konva";
500
+ import Konva from "konva";
501
+ import { jsxDEV as jsxDEV3, Fragment as Fragment3 } from "react/jsx-dev-runtime";
502
+ var UrlImageElement = ({ element, isSelected, onSelect, onChange }) => {
503
+ const [image, setImage] = useState(null);
504
+ const imageRef = useRef3(null);
505
+ const groupRef = useRef3(null);
506
+ const trRef = useRef3(null);
507
+ useEffect3(() => {
508
+ if (element.src) {
509
+ const img = new window.Image;
510
+ img.crossOrigin = "anonymous";
511
+ img.src = element.src;
512
+ img.onload = () => {
513
+ setImage(img);
514
+ if (!element.width || !element.height) {
515
+ onChange({
516
+ width: img.width,
517
+ height: img.height
518
+ });
519
+ return;
520
+ }
521
+ const naturalWidth = img.naturalWidth || img.width;
522
+ const naturalHeight = img.naturalHeight || img.height;
523
+ let updatedCrop = {};
524
+ let needsUpdate = false;
525
+ if (element.cropX !== undefined && element.cropX >= 0 && element.cropX <= 1) {
526
+ updatedCrop.cropX = element.cropX * naturalWidth;
527
+ needsUpdate = true;
528
+ }
529
+ if (element.cropY !== undefined && element.cropY >= 0 && element.cropY <= 1) {
530
+ updatedCrop.cropY = element.cropY * naturalHeight;
531
+ needsUpdate = true;
532
+ }
533
+ if (element.cropWidth !== undefined && element.cropWidth >= 0 && element.cropWidth <= 1) {
534
+ updatedCrop.cropWidth = element.cropWidth * naturalWidth;
535
+ needsUpdate = true;
536
+ }
537
+ if (element.cropHeight !== undefined && element.cropHeight >= 0 && element.cropHeight <= 1) {
538
+ updatedCrop.cropHeight = element.cropHeight * naturalHeight;
539
+ needsUpdate = true;
540
+ }
541
+ if (needsUpdate) {
542
+ onChange(updatedCrop);
543
+ }
544
+ };
545
+ img.onerror = (error) => {
546
+ console.error("Failed to load image:", element.src, error);
547
+ setImage(null);
548
+ };
549
+ } else {
550
+ setImage(null);
551
+ }
552
+ }, [element.src]);
553
+ useEffect3(() => {
554
+ if (isSelected && trRef.current && !element.locked) {
555
+ const targetNode = element.mask && element.mask.type ? groupRef.current : imageRef.current;
556
+ if (targetNode) {
557
+ trRef.current.nodes([targetNode]);
558
+ trRef.current.getLayer()?.batchDraw();
559
+ }
560
+ }
561
+ }, [isSelected, element.locked, element.mask]);
562
+ useEffect3(() => {
563
+ const node = imageRef.current;
564
+ if (node && image) {
565
+ const needsCache = element.filters;
566
+ const activeFilters = [];
567
+ if (element.filters?.blur) {
568
+ activeFilters.push(Konva.Filters.Blur);
569
+ node.blurRadius(element.filters.blur);
570
+ }
571
+ if (element.filters?.brightness !== undefined) {
572
+ activeFilters.push(Konva.Filters.Brighten);
573
+ node.brightness(element.filters.brightness);
574
+ }
575
+ if (element.filters?.grayscale)
576
+ activeFilters.push(Konva.Filters.Grayscale);
577
+ if (element.filters?.sepia)
578
+ activeFilters.push(Konva.Filters.Sepia);
579
+ node.filters(activeFilters.length > 0 ? activeFilters : undefined);
580
+ node.cropX(element.cropX || 0);
581
+ node.cropY(element.cropY || 0);
582
+ node.cropWidth(element.cropWidth || element.width);
583
+ node.cropHeight(element.cropHeight || element.height);
584
+ if (needsCache && !element.mask) {
585
+ node.clearCache();
586
+ node.cache();
587
+ } else {
588
+ node.clearCache();
589
+ }
590
+ node.getLayer()?.batchDraw();
591
+ }
592
+ }, [element.filters, element.mask, element.cropX, element.cropY, element.cropWidth, element.cropHeight, element.width, element.height, image]);
593
+ const handleDragEnd = (e) => {
594
+ onChange({
595
+ x: e.target.x(),
596
+ y: e.target.y()
597
+ });
598
+ };
599
+ const handleTransformEnd = () => {
600
+ const node = element.mask && element.mask.type ? groupRef.current : imageRef.current;
601
+ if (node) {
602
+ const scaleX = node.scaleX();
603
+ const scaleY = node.scaleY();
604
+ node.scaleX(1);
605
+ node.scaleY(1);
606
+ onChange({
607
+ x: node.x(),
608
+ y: node.y(),
609
+ rotation: node.rotation(),
610
+ width: Math.max(5, node.width() * scaleX),
611
+ height: Math.max(5, node.height() * scaleY)
612
+ });
613
+ }
614
+ };
615
+ const clipFunc = useCallback((ctx) => {
616
+ if (!element.mask || !element.mask.type || !element.width || !element.height) {
617
+ return;
618
+ }
619
+ const { type } = element.mask;
620
+ const width = element.width;
621
+ const height = element.height;
622
+ ctx.beginPath();
623
+ switch (type) {
624
+ case "circle": {
625
+ const radius = Math.min(width, height) / 2;
626
+ ctx.arc(width / 2, height / 2, radius, 0, Math.PI * 2, false);
627
+ ctx.clip();
628
+ break;
629
+ }
630
+ case "rect":
631
+ case "rectangle":
632
+ ctx.rect(0, 0, element.width, element.height);
633
+ ctx.clip();
634
+ break;
635
+ case "star": {
636
+ const points = element.numPoints || 5;
637
+ const outerRadius = Math.min(element.width, element.height) / 2;
638
+ const innerRadius = element.innerRadius || outerRadius / 2;
639
+ ctx.moveTo(element.width / 2, 0);
640
+ for (let i = 0;i < points * 2; i++) {
641
+ const angle = Math.PI / points * i - Math.PI / 2;
642
+ const radius = i % 2 === 0 ? outerRadius : innerRadius;
643
+ const x = element.width / 2 + radius * Math.cos(angle);
644
+ const y = element.height / 2 + radius * Math.sin(angle);
645
+ ctx.lineTo(x, y);
646
+ }
647
+ ctx.closePath();
648
+ ctx.clip();
649
+ break;
650
+ }
651
+ case "polygon": {
652
+ const sides = element.sides || 6;
653
+ const radius = Math.min(element.width, element.height) / 2;
654
+ ctx.moveTo(element.width / 2 + radius * Math.cos(0), element.height / 2 + radius * Math.sin(0));
655
+ for (let i = 1;i <= sides; i += 1) {
656
+ ctx.lineTo(element.width / 2 + radius * Math.cos(i * 2 * Math.PI / sides), element.height / 2 + radius * Math.sin(i * 2 * Math.PI / sides));
657
+ }
658
+ ctx.closePath();
659
+ ctx.clip();
660
+ break;
661
+ }
662
+ case "pentagon": {
663
+ const sides = 5;
664
+ const radius = Math.min(element.width, element.height) / 2;
665
+ ctx.moveTo(element.width / 2 + radius * Math.cos(0), element.height / 2 + radius * Math.sin(0));
666
+ for (let i = 1;i <= sides; i += 1) {
667
+ ctx.lineTo(element.width / 2 + radius * Math.cos(i * 2 * Math.PI / sides), element.height / 2 + radius * Math.sin(i * 2 * Math.PI / sides));
668
+ }
669
+ ctx.closePath();
670
+ ctx.clip();
671
+ break;
672
+ }
673
+ case "octagon": {
674
+ const sides = 8;
675
+ const radius = Math.min(element.width, element.height) / 2;
676
+ ctx.moveTo(element.width / 2 + radius * Math.cos(0), element.height / 2 + radius * Math.sin(0));
677
+ for (let i = 1;i <= sides; i += 1) {
678
+ ctx.lineTo(element.width / 2 + radius * Math.cos(i * 2 * Math.PI / sides), element.height / 2 + radius * Math.sin(i * 2 * Math.PI / sides));
679
+ }
680
+ ctx.closePath();
681
+ ctx.clip();
682
+ break;
683
+ }
684
+ case "nonagon": {
685
+ const sides = 9;
686
+ const radius = Math.min(element.width, element.height) / 2;
687
+ ctx.moveTo(element.width / 2 + radius * Math.cos(0), element.height / 2 + radius * Math.sin(0));
688
+ for (let i = 1;i <= sides; i += 1) {
689
+ ctx.lineTo(element.width / 2 + radius * Math.cos(i * 2 * Math.PI / sides), element.height / 2 + radius * Math.sin(i * 2 * Math.PI / sides));
690
+ }
691
+ ctx.closePath();
692
+ ctx.clip();
693
+ break;
694
+ }
695
+ case "decagon": {
696
+ const sides = 10;
697
+ const radius = Math.min(element.width, element.height) / 2;
698
+ ctx.moveTo(element.width / 2 + radius * Math.cos(0), element.height / 2 + radius * Math.sin(0));
699
+ for (let i = 1;i <= sides; i += 1) {
700
+ ctx.lineTo(element.width / 2 + radius * Math.cos(i * 2 * Math.PI / sides), element.height / 2 + radius * Math.sin(i * 2 * Math.PI / sides));
701
+ }
702
+ ctx.closePath();
703
+ ctx.clip();
704
+ break;
705
+ }
706
+ case "hendecagon": {
707
+ const sides = 11;
708
+ const radius = Math.min(element.width, element.height) / 2;
709
+ ctx.moveTo(element.width / 2 + radius * Math.cos(0), element.height / 2 + radius * Math.sin(0));
710
+ for (let i = 1;i <= sides; i += 1) {
711
+ ctx.lineTo(element.width / 2 + radius * Math.cos(i * 2 * Math.PI / sides), element.height / 2 + radius * Math.sin(i * 2 * Math.PI / sides));
712
+ }
713
+ ctx.closePath();
714
+ ctx.clip();
715
+ break;
716
+ }
717
+ case "dodecagon": {
718
+ const sides = 12;
719
+ const radius = Math.min(element.width, element.height) / 2;
720
+ ctx.moveTo(element.width / 2 + radius * Math.cos(0), element.height / 2 + radius * Math.sin(0));
721
+ for (let i = 1;i <= sides; i += 1) {
722
+ ctx.lineTo(element.width / 2 + radius * Math.cos(i * 2 * Math.PI / sides), element.height / 2 + radius * Math.sin(i * 2 * Math.PI / sides));
723
+ }
724
+ ctx.closePath();
725
+ ctx.clip();
726
+ break;
727
+ }
728
+ case "triangle":
729
+ ctx.moveTo(element.width / 2, 0);
730
+ ctx.lineTo(element.width, element.height);
731
+ ctx.lineTo(0, element.height);
732
+ ctx.closePath();
733
+ ctx.clip();
734
+ break;
735
+ case "right-triangle":
736
+ ctx.moveTo(0, 0);
737
+ ctx.lineTo(0, element.height);
738
+ ctx.lineTo(element.width, element.height);
739
+ ctx.closePath();
740
+ ctx.clip();
741
+ break;
742
+ case "isosceles-triangle":
743
+ ctx.moveTo(element.width / 2, 0);
744
+ ctx.lineTo(element.width, element.height);
745
+ ctx.lineTo(0, element.height);
746
+ ctx.closePath();
747
+ ctx.clip();
748
+ break;
749
+ case "scalene-triangle":
750
+ ctx.moveTo(element.width / 3, 0);
751
+ ctx.lineTo(element.width, element.height);
752
+ ctx.lineTo(0, element.height);
753
+ ctx.closePath();
754
+ ctx.clip();
755
+ break;
756
+ default:
757
+ break;
758
+ }
759
+ }, [element.mask, element.width, element.height]);
760
+ if (!image || !element.visible)
761
+ return null;
762
+ const imageComponent = /* @__PURE__ */ jsxDEV3(Image, {
763
+ ref: imageRef,
764
+ image,
765
+ x: 0,
766
+ y: 0,
767
+ width: element.width || 0,
768
+ height: element.height || 0,
769
+ rotation: 0,
770
+ opacity: element.opacity,
771
+ draggable: false,
772
+ cornerRadius: element.cornerRadius || 0,
773
+ cropX: element.cropX || 0,
774
+ cropY: element.cropY || 0,
775
+ cropWidth: element.cropWidth || element.width,
776
+ cropHeight: element.cropHeight || element.height
777
+ }, undefined, false, undefined, this);
778
+ if (element.mask && element.mask.type) {
779
+ return /* @__PURE__ */ jsxDEV3(Fragment3, {
780
+ children: [
781
+ /* @__PURE__ */ jsxDEV3(Group, {
782
+ ref: groupRef,
783
+ x: element.x,
784
+ y: element.y,
785
+ rotation: element.rotation,
786
+ draggable: !element.locked,
787
+ onClick: onSelect,
788
+ onTap: onSelect,
789
+ onDragEnd: handleDragEnd,
790
+ onTransformEnd: handleTransformEnd,
791
+ clipFunc,
792
+ children: imageComponent
793
+ }, undefined, false, undefined, this),
794
+ isSelected && !element.locked && /* @__PURE__ */ jsxDEV3(Transformer3, {
795
+ ref: trRef,
796
+ boundBoxFunc: (oldBox, newBox) => {
797
+ if (newBox.width < 5 || newBox.height < 5) {
798
+ return oldBox;
799
+ }
800
+ return newBox;
801
+ }
802
+ }, undefined, false, undefined, this)
803
+ ]
804
+ }, undefined, true, undefined, this);
805
+ }
806
+ return /* @__PURE__ */ jsxDEV3(Fragment3, {
807
+ children: [
808
+ /* @__PURE__ */ jsxDEV3(Image, {
809
+ ref: imageRef,
810
+ image,
811
+ x: element.x,
812
+ y: element.y,
813
+ width: element.width || 0,
814
+ height: element.height || 0,
815
+ rotation: element.rotation,
816
+ opacity: element.opacity,
817
+ draggable: !element.locked,
818
+ onClick: onSelect,
819
+ onTap: onSelect,
820
+ onDragEnd: handleDragEnd,
821
+ onTransformEnd: handleTransformEnd,
822
+ cornerRadius: element.cornerRadius || 0,
823
+ cropX: element.cropX || 0,
824
+ cropY: element.cropY || 0,
825
+ cropWidth: element.cropWidth || element.width,
826
+ cropHeight: element.cropHeight || element.height
827
+ }, undefined, false, undefined, this),
828
+ isSelected && !element.locked && /* @__PURE__ */ jsxDEV3(Transformer3, {
829
+ ref: trRef,
830
+ boundBoxFunc: (oldBox, newBox) => {
831
+ if (newBox.width < 5 || newBox.height < 5) {
832
+ return oldBox;
833
+ }
834
+ return newBox;
835
+ }
836
+ }, undefined, false, undefined, this)
837
+ ]
838
+ }, undefined, true, undefined, this);
839
+ };
840
+ // src/constants/CanvasPresets.ts
841
+ var CANVAS_PRESETS = [
842
+ { name: "Instagram Post", width: 1080, height: 1080 },
843
+ { name: "Instagram Story", width: 1080, height: 1920 },
844
+ { name: "Facebook Post", width: 1200, height: 630 },
845
+ { name: "Twitter Post", width: 1024, height: 512 },
846
+ { name: "A4", width: 794, height: 1123 },
847
+ { name: "Letter", width: 816, height: 1056 },
848
+ { name: "YouTube Thumbnail", width: 1280, height: 720 },
849
+ { name: "Custom", width: 800, height: 600 }
850
+ ];
851
+ // src/constants/DefaultColors.ts
852
+ var DEFAULT_COLORS = [
853
+ "#000000",
854
+ "#FFFFFF",
855
+ "#FF0000",
856
+ "#00FF00",
857
+ "#0000FF",
858
+ "#FFFF00",
859
+ "#FF00FF",
860
+ "#00FFFF",
861
+ "#FFA500",
862
+ "#800080",
863
+ "#FFC0CB",
864
+ "#A52A2A",
865
+ "#808080",
866
+ "#C0C0C0",
867
+ "#FFD700"
868
+ ];
869
+ // src/constants/FontFamilies.ts
870
+ var FONT_FAMILIES = [
871
+ "Arial",
872
+ "Helvetica",
873
+ "Times New Roman",
874
+ "Georgia",
875
+ "Verdana",
876
+ "Courier New",
877
+ "Comic Sans MS",
878
+ "Impact",
879
+ "Trebuchet MS",
880
+ "Palatino",
881
+ "Garamond",
882
+ "Bookman",
883
+ "Tahoma"
884
+ ];
885
+ // src/config.ts
886
+ var _unsplashAccessKey = typeof process !== "undefined" && process.env?.UNSPLASH_ACCESS_KEY ? process.env.UNSPLASH_ACCESS_KEY : undefined;
887
+ function setUnsplashAccessKey(key) {
888
+ _unsplashAccessKey = key;
889
+ }
890
+ function getUnsplashAccessKey() {
891
+ return _unsplashAccessKey;
892
+ }
893
+
894
+ // src/components/header/AppName.tsx
895
+ import { useTranslation } from "react-i18next";
896
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
897
+ var AppName = () => {
898
+ const { t } = useTranslation();
899
+ return /* @__PURE__ */ jsxDEV4("h1", {
900
+ className: "text-white font-bold text-lg",
901
+ children: t("Editor.app_name")
902
+ }, undefined, false, undefined, this);
903
+ };
904
+ var AppName_default = AppName;
905
+
906
+ // src/components/header/HistoryControls.tsx
907
+ import { useTranslation as useTranslation2 } from "react-i18next";
908
+ import { Undo, Redo } from "lucide-react";
909
+ import { jsxDEV as jsxDEV5, Fragment as Fragment4 } from "react/jsx-dev-runtime";
910
+ var HistoryControls = ({
911
+ historyIndex,
912
+ historyLength,
913
+ onUndo,
914
+ onRedo
915
+ }) => {
916
+ const { t } = useTranslation2();
917
+ return /* @__PURE__ */ jsxDEV5(Fragment4, {
918
+ children: [
919
+ /* @__PURE__ */ jsxDEV5("button", {
920
+ onClick: onUndo,
921
+ disabled: historyIndex <= 0,
922
+ className: "p-2 text-gray-400 hover:text-white disabled:text-gray-600",
923
+ title: t("Editor.undo"),
924
+ children: /* @__PURE__ */ jsxDEV5(Undo, {
925
+ className: "w-4 h-4 "
926
+ }, undefined, false, undefined, this)
927
+ }, undefined, false, undefined, this),
928
+ /* @__PURE__ */ jsxDEV5("button", {
929
+ onClick: onRedo,
930
+ disabled: historyIndex >= historyLength - 1,
931
+ className: "p-2 text-gray-400 hover:text-white disabled:text-gray-600",
932
+ title: t("Editor.redo"),
933
+ children: /* @__PURE__ */ jsxDEV5(Redo, {
934
+ className: "w-4 h-4"
935
+ }, undefined, false, undefined, this)
936
+ }, undefined, false, undefined, this)
937
+ ]
938
+ }, undefined, true, undefined, this);
939
+ };
940
+ var HistoryControls_default = HistoryControls;
941
+
942
+ // src/components/header/ZoomControls.tsx
943
+ import { useTranslation as useTranslation3 } from "react-i18next";
944
+ import { ZoomIn, ZoomOut, Maximize2 } from "lucide-react";
945
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
946
+ var ZoomControls = ({
947
+ zoom,
948
+ onZoomIn,
949
+ onZoomOut,
950
+ onZoomReset
951
+ }) => {
952
+ const { t } = useTranslation3();
953
+ return /* @__PURE__ */ jsxDEV6("div", {
954
+ className: "flex items-center space-x-1 bg-gray-700 rounded px-2 py-1",
955
+ children: [
956
+ /* @__PURE__ */ jsxDEV6("button", {
957
+ onClick: onZoomOut,
958
+ className: "text-gray-400 hover:text-white",
959
+ title: t("Editor.zoom_out"),
960
+ children: /* @__PURE__ */ jsxDEV6(ZoomOut, {
961
+ className: "w-4 h-4"
962
+ }, undefined, false, undefined, this)
963
+ }, undefined, false, undefined, this),
964
+ /* @__PURE__ */ jsxDEV6("span", {
965
+ className: "text-white text-sm mx-2 select-none",
966
+ children: [
967
+ Math.round(zoom * 100),
968
+ "%"
969
+ ]
970
+ }, undefined, true, undefined, this),
971
+ /* @__PURE__ */ jsxDEV6("button", {
972
+ onClick: onZoomIn,
973
+ className: "text-gray-400 hover:text-white",
974
+ title: t("Editor.zoom_in"),
975
+ children: /* @__PURE__ */ jsxDEV6(ZoomIn, {
976
+ className: "w-4 h-4"
977
+ }, undefined, false, undefined, this)
978
+ }, undefined, false, undefined, this),
979
+ /* @__PURE__ */ jsxDEV6("button", {
980
+ onClick: onZoomReset,
981
+ className: "text-gray-400 hover:text-white ml-2",
982
+ title: t("Editor.zoom_to_fit"),
983
+ children: /* @__PURE__ */ jsxDEV6(Maximize2, {
984
+ className: "w-4 h-4"
985
+ }, undefined, false, undefined, this)
986
+ }, undefined, false, undefined, this)
987
+ ]
988
+ }, undefined, true, undefined, this);
989
+ };
990
+ var ZoomControls_default = ZoomControls;
991
+
992
+ // src/components/Header.tsx
993
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
994
+ var Header = ({
995
+ name,
996
+ zoom,
997
+ historyIndex,
998
+ historyLength,
999
+ onUndo,
1000
+ onRedo,
1001
+ onZoomIn,
1002
+ onZoomOut,
1003
+ onZoomReset
1004
+ }) => {
1005
+ return /* @__PURE__ */ jsxDEV7("div", {
1006
+ className: "bg-gray-800 border-b border-gray-700 px-4 py-2 flex items-center justify-between",
1007
+ children: [
1008
+ /* @__PURE__ */ jsxDEV7("div", {
1009
+ className: "flex items-center space-x-4",
1010
+ children: /* @__PURE__ */ jsxDEV7(AppName_default, {}, undefined, false, undefined, this)
1011
+ }, undefined, false, undefined, this),
1012
+ name && /* @__PURE__ */ jsxDEV7("div", {
1013
+ className: "flex-1 text-center text-white text-lg font-semibold",
1014
+ children: name
1015
+ }, undefined, false, undefined, this),
1016
+ /* @__PURE__ */ jsxDEV7("div", {
1017
+ className: "flex items-center space-x-2",
1018
+ children: [
1019
+ /* @__PURE__ */ jsxDEV7(HistoryControls_default, {
1020
+ historyIndex,
1021
+ historyLength,
1022
+ onUndo,
1023
+ onRedo
1024
+ }, undefined, false, undefined, this),
1025
+ /* @__PURE__ */ jsxDEV7("div", {
1026
+ className: "border-l border-gray-600 mx-2 h-6"
1027
+ }, undefined, false, undefined, this),
1028
+ /* @__PURE__ */ jsxDEV7(ZoomControls_default, {
1029
+ zoom,
1030
+ onZoomIn,
1031
+ onZoomOut,
1032
+ onZoomReset
1033
+ }, undefined, false, undefined, this)
1034
+ ]
1035
+ }, undefined, true, undefined, this)
1036
+ ]
1037
+ }, undefined, true, undefined, this);
1038
+ };
1039
+ var Header_default = Header;
1040
+
1041
+ // src/components/LeftMenu.tsx
1042
+ import { Type, Square, Image as ImageIcon, Palette, Download, Layers, Variable } from "lucide-react";
1043
+ import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
1044
+ var LeftMenu = ({
1045
+ tool,
1046
+ activePanel,
1047
+ onSetTool,
1048
+ onSetActivePanel,
1049
+ config
1050
+ }) => {
1051
+ const menuItems = [
1052
+ {
1053
+ id: "text",
1054
+ tooltip: "Text",
1055
+ icon: /* @__PURE__ */ jsxDEV8(Type, {
1056
+ className: "w-5 h-5"
1057
+ }, undefined, false, undefined, this),
1058
+ actionType: "setToolAndPanel",
1059
+ toolValue: "text",
1060
+ panelValue: "text"
1061
+ },
1062
+ {
1063
+ id: "elements",
1064
+ tooltip: "Elements",
1065
+ icon: /* @__PURE__ */ jsxDEV8(Square, {
1066
+ className: "w-5 h-5"
1067
+ }, undefined, false, undefined, this),
1068
+ actionType: "setToolAndPanel",
1069
+ panelValue: "elements",
1070
+ toolValue: "shape"
1071
+ },
1072
+ {
1073
+ id: "image",
1074
+ tooltip: "Images",
1075
+ icon: /* @__PURE__ */ jsxDEV8(ImageIcon, {
1076
+ className: "w-5 h-5"
1077
+ }, undefined, false, undefined, this),
1078
+ actionType: "setPanel",
1079
+ panelValue: "image"
1080
+ },
1081
+ {
1082
+ id: "design",
1083
+ tooltip: "Design",
1084
+ icon: /* @__PURE__ */ jsxDEV8(Palette, {
1085
+ className: "w-5 h-5"
1086
+ }, undefined, false, undefined, this),
1087
+ actionType: "setPanel",
1088
+ panelValue: "design"
1089
+ }
1090
+ ];
1091
+ if (config?.export) {
1092
+ menuItems.push({
1093
+ id: "export",
1094
+ tooltip: "Export",
1095
+ icon: /* @__PURE__ */ jsxDEV8(Download, {
1096
+ className: "w-5 h-5"
1097
+ }, undefined, false, undefined, this),
1098
+ actionType: "setPanel",
1099
+ panelValue: "export"
1100
+ });
1101
+ }
1102
+ menuItems.push({
1103
+ id: "background",
1104
+ tooltip: "Background",
1105
+ icon: /* @__PURE__ */ jsxDEV8(Layers, {
1106
+ className: "w-5 h-5"
1107
+ }, undefined, false, undefined, this),
1108
+ actionType: "setPanel",
1109
+ panelValue: "background"
1110
+ });
1111
+ if (config?.variables) {
1112
+ menuItems.push({
1113
+ id: "variables",
1114
+ tooltip: "Variables",
1115
+ icon: /* @__PURE__ */ jsxDEV8(Variable, {
1116
+ className: "w-5 h-5"
1117
+ }, undefined, false, undefined, this),
1118
+ actionType: "setPanel",
1119
+ panelValue: "variables"
1120
+ });
1121
+ }
1122
+ const handleItemClick = (item) => {
1123
+ if (item.actionType === "setTool" && item.toolValue) {
1124
+ onSetTool(item.toolValue);
1125
+ } else if (item.actionType === "setPanel" && item.panelValue) {
1126
+ onSetActivePanel(item.panelValue);
1127
+ if (item.panelValue === "elements" && !item.toolValue)
1128
+ onSetTool("select");
1129
+ if (item.panelValue === "image" && !item.toolValue)
1130
+ onSetTool("select");
1131
+ } else if (item.actionType === "setToolAndPanel" && item.toolValue && item.panelValue) {
1132
+ onSetTool(item.toolValue);
1133
+ onSetActivePanel(item.panelValue);
1134
+ }
1135
+ };
1136
+ const getButtonClass = (item) => {
1137
+ let isActive = false;
1138
+ if (item.actionType === "setTool") {
1139
+ isActive = tool === item.toolValue;
1140
+ } else if (item.actionType === "setPanel") {
1141
+ isActive = activePanel === item.panelValue;
1142
+ } else if (item.actionType === "setToolAndPanel") {
1143
+ isActive = activePanel === item.panelValue || tool === item.toolValue;
1144
+ }
1145
+ return `w-full p-3 flex justify-center transition-colors duration-200 ${isActive ? "bg-gray-700 text-blue-400" : "text-gray-400 hover:text-white"}`;
1146
+ };
1147
+ return /* @__PURE__ */ jsxDEV8("div", {
1148
+ className: "w-16 bg-gray-900 border-r border-gray-700 py-4",
1149
+ children: /* @__PURE__ */ jsxDEV8("div", {
1150
+ className: "space-y-2",
1151
+ children: menuItems.map((item) => /* @__PURE__ */ jsxDEV8("button", {
1152
+ onClick: () => handleItemClick(item),
1153
+ className: getButtonClass(item),
1154
+ title: item.tooltip,
1155
+ children: item.icon
1156
+ }, item.id, false, undefined, this))
1157
+ }, undefined, false, undefined, this)
1158
+ }, undefined, false, undefined, this);
1159
+ };
1160
+ var LeftMenu_default = LeftMenu;
1161
+
1162
+ // src/panels/ElementPanel.tsx
1163
+ import { Square as Square2, Circle as CircleIcon, Star as StarIcon } from "lucide-react";
1164
+ import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
1165
+ var ElementPanel = ({ onAddShape }) => {
1166
+ return /* @__PURE__ */ jsxDEV9("div", {
1167
+ children: [
1168
+ /* @__PURE__ */ jsxDEV9("h3", {
1169
+ className: "text-white font-semibold mb-4",
1170
+ children: "Elements"
1171
+ }, undefined, false, undefined, this),
1172
+ /* @__PURE__ */ jsxDEV9("div", {
1173
+ className: "grid grid-cols-3 gap-3",
1174
+ children: [
1175
+ /* @__PURE__ */ jsxDEV9("button", {
1176
+ onClick: () => onAddShape("rect"),
1177
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1178
+ title: "Rectangle",
1179
+ children: /* @__PURE__ */ jsxDEV9(Square2, {
1180
+ className: "w-8 h-8 text-gray-300"
1181
+ }, undefined, false, undefined, this)
1182
+ }, undefined, false, undefined, this),
1183
+ /* @__PURE__ */ jsxDEV9("button", {
1184
+ onClick: () => onAddShape("circle"),
1185
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1186
+ title: "Circle",
1187
+ children: /* @__PURE__ */ jsxDEV9(CircleIcon, {
1188
+ className: "w-8 h-8 text-gray-300"
1189
+ }, undefined, false, undefined, this)
1190
+ }, undefined, false, undefined, this),
1191
+ /* @__PURE__ */ jsxDEV9("button", {
1192
+ onClick: () => onAddShape("triangle"),
1193
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1194
+ title: "Triangle",
1195
+ children: /* @__PURE__ */ jsxDEV9("div", {
1196
+ className: `w-0 h-0
1197
+ border-l-[16px] border-l-transparent
1198
+ border-b-[28px] border-b-gray-300
1199
+ border-r-[16px] border-r-transparent`
1200
+ }, undefined, false, undefined, this)
1201
+ }, undefined, false, undefined, this),
1202
+ /* @__PURE__ */ jsxDEV9("button", {
1203
+ onClick: () => onAddShape("star"),
1204
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1205
+ title: "Star",
1206
+ children: /* @__PURE__ */ jsxDEV9(StarIcon, {
1207
+ className: "w-8 h-8 text-gray-300"
1208
+ }, undefined, false, undefined, this)
1209
+ }, undefined, false, undefined, this),
1210
+ /* @__PURE__ */ jsxDEV9("button", {
1211
+ onClick: () => onAddShape("polygon"),
1212
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1213
+ title: "Polygon",
1214
+ children: /* @__PURE__ */ jsxDEV9("div", {
1215
+ className: "w-8 h-8 text-gray-300 flex items-center justify-center text-2xl",
1216
+ children: "⬡"
1217
+ }, undefined, false, undefined, this)
1218
+ }, undefined, false, undefined, this),
1219
+ /* @__PURE__ */ jsxDEV9("button", {
1220
+ onClick: () => onAddShape("pentagon"),
1221
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1222
+ title: "Pentagon",
1223
+ children: /* @__PURE__ */ jsxDEV9("div", {
1224
+ className: "w-8 h-8 text-gray-300 flex items-center justify-center text-2xl",
1225
+ children: "⬠"
1226
+ }, undefined, false, undefined, this)
1227
+ }, undefined, false, undefined, this),
1228
+ /* @__PURE__ */ jsxDEV9("button", {
1229
+ onClick: () => onAddShape("octagon"),
1230
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1231
+ title: "Octagon",
1232
+ children: /* @__PURE__ */ jsxDEV9("div", {
1233
+ className: "w-8 h-8 text-gray-300 flex items-center justify-center text-2xl",
1234
+ children: "⬡"
1235
+ }, undefined, false, undefined, this)
1236
+ }, undefined, false, undefined, this),
1237
+ /* @__PURE__ */ jsxDEV9("button", {
1238
+ onClick: () => onAddShape("nonagon"),
1239
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1240
+ title: "Nonagon",
1241
+ children: /* @__PURE__ */ jsxDEV9("div", {
1242
+ className: "w-8 h-8 text-gray-300 flex items-center justify-center text-2xl",
1243
+ children: "⬡"
1244
+ }, undefined, false, undefined, this)
1245
+ }, undefined, false, undefined, this),
1246
+ /* @__PURE__ */ jsxDEV9("button", {
1247
+ onClick: () => onAddShape("decagon"),
1248
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1249
+ title: "Decagon",
1250
+ children: /* @__PURE__ */ jsxDEV9("div", {
1251
+ className: "w-8 h-8 text-gray-300 flex items-center justify-center text-2xl",
1252
+ children: "⬡"
1253
+ }, undefined, false, undefined, this)
1254
+ }, undefined, false, undefined, this),
1255
+ /* @__PURE__ */ jsxDEV9("button", {
1256
+ onClick: () => onAddShape("hendecagon"),
1257
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1258
+ title: "Hendecagon",
1259
+ children: /* @__PURE__ */ jsxDEV9("div", {
1260
+ className: "w-8 h-8 text-gray-300 flex items-center justify-center text-2xl",
1261
+ children: "⬡"
1262
+ }, undefined, false, undefined, this)
1263
+ }, undefined, false, undefined, this),
1264
+ /* @__PURE__ */ jsxDEV9("button", {
1265
+ onClick: () => onAddShape("dodecagon"),
1266
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1267
+ title: "Dodecagon",
1268
+ children: /* @__PURE__ */ jsxDEV9("div", {
1269
+ className: "w-8 h-8 text-gray-300 flex items-center justify-center text-2xl",
1270
+ children: "⬡"
1271
+ }, undefined, false, undefined, this)
1272
+ }, undefined, false, undefined, this),
1273
+ /* @__PURE__ */ jsxDEV9("button", {
1274
+ onClick: () => onAddShape("right-triangle"),
1275
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1276
+ title: "Right Triangle",
1277
+ children: /* @__PURE__ */ jsxDEV9("div", {
1278
+ className: "w-8 h-8 text-gray-300 flex items-center justify-center text-2xl",
1279
+ children: "⊿"
1280
+ }, undefined, false, undefined, this)
1281
+ }, undefined, false, undefined, this),
1282
+ /* @__PURE__ */ jsxDEV9("button", {
1283
+ onClick: () => onAddShape("isosceles-triangle"),
1284
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1285
+ title: "Isosceles Triangle",
1286
+ children: /* @__PURE__ */ jsxDEV9("div", {
1287
+ className: "w-8 h-8 text-gray-300 flex items-center justify-center text-2xl",
1288
+ children: "△"
1289
+ }, undefined, false, undefined, this)
1290
+ }, undefined, false, undefined, this),
1291
+ /* @__PURE__ */ jsxDEV9("button", {
1292
+ onClick: () => onAddShape("scalene-triangle"),
1293
+ className: "aspect-square bg-gray-700 hover:bg-gray-600 rounded flex items-center justify-center",
1294
+ title: "Scalene Triangle",
1295
+ children: /* @__PURE__ */ jsxDEV9("div", {
1296
+ className: "w-8 h-8 text-gray-300 flex items-center justify-center text-2xl",
1297
+ children: "△"
1298
+ }, undefined, false, undefined, this)
1299
+ }, undefined, false, undefined, this)
1300
+ ]
1301
+ }, undefined, true, undefined, this)
1302
+ ]
1303
+ }, undefined, true, undefined, this);
1304
+ };
1305
+ var ElementPanel_default = ElementPanel;
1306
+
1307
+ // src/panels/TextPanel.tsx
1308
+ import {
1309
+ Bold,
1310
+ Italic,
1311
+ Underline,
1312
+ AlignLeft,
1313
+ AlignCenter,
1314
+ AlignRight,
1315
+ Strikethrough
1316
+ } from "lucide-react";
1317
+ import { jsxDEV as jsxDEV10 } from "react/jsx-dev-runtime";
1318
+ var TextPanel = ({
1319
+ selectedElement,
1320
+ updateElement,
1321
+ setTool,
1322
+ onAddText
1323
+ }) => {
1324
+ const isTextElementSelected = selectedElement?.type === "text";
1325
+ return /* @__PURE__ */ jsxDEV10("div", {
1326
+ children: [
1327
+ /* @__PURE__ */ jsxDEV10("h3", {
1328
+ className: "text-white font-semibold mb-4",
1329
+ children: "Text"
1330
+ }, undefined, false, undefined, this),
1331
+ /* @__PURE__ */ jsxDEV10("button", {
1332
+ onClick: onAddText,
1333
+ className: "w-full p-3 bg-blue-600 text-white rounded hover:bg-blue-700 mb-4",
1334
+ children: "Add Text"
1335
+ }, undefined, false, undefined, this),
1336
+ !isTextElementSelected && /* @__PURE__ */ jsxDEV10("p", {
1337
+ className: "text-gray-400",
1338
+ children: "Select a text element to edit its properties."
1339
+ }, undefined, false, undefined, this),
1340
+ isTextElementSelected && selectedElement && /* @__PURE__ */ jsxDEV10("div", {
1341
+ className: "space-y-4",
1342
+ children: [
1343
+ /* @__PURE__ */ jsxDEV10("div", {
1344
+ children: [
1345
+ /* @__PURE__ */ jsxDEV10("label", {
1346
+ className: "text-gray-400 text-sm",
1347
+ children: "Font Family"
1348
+ }, undefined, false, undefined, this),
1349
+ /* @__PURE__ */ jsxDEV10("select", {
1350
+ value: selectedElement.fontFamily || "Arial",
1351
+ onChange: (e) => updateElement(selectedElement.id, {
1352
+ fontFamily: e.target.value
1353
+ }),
1354
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2",
1355
+ children: FONT_FAMILIES.map((font) => /* @__PURE__ */ jsxDEV10("option", {
1356
+ value: font,
1357
+ children: font
1358
+ }, font, false, undefined, this))
1359
+ }, undefined, false, undefined, this)
1360
+ ]
1361
+ }, undefined, true, undefined, this),
1362
+ /* @__PURE__ */ jsxDEV10("div", {
1363
+ children: [
1364
+ /* @__PURE__ */ jsxDEV10("label", {
1365
+ className: "text-gray-400 text-sm",
1366
+ children: "Font Size"
1367
+ }, undefined, false, undefined, this),
1368
+ /* @__PURE__ */ jsxDEV10("input", {
1369
+ type: "number",
1370
+ value: selectedElement.fontSize || 16,
1371
+ onChange: (e) => updateElement(selectedElement.id, {
1372
+ fontSize: parseInt(e.target.value) || 16
1373
+ }),
1374
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
1375
+ }, undefined, false, undefined, this)
1376
+ ]
1377
+ }, undefined, true, undefined, this),
1378
+ /* @__PURE__ */ jsxDEV10("div", {
1379
+ children: [
1380
+ /* @__PURE__ */ jsxDEV10("label", {
1381
+ className: "text-gray-400 text-sm",
1382
+ children: "Text Color"
1383
+ }, undefined, false, undefined, this),
1384
+ /* @__PURE__ */ jsxDEV10("div", {
1385
+ className: "flex items-center mt-1",
1386
+ children: [
1387
+ /* @__PURE__ */ jsxDEV10("input", {
1388
+ type: "color",
1389
+ value: selectedElement.fill || "#000000",
1390
+ onChange: (e) => updateElement(selectedElement.id, {
1391
+ fill: e.target.value
1392
+ }),
1393
+ className: "w-12 h-12 border border-gray-600 rounded cursor-pointer"
1394
+ }, undefined, false, undefined, this),
1395
+ /* @__PURE__ */ jsxDEV10("input", {
1396
+ type: "text",
1397
+ value: selectedElement.fill || "#000000",
1398
+ onChange: (e) => updateElement(selectedElement.id, {
1399
+ fill: e.target.value
1400
+ }),
1401
+ className: "flex-1 ml-2 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
1402
+ }, undefined, false, undefined, this)
1403
+ ]
1404
+ }, undefined, true, undefined, this)
1405
+ ]
1406
+ }, undefined, true, undefined, this),
1407
+ /* @__PURE__ */ jsxDEV10("div", {
1408
+ children: [
1409
+ /* @__PURE__ */ jsxDEV10("label", {
1410
+ className: "text-gray-400 text-sm",
1411
+ children: "Style"
1412
+ }, undefined, false, undefined, this),
1413
+ /* @__PURE__ */ jsxDEV10("div", {
1414
+ className: "flex space-x-2 mt-1",
1415
+ children: [
1416
+ /* @__PURE__ */ jsxDEV10("button", {
1417
+ onClick: () => updateElement(selectedElement.id, {
1418
+ fontStyle: selectedElement.fontStyle?.includes("bold") ? selectedElement.fontStyle.replace("bold", "").trim() : `${selectedElement.fontStyle || ""} bold`.trim()
1419
+ }),
1420
+ className: `p-2 rounded ${selectedElement.fontStyle?.includes("bold") ? "bg-gray-600" : "bg-gray-700"} hover:bg-gray-600`,
1421
+ children: /* @__PURE__ */ jsxDEV10(Bold, {
1422
+ className: "w-4 h-4 text-white"
1423
+ }, undefined, false, undefined, this)
1424
+ }, undefined, false, undefined, this),
1425
+ /* @__PURE__ */ jsxDEV10("button", {
1426
+ onClick: () => updateElement(selectedElement.id, {
1427
+ fontStyle: selectedElement.fontStyle?.includes("italic") ? selectedElement.fontStyle.replace("italic", "").trim() : `${selectedElement.fontStyle || ""} italic`.trim()
1428
+ }),
1429
+ className: `p-2 rounded ${selectedElement.fontStyle?.includes("italic") ? "bg-gray-600" : "bg-gray-700"} hover:bg-gray-600`,
1430
+ children: /* @__PURE__ */ jsxDEV10(Italic, {
1431
+ className: "w-4 h-4 text-white"
1432
+ }, undefined, false, undefined, this)
1433
+ }, undefined, false, undefined, this),
1434
+ /* @__PURE__ */ jsxDEV10("button", {
1435
+ onClick: () => updateElement(selectedElement.id, {
1436
+ textDecoration: selectedElement.textDecoration === "underline" ? "" : "underline"
1437
+ }),
1438
+ className: `p-2 rounded ${selectedElement.textDecoration === "underline" ? "bg-gray-600" : "bg-gray-700"} hover:bg-gray-600`,
1439
+ children: /* @__PURE__ */ jsxDEV10(Underline, {
1440
+ className: "w-4 h-4 text-white"
1441
+ }, undefined, false, undefined, this)
1442
+ }, undefined, false, undefined, this),
1443
+ /* @__PURE__ */ jsxDEV10("button", {
1444
+ onClick: () => updateElement(selectedElement.id, {
1445
+ textDecoration: selectedElement.textDecoration === "strikethrough" ? "" : "strikethrough"
1446
+ }),
1447
+ className: `p-2 rounded ${selectedElement.textDecoration === "strikethrough" ? "bg-gray-600" : "bg-gray-700"} hover:bg-gray-600`,
1448
+ children: /* @__PURE__ */ jsxDEV10(Strikethrough, {
1449
+ className: "w-4 h-4 text-white"
1450
+ }, undefined, false, undefined, this)
1451
+ }, undefined, false, undefined, this)
1452
+ ]
1453
+ }, undefined, true, undefined, this)
1454
+ ]
1455
+ }, undefined, true, undefined, this),
1456
+ /* @__PURE__ */ jsxDEV10("div", {
1457
+ children: [
1458
+ /* @__PURE__ */ jsxDEV10("label", {
1459
+ className: "text-gray-400 text-sm",
1460
+ children: "Alignment"
1461
+ }, undefined, false, undefined, this),
1462
+ /* @__PURE__ */ jsxDEV10("div", {
1463
+ className: "flex space-x-2 mt-1",
1464
+ children: [
1465
+ /* @__PURE__ */ jsxDEV10("button", {
1466
+ onClick: () => updateElement(selectedElement.id, { align: "left" }),
1467
+ className: `p-2 rounded ${selectedElement.align === "left" ? "bg-gray-600" : "bg-gray-700"} hover:bg-gray-600`,
1468
+ children: /* @__PURE__ */ jsxDEV10(AlignLeft, {
1469
+ className: "w-4 h-4 text-white"
1470
+ }, undefined, false, undefined, this)
1471
+ }, undefined, false, undefined, this),
1472
+ /* @__PURE__ */ jsxDEV10("button", {
1473
+ onClick: () => updateElement(selectedElement.id, {
1474
+ align: "center"
1475
+ }),
1476
+ className: `p-2 rounded ${selectedElement.align === "center" ? "bg-gray-600" : "bg-gray-700"} hover:bg-gray-600`,
1477
+ children: /* @__PURE__ */ jsxDEV10(AlignCenter, {
1478
+ className: "w-4 h-4 text-white"
1479
+ }, undefined, false, undefined, this)
1480
+ }, undefined, false, undefined, this),
1481
+ /* @__PURE__ */ jsxDEV10("button", {
1482
+ onClick: () => updateElement(selectedElement.id, {
1483
+ align: "right"
1484
+ }),
1485
+ className: `p-2 rounded ${selectedElement.align === "right" ? "bg-gray-600" : "bg-gray-700"} hover:bg-gray-600`,
1486
+ children: /* @__PURE__ */ jsxDEV10(AlignRight, {
1487
+ className: "w-4 h-4 text-white"
1488
+ }, undefined, false, undefined, this)
1489
+ }, undefined, false, undefined, this)
1490
+ ]
1491
+ }, undefined, true, undefined, this)
1492
+ ]
1493
+ }, undefined, true, undefined, this),
1494
+ /* @__PURE__ */ jsxDEV10("div", {
1495
+ children: [
1496
+ /* @__PURE__ */ jsxDEV10("label", {
1497
+ className: "text-gray-400 text-sm",
1498
+ children: "Text Case"
1499
+ }, undefined, false, undefined, this),
1500
+ /* @__PURE__ */ jsxDEV10("select", {
1501
+ value: selectedElement.textCase || "none",
1502
+ onChange: (e) => updateElement(selectedElement.id, {
1503
+ textCase: e.target.value
1504
+ }),
1505
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2",
1506
+ children: [
1507
+ /* @__PURE__ */ jsxDEV10("option", {
1508
+ value: "none",
1509
+ children: "None"
1510
+ }, undefined, false, undefined, this),
1511
+ /* @__PURE__ */ jsxDEV10("option", {
1512
+ value: "uppercase",
1513
+ children: "Uppercase"
1514
+ }, undefined, false, undefined, this),
1515
+ /* @__PURE__ */ jsxDEV10("option", {
1516
+ value: "lowercase",
1517
+ children: "Lowercase"
1518
+ }, undefined, false, undefined, this),
1519
+ /* @__PURE__ */ jsxDEV10("option", {
1520
+ value: "capitalize",
1521
+ children: "Capitalize"
1522
+ }, undefined, false, undefined, this)
1523
+ ]
1524
+ }, undefined, true, undefined, this)
1525
+ ]
1526
+ }, undefined, true, undefined, this),
1527
+ /* @__PURE__ */ jsxDEV10("div", {
1528
+ children: [
1529
+ /* @__PURE__ */ jsxDEV10("label", {
1530
+ className: "text-gray-400 text-sm",
1531
+ children: "Line Height (e.g., 1.2)"
1532
+ }, undefined, false, undefined, this),
1533
+ /* @__PURE__ */ jsxDEV10("input", {
1534
+ type: "number",
1535
+ step: "0.1",
1536
+ value: selectedElement.lineHeight || 1.2,
1537
+ onChange: (e) => updateElement(selectedElement.id, {
1538
+ lineHeight: parseFloat(e.target.value) || 1.2
1539
+ }),
1540
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
1541
+ }, undefined, false, undefined, this)
1542
+ ]
1543
+ }, undefined, true, undefined, this),
1544
+ /* @__PURE__ */ jsxDEV10("div", {
1545
+ children: [
1546
+ /* @__PURE__ */ jsxDEV10("label", {
1547
+ className: "text-gray-400 text-sm",
1548
+ children: "Letter Spacing (px)"
1549
+ }, undefined, false, undefined, this),
1550
+ /* @__PURE__ */ jsxDEV10("input", {
1551
+ type: "number",
1552
+ value: selectedElement.letterSpacing || 0,
1553
+ onChange: (e) => updateElement(selectedElement.id, {
1554
+ letterSpacing: parseInt(e.target.value) || 0
1555
+ }),
1556
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
1557
+ }, undefined, false, undefined, this)
1558
+ ]
1559
+ }, undefined, true, undefined, this),
1560
+ /* @__PURE__ */ jsxDEV10("div", {
1561
+ children: [
1562
+ /* @__PURE__ */ jsxDEV10("label", {
1563
+ className: "text-gray-400 text-sm",
1564
+ children: "Text Box Width (px, auto if 0)"
1565
+ }, undefined, false, undefined, this),
1566
+ /* @__PURE__ */ jsxDEV10("input", {
1567
+ type: "number",
1568
+ value: selectedElement.textWidth || "",
1569
+ placeholder: "Auto",
1570
+ onChange: (e) => updateElement(selectedElement.id, {
1571
+ textWidth: parseInt(e.target.value) || undefined
1572
+ }),
1573
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
1574
+ }, undefined, false, undefined, this)
1575
+ ]
1576
+ }, undefined, true, undefined, this),
1577
+ /* @__PURE__ */ jsxDEV10("div", {
1578
+ children: [
1579
+ /* @__PURE__ */ jsxDEV10("label", {
1580
+ className: "text-gray-400 text-sm",
1581
+ children: "Text Box Height (px, auto if 0)"
1582
+ }, undefined, false, undefined, this),
1583
+ /* @__PURE__ */ jsxDEV10("input", {
1584
+ type: "number",
1585
+ value: selectedElement.textHeight || "",
1586
+ placeholder: "Auto",
1587
+ onChange: (e) => updateElement(selectedElement.id, {
1588
+ textHeight: parseInt(e.target.value) || undefined
1589
+ }),
1590
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
1591
+ }, undefined, false, undefined, this)
1592
+ ]
1593
+ }, undefined, true, undefined, this)
1594
+ ]
1595
+ }, undefined, true, undefined, this)
1596
+ ]
1597
+ }, undefined, true, undefined, this);
1598
+ };
1599
+ var TextPanel_default = TextPanel;
1600
+
1601
+ // src/panels/ImagePanel.tsx
1602
+ import { Upload, Search } from "lucide-react";
1603
+ import { jsxDEV as jsxDEV11 } from "react/jsx-dev-runtime";
1604
+ var ImagePanel = ({
1605
+ selectedElement,
1606
+ updateElement,
1607
+ onUploadClick,
1608
+ onUnsplashClick,
1609
+ canvasWidth,
1610
+ canvasHeight
1611
+ }) => {
1612
+ const isImageSelected = selectedElement?.type === "image";
1613
+ const handleFitToPage = () => {
1614
+ if (!selectedElement || !selectedElement.src)
1615
+ return;
1616
+ const img = new window.Image;
1617
+ img.src = selectedElement.src;
1618
+ img.onload = () => {
1619
+ const imgWidth = img.naturalWidth || selectedElement.width || 1;
1620
+ const imgHeight = img.naturalHeight || selectedElement.height || 1;
1621
+ const canvasAspect = canvasWidth / canvasHeight;
1622
+ const imgAspect = imgWidth / imgHeight;
1623
+ let newWidth, newHeight, newX, newY;
1624
+ if (imgAspect > canvasAspect) {
1625
+ newWidth = canvasWidth;
1626
+ newHeight = newWidth / imgAspect;
1627
+ newX = 0;
1628
+ newY = (canvasHeight - newHeight) / 2;
1629
+ } else {
1630
+ newHeight = canvasHeight;
1631
+ newWidth = newHeight * imgAspect;
1632
+ newY = 0;
1633
+ newX = (canvasWidth - newWidth) / 2;
1634
+ }
1635
+ updateElement(selectedElement.id, {
1636
+ width: newWidth,
1637
+ height: newHeight,
1638
+ x: newX,
1639
+ y: newY,
1640
+ cropX: 0,
1641
+ cropY: 0,
1642
+ cropWidth: newWidth,
1643
+ cropHeight: newHeight
1644
+ });
1645
+ };
1646
+ };
1647
+ const handleFillPage = () => {
1648
+ if (!selectedElement || !selectedElement.src)
1649
+ return;
1650
+ const img = new window.Image;
1651
+ img.src = selectedElement.src;
1652
+ img.onload = () => {
1653
+ const imgWidth = img.naturalWidth || selectedElement.width || 1;
1654
+ const imgHeight = img.naturalHeight || selectedElement.height || 1;
1655
+ const canvasAspect = canvasWidth / canvasHeight;
1656
+ const imgAspect = imgWidth / imgHeight;
1657
+ let newWidth, newHeight, newX, newY;
1658
+ if (imgAspect > canvasAspect) {
1659
+ newHeight = canvasHeight;
1660
+ newWidth = newHeight * imgAspect;
1661
+ newY = 0;
1662
+ newX = (canvasWidth - newWidth) / 2;
1663
+ } else {
1664
+ newWidth = canvasWidth;
1665
+ newHeight = newWidth / imgAspect;
1666
+ newX = 0;
1667
+ newY = (canvasHeight - newHeight) / 2;
1668
+ }
1669
+ updateElement(selectedElement.id, {
1670
+ width: newWidth,
1671
+ height: newHeight,
1672
+ x: newX,
1673
+ y: newY,
1674
+ cropX: 0,
1675
+ cropY: 0,
1676
+ cropWidth: newWidth,
1677
+ cropHeight: newHeight
1678
+ });
1679
+ };
1680
+ };
1681
+ return /* @__PURE__ */ jsxDEV11("div", {
1682
+ children: [
1683
+ /* @__PURE__ */ jsxDEV11("h3", {
1684
+ className: "text-white font-semibold mb-4",
1685
+ children: "Images"
1686
+ }, undefined, false, undefined, this),
1687
+ /* @__PURE__ */ jsxDEV11("div", {
1688
+ className: "space-y-3",
1689
+ children: [
1690
+ /* @__PURE__ */ jsxDEV11("button", {
1691
+ onClick: onUploadClick,
1692
+ className: "w-full p-3 bg-gray-700 text-white rounded hover:bg-gray-600 flex items-center justify-center space-x-2",
1693
+ children: [
1694
+ /* @__PURE__ */ jsxDEV11(Upload, {
1695
+ className: "w-4 h-4"
1696
+ }, undefined, false, undefined, this),
1697
+ /* @__PURE__ */ jsxDEV11("span", {
1698
+ children: "Upload Image"
1699
+ }, undefined, false, undefined, this)
1700
+ ]
1701
+ }, undefined, true, undefined, this),
1702
+ /* @__PURE__ */ jsxDEV11("button", {
1703
+ onClick: onUnsplashClick,
1704
+ className: "w-full p-3 bg-gray-700 text-white rounded hover:bg-gray-600 flex items-center justify-center space-x-2",
1705
+ children: [
1706
+ /* @__PURE__ */ jsxDEV11(Search, {
1707
+ className: "w-4 h-4"
1708
+ }, undefined, false, undefined, this),
1709
+ /* @__PURE__ */ jsxDEV11("span", {
1710
+ children: "Add Unsplash Image"
1711
+ }, undefined, false, undefined, this)
1712
+ ]
1713
+ }, undefined, true, undefined, this)
1714
+ ]
1715
+ }, undefined, true, undefined, this),
1716
+ isImageSelected && selectedElement && /* @__PURE__ */ jsxDEV11("div", {
1717
+ className: "mt-6 space-y-4",
1718
+ children: [
1719
+ /* @__PURE__ */ jsxDEV11("h4", {
1720
+ className: "text-white font-semibold",
1721
+ children: "Image Settings"
1722
+ }, undefined, false, undefined, this),
1723
+ /* @__PURE__ */ jsxDEV11("div", {
1724
+ children: [
1725
+ /* @__PURE__ */ jsxDEV11("label", {
1726
+ htmlFor: "opacity",
1727
+ className: "text-gray-400 text-sm",
1728
+ children: "Opacity"
1729
+ }, undefined, false, undefined, this),
1730
+ /* @__PURE__ */ jsxDEV11("input", {
1731
+ id: "opacity",
1732
+ type: "range",
1733
+ min: "0",
1734
+ max: "1",
1735
+ step: "0.1",
1736
+ value: selectedElement.opacity,
1737
+ onChange: (e) => updateElement(selectedElement.id, {
1738
+ opacity: parseFloat(e.target.value)
1739
+ }),
1740
+ className: "w-full mt-1"
1741
+ }, undefined, false, undefined, this)
1742
+ ]
1743
+ }, undefined, true, undefined, this),
1744
+ /* @__PURE__ */ jsxDEV11("div", {
1745
+ children: [
1746
+ /* @__PURE__ */ jsxDEV11("label", {
1747
+ htmlFor: "corner-radius",
1748
+ className: "text-gray-400 text-sm",
1749
+ children: "Corner Radius"
1750
+ }, undefined, false, undefined, this),
1751
+ /* @__PURE__ */ jsxDEV11("input", {
1752
+ id: "corner-radius",
1753
+ type: "number",
1754
+ value: selectedElement.cornerRadius || 0,
1755
+ onChange: (e) => updateElement(selectedElement.id, {
1756
+ cornerRadius: parseInt(e.target.value) || 0
1757
+ }),
1758
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
1759
+ }, undefined, false, undefined, this)
1760
+ ]
1761
+ }, undefined, true, undefined, this),
1762
+ /* @__PURE__ */ jsxDEV11("div", {
1763
+ className: "grid grid-cols-2 gap-2",
1764
+ children: [
1765
+ /* @__PURE__ */ jsxDEV11("button", {
1766
+ onClick: handleFitToPage,
1767
+ className: "p-2 bg-gray-600 text-white rounded hover:bg-gray-500 text-sm",
1768
+ children: "Fit to Page"
1769
+ }, undefined, false, undefined, this),
1770
+ /* @__PURE__ */ jsxDEV11("button", {
1771
+ onClick: handleFillPage,
1772
+ className: "p-2 bg-gray-600 text-white rounded hover:bg-gray-500 text-sm",
1773
+ children: "Fill Page"
1774
+ }, undefined, false, undefined, this)
1775
+ ]
1776
+ }, undefined, true, undefined, this),
1777
+ /* @__PURE__ */ jsxDEV11("div", {
1778
+ children: [
1779
+ /* @__PURE__ */ jsxDEV11("h5", {
1780
+ className: "text-gray-300 font-semibold my-2",
1781
+ children: "Crop"
1782
+ }, undefined, false, undefined, this),
1783
+ /* @__PURE__ */ jsxDEV11("div", {
1784
+ className: "space-y-2",
1785
+ children: [
1786
+ /* @__PURE__ */ jsxDEV11("div", {
1787
+ children: [
1788
+ /* @__PURE__ */ jsxDEV11("label", {
1789
+ htmlFor: "cropX",
1790
+ className: "text-gray-400 text-sm",
1791
+ children: "Crop X"
1792
+ }, undefined, false, undefined, this),
1793
+ /* @__PURE__ */ jsxDEV11("input", {
1794
+ id: "cropX",
1795
+ type: "range",
1796
+ min: "0",
1797
+ max: selectedElement.width,
1798
+ value: selectedElement.cropX || 0,
1799
+ onChange: (e) => updateElement(selectedElement.id, {
1800
+ cropX: parseInt(e.target.value)
1801
+ }),
1802
+ className: "w-full mt-1"
1803
+ }, undefined, false, undefined, this)
1804
+ ]
1805
+ }, undefined, true, undefined, this),
1806
+ /* @__PURE__ */ jsxDEV11("div", {
1807
+ children: [
1808
+ /* @__PURE__ */ jsxDEV11("label", {
1809
+ htmlFor: "cropY",
1810
+ className: "text-gray-400 text-sm",
1811
+ children: "Crop Y"
1812
+ }, undefined, false, undefined, this),
1813
+ /* @__PURE__ */ jsxDEV11("input", {
1814
+ id: "cropY",
1815
+ type: "range",
1816
+ min: "0",
1817
+ max: selectedElement.height,
1818
+ value: selectedElement.cropY || 0,
1819
+ onChange: (e) => updateElement(selectedElement.id, {
1820
+ cropY: parseInt(e.target.value)
1821
+ }),
1822
+ className: "w-full mt-1"
1823
+ }, undefined, false, undefined, this)
1824
+ ]
1825
+ }, undefined, true, undefined, this),
1826
+ /* @__PURE__ */ jsxDEV11("div", {
1827
+ children: [
1828
+ /* @__PURE__ */ jsxDEV11("label", {
1829
+ htmlFor: "cropWidth",
1830
+ className: "text-gray-400 text-sm",
1831
+ children: "Crop Width"
1832
+ }, undefined, false, undefined, this),
1833
+ /* @__PURE__ */ jsxDEV11("input", {
1834
+ id: "cropWidth",
1835
+ type: "range",
1836
+ min: "0",
1837
+ max: selectedElement.width,
1838
+ value: selectedElement.cropWidth || selectedElement.width,
1839
+ onChange: (e) => updateElement(selectedElement.id, {
1840
+ cropWidth: parseInt(e.target.value)
1841
+ }),
1842
+ className: "w-full mt-1"
1843
+ }, undefined, false, undefined, this)
1844
+ ]
1845
+ }, undefined, true, undefined, this),
1846
+ /* @__PURE__ */ jsxDEV11("div", {
1847
+ children: [
1848
+ /* @__PURE__ */ jsxDEV11("label", {
1849
+ htmlFor: "cropHeight",
1850
+ className: "text-gray-400 text-sm",
1851
+ children: "Crop Height"
1852
+ }, undefined, false, undefined, this),
1853
+ /* @__PURE__ */ jsxDEV11("input", {
1854
+ id: "cropHeight",
1855
+ type: "range",
1856
+ min: "0",
1857
+ max: selectedElement.height,
1858
+ value: selectedElement.cropHeight || selectedElement.height,
1859
+ onChange: (e) => updateElement(selectedElement.id, {
1860
+ cropHeight: parseInt(e.target.value)
1861
+ }),
1862
+ className: "w-full mt-1"
1863
+ }, undefined, false, undefined, this)
1864
+ ]
1865
+ }, undefined, true, undefined, this),
1866
+ /* @__PURE__ */ jsxDEV11("button", {
1867
+ onClick: () => updateElement(selectedElement.id, {
1868
+ cropX: 0,
1869
+ cropY: 0,
1870
+ cropWidth: selectedElement.width,
1871
+ cropHeight: selectedElement.height
1872
+ }),
1873
+ className: "w-full p-2 bg-gray-600 text-white rounded hover:bg-gray-500 text-sm",
1874
+ children: "Reset Crop"
1875
+ }, undefined, false, undefined, this)
1876
+ ]
1877
+ }, undefined, true, undefined, this)
1878
+ ]
1879
+ }, undefined, true, undefined, this),
1880
+ /* @__PURE__ */ jsxDEV11("div", {
1881
+ children: [
1882
+ /* @__PURE__ */ jsxDEV11("h5", {
1883
+ className: "text-gray-300 font-semibold my-2",
1884
+ children: "Mask"
1885
+ }, undefined, false, undefined, this),
1886
+ /* @__PURE__ */ jsxDEV11("select", {
1887
+ value: selectedElement.mask?.type || "",
1888
+ onChange: (e) => {
1889
+ const maskType = e.target.value;
1890
+ updateElement(selectedElement.id, {
1891
+ mask: maskType ? { type: maskType } : undefined
1892
+ });
1893
+ },
1894
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2",
1895
+ children: [
1896
+ /* @__PURE__ */ jsxDEV11("option", {
1897
+ value: "",
1898
+ children: "None"
1899
+ }, undefined, false, undefined, this),
1900
+ /* @__PURE__ */ jsxDEV11("option", {
1901
+ value: "rect",
1902
+ children: "Rectangle"
1903
+ }, undefined, false, undefined, this),
1904
+ /* @__PURE__ */ jsxDEV11("option", {
1905
+ value: "circle",
1906
+ children: "Circle"
1907
+ }, undefined, false, undefined, this),
1908
+ /* @__PURE__ */ jsxDEV11("option", {
1909
+ value: "star",
1910
+ children: "Star"
1911
+ }, undefined, false, undefined, this),
1912
+ /* @__PURE__ */ jsxDEV11("option", {
1913
+ value: "polygon",
1914
+ children: "Polygon"
1915
+ }, undefined, false, undefined, this),
1916
+ /* @__PURE__ */ jsxDEV11("option", {
1917
+ value: "triangle",
1918
+ children: "Triangle"
1919
+ }, undefined, false, undefined, this),
1920
+ /* @__PURE__ */ jsxDEV11("option", {
1921
+ value: "arrow",
1922
+ children: "Arrow"
1923
+ }, undefined, false, undefined, this),
1924
+ /* @__PURE__ */ jsxDEV11("option", {
1925
+ value: "pentagon",
1926
+ children: "Pentagon"
1927
+ }, undefined, false, undefined, this),
1928
+ /* @__PURE__ */ jsxDEV11("option", {
1929
+ value: "octagon",
1930
+ children: "Octagon"
1931
+ }, undefined, false, undefined, this),
1932
+ /* @__PURE__ */ jsxDEV11("option", {
1933
+ value: "nonagon",
1934
+ children: "Nonagon"
1935
+ }, undefined, false, undefined, this),
1936
+ /* @__PURE__ */ jsxDEV11("option", {
1937
+ value: "decagon",
1938
+ children: "Decagon"
1939
+ }, undefined, false, undefined, this),
1940
+ /* @__PURE__ */ jsxDEV11("option", {
1941
+ value: "hendecagon",
1942
+ children: "Hendecagon"
1943
+ }, undefined, false, undefined, this),
1944
+ /* @__PURE__ */ jsxDEV11("option", {
1945
+ value: "dodecagon",
1946
+ children: "Dodecagon"
1947
+ }, undefined, false, undefined, this),
1948
+ /* @__PURE__ */ jsxDEV11("option", {
1949
+ value: "right-triangle",
1950
+ children: "Right Triangle"
1951
+ }, undefined, false, undefined, this),
1952
+ /* @__PURE__ */ jsxDEV11("option", {
1953
+ value: "isosceles-triangle",
1954
+ children: "Isosceles Triangle"
1955
+ }, undefined, false, undefined, this),
1956
+ /* @__PURE__ */ jsxDEV11("option", {
1957
+ value: "scalene-triangle",
1958
+ children: "Scalene Triangle"
1959
+ }, undefined, false, undefined, this)
1960
+ ]
1961
+ }, undefined, true, undefined, this),
1962
+ /* @__PURE__ */ jsxDEV11("button", {
1963
+ onClick: () => updateElement(selectedElement.id, { mask: undefined }),
1964
+ className: "w-full p-2 bg-red-700 text-white rounded hover:bg-red-600 text-sm mt-2",
1965
+ children: "Remove Mask"
1966
+ }, undefined, false, undefined, this)
1967
+ ]
1968
+ }, undefined, true, undefined, this),
1969
+ /* @__PURE__ */ jsxDEV11("div", {
1970
+ children: [
1971
+ /* @__PURE__ */ jsxDEV11("label", {
1972
+ className: "text-gray-400 text-sm",
1973
+ children: "Filters"
1974
+ }, undefined, false, undefined, this),
1975
+ /* @__PURE__ */ jsxDEV11("div", {
1976
+ className: "space-y-2 mt-2",
1977
+ children: [
1978
+ /* @__PURE__ */ jsxDEV11("div", {
1979
+ className: "flex items-center justify-between",
1980
+ children: [
1981
+ /* @__PURE__ */ jsxDEV11("label", {
1982
+ htmlFor: "grayscale",
1983
+ className: "text-gray-300 text-sm",
1984
+ children: "Grayscale"
1985
+ }, undefined, false, undefined, this),
1986
+ /* @__PURE__ */ jsxDEV11("input", {
1987
+ id: "grayscale",
1988
+ type: "checkbox",
1989
+ checked: selectedElement.filters?.grayscale || false,
1990
+ onChange: (e) => updateElement(selectedElement.id, {
1991
+ filters: {
1992
+ ...selectedElement.filters,
1993
+ grayscale: e.target.checked
1994
+ }
1995
+ }),
1996
+ className: "rounded"
1997
+ }, undefined, false, undefined, this)
1998
+ ]
1999
+ }, undefined, true, undefined, this),
2000
+ /* @__PURE__ */ jsxDEV11("div", {
2001
+ className: "flex items-center justify-between",
2002
+ children: [
2003
+ /* @__PURE__ */ jsxDEV11("label", {
2004
+ htmlFor: "sepia",
2005
+ className: "text-gray-300 text-sm",
2006
+ children: "Sepia"
2007
+ }, undefined, false, undefined, this),
2008
+ /* @__PURE__ */ jsxDEV11("input", {
2009
+ id: "sepia",
2010
+ type: "checkbox",
2011
+ checked: selectedElement.filters?.sepia || false,
2012
+ onChange: (e) => updateElement(selectedElement.id, {
2013
+ filters: {
2014
+ ...selectedElement.filters,
2015
+ sepia: e.target.checked
2016
+ }
2017
+ }),
2018
+ className: "rounded"
2019
+ }, undefined, false, undefined, this)
2020
+ ]
2021
+ }, undefined, true, undefined, this)
2022
+ ]
2023
+ }, undefined, true, undefined, this)
2024
+ ]
2025
+ }, undefined, true, undefined, this)
2026
+ ]
2027
+ }, undefined, true, undefined, this)
2028
+ ]
2029
+ }, undefined, true, undefined, this);
2030
+ };
2031
+ var ImagePanel_default = ImagePanel;
2032
+
2033
+ // src/panels/DesignPanel.tsx
2034
+ import { jsxDEV as jsxDEV12 } from "react/jsx-dev-runtime";
2035
+ var DesignPanel = ({
2036
+ design,
2037
+ currentPage,
2038
+ selectedElement,
2039
+ setDesign,
2040
+ updateElement,
2041
+ onSetUnsplashBackground,
2042
+ config
2043
+ }) => {
2044
+ return /* @__PURE__ */ jsxDEV12("div", {
2045
+ children: [
2046
+ /* @__PURE__ */ jsxDEV12("h3", {
2047
+ className: "text-white font-semibold mb-4",
2048
+ children: "Design"
2049
+ }, undefined, false, undefined, this),
2050
+ /* @__PURE__ */ jsxDEV12("div", {
2051
+ className: "space-y-4",
2052
+ children: [
2053
+ /* @__PURE__ */ jsxDEV12("div", {
2054
+ children: [
2055
+ /* @__PURE__ */ jsxDEV12("label", {
2056
+ htmlFor: "canvas-size",
2057
+ className: "text-gray-400 text-sm",
2058
+ children: "Canvas Size"
2059
+ }, undefined, false, undefined, this),
2060
+ /* @__PURE__ */ jsxDEV12("select", {
2061
+ id: "canvas-size",
2062
+ value: CANVAS_PRESETS.find((p) => p.width === design.width && p.height === design.height)?.name || "",
2063
+ onChange: (e) => {
2064
+ const preset = CANVAS_PRESETS.find((p) => p.name === e.target.value);
2065
+ if (preset) {
2066
+ setDesign((prev) => ({
2067
+ ...prev,
2068
+ width: preset.width,
2069
+ height: preset.height
2070
+ }));
2071
+ }
2072
+ },
2073
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2",
2074
+ children: [
2075
+ /* @__PURE__ */ jsxDEV12("option", {
2076
+ value: "",
2077
+ disabled: true,
2078
+ children: "Select a preset"
2079
+ }, undefined, false, undefined, this),
2080
+ CANVAS_PRESETS.map((preset) => /* @__PURE__ */ jsxDEV12("option", {
2081
+ value: preset.name,
2082
+ children: [
2083
+ preset.name,
2084
+ " (",
2085
+ preset.width,
2086
+ "x",
2087
+ preset.height,
2088
+ ")"
2089
+ ]
2090
+ }, preset.name, true, undefined, this))
2091
+ ]
2092
+ }, undefined, true, undefined, this)
2093
+ ]
2094
+ }, undefined, true, undefined, this),
2095
+ (config?.designSize?.width || config?.designSize?.height) && /* @__PURE__ */ jsxDEV12("div", {
2096
+ className: "grid grid-cols-2 gap-2",
2097
+ children: [
2098
+ config?.designSize?.width && /* @__PURE__ */ jsxDEV12("div", {
2099
+ children: [
2100
+ /* @__PURE__ */ jsxDEV12("label", {
2101
+ htmlFor: "canvas-width",
2102
+ className: "text-gray-400 text-sm",
2103
+ children: "Width"
2104
+ }, undefined, false, undefined, this),
2105
+ /* @__PURE__ */ jsxDEV12("input", {
2106
+ id: "canvas-width",
2107
+ type: "number",
2108
+ value: design.width,
2109
+ onChange: (e) => setDesign((prev) => ({
2110
+ ...prev,
2111
+ width: parseInt(e.target.value) || 800
2112
+ })),
2113
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
2114
+ }, undefined, false, undefined, this)
2115
+ ]
2116
+ }, undefined, true, undefined, this),
2117
+ config?.designSize?.height && /* @__PURE__ */ jsxDEV12("div", {
2118
+ children: [
2119
+ /* @__PURE__ */ jsxDEV12("label", {
2120
+ htmlFor: "canvas-height",
2121
+ className: "text-gray-400 text-sm",
2122
+ children: "Height"
2123
+ }, undefined, false, undefined, this),
2124
+ /* @__PURE__ */ jsxDEV12("input", {
2125
+ id: "canvas-height",
2126
+ type: "number",
2127
+ value: design.height,
2128
+ onChange: (e) => setDesign((prev) => ({
2129
+ ...prev,
2130
+ height: parseInt(e.target.value) || 600
2131
+ })),
2132
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
2133
+ }, undefined, false, undefined, this)
2134
+ ]
2135
+ }, undefined, true, undefined, this)
2136
+ ]
2137
+ }, undefined, true, undefined, this),
2138
+ /* @__PURE__ */ jsxDEV12("div", {
2139
+ children: [
2140
+ /* @__PURE__ */ jsxDEV12("label", {
2141
+ id: "color-palette-label",
2142
+ className: "text-gray-400 text-sm mb-2 block",
2143
+ children: "Color Palette"
2144
+ }, undefined, false, undefined, this),
2145
+ /* @__PURE__ */ jsxDEV12("div", {
2146
+ className: "grid grid-cols-5 gap-2",
2147
+ role: "group",
2148
+ "aria-labelledby": "color-palette-label",
2149
+ children: design.colors.map((color, i) => /* @__PURE__ */ jsxDEV12("button", {
2150
+ "data-testid": `color-palette-button-${i}`,
2151
+ onClick: () => {
2152
+ if (selectedElement) {
2153
+ updateElement(selectedElement.id, { fill: color });
2154
+ } else {
2155
+ setDesign((prev) => ({
2156
+ ...prev,
2157
+ pages: prev.pages.map((p) => p.id === currentPage.id ? { ...p, background: color, backgroundImageObject: undefined } : p)
2158
+ }));
2159
+ }
2160
+ },
2161
+ className: "w-full aspect-square rounded border-2 border-gray-600",
2162
+ style: { backgroundColor: color },
2163
+ title: `Set color to ${color}`
2164
+ }, i, false, undefined, this))
2165
+ }, undefined, false, undefined, this)
2166
+ ]
2167
+ }, undefined, true, undefined, this)
2168
+ ]
2169
+ }, undefined, true, undefined, this)
2170
+ ]
2171
+ }, undefined, true, undefined, this);
2172
+ };
2173
+ var DesignPanel_default = DesignPanel;
2174
+
2175
+ // src/panels/BackgroundPanel.tsx
2176
+ import { ImageIcon as ImageIcon2 } from "lucide-react";
2177
+ import { jsxDEV as jsxDEV13 } from "react/jsx-dev-runtime";
2178
+ var BackgroundPanel = ({
2179
+ design,
2180
+ currentPage,
2181
+ setDesign,
2182
+ onSetUnsplashBackground
2183
+ }) => {
2184
+ return /* @__PURE__ */ jsxDEV13("div", {
2185
+ children: [
2186
+ /* @__PURE__ */ jsxDEV13("h3", {
2187
+ className: "text-white font-semibold mb-4",
2188
+ children: "Background"
2189
+ }, undefined, false, undefined, this),
2190
+ /* @__PURE__ */ jsxDEV13("div", {
2191
+ className: "space-y-4",
2192
+ children: [
2193
+ /* @__PURE__ */ jsxDEV13("div", {
2194
+ children: [
2195
+ /* @__PURE__ */ jsxDEV13("label", {
2196
+ htmlFor: "bg-color",
2197
+ className: "text-gray-400 text-sm",
2198
+ children: "Background Color"
2199
+ }, undefined, false, undefined, this),
2200
+ /* @__PURE__ */ jsxDEV13("div", {
2201
+ className: "flex items-center mt-1",
2202
+ children: [
2203
+ /* @__PURE__ */ jsxDEV13("input", {
2204
+ id: "bg-color",
2205
+ type: "color",
2206
+ value: currentPage.background.startsWith("url") ? "#FFFFFF" : currentPage.background,
2207
+ onChange: (e) => setDesign((prev) => ({
2208
+ ...prev,
2209
+ pages: prev.pages.map((p) => p.id === currentPage.id ? { ...p, background: e.target.value, backgroundImageObject: undefined } : p)
2210
+ })),
2211
+ className: "w-12 h-12 border border-gray-600 rounded cursor-pointer"
2212
+ }, undefined, false, undefined, this),
2213
+ /* @__PURE__ */ jsxDEV13("input", {
2214
+ type: "text",
2215
+ value: currentPage.background.startsWith("url") ? "#FFFFFF" : currentPage.background,
2216
+ onChange: (e) => setDesign((prev) => ({
2217
+ ...prev,
2218
+ pages: prev.pages.map((p) => p.id === currentPage.id ? { ...p, background: e.target.value, backgroundImageObject: undefined } : p)
2219
+ })),
2220
+ className: "flex-1 ml-2 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
2221
+ }, undefined, false, undefined, this)
2222
+ ]
2223
+ }, undefined, true, undefined, this)
2224
+ ]
2225
+ }, undefined, true, undefined, this),
2226
+ /* @__PURE__ */ jsxDEV13("button", {
2227
+ onClick: onSetUnsplashBackground,
2228
+ className: "w-full p-3 bg-gray-700 text-white rounded hover:bg-gray-600 flex items-center justify-center space-x-2",
2229
+ children: [
2230
+ /* @__PURE__ */ jsxDEV13(ImageIcon2, {
2231
+ className: "w-4 h-4"
2232
+ }, undefined, false, undefined, this),
2233
+ /* @__PURE__ */ jsxDEV13("span", {
2234
+ children: "Set Background from Unsplash"
2235
+ }, undefined, false, undefined, this)
2236
+ ]
2237
+ }, undefined, true, undefined, this)
2238
+ ]
2239
+ }, undefined, true, undefined, this)
2240
+ ]
2241
+ }, undefined, true, undefined, this);
2242
+ };
2243
+ var BackgroundPanel_default = BackgroundPanel;
2244
+
2245
+ // src/panels/VariablesPanel.tsx
2246
+ import { jsxDEV as jsxDEV14 } from "react/jsx-dev-runtime";
2247
+ var VariablesPanel = ({
2248
+ config,
2249
+ design,
2250
+ setDesign
2251
+ }) => {
2252
+ const handleVariableChange = (variable, value) => {
2253
+ setDesign((prev) => {
2254
+ const newDesign = { ...prev };
2255
+ newDesign.pages.forEach((page) => {
2256
+ page.elements.forEach((element) => {
2257
+ if (element.text?.includes(`{${variable}}`)) {
2258
+ element.text = element.text.replace(`{${variable}}`, value);
2259
+ }
2260
+ if (element.src?.includes(`{${variable}}`)) {
2261
+ element.src = element.src.replace(`{${variable}}`, value);
2262
+ }
2263
+ });
2264
+ });
2265
+ return newDesign;
2266
+ });
2267
+ };
2268
+ return /* @__PURE__ */ jsxDEV14("div", {
2269
+ children: [
2270
+ /* @__PURE__ */ jsxDEV14("h3", {
2271
+ className: "text-white font-semibold mb-4",
2272
+ children: "Variables"
2273
+ }, undefined, false, undefined, this),
2274
+ /* @__PURE__ */ jsxDEV14("div", {
2275
+ className: "space-y-4",
2276
+ children: config.steps?.map((step) => /* @__PURE__ */ jsxDEV14("div", {
2277
+ children: [
2278
+ /* @__PURE__ */ jsxDEV14("label", {
2279
+ htmlFor: step.variable,
2280
+ className: "text-gray-400 text-sm",
2281
+ children: step.name
2282
+ }, undefined, false, undefined, this),
2283
+ step.type === "text" && /* @__PURE__ */ jsxDEV14("input", {
2284
+ id: step.variable,
2285
+ type: "text",
2286
+ onChange: (e) => handleVariableChange(step.variable, e.target.value),
2287
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
2288
+ }, undefined, false, undefined, this),
2289
+ step.type === "image" && /* @__PURE__ */ jsxDEV14("input", {
2290
+ id: step.variable,
2291
+ type: "file",
2292
+ accept: "image/*",
2293
+ onChange: (e) => {
2294
+ const file = e.target.files?.[0];
2295
+ if (file) {
2296
+ const reader = new FileReader;
2297
+ reader.onload = (event) => {
2298
+ handleVariableChange(step.variable, event.target?.result);
2299
+ };
2300
+ reader.readAsDataURL(file);
2301
+ }
2302
+ },
2303
+ className: "w-full mt-1 bg-gray-700 text-white border border-gray-600 rounded px-3 py-2"
2304
+ }, undefined, false, undefined, this)
2305
+ ]
2306
+ }, step.variable, true, undefined, this))
2307
+ }, undefined, false, undefined, this)
2308
+ ]
2309
+ }, undefined, true, undefined, this);
2310
+ };
2311
+ var VariablesPanel_default = VariablesPanel;
2312
+
2313
+ // src/panels/ExportPanel.tsx
2314
+ import { jsxDEV as jsxDEV15 } from "react/jsx-dev-runtime";
2315
+ var ExportPanel = ({
2316
+ onExportToPNG,
2317
+ onExportToJPG,
2318
+ onExportToJSON,
2319
+ onImportJSON
2320
+ }) => {
2321
+ return /* @__PURE__ */ jsxDEV15("div", {
2322
+ children: [
2323
+ /* @__PURE__ */ jsxDEV15("h3", {
2324
+ className: "text-white font-semibold mb-4",
2325
+ children: "Export"
2326
+ }, undefined, false, undefined, this),
2327
+ /* @__PURE__ */ jsxDEV15("div", {
2328
+ className: "space-y-3",
2329
+ children: [
2330
+ /* @__PURE__ */ jsxDEV15("button", {
2331
+ onClick: onExportToPNG,
2332
+ className: "w-full p-3 bg-blue-600 text-white rounded hover:bg-blue-700",
2333
+ children: "Export as PNG"
2334
+ }, undefined, false, undefined, this),
2335
+ /* @__PURE__ */ jsxDEV15("button", {
2336
+ onClick: onExportToJPG,
2337
+ className: "w-full p-3 bg-blue-600 text-white rounded hover:bg-blue-700",
2338
+ children: "Export as JPG"
2339
+ }, undefined, false, undefined, this),
2340
+ /* @__PURE__ */ jsxDEV15("button", {
2341
+ onClick: onExportToJSON,
2342
+ className: "w-full p-3 bg-gray-700 text-white rounded hover:bg-gray-600",
2343
+ children: "Export as JSON"
2344
+ }, undefined, false, undefined, this),
2345
+ /* @__PURE__ */ jsxDEV15("button", {
2346
+ onClick: onImportJSON,
2347
+ className: "w-full p-3 bg-gray-700 text-white rounded hover:bg-gray-600",
2348
+ children: "Import JSON"
2349
+ }, undefined, false, undefined, this)
2350
+ ]
2351
+ }, undefined, true, undefined, this)
2352
+ ]
2353
+ }, undefined, true, undefined, this);
2354
+ };
2355
+ var ExportPanel_default = ExportPanel;
2356
+
2357
+ // src/components/sidebar/RightSidebar.tsx
2358
+ import {
2359
+ Layers as Layers2,
2360
+ Type as Type2,
2361
+ ImageIcon as ImageIcon3,
2362
+ Square as Square3,
2363
+ Eye,
2364
+ EyeOff,
2365
+ Lock,
2366
+ Unlock,
2367
+ Copy,
2368
+ ChevronDown,
2369
+ Trash2
2370
+ } from "lucide-react";
2371
+ import { jsxDEV as jsxDEV16 } from "react/jsx-dev-runtime";
2372
+ var RightSidebar = ({
2373
+ currentPageElements,
2374
+ selectedElement,
2375
+ selectedId,
2376
+ onSelectElement,
2377
+ onUpdateElement,
2378
+ onDuplicateSelected,
2379
+ onMoveElementUp,
2380
+ onMoveElementDown,
2381
+ onDeleteSelected
2382
+ }) => {
2383
+ return /* @__PURE__ */ jsxDEV16("div", {
2384
+ className: "w-64 bg-gray-800 border-l border-gray-700 p-4 flex flex-col",
2385
+ children: [
2386
+ /* @__PURE__ */ jsxDEV16("h3", {
2387
+ className: "text-white font-semibold mb-4 flex items-center",
2388
+ children: [
2389
+ /* @__PURE__ */ jsxDEV16(Layers2, {
2390
+ className: "w-4 h-4 mr-2"
2391
+ }, undefined, false, undefined, this),
2392
+ "Layers"
2393
+ ]
2394
+ }, undefined, true, undefined, this),
2395
+ /* @__PURE__ */ jsxDEV16("div", {
2396
+ className: "space-y-2 flex-1 overflow-y-auto",
2397
+ children: [...currentPageElements].reverse().map((element) => /* @__PURE__ */ jsxDEV16("div", {
2398
+ "data-testid": `sidebar-element-${element.id}`,
2399
+ onClick: () => onSelectElement(element.id),
2400
+ className: `p-2 rounded cursor-pointer flex items-center justify-between group ${element.id === selectedId ? "bg-gray-700" : "hover:bg-gray-700"}`,
2401
+ children: [
2402
+ /* @__PURE__ */ jsxDEV16("div", {
2403
+ className: "flex items-center space-x-2 flex-1 min-w-0",
2404
+ children: [
2405
+ /* @__PURE__ */ jsxDEV16("div", {
2406
+ className: "text-gray-400",
2407
+ children: [
2408
+ element.type === "text" && /* @__PURE__ */ jsxDEV16(Type2, {
2409
+ className: "w-4 h-4"
2410
+ }, undefined, false, undefined, this),
2411
+ element.type === "image" && /* @__PURE__ */ jsxDEV16(ImageIcon3, {
2412
+ className: "w-4 h-4"
2413
+ }, undefined, false, undefined, this),
2414
+ element.type === "shape" && /* @__PURE__ */ jsxDEV16(Square3, {
2415
+ className: "w-4 h-4"
2416
+ }, undefined, false, undefined, this)
2417
+ ]
2418
+ }, undefined, true, undefined, this),
2419
+ /* @__PURE__ */ jsxDEV16("span", {
2420
+ className: "text-gray-300 text-sm truncate",
2421
+ children: element.name
2422
+ }, undefined, false, undefined, this)
2423
+ ]
2424
+ }, undefined, true, undefined, this),
2425
+ /* @__PURE__ */ jsxDEV16("div", {
2426
+ className: "flex items-center space-x-1 opacity-0 group-hover:opacity-100",
2427
+ children: [
2428
+ /* @__PURE__ */ jsxDEV16("button", {
2429
+ "data-testid": `toggle-visibility-${element.id}`,
2430
+ onClick: (e) => {
2431
+ e.stopPropagation();
2432
+ onUpdateElement(element.id, { visible: !element.visible });
2433
+ },
2434
+ className: "p-1 hover:bg-gray-600 rounded",
2435
+ title: element.visible ? "Hide" : "Show",
2436
+ children: element.visible ? /* @__PURE__ */ jsxDEV16(Eye, {
2437
+ className: "w-3 h-3 text-gray-400"
2438
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV16(EyeOff, {
2439
+ className: "w-3 h-3 text-gray-400"
2440
+ }, undefined, false, undefined, this)
2441
+ }, undefined, false, undefined, this),
2442
+ /* @__PURE__ */ jsxDEV16("button", {
2443
+ "data-testid": `toggle-lock-${element.id}`,
2444
+ onClick: (e) => {
2445
+ e.stopPropagation();
2446
+ onUpdateElement(element.id, { locked: !element.locked });
2447
+ },
2448
+ className: "p-1 hover:bg-gray-600 rounded",
2449
+ title: element.locked ? "Unlock" : "Lock",
2450
+ children: element.locked ? /* @__PURE__ */ jsxDEV16(Lock, {
2451
+ className: "w-3 h-3 text-gray-400"
2452
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV16(Unlock, {
2453
+ className: "w-3 h-3 text-gray-400"
2454
+ }, undefined, false, undefined, this)
2455
+ }, undefined, false, undefined, this)
2456
+ ]
2457
+ }, undefined, true, undefined, this)
2458
+ ]
2459
+ }, element.id, true, undefined, this))
2460
+ }, undefined, false, undefined, this),
2461
+ selectedElement && /* @__PURE__ */ jsxDEV16("div", {
2462
+ className: "mt-6 space-y-3 border-t border-gray-700 pt-4",
2463
+ children: [
2464
+ /* @__PURE__ */ jsxDEV16("button", {
2465
+ onClick: onDuplicateSelected,
2466
+ className: "w-full p-2 bg-gray-700 text-white rounded hover:bg-gray-600 flex items-center justify-center space-x-2",
2467
+ children: [
2468
+ /* @__PURE__ */ jsxDEV16(Copy, {
2469
+ className: "w-4 h-4"
2470
+ }, undefined, false, undefined, this),
2471
+ /* @__PURE__ */ jsxDEV16("span", {
2472
+ children: "Duplicate"
2473
+ }, undefined, false, undefined, this)
2474
+ ]
2475
+ }, undefined, true, undefined, this),
2476
+ /* @__PURE__ */ jsxDEV16("div", {
2477
+ className: "flex space-x-2",
2478
+ children: [
2479
+ /* @__PURE__ */ jsxDEV16("button", {
2480
+ onClick: () => onMoveElementUp(selectedElement.id),
2481
+ className: "flex-1 p-2 bg-gray-700 text-white rounded hover:bg-gray-600",
2482
+ title: "Move Forward",
2483
+ children: /* @__PURE__ */ jsxDEV16(ChevronDown, {
2484
+ className: "w-4 h-4 mx-auto transform rotate-180"
2485
+ }, undefined, false, undefined, this)
2486
+ }, undefined, false, undefined, this),
2487
+ /* @__PURE__ */ jsxDEV16("button", {
2488
+ onClick: () => onMoveElementDown(selectedElement.id),
2489
+ className: "flex-1 p-2 bg-gray-700 text-white rounded hover:bg-gray-600",
2490
+ title: "Move Backward",
2491
+ children: /* @__PURE__ */ jsxDEV16(ChevronDown, {
2492
+ className: "w-4 h-4 mx-auto"
2493
+ }, undefined, false, undefined, this)
2494
+ }, undefined, false, undefined, this)
2495
+ ]
2496
+ }, undefined, true, undefined, this),
2497
+ /* @__PURE__ */ jsxDEV16("button", {
2498
+ onClick: onDeleteSelected,
2499
+ className: "w-full p-2 bg-red-600 text-white rounded hover:bg-red-700 flex items-center justify-center space-x-2",
2500
+ children: [
2501
+ /* @__PURE__ */ jsxDEV16(Trash2, {
2502
+ className: "w-4 h-4"
2503
+ }, undefined, false, undefined, this),
2504
+ /* @__PURE__ */ jsxDEV16("span", {
2505
+ children: "Delete"
2506
+ }, undefined, false, undefined, this)
2507
+ ]
2508
+ }, undefined, true, undefined, this)
2509
+ ]
2510
+ }, undefined, true, undefined, this)
2511
+ ]
2512
+ }, undefined, true, undefined, this);
2513
+ };
2514
+ var RightSidebar_default = RightSidebar;
2515
+
2516
+ // src/utils/exportImportUtils.ts
2517
+ function convertTemplateToCanvasDesign(json) {
2518
+ const converted = { ...json };
2519
+ if (!converted.id) {
2520
+ converted.id = Date.now().toString();
2521
+ }
2522
+ if (!converted.name) {
2523
+ converted.name = json.name || "Imported Design";
2524
+ }
2525
+ if (!converted.fonts) {
2526
+ converted.fonts = [];
2527
+ }
2528
+ if (!converted.colors) {
2529
+ converted.colors = [];
2530
+ }
2531
+ if (!converted.templateInputs) {
2532
+ converted.templateInputs = [];
2533
+ }
2534
+ if (converted.pages && Array.isArray(converted.pages)) {
2535
+ converted.pages = converted.pages.map((page, index) => {
2536
+ const elements = (page.elements || page.children || []).map((element) => {
2537
+ return {
2538
+ ...element,
2539
+ id: element.id || `${Date.now()}-${Math.random()}`,
2540
+ type: element.type || "text",
2541
+ name: element.name || "Untitled",
2542
+ x: element.x ?? 0,
2543
+ y: element.y ?? 0,
2544
+ rotation: element.rotation ?? 0,
2545
+ visible: element.visible !== undefined ? element.visible : true,
2546
+ locked: element.locked !== undefined ? element.locked : false,
2547
+ opacity: element.opacity !== undefined ? element.opacity : 1,
2548
+ width: element.width,
2549
+ height: element.height,
2550
+ src: element.src,
2551
+ text: element.text,
2552
+ fontSize: element.fontSize,
2553
+ fontFamily: element.fontFamily,
2554
+ fill: element.fill,
2555
+ stroke: element.stroke,
2556
+ strokeWidth: element.strokeWidth,
2557
+ cropX: element.cropX,
2558
+ cropY: element.cropY,
2559
+ cropWidth: element.cropWidth,
2560
+ cropHeight: element.cropHeight,
2561
+ cornerRadius: element.cornerRadius,
2562
+ filters: element.filters,
2563
+ mask: element.mask,
2564
+ shapeType: element.shapeType,
2565
+ align: element.align,
2566
+ lineHeight: element.lineHeight,
2567
+ letterSpacing: element.letterSpacing,
2568
+ textCase: element.textCase,
2569
+ strikethrough: element.strikethrough,
2570
+ textWidth: element.textWidth,
2571
+ textHeight: element.textHeight,
2572
+ userInput: element.userInput
2573
+ };
2574
+ });
2575
+ const convertedPage = {
2576
+ id: page.id || `${Date.now()}-${index}`,
2577
+ name: page.name || `Page ${index + 1}`,
2578
+ elements,
2579
+ background: page.background || "#ffffff"
2580
+ };
2581
+ return convertedPage;
2582
+ });
2583
+ } else {
2584
+ converted.pages = [
2585
+ {
2586
+ id: "1",
2587
+ name: "Page 1",
2588
+ elements: [],
2589
+ background: "#ffffff"
2590
+ }
2591
+ ];
2592
+ }
2593
+ return converted;
2594
+ }
2595
+ function findRequiredInputs(design) {
2596
+ const inputs = [];
2597
+ let hasImageInput = false;
2598
+ let hasTextInput = false;
2599
+ design.pages.forEach((page) => {
2600
+ page.elements.forEach((element) => {
2601
+ if (element.userInput) {
2602
+ inputs.push({
2603
+ config: element.userInput,
2604
+ elementId: element.id,
2605
+ elementType: element.type
2606
+ });
2607
+ if (element.userInput.type === "image") {
2608
+ hasImageInput = true;
2609
+ } else if (element.userInput.type === "text") {
2610
+ hasTextInput = true;
2611
+ }
2612
+ } else if (element.type === "image" && element.src?.match(/{{.*}}/)) {
2613
+ const match = element.src.match(/{{(.*?)}}/);
2614
+ const placeholderId = match ? match[1].trim() : "image";
2615
+ inputs.push({
2616
+ config: {
2617
+ required: true,
2618
+ type: "image",
2619
+ id: placeholderId,
2620
+ label: `Upload ${placeholderId}`,
2621
+ property: "src",
2622
+ accept: "image/*"
2623
+ },
2624
+ elementId: element.id,
2625
+ elementType: element.type
2626
+ });
2627
+ hasImageInput = true;
2628
+ } else if (element.type === "text" && element.text?.match(/{{.*}}/)) {
2629
+ const match = element.text.match(/{{(.*?)}}/);
2630
+ const placeholderId = match ? match[1].trim() : "text";
2631
+ inputs.push({
2632
+ config: {
2633
+ required: true,
2634
+ type: "text",
2635
+ id: placeholderId,
2636
+ label: `Enter ${placeholderId}`,
2637
+ property: "text",
2638
+ placeholder: `Enter ${placeholderId}`
2639
+ },
2640
+ elementId: element.id,
2641
+ elementType: element.type
2642
+ });
2643
+ hasTextInput = true;
2644
+ }
2645
+ });
2646
+ });
2647
+ if (design.templateInputs && Array.isArray(design.templateInputs)) {
2648
+ design.templateInputs.forEach((config) => {
2649
+ inputs.push({
2650
+ config,
2651
+ elementId: "",
2652
+ elementType: config.type
2653
+ });
2654
+ if (config.type === "image") {
2655
+ hasImageInput = true;
2656
+ } else if (config.type === "text") {
2657
+ hasTextInput = true;
2658
+ }
2659
+ });
2660
+ }
2661
+ if (!hasImageInput) {
2662
+ inputs.push({
2663
+ config: {
2664
+ required: false,
2665
+ type: "image",
2666
+ id: "optionalImage",
2667
+ label: "Optional Image Upload",
2668
+ property: "optionalImage",
2669
+ accept: "image/*",
2670
+ maxSize: 5242880,
2671
+ helpText: "You can optionally upload an image",
2672
+ order: 999
2673
+ },
2674
+ elementId: "",
2675
+ elementType: "image"
2676
+ });
2677
+ }
2678
+ if (!hasTextInput) {
2679
+ inputs.push({
2680
+ config: {
2681
+ required: false,
2682
+ type: "text",
2683
+ id: "optionalText",
2684
+ label: "Optional Text",
2685
+ property: "optionalText",
2686
+ placeholder: "Enter optional text",
2687
+ helpText: "You can optionally enter some text",
2688
+ order: 1000
2689
+ },
2690
+ elementId: "",
2691
+ elementType: "text"
2692
+ });
2693
+ }
2694
+ return inputs.sort((a, b) => (a.config.order || 0) - (b.config.order || 0));
2695
+ }
2696
+ function replaceUserInputs(design, collectedInputs) {
2697
+ const updatedDesign = { ...design };
2698
+ updatedDesign.pages = updatedDesign.pages.map((page) => ({
2699
+ ...page,
2700
+ elements: page.elements.map((element) => {
2701
+ if (element.userInput) {
2702
+ const inputId = element.userInput.id;
2703
+ const inputValue = collectedInputs[inputId];
2704
+ const propertyToReplace = element.userInput.property || (element.type === "image" ? "src" : "text");
2705
+ if (inputValue !== undefined) {
2706
+ return {
2707
+ ...element,
2708
+ [propertyToReplace]: inputValue,
2709
+ userInput: undefined
2710
+ };
2711
+ }
2712
+ } else if (element.type === "image" && element.src?.match(/{{.*}}/)) {
2713
+ const match = element.src.match(/{{(.*?)}}/);
2714
+ if (match) {
2715
+ const placeholderId = match[1].trim();
2716
+ const inputValue = collectedInputs[placeholderId];
2717
+ if (inputValue !== undefined) {
2718
+ return {
2719
+ ...element,
2720
+ src: inputValue
2721
+ };
2722
+ }
2723
+ }
2724
+ } else if (element.type === "text" && element.text?.match(/{{.*}}/)) {
2725
+ const match = element.text.match(/{{(.*?)}}/);
2726
+ if (match) {
2727
+ const placeholderId = match[1].trim();
2728
+ const inputValue = collectedInputs[placeholderId];
2729
+ if (inputValue !== undefined) {
2730
+ return {
2731
+ ...element,
2732
+ text: inputValue
2733
+ };
2734
+ }
2735
+ }
2736
+ }
2737
+ return element;
2738
+ })
2739
+ }));
2740
+ return updatedDesign;
2741
+ }
2742
+ var exportToPNG = (stage, design) => {
2743
+ if (stage) {
2744
+ const oldScale = stage.scaleX();
2745
+ stage.scale({ x: 1, y: 1 });
2746
+ const uri = stage.toDataURL({ pixelRatio: 2 });
2747
+ stage.scale({ x: oldScale, y: oldScale });
2748
+ const link = document.createElement("a");
2749
+ link.download = `${design.name}.png`;
2750
+ link.href = uri;
2751
+ document.body.appendChild(link);
2752
+ link.click();
2753
+ document.body.removeChild(link);
2754
+ }
2755
+ };
2756
+ var exportToJPG = (stage, design) => {
2757
+ if (stage) {
2758
+ const oldScale = stage.scaleX();
2759
+ stage.scale({ x: 1, y: 1 });
2760
+ const uri = stage.toDataURL({
2761
+ pixelRatio: 2,
2762
+ mimeType: "image/jpeg",
2763
+ quality: 0.9
2764
+ });
2765
+ stage.scale({ x: oldScale, y: oldScale });
2766
+ const link = document.createElement("a");
2767
+ link.download = `${design.name}.jpg`;
2768
+ link.href = uri;
2769
+ document.body.appendChild(link);
2770
+ link.click();
2771
+ document.body.removeChild(link);
2772
+ }
2773
+ };
2774
+ var exportToJSON = (design) => {
2775
+ const json = JSON.stringify(design, null, 2);
2776
+ const blob = new Blob([json], { type: "application/json" });
2777
+ const url = URL.createObjectURL(blob);
2778
+ const link = document.createElement("a");
2779
+ link.download = `${design.name}.json`;
2780
+ link.href = url;
2781
+ document.body.appendChild(link);
2782
+ link.click();
2783
+ document.body.removeChild(link);
2784
+ URL.revokeObjectURL(url);
2785
+ };
2786
+ var importFromJSON = (event, onLoad, onError, onInputsRequired) => {
2787
+ const file = event.target.files?.[0];
2788
+ if (file) {
2789
+ const reader = new FileReader;
2790
+ reader.onload = (e) => {
2791
+ try {
2792
+ const result = e.target?.result;
2793
+ if (typeof result === "string") {
2794
+ const json = JSON.parse(result);
2795
+ const convertedDesign = convertTemplateToCanvasDesign(json);
2796
+ const requiredInputs = findRequiredInputs(convertedDesign);
2797
+ if (requiredInputs.length > 0 && onInputsRequired) {
2798
+ onInputsRequired(requiredInputs, (collectedValues) => {
2799
+ const finalDesign = replaceUserInputs(convertedDesign, collectedValues);
2800
+ onLoad(finalDesign);
2801
+ });
2802
+ } else {
2803
+ onLoad(convertedDesign);
2804
+ }
2805
+ } else {
2806
+ onError("Failed to read file content as text.");
2807
+ }
2808
+ } catch (error) {
2809
+ onError(`Invalid JSON file: ${error instanceof Error ? error.message : String(error)}`);
2810
+ }
2811
+ };
2812
+ reader.onerror = () => {
2813
+ onError("Failed to read file.");
2814
+ };
2815
+ reader.readAsText(file);
2816
+ } else {}
2817
+ };
2818
+
2819
+ // src/components/TemplateInputModal.tsx
2820
+ import { useState as useState2, useRef as useRef4 } from "react";
2821
+ import { X, Upload as Upload2, Image as ImageIcon4, ChevronLeft, ChevronRight } from "lucide-react";
2822
+ import { jsxDEV as jsxDEV17 } from "react/jsx-dev-runtime";
2823
+ var TemplateInputModal = ({
2824
+ inputs,
2825
+ onComplete,
2826
+ onCancel
2827
+ }) => {
2828
+ const [values, setValues] = useState2({});
2829
+ const [errors, setErrors] = useState2({});
2830
+ const [currentStep, setCurrentStep] = useState2(0);
2831
+ const fileInputRefs = useRef4({});
2832
+ const sortedInputs = [...inputs].sort((a, b) => (a.config.order || 0) - (b.config.order || 0));
2833
+ const currentInput = sortedInputs[currentStep];
2834
+ const isFirstStep = currentStep === 0;
2835
+ const isLastStep = currentStep === sortedInputs.length - 1;
2836
+ const handleTextChange = (id, value) => {
2837
+ setValues((prev) => ({ ...prev, [id]: value }));
2838
+ if (errors[id]) {
2839
+ setErrors((prev) => {
2840
+ const newErrors = { ...prev };
2841
+ delete newErrors[id];
2842
+ return newErrors;
2843
+ });
2844
+ }
2845
+ };
2846
+ const handleFileChange = (id, file) => {
2847
+ if (!file) {
2848
+ setValues((prev) => {
2849
+ const newValues = { ...prev };
2850
+ delete newValues[id];
2851
+ return newValues;
2852
+ });
2853
+ return;
2854
+ }
2855
+ const config = inputs.find((input) => input.config.id === id)?.config;
2856
+ if (!config)
2857
+ return;
2858
+ if (config.accept && !file.type.match(config.accept.replace("*", ".*"))) {
2859
+ setErrors((prev) => ({
2860
+ ...prev,
2861
+ [id]: `File must be of type: ${config.accept}`
2862
+ }));
2863
+ return;
2864
+ }
2865
+ if (config.maxSize && file.size > config.maxSize) {
2866
+ const maxSizeMB = (config.maxSize / 1024 / 1024).toFixed(2);
2867
+ setErrors((prev) => ({
2868
+ ...prev,
2869
+ [id]: `File size must be less than ${maxSizeMB}MB`
2870
+ }));
2871
+ return;
2872
+ }
2873
+ const reader = new FileReader;
2874
+ reader.onload = (e) => {
2875
+ const result = e.target?.result;
2876
+ if (typeof result === "string") {
2877
+ setValues((prev) => ({ ...prev, [id]: result }));
2878
+ setErrors((prev) => {
2879
+ const newErrors = { ...prev };
2880
+ delete newErrors[id];
2881
+ return newErrors;
2882
+ });
2883
+ }
2884
+ };
2885
+ reader.onerror = () => {
2886
+ setErrors((prev) => ({
2887
+ ...prev,
2888
+ [id]: "Failed to read file"
2889
+ }));
2890
+ };
2891
+ reader.readAsDataURL(file);
2892
+ };
2893
+ const validateCurrentStep = () => {
2894
+ if (!currentInput)
2895
+ return false;
2896
+ const config = currentInput.config;
2897
+ const value = values[config.id];
2898
+ const newErrors = { ...errors };
2899
+ delete newErrors[config.id];
2900
+ if (config.required && !value) {
2901
+ newErrors[config.id] = "This field is required";
2902
+ setErrors(newErrors);
2903
+ return false;
2904
+ }
2905
+ if (config.type === "text" && value) {
2906
+ if (config.minLength && value.length < config.minLength) {
2907
+ newErrors[config.id] = `Minimum length is ${config.minLength} characters`;
2908
+ setErrors(newErrors);
2909
+ return false;
2910
+ }
2911
+ if (config.maxLength && value.length > config.maxLength) {
2912
+ newErrors[config.id] = `Maximum length is ${config.maxLength} characters`;
2913
+ setErrors(newErrors);
2914
+ return false;
2915
+ }
2916
+ }
2917
+ setErrors(newErrors);
2918
+ return true;
2919
+ };
2920
+ const handleNext = () => {
2921
+ if (!validateCurrentStep()) {
2922
+ return;
2923
+ }
2924
+ if (isLastStep) {
2925
+ const allErrors = {};
2926
+ sortedInputs.forEach((input) => {
2927
+ if (input.config.required && !values[input.config.id]) {
2928
+ allErrors[input.config.id] = "This field is required";
2929
+ }
2930
+ });
2931
+ if (Object.keys(allErrors).length > 0) {
2932
+ setErrors(allErrors);
2933
+ const firstErrorIndex = sortedInputs.findIndex((input) => allErrors[input.config.id]);
2934
+ if (firstErrorIndex !== -1) {
2935
+ setCurrentStep(firstErrorIndex);
2936
+ }
2937
+ return;
2938
+ }
2939
+ onComplete(values);
2940
+ } else {
2941
+ setCurrentStep((prev) => prev + 1);
2942
+ }
2943
+ };
2944
+ const handlePrevious = () => {
2945
+ if (!isFirstStep) {
2946
+ setCurrentStep((prev) => prev - 1);
2947
+ }
2948
+ };
2949
+ const handleSkip = () => {
2950
+ if (!currentInput.config.required) {
2951
+ const defaultValue = currentInput.config.defaultValue || "";
2952
+ const valueToSet = defaultValue;
2953
+ setValues((prev) => ({ ...prev, [currentInput.config.id]: valueToSet }));
2954
+ setErrors((prev) => {
2955
+ const newErrors = { ...prev };
2956
+ delete newErrors[currentInput.config.id];
2957
+ return newErrors;
2958
+ });
2959
+ const updatedValues = { ...values, [currentInput.config.id]: valueToSet };
2960
+ if (isLastStep) {
2961
+ const allErrors = {};
2962
+ sortedInputs.forEach((input) => {
2963
+ if (input.config.required && !updatedValues[input.config.id]) {
2964
+ allErrors[input.config.id] = "This field is required";
2965
+ }
2966
+ });
2967
+ if (Object.keys(allErrors).length > 0) {
2968
+ setErrors(allErrors);
2969
+ const firstErrorIndex = sortedInputs.findIndex((input) => allErrors[input.config.id]);
2970
+ if (firstErrorIndex !== -1) {
2971
+ setCurrentStep(firstErrorIndex);
2972
+ }
2973
+ return;
2974
+ }
2975
+ onComplete(updatedValues);
2976
+ } else {
2977
+ setCurrentStep((prev) => prev + 1);
2978
+ }
2979
+ }
2980
+ };
2981
+ const renderInput = (input) => {
2982
+ const { config } = input;
2983
+ const defaultValue = config.defaultValue || "";
2984
+ const value = values[config.id] !== undefined ? values[config.id] : defaultValue;
2985
+ const error = errors[config.id];
2986
+ switch (config.type) {
2987
+ case "image": {
2988
+ const isUrl = value && (value.startsWith("http://") || value.startsWith("https://") || value.startsWith("data:"));
2989
+ const isDefaultValue = value === defaultValue && defaultValue && (defaultValue.startsWith("http://") || defaultValue.startsWith("https://"));
2990
+ return /* @__PURE__ */ jsxDEV17("div", {
2991
+ className: "space-y-2",
2992
+ children: [
2993
+ /* @__PURE__ */ jsxDEV17("label", {
2994
+ className: "block text-sm font-medium text-gray-300",
2995
+ children: [
2996
+ config.label,
2997
+ config.required && /* @__PURE__ */ jsxDEV17("span", {
2998
+ className: "text-red-400 ml-1",
2999
+ children: "*"
3000
+ }, undefined, false, undefined, this)
3001
+ ]
3002
+ }, undefined, true, undefined, this),
3003
+ config.helpText && /* @__PURE__ */ jsxDEV17("p", {
3004
+ className: "text-xs text-gray-400",
3005
+ children: config.helpText
3006
+ }, undefined, false, undefined, this),
3007
+ isDefaultValue && /* @__PURE__ */ jsxDEV17("p", {
3008
+ className: "text-xs text-blue-400",
3009
+ children: "Using default image"
3010
+ }, undefined, false, undefined, this),
3011
+ /* @__PURE__ */ jsxDEV17("div", {
3012
+ className: "flex items-center space-x-4",
3013
+ children: [
3014
+ /* @__PURE__ */ jsxDEV17("input", {
3015
+ ref: (el) => {
3016
+ fileInputRefs.current[config.id] = el;
3017
+ },
3018
+ type: "file",
3019
+ accept: config.accept || "image/*",
3020
+ onChange: (e) => handleFileChange(config.id, e.target.files?.[0] || null),
3021
+ className: "hidden"
3022
+ }, undefined, false, undefined, this),
3023
+ /* @__PURE__ */ jsxDEV17("button", {
3024
+ type: "button",
3025
+ onClick: () => fileInputRefs.current[config.id]?.click(),
3026
+ className: `flex items-center space-x-2 px-4 py-2 rounded border-2 border-dashed transition-colors ${value ? "border-green-500 bg-green-500/10 text-green-400" : "border-gray-600 bg-gray-700/50 text-gray-300 hover:border-gray-500"}`,
3027
+ children: [
3028
+ /* @__PURE__ */ jsxDEV17(Upload2, {
3029
+ className: "w-4 h-4"
3030
+ }, undefined, false, undefined, this),
3031
+ /* @__PURE__ */ jsxDEV17("span", {
3032
+ children: value ? "Change Image" : "Upload Image"
3033
+ }, undefined, false, undefined, this)
3034
+ ]
3035
+ }, undefined, true, undefined, this),
3036
+ value && /* @__PURE__ */ jsxDEV17("div", {
3037
+ className: "flex items-center space-x-2",
3038
+ children: [
3039
+ /* @__PURE__ */ jsxDEV17(ImageIcon4, {
3040
+ className: "w-5 h-5 text-green-400"
3041
+ }, undefined, false, undefined, this),
3042
+ /* @__PURE__ */ jsxDEV17("span", {
3043
+ className: "text-sm text-gray-400",
3044
+ children: isUrl && !isDefaultValue ? "Image URL" : isDefaultValue ? "Default image" : "Image selected"
3045
+ }, undefined, false, undefined, this),
3046
+ /* @__PURE__ */ jsxDEV17("button", {
3047
+ type: "button",
3048
+ onClick: () => {
3049
+ const resetValue = defaultValue || "";
3050
+ setValues((prev) => ({ ...prev, [config.id]: resetValue }));
3051
+ setErrors((prev) => {
3052
+ const newErrors = { ...prev };
3053
+ delete newErrors[config.id];
3054
+ return newErrors;
3055
+ });
3056
+ },
3057
+ className: "text-red-400 hover:text-red-300 text-sm",
3058
+ children: isDefaultValue ? "Clear" : "Remove"
3059
+ }, undefined, false, undefined, this)
3060
+ ]
3061
+ }, undefined, true, undefined, this)
3062
+ ]
3063
+ }, undefined, true, undefined, this),
3064
+ value && isUrl && /* @__PURE__ */ jsxDEV17("div", {
3065
+ className: "mt-2",
3066
+ children: /* @__PURE__ */ jsxDEV17("img", {
3067
+ src: value,
3068
+ alt: "Preview",
3069
+ className: "max-w-xs max-h-32 object-contain rounded border border-gray-600",
3070
+ onError: (e) => {
3071
+ setErrors((prev) => ({
3072
+ ...prev,
3073
+ [config.id]: "Failed to load image"
3074
+ }));
3075
+ }
3076
+ }, undefined, false, undefined, this)
3077
+ }, undefined, false, undefined, this),
3078
+ error && /* @__PURE__ */ jsxDEV17("p", {
3079
+ className: "text-sm text-red-400",
3080
+ children: error
3081
+ }, undefined, false, undefined, this)
3082
+ ]
3083
+ }, undefined, true, undefined, this);
3084
+ }
3085
+ case "text":
3086
+ return /* @__PURE__ */ jsxDEV17("div", {
3087
+ className: "space-y-2",
3088
+ children: [
3089
+ /* @__PURE__ */ jsxDEV17("label", {
3090
+ className: "block text-sm font-medium text-gray-300",
3091
+ children: [
3092
+ config.label,
3093
+ config.required && /* @__PURE__ */ jsxDEV17("span", {
3094
+ className: "text-red-400 ml-1",
3095
+ children: "*"
3096
+ }, undefined, false, undefined, this)
3097
+ ]
3098
+ }, undefined, true, undefined, this),
3099
+ config.helpText && /* @__PURE__ */ jsxDEV17("p", {
3100
+ className: "text-xs text-gray-400",
3101
+ children: config.helpText
3102
+ }, undefined, false, undefined, this),
3103
+ /* @__PURE__ */ jsxDEV17("input", {
3104
+ type: "text",
3105
+ value,
3106
+ onChange: (e) => handleTextChange(config.id, e.target.value),
3107
+ placeholder: config.placeholder,
3108
+ maxLength: config.maxLength,
3109
+ className: `w-full px-3 py-2 bg-gray-700 text-white border rounded focus:outline-none focus:ring-2 focus:ring-blue-500 ${error ? "border-red-500" : "border-gray-600"}`
3110
+ }, undefined, false, undefined, this),
3111
+ config.maxLength && /* @__PURE__ */ jsxDEV17("p", {
3112
+ className: "text-xs text-gray-400 text-right",
3113
+ children: [
3114
+ value.length,
3115
+ " / ",
3116
+ config.maxLength
3117
+ ]
3118
+ }, undefined, true, undefined, this),
3119
+ error && /* @__PURE__ */ jsxDEV17("p", {
3120
+ className: "text-sm text-red-400",
3121
+ children: error
3122
+ }, undefined, false, undefined, this)
3123
+ ]
3124
+ }, undefined, true, undefined, this);
3125
+ default:
3126
+ return null;
3127
+ }
3128
+ };
3129
+ if (!currentInput) {
3130
+ return null;
3131
+ }
3132
+ return /* @__PURE__ */ jsxDEV17("div", {
3133
+ className: "fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 p-4",
3134
+ children: /* @__PURE__ */ jsxDEV17("div", {
3135
+ className: "bg-gray-800 rounded-lg w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col",
3136
+ children: [
3137
+ /* @__PURE__ */ jsxDEV17("div", {
3138
+ className: "p-6 border-b border-gray-700 flex items-center justify-between",
3139
+ children: [
3140
+ /* @__PURE__ */ jsxDEV17("div", {
3141
+ children: [
3142
+ /* @__PURE__ */ jsxDEV17("h2", {
3143
+ className: "text-xl font-semibold text-white",
3144
+ children: "Template Input Required"
3145
+ }, undefined, false, undefined, this),
3146
+ /* @__PURE__ */ jsxDEV17("p", {
3147
+ className: "text-sm text-gray-400 mt-1",
3148
+ children: [
3149
+ "Step ",
3150
+ currentStep + 1,
3151
+ " of ",
3152
+ sortedInputs.length
3153
+ ]
3154
+ }, undefined, true, undefined, this)
3155
+ ]
3156
+ }, undefined, true, undefined, this),
3157
+ /* @__PURE__ */ jsxDEV17("button", {
3158
+ onClick: onCancel,
3159
+ className: "text-gray-400 hover:text-white transition-colors",
3160
+ children: /* @__PURE__ */ jsxDEV17(X, {
3161
+ className: "w-5 h-5"
3162
+ }, undefined, false, undefined, this)
3163
+ }, undefined, false, undefined, this)
3164
+ ]
3165
+ }, undefined, true, undefined, this),
3166
+ /* @__PURE__ */ jsxDEV17("div", {
3167
+ className: "px-6 pt-4",
3168
+ children: /* @__PURE__ */ jsxDEV17("div", {
3169
+ className: "w-full bg-gray-700 rounded-full h-2",
3170
+ children: /* @__PURE__ */ jsxDEV17("div", {
3171
+ className: "bg-blue-600 h-2 rounded-full transition-all duration-300",
3172
+ style: {
3173
+ width: `${(currentStep + 1) / sortedInputs.length * 100}%`
3174
+ }
3175
+ }, undefined, false, undefined, this)
3176
+ }, undefined, false, undefined, this)
3177
+ }, undefined, false, undefined, this),
3178
+ /* @__PURE__ */ jsxDEV17("div", {
3179
+ className: "flex-1 overflow-y-auto p-6",
3180
+ children: /* @__PURE__ */ jsxDEV17("div", {
3181
+ className: "space-y-4",
3182
+ children: renderInput(currentInput)
3183
+ }, undefined, false, undefined, this)
3184
+ }, undefined, false, undefined, this),
3185
+ /* @__PURE__ */ jsxDEV17("div", {
3186
+ className: "p-6 border-t border-gray-700 flex items-center justify-between",
3187
+ children: [
3188
+ /* @__PURE__ */ jsxDEV17("button", {
3189
+ onClick: onCancel,
3190
+ className: "px-4 py-2 bg-gray-700 text-white rounded hover:bg-gray-600 transition-colors",
3191
+ children: "Cancel"
3192
+ }, undefined, false, undefined, this),
3193
+ /* @__PURE__ */ jsxDEV17("div", {
3194
+ className: "flex items-center space-x-3",
3195
+ children: [
3196
+ !isFirstStep && /* @__PURE__ */ jsxDEV17("button", {
3197
+ onClick: handlePrevious,
3198
+ className: "px-4 py-2 bg-gray-700 text-white rounded hover:bg-gray-600 transition-colors flex items-center space-x-2",
3199
+ children: [
3200
+ /* @__PURE__ */ jsxDEV17(ChevronLeft, {
3201
+ className: "w-4 h-4"
3202
+ }, undefined, false, undefined, this),
3203
+ /* @__PURE__ */ jsxDEV17("span", {
3204
+ children: "Previous"
3205
+ }, undefined, false, undefined, this)
3206
+ ]
3207
+ }, undefined, true, undefined, this),
3208
+ !currentInput.config.required && /* @__PURE__ */ jsxDEV17("button", {
3209
+ onClick: handleSkip,
3210
+ className: "px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-500 transition-colors",
3211
+ children: "Skip"
3212
+ }, undefined, false, undefined, this),
3213
+ /* @__PURE__ */ jsxDEV17("button", {
3214
+ onClick: handleNext,
3215
+ className: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors flex items-center space-x-2",
3216
+ children: [
3217
+ /* @__PURE__ */ jsxDEV17("span", {
3218
+ children: isLastStep ? "Complete" : "Next"
3219
+ }, undefined, false, undefined, this),
3220
+ !isLastStep && /* @__PURE__ */ jsxDEV17(ChevronRight, {
3221
+ className: "w-4 h-4"
3222
+ }, undefined, false, undefined, this)
3223
+ ]
3224
+ }, undefined, true, undefined, this)
3225
+ ]
3226
+ }, undefined, true, undefined, this)
3227
+ ]
3228
+ }, undefined, true, undefined, this)
3229
+ ]
3230
+ }, undefined, true, undefined, this)
3231
+ }, undefined, false, undefined, this);
3232
+ };
3233
+ var TemplateInputModal_default = TemplateInputModal;
3234
+
3235
+ // src/CanvasEditor.tsx
3236
+ import { jsxDEV as jsxDEV18 } from "react/jsx-dev-runtime";
3237
+ var CanvasEditor = ({
3238
+ name,
3239
+ config
3240
+ }) => {
3241
+ const [design, setDesign] = useState3({
3242
+ id: Date.now().toString(),
3243
+ name: "Untitled Design",
3244
+ width: 800,
3245
+ height: 600,
3246
+ pages: [
3247
+ {
3248
+ id: "1",
3249
+ name: "Page 1",
3250
+ elements: [],
3251
+ background: "#ffffff"
3252
+ }
3253
+ ],
3254
+ fonts: FONT_FAMILIES,
3255
+ colors: DEFAULT_COLORS
3256
+ });
3257
+ const [currentPageId, setCurrentPageId] = useState3("1");
3258
+ const [selectedId, setSelectedId] = useState3(null);
3259
+ const [selectedIds, setSelectedIds] = useState3([]);
3260
+ const [tool, setTool] = useState3("select");
3261
+ const [activePanelId, setActivePanelId] = useState3("elements");
3262
+ const [showUnsplash, setShowUnsplash] = useState3(false);
3263
+ const [unsplashQuery, setUnsplashQuery] = useState3("");
3264
+ const [unsplashResults, setUnsplashResults] = useState3([]);
3265
+ const [unsplashMode, setUnsplashMode] = useState3("element");
3266
+ const [zoom, setZoom] = useState3(1);
3267
+ const [selectedShape, setSelectedShape] = useState3("rect");
3268
+ const [history, setHistory] = useState3([]);
3269
+ const [historyIndex, setHistoryIndex] = useState3(-1);
3270
+ const [showInputModal, setShowInputModal] = useState3(false);
3271
+ const [pendingInputs, setPendingInputs] = useState3([]);
3272
+ const stageRef = useRef5(null);
3273
+ const fileInputRef = useRef5(null);
3274
+ const jsonInputRef = useRef5(null);
3275
+ const containerRef = useRef5(null);
3276
+ const currentPage = design.pages.find((p) => p.id === currentPageId) || design.pages[0];
3277
+ const selectedElement = currentPage.elements.find((el) => el.id === selectedId);
3278
+ const saveToHistory = useCallback2((newDesign) => {
3279
+ const newHistory = history.slice(0, historyIndex + 1);
3280
+ newHistory.push(JSON.parse(JSON.stringify(newDesign)));
3281
+ setHistory(newHistory);
3282
+ setHistoryIndex(newHistory.length - 1);
3283
+ }, [history, historyIndex]);
3284
+ const undo = useCallback2(() => {
3285
+ if (historyIndex > 0) {
3286
+ setHistoryIndex(historyIndex - 1);
3287
+ setDesign(JSON.parse(JSON.stringify(history[historyIndex - 1])));
3288
+ }
3289
+ }, [history, historyIndex]);
3290
+ const redo = useCallback2(() => {
3291
+ if (historyIndex < history.length - 1) {
3292
+ setHistoryIndex(historyIndex + 1);
3293
+ setDesign(JSON.parse(JSON.stringify(history[historyIndex + 1])));
3294
+ }
3295
+ }, [history, historyIndex]);
3296
+ const addText = useCallback2(() => {
3297
+ const newElement = {
3298
+ id: Date.now().toString(),
3299
+ type: "text",
3300
+ name: "Text",
3301
+ x: design.width / 2 - 50,
3302
+ y: design.height / 2 - 20,
3303
+ rotation: 0,
3304
+ visible: true,
3305
+ locked: false,
3306
+ opacity: 1,
3307
+ text: "Click to edit text",
3308
+ fontSize: 24,
3309
+ fontFamily: "Arial",
3310
+ fill: "#000000",
3311
+ align: "left",
3312
+ lineHeight: 1.2,
3313
+ letterSpacing: 0,
3314
+ textCase: "none",
3315
+ strikethrough: false
3316
+ };
3317
+ setDesign((prev) => {
3318
+ const newDesign = {
3319
+ ...prev,
3320
+ pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3321
+ };
3322
+ saveToHistory(newDesign);
3323
+ return newDesign;
3324
+ });
3325
+ setSelectedId(newElement.id);
3326
+ setTool("select");
3327
+ }, [currentPageId, saveToHistory, design.width, design.height]);
3328
+ const addShape = useCallback2((shapeType) => {
3329
+ const newElement = {
3330
+ id: Date.now().toString(),
3331
+ type: "shape",
3332
+ name: shapeType.charAt(0).toUpperCase() + shapeType.slice(1),
3333
+ x: design.width / 2 - 50,
3334
+ y: design.height / 2 - 50,
3335
+ width: 100,
3336
+ height: 100,
3337
+ rotation: 0,
3338
+ visible: true,
3339
+ locked: false,
3340
+ opacity: 1,
3341
+ fill: "#4299e1",
3342
+ stroke: "#000000",
3343
+ strokeWidth: 0,
3344
+ shapeType,
3345
+ sides: shapeType === "polygon" ? 6 : undefined,
3346
+ innerRadius: shapeType === "star" ? 40 : undefined,
3347
+ outerRadius: shapeType === "star" ? 70 : undefined
3348
+ };
3349
+ setDesign((prev) => {
3350
+ const newDesign = {
3351
+ ...prev,
3352
+ pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3353
+ };
3354
+ saveToHistory(newDesign);
3355
+ return newDesign;
3356
+ });
3357
+ setSelectedId(newElement.id);
3358
+ }, [currentPageId, saveToHistory, design.width, design.height]);
3359
+ const handleStageClick = useCallback2((e) => {
3360
+ const clickedOnEmpty = e.target === e.target.getStage();
3361
+ if (clickedOnEmpty) {
3362
+ setSelectedId(null);
3363
+ setSelectedIds([]);
3364
+ }
3365
+ }, []);
3366
+ const updateElement = useCallback2((id, attrs) => {
3367
+ setDesign((prev) => ({
3368
+ ...prev,
3369
+ pages: prev.pages.map((page) => ({
3370
+ ...page,
3371
+ elements: page.elements.map((el) => el.id === id ? { ...el, ...attrs } : el)
3372
+ }))
3373
+ }));
3374
+ }, []);
3375
+ const deleteSelected = useCallback2(() => {
3376
+ if (selectedId || selectedIds.length > 0) {
3377
+ const idsToDelete = selectedIds.length > 0 ? selectedIds : [selectedId];
3378
+ setDesign((prev) => ({
3379
+ ...prev,
3380
+ pages: prev.pages.map((page) => ({
3381
+ ...page,
3382
+ elements: page.elements.filter((el) => !idsToDelete.includes(el.id))
3383
+ }))
3384
+ }));
3385
+ setSelectedId(null);
3386
+ setSelectedIds([]);
3387
+ saveToHistory(design);
3388
+ }
3389
+ }, [selectedId, selectedIds, saveToHistory, design]);
3390
+ const duplicateSelected = useCallback2(() => {
3391
+ if (selectedElement) {
3392
+ const newElement = {
3393
+ ...selectedElement,
3394
+ id: Date.now().toString(),
3395
+ name: selectedElement.name + " copy",
3396
+ x: selectedElement.x + 20,
3397
+ y: selectedElement.y + 20
3398
+ };
3399
+ setDesign((prev) => {
3400
+ const newDesign = {
3401
+ ...prev,
3402
+ pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3403
+ };
3404
+ saveToHistory(newDesign);
3405
+ return newDesign;
3406
+ });
3407
+ setSelectedId(newElement.id);
3408
+ }
3409
+ }, [selectedElement, currentPageId, saveToHistory]);
3410
+ const moveElementUp = useCallback2((id) => {
3411
+ setDesign((prev) => ({
3412
+ ...prev,
3413
+ pages: prev.pages.map((page) => {
3414
+ if (page.id === currentPageId) {
3415
+ const index = page.elements.findIndex((el) => el.id === id);
3416
+ if (index < page.elements.length - 1) {
3417
+ const newElements = [...page.elements];
3418
+ [newElements[index], newElements[index + 1]] = [
3419
+ newElements[index + 1],
3420
+ newElements[index]
3421
+ ];
3422
+ return { ...page, elements: newElements };
3423
+ }
3424
+ }
3425
+ return page;
3426
+ })
3427
+ }));
3428
+ }, [currentPageId]);
3429
+ const moveElementDown = useCallback2((id) => {
3430
+ setDesign((prev) => ({
3431
+ ...prev,
3432
+ pages: prev.pages.map((page) => {
3433
+ if (page.id === currentPageId) {
3434
+ const index = page.elements.findIndex((el) => el.id === id);
3435
+ if (index > 0) {
3436
+ const newElements = [...page.elements];
3437
+ [newElements[index], newElements[index - 1]] = [
3438
+ newElements[index - 1],
3439
+ newElements[index]
3440
+ ];
3441
+ return { ...page, elements: newElements };
3442
+ }
3443
+ }
3444
+ return page;
3445
+ })
3446
+ }));
3447
+ }, [currentPageId]);
3448
+ const addPage = useCallback2(() => {
3449
+ const newPage = {
3450
+ id: Date.now().toString(),
3451
+ name: `Page ${design.pages.length + 1}`,
3452
+ elements: [],
3453
+ background: "#ffffff",
3454
+ backgroundImageObject: undefined
3455
+ };
3456
+ setDesign((prev) => ({
3457
+ ...prev,
3458
+ pages: [...prev.pages, newPage]
3459
+ }));
3460
+ setCurrentPageId(newPage.id);
3461
+ }, [design.pages.length]);
3462
+ const deletePage = useCallback2((pageId) => {
3463
+ if (design.pages.length > 1) {
3464
+ setDesign((prev) => ({
3465
+ ...prev,
3466
+ pages: prev.pages.filter((p) => p.id !== pageId)
3467
+ }));
3468
+ if (currentPageId === pageId) {
3469
+ setCurrentPageId(design.pages[0].id);
3470
+ }
3471
+ }
3472
+ }, [design.pages, currentPageId]);
3473
+ const handleImageUpload = useCallback2((e) => {
3474
+ const file = e.target.files?.[0];
3475
+ if (file) {
3476
+ const reader = new FileReader;
3477
+ reader.onload = (event) => {
3478
+ const img = new window.Image;
3479
+ img.src = event.target?.result;
3480
+ img.onload = () => {
3481
+ const newElement = {
3482
+ id: Date.now().toString(),
3483
+ type: "image",
3484
+ name: file.name,
3485
+ x: 100,
3486
+ y: 100,
3487
+ width: img.naturalWidth,
3488
+ height: img.naturalHeight,
3489
+ rotation: 0,
3490
+ visible: true,
3491
+ locked: false,
3492
+ opacity: 1,
3493
+ src: event.target?.result,
3494
+ cropX: 0,
3495
+ cropY: 0,
3496
+ cropWidth: img.naturalWidth,
3497
+ cropHeight: img.naturalHeight,
3498
+ mask: undefined
3499
+ };
3500
+ setDesign((prev) => ({
3501
+ ...prev,
3502
+ pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3503
+ }));
3504
+ saveToHistory(design);
3505
+ };
3506
+ };
3507
+ reader.readAsDataURL(file);
3508
+ }
3509
+ if (fileInputRef.current) {
3510
+ fileInputRef.current.value = "";
3511
+ }
3512
+ }, [currentPageId, saveToHistory]);
3513
+ const searchUnsplash = useCallback2(async () => {
3514
+ if (!unsplashQuery)
3515
+ return;
3516
+ const unsplashKey = getUnsplashAccessKey();
3517
+ if (!unsplashKey || unsplashKey === "YOUR_UNSPLASH_ACCESS_KEY_HERE") {
3518
+ console.warn("Unsplash API key is not configured. Please use setUnsplashAccessKey() or set UNSPLASH_ACCESS_KEY environment variable");
3519
+ const mockResults = Array(9).fill(null).map((_, i) => ({
3520
+ id: `unsplash-mock-${i}`,
3521
+ urls: {
3522
+ regular: `https://source.unsplash.com/400x300/?${unsplashQuery}&sig=${i}`,
3523
+ thumb: `https://source.unsplash.com/200x150/?${unsplashQuery}&sig=${i}`
3524
+ },
3525
+ user: { name: "Mock User" }
3526
+ }));
3527
+ setUnsplashResults(mockResults);
3528
+ return;
3529
+ }
3530
+ try {
3531
+ const response = await fetch(`https://api.unsplash.com/search/photos?query=${encodeURIComponent(unsplashQuery)}&per_page=9`, {
3532
+ headers: {
3533
+ Authorization: `Client-ID ${getUnsplashAccessKey() || ""}`
3534
+ }
3535
+ });
3536
+ if (!response.ok) {
3537
+ console.error("Failed to fetch from Unsplash:", response.statusText);
3538
+ setUnsplashResults([]);
3539
+ return;
3540
+ }
3541
+ const data = await response.json();
3542
+ setUnsplashResults(data.results.map((img) => ({
3543
+ id: img.id,
3544
+ urls: { regular: img.urls.regular, thumb: img.urls.thumb },
3545
+ user: { name: img.user.name }
3546
+ })));
3547
+ } catch (error) {
3548
+ console.error("Error searching Unsplash:", error);
3549
+ setUnsplashResults([]);
3550
+ }
3551
+ }, [unsplashQuery]);
3552
+ const addUnsplashImage = useCallback2((imageUrl) => {
3553
+ if (unsplashMode === "element") {
3554
+ const img = new window.Image;
3555
+ img.crossOrigin = "anonymous";
3556
+ img.src = imageUrl;
3557
+ img.onload = () => {
3558
+ const newElement = {
3559
+ id: Date.now().toString(),
3560
+ type: "image",
3561
+ name: "Unsplash Image",
3562
+ x: 100,
3563
+ y: 100,
3564
+ width: img.naturalWidth,
3565
+ height: img.naturalHeight,
3566
+ rotation: 0,
3567
+ visible: true,
3568
+ locked: false,
3569
+ opacity: 1,
3570
+ src: imageUrl,
3571
+ cropX: 0,
3572
+ cropY: 0,
3573
+ cropWidth: img.naturalWidth,
3574
+ cropHeight: img.naturalHeight,
3575
+ mask: undefined
3576
+ };
3577
+ setDesign((prev) => ({
3578
+ ...prev,
3579
+ pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
3580
+ }));
3581
+ saveToHistory(design);
3582
+ };
3583
+ } else if (unsplashMode === "background") {
3584
+ const bgImg = new window.Image;
3585
+ bgImg.crossOrigin = "anonymous";
3586
+ bgImg.src = imageUrl;
3587
+ bgImg.onload = () => {
3588
+ setDesign((prev) => ({
3589
+ ...prev,
3590
+ pages: prev.pages.map((p) => p.id === currentPageId ? {
3591
+ ...p,
3592
+ background: `url(${imageUrl})`,
3593
+ backgroundImageObject: bgImg
3594
+ } : p)
3595
+ }));
3596
+ saveToHistory(design);
3597
+ };
3598
+ }
3599
+ setShowUnsplash(false);
3600
+ setUnsplashQuery("");
3601
+ setUnsplashResults([]);
3602
+ setUnsplashMode("element");
3603
+ }, [currentPageId, saveToHistory, unsplashMode]);
3604
+ const handleImportJSON = (event) => {
3605
+ importFromJSON(event, (newDesign) => {
3606
+ if (newDesign && newDesign.pages && newDesign.pages.length > 0) {
3607
+ setDesign(newDesign);
3608
+ setSelectedId(null);
3609
+ setSelectedIds([]);
3610
+ const firstPageId = newDesign.pages[0]?.id;
3611
+ setCurrentPageId(firstPageId || "1");
3612
+ saveToHistory(newDesign);
3613
+ } else {
3614
+ alert("Invalid design structure in JSON file. Could not load.");
3615
+ }
3616
+ }, (errorMessage) => {
3617
+ alert(`Error importing JSON: ${errorMessage}`);
3618
+ }, (inputs, onComplete) => {
3619
+ setPendingInputs(inputs);
3620
+ setShowInputModal(true);
3621
+ window.__pendingImportComplete = onComplete;
3622
+ });
3623
+ if (jsonInputRef.current) {
3624
+ jsonInputRef.current.value = "";
3625
+ }
3626
+ };
3627
+ const handleInputModalComplete = (values) => {
3628
+ setShowInputModal(false);
3629
+ const onComplete = window.__pendingImportComplete;
3630
+ if (onComplete) {
3631
+ onComplete(values);
3632
+ delete window.__pendingImportComplete;
3633
+ }
3634
+ setPendingInputs([]);
3635
+ };
3636
+ const handleInputModalCancel = () => {
3637
+ setShowInputModal(false);
3638
+ setPendingInputs([]);
3639
+ delete window.__pendingImportComplete;
3640
+ };
3641
+ const panelConfigs = [
3642
+ {
3643
+ id: "elements",
3644
+ title: "Elements",
3645
+ component: ElementPanel_default,
3646
+ props: { onAddShape: addShape }
3647
+ },
3648
+ {
3649
+ id: "text",
3650
+ title: "Text",
3651
+ component: TextPanel_default,
3652
+ props: {
3653
+ selectedElement,
3654
+ updateElement,
3655
+ setTool,
3656
+ onAddText: addText
3657
+ }
3658
+ },
3659
+ {
3660
+ id: "image",
3661
+ title: "Images",
3662
+ component: ImagePanel_default,
3663
+ props: {
3664
+ selectedElement,
3665
+ updateElement,
3666
+ onUploadClick: () => fileInputRef.current?.click(),
3667
+ onUnsplashClick: () => {
3668
+ setShowUnsplash(true);
3669
+ setUnsplashMode("element");
3670
+ },
3671
+ canvasWidth: design.width,
3672
+ canvasHeight: design.height
3673
+ }
3674
+ },
3675
+ {
3676
+ id: "design",
3677
+ title: "Design",
3678
+ component: DesignPanel_default,
3679
+ props: {
3680
+ design,
3681
+ currentPage,
3682
+ selectedElement,
3683
+ setDesign,
3684
+ updateElement,
3685
+ onSetUnsplashBackground: () => {
3686
+ setShowUnsplash(true);
3687
+ setUnsplashMode("background");
3688
+ },
3689
+ config
3690
+ }
3691
+ },
3692
+ {
3693
+ id: "background",
3694
+ title: "Background",
3695
+ component: BackgroundPanel_default,
3696
+ props: {
3697
+ design,
3698
+ currentPage,
3699
+ setDesign,
3700
+ onSetUnsplashBackground: () => {
3701
+ setShowUnsplash(true);
3702
+ setUnsplashMode("background");
3703
+ }
3704
+ }
3705
+ },
3706
+ ...config?.variables ? [
3707
+ {
3708
+ id: "variables",
3709
+ title: "Variables",
3710
+ component: VariablesPanel_default,
3711
+ props: {
3712
+ config,
3713
+ design,
3714
+ setDesign
3715
+ }
3716
+ }
3717
+ ] : [],
3718
+ ...config?.export ? [
3719
+ {
3720
+ id: "export",
3721
+ title: "Export",
3722
+ component: ExportPanel_default,
3723
+ props: {
3724
+ onExportToPNG: config.export?.png ? () => {
3725
+ if (stageRef.current)
3726
+ exportToPNG(stageRef.current, design);
3727
+ } : undefined,
3728
+ onExportToJPG: config.export?.jpg ? () => {
3729
+ if (stageRef.current)
3730
+ exportToJPG(stageRef.current, design);
3731
+ } : undefined,
3732
+ onExportToJSON: config.export?.json ? () => exportToJSON(design) : undefined,
3733
+ onImportJSON: () => jsonInputRef.current?.click()
3734
+ }
3735
+ }
3736
+ ] : []
3737
+ ];
3738
+ const DynamicPanelRenderer = () => {
3739
+ const activePanelConfig = panelConfigs.find((p) => p.id === activePanelId);
3740
+ if (!activePanelConfig) {
3741
+ return /* @__PURE__ */ jsxDEV18("div", {
3742
+ className: "text-white",
3743
+ children: "Panel not found"
3744
+ }, undefined, false, undefined, this);
3745
+ }
3746
+ const PanelComponent = activePanelConfig.component;
3747
+ return /* @__PURE__ */ jsxDEV18(PanelComponent, {
3748
+ ...activePanelConfig.props
3749
+ }, undefined, false, undefined, this);
3750
+ };
3751
+ useEffect4(() => {
3752
+ const handleKeyDown = (e) => {
3753
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)
3754
+ return;
3755
+ if (e.metaKey || e.ctrlKey) {
3756
+ switch (e.key) {
3757
+ case "z":
3758
+ if (e.shiftKey) {
3759
+ redo();
3760
+ } else {
3761
+ undo();
3762
+ }
3763
+ e.preventDefault();
3764
+ break;
3765
+ case "c":
3766
+ break;
3767
+ case "v":
3768
+ break;
3769
+ case "d":
3770
+ duplicateSelected();
3771
+ e.preventDefault();
3772
+ break;
3773
+ case "s":
3774
+ exportToJSON(design);
3775
+ e.preventDefault();
3776
+ break;
3777
+ }
3778
+ } else {
3779
+ switch (e.key) {
3780
+ case "Delete":
3781
+ case "Backspace":
3782
+ deleteSelected();
3783
+ break;
3784
+ case "Escape":
3785
+ setSelectedId(null);
3786
+ setSelectedIds([]);
3787
+ break;
3788
+ }
3789
+ }
3790
+ };
3791
+ window.addEventListener("keydown", handleKeyDown);
3792
+ return () => window.removeEventListener("keydown", handleKeyDown);
3793
+ }, [deleteSelected, duplicateSelected, exportToJSON, undo, redo]);
3794
+ useEffect4(() => {
3795
+ if (history.length === 0) {
3796
+ setHistory([JSON.parse(JSON.stringify(design))]);
3797
+ setHistoryIndex(0);
3798
+ }
3799
+ }, []);
3800
+ return /* @__PURE__ */ jsxDEV18("div", {
3801
+ className: "h-screen flex flex-col bg-gray-900",
3802
+ children: [
3803
+ /* @__PURE__ */ jsxDEV18(Header_default, {
3804
+ name,
3805
+ zoom,
3806
+ historyIndex,
3807
+ historyLength: history.length,
3808
+ onUndo: undo,
3809
+ onRedo: redo,
3810
+ onZoomIn: () => setZoom(Math.min(3, zoom + 0.1)),
3811
+ onZoomOut: () => setZoom(Math.max(0.1, zoom - 0.1)),
3812
+ onZoomReset: () => setZoom(1),
3813
+ onSetActivePanel: setActivePanelId
3814
+ }, undefined, false, undefined, this),
3815
+ /* @__PURE__ */ jsxDEV18("div", {
3816
+ className: "flex-1 flex overflow-hidden",
3817
+ children: [
3818
+ /* @__PURE__ */ jsxDEV18("div", {
3819
+ className: "w-80 bg-gray-800 border-r border-gray-700 flex",
3820
+ children: [
3821
+ /* @__PURE__ */ jsxDEV18(LeftMenu_default, {
3822
+ tool,
3823
+ activePanel: activePanelId,
3824
+ onSetTool: setTool,
3825
+ onSetActivePanel: setActivePanelId,
3826
+ config
3827
+ }, undefined, false, undefined, this),
3828
+ /* @__PURE__ */ jsxDEV18("div", {
3829
+ className: "flex-1 p-4 overflow-y-auto",
3830
+ children: /* @__PURE__ */ jsxDEV18(DynamicPanelRenderer, {}, undefined, false, undefined, this)
3831
+ }, undefined, false, undefined, this)
3832
+ ]
3833
+ }, undefined, true, undefined, this),
3834
+ /* @__PURE__ */ jsxDEV18("div", {
3835
+ className: "flex-1 bg-gray-700 overflow-hidden relative",
3836
+ ref: containerRef,
3837
+ children: [
3838
+ /* @__PURE__ */ jsxDEV18("div", {
3839
+ className: "absolute inset-0 flex items-center justify-center",
3840
+ children: /* @__PURE__ */ jsxDEV18("div", {
3841
+ className: "bg-white shadow-2xl",
3842
+ style: {
3843
+ transform: `scale(${zoom})`,
3844
+ transformOrigin: "center center"
3845
+ },
3846
+ children: /* @__PURE__ */ jsxDEV18(Stage, {
3847
+ ref: stageRef,
3848
+ width: design.width,
3849
+ height: design.height,
3850
+ onClick: handleStageClick,
3851
+ onTap: handleStageClick,
3852
+ children: /* @__PURE__ */ jsxDEV18(Layer, {
3853
+ children: [
3854
+ /* @__PURE__ */ jsxDEV18(Rect2, {
3855
+ x: 0,
3856
+ y: 0,
3857
+ width: design.width,
3858
+ height: design.height,
3859
+ fill: currentPage.backgroundImageObject ? undefined : currentPage.background,
3860
+ fillPatternImage: currentPage.backgroundImageObject,
3861
+ fillPatternRepeat: "no-repeat"
3862
+ }, undefined, false, undefined, this),
3863
+ currentPage.elements.map((element) => {
3864
+ switch (element.type) {
3865
+ case "text":
3866
+ return /* @__PURE__ */ jsxDEV18(EditableTextElement, {
3867
+ element,
3868
+ isSelected: element.id === selectedId,
3869
+ onSelect: () => {
3870
+ setSelectedId(element.id);
3871
+ setSelectedIds([]);
3872
+ },
3873
+ onChange: (attrs) => updateElement(element.id, attrs)
3874
+ }, element.id, false, undefined, this);
3875
+ case "image":
3876
+ return /* @__PURE__ */ jsxDEV18(UrlImageElement, {
3877
+ element,
3878
+ isSelected: element.id === selectedId,
3879
+ onSelect: () => {
3880
+ setSelectedId(element.id);
3881
+ setSelectedIds([]);
3882
+ },
3883
+ onChange: (attrs) => updateElement(element.id, attrs)
3884
+ }, element.id, false, undefined, this);
3885
+ case "shape":
3886
+ return /* @__PURE__ */ jsxDEV18(ShapeElement, {
3887
+ element,
3888
+ isSelected: element.id === selectedId,
3889
+ onSelect: () => {
3890
+ setSelectedId(element.id);
3891
+ setSelectedIds([]);
3892
+ },
3893
+ onChange: (attrs) => updateElement(element.id, attrs)
3894
+ }, element.id, false, undefined, this);
3895
+ default:
3896
+ return null;
3897
+ }
3898
+ })
3899
+ ]
3900
+ }, undefined, true, undefined, this)
3901
+ }, undefined, false, undefined, this)
3902
+ }, undefined, false, undefined, this)
3903
+ }, undefined, false, undefined, this),
3904
+ config?.multiPage && /* @__PURE__ */ jsxDEV18("div", {
3905
+ className: "absolute bottom-4 left-4 flex items-center space-x-2",
3906
+ children: [
3907
+ design.pages.map((page, index) => /* @__PURE__ */ jsxDEV18("button", {
3908
+ onClick: () => setCurrentPageId(page.id),
3909
+ className: `px-3 py-1 rounded text-sm ${page.id === currentPageId ? "bg-blue-600 text-white" : "bg-gray-800 text-gray-300 hover:bg-gray-700"}`,
3910
+ children: [
3911
+ "Page ",
3912
+ index + 1
3913
+ ]
3914
+ }, page.id, true, undefined, this)),
3915
+ /* @__PURE__ */ jsxDEV18("button", {
3916
+ onClick: addPage,
3917
+ className: "p-1 bg-gray-800 text-gray-300 rounded hover:bg-gray-700",
3918
+ title: "Add Page",
3919
+ children: /* @__PURE__ */ jsxDEV18(PlusCircle, {
3920
+ className: "w-4 h-4"
3921
+ }, undefined, false, undefined, this)
3922
+ }, undefined, false, undefined, this)
3923
+ ]
3924
+ }, undefined, true, undefined, this)
3925
+ ]
3926
+ }, undefined, true, undefined, this),
3927
+ /* @__PURE__ */ jsxDEV18(RightSidebar_default, {
3928
+ currentPageElements: currentPage.elements,
3929
+ selectedElement,
3930
+ selectedId,
3931
+ onSelectElement: setSelectedId,
3932
+ onUpdateElement: updateElement,
3933
+ onDuplicateSelected: duplicateSelected,
3934
+ onMoveElementUp: moveElementUp,
3935
+ onMoveElementDown: moveElementDown,
3936
+ onDeleteSelected: deleteSelected
3937
+ }, undefined, false, undefined, this)
3938
+ ]
3939
+ }, undefined, true, undefined, this),
3940
+ /* @__PURE__ */ jsxDEV18("input", {
3941
+ ref: fileInputRef,
3942
+ type: "file",
3943
+ accept: "image/*",
3944
+ onChange: handleImageUpload,
3945
+ className: "hidden",
3946
+ multiple: true
3947
+ }, undefined, false, undefined, this),
3948
+ /* @__PURE__ */ jsxDEV18("input", {
3949
+ ref: jsonInputRef,
3950
+ type: "file",
3951
+ accept: ".json",
3952
+ onChange: handleImportJSON,
3953
+ className: "hidden"
3954
+ }, undefined, false, undefined, this),
3955
+ showInputModal && /* @__PURE__ */ jsxDEV18(TemplateInputModal_default, {
3956
+ inputs: pendingInputs,
3957
+ onComplete: handleInputModalComplete,
3958
+ onCancel: handleInputModalCancel
3959
+ }, undefined, false, undefined, this),
3960
+ showUnsplash && /* @__PURE__ */ jsxDEV18("div", {
3961
+ className: "fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 p-8",
3962
+ children: /* @__PURE__ */ jsxDEV18("div", {
3963
+ className: "bg-gray-800 rounded-lg w-full max-w-4xl max-h-full overflow-hidden flex flex-col",
3964
+ children: [
3965
+ /* @__PURE__ */ jsxDEV18("div", {
3966
+ className: "p-6 border-b border-gray-700",
3967
+ children: [
3968
+ /* @__PURE__ */ jsxDEV18("h3", {
3969
+ className: "text-xl font-semibold text-white mb-4",
3970
+ children: "Search Unsplash"
3971
+ }, undefined, false, undefined, this),
3972
+ /* @__PURE__ */ jsxDEV18("div", {
3973
+ className: "flex space-x-4",
3974
+ children: [
3975
+ /* @__PURE__ */ jsxDEV18("input", {
3976
+ type: "text",
3977
+ value: unsplashQuery,
3978
+ onChange: (e) => setUnsplashQuery(e.target.value),
3979
+ onKeyDown: (e) => e.key === "Enter" && searchUnsplash(),
3980
+ placeholder: "Search for images...",
3981
+ className: "flex-1 bg-gray-700 text-white border border-gray-600 rounded px-4 py-2 focus:border-blue-500 focus:outline-none",
3982
+ autoFocus: true
3983
+ }, undefined, false, undefined, this),
3984
+ /* @__PURE__ */ jsxDEV18("button", {
3985
+ onClick: searchUnsplash,
3986
+ className: "px-6 py-2 bg-blue-600 text-white rounded hover:bg-blue-700",
3987
+ children: "Search"
3988
+ }, undefined, false, undefined, this),
3989
+ /* @__PURE__ */ jsxDEV18("button", {
3990
+ onClick: () => {
3991
+ setShowUnsplash(false);
3992
+ setUnsplashQuery("");
3993
+ setUnsplashResults([]);
3994
+ },
3995
+ className: "px-6 py-2 bg-gray-700 text-white rounded hover:bg-gray-600",
3996
+ children: "Cancel"
3997
+ }, undefined, false, undefined, this)
3998
+ ]
3999
+ }, undefined, true, undefined, this)
4000
+ ]
4001
+ }, undefined, true, undefined, this),
4002
+ /* @__PURE__ */ jsxDEV18("div", {
4003
+ className: "flex-1 overflow-y-auto p-6",
4004
+ children: unsplashResults.length > 0 ? /* @__PURE__ */ jsxDEV18("div", {
4005
+ className: "grid grid-cols-3 gap-4",
4006
+ children: unsplashResults.map((result) => /* @__PURE__ */ jsxDEV18("button", {
4007
+ onClick: () => addUnsplashImage(result.urls.regular),
4008
+ className: "relative group overflow-hidden rounded-lg aspect-square",
4009
+ children: [
4010
+ /* @__PURE__ */ jsxDEV18("img", {
4011
+ src: result.urls.thumb,
4012
+ alt: "",
4013
+ className: "w-full h-full object-cover group-hover:scale-110 transition-transform duration-200"
4014
+ }, undefined, false, undefined, this),
4015
+ /* @__PURE__ */ jsxDEV18("div", {
4016
+ className: "absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-50 transition-opacity duration-200 flex items-center justify-center",
4017
+ children: /* @__PURE__ */ jsxDEV18(PlusCircle, {
4018
+ className: "w-8 h-8 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-200"
4019
+ }, undefined, false, undefined, this)
4020
+ }, undefined, false, undefined, this)
4021
+ ]
4022
+ }, result.id, true, undefined, this))
4023
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV18("div", {
4024
+ className: "text-center text-gray-400 py-12",
4025
+ children: "Enter a search term to find images"
4026
+ }, undefined, false, undefined, this)
4027
+ }, undefined, false, undefined, this)
4028
+ ]
4029
+ }, undefined, true, undefined, this)
4030
+ }, undefined, false, undefined, this)
4031
+ ]
4032
+ }, undefined, true, undefined, this);
4033
+ };
4034
+ var CanvasEditor_default = CanvasEditor;
4035
+ export {
4036
+ setUnsplashAccessKey,
4037
+ replaceUserInputs,
4038
+ importFromJSON,
4039
+ getUnsplashAccessKey,
4040
+ findRequiredInputs,
4041
+ exportToPNG,
4042
+ exportToJSON,
4043
+ exportToJPG,
4044
+ convertTemplateToCanvasDesign,
4045
+ UrlImageElement,
4046
+ ShapeElement,
4047
+ FONT_FAMILIES,
4048
+ EditableTextElement,
4049
+ DEFAULT_COLORS,
4050
+ CanvasEditor_default as CanvasEditor,
4051
+ CANVAS_PRESETS
4052
+ };