@varianta/sdk 0.1.6 → 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, s = window.devicePixelRatio || 1, a = this.canvas.width / s, o = this.canvas.height / s;
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, s = window.devicePixelRatio || 1, a = this.canvas.width / s - e * 2, o = this.canvas.height / s - 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 n = a / t, r = o / i, d = Math.min(n, r), l = this.canvas.width / s, c = this.canvas.height / s, 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, s = e / i, a = t / i, o = s / this.zoom - this.pan.x, n = a / this.zoom - this.pan.y;
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
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: s } = t, { x: a, y: o, width: n, height: r } = s;
198
- if (i.type === "text") {
199
- const d = i.offset || { x: 0, y: 0 }, l = i.scale ?? 1, c = i.rotation ?? 0, u = n * l, p = r * l, g = a + n / 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 = n / r;
214
- let m, v;
215
- p > g ? (m = n, v = n / p) : (v = r, m = r * p);
216
- const b = m * c, y = v * c, x = a + n / 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,12 +230,12 @@ 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 s = this.areas[i], a = this.getContentBounds(s.id);
238
- if (a && this.isPointInBounds(e, t, a))
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
239
  return s.id;
240
240
  }
241
241
  return null;
@@ -244,12 +244,12 @@ 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 s = this.areas[i], { x: a, y: o, width: n, height: r } = s.location;
252
- if (e >= a && e <= a + n && t >= o && t <= o + r)
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
253
  return s.id;
254
254
  }
255
255
  return null;
