@varianta/sdk 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/index.cjs.js +4 -4
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +586 -455
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +4 -4
- package/dist/index.umd.js.map +1 -1
- package/dist/react.cjs.js +1 -1
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.esm.js +207 -208
- package/dist/react.esm.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/react/Customizer.d.ts +1 -1
- package/dist/types/react/Customizer.d.ts.map +1 -1
- package/dist/types/react/index.d.ts +1 -0
- package/dist/types/react/index.d.ts.map +1 -1
- package/dist/types/react/useCustomizer.d.ts +2 -1
- package/dist/types/react/useCustomizer.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +13 -7
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/validation.d.ts +38 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/types/vanilla/index.d.ts +1 -1
- package/dist/types/vanilla/index.d.ts.map +1 -1
- package/dist/types/vue/index.d.ts +1 -0
- package/dist/types/vue/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class B {
|
|
2
2
|
constructor(e) {
|
|
3
3
|
this.animationFrameId = null, this.needsRender = !0, this.areas = [], this.contents = /* @__PURE__ */ new Map(), this.artboard = null, this.bgImageElement = null, this.loadedImages = /* @__PURE__ */ new Map(), this.loadingImages = /* @__PURE__ */ new Set(), this.zoom = 1, this.pan = { x: 0, y: 0 }, this.selectedAreaId = null, this.hoveredHandle = null, this.eventListeners = /* @__PURE__ */ new Map(), this.safeArea = null, this.safeAreaViolations = /* @__PURE__ */ new Set(), this.showAreaBorders = !0, this.areaSelectionEnabled = !0, this.renderLoop = () => {
|
|
4
4
|
try {
|
|
@@ -150,8 +150,8 @@ class N {
|
|
|
150
150
|
const e = 0, { width: t, height: i } = this.artboard, s = window.devicePixelRatio || 1, n = this.canvas.width / s - e * 2, o = this.canvas.height / s - e * 2;
|
|
151
151
|
if (n <= 0 || o <= 0)
|
|
152
152
|
return;
|
|
153
|
-
const a = n / t, r = o / i,
|
|
154
|
-
this.zoom =
|
|
153
|
+
const a = n / t, r = o / i, l = Math.min(a, r), d = this.canvas.width / s, h = this.canvas.height / s, p = (d / l - t) / 2, c = (h / l - i) / 2;
|
|
154
|
+
this.zoom = l, this.pan = { x: p, y: c }, this.requestRender();
|
|
155
155
|
}
|
|
156
156
|
/**
|
|
157
157
|
* Set the selected area
|
|
@@ -188,7 +188,7 @@ class N {
|
|
|
188
188
|
* Get content bounds for an area (in artboard coordinates)
|
|
189
189
|
*/
|
|
190
190
|
getContentBounds(e) {
|
|
191
|
-
const t = this.areas.find((
|
|
191
|
+
const t = this.areas.find((l) => l.id === e);
|
|
192
192
|
if (!t)
|
|
193
193
|
return null;
|
|
194
194
|
const i = this.contents.get(e);
|
|
@@ -196,32 +196,32 @@ class N {
|
|
|
196
196
|
return null;
|
|
197
197
|
const { location: s } = t, { x: n, y: o, width: a, height: r } = s;
|
|
198
198
|
if (i.type === "text") {
|
|
199
|
-
const
|
|
199
|
+
const l = i.offset || { x: 0, y: 0 }, d = i.scale ?? 1, h = i.rotation ?? 0, p = a * d, c = r * d, m = n + a / 2 + l.x, f = o + r / 2 + l.y;
|
|
200
200
|
return {
|
|
201
|
-
x:
|
|
202
|
-
y:
|
|
201
|
+
x: m - p / 2,
|
|
202
|
+
y: f - c / 2,
|
|
203
203
|
width: p,
|
|
204
|
-
height:
|
|
205
|
-
rotation:
|
|
206
|
-
centerX:
|
|
207
|
-
centerY:
|
|
204
|
+
height: c,
|
|
205
|
+
rotation: h,
|
|
206
|
+
centerX: m,
|
|
207
|
+
centerY: f
|
|
208
208
|
};
|
|
209
209
|
} else if (i.type === "image") {
|
|
210
|
-
const
|
|
211
|
-
if (!
|
|
210
|
+
const l = this.loadedImages.get(e);
|
|
211
|
+
if (!l)
|
|
212
212
|
return null;
|
|
213
|
-
const
|
|
214
|
-
let
|
|
215
|
-
|
|
216
|
-
const b =
|
|
213
|
+
const d = i.offset || { x: 0, y: 0 }, h = i.scale ?? 1, p = i.rotation ?? 0, c = l.naturalWidth / l.naturalHeight, m = a / r;
|
|
214
|
+
let f, v;
|
|
215
|
+
c > m ? (f = a, v = a / c) : (v = r, f = r * c);
|
|
216
|
+
const b = f * h, y = v * h, x = n + a / 2 + d.x, C = o + r / 2 + d.y;
|
|
217
217
|
return {
|
|
218
|
-
x:
|
|
219
|
-
y:
|
|
218
|
+
x: x - b / 2,
|
|
219
|
+
y: C - y / 2,
|
|
220
220
|
width: b,
|
|
221
|
-
height:
|
|
221
|
+
height: y,
|
|
222
222
|
rotation: p,
|
|
223
|
-
centerX:
|
|
224
|
-
centerY:
|
|
223
|
+
centerX: x,
|
|
224
|
+
centerY: C
|
|
225
225
|
};
|
|
226
226
|
}
|
|
227
227
|
return null;
|
|
@@ -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: s, centerY: n, width: o, height: a, rotation: r } = i,
|
|
262
|
-
return
|
|
261
|
+
const { centerX: s, centerY: n, width: o, height: a, rotation: r } = i, l = -r * Math.PI / 180, d = Math.cos(l), h = Math.sin(l), p = e - s, c = t - n, m = p * d - c * h, f = p * h + c * d;
|
|
262
|
+
return m >= -o / 2 && m <= o / 2 && f >= -a / 2 && f <= a / 2;
|
|
263
263
|
}
|
|
264
264
|
/**
|
|
265
265
|
* Get handle positions for the selected content
|
|
@@ -274,23 +274,23 @@ class N {
|
|
|
274
274
|
const s = this.getContentBounds(e);
|
|
275
275
|
if (!s)
|
|
276
276
|
return null;
|
|
277
|
-
const { centerX: n, centerY: o, width: a, height: r, rotation:
|
|
278
|
-
x: n + b *
|
|
279
|
-
y: o + b *
|
|
277
|
+
const { centerX: n, centerY: o, width: a, height: r, rotation: l } = s, d = 6 / this.zoom, h = 30 / this.zoom, p = l * Math.PI / 180, c = Math.cos(p), m = Math.sin(p), f = (b, y) => ({
|
|
278
|
+
x: n + b * c - y * m,
|
|
279
|
+
y: o + b * m + y * c
|
|
280
280
|
}), v = [];
|
|
281
281
|
if (i.type === "image" && t.imageOptions?.allowScaling || i.type === "text" && t.textOptions?.allowScaling) {
|
|
282
|
-
const b = a / 2,
|
|
283
|
-
v.push({ type: "nw", ...
|
|
284
|
-
const
|
|
285
|
-
v.push({ type: "ne", ...
|
|
286
|
-
const
|
|
287
|
-
v.push({ type: "se", ...
|
|
288
|
-
const
|
|
289
|
-
v.push({ type: "sw", ...
|
|
282
|
+
const b = a / 2, y = r / 2, x = f(-b, -y);
|
|
283
|
+
v.push({ type: "nw", ...x, radius: d });
|
|
284
|
+
const C = f(b, -y);
|
|
285
|
+
v.push({ type: "ne", ...C, radius: d });
|
|
286
|
+
const I = f(b, y);
|
|
287
|
+
v.push({ type: "se", ...I, radius: d });
|
|
288
|
+
const k = f(-b, y);
|
|
289
|
+
v.push({ type: "sw", ...k, radius: d });
|
|
290
290
|
}
|
|
291
291
|
if (i.type === "image" && t.imageOptions?.allowRotation || i.type === "text" && t.textOptions?.allowRotation) {
|
|
292
|
-
const b =
|
|
293
|
-
v.push({ type: "rotate", ...b, radius:
|
|
292
|
+
const b = f(0, -r / 2 - h);
|
|
293
|
+
v.push({ type: "rotate", ...b, radius: d });
|
|
294
294
|
}
|
|
295
295
|
return v;
|
|
296
296
|
}
|
|
@@ -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: s, left: n } = this.safeArea, o = n, a = t, r = this.artboard.width - i,
|
|
348
|
-
for (const
|
|
349
|
-
if (!this.contents.get(
|
|
347
|
+
const e = [], { top: t, right: i, bottom: s, left: n } = this.safeArea, o = n, a = t, r = this.artboard.width - i, l = this.artboard.height - s;
|
|
348
|
+
for (const d of this.areas) {
|
|
349
|
+
if (!this.contents.get(d.id))
|
|
350
350
|
continue;
|
|
351
|
-
const
|
|
352
|
-
if (!
|
|
351
|
+
const h = this.getContentBounds(d.id);
|
|
352
|
+
if (!h)
|
|
353
353
|
continue;
|
|
354
|
-
const { centerX: p, centerY:
|
|
355
|
-
(
|
|
354
|
+
const { centerX: p, centerY: c, width: m, height: f, rotation: v } = h, b = v * Math.PI / 180, y = Math.abs(Math.cos(b)), x = Math.abs(Math.sin(b)), C = m * y + f * x, I = m * x + f * y, k = p - C / 2, V = c - I / 2, F = p + C / 2, $ = c + I / 2;
|
|
355
|
+
(k < o || V < a || F > r || $ > l) && e.push(d.id);
|
|
356
356
|
}
|
|
357
357
|
return this.safeAreaViolations = new Set(e), this.requestRender(), e;
|
|
358
358
|
}
|
|
@@ -432,11 +432,11 @@ class N {
|
|
|
432
432
|
this.renderAreaWithContent(a);
|
|
433
433
|
this.backgroundImage?.position === "overlay" && this.renderBackgroundImage(), this.renderSafeAreaGuide();
|
|
434
434
|
for (const a of this.safeAreaViolations) {
|
|
435
|
-
const r = this.areas.find((
|
|
435
|
+
const r = this.areas.find((c) => c.id === a);
|
|
436
436
|
if (!r)
|
|
437
437
|
continue;
|
|
438
|
-
const { x:
|
|
439
|
-
e.save(), e.fillStyle = "rgba(239, 68, 68, 0.15)", e.fillRect(
|
|
438
|
+
const { x: l, y: d, width: h, height: p } = r.location;
|
|
439
|
+
e.save(), e.fillStyle = "rgba(239, 68, 68, 0.15)", e.fillRect(l, d, h, p), e.restore();
|
|
440
440
|
}
|
|
441
441
|
this.selectedAreaId && this.renderSelectionOverlay(this.selectedAreaId), e.restore();
|
|
442
442
|
}
|
|
@@ -446,25 +446,25 @@ class N {
|
|
|
446
446
|
renderSelectionOverlay(e) {
|
|
447
447
|
const t = this.getContentBounds(e);
|
|
448
448
|
if (!t) {
|
|
449
|
-
const
|
|
450
|
-
if (!
|
|
449
|
+
const d = this.areas.find((v) => v.id === e);
|
|
450
|
+
if (!d)
|
|
451
451
|
return;
|
|
452
|
-
const { ctx:
|
|
453
|
-
|
|
452
|
+
const { ctx: h } = this, { x: p, y: c, width: m, height: f } = d.location;
|
|
453
|
+
h.save(), h.strokeStyle = "#3b82f6", h.lineWidth = 2 / this.zoom, h.setLineDash([]), h.strokeRect(p, c, m, f), h.restore();
|
|
454
454
|
return;
|
|
455
455
|
}
|
|
456
456
|
const { ctx: i } = this, { centerX: s, centerY: n, width: o, height: a, rotation: r } = t;
|
|
457
457
|
i.save(), i.translate(s, n), i.rotate(r * Math.PI / 180), i.strokeStyle = "#3b82f6", i.lineWidth = 2 / this.zoom, i.setLineDash([]), i.strokeRect(-o / 2, -a / 2, o, a), i.restore();
|
|
458
|
-
const
|
|
459
|
-
if (
|
|
460
|
-
for (const
|
|
458
|
+
const l = this.getContentHandlePositions(e);
|
|
459
|
+
if (l)
|
|
460
|
+
for (const d of l) {
|
|
461
461
|
i.save();
|
|
462
|
-
const
|
|
463
|
-
if (
|
|
464
|
-
const
|
|
465
|
-
i.beginPath(), i.strokeStyle =
|
|
462
|
+
const h = this.hoveredHandle === d.type, p = h ? "#dbeafe" : "#ffffff", c = h ? "#2563eb" : "#3b82f6";
|
|
463
|
+
if (d.type === "rotate") {
|
|
464
|
+
const m = this.getRotatedPoint(s, n - a / 2, s, n, r);
|
|
465
|
+
i.beginPath(), i.strokeStyle = c, i.lineWidth = 1 / this.zoom, i.setLineDash([4 / this.zoom, 4 / this.zoom]), i.moveTo(m.x, m.y), i.lineTo(d.x, d.y), i.stroke(), i.setLineDash([]), i.beginPath(), i.fillStyle = p, i.strokeStyle = c, i.lineWidth = 2 / this.zoom, i.arc(d.x, d.y, d.radius, 0, Math.PI * 2), i.fill(), i.stroke(), i.beginPath(), i.strokeStyle = c, i.lineWidth = 1.5 / this.zoom, i.arc(d.x, d.y, d.radius * 0.5, -Math.PI * 0.8, Math.PI * 0.5), i.stroke();
|
|
466
466
|
} else
|
|
467
|
-
i.fillStyle = p, i.strokeStyle =
|
|
467
|
+
i.fillStyle = p, i.strokeStyle = c, i.lineWidth = 2 / this.zoom, i.fillRect(d.x - d.radius, d.y - d.radius, d.radius * 2, d.radius * 2), i.strokeRect(d.x - d.radius, d.y - d.radius, d.radius * 2, d.radius * 2);
|
|
468
468
|
i.restore();
|
|
469
469
|
}
|
|
470
470
|
}
|
|
@@ -472,10 +472,10 @@ class N {
|
|
|
472
472
|
* Rotate a point around a center
|
|
473
473
|
*/
|
|
474
474
|
getRotatedPoint(e, t, i, s, n) {
|
|
475
|
-
const o = n * Math.PI / 180, a = Math.cos(o), r = Math.sin(o),
|
|
475
|
+
const o = n * Math.PI / 180, a = Math.cos(o), r = Math.sin(o), l = e - i, d = t - s;
|
|
476
476
|
return {
|
|
477
|
-
x: i +
|
|
478
|
-
y: s +
|
|
477
|
+
x: i + l * a - d * r,
|
|
478
|
+
y: s + l * r + d * a
|
|
479
479
|
};
|
|
480
480
|
}
|
|
481
481
|
/**
|
|
@@ -487,8 +487,8 @@ class N {
|
|
|
487
487
|
const { ctx: e } = this, { opacity: t } = this.backgroundImage, { width: i, height: s } = this.artboard;
|
|
488
488
|
e.save(), e.globalAlpha = t;
|
|
489
489
|
const n = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = i / s;
|
|
490
|
-
let a, r,
|
|
491
|
-
n > o ? (r = s, a = s * n,
|
|
490
|
+
let a, r, l, d;
|
|
491
|
+
n > o ? (r = s, a = s * n, l = (i - a) / 2, d = 0) : (a = i, r = i / n, l = 0, d = (s - r) / 2), e.drawImage(this.bgImageElement, l, d, a, r), e.restore();
|
|
492
492
|
}
|
|
493
493
|
/**
|
|
494
494
|
* Render safe area guide as dashed green rectangle
|
|
@@ -496,8 +496,8 @@ class N {
|
|
|
496
496
|
renderSafeAreaGuide() {
|
|
497
497
|
if (!this.safeArea || !this.artboard)
|
|
498
498
|
return;
|
|
499
|
-
const { ctx: e } = this, { top: t, right: i, bottom: s, left: n } = this.safeArea, o = n, a = t, r = this.artboard.width - n - i,
|
|
500
|
-
r <= 0 ||
|
|
499
|
+
const { ctx: e } = this, { top: t, right: i, bottom: s, left: n } = this.safeArea, o = n, a = t, r = this.artboard.width - n - i, l = this.artboard.height - t - s;
|
|
500
|
+
r <= 0 || l <= 0 || (e.save(), e.strokeStyle = "#22c55e", e.lineWidth = 1 / this.zoom, e.setLineDash([6 / this.zoom, 4 / this.zoom]), e.strokeRect(o, a, r, l), e.setLineDash([]), e.restore());
|
|
501
501
|
}
|
|
502
502
|
/**
|
|
503
503
|
* Render an area with its content
|
|
@@ -505,8 +505,8 @@ class N {
|
|
|
505
505
|
renderAreaWithContent(e) {
|
|
506
506
|
const { ctx: t } = this, { location: i, backgroundColor: s } = e, { x: n, y: o, width: a, height: r } = i;
|
|
507
507
|
s && (t.fillStyle = s, t.fillRect(n, o, a, r));
|
|
508
|
-
const
|
|
509
|
-
|
|
508
|
+
const l = this.contents.get(e.id);
|
|
509
|
+
l && (l.type === "text" ? this.renderTextContent(e, l) : l.type === "image" && this.renderImageContent(e, l)), this.showAreaBorders && e.showBorder && (t.save(), t.strokeStyle = e.borderColor || "#3b82f6", t.lineWidth = 1 / this.zoom, t.setLineDash([4 / this.zoom, 4 / this.zoom]), t.strokeRect(n, o, a, r), t.setLineDash([]), t.restore());
|
|
510
510
|
}
|
|
511
511
|
/**
|
|
512
512
|
* Render text content in an area
|
|
@@ -514,22 +514,22 @@ class N {
|
|
|
514
514
|
renderTextContent(e, t) {
|
|
515
515
|
if (!t.text.trim())
|
|
516
516
|
return;
|
|
517
|
-
const { ctx: i } = this, { location: s } = e, { x: n, y: o, width: a, height: r } = s,
|
|
518
|
-
i.save(), i.beginPath(), i.rect(n, o, a, r), i.clip(), i.translate(p,
|
|
519
|
-
let
|
|
517
|
+
const { ctx: i } = this, { location: s } = e, { x: n, y: o, width: a, height: r } = s, l = t.offset || { x: 0, y: 0 }, d = t.scale ?? 1, h = t.rotation ?? 0, p = n + a / 2 + l.x, c = o + r / 2 + l.y;
|
|
518
|
+
i.save(), i.beginPath(), i.rect(n, o, a, r), i.clip(), i.translate(p, c), i.rotate(h * Math.PI / 180), i.scale(d, d), i.font = `${t.size}px ${t.font}, sans-serif`, i.fillStyle = t.color, i.textBaseline = "middle";
|
|
519
|
+
let m;
|
|
520
520
|
switch (t.align) {
|
|
521
521
|
case "left":
|
|
522
|
-
i.textAlign = "left",
|
|
522
|
+
i.textAlign = "left", m = -a / 2 + 10;
|
|
523
523
|
break;
|
|
524
524
|
case "right":
|
|
525
|
-
i.textAlign = "right",
|
|
525
|
+
i.textAlign = "right", m = a / 2 - 10;
|
|
526
526
|
break;
|
|
527
527
|
case "center":
|
|
528
528
|
default:
|
|
529
|
-
i.textAlign = "center",
|
|
529
|
+
i.textAlign = "center", m = 0;
|
|
530
530
|
break;
|
|
531
531
|
}
|
|
532
|
-
i.fillText(t.text,
|
|
532
|
+
i.fillText(t.text, m, 0), i.restore();
|
|
533
533
|
}
|
|
534
534
|
/**
|
|
535
535
|
* Render image content in an area
|
|
@@ -538,13 +538,13 @@ class N {
|
|
|
538
538
|
const i = this.loadedImages.get(e.id);
|
|
539
539
|
if (!i)
|
|
540
540
|
return;
|
|
541
|
-
const { ctx: s } = this, { location: n } = e, { x: o, y: a, width: r, height:
|
|
542
|
-
s.save(), s.beginPath(), s.rect(o, a, r,
|
|
543
|
-
const
|
|
544
|
-
let
|
|
545
|
-
|
|
546
|
-
const b =
|
|
547
|
-
s.translate(
|
|
541
|
+
const { ctx: s } = this, { location: n } = e, { x: o, y: a, width: r, height: l } = n, d = t.offset || { x: 0, y: 0 }, h = t.scale ?? 1, p = t.rotation ?? 0;
|
|
542
|
+
s.save(), s.beginPath(), s.rect(o, a, r, l), s.clip();
|
|
543
|
+
const c = i.naturalWidth / i.naturalHeight, m = r / l;
|
|
544
|
+
let f, v;
|
|
545
|
+
c > m ? (f = r, v = r / c) : (v = l, f = l * c);
|
|
546
|
+
const b = f * h, y = v * h, x = o + r / 2 + d.x, C = a + l / 2 + d.y;
|
|
547
|
+
s.translate(x, C), s.rotate(p * Math.PI / 180), s.drawImage(i, -b / 2, -y / 2, b, y), s.restore();
|
|
548
548
|
}
|
|
549
549
|
/**
|
|
550
550
|
* Draw background image on a specific context (for export)
|
|
@@ -555,8 +555,8 @@ class N {
|
|
|
555
555
|
const { opacity: s } = this.backgroundImage;
|
|
556
556
|
e.save(), e.globalAlpha = s;
|
|
557
557
|
const n = this.bgImageElement.naturalWidth / this.bgImageElement.naturalHeight, o = t / i;
|
|
558
|
-
let a, r,
|
|
559
|
-
n > o ? (r = i, a = i * n,
|
|
558
|
+
let a, r, l, d;
|
|
559
|
+
n > o ? (r = i, a = i * n, l = (t - a) / 2, d = 0) : (a = t, r = t / n, l = 0, d = (i - r) / 2), e.drawImage(this.bgImageElement, l, d, a, r), e.restore();
|
|
560
560
|
}
|
|
561
561
|
/**
|
|
562
562
|
* Draw area content on a specific context (for export)
|
|
@@ -564,38 +564,38 @@ class N {
|
|
|
564
564
|
drawAreaContentOnContext(e, t) {
|
|
565
565
|
const { location: i, backgroundColor: s } = t, { x: n, y: o, width: a, height: r } = i;
|
|
566
566
|
s && (e.fillStyle = s, e.fillRect(n, o, a, r));
|
|
567
|
-
const
|
|
568
|
-
if (
|
|
569
|
-
if (
|
|
570
|
-
if (!
|
|
567
|
+
const l = this.contents.get(t.id);
|
|
568
|
+
if (l) {
|
|
569
|
+
if (l.type === "text") {
|
|
570
|
+
if (!l.text.trim())
|
|
571
571
|
return;
|
|
572
|
-
const
|
|
573
|
-
e.save(), e.beginPath(), e.rect(n, o, a, r), e.clip(), e.translate(
|
|
574
|
-
let
|
|
575
|
-
switch (
|
|
572
|
+
const d = l.offset || { x: 0, y: 0 }, h = l.scale ?? 1, p = l.rotation ?? 0, c = n + a / 2 + d.x, m = o + r / 2 + d.y;
|
|
573
|
+
e.save(), e.beginPath(), e.rect(n, o, a, r), e.clip(), e.translate(c, m), e.rotate(p * Math.PI / 180), e.scale(h, h), e.font = `${l.size}px ${l.font}, sans-serif`, e.fillStyle = l.color, e.textBaseline = "middle";
|
|
574
|
+
let f;
|
|
575
|
+
switch (l.align) {
|
|
576
576
|
case "left":
|
|
577
|
-
e.textAlign = "left",
|
|
577
|
+
e.textAlign = "left", f = -a / 2 + 10;
|
|
578
578
|
break;
|
|
579
579
|
case "right":
|
|
580
|
-
e.textAlign = "right",
|
|
580
|
+
e.textAlign = "right", f = a / 2 - 10;
|
|
581
581
|
break;
|
|
582
582
|
case "center":
|
|
583
583
|
default:
|
|
584
|
-
e.textAlign = "center",
|
|
584
|
+
e.textAlign = "center", f = 0;
|
|
585
585
|
break;
|
|
586
586
|
}
|
|
587
|
-
e.fillText(
|
|
588
|
-
} else if (
|
|
589
|
-
const
|
|
590
|
-
if (!
|
|
587
|
+
e.fillText(l.text, f, 0), e.restore();
|
|
588
|
+
} else if (l.type === "image") {
|
|
589
|
+
const d = this.loadedImages.get(t.id);
|
|
590
|
+
if (!d)
|
|
591
591
|
return;
|
|
592
|
-
const
|
|
592
|
+
const h = l.offset || { x: 0, y: 0 }, p = l.scale ?? 1, c = l.rotation ?? 0;
|
|
593
593
|
e.save(), e.beginPath(), e.rect(n, o, a, r), e.clip();
|
|
594
|
-
const
|
|
594
|
+
const m = d.naturalWidth / d.naturalHeight, f = a / r;
|
|
595
595
|
let v, b;
|
|
596
|
-
|
|
597
|
-
const
|
|
598
|
-
e.translate(
|
|
596
|
+
m > f ? (v = a, b = a / m) : (b = r, v = r * m);
|
|
597
|
+
const y = v * p, x = b * p, C = n + a / 2 + h.x, I = o + r / 2 + h.y;
|
|
598
|
+
e.translate(C, I), e.rotate(c * Math.PI / 180), e.drawImage(d, -y / 2, -x / 2, y, x), e.restore();
|
|
599
599
|
}
|
|
600
600
|
}
|
|
601
601
|
}
|
|
@@ -606,8 +606,8 @@ class N {
|
|
|
606
606
|
this.stop(), this.bgImageElement = null, this.loadedImages.clear(), this.loadingImages.clear(), this.eventListeners.clear();
|
|
607
607
|
}
|
|
608
608
|
}
|
|
609
|
-
function
|
|
610
|
-
const e =
|
|
609
|
+
function T(g) {
|
|
610
|
+
const e = g.textOptions;
|
|
611
611
|
return {
|
|
612
612
|
type: "text",
|
|
613
613
|
text: e?.defaultText || "",
|
|
@@ -617,14 +617,14 @@ function S(f) {
|
|
|
617
617
|
align: e?.defaultAlign || "center"
|
|
618
618
|
};
|
|
619
619
|
}
|
|
620
|
-
function
|
|
621
|
-
return
|
|
620
|
+
function S(g) {
|
|
621
|
+
return g.contentType === "text" || g.contentType === "both";
|
|
622
622
|
}
|
|
623
|
-
function
|
|
624
|
-
return
|
|
623
|
+
function R(g) {
|
|
624
|
+
return g.contentType === "image" || g.contentType === "both";
|
|
625
625
|
}
|
|
626
|
-
const
|
|
627
|
-
class
|
|
626
|
+
const q = 50;
|
|
627
|
+
class H {
|
|
628
628
|
constructor(e) {
|
|
629
629
|
this.history = [], this.historyIndex = -1, this.listeners = /* @__PURE__ */ new Set(), this.isRestoring = !1, this.state = this.cloneState(e), this.history.push({
|
|
630
630
|
state: this.cloneState(e),
|
|
@@ -675,7 +675,7 @@ class F {
|
|
|
675
675
|
state: this.cloneState(e),
|
|
676
676
|
timestamp: Date.now(),
|
|
677
677
|
description: t
|
|
678
|
-
}), this.history.length >
|
|
678
|
+
}), this.history.length > q && this.history.shift(), this.historyIndex = this.history.length - 1;
|
|
679
679
|
}
|
|
680
680
|
restoreFromHistory() {
|
|
681
681
|
const e = this.history[this.historyIndex];
|
|
@@ -705,7 +705,7 @@ class P {
|
|
|
705
705
|
buildInitialContents() {
|
|
706
706
|
const e = /* @__PURE__ */ new Map();
|
|
707
707
|
for (const t of this.areas)
|
|
708
|
-
|
|
708
|
+
S(t) && e.set(t.id, T(t));
|
|
709
709
|
return e;
|
|
710
710
|
}
|
|
711
711
|
getContents() {
|
|
@@ -715,7 +715,7 @@ class P {
|
|
|
715
715
|
return this.contents.get(e);
|
|
716
716
|
}
|
|
717
717
|
setTextContent(e, t) {
|
|
718
|
-
const i = this.contents.get(e), s = this.areas.find((o) => o.id === e), n = s ?
|
|
718
|
+
const i = this.contents.get(e), s = this.areas.find((o) => o.id === e), n = s ? T(s) : {
|
|
719
719
|
type: "text",
|
|
720
720
|
text: "",
|
|
721
721
|
font: "Inter",
|
|
@@ -743,7 +743,7 @@ class P {
|
|
|
743
743
|
}
|
|
744
744
|
clearContent(e) {
|
|
745
745
|
const t = this.areas.find((i) => i.id === e);
|
|
746
|
-
t &&
|
|
746
|
+
t && S(t) ? this.contents.set(e, T(t)) : this.contents.delete(e), this.notify();
|
|
747
747
|
}
|
|
748
748
|
setContentOffset(e, t) {
|
|
749
749
|
const i = this.contents.get(e);
|
|
@@ -788,7 +788,7 @@ class P {
|
|
|
788
788
|
}
|
|
789
789
|
}
|
|
790
790
|
}
|
|
791
|
-
class
|
|
791
|
+
class M {
|
|
792
792
|
constructor() {
|
|
793
793
|
this.listeners = /* @__PURE__ */ new Map();
|
|
794
794
|
}
|
|
@@ -861,7 +861,7 @@ class z {
|
|
|
861
861
|
return this.listeners.get(e)?.size ?? 0;
|
|
862
862
|
}
|
|
863
863
|
}
|
|
864
|
-
class
|
|
864
|
+
class Z extends M {
|
|
865
865
|
constructor() {
|
|
866
866
|
super(...arguments), this.activeTouches = /* @__PURE__ */ new Map(), this.initialPinchDistance = null, this.lastPinchCenter = null, this.isTwoFingerGesture = !1;
|
|
867
867
|
}
|
|
@@ -924,9 +924,9 @@ class O extends z {
|
|
|
924
924
|
};
|
|
925
925
|
}
|
|
926
926
|
}
|
|
927
|
-
class
|
|
927
|
+
class j extends M {
|
|
928
928
|
constructor(e, t, i, s) {
|
|
929
|
-
super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine = t, this.getContents = i, this.getSelectedAreaId = s, this.gestureHandler = new
|
|
929
|
+
super(), this.interactionState = { mode: "idle" }, this.initialPinchZoom = 1, this.canvas = e, this.engine = t, this.getContents = i, this.getSelectedAreaId = s, this.gestureHandler = new Z(), this.boundMouseDown = this.handleMouseDown.bind(this), this.boundMouseMove = this.handleMouseMove.bind(this), this.boundMouseUp = this.handleMouseUp.bind(this), this.boundMouseLeave = this.handleMouseLeave.bind(this), this.boundContextMenu = this.handleContextMenu.bind(this), this.boundDoubleClick = this.handleDoubleClick.bind(this), this.boundTouchStart = this.handleTouchStart.bind(this), this.boundTouchMove = this.handleTouchMove.bind(this), this.boundTouchEnd = this.handleTouchEnd.bind(this), e.addEventListener("mousedown", this.boundMouseDown), e.addEventListener("mousemove", this.boundMouseMove), e.addEventListener("mouseup", this.boundMouseUp), e.addEventListener("mouseleave", this.boundMouseLeave), e.addEventListener("contextmenu", this.boundContextMenu), e.addEventListener("dblclick", this.boundDoubleClick), e.addEventListener("touchstart", this.boundTouchStart, { passive: !1 }), e.addEventListener("touchmove", this.boundTouchMove, { passive: !1 }), e.addEventListener("touchend", this.boundTouchEnd), this.gestureHandler.on("pinch", ({ scale: n }) => {
|
|
930
930
|
const o = Math.max(0.1, Math.min(5, this.initialPinchZoom * n));
|
|
931
931
|
this.emit("zoom", { zoom: o });
|
|
932
932
|
}), this.gestureHandler.on("pan", ({ deltaX: n, deltaY: o }) => {
|
|
@@ -992,13 +992,13 @@ class $ extends z {
|
|
|
992
992
|
if (o) {
|
|
993
993
|
const a = i.get(t), r = this.engine.getContentBounds(t);
|
|
994
994
|
if (o === "rotate" && a?.type === "image" && r) {
|
|
995
|
-
const
|
|
995
|
+
const l = Math.atan2(e.y - r.centerY, e.x - r.centerX) * (180 / Math.PI);
|
|
996
996
|
return this.interactionState = {
|
|
997
997
|
mode: "rotating",
|
|
998
998
|
areaId: t,
|
|
999
999
|
startRotation: a.rotation ?? 0,
|
|
1000
1000
|
centerPoint: { x: r.centerX, y: r.centerY },
|
|
1001
|
-
startAngle:
|
|
1001
|
+
startAngle: l
|
|
1002
1002
|
}, !0;
|
|
1003
1003
|
} else if (o !== "rotate" && a?.type === "image" && r)
|
|
1004
1004
|
return this.interactionState = {
|
|
@@ -1034,8 +1034,8 @@ class $ extends z {
|
|
|
1034
1034
|
y: i.y + o
|
|
1035
1035
|
}, r = this.engine.getAreaLocation(t);
|
|
1036
1036
|
if (r) {
|
|
1037
|
-
const
|
|
1038
|
-
a.x = Math.max(-
|
|
1037
|
+
const l = r.width / 2 - 10, d = r.height / 2 - 10;
|
|
1038
|
+
a.x = Math.max(-l, Math.min(l, a.x)), a.y = Math.max(-d, Math.min(d, a.y));
|
|
1039
1039
|
}
|
|
1040
1040
|
this.emit("content:drag", { areaId: t, offset: a });
|
|
1041
1041
|
return;
|
|
@@ -1047,8 +1047,8 @@ class $ extends z {
|
|
|
1047
1047
|
Math.pow(e.x - n.x, 2) + Math.pow(e.y - n.y, 2)
|
|
1048
1048
|
);
|
|
1049
1049
|
if (o > 0) {
|
|
1050
|
-
const r = a / o,
|
|
1051
|
-
this.emit("content:scale", { areaId: t, scale:
|
|
1050
|
+
const r = a / o, l = Math.max(0.1, Math.min(5, i * r));
|
|
1051
|
+
this.emit("content:scale", { areaId: t, scale: l });
|
|
1052
1052
|
}
|
|
1053
1053
|
return;
|
|
1054
1054
|
}
|
|
@@ -1057,9 +1057,9 @@ class $ extends z {
|
|
|
1057
1057
|
let a = i + (o - n);
|
|
1058
1058
|
a = a % 360, a < 0 && (a += 360);
|
|
1059
1059
|
const r = [0, 90, 180, 270, 360];
|
|
1060
|
-
for (const
|
|
1061
|
-
if (Math.abs(a -
|
|
1062
|
-
a =
|
|
1060
|
+
for (const l of r)
|
|
1061
|
+
if (Math.abs(a - l) < 5) {
|
|
1062
|
+
a = l === 360 ? 0 : l;
|
|
1063
1063
|
break;
|
|
1064
1064
|
}
|
|
1065
1065
|
this.emit("content:rotate", { areaId: t, rotation: a });
|
|
@@ -1087,7 +1087,7 @@ class $ extends z {
|
|
|
1087
1087
|
this.canvas.removeEventListener("mousedown", this.boundMouseDown), this.canvas.removeEventListener("mousemove", this.boundMouseMove), this.canvas.removeEventListener("mouseup", this.boundMouseUp), this.canvas.removeEventListener("mouseleave", this.boundMouseLeave), this.canvas.removeEventListener("contextmenu", this.boundContextMenu), this.canvas.removeEventListener("dblclick", this.boundDoubleClick), this.canvas.removeEventListener("touchstart", this.boundTouchStart), this.canvas.removeEventListener("touchmove", this.boundTouchMove), this.canvas.removeEventListener("touchend", this.boundTouchEnd), this.gestureHandler.reset(), this.clear();
|
|
1088
1088
|
}
|
|
1089
1089
|
}
|
|
1090
|
-
class
|
|
1090
|
+
class Y {
|
|
1091
1091
|
constructor(e = "/api") {
|
|
1092
1092
|
this.baseUrl = e;
|
|
1093
1093
|
}
|
|
@@ -1119,30 +1119,32 @@ class B {
|
|
|
1119
1119
|
throw t instanceof Error && t.message.includes("Failed to fetch") ? new Error(`Network error loading product: ${e}`) : t;
|
|
1120
1120
|
}
|
|
1121
1121
|
}
|
|
1122
|
-
async finalizeDesign(e, t) {
|
|
1122
|
+
async finalizeDesign(e, t, i) {
|
|
1123
1123
|
try {
|
|
1124
|
-
const
|
|
1124
|
+
const s = await fetch(`${this.baseUrl}/finalize`, {
|
|
1125
1125
|
method: "POST",
|
|
1126
1126
|
headers: { "Content-Type": "application/json" },
|
|
1127
1127
|
body: JSON.stringify({
|
|
1128
1128
|
shopId: e,
|
|
1129
1129
|
templateId: t.templateId,
|
|
1130
|
-
designJson: t
|
|
1130
|
+
designJson: t,
|
|
1131
|
+
...i?.cartContext && { cartContext: i.cartContext },
|
|
1132
|
+
...i?.customerEmail && { customerEmail: i.customerEmail }
|
|
1131
1133
|
})
|
|
1132
1134
|
});
|
|
1133
|
-
if (!
|
|
1134
|
-
let
|
|
1135
|
+
if (!s.ok) {
|
|
1136
|
+
let n = `Failed to finalize design (${s.status} ${s.statusText})
|
|
1135
1137
|
|
|
1136
1138
|
`;
|
|
1137
|
-
if (
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
} else
|
|
1141
|
-
throw new Error(
|
|
1139
|
+
if (s.status === 400) {
|
|
1140
|
+
const o = await s.json().catch(() => ({}));
|
|
1141
|
+
n += o.error || "Invalid design data.";
|
|
1142
|
+
} else s.status === 429 ? n += "Rate limit exceeded." : s.status >= 500 && (n += "Server error.");
|
|
1143
|
+
throw new Error(n);
|
|
1142
1144
|
}
|
|
1143
|
-
return
|
|
1144
|
-
} catch (
|
|
1145
|
-
throw
|
|
1145
|
+
return s.json();
|
|
1146
|
+
} catch (s) {
|
|
1147
|
+
throw s instanceof Error && s.message.includes("Failed to fetch") ? new Error("Network error finalizing design") : s;
|
|
1146
1148
|
}
|
|
1147
1149
|
}
|
|
1148
1150
|
async finalizeMultiView(e) {
|
|
@@ -1252,52 +1254,52 @@ class B {
|
|
|
1252
1254
|
}
|
|
1253
1255
|
}
|
|
1254
1256
|
}
|
|
1255
|
-
function
|
|
1256
|
-
const i = document.createElement(
|
|
1257
|
+
function u(g, e, ...t) {
|
|
1258
|
+
const i = document.createElement(g);
|
|
1257
1259
|
return e && Object.entries(e).forEach(([s, n]) => {
|
|
1258
1260
|
s === "class" ? i.className = n : s in i ? i[s] = n : i.setAttribute(s, String(n));
|
|
1259
1261
|
}), t.forEach((s) => {
|
|
1260
1262
|
typeof s == "string" ? i.appendChild(document.createTextNode(s)) : i.appendChild(s);
|
|
1261
1263
|
}), i;
|
|
1262
1264
|
}
|
|
1263
|
-
function
|
|
1265
|
+
function W(g, e) {
|
|
1264
1266
|
let t = null, i = null;
|
|
1265
1267
|
const s = (...n) => {
|
|
1266
1268
|
i = n, t && clearTimeout(t), t = setTimeout(() => {
|
|
1267
|
-
i &&
|
|
1269
|
+
i && g(...i), t = null, i = null;
|
|
1268
1270
|
}, e);
|
|
1269
1271
|
};
|
|
1270
1272
|
return s.flush = () => {
|
|
1271
|
-
t && (clearTimeout(t), t = null), i && (
|
|
1273
|
+
t && (clearTimeout(t), t = null), i && (g(...i), i = null);
|
|
1272
1274
|
}, s.cancel = () => {
|
|
1273
1275
|
t && (clearTimeout(t), t = null), i = null;
|
|
1274
1276
|
}, s;
|
|
1275
1277
|
}
|
|
1276
|
-
class
|
|
1278
|
+
class _ extends M {
|
|
1277
1279
|
constructor() {
|
|
1278
|
-
super(), this.saveEnabled = !0, this.percentLabel =
|
|
1279
|
-
const e =
|
|
1280
|
+
super(), this.saveEnabled = !0, this.percentLabel = u("span", { class: "zoom-percent" }, "100%");
|
|
1281
|
+
const e = u("button", {
|
|
1280
1282
|
class: "zoom-btn",
|
|
1281
1283
|
title: "Zoom out"
|
|
1282
1284
|
}, "−");
|
|
1283
1285
|
e.addEventListener("click", () => this.emit("zoom-out", void 0));
|
|
1284
|
-
const t =
|
|
1286
|
+
const t = u("button", {
|
|
1285
1287
|
class: "zoom-btn",
|
|
1286
1288
|
title: "Fit to view"
|
|
1287
1289
|
}, "Fit");
|
|
1288
1290
|
t.addEventListener("click", () => this.emit("zoom-fit", void 0));
|
|
1289
|
-
const i =
|
|
1291
|
+
const i = u("button", {
|
|
1290
1292
|
class: "zoom-btn",
|
|
1291
1293
|
title: "Zoom in"
|
|
1292
1294
|
}, "+");
|
|
1293
1295
|
i.addEventListener("click", () => this.emit("zoom-in", void 0)), this.viewSelect = document.createElement("select"), this.viewSelect.className = "view-select", this.viewSelect.title = "Switch view", this.viewSelect.style.display = "none", this.viewSelect.addEventListener("change", () => {
|
|
1294
1296
|
this.emit("view-change", { viewName: this.viewSelect.value });
|
|
1295
|
-
}), this.element =
|
|
1296
|
-
const s =
|
|
1297
|
-
this.element.appendChild(s), this.saveBtn =
|
|
1297
|
+
}), this.element = u("div", { class: "zoom-toolbar" }), this.element.appendChild(e), this.element.appendChild(t), this.element.appendChild(i), this.element.appendChild(this.percentLabel), this.element.appendChild(this.viewSelect);
|
|
1298
|
+
const s = u("div", { class: "zoom-toolbar-spacer" });
|
|
1299
|
+
this.element.appendChild(s), this.saveBtn = u("button", {
|
|
1298
1300
|
class: "zoom-btn zoom-save-btn",
|
|
1299
1301
|
title: "Save customization"
|
|
1300
|
-
}, "✓"), this.saveBtn.style.display = "none", this.saveBtn.addEventListener("click", () => this.emit("save", void 0)), this.element.appendChild(this.saveBtn), this.closeBtn =
|
|
1302
|
+
}, "✓"), this.saveBtn.style.display = "none", this.saveBtn.addEventListener("click", () => this.emit("save", void 0)), this.element.appendChild(this.saveBtn), this.closeBtn = u("button", {
|
|
1301
1303
|
class: "zoom-btn zoom-close-btn",
|
|
1302
1304
|
title: "Close editor"
|
|
1303
1305
|
}, "✕"), this.closeBtn.addEventListener("click", () => this.emit("close", void 0)), this.element.appendChild(this.closeBtn);
|
|
@@ -1332,56 +1334,67 @@ class H extends z {
|
|
|
1332
1334
|
return this.element;
|
|
1333
1335
|
}
|
|
1334
1336
|
}
|
|
1335
|
-
const
|
|
1337
|
+
const J = /[\x00-\x08\x0B\x0C\x0E-\x1F]/g;
|
|
1338
|
+
function X(g) {
|
|
1339
|
+
return typeof g != "string" ? "" : g.replace(J, "");
|
|
1340
|
+
}
|
|
1341
|
+
const G = /^#[0-9a-fA-F]{6}$/;
|
|
1342
|
+
function K(g) {
|
|
1343
|
+
return typeof g == "string" && G.test(g);
|
|
1344
|
+
}
|
|
1345
|
+
function Q(g) {
|
|
1346
|
+
return typeof g != "string" ? "" : g.replace(/[\\/\0]/g, "");
|
|
1347
|
+
}
|
|
1348
|
+
const ee = [
|
|
1336
1349
|
{ label: "Inter", value: "Inter" },
|
|
1337
1350
|
{ label: "Arial", value: "Arial" },
|
|
1338
1351
|
{ label: "Georgia", value: "Georgia" },
|
|
1339
1352
|
{ label: "Times New Roman", value: "Times New Roman" },
|
|
1340
1353
|
{ label: "Courier New", value: "Courier New" }
|
|
1341
|
-
],
|
|
1354
|
+
], te = [
|
|
1342
1355
|
{ label: "L", value: "left" },
|
|
1343
1356
|
{ label: "C", value: "center" },
|
|
1344
1357
|
{ label: "R", value: "right" }
|
|
1345
|
-
],
|
|
1346
|
-
class
|
|
1358
|
+
], L = ["image/png", "image/jpeg", "image/webp", "image/svg+xml"], ie = 15 * 1024 * 1024;
|
|
1359
|
+
class U extends M {
|
|
1347
1360
|
constructor(e) {
|
|
1348
1361
|
super(), this.mode = "text", this.isSelected = !1, this.isUploading = !1, this.textInputEl = null, this.fontSelectEl = null, this.sizeInputEl = null, this.colorInputEl = null, this.alignGroupEl = null, this.area = e;
|
|
1349
|
-
const t =
|
|
1350
|
-
this.mode = i && !t ? "image" : "text", this.element =
|
|
1362
|
+
const t = S(e), i = R(e);
|
|
1363
|
+
this.mode = i && !t ? "image" : "text", this.element = u("div", { class: "area-card" }), this.element.addEventListener("click", (r) => {
|
|
1351
1364
|
r.target.tagName === "INPUT" || r.target.tagName === "SELECT" || r.target.tagName === "BUTTON" || this.emit("select", { areaId: e.id });
|
|
1352
1365
|
});
|
|
1353
|
-
const s =
|
|
1366
|
+
const s = u("div", { class: "area-card-header" }), n = u("div", { class: "area-card-name-row" }), o = u("span", { class: "area-card-name" }, e.name);
|
|
1354
1367
|
if (n.appendChild(o), e.required) {
|
|
1355
|
-
const r =
|
|
1368
|
+
const r = u("span", { class: "area-card-badge" }, "Required");
|
|
1356
1369
|
n.appendChild(r);
|
|
1357
1370
|
}
|
|
1358
1371
|
s.appendChild(n);
|
|
1359
|
-
const a =
|
|
1372
|
+
const a = u("button", {
|
|
1360
1373
|
class: "area-card-reset-btn",
|
|
1361
1374
|
title: "Reset content"
|
|
1362
1375
|
}, "Reset");
|
|
1363
1376
|
if (a.addEventListener("click", (r) => {
|
|
1364
1377
|
r.stopPropagation(), this.emit("clear", { areaId: e.id });
|
|
1365
1378
|
}), s.appendChild(a), this.element.appendChild(s), t && i) {
|
|
1366
|
-
const r =
|
|
1379
|
+
const r = u("div", { class: "area-card-mode-switcher" }), l = u("button", {
|
|
1367
1380
|
class: "mode-btn mode-btn-active"
|
|
1368
1381
|
}, "Text");
|
|
1369
|
-
|
|
1370
|
-
const
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
}),
|
|
1374
|
-
|
|
1375
|
-
}), r.appendChild(
|
|
1382
|
+
l.dataset.mode = "text";
|
|
1383
|
+
const d = u("button", { class: "mode-btn" }, "Image");
|
|
1384
|
+
d.dataset.mode = "image", l.addEventListener("click", (h) => {
|
|
1385
|
+
h.stopPropagation(), this.setMode("text"), l.classList.add("mode-btn-active"), d.classList.remove("mode-btn-active"), this.currentContent?.type === "image" && this.emit("clear", { areaId: e.id });
|
|
1386
|
+
}), d.addEventListener("click", (h) => {
|
|
1387
|
+
h.stopPropagation(), this.setMode("image"), d.classList.add("mode-btn-active"), l.classList.remove("mode-btn-active"), this.currentContent?.type === "text" && this.emit("clear", { areaId: e.id });
|
|
1388
|
+
}), r.appendChild(l), r.appendChild(d), this.element.appendChild(r);
|
|
1376
1389
|
}
|
|
1377
|
-
this.contentContainer =
|
|
1390
|
+
this.contentContainer = u("div", { class: "area-card-content" }), this.element.appendChild(this.contentContainer), this.transformContainer = u("div", { class: "area-card-transforms" }), this.element.appendChild(this.transformContainer), this.renderContent();
|
|
1378
1391
|
}
|
|
1379
1392
|
setMode(e) {
|
|
1380
1393
|
this.mode = e, this.renderContent();
|
|
1381
1394
|
}
|
|
1382
1395
|
setContent(e) {
|
|
1383
1396
|
const t = this.currentContent?.type;
|
|
1384
|
-
this.currentContent = e, e?.type === "image" ? this.mode = "image" : t === "image" && (this.mode =
|
|
1397
|
+
this.currentContent = e, e?.type === "image" ? this.mode = "image" : t === "image" && (this.mode = S(this.area) ? "text" : "image"), this.element.querySelectorAll(".mode-btn").forEach((i) => {
|
|
1385
1398
|
const s = i;
|
|
1386
1399
|
s.classList.toggle("mode-btn-active", s.dataset.mode === this.mode);
|
|
1387
1400
|
}), e?.type !== t || e?.type === "image" || e === void 0 ? this.renderContent() : e?.type === "text" && this.updateTextValues(e), this.renderTransforms();
|
|
@@ -1397,27 +1410,28 @@ class L extends z {
|
|
|
1397
1410
|
}
|
|
1398
1411
|
renderContent() {
|
|
1399
1412
|
this.contentContainer.innerHTML = "", this.textInputEl = null, this.fontSelectEl = null, this.sizeInputEl = null, this.colorInputEl = null, this.alignGroupEl = null;
|
|
1400
|
-
const e =
|
|
1413
|
+
const e = S(this.area), t = R(this.area), i = !t || e && t && this.mode === "text", s = !e || e && t && this.mode === "image";
|
|
1401
1414
|
e && i && this.renderTextControls(), t && s && this.renderImageControls(), this.applyUploadSpinner();
|
|
1402
1415
|
}
|
|
1403
1416
|
renderTextControls() {
|
|
1404
|
-
const e = this.currentContent, t = e?.type === "text" ? e :
|
|
1417
|
+
const e = this.currentContent, t = e?.type === "text" ? e : T(this.area), i = u("input", {
|
|
1405
1418
|
class: "area-input-text",
|
|
1406
1419
|
type: "text",
|
|
1407
1420
|
placeholder: this.area.placeholder || "Enter text..."
|
|
1408
1421
|
});
|
|
1409
1422
|
i.value = t.text, this.area.textOptions?.maxLength && (i.maxLength = this.area.textOptions.maxLength), i.addEventListener("input", () => {
|
|
1410
|
-
|
|
1423
|
+
const d = X(i.value);
|
|
1424
|
+
d !== i.value && (i.value = d), this.emit("text:change", { areaId: this.area.id, updates: { text: d } });
|
|
1411
1425
|
}), this.contentContainer.appendChild(i), this.textInputEl = i;
|
|
1412
|
-
const s =
|
|
1413
|
-
for (const
|
|
1414
|
-
const
|
|
1415
|
-
|
|
1426
|
+
const s = u("div", { class: "area-input-row" }), n = u("select", { class: "area-input-font" });
|
|
1427
|
+
for (const d of ee) {
|
|
1428
|
+
const h = u("option");
|
|
1429
|
+
h.value = d.value, h.textContent = d.label, d.value === t.font && (h.selected = !0), n.appendChild(h);
|
|
1416
1430
|
}
|
|
1417
1431
|
n.addEventListener("change", () => {
|
|
1418
1432
|
this.emit("text:change", { areaId: this.area.id, updates: { font: n.value } });
|
|
1419
1433
|
}), s.appendChild(n), this.fontSelectEl = n;
|
|
1420
|
-
const o =
|
|
1434
|
+
const o = u("input", {
|
|
1421
1435
|
class: "area-input-size",
|
|
1422
1436
|
type: "number"
|
|
1423
1437
|
});
|
|
@@ -1427,49 +1441,49 @@ class L extends z {
|
|
|
1427
1441
|
updates: { size: parseInt(o.value, 10) || 24 }
|
|
1428
1442
|
});
|
|
1429
1443
|
}), s.appendChild(o), this.sizeInputEl = o, this.contentContainer.appendChild(s);
|
|
1430
|
-
const a =
|
|
1431
|
-
for (const
|
|
1432
|
-
const
|
|
1433
|
-
class: `align-btn${t.align ===
|
|
1434
|
-
title:
|
|
1435
|
-
},
|
|
1436
|
-
|
|
1437
|
-
p.stopPropagation(), this.emit("text:change", { areaId: this.area.id, updates: { align:
|
|
1438
|
-
}), r.appendChild(
|
|
1444
|
+
const a = u("div", { class: "area-input-row" }), r = u("div", { class: "area-input-align" });
|
|
1445
|
+
for (const d of te) {
|
|
1446
|
+
const h = u("button", {
|
|
1447
|
+
class: `align-btn${t.align === d.value ? " align-btn-active" : ""}`,
|
|
1448
|
+
title: d.label === "L" ? "Left" : d.label === "C" ? "Center" : "Right"
|
|
1449
|
+
}, d.label);
|
|
1450
|
+
h.addEventListener("click", (p) => {
|
|
1451
|
+
p.stopPropagation(), this.emit("text:change", { areaId: this.area.id, updates: { align: d.value } }), r.querySelectorAll(".align-btn").forEach((c) => c.classList.remove("align-btn-active")), h.classList.add("align-btn-active");
|
|
1452
|
+
}), r.appendChild(h);
|
|
1439
1453
|
}
|
|
1440
1454
|
a.appendChild(r), this.alignGroupEl = r;
|
|
1441
|
-
const
|
|
1455
|
+
const l = u("input", {
|
|
1442
1456
|
class: "area-input-color",
|
|
1443
1457
|
type: "color"
|
|
1444
1458
|
});
|
|
1445
|
-
|
|
1446
|
-
this.emit("text:change", { areaId: this.area.id, updates: { color:
|
|
1447
|
-
}), a.appendChild(
|
|
1459
|
+
l.value = t.color, l.addEventListener("input", () => {
|
|
1460
|
+
K(l.value) && this.emit("text:change", { areaId: this.area.id, updates: { color: l.value } });
|
|
1461
|
+
}), a.appendChild(l), this.colorInputEl = l, this.contentContainer.appendChild(a);
|
|
1448
1462
|
}
|
|
1449
1463
|
renderImageControls() {
|
|
1450
1464
|
const e = this.currentContent;
|
|
1451
1465
|
if (e?.type === "image") {
|
|
1452
|
-
const t =
|
|
1453
|
-
i.src = e.dataUrl, i.alt = e.filename
|
|
1454
|
-
const s =
|
|
1455
|
-
s.appendChild(
|
|
1456
|
-
const n =
|
|
1466
|
+
const t = u("div", { class: "area-image-preview" }), i = u("img", { class: "area-image-thumb" });
|
|
1467
|
+
i.crossOrigin = "anonymous", i.src = e.dataUrl, i.alt = e.filename ? Q(e.filename) : "Uploaded image", t.appendChild(i);
|
|
1468
|
+
const s = u("div", { class: "area-image-info" });
|
|
1469
|
+
s.appendChild(u("span", { class: "area-image-name" }, e.filename || "Image"));
|
|
1470
|
+
const n = u("button", { class: "area-image-remove-btn" }, "Remove");
|
|
1457
1471
|
n.addEventListener("click", (o) => {
|
|
1458
1472
|
o.stopPropagation(), this.emit("clear", { areaId: this.area.id });
|
|
1459
1473
|
}), s.appendChild(n), t.appendChild(s), this.contentContainer.appendChild(t);
|
|
1460
1474
|
} else {
|
|
1461
|
-
const t =
|
|
1462
|
-
i.accept =
|
|
1463
|
-
const s =
|
|
1475
|
+
const t = u("button", { class: "area-upload-btn" }, "Upload Image"), i = u("input", { type: "file" });
|
|
1476
|
+
i.accept = L.join(","), i.style.display = "none";
|
|
1477
|
+
const s = u("div", { class: "area-validation-error" });
|
|
1464
1478
|
s.style.display = "none", i.addEventListener("change", () => {
|
|
1465
1479
|
const n = i.files?.[0];
|
|
1466
1480
|
if (!n) return;
|
|
1467
|
-
if (!
|
|
1481
|
+
if (!L.includes(n.type)) {
|
|
1468
1482
|
const a = "Only PNG, JPEG, WebP, and SVG images are accepted";
|
|
1469
1483
|
s.textContent = a, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: a }), i.value = "";
|
|
1470
1484
|
return;
|
|
1471
1485
|
}
|
|
1472
|
-
if (n.size >
|
|
1486
|
+
if (n.size > ie) {
|
|
1473
1487
|
const a = "File must be smaller than 15MB";
|
|
1474
1488
|
s.textContent = a, s.style.display = "block", this.emit("validation:error", { areaId: this.area.id, message: a }), i.value = "";
|
|
1475
1489
|
return;
|
|
@@ -1491,44 +1505,44 @@ class L extends z {
|
|
|
1491
1505
|
if (!this.isSelected || !e) return;
|
|
1492
1506
|
const t = e.type === "text" ? this.area.textOptions?.allowPositioning : this.area.imageOptions?.allowPositioning, i = e.type === "image" && this.area.imageOptions?.allowScaling, s = e.type === "image" && this.area.imageOptions?.allowRotation;
|
|
1493
1507
|
if (!t && !i && !s) return;
|
|
1494
|
-
const n =
|
|
1508
|
+
const n = u("div", { class: "area-card-divider" });
|
|
1495
1509
|
this.transformContainer.appendChild(n);
|
|
1496
|
-
const o =
|
|
1497
|
-
o.appendChild(
|
|
1498
|
-
const a =
|
|
1499
|
-
a.addEventListener("click", (
|
|
1500
|
-
|
|
1510
|
+
const o = u("div", { class: "transform-header" });
|
|
1511
|
+
o.appendChild(u("span", { class: "transform-title" }, "Transform"));
|
|
1512
|
+
const a = u("button", { class: "transform-reset-btn" }, "Reset");
|
|
1513
|
+
a.addEventListener("click", (l) => {
|
|
1514
|
+
l.stopPropagation(), t && this.emit("offset:change", { areaId: this.area.id, offset: { x: 0, y: 0 } }), i && this.emit("scale:change", { areaId: this.area.id, scale: 1 }), s && this.emit("rotation:change", { areaId: this.area.id, rotation: 0 });
|
|
1501
1515
|
}), o.appendChild(a), this.transformContainer.appendChild(o);
|
|
1502
1516
|
const r = e.offset ?? { x: 0, y: 0 };
|
|
1503
1517
|
if (t) {
|
|
1504
|
-
const
|
|
1505
|
-
|
|
1518
|
+
const l = u("div", { class: "area-input-row" }), d = u("label", { class: "transform-label" }, "X"), h = u("input", { class: "transform-input", type: "number" });
|
|
1519
|
+
h.value = String(Math.round(r.x)), h.addEventListener("change", () => {
|
|
1506
1520
|
this.emit("offset:change", {
|
|
1507
1521
|
areaId: this.area.id,
|
|
1508
|
-
offset: { ...r, x: parseInt(
|
|
1522
|
+
offset: { ...r, x: parseInt(h.value, 10) || 0 }
|
|
1509
1523
|
});
|
|
1510
1524
|
});
|
|
1511
|
-
const p =
|
|
1512
|
-
|
|
1525
|
+
const p = u("label", { class: "transform-label" }, "Y"), c = u("input", { class: "transform-input", type: "number" });
|
|
1526
|
+
c.value = String(Math.round(r.y)), c.addEventListener("change", () => {
|
|
1513
1527
|
this.emit("offset:change", {
|
|
1514
1528
|
areaId: this.area.id,
|
|
1515
|
-
offset: { ...r, y: parseInt(
|
|
1529
|
+
offset: { ...r, y: parseInt(c.value, 10) || 0 }
|
|
1516
1530
|
});
|
|
1517
|
-
}),
|
|
1531
|
+
}), l.appendChild(d), l.appendChild(h), l.appendChild(p), l.appendChild(c), this.transformContainer.appendChild(l);
|
|
1518
1532
|
}
|
|
1519
1533
|
if (i && e.type === "image") {
|
|
1520
|
-
const
|
|
1521
|
-
p.min = "10", p.max = "500", p.step = "5", p.value = String(Math.round(
|
|
1522
|
-
const
|
|
1523
|
-
|
|
1524
|
-
}),
|
|
1534
|
+
const l = e.scale ?? 1, d = u("div", { class: "area-input-row transform-slider-row" }), h = u("label", { class: "transform-label" }, `Scale: ${Math.round(l * 100)}%`), p = u("input", { class: "transform-slider", type: "range" });
|
|
1535
|
+
p.min = "10", p.max = "500", p.step = "5", p.value = String(Math.round(l * 100)), p.addEventListener("input", () => {
|
|
1536
|
+
const c = parseInt(p.value, 10) / 100;
|
|
1537
|
+
h.textContent = `Scale: ${Math.round(c * 100)}%`, this.emit("scale:change", { areaId: this.area.id, scale: c });
|
|
1538
|
+
}), d.appendChild(h), d.appendChild(p), this.transformContainer.appendChild(d);
|
|
1525
1539
|
}
|
|
1526
1540
|
if (s && e.type === "image") {
|
|
1527
|
-
const
|
|
1528
|
-
p.min = "0", p.max = "360", p.step = "1", p.value = String(Math.round(
|
|
1529
|
-
const
|
|
1530
|
-
|
|
1531
|
-
}),
|
|
1541
|
+
const l = e.rotation ?? 0, d = u("div", { class: "area-input-row transform-slider-row" }), h = u("label", { class: "transform-label" }, `Rotation: ${Math.round(l)}°`), p = u("input", { class: "transform-slider", type: "range" });
|
|
1542
|
+
p.min = "0", p.max = "360", p.step = "1", p.value = String(Math.round(l)), p.addEventListener("input", () => {
|
|
1543
|
+
const c = parseInt(p.value, 10);
|
|
1544
|
+
h.textContent = `Rotation: ${c}°`, this.emit("rotation:change", { areaId: this.area.id, rotation: c });
|
|
1545
|
+
}), d.appendChild(h), d.appendChild(p), this.transformContainer.appendChild(d);
|
|
1532
1546
|
}
|
|
1533
1547
|
}
|
|
1534
1548
|
setUploading(e) {
|
|
@@ -1539,43 +1553,43 @@ class L extends z {
|
|
|
1539
1553
|
if (!e) return;
|
|
1540
1554
|
const t = e.querySelector(".area-upload-spinner-overlay");
|
|
1541
1555
|
if (this.isUploading && !t) {
|
|
1542
|
-
const i =
|
|
1543
|
-
i.appendChild(
|
|
1556
|
+
const i = u("div", { class: "area-upload-spinner-overlay" });
|
|
1557
|
+
i.appendChild(u("div", { class: "area-upload-spinner" })), e.appendChild(i);
|
|
1544
1558
|
} else !this.isUploading && t && t.remove();
|
|
1545
1559
|
}
|
|
1546
1560
|
getElement() {
|
|
1547
1561
|
return this.element;
|
|
1548
1562
|
}
|
|
1549
1563
|
}
|
|
1550
|
-
class
|
|
1564
|
+
class se extends M {
|
|
1551
1565
|
constructor(e) {
|
|
1552
1566
|
super(), this.cards = /* @__PURE__ */ new Map(), this.isMobile = !1, this.boundEscapeHandler = (s) => {
|
|
1553
1567
|
s.key === "Escape" && this.emit("dismiss", void 0);
|
|
1554
|
-
}, this.backdrop =
|
|
1568
|
+
}, this.backdrop = u("div", { class: "area-panel-backdrop" }), this.backdrop.addEventListener("click", () => {
|
|
1555
1569
|
this.emit("dismiss", void 0);
|
|
1556
|
-
}), this.element =
|
|
1557
|
-
const t =
|
|
1558
|
-
t.appendChild(
|
|
1559
|
-
const i =
|
|
1570
|
+
}), this.element = u("div", { class: "area-panel" });
|
|
1571
|
+
const t = u("div", { class: "area-panel-header" });
|
|
1572
|
+
t.appendChild(u("span", { class: "area-panel-title" }, "Customize"));
|
|
1573
|
+
const i = u("button", { class: "area-panel-close-btn", title: "Close" }, "×");
|
|
1560
1574
|
if (i.addEventListener("click", () => {
|
|
1561
1575
|
this.emit("dismiss", void 0);
|
|
1562
|
-
}), t.appendChild(i), this.element.appendChild(t), this.panelContent =
|
|
1563
|
-
const s =
|
|
1576
|
+
}), t.appendChild(i), this.element.appendChild(t), this.panelContent = u("div", { class: "area-panel-content" }), e.length === 0) {
|
|
1577
|
+
const s = u("p", { class: "area-panel-empty" }, "No customization areas defined.");
|
|
1564
1578
|
this.panelContent.appendChild(s);
|
|
1565
1579
|
} else
|
|
1566
1580
|
for (const s of e) {
|
|
1567
|
-
const n = new
|
|
1581
|
+
const n = new U(s);
|
|
1568
1582
|
this.cards.set(s.id, n), n.on("text:change", (o) => this.emit("text:change", o)), n.on("image:change", (o) => this.emit("image:change", o)), n.on("clear", (o) => this.emit("clear", o)), n.on("select", (o) => this.emit("select", o)), n.on("offset:change", (o) => this.emit("offset:change", o)), n.on("scale:change", (o) => this.emit("scale:change", o)), n.on("rotation:change", (o) => this.emit("rotation:change", o)), n.on("validation:error", (o) => this.emit("validation:error", o)), this.panelContent.appendChild(n.getElement());
|
|
1569
1583
|
}
|
|
1570
1584
|
this.element.appendChild(this.panelContent);
|
|
1571
1585
|
}
|
|
1572
1586
|
setAreas(e) {
|
|
1573
1587
|
if (this.cards.clear(), this.panelContent.innerHTML = "", e.length === 0) {
|
|
1574
|
-
const t =
|
|
1588
|
+
const t = u("p", { class: "area-panel-empty" }, "No customization areas defined.");
|
|
1575
1589
|
this.panelContent.appendChild(t);
|
|
1576
1590
|
} else
|
|
1577
1591
|
for (const t of e) {
|
|
1578
|
-
const i = new
|
|
1592
|
+
const i = new U(t);
|
|
1579
1593
|
this.cards.set(t.id, i), i.on("text:change", (s) => this.emit("text:change", s)), i.on("image:change", (s) => this.emit("image:change", s)), i.on("clear", (s) => this.emit("clear", s)), i.on("select", (s) => this.emit("select", s)), i.on("offset:change", (s) => this.emit("offset:change", s)), i.on("scale:change", (s) => this.emit("scale:change", s)), i.on("rotation:change", (s) => this.emit("rotation:change", s)), i.on("validation:error", (s) => this.emit("validation:error", s)), this.panelContent.appendChild(i.getElement());
|
|
1580
1594
|
}
|
|
1581
1595
|
}
|
|
@@ -1604,7 +1618,7 @@ class J extends z {
|
|
|
1604
1618
|
return this.backdrop;
|
|
1605
1619
|
}
|
|
1606
1620
|
}
|
|
1607
|
-
class
|
|
1621
|
+
class ne {
|
|
1608
1622
|
constructor(e = window) {
|
|
1609
1623
|
this.target = e, this.commands = [], this.isEnabled = !0, this.handleKeyDown = (t) => {
|
|
1610
1624
|
if (!this.isEnabled) return;
|
|
@@ -1658,31 +1672,31 @@ class W {
|
|
|
1658
1672
|
this.target.removeEventListener("keydown", this.handleKeyDown), this.commands = [];
|
|
1659
1673
|
}
|
|
1660
1674
|
}
|
|
1661
|
-
function
|
|
1675
|
+
function ae() {
|
|
1662
1676
|
return /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
|
1663
1677
|
}
|
|
1664
|
-
function
|
|
1665
|
-
return
|
|
1678
|
+
function oe() {
|
|
1679
|
+
return ae() ? "meta" : "ctrl";
|
|
1666
1680
|
}
|
|
1667
|
-
function
|
|
1668
|
-
const s =
|
|
1681
|
+
function re(g, e, t, i) {
|
|
1682
|
+
const s = g / t, n = e / i;
|
|
1669
1683
|
return Math.min(s, n);
|
|
1670
1684
|
}
|
|
1671
|
-
function
|
|
1672
|
-
const n =
|
|
1685
|
+
function le(g, e, t = 8, i = 10, s = 300) {
|
|
1686
|
+
const n = re(g, e, t, i), o = n >= s;
|
|
1673
1687
|
return {
|
|
1674
1688
|
actualDPI: Math.round(n),
|
|
1675
1689
|
targetDPI: s,
|
|
1676
|
-
width:
|
|
1690
|
+
width: g,
|
|
1677
1691
|
height: e,
|
|
1678
1692
|
passed: o,
|
|
1679
1693
|
warning: o ? void 0 : `Image resolution is ${Math.round(n)} DPI at 100% scale. For best print quality, use images with at least ${s} DPI. This may result in pixelated or blurry prints.`
|
|
1680
1694
|
};
|
|
1681
1695
|
}
|
|
1682
|
-
const k = ':host{--editor-bg: #f3f4f6;--editor-surface: #ffffff;--editor-border: #e5e7eb;--editor-text: #111827;--editor-text-muted: #6b7280;--editor-primary: #3b82f6;--editor-primary-hover: #2563eb;--editor-danger: #ef4444;--editor-radius: 8px;--editor-radius-sm: 4px;--editor-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--editor-panel-width: 280px;display:block;width:100%;height:100%;font-family:var(--editor-font);font-size:13px;color:var(--editor-text);line-height:1.4}:host([theme="dark"]){--editor-bg: #1f2937;--editor-surface: #374151;--editor-border: #4b5563;--editor-text: #f9fafb;--editor-text-muted: #9ca3af}.editor-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative;overflow:hidden;background:var(--editor-bg)}.layout-desktop .editor-main{display:flex;flex:1;min-height:0}.layout-desktop .canvas-area{flex:1;min-width:0;position:relative;display:flex;flex-direction:column}.layout-desktop .area-panel{width:var(--editor-panel-width);border-left:1px solid var(--editor-border);background:var(--editor-surface);display:none;flex-direction:column;overflow:hidden}.layout-desktop .area-panel.area-panel-visible{display:flex}.layout-mobile .editor-main{display:flex;flex-direction:column;flex:1;min-height:0}.layout-mobile .canvas-area{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}.layout-mobile .area-panel{position:absolute;bottom:0;left:0;right:0;max-height:50%;background:var(--editor-surface);border-top:1px solid var(--editor-border);border-radius:var(--editor-radius) var(--editor-radius) 0 0;box-shadow:0 -4px 20px #00000026;transform:translateY(100%);transition:transform .25s ease-out;z-index:20;display:flex;flex-direction:column;overflow:hidden}.layout-mobile .area-panel.area-panel-visible{transform:translateY(0)}.area-panel-backdrop{display:none}.layout-mobile .area-panel-backdrop{display:block;position:absolute;inset:0;background:#0000004d;z-index:15;opacity:0;pointer-events:none;transition:opacity .25s ease-out}.layout-mobile .area-panel-backdrop.area-panel-backdrop-visible{opacity:1;pointer-events:auto}.zoom-toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;background:var(--editor-surface);border-bottom:1px solid var(--editor-border)}.zoom-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:14px;font-family:var(--editor-font);transition:background .15s;-webkit-tap-highlight-color:transparent;touch-action:manipulation}.zoom-btn:hover{background:var(--editor-bg)}.zoom-btn:active{background:var(--editor-border)}.zoom-percent{margin-left:8px;font-size:12px;color:var(--editor-text-muted);min-width:40px}.view-select{margin-left:8px;padding:4px 8px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);font-family:var(--editor-font);font-size:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.view-select:hover{background:var(--editor-bg)}.view-select:focus{outline:2px solid var(--editor-primary);outline-offset:-1px}.zoom-toolbar-spacer{flex:1}.zoom-close-btn{width:auto;padding:0 12px;font-size:13px;gap:4px}.zoom-save-btn{width:auto;padding:0 16px;margin-left:8px;font-size:13px;font-weight:600;background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.zoom-save-btn:hover{background:var(--editor-primary-hover)}.zoom-save-btn:disabled{opacity:.5;cursor:not-allowed}.canvas-wrapper{flex:1;position:relative;overflow:hidden;min-height:200px}.editor-canvas{position:absolute;top:0;left:0}.area-panel-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--editor-border);flex-shrink:0}.area-panel-title{font-weight:600;font-size:14px}.area-panel-close-btn{width:28px;height:28px;border:none;background:none;color:var(--editor-text-muted);cursor:pointer;font-size:18px;line-height:1;border-radius:var(--editor-radius-sm);display:flex;align-items:center;justify-content:center}.area-panel-close-btn:hover{background:var(--editor-bg);color:var(--editor-text)}.area-panel-content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.area-panel-empty{color:var(--editor-text-muted);font-size:12px;text-align:center;padding:20px 0}.area-card{padding:12px;border:1px solid var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-surface);cursor:pointer;transition:border-color .15s}.area-card:hover{border-color:var(--editor-primary)}.area-card-selected{border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.area-card-name-row{display:flex;align-items:center;gap:6px}.area-card-name{font-weight:600;font-size:13px}.area-card-badge{display:inline-block;padding:1px 6px;font-size:10px;font-weight:500;background:#fef3c7;color:#92400e;border-radius:10px}.area-card-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.area-card-reset-btn:hover{background:var(--editor-bg);color:var(--editor-danger);border-color:var(--editor-danger)}.area-card-mode-switcher{display:flex;gap:4px;margin-bottom:8px}.mode-btn{flex:1;padding:4px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-family:var(--editor-font);transition:all .15s}.mode-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-text{width:100%;padding:6px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:13px;color:var(--editor-text);background:var(--editor-surface);box-sizing:border-box;margin-bottom:6px}.area-input-text:focus{outline:none;border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-input-row{display:flex;gap:6px;align-items:center;margin-bottom:6px}.area-input-font{flex:1;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface);min-width:0}.area-input-size{width:56px;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.area-input-align{display:flex;gap:2px}.align-btn{width:28px;height:28px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-weight:600;font-family:var(--editor-font)}.align-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-color{width:32px;height:28px;padding:0;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);cursor:pointer;background:none}.area-upload-btn{width:100%;padding:10px;border:2px dashed var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-bg);color:var(--editor-text-muted);cursor:pointer;font-family:var(--editor-font);font-size:12px;text-align:center;transition:all .15s;min-height:44px}.area-upload-btn:hover{border-color:var(--editor-primary);color:var(--editor-primary)}.area-image-preview{display:flex;gap:8px;align-items:center}.area-image-thumb{width:48px;height:48px;object-fit:cover;border-radius:var(--editor-radius-sm);border:1px solid var(--editor-border)}.area-image-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.area-image-name{font-size:12px;color:var(--editor-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.area-image-remove-btn{padding:2px 8px;border:1px solid var(--editor-danger);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-danger);cursor:pointer;font-size:11px;font-family:var(--editor-font);width:fit-content}.area-image-remove-btn:hover{background:#fef2f2}.area-card-divider{height:1px;background:var(--editor-border);margin:8px 0}.transform-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.transform-title{font-weight:600;font-size:12px}.transform-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.transform-reset-btn:hover{background:var(--editor-bg)}.transform-label{font-size:11px;color:var(--editor-text-muted);white-space:nowrap}.transform-input{width:60px;padding:3px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.transform-slider-row{flex-direction:column;align-items:stretch}.transform-slider{width:100%;accent-color:var(--editor-primary)}.area-image-preview{position:relative}.area-upload-spinner-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#fff9;border-radius:var(--editor-radius-sm);z-index:1}:host([theme="dark"]) .area-upload-spinner-overlay{background:#37415199}.area-upload-spinner{width:16px;height:16px;border:2px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}.loading-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px}.loading-spinner{width:32px;height:32px;border:3px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error-container{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px;padding:20px;text-align:center}.error-title{font-weight:600;font-size:16px;color:var(--editor-danger);margin-bottom:8px}.error-message{font-size:13px;color:var(--editor-text-muted);max-width:400px;white-space:pre-wrap}:host(:focus){outline:2px solid var(--editor-primary);outline-offset:-2px}input:focus,select:focus,button:focus-visible{outline:2px solid var(--editor-primary);outline-offset:-1px}.context-menu{position:absolute;z-index:100;min-width:160px;background:var(--editor-surface);border:1px solid var(--editor-border);border-radius:var(--editor-radius);box-shadow:0 4px 12px #00000026;padding:4px 0;font-size:13px}.context-menu-header{padding:6px 12px;font-weight:600;font-size:12px;color:var(--editor-text-muted);user-select:none}.context-menu-divider{height:1px;background:var(--editor-border);margin:4px 0}.context-menu-item{padding:6px 12px;cursor:pointer;color:var(--editor-text);user-select:none;transition:background .1s}.context-menu-item:hover{background:var(--editor-bg)}.context-menu-item:active{background:var(--editor-border)}.context-menu-item-disabled{color:var(--editor-text-muted);cursor:default;pointer-events:none}.editor-toast{position:absolute;top:16px;left:50%;transform:translate(-50%);padding:10px 20px;border-radius:var(--editor-radius);font-size:13px;font-family:var(--editor-font);line-height:1.4;z-index:200;max-width:400px;text-align:center;animation:toast-in .25s ease-out,toast-out .25s ease-in forwards;animation-delay:0s,var(--toast-duration, 3.75s);pointer-events:none;box-shadow:0 4px 12px #00000026}.editor-toast-warning{background:#fef3c7;color:#92400e;border:1px solid #f59e0b}.editor-toast-error{background:#fef2f2;color:#991b1b;border:1px solid var(--editor-danger)}.editor-toast-info{background:#eff6ff;color:#1e40af;border:1px solid var(--editor-primary)}@keyframes toast-in{0%{opacity:0;transform:translate(-50%) translateY(-10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes toast-out{0%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}.editor-modal-overlay{position:absolute;inset:0;background:#0006;z-index:300;display:flex;align-items:center;justify-content:center;animation:modal-overlay-in .2s ease-out}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}.editor-modal{background:var(--editor-surface);border-radius:var(--editor-radius);box-shadow:0 8px 32px #0003;max-width:380px;width:calc(100% - 32px);padding:20px;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.editor-modal-title{font-weight:600;font-size:15px;margin-bottom:8px;color:var(--editor-text)}.editor-modal-body{font-size:13px;color:var(--editor-text-muted);margin-bottom:16px;line-height:1.5}.editor-modal-actions{display:flex;justify-content:flex-end;gap:8px}.editor-modal-btn{padding:8px 16px;border-radius:var(--editor-radius-sm);font-size:13px;font-family:var(--editor-font);cursor:pointer;border:1px solid var(--editor-border);background:var(--editor-surface);color:var(--editor-text);transition:background .15s}.editor-modal-btn:hover{background:var(--editor-bg)}.editor-modal-btn-primary{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.editor-modal-btn-primary:hover{background:var(--editor-primary-hover)}.area-validation-error{font-size:11px;color:var(--editor-danger);margin-top:4px}@media(pointer:coarse){.zoom-toolbar{padding:6px 8px;gap:2px}.zoom-btn{min-width:44px;min-height:44px}.zoom-percent{display:none}.view-select{max-width:110px;text-overflow:ellipsis;overflow:hidden;min-height:44px;font-size:16px}.zoom-close-btn,.zoom-save-btn{padding:0 8px}.area-input-text,.area-input-font,.area-input-size{min-height:44px;font-size:16px}.align-btn{min-width:44px;min-height:44px}.area-upload-btn{min-height:48px}}', _ = "2.0.0", ee = 768;
|
|
1683
|
-
class
|
|
1696
|
+
const A = ':host{--editor-bg: #f3f4f6;--editor-surface: #ffffff;--editor-border: #e5e7eb;--editor-text: #111827;--editor-text-muted: #6b7280;--editor-primary: #3b82f6;--editor-primary-hover: #2563eb;--editor-danger: #ef4444;--editor-radius: 8px;--editor-radius-sm: 4px;--editor-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--editor-panel-width: 280px;display:block;width:100%;height:100%;font-family:var(--editor-font);font-size:13px;color:var(--editor-text);line-height:1.4}:host([theme="dark"]){--editor-bg: #1f2937;--editor-surface: #374151;--editor-border: #4b5563;--editor-text: #f9fafb;--editor-text-muted: #9ca3af}.editor-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative;overflow:hidden;background:var(--editor-bg)}.layout-desktop .editor-main{display:flex;flex:1;min-height:0}.layout-desktop .canvas-area{flex:1;min-width:0;position:relative;display:flex;flex-direction:column}.layout-desktop .area-panel{width:var(--editor-panel-width);border-left:1px solid var(--editor-border);background:var(--editor-surface);display:none;flex-direction:column;overflow:hidden}.layout-desktop .area-panel.area-panel-visible{display:flex}.layout-mobile .editor-main{display:flex;flex-direction:column;flex:1;min-height:0}.layout-mobile .canvas-area{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}.layout-mobile .area-panel{position:absolute;bottom:0;left:0;right:0;max-height:50%;background:var(--editor-surface);border-top:1px solid var(--editor-border);border-radius:var(--editor-radius) var(--editor-radius) 0 0;box-shadow:0 -4px 20px #00000026;transform:translateY(100%);transition:transform .25s ease-out;z-index:20;display:flex;flex-direction:column;overflow:hidden}.layout-mobile .area-panel.area-panel-visible{transform:translateY(0)}.area-panel-backdrop{display:none}.layout-mobile .area-panel-backdrop{display:block;position:absolute;inset:0;background:#0000004d;z-index:15;opacity:0;pointer-events:none;transition:opacity .25s ease-out}.layout-mobile .area-panel-backdrop.area-panel-backdrop-visible{opacity:1;pointer-events:auto}.zoom-toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;background:var(--editor-surface);border-bottom:1px solid var(--editor-border)}.zoom-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:14px;font-family:var(--editor-font);transition:background .15s;-webkit-tap-highlight-color:transparent;touch-action:manipulation}.zoom-btn:hover{background:var(--editor-bg)}.zoom-btn:active{background:var(--editor-border)}.zoom-percent{margin-left:8px;font-size:12px;color:var(--editor-text-muted);min-width:40px}.view-select{margin-left:8px;padding:4px 8px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);font-family:var(--editor-font);font-size:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.view-select:hover{background:var(--editor-bg)}.view-select:focus{outline:2px solid var(--editor-primary);outline-offset:-1px}.zoom-toolbar-spacer{flex:1}.zoom-close-btn{width:auto;padding:0 12px;font-size:13px;gap:4px}.zoom-save-btn{width:auto;padding:0 16px;margin-left:8px;font-size:13px;font-weight:600;background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.zoom-save-btn:hover{background:var(--editor-primary-hover)}.zoom-save-btn:disabled{opacity:.5;cursor:not-allowed}.canvas-wrapper{flex:1;position:relative;overflow:hidden;min-height:200px}.editor-canvas{position:absolute;top:0;left:0;will-change:transform}.area-panel-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--editor-border);flex-shrink:0}.area-panel-title{font-weight:600;font-size:14px}.area-panel-close-btn{width:28px;height:28px;border:none;background:none;color:var(--editor-text-muted);cursor:pointer;font-size:18px;line-height:1;border-radius:var(--editor-radius-sm);display:flex;align-items:center;justify-content:center}.area-panel-close-btn:hover{background:var(--editor-bg);color:var(--editor-text)}.area-panel-content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.area-panel-empty{color:var(--editor-text-muted);font-size:12px;text-align:center;padding:20px 0}.area-card{padding:12px;border:1px solid var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-surface);cursor:pointer;transition:border-color .15s}.area-card:hover{border-color:var(--editor-primary)}.area-card-selected{border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.area-card-name-row{display:flex;align-items:center;gap:6px}.area-card-name{font-weight:600;font-size:13px}.area-card-badge{display:inline-block;padding:1px 6px;font-size:10px;font-weight:500;background:#fef3c7;color:#92400e;border-radius:10px}.area-card-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.area-card-reset-btn:hover{background:var(--editor-bg);color:var(--editor-danger);border-color:var(--editor-danger)}.area-card-mode-switcher{display:flex;gap:4px;margin-bottom:8px}.mode-btn{flex:1;padding:4px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-family:var(--editor-font);transition:all .15s}.mode-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-text{width:100%;padding:6px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:13px;color:var(--editor-text);background:var(--editor-surface);box-sizing:border-box;margin-bottom:6px}.area-input-text:focus{outline:none;border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-input-row{display:flex;gap:6px;align-items:center;margin-bottom:6px}.area-input-font{flex:1;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface);min-width:0}.area-input-size{width:56px;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.area-input-align{display:flex;gap:2px}.align-btn{width:28px;height:28px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-weight:600;font-family:var(--editor-font)}.align-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-color{width:32px;height:28px;padding:0;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);cursor:pointer;background:none}.area-upload-btn{width:100%;padding:10px;border:2px dashed var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-bg);color:var(--editor-text-muted);cursor:pointer;font-family:var(--editor-font);font-size:12px;text-align:center;transition:all .15s;min-height:44px}.area-upload-btn:hover{border-color:var(--editor-primary);color:var(--editor-primary)}.area-image-preview{display:flex;gap:8px;align-items:center}.area-image-thumb{width:48px;height:48px;object-fit:cover;border-radius:var(--editor-radius-sm);border:1px solid var(--editor-border)}.area-image-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.area-image-name{font-size:12px;color:var(--editor-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.area-image-remove-btn{padding:2px 8px;border:1px solid var(--editor-danger);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-danger);cursor:pointer;font-size:11px;font-family:var(--editor-font);width:fit-content}.area-image-remove-btn:hover{background:#fef2f2}.area-card-divider{height:1px;background:var(--editor-border);margin:8px 0}.transform-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.transform-title{font-weight:600;font-size:12px}.transform-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.transform-reset-btn:hover{background:var(--editor-bg)}.transform-label{font-size:11px;color:var(--editor-text-muted);white-space:nowrap}.transform-input{width:60px;padding:3px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.transform-slider-row{flex-direction:column;align-items:stretch}.transform-slider{width:100%;accent-color:var(--editor-primary)}.area-image-preview{position:relative}.area-upload-spinner-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#fff9;border-radius:var(--editor-radius-sm);z-index:1}:host([theme="dark"]) .area-upload-spinner-overlay{background:#37415199}.area-upload-spinner{width:16px;height:16px;border:2px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}.loading-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px}.loading-spinner{width:32px;height:32px;border:3px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error-container{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px;padding:20px;text-align:center}.error-title{font-weight:600;font-size:16px;color:var(--editor-danger);margin-bottom:8px}.error-message{font-size:13px;color:var(--editor-text-muted);max-width:400px;white-space:pre-wrap}:host(:focus){outline:2px solid var(--editor-primary);outline-offset:-2px}input:focus,select:focus,button:focus-visible{outline:2px solid var(--editor-primary);outline-offset:-1px}.context-menu{position:absolute;z-index:100;min-width:160px;background:var(--editor-surface);border:1px solid var(--editor-border);border-radius:var(--editor-radius);box-shadow:0 4px 12px #00000026;padding:4px 0;font-size:13px}.context-menu-header{padding:6px 12px;font-weight:600;font-size:12px;color:var(--editor-text-muted);user-select:none}.context-menu-divider{height:1px;background:var(--editor-border);margin:4px 0}.context-menu-item{padding:6px 12px;cursor:pointer;color:var(--editor-text);user-select:none;transition:background .1s}.context-menu-item:hover{background:var(--editor-bg)}.context-menu-item:active{background:var(--editor-border)}.context-menu-item-disabled{color:var(--editor-text-muted);cursor:default;pointer-events:none}.editor-toast{position:absolute;top:16px;left:50%;transform:translate(-50%);padding:10px 20px;border-radius:var(--editor-radius);font-size:13px;font-family:var(--editor-font);line-height:1.4;z-index:200;max-width:400px;text-align:center;animation:toast-in .25s ease-out,toast-out .25s ease-in forwards;animation-delay:0s,var(--toast-duration, 3.75s);pointer-events:none;box-shadow:0 4px 12px #00000026}.editor-toast-warning{background:#fef3c7;color:#92400e;border:1px solid #f59e0b}.editor-toast-error{background:#fef2f2;color:#991b1b;border:1px solid var(--editor-danger)}.editor-toast-info{background:#eff6ff;color:#1e40af;border:1px solid var(--editor-primary)}@keyframes toast-in{0%{opacity:0;transform:translate(-50%) translateY(-10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes toast-out{0%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}.editor-modal-overlay{position:absolute;inset:0;background:#0006;z-index:300;display:flex;align-items:center;justify-content:center;animation:modal-overlay-in .2s ease-out}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}.editor-modal{background:var(--editor-surface);border-radius:var(--editor-radius);box-shadow:0 8px 32px #0003;max-width:380px;width:calc(100% - 32px);padding:20px;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.editor-modal-title{font-weight:600;font-size:15px;margin-bottom:8px;color:var(--editor-text)}.editor-modal-body{font-size:13px;color:var(--editor-text-muted);margin-bottom:16px;line-height:1.5}.editor-modal-actions{display:flex;justify-content:flex-end;gap:8px}.editor-modal-btn{padding:8px 16px;border-radius:var(--editor-radius-sm);font-size:13px;font-family:var(--editor-font);cursor:pointer;border:1px solid var(--editor-border);background:var(--editor-surface);color:var(--editor-text);transition:background .15s}.editor-modal-btn:hover{background:var(--editor-bg)}.editor-modal-btn-primary{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.editor-modal-btn-primary:hover{background:var(--editor-primary-hover)}.area-validation-error{font-size:11px;color:var(--editor-danger);margin-top:4px}@media(pointer:coarse){.zoom-toolbar{padding:6px 8px;gap:2px}.zoom-btn{min-width:44px;min-height:44px}.zoom-percent{display:none}.view-select{max-width:110px;text-overflow:ellipsis;overflow:hidden;min-height:44px;font-size:16px}.zoom-close-btn,.zoom-save-btn{padding:0 8px}.area-input-text,.area-input-font,.area-input-size{min-height:44px;font-size:16px}.align-btn{min-width:44px;min-height:44px}.area-upload-btn{min-height:48px}}', de = "2.0.0", D = 768;
|
|
1697
|
+
class E extends HTMLElement {
|
|
1684
1698
|
constructor() {
|
|
1685
|
-
super(), this.resizeObserver = null, this.pendingUploads = /* @__PURE__ */ new Map(), this.isReady = !1, this.currentTemplate = null, this.loadingTemplateId = null, this.isMobileLayout = !1, this.productViews = [], this.activeViewName = null, this.perViewContents = /* @__PURE__ */ new Map(), this.subscriptions = [], this.renderAbortController = null, this.contentManagerUnsub = null, this.
|
|
1699
|
+
super(), this.resizeObserver = null, this.isScrolling = !1, this.scrollCleanup = null, this.pendingResizeRect = null, this.pendingUploads = /* @__PURE__ */ new Map(), this.isReady = !1, this.currentTemplate = null, this.loadingTemplateId = null, this.isMobileLayout = !1, this.productViews = [], this.activeViewName = null, this.perViewContents = /* @__PURE__ */ new Map(), this.subscriptions = [], this.renderAbortController = null, this.contentManagerUnsub = null, this.storageUsage = null, this.storageUsageLastRefresh = 0, this.emitChange = W(() => {
|
|
1686
1700
|
this.dispatchEvent(
|
|
1687
1701
|
new CustomEvent("change", {
|
|
1688
1702
|
detail: { design: this.getDesign() },
|
|
@@ -1713,7 +1727,7 @@ class I extends HTMLElement {
|
|
|
1713
1727
|
if (!this.currentTemplate) throw new Error("No design loaded");
|
|
1714
1728
|
return {
|
|
1715
1729
|
templateId: this.currentTemplate.metadata.id,
|
|
1716
|
-
engineVersion:
|
|
1730
|
+
engineVersion: de,
|
|
1717
1731
|
contents: this.contentManager.toJSON(),
|
|
1718
1732
|
userData: {}
|
|
1719
1733
|
};
|
|
@@ -1780,7 +1794,7 @@ class I extends HTMLElement {
|
|
|
1780
1794
|
if (this.productViews.length > 1) {
|
|
1781
1795
|
this.activeViewName && this.contentManager && this.perViewContents.set(this.activeViewName, this.contentManager.toJSON());
|
|
1782
1796
|
for (const t of this.productViews) {
|
|
1783
|
-
const i = this.perViewContents.get(t.viewName), s = i?.some(([, o]) =>
|
|
1797
|
+
const i = this.perViewContents.get(t.viewName), s = i?.some(([, o]) => E.hasEditorContent(o)) ?? !1;
|
|
1784
1798
|
if (t.isRequired && !s) {
|
|
1785
1799
|
e.push(`View "${t.viewName}" is required`);
|
|
1786
1800
|
continue;
|
|
@@ -1789,7 +1803,7 @@ class I extends HTMLElement {
|
|
|
1789
1803
|
for (const o of n) {
|
|
1790
1804
|
if (!o.required) continue;
|
|
1791
1805
|
const a = i?.find(([r]) => r === o.id);
|
|
1792
|
-
a &&
|
|
1806
|
+
a && E.hasEditorContent(a[1]) || e.push(`"${o.name}" in view "${t.viewName}" is required`);
|
|
1793
1807
|
}
|
|
1794
1808
|
}
|
|
1795
1809
|
} else {
|
|
@@ -1797,27 +1811,27 @@ class I extends HTMLElement {
|
|
|
1797
1811
|
for (const s of t) {
|
|
1798
1812
|
if (!s.required) continue;
|
|
1799
1813
|
const n = i.get(s.id);
|
|
1800
|
-
n &&
|
|
1814
|
+
n && E.hasEditorContent(n) || e.push(`"${s.name}" is required`);
|
|
1801
1815
|
}
|
|
1802
1816
|
}
|
|
1803
1817
|
return e;
|
|
1804
1818
|
}
|
|
1805
1819
|
showValidationErrors(e) {
|
|
1806
1820
|
return new Promise((t) => {
|
|
1807
|
-
const i =
|
|
1808
|
-
s.appendChild(
|
|
1809
|
-
const n =
|
|
1821
|
+
const i = u("div", { class: "editor-modal-overlay" }), s = u("div", { class: "editor-modal" });
|
|
1822
|
+
s.appendChild(u("div", { class: "editor-modal-title" }, "Required content missing"));
|
|
1823
|
+
const n = u("div", { class: "editor-modal-body" }), o = document.createElement("ul");
|
|
1810
1824
|
o.style.margin = "8px 0", o.style.paddingLeft = "20px";
|
|
1811
|
-
for (const
|
|
1812
|
-
const
|
|
1813
|
-
|
|
1825
|
+
for (const l of e) {
|
|
1826
|
+
const d = document.createElement("li");
|
|
1827
|
+
d.textContent = l, o.appendChild(d);
|
|
1814
1828
|
}
|
|
1815
1829
|
n.appendChild(o), s.appendChild(n);
|
|
1816
|
-
const a =
|
|
1830
|
+
const a = u("div", { class: "editor-modal-actions" }), r = u("button", { class: "editor-modal-btn editor-modal-btn-primary" }, "OK");
|
|
1817
1831
|
r.addEventListener("click", () => {
|
|
1818
1832
|
i.remove(), t();
|
|
1819
|
-
}), a.appendChild(r), s.appendChild(a), i.appendChild(s), i.addEventListener("click", (
|
|
1820
|
-
|
|
1833
|
+
}), a.appendChild(r), s.appendChild(a), i.appendChild(s), i.addEventListener("click", (l) => {
|
|
1834
|
+
l.target === i && (i.remove(), t());
|
|
1821
1835
|
}), this.shadow.appendChild(i);
|
|
1822
1836
|
});
|
|
1823
1837
|
}
|
|
@@ -1827,7 +1841,7 @@ class I extends HTMLElement {
|
|
|
1827
1841
|
return await this.showValidationErrors(e), null;
|
|
1828
1842
|
const t = this.engine.checkSafeAreaViolations();
|
|
1829
1843
|
if (t.length > 0) {
|
|
1830
|
-
const o = this.currentTemplate?.areas || [], a = t.map((r) => o.find((
|
|
1844
|
+
const o = this.currentTemplate?.areas || [], a = t.map((r) => o.find((l) => l.id === r)?.name || r).join(", ");
|
|
1831
1845
|
if (!await this.showConfirmation(
|
|
1832
1846
|
"Content outside safe area",
|
|
1833
1847
|
`Content in ${a} extends beyond the safe area. It may be trimmed during printing. Continue?`,
|
|
@@ -1840,12 +1854,12 @@ class I extends HTMLElement {
|
|
|
1840
1854
|
if (this.pendingUploads.size > 0 && await Promise.allSettled(this.pendingUploads.values()), this.productViews.length > 1) {
|
|
1841
1855
|
this.activeViewName && this.contentManager && this.perViewContents.set(this.activeViewName, this.contentManager.toJSON());
|
|
1842
1856
|
const o = [];
|
|
1843
|
-
for (const
|
|
1844
|
-
const
|
|
1845
|
-
!
|
|
1846
|
-
templateId:
|
|
1847
|
-
viewName:
|
|
1848
|
-
contents:
|
|
1857
|
+
for (const p of this.productViews) {
|
|
1858
|
+
const c = this.perViewContents.get(p.viewName);
|
|
1859
|
+
!c || c.length === 0 || !c.some(([, m]) => E.hasEditorContent(m)) || o.push({
|
|
1860
|
+
templateId: p.template.id,
|
|
1861
|
+
viewName: p.viewName,
|
|
1862
|
+
contents: c
|
|
1849
1863
|
});
|
|
1850
1864
|
}
|
|
1851
1865
|
if (o.length === 0)
|
|
@@ -1853,30 +1867,38 @@ class I extends HTMLElement {
|
|
|
1853
1867
|
const a = this.getAttribute("product-id");
|
|
1854
1868
|
if (!a)
|
|
1855
1869
|
throw new Error("product-id attribute is required for multi-view finalize");
|
|
1856
|
-
const r = await this.apiClient.finalizeMultiView({
|
|
1870
|
+
const r = this.getAttribute("variant-id") || void 0, l = await this.apiClient.finalizeMultiView({
|
|
1857
1871
|
productId: a,
|
|
1858
|
-
designs: o
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1872
|
+
designs: o,
|
|
1873
|
+
cartContext: { productId: a, variantId: r },
|
|
1874
|
+
customerEmail: this.getAttribute("customer-email") || void 0
|
|
1875
|
+
}), d = l.views.map((p) => ({
|
|
1876
|
+
viewName: p.viewName,
|
|
1877
|
+
designId: p.designId,
|
|
1878
|
+
proofUrl: p.proofUrl || "",
|
|
1879
|
+
status: p.status
|
|
1880
|
+
})), h = d[0];
|
|
1865
1881
|
return this.dispatchEvent(
|
|
1866
1882
|
new CustomEvent("customizer:finalize", {
|
|
1867
1883
|
detail: {
|
|
1868
|
-
designId:
|
|
1869
|
-
proofUrl:
|
|
1884
|
+
designId: h.designId,
|
|
1885
|
+
proofUrl: h.proofUrl,
|
|
1870
1886
|
status: "processing",
|
|
1871
1887
|
views: d,
|
|
1872
|
-
requestId:
|
|
1888
|
+
requestId: l.requestId
|
|
1873
1889
|
},
|
|
1874
1890
|
bubbles: !0,
|
|
1875
1891
|
composed: !0
|
|
1876
1892
|
})
|
|
1877
|
-
), { ...
|
|
1893
|
+
), { ...h, views: d };
|
|
1878
1894
|
}
|
|
1879
|
-
const s = this.getDesign(), n = await this.apiClient.finalizeDesign(i, s
|
|
1895
|
+
const s = this.getDesign(), n = await this.apiClient.finalizeDesign(i, s, {
|
|
1896
|
+
cartContext: {
|
|
1897
|
+
productId: this.getAttribute("product-id") || void 0,
|
|
1898
|
+
variantId: this.getAttribute("variant-id") || void 0
|
|
1899
|
+
},
|
|
1900
|
+
customerEmail: this.getAttribute("customer-email") || void 0
|
|
1901
|
+
});
|
|
1880
1902
|
return this.dispatchEvent(
|
|
1881
1903
|
new CustomEvent("customizer:finalize", {
|
|
1882
1904
|
detail: {
|
|
@@ -1897,18 +1919,18 @@ class I extends HTMLElement {
|
|
|
1897
1919
|
for (; o < s; ) {
|
|
1898
1920
|
if (n?.aborted)
|
|
1899
1921
|
throw new DOMException("Polling aborted", "AbortError");
|
|
1900
|
-
await new Promise((
|
|
1901
|
-
const
|
|
1922
|
+
await new Promise((l, d) => {
|
|
1923
|
+
const h = setTimeout(l, i);
|
|
1902
1924
|
n?.addEventListener("abort", () => {
|
|
1903
|
-
clearTimeout(
|
|
1925
|
+
clearTimeout(h), d(new DOMException("Polling aborted", "AbortError"));
|
|
1904
1926
|
}, { once: !0 });
|
|
1905
1927
|
}), o++;
|
|
1906
1928
|
let r;
|
|
1907
1929
|
if (e.length === 1) {
|
|
1908
|
-
const
|
|
1930
|
+
const l = await this.apiClient.getDesignStatus(e[0]);
|
|
1909
1931
|
r = {
|
|
1910
|
-
designs: [
|
|
1911
|
-
allCompleted:
|
|
1932
|
+
designs: [l],
|
|
1933
|
+
allCompleted: l.status !== "processing"
|
|
1912
1934
|
};
|
|
1913
1935
|
} else
|
|
1914
1936
|
r = await this.apiClient.pollMultiViewStatus(e);
|
|
@@ -1941,17 +1963,17 @@ class I extends HTMLElement {
|
|
|
1941
1963
|
// ─── Initialization ───
|
|
1942
1964
|
renderLoading() {
|
|
1943
1965
|
const e = document.createElement("style");
|
|
1944
|
-
e.textContent =
|
|
1945
|
-
const t =
|
|
1966
|
+
e.textContent = A, this.shadow.appendChild(e);
|
|
1967
|
+
const t = u(
|
|
1946
1968
|
"div",
|
|
1947
1969
|
{ class: "loading-container" },
|
|
1948
|
-
|
|
1970
|
+
u("div", { class: "loading-spinner" })
|
|
1949
1971
|
);
|
|
1950
1972
|
this.shadow.appendChild(t);
|
|
1951
1973
|
}
|
|
1952
1974
|
async initialize() {
|
|
1953
1975
|
const e = this.getAttribute("api-url") || "http://localhost:4000";
|
|
1954
|
-
this.apiClient = new
|
|
1976
|
+
this.apiClient = new Y(`${e.replace(/\/+$/, "")}/public`);
|
|
1955
1977
|
const t = this.getAttribute("product-id"), i = this.getAttribute("template-id");
|
|
1956
1978
|
if (!t && !i)
|
|
1957
1979
|
throw new Error("Either template-id or product-id attribute is required");
|
|
@@ -1982,20 +2004,24 @@ class I extends HTMLElement {
|
|
|
1982
2004
|
isDirty: !1,
|
|
1983
2005
|
warnings: []
|
|
1984
2006
|
};
|
|
1985
|
-
this.stateManager = new
|
|
2007
|
+
this.stateManager = new H(i), this.buildUI(e, t), this.engine = new B(this.canvas), this.engine.setArtboard({
|
|
1986
2008
|
width: e.artboard.width,
|
|
1987
2009
|
height: e.artboard.height,
|
|
1988
2010
|
backgroundColor: e.artboard.backgroundColor ?? "#ffffff"
|
|
1989
2011
|
}), this.engine.setAreas(t), this.engine.setContents(this.contentManager.getContents()), e.backgroundImage && this.engine.setBackgroundImage(e.backgroundImage);
|
|
1990
2012
|
const s = e.artboard.safeArea ?? e.safeArea;
|
|
1991
|
-
s && this.engine.setSafeArea(s), this.engine.start(),
|
|
2013
|
+
s && this.engine.setSafeArea(s), this.engine.start(), requestAnimationFrame(() => {
|
|
2014
|
+
if (!this.canvasWrapper || !this.engine) return;
|
|
2015
|
+
const o = this.canvasWrapper.getBoundingClientRect();
|
|
2016
|
+
o.width > 0 && o.height > 0 && this.handleCanvasResize(o);
|
|
2017
|
+
}), this.interaction = new j(
|
|
1992
2018
|
this.canvas,
|
|
1993
2019
|
this.engine,
|
|
1994
2020
|
() => this.contentManager.getContents(),
|
|
1995
2021
|
() => this.stateManager.getState().selectedAreaId
|
|
1996
2022
|
), this.wireInteractionEvents(), this.contentManagerUnsub = this.contentManager.subscribe(() => {
|
|
1997
2023
|
this.syncEngineContents(), this.inputPanel.setContents(this.contentManager.getContents());
|
|
1998
|
-
}), this.subscriptions.push(this.contentManagerUnsub), this.keyboard = new
|
|
2024
|
+
}), this.subscriptions.push(this.contentManagerUnsub), this.keyboard = new ne(this), this.setupKeyboardShortcuts(), this.inputPanel.setContents(this.contentManager.getContents());
|
|
1999
2025
|
const n = this.getAttribute("store-id");
|
|
2000
2026
|
n && this.apiClient.getStorageUsage(n).then((o) => {
|
|
2001
2027
|
this.storageUsage = o, o.usagePercent >= 100 ? this.showToast("Storage quota exceeded. Upgrade your plan for more storage.", "error") : o.usagePercent >= 80 && this.showToast(`Storage ${o.usagePercent}% full. Consider upgrading your plan.`, "warning");
|
|
@@ -2048,9 +2074,9 @@ class I extends HTMLElement {
|
|
|
2048
2074
|
buildUI(e, t) {
|
|
2049
2075
|
this.shadow.innerHTML = "";
|
|
2050
2076
|
const i = document.createElement("style");
|
|
2051
|
-
i.textContent =
|
|
2052
|
-
const
|
|
2053
|
-
|
|
2077
|
+
i.textContent = A, this.shadow.appendChild(i), this.container = u("div", { class: "editor-container" }), this.zoomToolbar = new _(), this.subscriptions.push(this.zoomToolbar.on("zoom-in", () => this.handleZoomIn())), this.subscriptions.push(this.zoomToolbar.on("zoom-out", () => this.handleZoomOut())), this.subscriptions.push(this.zoomToolbar.on("zoom-fit", () => this.handleZoomFit())), this.subscriptions.push(this.zoomToolbar.on("view-change", ({ viewName: d }) => {
|
|
2078
|
+
const h = this.productViews.find((p) => p.viewName === d);
|
|
2079
|
+
h && this.switchToView(h);
|
|
2054
2080
|
})), this.subscriptions.push(this.zoomToolbar.on("close", () => {
|
|
2055
2081
|
this.dispatchEvent(new CustomEvent("customizer:close", {
|
|
2056
2082
|
bubbles: !0,
|
|
@@ -2059,35 +2085,77 @@ class I extends HTMLElement {
|
|
|
2059
2085
|
})), this.subscriptions.push(this.zoomToolbar.on("save", async () => {
|
|
2060
2086
|
this.pendingUploads.size > 0 && (this.zoomToolbar.setSaveDisabled(!0, "Uploading..."), await Promise.allSettled(this.pendingUploads.values())), this.zoomToolbar.setSaveDisabled(!0, "Saving...");
|
|
2061
2087
|
try {
|
|
2062
|
-
const
|
|
2063
|
-
if (!
|
|
2088
|
+
const d = await this.finalize();
|
|
2089
|
+
if (!d) {
|
|
2064
2090
|
this.zoomToolbar.setSaveDisabled(!1, "✓");
|
|
2065
2091
|
return;
|
|
2066
2092
|
}
|
|
2093
|
+
const h = this.getAttribute("variant-id");
|
|
2094
|
+
if (h)
|
|
2095
|
+
try {
|
|
2096
|
+
const c = d.views && d.requestId || "", m = await fetch("/cart/add.js", {
|
|
2097
|
+
method: "POST",
|
|
2098
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
2099
|
+
body: JSON.stringify({
|
|
2100
|
+
items: [{
|
|
2101
|
+
id: h,
|
|
2102
|
+
quantity: 1,
|
|
2103
|
+
properties: {
|
|
2104
|
+
_design_id: d.designId,
|
|
2105
|
+
...c && { _request_id: c },
|
|
2106
|
+
Customized: "Yes"
|
|
2107
|
+
}
|
|
2108
|
+
}]
|
|
2109
|
+
})
|
|
2110
|
+
});
|
|
2111
|
+
if (!m.ok) throw new Error("Failed to add to cart");
|
|
2112
|
+
const f = await m.json();
|
|
2113
|
+
this.dispatchEvent(new CustomEvent("customizer:cart-added", {
|
|
2114
|
+
detail: { designId: d.designId, cart: f },
|
|
2115
|
+
bubbles: !0,
|
|
2116
|
+
composed: !0
|
|
2117
|
+
}));
|
|
2118
|
+
} catch (c) {
|
|
2119
|
+
console.error("Customizer: cart add failed", c), this.showToast("Failed to add to cart. Please try again.", "error");
|
|
2120
|
+
}
|
|
2067
2121
|
this.zoomToolbar.setSaveDisabled(!0, "Saved!"), this.showToast("Design saved successfully!", "info"), this.dispatchEvent(new CustomEvent("customizer:save", {
|
|
2068
|
-
detail:
|
|
2122
|
+
detail: d,
|
|
2069
2123
|
bubbles: !0,
|
|
2070
2124
|
composed: !0
|
|
2071
2125
|
}));
|
|
2072
|
-
const
|
|
2073
|
-
this.renderAbortController?.abort(), this.renderAbortController = new AbortController(), this.waitForResult(
|
|
2074
|
-
|
|
2126
|
+
const p = d.views ? d.views.map((c) => c.designId) : [d.designId];
|
|
2127
|
+
this.renderAbortController?.abort(), this.renderAbortController = new AbortController(), this.waitForResult(p, { signal: this.renderAbortController.signal }).catch((c) => {
|
|
2128
|
+
c instanceof DOMException && c.name === "AbortError" || console.error("Customizer: background render polling failed", c);
|
|
2075
2129
|
}), setTimeout(() => {
|
|
2076
2130
|
this.zoomToolbar.setSaveDisabled(!1, "✓"), this.zoomToolbar.showSaveButton(!1), this.stateManager.update({ isDirty: !1 });
|
|
2077
2131
|
}, 2e3);
|
|
2078
|
-
} catch (
|
|
2079
|
-
console.error("Customizer: finalize failed",
|
|
2080
|
-
const
|
|
2081
|
-
this.showToast(
|
|
2132
|
+
} catch (d) {
|
|
2133
|
+
console.error("Customizer: finalize failed", d);
|
|
2134
|
+
const h = d instanceof Error ? d.message : "Save failed. Please try again.";
|
|
2135
|
+
this.showToast(h, "error"), this.zoomToolbar.setSaveDisabled(!1, "✓");
|
|
2082
2136
|
}
|
|
2083
2137
|
})), this.getAttribute("show-close-button") === "false" && this.zoomToolbar.setCloseButtonVisible(!1), this.getAttribute("show-save-button") === "false" && this.zoomToolbar.setSaveButtonEnabled(!1);
|
|
2084
|
-
const s =
|
|
2085
|
-
s.appendChild(this.zoomToolbar.getElement()), this.canvasWrapper =
|
|
2086
|
-
const n =
|
|
2087
|
-
n.appendChild(s), n.appendChild(this.inputPanel.getBackdrop()), n.appendChild(this.inputPanel.getElement()), this.container.appendChild(n), this.shadow.appendChild(this.container)
|
|
2088
|
-
|
|
2089
|
-
|
|
2138
|
+
const s = u("div", { class: "canvas-area" });
|
|
2139
|
+
s.appendChild(this.zoomToolbar.getElement()), this.canvasWrapper = u("div", { class: "canvas-wrapper" }), this.canvas = document.createElement("canvas"), this.canvas.className = "editor-canvas", this.canvasWrapper.appendChild(this.canvas), s.appendChild(this.canvasWrapper), this.inputPanel = new se(t), this.wireInputPanelEvents();
|
|
2140
|
+
const n = u("div", { class: "editor-main" });
|
|
2141
|
+
n.appendChild(s), n.appendChild(this.inputPanel.getBackdrop()), n.appendChild(this.inputPanel.getElement()), this.container.appendChild(n), this.shadow.appendChild(this.container);
|
|
2142
|
+
const o = this.getBoundingClientRect().width, a = o > 0 && o < D;
|
|
2143
|
+
this.container.classList.add(a ? "layout-mobile" : "layout-desktop"), this.isMobileLayout = a, this.inputPanel.setMobile(a), this.resizeObserver = new ResizeObserver((d) => {
|
|
2144
|
+
for (const h of d)
|
|
2145
|
+
h.target === this.canvasWrapper && this.handleCanvasResize(h.contentRect), h.target === this.container && this.handleLayoutResize(h.contentRect);
|
|
2090
2146
|
}), this.resizeObserver.observe(this.canvasWrapper), this.resizeObserver.observe(this.container);
|
|
2147
|
+
let r = null;
|
|
2148
|
+
const l = () => {
|
|
2149
|
+
this.isScrolling = !0, r && clearTimeout(r), r = setTimeout(() => {
|
|
2150
|
+
if (this.isScrolling = !1, this.pendingResizeRect) {
|
|
2151
|
+
const d = this.pendingResizeRect;
|
|
2152
|
+
this.pendingResizeRect = null, this.handleCanvasResize(d);
|
|
2153
|
+
}
|
|
2154
|
+
}, 200);
|
|
2155
|
+
};
|
|
2156
|
+
window.addEventListener("scroll", l, { passive: !0 }), this.scrollCleanup = () => {
|
|
2157
|
+
window.removeEventListener("scroll", l), r && clearTimeout(r);
|
|
2158
|
+
};
|
|
2091
2159
|
}
|
|
2092
2160
|
// ─── Event Wiring ───
|
|
2093
2161
|
wireInteractionEvents() {
|
|
@@ -2130,14 +2198,14 @@ class I extends HTMLElement {
|
|
|
2130
2198
|
wireInputPanelEvents() {
|
|
2131
2199
|
this.subscriptions.push(
|
|
2132
2200
|
this.inputPanel.on("text:change", ({ areaId: e, updates: t }) => {
|
|
2133
|
-
this.contentManager.setTextContent(e, t), this.saveContentState();
|
|
2201
|
+
t.text !== void 0 && t.text.length > 1e4 && (t = { ...t, text: t.text.slice(0, 1e4) }), this.contentManager.setTextContent(e, t), this.saveContentState();
|
|
2134
2202
|
})
|
|
2135
2203
|
), this.subscriptions.push(
|
|
2136
2204
|
this.inputPanel.on("image:change", async ({ areaId: e, dataUrl: t, filename: i }) => {
|
|
2137
2205
|
const s = this.contentManager.getContents();
|
|
2138
2206
|
let n = 0;
|
|
2139
|
-
for (const [r,
|
|
2140
|
-
|
|
2207
|
+
for (const [r, l] of s)
|
|
2208
|
+
l.type === "image" && r !== e && n++;
|
|
2141
2209
|
if (n >= 10) {
|
|
2142
2210
|
this.showToast("Maximum of 10 images per design reached", "error");
|
|
2143
2211
|
return;
|
|
@@ -2150,31 +2218,31 @@ class I extends HTMLElement {
|
|
|
2150
2218
|
if (o && this.currentTemplate) {
|
|
2151
2219
|
const r = this.currentTemplate.print.targetDpi;
|
|
2152
2220
|
try {
|
|
2153
|
-
const
|
|
2154
|
-
|
|
2155
|
-
const
|
|
2156
|
-
|
|
2157
|
-
}),
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2221
|
+
const l = new Image();
|
|
2222
|
+
l.crossOrigin = "anonymous";
|
|
2223
|
+
const d = await new Promise((m, f) => {
|
|
2224
|
+
l.onload = () => m({ width: l.naturalWidth, height: l.naturalHeight }), l.onerror = () => f(new Error("Failed to load image")), l.src = t;
|
|
2225
|
+
}), h = o.location.width / r, p = o.location.height / r, c = le(
|
|
2226
|
+
d.width,
|
|
2227
|
+
d.height,
|
|
2228
|
+
h,
|
|
2161
2229
|
p,
|
|
2162
2230
|
r
|
|
2163
2231
|
);
|
|
2164
|
-
if (
|
|
2232
|
+
if (c.actualDPI < 150) {
|
|
2165
2233
|
if (!await this.showConfirmation(
|
|
2166
2234
|
"Low resolution detected",
|
|
2167
|
-
`This image is ${
|
|
2235
|
+
`This image is ${c.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${r} DPI.`,
|
|
2168
2236
|
"Cancel",
|
|
2169
2237
|
"Use Anyway"
|
|
2170
2238
|
)) return;
|
|
2171
|
-
} else
|
|
2172
|
-
`Image resolution is ${
|
|
2239
|
+
} else c.actualDPI < r && (this.showToast(
|
|
2240
|
+
`Image resolution is ${c.actualDPI} DPI (${r} recommended)`,
|
|
2173
2241
|
"warning"
|
|
2174
2242
|
), this.stateManager.update({
|
|
2175
2243
|
warnings: [
|
|
2176
2244
|
...this.stateManager.getState().warnings,
|
|
2177
|
-
{ type: "dpi", message: `${
|
|
2245
|
+
{ type: "dpi", message: `${c.actualDPI} DPI`, areaId: e }
|
|
2178
2246
|
]
|
|
2179
2247
|
}));
|
|
2180
2248
|
} catch {
|
|
@@ -2241,28 +2309,19 @@ class I extends HTMLElement {
|
|
|
2241
2309
|
handleZoomFit() {
|
|
2242
2310
|
this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom());
|
|
2243
2311
|
}
|
|
2244
|
-
/**
|
|
2245
|
-
* Schedule a canvas refit after layout settles (double-rAF ensures
|
|
2246
|
-
* ResizeObserver callbacks and layout-class changes have been applied).
|
|
2247
|
-
*/
|
|
2248
|
-
scheduleCanvasRefit() {
|
|
2249
|
-
this.refitRafId != null && cancelAnimationFrame(this.refitRafId), this.refitRafId = requestAnimationFrame(() => {
|
|
2250
|
-
this.refitRafId = requestAnimationFrame(() => {
|
|
2251
|
-
if (this.refitRafId = null, !this.canvasWrapper || !this.engine) return;
|
|
2252
|
-
const e = this.canvasWrapper.getBoundingClientRect();
|
|
2253
|
-
e.width > 0 && e.height > 0 && this.handleCanvasResize(e);
|
|
2254
|
-
});
|
|
2255
|
-
});
|
|
2256
|
-
}
|
|
2257
2312
|
handleCanvasResize(e) {
|
|
2258
2313
|
const { width: t, height: i } = e;
|
|
2259
2314
|
if (t === 0 || i === 0) return;
|
|
2260
|
-
|
|
2261
|
-
|
|
2315
|
+
if (this.isScrolling) {
|
|
2316
|
+
this.pendingResizeRect = e;
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
const s = window.devicePixelRatio || 1, n = Math.round(t * s), o = Math.round(i * s);
|
|
2320
|
+
this.canvas.width === n && this.canvas.height === o || (this.canvas.width = n, this.canvas.height = o, this.canvas.style.width = `${t}px`, this.canvas.style.height = `${i}px`, this.engine && (this.engine.fitToView(), this.stateManager.update({ zoom: this.engine.getZoom(), pan: this.engine.getPan() }), this.zoomToolbar.setZoom(this.engine.getZoom())));
|
|
2262
2321
|
}
|
|
2263
2322
|
handleLayoutResize(e) {
|
|
2264
|
-
const t = e.width <
|
|
2265
|
-
this.container.classList.toggle("layout-desktop", !t), this.container.classList.toggle("layout-mobile", t), t !== this.isMobileLayout && (this.isMobileLayout = t, this.inputPanel.setMobile(t)
|
|
2323
|
+
const t = e.width < D;
|
|
2324
|
+
this.container.classList.toggle("layout-desktop", !t), this.container.classList.toggle("layout-mobile", t), t !== this.isMobileLayout && (this.isMobileLayout = t, this.inputPanel.setMobile(t));
|
|
2266
2325
|
}
|
|
2267
2326
|
// ─── State Management ───
|
|
2268
2327
|
async refreshStorageUsage() {
|
|
@@ -2286,27 +2345,33 @@ class I extends HTMLElement {
|
|
|
2286
2345
|
}), n;
|
|
2287
2346
|
}
|
|
2288
2347
|
async _doPreUpload(e, t, i, s) {
|
|
2289
|
-
const [n, o] = t.split(","), a = n.match(/:(.*?);/)?.[1] || "image/png", r = atob(o),
|
|
2290
|
-
for (let
|
|
2291
|
-
const
|
|
2292
|
-
let
|
|
2348
|
+
const [n, o] = t.split(","), a = n.match(/:(.*?);/)?.[1] || "image/png", r = atob(o), l = new Uint8Array(r.length);
|
|
2349
|
+
for (let m = 0; m < r.length; m++) l[m] = r.charCodeAt(m);
|
|
2350
|
+
const d = new Blob([l], { type: a }), h = a.split("/")[1] || "png", p = new File([d], `upload.${h}`, { type: a });
|
|
2351
|
+
let c;
|
|
2293
2352
|
try {
|
|
2294
|
-
|
|
2295
|
-
} catch (
|
|
2296
|
-
console.error("Customizer: image pre-upload failed",
|
|
2353
|
+
c = await this.apiClient.uploadAsset(p, i);
|
|
2354
|
+
} catch (m) {
|
|
2355
|
+
console.error("Customizer: image pre-upload failed", m), this.showToast("Image upload failed. Will retry on save.", "warning");
|
|
2297
2356
|
return;
|
|
2298
2357
|
}
|
|
2299
2358
|
if (this.activeViewName === s) {
|
|
2300
|
-
const
|
|
2301
|
-
|
|
2302
|
-
assetId:
|
|
2303
|
-
assetUrl:
|
|
2359
|
+
const m = this.contentManager.getContent(e);
|
|
2360
|
+
m?.type === "image" && m.dataUrl === t && this.contentManager.setImageContent(e, "", m.filename, {
|
|
2361
|
+
assetId: c.assetId,
|
|
2362
|
+
assetUrl: c.publicUrl
|
|
2304
2363
|
});
|
|
2305
2364
|
} else if (s) {
|
|
2306
|
-
const
|
|
2307
|
-
if (
|
|
2308
|
-
const
|
|
2309
|
-
|
|
2365
|
+
const m = this.perViewContents.get(s);
|
|
2366
|
+
if (m) {
|
|
2367
|
+
const f = m.map(([v, b]) => {
|
|
2368
|
+
if (v === e && b.type === "image" && b.dataUrl === t) {
|
|
2369
|
+
const { dataUrl: y, ...x } = b;
|
|
2370
|
+
return [v, { ...x, assetId: c.assetId, assetUrl: c.publicUrl }];
|
|
2371
|
+
}
|
|
2372
|
+
return [v, b];
|
|
2373
|
+
});
|
|
2374
|
+
this.perViewContents.set(s, f);
|
|
2310
2375
|
}
|
|
2311
2376
|
}
|
|
2312
2377
|
}
|
|
@@ -2325,7 +2390,7 @@ class I extends HTMLElement {
|
|
|
2325
2390
|
}
|
|
2326
2391
|
// ─── Keyboard ───
|
|
2327
2392
|
setupKeyboardShortcuts() {
|
|
2328
|
-
const e =
|
|
2393
|
+
const e = oe();
|
|
2329
2394
|
this.keyboard.register({
|
|
2330
2395
|
key: "z",
|
|
2331
2396
|
[e]: !0,
|
|
@@ -2345,22 +2410,22 @@ class I extends HTMLElement {
|
|
|
2345
2410
|
}
|
|
2346
2411
|
// ─── Toast & Modal ───
|
|
2347
2412
|
showToast(e, t = "info") {
|
|
2348
|
-
const i =
|
|
2413
|
+
const i = u("div", { class: `editor-toast editor-toast-${t}` }, e);
|
|
2349
2414
|
this.shadow.appendChild(i), setTimeout(() => i.remove(), 4e3);
|
|
2350
2415
|
}
|
|
2351
2416
|
showConfirmation(e, t, i = "Cancel", s = "Continue") {
|
|
2352
2417
|
return new Promise((n) => {
|
|
2353
|
-
const o =
|
|
2354
|
-
a.appendChild(
|
|
2355
|
-
const r =
|
|
2356
|
-
|
|
2418
|
+
const o = u("div", { class: "editor-modal-overlay" }), a = u("div", { class: "editor-modal" });
|
|
2419
|
+
a.appendChild(u("div", { class: "editor-modal-title" }, e)), a.appendChild(u("div", { class: "editor-modal-body" }, t));
|
|
2420
|
+
const r = u("div", { class: "editor-modal-actions" }), l = u("button", { class: "editor-modal-btn" }, i);
|
|
2421
|
+
l.addEventListener("click", () => {
|
|
2357
2422
|
o.remove(), n(!1);
|
|
2358
2423
|
});
|
|
2359
|
-
const
|
|
2360
|
-
|
|
2424
|
+
const d = u("button", { class: "editor-modal-btn editor-modal-btn-primary" }, s);
|
|
2425
|
+
d.addEventListener("click", () => {
|
|
2361
2426
|
o.remove(), n(!0);
|
|
2362
|
-
}), r.appendChild(
|
|
2363
|
-
|
|
2427
|
+
}), r.appendChild(l), r.appendChild(d), a.appendChild(r), o.appendChild(a), o.addEventListener("click", (h) => {
|
|
2428
|
+
h.target === o && (o.remove(), n(!1));
|
|
2364
2429
|
}), this.shadow.appendChild(o);
|
|
2365
2430
|
});
|
|
2366
2431
|
}
|
|
@@ -2368,9 +2433,9 @@ class I extends HTMLElement {
|
|
|
2368
2433
|
showError(e, t) {
|
|
2369
2434
|
this.shadow.innerHTML = "";
|
|
2370
2435
|
const i = document.createElement("style");
|
|
2371
|
-
i.textContent =
|
|
2372
|
-
const s =
|
|
2373
|
-
s.appendChild(
|
|
2436
|
+
i.textContent = A, this.shadow.appendChild(i);
|
|
2437
|
+
const s = u("div", { class: "error-container" });
|
|
2438
|
+
s.appendChild(u("div", { class: "error-title" }, e)), s.appendChild(u("div", { class: "error-message" }, t?.message ?? "")), this.shadow.appendChild(s), this.dispatchEvent(
|
|
2374
2439
|
new CustomEvent("error", {
|
|
2375
2440
|
detail: { message: e, error: t },
|
|
2376
2441
|
bubbles: !0,
|
|
@@ -2382,20 +2447,67 @@ class I extends HTMLElement {
|
|
|
2382
2447
|
this.renderAbortController?.abort(), this.renderAbortController = null;
|
|
2383
2448
|
for (const e of this.subscriptions)
|
|
2384
2449
|
e();
|
|
2385
|
-
this.subscriptions = [], this.
|
|
2450
|
+
this.subscriptions = [], this.resizeObserver && (this.resizeObserver.disconnect(), this.resizeObserver = null), this.scrollCleanup && (this.scrollCleanup(), this.scrollCleanup = null), this.engine?.destroy(), this.interaction?.destroy(), this.keyboard?.destroy(), this.isReady = !1, this.currentTemplate = null, this.productViews = [], this.activeViewName = null, this.perViewContents.clear();
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
customElements.get("customizer-editor") || customElements.define("customizer-editor", E);
|
|
2454
|
+
class w extends Error {
|
|
2455
|
+
code;
|
|
2456
|
+
details;
|
|
2457
|
+
constructor(e, t, i) {
|
|
2458
|
+
super(t), Object.setPrototypeOf(this, w.prototype), this.name = "CustomizerError", this.code = e, this.details = i;
|
|
2386
2459
|
}
|
|
2387
2460
|
}
|
|
2388
|
-
|
|
2389
|
-
function
|
|
2390
|
-
|
|
2461
|
+
const he = /[\x00-\x08\x0B\x0C\x0E-\x1F]/g, N = 1e4;
|
|
2462
|
+
function O(g) {
|
|
2463
|
+
if (typeof g != "string" || g.trim() === "")
|
|
2464
|
+
throw new w("VALIDATION_ERROR", "apiUrl must be a non-empty string");
|
|
2465
|
+
const e = g.trim().toLowerCase();
|
|
2466
|
+
if (!e.startsWith("http://") && !e.startsWith("https://"))
|
|
2467
|
+
throw new w("VALIDATION_ERROR", "apiUrl must use http:// or https:// protocol");
|
|
2468
|
+
}
|
|
2469
|
+
function z(g, e) {
|
|
2470
|
+
if (typeof g != "string" || g.trim() === "")
|
|
2471
|
+
throw new w("VALIDATION_ERROR", `${e} must be a non-empty string`);
|
|
2472
|
+
}
|
|
2473
|
+
function ce(g) {
|
|
2474
|
+
if (typeof g != "string")
|
|
2475
|
+
throw new w("VALIDATION_ERROR", "text must be a string");
|
|
2476
|
+
const e = g.replace(he, "");
|
|
2477
|
+
if (e.length > N)
|
|
2478
|
+
throw new w("VALIDATION_ERROR", `text exceeds maximum length of ${N} characters`);
|
|
2479
|
+
return e;
|
|
2480
|
+
}
|
|
2481
|
+
function ue(g) {
|
|
2482
|
+
if (typeof g != "string" || g.trim() === "")
|
|
2483
|
+
throw new w("VALIDATION_ERROR", "image URL must be a non-empty string");
|
|
2484
|
+
if (!pe(g))
|
|
2485
|
+
throw new w("VALIDATION_ERROR", "image URL uses a disallowed protocol");
|
|
2486
|
+
}
|
|
2487
|
+
function pe(g) {
|
|
2488
|
+
if (typeof g != "string") return !1;
|
|
2489
|
+
const e = g.trim().toLowerCase();
|
|
2490
|
+
return !!(e.startsWith("https://") || e.startsWith("http://") || e.startsWith("blob:") || e.startsWith("data:image/"));
|
|
2491
|
+
}
|
|
2492
|
+
function ge(g) {
|
|
2493
|
+
if (!Array.isArray(g) || g.length === 0)
|
|
2494
|
+
throw new w("VALIDATION_ERROR", "designIds must be a non-empty array");
|
|
2495
|
+
for (let e = 0; e < g.length; e++)
|
|
2496
|
+
if (typeof g[e] != "string" || g[e].trim() === "")
|
|
2497
|
+
throw new w("VALIDATION_ERROR", `designIds[${e}] must be a non-empty string`);
|
|
2498
|
+
}
|
|
2499
|
+
function me(g, e) {
|
|
2500
|
+
const t = typeof g == "string" ? document.querySelector(g) : g;
|
|
2391
2501
|
if (!t)
|
|
2392
|
-
throw new
|
|
2393
|
-
|
|
2502
|
+
throw new w(
|
|
2503
|
+
"INIT_ERROR",
|
|
2504
|
+
`Container not found: ${typeof g == "string" ? g : "provided element is null"}`
|
|
2394
2505
|
);
|
|
2395
2506
|
if (!e.templateId && !e.productId)
|
|
2396
|
-
throw new
|
|
2507
|
+
throw new w("INIT_ERROR", "Either templateId or productId must be provided");
|
|
2397
2508
|
if (e.templateId && e.productId)
|
|
2398
|
-
throw new
|
|
2509
|
+
throw new w("INIT_ERROR", "Only one of templateId or productId should be provided, not both");
|
|
2510
|
+
e.templateId && z(e.templateId, "templateId"), e.productId && z(e.productId, "productId"), e.apiUrl && O(e.apiUrl);
|
|
2399
2511
|
const i = document.createElement("customizer-editor");
|
|
2400
2512
|
e.productId ? i.setAttribute("product-id", e.productId) : i.setAttribute("template-id", e.templateId), e.theme && i.setAttribute("theme", e.theme), e.mode && i.setAttribute("mode", e.mode), e.className && i.classList.add(e.className), e.showCloseButton === !1 && i.setAttribute("show-close-button", "false"), e.showSaveButton === !1 && i.setAttribute("show-save-button", "false"), i.style.width = "100%", i.style.height = "100%";
|
|
2401
2513
|
const s = [], n = (a, r) => {
|
|
@@ -2405,22 +2517,24 @@ function te(f, e) {
|
|
|
2405
2517
|
const o = {
|
|
2406
2518
|
getDesign() {
|
|
2407
2519
|
if (typeof i.getDesign != "function")
|
|
2408
|
-
throw new
|
|
2520
|
+
throw new w("NOT_READY", "Editor not ready: getDesign method not available");
|
|
2409
2521
|
return i.getDesign();
|
|
2410
2522
|
},
|
|
2411
2523
|
setDesign(a) {
|
|
2524
|
+
if (a == null || typeof a != "object")
|
|
2525
|
+
throw new w("VALIDATION_ERROR", "design must be a non-null object");
|
|
2412
2526
|
if (typeof i.setDesign != "function")
|
|
2413
|
-
throw new
|
|
2527
|
+
throw new w("NOT_READY", "Editor not ready: setDesign method not available");
|
|
2414
2528
|
i.setDesign(a);
|
|
2415
2529
|
},
|
|
2416
2530
|
undo() {
|
|
2417
2531
|
if (typeof i.undo != "function")
|
|
2418
|
-
throw new
|
|
2532
|
+
throw new w("NOT_READY", "Editor not ready: undo method not available");
|
|
2419
2533
|
i.undo();
|
|
2420
2534
|
},
|
|
2421
2535
|
redo() {
|
|
2422
2536
|
if (typeof i.redo != "function")
|
|
2423
|
-
throw new
|
|
2537
|
+
throw new w("NOT_READY", "Editor not ready: redo method not available");
|
|
2424
2538
|
i.redo();
|
|
2425
2539
|
},
|
|
2426
2540
|
canUndo() {
|
|
@@ -2429,116 +2543,128 @@ function te(f, e) {
|
|
|
2429
2543
|
canRedo() {
|
|
2430
2544
|
return typeof i.canRedo != "function" ? !1 : i.canRedo();
|
|
2431
2545
|
},
|
|
2432
|
-
async finalize() {
|
|
2433
|
-
const
|
|
2546
|
+
async finalize(a) {
|
|
2547
|
+
const r = e.apiUrl || "https://api.varianta.io";
|
|
2548
|
+
O(r);
|
|
2434
2549
|
try {
|
|
2435
2550
|
if (e.productId && typeof i.finalize == "function") {
|
|
2436
|
-
const
|
|
2437
|
-
designId:
|
|
2551
|
+
const c = await i.finalize(), m = {
|
|
2552
|
+
designId: c.designId,
|
|
2438
2553
|
status: "processing",
|
|
2439
|
-
proofUrl:
|
|
2554
|
+
proofUrl: c.proofUrl ?? null,
|
|
2440
2555
|
errorMessage: null,
|
|
2441
|
-
requestId:
|
|
2442
|
-
views:
|
|
2556
|
+
requestId: c.requestId,
|
|
2557
|
+
views: c.views
|
|
2443
2558
|
};
|
|
2444
|
-
return
|
|
2559
|
+
return t.dispatchEvent(new CustomEvent("customizer:finalize", {
|
|
2560
|
+
bubbles: !0,
|
|
2561
|
+
detail: m
|
|
2562
|
+
})), e.onFinalize && e.onFinalize(m), m;
|
|
2445
2563
|
}
|
|
2446
|
-
const
|
|
2564
|
+
const l = this.getDesign(), d = await fetch(`${r}/public/finalize`, {
|
|
2447
2565
|
method: "POST",
|
|
2448
2566
|
headers: {
|
|
2449
2567
|
"Content-Type": "application/json"
|
|
2450
2568
|
},
|
|
2451
2569
|
body: JSON.stringify({
|
|
2452
|
-
templateId:
|
|
2453
|
-
designJson:
|
|
2454
|
-
})
|
|
2570
|
+
templateId: l.templateId || e.templateId,
|
|
2571
|
+
designJson: l
|
|
2572
|
+
}),
|
|
2573
|
+
signal: a
|
|
2455
2574
|
});
|
|
2456
2575
|
if (!d.ok) {
|
|
2457
|
-
const
|
|
2576
|
+
const c = await d.json().catch(() => ({
|
|
2458
2577
|
message: "Finalization failed"
|
|
2459
2578
|
}));
|
|
2460
|
-
throw new
|
|
2579
|
+
throw new w("FINALIZE_ERROR", c.message || "Finalization failed");
|
|
2461
2580
|
}
|
|
2462
|
-
const
|
|
2463
|
-
designId:
|
|
2464
|
-
status:
|
|
2465
|
-
proofUrl:
|
|
2466
|
-
errorMessage:
|
|
2467
|
-
};
|
|
2468
|
-
return e.onFinalize && e.onFinalize(c), c;
|
|
2469
|
-
} catch (r) {
|
|
2470
|
-
const d = {
|
|
2471
|
-
code: "FINALIZE_ERROR",
|
|
2472
|
-
message: r instanceof Error ? r.message : "Unknown error",
|
|
2473
|
-
details: r
|
|
2581
|
+
const h = await d.json(), p = {
|
|
2582
|
+
designId: h.designId,
|
|
2583
|
+
status: h.status,
|
|
2584
|
+
proofUrl: h.proofUrl ?? null,
|
|
2585
|
+
errorMessage: h.errorMessage ?? null
|
|
2474
2586
|
};
|
|
2475
|
-
|
|
2587
|
+
return t.dispatchEvent(new CustomEvent("customizer:finalize", {
|
|
2588
|
+
bubbles: !0,
|
|
2589
|
+
detail: p
|
|
2590
|
+
})), e.onFinalize && e.onFinalize(p), p;
|
|
2591
|
+
} catch (l) {
|
|
2592
|
+
if (l instanceof DOMException && l.name === "AbortError") {
|
|
2593
|
+
const h = new w("FINALIZE_CANCELLED", "Finalization was cancelled", l);
|
|
2594
|
+
throw e.onError && e.onError(h), h;
|
|
2595
|
+
}
|
|
2596
|
+
const d = l instanceof w ? l : new w(
|
|
2597
|
+
"FINALIZE_ERROR",
|
|
2598
|
+
l instanceof Error ? l.message : "Unknown error",
|
|
2599
|
+
l
|
|
2600
|
+
);
|
|
2601
|
+
throw e.onError && e.onError(d), d;
|
|
2476
2602
|
}
|
|
2477
2603
|
},
|
|
2478
2604
|
async waitForResult(a, r) {
|
|
2479
|
-
if (typeof i.waitForResult == "function") {
|
|
2480
|
-
const
|
|
2481
|
-
return e.onRenderComplete && e.onRenderComplete(
|
|
2605
|
+
if (ge(a), typeof i.waitForResult == "function") {
|
|
2606
|
+
const f = await i.waitForResult(a, r);
|
|
2607
|
+
return e.onRenderComplete && e.onRenderComplete(f), f;
|
|
2482
2608
|
}
|
|
2483
|
-
const
|
|
2484
|
-
let
|
|
2485
|
-
for (;
|
|
2609
|
+
const l = e.apiUrl || "https://api.varianta.io", d = r?.pollInterval ?? 1500, h = r?.maxPolls ?? 40, p = r?.signal;
|
|
2610
|
+
let c = 0;
|
|
2611
|
+
for (; c < h; ) {
|
|
2486
2612
|
if (p?.aborted)
|
|
2487
2613
|
throw new DOMException("Polling aborted", "AbortError");
|
|
2488
2614
|
await new Promise((v, b) => {
|
|
2489
|
-
const
|
|
2615
|
+
const y = setTimeout(v, d);
|
|
2490
2616
|
p?.addEventListener("abort", () => {
|
|
2491
|
-
clearTimeout(
|
|
2617
|
+
clearTimeout(y), b(new DOMException("Polling aborted", "AbortError"));
|
|
2492
2618
|
}, { once: !0 });
|
|
2493
|
-
}),
|
|
2494
|
-
let
|
|
2619
|
+
}), c++;
|
|
2620
|
+
let f;
|
|
2495
2621
|
if (a.length === 1) {
|
|
2496
|
-
const v = await fetch(`${
|
|
2497
|
-
if (!v.ok) throw new
|
|
2622
|
+
const v = await fetch(`${l}/public/designs/${a[0]}/status`);
|
|
2623
|
+
if (!v.ok) throw new w("NETWORK_ERROR", "Failed to check design status");
|
|
2498
2624
|
const b = await v.json();
|
|
2499
|
-
|
|
2625
|
+
f = {
|
|
2500
2626
|
designs: [b],
|
|
2501
2627
|
allCompleted: b.status !== "processing"
|
|
2502
2628
|
};
|
|
2503
2629
|
} else {
|
|
2504
2630
|
const v = await fetch(
|
|
2505
|
-
`${
|
|
2631
|
+
`${l}/public/finalize-status?designIds=${a.join(",")}`
|
|
2506
2632
|
);
|
|
2507
|
-
if (!v.ok) throw new
|
|
2508
|
-
|
|
2633
|
+
if (!v.ok) throw new w("NETWORK_ERROR", "Failed to check design status");
|
|
2634
|
+
f = await v.json();
|
|
2509
2635
|
}
|
|
2510
|
-
if (
|
|
2511
|
-
return e.onRenderComplete && e.onRenderComplete(
|
|
2636
|
+
if (f.allCompleted)
|
|
2637
|
+
return e.onRenderComplete && e.onRenderComplete(f), f;
|
|
2512
2638
|
}
|
|
2513
|
-
const
|
|
2514
|
-
designs: a.map((
|
|
2515
|
-
designId:
|
|
2639
|
+
const m = {
|
|
2640
|
+
designs: a.map((f) => ({
|
|
2641
|
+
designId: f,
|
|
2516
2642
|
status: "failed",
|
|
2517
2643
|
proofUrl: null,
|
|
2518
2644
|
errorMessage: "Render timed out"
|
|
2519
2645
|
})),
|
|
2520
2646
|
allCompleted: !0
|
|
2521
2647
|
};
|
|
2522
|
-
return e.onRenderComplete && e.onRenderComplete(
|
|
2648
|
+
return e.onRenderComplete && e.onRenderComplete(m), m;
|
|
2523
2649
|
},
|
|
2524
2650
|
addTextLayer(a) {
|
|
2525
|
-
if (typeof i.addTextLayer != "function")
|
|
2526
|
-
throw new
|
|
2651
|
+
if (a !== void 0 && (a = ce(a)), typeof i.addTextLayer != "function")
|
|
2652
|
+
throw new w("NOT_READY", "Editor not ready: addTextLayer method not available");
|
|
2527
2653
|
i.addTextLayer(a);
|
|
2528
2654
|
},
|
|
2529
2655
|
async addImageLayer(a) {
|
|
2530
|
-
if (typeof i.addImageLayer != "function")
|
|
2531
|
-
throw new
|
|
2656
|
+
if (ue(a), typeof i.addImageLayer != "function")
|
|
2657
|
+
throw new w("NOT_READY", "Editor not ready: addImageLayer method not available");
|
|
2532
2658
|
return i.addImageLayer(a);
|
|
2533
2659
|
},
|
|
2534
2660
|
removeLayer(a) {
|
|
2535
|
-
if (typeof i.removeLayer != "function")
|
|
2536
|
-
throw new
|
|
2661
|
+
if (z(a, "layerId"), typeof i.removeLayer != "function")
|
|
2662
|
+
throw new w("NOT_READY", "Editor not ready: removeLayer method not available");
|
|
2537
2663
|
i.removeLayer(a);
|
|
2538
2664
|
},
|
|
2539
2665
|
selectLayer(a) {
|
|
2540
|
-
if (typeof i.selectLayer != "function")
|
|
2541
|
-
throw new
|
|
2666
|
+
if (a !== null && z(a, "layerId"), typeof i.selectLayer != "function")
|
|
2667
|
+
throw new w("NOT_READY", "Editor not ready: selectLayer method not available");
|
|
2542
2668
|
i.selectLayer(a);
|
|
2543
2669
|
},
|
|
2544
2670
|
getSelectedLayerId() {
|
|
@@ -2551,8 +2677,8 @@ function te(f, e) {
|
|
|
2551
2677
|
return typeof i.getViews != "function" ? [] : i.getViews();
|
|
2552
2678
|
},
|
|
2553
2679
|
setActiveView(a) {
|
|
2554
|
-
if (typeof i.setActiveView != "function")
|
|
2555
|
-
throw new
|
|
2680
|
+
if (z(a, "viewName"), typeof i.setActiveView != "function")
|
|
2681
|
+
throw new w("NOT_READY", "Editor not ready: setActiveView method not available");
|
|
2556
2682
|
i.setActiveView(a);
|
|
2557
2683
|
},
|
|
2558
2684
|
setTheme(a) {
|
|
@@ -2584,8 +2710,12 @@ function te(f, e) {
|
|
|
2584
2710
|
})), e.onLayerUpdate && n("layer:update", ((a) => {
|
|
2585
2711
|
e.onLayerUpdate?.(a.detail.layerId);
|
|
2586
2712
|
})), e.onError && n("error", ((a) => {
|
|
2587
|
-
const r = a.detail
|
|
2588
|
-
|
|
2713
|
+
const r = a.detail, l = r instanceof w ? r : new w(
|
|
2714
|
+
"UNKNOWN_ERROR",
|
|
2715
|
+
typeof r == "string" ? r : r?.message ?? r?.error?.message ?? "Unknown error",
|
|
2716
|
+
r instanceof Error ? r : r?.error
|
|
2717
|
+
);
|
|
2718
|
+
e.onError?.(l);
|
|
2589
2719
|
})), e.onViewChange && n("view-change", ((a) => {
|
|
2590
2720
|
e.onViewChange?.(a.detail.viewName);
|
|
2591
2721
|
})), e.onRenderComplete && n("customizer:render-complete", ((a) => {
|
|
@@ -2603,6 +2733,7 @@ function te(f, e) {
|
|
|
2603
2733
|
), e.debug && (console.log("[Customizer SDK] Initialized with options:", e), console.log("[Customizer SDK] Instance:", o)), o;
|
|
2604
2734
|
}
|
|
2605
2735
|
export {
|
|
2606
|
-
|
|
2736
|
+
w as CustomizerError,
|
|
2737
|
+
me as initCustomizer
|
|
2607
2738
|
};
|
|
2608
2739
|
//# sourceMappingURL=index.esm.js.map
|