@primestyleai/tryon 5.8.57 → 5.9.0
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/api-client.d.ts +1 -1
- package/dist/{index-J6U0-q_3.js → index-CuIieeOy.js} +2 -1
- package/dist/primestyle-tryon.js +2 -2
- package/dist/react/index.js +243 -49
- package/dist/react/styles.d.ts +1 -1
- package/dist/react/views/PhotoStepMobile.d.ts +3 -1
- package/dist/react/views/ProcessingView.d.ts +3 -1
- package/dist/storefront/primestyle-tryon.js +244 -49
- package/package.json +1 -4
package/dist/api-client.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export declare class ApiClient {
|
|
|
4
4
|
private baseUrl;
|
|
5
5
|
constructor(apiKey: string, apiUrl?: string);
|
|
6
6
|
private get headers();
|
|
7
|
-
submitTryOn(modelImage: string, garmentImage: string, fitInfo?: FitAreaInfo[]): Promise<TryOnResponse>;
|
|
7
|
+
submitTryOn(modelImage: string, garmentImage: string, fitInfo?: FitAreaInfo[], category?: "apparel" | "hat" | "sunglasses"): Promise<TryOnResponse>;
|
|
8
8
|
getStatus(jobId: string): Promise<TryOnStatus>;
|
|
9
9
|
getStreamUrl(): string;
|
|
10
10
|
}
|
|
@@ -10,9 +10,10 @@ class ApiClient {
|
|
|
10
10
|
Authorization: `Bearer ${this.apiKey}`
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
-
async submitTryOn(modelImage, garmentImage, fitInfo) {
|
|
13
|
+
async submitTryOn(modelImage, garmentImage, fitInfo, category) {
|
|
14
14
|
const body = { modelImage, garmentImage };
|
|
15
15
|
if (fitInfo && fitInfo.length > 0) body.fitInfo = fitInfo;
|
|
16
|
+
if (category && category !== "apparel") body.category = category;
|
|
16
17
|
const res = await fetch(`${this.baseUrl}/api/v1/tryon`, {
|
|
17
18
|
method: "POST",
|
|
18
19
|
headers: this.headers,
|
package/dist/primestyle-tryon.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-
|
|
2
|
-
import { P, b, T, d, r } from "./index-
|
|
1
|
+
import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-CuIieeOy.js";
|
|
2
|
+
import { P, b, T, d, r } from "./index-CuIieeOy.js";
|
|
3
3
|
function detectProductImage() {
|
|
4
4
|
const ogImage = document.querySelector(
|
|
5
5
|
'meta[property="og:image"]'
|
package/dist/react/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { P as PrimeStyleError, L as LOCALE_LABELS, b as SUPPORTED_LOCALES, a as compressImage, c as createT, A as ApiClient, S as SseClient, i as isValidImageFile } from "../index-
|
|
2
|
+
import { P as PrimeStyleError, L as LOCALE_LABELS, b as SUPPORTED_LOCALES, a as compressImage, c as createT, A as ApiClient, S as SseClient, i as isValidImageFile } from "../index-CuIieeOy.js";
|
|
3
3
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
4
4
|
import { useState, useRef, useCallback, useEffect, useMemo } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
@@ -2084,22 +2084,61 @@ const STYLES = `
|
|
|
2084
2084
|
}
|
|
2085
2085
|
|
|
2086
2086
|
.ps-tryon-progress-section {
|
|
2087
|
-
display: flex; align-items: center; gap: 0.63vw; width: 100%; max-width:
|
|
2087
|
+
display: flex; align-items: center; gap: 0.63vw; width: 100%; max-width: 18vw; margin-bottom: 0.83vw;
|
|
2088
2088
|
}
|
|
2089
2089
|
.ps-tryon-progress-bar-wrap {
|
|
2090
2090
|
flex: 1; height: 0.31vw; background: var(--ps-border-color); border-radius: 3px; overflow: hidden;
|
|
2091
|
+
position: relative;
|
|
2092
|
+
}
|
|
2093
|
+
.ps-tryon-progress-bar-wrap::after {
|
|
2094
|
+
content: ""; position: absolute; inset: 0;
|
|
2095
|
+
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.45) 50%, transparent 100%);
|
|
2096
|
+
transform: translateX(-100%);
|
|
2097
|
+
animation: ps-progress-shimmer 2s linear infinite;
|
|
2098
|
+
pointer-events: none;
|
|
2099
|
+
}
|
|
2100
|
+
@keyframes ps-progress-shimmer {
|
|
2101
|
+
0% { transform: translateX(-100%); }
|
|
2102
|
+
100% { transform: translateX(100%); }
|
|
2091
2103
|
}
|
|
2092
2104
|
.ps-tryon-progress-bar-fill {
|
|
2093
2105
|
height: 100%; background: linear-gradient(90deg, var(--ps-accent), var(--ps-accent-light));
|
|
2094
2106
|
border-radius: 3px; transition: width 0.3s ease; width: 0%;
|
|
2107
|
+
position: relative; z-index: 1;
|
|
2095
2108
|
}
|
|
2096
2109
|
.ps-tryon-progress-pct {
|
|
2097
2110
|
font-size: 0.68vw; font-weight: 700; color: var(--ps-accent); min-width: 1.88vw; text-align: right;
|
|
2098
2111
|
font-variant-numeric: tabular-nums;
|
|
2099
2112
|
}
|
|
2100
2113
|
|
|
2114
|
+
/* Circular ETA ring — 48×48 px SVG with a track + progress circle; ETA
|
|
2115
|
+
text centered. strokeDashoffset is driven by the ticker in
|
|
2116
|
+
PrimeStyleTryonInner, so CSS only styles the appearance. */
|
|
2117
|
+
.ps-tryon-progress-ring {
|
|
2118
|
+
position: relative; width: 48px; height: 48px; flex: 0 0 48px;
|
|
2119
|
+
display: flex; align-items: center; justify-content: center;
|
|
2120
|
+
}
|
|
2121
|
+
.ps-tryon-progress-ring svg { transform: rotate(-90deg); }
|
|
2122
|
+
.ps-tryon-progress-ring-track {
|
|
2123
|
+
fill: none; stroke: var(--ps-border-color); stroke-width: 3.5;
|
|
2124
|
+
}
|
|
2125
|
+
.ps-tryon-progress-ring-fill {
|
|
2126
|
+
fill: none; stroke: var(--ps-accent); stroke-width: 3.5;
|
|
2127
|
+
stroke-linecap: round;
|
|
2128
|
+
transition: stroke-dashoffset 0.3s ease;
|
|
2129
|
+
}
|
|
2130
|
+
.ps-tryon-progress-eta {
|
|
2131
|
+
position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
|
|
2132
|
+
font-size: 10px; font-weight: 700; color: var(--ps-accent);
|
|
2133
|
+
font-variant-numeric: tabular-nums; letter-spacing: 0.01em;
|
|
2134
|
+
pointer-events: none;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2101
2137
|
@keyframes ps-scale-in { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
|
|
2102
|
-
.ps-tryon-processing-text {
|
|
2138
|
+
.ps-tryon-processing-text {
|
|
2139
|
+
font-size: 0.73vw; color: var(--ps-text-primary); margin: 0 0 0.21vw;
|
|
2140
|
+
opacity: 1; transition: opacity 0.18s ease;
|
|
2141
|
+
}
|
|
2103
2142
|
.ps-tryon-processing-sub { font-size: 0.63vw; color: var(--ps-text-secondary); margin: 0; }
|
|
2104
2143
|
|
|
2105
2144
|
/* Result — split layout */
|
|
@@ -8950,6 +8989,7 @@ function SizeResultView({
|
|
|
8950
8989
|
const allDone = hasPhoto ? sizingDone && tryOnDone : sizingDone;
|
|
8951
8990
|
const isMobile = useIsMobile();
|
|
8952
8991
|
const isAccessory = measurementType === "face" || measurementType === "head";
|
|
8992
|
+
const vtoExcluded = measurementType === "foot";
|
|
8953
8993
|
console.log("[PS-SDK] SizeResultView render:", {
|
|
8954
8994
|
hasPhoto,
|
|
8955
8995
|
isSnapProcessing,
|
|
@@ -9223,7 +9263,7 @@ function SizeResultView({
|
|
|
9223
9263
|
" →"
|
|
9224
9264
|
]
|
|
9225
9265
|
}
|
|
9226
|
-
) :
|
|
9266
|
+
) : vtoExcluded ? /* @__PURE__ */ jsxs(
|
|
9227
9267
|
"button",
|
|
9228
9268
|
{
|
|
9229
9269
|
className: "ps-tryon-v2-cta",
|
|
@@ -9283,7 +9323,7 @@ function SizeResultView({
|
|
|
9283
9323
|
onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
|
|
9284
9324
|
backLabel: t("Back"),
|
|
9285
9325
|
internationalSizes: sizingResult?.internationalSizes,
|
|
9286
|
-
onTryOn: resultImageUrl ||
|
|
9326
|
+
onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
|
|
9287
9327
|
continueLabel: resultImageUrl ? t("Continue Shopping") : void 0,
|
|
9288
9328
|
tryOnProcessing,
|
|
9289
9329
|
productImage: resultImageUrl || productImage,
|
|
@@ -9330,7 +9370,7 @@ function SizeResultView({
|
|
|
9330
9370
|
onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
|
|
9331
9371
|
backLabel: t("Back"),
|
|
9332
9372
|
internationalSizes: sizingResult?.internationalSizes,
|
|
9333
|
-
onTryOn: resultImageUrl ||
|
|
9373
|
+
onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
|
|
9334
9374
|
continueLabel: resultImageUrl ? t("Continue Shopping") : void 0,
|
|
9335
9375
|
tryOnProcessing,
|
|
9336
9376
|
t,
|
|
@@ -9340,14 +9380,14 @@ function SizeResultView({
|
|
|
9340
9380
|
] });
|
|
9341
9381
|
})()
|
|
9342
9382
|
),
|
|
9343
|
-
showPhotoGuide && !
|
|
9383
|
+
showPhotoGuide && !vtoExcluded && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-chart-overlay", children: isMobile ? (
|
|
9344
9384
|
/* ── Mobile: same layout as the AI-sizing photo step
|
|
9345
9385
|
(PhotoStepMobile) — title + subtitle, large preview,
|
|
9346
9386
|
checklist card, primary CTA + RETAKE secondary. ── */
|
|
9347
9387
|
/* @__PURE__ */ jsx("div", { className: "ps-bp-wrapper", style: { padding: "16px 16px 0", background: "var(--ps-bg-primary)" }, children: /* @__PURE__ */ jsxs("div", { className: "ps-pm-root", children: [
|
|
9348
9388
|
/* @__PURE__ */ jsxs("div", { className: "ps-pm-header", children: [
|
|
9349
9389
|
/* @__PURE__ */ jsx("h2", { className: "ps-pm-title", children: t("Review your photo") }),
|
|
9350
|
-
/* @__PURE__ */ jsx("p", { className: "ps-pm-subtitle", children: t("Ensure your full body is visible for the most accurate virtual try-on.") })
|
|
9390
|
+
/* @__PURE__ */ jsx("p", { className: "ps-pm-subtitle", children: measurementType === "face" ? t("A clear, front-facing face photo — no glasses on — gives us the most accurate try-on.") : measurementType === "head" ? t("Face the camera with your head and shoulders in frame, leaving space above your head.") : t("Ensure your full body is visible for the most accurate virtual try-on.") })
|
|
9351
9391
|
] }),
|
|
9352
9392
|
/* @__PURE__ */ jsx(
|
|
9353
9393
|
"input",
|
|
@@ -9398,11 +9438,19 @@ function SizeResultView({
|
|
|
9398
9438
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-checklist-icon", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "14", height: "14", children: /* @__PURE__ */ jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z" }) }) }),
|
|
9399
9439
|
/* @__PURE__ */ jsxs("div", { className: "ps-pm-checklist-body", children: [
|
|
9400
9440
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-checklist-title", children: t("Checklist for accuracy") }),
|
|
9401
|
-
/* @__PURE__ */
|
|
9441
|
+
/* @__PURE__ */ jsx("ul", { className: "ps-pm-checklist-items", children: measurementType === "face" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9442
|
+
/* @__PURE__ */ jsx("li", { children: t("Face the camera directly at eye level") }),
|
|
9443
|
+
/* @__PURE__ */ jsx("li", { children: t("Remove any glasses you're wearing") }),
|
|
9444
|
+
/* @__PURE__ */ jsx("li", { children: t("Good lighting, plain background") })
|
|
9445
|
+
] }) : measurementType === "head" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9446
|
+
/* @__PURE__ */ jsx("li", { children: t("Head and shoulders in frame") }),
|
|
9447
|
+
/* @__PURE__ */ jsx("li", { children: t("Leave space above your head") }),
|
|
9448
|
+
/* @__PURE__ */ jsx("li", { children: t("Good lighting, plain background") })
|
|
9449
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9402
9450
|
/* @__PURE__ */ jsx("li", { children: t("Form-fitting clothing is recommended") }),
|
|
9403
9451
|
/* @__PURE__ */ jsx("li", { children: t("Standing 2-3 meters from camera") }),
|
|
9404
9452
|
/* @__PURE__ */ jsx("li", { children: t("Neutral background with good lighting") })
|
|
9405
|
-
] })
|
|
9453
|
+
] }) })
|
|
9406
9454
|
] })
|
|
9407
9455
|
] }),
|
|
9408
9456
|
/* @__PURE__ */ jsx("div", { className: "ps-bpm-spacer" }),
|
|
@@ -9482,7 +9530,7 @@ function SizeResultView({
|
|
|
9482
9530
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9483
9531
|
/* @__PURE__ */ jsx(UploadIcon, { size: 32 }),
|
|
9484
9532
|
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.85vw", fontWeight: 600, color: "var(--ps-text-primary)", marginTop: "0.5vw" }, children: t("Upload your photo") }),
|
|
9485
|
-
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.6vw", color: "var(--ps-text-muted)", marginTop: "0.2vw" }, children: t("Click or drag a full-body photo") })
|
|
9533
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.6vw", color: "var(--ps-text-muted)", marginTop: "0.2vw" }, children: measurementType === "face" ? t("Click or drag a close-up face photo") : measurementType === "head" ? t("Click or drag a head-and-shoulders photo") : t("Click or drag a full-body photo") })
|
|
9486
9534
|
] }),
|
|
9487
9535
|
/* @__PURE__ */ jsx(
|
|
9488
9536
|
"input",
|
|
@@ -9507,7 +9555,23 @@ function SizeResultView({
|
|
|
9507
9555
|
/* @__PURE__ */ jsx("span", { style: { color: "#1c9d4c", fontSize: "0.75vw", fontWeight: 700 }, children: "✓" }),
|
|
9508
9556
|
/* @__PURE__ */ jsx("span", { style: { color: "#1c9d4c", fontSize: "0.65vw", fontWeight: 600 }, children: t("Do") })
|
|
9509
9557
|
] }),
|
|
9510
|
-
/* @__PURE__ */
|
|
9558
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-primary)", lineHeight: 1.8 }, children: measurementType === "face" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9559
|
+
t("Face the camera directly, centered in frame"),
|
|
9560
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9561
|
+
t("Use natural, even lighting (e.g. near a window)"),
|
|
9562
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9563
|
+
t("Keep hair away from your face and ears"),
|
|
9564
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9565
|
+
t("Choose a plain, light background")
|
|
9566
|
+
] }) : measurementType === "head" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9567
|
+
t("Face the camera with head and shoulders in frame"),
|
|
9568
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9569
|
+
t("Leave some space above your head"),
|
|
9570
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9571
|
+
t("Use natural, even lighting"),
|
|
9572
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9573
|
+
t("Choose a plain, light background")
|
|
9574
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9511
9575
|
t("Stand facing the camera with your full body in frame"),
|
|
9512
9576
|
/* @__PURE__ */ jsx("br", {}),
|
|
9513
9577
|
t("Use natural or even lighting"),
|
|
@@ -9515,14 +9579,30 @@ function SizeResultView({
|
|
|
9515
9579
|
t("Wear fitted, simple clothing"),
|
|
9516
9580
|
/* @__PURE__ */ jsx("br", {}),
|
|
9517
9581
|
t("Stand straight and still, arms relaxed")
|
|
9518
|
-
] })
|
|
9582
|
+
] }) })
|
|
9519
9583
|
] }),
|
|
9520
9584
|
/* @__PURE__ */ jsxs("div", { style: { background: "#ffe2e2", borderRadius: "0.5vw", padding: "0.6vw 0.8vw" }, children: [
|
|
9521
9585
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.3vw", marginBottom: "0.3vw" }, children: [
|
|
9522
9586
|
/* @__PURE__ */ jsx("span", { style: { color: "#e7000b", fontSize: "0.75vw", fontWeight: 700 }, children: "✗" }),
|
|
9523
9587
|
/* @__PURE__ */ jsx("span", { style: { color: "#e7000b", fontSize: "0.65vw", fontWeight: 600 }, children: t("Don't") })
|
|
9524
9588
|
] }),
|
|
9525
|
-
/* @__PURE__ */
|
|
9589
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-primary)", lineHeight: 1.8 }, children: measurementType === "face" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9590
|
+
t("Don't wear sunglasses or a hat in the photo"),
|
|
9591
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9592
|
+
t("Don't tilt or turn your head"),
|
|
9593
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9594
|
+
t("Don't use strong backlighting or flash"),
|
|
9595
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9596
|
+
t("Don't apply filters or edits")
|
|
9597
|
+
] }) : measurementType === "head" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9598
|
+
t("Don't wear a hat in the photo"),
|
|
9599
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9600
|
+
t("Don't crop out the top of your head"),
|
|
9601
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9602
|
+
t("Don't use strong backlighting or flash"),
|
|
9603
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9604
|
+
t("Don't apply filters or edits")
|
|
9605
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9526
9606
|
t("Don't wear loose or baggy clothing"),
|
|
9527
9607
|
/* @__PURE__ */ jsx("br", {}),
|
|
9528
9608
|
t("Don't sit, pose, or bend"),
|
|
@@ -9530,14 +9610,14 @@ function SizeResultView({
|
|
|
9530
9610
|
t("Don't take mirror photos or selfies"),
|
|
9531
9611
|
/* @__PURE__ */ jsx("br", {}),
|
|
9532
9612
|
t("Don't apply filters or edits")
|
|
9533
|
-
] })
|
|
9613
|
+
] }) })
|
|
9534
9614
|
] }),
|
|
9535
9615
|
/* @__PURE__ */ jsxs("div", { style: { background: "rgba(59,130,246,0.08)", border: "1px solid rgba(59,130,246,0.2)", borderRadius: "0.5vw", padding: "0.5vw 0.8vw" }, children: [
|
|
9536
9616
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.3vw", marginBottom: "0.2vw" }, children: [
|
|
9537
9617
|
/* @__PURE__ */ jsx(SparkleIcon, { size: 12 }),
|
|
9538
9618
|
/* @__PURE__ */ jsx("span", { style: { color: "var(--ps-accent)", fontSize: "0.65vw", fontWeight: 700 }, children: t("Pro Tip") })
|
|
9539
9619
|
] }),
|
|
9540
|
-
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.55vw", color: "var(--ps-text-secondary)", lineHeight: 1.7 }, children: t("Our AI works best with front-facing, full-body photos in fitted clothing. Better photos = more accurate virtual try-on!") })
|
|
9620
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.55vw", color: "var(--ps-text-secondary)", lineHeight: 1.7 }, children: measurementType === "face" ? t("A clear, well-lit face photo gives the most accurate eyewear try-on.") : measurementType === "head" ? t("A clear head-and-shoulders photo with space above your head gives the most accurate headwear try-on.") : t("Our AI works best with front-facing, full-body photos in fitted clothing. Better photos = more accurate virtual try-on!") })
|
|
9541
9621
|
] })
|
|
9542
9622
|
] })
|
|
9543
9623
|
] }),
|
|
@@ -9666,12 +9746,16 @@ function UploadView({
|
|
|
9666
9746
|
}
|
|
9667
9747
|
) });
|
|
9668
9748
|
}
|
|
9749
|
+
const RING_RADIUS = 20;
|
|
9750
|
+
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS;
|
|
9669
9751
|
function ProcessingView({
|
|
9670
9752
|
previewUrl,
|
|
9671
9753
|
progressRef,
|
|
9672
9754
|
progressBarRef,
|
|
9673
9755
|
progressTextRef,
|
|
9674
9756
|
progressStatusRef,
|
|
9757
|
+
progressEtaRef,
|
|
9758
|
+
progressRingRef,
|
|
9675
9759
|
cn,
|
|
9676
9760
|
t
|
|
9677
9761
|
}) {
|
|
@@ -9686,6 +9770,16 @@ function ProcessingView({
|
|
|
9686
9770
|
const statusCb = useCallback((el) => {
|
|
9687
9771
|
progressStatusRef.current = el;
|
|
9688
9772
|
}, []);
|
|
9773
|
+
const etaCb = useCallback((el) => {
|
|
9774
|
+
progressEtaRef.current = el;
|
|
9775
|
+
}, []);
|
|
9776
|
+
const ringCb = useCallback((el) => {
|
|
9777
|
+
progressRingRef.current = el;
|
|
9778
|
+
if (el) {
|
|
9779
|
+
const offset = RING_CIRCUMFERENCE * (1 - Math.round(progressRef.current) / 100);
|
|
9780
|
+
el.style.strokeDashoffset = String(offset);
|
|
9781
|
+
}
|
|
9782
|
+
}, []);
|
|
9689
9783
|
return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-processing", children: [
|
|
9690
9784
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-processing-image-wrap", children: [
|
|
9691
9785
|
previewUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -9696,6 +9790,32 @@ function ProcessingView({
|
|
|
9696
9790
|
/* @__PURE__ */ jsx("div", { className: "ps-tryon-scan-overlay" })
|
|
9697
9791
|
] }),
|
|
9698
9792
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-progress-section", children: [
|
|
9793
|
+
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-progress-ring", children: [
|
|
9794
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 48", width: "48", height: "48", "aria-hidden": "true", children: [
|
|
9795
|
+
/* @__PURE__ */ jsx(
|
|
9796
|
+
"circle",
|
|
9797
|
+
{
|
|
9798
|
+
cx: "24",
|
|
9799
|
+
cy: "24",
|
|
9800
|
+
r: RING_RADIUS,
|
|
9801
|
+
className: "ps-tryon-progress-ring-track"
|
|
9802
|
+
}
|
|
9803
|
+
),
|
|
9804
|
+
/* @__PURE__ */ jsx(
|
|
9805
|
+
"circle",
|
|
9806
|
+
{
|
|
9807
|
+
ref: ringCb,
|
|
9808
|
+
cx: "24",
|
|
9809
|
+
cy: "24",
|
|
9810
|
+
r: RING_RADIUS,
|
|
9811
|
+
className: "ps-tryon-progress-ring-fill",
|
|
9812
|
+
strokeDasharray: RING_CIRCUMFERENCE,
|
|
9813
|
+
strokeDashoffset: RING_CIRCUMFERENCE
|
|
9814
|
+
}
|
|
9815
|
+
)
|
|
9816
|
+
] }),
|
|
9817
|
+
/* @__PURE__ */ jsx("span", { ref: etaCb, className: "ps-tryon-progress-eta", children: `~22s` })
|
|
9818
|
+
] }),
|
|
9699
9819
|
/* @__PURE__ */ jsx("div", { className: "ps-tryon-progress-bar-wrap", children: /* @__PURE__ */ jsx("div", { ref: barCb, className: "ps-tryon-progress-bar-fill" }) }),
|
|
9700
9820
|
/* @__PURE__ */ jsx("span", { ref: pctCb, className: "ps-tryon-progress-pct", children: "0%" })
|
|
9701
9821
|
] }),
|
|
@@ -11361,12 +11481,13 @@ function PhotoStepMobile({
|
|
|
11361
11481
|
photoVariant = "full-body",
|
|
11362
11482
|
photoStepHeight,
|
|
11363
11483
|
onPhotoStepHeightChange,
|
|
11484
|
+
ageConfirmed,
|
|
11485
|
+
onAgeConfirmedChange,
|
|
11364
11486
|
t
|
|
11365
11487
|
}) {
|
|
11366
11488
|
const isCloseUp = photoVariant === "close-up";
|
|
11367
11489
|
const fileRef = useRef(null);
|
|
11368
11490
|
const hasPhoto = !!photoPreview;
|
|
11369
|
-
const [ageConfirmed, setAgeConfirmed] = useState(null);
|
|
11370
11491
|
const gated = !hasPhoto && ageConfirmed !== true;
|
|
11371
11492
|
return /* @__PURE__ */ jsxs("div", { className: "ps-pm-root", children: [
|
|
11372
11493
|
/* @__PURE__ */ jsxs("div", { className: "ps-pm-header", children: [
|
|
@@ -11418,14 +11539,14 @@ function PhotoStepMobile({
|
|
|
11418
11539
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-age-gate-eyebrow", children: t("AGE VERIFICATION") }),
|
|
11419
11540
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-age-gate-question", children: t("Is the person in this photo 18 years or older?") }),
|
|
11420
11541
|
/* @__PURE__ */ jsxs("div", { className: "ps-pm-age-gate-actions", children: [
|
|
11421
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-primary", onClick: () =>
|
|
11422
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () =>
|
|
11542
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-primary", onClick: () => onAgeConfirmedChange(true), children: t("Yes") }),
|
|
11543
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () => onAgeConfirmedChange(false), children: t("No") })
|
|
11423
11544
|
] })
|
|
11424
11545
|
] }) }),
|
|
11425
11546
|
ageConfirmed === false && /* @__PURE__ */ jsx("div", { className: "ps-pm-age-gate", role: "alert", children: /* @__PURE__ */ jsxs("div", { className: "ps-pm-age-gate-card", children: [
|
|
11426
11547
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-age-gate-eyebrow ps-pm-age-gate-eyebrow-blocked", children: t("UPLOAD NOT ALLOWED") }),
|
|
11427
11548
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-age-gate-question", children: t("For your safety, we cannot process photos of people under 18.") }),
|
|
11428
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () =>
|
|
11549
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () => onAgeConfirmedChange(null), children: t("Go back") })
|
|
11429
11550
|
] }) })
|
|
11430
11551
|
] }) }),
|
|
11431
11552
|
/* @__PURE__ */ jsxs("div", { className: "ps-pm-legal-notice", children: [
|
|
@@ -11893,6 +12014,11 @@ function BodyProfileView({
|
|
|
11893
12014
|
error,
|
|
11894
12015
|
photoStepHeight,
|
|
11895
12016
|
onPhotoStepHeightChange: setPhotoStepHeight,
|
|
12017
|
+
ageConfirmed,
|
|
12018
|
+
onAgeConfirmedChange: (v) => {
|
|
12019
|
+
setAgeConfirmed(v);
|
|
12020
|
+
if (v === true) setError("");
|
|
12021
|
+
},
|
|
11896
12022
|
t
|
|
11897
12023
|
}
|
|
11898
12024
|
) });
|
|
@@ -12639,6 +12765,11 @@ function AccessorySizeView({
|
|
|
12639
12765
|
onSwitchToManual: () => setStep("manual"),
|
|
12640
12766
|
error,
|
|
12641
12767
|
photoVariant,
|
|
12768
|
+
ageConfirmed,
|
|
12769
|
+
onAgeConfirmedChange: (v) => {
|
|
12770
|
+
setAgeConfirmed(v);
|
|
12771
|
+
if (v === true) setError("");
|
|
12772
|
+
},
|
|
12642
12773
|
t
|
|
12643
12774
|
}
|
|
12644
12775
|
) });
|
|
@@ -13049,13 +13180,26 @@ function buildFieldsFromSizeGuide$2(sizeGuide) {
|
|
|
13049
13180
|
const req = sizeGuide?.requiredFields;
|
|
13050
13181
|
if (!req || req.length === 0) return FALLBACK_FOOT_FIELDS;
|
|
13051
13182
|
const SIZE_CODE_KEYS = /* @__PURE__ */ new Set(["size", "country", "eu", "__skip__", "shoeEU", "shoeUS", "shoeUK", "shoeJP"]);
|
|
13052
|
-
const
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
}
|
|
13183
|
+
const defaultPlaceholderFor = (key, label) => {
|
|
13184
|
+
const exact = FALLBACK_FOOT_FIELDS.find((f) => f.key === key);
|
|
13185
|
+
if (exact) return exact.placeholder;
|
|
13186
|
+
const looksLikeFoot = /foot|length/i.test(key) || /foot|length/i.test(label);
|
|
13187
|
+
if (looksLikeFoot) return FALLBACK_FOOT_FIELDS[0].placeholder;
|
|
13188
|
+
return { cm: "", in: "" };
|
|
13189
|
+
};
|
|
13190
|
+
const out = req.filter((f) => !SIZE_CODE_KEYS.has(f.key) && f.unit !== "size").map((f) => {
|
|
13191
|
+
const fallback = defaultPlaceholderFor(f.key, f.label || "");
|
|
13192
|
+
return {
|
|
13193
|
+
key: f.key,
|
|
13194
|
+
label: f.label || f.key,
|
|
13195
|
+
placeholder: {
|
|
13196
|
+
cm: f.placeholder || fallback.cm,
|
|
13197
|
+
in: f.placeholder || fallback.in
|
|
13198
|
+
},
|
|
13199
|
+
min: 0,
|
|
13200
|
+
step: 0.5
|
|
13201
|
+
};
|
|
13202
|
+
});
|
|
13059
13203
|
return out.length > 0 ? out : FALLBACK_FOOT_FIELDS;
|
|
13060
13204
|
}
|
|
13061
13205
|
function FootSizeView(props) {
|
|
@@ -13183,6 +13327,12 @@ function detectMeasurementType(title) {
|
|
|
13183
13327
|
if (/\b(sunglass|sunglasses|eyewear|eyeglasses|glasses|spectacles|optical|goggles|frames|aviator|wayfarer|lens)\b/.test(t)) return "face";
|
|
13184
13328
|
return "body";
|
|
13185
13329
|
}
|
|
13330
|
+
function measurementTypeToVtoCategory(type) {
|
|
13331
|
+
if (type === "face") return "sunglasses";
|
|
13332
|
+
if (type === "head") return "hat";
|
|
13333
|
+
if (type === "body") return "apparel";
|
|
13334
|
+
return null;
|
|
13335
|
+
}
|
|
13186
13336
|
function PrimeStyleTryonInner({
|
|
13187
13337
|
productImage,
|
|
13188
13338
|
productTitle = "Product",
|
|
@@ -13288,15 +13438,22 @@ function PrimeStyleTryonInner({
|
|
|
13288
13438
|
if (pollingRef.current) clearInterval(pollingRef.current);
|
|
13289
13439
|
};
|
|
13290
13440
|
}, [apiUrl]);
|
|
13441
|
+
const TARGET_SECONDS = 22;
|
|
13291
13442
|
const progressRef = useRef(0);
|
|
13292
13443
|
const progressBarRef = useRef(null);
|
|
13293
13444
|
const progressTextRef = useRef(null);
|
|
13294
13445
|
const progressStatusRef = useRef(null);
|
|
13446
|
+
const progressEtaRef = useRef(null);
|
|
13447
|
+
const progressRingRef = useRef(null);
|
|
13448
|
+
const progressStartTsRef = useRef(null);
|
|
13449
|
+
const progressLastStageRef = useRef("");
|
|
13295
13450
|
const progressIntervalRef = useRef(null);
|
|
13296
13451
|
useEffect(() => {
|
|
13297
13452
|
if (view === "processing") {
|
|
13298
13453
|
if (progressIntervalRef.current) return;
|
|
13299
13454
|
progressRef.current = 0;
|
|
13455
|
+
progressStartTsRef.current = Date.now();
|
|
13456
|
+
progressLastStageRef.current = "";
|
|
13300
13457
|
const statuses = [
|
|
13301
13458
|
{ at: 0, text: t("Preparing your image...") },
|
|
13302
13459
|
{ at: 15, text: t("Analyzing body proportions...") },
|
|
@@ -13305,17 +13462,35 @@ function PrimeStyleTryonInner({
|
|
|
13305
13462
|
{ at: 75, text: t("Refining details...") },
|
|
13306
13463
|
{ at: 90, text: t("Almost there...") }
|
|
13307
13464
|
];
|
|
13465
|
+
const RING_CIRCUMFERENCE2 = 2 * Math.PI * 20;
|
|
13308
13466
|
progressIntervalRef.current = setInterval(() => {
|
|
13309
|
-
|
|
13310
|
-
|
|
13311
|
-
const
|
|
13312
|
-
|
|
13313
|
-
|
|
13467
|
+
if (completedRef.current) return;
|
|
13468
|
+
const startTs = progressStartTsRef.current || Date.now();
|
|
13469
|
+
const elapsed = (Date.now() - startTs) / 1e3;
|
|
13470
|
+
const target = Math.min(95, elapsed / TARGET_SECONDS * 100);
|
|
13471
|
+
progressRef.current = target;
|
|
13472
|
+
const val = Math.round(target);
|
|
13314
13473
|
if (progressBarRef.current) progressBarRef.current.style.width = `${val}%`;
|
|
13315
13474
|
if (progressTextRef.current) progressTextRef.current.textContent = `${val}%`;
|
|
13475
|
+
if (progressRingRef.current) {
|
|
13476
|
+
const offset = RING_CIRCUMFERENCE2 * (1 - target / 100);
|
|
13477
|
+
progressRingRef.current.style.strokeDashoffset = String(offset);
|
|
13478
|
+
}
|
|
13479
|
+
if (progressEtaRef.current) {
|
|
13480
|
+
const remaining = Math.max(0, TARGET_SECONDS - Math.floor(elapsed));
|
|
13481
|
+
progressEtaRef.current.textContent = elapsed >= TARGET_SECONDS ? t("Finalizing...") : `~${remaining}s`;
|
|
13482
|
+
}
|
|
13316
13483
|
if (progressStatusRef.current) {
|
|
13317
13484
|
const status = [...statuses].reverse().find((s) => val >= s.at);
|
|
13318
|
-
if (status
|
|
13485
|
+
if (status && status.text !== progressLastStageRef.current) {
|
|
13486
|
+
const el = progressStatusRef.current;
|
|
13487
|
+
el.style.opacity = "0";
|
|
13488
|
+
setTimeout(() => {
|
|
13489
|
+
el.textContent = status.text;
|
|
13490
|
+
el.style.opacity = "1";
|
|
13491
|
+
}, 180);
|
|
13492
|
+
progressLastStageRef.current = status.text;
|
|
13493
|
+
}
|
|
13319
13494
|
}
|
|
13320
13495
|
}, 200);
|
|
13321
13496
|
return () => {
|
|
@@ -13327,8 +13502,9 @@ function PrimeStyleTryonInner({
|
|
|
13327
13502
|
clearInterval(progressIntervalRef.current);
|
|
13328
13503
|
progressIntervalRef.current = null;
|
|
13329
13504
|
}
|
|
13505
|
+
progressStartTsRef.current = null;
|
|
13330
13506
|
}
|
|
13331
|
-
}, [view]);
|
|
13507
|
+
}, [view, t]);
|
|
13332
13508
|
useEffect(() => {
|
|
13333
13509
|
return () => {
|
|
13334
13510
|
if (previewUrl) URL.revokeObjectURL(previewUrl);
|
|
@@ -13655,7 +13831,16 @@ function PrimeStyleTryonInner({
|
|
|
13655
13831
|
progressRef.current = 100;
|
|
13656
13832
|
if (progressBarRef.current) progressBarRef.current.style.width = "100%";
|
|
13657
13833
|
if (progressTextRef.current) progressTextRef.current.textContent = "100%";
|
|
13658
|
-
if (
|
|
13834
|
+
if (progressRingRef.current) progressRingRef.current.style.strokeDashoffset = "0";
|
|
13835
|
+
if (progressEtaRef.current) progressEtaRef.current.textContent = t("Done");
|
|
13836
|
+
if (progressStatusRef.current) {
|
|
13837
|
+
const el = progressStatusRef.current;
|
|
13838
|
+
el.style.opacity = "0";
|
|
13839
|
+
setTimeout(() => {
|
|
13840
|
+
el.textContent = t("Complete!");
|
|
13841
|
+
el.style.opacity = "1";
|
|
13842
|
+
}, 180);
|
|
13843
|
+
}
|
|
13659
13844
|
cleanupJob();
|
|
13660
13845
|
setTryOnProcessing(false);
|
|
13661
13846
|
onComplete?.({ jobId: update.galleryId, imageUrl: update.imageUrl });
|
|
@@ -14064,25 +14249,29 @@ function PrimeStyleTryonInner({
|
|
|
14064
14249
|
}
|
|
14065
14250
|
completedRef.current = false;
|
|
14066
14251
|
setTryOnProcessing(true);
|
|
14252
|
+
const vtoCategory = measurementTypeToVtoCategory(detectMeasurementType(productTitle));
|
|
14253
|
+
const isApparel = vtoCategory === "apparel";
|
|
14067
14254
|
const previewObjUrl = (overrideFile ? null : previewUrl) || URL.createObjectURL(file);
|
|
14068
14255
|
if (overrideFile || !previewUrl) setPreviewUrl(previewObjUrl);
|
|
14069
14256
|
modelPoseRef.current = null;
|
|
14070
14257
|
setBodyLandmarks(null);
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14076
|
-
|
|
14077
|
-
|
|
14078
|
-
|
|
14258
|
+
if (isApparel) {
|
|
14259
|
+
detectMeasurementLines(previewObjUrl).then((lines) => {
|
|
14260
|
+
modelPoseRef.current = lines;
|
|
14261
|
+
}).catch(() => {
|
|
14262
|
+
});
|
|
14263
|
+
detectBodyLandmarks(previewObjUrl).then((lm) => {
|
|
14264
|
+
setBodyLandmarks(lm);
|
|
14265
|
+
}).catch(() => {
|
|
14266
|
+
});
|
|
14267
|
+
}
|
|
14079
14268
|
try {
|
|
14080
14269
|
const modelImage = await compressImage(file);
|
|
14081
14270
|
let fitInfo;
|
|
14082
|
-
if (sizingResult?.matchDetails?.length) {
|
|
14271
|
+
if (isApparel && sizingResult?.matchDetails?.length) {
|
|
14083
14272
|
fitInfo = buildFitInfo(sizingResult.matchDetails, modelPoseRef.current);
|
|
14084
14273
|
}
|
|
14085
|
-
const response = await apiRef.current.submitTryOn(modelImage, productImage, fitInfo);
|
|
14274
|
+
const response = await apiRef.current.submitTryOn(modelImage, productImage, fitInfo, vtoCategory ?? "apparel");
|
|
14086
14275
|
onProcessing?.(response.jobId);
|
|
14087
14276
|
unsubRef.current = sseRef.current.onJob(response.jobId, handleVtoUpdate);
|
|
14088
14277
|
let attempts = 0;
|
|
@@ -14113,11 +14302,13 @@ function PrimeStyleTryonInner({
|
|
|
14113
14302
|
setView("error");
|
|
14114
14303
|
onError?.({ message, code });
|
|
14115
14304
|
}
|
|
14116
|
-
}, [selectedFile, productImage, sizingResult, onProcessing, onError, handleVtoUpdate]);
|
|
14305
|
+
}, [selectedFile, productImage, productTitle, sizingResult, onProcessing, onError, handleVtoUpdate]);
|
|
14117
14306
|
const handleRetryWithFit = useCallback(async (fitInfo) => {
|
|
14118
14307
|
if (!selectedFile || !apiRef.current || !sseRef.current) return;
|
|
14119
14308
|
setRetryLoading(true);
|
|
14120
|
-
|
|
14309
|
+
const vtoCategory = measurementTypeToVtoCategory(detectMeasurementType(productTitle));
|
|
14310
|
+
const isApparel = vtoCategory === "apparel";
|
|
14311
|
+
if (isApparel && modelPoseRef.current) {
|
|
14121
14312
|
const AREA_MAP = {
|
|
14122
14313
|
chest: "chest",
|
|
14123
14314
|
bust: "chest",
|
|
@@ -14143,7 +14334,8 @@ function PrimeStyleTryonInner({
|
|
|
14143
14334
|
pollingRef.current = null;
|
|
14144
14335
|
}
|
|
14145
14336
|
const modelImage = await compressImage(selectedFile);
|
|
14146
|
-
const
|
|
14337
|
+
const outboundFitInfo = isApparel ? fitInfo : void 0;
|
|
14338
|
+
const response = await apiRef.current.submitTryOn(modelImage, productImage, outboundFitInfo, vtoCategory ?? "apparel");
|
|
14147
14339
|
unsubRef.current = sseRef.current.onJob(response.jobId, (update) => {
|
|
14148
14340
|
if (update.status === "completed" && update.imageUrl) {
|
|
14149
14341
|
setResultImageUrl(update.imageUrl);
|
|
@@ -14208,7 +14400,7 @@ function PrimeStyleTryonInner({
|
|
|
14208
14400
|
} catch {
|
|
14209
14401
|
setRetryLoading(false);
|
|
14210
14402
|
}
|
|
14211
|
-
}, [selectedFile, productImage]);
|
|
14403
|
+
}, [selectedFile, productImage, productTitle]);
|
|
14212
14404
|
const handleDownload = useCallback(() => {
|
|
14213
14405
|
if (!resultImageUrl) return;
|
|
14214
14406
|
if (resultImageUrl.startsWith("data:")) {
|
|
@@ -14650,6 +14842,8 @@ function PrimeStyleTryonInner({
|
|
|
14650
14842
|
progressBarRef,
|
|
14651
14843
|
progressTextRef,
|
|
14652
14844
|
progressStatusRef,
|
|
14845
|
+
progressEtaRef,
|
|
14846
|
+
progressRingRef,
|
|
14653
14847
|
cn,
|
|
14654
14848
|
t
|
|
14655
14849
|
}
|