@varianta/sdk 0.1.4 → 0.1.6

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
@@ -129,7 +129,7 @@ class N {
129
129
  getCenteredPan(e) {
130
130
  if (!this.artboard)
131
131
  return null;
132
- const { width: t, height: i } = this.artboard, n = window.devicePixelRatio || 1, a = this.canvas.width / n, o = this.canvas.height / n;
132
+ const { width: t, height: i } = this.artboard, s = window.devicePixelRatio || 1, a = this.canvas.width / s, o = this.canvas.height / s;
133
133
  return {
134
134
  x: (a / e - t) / 2,
135
135
  y: (o / e - i) / 2
@@ -147,10 +147,10 @@ class N {
147
147
  fitToView() {
148
148
  if (!this.artboard)
149
149
  return;
150
- const e = 0, { width: t, height: i } = this.artboard, n = window.devicePixelRatio || 1, a = this.canvas.width / n - e * 2, o = this.canvas.height / n - e * 2;
150
+ const e = 0, { width: t, height: i } = this.artboard, s = window.devicePixelRatio || 1, a = this.canvas.width / s - e * 2, o = this.canvas.height / s - e * 2;
151
151
  if (a <= 0 || o <= 0)
152
152
  return;
153
- const s = a / t, r = o / i, d = Math.min(s, r), l = this.canvas.width / n, c = this.canvas.height / n, u = (l / d - t) / 2, p = (c / d - i) / 2;
153
+ const n = a / t, r = o / i, d = Math.min(n, r), l = this.canvas.width / s, c = this.canvas.height / s, u = (l / d - t) / 2, p = (c / d - i) / 2;
154
154
  this.zoom = d, this.pan = { x: u, y: p }, this.requestRender();
155
155
  }
156
156
  /**
@@ -181,8 +181,8 @@ class N {
181
181
  * Convert screen coordinates to canvas coordinates
182
182
  */
183
183
  screenToCanvas(e, t) {
184
- const i = window.devicePixelRatio || 1, n = e / i, a = t / i, o = n / this.zoom - this.pan.x, s = a / this.zoom - this.pan.y;
185
- return { x: o, y: s };
184
+ const i = window.devicePixelRatio || 1, s = e / i, a = t / i, o = s / this.zoom - this.pan.x, n = a / this.zoom - this.pan.y;
185
+ return { x: o, y: n };
186
186
  }
187
187
  /**
188
188
  * Get content bounds for an area (in artboard coordinates)
@@ -194,9 +194,9 @@ class N {
194
194
  const i = this.contents.get(e);
195
195
  if (!i)
196
196
  return null;
197
- const { location: n } = t, { x: a, y: o, width: s, height: r } = n;
197
+ const { location: s } = t, { x: a, y: o, width: n, height: r } = s;
198
198
  if (i.type === "text") {
199
- const d = i.offset || { x: 0, y: 0 }, l = i.scale ?? 1, c = i.rotation ?? 0, u = s * l, p = r * l, g = a + s / 2 + d.x, m = o + r / 2 + d.y;
199
+ const d = i.offset || { x: 0, y: 0 }, l = i.scale ?? 1, c = i.rotation ?? 0, u = n * l, p = r * l, g = a + n / 2 + d.x, m = o + r / 2 + d.y;
200
200
  return {
201
201
  x: g - u / 2,
202
202
  y: m - p / 2,
@@ -210,10 +210,10 @@ class N {
210
210
  const d = this.loadedImages.get(e);
211
211
  if (!d)
212
212
  return null;
213
- const l = i.offset || { x: 0, y: 0 }, c = i.scale ?? 1, u = i.rotation ?? 0, p = d.naturalWidth / d.naturalHeight, g = s / r;
213
+ const l = i.offset || { x: 0, y: 0 }, c = i.scale ?? 1, u = i.rotation ?? 0, p = d.naturalWidth / d.naturalHeight, g = n / r;
214
214
  let m, v;
215
- p > g ? (m = s, v = s / p) : (v = r, m = r * p);
216
- const b = m * c, y = v * c, x = a + s / 2 + l.x, w = o + r / 2 + l.y;
215
+ p > g ? (m = n, v = n / p) : (v = r, m = r * p);
216
+ const b = m * c, y = v * c, x = a + n / 2 + l.x, w = o + r / 2 + l.y;
217
217
  return {
218
218
  x: x - b / 2,
219
219
  y: w - y / 2,
@@ -234,9 +234,9 @@ class N {
234
234
  if (!this.areaSelectionEnabled)
235
235
  return null;
236
236
  for (let i = this.areas.length - 1; i >= 0; i--) {
237
- const n = this.areas[i], a = this.getContentBounds(n.id);
237
+ const s = this.areas[i], a = this.getContentBounds(s.id);
238
238
  if (a && this.isPointInBounds(e, t, a))
239
- return n.id;
239
+ return s.id;
240
240
  }
241
241
  return null;
242
242
  }
@@ -248,9 +248,9 @@ class N {
248
248
  if (!this.areaSelectionEnabled)
249
249
  return null;
250
250
  for (let i = this.areas.length - 1; i >= 0; i--) {
251
- const n = this.areas[i], { x: a, y: o, width: s, height: r } = n.location;
252
- if (e >= a && e <= a + s && t >= o && t <= o + r)
253
- return n.id;
251
+ const s = this.areas[i], { x: a, y: o, width: n, height: r } = s.location;
252
+ if (e >= a && e <= a + n && t >= o && t <= o + r)
253
+ return s.id;
254
254
  }
255
255
  return null;
256
256
  }
@@ -258,8 +258,8 @@ class N {
258
258
  * Test if a point is inside rotated bounds
259
259
  */
260
260
  isPointInBounds(e, t, i) {
261
- const { centerX: n, centerY: a, width: o, height: s, rotation: r } = i, d = -r * Math.PI / 180, l = Math.cos(d), c = Math.sin(d), u = e - n, p = t - a, g = u * l - p * c, m = u * c + p * l;
262
- return g >= -o / 2 && g <= o / 2 && m >= -s / 2 && m <= s / 2;
261
+ const { centerX: s, centerY: a, width: o, height: n, rotation: r } = i, d = -r * Math.PI / 180, l = Math.cos(d), c = Math.sin(d), u = e - s, p = t - a, g = u * l - p * c, m = u * c + p * l;
262
+ return g >= -o / 2 && g <= o / 2 && m >= -n / 2 && m <= n / 2;
263
263
  }
264
264
  /**
265
265
  * Get handle positions for the selected content
@@ -271,22 +271,22 @@ class N {
271
271
  const i = this.contents.get(e);
272
272
  if (!i)
273
273
  return null;
274
- const n = this.getContentBounds(e);
275
- if (!n)
274
+ const s = this.getContentBounds(e);
275
+ if (!s)
276
276
  return null;
277
- const { centerX: a, centerY: o, width: s, height: r, rotation: d } = n, l = 6 / this.zoom, c = 30 / this.zoom, u = d * Math.PI / 180, p = Math.cos(u), g = Math.sin(u), m = (b, y) => ({
277
+ const { centerX: a, centerY: o, width: n, height: r, rotation: d } = s, l = 6 / this.zoom, c = 30 / this.zoom, u = d * Math.PI / 180, p = Math.cos(u), g = Math.sin(u), m = (b, y) => ({
278
278
  x: a + b * p - y * g,
279
279
  y: o + b * g + y * p
280
280
  }), v = [];
281
281
  if (i.type === "image" && t.imageOptions?.allowScaling || i.type === "text" && t.textOptions?.allowScaling) {
282
- const b = s / 2, y = r / 2, x = m(-b, -y);
282
+ const b = n / 2, y = r / 2, x = m(-b, -y);
283
283
  v.push({ type: "nw", ...x, radius: l });
284
284
  const w = m(b, -y);
285
285
  v.push({ type: "ne", ...w, radius: l });
286
286
  const C = m(b, y);
287
287
  v.push({ type: "se", ...C, radius: l });
288
- const M = m(-b, y);
289
- v.push({ type: "sw", ...M, radius: l });
288
+ const z = m(-b, y);
289
+ v.push({ type: "sw", ...z, radius: l });
290
290
  }
291
291
  if (i.type === "image" && t.imageOptions?.allowRotation || i.type === "text" && t.textOptions?.allowRotation) {
292
292
  const b = m(0, -r / 2 - c);
@@ -298,12 +298,12 @@ class N {
298
298
  * Hit test handles at given canvas coordinates
299
299
  */
300
300
  hitTestHandle(e, t, i) {
301
- const n = this.getContentHandlePositions(i);
302
- if (!n)
301
+ const s = this.getContentHandlePositions(i);
302
+ if (!s)
303
303
  return null;
304
- for (const a of n) {
305
- const o = e - a.x, s = t - a.y;
306
- if (Math.sqrt(o * o + s * s) <= a.radius * 2)
304
+ for (const a of s) {
305
+ const o = e - a.x, n = t - a.y;
306
+ if (Math.sqrt(o * o + n * n) <= a.radius * 2)
307
307
  return a.type;
308
308
  }
309
309
  return null;
@@ -312,7 +312,7 @@ class N {
312
312
  * Check if positioning is allowed for an area
313
313
  */
314
314
  isPositioningAllowed(e) {
315
- const t = this.areas.find((n) => n.id === e);
315
+ const t = this.areas.find((s) => s.id === e);
316
316
  if (!t)
317
317
  return !1;
318
318
  const i = this.contents.get(e);
@@ -344,15 +344,15 @@ class N {
344
344
  checkSafeAreaViolations() {
345
345
  if (!this.safeArea || !this.artboard)
346
346
  return [];
347
- const e = [], { top: t, right: i, bottom: n, left: a } = this.safeArea, o = a, s = t, r = this.artboard.width - i, d = this.artboard.height - n;
347
+ const e = [], { top: t, right: i, bottom: s, left: a } = this.safeArea, o = a, n = 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
351
  const c = this.getContentBounds(l.id);
352
352
  if (!c)
353
353
  continue;
354
- const { centerX: u, centerY: p, width: g, height: m, rotation: v } = c, b = v * Math.PI / 180, y = Math.abs(Math.cos(b)), x = Math.abs(Math.sin(b)), w = g * y + m * x, C = g * x + m * y, M = u - w / 2, A = p - C / 2, D = u + w / 2, R = p + C / 2;
355
- (M < o || A < s || D > r || R > d) && e.push(l.id);
354
+ const { centerX: u, centerY: p, width: g, height: m, rotation: v } = c, b = v * Math.PI / 180, y = Math.abs(Math.cos(b)), x = Math.abs(Math.sin(b)), w = g * y + m * x, C = g * x + m * y, z = u - w / 2, A = p - C / 2, D = u + w / 2, R = p + C / 2;
355
+ (z < o || A < n || D > r || R > d) && e.push(l.id);
356
356
  }
357
357
  return this.safeAreaViolations = new Set(e), this.requestRender(), e;
358
358
  }
@@ -370,8 +370,8 @@ class N {
370
370
  emit(e, t) {
371
371
  const i = this.eventListeners.get(e);
372
372
  if (i)
373
- for (const n of i)
374
- n(t);
373
+ for (const s of i)
374
+ s(t);
375
375
  }
376
376
  /**
377
377
  * Export to PNG at full resolution
@@ -379,17 +379,17 @@ class N {
379
379
  async exportToPNG() {
380
380
  if (!this.artboard)
381
381
  throw new Error("No artboard configured");
382
- const { width: e, height: t, backgroundColor: i } = this.artboard, n = document.createElement("canvas");
383
- n.width = e, n.height = t;
384
- const a = n.getContext("2d");
382
+ const { width: e, height: t, backgroundColor: i } = this.artboard, s = document.createElement("canvas");
383
+ s.width = e, s.height = t;
384
+ const a = s.getContext("2d");
385
385
  if (!a)
386
386
  throw new Error("Failed to get 2D context for export");
387
387
  a.fillStyle = i, a.fillRect(0, 0, e, t), this.backgroundImage?.position === "behind" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, t);
388
388
  for (const o of this.areas)
389
389
  this.drawAreaContentOnContext(a, o);
390
- return this.backgroundImage?.position === "overlay" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, t), new Promise((o, s) => {
391
- n.toBlob((r) => {
392
- r ? o(r) : s(new Error("Failed to create PNG blob"));
390
+ return this.backgroundImage?.position === "overlay" && this.bgImageElement && this.drawBackgroundImageOnContext(a, e, t), new Promise((o, n) => {
391
+ s.toBlob((r) => {
392
+ r ? o(r) : n(new Error("Failed to create PNG blob"));
393
393
  }, "image/png", 1);
394
394
  });
395
395
  }
@@ -422,13 +422,13 @@ class N {
422
422
  e.save();
423
423
  const i = window.devicePixelRatio || 1;
424
424
  e.scale(i, i), e.scale(this.zoom, this.zoom), e.translate(this.pan.x, this.pan.y);
425
- const { width: n, height: a, backgroundColor: o } = this.artboard;
426
- e.fillStyle = o, e.fillRect(0, 0, n, a), e.strokeStyle = "#d1d5db", e.lineWidth = 1 / this.zoom, e.strokeRect(0, 0, n, a), this.backgroundImage?.position === "behind" && this.renderBackgroundImage();
427
- for (const s of this.areas)
428
- this.renderAreaWithContent(s);
425
+ const { width: s, height: a, backgroundColor: o } = this.artboard;
426
+ e.fillStyle = o, e.fillRect(0, 0, s, a), e.strokeStyle = "#d1d5db", e.lineWidth = 1 / this.zoom, e.strokeRect(0, 0, s, a), this.backgroundImage?.position === "behind" && this.renderBackgroundImage();
427
+ for (const n of this.areas)
428
+ this.renderAreaWithContent(n);
429
429
  this.backgroundImage?.position === "overlay" && this.renderBackgroundImage(), this.renderSafeAreaGuide();
430
- for (const s of this.safeAreaViolations) {
431
- const r = this.areas.find((p) => p.id === s);
430
+ for (const n of this.safeAreaViolations) {
431
+ const r = this.areas.find((p) => p.id === n);
432
432
  if (!r)
433
433
  continue;
434
434
  const { x: d, y: l, width: c, height: u } = r.location;
@@ -449,15 +449,15 @@ class N {
449
449
  c.save(), c.strokeStyle = "#3b82f6", c.lineWidth = 2 / this.zoom, c.setLineDash([]), c.strokeRect(u, p, g, m), c.restore();
450
450
  return;
451
451
  }
452
- const { ctx: i } = this, { centerX: n, centerY: a, width: o, height: s, rotation: r } = t;
453
- i.save(), i.translate(n, a), i.rotate(r * Math.PI / 180), i.strokeStyle = "#3b82f6", i.lineWidth = 2 / this.zoom, i.setLineDash([]), i.strokeRect(-o / 2, -s / 2, o, s), i.restore();
452
+ const { ctx: i } = this, { centerX: s, centerY: a, width: o, height: n, rotation: r } = t;
453
+ i.save(), i.translate(s, a), i.rotate(r * Math.PI / 180), i.strokeStyle = "#3b82f6", i.lineWidth = 2 / this.zoom, i.setLineDash([]), i.strokeRect(-o / 2, -n / 2, o, n), i.restore();
454
454
  const d = this.getContentHandlePositions(e);
455
455
  if (d)
456
456
  for (const l of d) {
457
457
  i.save();
458
458
  const c = this.hoveredHandle === l.type, u = c ? "#dbeafe" : "#ffffff", p = c ? "#2563eb" : "#3b82f6";
459
459
  if (l.type === "rotate") {
460
- const g = this.getRotatedPoint(n, a - s / 2, n, a, r);
460
+ const g = this.getRotatedPoint(s, a - n / 2, s, a, r);
461
461
  i.beginPath(), i.strokeStyle = p, i.lineWidth = 1 / this.zoom, i.setLineDash([4 / this.zoom, 4 / this.zoom]), i.moveTo(g.x, g.y), i.lineTo(l.x, l.y), i.stroke(), i.setLineDash([]), i.beginPath(), i.fillStyle = u, i.strokeStyle = p, i.lineWidth = 2 / this.zoom, i.arc(l.x, l.y, l.radius, 0, Math.PI * 2), i.fill(), i.stroke(), i.beginPath(), i.strokeStyle = p, i.lineWidth = 1.5 / this.zoom, i.arc(l.x, l.y, l.radius * 0.5, -Math.PI * 0.8, Math.PI * 0.5), i.stroke();
462
462
  } else
463
463
  i.fillStyle = u, i.strokeStyle = p, i.lineWidth = 2 / this.zoom, i.fillRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2), i.strokeRect(l.x - l.radius, l.y - l.radius, l.radius * 2, l.radius * 2);
@@ -467,11 +467,11 @@ class N {
467
467
  /**
468
468
  * Rotate a point around a center
469
469
  */
470
- getRotatedPoint(e, t, i, n, a) {
471
- const o = a * Math.PI / 180, s = Math.cos(o), r = Math.sin(o), d = e - i, l = t - n;
470
+ getRotatedPoint(e, t, i, s, a) {
471
+ const o = a * Math.PI / 180, n = Math.cos(o), r = Math.sin(o), d = e - i, l = t - s;
472
472
  return {
473
- x: i + d * s - l * r,
474
- y: n + d * r + l * s
473
+ x: i + d * n - l * r,
474
+ y: s + d * r + l * n
475
475
  };
476
476
  }
477
477
  /**
@@ -480,11 +480,11 @@ class N {
480
480
  renderBackgroundImage() {
481
481
  if (!this.bgImageElement || !this.artboard || !this.backgroundImage)
482
482
  return;
483
- const { ctx: e } = this, { opacity: t } = this.backgroundImage, { width: i, height: n } = this.artboard;
483
+ const { ctx: e } = this, { opacity: t } = this.backgroundImage, { width: i, height: s } = this.artboard;
484
484
  e.save(), e.globalAlpha = t;
485
- const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = i / n;
486
- let s, r, d, l;
487
- a > o ? (r = n, s = n * a, d = (i - s) / 2, l = 0) : (s = i, r = i / a, d = 0, l = (n - r) / 2), e.drawImage(this.bgImageElement, d, l, s, r), e.restore();
485
+ const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = i / s;
486
+ let n, r, d, l;
487
+ a > o ? (r = s, n = s * a, d = (i - n) / 2, l = 0) : (n = i, r = i / a, d = 0, l = (s - r) / 2), e.drawImage(this.bgImageElement, d, l, n, r), e.restore();
488
488
  }
489
489
  /**
490
490
  * Render safe area guide as dashed green rectangle
@@ -492,17 +492,17 @@ class N {
492
492
  renderSafeAreaGuide() {
493
493
  if (!this.safeArea || !this.artboard)
494
494
  return;
495
- const { ctx: e } = this, { top: t, right: i, bottom: n, left: a } = this.safeArea, o = a, s = t, r = this.artboard.width - a - i, d = this.artboard.height - t - n;
496
- r <= 0 || d <= 0 || (e.save(), e.strokeStyle = "#22c55e", e.lineWidth = 1 / this.zoom, e.setLineDash([6 / this.zoom, 4 / this.zoom]), e.strokeRect(o, s, r, d), e.setLineDash([]), e.restore());
495
+ const { ctx: e } = this, { top: t, right: i, bottom: s, left: a } = this.safeArea, o = a, n = t, r = this.artboard.width - a - i, d = this.artboard.height - t - s;
496
+ r <= 0 || d <= 0 || (e.save(), e.strokeStyle = "#22c55e", e.lineWidth = 1 / this.zoom, e.setLineDash([6 / this.zoom, 4 / this.zoom]), e.strokeRect(o, n, r, d), e.setLineDash([]), e.restore());
497
497
  }
498
498
  /**
499
499
  * Render an area with its content
500
500
  */
501
501
  renderAreaWithContent(e) {
502
- const { ctx: t } = this, { location: i, backgroundColor: n } = e, { x: a, y: o, width: s, height: r } = i;
503
- n && (t.fillStyle = n, t.fillRect(a, o, s, r));
502
+ const { ctx: t } = this, { location: i, backgroundColor: s } = e, { x: a, y: o, width: n, height: r } = i;
503
+ s && (t.fillStyle = s, t.fillRect(a, o, n, r));
504
504
  const d = this.contents.get(e.id);
505
- d && (d.type === "text" ? this.renderTextContent(e, d) : d.type === "image" && this.renderImageContent(e, d)), this.showAreaBorders && e.showBorder && (t.save(), t.strokeStyle = e.borderColor || "#3b82f6", t.lineWidth = 1 / this.zoom, t.setLineDash([4 / this.zoom, 4 / this.zoom]), t.strokeRect(a, o, s, r), t.setLineDash([]), t.restore());
505
+ d && (d.type === "text" ? this.renderTextContent(e, d) : d.type === "image" && this.renderImageContent(e, d)), this.showAreaBorders && e.showBorder && (t.save(), t.strokeStyle = e.borderColor || "#3b82f6", t.lineWidth = 1 / this.zoom, t.setLineDash([4 / this.zoom, 4 / this.zoom]), t.strokeRect(a, o, n, r), t.setLineDash([]), t.restore());
506
506
  }
507
507
  /**
508
508
  * Render text content in an area
@@ -510,15 +510,15 @@ class N {
510
510
  renderTextContent(e, t) {
511
511
  if (!t.text.trim())
512
512
  return;
513
- const { ctx: i } = this, { location: n } = e, { x: a, y: o, width: s, height: r } = n, d = t.offset || { x: 0, y: 0 }, l = t.scale ?? 1, c = t.rotation ?? 0, u = a + s / 2 + d.x, p = o + r / 2 + d.y;
514
- i.save(), i.beginPath(), i.rect(a, o, s, r), i.clip(), i.translate(u, p), i.rotate(c * Math.PI / 180), i.scale(l, l), i.font = `${t.size}px ${t.font}, sans-serif`, i.fillStyle = t.color, i.textBaseline = "middle";
513
+ const { ctx: i } = this, { location: s } = e, { x: a, y: o, width: n, height: r } = s, d = t.offset || { x: 0, y: 0 }, l = t.scale ?? 1, c = t.rotation ?? 0, u = a + n / 2 + d.x, p = o + r / 2 + d.y;
514
+ i.save(), i.beginPath(), i.rect(a, o, n, r), i.clip(), i.translate(u, p), i.rotate(c * Math.PI / 180), i.scale(l, l), i.font = `${t.size}px ${t.font}, sans-serif`, i.fillStyle = t.color, i.textBaseline = "middle";
515
515
  let g;
516
516
  switch (t.align) {
517
517
  case "left":
518
- i.textAlign = "left", g = -s / 2 + 10;
518
+ i.textAlign = "left", g = -n / 2 + 10;
519
519
  break;
520
520
  case "right":
521
- i.textAlign = "right", g = s / 2 - 10;
521
+ i.textAlign = "right", g = n / 2 - 10;
522
522
  break;
523
523
  case "center":
524
524
  default:
@@ -534,13 +534,13 @@ class N {
534
534
  const i = this.loadedImages.get(e.id);
535
535
  if (!i)
536
536
  return;
537
- const { ctx: n } = this, { location: a } = e, { x: o, y: s, width: r, height: d } = a, l = t.offset || { x: 0, y: 0 }, c = t.scale ?? 1, u = t.rotation ?? 0;
538
- n.save(), n.beginPath(), n.rect(o, s, r, d), n.clip();
537
+ const { ctx: s } = this, { location: a } = e, { x: o, y: n, width: r, height: d } = a, l = t.offset || { x: 0, y: 0 }, c = t.scale ?? 1, u = t.rotation ?? 0;
538
+ s.save(), s.beginPath(), s.rect(o, n, r, d), s.clip();
539
539
  const p = i.naturalWidth / i.naturalHeight, g = r / d;
540
540
  let m, v;
541
541
  p > g ? (m = r, v = r / p) : (v = d, m = d * p);
542
- const b = m * c, y = v * c, x = o + r / 2 + l.x, w = s + d / 2 + l.y;
543
- n.translate(x, w), n.rotate(u * Math.PI / 180), n.drawImage(i, -b / 2, -y / 2, b, y), n.restore();
542
+ const b = m * c, y = v * c, x = o + r / 2 + l.x, w = n + d / 2 + l.y;
543
+ s.translate(x, w), s.rotate(u * Math.PI / 180), s.drawImage(i, -b / 2, -y / 2, b, y), s.restore();
544
544
  }
545
545
  /**
546
546
  * Draw background image on a specific context (for export)
@@ -548,32 +548,32 @@ class N {
548
548
  drawBackgroundImageOnContext(e, t, i) {
549
549
  if (!this.bgImageElement || !this.backgroundImage)
550
550
  return;
551
- const { opacity: n } = this.backgroundImage;
552
- e.save(), e.globalAlpha = n;
551
+ const { opacity: s } = this.backgroundImage;
552
+ e.save(), e.globalAlpha = s;
553
553
  const a = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = t / i;
554
- let s, r, d, l;
555
- a > o ? (r = i, s = i * a, d = (t - s) / 2, l = 0) : (s = t, r = t / a, d = 0, l = (i - r) / 2), e.drawImage(this.bgImageElement, d, l, s, r), e.restore();
554
+ let n, r, d, l;
555
+ a > o ? (r = i, n = i * a, d = (t - n) / 2, l = 0) : (n = t, r = t / a, d = 0, l = (i - r) / 2), e.drawImage(this.bgImageElement, d, l, n, r), e.restore();
556
556
  }
557
557
  /**
558
558
  * Draw area content on a specific context (for export)
559
559
  */
560
560
  drawAreaContentOnContext(e, t) {
561
- const { location: i, backgroundColor: n } = t, { x: a, y: o, width: s, height: r } = i;
562
- n && (e.fillStyle = n, e.fillRect(a, o, s, r));
561
+ const { location: i, backgroundColor: s } = t, { x: a, y: o, width: n, height: r } = i;
562
+ s && (e.fillStyle = s, e.fillRect(a, o, n, r));
563
563
  const d = this.contents.get(t.id);
564
564
  if (d) {
565
565
  if (d.type === "text") {
566
566
  if (!d.text.trim())
567
567
  return;
568
- const l = d.offset || { x: 0, y: 0 }, c = d.scale ?? 1, u = d.rotation ?? 0, p = a + s / 2 + l.x, g = o + r / 2 + l.y;
569
- e.save(), e.beginPath(), e.rect(a, o, s, r), e.clip(), e.translate(p, g), e.rotate(u * Math.PI / 180), e.scale(c, c), e.font = `${d.size}px ${d.font}, sans-serif`, e.fillStyle = d.color, e.textBaseline = "middle";
568
+ const l = d.offset || { x: 0, y: 0 }, c = d.scale ?? 1, u = d.rotation ?? 0, p = a + n / 2 + l.x, g = o + r / 2 + l.y;
569
+ e.save(), e.beginPath(), e.rect(a, o, n, r), e.clip(), e.translate(p, g), e.rotate(u * Math.PI / 180), e.scale(c, c), e.font = `${d.size}px ${d.font}, sans-serif`, e.fillStyle = d.color, e.textBaseline = "middle";
570
570
  let m;
571
571
  switch (d.align) {
572
572
  case "left":
573
- e.textAlign = "left", m = -s / 2 + 10;
573
+ e.textAlign = "left", m = -n / 2 + 10;
574
574
  break;
575
575
  case "right":
576
- e.textAlign = "right", m = s / 2 - 10;
576
+ e.textAlign = "right", m = n / 2 - 10;
577
577
  break;
578
578
  case "center":
579
579
  default:
@@ -586,11 +586,11 @@ class N {
586
586
  if (!l)
587
587
  return;
588
588
  const c = d.offset || { x: 0, y: 0 }, u = d.scale ?? 1, p = d.rotation ?? 0;
589
- e.save(), e.beginPath(), e.rect(a, o, s, r), e.clip();
590
- const g = l.naturalWidth / l.naturalHeight, m = s / r;
589
+ e.save(), e.beginPath(), e.rect(a, o, n, r), e.clip();
590
+ const g = l.naturalWidth / l.naturalHeight, m = n / r;
591
591
  let v, b;
592
- g > m ? (v = s, b = s / g) : (b = r, v = r * g);
593
- const y = v * u, x = b * u, w = a + s / 2 + c.x, C = o + r / 2 + c.y;
592
+ g > m ? (v = n, b = n / g) : (b = r, v = r * g);
593
+ const y = v * u, x = b * u, w = a + n / 2 + c.x, C = o + r / 2 + c.y;
594
594
  e.translate(w, C), e.rotate(p * Math.PI / 180), e.drawImage(l, -y / 2, -x / 2, y, x), e.restore();
595
595
  }
596
596
  }
@@ -616,11 +616,11 @@ function E(f) {
616
616
  function S(f) {
617
617
  return f.contentType === "text" || f.contentType === "both";
618
618
  }
619
- function k(f) {
619
+ function T(f) {
620
620
  return f.contentType === "image" || f.contentType === "both";
621
621
  }
622
622
  const U = 50;
623
- class O {
623
+ class V {
624
624
  constructor(e) {
625
625
  this.history = [], this.historyIndex = -1, this.listeners = /* @__PURE__ */ new Set(), this.isRestoring = !1, this.state = this.cloneState(e), this.history.push({
626
626
  state: this.cloneState(e),
@@ -635,8 +635,8 @@ class O {
635
635
  * Update state and save to history
636
636
  */
637
637
  setState(e, t, i = !1) {
638
- const n = this.state;
639
- this.state = { ...this.state, ...e }, !i && !this.isRestoring && this.saveToHistory(n, t), this.notifyListeners();
638
+ const s = this.state;
639
+ this.state = { ...this.state, ...e }, !i && !this.isRestoring && this.saveToHistory(s, t), this.notifyListeners();
640
640
  }
641
641
  /**
642
642
  * Update state without saving to history
@@ -694,7 +694,7 @@ class O {
694
694
  return JSON.parse(JSON.stringify(e));
695
695
  }
696
696
  }
697
- class T {
697
+ class k {
698
698
  constructor(e) {
699
699
  this.listeners = /* @__PURE__ */ new Set(), this.areas = e, this.contents = this.buildInitialContents();
700
700
  }
@@ -711,7 +711,7 @@ class T {
711
711
  return this.contents.get(e);
712
712
  }
713
713
  setTextContent(e, t) {
714
- const i = this.contents.get(e), n = this.areas.find((o) => o.id === e), a = n ? E(n) : {
714
+ const i = this.contents.get(e), s = this.areas.find((o) => o.id === e), a = s ? E(s) : {
715
715
  type: "text",
716
716
  text: "",
717
717
  font: "Inter",
@@ -722,12 +722,12 @@ class T {
722
722
  i?.type === "text" ? this.contents.set(e, { ...i, ...t }) : this.contents.set(e, { ...a, ...t }), this.notify();
723
723
  }
724
724
  setImageContent(e, t, i) {
725
- const n = {
725
+ const s = {
726
726
  type: "image",
727
727
  dataUrl: t,
728
728
  filename: i
729
729
  };
730
- this.contents.set(e, n), this.notify();
730
+ this.contents.set(e, s), this.notify();
731
731
  }
732
732
  clearContent(e) {
733
733
  const t = this.areas.find((i) => i.id === e);
@@ -740,14 +740,14 @@ class T {
740
740
  setImageScale(e, t) {
741
741
  const i = this.contents.get(e);
742
742
  if (!i || i.type !== "image") return;
743
- const n = Math.max(0.1, Math.min(5, t));
744
- this.contents.set(e, { ...i, scale: n }), this.notify();
743
+ const s = Math.max(0.1, Math.min(5, t));
744
+ this.contents.set(e, { ...i, scale: s }), this.notify();
745
745
  }
746
746
  setImageRotation(e, t) {
747
747
  const i = this.contents.get(e);
748
748
  if (!i || i.type !== "image") return;
749
- let n = t % 360;
750
- n < 0 && (n += 360), this.contents.set(e, { ...i, rotation: n }), this.notify();
749
+ let s = t % 360;
750
+ s < 0 && (s += 360), this.contents.set(e, { ...i, rotation: s }), this.notify();
751
751
  }
752
752
  reset() {
753
753
  this.contents = this.buildInitialContents(), this.notify();
@@ -798,8 +798,8 @@ class I {
798
798
  * @returns Unsubscribe function
799
799
  */
800
800
  once(e, t) {
801
- const i = (n) => {
802
- t(n), this.off(e, i);
801
+ const i = (s) => {
802
+ t(s), this.off(e, i);
803
803
  };
804
804
  return this.on(e, i);
805
805
  }
@@ -822,8 +822,8 @@ class I {
822
822
  emit(e, t) {
823
823
  const i = this.listeners.get(e);
824
824
  if (i) {
825
- const n = Array.from(i);
826
- for (const a of n)
825
+ const s = Array.from(i);
826
+ for (const a of s)
827
827
  try {
828
828
  a(t);
829
829
  } catch (o) {
@@ -849,7 +849,7 @@ class I {
849
849
  return this.listeners.get(e)?.size ?? 0;
850
850
  }
851
851
  }
852
- class V extends I {
852
+ class O extends I {
853
853
  constructor() {
854
854
  super(...arguments), this.activeTouches = /* @__PURE__ */ new Map(), this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1;
855
855
  }
@@ -881,14 +881,14 @@ class V extends I {
881
881
  }
882
882
  if (this.activeTouches.size === 2 && this.initialPinchDistance !== null && this.lastPinchCenter) {
883
883
  e.preventDefault();
884
- const [t, i] = Array.from(this.activeTouches.values()), n = this.getDistance(t, i), a = this.getCenter(t, i), o = n / this.initialPinchDistance;
884
+ const [t, i] = Array.from(this.activeTouches.values()), s = this.getDistance(t, i), a = this.getCenter(t, i), o = s / this.initialPinchDistance;
885
885
  this.emit("pinch", {
886
886
  scale: o,
887
887
  centerX: a.x,
888
888
  centerY: a.y
889
889
  });
890
- const s = a.x - this.lastPinchCenter.x, r = a.y - this.lastPinchCenter.y;
891
- (Math.abs(s) > 0.5 || Math.abs(r) > 0.5) && this.emit("pan", { deltaX: s, deltaY: r }), this.initialPinchDistance = n, this.lastPinchCenter = a;
890
+ const n = a.x - this.lastPinchCenter.x, r = a.y - this.lastPinchCenter.y;
891
+ (Math.abs(n) > 0.5 || Math.abs(r) > 0.5) && this.emit("pan", { deltaX: n, deltaY: r }), this.initialPinchDistance = s, this.lastPinchCenter = a;
892
892
  }
893
893
  }
894
894
  handleTouchEnd(e) {
@@ -902,8 +902,8 @@ class V extends I {
902
902
  this.activeTouches.clear(), this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1;
903
903
  }
904
904
  getDistance(e, t) {
905
- const i = t.x - e.x, n = t.y - e.y;
906
- return Math.sqrt(i * i + n * n);
905
+ const i = t.x - e.x, s = t.y - e.y;
906
+ return Math.sqrt(i * i + s * s);
907
907
  }
908
908
  getCenter(e, t) {
909
909
  return {
@@ -912,23 +912,23 @@ class V extends I {
912
912
  };
913
913
  }
914
914
  }
915
- class F extends I {
916
- constructor(e, t, i, n) {
917
- super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine = t, this.getContents = i, this.getSelectedAreaId = n, this.gestureHandler = new V(), this.boundMouseDown = this.handleMouseDown.bind(this), this.boundMouseMove = this.handleMouseMove.bind(this), this.boundMouseUp = this.handleMouseUp.bind(this), this.boundMouseLeave = this.handleMouseLeave.bind(this), this.boundContextMenu = this.handleContextMenu.bind(this), this.boundDoubleClick = this.handleDoubleClick.bind(this), this.boundTouchStart = this.handleTouchStart.bind(this), this.boundTouchMove = this.handleTouchMove.bind(this), this.boundTouchEnd = this.handleTouchEnd.bind(this), e.addEventListener("mousedown", this.boundMouseDown), e.addEventListener("mousemove", this.boundMouseMove), e.addEventListener("mouseup", this.boundMouseUp), e.addEventListener("mouseleave", this.boundMouseLeave), e.addEventListener("contextmenu", this.boundContextMenu), e.addEventListener("dblclick", this.boundDoubleClick), e.addEventListener("touchstart", this.boundTouchStart, { passive: !1 }), e.addEventListener("touchmove", this.boundTouchMove, { passive: !1 }), e.addEventListener("touchend", this.boundTouchEnd), this.gestureHandler.on("pinch", ({ scale: a }) => {
915
+ class B extends I {
916
+ constructor(e, t, i, s) {
917
+ super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine = t, this.getContents = i, this.getSelectedAreaId = s, this.gestureHandler = new O(), this.boundMouseDown = this.handleMouseDown.bind(this), this.boundMouseMove = this.handleMouseMove.bind(this), this.boundMouseUp = this.handleMouseUp.bind(this), this.boundMouseLeave = this.handleMouseLeave.bind(this), this.boundContextMenu = this.handleContextMenu.bind(this), this.boundDoubleClick = this.handleDoubleClick.bind(this), this.boundTouchStart = this.handleTouchStart.bind(this), this.boundTouchMove = this.handleTouchMove.bind(this), this.boundTouchEnd = this.handleTouchEnd.bind(this), e.addEventListener("mousedown", this.boundMouseDown), e.addEventListener("mousemove", this.boundMouseMove), e.addEventListener("mouseup", this.boundMouseUp), e.addEventListener("mouseleave", this.boundMouseLeave), e.addEventListener("contextmenu", this.boundContextMenu), e.addEventListener("dblclick", this.boundDoubleClick), e.addEventListener("touchstart", this.boundTouchStart, { passive: !1 }), e.addEventListener("touchmove", this.boundTouchMove, { passive: !1 }), e.addEventListener("touchend", this.boundTouchEnd), this.gestureHandler.on("pinch", ({ scale: a }) => {
918
918
  const o = Math.max(0.1, Math.min(5, this.initialPinchZoom * a));
919
919
  this.emit("zoom", { zoom: o });
920
920
  }), this.gestureHandler.on("pan", ({ deltaX: a, deltaY: o }) => {
921
- const s = this.engine.getPan(), r = this.engine.getZoom();
921
+ const n = this.engine.getPan(), r = this.engine.getZoom();
922
922
  this.emit("pan", {
923
923
  pan: {
924
- x: s.x + a / r,
925
- y: s.y + o / r
924
+ x: n.x + a / r,
925
+ y: n.y + o / r
926
926
  }
927
927
  });
928
928
  });
929
929
  }
930
930
  getCanvasPosition(e, t) {
931
- const i = this.canvas.getBoundingClientRect(), n = window.devicePixelRatio || 1, a = (e - i.left) * n, o = (t - i.top) * n;
931
+ const i = this.canvas.getBoundingClientRect(), s = window.devicePixelRatio || 1, a = (e - i.left) * s, o = (t - i.top) * s;
932
932
  return this.engine.screenToCanvas(a, o);
933
933
  }
934
934
  handleMouseDown(e) {
@@ -978,34 +978,34 @@ class F extends I {
978
978
  if (t) {
979
979
  const o = this.engine.hitTestHandle(e.x, e.y, t);
980
980
  if (o) {
981
- const s = i.get(t), r = this.engine.getContentBounds(t);
982
- if (o === "rotate" && s?.type === "image" && r) {
981
+ const n = i.get(t), r = this.engine.getContentBounds(t);
982
+ if (o === "rotate" && n?.type === "image" && r) {
983
983
  const d = Math.atan2(e.y - r.centerY, e.x - r.centerX) * (180 / Math.PI);
984
984
  return this.interactionState = {
985
985
  mode: "rotating",
986
986
  areaId: t,
987
- startRotation: s.rotation ?? 0,
987
+ startRotation: n.rotation ?? 0,
988
988
  centerPoint: { x: r.centerX, y: r.centerY },
989
989
  startAngle: d
990
990
  }, !0;
991
- } else if (o !== "rotate" && s?.type === "image" && r)
991
+ } else if (o !== "rotate" && n?.type === "image" && r)
992
992
  return this.interactionState = {
993
993
  mode: "scaling",
994
994
  areaId: t,
995
995
  handle: o,
996
- startScale: s.scale ?? 1,
996
+ startScale: n.scale ?? 1,
997
997
  startMousePos: e,
998
998
  pivotPoint: { x: r.centerX, y: r.centerY }
999
999
  }, !0;
1000
1000
  }
1001
1001
  }
1002
- const n = this.engine.hitTestContent(e.x, e.y);
1003
- if (n) {
1004
- if (this.emit("area:select", { areaId: n }), this.engine.isPositioningAllowed(n)) {
1005
- const o = i.get(n)?.offset ?? { x: 0, y: 0 };
1002
+ const s = this.engine.hitTestContent(e.x, e.y);
1003
+ if (s) {
1004
+ if (this.emit("area:select", { areaId: s }), this.engine.isPositioningAllowed(s)) {
1005
+ const o = i.get(s)?.offset ?? { x: 0, y: 0 };
1006
1006
  return this.interactionState = {
1007
1007
  mode: "dragging",
1008
- areaId: n,
1008
+ areaId: s,
1009
1009
  startOffset: o,
1010
1010
  startMousePos: e
1011
1011
  }, !0;
@@ -1017,42 +1017,42 @@ class F extends I {
1017
1017
  }
1018
1018
  processPointerMove(e) {
1019
1019
  if (this.interactionState.mode === "dragging") {
1020
- const { areaId: t, startOffset: i, startMousePos: n } = this.interactionState, a = e.x - n.x, o = e.y - n.y;
1021
- let s = {
1020
+ const { areaId: t, startOffset: i, startMousePos: s } = this.interactionState, a = e.x - s.x, o = e.y - s.y;
1021
+ let n = {
1022
1022
  x: i.x + a,
1023
1023
  y: i.y + o
1024
1024
  };
1025
1025
  const r = this.engine.getAreaLocation(t);
1026
1026
  if (r) {
1027
1027
  const d = r.width / 2 - 10, l = r.height / 2 - 10;
1028
- s.x = Math.max(-d, Math.min(d, s.x)), s.y = Math.max(-l, Math.min(l, s.y));
1028
+ n.x = Math.max(-d, Math.min(d, n.x)), n.y = Math.max(-l, Math.min(l, n.y));
1029
1029
  }
1030
- this.emit("content:drag", { areaId: t, offset: s });
1030
+ this.emit("content:drag", { areaId: t, offset: n });
1031
1031
  return;
1032
1032
  }
1033
1033
  if (this.interactionState.mode === "scaling") {
1034
- const { areaId: t, startScale: i, startMousePos: n, pivotPoint: a } = this.interactionState, o = Math.sqrt(
1035
- Math.pow(n.x - a.x, 2) + Math.pow(n.y - a.y, 2)
1036
- ), s = Math.sqrt(
1034
+ const { areaId: t, startScale: i, startMousePos: s, pivotPoint: a } = this.interactionState, o = Math.sqrt(
1035
+ Math.pow(s.x - a.x, 2) + Math.pow(s.y - a.y, 2)
1036
+ ), n = Math.sqrt(
1037
1037
  Math.pow(e.x - a.x, 2) + Math.pow(e.y - a.y, 2)
1038
1038
  );
1039
1039
  if (o > 0) {
1040
- const r = s / o, d = Math.max(0.1, Math.min(5, i * r));
1040
+ const r = n / o, d = Math.max(0.1, Math.min(5, i * r));
1041
1041
  this.emit("content:scale", { areaId: t, scale: d });
1042
1042
  }
1043
1043
  return;
1044
1044
  }
1045
1045
  if (this.interactionState.mode === "rotating") {
1046
- const { areaId: t, startRotation: i, centerPoint: n, startAngle: a } = this.interactionState, o = Math.atan2(e.y - n.y, e.x - n.x) * (180 / Math.PI);
1047
- let s = i + (o - a);
1048
- s = s % 360, s < 0 && (s += 360);
1046
+ const { areaId: t, startRotation: i, centerPoint: s, startAngle: a } = this.interactionState, o = Math.atan2(e.y - s.y, e.x - s.x) * (180 / Math.PI);
1047
+ let n = i + (o - a);
1048
+ n = n % 360, n < 0 && (n += 360);
1049
1049
  const r = [0, 90, 180, 270, 360];
1050
1050
  for (const d of r)
1051
- if (Math.abs(s - d) < 5) {
1052
- s = d === 360 ? 0 : d;
1051
+ if (Math.abs(n - d) < 5) {
1052
+ n = d === 360 ? 0 : d;
1053
1053
  break;
1054
1054
  }
1055
- this.emit("content:rotate", { areaId: t, rotation: s });
1055
+ this.emit("content:rotate", { areaId: t, rotation: n });
1056
1056
  return;
1057
1057
  }
1058
1058
  this.updateCursor(e);
@@ -1064,8 +1064,8 @@ class F extends I {
1064
1064
  const t = this.getSelectedAreaId();
1065
1065
  let i = "default";
1066
1066
  if (t) {
1067
- const n = this.engine.hitTestHandle(e.x, e.y, t);
1068
- n ? i = n === "rotate" ? "crosshair" : n === "nw" || n === "se" ? "nwse-resize" : "nesw-resize" : this.engine.hitTestContent(e.x, e.y) === t && (i = this.engine.isPositioningAllowed(t) ? "move" : "pointer");
1067
+ const s = this.engine.hitTestHandle(e.x, e.y, t);
1068
+ s ? i = s === "rotate" ? "crosshair" : s === "nw" || s === "se" ? "nwse-resize" : "nesw-resize" : this.engine.hitTestContent(e.x, e.y) === t && (i = this.engine.isPositioningAllowed(t) ? "move" : "pointer");
1069
1069
  } else
1070
1070
  this.engine.hitTestContent(e.x, e.y) && (i = "pointer");
1071
1071
  this.emit("cursor", { cursor: i });
@@ -1077,7 +1077,7 @@ class F extends I {
1077
1077
  this.canvas.removeEventListener("mousedown", this.boundMouseDown), this.canvas.removeEventListener("mousemove", this.boundMouseMove), this.canvas.removeEventListener("mouseup", this.boundMouseUp), this.canvas.removeEventListener("mouseleave", this.boundMouseLeave), this.canvas.removeEventListener("contextmenu", this.boundContextMenu), this.canvas.removeEventListener("dblclick", this.boundDoubleClick), this.canvas.removeEventListener("touchstart", this.boundTouchStart), this.canvas.removeEventListener("touchmove", this.boundTouchMove), this.canvas.removeEventListener("touchend", this.boundTouchEnd), this.gestureHandler.reset(), this.clear();
1078
1078
  }
1079
1079
  }
1080
- class $ {
1080
+ class F {
1081
1081
  constructor(e = "/api") {
1082
1082
  this.baseUrl = e;
1083
1083
  }
@@ -1121,14 +1121,14 @@ class $ {
1121
1121
  })
1122
1122
  });
1123
1123
  if (!i.ok) {
1124
- let n = `Failed to finalize design (${i.status} ${i.statusText})
1124
+ let s = `Failed to finalize design (${i.status} ${i.statusText})
1125
1125
 
1126
1126
  `;
1127
1127
  if (i.status === 400) {
1128
1128
  const a = await i.json().catch(() => ({}));
1129
- n += a.error || "Invalid design data.";
1130
- } else i.status === 429 ? n += "Rate limit exceeded." : i.status >= 500 && (n += "Server error.");
1131
- throw new Error(n);
1129
+ s += a.error || "Invalid design data.";
1130
+ } else i.status === 429 ? s += "Rate limit exceeded." : i.status >= 500 && (s += "Server error.");
1131
+ throw new Error(s);
1132
1132
  }
1133
1133
  return i.json();
1134
1134
  } catch (i) {
@@ -1139,21 +1139,21 @@ class $ {
1139
1139
  try {
1140
1140
  const i = new FormData();
1141
1141
  i.append("shopId", t), i.append("file", e);
1142
- const n = await fetch(`${this.baseUrl}/assets/upload`, {
1142
+ const s = await fetch(`${this.baseUrl}/assets/upload`, {
1143
1143
  method: "POST",
1144
1144
  body: i
1145
1145
  });
1146
- if (!n.ok) {
1147
- let a = `Failed to upload asset (${n.status} ${n.statusText})
1146
+ if (!s.ok) {
1147
+ let a = `Failed to upload asset (${s.status} ${s.statusText})
1148
1148
 
1149
1149
  `;
1150
- if (n.status === 400) {
1151
- const o = await n.json().catch(() => ({}));
1150
+ if (s.status === 400) {
1151
+ const o = await s.json().catch(() => ({}));
1152
1152
  a += o.error || "Invalid file.";
1153
- } else n.status === 413 ? a += "File too large (max 15MB)." : n.status >= 500 && (a += "Server error.");
1153
+ } else s.status === 413 ? a += "File too large (max 15MB)." : s.status >= 500 && (a += "Server error.");
1154
1154
  throw new Error(a);
1155
1155
  }
1156
- return n.json();
1156
+ return s.json();
1157
1157
  } catch (i) {
1158
1158
  throw i instanceof Error && i.message.includes("Failed to fetch") ? new Error("Network error uploading asset") : i;
1159
1159
  }
@@ -1171,28 +1171,28 @@ class $ {
1171
1171
  }
1172
1172
  function h(f, e, ...t) {
1173
1173
  const i = document.createElement(f);
1174
- return e && Object.entries(e).forEach(([n, a]) => {
1175
- n === "class" ? i.className = a : n in i ? i[n] = a : i.setAttribute(n, String(a));
1176
- }), t.forEach((n) => {
1177
- typeof n == "string" ? i.appendChild(document.createTextNode(n)) : i.appendChild(n);
1174
+ return e && Object.entries(e).forEach(([s, a]) => {
1175
+ s === "class" ? i.className = a : s in i ? i[s] = a : i.setAttribute(s, String(a));
1176
+ }), t.forEach((s) => {
1177
+ typeof s == "string" ? i.appendChild(document.createTextNode(s)) : i.appendChild(s);
1178
1178
  }), i;
1179
1179
  }
1180
- function H(f, e) {
1180
+ function $(f, e) {
1181
1181
  let t = null, i = null;
1182
- const n = (...a) => {
1182
+ const s = (...a) => {
1183
1183
  i = a, t && clearTimeout(t), t = setTimeout(() => {
1184
1184
  i && f(...i), t = null, i = null;
1185
1185
  }, e);
1186
1186
  };
1187
- return n.flush = () => {
1187
+ return s.flush = () => {
1188
1188
  t && (clearTimeout(t), t = null), i && (f(...i), i = null);
1189
- }, n.cancel = () => {
1189
+ }, s.cancel = () => {
1190
1190
  t && (clearTimeout(t), t = null), i = null;
1191
- }, n;
1191
+ }, s;
1192
1192
  }
1193
- class B extends I {
1193
+ class H extends I {
1194
1194
  constructor() {
1195
- super(), this.percentLabel = h("span", { class: "zoom-percent" }, "100%");
1195
+ super(), this.saveEnabled = !0, this.percentLabel = h("span", { class: "zoom-percent" }, "100%");
1196
1196
  const e = h("button", {
1197
1197
  class: "zoom-btn",
1198
1198
  title: "Zoom out"
@@ -1210,13 +1210,11 @@ class B extends I {
1210
1210
  i.addEventListener("click", () => this.emit("zoom-in", void 0)), this.viewSelect = document.createElement("select"), this.viewSelect.className = "view-select", this.viewSelect.title = "Switch view", this.viewSelect.style.display = "none", this.viewSelect.addEventListener("change", () => {
1211
1211
  this.emit("view-change", { viewName: this.viewSelect.value });
1212
1212
  }), this.element = h("div", { class: "zoom-toolbar" }), this.element.appendChild(e), this.element.appendChild(t), this.element.appendChild(i), this.element.appendChild(this.percentLabel), this.element.appendChild(this.viewSelect);
1213
- const n = h("div", { class: "zoom-toolbar-spacer" });
1214
- this.element.appendChild(n);
1215
- const a = h("button", {
1213
+ const s = h("div", { class: "zoom-toolbar-spacer" });
1214
+ this.element.appendChild(s), this.closeBtn = h("button", {
1216
1215
  class: "zoom-btn zoom-close-btn",
1217
1216
  title: "Close editor"
1218
- }, "✕ Close");
1219
- a.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(a), this.saveBtn = h("button", {
1217
+ }, "✕ Close"), this.closeBtn.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(this.closeBtn), this.saveBtn = h("button", {
1220
1218
  class: "zoom-btn zoom-save-btn",
1221
1219
  title: "Save customization"
1222
1220
  }, "Save Customization"), this.saveBtn.style.display = "none", this.saveBtn.addEventListener("click", () => this.emit("save", void 0)), this.element.appendChild(this.saveBtn);
@@ -1225,7 +1223,13 @@ class B extends I {
1225
1223
  this.percentLabel.textContent = `${Math.round(e * 100)}%`;
1226
1224
  }
1227
1225
  showSaveButton(e) {
1228
- this.saveBtn.style.display = e ? "" : "none";
1226
+ this.saveEnabled && (this.saveBtn.style.display = e ? "" : "none");
1227
+ }
1228
+ setCloseButtonVisible(e) {
1229
+ this.closeBtn.style.display = e ? "" : "none";
1230
+ }
1231
+ setSaveButtonEnabled(e) {
1232
+ this.saveEnabled = e, e || (this.saveBtn.style.display = "none");
1229
1233
  }
1230
1234
  setSaveDisabled(e, t) {
1231
1235
  this.saveBtn.disabled = e, t && (this.saveBtn.textContent = t);
@@ -1259,23 +1263,23 @@ const Z = [
1259
1263
  class L extends I {
1260
1264
  constructor(e) {
1261
1265
  super(), this.mode = "text", this.isSelected = !1, this.textInputEl = null, this.fontSelectEl = null, this.sizeInputEl = null, this.colorInputEl = null, this.alignGroupEl = null, this.area = e;
1262
- const t = S(e), i = k(e);
1266
+ const t = S(e), i = T(e);
1263
1267
  this.mode = i && !t ? "image" : "text", this.element = h("div", { class: "area-card" }), this.element.addEventListener("click", (r) => {
1264
1268
  r.target.tagName === "INPUT" || r.target.tagName === "SELECT" || r.target.tagName === "BUTTON" || this.emit("select", { areaId: e.id });
1265
1269
  });
1266
- const n = h("div", { class: "area-card-header" }), a = h("div", { class: "area-card-name-row" }), o = h("span", { class: "area-card-name" }, e.name);
1270
+ const s = h("div", { class: "area-card-header" }), a = h("div", { class: "area-card-name-row" }), o = h("span", { class: "area-card-name" }, e.name);
1267
1271
  if (a.appendChild(o), e.required) {
1268
1272
  const r = h("span", { class: "area-card-badge" }, "Required");
1269
1273
  a.appendChild(r);
1270
1274
  }
1271
- n.appendChild(a);
1272
- const s = h("button", {
1275
+ s.appendChild(a);
1276
+ const n = h("button", {
1273
1277
  class: "area-card-reset-btn",
1274
1278
  title: "Reset content"
1275
1279
  }, "Reset");
1276
- if (s.addEventListener("click", (r) => {
1280
+ if (n.addEventListener("click", (r) => {
1277
1281
  r.stopPropagation(), this.emit("clear", { areaId: e.id });
1278
- }), n.appendChild(s), this.element.appendChild(n), t && i) {
1282
+ }), s.appendChild(n), this.element.appendChild(s), t && i) {
1279
1283
  const r = h("div", { class: "area-card-mode-switcher" }), d = h("button", {
1280
1284
  class: "mode-btn mode-btn-active"
1281
1285
  }, "Text");
@@ -1295,8 +1299,8 @@ class L extends I {
1295
1299
  setContent(e) {
1296
1300
  const t = this.currentContent?.type;
1297
1301
  this.currentContent = e, e?.type === "image" && (this.mode = "image"), this.element.querySelectorAll(".mode-btn").forEach((i) => {
1298
- const n = i;
1299
- n.classList.toggle("mode-btn-active", n.dataset.mode === this.mode);
1302
+ const s = i;
1303
+ s.classList.toggle("mode-btn-active", s.dataset.mode === this.mode);
1300
1304
  }), e?.type !== t || e?.type === "image" || e === void 0 ? this.renderContent() : e?.type === "text" && this.updateTextValues(e), this.renderTransforms();
1301
1305
  }
1302
1306
  setSelected(e) {
@@ -1304,14 +1308,14 @@ class L extends I {
1304
1308
  }
1305
1309
  updateTextValues(e) {
1306
1310
  this.textInputEl && this.textInputEl.value !== e.text && (this.textInputEl.value = e.text), this.fontSelectEl && this.fontSelectEl.value !== e.font && (this.fontSelectEl.value = e.font), this.sizeInputEl && this.sizeInputEl.value !== String(e.size) && (this.sizeInputEl.value = String(e.size)), this.colorInputEl && this.colorInputEl.value !== e.color && (this.colorInputEl.value = e.color), this.alignGroupEl && this.alignGroupEl.querySelectorAll(".align-btn").forEach((t) => {
1307
- const i = t, n = i.title === "Left" ? "left" : i.title === "Center" ? "center" : "right";
1308
- i.classList.toggle("align-btn-active", n === e.align);
1311
+ const i = t, s = i.title === "Left" ? "left" : i.title === "Center" ? "center" : "right";
1312
+ i.classList.toggle("align-btn-active", s === e.align);
1309
1313
  });
1310
1314
  }
1311
1315
  renderContent() {
1312
1316
  this.contentContainer.innerHTML = "", this.textInputEl = null, this.fontSelectEl = null, this.sizeInputEl = null, this.colorInputEl = null, this.alignGroupEl = null;
1313
- const e = S(this.area), t = k(this.area), i = !t || e && t && this.mode === "text", n = !e || e && t && this.mode === "image";
1314
- e && i && this.renderTextControls(), t && n && this.renderImageControls();
1317
+ const e = S(this.area), t = T(this.area), i = !t || e && t && this.mode === "text", s = !e || e && t && this.mode === "image";
1318
+ e && i && this.renderTextControls(), t && s && this.renderImageControls();
1315
1319
  }
1316
1320
  renderTextControls() {
1317
1321
  const e = this.currentContent, t = e?.type === "text" ? e : E(this.area), i = h("input", {
@@ -1322,14 +1326,14 @@ class L extends I {
1322
1326
  i.value = t.text, this.area.textOptions?.maxLength && (i.maxLength = this.area.textOptions.maxLength), i.addEventListener("input", () => {
1323
1327
  this.emit("text:change", { areaId: this.area.id, updates: { text: i.value } });
1324
1328
  }), this.contentContainer.appendChild(i), this.textInputEl = i;
1325
- const n = h("div", { class: "area-input-row" }), a = h("select", { class: "area-input-font" });
1329
+ const s = h("div", { class: "area-input-row" }), a = h("select", { class: "area-input-font" });
1326
1330
  for (const l of Z) {
1327
1331
  const c = h("option");
1328
1332
  c.value = l.value, c.textContent = l.label, l.value === t.font && (c.selected = !0), a.appendChild(c);
1329
1333
  }
1330
1334
  a.addEventListener("change", () => {
1331
1335
  this.emit("text:change", { areaId: this.area.id, updates: { font: a.value } });
1332
- }), n.appendChild(a), this.fontSelectEl = a;
1336
+ }), s.appendChild(a), this.fontSelectEl = a;
1333
1337
  const o = h("input", {
1334
1338
  class: "area-input-size",
1335
1339
  type: "number"
@@ -1339,8 +1343,8 @@ class L extends I {
1339
1343
  areaId: this.area.id,
1340
1344
  updates: { size: parseInt(o.value, 10) || 24 }
1341
1345
  });
1342
- }), n.appendChild(o), this.sizeInputEl = o, this.contentContainer.appendChild(n);
1343
- const s = h("div", { class: "area-input-row" }), r = h("div", { class: "area-input-align" });
1346
+ }), s.appendChild(o), this.sizeInputEl = o, this.contentContainer.appendChild(s);
1347
+ const n = h("div", { class: "area-input-row" }), r = h("div", { class: "area-input-align" });
1344
1348
  for (const l of q) {
1345
1349
  const c = h("button", {
1346
1350
  class: `align-btn${t.align === l.value ? " align-btn-active" : ""}`,
@@ -1350,68 +1354,68 @@ class L extends I {
1350
1354
  u.stopPropagation(), this.emit("text:change", { areaId: this.area.id, updates: { align: l.value } }), r.querySelectorAll(".align-btn").forEach((p) => p.classList.remove("align-btn-active")), c.classList.add("align-btn-active");
1351
1355
  }), r.appendChild(c);
1352
1356
  }
1353
- s.appendChild(r), this.alignGroupEl = r;
1357
+ n.appendChild(r), this.alignGroupEl = r;
1354
1358
  const d = h("input", {
1355
1359
  class: "area-input-color",
1356
1360
  type: "color"
1357
1361
  });
1358
1362
  d.value = t.color, d.addEventListener("input", () => {
1359
1363
  this.emit("text:change", { areaId: this.area.id, updates: { color: d.value } });
1360
- }), s.appendChild(d), this.colorInputEl = d, this.contentContainer.appendChild(s);
1364
+ }), n.appendChild(d), this.colorInputEl = d, this.contentContainer.appendChild(n);
1361
1365
  }
1362
1366
  renderImageControls() {
1363
1367
  const e = this.currentContent;
1364
1368
  if (e?.type === "image") {
1365
1369
  const t = h("div", { class: "area-image-preview" }), i = h("img", { class: "area-image-thumb" });
1366
1370
  i.src = e.dataUrl, i.alt = e.filename || "Uploaded image", t.appendChild(i);
1367
- const n = h("div", { class: "area-image-info" });
1368
- n.appendChild(h("span", { class: "area-image-name" }, e.filename || "Image"));
1371
+ const s = h("div", { class: "area-image-info" });
1372
+ s.appendChild(h("span", { class: "area-image-name" }, e.filename || "Image"));
1369
1373
  const a = h("button", { class: "area-image-remove-btn" }, "Remove");
1370
1374
  a.addEventListener("click", (o) => {
1371
1375
  o.stopPropagation(), this.emit("clear", { areaId: this.area.id });
1372
- }), n.appendChild(a), t.appendChild(n), this.contentContainer.appendChild(t);
1376
+ }), s.appendChild(a), t.appendChild(s), this.contentContainer.appendChild(t);
1373
1377
  } else {
1374
1378
  const t = h("button", { class: "area-upload-btn" }, "Upload Image"), i = h("input", { type: "file" });
1375
1379
  i.accept = P.join(","), i.style.display = "none";
1376
- const n = h("div", { class: "area-validation-error" });
1377
- n.style.display = "none", i.addEventListener("change", () => {
1380
+ const s = h("div", { class: "area-validation-error" });
1381
+ s.style.display = "none", i.addEventListener("change", () => {
1378
1382
  const a = i.files?.[0];
1379
1383
  if (!a) return;
1380
1384
  if (!P.includes(a.type)) {
1381
- const s = "Only PNG, JPEG, WebP, and SVG images are accepted";
1382
- n.textContent = s, n.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: s }), i.value = "";
1385
+ const n = "Only PNG, JPEG, WebP, and SVG images are accepted";
1386
+ s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), i.value = "";
1383
1387
  return;
1384
1388
  }
1385
1389
  if (a.size > Y) {
1386
- const s = "File must be smaller than 15MB";
1387
- n.textContent = s, n.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: s }), i.value = "";
1390
+ const n = "File must be smaller than 15MB";
1391
+ s.textContent = n, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: n }), i.value = "";
1388
1392
  return;
1389
1393
  }
1390
- n.style.display = "none";
1394
+ s.style.display = "none";
1391
1395
  const o = new FileReader();
1392
1396
  o.onload = () => {
1393
- const s = o.result;
1394
- this.emit("image:change", { areaId: this.area.id, dataUrl: s, filename: a.name });
1397
+ const n = o.result;
1398
+ this.emit("image:change", { areaId: this.area.id, dataUrl: n, filename: a.name });
1395
1399
  }, o.readAsDataURL(a), i.value = "";
1396
1400
  }), t.addEventListener("click", (a) => {
1397
1401
  a.stopPropagation(), i.click();
1398
- }), this.contentContainer.appendChild(t), this.contentContainer.appendChild(i), this.contentContainer.appendChild(n);
1402
+ }), this.contentContainer.appendChild(t), this.contentContainer.appendChild(i), this.contentContainer.appendChild(s);
1399
1403
  }
1400
1404
  }
1401
1405
  renderTransforms() {
1402
1406
  this.transformContainer.innerHTML = "";
1403
1407
  const e = this.currentContent;
1404
1408
  if (!this.isSelected || !e) return;
1405
- const t = e.type === "text" ? this.area.textOptions?.allowPositioning : this.area.imageOptions?.allowPositioning, i = e.type === "image" && this.area.imageOptions?.allowScaling, n = e.type === "image" && this.area.imageOptions?.allowRotation;
1406
- if (!t && !i && !n) return;
1409
+ const t = e.type === "text" ? this.area.textOptions?.allowPositioning : this.area.imageOptions?.allowPositioning, i = e.type === "image" && this.area.imageOptions?.allowScaling, s = e.type === "image" && this.area.imageOptions?.allowRotation;
1410
+ if (!t && !i && !s) return;
1407
1411
  const a = h("div", { class: "area-card-divider" });
1408
1412
  this.transformContainer.appendChild(a);
1409
1413
  const o = h("div", { class: "transform-header" });
1410
1414
  o.appendChild(h("span", { class: "transform-title" }, "Transform"));
1411
- const s = h("button", { class: "transform-reset-btn" }, "Reset");
1412
- s.addEventListener("click", (d) => {
1413
- d.stopPropagation(), t && this.emit("offset:change", { areaId: this.area.id, offset: { x: 0, y: 0 } }), i && this.emit("scale:change", { areaId: this.area.id, scale: 1 }), n && this.emit("rotation:change", { areaId: this.area.id, rotation: 0 });
1414
- }), o.appendChild(s), this.transformContainer.appendChild(o);
1415
+ const n = h("button", { class: "transform-reset-btn" }, "Reset");
1416
+ n.addEventListener("click", (d) => {
1417
+ d.stopPropagation(), t && this.emit("offset:change", { areaId: this.area.id, offset: { x: 0, y: 0 } }), i && this.emit("scale:change", { areaId: this.area.id, scale: 1 }), s && this.emit("rotation:change", { areaId: this.area.id, rotation: 0 });
1418
+ }), o.appendChild(n), this.transformContainer.appendChild(o);
1415
1419
  const r = e.offset ?? { x: 0, y: 0 };
1416
1420
  if (t) {
1417
1421
  const d = h("div", { class: "area-input-row" }), l = h("label", { class: "transform-label" }, "X"), c = h("input", { class: "transform-input", type: "number" });
@@ -1436,7 +1440,7 @@ class L extends I {
1436
1440
  c.textContent = `Scale: ${Math.round(p * 100)}%`, this.emit("scale:change", { areaId: this.area.id, scale: p });
1437
1441
  }), l.appendChild(c), l.appendChild(u), this.transformContainer.appendChild(l);
1438
1442
  }
1439
- if (n && e.type === "image") {
1443
+ if (s && e.type === "image") {
1440
1444
  const d = e.rotation ?? 0, l = h("div", { class: "area-input-row transform-slider-row" }), c = h("label", { class: "transform-label" }, `Rotation: ${Math.round(d)}°`), u = h("input", { class: "transform-slider", type: "range" });
1441
1445
  u.min = "0", u.max = "360", u.step = "1", u.value = String(Math.round(d)), u.addEventListener("input", () => {
1442
1446
  const p = parseInt(u.value, 10);
@@ -1450,8 +1454,8 @@ class L extends I {
1450
1454
  }
1451
1455
  class j extends I {
1452
1456
  constructor(e) {
1453
- super(), this.cards = /* @__PURE__ */ new Map(), this.isMobile = !1, this.boundEscapeHandler = (n) => {
1454
- n.key === "Escape" && this.emit("dismiss", void 0);
1457
+ super(), this.cards = /* @__PURE__ */ new Map(), this.isMobile = !1, this.boundEscapeHandler = (s) => {
1458
+ s.key === "Escape" && this.emit("dismiss", void 0);
1455
1459
  }, this.backdrop = h("div", { class: "area-panel-backdrop" }), this.backdrop.addEventListener("click", () => {
1456
1460
  this.emit("dismiss", void 0);
1457
1461
  }), this.element = h("div", { class: "area-panel" });
@@ -1461,12 +1465,12 @@ class j extends I {
1461
1465
  if (i.addEventListener("click", () => {
1462
1466
  this.emit("dismiss", void 0);
1463
1467
  }), t.appendChild(i), this.element.appendChild(t), this.panelContent = h("div", { class: "area-panel-content" }), e.length === 0) {
1464
- const n = h("p", { class: "area-panel-empty" }, "No customization areas defined.");
1465
- this.panelContent.appendChild(n);
1468
+ const s = h("p", { class: "area-panel-empty" }, "No customization areas defined.");
1469
+ this.panelContent.appendChild(s);
1466
1470
  } else
1467
- for (const n of e) {
1468
- const a = new L(n);
1469
- this.cards.set(n.id, a), a.on("text:change", (o) => this.emit("text:change", o)), a.on("image:change", (o) => this.emit("image:change", o)), a.on("clear", (o) => this.emit("clear", o)), a.on("select", (o) => this.emit("select", o)), a.on("offset:change", (o) => this.emit("offset:change", o)), a.on("scale:change", (o) => this.emit("scale:change", o)), a.on("rotation:change", (o) => this.emit("rotation:change", o)), a.on("validation:error", (o) => this.emit("validation:error", o)), this.panelContent.appendChild(a.getElement());
1471
+ for (const s of e) {
1472
+ const a = new L(s);
1473
+ this.cards.set(s.id, a), a.on("text:change", (o) => this.emit("text:change", o)), a.on("image:change", (o) => this.emit("image:change", o)), a.on("clear", (o) => this.emit("clear", o)), a.on("select", (o) => this.emit("select", o)), a.on("offset:change", (o) => this.emit("offset:change", o)), a.on("scale:change", (o) => this.emit("scale:change", o)), a.on("rotation:change", (o) => this.emit("rotation:change", o)), a.on("validation:error", (o) => this.emit("validation:error", o)), this.panelContent.appendChild(a.getElement());
1470
1474
  }
1471
1475
  this.element.appendChild(this.panelContent);
1472
1476
  }
@@ -1477,7 +1481,7 @@ class j extends I {
1477
1481
  } else
1478
1482
  for (const t of e) {
1479
1483
  const i = new L(t);
1480
- this.cards.set(t.id, i), i.on("text:change", (n) => this.emit("text:change", n)), i.on("image:change", (n) => this.emit("image:change", n)), i.on("clear", (n) => this.emit("clear", n)), i.on("select", (n) => this.emit("select", n)), i.on("offset:change", (n) => this.emit("offset:change", n)), i.on("scale:change", (n) => this.emit("scale:change", n)), i.on("rotation:change", (n) => this.emit("rotation:change", n)), i.on("validation:error", (n) => this.emit("validation:error", n)), this.panelContent.appendChild(i.getElement());
1484
+ this.cards.set(t.id, i), i.on("text:change", (s) => this.emit("text:change", s)), i.on("image:change", (s) => this.emit("image:change", s)), i.on("clear", (s) => this.emit("clear", s)), i.on("select", (s) => this.emit("select", s)), i.on("offset:change", (s) => this.emit("offset:change", s)), i.on("scale:change", (s) => this.emit("scale:change", s)), i.on("rotation:change", (s) => this.emit("rotation:change", s)), i.on("validation:error", (s) => this.emit("validation:error", s)), this.panelContent.appendChild(i.getElement());
1481
1485
  }
1482
1486
  }
1483
1487
  setContents(e) {
@@ -1486,8 +1490,8 @@ class j extends I {
1486
1490
  }
1487
1491
  setSelectedArea(e) {
1488
1492
  for (const [t, i] of this.cards) {
1489
- const n = t === e;
1490
- i.setSelected(n), i.getElement().style.display = n ? "" : "none";
1493
+ const s = t === e;
1494
+ i.setSelected(s), i.getElement().style.display = s ? "" : "none";
1491
1495
  }
1492
1496
  e ? (this.element.classList.add("area-panel-visible"), this.isMobile && this.backdrop.classList.add("area-panel-backdrop-visible"), document.addEventListener("keydown", this.boundEscapeHandler)) : (this.element.classList.remove("area-panel-visible"), this.isMobile && this.backdrop.classList.remove("area-panel-backdrop-visible"), document.removeEventListener("keydown", this.boundEscapeHandler));
1493
1497
  }
@@ -1507,9 +1511,9 @@ class X {
1507
1511
  if (!this.isEnabled) return;
1508
1512
  const i = t.target;
1509
1513
  if (!(i.tagName === "INPUT" || i.tagName === "TEXTAREA" || i.isContentEditable)) {
1510
- for (const n of this.commands)
1511
- if (this.matchesCommand(t, n)) {
1512
- t.preventDefault(), n.handler(t);
1514
+ for (const s of this.commands)
1515
+ if (this.matchesCommand(t, s)) {
1516
+ t.preventDefault(), s.handler(t);
1513
1517
  break;
1514
1518
  }
1515
1519
  }
@@ -1562,24 +1566,24 @@ function J() {
1562
1566
  return W() ? "meta" : "ctrl";
1563
1567
  }
1564
1568
  function G(f, e, t, i) {
1565
- const n = f / t, a = e / i;
1566
- return Math.min(n, a);
1569
+ const s = f / t, a = e / i;
1570
+ return Math.min(s, a);
1567
1571
  }
1568
- function K(f, e, t = 8, i = 10, n = 300) {
1569
- const a = G(f, e, t, i), o = a >= n;
1572
+ function K(f, e, t = 8, i = 10, s = 300) {
1573
+ const a = G(f, e, t, i), o = a >= s;
1570
1574
  return {
1571
1575
  actualDPI: Math.round(a),
1572
- targetDPI: n,
1576
+ targetDPI: s,
1573
1577
  width: f,
1574
1578
  height: e,
1575
1579
  passed: o,
1576
- warning: o ? void 0 : `Image resolution is ${Math.round(a)} DPI at 100% scale. For best print quality, use images with at least ${n} DPI. This may result in pixelated or blurry prints.`
1580
+ warning: o ? void 0 : `Image resolution is ${Math.round(a)} DPI at 100% scale. For best print quality, use images with at least ${s} DPI. This may result in pixelated or blurry prints.`
1577
1581
  };
1578
1582
  }
1579
- const z = ':host{--editor-bg: #f3f4f6;--editor-surface: #ffffff;--editor-border: #e5e7eb;--editor-text: #111827;--editor-text-muted: #6b7280;--editor-primary: #3b82f6;--editor-primary-hover: #2563eb;--editor-danger: #ef4444;--editor-radius: 8px;--editor-radius-sm: 4px;--editor-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--editor-panel-width: 280px;display:block;width:100%;height:100%;font-family:var(--editor-font);font-size:13px;color:var(--editor-text);line-height:1.4}:host([theme="dark"]){--editor-bg: #1f2937;--editor-surface: #374151;--editor-border: #4b5563;--editor-text: #f9fafb;--editor-text-muted: #9ca3af}.editor-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative;overflow:hidden;background:var(--editor-bg)}.layout-desktop .editor-main{display:flex;flex:1;min-height:0}.layout-desktop .canvas-area{flex:1;min-width:0;position:relative;display:flex;flex-direction:column}.layout-desktop .area-panel{width:var(--editor-panel-width);border-left:1px solid var(--editor-border);background:var(--editor-surface);display:none;flex-direction:column;overflow:hidden}.layout-desktop .area-panel.area-panel-visible{display:flex}.layout-mobile .editor-main{display:flex;flex-direction:column;flex:1;min-height:0}.layout-mobile .canvas-area{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}.layout-mobile .area-panel{position:absolute;bottom:0;left:0;right:0;max-height:50%;background:var(--editor-surface);border-top:1px solid var(--editor-border);border-radius:var(--editor-radius) var(--editor-radius) 0 0;box-shadow:0 -4px 20px #00000026;transform:translateY(100%);transition:transform .25s ease-out;z-index:20;display:flex;flex-direction:column;overflow:hidden}.layout-mobile .area-panel.area-panel-visible{transform:translateY(0)}.area-panel-backdrop{display:none}.layout-mobile .area-panel-backdrop{display:block;position:absolute;inset:0;background:#0000004d;z-index:15;opacity:0;pointer-events:none;transition:opacity .25s ease-out}.layout-mobile .area-panel-backdrop.area-panel-backdrop-visible{opacity:1;pointer-events:auto}.zoom-toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;background:var(--editor-surface);border-bottom:1px solid var(--editor-border)}.zoom-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:14px;font-family:var(--editor-font);transition:background .15s;-webkit-tap-highlight-color:transparent;touch-action:manipulation}.zoom-btn:hover{background:var(--editor-bg)}.zoom-btn:active{background:var(--editor-border)}.zoom-percent{margin-left:8px;font-size:12px;color:var(--editor-text-muted);min-width:40px}.view-select{margin-left:8px;padding:4px 8px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);font-family:var(--editor-font);font-size:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.view-select:hover{background:var(--editor-bg)}.view-select:focus{outline:2px solid var(--editor-primary);outline-offset:-1px}.zoom-toolbar-spacer{flex:1}.zoom-close-btn{width:auto;padding:0 12px;font-size:13px;gap:4px}.zoom-save-btn{width:auto;padding:0 16px;margin-left:8px;font-size:13px;font-weight:600;background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.zoom-save-btn:hover{background:var(--editor-primary-hover)}.zoom-save-btn:disabled{opacity:.5;cursor:not-allowed}.canvas-wrapper{flex:1;position:relative;overflow:hidden;min-height:200px}.editor-canvas{position:absolute;top:0;left:0}.area-panel-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--editor-border);flex-shrink:0}.area-panel-title{font-weight:600;font-size:14px}.area-panel-close-btn{width:28px;height:28px;border:none;background:none;color:var(--editor-text-muted);cursor:pointer;font-size:18px;line-height:1;border-radius:var(--editor-radius-sm);display:flex;align-items:center;justify-content:center}.area-panel-close-btn:hover{background:var(--editor-bg);color:var(--editor-text)}.area-panel-content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.area-panel-empty{color:var(--editor-text-muted);font-size:12px;text-align:center;padding:20px 0}.area-card{padding:12px;border:1px solid var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-surface);cursor:pointer;transition:border-color .15s}.area-card:hover{border-color:var(--editor-primary)}.area-card-selected{border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.area-card-name-row{display:flex;align-items:center;gap:6px}.area-card-name{font-weight:600;font-size:13px}.area-card-badge{display:inline-block;padding:1px 6px;font-size:10px;font-weight:500;background:#fef3c7;color:#92400e;border-radius:10px}.area-card-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.area-card-reset-btn:hover{background:var(--editor-bg);color:var(--editor-danger);border-color:var(--editor-danger)}.area-card-mode-switcher{display:flex;gap:4px;margin-bottom:8px}.mode-btn{flex:1;padding:4px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-family:var(--editor-font);transition:all .15s}.mode-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-text{width:100%;padding:6px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:13px;color:var(--editor-text);background:var(--editor-surface);box-sizing:border-box;margin-bottom:6px}.area-input-text:focus{outline:none;border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-input-row{display:flex;gap:6px;align-items:center;margin-bottom:6px}.area-input-font{flex:1;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface);min-width:0}.area-input-size{width:56px;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.area-input-align{display:flex;gap:2px}.align-btn{width:28px;height:28px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-weight:600;font-family:var(--editor-font)}.align-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-color{width:32px;height:28px;padding:0;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);cursor:pointer;background:none}.area-upload-btn{width:100%;padding:10px;border:2px dashed var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-bg);color:var(--editor-text-muted);cursor:pointer;font-family:var(--editor-font);font-size:12px;text-align:center;transition:all .15s;min-height:44px}.area-upload-btn:hover{border-color:var(--editor-primary);color:var(--editor-primary)}.area-image-preview{display:flex;gap:8px;align-items:center}.area-image-thumb{width:48px;height:48px;object-fit:cover;border-radius:var(--editor-radius-sm);border:1px solid var(--editor-border)}.area-image-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.area-image-name{font-size:12px;color:var(--editor-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.area-image-remove-btn{padding:2px 8px;border:1px solid var(--editor-danger);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-danger);cursor:pointer;font-size:11px;font-family:var(--editor-font);width:fit-content}.area-image-remove-btn:hover{background:#fef2f2}.area-card-divider{height:1px;background:var(--editor-border);margin:8px 0}.transform-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.transform-title{font-weight:600;font-size:12px}.transform-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.transform-reset-btn:hover{background:var(--editor-bg)}.transform-label{font-size:11px;color:var(--editor-text-muted);white-space:nowrap}.transform-input{width:60px;padding:3px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.transform-slider-row{flex-direction:column;align-items:stretch}.transform-slider{width:100%;accent-color:var(--editor-primary)}.loading-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px}.loading-spinner{width:32px;height:32px;border:3px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error-container{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px;padding:20px;text-align:center}.error-title{font-weight:600;font-size:16px;color:var(--editor-danger);margin-bottom:8px}.error-message{font-size:13px;color:var(--editor-text-muted);max-width:400px;white-space:pre-wrap}:host(:focus){outline:2px solid var(--editor-primary);outline-offset:-2px}input:focus,select:focus,button:focus-visible{outline:2px solid var(--editor-primary);outline-offset:-1px}.context-menu{position:absolute;z-index:100;min-width:160px;background:var(--editor-surface);border:1px solid var(--editor-border);border-radius:var(--editor-radius);box-shadow:0 4px 12px #00000026;padding:4px 0;font-size:13px}.context-menu-header{padding:6px 12px;font-weight:600;font-size:12px;color:var(--editor-text-muted);user-select:none}.context-menu-divider{height:1px;background:var(--editor-border);margin:4px 0}.context-menu-item{padding:6px 12px;cursor:pointer;color:var(--editor-text);user-select:none;transition:background .1s}.context-menu-item:hover{background:var(--editor-bg)}.context-menu-item:active{background:var(--editor-border)}.context-menu-item-disabled{color:var(--editor-text-muted);cursor:default;pointer-events:none}.editor-toast{position:absolute;top:16px;left:50%;transform:translate(-50%);padding:10px 20px;border-radius:var(--editor-radius);font-size:13px;font-family:var(--editor-font);line-height:1.4;z-index:200;max-width:400px;text-align:center;animation:toast-in .25s ease-out,toast-out .25s ease-in forwards;animation-delay:0s,var(--toast-duration, 3.75s);pointer-events:none;box-shadow:0 4px 12px #00000026}.editor-toast-warning{background:#fef3c7;color:#92400e;border:1px solid #f59e0b}.editor-toast-error{background:#fef2f2;color:#991b1b;border:1px solid var(--editor-danger)}.editor-toast-info{background:#eff6ff;color:#1e40af;border:1px solid var(--editor-primary)}@keyframes toast-in{0%{opacity:0;transform:translate(-50%) translateY(-10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes toast-out{0%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}.editor-modal-overlay{position:absolute;inset:0;background:#0006;z-index:300;display:flex;align-items:center;justify-content:center;animation:modal-overlay-in .2s ease-out}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}.editor-modal{background:var(--editor-surface);border-radius:var(--editor-radius);box-shadow:0 8px 32px #0003;max-width:380px;width:calc(100% - 32px);padding:20px;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.editor-modal-title{font-weight:600;font-size:15px;margin-bottom:8px;color:var(--editor-text)}.editor-modal-body{font-size:13px;color:var(--editor-text-muted);margin-bottom:16px;line-height:1.5}.editor-modal-actions{display:flex;justify-content:flex-end;gap:8px}.editor-modal-btn{padding:8px 16px;border-radius:var(--editor-radius-sm);font-size:13px;font-family:var(--editor-font);cursor:pointer;border:1px solid var(--editor-border);background:var(--editor-surface);color:var(--editor-text);transition:background .15s}.editor-modal-btn:hover{background:var(--editor-bg)}.editor-modal-btn-primary{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.editor-modal-btn-primary:hover{background:var(--editor-primary-hover)}.area-validation-error{font-size:11px;color:var(--editor-danger);margin-top:4px}@media(pointer:coarse){.zoom-btn{min-width:44px;min-height:44px}.area-input-text,.area-input-font,.area-input-size{min-height:44px;font-size:16px}.align-btn{min-width:44px;min-height:44px}.area-upload-btn{min-height:48px}.view-select{min-height:44px;font-size:16px}}', Q = "2.0.0", _ = 768;
1583
+ const M = ':host{--editor-bg: #f3f4f6;--editor-surface: #ffffff;--editor-border: #e5e7eb;--editor-text: #111827;--editor-text-muted: #6b7280;--editor-primary: #3b82f6;--editor-primary-hover: #2563eb;--editor-danger: #ef4444;--editor-radius: 8px;--editor-radius-sm: 4px;--editor-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--editor-panel-width: 280px;display:block;width:100%;height:100%;font-family:var(--editor-font);font-size:13px;color:var(--editor-text);line-height:1.4}:host([theme="dark"]){--editor-bg: #1f2937;--editor-surface: #374151;--editor-border: #4b5563;--editor-text: #f9fafb;--editor-text-muted: #9ca3af}.editor-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative;overflow:hidden;background:var(--editor-bg)}.layout-desktop .editor-main{display:flex;flex:1;min-height:0}.layout-desktop .canvas-area{flex:1;min-width:0;position:relative;display:flex;flex-direction:column}.layout-desktop .area-panel{width:var(--editor-panel-width);border-left:1px solid var(--editor-border);background:var(--editor-surface);display:none;flex-direction:column;overflow:hidden}.layout-desktop .area-panel.area-panel-visible{display:flex}.layout-mobile .editor-main{display:flex;flex-direction:column;flex:1;min-height:0}.layout-mobile .canvas-area{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}.layout-mobile .area-panel{position:absolute;bottom:0;left:0;right:0;max-height:50%;background:var(--editor-surface);border-top:1px solid var(--editor-border);border-radius:var(--editor-radius) var(--editor-radius) 0 0;box-shadow:0 -4px 20px #00000026;transform:translateY(100%);transition:transform .25s ease-out;z-index:20;display:flex;flex-direction:column;overflow:hidden}.layout-mobile .area-panel.area-panel-visible{transform:translateY(0)}.area-panel-backdrop{display:none}.layout-mobile .area-panel-backdrop{display:block;position:absolute;inset:0;background:#0000004d;z-index:15;opacity:0;pointer-events:none;transition:opacity .25s ease-out}.layout-mobile .area-panel-backdrop.area-panel-backdrop-visible{opacity:1;pointer-events:auto}.zoom-toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;background:var(--editor-surface);border-bottom:1px solid var(--editor-border)}.zoom-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:14px;font-family:var(--editor-font);transition:background .15s;-webkit-tap-highlight-color:transparent;touch-action:manipulation}.zoom-btn:hover{background:var(--editor-bg)}.zoom-btn:active{background:var(--editor-border)}.zoom-percent{margin-left:8px;font-size:12px;color:var(--editor-text-muted);min-width:40px}.view-select{margin-left:8px;padding:4px 8px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);font-family:var(--editor-font);font-size:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.view-select:hover{background:var(--editor-bg)}.view-select:focus{outline:2px solid var(--editor-primary);outline-offset:-1px}.zoom-toolbar-spacer{flex:1}.zoom-close-btn{width:auto;padding:0 12px;font-size:13px;gap:4px}.zoom-save-btn{width:auto;padding:0 16px;margin-left:8px;font-size:13px;font-weight:600;background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.zoom-save-btn:hover{background:var(--editor-primary-hover)}.zoom-save-btn:disabled{opacity:.5;cursor:not-allowed}.canvas-wrapper{flex:1;position:relative;overflow:hidden;min-height:200px}.editor-canvas{position:absolute;top:0;left:0}.area-panel-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--editor-border);flex-shrink:0}.area-panel-title{font-weight:600;font-size:14px}.area-panel-close-btn{width:28px;height:28px;border:none;background:none;color:var(--editor-text-muted);cursor:pointer;font-size:18px;line-height:1;border-radius:var(--editor-radius-sm);display:flex;align-items:center;justify-content:center}.area-panel-close-btn:hover{background:var(--editor-bg);color:var(--editor-text)}.area-panel-content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.area-panel-empty{color:var(--editor-text-muted);font-size:12px;text-align:center;padding:20px 0}.area-card{padding:12px;border:1px solid var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-surface);cursor:pointer;transition:border-color .15s}.area-card:hover{border-color:var(--editor-primary)}.area-card-selected{border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.area-card-name-row{display:flex;align-items:center;gap:6px}.area-card-name{font-weight:600;font-size:13px}.area-card-badge{display:inline-block;padding:1px 6px;font-size:10px;font-weight:500;background:#fef3c7;color:#92400e;border-radius:10px}.area-card-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.area-card-reset-btn:hover{background:var(--editor-bg);color:var(--editor-danger);border-color:var(--editor-danger)}.area-card-mode-switcher{display:flex;gap:4px;margin-bottom:8px}.mode-btn{flex:1;padding:4px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-family:var(--editor-font);transition:all .15s}.mode-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-text{width:100%;padding:6px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:13px;color:var(--editor-text);background:var(--editor-surface);box-sizing:border-box;margin-bottom:6px}.area-input-text:focus{outline:none;border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-input-row{display:flex;gap:6px;align-items:center;margin-bottom:6px}.area-input-font{flex:1;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface);min-width:0}.area-input-size{width:56px;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.area-input-align{display:flex;gap:2px}.align-btn{width:28px;height:28px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-weight:600;font-family:var(--editor-font)}.align-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-color{width:32px;height:28px;padding:0;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);cursor:pointer;background:none}.area-upload-btn{width:100%;padding:10px;border:2px dashed var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-bg);color:var(--editor-text-muted);cursor:pointer;font-family:var(--editor-font);font-size:12px;text-align:center;transition:all .15s;min-height:44px}.area-upload-btn:hover{border-color:var(--editor-primary);color:var(--editor-primary)}.area-image-preview{display:flex;gap:8px;align-items:center}.area-image-thumb{width:48px;height:48px;object-fit:cover;border-radius:var(--editor-radius-sm);border:1px solid var(--editor-border)}.area-image-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.area-image-name{font-size:12px;color:var(--editor-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.area-image-remove-btn{padding:2px 8px;border:1px solid var(--editor-danger);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-danger);cursor:pointer;font-size:11px;font-family:var(--editor-font);width:fit-content}.area-image-remove-btn:hover{background:#fef2f2}.area-card-divider{height:1px;background:var(--editor-border);margin:8px 0}.transform-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.transform-title{font-weight:600;font-size:12px}.transform-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.transform-reset-btn:hover{background:var(--editor-bg)}.transform-label{font-size:11px;color:var(--editor-text-muted);white-space:nowrap}.transform-input{width:60px;padding:3px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.transform-slider-row{flex-direction:column;align-items:stretch}.transform-slider{width:100%;accent-color:var(--editor-primary)}.loading-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px}.loading-spinner{width:32px;height:32px;border:3px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error-container{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px;padding:20px;text-align:center}.error-title{font-weight:600;font-size:16px;color:var(--editor-danger);margin-bottom:8px}.error-message{font-size:13px;color:var(--editor-text-muted);max-width:400px;white-space:pre-wrap}:host(:focus){outline:2px solid var(--editor-primary);outline-offset:-2px}input:focus,select:focus,button:focus-visible{outline:2px solid var(--editor-primary);outline-offset:-1px}.context-menu{position:absolute;z-index:100;min-width:160px;background:var(--editor-surface);border:1px solid var(--editor-border);border-radius:var(--editor-radius);box-shadow:0 4px 12px #00000026;padding:4px 0;font-size:13px}.context-menu-header{padding:6px 12px;font-weight:600;font-size:12px;color:var(--editor-text-muted);user-select:none}.context-menu-divider{height:1px;background:var(--editor-border);margin:4px 0}.context-menu-item{padding:6px 12px;cursor:pointer;color:var(--editor-text);user-select:none;transition:background .1s}.context-menu-item:hover{background:var(--editor-bg)}.context-menu-item:active{background:var(--editor-border)}.context-menu-item-disabled{color:var(--editor-text-muted);cursor:default;pointer-events:none}.editor-toast{position:absolute;top:16px;left:50%;transform:translate(-50%);padding:10px 20px;border-radius:var(--editor-radius);font-size:13px;font-family:var(--editor-font);line-height:1.4;z-index:200;max-width:400px;text-align:center;animation:toast-in .25s ease-out,toast-out .25s ease-in forwards;animation-delay:0s,var(--toast-duration, 3.75s);pointer-events:none;box-shadow:0 4px 12px #00000026}.editor-toast-warning{background:#fef3c7;color:#92400e;border:1px solid #f59e0b}.editor-toast-error{background:#fef2f2;color:#991b1b;border:1px solid var(--editor-danger)}.editor-toast-info{background:#eff6ff;color:#1e40af;border:1px solid var(--editor-primary)}@keyframes toast-in{0%{opacity:0;transform:translate(-50%) translateY(-10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes toast-out{0%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}.editor-modal-overlay{position:absolute;inset:0;background:#0006;z-index:300;display:flex;align-items:center;justify-content:center;animation:modal-overlay-in .2s ease-out}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}.editor-modal{background:var(--editor-surface);border-radius:var(--editor-radius);box-shadow:0 8px 32px #0003;max-width:380px;width:calc(100% - 32px);padding:20px;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.editor-modal-title{font-weight:600;font-size:15px;margin-bottom:8px;color:var(--editor-text)}.editor-modal-body{font-size:13px;color:var(--editor-text-muted);margin-bottom:16px;line-height:1.5}.editor-modal-actions{display:flex;justify-content:flex-end;gap:8px}.editor-modal-btn{padding:8px 16px;border-radius:var(--editor-radius-sm);font-size:13px;font-family:var(--editor-font);cursor:pointer;border:1px solid var(--editor-border);background:var(--editor-surface);color:var(--editor-text);transition:background .15s}.editor-modal-btn:hover{background:var(--editor-bg)}.editor-modal-btn-primary{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.editor-modal-btn-primary:hover{background:var(--editor-primary-hover)}.area-validation-error{font-size:11px;color:var(--editor-danger);margin-top:4px}@media(pointer:coarse){.zoom-btn{min-width:44px;min-height:44px}.area-input-text,.area-input-font,.area-input-size{min-height:44px;font-size:16px}.align-btn{min-width:44px;min-height:44px}.area-upload-btn{min-height:48px}.view-select{min-height:44px;font-size:16px}}', Q = "2.0.0", _ = 768;
1580
1584
  class ee extends HTMLElement {
1581
1585
  constructor() {
1582
- super(), this.resizeObserver = null, this.isReady = !1, this.currentTemplate = null, this.loadingTemplateId = null, this.isMobileLayout = !1, this.productViews = [], this.activeViewName = null, this.perViewContents = /* @__PURE__ */ new Map(), this.subscriptions = [], this.storageUsage = null, this.storageUsageLastRefresh = 0, this.emitChange = H(() => {
1586
+ super(), this.resizeObserver = null, this.isReady = !1, this.currentTemplate = null, this.loadingTemplateId = null, this.isMobileLayout = !1, this.productViews = [], this.activeViewName = null, this.perViewContents = /* @__PURE__ */ new Map(), this.subscriptions = [], this.storageUsage = null, this.storageUsageLastRefresh = 0, this.emitChange = $(() => {
1583
1587
  this.dispatchEvent(
1584
1588
  new CustomEvent("change", {
1585
1589
  detail: { design: this.getDesign() },
@@ -1590,7 +1594,7 @@ class ee extends HTMLElement {
1590
1594
  }, 300), this.shadow = this.attachShadow({ mode: "open" });
1591
1595
  }
1592
1596
  static get observedAttributes() {
1593
- return ["template-id", "product-id", "theme", "mode", "store-id", "api-url"];
1597
+ return ["template-id", "product-id", "theme", "mode", "store-id", "api-url", "show-close-button", "show-save-button"];
1594
1598
  }
1595
1599
  async connectedCallback() {
1596
1600
  try {
@@ -1603,7 +1607,7 @@ class ee extends HTMLElement {
1603
1607
  this.cleanup();
1604
1608
  }
1605
1609
  attributeChangedCallback(e, t, i) {
1606
- t !== i && (e === "template-id" && this.isReady && i && this.loadTemplate(i), e === "product-id" && this.isReady && i && this.loadProduct(i));
1610
+ t !== i && (e === "template-id" && this.isReady && i && this.loadTemplate(i), e === "product-id" && this.isReady && i && this.loadProduct(i), e === "show-close-button" && this.zoomToolbar && this.zoomToolbar.setCloseButtonVisible(i !== "false"), e === "show-save-button" && this.zoomToolbar && this.zoomToolbar.setSaveButtonEnabled(i !== "false"));
1607
1611
  }
1608
1612
  // ─── Public API ───
1609
1613
  getDesign() {
@@ -1672,7 +1676,7 @@ class ee extends HTMLElement {
1672
1676
  async finalize() {
1673
1677
  const e = this.engine.checkSafeAreaViolations();
1674
1678
  if (e.length > 0) {
1675
- const a = this.currentTemplate?.areas || [], o = e.map((s) => a.find((r) => r.id === s)?.name || s).join(", ");
1679
+ const a = this.currentTemplate?.areas || [], o = e.map((n) => a.find((r) => r.id === n)?.name || n).join(", ");
1676
1680
  if (!await this.showConfirmation(
1677
1681
  "Content outside safe area",
1678
1682
  `Content in ${o} extends beyond the safe area. It may be trimmed during printing. Continue?`,
@@ -1681,25 +1685,25 @@ class ee extends HTMLElement {
1681
1685
  ))
1682
1686
  throw new Error("Finalization cancelled by user");
1683
1687
  }
1684
- const t = this.getDesign(), i = this.getAttribute("store-id") || "demo-shop", n = await this.apiClient.finalizeDesign(i, t);
1688
+ const t = this.getDesign(), i = this.getAttribute("store-id") || "demo-shop", s = await this.apiClient.finalizeDesign(i, t);
1685
1689
  return this.dispatchEvent(
1686
1690
  new CustomEvent("customizer:finalize", {
1687
1691
  detail: {
1688
- designId: n.designId,
1689
- proofUrl: n.proofUrl,
1692
+ designId: s.designId,
1693
+ proofUrl: s.proofUrl,
1690
1694
  templateId: t.templateId,
1691
1695
  designJson: t,
1692
- status: n.status
1696
+ status: s.status
1693
1697
  },
1694
1698
  bubbles: !0,
1695
1699
  composed: !0
1696
1700
  })
1697
- ), n;
1701
+ ), s;
1698
1702
  }
1699
1703
  // ─── Initialization ───
1700
1704
  renderLoading() {
1701
1705
  const e = document.createElement("style");
1702
- e.textContent = z, this.shadow.appendChild(e);
1706
+ e.textContent = M, this.shadow.appendChild(e);
1703
1707
  const t = h(
1704
1708
  "div",
1705
1709
  { class: "loading-container" },
@@ -1709,7 +1713,7 @@ class ee extends HTMLElement {
1709
1713
  }
1710
1714
  async initialize() {
1711
1715
  const e = this.getAttribute("api-url") || "http://localhost:4000";
1712
- this.apiClient = new $(`${e.replace(/\/+$/, "")}/public`);
1716
+ this.apiClient = new F(`${e.replace(/\/+$/, "")}/public`);
1713
1717
  const t = this.getAttribute("product-id"), i = this.getAttribute("template-id");
1714
1718
  if (!t && !i)
1715
1719
  throw new Error("Either template-id or product-id attribute is required");
@@ -1730,7 +1734,7 @@ class ee extends HTMLElement {
1730
1734
  async loadTemplateData(e) {
1731
1735
  this.currentTemplate = e;
1732
1736
  const t = e.areas || [];
1733
- this.contentManager = new T(t);
1737
+ this.contentManager = new k(t);
1734
1738
  const i = {
1735
1739
  template: e,
1736
1740
  contents: this.contentManager.toJSON(),
@@ -1740,16 +1744,16 @@ class ee extends HTMLElement {
1740
1744
  isDirty: !1,
1741
1745
  warnings: []
1742
1746
  };
1743
- this.stateManager = new O(i), this.buildUI(e, t), this.engine = new N(this.canvas), this.engine.setArtboard({
1747
+ this.stateManager = new V(i), this.buildUI(e, t), this.engine = new N(this.canvas), this.engine.setArtboard({
1744
1748
  width: e.artboard.width,
1745
1749
  height: e.artboard.height,
1746
1750
  backgroundColor: e.artboard.backgroundColor ?? "#ffffff"
1747
1751
  }), this.engine.setAreas(t), this.engine.setContents(this.contentManager.getContents()), e.backgroundImage && this.engine.setBackgroundImage(e.backgroundImage);
1748
- const n = e.artboard.safeArea ?? e.safeArea;
1749
- n && this.engine.setSafeArea(n), this.engine.start(), this.engine.fitToView(), this.stateManager.update({
1752
+ const s = e.artboard.safeArea ?? e.safeArea;
1753
+ s && this.engine.setSafeArea(s), this.engine.start(), this.engine.fitToView(), this.stateManager.update({
1750
1754
  zoom: this.engine.getZoom(),
1751
1755
  pan: this.engine.getPan()
1752
- }), this.zoomToolbar.setZoom(this.engine.getZoom()), this.interaction = new F(
1756
+ }), this.zoomToolbar.setZoom(this.engine.getZoom()), this.interaction = new B(
1753
1757
  this.canvas,
1754
1758
  this.engine,
1755
1759
  () => this.contentManager.getContents(),
@@ -1771,7 +1775,7 @@ class ee extends HTMLElement {
1771
1775
  if (t.views.length === 0)
1772
1776
  throw new Error("Product has no active templates");
1773
1777
  this.productViews = t.views, this.perViewContents.clear();
1774
- const i = t.views.find((n) => n.isDefault) || t.views[0];
1778
+ const i = t.views.find((s) => s.isDefault) || t.views[0];
1775
1779
  await this.loadTemplateData(i.template.templateJson), this.activeViewName = i.viewName, this.zoomToolbar && t.views.length > 1 && (this.zoomToolbar.setViews(this.getViews()), this.zoomToolbar.setActiveView(i.viewName));
1776
1780
  } catch (t) {
1777
1781
  throw new Error(`Failed to load product: ${t.message}`);
@@ -1782,9 +1786,9 @@ class ee extends HTMLElement {
1782
1786
  const t = e.template.templateJson;
1783
1787
  this.currentTemplate = t;
1784
1788
  const i = t.areas || [];
1785
- this.contentManager = new T(i);
1786
- const n = this.perViewContents.get(e.viewName);
1787
- n && this.contentManager.fromJSON(n), this.engine.setArtboard({
1789
+ this.contentManager = new k(i);
1790
+ const s = this.perViewContents.get(e.viewName);
1791
+ s && this.contentManager.fromJSON(s), this.engine.setArtboard({
1788
1792
  width: t.artboard.width,
1789
1793
  height: t.artboard.height,
1790
1794
  backgroundColor: t.artboard.backgroundColor ?? "#ffffff"
@@ -1809,9 +1813,9 @@ class ee extends HTMLElement {
1809
1813
  buildUI(e, t) {
1810
1814
  this.shadow.innerHTML = "";
1811
1815
  const i = document.createElement("style");
1812
- i.textContent = z, this.shadow.appendChild(i), this.container = h("div", { class: "editor-container" }), this.zoomToolbar = new B(), this.subscriptions.push(this.zoomToolbar.on("zoom-in", () => this.handleZoomIn())), this.subscriptions.push(this.zoomToolbar.on("zoom-out", () => this.handleZoomOut())), this.subscriptions.push(this.zoomToolbar.on("zoom-fit", () => this.handleZoomFit())), this.subscriptions.push(this.zoomToolbar.on("view-change", ({ viewName: o }) => {
1813
- const s = this.productViews.find((r) => r.viewName === o);
1814
- s && this.switchToView(s);
1816
+ i.textContent = M, this.shadow.appendChild(i), this.container = h("div", { class: "editor-container" }), this.zoomToolbar = new H(), this.subscriptions.push(this.zoomToolbar.on("zoom-in", () => this.handleZoomIn())), this.subscriptions.push(this.zoomToolbar.on("zoom-out", () => this.handleZoomOut())), this.subscriptions.push(this.zoomToolbar.on("zoom-fit", () => this.handleZoomFit())), this.subscriptions.push(this.zoomToolbar.on("view-change", ({ viewName: o }) => {
1817
+ const n = this.productViews.find((r) => r.viewName === o);
1818
+ n && this.switchToView(n);
1815
1819
  })), this.subscriptions.push(this.zoomToolbar.on("close", () => {
1816
1820
  this.dispatchEvent(new CustomEvent("customizer:close", {
1817
1821
  bubbles: !0,
@@ -1820,21 +1824,26 @@ class ee extends HTMLElement {
1820
1824
  })), this.subscriptions.push(this.zoomToolbar.on("save", async () => {
1821
1825
  this.zoomToolbar.setSaveDisabled(!0, "Saving...");
1822
1826
  try {
1823
- await this.finalize(), this.zoomToolbar.setSaveDisabled(!0, "Saved!"), this.showToast("Design saved successfully!", "info"), setTimeout(() => {
1827
+ const o = await this.finalize();
1828
+ this.zoomToolbar.setSaveDisabled(!0, "Saved!"), this.showToast("Design saved successfully!", "info"), this.dispatchEvent(new CustomEvent("customizer:save", {
1829
+ detail: o,
1830
+ bubbles: !0,
1831
+ composed: !0
1832
+ })), setTimeout(() => {
1824
1833
  this.zoomToolbar.setSaveDisabled(!1, "Save Customization"), this.zoomToolbar.showSaveButton(!1), this.stateManager.update({ isDirty: !1 });
1825
1834
  }, 2e3);
1826
1835
  } catch (o) {
1827
1836
  console.error("Customizer: finalize failed", o);
1828
- const s = o instanceof Error ? o.message : "Save failed. Please try again.";
1829
- this.showToast(s, "error"), this.zoomToolbar.setSaveDisabled(!1, "Save Customization");
1837
+ const n = o instanceof Error ? o.message : "Save failed. Please try again.";
1838
+ this.showToast(n, "error"), this.zoomToolbar.setSaveDisabled(!1, "Save Customization");
1830
1839
  }
1831
- }));
1832
- const n = h("div", { class: "canvas-area" });
1833
- n.appendChild(this.zoomToolbar.getElement()), this.canvasWrapper = h("div", { class: "canvas-wrapper" }), this.canvas = document.createElement("canvas"), this.canvas.className = "editor-canvas", this.canvasWrapper.appendChild(this.canvas), n.appendChild(this.canvasWrapper), this.inputPanel = new j(t), this.wireInputPanelEvents();
1840
+ })), this.getAttribute("show-close-button") === "false" && this.zoomToolbar.setCloseButtonVisible(!1), this.getAttribute("show-save-button") === "false" && this.zoomToolbar.setSaveButtonEnabled(!1);
1841
+ const s = h("div", { class: "canvas-area" });
1842
+ s.appendChild(this.zoomToolbar.getElement()), this.canvasWrapper = h("div", { class: "canvas-wrapper" }), this.canvas = document.createElement("canvas"), this.canvas.className = "editor-canvas", this.canvasWrapper.appendChild(this.canvas), s.appendChild(this.canvasWrapper), this.inputPanel = new j(t), this.wireInputPanelEvents();
1834
1843
  const a = h("div", { class: "editor-main" });
1835
- a.appendChild(n), a.appendChild(this.inputPanel.getBackdrop()), a.appendChild(this.inputPanel.getElement()), this.container.appendChild(a), this.shadow.appendChild(this.container), this.resizeObserver = new ResizeObserver((o) => {
1836
- for (const s of o)
1837
- s.target === this.canvasWrapper && this.handleCanvasResize(s.contentRect), s.target === this.container && this.handleLayoutResize(s.contentRect);
1844
+ a.appendChild(s), a.appendChild(this.inputPanel.getBackdrop()), a.appendChild(this.inputPanel.getElement()), this.container.appendChild(a), this.shadow.appendChild(this.container), this.resizeObserver = new ResizeObserver((o) => {
1845
+ for (const n of o)
1846
+ n.target === this.canvasWrapper && this.handleCanvasResize(n.contentRect), n.target === this.container && this.handleLayoutResize(n.contentRect);
1838
1847
  }), this.resizeObserver.observe(this.canvasWrapper), this.resizeObserver.observe(this.container);
1839
1848
  }
1840
1849
  // ─── Event Wiring ───
@@ -1882,10 +1891,10 @@ class ee extends HTMLElement {
1882
1891
  })
1883
1892
  ), this.subscriptions.push(
1884
1893
  this.inputPanel.on("image:change", async ({ areaId: e, dataUrl: t, filename: i }) => {
1885
- const n = this.contentManager.getContents();
1894
+ const s = this.contentManager.getContents();
1886
1895
  let a = 0;
1887
- for (const [s, r] of n)
1888
- r.type === "image" && s !== e && a++;
1896
+ for (const [n, r] of s)
1897
+ r.type === "image" && n !== e && a++;
1889
1898
  if (a >= 10) {
1890
1899
  this.showToast("Maximum of 10 images per design reached", "error");
1891
1900
  return;
@@ -1894,28 +1903,28 @@ class ee extends HTMLElement {
1894
1903
  this.showToast("Storage quota exceeded. Upgrade your plan for more storage.", "error");
1895
1904
  return;
1896
1905
  }
1897
- const o = this.currentTemplate?.areas.find((s) => s.id === e);
1906
+ const o = this.currentTemplate?.areas.find((n) => n.id === e);
1898
1907
  if (o && this.currentTemplate) {
1899
- const s = this.currentTemplate.print.targetDpi;
1908
+ const n = this.currentTemplate.print.targetDpi;
1900
1909
  try {
1901
1910
  const r = new Image(), d = await new Promise((p, g) => {
1902
1911
  r.onload = () => p({ width: r.naturalWidth, height: r.naturalHeight }), r.onerror = () => g(new Error("Failed to load image")), r.src = t;
1903
- }), l = o.location.width / s, c = o.location.height / s, u = K(
1912
+ }), l = o.location.width / n, c = o.location.height / n, u = K(
1904
1913
  d.width,
1905
1914
  d.height,
1906
1915
  l,
1907
1916
  c,
1908
- s
1917
+ n
1909
1918
  );
1910
1919
  if (u.actualDPI < 150) {
1911
1920
  if (!await this.showConfirmation(
1912
1921
  "Low resolution detected",
1913
- `This image is ${u.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${s} DPI.`,
1922
+ `This image is ${u.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${n} DPI.`,
1914
1923
  "Cancel",
1915
1924
  "Use Anyway"
1916
1925
  )) return;
1917
- } else u.actualDPI < s && (this.showToast(
1918
- `Image resolution is ${u.actualDPI} DPI (${s} recommended)`,
1926
+ } else u.actualDPI < n && (this.showToast(
1927
+ `Image resolution is ${u.actualDPI} DPI (${n} recommended)`,
1919
1928
  "warning"
1920
1929
  ), this.stateManager.update({
1921
1930
  warnings: [
@@ -1927,8 +1936,8 @@ class ee extends HTMLElement {
1927
1936
  }
1928
1937
  }
1929
1938
  if (this.contentManager.setImageContent(e, t, i), this.saveContentState(), this.storageUsage) {
1930
- const s = Math.round(t.length * 0.75);
1931
- this.storageUsage.storageUsed += s, this.storageUsage.usagePercent = this.storageUsage.storageQuota > 0 ? Math.round(this.storageUsage.storageUsed / this.storageUsage.storageQuota * 100) : 0;
1939
+ const n = Math.round(t.length * 0.75);
1940
+ this.storageUsage.storageUsed += n, this.storageUsage.usagePercent = this.storageUsage.storageQuota > 0 ? Math.round(this.storageUsage.storageUsed / this.storageUsage.storageQuota * 100) : 0;
1932
1941
  }
1933
1942
  })
1934
1943
  ), this.subscriptions.push(
@@ -1988,8 +1997,8 @@ class ee extends HTMLElement {
1988
1997
  handleCanvasResize(e) {
1989
1998
  const { width: t, height: i } = e;
1990
1999
  if (t === 0 || i === 0) return;
1991
- const n = window.devicePixelRatio || 1;
1992
- this.canvas.width = t * n, this.canvas.height = i * n, this.canvas.style.width = `${t}px`, this.canvas.style.height = `${i}px`, this.engine && (this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom()));
2000
+ const s = window.devicePixelRatio || 1;
2001
+ this.canvas.width = t * s, this.canvas.height = i * s, this.canvas.style.width = `${t}px`, this.canvas.style.height = `${i}px`, this.engine && (this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom()));
1993
2002
  }
1994
2003
  handleLayoutResize(e) {
1995
2004
  const t = e.width < _;
@@ -2040,18 +2049,18 @@ class ee extends HTMLElement {
2040
2049
  const i = h("div", { class: `editor-toast editor-toast-${t}` }, e);
2041
2050
  this.shadow.appendChild(i), setTimeout(() => i.remove(), 4e3);
2042
2051
  }
2043
- showConfirmation(e, t, i = "Cancel", n = "Continue") {
2052
+ showConfirmation(e, t, i = "Cancel", s = "Continue") {
2044
2053
  return new Promise((a) => {
2045
- const o = h("div", { class: "editor-modal-overlay" }), s = h("div", { class: "editor-modal" });
2046
- s.appendChild(h("div", { class: "editor-modal-title" }, e)), s.appendChild(h("div", { class: "editor-modal-body" }, t));
2054
+ const o = h("div", { class: "editor-modal-overlay" }), n = h("div", { class: "editor-modal" });
2055
+ n.appendChild(h("div", { class: "editor-modal-title" }, e)), n.appendChild(h("div", { class: "editor-modal-body" }, t));
2047
2056
  const r = h("div", { class: "editor-modal-actions" }), d = h("button", { class: "editor-modal-btn" }, i);
2048
2057
  d.addEventListener("click", () => {
2049
2058
  o.remove(), a(!1);
2050
2059
  });
2051
- const l = h("button", { class: "editor-modal-btn editor-modal-btn-primary" }, n);
2060
+ const l = h("button", { class: "editor-modal-btn editor-modal-btn-primary" }, s);
2052
2061
  l.addEventListener("click", () => {
2053
2062
  o.remove(), a(!0);
2054
- }), r.appendChild(d), r.appendChild(l), s.appendChild(r), o.appendChild(s), o.addEventListener("click", (c) => {
2063
+ }), r.appendChild(d), r.appendChild(l), n.appendChild(r), o.appendChild(n), o.addEventListener("click", (c) => {
2055
2064
  c.target === o && (o.remove(), a(!1));
2056
2065
  }), this.shadow.appendChild(o);
2057
2066
  });
@@ -2060,9 +2069,9 @@ class ee extends HTMLElement {
2060
2069
  showError(e, t) {
2061
2070
  this.shadow.innerHTML = "";
2062
2071
  const i = document.createElement("style");
2063
- i.textContent = z, this.shadow.appendChild(i);
2064
- const n = h("div", { class: "error-container" });
2065
- n.appendChild(h("div", { class: "error-title" }, e)), n.appendChild(h("div", { class: "error-message" }, t?.message ?? "")), this.shadow.appendChild(n), this.dispatchEvent(
2072
+ i.textContent = M, this.shadow.appendChild(i);
2073
+ const s = h("div", { class: "error-container" });
2074
+ s.appendChild(h("div", { class: "error-title" }, e)), s.appendChild(h("div", { class: "error-message" }, t?.message ?? "")), this.shadow.appendChild(s), this.dispatchEvent(
2066
2075
  new CustomEvent("error", {
2067
2076
  detail: { message: e, error: t },
2068
2077
  bubbles: !0,
@@ -2088,9 +2097,9 @@ function te(f, e) {
2088
2097
  if (e.templateId && e.productId)
2089
2098
  throw new Error("Only one of templateId or productId should be provided, not both");
2090
2099
  const i = document.createElement("customizer-editor");
2091
- e.productId ? i.setAttribute("product-id", e.productId) : i.setAttribute("template-id", e.templateId), e.theme && i.setAttribute("theme", e.theme), e.mode && i.setAttribute("mode", e.mode), e.className && i.classList.add(e.className), i.style.width = "100%", i.style.height = "100%";
2092
- const n = [], a = (s, r) => {
2093
- i.addEventListener(s, r), n.push({ event: s, handler: r });
2100
+ e.productId ? i.setAttribute("product-id", e.productId) : i.setAttribute("template-id", e.templateId), e.theme && i.setAttribute("theme", e.theme), e.mode && i.setAttribute("mode", e.mode), e.className && i.classList.add(e.className), e.showCloseButton === !1 && i.setAttribute("show-close-button", "false"), e.showSaveButton === !1 && i.setAttribute("show-save-button", "false"), i.style.width = "100%", i.style.height = "100%";
2101
+ const s = [], a = (n, r) => {
2102
+ i.addEventListener(n, r), s.push({ event: n, handler: r });
2094
2103
  };
2095
2104
  t.innerHTML = "", t.appendChild(i);
2096
2105
  const o = {
@@ -2099,10 +2108,10 @@ function te(f, e) {
2099
2108
  throw new Error("Editor not ready: getDesign method not available");
2100
2109
  return i.getDesign();
2101
2110
  },
2102
- setDesign(s) {
2111
+ setDesign(n) {
2103
2112
  if (typeof i.setDesign != "function")
2104
2113
  throw new Error("Editor not ready: setDesign method not available");
2105
- i.setDesign(s);
2114
+ i.setDesign(n);
2106
2115
  },
2107
2116
  undo() {
2108
2117
  if (typeof i.undo != "function")
@@ -2121,7 +2130,7 @@ function te(f, e) {
2121
2130
  return typeof i.canRedo != "function" ? !1 : i.canRedo();
2122
2131
  },
2123
2132
  async finalize() {
2124
- const s = this.getDesign(), r = e.apiUrl || "https://api.varianta.io";
2133
+ const n = this.getDesign(), r = e.apiUrl || "https://api.varianta.io";
2125
2134
  try {
2126
2135
  const d = await fetch(`${r}/public/finalize`, {
2127
2136
  method: "POST",
@@ -2129,8 +2138,8 @@ function te(f, e) {
2129
2138
  "Content-Type": "application/json"
2130
2139
  },
2131
2140
  body: JSON.stringify({
2132
- templateId: s.templateId || e.templateId,
2133
- designJson: s
2141
+ templateId: n.templateId || e.templateId,
2142
+ designJson: n
2134
2143
  })
2135
2144
  });
2136
2145
  if (!d.ok) {
@@ -2173,25 +2182,25 @@ function te(f, e) {
2173
2182
  throw e.onError && e.onError(l), d;
2174
2183
  }
2175
2184
  },
2176
- addTextLayer(s) {
2185
+ addTextLayer(n) {
2177
2186
  if (typeof i.addTextLayer != "function")
2178
2187
  throw new Error("Editor not ready: addTextLayer method not available");
2179
- i.addTextLayer(s);
2188
+ i.addTextLayer(n);
2180
2189
  },
2181
- async addImageLayer(s) {
2190
+ async addImageLayer(n) {
2182
2191
  if (typeof i.addImageLayer != "function")
2183
2192
  throw new Error("Editor not ready: addImageLayer method not available");
2184
- return i.addImageLayer(s);
2193
+ return i.addImageLayer(n);
2185
2194
  },
2186
- removeLayer(s) {
2195
+ removeLayer(n) {
2187
2196
  if (typeof i.removeLayer != "function")
2188
2197
  throw new Error("Editor not ready: removeLayer method not available");
2189
- i.removeLayer(s);
2198
+ i.removeLayer(n);
2190
2199
  },
2191
- selectLayer(s) {
2200
+ selectLayer(n) {
2192
2201
  if (typeof i.selectLayer != "function")
2193
2202
  throw new Error("Editor not ready: selectLayer method not available");
2194
- i.selectLayer(s);
2203
+ i.selectLayer(n);
2195
2204
  },
2196
2205
  getSelectedLayerId() {
2197
2206
  return typeof i.getSelectedLayerId != "function" ? null : i.getSelectedLayerId();
@@ -2202,20 +2211,20 @@ function te(f, e) {
2202
2211
  getViews() {
2203
2212
  return typeof i.getViews != "function" ? [] : i.getViews();
2204
2213
  },
2205
- setActiveView(s) {
2214
+ setActiveView(n) {
2206
2215
  if (typeof i.setActiveView != "function")
2207
2216
  throw new Error("Editor not ready: setActiveView method not available");
2208
- i.setActiveView(s);
2217
+ i.setActiveView(n);
2209
2218
  },
2210
- setTheme(s) {
2211
- i.setAttribute("theme", s);
2219
+ setTheme(n) {
2220
+ i.setAttribute("theme", n);
2212
2221
  },
2213
- setMode(s) {
2214
- i.setAttribute("mode", s);
2222
+ setMode(n) {
2223
+ i.setAttribute("mode", n);
2215
2224
  },
2216
2225
  destroy() {
2217
- n.forEach(({ event: s, handler: r }) => {
2218
- i.removeEventListener(s, r);
2226
+ s.forEach(({ event: n, handler: r }) => {
2227
+ i.removeEventListener(n, r);
2219
2228
  }), i.parentNode && i.parentNode.removeChild(i);
2220
2229
  },
2221
2230
  getElement() {
@@ -2224,22 +2233,26 @@ function te(f, e) {
2224
2233
  };
2225
2234
  return e.onReady && a("ready", (() => {
2226
2235
  e.onReady?.(o);
2227
- })), e.onChange && a("change", ((s) => {
2228
- const r = s.detail.design;
2236
+ })), e.onChange && a("change", ((n) => {
2237
+ const r = n.detail.design;
2229
2238
  e.onChange?.(r);
2230
- })), e.onLayerSelect && a("layer:select", ((s) => {
2231
- e.onLayerSelect?.(s.detail.layerId);
2232
- })), e.onLayerAdd && a("layer:add", ((s) => {
2233
- e.onLayerAdd?.(s.detail.layerId);
2234
- })), e.onLayerRemove && a("layer:remove", ((s) => {
2235
- e.onLayerRemove?.(s.detail.layerId);
2236
- })), e.onLayerUpdate && a("layer:update", ((s) => {
2237
- e.onLayerUpdate?.(s.detail.layerId);
2238
- })), e.onError && a("error", ((s) => {
2239
- const r = s.detail.error;
2239
+ })), e.onLayerSelect && a("layer:select", ((n) => {
2240
+ e.onLayerSelect?.(n.detail.layerId);
2241
+ })), e.onLayerAdd && a("layer:add", ((n) => {
2242
+ e.onLayerAdd?.(n.detail.layerId);
2243
+ })), e.onLayerRemove && a("layer:remove", ((n) => {
2244
+ e.onLayerRemove?.(n.detail.layerId);
2245
+ })), e.onLayerUpdate && a("layer:update", ((n) => {
2246
+ e.onLayerUpdate?.(n.detail.layerId);
2247
+ })), e.onError && a("error", ((n) => {
2248
+ const r = n.detail.error;
2240
2249
  e.onError?.(r);
2241
- })), e.onViewChange && a("view-change", ((s) => {
2242
- e.onViewChange?.(s.detail.viewName);
2250
+ })), e.onViewChange && a("view-change", ((n) => {
2251
+ e.onViewChange?.(n.detail.viewName);
2252
+ })), e.onClose && a("customizer:close", (() => {
2253
+ e.onClose?.();
2254
+ })), e.onSave && a("customizer:save", ((n) => {
2255
+ e.onSave?.(n.detail);
2243
2256
  })), e.initialDesign && i.addEventListener(
2244
2257
  "ready",
2245
2258
  () => {