@primestyleai/tryon 5.10.135 → 5.10.137

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.
@@ -19,8 +19,10 @@ interface MobileScanningViewProps {
19
19
  productDescription?: string;
20
20
  /** Optional cancel handler shown as a text link under the cards. */
21
21
  onCancelTryOn?: () => void;
22
- onSwitchToManual: () => void;
22
+ /** No longer rendered (tabs removed mid-scan), kept for backwards-compat
23
+ * with the photo-step caller that passes it down. */
24
+ onSwitchToManual?: () => void;
23
25
  t: TranslateFn;
24
26
  }
25
- export declare function MobileScanningView({ previewUrl, productImage, bodyLandmarks, sizingDone, tryOnProcessing, productMaterial, productDescription, onCancelTryOn, onSwitchToManual, t, }: MobileScanningViewProps): import("react/jsx-runtime").JSX.Element;
27
+ export declare function MobileScanningView({ previewUrl, productImage, bodyLandmarks, sizingDone, tryOnProcessing, productMaterial, productDescription, onCancelTryOn, t, }: MobileScanningViewProps): import("react/jsx-runtime").JSX.Element;
26
28
  export {};
@@ -24,6 +24,9 @@ export interface MultiSectionMobileProps {
24
24
  onSelectSection: (name: string) => void;
25
25
  onTryOn: () => void;
26
26
  tryOnProcessing?: boolean;
27
+ /** Epoch ms timestamp the try-on request started — drives the
28
+ * TryOnGenerationBadge's progress + ETA copy. */
29
+ tryOnStartedAt?: number | null;
27
30
  /** When set, the image area shows the try-on result instead of productImage. */
28
31
  resultImageUrl?: string | null;
29
32
  /** Try-on has finished and result is ready. Switches the bottom CTA into
@@ -44,4 +47,4 @@ export interface MultiSectionMobileProps {
44
47
  onImageLoad?: (e: React.SyntheticEvent<HTMLImageElement>) => void;
45
48
  t: TranslateFn;
46
49
  }
47
- export declare function MultiSectionMobile({ productImage, productTitle, sizingResult, sizeGuide, sectionEntries, pendingCustomSizes, onSelectSection, onTryOn, tryOnProcessing, resultImageUrl, tryOnDone, onTryAgain, onClose, overlayNode, showLines, onToggleLines, onImageLoad, t, }: MultiSectionMobileProps): import("react/jsx-runtime").JSX.Element;
50
+ export declare function MultiSectionMobile({ productImage, productTitle, sizingResult, sizeGuide, sectionEntries, pendingCustomSizes, onSelectSection, onTryOn, tryOnProcessing, tryOnStartedAt, resultImageUrl, tryOnDone, onTryAgain, onClose, overlayNode, showLines, onToggleLines, onImageLoad, t, }: MultiSectionMobileProps): import("react/jsx-runtime").JSX.Element;
@@ -11563,6 +11563,22 @@ const STYLES$1 = `
11563
11563
  @keyframes ps-tryon-badge-spin {
11564
11564
  to { transform: rotate(360deg); }
11565
11565
  }
11566
+ /* Mobile sizing for the try-on badge — desktop's max-width: 420px lives
11567
+ inside a roughly 360 px image hero, leaving the pill awkwardly clipped.
11568
+ Stretch it edge-to-edge with smaller text so it always fits. */
11569
+ @media (max-width: 768px) {
11570
+ .ps-tryon-badge {
11571
+ left: 12px; right: 12px; bottom: 12px;
11572
+ max-width: none;
11573
+ padding: 12px 14px;
11574
+ gap: 8px;
11575
+ border-radius: 12px;
11576
+ }
11577
+ .ps-tryon-badge-row { gap: 10px; }
11578
+ .ps-tryon-badge-title { font-size: 13px; }
11579
+ .ps-tryon-badge-pct { font-size: 11px; padding: 3px 9px; }
11580
+ .ps-tryon-badge-foot { font-size: 11px; gap: 8px; }
11581
+ }
11566
11582
 
11567
11583
  /* ── Product photo strip (single-garment, below the size card) ──
11568
11584
  Three thumbnails per slide, evenly spaced, auto-advances. Lives at
@@ -15316,9 +15332,12 @@ const STYLES$1 = `
15316
15332
  text-align: center; padding: 4px 12px;
15317
15333
  min-height: 64px;
15318
15334
  /* Lock width so the cycling stage text (shorter/longer strings every
15319
- ~2s) doesn't reflow the sibling TryOnProgress bar. */
15335
+ ~2s) doesn't reflow the sibling TryOnProgress bar. align-self:
15336
+ center centers the 320px container inside the flex column — without
15337
+ it the container sat at the left edge of the screen on mobile. */
15320
15338
  width: 320px;