@@ -257,52 +257,52 @@ class N {
257
257
  /**
258
258
  * Test if a point is inside rotated bounds
259
259
  */
260
- isPointInBounds(e, t, i) {
261
- const { centerX: s, centerY: a, width: o, height: n, rotation: r } = i, d = -r * Math.PI / 180, l = Math.cos(d), c = Math.sin(d), u = e - s, p = t - a, g = u * l - p * c, m = u * c + p * l;
262
- return g >= -o / 2 && g <= o / 2 && m >= -n / 2 && m <= n / 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;
271
+ const t = this.contents.get(e);
272
+ if (!t)
273
+ return null;
274
274
  const s = this.getContentBounds(e);
275
275
  if (!s)
276
276
  return null;
277
- const { centerX: a, centerY: o, width: n, height: r, rotation: d } = s, 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 = n / 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 z = m(-b, y);
289
- v.push({ type: "sw", ...z, radius: l });
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 s = this.getContentHandlePositions(i);
300
+ hitTestHandle(e, i, t) {
301
+ const s = this.getContentHandlePositions(t);
302
302
  if (!s)
303
303
  return null;
304
304
  for (const a of s) {
305
- const o = e - a.x, n = t - a.y;
305
+ const o = e - a.x, n = i - a.y;
306
306
  if (Math.sqrt(o * o + n * n) <= a.radius * 2)
307
307
  return a.type;
308
308
  }
@@ -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((s) => s.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: s, left: a } = this.safeArea, o = a, n = t, r = this.artboard.width - i, d = this.artboard.height - s;
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, z = u - w / 2, A = p - C / 2, D = u + w / 2, R = p + C / 2;
355
- (z < o || A < n || 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 s of i)
374
- s(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,49 +379,53 @@ 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, s = document.createElement("canvas");
383
- s.width = e, s.height = t;
382
+ const { width: e, height: i, backgroundColor: t } = this.artboard, s = document.createElement("canvas");
383
+ s.width = e, s.height = i;
384
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, n) => {
391
- s.toBlob((r) => {
392
- r ? o(r) : n(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);
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);
425
429
  const { width: s, height: a, backgroundColor: o } = this.artboard;
426
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();
427
431
  for (const n of this.areas)
@@ -431,8 +435,8 @@ class N {
431
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,37 +444,37 @@ 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: s, centerY: a, width: o, height: n, rotation: r } = t;
453
- i.save(), i.translate(s, a), i.rotate(r * Math.PI / 180), i.strokeStyle = "#3b82f6", i.lineWidth = 2 / this.zoom, i.setLineDash([]), i.strokeRect(-o / 2, -n / 2, o, n), 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(s, a - n / 2, s, 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, s, a) {
471
- const o = a * Math.PI / 180, n = Math.cos(o), r = Math.sin(o), d = e - i, l = t - s;
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 * n - l * r,
477
+ x: t + d * n - l * r,
474
478
  y: s + d * r + l * n
475
479
  };
476
480
  }
@@ -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: s } = this.artboard;
484
- e.save(), e.globalAlpha = t;
485
- const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = i / s;
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;
486
490
  let n, r, d, l;
487
- a > o ? (r = s, n = s * a, d = (i - n) / 2, l = 0) : (n = i, r = i / a, d = 0, l = (s - r) / 2), e.drawImage(this.bgImageElement, d, l, n, r), e.restore();
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: s, left: a } = this.safeArea, o = a, n = t, r = this.artboard.width - a - i, d = this.artboard.height - t - s;
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;
496
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: s } = e, { x: a, y: o, width: n, height: r } = i;
503
- s && (t.fillStyle = s, t.fillRect(a, o, n, 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, n, 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: s } = e, { x: a, y: o, width: n, height: r } = s, d = t.offset || { x: 0, y: 0 }, l = t.scale ?? 1, c = t.rotation ?? 0, u = a + n / 2 + d.x, p = o + r / 2 + d.y;
514
- i.save(), i.beginPath(), i.rect(a, o, n, 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 = -n / 2 + 10;
522
+ t.textAlign = "left", m = -n / 2 + 10;
519
523
  break;
520
524
  case "right":
521
- i.textAlign = "right", g = n / 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: s } = this, { location: a } = e, { x: o, y: n, width: r, height: d } = a, l = t.offset || { x: 0, y: 0 }, c = t.scale ?? 1, u = t.rotation ?? 0;
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;
538
542
  s.save(), s.beginPath(), s.rect(o, n, r, d), s.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 = n + d / 2 + l.y;
543
- s.translate(x, w), s.rotate(u * Math.PI / 180), s.drawImage(i, -b / 2, -y / 2, b, y), s.restore();
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
555
  const { opacity: s } = this.backgroundImage;
552
556
  e.save(), e.globalAlpha = s;
553
- const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = t / i;
557
+ const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = i / t;
554
558
  let n, r, d, l;
555
- a > o ? (r = i, n = i * a, d = (t - n) / 2, l = 0) : (n = t, r = t / a, d = 0, l = (i - r) / 2), e.drawImage(this.bgImageElement, d, l, n, r), e.restore();
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: s } = t, { x: a, y: o, width: n, height: r } = i;
564
+ drawAreaContentOnContext(e, i) {
565
+ const { location: t, backgroundColor: s } = i, { x: a, y: o, width: n, height: r } = t;
562
566
  s && (e.fillStyle = s, e.fillRect(a, o, n, r));
563
- const d = this.contents.get(t.id);
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 + n / 2 + l.x, g = o + r / 2 + l.y;
569
- e.save(), e.beginPath(), e.rect(a, o, n, 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 = -n / 2 + 10;
577
+ e.textAlign = "left", g = -n / 2 + 10;
574
578
  break;
575
579
  case "right":
576
- e.textAlign = "right", m = n / 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;
592
+ const h = d.offset || { x: 0, y: 0 }, u = d.scale ?? 1, p = d.rotation ?? 0;
589
593
  e.save(), e.beginPath(), e.rect(a, o, n, r), e.clip();
590
- const g = l.naturalWidth / l.naturalHeight, m = n / r;
591
- let v, b;
592
- g > m ? (v = n, b = n / g) : (b = r, v = r * g);
593
- const y = v * u, x = b * u, w = a + n / 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();
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,14 +617,14 @@ 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 T(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;
623
- class V {
626
+ const V = 50;
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({
626
630
  state: this.cloneState(e),
@@ -634,9 +638,9 @@ class V {
634
638
  /**
635
639
  * Update state and save to history
636
640
  */
637
- setState(e, t, i = !1) {
641
+ setState(e, i, t = !1) {
638
642
  const s = this.state;
639
- this.state = { ...this.state, ...e }, !i && !this.isRestoring && this.saveToHistory(s, t), this.notifyListeners();
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 V {
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 V {
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 V {
694
698
  return JSON.parse(JSON.stringify(e));
695
699
  }
696
700
  }
697
- class k {
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 k {
710
714
  getContent(e) {
711
715
  return this.contents.get(e);
712
716
  }
713
- setTextContent(e, t) {
714
- const i = this.contents.get(e), s = this.areas.find((o) => o.id === e), a = s ? E(s) : {
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 k {
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) {
728
+ setImageContent(e, i, t) {
725
729
  const s = {
726
730
  type: "image",
727
- dataUrl: t,
728
- filename: i
731
+ dataUrl: i,
732
+ filename: t
729
733
  };
730
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 s = Math.max(0.1, Math.min(5, t));
744
- this.contents.set(e, { ...i, scale: s }), 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 s = t % 360;
750
- s < 0 && (s += 360), this.contents.set(e, { ...i, rotation: s }), 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 k {
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 = (s) => {
802
- t(s), 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 s = Array.from(i);
826
+ emit(e, i) {
827
+ const t = this.listeners.get(e);
828
+ if (t) {
829
+ const s = Array.from(t);
826
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 O 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,30 +862,30 @@ class O 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()), s = this.getDistance(t, i), a = this.getCenter(t, i), o = s / 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,
@@ -892,29 +896,29 @@ class O extends I {
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, s = t.y - e.y;
906
- return Math.sqrt(i * i + s * s);
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 B extends I {
916
- constructor(e, t, i, s) {
917
- super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine = t, this.getContents = i, this.getSelectedAreaId = s, this.gestureHandler = new O(), 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 }) => {
@@ -927,17 +931,17 @@ class B extends I {
927
931
  });
928
932
  });
929
933
  }
930
- getCanvasPosition(e, t) {
931
- const i = this.canvas.getBoundingClientRect(), s = window.devicePixelRatio || 1, a = (e - i.left) * s, o = (t - i.top) * s;
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 B 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 B 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,16 +978,16 @@ class B 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 n = i.get(t), r = this.engine.getContentBounds(t);
985
+ const n = t.get(i), r = this.engine.getContentBounds(i);
982
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,
990
+ areaId: i,
987
991
  startRotation: n.rotation ?? 0,
988
992
  centerPoint: { x: r.centerX, y: r.centerY },
989
993
  startAngle: d
@@ -991,7 +995,7 @@ class B extends I {
991
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
1000
  startScale: n.scale ?? 1,
997
1001
  startMousePos: e,
@@ -1002,7 +1006,7 @@ class B extends I {
1002
1006
  const s = this.engine.hitTestContent(e.x, e.y);
1003
1007
  if (s) {
1004
1008
  if (this.emit("area:select", { areaId: s }), this.engine.isPositioningAllowed(s)) {
1005
- const o = i.get(s)?.offset ?? { x: 0, y: 0 };
1009
+ const o = t.get(s)?.offset ?? { x: 0, y: 0 };
1006
1010
  return this.interactionState = {
1007
1011
  mode: "dragging",
1008
1012
  areaId: s,
@@ -1017,34 +1021,32 @@ class B extends I {
1017
1021
  }
1018
1022
  processPointerMove(e) {
1019
1023
  if (this.interactionState.mode === "dragging") {
1020
- const { areaId: t, startOffset: i, startMousePos: s } = this.interactionState, a = e.x - s.x, o = e.y - s.y;
1021
- let n = {
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
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: n });
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: s, pivotPoint: a } = this.interactionState, o = Math.sqrt(
1036
+ const { areaId: i, startScale: t, startMousePos: s, pivotPoint: a } = this.interactionState, o = Math.sqrt(
1035
1037
  Math.pow(s.x - a.x, 2) + Math.pow(s.y - a.y, 2)
1036
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 = n / 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: s, startAngle: a } = this.interactionState, o = Math.atan2(e.y - s.y, e.x - s.x) * (180 / Math.PI);
1047
- let n = i + (o - a);
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);
1048
1050
  n = n % 360, n < 0 && (n += 360);
1049
1051
  const r = [0, 90, 180, 270, 360];
1050
1052
  for (const d of r)
@@ -1052,7 +1054,7 @@ class B extends I {
1052
1054
  n = d === 360 ? 0 : d;
1053
1055
  break;
1054
1056
  }
1055
- this.emit("content:rotate", { areaId: t, rotation: n });
1057
+ this.emit("content:rotate", { areaId: i, rotation: n });
1056
1058
  return;
1057
1059
  }
1058
1060
  this.updateCursor(e);
@@ -1061,14 +1063,14 @@ class B 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 s = this.engine.hitTestHandle(e.x, e.y, t);
1068
- s ? i = s === "rotate" ? "crosshair" : s === "nw" || s === "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,71 +1079,133 @@ class B 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 F {
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 s = `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(() => ({}));
1179
+ if (t.status === 400) {
1180
+ const a = await t.json().catch(() => ({}));
1129
1181
  s += a.error || "Invalid design data.";
1130
- } else i.status === 429 ? s += "Rate limit exceeded." : i.status >= 500 && (s += "Server error.");
1182
+ } else t.status === 429 ? s += "Rate limit exceeded." : t.status >= 500 && (s += "Server error.");
1131
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);
1204
+ const t = new FormData();
1205
+ t.append("shopId", i), t.append("file", e);
1142
1206
  const s = await fetch(`${this.baseUrl}/assets/upload`, {
1143
1207
  method: "POST",
1144
- body: i
1208
+ body: t
1145
1209
  });
1146
1210
  if (!s.ok) {
1147
1211
  let a = `Failed to upload asset (${s.status} ${s.statusText})
@@ -1154,70 +1218,70 @@ class F {
1154
1218
  throw new Error(a);
1155
1219
  }
1156
1220
  return s.json();
1157
- } catch (i) {
1158
- throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error uploading asset") : i;
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);
1236
+ function c(v, e, ...i) {
1237
+ const t = document.createElement(v);
1174
1238
  return e && Object.entries(e).forEach(([s, a]) => {
1175
- s === "class" ? i.className = a : s in i ? i[s] = a : i.setAttribute(s, String(a));
1176
- }), t.forEach((s) => {
1177
- typeof s == "string" ? i.appendChild(document.createTextNode(s)) : i.appendChild(s);
1178
- }), i;
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 $(f, e) {
1181
- let t = null, i = null;
1244
+ function q(v, e) {
1245
+ let i = null, t = null;
1182
1246
  const s = (...a) => {
1183
- i = a, t && clearTimeout(t), t = setTimeout(() => {
1184
- i && f(...i), t = null, i = null;
1247
+ t = a, i && clearTimeout(i), i = setTimeout(() => {
1248
+ t && v(...t), i = null, t = null;
1185
1249
  }, e);
1186
1250
  };
1187
1251
  return s.flush = () => {
1188
- t && (clearTimeout(t), t = null), i && (f(...i), i = null);
1252
+ i && (clearTimeout(i), i = null), t && (v(...t), t = null);
1189
1253
  }, s.cancel = () => {
1190
- t && (clearTimeout(t), t = null), i = null;
1254
+ i && (clearTimeout(i), i = null), t = null;
1191
1255
  }, s;
1192
1256
  }
1193
- class H extends I {
1257
+ class H extends E {
1194
1258
  constructor() {
1195
- super(), this.saveEnabled = !0, 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 s = h("div", { class: "zoom-toolbar-spacer" });
1214
- this.element.appendChild(s), this.closeBtn = h("button", {
1215
- class: "zoom-btn zoom-close-btn",
1216
- title: "Close editor"
1217
- }, "✕ Close"), this.closeBtn.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(this.closeBtn), 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", {
1218
1279
  class: "zoom-btn zoom-save-btn",
1219
1280
  title: "Save customization"
1220
- }, "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);
1221
1285
  }
1222
1286
  setZoom(e) {
1223
1287
  this.percentLabel.textContent = `${Math.round(e * 100)}%`;
@@ -1231,14 +1295,14 @@ class H extends I {
1231
1295
  setSaveButtonEnabled(e) {
1232
1296
  this.saveEnabled = e, e || (this.saveBtn.style.display = "none");
1233
1297
  }
1234
- setSaveDisabled(e, t) {
1235
- this.saveBtn.disabled = e, t && (this.saveBtn.textContent = t);
1298
+ setSaveDisabled(e, i) {
1299
+ this.saveBtn.disabled = e, i && (this.saveBtn.textContent = i);
1236
1300
  }
1237
1301
  setViews(e) {
1238
1302
  this.viewSelect.innerHTML = "";
1239
- for (const t of e) {
1240
- const i = document.createElement("option");
1241
- 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);
1242
1306
  }
1243
1307
  this.viewSelect.style.display = e.length > 1 ? "" : "none";
1244
1308
  }
@@ -1255,140 +1319,140 @@ const Z = [
1255
1319
  { label: "Georgia", value: "Georgia" },
1256
1320
  { label: "Times New Roman", value: "Times New Roman" },
1257
1321
  { label: "Courier New", value: "Courier New" }
1258
- ], q = [
1322
+ ], j = [
1259
1323
  { label: "L", value: "left" },
1260
1324
  { label: "C", value: "center" },
1261
1325
  { label: "R", value: "right" }
1262
- ], P = ["image/png", "image/jpeg", "image/webp", "image/svg+xml"], Y = 15 * 1024 * 1024;
1263
- class L extends I {
1326
+ ], L = ["image/png", "image/jpeg", "image/webp", "image/svg+xml"], Y = 15 * 1024 * 1024;
1327
+ class A extends E {
1264
1328
  constructor(e) {
1265
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;
1266
- const t = S(e), i = T(e);
1267
- 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) => {
1268
1332
  r.target.tagName === "INPUT" || r.target.tagName === "SELECT" || r.target.tagName === "BUTTON" || this.emit("select", { areaId: e.id });
1269
1333
  });
1270
- const s = 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);
1271
1335
  if (a.appendChild(o), e.required) {
1272
- const r = h("span", { class: "area-card-badge" }, "Required");
1336
+ const r = c("span", { class: "area-card-badge" }, "Required");
1273
1337
  a.appendChild(r);
1274
1338
  }
1275
1339
  s.appendChild(a);
1276
- const n = h("button", {
1340
+ const n = c("button", {
1277
1341
  class: "area-card-reset-btn",
1278
1342
  title: "Reset content"
1279
1343
  }, "Reset");
1280
1344
  if (n.addEventListener("click", (r) => {
1281
1345
  r.stopPropagation(), this.emit("clear", { areaId: e.id });
1282
- }), s.appendChild(n), this.element.appendChild(s), t && i) {
1283
- 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", {
1284
1348
  class: "mode-btn mode-btn-active"
1285
1349
  }, "Text");
1286
1350
  d.dataset.mode = "text";
1287
- const l = h("button", { class: "mode-btn" }, "Image");
1288
- l.dataset.mode = "image", d.addEventListener("click", (c) => {
1289
- 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 });
1290
- }), l.addEventListener("click", (c) => {
1291
- 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 });
1292
1356
  }), r.appendChild(d), r.appendChild(l), this.element.appendChild(r);
1293
1357
  }
1294
- 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();
1295
1359
  }
1296
1360
  setMode(e) {
1297
1361
  this.mode = e, this.renderContent();
1298
1362
  }
1299
1363
  setContent(e) {
1300
- const t = this.currentContent?.type;
1301
- this.currentContent = e, e?.type === "image" && (this.mode = "image"), this.element.querySelectorAll(".mode-btn").forEach((i) => {
1302
- const s = i;
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;
1303
1367
  s.classList.toggle("mode-btn-active", s.dataset.mode === this.mode);
1304
- }), e?.type !== t || e?.type === "image" || e === void 0 ? this.renderContent() : e?.type === "text" && this.updateTextValues(e), this.renderTransforms();
1368
+ }), e?.type !== i || e?.type === "image" || e === void 0 ? this.renderContent() : e?.type === "text" && this.updateTextValues(e), this.renderTransforms();
1305
1369
  }
1306
1370
  setSelected(e) {
1307
1371
  this.isSelected = e, this.element.classList.toggle("area-card-selected", e), this.renderTransforms();
1308
1372
  }
1309
1373
  updateTextValues(e) {
1310
- 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) => {
1311
- const i = t, s = i.title === "Left" ? "left" : i.title === "Center" ? "center" : "right";
1312
- i.classList.toggle("align-btn-active", s === 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);
1313
1377
  });
1314
1378
  }
1315
1379
  renderContent() {
1316
1380
  this.contentContainer.innerHTML = "", this.textInputEl = null, this.fontSelectEl = null, this.sizeInputEl = null, this.colorInputEl = null, this.alignGroupEl = null;
1317
- const e = S(this.area), t = T(this.area), i = !t || e && t && this.mode === "text", s = !e || e && t && this.mode === "image";
1318
- e && i && this.renderTextControls(), t && s && 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();
1319
1383
  }
1320
1384
  renderTextControls() {
1321
- 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", {
1322
1386
  class: "area-input-text",
1323
1387
  type: "text",
1324
1388
  placeholder: this.area.placeholder || "Enter text..."
1325
1389
  });
1326
- i.value = t.text, this.area.textOptions?.maxLength && (i.maxLength = this.area.textOptions.maxLength), i.addEventListener("input", () => {
1327
- this.emit("text:change", { areaId: this.area.id, updates: { text: i.value } });
1328
- }), this.contentContainer.appendChild(i), this.textInputEl = i;
1329
- const s = 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" });
1330
1394
  for (const l of Z) {
1331
- const c = h("option");
1332
- 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);
1333
1397
  }
1334
1398
  a.addEventListener("change", () => {
1335
1399
  this.emit("text:change", { areaId: this.area.id, updates: { font: a.value } });
1336
1400
  }), s.appendChild(a), this.fontSelectEl = a;
1337
- const o = h("input", {
1401
+ const o = c("input", {
1338
1402
  class: "area-input-size",
1339
1403
  type: "number"
1340
1404
  });
1341
- 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", () => {
1342
1406
  this.emit("text:change", {
1343
1407
  areaId: this.area.id,
1344
1408
  updates: { size: parseInt(o.value, 10) || 24 }
1345
1409
  });
1346
1410
  }), s.appendChild(o), this.sizeInputEl = o, this.contentContainer.appendChild(s);
1347
- const n = h("div", { class: "area-input-row" }), r = h("div", { class: "area-input-align" });
1348
- for (const l of q) {
1349
- const c = h("button", {
1350
- class: `align-btn${t.align === l.value ? " align-btn-active" : ""}`,
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" : ""}`,
1351
1415
  title: l.label === "L" ? "Left" : l.label === "C" ? "Center" : "Right"
1352
1416
  }, l.label);
1353
- c.addEventListener("click", (u) => {
1354
- 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");
1355
- }), 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);
1356
1420
  }
1357
1421
  n.appendChild(r), this.alignGroupEl = r;
1358
- const d = h("input", {
1422
+ const d = c("input", {
1359
1423
  class: "area-input-color",
1360
1424
  type: "color"
1361
1425
  });
1362
- d.value = t.color, d.addEventListener("input", () => {
1426
+ d.value = i.color, d.addEventListener("input", () => {
1363
1427
  this.emit("text:change", { areaId: this.area.id, updates: { color: d.value } });
1364
1428
  }), n.appendChild(d), this.colorInputEl = d, this.contentContainer.appendChild(n);
1365
1429
  }
1366
1430
  renderImageControls() {
1367
1431
  const e = this.currentContent;
1368
1432
  if (e?.type === "image") {
1369
- const t = h("div", { class: "area-image-preview" }), i = h("img", { class: "area-image-thumb" });
1370
- i.src = e.dataUrl, i.alt = e.filename || "Uploaded image", t.appendChild(i);
1371
- const s = h("div", { class: "area-image-info" });
1372
- s.appendChild(h("span", { class: "area-image-name" }, e.filename || "Image"));
1373
- 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");
1374
1438
  a.addEventListener("click", (o) => {
1375
1439
  o.stopPropagation(), this.emit("clear", { areaId: this.area.id });
1376
- }), s.appendChild(a), t.appendChild(s), this.contentContainer.appendChild(t);
1440
+ }), s.appendChild(a), i.appendChild(s), this.contentContainer.appendChild(i);
1377
1441
  } else {
1378
- const t = h("button", { class: "area-upload-btn" }, "Upload Image"), i = h("input", { type: "file" });
1379
- i.accept = P.join(","), i.style.display = "none";
1380
- const s = h("div", { class: "area-validation-error" });
1381
- s.style.display = "none", i.addEventListener("change", () => {
1382
- 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];
1383
1447
  if (!a) return;
1384
- if (!P.includes(a.type)) {
1448
+ if (!L.includes(a.type)) {
1385
1449
  const n = "Only PNG, JPEG, WebP, and SVG images are accepted";
1386
- s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), i.value = "";
1450
+ s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), t.value = "";
1387
1451
  return;
1388
1452
  }
1389
1453
  if (a.size > Y) {
1390
1454
  const n = "File must be smaller than 15MB";
1391
- s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), i.value = "";
1455
+ s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), t.value = "";
1392
1456
  return;
1393
1457
  }
1394
1458
  s.style.display = "none";
@@ -1396,102 +1460,102 @@ class L extends I {
1396
1460
  o.onload = () => {
1397
1461
  const n = o.result;
1398
1462
  this.emit("image:change", { areaId: this.area.id, dataUrl: n, filename: a.name });
1399
- }, o.readAsDataURL(a), i.value = "";
1400
- }), t.addEventListener("click", (a) => {
1401
- a.stopPropagation(), i.click();
1402
- }), this.contentContainer.appendChild(t), this.contentContainer.appendChild(i), this.contentContainer.appendChild(s);
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);
1403
1467
  }
1404
1468
  }
1405
1469
  renderTransforms() {
1406
1470
  this.transformContainer.innerHTML = "";
1407
1471
  const e = this.currentContent;
1408
1472
  if (!this.isSelected || !e) return;
1409
- const t = e.type === "text" ? this.area.textOptions?.allowPositioning : this.area.imageOptions?.allowPositioning, i = e.type === "image" && this.area.imageOptions?.allowScaling, s = e.type === "image" && this.area.imageOptions?.allowRotation;
1410
- if (!t && !i && !s) return;
1411
- 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" });
1412
1476
  this.transformContainer.appendChild(a);
1413
- const o = h("div", { class: "transform-header" });
1414
- o.appendChild(h("span", { class: "transform-title" }, "Transform"));
1415
- const n = h("button", { class: "transform-reset-btn" }, "Reset");
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");
1416
1480
  n.addEventListener("click", (d) => {
1417
- 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 }), s && this.emit("rotation:change", { areaId: this.area.id, rotation: 0 });
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 });
1418
1482
  }), o.appendChild(n), this.transformContainer.appendChild(o);
1419
1483
  const r = e.offset ?? { x: 0, y: 0 };
1420
- if (t) {
1421
- const d = h("div", { class: "area-input-row" }), l = h("label", { class: "transform-label" }, "X"), c = h("input", { class: "transform-input", type: "number" });
1422
- 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", () => {
1423
1487
  this.emit("offset:change", {
1424
1488
  areaId: this.area.id,
1425
- offset: { ...r, x: parseInt(c.value, 10) || 0 }
1489
+ offset: { ...r, x: parseInt(h.value, 10) || 0 }
1426
1490
  });
1427
1491
  });
1428
- 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" });
1429
1493
  p.value = String(Math.round(r.y)), p.addEventListener("change", () => {
1430
1494
  this.emit("offset:change", {
1431
1495
  areaId: this.area.id,
1432
1496
  offset: { ...r, y: parseInt(p.value, 10) || 0 }
1433
1497
  });
1434
- }), 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);
1435
1499
  }
1436
- if (i && e.type === "image") {
1437
- 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" });
1438
1502
  u.min = "10", u.max = "500", u.step = "5", u.value = String(Math.round(d * 100)), u.addEventListener("input", () => {
1439
1503
  const p = parseInt(u.value, 10) / 100;
1440
- c.textContent = `Scale: ${Math.round(p * 100)}%`, this.emit("scale:change", { areaId: this.area.id, scale: p });
1441
- }), 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);
1442
1506
  }
1443
1507
  if (s && e.type === "image") {
1444
- 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" });
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" });
1445
1509
  u.min = "0", u.max = "360", u.step = "1", u.value = String(Math.round(d)), u.addEventListener("input", () => {
1446
1510
  const p = parseInt(u.value, 10);
1447
- c.textContent = `Rotation: ${p}°`, this.emit("rotation:change", { areaId: this.area.id, rotation: p });
1448
- }), 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);
1449
1513
  }
1450
1514
  }
1451
1515
  getElement() {
1452
1516
  return this.element;
1453
1517
  }
1454
1518
  }
1455
- class j extends I {
1519
+ class J extends E {
1456
1520
  constructor(e) {
1457
1521
  super(), this.cards = /* @__PURE__ */ new Map(), this.isMobile = !1, this.boundEscapeHandler = (s) => {
1458
1522
  s.key === "Escape" && this.emit("dismiss", void 0);
1459
- }, this.backdrop = h("div", { class: "area-panel-backdrop" }), this.backdrop.addEventListener("click", () => {
1523
+ }, this.backdrop = c("div", { class: "area-panel-backdrop" }), this.backdrop.addEventListener("click", () => {
1460
1524
  this.emit("dismiss", void 0);
1461
- }), this.element = h("div", { class: "area-panel" });
1462
- const t = h("div", { class: "area-panel-header" });
1463
- t.appendChild(h("span", { class: "area-panel-title" }, "Customize"));
1464
- const i = h("button", { class: "area-panel-close-btn", title: "Close" }, "×");
1465
- 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", () => {
1466
1530
  this.emit("dismiss", void 0);
1467
- }), t.appendChild(i), this.element.appendChild(t), this.panelContent = h("div", { class: "area-panel-content" }), e.length === 0) {
1468
- const s = h("p", { class: "area-panel-empty" }, "No customization areas defined.");
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.");
1469
1533
  this.panelContent.appendChild(s);
1470
1534
  } else
1471
1535
  for (const s of e) {
1472
- const a = new L(s);
1536
+ const a = new A(s);
1473
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());
1474
1538
  }
1475
1539
  this.element.appendChild(this.panelContent);
1476
1540
  }
1477
1541
  setAreas(e) {
1478
1542
  if (this.cards.clear(), this.panelContent.innerHTML = "", e.length === 0) {
1479
- const t = h("p", { class: "area-panel-empty" }, "No customization areas defined.");
1480
- this.panelContent.appendChild(t);
1543
+ const i = c("p", { class: "area-panel-empty" }, "No customization areas defined.");
1544
+ this.panelContent.appendChild(i);
1481
1545
  } else
1482
- for (const t of e) {
1483
- const i = new L(t);
1484
- this.cards.set(t.id, i), i.on("text:change", (s) => this.emit("text:change", s)), i.on("image:change", (s) => this.emit("image:change", s)), i.on("clear", (s) => this.emit("clear", s)), i.on("select", (s) => this.emit("select", s)), i.on("offset:change", (s) => this.emit("offset:change", s)), i.on("scale:change", (s) => this.emit("scale:change", s)), i.on("rotation:change", (s) => this.emit("rotation:change", s)), i.on("validation:error", (s) => this.emit("validation:error", s)), 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());
1485
1549
  }
1486
1550
  }
1487
1551
  setContents(e) {
1488
- for (const [t, i] of this.cards)
1489
- i.setContent(e.get(t));
1552
+ for (const [i, t] of this.cards)
1553
+ t.setContent(e.get(i));
1490
1554
  }
1491
1555
  setSelectedArea(e) {
1492
- for (const [t, i] of this.cards) {
1493
- const s = t === e;
1494
- i.setSelected(s), i.getElement().style.display = s ? "" : "none";
1556
+ for (const [i, t] of this.cards) {
1557
+ const s = i === e;
1558
+ t.setSelected(s), t.getElement().style.display = s ? "" : "none";
1495
1559
  }
1496
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));
1497
1561
  }
