@varianta/sdk 0.1.7 → 0.1.8

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 N {
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, d = Math.min(a, r), l = this.canvas.width / s, c = this.canvas.height / s, p = (l / d - t) / 2, u = (c / d - i) / 2;
154
+ this.zoom = d, this.pan = { x: p, y: u }, this.requestRender();
155
155
  }
156
156
  /**
157
157
  * Set the selected area
@@ -180,46 +180,46 @@ 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((d) => d.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 d = i.offset || { x: 0, y: 0 }, l = i.scale ?? 1, c = i.rotation ?? 0, p = a * l, u = r * l, g = n + a / 2 + d.x, m = o + r / 2 + d.y;
200
200
  return {
201
- x: m - u / 2,
202
- y: g - p / 2,
203
- width: u,
204
- height: p,
205
- rotation: h,
206
- centerX: m,
207
- centerY: g
201
+ x: g - p / 2,
202
+ y: m - u / 2,
203
+ width: p,
204
+ height: u,
205
+ rotation: c,
206
+ centerX: g,
207
+ centerY: m
208
208
  };
209
- } else if (t.type === "image") {
209
+ } else if (i.type === "image") {
210
210
  const d = this.loadedImages.get(e);
211
211
  if (!d)
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 l = i.offset || { x: 0, y: 0 }, c = i.scale ?? 1, p = i.rotation ?? 0, u = d.naturalWidth / d.naturalHeight, g = a / r;
214
+ let m, v;
215
+ u > g ? (m = a, v = a / u) : (v = r, m = r * u);
216
+ const b = m * c, w = v * c, y = n + a / 2 + l.x, x = o + r / 2 + l.y;
217
217
  return {
218
218
  x: y - b / 2,
219
219
  y: x - w / 2,
220
220
  width: b,
221
221
  height: w,
222
- rotation: u,
222
+ rotation: p,
223
223
  centerX: y,
224
224
  centerY: x
225
225
  };
@@ -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, d = -r * Math.PI / 180, l = Math.cos(d), c = Math.sin(d), p = e - s, u = t - n, g = p * l - u * c, m = p * c + u * l;
262
+ return g >= -o / 2 && g <= o / 2 && m >= -a / 2 && m <= 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: d } = s, l = 6 / this.zoom, c = 30 / this.zoom, p = d * Math.PI / 180, u = Math.cos(p), g = Math.sin(p), m = (b, w) => ({
278
+ x: n + b * u - w * g,
279
+ y: o + b * g + w * u
280
+ }), v = [];
281
+ if (i.type === "image" && t.imageOptions?.allowScaling || i.type === "text" && t.textOptions?.allowScaling) {
282
+ const b = a / 2, w = r / 2, y = m(-b, -w);
283
+ v.push({ type: "nw", ...y, radius: l });
284
+ const x = m(b, -w);
285
+ v.push({ type: "ne", ...x, radius: l });
286
+ const C = m(b, w);
287
+ v.push({ type: "se", ...C, radius: l });
288
+ const M = m(-b, w);
289
+ v.push({ type: "sw", ...M, radius: l });
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 = m(0, -r / 2 - c);
293
+ v.push({ type: "rotate", ...b, radius: l });
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;
347
+ const e = [], { top: t, right: i, bottom: s, left: n } = this.safeArea, o = n, a = t, r = this.artboard.width - i, d = this.artboard.height - s;
348
348
  for (const l of this.areas) {
349
349
  if (!this.contents.get(l.id))
350
350
  continue;
351
- const h = this.getContentBounds(l.id);
352
- if (!h)
351
+ const c = this.getContentBounds(l.id);
352
+ if (!c)
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: u, width: g, height: m, rotation: v } = c, b = v * Math.PI / 180, w = Math.abs(Math.cos(b)), y = Math.abs(Math.sin(b)), x = g * w + m * y, C = g * y + m * w, M = p - x / 2, R = u - C / 2, U = p + x / 2, D = u + C / 2;
355
+ (M < o || R < a || U > r || D > d) && e.push(l.id);
356
356
  }
357
357
  return this.safeAreaViolations = new Set(e), this.requestRender(), e;
358
358
  }
359
359
  /**
360
360
  * Add event listener
361
361
  */