15321
15339
  max-width: 100%;
15340
+ align-self: center;
15322
15341
  overflow: hidden;
15323
15342
  }
15324
15343
  .ps-msc-stage-slot {
@@ -15403,11 +15422,23 @@ const STYLES$1 = `
15403
15422
  .ps-msr-fit-toggle[aria-pressed="true"] {
15404
15423
  background: var(--ps-accent); color: #FFFFFF;
15405
15424
  }
15406
- /* Bottom action row when try-on finished — Try Again + Continue Shopping */
15425
+ /* Bottom action row when try-on finished — Try Again + Continue Shopping.
15426
+ min-width: 0 lets each button actually shrink to share the row equally
15427
+ on narrow viewports; white-space: nowrap stops "Continue Shopping"
15428
+ from wrapping to a second line. */
15407
15429
  .ps-msr-bottom-row {
15408
- display: flex; gap: 10px; align-items: stretch;
15430
+ display: flex; gap: 8px; align-items: stretch;
15431
+ }
15432
+ .ps-msr-bottom-row .ps-msr-tryon-cta {
15433
+ flex: 1 1 0; min-width: 0;
15434
+ padding: 14px 12px; font-size: 13px;
15435
+ white-space: nowrap;
15436
+ }
15437
+ @media (max-width: 360px) {
15438
+ .ps-msr-bottom-row .ps-msr-tryon-cta {
15439
+ padding: 12px 10px; font-size: 12px;
15440
+ }
15409
15441
  }
15410
- .ps-msr-bottom-row .ps-msr-tryon-cta { flex: 1; }
15411
15442
  .ps-msr-tryon-cta.ps-secondary {
15412
15443
  background: transparent;
15413
15444
  color: var(--ps-text-primary);
@@ -17477,6 +17508,11 @@ const STYLES$1 = `
17477
17508
 
17478
17509
  /* Big product / try-on image */
17479
17510
  .ps-msd-image {
17511
+ /* position: relative is required so the absolute children
17512
+ (.ps-msr-fit-toggle, .ps-tryon-badge) anchor to the image instead of
17513
+ the viewport. Without it the Show Fit pill rendered bottom-left of
17514
+ the screen on mobile. */
17515
+ position: relative;
17480
17516
  width: 100%; height: 420px;
17481
17517
  border-radius: 12px; overflow: hidden;
17482
17518
  background: var(--ps-bg-secondary);
@@ -17517,6 +17553,18 @@ const STYLES$1 = `
17517
17553
  letter-spacing: 0.05em;
17518
17554
  color: var(--ps-text-muted);
17519
17555
  }
17556
+ /* Mobile: long labels like "MISSY 12 / Standard" wrap to a second line
17557
+ at 36 px in a ~280 px card. Drop further to 28 px and let the size
17558
+ pill take the full card width so wrapping doesn't strand the meta
17559
+ value awkwardly to the side. */
17560
+ @media (max-width: 768px) {
17561
+ .ps-msd-card-size {
17562
+ font-size: 28px;
17563
+ letter-spacing: -0.015em;
17564
+ line-height: 1.1;
17565
+ }
17566
+ .ps-msd-card-size-meta { font-size: 11px; }
17567
+ }
17520
17568
  .ps-msd-card-divider {
17521
17569
  height: 1px; background: var(--ps-border-subtle);
17522
17570
  margin: 6px 0;
@@ -17885,6 +17933,34 @@ const STYLES$1 = `
17885
17933
  color: var(--ps-accent);
17886
17934
  }
17887
17935
 
17936
+ /* Mobile px-based override — desktop rules use vw units which collapse
17937
+ to 1–4 px on a 375 px viewport. */
17938
+ @media (max-width: 768px) {
17939
+ .ps-bp-mini-select-trigger {
17940
+ padding: 10px 14px;
17941
+ font-size: 14px;
17942
+ border-radius: 8px;
17943
+ gap: 8px;
17944
+ min-width: 0;
17945
+ }
17946
+ .ps-bp-mini-select-arrow { font-size: 10px; }
17947
+ .ps-bp-mini-select-panel {
17948
+ max-height: 240px;
17949
+ border-radius: 10px;
17950
+ padding: 4px;
17951
+ box-shadow:
17952
+ 0 4px 12px rgba(15, 23, 42, 0.10),
17953
+ 0 12px 32px rgba(15, 23, 42, 0.16);
17954
+ }
17955
+ .ps-bp-mini-select[data-dir="down"] .ps-bp-mini-select-panel { top: calc(100% + 6px); }
17956
+ .ps-bp-mini-select[data-dir="up"] .ps-bp-mini-select-panel { bottom: calc(100% + 6px); }
17957
+ .ps-bp-mini-select-item {
17958
+ padding: 10px 12px;
17959
+ font-size: 14px;
17960
+ border-radius: 6px;
17961
+ }
17962
+ }
17963
+
17888
17964
  .ps-bp-bra-region-wrap { position: relative; z-index: 100; }
17889
17965
  .ps-bp-bra-region-trigger {
17890
17966
  display: flex; align-items: center; gap: 0.35vw;
@@ -18998,47 +19074,11 @@ function useIsMobile() {
18998
19074
  }, []);
18999
19075
  return isMobile;
19000
19076
  }