@@ -1507,13 +1571,13 @@ class j extends I {
1507
1571
  }
1508
1572
  class X {
1509
1573
  constructor(e = window) {
1510
- this.target = e, this.commands = [], this.isEnabled = !0, this.handleKeyDown = (t) => {
1574
+ this.target = e, this.commands = [], this.isEnabled = !0, this.handleKeyDown = (i) => {
1511
1575
  if (!this.isEnabled) return;
1512
- const i = t.target;
1513
- if (!(i.tagName === "INPUT" || i.tagName === "TEXTAREA" || i.isContentEditable)) {
1576
+ const t = i.target;
1577
+ if (!(t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) {
1514
1578
  for (const s of this.commands)
1515
- if (this.matchesCommand(t, s)) {
1516
- t.preventDefault(), s.handler(t);
1579
+ if (this.matchesCommand(i, s)) {
1580
+ i.preventDefault(), s.handler(i);
1517
1581
  break;
1518
1582
  }
1519
1583
  }
@@ -1524,8 +1588,8 @@ class X {
1524
1588
  */
1525
1589
  register(e) {
1526
1590
  return this.commands.push(e), () => {
1527
- const t = this.commands.indexOf(e);
1528
- t !== -1 && this.commands.splice(t, 1);
1591
+ const i = this.commands.indexOf(e);
1592
+ i !== -1 && this.commands.splice(i, 1);
1529
1593
  };
1530
1594
  }
1531
1595
  /**
@@ -1549,8 +1613,8 @@ class X {
1549
1613
  /**
1550
1614
  * Check if event matches a command
1551
1615
  */
1552
- matchesCommand(e, t) {
1553
- 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);
1554
1618
  }
1555
1619
  /**
1556
1620
  * Cleanup
@@ -1562,28 +1626,28 @@ class X {
1562
1626
  function W() {
1563
1627
  return /Mac|iPod|iPhone|iPad/.test(navigator.platform);
1564
1628
  }
1565
- function J() {
1629
+ function G() {
1566
1630
  return W() ? "meta" : "ctrl";
1567
1631
  }
1568
- function G(f, e, t, i) {
1569
- const s = f / t, a = e / i;
1632
+ function K(v, e, i, t) {
1633
+ const s = v / i, a = e / t;
1570
1634
  return Math.min(s, a);
1571
1635
  }
1572
- function K(f, e, t = 8, i = 10, s = 300) {
1573
- const a = G(f, e, t, i), o = a >= s;
1636
+ function Q(v, e, i = 8, t = 10, s = 300) {
1637
+ const a = K(v, e, i, t), o = a >= s;
1574
1638
  return {
1575
1639
  actualDPI: Math.round(a),
1576
1640
  targetDPI: s,
1577
- width: f,
1641
+ width: v,
1578
1642
  height: e,
1579
1643
  passed: o,
1580
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.`
1581
1645
  };
1582
1646
  }
1583
- const M = ':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;
1584
- 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 {
1585
1649
  constructor() {
1586
- 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 = $(() => {
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(() => {
1587
1651
  this.dispatchEvent(
1588
1652
  new CustomEvent("change", {
1589
1653
  detail: { design: this.getDesign() },
@@ -1606,15 +1670,15 @@ class ee extends HTMLElement {
1606
1670
  disconnectedCallback() {
1607
1671
  this.cleanup();
1608
1672
  }
1609
- attributeChangedCallback(e, t, i) {
1610
- t !== i && (e === "template-id" && this.isReady && i && this.loadTemplate(i), e === "product-id" && this.isReady && i && this.loadProduct(i), e === "show-close-button" && this.zoomToolbar && this.zoomToolbar.setCloseButtonVisible(i !== "false"), e === "show-save-button" && this.zoomToolbar && this.zoomToolbar.setSaveButtonEnabled(i !== "false"));
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"));
1611
1675
  }
1612
1676
  // ─── Public API ───
1613
1677
  getDesign() {
1614
1678
  if (!this.currentTemplate) throw new Error("No design loaded");
1615
1679
  return {
1616
1680
  templateId: this.currentTemplate.metadata.id,
1617
- engineVersion: Q,
1681
+ engineVersion: _,
1618
1682
  contents: this.contentManager.toJSON(),
1619
1683
  userData: {}
1620
1684
  };
@@ -1663,79 +1727,188 @@ class ee extends HTMLElement {
1663
1727
  }
1664
1728
  setActiveView(e) {
1665
1729
  if (this.activeViewName === e) return;
1666
- const t = this.productViews.find((i) => i.viewName === e);
1667
- if (!t) throw new Error(`View not found: ${e}`);
1668
- 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);
1669
1733
  }
1670
- setAreaContent(e, t) {
1671
- 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();
1672
1736
  }
1673
1737
  getAreaContent(e) {
1674
1738
  return this.contentManager.getContent(e);
1675
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
+ }
1676
1789
  async finalize() {
1677
- const e = this.engine.checkSafeAreaViolations();
1678
- if (e.length > 0) {
1679
- const a = this.currentTemplate?.areas || [], o = e.map((n) => a.find((r) => r.id === n)?.name || n).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(", ");
1680
1796
  if (!await this.showConfirmation(
1681
1797
  "Content outside safe area",
1682
- `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?`,
1683
1799
  "Cancel",
1684
1800
  "Proceed Anyway"
1685
1801
  ))
1686
1802
  throw new Error("Finalization cancelled by user");
1687
1803
  }
1688
- const t = this.getDesign(), i = this.getAttribute("store-id") || "demo-shop", s = 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);
1689
1862
  return this.dispatchEvent(
1690
1863
  new CustomEvent("customizer:finalize", {
1691
1864
  detail: {
1692
- designId: s.designId,
1693
- proofUrl: s.proofUrl,
1694
- templateId: t.templateId,
1695
- designJson: t,
1696
- status: s.status
1865
+ designId: a.designId,
1866
+ proofUrl: a.proofUrl,
1867
+ templateId: s.templateId,
1868
+ designJson: s,
1869
+ status: a.status
1697
1870
  },
1698
1871
  bubbles: !0,
1699
1872
  composed: !0
1700
1873
  })
1701
- ), s;
1874
+ ), a;
1702
1875
  }
1703
1876
  // ─── Initialization ───
1704
1877
  renderLoading() {
1705
1878
  const e = document.createElement("style");
1706
- e.textContent = M, this.shadow.appendChild(e);
1707
- const t = h(
1879
+ e.textContent = k, this.shadow.appendChild(e);
1880
+ const i = c(
1708
1881
  "div",
1709
1882
  { class: "loading-container" },
1710
- h("div", { class: "loading-spinner" })
1883
+ c("div", { class: "loading-spinner" })
1711
1884
  );
1712
- this.shadow.appendChild(t);
1885
+ this.shadow.appendChild(i);
1713
1886
  }
1714
1887
  async initialize() {
1715
1888
  const e = this.getAttribute("api-url") || "http://localhost:4000";
1716
- this.apiClient = new F(`${e.replace(/\/+$/, "")}/public`);
1717
- const t = this.getAttribute("product-id"), i = this.getAttribute("template-id");
1718
- 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)
1719
1892
  throw new Error("Either template-id or product-id attribute is required");
1720
- 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 }));
1721
1894
  }
1722
1895
  async loadTemplate(e) {
1723
1896
  if (this.loadingTemplateId !== e) {
1724
1897
  this.loadingTemplateId = e;
1725
1898
  try {
1726
- const t = await this.apiClient.getTemplate(e);
1899
+ const i = await this.apiClient.getTemplate(e);
1727
1900
  if (this.loadingTemplateId !== e) return;
1728
- await this.loadTemplateData(t), this.loadingTemplateId = null;
1729
- } catch (t) {
1730
- 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}`);
1731
1904
  }
1732
1905
  }
1733
1906
  }
1734
1907
  async loadTemplateData(e) {
1735
1908
  this.currentTemplate = e;
1736
- const t = e.areas || [];
1737
- this.contentManager = new k(t);
1738
- const i = {
1909
+ const i = e.areas || [];
1910
+ this.contentManager = new P(i);
1911
+ const t = {
1739
1912
  template: e,
1740
1913
  contents: this.contentManager.toJSON(),
1741
1914
  selectedAreaId: null,
@@ -1744,16 +1917,16 @@ class ee extends HTMLElement {
1744
1917
  isDirty: !1,
1745
1918
  warnings: []
1746
1919
  };
1747
- this.stateManager = new V(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({
1748
1921
  width: e.artboard.width,
1749
1922
  height: e.artboard.height,
1750
1923
  backgroundColor: e.artboard.backgroundColor ?? "#ffffff"
1751
- }), this.engine.setAreas(t), this.engine.setContents(this.contentManager.getContents()), e.backgroundImage && this.engine.setBackgroundImage(e.backgroundImage);
1924
+ }), this.engine.setAreas(i), this.engine.setContents(this.contentManager.getContents()), e.backgroundImage && this.engine.setBackgroundImage(e.backgroundImage);
1752
1925
  const s = e.artboard.safeArea ?? e.safeArea;
1753
1926
  s && this.engine.setSafeArea(s), this.engine.start(), this.engine.fitToView(), this.stateManager.update({
1754
1927
  zoom: this.engine.getZoom(),
1755
1928
  pan: this.engine.getPan()
1756
- }), this.zoomToolbar.setZoom(this.engine.getZoom()), this.interaction = new B(
1929
+ }), this.zoomToolbar.setZoom(this.engine.getZoom()), this.interaction = new F(
1757
1930
  this.canvas,
1758
1931
  this.engine,
1759
1932
  () => this.contentManager.getContents(),
@@ -1771,38 +1944,38 @@ class ee extends HTMLElement {
1771
1944
  }
1772
1945
  async loadProduct(e) {
1773
1946
  try {
1774
- const t = await this.apiClient.getProduct(e);
1775
- if (t.views.length === 0)
1947
+ const i = await this.apiClient.getProduct(e);
1948
+ if (i.views.length === 0)
1776
1949
  throw new Error("Product has no active templates");
1777
- this.productViews = t.views, this.perViewContents.clear();
1778
- const i = t.views.find((s) => s.isDefault) || t.views[0];
1779
- 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));
1780
- } catch (t) {
1781
- 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}`);
1782
1955
  }
1783
1956
  }
1784
1957
  switchToView(e) {
1785
1958
  this.activeViewName && this.contentManager && this.perViewContents.set(this.activeViewName, this.contentManager.toJSON()), this.activeViewName = e.viewName, this.zoomToolbar?.setActiveView(e.viewName);
1786
- const t = e.template.templateJson;
1787
- this.currentTemplate = t;
1788
- const i = t.areas || [];
1789
- this.contentManager = new k(i);
1959
+ const i = e.template.templateJson;
1960
+ this.currentTemplate = i;
1961
+ const t = i.areas || [];
1962
+ this.contentManager = new P(t);
1790
1963
  const s = this.perViewContents.get(e.viewName);
1791
1964
  s && this.contentManager.fromJSON(s), this.engine.setArtboard({
1792
- width: t.artboard.width,
1793
- height: t.artboard.height,
1794
- backgroundColor: t.artboard.backgroundColor ?? "#ffffff"
1795
- }), this.engine.setAreas(i), this.engine.setContents(this.contentManager.getContents()), this.engine.setBackgroundImage(t.backgroundImage ?? void 0);
1796
- const a = t.artboard.safeArea ?? t.safeArea;
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;
1797
1970
  this.engine.setSafeArea(a ?? null), this.engine.fitToView(), this.stateManager.setState({
1798
- template: t,
1971
+ template: i,
1799
1972
  contents: this.contentManager.toJSON(),
1800
1973
  selectedAreaId: null,
1801
1974
  isDirty: this.stateManager.getState().isDirty,
1802
1975
  zoom: this.engine.getZoom(),
1803
1976
  pan: this.engine.getPan(),
1804
1977
  warnings: []
1805
- }), 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(
1806
1979
  new CustomEvent("view-change", {
1807
1980
  detail: { viewName: e.viewName },
1808
1981
  bubbles: !0,
@@ -1810,10 +1983,10 @@ class ee extends HTMLElement {
1810
1983
  })
1811
1984
  );
1812
1985
  }
1813
- buildUI(e, t) {
1986
+ buildUI(e, i) {
1814
1987
  this.shadow.innerHTML = "";
1815
- const i = document.createElement("style");
1816
- i.textContent = M, this.shadow.appendChild(i), this.container = h("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 }) => {
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 }) => {
1817
1990
  const n = this.productViews.find((r) => r.viewName === o);
1818
1991
  n && this.switchToView(n);
1819
1992
  })), this.subscriptions.push(this.zoomToolbar.on("close", () => {
@@ -1825,22 +1998,26 @@ class ee extends HTMLElement {
1825
1998
  this.zoomToolbar.setSaveDisabled(!0, "Saving...");
1826
1999
  try {
1827
2000
  const o = await this.finalize();
2001
+ if (!o) {
2002
+ this.zoomToolbar.setSaveDisabled(!1, "Save");
2003
+ return;
2004
+ }
1828
2005
  this.zoomToolbar.setSaveDisabled(!0, "Saved!"), this.showToast("Design saved successfully!", "info"), this.dispatchEvent(new CustomEvent("customizer:save", {
1829
2006
  detail: o,
1830
2007
  bubbles: !0,
1831
2008
  composed: !0
1832
2009
  })), setTimeout(() => {
1833
- this.zoomToolbar.setSaveDisabled(!1, "Save Customization"), this.zoomToolbar.showSaveButton(!1), this.stateManager.update({ isDirty: !1 });
2010
+ this.zoomToolbar.setSaveDisabled(!1, "Save"), this.zoomToolbar.showSaveButton(!1), this.stateManager.update({ isDirty: !1 });
1834
2011
  }, 2e3);
1835
2012
  } catch (o) {
1836
2013
  console.error("Customizer: finalize failed", o);
1837
2014
  const n = o instanceof Error ? o.message : "Save failed. Please try again.";
1838
- this.showToast(n, "error"), this.zoomToolbar.setSaveDisabled(!1, "Save Customization");
2015
+ this.showToast(n, "error"), this.zoomToolbar.setSaveDisabled(!1, "Save");
1839
2016
  }
1840
2017
  })), this.getAttribute("show-close-button") === "false" && this.zoomToolbar.setCloseButtonVisible(!1), this.getAttribute("show-save-button") === "false" && this.zoomToolbar.setSaveButtonEnabled(!1);
1841
- const s = h("div", { class: "canvas-area" });
1842
- s.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), s.appendChild(this.canvasWrapper), this.inputPanel = new j(t), this.wireInputPanelEvents();
1843
- const a = h("div", { class: "editor-main" });
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" });
1844
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) => {
1845
2022
  for (const n of o)
1846
2023
  n.target === this.canvasWrapper && this.handleCanvasResize(n.contentRect), n.target === this.container && this.handleLayoutResize(n.contentRect);
@@ -1853,22 +2030,22 @@ class ee extends HTMLElement {
1853
2030
  this.handleAreaSelect(e);
1854
2031
  })
1855
2032
  ), this.subscriptions.push(
1856
- this.interaction.on("content:drag", ({ areaId: e, offset: t }) => {
1857
- 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();
1858
2035
  })
1859
2036
  ), this.subscriptions.push(
1860
- this.interaction.on("content:scale", ({ areaId: e, scale: t }) => {
1861
- 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();
1862
2039
  })
1863
2040
  ), this.subscriptions.push(
1864
- this.interaction.on("content:rotate", ({ areaId: e, rotation: t }) => {
1865
- 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();
1866
2043
  })
1867
2044
  ), this.subscriptions.push(
1868
2045
  this.interaction.on("zoom", ({ zoom: e }) => {
1869
2046
  this.engine.setZoom(e);
1870
- const t = this.engine.getCenteredPan(e);
1871
- 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());
1872
2049
  })
1873
2050
  ), this.subscriptions.push(
1874
2051
  this.interaction.on("pan", ({ pan: e }) => {
@@ -1879,18 +2056,18 @@ class ee extends HTMLElement {
1879
2056
  this.canvas.style.cursor = e;
1880
2057
  })
1881
2058
  ), this.subscriptions.push(
1882
- this.interaction.on("context-menu", ({ areaId: e, clientX: t, clientY: i }) => {
1883
- this.handleContextMenu(e, t, i);
2059
+ this.interaction.on("context-menu", ({ areaId: e, clientX: i, clientY: t }) => {
2060
+ this.handleContextMenu(e, i, t);
1884
2061
  })
1885
2062
  );
1886
2063
  }
1887
2064
  wireInputPanelEvents() {
1888
2065
  this.subscriptions.push(
1889
- this.inputPanel.on("text:change", ({ areaId: e, updates: t }) => {
1890
- 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();
1891
2068
  })
1892
2069
  ), this.subscriptions.push(
1893
- this.inputPanel.on("image:change", async ({ areaId: e, dataUrl: t, filename: i }) => {
2070
+ this.inputPanel.on("image:change", async ({ areaId: e, dataUrl: i, filename: t }) => {
1894
2071
  const s = this.contentManager.getContents();
1895
2072
  let a = 0;
1896
2073
  for (const [n, r] of s)
@@ -1907,13 +2084,15 @@ class ee extends HTMLElement {
1907
2084
  if (o && this.currentTemplate) {
1908
2085
  const n = this.currentTemplate.print.targetDpi;
1909
2086
  try {
1910
- const r = new Image(), d = await new Promise((p, g) => {
1911
- r.onload = () => p({ width: r.naturalWidth, height: r.naturalHeight }), r.onerror = () => g(new Error("Failed to load image")), r.src = t;
1912
- }), l = o.location.width / n, c = o.location.height / n, 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(
1913
2092
  d.width,
1914
2093
  d.height,
1915
2094
  l,
1916
- c,
2095
+ h,
1917
2096
  n
1918
2097
  );
1919
2098
  if (u.actualDPI < 150) {
@@ -1935,8 +2114,8 @@ class ee extends HTMLElement {
1935
2114
  } catch {
1936
2115
  }
1937
2116
  }
1938
- if (this.contentManager.setImageContent(e, t, i), this.saveContentState(), this.storageUsage) {
1939
- const n = Math.round(t.length * 0.75);
2117
+ if (this.contentManager.setImageContent(e, i, t), this.saveContentState(), this.storageUsage) {
2118
+ const n = Math.round(i.length * 0.75);
1940
2119
  this.storageUsage.storageUsed += n, this.storageUsage.usagePercent = this.storageUsage.storageQuota > 0 ? Math.round(this.storageUsage.storageUsed / this.storageUsage.storageQuota * 100) : 0;
1941
2120
  }
1942
2121
  })
@@ -1953,16 +2132,16 @@ class ee extends HTMLElement {
1953
2132
  this.handleAreaSelect(e);
1954
2133
  })
1955
2134
  ), this.subscriptions.push(
1956
- this.inputPanel.on("offset:change", ({ areaId: e, offset: t }) => {
1957
- 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();
1958
2137
  })
1959
2138
  ), this.subscriptions.push(
1960
- this.inputPanel.on("scale:change", ({ areaId: e, scale: t }) => {
1961
- 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();
1962
2141
  })
1963
2142
  ), this.subscriptions.push(
1964
- this.inputPanel.on("rotation:change", ({ areaId: e, rotation: t }) => {
1965
- 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();
1966
2145
  })
1967
2146
  ), this.subscriptions.push(
1968
2147
  this.inputPanel.on("dismiss", () => {
@@ -1971,8 +2150,8 @@ class ee extends HTMLElement {
1971
2150
  );
1972
2151
  }
1973
2152
  // ─── Handlers ───
1974
- handleAreaSelect(e, t = !1) {
1975
- 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(
1976
2155
  new CustomEvent("area:select", {
1977
2156
  detail: { areaId: e },
1978
2157
  bubbles: !0,
@@ -1980,29 +2159,29 @@ class ee extends HTMLElement {
1980
2159
  })
1981
2160
  );
1982
2161
  }
1983
- handleContextMenu(e, t, i) {
2162
+ handleContextMenu(e, i, t) {
1984
2163
  this.handleAreaSelect(e, !!e);
1985
2164
  }
1986
2165
  handleZoomIn() {
1987
- const e = this.engine.getZoom(), t = Math.min(e * 1.25, 5), i = this.engine.getCenteredPan(t);
1988
- 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());
1989
2168
  }
1990
2169
  handleZoomOut() {
1991
- const e = this.engine.getZoom(), t = Math.max(e * 0.8, 0.1), i = this.engine.getCenteredPan(t);
1992
- 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());
1993
2172
  }
1994
2173
  handleZoomFit() {
1995
2174
  this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
1996
2175
  }
1997
2176
  handleCanvasResize(e) {
1998
- const { width: t, height: i } = e;
1999
- if (t === 0 || i === 0) return;
2177
+ const { width: i, height: t } = e;
2178
+ if (i === 0 || t === 0) return;
2000
2179
  const s = window.devicePixelRatio || 1;
2001
- this.canvas.width = t * s, this.canvas.height = i * s, 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()));
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()));
2002
2181
  }
2003
2182
  handleLayoutResize(e) {
2004
- const t = e.width < _;
2005
- 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));
2006
2185
  }
2007
2186
  // ─── State Management ───
2008
2187
  async refreshStorageUsage() {
@@ -2029,7 +2208,7 @@ class ee extends HTMLElement {
2029
2208
  }
2030
2209
  // ─── Keyboard ───
2031
2210
  setupKeyboardShortcuts() {
2032
- const e = J();
2211
+ const e = G();
2033
2212
  this.keyboard.register({
2034
2213
  key: "z",
2035
2214
  [e]: !0,
@@ -2045,35 +2224,35 @@ class ee extends HTMLElement {
2045
2224
  });
2046
2225
  }
2047
2226
  // ─── Toast & Modal ───
2048
- showToast(e, t = "info") {
2049
- const i = h("div", { class: `editor-toast editor-toast-${t}` }, e);
2050
- 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);
2051
2230
  }
2052
- showConfirmation(e, t, i = "Cancel", s = "Continue") {
2231
+ showConfirmation(e, i, t = "Cancel", s = "Continue") {
2053
2232
  return new Promise((a) => {
2054
- const o = h("div", { class: "editor-modal-overlay" }), n = h("div", { class: "editor-modal" });
2055
- n.appendChild(h("div", { class: "editor-modal-title" }, e)), n.appendChild(h("div", { class: "editor-modal-body" }, t));
2056
- 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);
2057
2236
  d.addEventListener("click", () => {
2058
2237
  o.remove(), a(!1);
2059
2238
  });
2060
- const l = h("button", { class: "editor-modal-btn editor-modal-btn-primary" }, s);
2239
+ const l = c("button", { class: "editor-modal-btn editor-modal-btn-primary" }, s);
2061
2240
  l.addEventListener("click", () => {
2062
2241
  o.remove(), a(!0);
2063
- }), r.appendChild(d), r.appendChild(l), n.appendChild(r), o.appendChild(n), o.addEventListener("click", (c) => {
2064
- 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));
2065
2244
  }), this.shadow.appendChild(o);
2066
2245
  });
2067
2246
  }
2068
2247
  // ─── Error / Cleanup ───
2069
- showError(e, t) {
2248
+ showError(e, i) {
2070
2249
  this.shadow.innerHTML = "";
2071
- const i = document.createElement("style");
2072
- i.textContent = M, this.shadow.appendChild(i);
2073
- const s = h("div", { class: "error-container" });
2074
- s.appendChild(h("div", { class: "error-title" }, e)), s.appendChild(h("div", { class: "error-message" }, t?.message ?? "")), this.shadow.appendChild(s), 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(
2075
2254
  new CustomEvent("error", {
2076
- detail: { message: e, error: t },
2255
+ detail: { message: e, error: i },
2077
2256
  bubbles: !0,
2078
2257
  composed: !0
2079
2258
  })
@@ -2085,150 +2264,161 @@ class ee extends HTMLElement {
2085
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();
2086
2265
  }
2087
2266
  }
2088
- customElements.get("customizer-editor") || customElements.define("customizer-editor", ee);
2089
- function te(f, e) {
2090
- const t = typeof f == "string" ? document.querySelector(f) : f;
2091
- 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)
2092
2271
  throw new Error(
2093
- `Container not found: ${typeof f == "string" ? f : "provided element is null"}`
2272
+ `Container not found: ${typeof v == "string" ? v : "provided element is null"}`
2094
2273
  );
2095
2274
  if (!e.templateId && !e.productId)
2096
2275
  throw new Error("Either templateId or productId must be provided");
2097
2276
  if (e.templateId && e.productId)
2098
2277
  throw new Error("Only one of templateId or productId should be provided, not both");
2099
- const i = document.createElement("customizer-editor");
2100
- 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), e.showCloseButton === !1 && i.setAttribute("show-close-button", "false"), e.showSaveButton === !1 && i.setAttribute("show-save-button", "false"), i.style.width = "100%", i.style.height = "100%";
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%";
2101
2280
  const s = [], a = (n, r) => {
2102
- i.addEventListener(n, r), s.push({ event: n, handler: r });
2281
+ t.addEventListener(n, r), s.push({ event: n, handler: r });
2103
2282
  };
2104
- t.innerHTML = "", t.appendChild(i);
2283
+ i.innerHTML = "", i.appendChild(t);
2105
2284
  const o = {
2106
2285
  getDesign() {
2107
- if (typeof i.getDesign != "function")
2286
+ if (typeof t.getDesign != "function")
2108
2287
  throw new Error("Editor not ready: getDesign method not available");
2109
- return i.getDesign();
2288
+ return t.getDesign();
2110
2289
  },
2111
2290
  setDesign(n) {
2112
- if (typeof i.setDesign != "function")
2291
+ if (typeof t.setDesign != "function")
2113
2292
  throw new Error("Editor not ready: setDesign method not available");
2114
- i.setDesign(n);
2293
+ t.setDesign(n);
2115
2294
  },
2116
2295
  undo() {
2117
- if (typeof i.undo != "function")
2296
+ if (typeof t.undo != "function")
2118
2297
  throw new Error("Editor not ready: undo method not available");
2119
- i.undo();
2298
+ t.undo();
2120
2299
  },
2121
2300
  redo() {
2122
- if (typeof i.redo != "function")
2301
+ if (typeof t.redo != "function")
2123
2302
  throw new Error("Editor not ready: redo method not available");
2124
- i.redo();
2303
+ t.redo();
2125
2304
  },
2126
2305
  canUndo() {
2127
- return typeof i.canUndo != "function" ? !1 : i.canUndo();
2306
+ return typeof t.canUndo != "function" ? !1 : t.canUndo();
2128
2307
  },
2129
2308
  canRedo() {
2130
- return typeof i.canRedo != "function" ? !1 : i.canRedo();
2309
+ return typeof t.canRedo != "function" ? !1 : t.canRedo();
2131
2310
  },
2132
2311
  async finalize() {
2133
- const n = this.getDesign(), r = e.apiUrl || "https://api.varianta.io";
2312
+ const n = e.apiUrl || "https://api.varianta.io";
2134
2313
  try {
2135
- 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`, {
2136
2326
  method: "POST",
2137
2327
  headers: {
2138
2328
  "Content-Type": "application/json"
2139
2329
  },
2140
2330
  body: JSON.stringify({
2141
- templateId: n.templateId || e.templateId,
2142
- designJson: n
2331
+ templateId: r.templateId || e.templateId,
2332
+ designJson: r
2143
2333
  })
2144
2334
  });
2145
2335
  if (!d.ok) {
2146
- const m = await d.json().catch(() => ({
2336
+ const g = await d.json().catch(() => ({
2147
2337
  message: "Finalization failed"
2148
2338
  }));
2149
- throw new Error(m.message || "Finalization failed");
2339
+ throw new Error(g.message || "Finalization failed");
2150
2340
  }
2151
2341
  const l = await d.json();
2152
- let c = {
2342
+ let h = {
2153
2343
  designId: l.designId,
2154
2344
  status: l.status,
2155
2345
  proofUrl: l.proofUrl ?? null,
2156
2346
  errorMessage: l.errorMessage ?? null
2157
2347
  };
2158
2348
  const u = 1500, p = 40;
2159
- let g = 0;
2160
- for (; c.status === "processing" && g < p; ) {
2161
- await new Promise((b) => setTimeout(b, u)), g++;
2162
- const m = await fetch(
2163
- `${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`
2164
2354
  );
2165
- if (!m.ok)
2355
+ if (!g.ok)
2166
2356
  throw new Error("Failed to check design status");
2167
- const v = await m.json();
2168
- c = {
2169
- designId: v.designId,
2170
- status: v.status,
2171
- proofUrl: v.proofUrl ?? null,
2172
- 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
2173
2363
  };
2174
2364
  }
2175
- return c.status === "processing" && (c = { ...c, status: "failed", errorMessage: "Render timed out" }), e.onFinalize && e.onFinalize(c), c;
2176
- } catch (d) {
2177
- 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 = {
2178
2368
  code: "FINALIZE_ERROR",
2179
- message: d instanceof Error ? d.message : "Unknown error",
2180
- details: d
2369
+ message: r instanceof Error ? r.message : "Unknown error",
2370
+ details: r
2181
2371
  };
2182
- throw e.onError && e.onError(l), d;
2372
+ throw e.onError && e.onError(d), r;
2183
2373
  }
2184
2374
  },
2185
2375
  addTextLayer(n) {
2186
- if (typeof i.addTextLayer != "function")
2376
+ if (typeof t.addTextLayer != "function")
2187
2377
  throw new Error("Editor not ready: addTextLayer method not available");
2188
- i.addTextLayer(n);
2378
+ t.addTextLayer(n);
2189
2379
  },
2190
2380
  async addImageLayer(n) {
2191
- if (typeof i.addImageLayer != "function")
2381
+ if (typeof t.addImageLayer != "function")
2192
2382
  throw new Error("Editor not ready: addImageLayer method not available");
2193
- return i.addImageLayer(n);
2383
+ return t.addImageLayer(n);
2194
2384
  },
2195
2385
  removeLayer(n) {
2196
- if (typeof i.removeLayer != "function")
2386
+ if (typeof t.removeLayer != "function")
2197
2387
  throw new Error("Editor not ready: removeLayer method not available");
2198
- i.removeLayer(n);
2388
+ t.removeLayer(n);
2199
2389
  },
2200
2390
  selectLayer(n) {
2201
- if (typeof i.selectLayer != "function")
2391
+ if (typeof t.selectLayer != "function")
2202
2392
  throw new Error("Editor not ready: selectLayer method not available");
2203
- i.selectLayer(n);
2393
+ t.selectLayer(n);
2204
2394
  },
2205
2395
  getSelectedLayerId() {
2206
- return typeof i.getSelectedLayerId != "function" ? null : i.getSelectedLayerId();
2396
+ return typeof t.getSelectedLayerId != "function" ? null : t.getSelectedLayerId();
2207
2397
  },
2208
2398
  getActiveView() {
2209
- return typeof i.getActiveView != "function" ? null : i.getActiveView();
2399
+ return typeof t.getActiveView != "function" ? null : t.getActiveView();
2210
2400
  },
2211
2401
  getViews() {
2212
- return typeof i.getViews != "function" ? [] : i.getViews();
2402
+ return typeof t.getViews != "function" ? [] : t.getViews();
2213
2403
  },
2214
2404
  setActiveView(n) {
2215
- if (typeof i.setActiveView != "function")
2405
+ if (typeof t.setActiveView != "function")
2216
2406
  throw new Error("Editor not ready: setActiveView method not available");
2217
- i.setActiveView(n);
2407
+ t.setActiveView(n);
2218
2408
  },
2219
2409
  setTheme(n) {
2220
- i.setAttribute("theme", n);
2410
+ t.setAttribute("theme", n);
2221
2411
  },
2222
2412
  setMode(n) {
2223
- i.setAttribute("mode", n);
2413
+ t.setAttribute("mode", n);
2224
2414
  },
2225
2415
  destroy() {
2226
2416
  s.forEach(({ event: n, handler: r }) => {
2227
- i.removeEventListener(n, r);
2228
- }), i.parentNode && i.parentNode.removeChild(i);
2417
+ t.removeEventListener(n, r);
2418
+ }), t.parentNode && t.parentNode.removeChild(t);
2229
2419
  },
2230
2420
  getElement() {
2231
- return i;
2421
+ return t;
2232
2422
  }
2233
2423
  };
2234
2424
  return e.onReady && a("ready", (() => {
@@ -2253,10 +2443,10 @@ function te(f, e) {
2253
2443
  e.onClose?.();
2254
2444
  })), e.onSave && a("customizer:save", ((n) => {
2255
2445
  e.onSave?.(n.detail);
2256
- })), e.initialDesign && i.addEventListener(
2446
+ })), e.initialDesign && t.addEventListener(
2257
2447
  "ready",
2258
2448
  () => {
2259
- e.initialDesign && i.setDesign(e.initialDesign);
2449
+ e.initialDesign && t.setDesign(e.initialDesign);
2260
2450
  },
2261
2451
  { once: !0 }
2262
2452
  ), e.debug && (console.log("[Customizer SDK] Initialized with options:", e), console.log("[Customizer SDK] Instance:", o)), o;