@primestyleai/tryon 5.8.58 → 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 +223 -42
- 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 +224 -42
- 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
|
) });
|
|
@@ -13196,6 +13327,12 @@ function detectMeasurementType(title) {
|
|
|
13196
13327
|
if (/\b(sunglass|sunglasses|eyewear|eyeglasses|glasses|spectacles|optical|goggles|frames|aviator|wayfarer|lens)\b/.test(t)) return "face";
|
|
13197
13328
|
return "body";
|
|
13198
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
|
+
}
|
|
13199
13336
|
function PrimeStyleTryonInner({
|
|
13200
13337
|
productImage,
|
|
13201
13338
|
productTitle = "Product",
|
|
@@ -13301,15 +13438,22 @@ function PrimeStyleTryonInner({
|
|
|
13301
13438
|
if (pollingRef.current) clearInterval(pollingRef.current);
|
|
13302
13439
|
};
|
|
13303
13440
|
}, [apiUrl]);
|
|
13441
|
+
const TARGET_SECONDS = 22;
|
|
13304
13442
|
const progressRef = useRef(0);
|
|
13305
13443
|
const progressBarRef = useRef(null);
|
|
13306
13444
|
const progressTextRef = useRef(null);
|
|
13307
13445
|
const progressStatusRef = useRef(null);
|
|
13446
|
+
const progressEtaRef = useRef(null);
|
|
13447
|
+
const progressRingRef = useRef(null);
|
|
13448
|
+
const progressStartTsRef = useRef(null);
|
|
13449
|
+
const progressLastStageRef = useRef("");
|
|
13308
13450
|
const progressIntervalRef = useRef(null);
|
|
13309
13451
|
useEffect(() => {
|
|
13310
13452
|
if (view === "processing") {
|
|
13311
13453
|
if (progressIntervalRef.current) return;
|
|
13312
13454
|
progressRef.current = 0;
|
|
13455
|
+
progressStartTsRef.current = Date.now();
|
|
13456
|
+
progressLastStageRef.current = "";
|
|
13313
13457
|
const statuses = [
|
|
13314
13458
|
{ at: 0, text: t("Preparing your image...") },
|
|
13315
13459
|
{ at: 15, text: t("Analyzing body proportions...") },
|
|
@@ -13318,17 +13462,35 @@ function PrimeStyleTryonInner({
|
|
|
13318
13462
|
{ at: 75, text: t("Refining details...") },
|
|
13319
13463
|
{ at: 90, text: t("Almost there...") }
|
|
13320
13464
|
];
|
|
13465
|
+
const RING_CIRCUMFERENCE2 = 2 * Math.PI * 20;
|
|
13321
13466
|
progressIntervalRef.current = setInterval(() => {
|
|
13322
|
-
|
|
13323
|
-
|
|
13324
|
-
const
|
|
13325
|
-
|
|
13326
|
-
|
|
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);
|
|
13327
13473
|
if (progressBarRef.current) progressBarRef.current.style.width = `${val}%`;
|
|
13328
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
|
+
}
|
|
13329
13483
|
if (progressStatusRef.current) {
|
|
13330
13484
|
const status = [...statuses].reverse().find((s) => val >= s.at);
|
|
13331
|
-
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
|
+
}
|
|
13332
13494
|
}
|
|
13333
13495
|
}, 200);
|
|
13334
13496
|
return () => {
|
|
@@ -13340,8 +13502,9 @@ function PrimeStyleTryonInner({
|
|
|
13340
13502
|
clearInterval(progressIntervalRef.current);
|
|
13341
13503
|
progressIntervalRef.current = null;
|
|
13342
13504
|
}
|
|
13505
|
+
progressStartTsRef.current = null;
|
|
13343
13506
|
}
|
|
13344
|
-
}, [view]);
|
|
13507
|
+
}, [view, t]);
|
|
13345
13508
|
useEffect(() => {
|
|
13346
13509
|
return () => {
|
|
13347
13510
|
if (previewUrl) URL.revokeObjectURL(previewUrl);
|
|
@@ -13668,7 +13831,16 @@ function PrimeStyleTryonInner({
|
|
|
13668
13831
|
progressRef.current = 100;
|
|
13669
13832
|
if (progressBarRef.current) progressBarRef.current.style.width = "100%";
|
|
13670
13833
|
if (progressTextRef.current) progressTextRef.current.textContent = "100%";
|
|
13671
|
-
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
|
+
}
|
|
13672
13844
|
cleanupJob();
|
|
13673
13845
|
setTryOnProcessing(false);
|
|
13674
13846
|
onComplete?.({ jobId: update.galleryId, imageUrl: update.imageUrl });
|
|
@@ -14077,25 +14249,29 @@ function PrimeStyleTryonInner({
|
|
|
14077
14249
|
}
|
|
14078
14250
|
completedRef.current = false;
|
|
14079
14251
|
setTryOnProcessing(true);
|
|
14252
|
+
const vtoCategory = measurementTypeToVtoCategory(detectMeasurementType(productTitle));
|
|
14253
|
+
const isApparel = vtoCategory === "apparel";
|
|
14080
14254
|
const previewObjUrl = (overrideFile ? null : previewUrl) || URL.createObjectURL(file);
|
|
14081
14255
|
if (overrideFile || !previewUrl) setPreviewUrl(previewObjUrl);
|
|
14082
14256
|
modelPoseRef.current = null;
|
|
14083
14257
|
setBodyLandmarks(null);
|
|
14084
|
-
|
|
14085
|
-
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
|
|
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
|
+
}
|
|
14092
14268
|
try {
|
|
14093
14269
|
const modelImage = await compressImage(file);
|
|
14094
14270
|
let fitInfo;
|
|
14095
|
-
if (sizingResult?.matchDetails?.length) {
|
|
14271
|
+
if (isApparel && sizingResult?.matchDetails?.length) {
|
|
14096
14272
|
fitInfo = buildFitInfo(sizingResult.matchDetails, modelPoseRef.current);
|
|
14097
14273
|
}
|
|
14098
|
-
const response = await apiRef.current.submitTryOn(modelImage, productImage, fitInfo);
|
|
14274
|
+
const response = await apiRef.current.submitTryOn(modelImage, productImage, fitInfo, vtoCategory ?? "apparel");
|
|
14099
14275
|
onProcessing?.(response.jobId);
|
|
14100
14276
|
unsubRef.current = sseRef.current.onJob(response.jobId, handleVtoUpdate);
|
|
14101
14277
|
let attempts = 0;
|
|
@@ -14126,11 +14302,13 @@ function PrimeStyleTryonInner({
|
|
|
14126
14302
|
setView("error");
|
|
14127
14303
|
onError?.({ message, code });
|
|
14128
14304
|
}
|
|
14129
|
-
}, [selectedFile, productImage, sizingResult, onProcessing, onError, handleVtoUpdate]);
|
|
14305
|
+
}, [selectedFile, productImage, productTitle, sizingResult, onProcessing, onError, handleVtoUpdate]);
|
|
14130
14306
|
const handleRetryWithFit = useCallback(async (fitInfo) => {
|
|
14131
14307
|
if (!selectedFile || !apiRef.current || !sseRef.current) return;
|
|
14132
14308
|
setRetryLoading(true);
|
|
14133
|
-
|
|
14309
|
+
const vtoCategory = measurementTypeToVtoCategory(detectMeasurementType(productTitle));
|
|
14310
|
+
const isApparel = vtoCategory === "apparel";
|
|
14311
|
+
if (isApparel && modelPoseRef.current) {
|
|
14134
14312
|
const AREA_MAP = {
|
|
14135
14313
|
chest: "chest",
|
|
14136
14314
|
bust: "chest",
|
|
@@ -14156,7 +14334,8 @@ function PrimeStyleTryonInner({
|
|
|
14156
14334
|
pollingRef.current = null;
|
|
14157
14335
|
}
|
|
14158
14336
|
const modelImage = await compressImage(selectedFile);
|
|
14159
|
-
const
|
|
14337
|
+
const outboundFitInfo = isApparel ? fitInfo : void 0;
|
|
14338
|
+
const response = await apiRef.current.submitTryOn(modelImage, productImage, outboundFitInfo, vtoCategory ?? "apparel");
|
|
14160
14339
|
unsubRef.current = sseRef.current.onJob(response.jobId, (update) => {
|
|
14161
14340
|
if (update.status === "completed" && update.imageUrl) {
|
|
14162
14341
|
setResultImageUrl(update.imageUrl);
|
|
@@ -14221,7 +14400,7 @@ function PrimeStyleTryonInner({
|
|
|
14221
14400
|
} catch {
|
|
14222
14401
|
setRetryLoading(false);
|
|
14223
14402
|
}
|
|
14224
|
-
}, [selectedFile, productImage]);
|
|
14403
|
+
}, [selectedFile, productImage, productTitle]);
|
|
14225
14404
|
const handleDownload = useCallback(() => {
|
|
14226
14405
|
if (!resultImageUrl) return;
|
|
14227
14406
|
if (resultImageUrl.startsWith("data:")) {
|
|
@@ -14663,6 +14842,8 @@ function PrimeStyleTryonInner({
|
|
|
14663
14842
|
progressBarRef,
|
|
14664
14843
|
progressTextRef,
|
|
14665
14844
|
progressStatusRef,
|
|
14845
|
+
progressEtaRef,
|
|
14846
|
+
progressRingRef,
|
|
14666
14847
|
cn,
|
|
14667
14848
|
t
|
|
14668
14849
|
}
|