19001
- function RulerIcon() {
19002
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", width: "13", height: "13", children: [
19003
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 17l6 6 12-12-6-6z" }),
19004
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9 11l2 2M11 9l2 2M13 7l2 2M7 13l2 2" })
19005
- ] });
19006
- }
19007
- function ScanIcon() {
19008
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", width: "13", height: "13", children: [
19009
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2" }),
19010
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "3" })
19011
- ] });
19012
- }
19013
- function MobileBottomTabs({ mode, onSwitchToManual, onSwitchToScan, t: t2 }) {
19014
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bpm-bottom-tabs", children: [
19015
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
19016
- "button",
19017
- {
19018
- type: "button",
19019
- className: `ps-bpm-bottom-tab${mode === "manual" ? " ps-active" : ""}`,
19020
- onClick: onSwitchToManual,
19021
- children: [
19022
- /* @__PURE__ */ jsxRuntimeExports.jsx(RulerIcon, {}),
19023
- t2("MANUAL FIT")
19024
- ]
19025
- }
19026
- ),
19027
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
19028
- "button",
19029
- {
19030
- type: "button",
19031
- className: `ps-bpm-bottom-tab${mode === "scan" ? " ps-active" : ""}`,
19032
- onClick: onSwitchToScan,
19033
- children: [
19034
- /* @__PURE__ */ jsxRuntimeExports.jsx(ScanIcon, {}),
19035
- t2("AI SCAN")
19036
- ]
19037
- }
19038
- )
19039
- ] });
19040
- }
19041
19077
  const SKELETON_CONNECTIONS$1 = [
19078
+ // Head → torso. Bridges the gap between the nose dot and the shoulder
19079
+ // line — without these the head reads as floating above the body.
19080
+ ["nose", "leftShoulder"],
19081
+ ["nose", "rightShoulder"],
19042
19082
  ["leftShoulder", "rightShoulder"],
19043
19083
  ["leftShoulder", "leftElbow"],
19044
19084
  ["leftElbow", "leftWrist"],
@@ -19052,6 +19092,14 @@ const SKELETON_CONNECTIONS$1 = [
19052
19092
  ["rightHip", "rightKnee"],
19053
19093
  ["rightKnee", "rightAnkle"]
19054
19094
  ];
19095
+ function isVisible(p2) {
19096
+ if (!p2 || typeof p2 !== "object") return false;
19097
+ const pt2 = p2;
19098
+ if (typeof pt2.x !== "number" || typeof pt2.y !== "number") return false;
19099
+ if (typeof pt2.visibility === "number" && pt2.visibility < 0.3) return false;
19100
+ if (pt2.x < 1e-3 && pt2.y < 1e-3) return false;
19101
+ return true;
19102
+ }
19055
19103
  function MobileSkeleton({ landmarks, w: w2, h }) {
19056
19104
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
19057
19105
  "svg",
@@ -19063,7 +19111,7 @@ function MobileSkeleton({ landmarks, w: w2, h }) {
19063
19111
  SKELETON_CONNECTIONS$1.map(([a, b], i) => {
19064
19112
  const pa2 = landmarks[a];
19065
19113
  const pb2 = landmarks[b];
19066
- if (!pa2 || !pb2) return null;
19114
+ if (!isVisible(pa2) || !isVisible(pb2)) return null;
19067
19115
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
19068
19116
  "line",
19069
19117
  {
@@ -19073,14 +19121,12 @@ function MobileSkeleton({ landmarks, w: w2, h }) {
19073
19121
  y2: pb2.y * h,
19074
19122
  stroke: "rgba(100,210,255,0.9)",
19075
19123
  strokeWidth: "4",
19076
- strokeLinecap: "round",
19077
- opacity: 0,
19078
- style: { animation: `ps-msc-fade 0.4s ease ${i * 0.05}s forwards` }
19124
+ strokeLinecap: "round"
19079
19125
  },
19080
19126
  `l-${i}`
19081
19127
  );
19082
19128
  }),
19083
- Object.entries(landmarks).filter(([, v2]) => v2 && typeof v2 === "object" && typeof v2.x === "number" && typeof v2.y === "number").map(([key, v2], i) => {
19129
+ Object.entries(landmarks).filter(([, v2]) => isVisible(v2)).map(([key, v2]) => {
19084
19130
  const p2 = v2;
19085
19131
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
19086
19132
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -19088,10 +19134,8 @@ function MobileSkeleton({ landmarks, w: w2, h }) {
19088
19134
  {
19089
19135
  cx: p2.x * w2,
19090
19136
  cy: p2.y * h,
19091
- r: 10,
19092
- fill: "rgba(100,210,255,0.25)",
19093
- opacity: 0,
19094
- style: { animation: `ps-msc-fade 0.3s ease ${i * 0.04}s forwards` }
19137
+ r: 14,
19138
+ fill: "rgba(100,210,255,0.30)"
19095
19139
  }
19096
19140
  ),
19097
19141
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -19099,10 +19143,8 @@ function MobileSkeleton({ landmarks, w: w2, h }) {
19099
19143
  {
19100
19144
  cx: p2.x * w2,
19101
19145
  cy: p2.y * h,
19102
- r: 6,
19103
- fill: "rgba(100,210,255,0.95)",
19104
- opacity: 0,
19105
- style: { animation: `ps-msc-fade 0.3s ease ${i * 0.04}s forwards` }
19146
+ r: 9,
19147
+ fill: "rgba(100,210,255,0.95)"
19106
19148
  }
19107
19149
  )
19108
19150
  ] }, key);
@@ -19120,7 +19162,8 @@ function MobileScanningView({
19120
19162
  productMaterial,
19121
19163
  productDescription,
19122
19164
  onCancelTryOn,
19123
- onSwitchToManual,
19165
+ // onSwitchToManual is intentionally not destructured — bottom tabs are
19166
+ // hidden during analysis and the prop is kept on the type for callers.
19124
19167
  t: t2
19125
19168
  }) {
19126
19169
  const displayImage = previewUrl || productImage || "";
@@ -19179,17 +19222,100 @@ function MobileScanningView({
19179
19222
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-title", children: current.title }),
19180
19223
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
19181
19224
  ] }, stageIdx) }),
19182
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-bpm-spacer" }),
19183
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-bpm-bottom", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
19184
- MobileBottomTabs,
19185
- {
19186
- mode: "scan",
19187
- onSwitchToManual,
19188
- onSwitchToScan: () => {
19189
- },
19190
- t: t2
19191
- }
19192
- ) })
19225
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-bpm-spacer" })
19226
+ ] });
19227
+ }
19228
+ const TARGET_SECONDS = 22;
19229
+ function TryOnGenerationBadge({
19230
+ tryOnStartedAt,
19231
+ t: t2
19232
+ }) {
19233
+ const [, force] = reactExports.useState(0);
19234
+ reactExports.useEffect(() => {
19235
+ if (tryOnStartedAt == null) return;
19236
+ const id2 = setInterval(() => force((v2) => v2 + 1), 200);
19237
+ return () => clearInterval(id2);
19238
+ }, [tryOnStartedAt]);
19239
+ if (tryOnStartedAt == null) return null;
19240
+ const elapsed = (Date.now() - tryOnStartedAt) / 1e3;
19241
+ const pct = Math.min(95, elapsed / TARGET_SECONDS * 100);
19242
+ const pctRounded = Math.round(pct);
19243
+ const remaining = Math.max(0, TARGET_SECONDS - Math.floor(elapsed));
19244
+ const etaText = elapsed >= TARGET_SECONDS ? t2("almost done") : `~${remaining}s ${t2("left")}`;
19245
+ const statuses = [
19246
+ t2("Preparing your image"),
19247
+ t2("Analyzing body proportions"),
19248
+ t2("Mapping garment to body"),
19249
+ t2("Refining drape and shadows"),
19250
+ t2("Almost done")
19251
+ ];
19252
+ const stepIdx = Math.min(
19253
+ statuses.length - 1,
19254
+ Math.floor(elapsed / TARGET_SECONDS * statuses.length)
19255
+ );
19256
+ const status = statuses[stepIdx];
19257
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-badge", role: "status", "aria-live": "polite", children: [
19258
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-badge-row", children: [
19259
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-badge-spinner", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 24 24", width: "22", height: "22", children: [
19260
+ /* @__PURE__ */ jsxRuntimeExports.jsx("defs", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("linearGradient", { id: "ps-tryon-badge-grad", x1: "0", y1: "0", x2: "1", y2: "1", children: [
19261
+ /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: "#3B82F6" }),
19262
+ /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: "#2563EB" })
19263
+ ] }) }),
19264
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "9", fill: "none", stroke: "rgba(59,130,246,0.18)", strokeWidth: "2.4" }),
19265
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
19266
+ "circle",
19267
+ {
19268
+ cx: "12",
19269
+ cy: "12",
19270
+ r: "9",
19271
+ fill: "none",
19272
+ stroke: "url(#ps-tryon-badge-grad)",
19273
+ strokeWidth: "2.4",
19274
+ strokeLinecap: "round",
19275
+ strokeDasharray: "56.5",
19276
+ strokeDashoffset: "38"
19277
+ }
19278
+ )
19279
+ ] }) }),
19280
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-badge-title", children: t2("Generating your look...") }),
19281
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ps-tryon-badge-pct", children: [
19282
+ pctRounded,
19283
+ "%"
19284
+ ] })
19285
+ ] }),
19286
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-badge-bar", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-badge-bar-fill", style: { width: `${pctRounded}%` } }) }),
19287
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-badge-foot", children: [
19288
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ps-tryon-badge-status", children: [
19289
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-badge-status-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
19290
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6", y1: "10", x2: "6", y2: "14" }),
19291
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "6", x2: "10", y2: "18" }),
19292
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "9", x2: "14", y2: "15" }),
19293
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "18", y1: "11", x2: "18", y2: "13" })
19294
+ ] }) }),
19295
+ status
19296
+ ] }),
19297
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ps-tryon-badge-eta", children: [
19298
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
19299
+ "svg",
19300
+ {
19301
+ width: "11",
19302
+ height: "11",
19303
+ viewBox: "0 0 24 24",
19304
+ fill: "none",
19305
+ stroke: "currentColor",
19306
+ strokeWidth: "2",
19307
+ strokeLinecap: "round",
19308
+ strokeLinejoin: "round",
19309
+ "aria-hidden": "true",
19310
+ children: [
19311
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "10" }),
19312
+ /* @__PURE__ */ jsxRuntimeExports.jsx("polyline", { points: "12 6 12 12 16 14" })
19313
+ ]
19314
+ }
19315
+ ),
19316
+ etaText
19317
+ ] })
19318
+ ] })
19193
19319
  ] });
