@primestyleai/tryon 5.10.193 → 5.10.195
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PrimeStyleTryon.d.ts +5 -0
- package/dist/api-client.d.ts +30 -1
- package/dist/{index-D9sdGV4C.js → index-D_9-KLXy.js} +198 -142
- package/dist/index-D_9-KLXy.js.map +1 -0
- package/dist/primestyle-tryon.js +232 -164
- package/dist/primestyle-tryon.js.map +1 -1
- package/dist/react/index.js +6010 -5609
- package/dist/react/index.js.map +1 -1
- package/dist/react/recommendForProduct.d.ts +4 -4
- package/dist/react/styles.d.ts +1 -1
- package/dist/react/usePrimeStyleSize.d.ts +2 -2
- package/dist/react/views/SizeResultView.d.ts +2 -1
- package/dist/storefront/primestyle-tryon.js +683 -201
- package/package.json +1 -1
- package/dist/index-D9sdGV4C.js.map +0 -1
package/dist/primestyle-tryon.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { c as l, A as c,
|
|
3
|
-
import { P as x, b as w, T as C, d as
|
|
2
|
+
import { c as l, A as c, i as h, a as u, S as m } from "./index-D_9-KLXy.js";
|
|
3
|
+
import { P as x, b as w, T as C, d as S, e as E, r as U } from "./index-D_9-KLXy.js";
|
|
4
4
|
function d() {
|
|
5
5
|
const i = document.querySelector(
|
|
6
6
|
'meta[property="og:image"]'
|
|
7
7
|
);
|
|
8
8
|
if (i?.content) return i.content;
|
|
9
|
-
const
|
|
9
|
+
const e = document.querySelectorAll(
|
|
10
10
|
'script[type="application/ld+json"]'
|
|
11
11
|
);
|
|
12
|
-
for (const
|
|
12
|
+
for (const r of e)
|
|
13
13
|
try {
|
|
14
|
-
const
|
|
14
|
+
const s = JSON.parse(r.textContent || ""), o = p(s);
|
|
15
15
|
if (o) return o;
|
|
16
16
|
} catch {
|
|
17
17
|
}
|
|
18
|
-
const
|
|
18
|
+
const t = [
|
|
19
19
|
"[data-product-image] img",
|
|
20
20
|
"[data-product-image]",
|
|
21
21
|
".product-image img",
|
|
@@ -26,10 +26,10 @@ function d() {
|
|
|
26
26
|
".woocommerce-product-gallery img",
|
|
27
27
|
".product-media img"
|
|
28
28
|
];
|
|
29
|
-
for (const
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
-
const o =
|
|
29
|
+
for (const r of t) {
|
|
30
|
+
const s = document.querySelector(r);
|
|
31
|
+
if (s) {
|
|
32
|
+
const o = s.src || s.dataset.src || s.dataset.zoom;
|
|
33
33
|
if (o) return o;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -38,24 +38,24 @@ function d() {
|
|
|
38
38
|
function p(i) {
|
|
39
39
|
if (!i || typeof i != "object") return null;
|
|
40
40
|
if (Array.isArray(i)) {
|
|
41
|
-
for (const
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
41
|
+
for (const t of i) {
|
|
42
|
+
const r = p(t);
|
|
43
|
+
if (r) return r;
|
|
44
44
|
}
|
|
45
45
|
return null;
|
|
46
46
|
}
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
const
|
|
50
|
-
if (typeof
|
|
51
|
-
if (Array.isArray(
|
|
52
|
-
if (
|
|
53
|
-
const
|
|
54
|
-
if (typeof
|
|
55
|
-
if (typeof
|
|
47
|
+
const e = i;
|
|
48
|
+
if (e["@type"] === "Product" || e["@type"] === "IndividualProduct") {
|
|
49
|
+
const t = e.image;
|
|
50
|
+
if (typeof t == "string") return t;
|
|
51
|
+
if (Array.isArray(t) && typeof t[0] == "string") return t[0];
|
|
52
|
+
if (t && typeof t == "object") {
|
|
53
|
+
const r = t;
|
|
54
|
+
if (typeof r.url == "string") return r.url;
|
|
55
|
+
if (typeof r.contentUrl == "string") return r.contentUrl;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
return Array.isArray(
|
|
58
|
+
return Array.isArray(e["@graph"]) ? p(e["@graph"]) : null;
|
|
59
59
|
}
|
|
60
60
|
function g() {
|
|
61
61
|
return `
|
|
@@ -442,7 +442,7 @@ const n = {
|
|
|
442
442
|
};
|
|
443
443
|
class b extends HTMLElement {
|
|
444
444
|
constructor() {
|
|
445
|
-
super(), this.apiClient = null, this.sseClient = null, this.sseUnsubscribe = null, this.state = "idle", this.selectedFile = null, this.previewUrl = null, this.resultImageUrl = null, this.errorMessage = null, this.currentJobId = null, this.productImageUrl = null, this.t = l(), this.buttonStyles = {}, this.modalStyles = {}, this.savedScrollY = 0, this.shadow = this.attachShadow({ mode: "open" });
|
|
445
|
+
super(), this.apiClient = null, this.sseClient = null, this.sseUnsubscribe = null, this.state = "idle", this.selectedFile = null, this.previewUrl = null, this.resultImageUrl = null, this.errorMessage = null, this.currentJobId = null, this.productImageUrl = null, this.productViewReportedFor = null, this.t = l(), this.buttonStyles = {}, this.modalStyles = {}, this.savedScrollY = 0, this.shadow = this.attachShadow({ mode: "open" });
|
|
446
446
|
}
|
|
447
447
|
static get observedAttributes() {
|
|
448
448
|
return [
|
|
@@ -462,27 +462,27 @@ class b extends HTMLElement {
|
|
|
462
462
|
disconnectedCallback() {
|
|
463
463
|
this.cleanup();
|
|
464
464
|
}
|
|
465
|
-
attributeChangedCallback(
|
|
466
|
-
if ((
|
|
465
|
+
attributeChangedCallback(e, t, r) {
|
|
466
|
+
if ((e === "api-key" || e === "api-url") && this.initApi(), e === "locale" && (this.t = l(r || void 0)), e === "product-image" && (this.productImageUrl = r), e === "button-styles")
|
|
467
467
|
try {
|
|
468
|
-
this.buttonStyles = JSON.parse(
|
|
468
|
+
this.buttonStyles = JSON.parse(r);
|
|
469
469
|
} catch {
|
|
470
470
|
}
|
|
471
|
-
if (
|
|
471
|
+
if (e === "modal-styles")
|
|
472
472
|
try {
|
|
473
|
-
this.modalStyles = JSON.parse(
|
|
473
|
+
this.modalStyles = JSON.parse(r);
|
|
474
474
|
} catch {
|
|
475
475
|
}
|
|
476
476
|
this.isConnected && this.render();
|
|
477
477
|
}
|
|
478
478
|
// ── Public API ──────────────────────────────
|
|
479
479
|
/** Configure button appearance programmatically */
|
|
480
|
-
setButtonStyles(
|
|
481
|
-
this.buttonStyles = { ...this.buttonStyles, ...
|
|
480
|
+
setButtonStyles(e) {
|
|
481
|
+
this.buttonStyles = { ...this.buttonStyles, ...e }, this.applyButtonStyles();
|
|
482
482
|
}
|
|
483
483
|
/** Configure modal appearance programmatically */
|
|
484
|
-
setModalStyles(
|
|
485
|
-
this.modalStyles = { ...this.modalStyles, ...
|
|
484
|
+
setModalStyles(e) {
|
|
485
|
+
this.modalStyles = { ...this.modalStyles, ...e }, this.applyModalStyles();
|
|
486
486
|
}
|
|
487
487
|
lockBodyScroll() {
|
|
488
488
|
this.savedScrollY = window.scrollY, document.body.style.overflow = "hidden", document.body.style.position = "fixed", document.body.style.top = `-${this.savedScrollY}px`, document.body.style.left = "0", document.body.style.right = "0";
|
|
@@ -492,7 +492,7 @@ class b extends HTMLElement {
|
|
|
492
492
|
}
|
|
493
493
|
/** Open the try-on modal */
|
|
494
494
|
open() {
|
|
495
|
-
this.state = "upload", this.lockBodyScroll(), this.render(), this.emit("ps:open");
|
|
495
|
+
this.reportEvent("SDK_OPENED", { metadata: { component: "PrimeStyleTryonElement" } }), this.state = "upload", this.lockBodyScroll(), this.render(), this.emit("ps:open");
|
|
496
496
|
}
|
|
497
497
|
/** Close the try-on modal */
|
|
498
498
|
close() {
|
|
@@ -500,38 +500,64 @@ class b extends HTMLElement {
|
|
|
500
500
|
}
|
|
501
501
|
/** Detect product image from the current page */
|
|
502
502
|
detectProduct() {
|
|
503
|
-
const
|
|
504
|
-
return
|
|
503
|
+
const e = d();
|
|
504
|
+
return e && (this.productImageUrl = e, this.emit("ps:product-detected", { imageUrl: e })), e;
|
|
505
505
|
}
|
|
506
506
|
// ── Private ─────────────────────────────────
|
|
507
507
|
init() {
|
|
508
|
-
const
|
|
509
|
-
if (
|
|
508
|
+
const e = this.getAttribute("button-styles");
|
|
509
|
+
if (e)
|
|
510
510
|
try {
|
|
511
|
-
this.buttonStyles = JSON.parse(
|
|
511
|
+
this.buttonStyles = JSON.parse(e);
|
|
512
512
|
} catch {
|
|
513
513
|
}
|
|
514
|
-
const
|
|
515
|
-
if (
|
|
514
|
+
const t = this.getAttribute("modal-styles");
|
|
515
|
+
if (t)
|
|
516
516
|
try {
|
|
517
|
-
this.modalStyles = JSON.parse(
|
|
517
|
+
this.modalStyles = JSON.parse(t);
|
|
518
518
|
} catch {
|
|
519
519
|
}
|
|
520
|
-
const
|
|
521
|
-
|
|
520
|
+
const r = this.getAttribute("locale");
|
|
521
|
+
r && (this.t = l(r)), this.productImageUrl = this.getAttribute("product-image") || null, this.productImageUrl || (this.productImageUrl = d(), this.productImageUrl && this.emit("ps:product-detected", { imageUrl: this.productImageUrl })), this.initApi(), this.reportProductView();
|
|
522
522
|
}
|
|
523
523
|
initApi() {
|
|
524
|
-
const
|
|
525
|
-
this.apiClient = new c(
|
|
524
|
+
const e = this.getAttribute("api-key") || void 0, t = this.getAttribute("api-url") || void 0;
|
|
525
|
+
this.apiClient = new c(e, t), this.sseClient = null;
|
|
526
526
|
}
|
|
527
527
|
cleanup() {
|
|
528
528
|
this.state !== "idle" && this.unlockBodyScroll(), this.sseUnsubscribe && (this.sseUnsubscribe(), this.sseUnsubscribe = null), this.sseClient && (this.sseClient.disconnect(), this.sseClient = null), this.previewUrl && URL.revokeObjectURL(this.previewUrl);
|
|
529
529
|
}
|
|
530
|
-
emit(
|
|
530
|
+
emit(e, t) {
|
|
531
531
|
this.dispatchEvent(
|
|
532
|
-
new CustomEvent(
|
|
532
|
+
new CustomEvent(e, { bubbles: !0, composed: !0, detail: t })
|
|
533
533
|
);
|
|
534
534
|
}
|
|
535
|
+
get eventProductContext() {
|
|
536
|
+
return {
|
|
537
|
+
productId: this.getAttribute("product-id") || this.productImageUrl || void 0,
|
|
538
|
+
productTitle: this.getAttribute("product-title") || document.title || void 0,
|
|
539
|
+
productUrl: typeof window < "u" ? window.location.href : void 0
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
reportProductView() {
|
|
543
|
+
const e = `${this.eventProductContext.productId || ""}|${this.eventProductContext.productUrl || ""}`;
|
|
544
|
+
!e.trim() || this.productViewReportedFor === e || (this.productViewReportedFor = e, this.reportEvent("PRODUCT_VIEW", { metadata: { component: "PrimeStyleTryonElement" } }));
|
|
545
|
+
}
|
|
546
|
+
reportEvent(e, t = {}) {
|
|
547
|
+
this.apiClient?.reportEvent({
|
|
548
|
+
eventType: e,
|
|
549
|
+
...this.eventProductContext,
|
|
550
|
+
...t
|
|
551
|
+
}).catch(() => {
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
reportClientError(e) {
|
|
555
|
+
this.apiClient?.reportClientError({
|
|
556
|
+
...this.eventProductContext,
|
|
557
|
+
...e
|
|
558
|
+
}).catch(() => {
|
|
559
|
+
});
|
|
560
|
+
}
|
|
535
561
|
get buttonText() {
|
|
536
562
|
return this.getAttribute("button-text") || this.t("Virtual Try-On");
|
|
537
563
|
}
|
|
@@ -541,32 +567,32 @@ class b extends HTMLElement {
|
|
|
541
567
|
// ── Rendering ───────────────────────────────
|
|
542
568
|
render() {
|
|
543
569
|
this.shadow.innerHTML = "";
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
const
|
|
547
|
-
if (this.shadow.appendChild(
|
|
548
|
-
const
|
|
549
|
-
this.shadow.appendChild(
|
|
570
|
+
const e = document.createElement("style");
|
|
571
|
+
e.textContent = g(), this.shadow.appendChild(e);
|
|
572
|
+
const t = this.createButton();
|
|
573
|
+
if (this.shadow.appendChild(t), this.state !== "idle") {
|
|
574
|
+
const r = this.createModal();
|
|
575
|
+
this.shadow.appendChild(r), requestAnimationFrame(() => r.classList.add("ps-open"));
|
|
550
576
|
}
|
|
551
577
|
this.applyButtonStyles(), this.applyModalStyles();
|
|
552
578
|
}
|
|
553
579
|
createButton() {
|
|
554
|
-
const
|
|
555
|
-
return
|
|
580
|
+
const e = document.createElement("button");
|
|
581
|
+
return e.className = "ps-button", e.innerHTML = `${n.camera}<span>${this.buttonText}</span>`, e.addEventListener("click", () => this.open()), e;
|
|
556
582
|
}
|
|
557
583
|
createModal() {
|
|
558
|
-
const t = document.createElement("div");
|
|
559
|
-
t.className = "ps-overlay", t.addEventListener("click", (a) => {
|
|
560
|
-
a.target === t && this.close();
|
|
561
|
-
});
|
|
562
584
|
const e = document.createElement("div");
|
|
563
|
-
e.className = "ps-
|
|
564
|
-
|
|
565
|
-
|
|
585
|
+
e.className = "ps-overlay", e.addEventListener("click", (a) => {
|
|
586
|
+
a.target === e && this.close();
|
|
587
|
+
});
|
|
588
|
+
const t = document.createElement("div");
|
|
589
|
+
t.className = "ps-modal";
|
|
590
|
+
const r = document.createElement("div");
|
|
591
|
+
r.className = "ps-header", r.innerHTML = `
|
|
566
592
|
<span class="ps-header-title">${this.t("Virtual Try-On")}</span>
|
|
567
593
|
`;
|
|
568
|
-
const
|
|
569
|
-
|
|
594
|
+
const s = document.createElement("button");
|
|
595
|
+
s.className = "ps-close", s.innerHTML = n.x, s.addEventListener("click", () => this.close()), r.appendChild(s), t.appendChild(r);
|
|
570
596
|
const o = document.createElement("div");
|
|
571
597
|
switch (o.className = "ps-body", this.state) {
|
|
572
598
|
case "upload":
|
|
@@ -582,160 +608,202 @@ class b extends HTMLElement {
|
|
|
582
608
|
o.appendChild(this.createErrorView());
|
|
583
609
|
break;
|
|
584
610
|
}
|
|
585
|
-
if (
|
|
611
|
+
if (t.appendChild(o), this.showPoweredBy) {
|
|
586
612
|
const a = document.createElement("div");
|
|
587
|
-
a.className = "ps-powered", a.innerHTML = `${this.t("Powered by")} <a href="https://myaifitting.com" target="_blank" rel="noopener">PrimeStyle AI</a>`,
|
|
613
|
+
a.className = "ps-powered", a.innerHTML = `${this.t("Powered by")} <a href="https://myaifitting.com" target="_blank" rel="noopener">PrimeStyle AI</a>`, t.appendChild(a);
|
|
588
614
|
}
|
|
589
|
-
return
|
|
615
|
+
return e.appendChild(t), e;
|
|
590
616
|
}
|
|
591
617
|
createUploadView() {
|
|
592
|
-
const
|
|
618
|
+
const e = document.createDocumentFragment();
|
|
593
619
|
if (this.selectedFile && this.previewUrl) {
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
const
|
|
599
|
-
|
|
620
|
+
const t = document.createElement("div");
|
|
621
|
+
t.className = "ps-preview";
|
|
622
|
+
const r = document.createElement("img");
|
|
623
|
+
r.src = this.previewUrl, r.alt = this.t("Your photo"), t.appendChild(r);
|
|
624
|
+
const s = document.createElement("button");
|
|
625
|
+
s.className = "ps-preview-remove", s.textContent = "×", s.addEventListener("click", () => {
|
|
600
626
|
this.resetUpload(), this.render();
|
|
601
|
-
}),
|
|
627
|
+
}), t.appendChild(s), e.appendChild(t);
|
|
602
628
|
const o = document.createElement("button");
|
|
603
|
-
o.className = "ps-submit", o.textContent = this.t("Try It On"), o.addEventListener("click", () => this.handleSubmit()),
|
|
629
|
+
o.className = "ps-submit", o.textContent = this.t("Try It On"), o.addEventListener("click", () => this.handleSubmit()), e.appendChild(o);
|
|
604
630
|
} else {
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
const o =
|
|
631
|
+
const t = document.createElement("div");
|
|
632
|
+
t.className = "ps-upload-zone";
|
|
633
|
+
const r = document.createElement("input");
|
|
634
|
+
r.type = "file", r.accept = "image/jpeg,image/png,image/webp", r.addEventListener("change", (s) => {
|
|
635
|
+
const o = s.target.files?.[0];
|
|
610
636
|
o && this.handleFileSelect(o);
|
|
611
|
-
}),
|
|
637
|
+
}), t.innerHTML = `
|
|
612
638
|
<svg class="ps-upload-icon" viewBox="0 0 24 24">${n.upload.replace(/<\/?svg[^>]*>/g, "")}</svg>
|
|
613
639
|
<p class="ps-upload-text">${this.t("Drop your photo here or click to upload")}</p>
|
|
614
640
|
<p class="ps-upload-hint">${this.t("JPEG, PNG or WebP (max 10MB)")}</p>
|
|
615
|
-
`,
|
|
616
|
-
|
|
617
|
-
}),
|
|
618
|
-
|
|
619
|
-
}),
|
|
620
|
-
|
|
621
|
-
const o =
|
|
641
|
+
`, t.appendChild(r), t.addEventListener("click", () => r.click()), t.addEventListener("dragover", (s) => {
|
|
642
|
+
s.preventDefault(), t.classList.add("ps-drag-over");
|
|
643
|
+
}), t.addEventListener("dragleave", () => {
|
|
644
|
+
t.classList.remove("ps-drag-over");
|
|
645
|
+
}), t.addEventListener("drop", (s) => {
|
|
646
|
+
s.preventDefault(), t.classList.remove("ps-drag-over");
|
|
647
|
+
const o = s.dataTransfer?.files?.[0];
|
|
622
648
|
o && this.handleFileSelect(o);
|
|
623
|
-
}),
|
|
649
|
+
}), e.appendChild(t);
|
|
624
650
|
}
|
|
625
|
-
return
|
|
651
|
+
return e;
|
|
626
652
|
}
|
|
627
653
|
createProcessingView() {
|
|
628
|
-
const
|
|
629
|
-
return
|
|
654
|
+
const e = document.createElement("div");
|
|
655
|
+
return e.className = "ps-processing", e.innerHTML = `
|
|
630
656
|
<div class="ps-spinner"></div>
|
|
631
657
|
<p class="ps-processing-text">${this.t("Generating virtual try-on...")}</p>
|
|
632
658
|
<p class="ps-processing-sub">${this.t("This usually takes 15-20 seconds")}</p>
|
|
633
|
-
`,
|
|
659
|
+
`, e;
|
|
634
660
|
}
|
|
635
661
|
createResultView() {
|
|
636
|
-
const
|
|
637
|
-
if (
|
|
662
|
+
const e = document.createElement("div");
|
|
663
|
+
if (e.className = "ps-result", this.resultImageUrl) {
|
|
638
664
|
const o = document.createElement("img");
|
|
639
|
-
o.src = this.resultImageUrl, o.alt = this.t("Try-on result"),
|
|
665
|
+
o.src = this.resultImageUrl, o.alt = this.t("Try-on result"), e.appendChild(o);
|
|
640
666
|
}
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
const s = document.createElement("button");
|
|
644
|
-
s.className = "ps-btn-download", s.textContent = this.t("Download"), s.addEventListener("click", () => this.handleDownload()), e.appendChild(s);
|
|
667
|
+
const t = document.createElement("div");
|
|
668
|
+
t.className = "ps-result-actions";
|
|
645
669
|
const r = document.createElement("button");
|
|
646
|
-
|
|
670
|
+
r.className = "ps-btn-download", r.textContent = this.t("Download"), r.addEventListener("click", () => this.handleDownload()), t.appendChild(r);
|
|
671
|
+
const s = document.createElement("button");
|
|
672
|
+
return s.className = "ps-btn-retry", s.textContent = this.t("Try Another"), s.addEventListener("click", () => {
|
|
647
673
|
this.resetUpload(), this.state = "upload", this.render();
|
|
648
|
-
}),
|
|
674
|
+
}), t.appendChild(s), e.appendChild(t), e;
|
|
649
675
|
}
|
|
650
676
|
createErrorView() {
|
|
651
|
-
const
|
|
652
|
-
|
|
677
|
+
const e = document.createElement("div");
|
|
678
|
+
e.className = "ps-error", e.innerHTML = `
|
|
653
679
|
<svg class="ps-error-icon" viewBox="0 0 24 24">${n.alert.replace(/<\/?svg[^>]*>/g, "")}</svg>
|
|
654
680
|
<p class="ps-error-text">${this.errorMessage || this.t("Something went wrong")}</p>
|
|
655
681
|
`;
|
|
656
|
-
const
|
|
657
|
-
return
|
|
682
|
+
const t = document.createElement("button");
|
|
683
|
+
return t.className = "ps-submit", t.textContent = this.t("Try Again"), t.addEventListener("click", () => {
|
|
658
684
|
this.state = "upload", this.errorMessage = null, this.render();
|
|
659
|
-
}),
|
|
685
|
+
}), e.appendChild(t), e;
|
|
660
686
|
}
|
|
661
687
|
// ── Handlers ────────────────────────────────
|
|
662
|
-
handleFileSelect(
|
|
663
|
-
if (!
|
|
664
|
-
this.errorMessage = this.t("Please upload a JPEG, PNG, or WebP image."), this.state = "error", this.
|
|
688
|
+
handleFileSelect(e) {
|
|
689
|
+
if (!h(e)) {
|
|
690
|
+
this.errorMessage = this.t("Please upload a JPEG, PNG, or WebP image."), this.state = "error", this.reportClientError({
|
|
691
|
+
message: "Invalid image file type",
|
|
692
|
+
code: "INVALID_FILE",
|
|
693
|
+
component: "PrimeStyleTryonElement",
|
|
694
|
+
view: "upload",
|
|
695
|
+
severity: "low",
|
|
696
|
+
metadata: { fileType: e.type, fileSize: e.size }
|
|
697
|
+
}), this.render();
|
|
665
698
|
return;
|
|
666
699
|
}
|
|
667
|
-
if (
|
|
668
|
-
this.errorMessage = this.t("Image must be under 10MB."), this.state = "error", this.
|
|
700
|
+
if (e.size > 10 * 1024 * 1024) {
|
|
701
|
+
this.errorMessage = this.t("Image must be under 10MB."), this.state = "error", this.reportClientError({
|
|
702
|
+
message: "Image file too large",
|
|
703
|
+
code: "FILE_TOO_LARGE",
|
|
704
|
+
component: "PrimeStyleTryonElement",
|
|
705
|
+
view: "upload",
|
|
706
|
+
severity: "low",
|
|
707
|
+
metadata: { fileType: e.type, fileSize: e.size }
|
|
708
|
+
}), this.render();
|
|
669
709
|
return;
|
|
670
710
|
}
|
|
671
|
-
this.selectedFile =
|
|
711
|
+
this.selectedFile = e, this.previewUrl = URL.createObjectURL(e), this.reportEvent("PHOTO_UPLOADED", {
|
|
712
|
+
metadata: { fileType: e.type, fileSize: e.size, component: "PrimeStyleTryonElement" }
|
|
713
|
+
}), this.emit("ps:upload", { file: e }), this.render();
|
|
672
714
|
}
|
|
673
715
|
async handleSubmit() {
|
|
674
|
-
if (!this.selectedFile || !this.apiClient
|
|
675
|
-
this.errorMessage = this.t("SDK not configured. Please provide an API key."), this.state = "error", this.
|
|
716
|
+
if (!this.selectedFile || !this.apiClient) {
|
|
717
|
+
this.errorMessage = this.t("SDK not configured. Please provide an API key."), this.state = "error", this.reportClientError({
|
|
718
|
+
message: this.errorMessage,
|
|
719
|
+
code: this.apiClient ? "PHOTO_MISSING" : "SDK_NOT_CONFIGURED",
|
|
720
|
+
component: "PrimeStyleTryonElement",
|
|
721
|
+
view: "upload",
|
|
722
|
+
severity: this.apiClient ? "low" : "high"
|
|
723
|
+
}), this.render();
|
|
676
724
|
return;
|
|
677
725
|
}
|
|
678
726
|
if (!this.productImageUrl) {
|
|
679
|
-
this.errorMessage = this.t("No product image found. Please set the product-image attribute."), this.state = "error", this.
|
|
727
|
+
this.errorMessage = this.t("No product image found. Please set the product-image attribute."), this.state = "error", this.reportClientError({
|
|
728
|
+
message: this.errorMessage,
|
|
729
|
+
code: "PRODUCT_IMAGE_MISSING",
|
|
730
|
+
component: "PrimeStyleTryonElement",
|
|
731
|
+
view: "upload",
|
|
732
|
+
severity: "medium"
|
|
733
|
+
}), this.render();
|
|
680
734
|
return;
|
|
681
735
|
}
|
|
682
736
|
this.state = "processing", this.render();
|
|
683
737
|
try {
|
|
684
|
-
const
|
|
685
|
-
|
|
738
|
+
const e = await u(this.selectedFile), t = await this.apiClient.submitTryOn(
|
|
739
|
+
e,
|
|
686
740
|
this.productImageUrl
|
|
687
741
|
);
|
|
688
|
-
this.currentJobId =
|
|
689
|
-
|
|
690
|
-
(
|
|
691
|
-
), this.startPolling(
|
|
692
|
-
} catch (
|
|
693
|
-
const
|
|
694
|
-
this.errorMessage =
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
742
|
+
this.currentJobId = t.jobId, this.emit("ps:processing", { jobId: t.jobId }), this.sseClient?.disconnect(), this.sseClient = new m(t.streamUrl || this.apiClient.getStreamUrl(t.jobId)), this.sseUnsubscribe = this.sseClient.onJob(
|
|
743
|
+
t.jobId,
|
|
744
|
+
(r) => this.handleVtoUpdate(r)
|
|
745
|
+
), this.startPolling(t.jobId);
|
|
746
|
+
} catch (e) {
|
|
747
|
+
const t = e instanceof Error ? e.message : this.t("Failed to start try-on");
|
|
748
|
+
this.errorMessage = t, this.state = "error", this.reportClientError({
|
|
749
|
+
message: t,
|
|
750
|
+
code: e?.code || "TRYON_SUBMIT_FAILED",
|
|
751
|
+
stack: e instanceof Error ? e.stack : void 0,
|
|
752
|
+
component: "PrimeStyleTryonElement",
|
|
753
|
+
view: "processing",
|
|
754
|
+
severity: "medium"
|
|
755
|
+
}), this.emit("ps:error", { message: t, code: e?.code }), this.render();
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
handleVtoUpdate(e) {
|
|
759
|
+
e.status === "completed" && e.imageUrl ? (this.state !== "result" || this.resultImageUrl?.startsWith("data:") && !e.imageUrl.startsWith("data:")) && (this.resultImageUrl = e.imageUrl, this.state = "result", this.emit("ps:complete", {
|
|
760
|
+
jobId: e.galleryId,
|
|
761
|
+
imageUrl: e.imageUrl
|
|
762
|
+
}), this.render()) : e.status === "failed" && (this.errorMessage = e.error || this.t("Try-on generation failed"), this.state = "error", this.reportClientError({
|
|
763
|
+
message: this.errorMessage,
|
|
764
|
+
code: "TRYON_GENERATION_FAILED",
|
|
765
|
+
component: "PrimeStyleTryonElement",
|
|
766
|
+
view: "processing",
|
|
767
|
+
severity: "medium",
|
|
768
|
+
jobId: e.galleryId
|
|
769
|
+
}), this.emit("ps:error", { message: this.errorMessage }), this.render());
|
|
770
|
+
}
|
|
771
|
+
startPolling(e) {
|
|
772
|
+
let t = 0;
|
|
773
|
+
const r = 60, s = setInterval(async () => {
|
|
774
|
+
if (t++, t > r || this.state === "result" || this.state === "idle") {
|
|
775
|
+
clearInterval(s);
|
|
708
776
|
return;
|
|
709
777
|
}
|
|
710
778
|
if (!this.apiClient) {
|
|
711
|
-
clearInterval(
|
|
779
|
+
clearInterval(s);
|
|
712
780
|
return;
|
|
713
781
|
}
|
|
714
782
|
try {
|
|
715
|
-
const o = await this.apiClient.getStatus(
|
|
783
|
+
const o = await this.apiClient.getStatus(e);
|
|
716
784
|
o.status === "completed" && o.imageUrl ? (this.state === "processing" && this.handleVtoUpdate({
|
|
717
|
-
galleryId:
|
|
785
|
+
galleryId: e,
|
|
718
786
|
status: "completed",
|
|
719
787
|
imageUrl: o.imageUrl,
|
|
720
788
|
error: null,
|
|
721
789
|
timestamp: Date.now()
|
|
722
|
-
}), clearInterval(
|
|
723
|
-
galleryId:
|
|
790
|
+
}), clearInterval(s)) : o.status === "failed" && (this.state === "processing" && this.handleVtoUpdate({
|
|
791
|
+
galleryId: e,
|
|
724
792
|
status: "failed",
|
|
725
793
|
imageUrl: null,
|
|
726
794
|
error: o.message,
|
|
727
795
|
timestamp: Date.now()
|
|
728
|
-
}), clearInterval(
|
|
796
|
+
}), clearInterval(s));
|
|
729
797
|
} catch {
|
|
730
798
|
}
|
|
731
799
|
}, 2e3);
|
|
732
800
|
}
|
|
733
801
|
handleDownload() {
|
|
734
802
|
if (!this.resultImageUrl) return;
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
const
|
|
738
|
-
|
|
803
|
+
const e = document.createElement("a");
|
|
804
|
+
e.href = this.resultImageUrl, e.download = `primestyle-tryon-${Date.now()}.png`, e.target = "_blank", this.resultImageUrl.startsWith("data:") ? e.click() : fetch(this.resultImageUrl).then((t) => t.blob()).then((t) => {
|
|
805
|
+
const r = URL.createObjectURL(t);
|
|
806
|
+
e.href = r, e.click(), setTimeout(() => URL.revokeObjectURL(r), 100);
|
|
739
807
|
}).catch(() => {
|
|
740
808
|
window.open(this.resultImageUrl, "_blank");
|
|
741
809
|
});
|
|
@@ -746,7 +814,7 @@ class b extends HTMLElement {
|
|
|
746
814
|
// ── Custom Style Application ────────────────
|
|
747
815
|
applyButtonStyles() {
|
|
748
816
|
if (!this.shadow.querySelector(".ps-button")) return;
|
|
749
|
-
const
|
|
817
|
+
const t = {
|
|
750
818
|
backgroundColor: "--ps-btn-bg",
|
|
751
819
|
textColor: "--ps-btn-color",
|
|
752
820
|
borderRadius: "--ps-btn-radius",
|
|
@@ -770,13 +838,13 @@ class b extends HTMLElement {
|
|
|
770
838
|
iconColor: "--ps-btn-icon-color",
|
|
771
839
|
boxShadow: "--ps-btn-shadow"
|
|
772
840
|
};
|
|
773
|
-
for (const [
|
|
774
|
-
const o = this.buttonStyles[
|
|
775
|
-
o && this.style.setProperty(
|
|
841
|
+
for (const [r, s] of Object.entries(t)) {
|
|
842
|
+
const o = this.buttonStyles[r];
|
|
843
|
+
o && this.style.setProperty(s, o);
|
|
776
844
|
}
|
|
777
845
|
}
|
|
778
846
|
applyModalStyles() {
|
|
779
|
-
const
|
|
847
|
+
const e = {
|
|
780
848
|
overlayColor: "--ps-modal-overlay",
|
|
781
849
|
backgroundColor: "--ps-modal-bg",
|
|
782
850
|
textColor: "--ps-modal-color",
|
|
@@ -808,9 +876,9 @@ class b extends HTMLElement {
|
|
|
808
876
|
successColor: "--ps-success-color",
|
|
809
877
|
logoHeight: "--ps-logo-height"
|
|
810
878
|
};
|
|
811
|
-
for (const [
|
|
812
|
-
const
|
|
813
|
-
|
|
879
|
+
for (const [t, r] of Object.entries(e)) {
|
|
880
|
+
const s = this.modalStyles[t];
|
|
881
|
+
s && this.style.setProperty(r, s);
|
|
814
882
|
}
|
|
815
883
|
}
|
|
816
884
|
}
|
|
@@ -820,14 +888,14 @@ export {
|
|
|
820
888
|
x as PrimeStyleError,
|
|
821
889
|
b as PrimeStyleTryon,
|
|
822
890
|
w as SUPPORTED_LOCALES,
|
|
823
|
-
|
|
891
|
+
m as SseClient,
|
|
824
892
|
C as TRANSLATION_KEYS,
|
|
825
|
-
|
|
826
|
-
|
|
893
|
+
S as checkAgeBeforeUpload,
|
|
894
|
+
u as compressImage,
|
|
827
895
|
l as createT,
|
|
828
|
-
|
|
896
|
+
E as detectLanguage,
|
|
829
897
|
d as detectProductImage,
|
|
830
|
-
|
|
831
|
-
|
|
898
|
+
h as isValidImageFile,
|
|
899
|
+
U as registerLocale
|
|
832
900
|
};
|
|
833
901
|
//# sourceMappingURL=primestyle-tryon.js.map
|