@varianta/sdk 0.1.7 → 0.1.9

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