19194
19320
  }
19195
19321
  function garmentIconForSection$1(name) {
@@ -19223,6 +19349,7 @@ function MultiSectionMobile({
19223
19349
  onSelectSection,
19224
19350
  onTryOn,
19225
19351
  tryOnProcessing,
19352
+ tryOnStartedAt,
19226
19353
  resultImageUrl,
19227
19354
  tryOnDone,
19228
19355
  onTryAgain,
@@ -19268,6 +19395,7 @@ function MultiSectionMobile({
19268
19395
  }
19269
19396
  ),
19270
19397
  showLines && overlayNode,
19398
+ tryOnProcessing && tryOnStartedAt != null && /* @__PURE__ */ jsxRuntimeExports.jsx(TryOnGenerationBadge, { tryOnStartedAt, t: t2 }),
19271
19399
  showingTryOn && onToggleLines && /* @__PURE__ */ jsxRuntimeExports.jsxs(
19272
19400
  "button",
19273
19401
  {
@@ -19388,99 +19516,6 @@ function MultiSectionMobile({
19388
19516
  sizeGuide ? null : null
19389
19517
  ] });
19390
19518
  }
19391
- const TARGET_SECONDS = 22;
19392
- function TryOnGenerationBadge({
19393
- tryOnStartedAt,
19394
- t: t2
19395
- }) {
19396
- const [, force] = reactExports.useState(0);
19397
- reactExports.useEffect(() => {
19398
- if (tryOnStartedAt == null) return;
19399
- const id2 = setInterval(() => force((v2) => v2 + 1), 200);
19400
- return () => clearInterval(id2);
19401
- }, [tryOnStartedAt]);
19402
- if (tryOnStartedAt == null) return null;
19403
- const elapsed = (Date.now() - tryOnStartedAt) / 1e3;
19404
- const pct = Math.min(95, elapsed / TARGET_SECONDS * 100);
19405
- const pctRounded = Math.round(pct);
19406
- const remaining = Math.max(0, TARGET_SECONDS - Math.floor(elapsed));
19407
- const etaText = elapsed >= TARGET_SECONDS ? t2("almost done") : `~${remaining}s ${t2("left")}`;
19408
- const statuses = [
19409
- t2("Preparing your image"),
19410
- t2("Analyzing body proportions"),
19411
- t2("Mapping garment to body"),
19412
- t2("Refining drape and shadows"),
19413
- t2("Almost done")
19414
- ];
19415
- const stepIdx = Math.min(
19416
- statuses.length - 1,
19417
- Math.floor(elapsed / TARGET_SECONDS * statuses.length)
19418
- );
19419
- const status = statuses[stepIdx];
19420
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-badge", role: "status", "aria-live": "polite", children: [
19421
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-badge-row", children: [
19422
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-badge-spinner", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 24 24", width: "22", height: "22", children: [
19423
- /* @__PURE__ */ jsxRuntimeExports.jsx("defs", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("linearGradient", { id: "ps-tryon-badge-grad", x1: "0", y1: "0", x2: "1", y2: "1", children: [
19424
- /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: "#3B82F6" }),
19425
- /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: "#2563EB" })
19426
- ] }) }),
19427
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "9", fill: "none", stroke: "rgba(59,130,246,0.18)", strokeWidth: "2.4" }),
19428
- /* @__PURE__ */ jsxRuntimeExports.jsx(
19429
- "circle",
19430
- {
19431
- cx: "12",
19432
- cy: "12",
19433
- r: "9",
19434
- fill: "none",
19435
- stroke: "url(#ps-tryon-badge-grad)",
19436
- strokeWidth: "2.4",
19437
- strokeLinecap: "round",
19438
- strokeDasharray: "56.5",
19439
- strokeDashoffset: "38"
19440
- }
19441
- )
19442
- ] }) }),
19443
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-badge-title", children: t2("Generating your look...") }),
19444
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ps-tryon-badge-pct", children: [
19445
- pctRounded,
19446
- "%"
19447
- ] })
19448
- ] }),
19449
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-badge-bar", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-badge-bar-fill", style: { width: `${pctRounded}%` } }) }),
19450
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-badge-foot", children: [
19451
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ps-tryon-badge-status", children: [
19452
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-badge-status-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
19453
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6", y1: "10", x2: "6", y2: "14" }),
19454
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "6", x2: "10", y2: "18" }),
19455
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "9", x2: "14", y2: "15" }),
19456
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "18", y1: "11", x2: "18", y2: "13" })
19457
- ] }) }),
19458
- status
19459
- ] }),
19460
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ps-tryon-badge-eta", children: [
19461
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
19462
- "svg",
19463
- {
19464
- width: "11",
19465
- height: "11",
19466
- viewBox: "0 0 24 24",
19467
- fill: "none",
19468
- stroke: "currentColor",
19469
- strokeWidth: "2",
19470
- strokeLinecap: "round",
19471
- strokeLinejoin: "round",
19472
- "aria-hidden": "true",
19473
- children: [
19474
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "10" }),
19475
- /* @__PURE__ */ jsxRuntimeExports.jsx("polyline", { points: "12 6 12 12 16 14" })
19476
- ]
19477
- }
19478
- ),
19479
- etaText
19480
- ] })
19481
- ] })
19482
- ] });
19483
- }
19484
19519
  const PER_SLIDE = 3;