362
- on(e, 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((u) => u.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: d, y: l, width: c, height: p } = r.location;
439
+ e.save(), e.fillStyle = "rgba(239, 68, 68, 0.15)", e.fillRect(d, l, c, 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);
447
+ const t = this.getContentBounds(e);
448
+ if (!t) {
449
+ const l = this.areas.find((v) => v.id === e);
450
450
  if (!l)
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: c } = this, { x: p, y: u, width: g, height: m } = l.location;
453
+ c.save(), c.strokeStyle = "#3b82f6", c.lineWidth = 2 / this.zoom, c.setLineDash([]), c.strokeRect(p, u, g, m), c.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();
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
458
  const d = this.getContentHandlePositions(e);
459
459
  if (d)
460
460
  for (const l of d) {
461
- t.save();
462
- const h = this.hoveredHandle === l.type, u = h ? "#dbeafe" : "#ffffff", p = h ? "#2563eb" : "#3b82f6";
461
+ i.save();
462
+ const c = this.hoveredHandle === l.type, p = c ? "#dbeafe" : "#ffffff", u = c ? "#2563eb" : "#3b82f6";
463
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();
464
+ const g = this.getRotatedPoint(s, n - a / 2, s, n, r);
465
+ i.beginPath(), i.strokeStyle = u, i.lineWidth = 1 / this.zoom, i.setLineDash([4 / this.zoom, 4 / this.zoom]), i.moveTo(g.x, g.y), i.lineTo(l.x, l.y), i.stroke(), i.setLineDash([]), i.beginPath(), i.fillStyle = p, i.strokeStyle = u, i.lineWidth = 2 / this.zoom, i.arc(l.x, l.y, l.radius, 0, Math.PI * 2), i.fill(), i.stroke(), i.beginPath(), i.strokeStyle = u, i.lineWidth = 1.5 / this.zoom, i.arc(l.x, l.y, l.radius * 0.5, -Math.PI * 0.8, Math.PI * 0.5), i.stroke();
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 = u, i.lineWidth = 2 / this.zoom, i.fillRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2), i.strokeRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2);
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), d = e - i, l = t - s;
476
476
  return {
477
- x: t + d * n - l * r,
478
- y: s + d * r + l * n
477
+ x: i + d * a - l * r,
478
+ y: s + d * r + l * 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, d, l;
491
+ n > o ? (r = s, a = s * n, d = (i - a) / 2, l = 0) : (a = i, r = i / n, d = 0, l = (s - r) / 2), e.drawImage(this.bgImageElement, d, l, 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, d = this.artboard.height - t - 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, a, r, d), 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));
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
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());
509
+ d && (d.type === "text" ? this.renderTextContent(e, d) : d.type === "image" && this.renderImageContent(e, d)), this.showAreaBorders && e.showBorder && (t.save(), t.strokeStyle = e.borderColor || "#3b82f6", t.lineWidth = 1 / this.zoom, t.setLineDash([4 / this.zoom, 4 / this.zoom]), t.strokeRect(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";
519
- let m;
520
- switch (i.align) {
517
+ const { ctx: i } = this, { location: s } = e, { x: n, y: o, width: a, height: r } = s, d = t.offset || { x: 0, y: 0 }, l = t.scale ?? 1, c = t.rotation ?? 0, p = n + a / 2 + d.x, u = o + r / 2 + d.y;
518
+ i.save(), i.beginPath(), i.rect(n, o, a, r), i.clip(), i.translate(p, u), i.rotate(c * Math.PI / 180), i.scale(l, l), i.font = `${t.size}px ${t.font}, sans-serif`, i.fillStyle = t.color, i.textBaseline = "middle";
519
+ let g;
520
+ switch (t.align) {
521
521
  case "left":
522
- t.textAlign = "left", m = -n / 2 + 10;
522
+ i.textAlign = "left", g = -a / 2 + 10;
523
523
  break;
524
524
  case "right":
525
- t.textAlign = "right", m = n / 2 - 10;
525
+ i.textAlign = "right", g = a / 2 - 10;
526
526
  break;
527
527
  case "center":
528
528
  default:
529
- t.textAlign = "center", m = 0;
529
+ i.textAlign = "center", g = 0;
530
530
  break;
531
531
  }
532
- t.fillText(i.text, m, 0), t.restore();
532
+ i.fillText(t.text, g, 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: d } = n, l = t.offset || { x: 0, y: 0 }, c = t.scale ?? 1, p = t.rotation ?? 0;
542
+ s.save(), s.beginPath(), s.rect(o, a, r, d), s.clip();
543
+ const u = i.naturalWidth / i.naturalHeight, g = r / d;
544
+ let m, v;
545
+ u > g ? (m = r, v = r / u) : (v = d, m = d * u);
546
+ const b = m * c, w = v * c, y = o + r / 2 + l.x, x = a + d / 2 + l.y;
547
+ s.translate(y, x), s.rotate(p * Math.PI / 180), s.drawImage(i, -b / 2, -w / 2, b, w), 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, d, l;
559
+ n > o ? (r = i, a = i * n, d = (t - a) / 2, l = 0) : (a = t, r = t / n, d = 0, l = (i - r) / 2), e.drawImage(this.bgImageElement, d, l, 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);
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 d = this.contents.get(t.id);
568
568
  if (d) {
569
569
  if (d.type === "text") {
570
570
  if (!d.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;
572
+ const l = d.offset || { x: 0, y: 0 }, c = d.scale ?? 1, p = d.rotation ?? 0, u = n + a / 2 + l.x, g = o + r / 2 + l.y;
573
+ e.save(), e.beginPath(), e.rect(n, o, a, r), e.clip(), e.translate(u, g), e.rotate(p * Math.PI / 180), e.scale(c, c), e.font = `${d.size}px ${d.font}, sans-serif`, e.fillStyle = d.color, e.textBaseline = "middle";
574
+ let m;
575
575
  switch (d.align) {
576
576
  case "left":
577
- e.textAlign = "left", g = -n / 2 + 10;
577
+ e.textAlign = "left", m = -a / 2 + 10;
578
578
  break;
579
579
  case "right":
580
- e.textAlign = "right", g = n / 2 - 10;
580
+ e.textAlign = "right", m = a / 2 - 10;
581
581
  break;
582
582
  case "center":
583
583
  default:
584
- e.textAlign = "center", g = 0;
584
+ e.textAlign = "center", m = 0;
585
585
  break;
586
586
  }
587
- e.fillText(d.text, g, 0), e.restore();
587
+ e.fillText(d.text, m, 0), e.restore();
588
588
  } else if (d.type === "image") {
589
- const l = this.loadedImages.get(i.id);
589
+ const l = this.loadedImages.get(t.id);
590
590
  if (!l)
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 c = d.offset || { x: 0, y: 0 }, p = d.scale ?? 1, u = d.rotation ?? 0;
593
+ e.save(), e.beginPath(), e.rect(n, o, a, r), e.clip();
594
+ const g = l.naturalWidth / l.naturalHeight, m = a / r;
595
+ let v, b;
596
+ g > m ? (v = a, b = a / g) : (b = r, v = r * g);
597
+ const w = v * p, y = b * p, x = n + a / 2 + c.x, C = o + r / 2 + c.y;
598
+ e.translate(x, C), e.rotate(u * Math.PI / 180), e.drawImage(l, -w / 2, -y / 2, w, y), 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 S(f) {
610
+ const e = f.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 E(f) {
621
+ return f.contentType === "text" || f.contentType === "both";
622
622
  }
623
- function T(v) {
624
- return v.contentType === "image" || v.contentType === "both";
623
+ function T(f) {
624
+ return f.contentType === "image" || f.contentType === "both";
625
625
  }
626
626
  const V = 50;
627
- class O {
627
+ class F {
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,11 +670,11 @@ 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
677
+ description: t
678
678
  }), this.history.length > V && this.history.shift(), this.historyIndex = this.history.length - 1;
679
679
  }
680
680
  restoreFromHistory() {
@@ -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
+ E(t) && e.set(t.id, S(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 ? S(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 && E(t) ? this.contents.set(e, S(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 z {
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 O extends z {
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 $ extends z {
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 O(), this.boundMouseDown = this.handleMouseDown.bind(this), this.boundMouseMove = this.handleMouseMove.bind(this), this.boundMouseUp = this.handleMouseUp.bind(this), this.boundMouseLeave = this.handleMouseLeave.bind(this), this.boundContextMenu = this.handleContextMenu.bind(this), this.boundDoubleClick = this.handleDoubleClick.bind(this), this.boundTouchStart = this.handleTouchStart.bind(this), this.boundTouchMove = this.handleTouchMove.bind(this), this.boundTouchEnd = this.handleTouchEnd.bind(this), e.addEventListener("mousedown", this.boundMouseDown), e.addEventListener("mousemove", this.boundMouseMove), e.addEventListener("mouseup", this.boundMouseUp), e.addEventListener("mouseleave", this.boundMouseLeave), e.addEventListener("contextmenu", this.boundContextMenu), e.addEventListener("dblclick", this.boundDoubleClick), e.addEventListener("touchstart", this.boundTouchStart, { passive: !1 }), e.addEventListener("touchmove", this.boundTouchMove, { passive: !1 }), e.addEventListener("touchend", this.boundTouchEnd), this.gestureHandler.on("pinch", ({ scale: 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) {
993
+ const a = i.get(t), r = this.engine.getContentBounds(t);
994
+ if (o === "rotate" && a?.type === "image" && r) {
987
995
  const d = 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
1001
  startAngle: d
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
1037
  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));
1038
+ a.x = Math.max(-d, Math.min(d, a.x)), a.y = Math.max(-l, Math.min(l, 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, d = Math.max(0.1, Math.min(5, i * r));
1051
+ this.emit("content:scale", { areaId: t, scale: d });
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
1060
  for (const d of r)
1053
- if (Math.abs(n - d) < 5) {
1054
- n = d === 360 ? 0 : d;
1061
+ if (Math.abs(a - d) < 5) {
1062
+ a = d === 360 ? 0 : d;
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);
@@ -1085,203 +1093,214 @@ class B {
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) {
1115
1123
  try {
1116
- const t = await fetch(`${this.baseUrl}/finalize`, {
1124
+ const i = 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
1123
1131
  })
1124
1132
  });
1125
- if (!t.ok) {
1126
- let s = `Failed to finalize design (${t.status} ${t.statusText})
1133
+ if (!i.ok) {
1134
+ let s = `Failed to finalize design (${i.status} ${i.statusText})
1127
1135
 
1128
1136
  `;
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.");
1137
+ if (i.status === 400) {
1138
+ const n = await i.json().catch(() => ({}));
1139
+ s += n.error || "Invalid design data.";
1140
+ } else i.status === 429 ? s += "Rate limit exceeded." : i.status >= 500 && (s += "Server error.");
1133
1141
  throw new Error(s);
1134
1142
  }
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;
1143
+ return i.json();
1144
+ } catch (i) {
1145
+ throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error finalizing design") : i;
1138
1146
  }
1139
1147
  }
1140
1148
  async finalizeMultiView(e) {
1141
1149
  try {
1142
- const i = e.designs.map((s) => ({
1150
+ const t = e.designs.map((s) => ({
1143
1151
  templateId: s.templateId,
1144
1152
  viewName: s.viewName,
1145
- areaContents: s.contents.map(([a, o]) => {
1146
- const n = { areaId: a, isModified: !0 };
1147
- return o.type === "text" ? n.text = {
1153
+ areaContents: s.contents.map(([n, o]) => {
1154
+ const a = { areaId: n, isModified: !0 };
1155
+ return o.type === "text" ? a.text = {
1148
1156
  content: o.text,
1149
1157
  font: o.font,
1150
1158
  size: o.size,
1151
1159
  color: o.color,
1152
1160
  align: o.align,
1153
1161
  position: { x: o.offset?.x ?? 0, y: o.offset?.y ?? 0 }
1154
- } : o.type === "image" && (n.image = {
1155
- assetId: "inline",
1156
- assetUrl: "",
1162
+ } : o.type === "image" && (a.image = {
1163
+ assetId: o.assetId || "inline",
1164
+ assetUrl: o.assetUrl || "",
1157
1165
  rotation: o.rotation ?? 0,
1158
1166
  scale: o.scale ?? 1,
1159
1167
  position: { x: o.offset?.x ?? 0, y: o.offset?.y ?? 0 },
1160
- dataUrl: o.dataUrl
1161
- }), n;
1168
+ // Only send dataUrl if the image wasn't pre-uploaded
1169
+ ...o.assetId ? {} : { dataUrl: o.dataUrl }
1170
+ }), a;
1162
1171
  })
1163
- })), t = await fetch(`${this.baseUrl}/finalize/multi`, {
1172
+ })), i = await fetch(`${this.baseUrl}/finalize`, {
1164
1173
  method: "POST",
1165
1174
  headers: { "Content-Type": "application/json" },
1166
1175
  body: JSON.stringify({
1167
1176
  productId: e.productId,
1168
- designs: i,
1177
+ designs: t,
1169
1178
  cartContext: e.cartContext,
1170
1179
  customerId: e.customerId,
1171
1180
  customerEmail: e.customerEmail,
1172
1181
  sessionId: e.sessionId
1173
1182
  })
1174
1183
  });
1175
- if (!t.ok) {
1176
- let s = `Failed to finalize multi-view design (${t.status} ${t.statusText})
1184
+ if (!i.ok) {
1185
+ let s = `Failed to finalize multi-view design (${i.status} ${i.statusText})
1177
1186
 
1178
1187
  `;
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.");
1188
+ if (i.status === 400) {
1189
+ const n = await i.json().catch(() => ({}));
1190
+ s += n.error || "Invalid design data.";
1191
+ } else i.status === 429 ? s += "Rate limit exceeded." : i.status >= 500 && (s += "Server error.");
1183
1192
  throw new Error(s);
1184
1193
  }
1194
+ return i.json();
1195
+ } catch (t) {
1196
+ throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error("Network error finalizing multi-view design") : t;
1197
+ }
1198
+ }
1199
+ async getDesignStatus(e) {
1200
+ try {
1201
+ const t = await fetch(`${this.baseUrl}/designs/${e}/status`);
1202
+ if (!t.ok)
1203
+ throw new Error(`Failed to check design status (${t.status})`);
1185
1204
  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;
1205
+ } catch (t) {
1206
+ throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error("Network error checking design status") : t;
1188
1207
  }
1189
1208
  }
1190
1209
  async pollMultiViewStatus(e) {
1191
1210
  try {
1192
- const i = await fetch(
1193
- `${this.baseUrl}/designs/multi-status?designIds=${e.join(",")}`
1211
+ const t = await fetch(
1212
+ `${this.baseUrl}/finalize-status?designIds=${e.join(",")}`
1194
1213
  );
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;
1214
+ if (!t.ok)
1215
+ throw new Error(`Failed to check multi-view design status (${t.status})`);
1216
+ return t.json();
1217
+ } catch (t) {
1218
+ throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error("Network error checking multi-view design status") : t;
1200
1219
  }
1201
1220
  }
1202
- async uploadAsset(e, i) {
1221
+ async uploadAsset(e, t) {
1203
1222
  try {
1204
- const t = new FormData();
1205
- t.append("shopId", i), t.append("file", e);
1223
+ const i = new FormData();
1224
+ i.append("shopId", t), i.append("file", e);
1206
1225
  const s = await fetch(`${this.baseUrl}/assets/upload`, {
1207
1226
  method: "POST",
1208
- body: t
1227
+ body: i
1209
1228
  });
1210
1229
  if (!s.ok) {
1211
- let a = `Failed to upload asset (${s.status} ${s.statusText})
1230
+ let n = `Failed to upload asset (${s.status} ${s.statusText})
1212
1231
 
1213
1232
  `;
1214
1233
  if (s.status === 400) {
1215
1234
  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);
1235
+ n += o.error || "Invalid file.";
1236
+ } else s.status === 413 ? n += "File too large (max 15MB)." : s.status >= 500 && (n += "Server error.");
1237
+ throw new Error(n);
1219
1238
  }
1220
1239
  return s.json();
1221
- } catch (t) {
1222
- throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error("Network error uploading asset") : t;
1240
+ } catch (i) {
1241
+ throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error uploading asset") : i;
1223
1242
  }
1224
1243
  }
1225
1244
  async getStorageUsage(e) {
1226
1245
  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;
1246
+ const t = await fetch(`${this.baseUrl}/assets/usage?shopId=${encodeURIComponent(e)}`);
1247
+ if (!t.ok)
1248
+ throw new Error(`Failed to fetch storage usage (${t.status})`);
1249
+ return t.json();
1250
+ } catch (t) {
1251
+ throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error("Network error fetching storage usage") : t;
1233
1252
  }
1234
1253
  }
1235
1254
  }
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;
1255
+ function h(f, e, ...t) {
1256
+ const i = document.createElement(f);
1257
+ return e && Object.entries(e).forEach(([s, n]) => {
1258
+ s === "class" ? i.className = n : s in i ? i[s] = n : i.setAttribute(s, String(n));
1259
+ }), t.forEach((s) => {
1260
+ typeof s == "string" ? i.appendChild(document.createTextNode(s)) : i.appendChild(s);
1261
+ }), i;
1243
1262
  }
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;
1263
+ function q(f, e) {
1264
+ let t = null, i = null;
1265
+ const s = (...n) => {
1266
+ i = n, t && clearTimeout(t), t = setTimeout(() => {
1267
+ i && f(...i), t = null, i = null;
1249
1268
  }, e);
1250
1269
  };
1251
1270
  return s.flush = () => {
1252
- i && (clearTimeout(i), i = null), t && (v(...t), t = null);
1271
+ t && (clearTimeout(t), t = null), i && (f(...i), i = null);
1253
1272
  }, s.cancel = () => {
1254
- i && (clearTimeout(i), i = null), t = null;
1273
+ t && (clearTimeout(t), t = null), i = null;
1255
1274
  }, s;
1256
1275
  }
1257
- class H extends E {
1276
+ class H extends z {
1258
1277
  constructor() {
1259
- super(), this.saveEnabled = !0, this.percentLabel = c("span", { class: "zoom-percent" }, "100%");
1260
- const e = c("button", {
1278
+ super(), this.saveEnabled = !0, this.percentLabel = h("span", { class: "zoom-percent" }, "100%");
1279
+ const e = h("button", {
1261
1280
  class: "zoom-btn",
1262
1281
  title: "Zoom out"
1263
1282
  }, "−");
1264
1283
  e.addEventListener("click", () => this.emit("zoom-out", void 0));
1265
- const i = c("button", {
1284
+ const t = h("button", {
1266
1285
  class: "zoom-btn",
1267
1286
  title: "Fit to view"
1268
1287
  }, "Fit");
1269
- i.addEventListener("click", () => this.emit("zoom-fit", void 0));
1270
- const t = c("button", {
1288
+ t.addEventListener("click", () => this.emit("zoom-fit", void 0));
1289
+ const i = h("button", {
1271
1290
  class: "zoom-btn",
1272
1291
  title: "Zoom in"
1273
1292
  }, "+");
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", () => {
1293
+ 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
1294
  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", {
1295
+ }), this.element = h("div", { class: "zoom-toolbar" }), this.element.appendChild(e), this.element.appendChild(t), this.element.appendChild(i), this.element.appendChild(this.percentLabel), this.element.appendChild(this.viewSelect);
1296
+ const s = h("div", { class: "zoom-toolbar-spacer" });
1297
+ this.element.appendChild(s), this.saveBtn = h("button", {
1279
1298
  class: "zoom-btn zoom-save-btn",
1280
1299
  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", {
1300
+ }, ""), this.saveBtn.style.display = "none", this.saveBtn.addEventListener("click", () => this.emit("save", void 0)), this.element.appendChild(this.saveBtn), this.closeBtn = h("button", {
1282
1301
  class: "zoom-btn zoom-close-btn",
1283
1302
  title: "Close editor"
1284
- }, "✕ Close"), this.closeBtn.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(this.closeBtn);
1303
+ }, "✕"), this.closeBtn.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(this.closeBtn);
1285
1304
  }
1286
1305
  setZoom(e) {
1287
1306
  this.percentLabel.textContent = `${Math.round(e * 100)}%`;
@@ -1295,14 +1314,14 @@ class H extends E {
1295
1314
  setSaveButtonEnabled(e) {
1296
1315
  this.saveEnabled = e, e || (this.saveBtn.style.display = "none");
1297
1316
  }
1298
- setSaveDisabled(e, i) {
1299
- this.saveBtn.disabled = e, i && (this.saveBtn.textContent = i);
1317
+ setSaveDisabled(e, t) {
1318
+ this.saveBtn.disabled = e, t && (this.saveBtn.textContent = t);
1300
1319
  }
1301
1320
  setViews(e) {
1302
1321
  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);
1322
+ for (const t of e) {
1323
+ const i = document.createElement("option");
1324
+ i.value = t.viewName, i.textContent = t.viewName, this.viewSelect.appendChild(i);
1306
1325
  }
1307
1326
  this.viewSelect.style.display = e.length > 1 ? "" : "none";
1308
1327
  }
@@ -1323,242 +1342,258 @@ const Z = [
1323
1342
  { label: "L", value: "left" },
1324
1343
  { label: "C", value: "center" },
1325
1344
  { label: "R", value: "right" }
1326
- ], L = ["image/png", "image/jpeg", "image/webp", "image/svg+xml"], Y = 15 * 1024 * 1024;
1327
- class A extends E {
1345
+ ], A = ["image/png", "image/jpeg", "image/webp", "image/svg+xml"], Y = 15 * 1024 * 1024;
1346
+ class L extends z {
1328
1347
  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) => {
1348
+ 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;
1349
+ const t = E(e), i = T(e);
1350
+ this.mode = i && !t ? "image" : "text", this.element = h("div", { class: "area-card" }), this.element.addEventListener("click", (r) => {
1332
1351
  r.target.tagName === "INPUT" || r.target.tagName === "SELECT" || r.target.tagName === "BUTTON" || this.emit("select", { areaId: e.id });
1333
1352
  });
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);
1353
+ const s = h("div", { class: "area-card-header" }), n = h("div", { class: "area-card-name-row" }), o = h("span", { class: "area-card-name" }, e.name);
1354
+ if (n.appendChild(o), e.required) {
1355
+ const r = h("span", { class: "area-card-badge" }, "Required");
1356
+ n.appendChild(r);
1338
1357
  }
1339
- s.appendChild(a);
1340
- const n = c("button", {
1358
+ s.appendChild(n);
1359
+ const a = h("button", {
1341
1360
  class: "area-card-reset-btn",
1342
1361
  title: "Reset content"
1343
1362
  }, "Reset");
1344
- if (n.addEventListener("click", (r) => {
1363
+ if (a.addEventListener("click", (r) => {
1345
1364
  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", {
1365
+ }), s.appendChild(a), this.element.appendChild(s), t && i) {
1366
+ const r = h("div", { class: "area-card-mode-switcher" }), d = h("button", {
1348
1367
  class: "mode-btn mode-btn-active"
1349
1368
  }, "Text");
1350
1369
  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 });
1370
+ const l = h("button", { class: "mode-btn" }, "Image");
1371
+ l.dataset.mode = "image", d.addEventListener("click", (c) => {
1372
+ c.stopPropagation(), this.setMode("text"), d.classList.add("mode-btn-active"), l.classList.remove("mode-btn-active"), this.currentContent?.type === "image" && this.emit("clear", { areaId: e.id });
1373
+ }), l.addEventListener("click", (c) => {
1374
+ c.stopPropagation(), this.setMode("image"), l.classList.add("mode-btn-active"), d.classList.remove("mode-btn-active"), this.currentContent?.type === "text" && this.emit("clear", { areaId: e.id });
1356
1375
  }), r.appendChild(d), r.appendChild(l), this.element.appendChild(r);
1357
1376
  }
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();
1377
+ this.contentContainer = h("div", { class: "area-card-content" }), this.element.appendChild(this.contentContainer), this.transformContainer = h("div", { class: "area-card-transforms" }), this.element.appendChild(this.transformContainer), this.renderContent();
1359
1378
  }
1360
1379
  setMode(e) {
1361
1380
  this.mode = e, this.renderContent();
1362
1381
  }
1363
1382
  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;
1383
+ const t = this.currentContent?.type;
1384
+ this.currentContent = e, e?.type === "image" ? this.mode = "image" : t === "image" && (this.mode = E(this.area) ? "text" : "image"), this.element.querySelectorAll(".mode-btn").forEach((i) => {
1385
+ const s = i;
1367
1386
  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();
1387
+ }), e?.type !== t || e?.type === "image" || e === void 0 ? this.renderContent() : e?.type === "text" && this.updateTextValues(e), this.renderTransforms();
1369
1388
  }
1370
1389
  setSelected(e) {
1371
1390
  this.isSelected = e, this.element.classList.toggle("area-card-selected", e), this.renderTransforms();
1372
1391
  }
1373
1392
  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);
1393
+ 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) => {
1394
+ const i = t, s = i.title === "Left" ? "left" : i.title === "Center" ? "center" : "right";
1395
+ i.classList.toggle("align-btn-active", s === e.align);
1377
1396
  });
1378
1397
  }
1379
1398
  renderContent() {
1380
1399
  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();
1400
+ const e = E(this.area), t = T(this.area), i = !t || e && t && this.mode === "text", s = !e || e && t && this.mode === "image";
1401
+ e && i && this.renderTextControls(), t && s && this.renderImageControls(), this.applyUploadSpinner();
1383
1402
  }
1384
1403
  renderTextControls() {
1385
- const e = this.currentContent, i = e?.type === "text" ? e : S(this.area), t = c("input", {
1404
+ const e = this.currentContent, t = e?.type === "text" ? e : S(this.area), i = h("input", {
1386
1405
  class: "area-input-text",
1387
1406
  type: "text",
1388
1407
  placeholder: this.area.placeholder || "Enter text..."
1389
1408
  });
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" });
1409
+ i.value = t.text, this.area.textOptions?.maxLength && (i.maxLength = this.area.textOptions.maxLength), i.addEventListener("input", () => {
1410
+ this.emit("text:change", { areaId: this.area.id, updates: { text: i.value } });
1411
+ }), this.contentContainer.appendChild(i), this.textInputEl = i;
1412
+ const s = h("div", { class: "area-input-row" }), n = h("select", { class: "area-input-font" });
1394
1413
  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);
1414
+ const c = h("option");
1415
+ c.value = l.value, c.textContent = l.label, l.value === t.font && (c.selected = !0), n.appendChild(c);
1397
1416
  }
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", {
1417
+ n.addEventListener("change", () => {
1418
+ this.emit("text:change", { areaId: this.area.id, updates: { font: n.value } });
1419
+ }), s.appendChild(n), this.fontSelectEl = n;
1420
+ const o = h("input", {
1402
1421
  class: "area-input-size",
1403
1422
  type: "number"
1404
1423
  });
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", () => {
1424
+ 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
1425
  this.emit("text:change", {
1407
1426
  areaId: this.area.id,
1408
1427
  updates: { size: parseInt(o.value, 10) || 24 }
1409
1428
  });
1410
1429
  }), 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" });
1430
+ const a = h("div", { class: "area-input-row" }), r = h("div", { class: "area-input-align" });
1412
1431
  for (const l of j) {
1413
- const h = c("button", {
1414
- class: `align-btn${i.align === l.value ? " align-btn-active" : ""}`,
1432
+ const c = h("button", {
1433
+ class: `align-btn${t.align === l.value ? " align-btn-active" : ""}`,
1415
1434
  title: l.label === "L" ? "Left" : l.label === "C" ? "Center" : "Right"
1416
1435
  }, 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");
1419
- }), r.appendChild(h);
1436
+ c.addEventListener("click", (p) => {
1437
+ p.stopPropagation(), this.emit("text:change", { areaId: this.area.id, updates: { align: l.value } }), r.querySelectorAll(".align-btn").forEach((u) => u.classList.remove("align-btn-active")), c.classList.add("align-btn-active");
1438
+ }), r.appendChild(c);
1420
1439
  }
1421
- n.appendChild(r), this.alignGroupEl = r;
1422
- const d = c("input", {
1440
+ a.appendChild(r), this.alignGroupEl = r;
1441
+ const d = h("input", {
1423
1442
  class: "area-input-color",
1424
1443
  type: "color"
1425
1444
  });
1426
- d.value = i.color, d.addEventListener("input", () => {
1445
+ d.value = t.color, d.addEventListener("input", () => {
1427
1446
  this.emit("text:change", { areaId: this.area.id, updates: { color: d.value } });
1428
- }), n.appendChild(d), this.colorInputEl = d, this.contentContainer.appendChild(n);
1447
+ }), a.appendChild(d), this.colorInputEl = d, this.contentContainer.appendChild(a);
1429
1448
  }
1430
1449
  renderImageControls() {
1431
1450
  const e = this.currentContent;
1432
1451
  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) => {
1452
+ const t = h("div", { class: "area-image-preview" }), i = h("img", { class: "area-image-thumb" });
1453
+ i.src = e.dataUrl, i.alt = e.filename || "Uploaded image", t.appendChild(i);
1454
+ const s = h("div", { class: "area-image-info" });
1455
+ s.appendChild(h("span", { class: "area-image-name" }, e.filename || "Image"));
1456
+ const n = h("button", { class: "area-image-remove-btn" }, "Remove");
1457
+ n.addEventListener("click", (o) => {
1439
1458
  o.stopPropagation(), this.emit("clear", { areaId: this.area.id });
1440
- }), s.appendChild(a), i.appendChild(s), this.contentContainer.appendChild(i);
1459
+ }), s.appendChild(n), t.appendChild(s), this.contentContainer.appendChild(t);
1441
1460
  } 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 = "";
1461
+ const t = h("button", { class: "area-upload-btn" }, "Upload Image"), i = h("input", { type: "file" });
1462
+ i.accept = A.join(","), i.style.display = "none";
1463
+ const s = h("div", { class: "area-validation-error" });
1464
+ s.style.display = "none", i.addEventListener("change", () => {
1465
+ const n = i.files?.[0];
1466
+ if (!n) return;
1467
+ if (!A.includes(n.type)) {
1468
+ const a = "Only PNG, JPEG, WebP, and SVG images are accepted";
1469
+ s.textContent = a, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: a }), i.value = "";
1451
1470
  return;
1452
1471
  }
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 = "";
1472
+ if (n.size > Y) {
1473
+ const a = "File must be smaller than 15MB";
1474
+ s.textContent = a, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: a }), i.value = "";
1456
1475
  return;
1457
1476
  }
1458
1477
  s.style.display = "none";
1459
1478
  const o = new FileReader();
1460
1479
  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);
1480
+ const a = o.result;
1481
+ this.emit("image:change", { areaId: this.area.id, dataUrl: a, filename: n.name });
1482
+ }, o.readAsDataURL(n), i.value = "";
1483
+ }), t.addEventListener("click", (n) => {
1484
+ n.stopPropagation(), i.click();
1485
+ }), this.contentContainer.appendChild(t), this.contentContainer.appendChild(i), this.contentContainer.appendChild(s);
1467
1486
  }
1468
1487
  }
1469
1488
  renderTransforms() {
1470
1489
  this.transformContainer.innerHTML = "";
1471
1490
  const e = this.currentContent;
1472
1491
  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);
1492
+ 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;
1493
+ if (!t && !i && !s) return;
1494
+ const n = h("div", { class: "area-card-divider" });
1495
+ this.transformContainer.appendChild(n);
1496
+ const o = h("div", { class: "transform-header" });
1497
+ o.appendChild(h("span", { class: "transform-title" }, "Transform"));
1498
+ const a = h("button", { class: "transform-reset-btn" }, "Reset");
1499
+ a.addEventListener("click", (d) => {
1500
+ d.stopPropagation(), t && this.emit("offset:change", { areaId: this.area.id, offset: { x: 0, y: 0 } }), i && this.emit("scale:change", { areaId: this.area.id, scale: 1 }), s && this.emit("rotation:change", { areaId: this.area.id, rotation: 0 });
1501
+ }), o.appendChild(a), this.transformContainer.appendChild(o);
1483
1502
  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" });
1486
- h.value = String(Math.round(r.x)), h.addEventListener("change", () => {
1503
+ if (t) {
1504
+ const d = h("div", { class: "area-input-row" }), l = h("label", { class: "transform-label" }, "X"), c = h("input", { class: "transform-input", type: "number" });
1505
+ c.value = String(Math.round(r.x)), c.addEventListener("change", () => {
1487
1506
  this.emit("offset:change", {
1488
1507
  areaId: this.area.id,
1489
- offset: { ...r, x: parseInt(h.value, 10) || 0 }
1508
+ offset: { ...r, x: parseInt(c.value, 10) || 0 }
1490
1509
  });
1491
1510
  });
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", () => {
1511
+ const p = h("label", { class: "transform-label" }, "Y"), u = h("input", { class: "transform-input", type: "number" });
1512
+ u.value = String(Math.round(r.y)), u.addEventListener("change", () => {
1494
1513
  this.emit("offset:change", {
1495
1514
  areaId: this.area.id,
1496
- offset: { ...r, y: parseInt(p.value, 10) || 0 }
1515
+ offset: { ...r, y: parseInt(u.value, 10) || 0 }
1497
1516
  });
1498
- }), d.appendChild(l), d.appendChild(h), d.appendChild(u), d.appendChild(p), this.transformContainer.appendChild(d);
1517
+ }), d.appendChild(l), d.appendChild(c), d.appendChild(p), d.appendChild(u), this.transformContainer.appendChild(d);
1499
1518
  }
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);
1519
+ if (i && e.type === "image") {
1520
+ const d = e.scale ?? 1, l = h("div", { class: "area-input-row transform-slider-row" }), c = h("label", { class: "transform-label" }, `Scale: ${Math.round(d * 100)}%`), p = h("input", { class: "transform-slider", type: "range" });
1521
+ p.min = "10", p.max = "500", p.step = "5", p.value = String(Math.round(d * 100)), p.addEventListener("input", () => {
1522
+ const u = parseInt(p.value, 10) / 100;
1523
+ c.textContent = `Scale: ${Math.round(u * 100)}%`, this.emit("scale:change", { areaId: this.area.id, scale: u });
1524
+ }), l.appendChild(c), l.appendChild(p), this.transformContainer.appendChild(l);
1506
1525
  }
1507
1526
  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);
1527
+ const d = e.rotation ?? 0, l = h("div", { class: "area-input-row transform-slider-row" }), c = h("label", { class: "transform-label" }, `Rotation: ${Math.round(d)}°`), p = h("input", { class: "transform-slider", type: "range" });
1528
+ p.min = "0", p.max = "360", p.step = "1", p.value = String(Math.round(d)), p.addEventListener("input", () => {
1529
+ const u = parseInt(p.value, 10);
1530
+ c.textContent = `Rotation: ${u}°`, this.emit("rotation:change", { areaId: this.area.id, rotation: u });
1531
+ }), l.appendChild(c), l.appendChild(p), this.transformContainer.appendChild(l);
1513
1532
  }
1514
1533
  }
1534
+ setUploading(e) {
1535
+ this.isUploading = e, this.applyUploadSpinner();
1536
+ }
1537
+ applyUploadSpinner() {
1538
+ const e = this.contentContainer.querySelector(".area-image-preview");
1539
+ if (!e) return;
1540
+ const t = e.querySelector(".area-upload-spinner-overlay");
1541
+ if (this.isUploading && !t) {
1542
+ const i = h("div", { class: "area-upload-spinner-overlay" });
1543
+ i.appendChild(h("div", { class: "area-upload-spinner" })), e.appendChild(i);
1544
+ } else !this.isUploading && t && t.remove();
1545
+ }
1515
1546
  getElement() {
1516
1547
  return this.element;
1517
1548
  }
1518
1549
  }
1519
- class J extends E {
1550
+ class J extends z {
1520
1551
  constructor(e) {
1521
1552
  super(), this.cards = /* @__PURE__ */ new Map(), this.isMobile = !1, this.boundEscapeHandler = (s) => {
1522
1553
  s.key === "Escape" && this.emit("dismiss", void 0);
1523
- }, this.backdrop = c("div", { class: "area-panel-backdrop" }), this.backdrop.addEventListener("click", () => {
1554
+ }, this.backdrop = h("div", { class: "area-panel-backdrop" }), this.backdrop.addEventListener("click", () => {
1524
1555
  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", () => {
1556
+ }), this.element = h("div", { class: "area-panel" });
1557
+ const t = h("div", { class: "area-panel-header" });
1558
+ t.appendChild(h("span", { class: "area-panel-title" }, "Customize"));
1559
+ const i = h("button", { class: "area-panel-close-btn", title: "Close" }, "×");
1560
+ if (i.addEventListener("click", () => {
1530
1561
  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.");
1562
+ }), t.appendChild(i), this.element.appendChild(t), this.panelContent = h("div", { class: "area-panel-content" }), e.length === 0) {
1563
+ const s = h("p", { class: "area-panel-empty" }, "No customization areas defined.");
1533
1564
  this.panelContent.appendChild(s);
1534
1565
  } else
1535
1566
  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());
1567
+ const n = new L(s);
1568
+ 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
1569
  }
1539
1570
  this.element.appendChild(this.panelContent);
1540
1571
  }
1541
1572
  setAreas(e) {
1542
1573
  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);
1574
+ const t = h("p", { class: "area-panel-empty" }, "No customization areas defined.");
1575
+ this.panelContent.appendChild(t);
1545
1576
  } 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());
1577
+ for (const t of e) {
1578
+ const i = new L(t);
1579
+ 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
1580
  }
1550
1581
  }
1551
1582
  setContents(e) {
1552
- for (const [i, t] of this.cards)
1553
- t.setContent(e.get(i));
1583
+ for (const [t, i] of this.cards)
1584
+ i.setContent(e.get(t));
1554
1585
  }
1555
1586
  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";
1587
+ for (const [t, i] of this.cards) {
1588
+ const s = t === e;
1589
+ i.setSelected(s), i.getElement().style.display = s ? "" : "none";
1559
1590
  }
1560
1591
  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
1592
  }
1593
+ setUploading(e, t) {
1594
+ const i = this.cards.get(e);
1595
+ i && i.setUploading(t);
1596
+ }
1562
1597
  setMobile(e) {
1563
1598
  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
1599
  }
@@ -1569,15 +1604,15 @@ class J extends E {
1569
1604
  return this.backdrop;
1570
1605
  }
1571
1606
  }
1572
- class X {
1607
+ class W {
1573
1608
  constructor(e = window) {
1574
- this.target = e, this.commands = [], this.isEnabled = !0, this.handleKeyDown = (i) => {
1609
+ this.target = e, this.commands = [], this.isEnabled = !0, this.handleKeyDown = (t) => {
1575
1610
  if (!this.isEnabled) return;
1576
- const t = i.target;
1577
- if (!(t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) {
1611
+ const i = t.target;
1612
+ if (!(i.tagName === "INPUT" || i.tagName === "TEXTAREA" || i.isContentEditable)) {
1578
1613
  for (const s of this.commands)
1579
- if (this.matchesCommand(i, s)) {
1580
- i.preventDefault(), s.handler(i);
1614
+ if (this.matchesCommand(t, s)) {
1615
+ t.preventDefault(), s.handler(t);
1581
1616
  break;
1582
1617
  }
1583
1618
  }
@@ -1588,8 +1623,8 @@ class X {
1588
1623
  */
1589
1624
  register(e) {
1590
1625
  return this.commands.push(e), () => {
1591
- const i = this.commands.indexOf(e);
1592
- i !== -1 && this.commands.splice(i, 1);
1626
+ const t = this.commands.indexOf(e);
1627
+ t !== -1 && this.commands.splice(t, 1);
1593
1628
  };
1594
1629
  }
1595
1630
  /**
@@ -1613,8 +1648,8 @@ class X {
1613
1648
  /**
1614
1649
  * Check if event matches a command
1615
1650
  */
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);
1651
+ matchesCommand(e, t) {
1652
+ 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
1653
  }
1619
1654
  /**
1620
1655
  * Cleanup
@@ -1623,31 +1658,31 @@ class X {
1623
1658
  this.target.removeEventListener("keydown", this.handleKeyDown), this.commands = [];
1624
1659
  }
1625
1660
  }
1626
- function W() {
1661
+ function X() {
1627
1662
  return /Mac|iPod|iPhone|iPad/.test(navigator.platform);
1628
1663
  }
1629
1664
  function G() {
1630
- return W() ? "meta" : "ctrl";
1665
+ return X() ? "meta" : "ctrl";
1631
1666
  }
1632
- function K(v, e, i, t) {
1633
- const s = v / i, a = e / t;
1634
- return Math.min(s, a);
1667
+ function K(f, e, t, i) {
1668
+ const s = f / t, n = e / i;
1669
+ return Math.min(s, n);
1635
1670
  }
1636
- function Q(v, e, i = 8, t = 10, s = 300) {
1637
- const a = K(v, e, i, t), o = a >= s;
1671
+ function Q(f, e, t = 8, i = 10, s = 300) {
1672
+ const n = K(f, e, t, i), o = n >= s;
1638
1673
  return {
1639
- actualDPI: Math.round(a),
1674
+ actualDPI: Math.round(n),
1640
1675
  targetDPI: s,
1641
- width: v,
1676
+ width: f,
1642
1677
  height: e,
1643
1678
  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.`
1679
+ 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
1680
  };
1646
1681
  }
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;
1682
+ 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)}.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}}', _ = "2.0.0", ee = 768;
1648
1683
  class I extends HTMLElement {
1649
1684
  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(() => {
1685
+ super(), this.resizeObserver = 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.refitRafId = null, this.storageUsage = null, this.storageUsageLastRefresh = 0, this.emitChange = q(() => {
1651
1686
  this.dispatchEvent(
1652
1687
  new CustomEvent("change", {
1653
1688
  detail: { design: this.getDesign() },
@@ -1670,8 +1705,8 @@ class I extends HTMLElement {
1670
1705
  disconnectedCallback() {
1671
1706
  this.cleanup();
1672
1707
  }
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"));
1708
+ attributeChangedCallback(e, t, i) {
1709
+ 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
1710
  }
1676
1711
  // ─── Public API ───
1677
1712
  getDesign() {
@@ -1727,12 +1762,12 @@ class I extends HTMLElement {
1727
1762
  }
1728
1763
  setActiveView(e) {
1729
1764
  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);
1765
+ const t = this.productViews.find((i) => i.viewName === e);
1766
+ if (!t) throw new Error(`View not found: ${e}`);
1767
+ this.switchToView(t);
1733
1768
  }
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();
1769
+ setAreaContent(e, t) {
1770
+ t.type === "text" ? this.contentManager.setTextContent(e, t) : this.contentManager.setImageContent(e, t.dataUrl, t.filename), this.syncEngineContents(), this.saveContentState();
1736
1771
  }
1737
1772
  getAreaContent(e) {
1738
1773
  return this.contentManager.getContent(e);
@@ -1744,112 +1779,95 @@ class I extends HTMLElement {
1744
1779
  const e = [];
1745
1780
  if (this.productViews.length > 1) {
1746
1781
  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`);
1782
+ for (const t of this.productViews) {
1783
+ const i = this.perViewContents.get(t.viewName), s = i?.some(([, o]) => I.hasEditorContent(o)) ?? !1;
1784
+ if (t.isRequired && !s) {
1785
+ e.push(`View "${t.viewName}" is required`);
1751
1786
  continue;
1752
1787
  }
1753
- const a = i.template.templateJson.areas || [];
1754
- for (const o of a) {
1788
+ const n = t.template.templateJson.areas || [];
1789
+ for (const o of n) {
1755
1790
  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`);
1791
+ const a = i?.find(([r]) => r === o.id);
1792
+ a && I.hasEditorContent(a[1]) || e.push(`"${o.name}" in view "${t.viewName}" is required`);
1758
1793
  }
1759
1794
  }
1760
1795
  } else {
1761
- const i = this.currentTemplate?.areas || [], t = this.contentManager.getContents();
1762
- for (const s of i) {
1796
+ const t = this.currentTemplate?.areas || [], i = this.contentManager.getContents();
1797
+ for (const s of t) {
1763
1798
  if (!s.required) continue;
1764
- const a = t.get(s.id);
1765
- a && I.hasEditorContent(a) || e.push(`"${s.name}" is required`);
1799
+ const n = i.get(s.id);
1800
+ n && I.hasEditorContent(n) || e.push(`"${s.name}" is required`);
1766
1801
  }
1767
1802
  }
1768
1803
  return e;
1769
1804
  }
1770
1805
  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");
1806
+ return new Promise((t) => {
1807
+ const i = h("div", { class: "editor-modal-overlay" }), s = h("div", { class: "editor-modal" });
1808
+ s.appendChild(h("div", { class: "editor-modal-title" }, "Required content missing"));
1809
+ const n = h("div", { class: "editor-modal-body" }), o = document.createElement("ul");
1775
1810
  o.style.margin = "8px 0", o.style.paddingLeft = "20px";
1776
1811
  for (const d of e) {
1777
1812
  const l = document.createElement("li");
1778
1813
  l.textContent = d, o.appendChild(l);
1779
1814
  }
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");
1815
+ n.appendChild(o), s.appendChild(n);
1816
+ const a = h("div", { class: "editor-modal-actions" }), r = h("button", { class: "editor-modal-btn editor-modal-btn-primary" }, "OK");
1782
1817
  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);
1818
+ i.remove(), t();
1819
+ }), a.appendChild(r), s.appendChild(a), i.appendChild(s), i.addEventListener("click", (d) => {
1820
+ d.target === i && (i.remove(), t());
1821
+ }), this.shadow.appendChild(i);
1787
1822
  });
1788
1823
  }
1789
1824
  async finalize() {
1790
1825
  const e = this.validateRequiredContent();
1791
1826
  if (e.length > 0)
1792
1827
  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(", ");
1828
+ const t = this.engine.checkSafeAreaViolations();
1829
+ if (t.length > 0) {
1830
+ const o = this.currentTemplate?.areas || [], a = t.map((r) => o.find((d) => d.id === r)?.name || r).join(", ");
1796
1831
  if (!await this.showConfirmation(
1797
1832
  "Content outside safe area",
1798
- `Content in ${n} extends beyond the safe area. It may be trimmed during printing. Continue?`,
1833
+ `Content in ${a} extends beyond the safe area. It may be trimmed during printing. Continue?`,
1799
1834
  "Cancel",
1800
1835
  "Proceed Anyway"
1801
1836
  ))
1802
1837
  throw new Error("Finalization cancelled by user");
1803
1838
  }
1804
- const t = this.getAttribute("store-id") || "demo-shop";
1805
- if (this.productViews.length > 1) {
1839
+ const i = this.getAttribute("store-id") || "demo-shop";
1840
+ if (this.pendingUploads.size > 0 && await Promise.allSettled(this.pendingUploads.values()), this.productViews.length > 1) {
1806
1841
  this.activeViewName && this.contentManager && this.perViewContents.set(this.activeViewName, this.contentManager.toJSON());
1807
1842
  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
1843
+ for (const c of this.productViews) {
1844
+ const p = this.perViewContents.get(c.viewName);
1845
+ !p || p.length === 0 || !p.some(([, u]) => I.hasEditorContent(u)) || o.push({
1846
+ templateId: c.template.id,
1847
+ viewName: c.viewName,
1848
+ contents: p
1814
1849
  });
1815
1850
  }
1816
1851
  if (o.length === 0)
1817
1852
  throw new Error("No views have content to finalize");
1818
- const n = this.getAttribute("product-id");
1819
- if (!n)
1853
+ const a = this.getAttribute("product-id");
1854
+ if (!a)
1820
1855
  throw new Error("product-id attribute is required for multi-view finalize");
1821
1856
  const r = await this.apiClient.finalizeMultiView({
1822
- productId: n,
1857
+ productId: a,
1823
1858
  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
1859
+ }), d = r.views.map((c) => ({
1860
+ viewName: c.viewName,
1861
+ designId: c.designId,
1862
+ proofUrl: c.proofUrl || "",
1863
+ status: c.status
1846
1864
  })), l = d[0];
1847
1865
  return this.dispatchEvent(
1848
1866
  new CustomEvent("customizer:finalize", {
1849
1867
  detail: {
1850
1868
  designId: l.designId,
1851
1869
  proofUrl: l.proofUrl,
1852
- status: l.status,
1870
+ status: "processing",
1853
1871
  views: d,
1854
1872
  requestId: r.requestId
1855
1873
  },
@@ -1858,57 +1876,104 @@ class I extends HTMLElement {
1858
1876
  })
1859
1877
  ), { ...l, views: d };
1860
1878
  }
1861
- const s = this.getDesign(), a = await this.apiClient.finalizeDesign(t, s);
1879
+ const s = this.getDesign(), n = await this.apiClient.finalizeDesign(i, s);
1862
1880
  return this.dispatchEvent(
1863
1881
  new CustomEvent("customizer:finalize", {
1864
1882
  detail: {
1865
- designId: a.designId,
1866
- proofUrl: a.proofUrl,
1883
+ designId: n.designId,
1884
+ proofUrl: n.proofUrl,
1867
1885
  templateId: s.templateId,
1868
1886
  designJson: s,
1869
- status: a.status
1887
+ status: n.status
1870
1888
  },
1871
1889
  bubbles: !0,
1872
1890
  composed: !0
1873
1891
  })
1892
+ ), n;
1893
+ }
1894
+ async waitForResult(e, t) {
1895
+ const i = t?.pollInterval ?? 1500, s = t?.maxPolls ?? 40, n = t?.signal;
1896
+ let o = 0;
1897
+ for (; o < s; ) {
1898
+ if (n?.aborted)
1899
+ throw new DOMException("Polling aborted", "AbortError");
1900
+ await new Promise((d, l) => {
1901
+ const c = setTimeout(d, i);
1902
+ n?.addEventListener("abort", () => {
1903
+ clearTimeout(c), l(new DOMException("Polling aborted", "AbortError"));
1904
+ }, { once: !0 });
1905
+ }), o++;
1906
+ let r;
1907
+ if (e.length === 1) {
1908
+ const d = await this.apiClient.getDesignStatus(e[0]);
1909
+ r = {
1910
+ designs: [d],
1911
+ allCompleted: d.status !== "processing"
1912
+ };
1913
+ } else
1914
+ r = await this.apiClient.pollMultiViewStatus(e);
1915
+ if (r.allCompleted)
1916
+ return this.dispatchEvent(
1917
+ new CustomEvent("customizer:render-complete", {
1918
+ detail: r,
1919
+ bubbles: !0,
1920
+ composed: !0
1921
+ })
1922
+ ), r;
1923
+ }
1924
+ const a = {
1925
+ designs: e.map((r) => ({
1926
+ designId: r,
1927
+ status: "failed",
1928
+ proofUrl: null,
1929
+ errorMessage: "Render timed out"
1930
+ })),
1931
+ allCompleted: !0
1932
+ };
1933
+ return this.dispatchEvent(
1934
+ new CustomEvent("customizer:render-complete", {
1935
+ detail: a,
1936
+ bubbles: !0,
1937
+ composed: !0
1938
+ })
1874
1939
  ), a;
1875
1940
  }
1876
1941
  // ─── Initialization ───
1877
1942
  renderLoading() {
1878
1943
  const e = document.createElement("style");
1879
1944
  e.textContent = k, this.shadow.appendChild(e);
1880
- const i = c(
1945
+ const t = h(
1881
1946
  "div",
1882
1947
  { class: "loading-container" },
1883
- c("div", { class: "loading-spinner" })
1948
+ h("div", { class: "loading-spinner" })
1884
1949
  );
1885
- this.shadow.appendChild(i);
1950
+ this.shadow.appendChild(t);
1886
1951
  }
1887
1952
  async initialize() {
1888
1953
  const e = this.getAttribute("api-url") || "http://localhost:4000";
1889
1954
  this.apiClient = new B(`${e.replace(/\/+$/, "")}/public`);
1890
- const i = this.getAttribute("product-id"), t = this.getAttribute("template-id");
1891
- if (!i && !t)
1955
+ const t = this.getAttribute("product-id"), i = this.getAttribute("template-id");
1956
+ if (!t && !i)
1892
1957
  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 }));
1958
+ t ? await this.loadProduct(t) : await this.loadTemplate(i), this.isReady = !0, this.dispatchEvent(new CustomEvent("ready", { bubbles: !0, composed: !0 }));
1894
1959
  }
1895
1960
  async loadTemplate(e) {
1896
1961
  if (this.loadingTemplateId !== e) {
1897
1962
  this.loadingTemplateId = e;
1898
1963
  try {
1899
- const i = await this.apiClient.getTemplate(e);
1964
+ const t = await this.apiClient.getTemplate(e);
1900
1965
  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}`);
1966
+ await this.loadTemplateData(t), this.loadingTemplateId = null;
1967
+ } catch (t) {
1968
+ throw this.loadingTemplateId = null, new Error(`Failed to load template: ${t.message}`);
1904
1969
  }
1905
1970
  }
1906
1971
  }
1907
1972
  async loadTemplateData(e) {
1908
1973
  this.currentTemplate = e;
1909
- const i = e.areas || [];
1910
- this.contentManager = new P(i);
1911
- const t = {
1974
+ const t = e.areas || [];
1975
+ this.contentManager = new P(t);
1976
+ const i = {
1912
1977
  template: e,
1913
1978
  contents: this.contentManager.toJSON(),
1914
1979
  selectedAreaId: null,
@@ -1917,65 +1982,62 @@ class I extends HTMLElement {
1917
1982
  isDirty: !1,
1918
1983
  warnings: []
1919
1984
  };
1920
- this.stateManager = new O(t), this.buildUI(e, i), this.engine = new U(this.canvas), this.engine.setArtboard({
1985
+ this.stateManager = new F(i), this.buildUI(e, t), this.engine = new N(this.canvas), this.engine.setArtboard({
1921
1986
  width: e.artboard.width,
1922
1987
  height: e.artboard.height,
1923
1988
  backgroundColor: e.artboard.backgroundColor ?? "#ffffff"
1924
- }), this.engine.setAreas(i), this.engine.setContents(this.contentManager.getContents()), e.backgroundImage && this.engine.setBackgroundImage(e.backgroundImage);
1989
+ }), this.engine.setAreas(t), this.engine.setContents(this.contentManager.getContents()), e.backgroundImage && this.engine.setBackgroundImage(e.backgroundImage);
1925
1990
  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(
1991
+ s && this.engine.setSafeArea(s), this.engine.start(), this.scheduleCanvasRefit(), this.interaction = new $(
1930
1992
  this.canvas,
1931
1993
  this.engine,
1932
1994
  () => this.contentManager.getContents(),
1933
1995
  () => 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) => {
1996
+ ), this.wireInteractionEvents(), this.contentManagerUnsub = this.contentManager.subscribe(() => {
1997
+ this.syncEngineContents(), this.inputPanel.setContents(this.contentManager.getContents());
1998
+ }), this.subscriptions.push(this.contentManagerUnsub), this.keyboard = new W(this), this.setupKeyboardShortcuts(), this.inputPanel.setContents(this.contentManager.getContents());
1999
+ const n = this.getAttribute("store-id");
2000
+ n && this.apiClient.getStorageUsage(n).then((o) => {
1941
2001
  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
2002
  }).catch(() => {
1943
2003
  });
1944
2004
  }
1945
2005
  async loadProduct(e) {
1946
2006
  try {
1947
- const i = await this.apiClient.getProduct(e);
1948
- if (i.views.length === 0)
2007
+ const t = await this.apiClient.getProduct(e);
2008
+ if (t.views.length === 0)
1949
2009
  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}`);
2010
+ this.productViews = t.views, this.perViewContents.clear();
2011
+ const i = t.views.find((s) => s.isDefault) || t.views[0];
2012
+ 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));
2013
+ } catch (t) {
2014
+ throw new Error(`Failed to load product: ${t.message}`);
1955
2015
  }
1956
2016
  }
1957
2017
  switchToView(e) {
1958
2018
  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);
2019
+ const t = e.template.templateJson;
2020
+ this.currentTemplate = t;
2021
+ const i = t.areas || [];
2022
+ this.contentManager = new P(i), this.contentManagerUnsub && this.contentManagerUnsub(), this.contentManagerUnsub = this.contentManager.subscribe(() => {
2023
+ this.syncEngineContents(), this.inputPanel.setContents(this.contentManager.getContents());
2024
+ });
1963
2025
  const s = this.perViewContents.get(e.viewName);
1964
2026
  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,
2027
+ width: t.artboard.width,
2028
+ height: t.artboard.height,
2029
+ backgroundColor: t.artboard.backgroundColor ?? "#ffffff"
2030
+ }), this.engine.setAreas(i), this.engine.setContents(this.contentManager.getContents()), this.engine.setBackgroundImage(t.backgroundImage ?? void 0);
2031
+ const n = t.artboard.safeArea ?? t.safeArea;
2032
+ this.engine.setSafeArea(n ?? null), this.engine.fitToView(), this.stateManager.setState({
2033
+ template: t,
1972
2034
  contents: this.contentManager.toJSON(),
1973
2035
  selectedAreaId: null,
1974
2036
  isDirty: this.stateManager.getState().isDirty,
1975
2037
  zoom: this.engine.getZoom(),
1976
2038
  pan: this.engine.getPan(),
1977
2039
  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(
2040
+ }), 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
2041
  new CustomEvent("view-change", {
1980
2042
  detail: { viewName: e.viewName },
1981
2043
  bubbles: !0,
@@ -1983,44 +2045,48 @@ class I extends HTMLElement {
1983
2045
  })
1984
2046
  );
1985
2047
  }
1986
- buildUI(e, i) {
2048
+ buildUI(e, t) {
1987
2049
  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);
2050
+ const i = document.createElement("style");
2051
+ i.textContent = k, this.shadow.appendChild(i), this.container = h("div", { class: "editor-container" }), this.zoomToolbar = new H(), this.subscriptions.push(this.zoomToolbar.on("zoom-in", () => this.handleZoomIn())), this.subscriptions.push(this.zoomToolbar.on("zoom-out", () => this.handleZoomOut())), this.subscriptions.push(this.zoomToolbar.on("zoom-fit", () => this.handleZoomFit())), this.subscriptions.push(this.zoomToolbar.on("view-change", ({ viewName: o }) => {
2052
+ const a = this.productViews.find((r) => r.viewName === o);
2053
+ a && this.switchToView(a);
1992
2054
  })), this.subscriptions.push(this.zoomToolbar.on("close", () => {
1993
2055
  this.dispatchEvent(new CustomEvent("customizer:close", {
1994
2056
  bubbles: !0,
1995
2057
  composed: !0
1996
2058
  }));
1997
2059
  })), this.subscriptions.push(this.zoomToolbar.on("save", async () => {
1998
- this.zoomToolbar.setSaveDisabled(!0, "Saving...");
2060
+ this.pendingUploads.size > 0 && (this.zoomToolbar.setSaveDisabled(!0, "Uploading..."), await Promise.allSettled(this.pendingUploads.values())), this.zoomToolbar.setSaveDisabled(!0, "Saving...");
1999
2061
  try {
2000
2062
  const o = await this.finalize();
2001
2063
  if (!o) {
2002
- this.zoomToolbar.setSaveDisabled(!1, "Save");
2064
+ this.zoomToolbar.setSaveDisabled(!1, "");
2003
2065
  return;
2004
2066
  }
2005
2067
  this.zoomToolbar.setSaveDisabled(!0, "Saved!"), this.showToast("Design saved successfully!", "info"), this.dispatchEvent(new CustomEvent("customizer:save", {
2006
2068
  detail: o,
2007
2069
  bubbles: !0,
2008
2070
  composed: !0
2009
- })), setTimeout(() => {
2010
- this.zoomToolbar.setSaveDisabled(!1, "Save"), this.zoomToolbar.showSaveButton(!1), this.stateManager.update({ isDirty: !1 });
2071
+ }));
2072
+ const a = o.views ? o.views.map((r) => r.designId) : [o.designId];
2073
+ this.renderAbortController?.abort(), this.renderAbortController = new AbortController(), this.waitForResult(a, { signal: this.renderAbortController.signal }).catch((r) => {
2074
+ r instanceof DOMException && r.name === "AbortError" || console.error("Customizer: background render polling failed", r);
2075
+ }), setTimeout(() => {
2076
+ this.zoomToolbar.setSaveDisabled(!1, "✓"), this.zoomToolbar.showSaveButton(!1), this.stateManager.update({ isDirty: !1 });
2011
2077
  }, 2e3);
2012
2078
  } catch (o) {
2013
2079
  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");
2080
+ const a = o instanceof Error ? o.message : "Save failed. Please try again.";
2081
+ this.showToast(a, "error"), this.zoomToolbar.setSaveDisabled(!1, "");
2016
2082
  }
2017
2083
  })), 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);
2084
+ const s = h("div", { class: "canvas-area" });
2085
+ s.appendChild(this.zoomToolbar.getElement()), this.canvasWrapper = h("div", { class: "canvas-wrapper" }), this.canvas = document.createElement("canvas"), this.canvas.className = "editor-canvas", this.canvasWrapper.appendChild(this.canvas), s.appendChild(this.canvasWrapper), this.inputPanel = new J(t), this.wireInputPanelEvents();
2086
+ const n = h("div", { class: "editor-main" });
2087
+ n.appendChild(s), n.appendChild(this.inputPanel.getBackdrop()), n.appendChild(this.inputPanel.getElement()), this.container.appendChild(n), this.shadow.appendChild(this.container), this.resizeObserver = new ResizeObserver((o) => {
2088
+ for (const a of o)
2089
+ a.target === this.canvasWrapper && this.handleCanvasResize(a.contentRect), a.target === this.container && this.handleLayoutResize(a.contentRect);
2024
2090
  }), this.resizeObserver.observe(this.canvasWrapper), this.resizeObserver.observe(this.container);
2025
2091
  }
2026
2092
  // ─── Event Wiring ───
@@ -2030,22 +2096,22 @@ class I extends HTMLElement {
2030
2096
  this.handleAreaSelect(e);
2031
2097
  })
2032
2098
  ), this.subscriptions.push(
2033
- this.interaction.on("content:drag", ({ areaId: e, offset: i }) => {
2034
- this.contentManager.setContentOffset(e, i), this.saveContentState();
2099
+ this.interaction.on("content:drag", ({ areaId: e, offset: t }) => {
2100
+ this.contentManager.setContentOffset(e, t), this.saveContentState();
2035
2101
  })
2036
2102
  ), this.subscriptions.push(
2037
- this.interaction.on("content:scale", ({ areaId: e, scale: i }) => {
2038
- this.contentManager.setImageScale(e, i), this.saveContentState();
2103
+ this.interaction.on("content:scale", ({ areaId: e, scale: t }) => {
2104
+ this.contentManager.setImageScale(e, t), this.saveContentState();
2039
2105
  })
2040
2106
  ), this.subscriptions.push(
2041
- this.interaction.on("content:rotate", ({ areaId: e, rotation: i }) => {
2042
- this.contentManager.setImageRotation(e, i), this.saveContentState();
2107
+ this.interaction.on("content:rotate", ({ areaId: e, rotation: t }) => {
2108
+ this.contentManager.setImageRotation(e, t), this.saveContentState();
2043
2109
  })
2044
2110
  ), this.subscriptions.push(
2045
2111
  this.interaction.on("zoom", ({ zoom: e }) => {
2046
2112
  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());
2113
+ const t = this.engine.getCenteredPan(e);
2114
+ t && this.engine.setPan(t), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
2049
2115
  })
2050
2116
  ), this.subscriptions.push(
2051
2117
  this.interaction.on("pan", ({ pan: e }) => {
@@ -2056,23 +2122,23 @@ class I extends HTMLElement {
2056
2122
  this.canvas.style.cursor = e;
2057
2123
  })
2058
2124
  ), this.subscriptions.push(
2059
- this.interaction.on("context-menu", ({ areaId: e, clientX: i, clientY: t }) => {
2060
- this.handleContextMenu(e, i, t);
2125
+ this.interaction.on("context-menu", ({ areaId: e, clientX: t, clientY: i }) => {
2126
+ this.handleContextMenu(e, t, i);
2061
2127
  })
2062
2128
  );
2063
2129
  }
2064
2130
  wireInputPanelEvents() {
2065
2131
  this.subscriptions.push(
2066
- this.inputPanel.on("text:change", ({ areaId: e, updates: i }) => {
2067
- this.contentManager.setTextContent(e, i), this.saveContentState();
2132
+ this.inputPanel.on("text:change", ({ areaId: e, updates: t }) => {
2133
+ this.contentManager.setTextContent(e, t), this.saveContentState();
2068
2134
  })
2069
2135
  ), this.subscriptions.push(
2070
- this.inputPanel.on("image:change", async ({ areaId: e, dataUrl: i, filename: t }) => {
2136
+ this.inputPanel.on("image:change", async ({ areaId: e, dataUrl: t, filename: i }) => {
2071
2137
  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) {
2138
+ let n = 0;
2139
+ for (const [r, d] of s)
2140
+ d.type === "image" && r !== e && n++;
2141
+ if (n >= 10) {
2076
2142
  this.showToast("Maximum of 10 images per design reached", "error");
2077
2143
  return;
2078
2144
  }
@@ -2080,30 +2146,30 @@ class I extends HTMLElement {
2080
2146
  this.showToast("Storage quota exceeded. Upgrade your plan for more storage.", "error");
2081
2147
  return;
2082
2148
  }
2083
- const o = this.currentTemplate?.areas.find((n) => n.id === e);
2149
+ const o = this.currentTemplate?.areas.find((r) => r.id === e);
2084
2150
  if (o && this.currentTemplate) {
2085
- const n = this.currentTemplate.print.targetDpi;
2151
+ const r = this.currentTemplate.print.targetDpi;
2086
2152
  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(
2092
- d.width,
2093
- d.height,
2094
- l,
2095
- h,
2096
- n
2153
+ const d = new Image();
2154
+ d.crossOrigin = "anonymous";
2155
+ const l = await new Promise((g, m) => {
2156
+ d.onload = () => g({ width: d.naturalWidth, height: d.naturalHeight }), d.onerror = () => m(new Error("Failed to load image")), d.src = t;
2157
+ }), c = o.location.width / r, p = o.location.height / r, u = Q(
2158
+ l.width,
2159
+ l.height,
2160
+ c,
2161
+ p,
2162
+ r
2097
2163
  );
2098
2164
  if (u.actualDPI < 150) {
2099
2165
  if (!await this.showConfirmation(
2100
2166
  "Low resolution detected",
2101
- `This image is ${u.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${n} DPI.`,
2167
+ `This image is ${u.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${r} DPI.`,
2102
2168
  "Cancel",
2103
2169
  "Use Anyway"
2104
2170
  )) return;
2105
- } else u.actualDPI < n && (this.showToast(
2106
- `Image resolution is ${u.actualDPI} DPI (${n} recommended)`,
2171
+ } else u.actualDPI < r && (this.showToast(
2172
+ `Image resolution is ${u.actualDPI} DPI (${r} recommended)`,
2107
2173
  "warning"
2108
2174
  ), this.stateManager.update({
2109
2175
  warnings: [
@@ -2114,9 +2180,11 @@ class I extends HTMLElement {
2114
2180
  } catch {
2115
2181
  }
2116
2182
  }
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;
2183
+ this.contentManager.setImageContent(e, t, i), this.saveContentState();
2184
+ const a = this.getAttribute("store-id");
2185
+ if (a && this.preUploadImage(e, t, a), this.storageUsage) {
2186
+ const r = Math.round(t.length * 0.75);
2187
+ this.storageUsage.storageUsed += r, this.storageUsage.usagePercent = this.storageUsage.storageQuota > 0 ? Math.round(this.storageUsage.storageUsed / this.storageUsage.storageQuota * 100) : 0;
2120
2188
  }
2121
2189
  })
2122
2190
  ), this.subscriptions.push(
@@ -2125,23 +2193,23 @@ class I extends HTMLElement {
2125
2193
  })
2126
2194
  ), this.subscriptions.push(
2127
2195
  this.inputPanel.on("clear", ({ areaId: e }) => {
2128
- this.contentManager.clearContent(e), this.saveContentState();
2196
+ this.contentManager.clearContent(e), this.syncEngineContents(), this.inputPanel.setContents(this.contentManager.getContents()), this.saveContentState();
2129
2197
  })
2130
2198
  ), this.subscriptions.push(
2131
2199
  this.inputPanel.on("select", ({ areaId: e }) => {
2132
2200
  this.handleAreaSelect(e);
2133
2201
  })
2134
2202
  ), this.subscriptions.push(
2135
- this.inputPanel.on("offset:change", ({ areaId: e, offset: i }) => {
2136
- this.contentManager.setContentOffset(e, i), this.saveContentState();
2203
+ this.inputPanel.on("offset:change", ({ areaId: e, offset: t }) => {
2204
+ this.contentManager.setContentOffset(e, t), this.saveContentState();
2137
2205
  })
2138
2206
  ), this.subscriptions.push(
2139
- this.inputPanel.on("scale:change", ({ areaId: e, scale: i }) => {
2140
- this.contentManager.setImageScale(e, i), this.saveContentState();
2207
+ this.inputPanel.on("scale:change", ({ areaId: e, scale: t }) => {
2208
+ this.contentManager.setImageScale(e, t), this.saveContentState();
2141
2209
  })
2142
2210
  ), this.subscriptions.push(
2143
- this.inputPanel.on("rotation:change", ({ areaId: e, rotation: i }) => {
2144
- this.contentManager.setImageRotation(e, i), this.saveContentState();
2211
+ this.inputPanel.on("rotation:change", ({ areaId: e, rotation: t }) => {
2212
+ this.contentManager.setImageRotation(e, t), this.saveContentState();
2145
2213
  })
2146
2214
  ), this.subscriptions.push(
2147
2215
  this.inputPanel.on("dismiss", () => {
@@ -2150,8 +2218,8 @@ class I extends HTMLElement {
2150
2218
  );
2151
2219
  }
2152
2220
  // ─── 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(
2221
+ handleAreaSelect(e, t = !1) {
2222
+ this.stateManager.update({ selectedAreaId: e }), this.engine.setSelectedArea(e), this.inputPanel.setSelectedArea(t ? e : null), e && document.activeElement !== this && this.focus(), this.dispatchEvent(
2155
2223
  new CustomEvent("area:select", {
2156
2224
  detail: { areaId: e },
2157
2225
  bubbles: !0,
@@ -2159,29 +2227,42 @@ class I extends HTMLElement {
2159
2227
  })
2160
2228
  );
2161
2229
  }
2162
- handleContextMenu(e, i, t) {
2230
+ handleContextMenu(e, t, i) {
2163
2231
  this.handleAreaSelect(e, !!e);
2164
2232
  }
2165
2233
  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());
2234
+ const e = this.engine.getZoom(), t = Math.min(e * 1.25, 5), i = this.engine.getCenteredPan(t);
2235
+ 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
2236
  }
2169
2237
  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());
2238
+ const e = this.engine.getZoom(), t = Math.max(e * 0.8, 0.1), i = this.engine.getCenteredPan(t);
2239
+ 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
2240
  }
2173
2241
  handleZoomFit() {
2174
2242
  this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
2175
2243
  }
2244
+ /**
2245
+ * Schedule a canvas refit after layout settles (double-rAF ensures
2246
+ * ResizeObserver callbacks and layout-class changes have been applied).
2247
+ */
2248
+ scheduleCanvasRefit() {
2249
+ this.refitRafId != null && cancelAnimationFrame(this.refitRafId), this.refitRafId = requestAnimationFrame(() => {
2250
+ this.refitRafId = requestAnimationFrame(() => {
2251
+ if (this.refitRafId = null, !this.canvasWrapper || !this.engine) return;
2252
+ const e = this.canvasWrapper.getBoundingClientRect();
2253
+ e.width > 0 && e.height > 0 && this.handleCanvasResize(e);
2254
+ });
2255
+ });
2256
+ }
2176
2257
  handleCanvasResize(e) {
2177
- const { width: i, height: t } = e;
2178
- if (i === 0 || t === 0) return;
2258
+ const { width: t, height: i } = e;
2259
+ if (t === 0 || i === 0) return;
2179
2260
  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()));
2261
+ this.canvas.width = t * s, this.canvas.height = i * s, this.canvas.style.width = `${t}px`, this.canvas.style.height = `${i}px`, this.engine && (this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom()));
2181
2262
  }
2182
2263
  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));
2264
+ const t = e.width < ee;
2265
+ this.container.classList.toggle("layout-desktop", !t), this.container.classList.toggle("layout-mobile", t), t !== this.isMobileLayout && (this.isMobileLayout = t, this.inputPanel.setMobile(t), this.scheduleCanvasRefit());
2185
2266
  }
2186
2267
  // ─── State Management ───
2187
2268
  async refreshStorageUsage() {
@@ -2193,6 +2274,42 @@ class I extends HTMLElement {
2193
2274
  } catch {
2194
2275
  }
2195
2276
  }
2277
+ /**
2278
+ * Pre-upload an image to the server so finalize doesn't need to send base64.
2279
+ * On success, updates the content manager with assetId/assetUrl.
2280
+ * Tracks the promise so finalize() can await it.
2281
+ */
2282
+ preUploadImage(e, t, i) {
2283
+ const s = this.activeViewName, n = this._doPreUpload(e, t, i, s);
2284
+ return this.pendingUploads.set(e, n), this.inputPanel?.setUploading(e, !0), this.updateSaveButtonForUploads(), n.finally(() => {
2285
+ this.pendingUploads.get(e) === n && this.pendingUploads.delete(e), this.inputPanel?.setUploading(e, !1), this.updateSaveButtonForUploads();
2286
+ }), n;
2287
+ }
2288
+ async _doPreUpload(e, t, i, s) {
2289
+ const [n, o] = t.split(","), a = n.match(/:(.*?);/)?.[1] || "image/png", r = atob(o), d = new Uint8Array(r.length);
2290
+ for (let g = 0; g < r.length; g++) d[g] = r.charCodeAt(g);
2291
+ const l = new Blob([d], { type: a }), c = a.split("/")[1] || "png", p = new File([l], `upload.${c}`, { type: a });
2292
+ let u;
2293
+ try {
2294
+ u = await this.apiClient.uploadAsset(p, i);
2295
+ } catch (g) {
2296
+ console.error("Customizer: image pre-upload failed", g), this.showToast("Image upload failed. Will retry on save.", "warning");
2297
+ return;
2298
+ }
2299
+ if (this.activeViewName === s) {
2300
+ const g = this.contentManager.getContent(e);
2301
+ g?.type === "image" && g.dataUrl === t && this.contentManager.setImageContent(e, u.publicUrl, g.filename, {
2302
+ assetId: u.assetId,
2303
+ assetUrl: u.publicUrl
2304
+ });
2305
+ } else if (s) {
2306
+ const g = this.perViewContents.get(s);
2307
+ if (g) {
2308
+ const m = g.map(([v, b]) => v === e && b.type === "image" && b.dataUrl === t ? [v, { ...b, dataUrl: u.publicUrl, assetId: u.assetId, assetUrl: u.publicUrl }] : [v, b]);
2309
+ this.perViewContents.set(s, m);
2310
+ }
2311
+ }
2312
+ }
2196
2313
  saveContentState() {
2197
2314
  this.stateManager.setState({
2198
2315
  contents: this.contentManager.toJSON(),
@@ -2223,106 +2340,110 @@ class I extends HTMLElement {
2223
2340
  handler: () => this.handleAreaSelect(null)
2224
2341
  });
2225
2342
  }
2343
+ updateSaveButtonForUploads() {
2344
+ this.zoomToolbar && this.stateManager?.getState().isDirty && (this.pendingUploads.size > 0 ? this.zoomToolbar.setSaveDisabled(!0, "Uploading...") : this.zoomToolbar.setSaveDisabled(!1, "✓"));
2345
+ }
2226
2346
  // ─── 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);
2347
+ showToast(e, t = "info") {
2348
+ const i = h("div", { class: `editor-toast editor-toast-${t}` }, e);
2349
+ this.shadow.appendChild(i), setTimeout(() => i.remove(), 4e3);
2350
+ }
2351
+ showConfirmation(e, t, i = "Cancel", s = "Continue") {
2352
+ return new Promise((n) => {
2353
+ const o = h("div", { class: "editor-modal-overlay" }), a = h("div", { class: "editor-modal" });
2354
+ a.appendChild(h("div", { class: "editor-modal-title" }, e)), a.appendChild(h("div", { class: "editor-modal-body" }, t));
2355
+ const r = h("div", { class: "editor-modal-actions" }), d = h("button", { class: "editor-modal-btn" }, i);
2236
2356
  d.addEventListener("click", () => {
2237
- o.remove(), a(!1);
2357
+ o.remove(), n(!1);
2238
2358
  });
2239
- const l = c("button", { class: "editor-modal-btn editor-modal-btn-primary" }, s);
2359
+ const l = h("button", { class: "editor-modal-btn editor-modal-btn-primary" }, s);
2240
2360
  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));
2361
+ o.remove(), n(!0);
2362
+ }), r.appendChild(d), r.appendChild(l), a.appendChild(r), o.appendChild(a), o.addEventListener("click", (c) => {
2363
+ c.target === o && (o.remove(), n(!1));
2244
2364
  }), this.shadow.appendChild(o);
2245
2365
  });
2246
2366
  }
2247
2367
  // ─── Error / Cleanup ───
2248
- showError(e, i) {
2368
+ showError(e, t) {
2249
2369
  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(
2370
+ const i = document.createElement("style");
2371
+ i.textContent = k, this.shadow.appendChild(i);
2372
+ const s = h("div", { class: "error-container" });
2373
+ s.appendChild(h("div", { class: "error-title" }, e)), s.appendChild(h("div", { class: "error-message" }, t?.message ?? "")), this.shadow.appendChild(s), this.dispatchEvent(
2254
2374
  new CustomEvent("error", {
2255
- detail: { message: e, error: i },
2375
+ detail: { message: e, error: t },
2256
2376
  bubbles: !0,
2257
2377
  composed: !0
2258
2378
  })
2259
2379
  );
2260
2380
  }
2261
2381
  cleanup() {
2382
+ this.renderAbortController?.abort(), this.renderAbortController = null;
2262
2383
  for (const e of this.subscriptions)
2263
2384
  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();
2385
+ this.subscriptions = [], this.refitRafId != null && (cancelAnimationFrame(this.refitRafId), this.refitRafId = null), 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();
2265
2386
  }
2266
2387
  }
2267
2388
  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)
2389
+ function te(f, e) {
2390
+ const t = typeof f == "string" ? document.querySelector(f) : f;
2391
+ if (!t)
2271
2392
  throw new Error(
2272
- `Container not found: ${typeof v == "string" ? v : "provided element is null"}`
2393
+ `Container not found: ${typeof f == "string" ? f : "provided element is null"}`
2273
2394
  );
2274
2395
  if (!e.templateId && !e.productId)
2275
2396
  throw new Error("Either templateId or productId must be provided");
2276
2397
  if (e.templateId && e.productId)
2277
2398
  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 });
2399
+ const i = document.createElement("customizer-editor");
2400
+ 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%";
2401
+ const s = [], n = (a, r) => {
2402
+ i.addEventListener(a, r), s.push({ event: a, handler: r });
2282
2403
  };
2283
- i.innerHTML = "", i.appendChild(t);
2404
+ t.innerHTML = "", t.appendChild(i);
2284
2405
  const o = {
2285
2406
  getDesign() {
2286
- if (typeof t.getDesign != "function")
2407
+ if (typeof i.getDesign != "function")
2287
2408
  throw new Error("Editor not ready: getDesign method not available");
2288
- return t.getDesign();
2409
+ return i.getDesign();
2289
2410
  },
2290
- setDesign(n) {
2291
- if (typeof t.setDesign != "function")
2411
+ setDesign(a) {
2412
+ if (typeof i.setDesign != "function")
2292
2413
  throw new Error("Editor not ready: setDesign method not available");
2293
- t.setDesign(n);
2414
+ i.setDesign(a);
2294
2415
  },
2295
2416
  undo() {
2296
- if (typeof t.undo != "function")
2417
+ if (typeof i.undo != "function")
2297
2418
  throw new Error("Editor not ready: undo method not available");
2298
- t.undo();
2419
+ i.undo();
2299
2420
  },
2300
2421
  redo() {
2301
- if (typeof t.redo != "function")
2422
+ if (typeof i.redo != "function")
2302
2423
  throw new Error("Editor not ready: redo method not available");
2303
- t.redo();
2424
+ i.redo();
2304
2425
  },
2305
2426
  canUndo() {
2306
- return typeof t.canUndo != "function" ? !1 : t.canUndo();
2427
+ return typeof i.canUndo != "function" ? !1 : i.canUndo();
2307
2428
  },
2308
2429
  canRedo() {
2309
- return typeof t.canRedo != "function" ? !1 : t.canRedo();
2430
+ return typeof i.canRedo != "function" ? !1 : i.canRedo();
2310
2431
  },
2311
2432
  async finalize() {
2312
- const n = e.apiUrl || "https://api.varianta.io";
2433
+ const a = e.apiUrl || "https://api.varianta.io";
2313
2434
  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,
2435
+ if (e.productId && typeof i.finalize == "function") {
2436
+ const p = await i.finalize(), u = {
2437
+ designId: p.designId,
2438
+ status: "processing",
2439
+ proofUrl: p.proofUrl ?? null,
2319
2440
  errorMessage: null,
2320
- requestId: g.requestId,
2321
- views: g.views
2441
+ requestId: p.requestId,
2442
+ views: p.views
2322
2443
  };
2323
- return e.onFinalize && e.onFinalize(f), f;
2444
+ return e.onFinalize && e.onFinalize(u), u;
2324
2445
  }
2325
- const r = this.getDesign(), d = await fetch(`${n}/public/finalize`, {
2446
+ const r = this.getDesign(), d = await fetch(`${a}/public/finalize`, {
2326
2447
  method: "POST",
2327
2448
  headers: {
2328
2449
  "Content-Type": "application/json"
@@ -2333,36 +2454,18 @@ function te(v, e) {
2333
2454
  })
2334
2455
  });
2335
2456
  if (!d.ok) {
2336
- const g = await d.json().catch(() => ({
2457
+ const p = await d.json().catch(() => ({
2337
2458
  message: "Finalization failed"
2338
2459
  }));
2339
- throw new Error(g.message || "Finalization failed");
2460
+ throw new Error(p.message || "Finalization failed");
2340
2461
  }
2341
- const l = await d.json();
2342
- let h = {
2462
+ const l = await d.json(), c = {
2343
2463
  designId: l.designId,
2344
2464
  status: l.status,
2345
2465
  proofUrl: l.proofUrl ?? null,
2346
2466
  errorMessage: l.errorMessage ?? null
2347
2467
  };
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
2363
- };
2364
- }
2365
- return h.status === "processing" && (h = { ...h, status: "failed", errorMessage: "Render timed out" }), e.onFinalize && e.onFinalize(h), h;
2468
+ return e.onFinalize && e.onFinalize(c), c;
2366
2469
  } catch (r) {
2367
2470
  const d = {
2368
2471
  code: "FINALIZE_ERROR",
@@ -2372,81 +2475,129 @@ function te(v, e) {
2372
2475
  throw e.onError && e.onError(d), r;
2373
2476
  }
2374
2477
  },
2375
- addTextLayer(n) {
2376
- if (typeof t.addTextLayer != "function")
2478
+ async waitForResult(a, r) {
2479
+ if (typeof i.waitForResult == "function") {
2480
+ const m = await i.waitForResult(a, r);
2481
+ return e.onRenderComplete && e.onRenderComplete(m), m;
2482
+ }
2483
+ const d = e.apiUrl || "https://api.varianta.io", l = r?.pollInterval ?? 1500, c = r?.maxPolls ?? 40, p = r?.signal;
2484
+ let u = 0;
2485
+ for (; u < c; ) {
2486
+ if (p?.aborted)
2487
+ throw new DOMException("Polling aborted", "AbortError");
2488
+ await new Promise((v, b) => {
2489
+ const w = setTimeout(v, l);
2490
+ p?.addEventListener("abort", () => {
2491
+ clearTimeout(w), b(new DOMException("Polling aborted", "AbortError"));
2492
+ }, { once: !0 });
2493
+ }), u++;
2494
+ let m;
2495
+ if (a.length === 1) {
2496
+ const v = await fetch(`${d}/public/designs/${a[0]}/status`);
2497
+ if (!v.ok) throw new Error("Failed to check design status");
2498
+ const b = await v.json();
2499
+ m = {
2500
+ designs: [b],
2501
+ allCompleted: b.status !== "processing"
2502
+ };
2503
+ } else {
2504
+ const v = await fetch(
2505
+ `${d}/public/finalize-status?designIds=${a.join(",")}`
2506
+ );
2507
+ if (!v.ok) throw new Error("Failed to check design status");
2508
+ m = await v.json();
2509
+ }
2510
+ if (m.allCompleted)
2511
+ return e.onRenderComplete && e.onRenderComplete(m), m;
2512
+ }
2513
+ const g = {
2514
+ designs: a.map((m) => ({
2515
+ designId: m,
2516
+ status: "failed",
2517
+ proofUrl: null,
2518
+ errorMessage: "Render timed out"
2519
+ })),
2520
+ allCompleted: !0
2521
+ };
2522
+ return e.onRenderComplete && e.onRenderComplete(g), g;
2523
+ },
2524
+ addTextLayer(a) {
2525
+ if (typeof i.addTextLayer != "function")
2377
2526
  throw new Error("Editor not ready: addTextLayer method not available");
2378
- t.addTextLayer(n);
2527
+ i.addTextLayer(a);
2379
2528
  },
2380
- async addImageLayer(n) {
2381
- if (typeof t.addImageLayer != "function")
2529
+ async addImageLayer(a) {
2530
+ if (typeof i.addImageLayer != "function")
2382
2531
  throw new Error("Editor not ready: addImageLayer method not available");
2383
- return t.addImageLayer(n);
2532
+ return i.addImageLayer(a);
2384
2533
  },
2385
- removeLayer(n) {
2386
- if (typeof t.removeLayer != "function")
2534
+ removeLayer(a) {
2535
+ if (typeof i.removeLayer != "function")
2387
2536
  throw new Error("Editor not ready: removeLayer method not available");
2388
- t.removeLayer(n);
2537
+ i.removeLayer(a);
2389
2538
  },
2390
- selectLayer(n) {
2391
- if (typeof t.selectLayer != "function")
2539
+ selectLayer(a) {
2540
+ if (typeof i.selectLayer != "function")
2392
2541
  throw new Error("Editor not ready: selectLayer method not available");
2393
- t.selectLayer(n);
2542
+ i.selectLayer(a);
2394
2543
  },
2395
2544
  getSelectedLayerId() {
2396
- return typeof t.getSelectedLayerId != "function" ? null : t.getSelectedLayerId();
2545
+ return typeof i.getSelectedLayerId != "function" ? null : i.getSelectedLayerId();
2397
2546
  },
2398
2547
  getActiveView() {
2399
- return typeof t.getActiveView != "function" ? null : t.getActiveView();
2548
+ return typeof i.getActiveView != "function" ? null : i.getActiveView();
2400
2549
  },
2401
2550
  getViews() {
2402
- return typeof t.getViews != "function" ? [] : t.getViews();
2551
+ return typeof i.getViews != "function" ? [] : i.getViews();
2403
2552
  },
2404
- setActiveView(n) {
2405
- if (typeof t.setActiveView != "function")
2553
+ setActiveView(a) {
2554
+ if (typeof i.setActiveView != "function")
2406
2555
  throw new Error("Editor not ready: setActiveView method not available");
2407
- t.setActiveView(n);
2556
+ i.setActiveView(a);
2408
2557
  },
2409
- setTheme(n) {
2410
- t.setAttribute("theme", n);
2558
+ setTheme(a) {
2559
+ i.setAttribute("theme", a);
2411
2560
  },
2412
- setMode(n) {
2413
- t.setAttribute("mode", n);
2561
+ setMode(a) {
2562
+ i.setAttribute("mode", a);
2414
2563
  },
2415
2564
  destroy() {
2416
- s.forEach(({ event: n, handler: r }) => {
2417
- t.removeEventListener(n, r);
2418
- }), t.parentNode && t.parentNode.removeChild(t);
2565
+ s.forEach(({ event: a, handler: r }) => {
2566
+ i.removeEventListener(a, r);
2567
+ }), i.parentNode && i.parentNode.removeChild(i);
2419
2568
  },
2420
2569
  getElement() {
2421
- return t;
2570
+ return i;
2422
2571
  }
2423
2572
  };
2424
- return e.onReady && a("ready", (() => {
2573
+ return e.onReady && n("ready", (() => {
2425
2574
  e.onReady?.(o);
2426
- })), e.onChange && a("change", ((n) => {
2427
- const r = n.detail.design;
2575
+ })), e.onChange && n("change", ((a) => {
2576
+ const r = a.detail.design;
2428
2577
  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;
2578
+ })), e.onLayerSelect && n("layer:select", ((a) => {
2579
+ e.onLayerSelect?.(a.detail.layerId);
2580
+ })), e.onLayerAdd && n("layer:add", ((a) => {
2581
+ e.onLayerAdd?.(a.detail.layerId);
2582
+ })), e.onLayerRemove && n("layer:remove", ((a) => {
2583
+ e.onLayerRemove?.(a.detail.layerId);
2584
+ })), e.onLayerUpdate && n("layer:update", ((a) => {
2585
+ e.onLayerUpdate?.(a.detail.layerId);
2586
+ })), e.onError && n("error", ((a) => {
2587
+ const r = a.detail.error;
2439
2588
  e.onError?.(r);
2440
- })), e.onViewChange && a("view-change", ((n) => {
2441
- e.onViewChange?.(n.detail.viewName);
2442
- })), e.onClose && a("customizer:close", (() => {
2589
+ })), e.onViewChange && n("view-change", ((a) => {
2590
+ e.onViewChange?.(a.detail.viewName);
2591
+ })), e.onRenderComplete && n("customizer:render-complete", ((a) => {
2592
+ e.onRenderComplete?.(a.detail);
2593
+ })), e.onClose && n("customizer:close", (() => {
2443
2594
  e.onClose?.();
2444
- })), e.onSave && a("customizer:save", ((n) => {
2445
- e.onSave?.(n.detail);
2446
- })), e.initialDesign && t.addEventListener(
2595
+ })), e.onSave && n("customizer:save", ((a) => {
2596
+ e.onSave?.(a.detail);
2597
+ })), e.initialDesign && i.addEventListener(
2447
2598
  "ready",
2448
2599
  () => {
2449
- e.initialDesign && t.setDesign(e.initialDesign);
2600
+ e.initialDesign && i.setDesign(e.initialDesign);
2450
2601
  },
2451
2602
  { once: !0 }
2452
2603
  ), e.debug && (console.log("[Customizer SDK] Initialized with options:", e), console.log("[Customizer SDK] Instance:", o)), o;