@varianta/sdk 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -1,17 +1,17 @@
1
- class N {
1
+ class U {
2
2
  constructor(e) {
3
3
  this.animationFrameId = null, this.needsRender = !0, this.areas = [], this.contents = /* @__PURE__ */ new Map(), this.artboard = null, this.bgImageElement = null, this.loadedImages = /* @__PURE__ */ new Map(), this.loadingImages = /* @__PURE__ */ new Set(), this.zoom = 1, this.pan = { x: 0, y: 0 }, this.selectedAreaId = null, this.hoveredHandle = null, this.eventListeners = /* @__PURE__ */ new Map(), this.safeArea = null, this.safeAreaViolations = /* @__PURE__ */ new Set(), this.showAreaBorders = !0, this.areaSelectionEnabled = !0, this.renderLoop = () => {
4
4
  try {
5
5
  this.needsRender && (this.render(), this.needsRender = !1, this.emit("render", void 0));
6
- } catch (i) {
7
- console.error("Error in render loop:", i);
6
+ } catch (t) {
7
+ console.error("Error in render loop:", t);
8
8
  }
9
9
  this.animationFrameId = requestAnimationFrame(this.renderLoop);
10
10
  }, this.canvas = e;
11
- const t = e.getContext("2d");
12
- if (!t)
11
+ const i = e.getContext("2d");
12
+ if (!i)
13
13
  throw new Error("Failed to get 2D context from canvas");
14
- this.ctx = t;
14
+ this.ctx = i;
15
15
  }
16
16
  /**
17
17
  * Start the render loop
@@ -41,7 +41,7 @@ class N {
41
41
  * Set areas to render
42
42
  */
43
43
  setAreas(e) {
44
- this.areas = [...e].sort((t, i) => t.zIndex - i.zIndex), this.requestRender();
44
+ this.areas = [...e].sort((i, t) => i.zIndex - t.zIndex), this.requestRender();
45
45
  }
46
46
  /**
47
47
  * Get areas
@@ -64,8 +64,8 @@ class N {
64
64
  /**
65
65
  * Update content for a single area
66
66
  */
67
- updateContent(e, t) {
68
- this.contents.set(e, t), t.type === "image" && this.loadContentImages(), this.requestRender(), this.emit("content:change", { areaId: e, content: t });
67
+ updateContent(e, i) {
68
+ this.contents.set(e, i), i.type === "image" && this.loadContentImages(), this.requestRender(), this.emit("content:change", { areaId: e, content: i });
69
69
  }
70
70
  /**
71
71
  * Remove content for an area
@@ -78,12 +78,12 @@ class N {
78
78
  */
79
79
  setBackgroundImage(e) {
80
80
  if (this.backgroundImage = e, e?.url && e.url !== this.bgImageElement?.src) {
81
- const t = new Image();
81
+ const i = e.url, t = new Image();
82
82
  t.crossOrigin = "anonymous", t.onload = () => {
83
83
  this.bgImageElement = t, this.requestRender();
84
84
  }, t.onerror = () => {
85
- this.bgImageElement = null, console.error("Failed to load background image:", e.url), this.requestRender();
86
- }, t.src = e.url;
85
+ this.bgImageElement = null, console.error("Failed to load background image:", i), this.requestRender();
86
+ }, t.src = i;
87
87
  } else e?.url || (this.bgImageElement = null);
88
88
  this.requestRender();
89
89
  }
@@ -129,10 +129,10 @@ class N {
129
129
  getCenteredPan(e) {
130
130
  if (!this.artboard)
131
131
  return null;
132
- const { width: t, height: i } = this.artboard, n = window.devicePixelRatio || 1, a = this.canvas.width / n, o = this.canvas.height / n;
132
+ const { width: i, height: t } = this.artboard, s = window.devicePixelRatio || 1, a = this.canvas.width / s, o = this.canvas.height / s;
133
133
  return {
134
- x: (a / e - t) / 2,
135
- y: (o / e - i) / 2
134
+ x: (a / e - i) / 2,
135
+ y: (o / e - t) / 2
136
136
  };
137
137
  }
138
138
  /**
@@ -147,10 +147,10 @@ class N {
147
147
  fitToView() {
148
148
  if (!this.artboard)
149
149
  return;
150
- const e = 0, { width: t, height: i } = this.artboard, n = window.devicePixelRatio || 1, a = this.canvas.width / n - e * 2, o = this.canvas.height / n - e * 2;
150
+ const e = 0, { width: i, height: t } = this.artboard, s = window.devicePixelRatio || 1, a = this.canvas.width / s - e * 2, o = this.canvas.height / s - e * 2;
151
151
  if (a <= 0 || o <= 0)
152
152
  return;
153
- const s = a / t, r = o / i, d = Math.min(s, r), l = this.canvas.width / n, c = this.canvas.height / n, u = (l / d - t) / 2, p = (c / d - i) / 2;
153
+ const n = a / i, r = o / t, d = Math.min(n, r), l = this.canvas.width / s, h = this.canvas.height / s, u = (l / d - i) / 2, p = (h / d - t) / 2;
154
154
  this.zoom = d, this.pan = { x: u, y: p }, this.requestRender();
155
155
  }
156
156
  /**
@@ -180,48 +180,48 @@ class N {
180
180
  /**
181
181
  * Convert screen coordinates to canvas coordinates
182
182
  */
183
- screenToCanvas(e, t) {
184
- const i = window.devicePixelRatio || 1, n = e / i, a = t / i, o = n / this.zoom - this.pan.x, s = a / this.zoom - this.pan.y;
185
- return { x: o, y: s };
183
+ screenToCanvas(e, i) {
184
+ const t = window.devicePixelRatio || 1, s = e / t, a = i / t, o = s / this.zoom - this.pan.x, n = a / this.zoom - this.pan.y;
185
+ return { x: o, y: n };
186
186
  }
187
187
  /**
188
188
  * Get content bounds for an area (in artboard coordinates)
189
189
  */
190
190
  getContentBounds(e) {
191
- const t = this.areas.find((d) => d.id === e);
192
- if (!t)
193
- return null;
194
- const i = this.contents.get(e);
191
+ const i = this.areas.find((d) => d.id === e);
195
192
  if (!i)
196
193
  return null;
197
- const { location: n } = t, { x: a, y: o, width: s, height: r } = n;
198
- if (i.type === "text") {
199
- const d = i.offset || { x: 0, y: 0 }, l = i.scale ?? 1, c = i.rotation ?? 0, u = s * l, p = r * l, g = a + s / 2 + d.x, m = o + r / 2 + d.y;
194
+ const t = this.contents.get(e);
195
+ if (!t)
196
+ return null;
197
+ const { location: s } = i, { x: a, y: o, width: n, height: r } = s;
198
+ if (t.type === "text") {
199
+ const d = t.offset || { x: 0, y: 0 }, l = t.scale ?? 1, h = t.rotation ?? 0, u = n * l, p = r * l, m = a + n / 2 + d.x, g = o + r / 2 + d.y;
200
200
  return {
201
- x: g - u / 2,
202
- y: m - p / 2,
201
+ x: m - u / 2,
202
+ y: g - p / 2,
203
203
  width: u,
204
204
  height: p,
205
- rotation: c,
206
- centerX: g,
207
- centerY: m
205
+ rotation: h,
206
+ centerX: m,
207
+ centerY: g
208
208
  };
209
- } else if (i.type === "image") {
209
+ } else if (t.type === "image") {
210
210
  const d = this.loadedImages.get(e);
211
211
  if (!d)
212
212
  return null;
213
- const l = i.offset || { x: 0, y: 0 }, c = i.scale ?? 1, u = i.rotation ?? 0, p = d.naturalWidth / d.naturalHeight, g = s / r;
214
- let m, v;
215
- p > g ? (m = s, v = s / p) : (v = r, m = r * p);
216
- const b = m * c, y = v * c, x = a + s / 2 + l.x, w = o + r / 2 + l.y;
213
+ const l = t.offset || { x: 0, y: 0 }, h = t.scale ?? 1, u = t.rotation ?? 0, p = d.naturalWidth / d.naturalHeight, m = n / r;
214
+ let g, f;
215
+ p > m ? (g = n, f = n / p) : (f = r, g = r * p);
216
+ const b = g * h, w = f * h, y = a + n / 2 + l.x, x = o + r / 2 + l.y;
217
217
  return {
218
- x: x - b / 2,
219
- y: w - y / 2,
218
+ x: y - b / 2,
219
+ y: x - w / 2,
220
220
  width: b,
221
- height: y,
221
+ height: w,
222
222
  rotation: u,
223
- centerX: x,
224
- centerY: w
223
+ centerX: y,
224
+ centerY: x
225
225
  };
226
226
  }
227
227
  return null;
@@ -230,13 +230,13 @@ class N {
230
230
  * Hit test content at given canvas coordinates
231
231
  * Returns area ID if content was hit, null otherwise
232
232
  */
233
- hitTestContent(e, t) {
233
+ hitTestContent(e, i) {
234
234
  if (!this.areaSelectionEnabled)
235
235
  return null;
236
- for (let i = this.areas.length - 1; i >= 0; i--) {
237
- const n = this.areas[i], a = this.getContentBounds(n.id);
238
- if (a && this.isPointInBounds(e, t, a))
239
- return n.id;
236
+ for (let t = this.areas.length - 1; t >= 0; t--) {
237
+ const s = this.areas[t], a = this.getContentBounds(s.id);
238
+ if (a && this.isPointInBounds(e, i, a))
239
+ return s.id;
240
240
  }
241
241
  return null;
242
242
  }
@@ -244,66 +244,66 @@ class N {
244
244
  * Hit test area at given canvas coordinates (ignoring content)
245
245
  * Returns area ID if point is inside area bounds
246
246
  */
247
- hitTestArea(e, t) {
247
+ hitTestArea(e, i) {
248
248
  if (!this.areaSelectionEnabled)
249
249
  return null;
250
- for (let i = this.areas.length - 1; i >= 0; i--) {
251
- const n = this.areas[i], { x: a, y: o, width: s, height: r } = n.location;
252
- if (e >= a && e <= a + s && t >= o && t <= o + r)
253
- return n.id;
250
+ for (let t = this.areas.length - 1; t >= 0; t--) {
251
+ const s = this.areas[t], { x: a, y: o, width: n, height: r } = s.location;
252
+ if (e >= a && e <= a + n && i >= o && i <= o + r)
253
+ return s.id;
254
254
  }
255
255
  return null;
256
256
  }
257
257
  /**
258
258
  * Test if a point is inside rotated bounds
259
259
  */
260
- isPointInBounds(e, t, i) {
261
- const { centerX: n, centerY: a, width: o, height: s, rotation: r } = i, d = -r * Math.PI / 180, l = Math.cos(d), c = Math.sin(d), u = e - n, p = t - a, g = u * l - p * c, m = u * c + p * l;
262
- return g >= -o / 2 && g <= o / 2 && m >= -s / 2 && m <= s / 2;
260
+ isPointInBounds(e, i, t) {
261
+ const { centerX: s, centerY: a, width: o, height: n, rotation: r } = t, d = -r * Math.PI / 180, l = Math.cos(d), h = Math.sin(d), u = e - s, p = i - a, m = u * l - p * h, g = u * h + p * l;
262
+ return m >= -o / 2 && m <= o / 2 && g >= -n / 2 && g <= n / 2;
263
263
  }
264
264
  /**
265
265
  * Get handle positions for the selected content
266
266
  */
267
267
  getContentHandlePositions(e) {
268
- const t = this.areas.find((b) => b.id === e);
269
- if (!t)
270
- return null;
271
- const i = this.contents.get(e);
268
+ const i = this.areas.find((b) => b.id === e);
272
269
  if (!i)
273
270
  return null;
274
- const n = this.getContentBounds(e);
275
- if (!n)
271
+ const t = this.contents.get(e);
272
+ if (!t)
276
273
  return null;
277
- const { centerX: a, centerY: o, width: s, height: r, rotation: d } = n, l = 6 / this.zoom, c = 30 / this.zoom, u = d * Math.PI / 180, p = Math.cos(u), g = Math.sin(u), m = (b, y) => ({
278
- x: a + b * p - y * g,
279
- y: o + b * g + y * p
280
- }), v = [];
281
- if (i.type === "image" && t.imageOptions?.allowScaling || i.type === "text" && t.textOptions?.allowScaling) {
282
- const b = s / 2, y = r / 2, x = m(-b, -y);
283
- v.push({ type: "nw", ...x, radius: l });
284
- const w = m(b, -y);
285
- v.push({ type: "ne", ...w, radius: l });
286
- const C = m(b, y);
287
- v.push({ type: "se", ...C, radius: l });
288
- const M = m(-b, y);
289
- v.push({ type: "sw", ...M, radius: l });
274
+ const s = this.getContentBounds(e);
275
+ if (!s)
276
+ return null;
277
+ const { centerX: a, centerY: o, width: n, height: r, rotation: d } = s, l = 6 / this.zoom, h = 30 / this.zoom, u = d * Math.PI / 180, p = Math.cos(u), m = Math.sin(u), g = (b, w) => ({
278
+ x: a + b * p - w * m,
279
+ y: o + b * m + w * p
280
+ }), f = [];
281
+ if (t.type === "image" && i.imageOptions?.allowScaling || t.type === "text" && i.textOptions?.allowScaling) {
282
+ const b = n / 2, w = r / 2, y = g(-b, -w);
283
+ f.push({ type: "nw", ...y, radius: l });
284
+ const x = g(b, -w);
285
+ f.push({ type: "ne", ...x, radius: l });
286
+ const C = g(b, w);
287
+ f.push({ type: "se", ...C, radius: l });
288
+ const M = g(-b, w);
289
+ f.push({ type: "sw", ...M, radius: l });
290
290
  }
291
- if (i.type === "image" && t.imageOptions?.allowRotation || i.type === "text" && t.textOptions?.allowRotation) {
292
- const b = m(0, -r / 2 - c);
293
- v.push({ type: "rotate", ...b, radius: l });
291
+ if (t.type === "image" && i.imageOptions?.allowRotation || t.type === "text" && i.textOptions?.allowRotation) {
292
+ const b = g(0, -r / 2 - h);
293
+ f.push({ type: "rotate", ...b, radius: l });
294
294
  }
295
- return v;
295
+ return f;
296
296
  }
297
297
  /**
298
298
  * Hit test handles at given canvas coordinates
299
299
  */
300
- hitTestHandle(e, t, i) {
301
- const n = this.getContentHandlePositions(i);
302
- if (!n)
300
+ hitTestHandle(e, i, t) {
301
+ const s = this.getContentHandlePositions(t);
302
+ if (!s)
303
303
  return null;
304
- for (const a of n) {
305
- const o = e - a.x, s = t - a.y;
306
- if (Math.sqrt(o * o + s * s) <= a.radius * 2)
304
+ for (const a of s) {
305
+ const o = e - a.x, n = i - a.y;
306
+ if (Math.sqrt(o * o + n * n) <= a.radius * 2)
307
307
  return a.type;
308
308
  }
309
309
  return null;
@@ -312,24 +312,24 @@ class N {
312
312
  * Check if positioning is allowed for an area
313
313
  */
314
314
  isPositioningAllowed(e) {
315
- const t = this.areas.find((n) => n.id === e);
316
- if (!t)
315
+ const i = this.areas.find((s) => s.id === e);
316
+ if (!i)
317
317
  return !1;
318
- const i = this.contents.get(e);
319
- return i ? i.type === "text" ? t.textOptions?.allowPositioning ?? !1 : t.imageOptions?.allowPositioning ?? !1 : !1;
318
+ const t = this.contents.get(e);
319
+ return t ? t.type === "text" ? i.textOptions?.allowPositioning ?? !1 : i.imageOptions?.allowPositioning ?? !1 : !1;
320
320
  }
321
321
  /**
322
322
  * Get area location (for constraint calculations)
323
323
  */
324
324
  getAreaLocation(e) {
325
- const t = this.areas.find((i) => i.id === e);
326
- return t ? t.location : null;
325
+ const i = this.areas.find((t) => t.id === e);
326
+ return i ? i.location : null;
327
327
  }
328
328
  /**
329
329
  * Get area by ID
330
330
  */
331
331
  getArea(e) {
332
- return this.areas.find((t) => t.id === e);
332
+ return this.areas.find((i) => i.id === e);
333
333
  }
334
334
  /**
335
335
  * Set safe area margins for content safety guides
@@ -344,34 +344,34 @@ class N {
344
344
  checkSafeAreaViolations() {
345
345
  if (!this.safeArea || !this.artboard)
346
346
  return [];
347
- const e = [], { top: t, right: i, bottom: n, left: a } = this.safeArea, o = a, s = t, r = this.artboard.width - i, d = this.artboard.height - n;
347
+ const e = [], { top: i, right: t, bottom: s, left: a } = this.safeArea, o = a, n = i, r = this.artboard.width - t, d = this.artboard.height - s;
348
348
  for (const l of this.areas) {
349
349
  if (!this.contents.get(l.id))
350
350
  continue;
351
- const c = this.getContentBounds(l.id);
352
- if (!c)
351
+ const h = this.getContentBounds(l.id);
352
+ if (!h)
353
353
  continue;
354
- const { centerX: u, centerY: p, width: g, height: m, rotation: v } = c, b = v * Math.PI / 180, y = Math.abs(Math.cos(b)), x = Math.abs(Math.sin(b)), w = g * y + m * x, C = g * x + m * y, M = u - w / 2, A = p - C / 2, D = u + w / 2, R = p + C / 2;
355
- (M < o || A < s || D > r || R > d) && e.push(l.id);
354
+ const { centerX: u, centerY: p, width: m, height: g, rotation: f } = h, b = f * Math.PI / 180, w = Math.abs(Math.cos(b)), y = Math.abs(Math.sin(b)), x = m * w + g * y, C = m * y + g * w, M = u - x / 2, R = p - C / 2, D = u + x / 2, N = p + C / 2;
355
+ (M < o || R < n || D > r || N > d) && e.push(l.id);
356
356
  }
357
357
  return this.safeAreaViolations = new Set(e), this.requestRender(), e;
358
358
  }
359
359
  /**
360
360
  * Add event listener
361
361
  */
362
- on(e, t) {
363
- return this.eventListeners.has(e) || this.eventListeners.set(e, /* @__PURE__ */ new Set()), this.eventListeners.get(e).add(t), () => {
364
- this.eventListeners.get(e)?.delete(t);
362
+ on(e, i) {
363
+ return this.eventListeners.has(e) || this.eventListeners.set(e, /* @__PURE__ */ new Set()), this.eventListeners.get(e).add(i), () => {
364
+ this.eventListeners.get(e)?.delete(i);
365
365
  };
366
366
  }
367
367
  /**
368
368
  * Emit event
369
369
  */
370
- emit(e, t) {
371
- const i = this.eventListeners.get(e);
372
- if (i)
373
- for (const n of i)
374
- n(t);
370
+ emit(e, i) {
371
+ const t = this.eventListeners.get(e);
372
+ if (t)
373
+ for (const s of t)
374
+ s(i);
375
375
  }
376
376
  /**
377
377
  * Export to PNG at full resolution
@@ -379,60 +379,64 @@ class N {
379
379
  async exportToPNG() {
380
380
  if (!this.artboard)
381
381
  throw new Error("No artboard configured");
382
- const { width: e, height: t, backgroundColor: i } = this.artboard, n = document.createElement("canvas");
383
- n.width = e, n.height = t;
384
- const a = n.getContext("2d");
382
+ const { width: e, height: i, backgroundColor: t } = this.artboard, s = document.createElement("canvas");
383
+ s.width = e, s.height = i;
384
+ const a = s.getContext("2d");
385
385
  if (!a)
386
386
  throw new Error("Failed to get 2D context for export");
387
- a.fillStyle = i, a.fillRect(0, 0, e, t), this.backgroundImage?.position === "behind" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, t);
387
+ a.fillStyle = t, a.fillRect(0, 0, e, i), this.backgroundImage?.position === "behind" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, i);
388
388
  for (const o of this.areas)
389
389
  this.drawAreaContentOnContext(a, o);
390
- return this.backgroundImage?.position === "overlay" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, t), new Promise((o, s) => {
391
- n.toBlob((r) => {
392
- r ? o(r) : s(new Error("Failed to create PNG blob"));
393
- }, "image/png", 1);
390
+ return this.backgroundImage?.position === "overlay" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, i), new Promise((o, n) => {
391
+ try {
392
+ s.toBlob((r) => {
393
+ r ? o(r) : n(new Error("Failed to create PNG blob"));
394
+ }, "image/png", 1);
395
+ } catch (r) {
396
+ r instanceof DOMException && r.name === "SecurityError" ? n(new Error("Cannot export: images failed to load with CORS. Check CDN configuration.")) : n(r);
397
+ }
394
398
  });
395
399
  }
396
400
  /**
397
401
  * Load images from content data URLs
398
402
  */
399
403
  loadContentImages() {
400
- for (const [e, t] of this.contents)
401
- if (t.type === "image" && !this.loadedImages.has(e) && !this.loadingImages.has(e)) {
404
+ for (const [e, i] of this.contents)
405
+ if (i.type === "image" && !this.loadedImages.has(e) && !this.loadingImages.has(e)) {
402
406
  this.loadingImages.add(e);
403
- const i = new Image();
404
- i.onload = () => {
405
- this.loadedImages.set(e, i), this.loadingImages.delete(e), this.requestRender();
406
- }, i.onerror = () => {
407
+ const t = new Image();
408
+ t.crossOrigin = "anonymous", t.onload = () => {
409
+ this.loadedImages.set(e, t), this.loadingImages.delete(e), this.requestRender();
410
+ }, t.onerror = () => {
407
411
  this.loadingImages.delete(e), console.error("Failed to load image for area:", e);
408
- }, i.src = t.dataUrl;
412
+ }, t.src = i.dataUrl;
409
413
  }
410
414
  for (const e of this.loadedImages.keys()) {
411
- const t = this.contents.get(e);
412
- (!t || t.type !== "image") && this.loadedImages.delete(e);
415
+ const i = this.contents.get(e);
416
+ (!i || i.type !== "image") && this.loadedImages.delete(e);
413
417
  }
414
418
  }
415
419
  /**
416
420
  * Render the canvas
417
421
  */
418
422
  render() {
419
- const { ctx: e, canvas: t } = this;
420
- if (e.fillStyle = "#f3f4f6", e.fillRect(0, 0, t.width, t.height), !this.artboard)
423
+ const { ctx: e, canvas: i } = this;
424
+ if (e.fillStyle = "#f3f4f6", e.fillRect(0, 0, i.width, i.height), !this.artboard)
421
425
  return;
422
426
  e.save();
423
- const i = window.devicePixelRatio || 1;
424
- e.scale(i, i), e.scale(this.zoom, this.zoom), e.translate(this.pan.x, this.pan.y);
425
- const { width: n, height: a, backgroundColor: o } = this.artboard;
426
- e.fillStyle = o, e.fillRect(0, 0, n, a), e.strokeStyle = "#d1d5db", e.lineWidth = 1 / this.zoom, e.strokeRect(0, 0, n, a), this.backgroundImage?.position === "behind" && this.renderBackgroundImage();
427
- for (const s of this.areas)
428
- this.renderAreaWithContent(s);
427
+ const t = window.devicePixelRatio || 1;
428
+ e.scale(t, t), e.scale(this.zoom, this.zoom), e.translate(this.pan.x, this.pan.y);
429
+ const { width: s, height: a, backgroundColor: o } = this.artboard;
430
+ e.fillStyle = o, e.fillRect(0, 0, s, a), e.strokeStyle = "#d1d5db", e.lineWidth = 1 / this.zoom, e.strokeRect(0, 0, s, a), this.backgroundImage?.position === "behind" && this.renderBackgroundImage();
431
+ for (const n of this.areas)
432
+ this.renderAreaWithContent(n);
429
433
  this.backgroundImage?.position === "overlay" && this.renderBackgroundImage(), this.renderSafeAreaGuide();
430
- for (const s of this.safeAreaViolations) {
431
- const r = this.areas.find((p) => p.id === s);
434
+ for (const n of this.safeAreaViolations) {
435
+ const r = this.areas.find((p) => p.id === n);
432
436
  if (!r)
433
437
  continue;
434
- const { x: d, y: l, width: c, height: u } = r.location;
435
- e.save(), e.fillStyle = "rgba(239, 68, 68, 0.15)", e.fillRect(d, l, c, u), e.restore();
438
+ const { x: d, y: l, width: h, height: u } = r.location;
439
+ e.save(), e.fillStyle = "rgba(239, 68, 68, 0.15)", e.fillRect(d, l, h, u), e.restore();
436
440
  }
437
441
  this.selectedAreaId && this.renderSelectionOverlay(this.selectedAreaId), e.restore();
438
442
  }
@@ -440,38 +444,38 @@ class N {
440
444
  * Render selection overlay with handles
441
445
  */
442
446
  renderSelectionOverlay(e) {
443
- const t = this.getContentBounds(e);
444
- if (!t) {
445
- const l = this.areas.find((v) => v.id === e);
447
+ const i = this.getContentBounds(e);
448
+ if (!i) {
449
+ const l = this.areas.find((f) => f.id === e);
446
450
  if (!l)
447
451
  return;
448
- const { ctx: c } = this, { x: u, y: p, width: g, height: m } = l.location;
449
- c.save(), c.strokeStyle = "#3b82f6", c.lineWidth = 2 / this.zoom, c.setLineDash([]), c.strokeRect(u, p, g, m), c.restore();
452
+ const { ctx: h } = this, { x: u, y: p, width: m, height: g } = l.location;
453
+ h.save(), h.strokeStyle = "#3b82f6", h.lineWidth = 2 / this.zoom, h.setLineDash([]), h.strokeRect(u, p, m, g), h.restore();
450
454
  return;
451
455
  }
452
- const { ctx: i } = this, { centerX: n, centerY: a, width: o, height: s, rotation: r } = t;
453
- i.save(), i.translate(n, a), i.rotate(r * Math.PI / 180), i.strokeStyle = "#3b82f6", i.lineWidth = 2 / this.zoom, i.setLineDash([]), i.strokeRect(-o / 2, -s / 2, o, s), i.restore();
456
+ const { ctx: t } = this, { centerX: s, centerY: a, width: o, height: n, rotation: r } = i;
457
+ t.save(), t.translate(s, a), t.rotate(r * Math.PI / 180), t.strokeStyle = "#3b82f6", t.lineWidth = 2 / this.zoom, t.setLineDash([]), t.strokeRect(-o / 2, -n / 2, o, n), t.restore();
454
458
  const d = this.getContentHandlePositions(e);
455
459
  if (d)
456
460
  for (const l of d) {
457
- i.save();
458
- const c = this.hoveredHandle === l.type, u = c ? "#dbeafe" : "#ffffff", p = c ? "#2563eb" : "#3b82f6";
461
+ t.save();
462
+ const h = this.hoveredHandle === l.type, u = h ? "#dbeafe" : "#ffffff", p = h ? "#2563eb" : "#3b82f6";
459
463
  if (l.type === "rotate") {
460
- const g = this.getRotatedPoint(n, a - s / 2, n, a, r);
461
- i.beginPath(), i.strokeStyle = p, i.lineWidth = 1 / this.zoom, i.setLineDash([4 / this.zoom, 4 / this.zoom]), i.moveTo(g.x, g.y), i.lineTo(l.x, l.y), i.stroke(), i.setLineDash([]), i.beginPath(), i.fillStyle = u, i.strokeStyle = p, i.lineWidth = 2 / this.zoom, i.arc(l.x, l.y, l.radius, 0, Math.PI * 2), i.fill(), i.stroke(), i.beginPath(), i.strokeStyle = p, i.lineWidth = 1.5 / this.zoom, i.arc(l.x, l.y, l.radius * 0.5, -Math.PI * 0.8, Math.PI * 0.5), i.stroke();
464
+ const m = this.getRotatedPoint(s, a - n / 2, s, a, r);
465
+ t.beginPath(), t.strokeStyle = p, t.lineWidth = 1 / this.zoom, t.setLineDash([4 / this.zoom, 4 / this.zoom]), t.moveTo(m.x, m.y), t.lineTo(l.x, l.y), t.stroke(), t.setLineDash([]), t.beginPath(), t.fillStyle = u, t.strokeStyle = p, t.lineWidth = 2 / this.zoom, t.arc(l.x, l.y, l.radius, 0, Math.PI * 2), t.fill(), t.stroke(), t.beginPath(), t.strokeStyle = p, t.lineWidth = 1.5 / this.zoom, t.arc(l.x, l.y, l.radius * 0.5, -Math.PI * 0.8, Math.PI * 0.5), t.stroke();
462
466
  } else
463
- i.fillStyle = u, i.strokeStyle = p, i.lineWidth = 2 / this.zoom, i.fillRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2), i.strokeRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2);
464
- i.restore();
467
+ t.fillStyle = u, t.strokeStyle = p, t.lineWidth = 2 / this.zoom, t.fillRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2), t.strokeRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2);
468
+ t.restore();
465
469
  }
466
470
  }
467
471
  /**
468
472
  * Rotate a point around a center
469
473
  */
470
- getRotatedPoint(e, t, i, n, a) {
471
- const o = a * Math.PI / 180, s = Math.cos(o), r = Math.sin(o), d = e - i, l = t - n;
474
+ getRotatedPoint(e, i, t, s, a) {
475
+ const o = a * Math.PI / 180, n = Math.cos(o), r = Math.sin(o), d = e - t, l = i - s;
472
476
  return {
473
- x: i + d * s - l * r,
474
- y: n + d * r + l * s
477
+ x: t + d * n - l * r,
478
+ y: s + d * r + l * n
475
479
  };
476
480
  }
477
481
  /**
@@ -480,11 +484,11 @@ class N {
480
484
  renderBackgroundImage() {
481
485
  if (!this.bgImageElement || !this.artboard || !this.backgroundImage)
482
486
  return;
483
- const { ctx: e } = this, { opacity: t } = this.backgroundImage, { width: i, height: n } = this.artboard;
484
- e.save(), e.globalAlpha = t;
485
- const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = i / n;
486
- let s, r, d, l;
487
- a > o ? (r = n, s = n * a, d = (i - s) / 2, l = 0) : (s = i, r = i / a, d = 0, l = (n - r) / 2), e.drawImage(this.bgImageElement, d, l, s, r), e.restore();
487
+ const { ctx: e } = this, { opacity: i } = this.backgroundImage, { width: t, height: s } = this.artboard;
488
+ e.save(), e.globalAlpha = i;
489
+ const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = t / s;
490
+ let n, r, d, l;
491
+ a > o ? (r = s, n = s * a, d = (t - n) / 2, l = 0) : (n = t, r = t / a, d = 0, l = (s - r) / 2), e.drawImage(this.bgImageElement, d, l, n, r), e.restore();
488
492
  }
489
493
  /**
490
494
  * Render safe area guide as dashed green rectangle
@@ -492,106 +496,106 @@ class N {
492
496
  renderSafeAreaGuide() {
493
497
  if (!this.safeArea || !this.artboard)
494
498
  return;
495
- const { ctx: e } = this, { top: t, right: i, bottom: n, left: a } = this.safeArea, o = a, s = t, r = this.artboard.width - a - i, d = this.artboard.height - t - n;
496
- r <= 0 || d <= 0 || (e.save(), e.strokeStyle = "#22c55e", e.lineWidth = 1 / this.zoom, e.setLineDash([6 / this.zoom, 4 / this.zoom]), e.strokeRect(o, s, r, d), e.setLineDash([]), e.restore());
499
+ const { ctx: e } = this, { top: i, right: t, bottom: s, left: a } = this.safeArea, o = a, n = i, r = this.artboard.width - a - t, d = this.artboard.height - i - s;
500
+ r <= 0 || d <= 0 || (e.save(), e.strokeStyle = "#22c55e", e.lineWidth = 1 / this.zoom, e.setLineDash([6 / this.zoom, 4 / this.zoom]), e.strokeRect(o, n, r, d), e.setLineDash([]), e.restore());
497
501
  }
498
502
  /**
499
503
  * Render an area with its content
500
504
  */
501
505
  renderAreaWithContent(e) {
502
- const { ctx: t } = this, { location: i, backgroundColor: n } = e, { x: a, y: o, width: s, height: r } = i;
503
- n && (t.fillStyle = n, t.fillRect(a, o, s, r));
506
+ const { ctx: i } = this, { location: t, backgroundColor: s } = e, { x: a, y: o, width: n, height: r } = t;
507
+ s && (i.fillStyle = s, i.fillRect(a, o, n, r));
504
508
  const d = this.contents.get(e.id);
505
- d && (d.type === "text" ? this.renderTextContent(e, d) : d.type === "image" && this.renderImageContent(e, d)), this.showAreaBorders && e.showBorder && (t.save(), t.strokeStyle = e.borderColor || "#3b82f6", t.lineWidth = 1 / this.zoom, t.setLineDash([4 / this.zoom, 4 / this.zoom]), t.strokeRect(a, o, s, r), t.setLineDash([]), t.restore());
509
+ d && (d.type === "text" ? this.renderTextContent(e, d) : d.type === "image" && this.renderImageContent(e, d)), this.showAreaBorders && e.showBorder && (i.save(), i.strokeStyle = e.borderColor || "#3b82f6", i.lineWidth = 1 / this.zoom, i.setLineDash([4 / this.zoom, 4 / this.zoom]), i.strokeRect(a, o, n, r), i.setLineDash([]), i.restore());
506
510
  }
507
511
  /**
508
512
  * Render text content in an area
509
513
  */
510
- renderTextContent(e, t) {
511
- if (!t.text.trim())
514
+ renderTextContent(e, i) {
515
+ if (!i.text.trim())
512
516
  return;
513
- const { ctx: i } = this, { location: n } = e, { x: a, y: o, width: s, height: r } = n, d = t.offset || { x: 0, y: 0 }, l = t.scale ?? 1, c = t.rotation ?? 0, u = a + s / 2 + d.x, p = o + r / 2 + d.y;
514
- i.save(), i.beginPath(), i.rect(a, o, s, r), i.clip(), i.translate(u, p), i.rotate(c * Math.PI / 180), i.scale(l, l), i.font = `${t.size}px ${t.font}, sans-serif`, i.fillStyle = t.color, i.textBaseline = "middle";
515
- let g;
516
- switch (t.align) {
517
+ const { ctx: t } = this, { location: s } = e, { x: a, y: o, width: n, height: r } = s, d = i.offset || { x: 0, y: 0 }, l = i.scale ?? 1, h = i.rotation ?? 0, u = a + n / 2 + d.x, p = o + r / 2 + d.y;
518
+ t.save(), t.beginPath(), t.rect(a, o, n, r), t.clip(), t.translate(u, p), t.rotate(h * Math.PI / 180), t.scale(l, l), t.font = `${i.size}px ${i.font}, sans-serif`, t.fillStyle = i.color, t.textBaseline = "middle";
519
+ let m;
520
+ switch (i.align) {
517
521
  case "left":
518
- i.textAlign = "left", g = -s / 2 + 10;
522
+ t.textAlign = "left", m = -n / 2 + 10;
519
523
  break;
520
524
  case "right":
521
- i.textAlign = "right", g = s / 2 - 10;
525
+ t.textAlign = "right", m = n / 2 - 10;
522
526
  break;
523
527
  case "center":
524
528
  default:
525
- i.textAlign = "center", g = 0;
529
+ t.textAlign = "center", m = 0;
526
530
  break;
527
531
  }
528
- i.fillText(t.text, g, 0), i.restore();
532
+ t.fillText(i.text, m, 0), t.restore();
529
533
  }
530
534
  /**
531
535
  * Render image content in an area
532
536
  */
533
- renderImageContent(e, t) {
534
- const i = this.loadedImages.get(e.id);
535
- if (!i)
537
+ renderImageContent(e, i) {
538
+ const t = this.loadedImages.get(e.id);
539
+ if (!t)
536
540
  return;
537
- const { ctx: n } = this, { location: a } = e, { x: o, y: s, width: r, height: d } = a, l = t.offset || { x: 0, y: 0 }, c = t.scale ?? 1, u = t.rotation ?? 0;
538
- n.save(), n.beginPath(), n.rect(o, s, r, d), n.clip();
539
- const p = i.naturalWidth / i.naturalHeight, g = r / d;
540
- let m, v;
541
- p > g ? (m = r, v = r / p) : (v = d, m = d * p);
542
- const b = m * c, y = v * c, x = o + r / 2 + l.x, w = s + d / 2 + l.y;
543
- n.translate(x, w), n.rotate(u * Math.PI / 180), n.drawImage(i, -b / 2, -y / 2, b, y), n.restore();
541
+ const { ctx: s } = this, { location: a } = e, { x: o, y: n, width: r, height: d } = a, l = i.offset || { x: 0, y: 0 }, h = i.scale ?? 1, u = i.rotation ?? 0;
542
+ s.save(), s.beginPath(), s.rect(o, n, r, d), s.clip();
543
+ const p = t.naturalWidth / t.naturalHeight, m = r / d;
544
+ let g, f;
545
+ p > m ? (g = r, f = r / p) : (f = d, g = d * p);
546
+ const b = g * h, w = f * h, y = o + r / 2 + l.x, x = n + d / 2 + l.y;
547
+ s.translate(y, x), s.rotate(u * Math.PI / 180), s.drawImage(t, -b / 2, -w / 2, b, w), s.restore();
544
548
  }
545
549
  /**
546
550
  * Draw background image on a specific context (for export)
547
551
  */
548
- drawBackgroundImageOnContext(e, t, i) {
552
+ drawBackgroundImageOnContext(e, i, t) {
549
553
  if (!this.bgImageElement || !this.backgroundImage)
550
554
  return;
551
- const { opacity: n } = this.backgroundImage;
552
- e.save(), e.globalAlpha = n;
553
- const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = t / i;
554
- let s, r, d, l;
555
- a > o ? (r = i, s = i * a, d = (t - s) / 2, l = 0) : (s = t, r = t / a, d = 0, l = (i - r) / 2), e.drawImage(this.bgImageElement, d, l, s, r), e.restore();
555
+ const { opacity: s } = this.backgroundImage;
556
+ e.save(), e.globalAlpha = s;
557
+ const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = i / t;
558
+ let n, r, d, l;
559
+ a > o ? (r = t, n = t * a, d = (i - n) / 2, l = 0) : (n = i, r = i / a, d = 0, l = (t - r) / 2), e.drawImage(this.bgImageElement, d, l, n, r), e.restore();
556
560
  }
557
561
  /**
558
562
  * Draw area content on a specific context (for export)
559
563
  */
560
- drawAreaContentOnContext(e, t) {
561
- const { location: i, backgroundColor: n } = t, { x: a, y: o, width: s, height: r } = i;
562
- n && (e.fillStyle = n, e.fillRect(a, o, s, r));
563
- const d = this.contents.get(t.id);
564
+ drawAreaContentOnContext(e, i) {
565
+ const { location: t, backgroundColor: s } = i, { x: a, y: o, width: n, height: r } = t;
566
+ s && (e.fillStyle = s, e.fillRect(a, o, n, r));
567
+ const d = this.contents.get(i.id);
564
568
  if (d) {
565
569
  if (d.type === "text") {
566
570
  if (!d.text.trim())
567
571
  return;
568
- const l = d.offset || { x: 0, y: 0 }, c = d.scale ?? 1, u = d.rotation ?? 0, p = a + s / 2 + l.x, g = o + r / 2 + l.y;
569
- e.save(), e.beginPath(), e.rect(a, o, s, r), e.clip(), e.translate(p, g), e.rotate(u * Math.PI / 180), e.scale(c, c), e.font = `${d.size}px ${d.font}, sans-serif`, e.fillStyle = d.color, e.textBaseline = "middle";
570
- let m;
572
+ const l = d.offset || { x: 0, y: 0 }, h = d.scale ?? 1, u = d.rotation ?? 0, p = a + n / 2 + l.x, m = o + r / 2 + l.y;
573
+ e.save(), e.beginPath(), e.rect(a, o, n, r), e.clip(), e.translate(p, m), e.rotate(u * Math.PI / 180), e.scale(h, h), e.font = `${d.size}px ${d.font}, sans-serif`, e.fillStyle = d.color, e.textBaseline = "middle";
574
+ let g;
571
575
  switch (d.align) {
572
576
  case "left":
573
- e.textAlign = "left", m = -s / 2 + 10;
577
+ e.textAlign = "left", g = -n / 2 + 10;
574
578
  break;
575
579
  case "right":
576
- e.textAlign = "right", m = s / 2 - 10;
580
+ e.textAlign = "right", g = n / 2 - 10;
577
581
  break;
578
582
  case "center":
579
583
  default:
580
- e.textAlign = "center", m = 0;
584
+ e.textAlign = "center", g = 0;
581
585
  break;
582
586
  }
583
- e.fillText(d.text, m, 0), e.restore();
587
+ e.fillText(d.text, g, 0), e.restore();
584
588
  } else if (d.type === "image") {
585
- const l = this.loadedImages.get(t.id);
589
+ const l = this.loadedImages.get(i.id);
586
590
  if (!l)
587
591
  return;
588
- const c = d.offset || { x: 0, y: 0 }, u = d.scale ?? 1, p = d.rotation ?? 0;
589
- e.save(), e.beginPath(), e.rect(a, o, s, r), e.clip();
590
- const g = l.naturalWidth / l.naturalHeight, m = s / r;
591
- let v, b;
592
- g > m ? (v = s, b = s / g) : (b = r, v = r * g);
593
- const y = v * u, x = b * u, w = a + s / 2 + c.x, C = o + r / 2 + c.y;
594
- e.translate(w, C), e.rotate(p * Math.PI / 180), e.drawImage(l, -y / 2, -x / 2, y, x), e.restore();
592
+ const h = d.offset || { x: 0, y: 0 }, u = d.scale ?? 1, p = d.rotation ?? 0;
593
+ e.save(), e.beginPath(), e.rect(a, o, n, r), e.clip();
594
+ const m = l.naturalWidth / l.naturalHeight, g = n / r;
595
+ let f, b;
596
+ m > g ? (f = n, b = n / m) : (b = r, f = r * m);
597
+ const w = f * u, y = b * u, x = a + n / 2 + h.x, C = o + r / 2 + h.y;
598
+ e.translate(x, C), e.rotate(p * Math.PI / 180), e.drawImage(l, -w / 2, -y / 2, w, y), e.restore();
595
599
  }
596
600
  }
597
601
  }
@@ -602,8 +606,8 @@ class N {
602
606
  this.stop(), this.bgImageElement = null, this.loadedImages.clear(), this.loadingImages.clear(), this.eventListeners.clear();
603
607
  }
604
608
  }
605
- function E(f) {
606
- const e = f.textOptions;
609
+ function S(v) {
610
+ const e = v.textOptions;
607
611
  return {
608
612
  type: "text",
609
613
  text: e?.defaultText || "",
@@ -613,13 +617,13 @@ function E(f) {
613
617
  align: e?.defaultAlign || "center"
614
618
  };
615
619
  }
616
- function S(f) {
617
- return f.contentType === "text" || f.contentType === "both";
620
+ function z(v) {
621
+ return v.contentType === "text" || v.contentType === "both";
618
622
  }
619
- function k(f) {
620
- return f.contentType === "image" || f.contentType === "both";
623
+ function T(v) {
624
+ return v.contentType === "image" || v.contentType === "both";
621
625
  }
622
- const U = 50;
626
+ const V = 50;
623
627
  class O {
624
628
  constructor(e) {
625
629
  this.history = [], this.historyIndex = -1, this.listeners = /* @__PURE__ */ new Set(), this.isRestoring = !1, this.state = this.cloneState(e), this.history.push({
@@ -634,9 +638,9 @@ class O {
634
638
  /**
635
639
  * Update state and save to history
636
640
  */
637
- setState(e, t, i = !1) {
638
- const n = this.state;
639
- this.state = { ...this.state, ...e }, !i && !this.isRestoring && this.saveToHistory(n, t), this.notifyListeners();
641
+ setState(e, i, t = !1) {
642
+ const s = this.state;
643
+ this.state = { ...this.state, ...e }, !t && !this.isRestoring && this.saveToHistory(s, i), this.notifyListeners();
640
644
  }
641
645
  /**
642
646
  * Update state without saving to history
@@ -666,12 +670,12 @@ class O {
666
670
  getHistory() {
667
671
  return this.history;
668
672
  }
669
- saveToHistory(e, t) {
673
+ saveToHistory(e, i) {
670
674
  this.historyIndex < this.history.length - 1 && (this.history = this.history.slice(0, this.historyIndex + 1)), this.history.push({
671
675
  state: this.cloneState(e),
672
676
  timestamp: Date.now(),
673
- description: t
674
- }), this.history.length > U && this.history.shift(), this.historyIndex = this.history.length - 1;
677
+ description: i
678
+ }), this.history.length > V && this.history.shift(), this.historyIndex = this.history.length - 1;
675
679
  }
676
680
  restoreFromHistory() {
677
681
  const e = this.history[this.historyIndex];
@@ -679,11 +683,11 @@ class O {
679
683
  }
680
684
  notifyListeners() {
681
685
  const e = this.state;
682
- for (const t of this.listeners)
686
+ for (const i of this.listeners)
683
687
  try {
684
- t(e);
685
- } catch (i) {
686
- console.error("Error in state change listener:", i);
688
+ i(e);
689
+ } catch (t) {
690
+ console.error("Error in state change listener:", t);
687
691
  }
688
692
  }
689
693
  /**
@@ -694,14 +698,14 @@ class O {
694
698
  return JSON.parse(JSON.stringify(e));
695
699
  }
696
700
  }
697
- class T {
701
+ class P {
698
702
  constructor(e) {
699
703
  this.listeners = /* @__PURE__ */ new Set(), this.areas = e, this.contents = this.buildInitialContents();
700
704
  }
701
705
  buildInitialContents() {
702
706
  const e = /* @__PURE__ */ new Map();
703
- for (const t of this.areas)
704
- S(t) && e.set(t.id, E(t));
707
+ for (const i of this.areas)
708
+ z(i) && e.set(i.id, S(i));
705
709
  return e;
706
710
  }
707
711
  getContents() {
@@ -710,8 +714,8 @@ class T {
710
714
  getContent(e) {
711
715
  return this.contents.get(e);
712
716
  }
713
- setTextContent(e, t) {
714
- const i = this.contents.get(e), n = this.areas.find((o) => o.id === e), a = n ? E(n) : {
717
+ setTextContent(e, i) {
718
+ const t = this.contents.get(e), s = this.areas.find((o) => o.id === e), a = s ? S(s) : {
715
719
  type: "text",
716
720
  text: "",
717
721
  font: "Inter",
@@ -719,35 +723,35 @@ class T {
719
723
  color: "#000000",
720
724
  align: "center"
721
725
  };
722
- i?.type === "text" ? this.contents.set(e, { ...i, ...t }) : this.contents.set(e, { ...a, ...t }), this.notify();
726
+ t?.type === "text" ? this.contents.set(e, { ...t, ...i }) : this.contents.set(e, { ...a, ...i }), this.notify();
723
727
  }
724
- setImageContent(e, t, i) {
725
- const n = {
728
+ setImageContent(e, i, t) {
729
+ const s = {
726
730
  type: "image",
727
- dataUrl: t,
728
- filename: i
731
+ dataUrl: i,
732
+ filename: t
729
733
  };
730
- this.contents.set(e, n), this.notify();
734
+ this.contents.set(e, s), this.notify();
731
735
  }
732
736
  clearContent(e) {
733
- const t = this.areas.find((i) => i.id === e);
734
- t && S(t) ? this.contents.set(e, E(t)) : this.contents.delete(e), this.notify();
737
+ const i = this.areas.find((t) => t.id === e);
738
+ i && z(i) ? this.contents.set(e, S(i)) : this.contents.delete(e), this.notify();
735
739
  }
736
- setContentOffset(e, t) {
737
- const i = this.contents.get(e);
738
- i && (this.contents.set(e, { ...i, offset: t }), this.notify());
740
+ setContentOffset(e, i) {
741
+ const t = this.contents.get(e);
742
+ t && (this.contents.set(e, { ...t, offset: i }), this.notify());
739
743
  }
740
- setImageScale(e, t) {
741
- const i = this.contents.get(e);
742
- if (!i || i.type !== "image") return;
743
- const n = Math.max(0.1, Math.min(5, t));
744
- this.contents.set(e, { ...i, scale: n }), this.notify();
744
+ setImageScale(e, i) {
745
+ const t = this.contents.get(e);
746
+ if (!t || t.type !== "image") return;
747
+ const s = Math.max(0.1, Math.min(5, i));
748
+ this.contents.set(e, { ...t, scale: s }), this.notify();
745
749
  }
746
- setImageRotation(e, t) {
747
- const i = this.contents.get(e);
748
- if (!i || i.type !== "image") return;
749
- let n = t % 360;
750
- n < 0 && (n += 360), this.contents.set(e, { ...i, rotation: n }), this.notify();
750
+ setImageRotation(e, i) {
751
+ const t = this.contents.get(e);
752
+ if (!t || t.type !== "image") return;
753
+ let s = i % 360;
754
+ s < 0 && (s += 360), this.contents.set(e, { ...t, rotation: s }), this.notify();
751
755
  }
752
756
  reset() {
753
757
  this.contents = this.buildInitialContents(), this.notify();
@@ -771,12 +775,12 @@ class T {
771
775
  for (const e of this.listeners)
772
776
  try {
773
777
  e();
774
- } catch (t) {
775
- console.error("Error in AreaContentManager listener:", t);
778
+ } catch (i) {
779
+ console.error("Error in AreaContentManager listener:", i);
776
780
  }
777
781
  }
778
782
  }
779
- class I {
783
+ class E {
780
784
  constructor() {
781
785
  this.listeners = /* @__PURE__ */ new Map();
782
786
  }
@@ -787,8 +791,8 @@ class I {
787
791
  * @param handler - Event handler function
788
792
  * @returns Unsubscribe function
789
793
  */
790
- on(e, t) {
791
- return this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(t), () => this.off(e, t);
794
+ on(e, i) {
795
+ return this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(i), () => this.off(e, i);
792
796
  }
793
797
  /**
794
798
  * Register a one-time event listener
@@ -797,11 +801,11 @@ class I {
797
801
  * @param handler - Event handler function
798
802
  * @returns Unsubscribe function
799
803
  */
800
- once(e, t) {
801
- const i = (n) => {
802
- t(n), this.off(e, i);
804
+ once(e, i) {
805
+ const t = (s) => {
806
+ i(s), this.off(e, t);
803
807
  };
804
- return this.on(e, i);
808
+ return this.on(e, t);
805
809
  }
806
810
  /**
807
811
  * Remove an event listener
@@ -809,9 +813,9 @@ class I {
809
813
  * @param event - Event name
810
814
  * @param handler - Event handler function to remove
811
815
  */
812
- off(e, t) {
813
- const i = this.listeners.get(e);
814
- i && (i.delete(t), i.size === 0 && this.listeners.delete(e));
816
+ off(e, i) {
817
+ const t = this.listeners.get(e);
818
+ t && (t.delete(i), t.size === 0 && this.listeners.delete(e));
815
819
  }
816
820
  /**
817
821
  * Emit an event to all registered listeners
@@ -819,13 +823,13 @@ class I {
819
823
  * @param event - Event name
820
824
  * @param data - Event data
821
825
  */
822
- emit(e, t) {
823
- const i = this.listeners.get(e);
824
- if (i) {
825
- const n = Array.from(i);
826
- for (const a of n)
826
+ emit(e, i) {
827
+ const t = this.listeners.get(e);
828
+ if (t) {
829
+ const s = Array.from(t);
830
+ for (const a of s)
827
831
  try {
828
- a(t);
832
+ a(i);
829
833
  } catch (o) {
830
834
  console.error(`Error in event handler for "${String(e)}":`, o);
831
835
  }
@@ -849,7 +853,7 @@ class I {
849
853
  return this.listeners.get(e)?.size ?? 0;
850
854
  }
851
855
  }
852
- class V extends I {
856
+ class $ extends E {
853
857
  constructor() {
854
858
  super(...arguments), this.activeTouches = /* @__PURE__ */ new Map(), this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1;
855
859
  }
@@ -858,86 +862,86 @@ class V extends I {
858
862
  return this.isTwoFingerGesture;
859
863
  }
860
864
  handleTouchStart(e) {
861
- for (let t = 0; t < e.changedTouches.length; t++) {
862
- const i = e.changedTouches[t];
863
- this.activeTouches.set(i.identifier, {
864
- x: i.clientX,
865
- y: i.clientY
865
+ for (let i = 0; i < e.changedTouches.length; i++) {
866
+ const t = e.changedTouches[i];
867
+ this.activeTouches.set(t.identifier, {
868
+ x: t.clientX,
869
+ y: t.clientY
866
870
  });
867
871
  }
868
872
  if (this.activeTouches.size === 2) {
869
873
  this.isTwoFingerGesture = !0, e.preventDefault();
870
- const [t, i] = Array.from(this.activeTouches.values());
871
- this.initialPinchDistance = this.getDistance(t, i), this.lastPinchCenter = this.getCenter(t, i);
874
+ const [i, t] = Array.from(this.activeTouches.values());
875
+ this.initialPinchDistance = this.getDistance(i, t), this.lastPinchCenter = this.getCenter(i, t);
872
876
  }
873
877
  }
874
878
  handleTouchMove(e) {
875
- for (let t = 0; t < e.changedTouches.length; t++) {
876
- const i = e.changedTouches[t];
877
- this.activeTouches.set(i.identifier, {
878
- x: i.clientX,
879
- y: i.clientY
879
+ for (let i = 0; i < e.changedTouches.length; i++) {
880
+ const t = e.changedTouches[i];
881
+ this.activeTouches.set(t.identifier, {
882
+ x: t.clientX,
883
+ y: t.clientY
880
884
  });
881
885
  }
882
886
  if (this.activeTouches.size === 2 && this.initialPinchDistance !== null && this.lastPinchCenter) {
883
887
  e.preventDefault();
884
- const [t, i] = Array.from(this.activeTouches.values()), n = this.getDistance(t, i), a = this.getCenter(t, i), o = n / this.initialPinchDistance;
888
+ const [i, t] = Array.from(this.activeTouches.values()), s = this.getDistance(i, t), a = this.getCenter(i, t), o = s / this.initialPinchDistance;
885
889
  this.emit("pinch", {
886
890
  scale: o,
887
891
  centerX: a.x,
888
892
  centerY: a.y
889
893
  });
890
- const s = a.x - this.lastPinchCenter.x, r = a.y - this.lastPinchCenter.y;
891
- (Math.abs(s) > 0.5 || Math.abs(r) > 0.5) && this.emit("pan", { deltaX: s, deltaY: r }), this.initialPinchDistance = n, this.lastPinchCenter = a;
894
+ const n = a.x - this.lastPinchCenter.x, r = a.y - this.lastPinchCenter.y;
895
+ (Math.abs(n) > 0.5 || Math.abs(r) > 0.5) && this.emit("pan", { deltaX: n, deltaY: r }), this.initialPinchDistance = s, this.lastPinchCenter = a;
892
896
  }
893
897
  }
894
898
  handleTouchEnd(e) {
895
- for (let t = 0; t < e.changedTouches.length; t++) {
896
- const i = e.changedTouches[t];
897
- this.activeTouches.delete(i.identifier);
899
+ for (let i = 0; i < e.changedTouches.length; i++) {
900
+ const t = e.changedTouches[i];
901
+ this.activeTouches.delete(t.identifier);
898
902
  }
899
903
  this.activeTouches.size < 2 && (this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1);
900
904
  }
901
905
  reset() {
902
906
  this.activeTouches.clear(), this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1;
903
907
  }
904
- getDistance(e, t) {
905
- const i = t.x - e.x, n = t.y - e.y;
906
- return Math.sqrt(i * i + n * n);
908
+ getDistance(e, i) {
909
+ const t = i.x - e.x, s = i.y - e.y;
910
+ return Math.sqrt(t * t + s * s);
907
911
  }
908
- getCenter(e, t) {
912
+ getCenter(e, i) {
909
913
  return {
910
- x: (e.x + t.x) / 2,
911
- y: (e.y + t.y) / 2
914
+ x: (e.x + i.x) / 2,
915
+ y: (e.y + i.y) / 2
912
916
  };
913
917
  }
914
918
  }
915
- class F extends I {
916
- constructor(e, t, i, n) {
917
- super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine = t, this.getContents = i, this.getSelectedAreaId = n, this.gestureHandler = new V(), this.boundMouseDown = this.handleMouseDown.bind(this), this.boundMouseMove = this.handleMouseMove.bind(this), this.boundMouseUp = this.handleMouseUp.bind(this), this.boundMouseLeave = this.handleMouseLeave.bind(this), this.boundContextMenu = this.handleContextMenu.bind(this), this.boundDoubleClick = this.handleDoubleClick.bind(this), this.boundTouchStart = this.handleTouchStart.bind(this), this.boundTouchMove = this.handleTouchMove.bind(this), this.boundTouchEnd = this.handleTouchEnd.bind(this), e.addEventListener("mousedown", this.boundMouseDown), e.addEventListener("mousemove", this.boundMouseMove), e.addEventListener("mouseup", this.boundMouseUp), e.addEventListener("mouseleave", this.boundMouseLeave), e.addEventListener("contextmenu", this.boundContextMenu), e.addEventListener("dblclick", this.boundDoubleClick), e.addEventListener("touchstart", this.boundTouchStart, { passive: !1 }), e.addEventListener("touchmove", this.boundTouchMove, { passive: !1 }), e.addEventListener("touchend", this.boundTouchEnd), this.gestureHandler.on("pinch", ({ scale: a }) => {
919
+ class F extends E {
920
+ constructor(e, i, t, s) {
921
+ super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine = i, this.getContents = t, this.getSelectedAreaId = s, this.gestureHandler = new $(), this.boundMouseDown = this.handleMouseDown.bind(this), this.boundMouseMove = this.handleMouseMove.bind(this), this.boundMouseUp = this.handleMouseUp.bind(this), this.boundMouseLeave = this.handleMouseLeave.bind(this), this.boundContextMenu = this.handleContextMenu.bind(this), this.boundDoubleClick = this.handleDoubleClick.bind(this), this.boundTouchStart = this.handleTouchStart.bind(this), this.boundTouchMove = this.handleTouchMove.bind(this), this.boundTouchEnd = this.handleTouchEnd.bind(this), e.addEventListener("mousedown", this.boundMouseDown), e.addEventListener("mousemove", this.boundMouseMove), e.addEventListener("mouseup", this.boundMouseUp), e.addEventListener("mouseleave", this.boundMouseLeave), e.addEventListener("contextmenu", this.boundContextMenu), e.addEventListener("dblclick", this.boundDoubleClick), e.addEventListener("touchstart", this.boundTouchStart, { passive: !1 }), e.addEventListener("touchmove", this.boundTouchMove, { passive: !1 }), e.addEventListener("touchend", this.boundTouchEnd), this.gestureHandler.on("pinch", ({ scale: a }) => {
918
922
  const o = Math.max(0.1, Math.min(5, this.initialPinchZoom * a));
919
923
  this.emit("zoom", { zoom: o });
920
924
  }), this.gestureHandler.on("pan", ({ deltaX: a, deltaY: o }) => {
921
- const s = this.engine.getPan(), r = this.engine.getZoom();
925
+ const n = this.engine.getPan(), r = this.engine.getZoom();
922
926
  this.emit("pan", {
923
927
  pan: {
924
- x: s.x + a / r,
925
- y: s.y + o / r
928
+ x: n.x + a / r,
929
+ y: n.y + o / r
926
930
  }
927
931
  });
928
932
  });
929
933
  }
930
- getCanvasPosition(e, t) {
931
- const i = this.canvas.getBoundingClientRect(), n = window.devicePixelRatio || 1, a = (e - i.left) * n, o = (t - i.top) * n;
934
+ getCanvasPosition(e, i) {
935
+ const t = this.canvas.getBoundingClientRect(), s = window.devicePixelRatio || 1, a = (e - t.left) * s, o = (i - t.top) * s;
932
936
  return this.engine.screenToCanvas(a, o);
933
937
  }
934
938
  handleMouseDown(e) {
935
- const t = this.getCanvasPosition(e.clientX, e.clientY);
936
- this.processPointerDown(t);
939
+ const i = this.getCanvasPosition(e.clientX, e.clientY);
940
+ this.processPointerDown(i);
937
941
  }
938
942
  handleMouseMove(e) {
939
- const t = this.getCanvasPosition(e.clientX, e.clientY);
940
- this.processPointerMove(t);
943
+ const i = this.getCanvasPosition(e.clientX, e.clientY);
944
+ this.processPointerMove(i);
941
945
  }
942
946
  handleMouseUp() {
943
947
  this.processPointerUp();
@@ -949,9 +953,9 @@ class F extends I {
949
953
  e.preventDefault();
950
954
  }
951
955
  handleDoubleClick(e) {
952
- const t = this.getCanvasPosition(e.clientX, e.clientY);
953
- let i = this.engine.hitTestContent(t.x, t.y);
954
- i || (i = this.engine.hitTestArea(t.x, t.y) ?? null), this.emit("context-menu", { areaId: i, clientX: e.clientX, clientY: e.clientY });
956
+ const i = this.getCanvasPosition(e.clientX, e.clientY);
957
+ let t = this.engine.hitTestContent(i.x, i.y);
958
+ t || (t = this.engine.hitTestArea(i.x, i.y) ?? null), this.emit("context-menu", { areaId: t, clientX: e.clientX, clientY: e.clientY });
955
959
  }
956
960
  handleTouchStart(e) {
957
961
  if (this.gestureHandler.handleTouchStart(e), this.gestureHandler.isMultiTouch) {
@@ -959,13 +963,13 @@ class F extends I {
959
963
  return;
960
964
  }
961
965
  if (e.touches.length !== 1) return;
962
- const t = e.touches[0], i = this.getCanvasPosition(t.clientX, t.clientY);
963
- this.processPointerDown(i) && e.preventDefault();
966
+ const i = e.touches[0], t = this.getCanvasPosition(i.clientX, i.clientY);
967
+ this.processPointerDown(t) && e.preventDefault();
964
968
  }
965
969
  handleTouchMove(e) {
966
970
  if (this.gestureHandler.handleTouchMove(e), this.gestureHandler.isMultiTouch || e.touches.length !== 1 || this.interactionState.mode === "idle") return;
967
- const t = e.touches[0], i = this.getCanvasPosition(t.clientX, t.clientY);
968
- this.processPointerMove(i), e.preventDefault();
971
+ const i = e.touches[0], t = this.getCanvasPosition(i.clientX, i.clientY);
972
+ this.processPointerMove(t), e.preventDefault();
969
973
  }
970
974
  handleTouchEnd(e) {
971
975
  this.gestureHandler.handleTouchEnd(e), this.gestureHandler.isMultiTouch || this.processPointerUp();
@@ -974,38 +978,38 @@ class F extends I {
974
978
  * Process pointer down. Returns true if an interaction started.
975
979
  */
976
980
  processPointerDown(e) {
977
- const t = this.getSelectedAreaId(), i = this.getContents();
978
- if (t) {
979
- const o = this.engine.hitTestHandle(e.x, e.y, t);
981
+ const i = this.getSelectedAreaId(), t = this.getContents();
982
+ if (i) {
983
+ const o = this.engine.hitTestHandle(e.x, e.y, i);
980
984
  if (o) {
981
- const s = i.get(t), r = this.engine.getContentBounds(t);
982
- if (o === "rotate" && s?.type === "image" && r) {
985
+ const n = t.get(i), r = this.engine.getContentBounds(i);
986
+ if (o === "rotate" && n?.type === "image" && r) {
983
987
  const d = Math.atan2(e.y - r.centerY, e.x - r.centerX) * (180 / Math.PI);
984
988
  return this.interactionState = {
985
989
  mode: "rotating",
986
- areaId: t,
987
- startRotation: s.rotation ?? 0,
990
+ areaId: i,
991
+ startRotation: n.rotation ?? 0,
988
992
  centerPoint: { x: r.centerX, y: r.centerY },
989
993
  startAngle: d
990
994
  }, !0;
991
- } else if (o !== "rotate" && s?.type === "image" && r)
995
+ } else if (o !== "rotate" && n?.type === "image" && r)
992
996
  return this.interactionState = {
993
997
  mode: "scaling",
994
- areaId: t,
998
+ areaId: i,
995
999
  handle: o,
996
- startScale: s.scale ?? 1,
1000
+ startScale: n.scale ?? 1,
997
1001
  startMousePos: e,
998
1002
  pivotPoint: { x: r.centerX, y: r.centerY }
999
1003
  }, !0;
1000
1004
  }
1001
1005
  }
1002
- const n = this.engine.hitTestContent(e.x, e.y);
1003
- if (n) {
1004
- if (this.emit("area:select", { areaId: n }), this.engine.isPositioningAllowed(n)) {
1005
- const o = i.get(n)?.offset ?? { x: 0, y: 0 };
1006
+ const s = this.engine.hitTestContent(e.x, e.y);
1007
+ if (s) {
1008
+ if (this.emit("area:select", { areaId: s }), this.engine.isPositioningAllowed(s)) {
1009
+ const o = t.get(s)?.offset ?? { x: 0, y: 0 };
1006
1010
  return this.interactionState = {
1007
1011
  mode: "dragging",
1008
- areaId: n,
1012
+ areaId: s,
1009
1013
  startOffset: o,
1010
1014
  startMousePos: e
1011
1015
  }, !0;
@@ -1017,42 +1021,40 @@ class F extends I {
1017
1021
  }
1018
1022
  processPointerMove(e) {
1019
1023
  if (this.interactionState.mode === "dragging") {
1020
- const { areaId: t, startOffset: i, startMousePos: n } = this.interactionState, a = e.x - n.x, o = e.y - n.y;
1021
- let s = {
1022
- x: i.x + a,
1023
- y: i.y + o
1024
- };
1025
- const r = this.engine.getAreaLocation(t);
1024
+ const { areaId: i, startOffset: t, startMousePos: s } = this.interactionState, a = e.x - s.x, o = e.y - s.y, n = {
1025
+ x: t.x + a,
1026
+ y: t.y + o
1027
+ }, r = this.engine.getAreaLocation(i);
1026
1028
  if (r) {
1027
1029
  const d = r.width / 2 - 10, l = r.height / 2 - 10;
1028
- s.x = Math.max(-d, Math.min(d, s.x)), s.y = Math.max(-l, Math.min(l, s.y));
1030
+ n.x = Math.max(-d, Math.min(d, n.x)), n.y = Math.max(-l, Math.min(l, n.y));
1029
1031
  }
1030
- this.emit("content:drag", { areaId: t, offset: s });
1032
+ this.emit("content:drag", { areaId: i, offset: n });
1031
1033
  return;
1032
1034
  }
1033
1035
  if (this.interactionState.mode === "scaling") {
1034
- const { areaId: t, startScale: i, startMousePos: n, pivotPoint: a } = this.interactionState, o = Math.sqrt(
1035
- Math.pow(n.x - a.x, 2) + Math.pow(n.y - a.y, 2)
1036
- ), s = Math.sqrt(
1036
+ const { areaId: i, startScale: t, startMousePos: s, pivotPoint: a } = this.interactionState, o = Math.sqrt(
1037
+ Math.pow(s.x - a.x, 2) + Math.pow(s.y - a.y, 2)
1038
+ ), n = Math.sqrt(
1037
1039
  Math.pow(e.x - a.x, 2) + Math.pow(e.y - a.y, 2)
1038
1040
  );
1039
1041
  if (o > 0) {
1040
- const r = s / o, d = Math.max(0.1, Math.min(5, i * r));
1041
- this.emit("content:scale", { areaId: t, scale: d });
1042
+ const r = n / o, d = Math.max(0.1, Math.min(5, t * r));
1043
+ this.emit("content:scale", { areaId: i, scale: d });
1042
1044
  }
1043
1045
  return;
1044
1046
  }
1045
1047
  if (this.interactionState.mode === "rotating") {
1046
- const { areaId: t, startRotation: i, centerPoint: n, startAngle: a } = this.interactionState, o = Math.atan2(e.y - n.y, e.x - n.x) * (180 / Math.PI);
1047
- let s = i + (o - a);
1048
- s = s % 360, s < 0 && (s += 360);
1048
+ const { areaId: i, startRotation: t, centerPoint: s, startAngle: a } = this.interactionState, o = Math.atan2(e.y - s.y, e.x - s.x) * (180 / Math.PI);
1049
+ let n = t + (o - a);
1050
+ n = n % 360, n < 0 && (n += 360);
1049
1051
  const r = [0, 90, 180, 270, 360];
1050
1052
  for (const d of r)
1051
- if (Math.abs(s - d) < 5) {
1052
- s = d === 360 ? 0 : d;
1053
+ if (Math.abs(n - d) < 5) {
1054
+ n = d === 360 ? 0 : d;
1053
1055
  break;
1054
1056
  }
1055
- this.emit("content:rotate", { areaId: t, rotation: s });
1057
+ this.emit("content:rotate", { areaId: i, rotation: n });
1056
1058
  return;
1057
1059
  }
1058
1060
  this.updateCursor(e);
@@ -1061,14 +1063,14 @@ class F extends I {
1061
1063
  this.interactionState.mode !== "idle" && (this.interactionState = { mode: "idle" });
1062
1064
  }
1063
1065
  updateCursor(e) {
1064
- const t = this.getSelectedAreaId();
1065
- let i = "default";
1066
- if (t) {
1067
- const n = this.engine.hitTestHandle(e.x, e.y, t);
1068
- n ? i = n === "rotate" ? "crosshair" : n === "nw" || n === "se" ? "nwse-resize" : "nesw-resize" : this.engine.hitTestContent(e.x, e.y) === t && (i = this.engine.isPositioningAllowed(t) ? "move" : "pointer");
1066
+ const i = this.getSelectedAreaId();
1067
+ let t = "default";
1068
+ if (i) {
1069
+ const s = this.engine.hitTestHandle(e.x, e.y, i);
1070
+ s ? t = s === "rotate" ? "crosshair" : s === "nw" || s === "se" ? "nwse-resize" : "nesw-resize" : this.engine.hitTestContent(e.x, e.y) === i && (t = this.engine.isPositioningAllowed(i) ? "move" : "pointer");
1069
1071
  } else
1070
- this.engine.hitTestContent(e.x, e.y) && (i = "pointer");
1071
- this.emit("cursor", { cursor: i });
1072
+ this.engine.hitTestContent(e.x, e.y) && (t = "pointer");
1073
+ this.emit("cursor", { cursor: t });
1072
1074
  }
1073
1075
  setSelectedArea(e) {
1074
1076
  this.engine.setSelectedArea(e);
@@ -1077,164 +1079,230 @@ class F extends I {
1077
1079
  this.canvas.removeEventListener("mousedown", this.boundMouseDown), this.canvas.removeEventListener("mousemove", this.boundMouseMove), this.canvas.removeEventListener("mouseup", this.boundMouseUp), this.canvas.removeEventListener("mouseleave", this.boundMouseLeave), this.canvas.removeEventListener("contextmenu", this.boundContextMenu), this.canvas.removeEventListener("dblclick", this.boundDoubleClick), this.canvas.removeEventListener("touchstart", this.boundTouchStart), this.canvas.removeEventListener("touchmove", this.boundTouchMove), this.canvas.removeEventListener("touchend", this.boundTouchEnd), this.gestureHandler.reset(), this.clear();
1078
1080
  }
1079
1081
  }
1080
- class $ {
1082
+ class B {
1081
1083
  constructor(e = "/api") {
1082
1084
  this.baseUrl = e;
1083
1085
  }
1084
1086
  async getTemplate(e) {
1085
1087
  try {
1086
- const t = await fetch(`${this.baseUrl}/templates/${e}`);
1087
- if (!t.ok) {
1088
- let i = `Failed to load template (${t.status} ${t.statusText}): ${e}
1088
+ const i = await fetch(`${this.baseUrl}/templates/${e}`);
1089
+ if (!i.ok) {
1090
+ let t = `Failed to load template (${i.status} ${i.statusText}): ${e}
1089
1091
 
1090
1092
  `;
1091
- throw t.status === 404 ? i += "Template not found." : t.status === 403 ? i += "Access forbidden." : t.status >= 500 && (i += "Server error."), new Error(i);
1093
+ throw i.status === 404 ? t += "Template not found." : i.status === 403 ? t += "Access forbidden." : i.status >= 500 && (t += "Server error."), new Error(t);
1092
1094
  }
1093
- return (await t.json()).templateJson;
1094
- } catch (t) {
1095
- throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error(`Network error loading template: ${e}`) : t;
1095
+ return (await i.json()).templateJson;
1096
+ } catch (i) {
1097
+ throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error(`Network error loading template: ${e}`) : i;
1096
1098
  }
1097
1099
  }
1098
1100
  async getProduct(e) {
1099
1101
  try {
1100
- const t = await fetch(`${this.baseUrl}/products/${e}`);
1102
+ const i = await fetch(`${this.baseUrl}/products/${e}`);
1103
+ if (!i.ok) {
1104
+ let t = `Failed to load product (${i.status} ${i.statusText}): ${e}
1105
+
1106
+ `;
1107
+ throw i.status === 404 ? t += "Product not found." : i.status === 403 ? t += "Access forbidden." : i.status >= 500 && (t += "Server error."), new Error(t);
1108
+ }
1109
+ return i.json();
1110
+ } catch (i) {
1111
+ throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error(`Network error loading product: ${e}`) : i;
1112
+ }
1113
+ }
1114
+ async finalizeDesign(e, i) {
1115
+ try {
1116
+ const t = await fetch(`${this.baseUrl}/finalize`, {
1117
+ method: "POST",
1118
+ headers: { "Content-Type": "application/json" },
1119
+ body: JSON.stringify({
1120
+ shopId: e,
1121
+ templateId: i.templateId,
1122
+ designJson: i
1123
+ })
1124
+ });
1101
1125
  if (!t.ok) {
1102
- let i = `Failed to load product (${t.status} ${t.statusText}): ${e}
1126
+ let s = `Failed to finalize design (${t.status} ${t.statusText})
1103
1127
 
1104
1128
  `;
1105
- throw t.status === 404 ? i += "Product not found." : t.status === 403 ? i += "Access forbidden." : t.status >= 500 && (i += "Server error."), new Error(i);
1129
+ if (t.status === 400) {
1130
+ const a = await t.json().catch(() => ({}));
1131
+ s += a.error || "Invalid design data.";
1132
+ } else t.status === 429 ? s += "Rate limit exceeded." : t.status >= 500 && (s += "Server error.");
1133
+ throw new Error(s);
1106
1134
  }
1107
1135
  return t.json();
1108
1136
  } catch (t) {
1109
- throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error(`Network error loading product: ${e}`) : t;
1137
+ throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error("Network error finalizing design") : t;
1110
1138
  }
1111
1139
  }
1112
- async finalizeDesign(e, t) {
1140
+ async finalizeMultiView(e) {
1113
1141
  try {
1114
- const i = await fetch(`${this.baseUrl}/finalize`, {
1142
+ const i = e.designs.map((s) => ({
1143
+ templateId: s.templateId,
1144
+ viewName: s.viewName,
1145
+ areaContents: s.contents.map(([a, o]) => {
1146
+ const n = { areaId: a, isModified: !0 };
1147
+ return o.type === "text" ? n.text = {
1148
+ content: o.text,
1149
+ font: o.font,
1150
+ size: o.size,
1151
+ color: o.color,
1152
+ align: o.align,
1153
+ position: { x: o.offset?.x ?? 0, y: o.offset?.y ?? 0 }
1154
+ } : o.type === "image" && (n.image = {
1155
+ assetId: "inline",
1156
+ assetUrl: "",
1157
+ rotation: o.rotation ?? 0,
1158
+ scale: o.scale ?? 1,
1159
+ position: { x: o.offset?.x ?? 0, y: o.offset?.y ?? 0 },
1160
+ dataUrl: o.dataUrl
1161
+ }), n;
1162
+ })
1163
+ })), t = await fetch(`${this.baseUrl}/finalize/multi`, {
1115
1164
  method: "POST",
1116
1165
  headers: { "Content-Type": "application/json" },
1117
1166
  body: JSON.stringify({
1118
- shopId: e,
1119
- templateId: t.templateId,
1120
- designJson: t
1167
+ productId: e.productId,
1168
+ designs: i,
1169
+ cartContext: e.cartContext,
1170
+ customerId: e.customerId,
1171
+ customerEmail: e.customerEmail,
1172
+ sessionId: e.sessionId
1121
1173
  })
1122
1174
  });
1123
- if (!i.ok) {
1124
- let n = `Failed to finalize design (${i.status} ${i.statusText})
1175
+ if (!t.ok) {
1176
+ let s = `Failed to finalize multi-view design (${t.status} ${t.statusText})
1125
1177
 
1126
1178
  `;
1127
- if (i.status === 400) {
1128
- const a = await i.json().catch(() => ({}));
1129
- n += a.error || "Invalid design data.";
1130
- } else i.status === 429 ? n += "Rate limit exceeded." : i.status >= 500 && (n += "Server error.");
1131
- throw new Error(n);
1179
+ if (t.status === 400) {
1180
+ const a = await t.json().catch(() => ({}));
1181
+ s += a.error || "Invalid design data.";
1182
+ } else t.status === 429 ? s += "Rate limit exceeded." : t.status >= 500 && (s += "Server error.");
1183
+ throw new Error(s);
1132
1184
  }
1185
+ return t.json();
1186
+ } catch (i) {
1187
+ throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error finalizing multi-view design") : i;
1188
+ }
1189
+ }
1190
+ async pollMultiViewStatus(e) {
1191
+ try {
1192
+ const i = await fetch(
1193
+ `${this.baseUrl}/designs/multi-status?designIds=${e.join(",")}`
1194
+ );
1195
+ if (!i.ok)
1196
+ throw new Error(`Failed to check multi-view design status (${i.status})`);
1133
1197
  return i.json();
1134
1198
  } catch (i) {
1135
- throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error finalizing design") : i;
1199
+ throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error checking multi-view design status") : i;
1136
1200
  }
1137
1201
  }
1138
- async uploadAsset(e, t) {
1202
+ async uploadAsset(e, i) {
1139
1203
  try {
1140
- const i = new FormData();
1141
- i.append("shopId", t), i.append("file", e);
1142
- const n = await fetch(`${this.baseUrl}/assets/upload`, {
1204
+ const t = new FormData();
1205
+ t.append("shopId", i), t.append("file", e);
1206
+ const s = await fetch(`${this.baseUrl}/assets/upload`, {
1143
1207
  method: "POST",
1144
- body: i
1208
+ body: t
1145
1209
  });
1146
- if (!n.ok) {
1147
- let a = `Failed to upload asset (${n.status} ${n.statusText})
1210
+ if (!s.ok) {
1211
+ let a = `Failed to upload asset (${s.status} ${s.statusText})
1148
1212
 
1149
1213
  `;
1150
- if (n.status === 400) {
1151
- const o = await n.json().catch(() => ({}));
1214
+ if (s.status === 400) {
1215
+ const o = await s.json().catch(() => ({}));
1152
1216
  a += o.error || "Invalid file.";
1153
- } else n.status === 413 ? a += "File too large (max 15MB)." : n.status >= 500 && (a += "Server error.");
1217
+ } else s.status === 413 ? a += "File too large (max 15MB)." : s.status >= 500 && (a += "Server error.");
1154
1218
  throw new Error(a);
1155
1219
  }
1156
- return n.json();
1157
- } catch (i) {
1158
- throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error uploading asset") : i;
1220
+ return s.json();
1221
+ } catch (t) {
1222
+ throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error("Network error uploading asset") : t;
1159
1223
  }
1160
1224
  }
1161
1225
  async getStorageUsage(e) {
1162
1226
  try {
1163
- const t = await fetch(`${this.baseUrl}/assets/usage?shopId=${encodeURIComponent(e)}`);
1164
- if (!t.ok)
1165
- throw new Error(`Failed to fetch storage usage (${t.status})`);
1166
- return t.json();
1167
- } catch (t) {
1168
- throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error("Network error fetching storage usage") : t;
1227
+ const i = await fetch(`${this.baseUrl}/assets/usage?shopId=${encodeURIComponent(e)}`);
1228
+ if (!i.ok)
1229
+ throw new Error(`Failed to fetch storage usage (${i.status})`);
1230
+ return i.json();
1231
+ } catch (i) {
1232
+ throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error fetching storage usage") : i;
1169
1233
  }
1170
1234
  }
1171
1235
  }
1172
- function h(f, e, ...t) {
1173
- const i = document.createElement(f);
1174
- return e && Object.entries(e).forEach(([n, a]) => {
1175
- n === "class" ? i.className = a : n in i ? i[n] = a : i.setAttribute(n, String(a));
1176
- }), t.forEach((n) => {
1177
- typeof n == "string" ? i.appendChild(document.createTextNode(n)) : i.appendChild(n);
1178
- }), i;
1236
+ function c(v, e, ...i) {
1237
+ const t = document.createElement(v);
1238
+ return e && Object.entries(e).forEach(([s, a]) => {
1239
+ s === "class" ? t.className = a : s in t ? t[s] = a : t.setAttribute(s, String(a));
1240
+ }), i.forEach((s) => {
1241
+ typeof s == "string" ? t.appendChild(document.createTextNode(s)) : t.appendChild(s);
1242
+ }), t;
1179
1243
  }
1180
- function H(f, e) {
1181
- let t = null, i = null;
1182
- const n = (...a) => {
1183
- i = a, t && clearTimeout(t), t = setTimeout(() => {
1184
- i && f(...i), t = null, i = null;
1244
+ function q(v, e) {
1245
+ let i = null, t = null;
1246
+ const s = (...a) => {
1247
+ t = a, i && clearTimeout(i), i = setTimeout(() => {
1248
+ t && v(...t), i = null, t = null;
1185
1249
  }, e);
1186
1250
  };
1187
- return n.flush = () => {
1188
- t && (clearTimeout(t), t = null), i && (f(...i), i = null);
1189
- }, n.cancel = () => {
1190
- t && (clearTimeout(t), t = null), i = null;
1191
- }, n;
1251
+ return s.flush = () => {
1252
+ i && (clearTimeout(i), i = null), t && (v(...t), t = null);
1253
+ }, s.cancel = () => {
1254
+ i && (clearTimeout(i), i = null), t = null;
1255
+ }, s;
1192
1256
  }
1193
- class B extends I {
1257
+ class H extends E {
1194
1258
  constructor() {
1195
- super(), this.percentLabel = h("span", { class: "zoom-percent" }, "100%");
1196
- const e = h("button", {
1259
+ super(), this.saveEnabled = !0, this.percentLabel = c("span", { class: "zoom-percent" }, "100%");
1260
+ const e = c("button", {
1197
1261
  class: "zoom-btn",
1198
1262
  title: "Zoom out"
1199
1263
  }, "−");
1200
1264
  e.addEventListener("click", () => this.emit("zoom-out", void 0));
1201
- const t = h("button", {
1265
+ const i = c("button", {
1202
1266
  class: "zoom-btn",
1203
1267
  title: "Fit to view"
1204
1268
  }, "Fit");
1205
- t.addEventListener("click", () => this.emit("zoom-fit", void 0));
1206
- const i = h("button", {
1269
+ i.addEventListener("click", () => this.emit("zoom-fit", void 0));
1270
+ const t = c("button", {
1207
1271
  class: "zoom-btn",
1208
1272
  title: "Zoom in"
1209
1273
  }, "+");
1210
- i.addEventListener("click", () => this.emit("zoom-in", void 0)), this.viewSelect = document.createElement("select"), this.viewSelect.className = "view-select", this.viewSelect.title = "Switch view", this.viewSelect.style.display = "none", this.viewSelect.addEventListener("change", () => {
1274
+ t.addEventListener("click", () => this.emit("zoom-in", void 0)), this.viewSelect = document.createElement("select"), this.viewSelect.className = "view-select", this.viewSelect.title = "Switch view", this.viewSelect.style.display = "none", this.viewSelect.addEventListener("change", () => {
1211
1275
  this.emit("view-change", { viewName: this.viewSelect.value });
1212
- }), this.element = h("div", { class: "zoom-toolbar" }), this.element.appendChild(e), this.element.appendChild(t), this.element.appendChild(i), this.element.appendChild(this.percentLabel), this.element.appendChild(this.viewSelect);
1213
- const n = h("div", { class: "zoom-toolbar-spacer" });
1214
- this.element.appendChild(n);
1215
- const a = h("button", {
1216
- class: "zoom-btn zoom-close-btn",
1217
- title: "Close editor"
1218
- }, "✕ Close");
1219
- a.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(a), this.saveBtn = h("button", {
1276
+ }), this.element = c("div", { class: "zoom-toolbar" }), this.element.appendChild(e), this.element.appendChild(i), this.element.appendChild(t), this.element.appendChild(this.percentLabel), this.element.appendChild(this.viewSelect);
1277
+ const s = c("div", { class: "zoom-toolbar-spacer" });
1278
+ this.element.appendChild(s), this.saveBtn = c("button", {
1220
1279
  class: "zoom-btn zoom-save-btn",
1221
1280
  title: "Save customization"
1222
- }, "Save Customization"), this.saveBtn.style.display = "none", this.saveBtn.addEventListener("click", () => this.emit("save", void 0)), this.element.appendChild(this.saveBtn);
1281
+ }, "Save"), this.saveBtn.style.display = "none", this.saveBtn.addEventListener("click", () => this.emit("save", void 0)), this.element.appendChild(this.saveBtn), this.closeBtn = c("button", {
1282
+ class: "zoom-btn zoom-close-btn",
1283
+ title: "Close editor"
1284
+ }, "✕ Close"), this.closeBtn.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(this.closeBtn);
1223
1285
  }
1224
1286
  setZoom(e) {
1225
1287
  this.percentLabel.textContent = `${Math.round(e * 100)}%`;
1226
1288
  }
1227
1289
  showSaveButton(e) {
1228
- this.saveBtn.style.display = e ? "" : "none";
1290
+ this.saveEnabled && (this.saveBtn.style.display = e ? "" : "none");
1291
+ }
1292
+ setCloseButtonVisible(e) {
1293
+ this.closeBtn.style.display = e ? "" : "none";
1229
1294
  }
1230
- setSaveDisabled(e, t) {
1231
- this.saveBtn.disabled = e, t && (this.saveBtn.textContent = t);
1295
+ setSaveButtonEnabled(e) {
1296
+ this.saveEnabled = e, e || (this.saveBtn.style.display = "none");
1297
+ }
1298
+ setSaveDisabled(e, i) {
1299
+ this.saveBtn.disabled = e, i && (this.saveBtn.textContent = i);
1232
1300
  }
1233
1301
  setViews(e) {
1234
1302
  this.viewSelect.innerHTML = "";
1235
- for (const t of e) {
1236
- const i = document.createElement("option");
1237
- i.value = t.viewName, i.textContent = t.viewName, this.viewSelect.appendChild(i);
1303
+ for (const i of e) {
1304
+ const t = document.createElement("option");
1305
+ t.value = i.viewName, t.textContent = i.viewName, this.viewSelect.appendChild(t);
1238
1306
  }
1239
1307
  this.viewSelect.style.display = e.length > 1 ? "" : "none";
1240
1308
  }
@@ -1251,243 +1319,243 @@ const Z = [
1251
1319
  { label: "Georgia", value: "Georgia" },
1252
1320
  { label: "Times New Roman", value: "Times New Roman" },
1253
1321
  { label: "Courier New", value: "Courier New" }
1254
- ], q = [
1322
+ ], j = [
1255
1323
  { label: "L", value: "left" },
1256
1324
  { label: "C", value: "center" },
1257
1325
  { label: "R", value: "right" }
1258
- ], P = ["image/png", "image/jpeg", "image/webp", "image/svg+xml"], Y = 15 * 1024 * 1024;
1259
- class L extends I {
1326
+ ], L = ["image/png", "image/jpeg", "image/webp", "image/svg+xml"], Y = 15 * 1024 * 1024;
1327
+ class A extends E {
1260
1328
  constructor(e) {
1261
1329
  super(), this.mode = "text", this.isSelected = !1, this.textInputEl = null, this.fontSelectEl = null, this.sizeInputEl = null, this.colorInputEl = null, this.alignGroupEl = null, this.area = e;
1262
- const t = S(e), i = k(e);
1263
- this.mode = i && !t ? "image" : "text", this.element = h("div", { class: "area-card" }), this.element.addEventListener("click", (r) => {
1330
+ const i = z(e), t = T(e);
1331
+ this.mode = t && !i ? "image" : "text", this.element = c("div", { class: "area-card" }), this.element.addEventListener("click", (r) => {
1264
1332
  r.target.tagName === "INPUT" || r.target.tagName === "SELECT" || r.target.tagName === "BUTTON" || this.emit("select", { areaId: e.id });
1265
1333
  });
1266
- const n = h("div", { class: "area-card-header" }), a = h("div", { class: "area-card-name-row" }), o = h("span", { class: "area-card-name" }, e.name);
1334
+ const s = c("div", { class: "area-card-header" }), a = c("div", { class: "area-card-name-row" }), o = c("span", { class: "area-card-name" }, e.name);
1267
1335
  if (a.appendChild(o), e.required) {
1268
- const r = h("span", { class: "area-card-badge" }, "Required");
1336
+ const r = c("span", { class: "area-card-badge" }, "Required");
1269
1337
  a.appendChild(r);
1270
1338
  }
1271
- n.appendChild(a);
1272
- const s = h("button", {
1339
+ s.appendChild(a);
1340
+ const n = c("button", {
1273
1341
  class: "area-card-reset-btn",
1274
1342
  title: "Reset content"
1275
1343
  }, "Reset");
1276
- if (s.addEventListener("click", (r) => {
1344
+ if (n.addEventListener("click", (r) => {
1277
1345
  r.stopPropagation(), this.emit("clear", { areaId: e.id });
1278
- }), n.appendChild(s), this.element.appendChild(n), t && i) {
1279
- const r = h("div", { class: "area-card-mode-switcher" }), d = h("button", {
1346
+ }), s.appendChild(n), this.element.appendChild(s), i && t) {
1347
+ const r = c("div", { class: "area-card-mode-switcher" }), d = c("button", {
1280
1348
  class: "mode-btn mode-btn-active"
1281
1349
  }, "Text");
1282
1350
  d.dataset.mode = "text";
1283
- const l = h("button", { class: "mode-btn" }, "Image");
1284
- l.dataset.mode = "image", d.addEventListener("click", (c) => {
1285
- c.stopPropagation(), this.setMode("text"), d.classList.add("mode-btn-active"), l.classList.remove("mode-btn-active"), this.currentContent?.type === "image" && this.emit("clear", { areaId: e.id });
1286
- }), l.addEventListener("click", (c) => {
1287
- c.stopPropagation(), this.setMode("image"), l.classList.add("mode-btn-active"), d.classList.remove("mode-btn-active"), this.currentContent?.type === "text" && this.emit("clear", { areaId: e.id });
1351
+ const l = c("button", { class: "mode-btn" }, "Image");
1352
+ l.dataset.mode = "image", d.addEventListener("click", (h) => {
1353
+ h.stopPropagation(), this.setMode("text"), d.classList.add("mode-btn-active"), l.classList.remove("mode-btn-active"), this.currentContent?.type === "image" && this.emit("clear", { areaId: e.id });
1354
+ }), l.addEventListener("click", (h) => {
1355
+ h.stopPropagation(), this.setMode("image"), l.classList.add("mode-btn-active"), d.classList.remove("mode-btn-active"), this.currentContent?.type === "text" && this.emit("clear", { areaId: e.id });
1288
1356
  }), r.appendChild(d), r.appendChild(l), this.element.appendChild(r);
1289
1357
  }
1290
- this.contentContainer = h("div", { class: "area-card-content" }), this.element.appendChild(this.contentContainer), this.transformContainer = h("div", { class: "area-card-transforms" }), this.element.appendChild(this.transformContainer), this.renderContent();
1358
+ this.contentContainer = c("div", { class: "area-card-content" }), this.element.appendChild(this.contentContainer), this.transformContainer = c("div", { class: "area-card-transforms" }), this.element.appendChild(this.transformContainer), this.renderContent();
1291
1359
  }
1292
1360
  setMode(e) {
1293
1361
  this.mode = e, this.renderContent();
1294
1362
  }
1295
1363
  setContent(e) {
1296
- const t = this.currentContent?.type;
1297
- this.currentContent = e, e?.type === "image" && (this.mode = "image"), this.element.querySelectorAll(".mode-btn").forEach((i) => {
1298
- const n = i;
1299
- n.classList.toggle("mode-btn-active", n.dataset.mode === this.mode);
1300
- }), e?.type !== t || e?.type === "image" || e === void 0 ? this.renderContent() : e?.type === "text" && this.updateTextValues(e), this.renderTransforms();
1364
+ const i = this.currentContent?.type;
1365
+ this.currentContent = e, e?.type === "image" && (this.mode = "image"), this.element.querySelectorAll(".mode-btn").forEach((t) => {
1366
+ const s = t;
1367
+ s.classList.toggle("mode-btn-active", s.dataset.mode === this.mode);
1368
+ }), e?.type !== i || e?.type === "image" || e === void 0 ? this.renderContent() : e?.type === "text" && this.updateTextValues(e), this.renderTransforms();
1301
1369
  }
1302
1370
  setSelected(e) {
1303
1371
  this.isSelected = e, this.element.classList.toggle("area-card-selected", e), this.renderTransforms();
1304
1372
  }
1305
1373
  updateTextValues(e) {
1306
- this.textInputEl && this.textInputEl.value !== e.text && (this.textInputEl.value = e.text), this.fontSelectEl && this.fontSelectEl.value !== e.font && (this.fontSelectEl.value = e.font), this.sizeInputEl && this.sizeInputEl.value !== String(e.size) && (this.sizeInputEl.value = String(e.size)), this.colorInputEl && this.colorInputEl.value !== e.color && (this.colorInputEl.value = e.color), this.alignGroupEl && this.alignGroupEl.querySelectorAll(".align-btn").forEach((t) => {
1307
- const i = t, n = i.title === "Left" ? "left" : i.title === "Center" ? "center" : "right";
1308
- i.classList.toggle("align-btn-active", n === e.align);
1374
+ this.textInputEl && this.textInputEl.value !== e.text && (this.textInputEl.value = e.text), this.fontSelectEl && this.fontSelectEl.value !== e.font && (this.fontSelectEl.value = e.font), this.sizeInputEl && this.sizeInputEl.value !== String(e.size) && (this.sizeInputEl.value = String(e.size)), this.colorInputEl && this.colorInputEl.value !== e.color && (this.colorInputEl.value = e.color), this.alignGroupEl && this.alignGroupEl.querySelectorAll(".align-btn").forEach((i) => {
1375
+ const t = i, s = t.title === "Left" ? "left" : t.title === "Center" ? "center" : "right";
1376
+ t.classList.toggle("align-btn-active", s === e.align);
1309
1377
  });
1310
1378
  }
1311
1379
  renderContent() {
1312
1380
  this.contentContainer.innerHTML = "", this.textInputEl = null, this.fontSelectEl = null, this.sizeInputEl = null, this.colorInputEl = null, this.alignGroupEl = null;
1313
- const e = S(this.area), t = k(this.area), i = !t || e && t && this.mode === "text", n = !e || e && t && this.mode === "image";
1314
- e && i && this.renderTextControls(), t && n && this.renderImageControls();
1381
+ const e = z(this.area), i = T(this.area), t = !i || e && i && this.mode === "text", s = !e || e && i && this.mode === "image";
1382
+ e && t && this.renderTextControls(), i && s && this.renderImageControls();
1315
1383
  }
1316
1384
  renderTextControls() {
1317
- const e = this.currentContent, t = e?.type === "text" ? e : E(this.area), i = h("input", {
1385
+ const e = this.currentContent, i = e?.type === "text" ? e : S(this.area), t = c("input", {
1318
1386
  class: "area-input-text",
1319
1387
  type: "text",
1320
1388
  placeholder: this.area.placeholder || "Enter text..."
1321
1389
  });
1322
- i.value = t.text, this.area.textOptions?.maxLength && (i.maxLength = this.area.textOptions.maxLength), i.addEventListener("input", () => {
1323
- this.emit("text:change", { areaId: this.area.id, updates: { text: i.value } });
1324
- }), this.contentContainer.appendChild(i), this.textInputEl = i;
1325
- const n = h("div", { class: "area-input-row" }), a = h("select", { class: "area-input-font" });
1390
+ t.value = i.text, this.area.textOptions?.maxLength && (t.maxLength = this.area.textOptions.maxLength), t.addEventListener("input", () => {
1391
+ this.emit("text:change", { areaId: this.area.id, updates: { text: t.value } });
1392
+ }), this.contentContainer.appendChild(t), this.textInputEl = t;
1393
+ const s = c("div", { class: "area-input-row" }), a = c("select", { class: "area-input-font" });
1326
1394
  for (const l of Z) {
1327
- const c = h("option");
1328
- c.value = l.value, c.textContent = l.label, l.value === t.font && (c.selected = !0), a.appendChild(c);
1395
+ const h = c("option");
1396
+ h.value = l.value, h.textContent = l.label, l.value === i.font && (h.selected = !0), a.appendChild(h);
1329
1397
  }
1330
1398
  a.addEventListener("change", () => {
1331
1399
  this.emit("text:change", { areaId: this.area.id, updates: { font: a.value } });
1332
- }), n.appendChild(a), this.fontSelectEl = a;
1333
- const o = h("input", {
1400
+ }), s.appendChild(a), this.fontSelectEl = a;
1401
+ const o = c("input", {
1334
1402
  class: "area-input-size",
1335
1403
  type: "number"
1336
1404
  });
1337
- o.value = String(t.size), o.min = String(this.area.textOptions?.minSize || 8), o.max = String(this.area.textOptions?.maxSize || 200), o.addEventListener("change", () => {
1405
+ o.value = String(i.size), o.min = String(this.area.textOptions?.minSize || 8), o.max = String(this.area.textOptions?.maxSize || 200), o.addEventListener("change", () => {
1338
1406
  this.emit("text:change", {
1339
1407
  areaId: this.area.id,
1340
1408
  updates: { size: parseInt(o.value, 10) || 24 }
1341
1409
  });
1342
- }), n.appendChild(o), this.sizeInputEl = o, this.contentContainer.appendChild(n);
1343
- const s = h("div", { class: "area-input-row" }), r = h("div", { class: "area-input-align" });
1344
- for (const l of q) {
1345
- const c = h("button", {
1346
- class: `align-btn${t.align === l.value ? " align-btn-active" : ""}`,
1410
+ }), s.appendChild(o), this.sizeInputEl = o, this.contentContainer.appendChild(s);
1411
+ const n = c("div", { class: "area-input-row" }), r = c("div", { class: "area-input-align" });
1412
+ for (const l of j) {
1413
+ const h = c("button", {
1414
+ class: `align-btn${i.align === l.value ? " align-btn-active" : ""}`,
1347
1415
  title: l.label === "L" ? "Left" : l.label === "C" ? "Center" : "Right"
1348
1416
  }, l.label);
1349
- c.addEventListener("click", (u) => {
1350
- u.stopPropagation(), this.emit("text:change", { areaId: this.area.id, updates: { align: l.value } }), r.querySelectorAll(".align-btn").forEach((p) => p.classList.remove("align-btn-active")), c.classList.add("align-btn-active");
1351
- }), r.appendChild(c);
1417
+ h.addEventListener("click", (u) => {
1418
+ u.stopPropagation(), this.emit("text:change", { areaId: this.area.id, updates: { align: l.value } }), r.querySelectorAll(".align-btn").forEach((p) => p.classList.remove("align-btn-active")), h.classList.add("align-btn-active");
1419
+ }), r.appendChild(h);
1352
1420
  }
1353
- s.appendChild(r), this.alignGroupEl = r;
1354
- const d = h("input", {
1421
+ n.appendChild(r), this.alignGroupEl = r;
1422
+ const d = c("input", {
1355
1423
  class: "area-input-color",
1356
1424
  type: "color"
1357
1425
  });
1358
- d.value = t.color, d.addEventListener("input", () => {
1426
+ d.value = i.color, d.addEventListener("input", () => {
1359
1427
  this.emit("text:change", { areaId: this.area.id, updates: { color: d.value } });
1360
- }), s.appendChild(d), this.colorInputEl = d, this.contentContainer.appendChild(s);
1428
+ }), n.appendChild(d), this.colorInputEl = d, this.contentContainer.appendChild(n);
1361
1429
  }
1362
1430
  renderImageControls() {
1363
1431
  const e = this.currentContent;
1364
1432
  if (e?.type === "image") {
1365
- const t = h("div", { class: "area-image-preview" }), i = h("img", { class: "area-image-thumb" });
1366
- i.src = e.dataUrl, i.alt = e.filename || "Uploaded image", t.appendChild(i);
1367
- const n = h("div", { class: "area-image-info" });
1368
- n.appendChild(h("span", { class: "area-image-name" }, e.filename || "Image"));
1369
- const a = h("button", { class: "area-image-remove-btn" }, "Remove");
1433
+ const i = c("div", { class: "area-image-preview" }), t = c("img", { class: "area-image-thumb" });
1434
+ t.src = e.dataUrl, t.alt = e.filename || "Uploaded image", i.appendChild(t);
1435
+ const s = c("div", { class: "area-image-info" });
1436
+ s.appendChild(c("span", { class: "area-image-name" }, e.filename || "Image"));
1437
+ const a = c("button", { class: "area-image-remove-btn" }, "Remove");
1370
1438
  a.addEventListener("click", (o) => {
1371
1439
  o.stopPropagation(), this.emit("clear", { areaId: this.area.id });
1372
- }), n.appendChild(a), t.appendChild(n), this.contentContainer.appendChild(t);
1440
+ }), s.appendChild(a), i.appendChild(s), this.contentContainer.appendChild(i);
1373
1441
  } else {
1374
- const t = h("button", { class: "area-upload-btn" }, "Upload Image"), i = h("input", { type: "file" });
1375
- i.accept = P.join(","), i.style.display = "none";
1376
- const n = h("div", { class: "area-validation-error" });
1377
- n.style.display = "none", i.addEventListener("change", () => {
1378
- const a = i.files?.[0];
1442
+ const i = c("button", { class: "area-upload-btn" }, "Upload Image"), t = c("input", { type: "file" });
1443
+ t.accept = L.join(","), t.style.display = "none";
1444
+ const s = c("div", { class: "area-validation-error" });
1445
+ s.style.display = "none", t.addEventListener("change", () => {
1446
+ const a = t.files?.[0];
1379
1447
  if (!a) return;
1380
- if (!P.includes(a.type)) {
1381
- const s = "Only PNG, JPEG, WebP, and SVG images are accepted";
1382
- n.textContent = s, n.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: s }), i.value = "";
1448
+ if (!L.includes(a.type)) {
1449
+ const n = "Only PNG, JPEG, WebP, and SVG images are accepted";
1450
+ s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), t.value = "";
1383
1451
  return;
1384
1452
  }
1385
1453
  if (a.size > Y) {
1386
- const s = "File must be smaller than 15MB";
1387
- n.textContent = s, n.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: s }), i.value = "";
1454
+ const n = "File must be smaller than 15MB";
1455
+ s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), t.value = "";
1388
1456
  return;
1389
1457
  }
1390
- n.style.display = "none";
1458
+ s.style.display = "none";
1391
1459
  const o = new FileReader();
1392
1460
  o.onload = () => {
1393
- const s = o.result;
1394
- this.emit("image:change", { areaId: this.area.id, dataUrl: s, filename: a.name });
1395
- }, o.readAsDataURL(a), i.value = "";
1396
- }), t.addEventListener("click", (a) => {
1397
- a.stopPropagation(), i.click();
1398
- }), this.contentContainer.appendChild(t), this.contentContainer.appendChild(i), this.contentContainer.appendChild(n);
1461
+ const n = o.result;
1462
+ this.emit("image:change", { areaId: this.area.id, dataUrl: n, filename: a.name });
1463
+ }, o.readAsDataURL(a), t.value = "";
1464
+ }), i.addEventListener("click", (a) => {
1465
+ a.stopPropagation(), t.click();
1466
+ }), this.contentContainer.appendChild(i), this.contentContainer.appendChild(t), this.contentContainer.appendChild(s);
1399
1467
  }
1400
1468
  }
1401
1469
  renderTransforms() {
1402
1470
  this.transformContainer.innerHTML = "";
1403
1471
  const e = this.currentContent;
1404
1472
  if (!this.isSelected || !e) return;
1405
- const t = e.type === "text" ? this.area.textOptions?.allowPositioning : this.area.imageOptions?.allowPositioning, i = e.type === "image" && this.area.imageOptions?.allowScaling, n = e.type === "image" && this.area.imageOptions?.allowRotation;
1406
- if (!t && !i && !n) return;
1407
- const a = h("div", { class: "area-card-divider" });
1473
+ const i = e.type === "text" ? this.area.textOptions?.allowPositioning : this.area.imageOptions?.allowPositioning, t = e.type === "image" && this.area.imageOptions?.allowScaling, s = e.type === "image" && this.area.imageOptions?.allowRotation;
1474
+ if (!i && !t && !s) return;
1475
+ const a = c("div", { class: "area-card-divider" });
1408
1476
  this.transformContainer.appendChild(a);
1409
- const o = h("div", { class: "transform-header" });
1410
- o.appendChild(h("span", { class: "transform-title" }, "Transform"));
1411
- const s = h("button", { class: "transform-reset-btn" }, "Reset");
1412
- s.addEventListener("click", (d) => {
1413
- d.stopPropagation(), t && this.emit("offset:change", { areaId: this.area.id, offset: { x: 0, y: 0 } }), i && this.emit("scale:change", { areaId: this.area.id, scale: 1 }), n && this.emit("rotation:change", { areaId: this.area.id, rotation: 0 });
1414
- }), o.appendChild(s), this.transformContainer.appendChild(o);
1477
+ const o = c("div", { class: "transform-header" });
1478
+ o.appendChild(c("span", { class: "transform-title" }, "Transform"));
1479
+ const n = c("button", { class: "transform-reset-btn" }, "Reset");
1480
+ n.addEventListener("click", (d) => {
1481
+ d.stopPropagation(), i && this.emit("offset:change", { areaId: this.area.id, offset: { x: 0, y: 0 } }), t && this.emit("scale:change", { areaId: this.area.id, scale: 1 }), s && this.emit("rotation:change", { areaId: this.area.id, rotation: 0 });
1482
+ }), o.appendChild(n), this.transformContainer.appendChild(o);
1415
1483
  const r = e.offset ?? { x: 0, y: 0 };
1416
- if (t) {
1417
- const d = h("div", { class: "area-input-row" }), l = h("label", { class: "transform-label" }, "X"), c = h("input", { class: "transform-input", type: "number" });
1418
- c.value = String(Math.round(r.x)), c.addEventListener("change", () => {
1484
+ if (i) {
1485
+ const d = c("div", { class: "area-input-row" }), l = c("label", { class: "transform-label" }, "X"), h = c("input", { class: "transform-input", type: "number" });
1486
+ h.value = String(Math.round(r.x)), h.addEventListener("change", () => {
1419
1487
  this.emit("offset:change", {
1420
1488
  areaId: this.area.id,
1421
- offset: { ...r, x: parseInt(c.value, 10) || 0 }
1489
+ offset: { ...r, x: parseInt(h.value, 10) || 0 }
1422
1490
  });
1423
1491
  });
1424
- const u = h("label", { class: "transform-label" }, "Y"), p = h("input", { class: "transform-input", type: "number" });
1492
+ const u = c("label", { class: "transform-label" }, "Y"), p = c("input", { class: "transform-input", type: "number" });
1425
1493
  p.value = String(Math.round(r.y)), p.addEventListener("change", () => {
1426
1494
  this.emit("offset:change", {
1427
1495
  areaId: this.area.id,
1428
1496
  offset: { ...r, y: parseInt(p.value, 10) || 0 }
1429
1497
  });
1430
- }), d.appendChild(l), d.appendChild(c), d.appendChild(u), d.appendChild(p), this.transformContainer.appendChild(d);
1498
+ }), d.appendChild(l), d.appendChild(h), d.appendChild(u), d.appendChild(p), this.transformContainer.appendChild(d);
1431
1499
  }
1432
- if (i && e.type === "image") {
1433
- const d = e.scale ?? 1, l = h("div", { class: "area-input-row transform-slider-row" }), c = h("label", { class: "transform-label" }, `Scale: ${Math.round(d * 100)}%`), u = h("input", { class: "transform-slider", type: "range" });
1500
+ if (t && e.type === "image") {
1501
+ const d = e.scale ?? 1, l = c("div", { class: "area-input-row transform-slider-row" }), h = c("label", { class: "transform-label" }, `Scale: ${Math.round(d * 100)}%`), u = c("input", { class: "transform-slider", type: "range" });
1434
1502
  u.min = "10", u.max = "500", u.step = "5", u.value = String(Math.round(d * 100)), u.addEventListener("input", () => {
1435
1503
  const p = parseInt(u.value, 10) / 100;
1436
- c.textContent = `Scale: ${Math.round(p * 100)}%`, this.emit("scale:change", { areaId: this.area.id, scale: p });
1437
- }), l.appendChild(c), l.appendChild(u), this.transformContainer.appendChild(l);
1504
+ h.textContent = `Scale: ${Math.round(p * 100)}%`, this.emit("scale:change", { areaId: this.area.id, scale: p });
1505
+ }), l.appendChild(h), l.appendChild(u), this.transformContainer.appendChild(l);
1438
1506
  }
1439
- if (n && e.type === "image") {
1440
- const d = e.rotation ?? 0, l = h("div", { class: "area-input-row transform-slider-row" }), c = h("label", { class: "transform-label" }, `Rotation: ${Math.round(d)}°`), u = h("input", { class: "transform-slider", type: "range" });
1507
+ if (s && e.type === "image") {
1508
+ const d = e.rotation ?? 0, l = c("div", { class: "area-input-row transform-slider-row" }), h = c("label", { class: "transform-label" }, `Rotation: ${Math.round(d)}°`), u = c("input", { class: "transform-slider", type: "range" });
1441
1509
  u.min = "0", u.max = "360", u.step = "1", u.value = String(Math.round(d)), u.addEventListener("input", () => {
1442
1510
  const p = parseInt(u.value, 10);
1443
- c.textContent = `Rotation: ${p}°`, this.emit("rotation:change", { areaId: this.area.id, rotation: p });
1444
- }), l.appendChild(c), l.appendChild(u), this.transformContainer.appendChild(l);
1511
+ h.textContent = `Rotation: ${p}°`, this.emit("rotation:change", { areaId: this.area.id, rotation: p });
1512
+ }), l.appendChild(h), l.appendChild(u), this.transformContainer.appendChild(l);
1445
1513
  }
1446
1514
  }
1447
1515
  getElement() {
1448
1516
  return this.element;
1449
1517
  }
1450
1518
  }
1451
- class j extends I {
1519
+ class J extends E {
1452
1520
  constructor(e) {
1453
- super(), this.cards = /* @__PURE__ */ new Map(), this.isMobile = !1, this.boundEscapeHandler = (n) => {
1454
- n.key === "Escape" && this.emit("dismiss", void 0);
1455
- }, this.backdrop = h("div", { class: "area-panel-backdrop" }), this.backdrop.addEventListener("click", () => {
1521
+ super(), this.cards = /* @__PURE__ */ new Map(), this.isMobile = !1, this.boundEscapeHandler = (s) => {
1522
+ s.key === "Escape" && this.emit("dismiss", void 0);
1523
+ }, this.backdrop = c("div", { class: "area-panel-backdrop" }), this.backdrop.addEventListener("click", () => {
1456
1524
  this.emit("dismiss", void 0);
1457
- }), this.element = h("div", { class: "area-panel" });
1458
- const t = h("div", { class: "area-panel-header" });
1459
- t.appendChild(h("span", { class: "area-panel-title" }, "Customize"));
1460
- const i = h("button", { class: "area-panel-close-btn", title: "Close" }, "×");
1461
- if (i.addEventListener("click", () => {
1525
+ }), this.element = c("div", { class: "area-panel" });
1526
+ const i = c("div", { class: "area-panel-header" });
1527
+ i.appendChild(c("span", { class: "area-panel-title" }, "Customize"));
1528
+ const t = c("button", { class: "area-panel-close-btn", title: "Close" }, "×");
1529
+ if (t.addEventListener("click", () => {
1462
1530
  this.emit("dismiss", void 0);
1463
- }), t.appendChild(i), this.element.appendChild(t), this.panelContent = h("div", { class: "area-panel-content" }), e.length === 0) {
1464
- const n = h("p", { class: "area-panel-empty" }, "No customization areas defined.");
1465
- this.panelContent.appendChild(n);
1531
+ }), i.appendChild(t), this.element.appendChild(i), this.panelContent = c("div", { class: "area-panel-content" }), e.length === 0) {
1532
+ const s = c("p", { class: "area-panel-empty" }, "No customization areas defined.");
1533
+ this.panelContent.appendChild(s);
1466
1534
  } else
1467
- for (const n of e) {
1468
- const a = new L(n);
1469
- this.cards.set(n.id, a), a.on("text:change", (o) => this.emit("text:change", o)), a.on("image:change", (o) => this.emit("image:change", o)), a.on("clear", (o) => this.emit("clear", o)), a.on("select", (o) => this.emit("select", o)), a.on("offset:change", (o) => this.emit("offset:change", o)), a.on("scale:change", (o) => this.emit("scale:change", o)), a.on("rotation:change", (o) => this.emit("rotation:change", o)), a.on("validation:error", (o) => this.emit("validation:error", o)), this.panelContent.appendChild(a.getElement());
1535
+ for (const s of e) {
1536
+ const a = new A(s);
1537
+ this.cards.set(s.id, a), a.on("text:change", (o) => this.emit("text:change", o)), a.on("image:change", (o) => this.emit("image:change", o)), a.on("clear", (o) => this.emit("clear", o)), a.on("select", (o) => this.emit("select", o)), a.on("offset:change", (o) => this.emit("offset:change", o)), a.on("scale:change", (o) => this.emit("scale:change", o)), a.on("rotation:change", (o) => this.emit("rotation:change", o)), a.on("validation:error", (o) => this.emit("validation:error", o)), this.panelContent.appendChild(a.getElement());
1470
1538
  }
1471
1539
  this.element.appendChild(this.panelContent);
1472
1540
  }
1473
1541
  setAreas(e) {
1474
1542
  if (this.cards.clear(), this.panelContent.innerHTML = "", e.length === 0) {
1475
- const t = h("p", { class: "area-panel-empty" }, "No customization areas defined.");
1476
- this.panelContent.appendChild(t);
1543
+ const i = c("p", { class: "area-panel-empty" }, "No customization areas defined.");
1544
+ this.panelContent.appendChild(i);
1477
1545
  } else
1478
- for (const t of e) {
1479
- const i = new L(t);
1480
- this.cards.set(t.id, i), i.on("text:change", (n) => this.emit("text:change", n)), i.on("image:change", (n) => this.emit("image:change", n)), i.on("clear", (n) => this.emit("clear", n)), i.on("select", (n) => this.emit("select", n)), i.on("offset:change", (n) => this.emit("offset:change", n)), i.on("scale:change", (n) => this.emit("scale:change", n)), i.on("rotation:change", (n) => this.emit("rotation:change", n)), i.on("validation:error", (n) => this.emit("validation:error", n)), this.panelContent.appendChild(i.getElement());
1546
+ for (const i of e) {
1547
+ const t = new A(i);
1548
+ this.cards.set(i.id, t), t.on("text:change", (s) => this.emit("text:change", s)), t.on("image:change", (s) => this.emit("image:change", s)), t.on("clear", (s) => this.emit("clear", s)), t.on("select", (s) => this.emit("select", s)), t.on("offset:change", (s) => this.emit("offset:change", s)), t.on("scale:change", (s) => this.emit("scale:change", s)), t.on("rotation:change", (s) => this.emit("rotation:change", s)), t.on("validation:error", (s) => this.emit("validation:error", s)), this.panelContent.appendChild(t.getElement());
1481
1549
  }
1482
1550
  }
1483
1551
  setContents(e) {
1484
- for (const [t, i] of this.cards)
1485
- i.setContent(e.get(t));
1552
+ for (const [i, t] of this.cards)
1553
+ t.setContent(e.get(i));
1486
1554
  }
1487
1555
  setSelectedArea(e) {
1488
- for (const [t, i] of this.cards) {
1489
- const n = t === e;
1490
- i.setSelected(n), i.getElement().style.display = n ? "" : "none";
1556
+ for (const [i, t] of this.cards) {
1557
+ const s = i === e;
1558
+ t.setSelected(s), t.getElement().style.display = s ? "" : "none";
1491
1559
  }
1492
1560
  e ? (this.element.classList.add("area-panel-visible"), this.isMobile && this.backdrop.classList.add("area-panel-backdrop-visible"), document.addEventListener("keydown", this.boundEscapeHandler)) : (this.element.classList.remove("area-panel-visible"), this.isMobile && this.backdrop.classList.remove("area-panel-backdrop-visible"), document.removeEventListener("keydown", this.boundEscapeHandler));
1493
1561
  }
@@ -1503,13 +1571,13 @@ class j extends I {
1503
1571
  }
1504
1572
  class X {
1505
1573
  constructor(e = window) {
1506
- this.target = e, this.commands = [], this.isEnabled = !0, this.handleKeyDown = (t) => {
1574
+ this.target = e, this.commands = [], this.isEnabled = !0, this.handleKeyDown = (i) => {
1507
1575
  if (!this.isEnabled) return;
1508
- const i = t.target;
1509
- if (!(i.tagName === "INPUT" || i.tagName === "TEXTAREA" || i.isContentEditable)) {
1510
- for (const n of this.commands)
1511
- if (this.matchesCommand(t, n)) {
1512
- t.preventDefault(), n.handler(t);
1576
+ const t = i.target;
1577
+ if (!(t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) {
1578
+ for (const s of this.commands)
1579
+ if (this.matchesCommand(i, s)) {
1580
+ i.preventDefault(), s.handler(i);
1513
1581
  break;
1514
1582
  }
1515
1583
  }
@@ -1520,8 +1588,8 @@ class X {
1520
1588
  */
1521
1589
  register(e) {
1522
1590
  return this.commands.push(e), () => {
1523
- const t = this.commands.indexOf(e);
1524
- t !== -1 && this.commands.splice(t, 1);
1591
+ const i = this.commands.indexOf(e);
1592
+ i !== -1 && this.commands.splice(i, 1);
1525
1593
  };
1526
1594
  }
1527
1595
  /**
@@ -1545,8 +1613,8 @@ class X {
1545
1613
  /**
1546
1614
  * Check if event matches a command
1547
1615
  */
1548
- matchesCommand(e, t) {
1549
- return !(e.key.toLowerCase() !== t.key.toLowerCase() || t.ctrl !== void 0 && t.ctrl !== e.ctrlKey || t.shift !== void 0 && t.shift !== e.shiftKey || t.alt !== void 0 && t.alt !== e.altKey || t.meta !== void 0 && t.meta !== e.metaKey);
1616
+ matchesCommand(e, i) {
1617
+ return !(e.key.toLowerCase() !== i.key.toLowerCase() || i.ctrl !== void 0 && i.ctrl !== e.ctrlKey || i.shift !== void 0 && i.shift !== e.shiftKey || i.alt !== void 0 && i.alt !== e.altKey || i.meta !== void 0 && i.meta !== e.metaKey);
1550
1618
  }
1551
1619
  /**
1552
1620
  * Cleanup
@@ -1558,28 +1626,28 @@ class X {
1558
1626
  function W() {
1559
1627
  return /Mac|iPod|iPhone|iPad/.test(navigator.platform);
1560
1628
  }
1561
- function J() {
1629
+ function G() {
1562
1630
  return W() ? "meta" : "ctrl";
1563
1631
  }
1564
- function G(f, e, t, i) {
1565
- const n = f / t, a = e / i;
1566
- return Math.min(n, a);
1632
+ function K(v, e, i, t) {
1633
+ const s = v / i, a = e / t;
1634
+ return Math.min(s, a);
1567
1635
  }
1568
- function K(f, e, t = 8, i = 10, n = 300) {
1569
- const a = G(f, e, t, i), o = a >= n;
1636
+ function Q(v, e, i = 8, t = 10, s = 300) {
1637
+ const a = K(v, e, i, t), o = a >= s;
1570
1638
  return {
1571
1639
  actualDPI: Math.round(a),
1572
- targetDPI: n,
1573
- width: f,
1640
+ targetDPI: s,
1641
+ width: v,
1574
1642
  height: e,
1575
1643
  passed: o,
1576
- warning: o ? void 0 : `Image resolution is ${Math.round(a)} DPI at 100% scale. For best print quality, use images with at least ${n} DPI. This may result in pixelated or blurry prints.`
1644
+ warning: o ? void 0 : `Image resolution is ${Math.round(a)} DPI at 100% scale. For best print quality, use images with at least ${s} DPI. This may result in pixelated or blurry prints.`
1577
1645
  };
1578
1646
  }
1579
- const z = ':host{--editor-bg: #f3f4f6;--editor-surface: #ffffff;--editor-border: #e5e7eb;--editor-text: #111827;--editor-text-muted: #6b7280;--editor-primary: #3b82f6;--editor-primary-hover: #2563eb;--editor-danger: #ef4444;--editor-radius: 8px;--editor-radius-sm: 4px;--editor-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--editor-panel-width: 280px;display:block;width:100%;height:100%;font-family:var(--editor-font);font-size:13px;color:var(--editor-text);line-height:1.4}:host([theme="dark"]){--editor-bg: #1f2937;--editor-surface: #374151;--editor-border: #4b5563;--editor-text: #f9fafb;--editor-text-muted: #9ca3af}.editor-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative;overflow:hidden;background:var(--editor-bg)}.layout-desktop .editor-main{display:flex;flex:1;min-height:0}.layout-desktop .canvas-area{flex:1;min-width:0;position:relative;display:flex;flex-direction:column}.layout-desktop .area-panel{width:var(--editor-panel-width);border-left:1px solid var(--editor-border);background:var(--editor-surface);display:none;flex-direction:column;overflow:hidden}.layout-desktop .area-panel.area-panel-visible{display:flex}.layout-mobile .editor-main{display:flex;flex-direction:column;flex:1;min-height:0}.layout-mobile .canvas-area{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}.layout-mobile .area-panel{position:absolute;bottom:0;left:0;right:0;max-height:50%;background:var(--editor-surface);border-top:1px solid var(--editor-border);border-radius:var(--editor-radius) var(--editor-radius) 0 0;box-shadow:0 -4px 20px #00000026;transform:translateY(100%);transition:transform .25s ease-out;z-index:20;display:flex;flex-direction:column;overflow:hidden}.layout-mobile .area-panel.area-panel-visible{transform:translateY(0)}.area-panel-backdrop{display:none}.layout-mobile .area-panel-backdrop{display:block;position:absolute;inset:0;background:#0000004d;z-index:15;opacity:0;pointer-events:none;transition:opacity .25s ease-out}.layout-mobile .area-panel-backdrop.area-panel-backdrop-visible{opacity:1;pointer-events:auto}.zoom-toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;background:var(--editor-surface);border-bottom:1px solid var(--editor-border)}.zoom-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:14px;font-family:var(--editor-font);transition:background .15s;-webkit-tap-highlight-color:transparent;touch-action:manipulation}.zoom-btn:hover{background:var(--editor-bg)}.zoom-btn:active{background:var(--editor-border)}.zoom-percent{margin-left:8px;font-size:12px;color:var(--editor-text-muted);min-width:40px}.view-select{margin-left:8px;padding:4px 8px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);font-family:var(--editor-font);font-size:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.view-select:hover{background:var(--editor-bg)}.view-select:focus{outline:2px solid var(--editor-primary);outline-offset:-1px}.zoom-toolbar-spacer{flex:1}.zoom-close-btn{width:auto;padding:0 12px;font-size:13px;gap:4px}.zoom-save-btn{width:auto;padding:0 16px;margin-left:8px;font-size:13px;font-weight:600;background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.zoom-save-btn:hover{background:var(--editor-primary-hover)}.zoom-save-btn:disabled{opacity:.5;cursor:not-allowed}.canvas-wrapper{flex:1;position:relative;overflow:hidden;min-height:200px}.editor-canvas{position:absolute;top:0;left:0}.area-panel-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--editor-border);flex-shrink:0}.area-panel-title{font-weight:600;font-size:14px}.area-panel-close-btn{width:28px;height:28px;border:none;background:none;color:var(--editor-text-muted);cursor:pointer;font-size:18px;line-height:1;border-radius:var(--editor-radius-sm);display:flex;align-items:center;justify-content:center}.area-panel-close-btn:hover{background:var(--editor-bg);color:var(--editor-text)}.area-panel-content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.area-panel-empty{color:var(--editor-text-muted);font-size:12px;text-align:center;padding:20px 0}.area-card{padding:12px;border:1px solid var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-surface);cursor:pointer;transition:border-color .15s}.area-card:hover{border-color:var(--editor-primary)}.area-card-selected{border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.area-card-name-row{display:flex;align-items:center;gap:6px}.area-card-name{font-weight:600;font-size:13px}.area-card-badge{display:inline-block;padding:1px 6px;font-size:10px;font-weight:500;background:#fef3c7;color:#92400e;border-radius:10px}.area-card-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.area-card-reset-btn:hover{background:var(--editor-bg);color:var(--editor-danger);border-color:var(--editor-danger)}.area-card-mode-switcher{display:flex;gap:4px;margin-bottom:8px}.mode-btn{flex:1;padding:4px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-family:var(--editor-font);transition:all .15s}.mode-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-text{width:100%;padding:6px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:13px;color:var(--editor-text);background:var(--editor-surface);box-sizing:border-box;margin-bottom:6px}.area-input-text:focus{outline:none;border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-input-row{display:flex;gap:6px;align-items:center;margin-bottom:6px}.area-input-font{flex:1;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface);min-width:0}.area-input-size{width:56px;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.area-input-align{display:flex;gap:2px}.align-btn{width:28px;height:28px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-weight:600;font-family:var(--editor-font)}.align-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-color{width:32px;height:28px;padding:0;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);cursor:pointer;background:none}.area-upload-btn{width:100%;padding:10px;border:2px dashed var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-bg);color:var(--editor-text-muted);cursor:pointer;font-family:var(--editor-font);font-size:12px;text-align:center;transition:all .15s;min-height:44px}.area-upload-btn:hover{border-color:var(--editor-primary);color:var(--editor-primary)}.area-image-preview{display:flex;gap:8px;align-items:center}.area-image-thumb{width:48px;height:48px;object-fit:cover;border-radius:var(--editor-radius-sm);border:1px solid var(--editor-border)}.area-image-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.area-image-name{font-size:12px;color:var(--editor-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.area-image-remove-btn{padding:2px 8px;border:1px solid var(--editor-danger);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-danger);cursor:pointer;font-size:11px;font-family:var(--editor-font);width:fit-content}.area-image-remove-btn:hover{background:#fef2f2}.area-card-divider{height:1px;background:var(--editor-border);margin:8px 0}.transform-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.transform-title{font-weight:600;font-size:12px}.transform-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.transform-reset-btn:hover{background:var(--editor-bg)}.transform-label{font-size:11px;color:var(--editor-text-muted);white-space:nowrap}.transform-input{width:60px;padding:3px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.transform-slider-row{flex-direction:column;align-items:stretch}.transform-slider{width:100%;accent-color:var(--editor-primary)}.loading-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px}.loading-spinner{width:32px;height:32px;border:3px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error-container{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px;padding:20px;text-align:center}.error-title{font-weight:600;font-size:16px;color:var(--editor-danger);margin-bottom:8px}.error-message{font-size:13px;color:var(--editor-text-muted);max-width:400px;white-space:pre-wrap}:host(:focus){outline:2px solid var(--editor-primary);outline-offset:-2px}input:focus,select:focus,button:focus-visible{outline:2px solid var(--editor-primary);outline-offset:-1px}.context-menu{position:absolute;z-index:100;min-width:160px;background:var(--editor-surface);border:1px solid var(--editor-border);border-radius:var(--editor-radius);box-shadow:0 4px 12px #00000026;padding:4px 0;font-size:13px}.context-menu-header{padding:6px 12px;font-weight:600;font-size:12px;color:var(--editor-text-muted);user-select:none}.context-menu-divider{height:1px;background:var(--editor-border);margin:4px 0}.context-menu-item{padding:6px 12px;cursor:pointer;color:var(--editor-text);user-select:none;transition:background .1s}.context-menu-item:hover{background:var(--editor-bg)}.context-menu-item:active{background:var(--editor-border)}.context-menu-item-disabled{color:var(--editor-text-muted);cursor:default;pointer-events:none}.editor-toast{position:absolute;top:16px;left:50%;transform:translate(-50%);padding:10px 20px;border-radius:var(--editor-radius);font-size:13px;font-family:var(--editor-font);line-height:1.4;z-index:200;max-width:400px;text-align:center;animation:toast-in .25s ease-out,toast-out .25s ease-in forwards;animation-delay:0s,var(--toast-duration, 3.75s);pointer-events:none;box-shadow:0 4px 12px #00000026}.editor-toast-warning{background:#fef3c7;color:#92400e;border:1px solid #f59e0b}.editor-toast-error{background:#fef2f2;color:#991b1b;border:1px solid var(--editor-danger)}.editor-toast-info{background:#eff6ff;color:#1e40af;border:1px solid var(--editor-primary)}@keyframes toast-in{0%{opacity:0;transform:translate(-50%) translateY(-10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes toast-out{0%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}.editor-modal-overlay{position:absolute;inset:0;background:#0006;z-index:300;display:flex;align-items:center;justify-content:center;animation:modal-overlay-in .2s ease-out}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}.editor-modal{background:var(--editor-surface);border-radius:var(--editor-radius);box-shadow:0 8px 32px #0003;max-width:380px;width:calc(100% - 32px);padding:20px;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.editor-modal-title{font-weight:600;font-size:15px;margin-bottom:8px;color:var(--editor-text)}.editor-modal-body{font-size:13px;color:var(--editor-text-muted);margin-bottom:16px;line-height:1.5}.editor-modal-actions{display:flex;justify-content:flex-end;gap:8px}.editor-modal-btn{padding:8px 16px;border-radius:var(--editor-radius-sm);font-size:13px;font-family:var(--editor-font);cursor:pointer;border:1px solid var(--editor-border);background:var(--editor-surface);color:var(--editor-text);transition:background .15s}.editor-modal-btn:hover{background:var(--editor-bg)}.editor-modal-btn-primary{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.editor-modal-btn-primary:hover{background:var(--editor-primary-hover)}.area-validation-error{font-size:11px;color:var(--editor-danger);margin-top:4px}@media(pointer:coarse){.zoom-btn{min-width:44px;min-height:44px}.area-input-text,.area-input-font,.area-input-size{min-height:44px;font-size:16px}.align-btn{min-width:44px;min-height:44px}.area-upload-btn{min-height:48px}.view-select{min-height:44px;font-size:16px}}', Q = "2.0.0", _ = 768;
1580
- class ee extends HTMLElement {
1647
+ const k = ':host{--editor-bg: #f3f4f6;--editor-surface: #ffffff;--editor-border: #e5e7eb;--editor-text: #111827;--editor-text-muted: #6b7280;--editor-primary: #3b82f6;--editor-primary-hover: #2563eb;--editor-danger: #ef4444;--editor-radius: 8px;--editor-radius-sm: 4px;--editor-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--editor-panel-width: 280px;display:block;width:100%;height:100%;font-family:var(--editor-font);font-size:13px;color:var(--editor-text);line-height:1.4}:host([theme="dark"]){--editor-bg: #1f2937;--editor-surface: #374151;--editor-border: #4b5563;--editor-text: #f9fafb;--editor-text-muted: #9ca3af}.editor-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative;overflow:hidden;background:var(--editor-bg)}.layout-desktop .editor-main{display:flex;flex:1;min-height:0}.layout-desktop .canvas-area{flex:1;min-width:0;position:relative;display:flex;flex-direction:column}.layout-desktop .area-panel{width:var(--editor-panel-width);border-left:1px solid var(--editor-border);background:var(--editor-surface);display:none;flex-direction:column;overflow:hidden}.layout-desktop .area-panel.area-panel-visible{display:flex}.layout-mobile .editor-main{display:flex;flex-direction:column;flex:1;min-height:0}.layout-mobile .canvas-area{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}.layout-mobile .area-panel{position:absolute;bottom:0;left:0;right:0;max-height:50%;background:var(--editor-surface);border-top:1px solid var(--editor-border);border-radius:var(--editor-radius) var(--editor-radius) 0 0;box-shadow:0 -4px 20px #00000026;transform:translateY(100%);transition:transform .25s ease-out;z-index:20;display:flex;flex-direction:column;overflow:hidden}.layout-mobile .area-panel.area-panel-visible{transform:translateY(0)}.area-panel-backdrop{display:none}.layout-mobile .area-panel-backdrop{display:block;position:absolute;inset:0;background:#0000004d;z-index:15;opacity:0;pointer-events:none;transition:opacity .25s ease-out}.layout-mobile .area-panel-backdrop.area-panel-backdrop-visible{opacity:1;pointer-events:auto}.zoom-toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;background:var(--editor-surface);border-bottom:1px solid var(--editor-border)}.zoom-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:14px;font-family:var(--editor-font);transition:background .15s;-webkit-tap-highlight-color:transparent;touch-action:manipulation}.zoom-btn:hover{background:var(--editor-bg)}.zoom-btn:active{background:var(--editor-border)}.zoom-percent{margin-left:8px;font-size:12px;color:var(--editor-text-muted);min-width:40px}.view-select{margin-left:8px;padding:4px 8px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);font-family:var(--editor-font);font-size:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.view-select:hover{background:var(--editor-bg)}.view-select:focus{outline:2px solid var(--editor-primary);outline-offset:-1px}.zoom-toolbar-spacer{flex:1}.zoom-close-btn{width:auto;padding:0 12px;font-size:13px;gap:4px}.zoom-save-btn{width:auto;padding:0 16px;margin-left:8px;font-size:13px;font-weight:600;background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.zoom-save-btn:hover{background:var(--editor-primary-hover)}.zoom-save-btn:disabled{opacity:.5;cursor:not-allowed}.canvas-wrapper{flex:1;position:relative;overflow:hidden;min-height:200px}.editor-canvas{position:absolute;top:0;left:0}.area-panel-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--editor-border);flex-shrink:0}.area-panel-title{font-weight:600;font-size:14px}.area-panel-close-btn{width:28px;height:28px;border:none;background:none;color:var(--editor-text-muted);cursor:pointer;font-size:18px;line-height:1;border-radius:var(--editor-radius-sm);display:flex;align-items:center;justify-content:center}.area-panel-close-btn:hover{background:var(--editor-bg);color:var(--editor-text)}.area-panel-content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.area-panel-empty{color:var(--editor-text-muted);font-size:12px;text-align:center;padding:20px 0}.area-card{padding:12px;border:1px solid var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-surface);cursor:pointer;transition:border-color .15s}.area-card:hover{border-color:var(--editor-primary)}.area-card-selected{border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.area-card-name-row{display:flex;align-items:center;gap:6px}.area-card-name{font-weight:600;font-size:13px}.area-card-badge{display:inline-block;padding:1px 6px;font-size:10px;font-weight:500;background:#fef3c7;color:#92400e;border-radius:10px}.area-card-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.area-card-reset-btn:hover{background:var(--editor-bg);color:var(--editor-danger);border-color:var(--editor-danger)}.area-card-mode-switcher{display:flex;gap:4px;margin-bottom:8px}.mode-btn{flex:1;padding:4px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-family:var(--editor-font);transition:all .15s}.mode-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-text{width:100%;padding:6px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:13px;color:var(--editor-text);background:var(--editor-surface);box-sizing:border-box;margin-bottom:6px}.area-input-text:focus{outline:none;border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-input-row{display:flex;gap:6px;align-items:center;margin-bottom:6px}.area-input-font{flex:1;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface);min-width:0}.area-input-size{width:56px;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.area-input-align{display:flex;gap:2px}.align-btn{width:28px;height:28px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-weight:600;font-family:var(--editor-font)}.align-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-color{width:32px;height:28px;padding:0;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);cursor:pointer;background:none}.area-upload-btn{width:100%;padding:10px;border:2px dashed var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-bg);color:var(--editor-text-muted);cursor:pointer;font-family:var(--editor-font);font-size:12px;text-align:center;transition:all .15s;min-height:44px}.area-upload-btn:hover{border-color:var(--editor-primary);color:var(--editor-primary)}.area-image-preview{display:flex;gap:8px;align-items:center}.area-image-thumb{width:48px;height:48px;object-fit:cover;border-radius:var(--editor-radius-sm);border:1px solid var(--editor-border)}.area-image-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.area-image-name{font-size:12px;color:var(--editor-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.area-image-remove-btn{padding:2px 8px;border:1px solid var(--editor-danger);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-danger);cursor:pointer;font-size:11px;font-family:var(--editor-font);width:fit-content}.area-image-remove-btn:hover{background:#fef2f2}.area-card-divider{height:1px;background:var(--editor-border);margin:8px 0}.transform-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.transform-title{font-weight:600;font-size:12px}.transform-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.transform-reset-btn:hover{background:var(--editor-bg)}.transform-label{font-size:11px;color:var(--editor-text-muted);white-space:nowrap}.transform-input{width:60px;padding:3px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.transform-slider-row{flex-direction:column;align-items:stretch}.transform-slider{width:100%;accent-color:var(--editor-primary)}.loading-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px}.loading-spinner{width:32px;height:32px;border:3px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error-container{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px;padding:20px;text-align:center}.error-title{font-weight:600;font-size:16px;color:var(--editor-danger);margin-bottom:8px}.error-message{font-size:13px;color:var(--editor-text-muted);max-width:400px;white-space:pre-wrap}:host(:focus){outline:2px solid var(--editor-primary);outline-offset:-2px}input:focus,select:focus,button:focus-visible{outline:2px solid var(--editor-primary);outline-offset:-1px}.context-menu{position:absolute;z-index:100;min-width:160px;background:var(--editor-surface);border:1px solid var(--editor-border);border-radius:var(--editor-radius);box-shadow:0 4px 12px #00000026;padding:4px 0;font-size:13px}.context-menu-header{padding:6px 12px;font-weight:600;font-size:12px;color:var(--editor-text-muted);user-select:none}.context-menu-divider{height:1px;background:var(--editor-border);margin:4px 0}.context-menu-item{padding:6px 12px;cursor:pointer;color:var(--editor-text);user-select:none;transition:background .1s}.context-menu-item:hover{background:var(--editor-bg)}.context-menu-item:active{background:var(--editor-border)}.context-menu-item-disabled{color:var(--editor-text-muted);cursor:default;pointer-events:none}.editor-toast{position:absolute;top:16px;left:50%;transform:translate(-50%);padding:10px 20px;border-radius:var(--editor-radius);font-size:13px;font-family:var(--editor-font);line-height:1.4;z-index:200;max-width:400px;text-align:center;animation:toast-in .25s ease-out,toast-out .25s ease-in forwards;animation-delay:0s,var(--toast-duration, 3.75s);pointer-events:none;box-shadow:0 4px 12px #00000026}.editor-toast-warning{background:#fef3c7;color:#92400e;border:1px solid #f59e0b}.editor-toast-error{background:#fef2f2;color:#991b1b;border:1px solid var(--editor-danger)}.editor-toast-info{background:#eff6ff;color:#1e40af;border:1px solid var(--editor-primary)}@keyframes toast-in{0%{opacity:0;transform:translate(-50%) translateY(-10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes toast-out{0%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}.editor-modal-overlay{position:absolute;inset:0;background:#0006;z-index:300;display:flex;align-items:center;justify-content:center;animation:modal-overlay-in .2s ease-out}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}.editor-modal{background:var(--editor-surface);border-radius:var(--editor-radius);box-shadow:0 8px 32px #0003;max-width:380px;width:calc(100% - 32px);padding:20px;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.editor-modal-title{font-weight:600;font-size:15px;margin-bottom:8px;color:var(--editor-text)}.editor-modal-body{font-size:13px;color:var(--editor-text-muted);margin-bottom:16px;line-height:1.5}.editor-modal-actions{display:flex;justify-content:flex-end;gap:8px}.editor-modal-btn{padding:8px 16px;border-radius:var(--editor-radius-sm);font-size:13px;font-family:var(--editor-font);cursor:pointer;border:1px solid var(--editor-border);background:var(--editor-surface);color:var(--editor-text);transition:background .15s}.editor-modal-btn:hover{background:var(--editor-bg)}.editor-modal-btn-primary{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.editor-modal-btn-primary:hover{background:var(--editor-primary-hover)}.area-validation-error{font-size:11px;color:var(--editor-danger);margin-top:4px}@media(pointer:coarse){.zoom-btn{min-width:44px;min-height:44px}.area-input-text,.area-input-font,.area-input-size{min-height:44px;font-size:16px}.align-btn{min-width:44px;min-height:44px}.area-upload-btn{min-height:48px}.view-select{min-height:44px;font-size:16px}}', _ = "2.0.0", ee = 768;
1648
+ class I extends HTMLElement {
1581
1649
  constructor() {
1582
- super(), this.resizeObserver = null, this.isReady = !1, this.currentTemplate = null, this.loadingTemplateId = null, this.isMobileLayout = !1, this.productViews = [], this.activeViewName = null, this.perViewContents = /* @__PURE__ */ new Map(), this.subscriptions = [], this.storageUsage = null, this.storageUsageLastRefresh = 0, this.emitChange = H(() => {
1650
+ super(), this.resizeObserver = null, this.isReady = !1, this.currentTemplate = null, this.loadingTemplateId = null, this.isMobileLayout = !1, this.productViews = [], this.activeViewName = null, this.perViewContents = /* @__PURE__ */ new Map(), this.subscriptions = [], this.storageUsage = null, this.storageUsageLastRefresh = 0, this.emitChange = q(() => {
1583
1651
  this.dispatchEvent(
1584
1652
  new CustomEvent("change", {
1585
1653
  detail: { design: this.getDesign() },
@@ -1590,7 +1658,7 @@ class ee extends HTMLElement {
1590
1658
  }, 300), this.shadow = this.attachShadow({ mode: "open" });
1591
1659
  }
1592
1660
  static get observedAttributes() {
1593
- return ["template-id", "product-id", "theme", "mode", "store-id", "api-url"];
1661
+ return ["template-id", "product-id", "theme", "mode", "store-id", "api-url", "show-close-button", "show-save-button"];
1594
1662
  }
1595
1663
  async connectedCallback() {
1596
1664
  try {
@@ -1602,15 +1670,15 @@ class ee extends HTMLElement {
1602
1670
  disconnectedCallback() {
1603
1671
  this.cleanup();
1604
1672
  }
1605
- attributeChangedCallback(e, t, i) {
1606
- t !== i && (e === "template-id" && this.isReady && i && this.loadTemplate(i), e === "product-id" && this.isReady && i && this.loadProduct(i));
1673
+ attributeChangedCallback(e, i, t) {
1674
+ i !== t && (e === "template-id" && this.isReady && t && this.loadTemplate(t), e === "product-id" && this.isReady && t && this.loadProduct(t), e === "show-close-button" && this.zoomToolbar && this.zoomToolbar.setCloseButtonVisible(t !== "false"), e === "show-save-button" && this.zoomToolbar && this.zoomToolbar.setSaveButtonEnabled(t !== "false"));
1607
1675
  }
1608
1676
  // ─── Public API ───
1609
1677
  getDesign() {
1610
1678
  if (!this.currentTemplate) throw new Error("No design loaded");
1611
1679
  return {
1612
1680
  templateId: this.currentTemplate.metadata.id,
1613
- engineVersion: Q,
1681
+ engineVersion: _,
1614
1682
  contents: this.contentManager.toJSON(),
1615
1683
  userData: {}
1616
1684
  };
@@ -1659,79 +1727,188 @@ class ee extends HTMLElement {
1659
1727
  }
1660
1728
  setActiveView(e) {
1661
1729
  if (this.activeViewName === e) return;
1662
- const t = this.productViews.find((i) => i.viewName === e);
1663
- if (!t) throw new Error(`View not found: ${e}`);
1664
- this.switchToView(t);
1730
+ const i = this.productViews.find((t) => t.viewName === e);
1731
+ if (!i) throw new Error(`View not found: ${e}`);
1732
+ this.switchToView(i);
1665
1733
  }
1666
- setAreaContent(e, t) {
1667
- t.type === "text" ? this.contentManager.setTextContent(e, t) : this.contentManager.setImageContent(e, t.dataUrl, t.filename), this.syncEngineContents(), this.saveContentState();
1734
+ setAreaContent(e, i) {
1735
+ i.type === "text" ? this.contentManager.setTextContent(e, i) : this.contentManager.setImageContent(e, i.dataUrl, i.filename), this.syncEngineContents(), this.saveContentState();
1668
1736
  }
1669
1737
  getAreaContent(e) {
1670
1738
  return this.contentManager.getContent(e);
1671
1739
  }
1740
+ static hasEditorContent(e) {
1741
+ return e.type === "text" ? e.text.trim().length > 0 : e.type === "image" ? !!e.dataUrl : !1;
1742
+ }
1743
+ validateRequiredContent() {
1744
+ const e = [];
1745
+ if (this.productViews.length > 1) {
1746
+ this.activeViewName && this.contentManager && this.perViewContents.set(this.activeViewName, this.contentManager.toJSON());
1747
+ for (const i of this.productViews) {
1748
+ const t = this.perViewContents.get(i.viewName), s = t?.some(([, o]) => I.hasEditorContent(o)) ?? !1;
1749
+ if (i.isRequired && !s) {
1750
+ e.push(`View "${i.viewName}" is required`);
1751
+ continue;
1752
+ }
1753
+ const a = i.template.templateJson.areas || [];
1754
+ for (const o of a) {
1755
+ if (!o.required) continue;
1756
+ const n = t?.find(([r]) => r === o.id);
1757
+ n && I.hasEditorContent(n[1]) || e.push(`"${o.name}" in view "${i.viewName}" is required`);
1758
+ }
1759
+ }
1760
+ } else {
1761
+ const i = this.currentTemplate?.areas || [], t = this.contentManager.getContents();
1762
+ for (const s of i) {
1763
+ if (!s.required) continue;
1764
+ const a = t.get(s.id);
1765
+ a && I.hasEditorContent(a) || e.push(`"${s.name}" is required`);
1766
+ }
1767
+ }
1768
+ return e;
1769
+ }
1770
+ showValidationErrors(e) {
1771
+ return new Promise((i) => {
1772
+ const t = c("div", { class: "editor-modal-overlay" }), s = c("div", { class: "editor-modal" });
1773
+ s.appendChild(c("div", { class: "editor-modal-title" }, "Required content missing"));
1774
+ const a = c("div", { class: "editor-modal-body" }), o = document.createElement("ul");
1775
+ o.style.margin = "8px 0", o.style.paddingLeft = "20px";
1776
+ for (const d of e) {
1777
+ const l = document.createElement("li");
1778
+ l.textContent = d, o.appendChild(l);
1779
+ }
1780
+ a.appendChild(o), s.appendChild(a);
1781
+ const n = c("div", { class: "editor-modal-actions" }), r = c("button", { class: "editor-modal-btn editor-modal-btn-primary" }, "OK");
1782
+ r.addEventListener("click", () => {
1783
+ t.remove(), i();
1784
+ }), n.appendChild(r), s.appendChild(n), t.appendChild(s), t.addEventListener("click", (d) => {
1785
+ d.target === t && (t.remove(), i());
1786
+ }), this.shadow.appendChild(t);
1787
+ });
1788
+ }
1672
1789
  async finalize() {
1673
- const e = this.engine.checkSafeAreaViolations();
1674
- if (e.length > 0) {
1675
- const a = this.currentTemplate?.areas || [], o = e.map((s) => a.find((r) => r.id === s)?.name || s).join(", ");
1790
+ const e = this.validateRequiredContent();
1791
+ if (e.length > 0)
1792
+ return await this.showValidationErrors(e), null;
1793
+ const i = this.engine.checkSafeAreaViolations();
1794
+ if (i.length > 0) {
1795
+ const o = this.currentTemplate?.areas || [], n = i.map((r) => o.find((d) => d.id === r)?.name || r).join(", ");
1676
1796
  if (!await this.showConfirmation(
1677
1797
  "Content outside safe area",
1678
- `Content in ${o} extends beyond the safe area. It may be trimmed during printing. Continue?`,
1798
+ `Content in ${n} extends beyond the safe area. It may be trimmed during printing. Continue?`,
1679
1799
  "Cancel",
1680
1800
  "Proceed Anyway"
1681
1801
  ))
1682
1802
  throw new Error("Finalization cancelled by user");
1683
1803
  }
1684
- const t = this.getDesign(), i = this.getAttribute("store-id") || "demo-shop", n = await this.apiClient.finalizeDesign(i, t);
1804
+ const t = this.getAttribute("store-id") || "demo-shop";
1805
+ if (this.productViews.length > 1) {
1806
+ this.activeViewName && this.contentManager && this.perViewContents.set(this.activeViewName, this.contentManager.toJSON());
1807
+ const o = [];
1808
+ for (const h of this.productViews) {
1809
+ const u = this.perViewContents.get(h.viewName);
1810
+ !u || u.length === 0 || !u.some(([, p]) => I.hasEditorContent(p)) || o.push({
1811
+ templateId: h.template.id,
1812
+ viewName: h.viewName,
1813
+ contents: u
1814
+ });
1815
+ }
1816
+ if (o.length === 0)
1817
+ throw new Error("No views have content to finalize");
1818
+ const n = this.getAttribute("product-id");
1819
+ if (!n)
1820
+ throw new Error("product-id attribute is required for multi-view finalize");
1821
+ const r = await this.apiClient.finalizeMultiView({
1822
+ productId: n,
1823
+ designs: o
1824
+ });
1825
+ if (!r.allCompleted) {
1826
+ const h = r.views.map((g) => g.designId), u = 1500, p = 40;
1827
+ let m = 0;
1828
+ for (; m < p; ) {
1829
+ await new Promise((f) => setTimeout(f, u)), m++;
1830
+ const g = await this.apiClient.pollMultiViewStatus(h);
1831
+ if (g.allCompleted) {
1832
+ for (const f of g.designs) {
1833
+ const b = r.views.find((w) => w.designId === f.designId);
1834
+ b && (b.status = f.status, b.proofUrl = f.proofUrl, b.errorMessage = f.errorMessage);
1835
+ }
1836
+ r.allCompleted = !0;
1837
+ break;
1838
+ }
1839
+ }
1840
+ }
1841
+ const d = r.views.map((h) => ({
1842
+ viewName: h.viewName,
1843
+ designId: h.designId,
1844
+ proofUrl: h.proofUrl || "",
1845
+ status: h.status
1846
+ })), l = d[0];
1847
+ return this.dispatchEvent(
1848
+ new CustomEvent("customizer:finalize", {
1849
+ detail: {
1850
+ designId: l.designId,
1851
+ proofUrl: l.proofUrl,
1852
+ status: l.status,
1853
+ views: d,
1854
+ requestId: r.requestId
1855
+ },
1856
+ bubbles: !0,
1857
+ composed: !0
1858
+ })
1859
+ ), { ...l, views: d };
1860
+ }
1861
+ const s = this.getDesign(), a = await this.apiClient.finalizeDesign(t, s);
1685
1862
  return this.dispatchEvent(
1686
1863
  new CustomEvent("customizer:finalize", {
1687
1864
  detail: {
1688
- designId: n.designId,
1689
- proofUrl: n.proofUrl,
1690
- templateId: t.templateId,
1691
- designJson: t,
1692
- status: n.status
1865
+ designId: a.designId,
1866
+ proofUrl: a.proofUrl,
1867
+ templateId: s.templateId,
1868
+ designJson: s,
1869
+ status: a.status
1693
1870
  },
1694
1871
  bubbles: !0,
1695
1872
  composed: !0
1696
1873
  })
1697
- ), n;
1874
+ ), a;
1698
1875
  }
1699
1876
  // ─── Initialization ───
1700
1877
  renderLoading() {
1701
1878
  const e = document.createElement("style");
1702
- e.textContent = z, this.shadow.appendChild(e);
1703
- const t = h(
1879
+ e.textContent = k, this.shadow.appendChild(e);
1880
+ const i = c(
1704
1881
  "div",
1705
1882
  { class: "loading-container" },
1706
- h("div", { class: "loading-spinner" })
1883
+ c("div", { class: "loading-spinner" })
1707
1884
  );
1708
- this.shadow.appendChild(t);
1885
+ this.shadow.appendChild(i);
1709
1886
  }
1710
1887
  async initialize() {
1711
1888
  const e = this.getAttribute("api-url") || "http://localhost:4000";
1712
- this.apiClient = new $(`${e.replace(/\/+$/, "")}/public`);
1713
- const t = this.getAttribute("product-id"), i = this.getAttribute("template-id");
1714
- if (!t && !i)
1889
+ this.apiClient = new B(`${e.replace(/\/+$/, "")}/public`);
1890
+ const i = this.getAttribute("product-id"), t = this.getAttribute("template-id");
1891
+ if (!i && !t)
1715
1892
  throw new Error("Either template-id or product-id attribute is required");
1716
- t ? await this.loadProduct(t) : await this.loadTemplate(i), this.isReady = !0, this.dispatchEvent(new CustomEvent("ready", { bubbles: !0, composed: !0 }));
1893
+ i ? await this.loadProduct(i) : await this.loadTemplate(t), this.isReady = !0, this.dispatchEvent(new CustomEvent("ready", { bubbles: !0, composed: !0 }));
1717
1894
  }
1718
1895
  async loadTemplate(e) {
1719
1896
  if (this.loadingTemplateId !== e) {
1720
1897
  this.loadingTemplateId = e;
1721
1898
  try {
1722
- const t = await this.apiClient.getTemplate(e);
1899
+ const i = await this.apiClient.getTemplate(e);
1723
1900
  if (this.loadingTemplateId !== e) return;
1724
- await this.loadTemplateData(t), this.loadingTemplateId = null;
1725
- } catch (t) {
1726
- throw this.loadingTemplateId = null, new Error(`Failed to load template: ${t.message}`);
1901
+ await this.loadTemplateData(i), this.loadingTemplateId = null;
1902
+ } catch (i) {
1903
+ throw this.loadingTemplateId = null, new Error(`Failed to load template: ${i.message}`);
1727
1904
  }
1728
1905
  }
1729
1906
  }
1730
1907
  async loadTemplateData(e) {
1731
1908
  this.currentTemplate = e;
1732
- const t = e.areas || [];
1733
- this.contentManager = new T(t);
1734
- const i = {
1909
+ const i = e.areas || [];
1910
+ this.contentManager = new P(i);
1911
+ const t = {
1735
1912
  template: e,
1736
1913
  contents: this.contentManager.toJSON(),
1737
1914
  selectedAreaId: null,
@@ -1740,13 +1917,13 @@ class ee extends HTMLElement {
1740
1917
  isDirty: !1,
1741
1918
  warnings: []
1742
1919
  };
1743
- this.stateManager = new O(i), this.buildUI(e, t), this.engine = new N(this.canvas), this.engine.setArtboard({
1920
+ this.stateManager = new O(t), this.buildUI(e, i), this.engine = new U(this.canvas), this.engine.setArtboard({
1744
1921
  width: e.artboard.width,
1745
1922
  height: e.artboard.height,
1746
1923
  backgroundColor: e.artboard.backgroundColor ?? "#ffffff"
1747
- }), this.engine.setAreas(t), this.engine.setContents(this.contentManager.getContents()), e.backgroundImage && this.engine.setBackgroundImage(e.backgroundImage);
1748
- const n = e.artboard.safeArea ?? e.safeArea;
1749
- n && this.engine.setSafeArea(n), this.engine.start(), this.engine.fitToView(), this.stateManager.update({
1924
+ }), this.engine.setAreas(i), this.engine.setContents(this.contentManager.getContents()), e.backgroundImage && this.engine.setBackgroundImage(e.backgroundImage);
1925
+ const s = e.artboard.safeArea ?? e.safeArea;
1926
+ s && this.engine.setSafeArea(s), this.engine.start(), this.engine.fitToView(), this.stateManager.update({
1750
1927
  zoom: this.engine.getZoom(),
1751
1928
  pan: this.engine.getPan()
1752
1929
  }), this.zoomToolbar.setZoom(this.engine.getZoom()), this.interaction = new F(
@@ -1767,38 +1944,38 @@ class ee extends HTMLElement {
1767
1944
  }
1768
1945
  async loadProduct(e) {
1769
1946
  try {
1770
- const t = await this.apiClient.getProduct(e);
1771
- if (t.views.length === 0)
1947
+ const i = await this.apiClient.getProduct(e);
1948
+ if (i.views.length === 0)
1772
1949
  throw new Error("Product has no active templates");
1773
- this.productViews = t.views, this.perViewContents.clear();
1774
- const i = t.views.find((n) => n.isDefault) || t.views[0];
1775
- await this.loadTemplateData(i.template.templateJson), this.activeViewName = i.viewName, this.zoomToolbar && t.views.length > 1 && (this.zoomToolbar.setViews(this.getViews()), this.zoomToolbar.setActiveView(i.viewName));
1776
- } catch (t) {
1777
- throw new Error(`Failed to load product: ${t.message}`);
1950
+ this.productViews = i.views, this.perViewContents.clear();
1951
+ const t = i.views.find((s) => s.isDefault) || i.views[0];
1952
+ await this.loadTemplateData(t.template.templateJson), this.activeViewName = t.viewName, this.zoomToolbar && i.views.length > 1 && (this.zoomToolbar.setViews(this.getViews()), this.zoomToolbar.setActiveView(t.viewName));
1953
+ } catch (i) {
1954
+ throw new Error(`Failed to load product: ${i.message}`);
1778
1955
  }
1779
1956
  }
1780
1957
  switchToView(e) {
1781
1958
  this.activeViewName && this.contentManager && this.perViewContents.set(this.activeViewName, this.contentManager.toJSON()), this.activeViewName = e.viewName, this.zoomToolbar?.setActiveView(e.viewName);
1782
- const t = e.template.templateJson;
1783
- this.currentTemplate = t;
1784
- const i = t.areas || [];
1785
- this.contentManager = new T(i);
1786
- const n = this.perViewContents.get(e.viewName);
1787
- n && this.contentManager.fromJSON(n), this.engine.setArtboard({
1788
- width: t.artboard.width,
1789
- height: t.artboard.height,
1790
- backgroundColor: t.artboard.backgroundColor ?? "#ffffff"
1791
- }), this.engine.setAreas(i), this.engine.setContents(this.contentManager.getContents()), this.engine.setBackgroundImage(t.backgroundImage ?? void 0);
1792
- const a = t.artboard.safeArea ?? t.safeArea;
1959
+ const i = e.template.templateJson;
1960
+ this.currentTemplate = i;
1961
+ const t = i.areas || [];
1962
+ this.contentManager = new P(t);
1963
+ const s = this.perViewContents.get(e.viewName);
1964
+ s && this.contentManager.fromJSON(s), this.engine.setArtboard({
1965
+ width: i.artboard.width,
1966
+ height: i.artboard.height,
1967
+ backgroundColor: i.artboard.backgroundColor ?? "#ffffff"
1968
+ }), this.engine.setAreas(t), this.engine.setContents(this.contentManager.getContents()), this.engine.setBackgroundImage(i.backgroundImage ?? void 0);
1969
+ const a = i.artboard.safeArea ?? i.safeArea;
1793
1970
  this.engine.setSafeArea(a ?? null), this.engine.fitToView(), this.stateManager.setState({
1794
- template: t,
1971
+ template: i,
1795
1972
  contents: this.contentManager.toJSON(),
1796
1973
  selectedAreaId: null,
1797
1974
  isDirty: this.stateManager.getState().isDirty,
1798
1975
  zoom: this.engine.getZoom(),
1799
1976
  pan: this.engine.getPan(),
1800
1977
  warnings: []
1801
- }), this.zoomToolbar.setZoom(this.engine.getZoom()), this.inputPanel.setAreas(i), this.inputPanel.setContents(this.contentManager.getContents()), this.inputPanel.setSelectedArea(null), this.engine.setSelectedArea(null), this.dispatchEvent(
1978
+ }), this.zoomToolbar.setZoom(this.engine.getZoom()), this.inputPanel.setAreas(t), this.inputPanel.setContents(this.contentManager.getContents()), this.inputPanel.setSelectedArea(null), this.engine.setSelectedArea(null), this.dispatchEvent(
1802
1979
  new CustomEvent("view-change", {
1803
1980
  detail: { viewName: e.viewName },
1804
1981
  bubbles: !0,
@@ -1806,12 +1983,12 @@ class ee extends HTMLElement {
1806
1983
  })
1807
1984
  );
1808
1985
  }
1809
- buildUI(e, t) {
1986
+ buildUI(e, i) {
1810
1987
  this.shadow.innerHTML = "";
1811
- const i = document.createElement("style");
1812
- i.textContent = z, this.shadow.appendChild(i), this.container = h("div", { class: "editor-container" }), this.zoomToolbar = new B(), this.subscriptions.push(this.zoomToolbar.on("zoom-in", () => this.handleZoomIn())), this.subscriptions.push(this.zoomToolbar.on("zoom-out", () => this.handleZoomOut())), this.subscriptions.push(this.zoomToolbar.on("zoom-fit", () => this.handleZoomFit())), this.subscriptions.push(this.zoomToolbar.on("view-change", ({ viewName: o }) => {
1813
- const s = this.productViews.find((r) => r.viewName === o);
1814
- s && this.switchToView(s);
1988
+ const t = document.createElement("style");
1989
+ t.textContent = k, this.shadow.appendChild(t), this.container = c("div", { class: "editor-container" }), this.zoomToolbar = new H(), this.subscriptions.push(this.zoomToolbar.on("zoom-in", () => this.handleZoomIn())), this.subscriptions.push(this.zoomToolbar.on("zoom-out", () => this.handleZoomOut())), this.subscriptions.push(this.zoomToolbar.on("zoom-fit", () => this.handleZoomFit())), this.subscriptions.push(this.zoomToolbar.on("view-change", ({ viewName: o }) => {
1990
+ const n = this.productViews.find((r) => r.viewName === o);
1991
+ n && this.switchToView(n);
1815
1992
  })), this.subscriptions.push(this.zoomToolbar.on("close", () => {
1816
1993
  this.dispatchEvent(new CustomEvent("customizer:close", {
1817
1994
  bubbles: !0,
@@ -1820,21 +1997,30 @@ class ee extends HTMLElement {
1820
1997
  })), this.subscriptions.push(this.zoomToolbar.on("save", async () => {
1821
1998
  this.zoomToolbar.setSaveDisabled(!0, "Saving...");
1822
1999
  try {
1823
- await this.finalize(), this.zoomToolbar.setSaveDisabled(!0, "Saved!"), this.showToast("Design saved successfully!", "info"), setTimeout(() => {
1824
- this.zoomToolbar.setSaveDisabled(!1, "Save Customization"), this.zoomToolbar.showSaveButton(!1), this.stateManager.update({ isDirty: !1 });
2000
+ const o = await this.finalize();
2001
+ if (!o) {
2002
+ this.zoomToolbar.setSaveDisabled(!1, "Save");
2003
+ return;
2004
+ }
2005
+ this.zoomToolbar.setSaveDisabled(!0, "Saved!"), this.showToast("Design saved successfully!", "info"), this.dispatchEvent(new CustomEvent("customizer:save", {
2006
+ detail: o,
2007
+ bubbles: !0,
2008
+ composed: !0
2009
+ })), setTimeout(() => {
2010
+ this.zoomToolbar.setSaveDisabled(!1, "Save"), this.zoomToolbar.showSaveButton(!1), this.stateManager.update({ isDirty: !1 });
1825
2011
  }, 2e3);
1826
2012
  } catch (o) {
1827
2013
  console.error("Customizer: finalize failed", o);
1828
- const s = o instanceof Error ? o.message : "Save failed. Please try again.";
1829
- this.showToast(s, "error"), this.zoomToolbar.setSaveDisabled(!1, "Save Customization");
2014
+ const n = o instanceof Error ? o.message : "Save failed. Please try again.";
2015
+ this.showToast(n, "error"), this.zoomToolbar.setSaveDisabled(!1, "Save");
1830
2016
  }
1831
- }));
1832
- const n = h("div", { class: "canvas-area" });
1833
- n.appendChild(this.zoomToolbar.getElement()), this.canvasWrapper = h("div", { class: "canvas-wrapper" }), this.canvas = document.createElement("canvas"), this.canvas.className = "editor-canvas", this.canvasWrapper.appendChild(this.canvas), n.appendChild(this.canvasWrapper), this.inputPanel = new j(t), this.wireInputPanelEvents();
1834
- const a = h("div", { class: "editor-main" });
1835
- a.appendChild(n), a.appendChild(this.inputPanel.getBackdrop()), a.appendChild(this.inputPanel.getElement()), this.container.appendChild(a), this.shadow.appendChild(this.container), this.resizeObserver = new ResizeObserver((o) => {
1836
- for (const s of o)
1837
- s.target === this.canvasWrapper && this.handleCanvasResize(s.contentRect), s.target === this.container && this.handleLayoutResize(s.contentRect);
2017
+ })), this.getAttribute("show-close-button") === "false" && this.zoomToolbar.setCloseButtonVisible(!1), this.getAttribute("show-save-button") === "false" && this.zoomToolbar.setSaveButtonEnabled(!1);
2018
+ const s = c("div", { class: "canvas-area" });
2019
+ s.appendChild(this.zoomToolbar.getElement()), this.canvasWrapper = c("div", { class: "canvas-wrapper" }), this.canvas = document.createElement("canvas"), this.canvas.className = "editor-canvas", this.canvasWrapper.appendChild(this.canvas), s.appendChild(this.canvasWrapper), this.inputPanel = new J(i), this.wireInputPanelEvents();
2020
+ const a = c("div", { class: "editor-main" });
2021
+ a.appendChild(s), a.appendChild(this.inputPanel.getBackdrop()), a.appendChild(this.inputPanel.getElement()), this.container.appendChild(a), this.shadow.appendChild(this.container), this.resizeObserver = new ResizeObserver((o) => {
2022
+ for (const n of o)
2023
+ n.target === this.canvasWrapper && this.handleCanvasResize(n.contentRect), n.target === this.container && this.handleLayoutResize(n.contentRect);
1838
2024
  }), this.resizeObserver.observe(this.canvasWrapper), this.resizeObserver.observe(this.container);
1839
2025
  }
1840
2026
  // ─── Event Wiring ───
@@ -1844,22 +2030,22 @@ class ee extends HTMLElement {
1844
2030
  this.handleAreaSelect(e);
1845
2031
  })
1846
2032
  ), this.subscriptions.push(
1847
- this.interaction.on("content:drag", ({ areaId: e, offset: t }) => {
1848
- this.contentManager.setContentOffset(e, t), this.saveContentState();
2033
+ this.interaction.on("content:drag", ({ areaId: e, offset: i }) => {
2034
+ this.contentManager.setContentOffset(e, i), this.saveContentState();
1849
2035
  })
1850
2036
  ), this.subscriptions.push(
1851
- this.interaction.on("content:scale", ({ areaId: e, scale: t }) => {
1852
- this.contentManager.setImageScale(e, t), this.saveContentState();
2037
+ this.interaction.on("content:scale", ({ areaId: e, scale: i }) => {
2038
+ this.contentManager.setImageScale(e, i), this.saveContentState();
1853
2039
  })
1854
2040
  ), this.subscriptions.push(
1855
- this.interaction.on("content:rotate", ({ areaId: e, rotation: t }) => {
1856
- this.contentManager.setImageRotation(e, t), this.saveContentState();
2041
+ this.interaction.on("content:rotate", ({ areaId: e, rotation: i }) => {
2042
+ this.contentManager.setImageRotation(e, i), this.saveContentState();
1857
2043
  })
1858
2044
  ), this.subscriptions.push(
1859
2045
  this.interaction.on("zoom", ({ zoom: e }) => {
1860
2046
  this.engine.setZoom(e);
1861
- const t = this.engine.getCenteredPan(e);
1862
- t && this.engine.setPan(t), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
2047
+ const i = this.engine.getCenteredPan(e);
2048
+ i && this.engine.setPan(i), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
1863
2049
  })
1864
2050
  ), this.subscriptions.push(
1865
2051
  this.interaction.on("pan", ({ pan: e }) => {
@@ -1870,22 +2056,22 @@ class ee extends HTMLElement {
1870
2056
  this.canvas.style.cursor = e;
1871
2057
  })
1872
2058
  ), this.subscriptions.push(
1873
- this.interaction.on("context-menu", ({ areaId: e, clientX: t, clientY: i }) => {
1874
- this.handleContextMenu(e, t, i);
2059
+ this.interaction.on("context-menu", ({ areaId: e, clientX: i, clientY: t }) => {
2060
+ this.handleContextMenu(e, i, t);
1875
2061
  })
1876
2062
  );
1877
2063
  }
1878
2064
  wireInputPanelEvents() {
1879
2065
  this.subscriptions.push(
1880
- this.inputPanel.on("text:change", ({ areaId: e, updates: t }) => {
1881
- this.contentManager.setTextContent(e, t), this.saveContentState();
2066
+ this.inputPanel.on("text:change", ({ areaId: e, updates: i }) => {
2067
+ this.contentManager.setTextContent(e, i), this.saveContentState();
1882
2068
  })
1883
2069
  ), this.subscriptions.push(
1884
- this.inputPanel.on("image:change", async ({ areaId: e, dataUrl: t, filename: i }) => {
1885
- const n = this.contentManager.getContents();
2070
+ this.inputPanel.on("image:change", async ({ areaId: e, dataUrl: i, filename: t }) => {
2071
+ const s = this.contentManager.getContents();
1886
2072
  let a = 0;
1887
- for (const [s, r] of n)
1888
- r.type === "image" && s !== e && a++;
2073
+ for (const [n, r] of s)
2074
+ r.type === "image" && n !== e && a++;
1889
2075
  if (a >= 10) {
1890
2076
  this.showToast("Maximum of 10 images per design reached", "error");
1891
2077
  return;
@@ -1894,28 +2080,30 @@ class ee extends HTMLElement {
1894
2080
  this.showToast("Storage quota exceeded. Upgrade your plan for more storage.", "error");
1895
2081
  return;
1896
2082
  }
1897
- const o = this.currentTemplate?.areas.find((s) => s.id === e);
2083
+ const o = this.currentTemplate?.areas.find((n) => n.id === e);
1898
2084
  if (o && this.currentTemplate) {
1899
- const s = this.currentTemplate.print.targetDpi;
2085
+ const n = this.currentTemplate.print.targetDpi;
1900
2086
  try {
1901
- const r = new Image(), d = await new Promise((p, g) => {
1902
- r.onload = () => p({ width: r.naturalWidth, height: r.naturalHeight }), r.onerror = () => g(new Error("Failed to load image")), r.src = t;
1903
- }), l = o.location.width / s, c = o.location.height / s, u = K(
2087
+ const r = new Image();
2088
+ r.crossOrigin = "anonymous";
2089
+ const d = await new Promise((p, m) => {
2090
+ r.onload = () => p({ width: r.naturalWidth, height: r.naturalHeight }), r.onerror = () => m(new Error("Failed to load image")), r.src = i;
2091
+ }), l = o.location.width / n, h = o.location.height / n, u = Q(
1904
2092
  d.width,
1905
2093
  d.height,
1906
2094
  l,
1907
- c,
1908
- s
2095
+ h,
2096
+ n
1909
2097
  );
1910
2098
  if (u.actualDPI < 150) {
1911
2099
  if (!await this.showConfirmation(
1912
2100
  "Low resolution detected",
1913
- `This image is ${u.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${s} DPI.`,
2101
+ `This image is ${u.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${n} DPI.`,
1914
2102
  "Cancel",
1915
2103
  "Use Anyway"
1916
2104
  )) return;
1917
- } else u.actualDPI < s && (this.showToast(
1918
- `Image resolution is ${u.actualDPI} DPI (${s} recommended)`,
2105
+ } else u.actualDPI < n && (this.showToast(
2106
+ `Image resolution is ${u.actualDPI} DPI (${n} recommended)`,
1919
2107
  "warning"
1920
2108
  ), this.stateManager.update({
1921
2109
  warnings: [
@@ -1926,9 +2114,9 @@ class ee extends HTMLElement {
1926
2114
  } catch {
1927
2115
  }
1928
2116
  }
1929
- if (this.contentManager.setImageContent(e, t, i), this.saveContentState(), this.storageUsage) {
1930
- const s = Math.round(t.length * 0.75);
1931
- this.storageUsage.storageUsed += s, this.storageUsage.usagePercent = this.storageUsage.storageQuota > 0 ? Math.round(this.storageUsage.storageUsed / this.storageUsage.storageQuota * 100) : 0;
2117
+ if (this.contentManager.setImageContent(e, i, t), this.saveContentState(), this.storageUsage) {
2118
+ const n = Math.round(i.length * 0.75);
2119
+ this.storageUsage.storageUsed += n, this.storageUsage.usagePercent = this.storageUsage.storageQuota > 0 ? Math.round(this.storageUsage.storageUsed / this.storageUsage.storageQuota * 100) : 0;
1932
2120
  }
1933
2121
  })
1934
2122
  ), this.subscriptions.push(
@@ -1944,16 +2132,16 @@ class ee extends HTMLElement {
1944
2132
  this.handleAreaSelect(e);
1945
2133
  })
1946
2134
  ), this.subscriptions.push(
1947
- this.inputPanel.on("offset:change", ({ areaId: e, offset: t }) => {
1948
- this.contentManager.setContentOffset(e, t), this.saveContentState();
2135
+ this.inputPanel.on("offset:change", ({ areaId: e, offset: i }) => {
2136
+ this.contentManager.setContentOffset(e, i), this.saveContentState();
1949
2137
  })
1950
2138
  ), this.subscriptions.push(
1951
- this.inputPanel.on("scale:change", ({ areaId: e, scale: t }) => {
1952
- this.contentManager.setImageScale(e, t), this.saveContentState();
2139
+ this.inputPanel.on("scale:change", ({ areaId: e, scale: i }) => {
2140
+ this.contentManager.setImageScale(e, i), this.saveContentState();
1953
2141
  })
1954
2142
  ), this.subscriptions.push(
1955
- this.inputPanel.on("rotation:change", ({ areaId: e, rotation: t }) => {
1956
- this.contentManager.setImageRotation(e, t), this.saveContentState();
2143
+ this.inputPanel.on("rotation:change", ({ areaId: e, rotation: i }) => {
2144
+ this.contentManager.setImageRotation(e, i), this.saveContentState();
1957
2145
  })
1958
2146
  ), this.subscriptions.push(
1959
2147
  this.inputPanel.on("dismiss", () => {
@@ -1962,8 +2150,8 @@ class ee extends HTMLElement {
1962
2150
  );
1963
2151
  }
1964
2152
  // ─── Handlers ───
1965
- handleAreaSelect(e, t = !1) {
1966
- this.stateManager.update({ selectedAreaId: e }), this.engine.setSelectedArea(e), this.inputPanel.setSelectedArea(t ? e : null), e && document.activeElement !== this && this.focus(), this.dispatchEvent(
2153
+ handleAreaSelect(e, i = !1) {
2154
+ this.stateManager.update({ selectedAreaId: e }), this.engine.setSelectedArea(e), this.inputPanel.setSelectedArea(i ? e : null), e && document.activeElement !== this && this.focus(), this.dispatchEvent(
1967
2155
  new CustomEvent("area:select", {
1968
2156
  detail: { areaId: e },
1969
2157
  bubbles: !0,
@@ -1971,29 +2159,29 @@ class ee extends HTMLElement {
1971
2159
  })
1972
2160
  );
1973
2161
  }
1974
- handleContextMenu(e, t, i) {
2162
+ handleContextMenu(e, i, t) {
1975
2163
  this.handleAreaSelect(e, !!e);
1976
2164
  }
1977
2165
  handleZoomIn() {
1978
- const e = this.engine.getZoom(), t = Math.min(e * 1.25, 5), i = this.engine.getCenteredPan(t);
1979
- this.engine.setZoom(t), i && this.engine.setPan(i), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
2166
+ const e = this.engine.getZoom(), i = Math.min(e * 1.25, 5), t = this.engine.getCenteredPan(i);
2167
+ this.engine.setZoom(i), t && this.engine.setPan(t), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
1980
2168
  }
1981
2169
  handleZoomOut() {
1982
- const e = this.engine.getZoom(), t = Math.max(e * 0.8, 0.1), i = this.engine.getCenteredPan(t);
1983
- this.engine.setZoom(t), i && this.engine.setPan(i), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
2170
+ const e = this.engine.getZoom(), i = Math.max(e * 0.8, 0.1), t = this.engine.getCenteredPan(i);
2171
+ this.engine.setZoom(i), t && this.engine.setPan(t), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
1984
2172
  }
1985
2173
  handleZoomFit() {
1986
2174
  this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
1987
2175
  }
1988
2176
  handleCanvasResize(e) {
1989
- const { width: t, height: i } = e;
1990
- if (t === 0 || i === 0) return;
1991
- const n = window.devicePixelRatio || 1;
1992
- this.canvas.width = t * n, this.canvas.height = i * n, this.canvas.style.width = `${t}px`, this.canvas.style.height = `${i}px`, this.engine && (this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom()));
2177
+ const { width: i, height: t } = e;
2178
+ if (i === 0 || t === 0) return;
2179
+ const s = window.devicePixelRatio || 1;
2180
+ this.canvas.width = i * s, this.canvas.height = t * s, this.canvas.style.width = `${i}px`, this.canvas.style.height = `${t}px`, this.engine && (this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom()));
1993
2181
  }
1994
2182
  handleLayoutResize(e) {
1995
- const t = e.width < _;
1996
- this.container.classList.toggle("layout-desktop", !t), this.container.classList.toggle("layout-mobile", t), t !== this.isMobileLayout && (this.isMobileLayout = t, this.inputPanel.setMobile(t));
2183
+ const i = e.width < ee;
2184
+ this.container.classList.toggle("layout-desktop", !i), this.container.classList.toggle("layout-mobile", i), i !== this.isMobileLayout && (this.isMobileLayout = i, this.inputPanel.setMobile(i));
1997
2185
  }
1998
2186
  // ─── State Management ───
1999
2187
  async refreshStorageUsage() {
@@ -2020,7 +2208,7 @@ class ee extends HTMLElement {
2020
2208
  }
2021
2209
  // ─── Keyboard ───
2022
2210
  setupKeyboardShortcuts() {
2023
- const e = J();
2211
+ const e = G();
2024
2212
  this.keyboard.register({
2025
2213
  key: "z",
2026
2214
  [e]: !0,
@@ -2036,35 +2224,35 @@ class ee extends HTMLElement {
2036
2224
  });
2037
2225
  }
2038
2226
  // ─── Toast & Modal ───
2039
- showToast(e, t = "info") {
2040
- const i = h("div", { class: `editor-toast editor-toast-${t}` }, e);
2041
- this.shadow.appendChild(i), setTimeout(() => i.remove(), 4e3);
2227
+ showToast(e, i = "info") {
2228
+ const t = c("div", { class: `editor-toast editor-toast-${i}` }, e);
2229
+ this.shadow.appendChild(t), setTimeout(() => t.remove(), 4e3);
2042
2230
  }
2043
- showConfirmation(e, t, i = "Cancel", n = "Continue") {
2231
+ showConfirmation(e, i, t = "Cancel", s = "Continue") {
2044
2232
  return new Promise((a) => {
2045
- const o = h("div", { class: "editor-modal-overlay" }), s = h("div", { class: "editor-modal" });
2046
- s.appendChild(h("div", { class: "editor-modal-title" }, e)), s.appendChild(h("div", { class: "editor-modal-body" }, t));
2047
- const r = h("div", { class: "editor-modal-actions" }), d = h("button", { class: "editor-modal-btn" }, i);
2233
+ const o = c("div", { class: "editor-modal-overlay" }), n = c("div", { class: "editor-modal" });
2234
+ n.appendChild(c("div", { class: "editor-modal-title" }, e)), n.appendChild(c("div", { class: "editor-modal-body" }, i));
2235
+ const r = c("div", { class: "editor-modal-actions" }), d = c("button", { class: "editor-modal-btn" }, t);
2048
2236
  d.addEventListener("click", () => {
2049
2237
  o.remove(), a(!1);
2050
2238
  });
2051
- const l = h("button", { class: "editor-modal-btn editor-modal-btn-primary" }, n);
2239
+ const l = c("button", { class: "editor-modal-btn editor-modal-btn-primary" }, s);
2052
2240
  l.addEventListener("click", () => {
2053
2241
  o.remove(), a(!0);
2054
- }), r.appendChild(d), r.appendChild(l), s.appendChild(r), o.appendChild(s), o.addEventListener("click", (c) => {
2055
- c.target === o && (o.remove(), a(!1));
2242
+ }), r.appendChild(d), r.appendChild(l), n.appendChild(r), o.appendChild(n), o.addEventListener("click", (h) => {
2243
+ h.target === o && (o.remove(), a(!1));
2056
2244
  }), this.shadow.appendChild(o);
2057
2245
  });
2058
2246
  }
2059
2247
  // ─── Error / Cleanup ───
2060
- showError(e, t) {
2248
+ showError(e, i) {
2061
2249
  this.shadow.innerHTML = "";
2062
- const i = document.createElement("style");
2063
- i.textContent = z, this.shadow.appendChild(i);
2064
- const n = h("div", { class: "error-container" });
2065
- n.appendChild(h("div", { class: "error-title" }, e)), n.appendChild(h("div", { class: "error-message" }, t?.message ?? "")), this.shadow.appendChild(n), this.dispatchEvent(
2250
+ const t = document.createElement("style");
2251
+ t.textContent = k, this.shadow.appendChild(t);
2252
+ const s = c("div", { class: "error-container" });
2253
+ s.appendChild(c("div", { class: "error-title" }, e)), s.appendChild(c("div", { class: "error-message" }, i?.message ?? "")), this.shadow.appendChild(s), this.dispatchEvent(
2066
2254
  new CustomEvent("error", {
2067
- detail: { message: e, error: t },
2255
+ detail: { message: e, error: i },
2068
2256
  bubbles: !0,
2069
2257
  composed: !0
2070
2258
  })
@@ -2076,174 +2264,189 @@ class ee extends HTMLElement {
2076
2264
  this.subscriptions = [], this.resizeObserver && (this.resizeObserver.disconnect(), this.resizeObserver = null), this.engine?.destroy(), this.interaction?.destroy(), this.keyboard?.destroy(), this.isReady = !1, this.currentTemplate = null, this.productViews = [], this.activeViewName = null, this.perViewContents.clear();
2077
2265
  }
2078
2266
  }
2079
- customElements.get("customizer-editor") || customElements.define("customizer-editor", ee);
2080
- function te(f, e) {
2081
- const t = typeof f == "string" ? document.querySelector(f) : f;
2082
- if (!t)
2267
+ customElements.get("customizer-editor") || customElements.define("customizer-editor", I);
2268
+ function te(v, e) {
2269
+ const i = typeof v == "string" ? document.querySelector(v) : v;
2270
+ if (!i)
2083
2271
  throw new Error(
2084
- `Container not found: ${typeof f == "string" ? f : "provided element is null"}`
2272
+ `Container not found: ${typeof v == "string" ? v : "provided element is null"}`
2085
2273
  );
2086
2274
  if (!e.templateId && !e.productId)
2087
2275
  throw new Error("Either templateId or productId must be provided");
2088
2276
  if (e.templateId && e.productId)
2089
2277
  throw new Error("Only one of templateId or productId should be provided, not both");
2090
- const i = document.createElement("customizer-editor");
2091
- e.productId ? i.setAttribute("product-id", e.productId) : i.setAttribute("template-id", e.templateId), e.theme && i.setAttribute("theme", e.theme), e.mode && i.setAttribute("mode", e.mode), e.className && i.classList.add(e.className), i.style.width = "100%", i.style.height = "100%";
2092
- const n = [], a = (s, r) => {
2093
- i.addEventListener(s, r), n.push({ event: s, handler: r });
2278
+ const t = document.createElement("customizer-editor");
2279
+ e.productId ? t.setAttribute("product-id", e.productId) : t.setAttribute("template-id", e.templateId), e.theme && t.setAttribute("theme", e.theme), e.mode && t.setAttribute("mode", e.mode), e.className && t.classList.add(e.className), e.showCloseButton === !1 && t.setAttribute("show-close-button", "false"), e.showSaveButton === !1 && t.setAttribute("show-save-button", "false"), t.style.width = "100%", t.style.height = "100%";
2280
+ const s = [], a = (n, r) => {
2281
+ t.addEventListener(n, r), s.push({ event: n, handler: r });
2094
2282
  };
2095
- t.innerHTML = "", t.appendChild(i);
2283
+ i.innerHTML = "", i.appendChild(t);
2096
2284
  const o = {
2097
2285
  getDesign() {
2098
- if (typeof i.getDesign != "function")
2286
+ if (typeof t.getDesign != "function")
2099
2287
  throw new Error("Editor not ready: getDesign method not available");
2100
- return i.getDesign();
2288
+ return t.getDesign();
2101
2289
  },
2102
- setDesign(s) {
2103
- if (typeof i.setDesign != "function")
2290
+ setDesign(n) {
2291
+ if (typeof t.setDesign != "function")
2104
2292
  throw new Error("Editor not ready: setDesign method not available");
2105
- i.setDesign(s);
2293
+ t.setDesign(n);
2106
2294
  },
2107
2295
  undo() {
2108
- if (typeof i.undo != "function")
2296
+ if (typeof t.undo != "function")
2109
2297
  throw new Error("Editor not ready: undo method not available");
2110
- i.undo();
2298
+ t.undo();
2111
2299
  },
2112
2300
  redo() {
2113
- if (typeof i.redo != "function")
2301
+ if (typeof t.redo != "function")
2114
2302
  throw new Error("Editor not ready: redo method not available");
2115
- i.redo();
2303
+ t.redo();
2116
2304
  },
2117
2305
  canUndo() {
2118
- return typeof i.canUndo != "function" ? !1 : i.canUndo();
2306
+ return typeof t.canUndo != "function" ? !1 : t.canUndo();
2119
2307
  },
2120
2308
  canRedo() {
2121
- return typeof i.canRedo != "function" ? !1 : i.canRedo();
2309
+ return typeof t.canRedo != "function" ? !1 : t.canRedo();
2122
2310
  },
2123
2311
  async finalize() {
2124
- const s = this.getDesign(), r = e.apiUrl || "https://api.varianta.io";
2312
+ const n = e.apiUrl || "https://api.varianta.io";
2125
2313
  try {
2126
- const d = await fetch(`${r}/public/finalize`, {
2314
+ if (e.productId && typeof t.finalize == "function") {
2315
+ const g = await t.finalize(), f = {
2316
+ designId: g.designId,
2317
+ status: g.status,
2318
+ proofUrl: g.proofUrl ?? null,
2319
+ errorMessage: null,
2320
+ requestId: g.requestId,
2321
+ views: g.views
2322
+ };
2323
+ return e.onFinalize && e.onFinalize(f), f;
2324
+ }
2325
+ const r = this.getDesign(), d = await fetch(`${n}/public/finalize`, {
2127
2326
  method: "POST",
2128
2327
  headers: {
2129
2328
  "Content-Type": "application/json"
2130
2329
  },
2131
2330
  body: JSON.stringify({
2132
- templateId: s.templateId || e.templateId,
2133
- designJson: s
2331
+ templateId: r.templateId || e.templateId,
2332
+ designJson: r
2134
2333
  })
2135
2334
  });
2136
2335
  if (!d.ok) {
2137
- const m = await d.json().catch(() => ({
2336
+ const g = await d.json().catch(() => ({
2138
2337
  message: "Finalization failed"
2139
2338
  }));
2140
- throw new Error(m.message || "Finalization failed");
2339
+ throw new Error(g.message || "Finalization failed");
2141
2340
  }
2142
2341
  const l = await d.json();
2143
- let c = {
2342
+ let h = {
2144
2343
  designId: l.designId,
2145
2344
  status: l.status,
2146
2345
  proofUrl: l.proofUrl ?? null,
2147
2346
  errorMessage: l.errorMessage ?? null
2148
2347
  };
2149
2348
  const u = 1500, p = 40;
2150
- let g = 0;
2151
- for (; c.status === "processing" && g < p; ) {
2152
- await new Promise((b) => setTimeout(b, u)), g++;
2153
- const m = await fetch(
2154
- `${r}/public/designs/${c.designId}/status`
2349
+ let m = 0;
2350
+ for (; h.status === "processing" && m < p; ) {
2351
+ await new Promise((b) => setTimeout(b, u)), m++;
2352
+ const g = await fetch(
2353
+ `${n}/public/designs/${h.designId}/status`
2155
2354
  );
2156
- if (!m.ok)
2355
+ if (!g.ok)
2157
2356
  throw new Error("Failed to check design status");
2158
- const v = await m.json();
2159
- c = {
2160
- designId: v.designId,
2161
- status: v.status,
2162
- proofUrl: v.proofUrl ?? null,
2163
- errorMessage: v.errorMessage ?? null
2357
+ const f = await g.json();
2358
+ h = {
2359
+ designId: f.designId,
2360
+ status: f.status,
2361
+ proofUrl: f.proofUrl ?? null,
2362
+ errorMessage: f.errorMessage ?? null
2164
2363
  };
2165
2364
  }
2166
- return c.status === "processing" && (c = { ...c, status: "failed", errorMessage: "Render timed out" }), e.onFinalize && e.onFinalize(c), c;
2167
- } catch (d) {
2168
- const l = {
2365
+ return h.status === "processing" && (h = { ...h, status: "failed", errorMessage: "Render timed out" }), e.onFinalize && e.onFinalize(h), h;
2366
+ } catch (r) {
2367
+ const d = {
2169
2368
  code: "FINALIZE_ERROR",
2170
- message: d instanceof Error ? d.message : "Unknown error",
2171
- details: d
2369
+ message: r instanceof Error ? r.message : "Unknown error",
2370
+ details: r
2172
2371
  };
2173
- throw e.onError && e.onError(l), d;
2372
+ throw e.onError && e.onError(d), r;
2174
2373
  }
2175
2374
  },
2176
- addTextLayer(s) {
2177
- if (typeof i.addTextLayer != "function")
2375
+ addTextLayer(n) {
2376
+ if (typeof t.addTextLayer != "function")
2178
2377
  throw new Error("Editor not ready: addTextLayer method not available");
2179
- i.addTextLayer(s);
2378
+ t.addTextLayer(n);
2180
2379
  },
2181
- async addImageLayer(s) {
2182
- if (typeof i.addImageLayer != "function")
2380
+ async addImageLayer(n) {
2381
+ if (typeof t.addImageLayer != "function")
2183
2382
  throw new Error("Editor not ready: addImageLayer method not available");
2184
- return i.addImageLayer(s);
2383
+ return t.addImageLayer(n);
2185
2384
  },
2186
- removeLayer(s) {
2187
- if (typeof i.removeLayer != "function")
2385
+ removeLayer(n) {
2386
+ if (typeof t.removeLayer != "function")
2188
2387
  throw new Error("Editor not ready: removeLayer method not available");
2189
- i.removeLayer(s);
2388
+ t.removeLayer(n);
2190
2389
  },
2191
- selectLayer(s) {
2192
- if (typeof i.selectLayer != "function")
2390
+ selectLayer(n) {
2391
+ if (typeof t.selectLayer != "function")
2193
2392
  throw new Error("Editor not ready: selectLayer method not available");
2194
- i.selectLayer(s);
2393
+ t.selectLayer(n);
2195
2394
  },
2196
2395
  getSelectedLayerId() {
2197
- return typeof i.getSelectedLayerId != "function" ? null : i.getSelectedLayerId();
2396
+ return typeof t.getSelectedLayerId != "function" ? null : t.getSelectedLayerId();
2198
2397
  },
2199
2398
  getActiveView() {
2200
- return typeof i.getActiveView != "function" ? null : i.getActiveView();
2399
+ return typeof t.getActiveView != "function" ? null : t.getActiveView();
2201
2400
  },
2202
2401
  getViews() {
2203
- return typeof i.getViews != "function" ? [] : i.getViews();
2402
+ return typeof t.getViews != "function" ? [] : t.getViews();
2204
2403
  },
2205
- setActiveView(s) {
2206
- if (typeof i.setActiveView != "function")
2404
+ setActiveView(n) {
2405
+ if (typeof t.setActiveView != "function")
2207
2406
  throw new Error("Editor not ready: setActiveView method not available");
2208
- i.setActiveView(s);
2407
+ t.setActiveView(n);
2209
2408
  },
2210
- setTheme(s) {
2211
- i.setAttribute("theme", s);
2409
+ setTheme(n) {
2410
+ t.setAttribute("theme", n);
2212
2411
  },
2213
- setMode(s) {
2214
- i.setAttribute("mode", s);
2412
+ setMode(n) {
2413
+ t.setAttribute("mode", n);
2215
2414
  },
2216
2415
  destroy() {
2217
- n.forEach(({ event: s, handler: r }) => {
2218
- i.removeEventListener(s, r);
2219
- }), i.parentNode && i.parentNode.removeChild(i);
2416
+ s.forEach(({ event: n, handler: r }) => {
2417
+ t.removeEventListener(n, r);
2418
+ }), t.parentNode && t.parentNode.removeChild(t);
2220
2419
  },
2221
2420
  getElement() {
2222
- return i;
2421
+ return t;
2223
2422
  }
2224
2423
  };
2225
2424
  return e.onReady && a("ready", (() => {
2226
2425
  e.onReady?.(o);
2227
- })), e.onChange && a("change", ((s) => {
2228
- const r = s.detail.design;
2426
+ })), e.onChange && a("change", ((n) => {
2427
+ const r = n.detail.design;
2229
2428
  e.onChange?.(r);
2230
- })), e.onLayerSelect && a("layer:select", ((s) => {
2231
- e.onLayerSelect?.(s.detail.layerId);
2232
- })), e.onLayerAdd && a("layer:add", ((s) => {
2233
- e.onLayerAdd?.(s.detail.layerId);
2234
- })), e.onLayerRemove && a("layer:remove", ((s) => {
2235
- e.onLayerRemove?.(s.detail.layerId);
2236
- })), e.onLayerUpdate && a("layer:update", ((s) => {
2237
- e.onLayerUpdate?.(s.detail.layerId);
2238
- })), e.onError && a("error", ((s) => {
2239
- const r = s.detail.error;
2429
+ })), e.onLayerSelect && a("layer:select", ((n) => {
2430
+ e.onLayerSelect?.(n.detail.layerId);
2431
+ })), e.onLayerAdd && a("layer:add", ((n) => {
2432
+ e.onLayerAdd?.(n.detail.layerId);
2433
+ })), e.onLayerRemove && a("layer:remove", ((n) => {
2434
+ e.onLayerRemove?.(n.detail.layerId);
2435
+ })), e.onLayerUpdate && a("layer:update", ((n) => {
2436
+ e.onLayerUpdate?.(n.detail.layerId);
2437
+ })), e.onError && a("error", ((n) => {
2438
+ const r = n.detail.error;
2240
2439
  e.onError?.(r);
2241
- })), e.onViewChange && a("view-change", ((s) => {
2242
- e.onViewChange?.(s.detail.viewName);
2243
- })), e.initialDesign && i.addEventListener(
2440
+ })), e.onViewChange && a("view-change", ((n) => {
2441
+ e.onViewChange?.(n.detail.viewName);
2442
+ })), e.onClose && a("customizer:close", (() => {
2443
+ e.onClose?.();
2444
+ })), e.onSave && a("customizer:save", ((n) => {
2445
+ e.onSave?.(n.detail);
2446
+ })), e.initialDesign && t.addEventListener(
2244
2447
  "ready",
2245
2448
  () => {
2246
- e.initialDesign && i.setDesign(e.initialDesign);
2449
+ e.initialDesign && t.setDesign(e.initialDesign);
2247
2450
  },
2248
2451
  { once: !0 }
2249
2452
  ), e.debug && (console.log("[Customizer SDK] Initialized with options:", e), console.log("[Customizer SDK] Instance:", o)), o;