19485
19520
  const CYCLE_MS = 4e3;
19486
19521
  function ProductPhotoCarouselCard({
@@ -20253,6 +20288,7 @@ function SectionDetailView({
20253
20288
  }
20254
20289
  ),
20255
20290
  showLines && overlayNode,
20291
+ tryOnProcessing && tryOnStartedAt != null && /* @__PURE__ */ jsxRuntimeExports.jsx(TryOnGenerationBadge, { tryOnStartedAt, t: t2 }),
20256
20292
  isTryOnImage && onToggleLines && /* @__PURE__ */ jsxRuntimeExports.jsxs(
20257
20293
  "button",
20258
20294
  {
@@ -20437,14 +20473,14 @@ function SectionDetailView({
20437
20473
  " ",
20438
20474
  t2("based on your measurements and the garment's tailoring chart.")
20439
20475
  ] }),
20440
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: 10, padding: "12px 0 4px", flexShrink: 0 }, children: [
20476
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: 10, padding: "12px 0 4px", flexShrink: 0, alignItems: "center" }, children: [
20441
20477
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
20442
20478
  "button",
20443
20479
  {
20444
20480
  type: "button",
20445
20481
  className: "ps-bp-back-btn",
20446
20482
  onClick: onBack,
20447
- style: { fontSize: 13, padding: "10px 6px" },
20483
+ style: { fontSize: 13, padding: "10px 6px", flexShrink: 0 },
20448
20484
  children: [
20449
20485
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-bp-back-arrow", children: "←" }),
20450
20486
  " ",
@@ -20452,7 +20488,6 @@ function SectionDetailView({
20452
20488
  ]
20453
20489
  }
20454
20490
  ),
20455
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { flex: 1 } }),
20456
20491
  onTryOn ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
20457
20492
  "button",
20458
20493
  {
@@ -20460,7 +20495,8 @@ function SectionDetailView({
20460
20495
  onClick: onTryOn,
20461
20496
  disabled: tryOnProcessing,
20462
20497
  style: {
20463
- flex: 1,
20498
+ flex: "1 1 0",
20499
+ minWidth: 0,
20464
20500
  padding: "12px 16px",
20465
20501
  borderRadius: 10,
20466
20502
  background: "var(--ps-accent)",
@@ -20474,7 +20510,8 @@ function SectionDetailView({
20474
20510
  display: "flex",
20475
20511
  alignItems: "center",
20476
20512
  justifyContent: "center",
20477
- gap: 6
20513
+ gap: 6,
20514
+ whiteSpace: "nowrap"
20478
20515
  },
20479
20516
  children: [
20480
20517
  /* @__PURE__ */ jsxRuntimeExports.jsx(CameraIcon$1, { size: 14 }),
@@ -20487,7 +20524,8 @@ function SectionDetailView({
20487
20524
  type: "button",
20488
20525
  onClick: onBack,
20489
20526
  style: {
20490
- flex: 1,
20527
+ flex: "1 1 0",
20528
+ minWidth: 0,
20491
20529
  padding: "12px 16px",
20492
20530
  borderRadius: 10,
20493
20531
  background: "var(--ps-accent)",
@@ -20500,7 +20538,8 @@ function SectionDetailView({
20500
20538
  display: "flex",
20501
20539
  alignItems: "center",
20502
20540
  justifyContent: "center",
20503
- gap: 6
20541
+ gap: 6,
20542
+ whiteSpace: "nowrap"
20504
20543
  },
20505
20544
  children: [
20506
20545
  continueLabel || t2("Continue"),
@@ -21295,6 +21334,7 @@ function SizeResultView({
21295
21334
  setShowPhotoGuide(true);
21296
21335
  },
21297
21336
  tryOnProcessing,
21337
+ tryOnStartedAt,
21298
21338
  resultImageUrl,
21299
21339
  tryOnDone: !!resultImageUrl && !tryOnProcessing,
21300
21340
  onTryAgain: () => {
@@ -24708,6 +24748,46 @@ function ResultView({ setView }) {
24708
24748
  }, []);
24709
24749
  return null;
24710
24750
  }
24751
+ function RulerIcon() {
24752
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", width: "13", height: "13", children: [
24753
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 17l6 6 12-12-6-6z" }),
24754
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9 11l2 2M11 9l2 2M13 7l2 2M7 13l2 2" })
24755
+ ] });
24756
+ }
24757
+ function ScanIcon() {
24758
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", width: "13", height: "13", children: [
24759
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2" }),
24760
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "3" })
24761
+ ] });
24762
+ }
24763
+ function MobileBottomTabs({ mode, onSwitchToManual, onSwitchToScan, t: t2 }) {
24764
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bpm-bottom-tabs", children: [
24765
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
24766
+ "button",
24767
+ {
24768
+ type: "button",
24769
+ className: `ps-bpm-bottom-tab${mode === "manual" ? " ps-active" : ""}`,
24770
+ onClick: onSwitchToManual,
24771
+ children: [
24772
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RulerIcon, {}),
24773
+ t2("MANUAL FIT")
24774
+ ]
24775
+ }
24776
+ ),
24777
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
24778
+ "button",
24779
+ {
24780
+ type: "button",
24781
+ className: `ps-bpm-bottom-tab${mode === "scan" ? " ps-active" : ""}`,
24782
+ onClick: onSwitchToScan,
24783
+ children: [
24784
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ScanIcon, {}),
24785
+ t2("AI SCAN")
24786
+ ]
24787
+ }
24788
+ )
24789
+ ] });
24790
+ }
24711
24791
  function CameraSmallIcon({ size = 16 }) {
24712
24792
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", width: size, height: size, children: [
24713
24793
  /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primestyleai/tryon",
3
- "version": "5.10.135",
3
+ "version": "5.10.137",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",