@primestyleai/tryon 3.15.0 → 3.17.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.
@@ -306,6 +306,7 @@ const en = {
306
306
  "tap to compare": "tap to compare",
307
307
  "Comparing size": "Comparing size",
308
308
  "Fit Analysis": "Fit Analysis",
309
+ "Show more": "Show more",
309
310
  "Done": "Done",
310
311
  // ── Try-on result ───────────────────────────────────
311
312
  "Try-on result": "Try-on result",
@@ -1,5 +1,5 @@
1
- import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-B8Dg-nJ7.js";
2
- import { P, b, T, d, r } from "./index-B8Dg-nJ7.js";
1
+ import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-DvAzTRFF.js";
2
+ import { P, b, T, d, r } from "./index-DvAzTRFF.js";
3
3
  function detectProductImage() {
4
4
  const ogImage = document.querySelector(
5
5
  'meta[property="og:image"]'
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useMemo, useRef, useCallback } from "react";
4
- import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage, P as PrimeStyleError, L as LOCALE_LABELS, b as SUPPORTED_LOCALES } from "../index-B8Dg-nJ7.js";
4
+ import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage, P as PrimeStyleError, L as LOCALE_LABELS, b as SUPPORTED_LOCALES } from "../index-DvAzTRFF.js";
5
5
  const HEADER_ALIASES = {
6
6
  // ── Size label columns (skipped during field derivation) ──
7
7
  size: "__size__",
@@ -406,20 +406,6 @@ const FALLBACK_FIELDS_MALE = [
406
406
  { key: "inseam", label: "Inseam", required: false, unit: "cm", placeholder: "e.g. 81", category: "body" },
407
407
  { key: "footLengthCm", label: "Foot length", required: false, unit: "cm", placeholder: "e.g. 27", category: "shoe" }
408
408
  ];
409
- const SIZING_COUNTRIES = [
410
- { code: "US", label: "United States" },
411
- { code: "UK", label: "United Kingdom" },
412
- { code: "EU", label: "Europe (EU)" },
413
- { code: "FR", label: "France" },
414
- { code: "IT", label: "Italy" },
415
- { code: "DE", label: "Germany" },
416
- { code: "ES", label: "Spain" },
417
- { code: "JP", label: "Japan" },
418
- { code: "CN", label: "China" },
419
- { code: "KR", label: "South Korea" },
420
- { code: "AU", label: "Australia" },
421
- { code: "BR", label: "Brazil" }
422
- ];
423
409
  const STEP_LABELS = ["", "Welcome", "Size", "Your Fit", "Try On"];
424
410
  const TOTAL_STEPS = 4;
425
411
  function detectLocale() {
@@ -840,7 +826,115 @@ function PrimeStyleTryonInner({
840
826
  }
841
827
  return formGender === "female" ? FALLBACK_FIELDS_FEMALE : FALLBACK_FIELDS_MALE;
842
828
  }, [sizeGuide, formGender]);
829
+ const computeSizingLocally = useCallback(() => {
830
+ if (sizingMethod !== "exact" || !sizeGuide?.found || !sizeGuide.headers || !sizeGuide.rows) return null;
831
+ const HEADER_MAP = {
832
+ chest: "chest",
833
+ bust: "bust",
834
+ waist: "waist",
835
+ hips: "hips",
836
+ hip: "hips",
837
+ shoulder: "shoulderWidth",
838
+ shoulders: "shoulderWidth",
839
+ "shoulder width": "shoulderWidth",
840
+ sleeve: "sleeveLength",
841
+ "sleeve length": "sleeveLength",
842
+ inseam: "inseam",
843
+ neck: "neckCircumference",
844
+ foot: "footLengthCm",
845
+ "foot length": "footLengthCm"
846
+ };
847
+ const INTL = /* @__PURE__ */ new Set(["eu", "us", "uk", "it", "fr", "de", "jp", "cn", "kr", "au", "br", "eur"]);
848
+ const userMeas = {};
849
+ for (const f of dynamicFields) {
850
+ if (f.unit === "size" || ["shoeEU", "shoeUS", "shoeUK"].includes(f.key)) continue;
851
+ const raw = formRef.current[f.key];
852
+ if (!raw) continue;
853
+ const v = parseFloat(raw);
854
+ if (isNaN(v)) continue;
855
+ userMeas[f.key] = sizingUnit === "in" ? inToCm(v) : v;
856
+ }
857
+ for (const k of ["chest", "bust", "waist", "hips", "shoulderWidth", "sleeveLength", "inseam", "neckCircumference", "footLengthCm"]) {
858
+ if (userMeas[k] || !formRef.current[k]) continue;
859
+ const v = parseFloat(formRef.current[k]);
860
+ if (!isNaN(v)) userMeas[k] = sizingUnit === "in" ? inToCm(v) : v;
861
+ }
862
+ if (Object.keys(userMeas).length === 0) return null;
863
+ let idx = sizeGuide.headers.findIndex((h) => /^size$/i.test(h.trim()));
864
+ if (idx < 0) idx = sizeGuide.headers.findIndex((h) => /size|taglia|größe|taille/i.test(h.trim()));
865
+ if (idx < 0) {
866
+ const firstColVals = sizeGuide.rows.map((r) => r[0]?.trim() || "");
867
+ idx = firstColVals.some((v) => /^(XXS|XS|S|M|L|XL|XXL|XXXL|\d{1,3})$/i.test(v)) ? 0 : 0;
868
+ }
869
+ const colMap = [];
870
+ const intlCols = [];
871
+ sizeGuide.headers.forEach((h, i) => {
872
+ if (i === idx) return;
873
+ const lower = h.toLowerCase().trim().replace(/\s*\(.*\)/, "");
874
+ const clean = lower.replace(/\s*size\s*/gi, "").trim();
875
+ if (INTL.has(clean)) {
876
+ intlCols.push({ hi: i, code: clean.toUpperCase() });
877
+ return;
878
+ }
879
+ let fk = HEADER_MAP[clean];
880
+ if (!fk) {
881
+ const pk = Object.keys(HEADER_MAP).find((k) => clean.includes(k));
882
+ if (pk) fk = HEADER_MAP[pk];
883
+ }
884
+ if (fk && userMeas[fk] !== void 0) colMap.push({ hi: i, formKey: fk, label: h.trim() });
885
+ });
886
+ if (colMap.length === 0) return null;
887
+ const parseRange = (cell) => {
888
+ const nums = cell.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map(Number).filter((n) => !isNaN(n));
889
+ if (nums.length === 0) return null;
890
+ return { min: Math.min(...nums), max: Math.max(...nums) };
891
+ };
892
+ const scores = [];
893
+ for (const row of sizeGuide.rows) {
894
+ const label = row[idx] || "";
895
+ let fitting = 0;
896
+ let total = 0;
897
+ let dist = 0;
898
+ const details = [];
899
+ for (const col of colMap) {
900
+ const range = parseRange(row[col.hi] || "");
901
+ if (!range) continue;
902
+ total++;
903
+ const uv = userMeas[col.formKey];
904
+ const fit = uv >= range.min && uv <= range.max ? "good" : uv < range.min ? "tight" : "loose";
905
+ if (fit === "good") fitting++;
906
+ dist += Math.abs(uv - (range.min + range.max) / 2);
907
+ const dispVal = sizingUnit === "in" ? `${cmToIn(uv)} in` : `${Math.round(uv)} cm`;
908
+ const dispRange = sizingUnit === "in" ? `${cmToIn(range.min)}–${cmToIn(range.max)} in` : `${Math.round(range.min)}–${Math.round(range.max)} cm`;
909
+ details.push({ measurement: col.label, userValue: dispVal, chartRange: dispRange, fit });
910
+ }
911
+ if (total === 0) continue;
912
+ const intl = {};
913
+ for (const ic of intlCols) {
914
+ if (row[ic.hi]) intl[ic.code] = row[ic.hi];
915
+ }
916
+ scores.push({ label, fitting, total, dist, details, intl, row });
917
+ }
918
+ if (scores.length === 0) return null;
919
+ scores.sort((a, b) => b.fitting - a.fitting || a.dist - b.dist);
920
+ const best = scores[0];
921
+ const conf = best.fitting === best.total ? "high" : best.fitting >= best.total * 0.6 ? "medium" : "low";
922
+ return {
923
+ recommendedSize: best.label,
924
+ confidence: conf,
925
+ reasoning: `Based on your measurements, size ${best.label} is the best fit.`,
926
+ internationalSizes: best.intl,
927
+ matchDetails: best.details,
928
+ method: "deterministic"
929
+ };
930
+ }, [sizingMethod, sizeGuide, dynamicFields, sizingUnit]);
843
931
  const submitSizing = useCallback(async () => {
932
+ const localResult = computeSizingLocally();
933
+ if (localResult) {
934
+ setSizingResult(localResult);
935
+ setSizingLoading(false);
936
+ return;
937
+ }
844
938
  if (!apiRef.current) return;
845
939
  const baseUrl = getApiUrl(apiUrl);
846
940
  const key = getApiKey();
@@ -899,7 +993,7 @@ function PrimeStyleTryonInner({
899
993
  } finally {
900
994
  setSizingLoading(false);
901
995
  }
902
- }, [apiUrl, sizingMethod, sizingCountry, heightUnit, weightUnit, sizingUnit, sizeGuide, productTitle, dynamicFields]);
996
+ }, [apiUrl, sizingMethod, sizingCountry, heightUnit, weightUnit, sizingUnit, sizeGuide, productTitle, dynamicFields, computeSizingLocally]);
903
997
  const handleTryOnSubmit = useCallback(async () => {
904
998
  if (!selectedFile || !apiRef.current || !sseRef.current) {
905
999
  const msg = !apiRef.current ? t("SDK not configured. Please provide an API key.") : t("Something went wrong");
@@ -1339,10 +1433,6 @@ function PrimeStyleTryonInner({
1339
1433
  ")"
1340
1434
  ] }, p.id))
1341
1435
  ] }) }),
1342
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-input-row", children: [
1343
- /* @__PURE__ */ jsx("label", { children: t("Sizing region") }),
1344
- /* @__PURE__ */ jsx("select", { className: "ps-tryon-country-select", value: sizingCountry, onChange: (e) => setSizingCountry(e.target.value), children: SIZING_COUNTRIES.map((c) => /* @__PURE__ */ jsx("option", { value: c.code, children: t(c.label) }, c.code)) })
1345
- ] }),
1346
1436
  sizingMethod === "exact" && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-unit-tabs", children: [
1347
1437
  /* @__PURE__ */ jsx("button", { className: `ps-tryon-unit-tab${isCm ? " ps-active" : ""}`, onClick: () => {
1348
1438
  setSizingUnit("cm");
@@ -1515,8 +1605,14 @@ function PrimeStyleTryonInner({
1515
1605
  foot: "footLengthCm",
1516
1606
  "foot length": "footLengthCm"
1517
1607
  };
1518
- const sizeColIdx = sizeGuide.headers.findIndex((h) => /^size$/i.test(h.trim()));
1519
- const idx = sizeColIdx >= 0 ? sizeColIdx : 0;
1608
+ let idx = sizeGuide.headers.findIndex((h) => /^size$/i.test(h.trim()));
1609
+ if (idx < 0) idx = sizeGuide.headers.findIndex((h) => /size|taglia|größe|taille/i.test(h.trim()));
1610
+ if (idx < 0) {
1611
+ const firstColVals = sizeGuide.rows.map((r) => r[0]?.trim() || "");
1612
+ const looksLikeSize = firstColVals.some((v) => /^(XXS|XS|S|M|L|XL|XXL|XXXL|\d{1,3})$/i.test(v));
1613
+ idx = looksLikeSize ? 0 : -1;
1614
+ }
1615
+ if (idx < 0) idx = 0;
1520
1616
  const measCols = [];
1521
1617
  const intlCols = [];
1522
1618
  sizeGuide.headers.forEach((h, i) => {
@@ -1527,8 +1623,10 @@ function PrimeStyleTryonInner({
1527
1623
  intlCols.push({ headerIdx: i, code: clean.toUpperCase() });
1528
1624
  } else if (MEAS_MAP[clean]) {
1529
1625
  measCols.push({ headerIdx: i, label: h.trim(), formKey: MEAS_MAP[clean] });
1530
- } else if (lower !== "size") {
1531
- measCols.push({ headerIdx: i, label: h.trim(), formKey: lower.replace(/\s+/g, "") });
1626
+ } else if (clean && clean !== "size") {
1627
+ const partialKey = Object.keys(MEAS_MAP).find((k) => clean.includes(k));
1628
+ const fk = partialKey ? MEAS_MAP[partialKey] : clean.replace(/\s+/g, "");
1629
+ measCols.push({ headerIdx: i, label: h.trim(), formKey: fk });
1532
1630
  }
1533
1631
  });
1534
1632
  const sizes = sizeGuide.rows.map((row) => {
@@ -1547,7 +1645,7 @@ function PrimeStyleTryonInner({
1547
1645
  }
1548
1646
  return { label, measurements, intl };
1549
1647
  });
1550
- return { sizes, measCols, intlCols };
1648
+ return { sizes: sizes.filter((s) => s.label.trim()), measCols, intlCols };
1551
1649
  }, [sizeGuide]);
1552
1650
  const getUserVal = useCallback((formKey2) => {
1553
1651
  const edited = editedValues[formKey2];
@@ -1599,13 +1697,30 @@ function PrimeStyleTryonInner({
1599
1697
  /* @__PURE__ */ jsx("div", { className: `ps-tryon-sr-hero-conf ps-conf-${sizingResult.confidence}`, children: sizingResult.confidence === "high" ? t("High Confidence") : sizingResult.confidence === "medium" ? t("Medium Confidence") : t("Low Confidence") })
1600
1698
  ] })
1601
1699
  ] }),
1602
- Object.keys(activeIntl).length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl", children: [
1603
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Equivalent Sizes") }),
1604
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-intl-grid", children: Object.entries(activeIntl).map(([code, val]) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl-item", children: [
1605
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-code", children: code }),
1606
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-val", children: val })
1607
- ] }, code)) })
1608
- ] }),
1700
+ Object.keys(activeIntl).length > 0 && (() => {
1701
+ const PRIMARY_CODES = ["US", "UK", "EU", "IT", "FR", "DE"];
1702
+ const primary = PRIMARY_CODES.filter((c) => activeIntl[c]).map((c) => ({ code: c, val: activeIntl[c] }));
1703
+ const rest = Object.entries(activeIntl).filter(([c]) => !PRIMARY_CODES.includes(c)).map(([code, val]) => ({ code, val }));
1704
+ return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl", children: [
1705
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Equivalent Sizes") }),
1706
+ primary.length > 0 && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-intl-primary", children: primary.map((s) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl-card", children: [
1707
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-val", children: s.val }),
1708
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-code", children: s.code })
1709
+ ] }, s.code)) }),
1710
+ rest.length > 0 && /* @__PURE__ */ jsxs("details", { className: "ps-tryon-sr-intl-more", children: [
1711
+ /* @__PURE__ */ jsxs("summary", { className: "ps-tryon-sr-intl-more-btn", children: [
1712
+ t("Show more"),
1713
+ " (",
1714
+ rest.length,
1715
+ ")"
1716
+ ] }),
1717
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-intl-grid", children: rest.map((s) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl-item", children: [
1718
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-code", children: s.code }),
1719
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-val", children: s.val })
1720
+ ] }, s.code)) })
1721
+ ] })
1722
+ ] });
1723
+ })(),
1609
1724
  chartData && chartData.sizes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-sizes", children: [
1610
1725
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-label", children: [
1611
1726
  t("Size Chart"),
@@ -1633,49 +1748,72 @@ function PrimeStyleTryonInner({
1633
1748
  /* @__PURE__ */ jsx("strong", { children: activeSize })
1634
1749
  ] })
1635
1750
  ] }),
1636
- activeFit && activeFit.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit", children: [
1637
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Fit Analysis") }),
1638
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-table", children: [
1639
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-header", children: [
1640
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-area", children: t("Area") }),
1641
- /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-fit-col-you", children: [
1642
- t("You"),
1643
- " (",
1644
- unitLabel,
1645
- ")"
1646
- ] }),
1647
- /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-fit-col-chart", children: [
1648
- t("Chart"),
1649
- " (",
1650
- unitLabel,
1651
- ")"
1751
+ (() => {
1752
+ const hasDynamic = activeFit && activeFit.length > 0;
1753
+ const hasBackend = sizingResult.matchDetails && sizingResult.matchDetails.length > 0;
1754
+ if (hasDynamic) return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit", children: [
1755
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Fit Analysis") }),
1756
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-table", children: [
1757
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-header", children: [
1758
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-area", children: t("Area") }),
1759
+ /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-fit-col-you", children: [
1760
+ t("You"),
1761
+ " (",
1762
+ unitLabel,
1763
+ ")"
1764
+ ] }),
1765
+ /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-fit-col-chart", children: [
1766
+ t("Chart"),
1767
+ " (",
1768
+ unitLabel,
1769
+ ")"
1770
+ ] }),
1771
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: t("Fit") })
1652
1772
  ] }),
1653
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: t("Fit") })
1654
- ] }),
1655
- activeFit.map((row, i) => /* @__PURE__ */ jsxs("div", { className: `ps-tryon-sr-fit-row ps-fit-${row.fit}`, children: [
1656
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-area", children: row.label }),
1657
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-you", children: /* @__PURE__ */ jsx(
1658
- "input",
1659
- {
1660
- type: "number",
1661
- className: "ps-tryon-sr-fit-input",
1662
- defaultValue: isCmResult ? Math.round(row.userVal) : cmToIn(row.userVal),
1663
- onBlur: (e) => {
1664
- const val = e.target.value;
1665
- setEditedValues((prev) => ({ ...prev, [row.formKey]: val }));
1773
+ activeFit.map((row, i) => /* @__PURE__ */ jsxs("div", { className: `ps-tryon-sr-fit-row ps-fit-${row.fit}`, children: [
1774
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-area", children: row.label }),
1775
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-you", children: /* @__PURE__ */ jsx(
1776
+ "input",
1777
+ {
1778
+ type: "number",
1779
+ className: "ps-tryon-sr-fit-input",
1780
+ defaultValue: isCmResult ? Math.round(row.userVal) : cmToIn(row.userVal),
1781
+ onBlur: (e) => setEditedValues((prev) => ({ ...prev, [row.formKey]: e.target.value }))
1666
1782
  }
1667
- }
1668
- ) }),
1669
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-chart", children: fmtRange(row.chartMin, row.chartMax) }),
1670
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: /* @__PURE__ */ jsxs("span", { className: `ps-tryon-sr-fit-badge ps-fit-${row.fit}`, children: [
1671
- row.fit === "good" ? "✓" : row.fit === "tight" ? "↑" : "↓",
1672
- " ",
1673
- row.fit === "good" ? t("within range") : row.fit === "tight" ? t("may be snug") : t("may be loose")
1674
- ] }) })
1675
- ] }, i))
1676
- ] })
1677
- ] }),
1678
- (!activeFit || activeFit.length === 0) && sizingResult.reasoning && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-reasoning", children: /* @__PURE__ */ jsx("p", { children: sizingResult.reasoning }) }),
1783
+ ) }),
1784
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-chart", children: fmtRange(row.chartMin, row.chartMax) }),
1785
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: /* @__PURE__ */ jsxs("span", { className: `ps-tryon-sr-fit-badge ps-fit-${row.fit}`, children: [
1786
+ row.fit === "good" ? "" : row.fit === "tight" ? "↑" : "↓",
1787
+ " ",
1788
+ row.fit === "good" ? t("within range") : row.fit === "tight" ? t("may be snug") : t("may be loose")
1789
+ ] }) })
1790
+ ] }, i))
1791
+ ] })
1792
+ ] });
1793
+ if (hasBackend) return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit", children: [
1794
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Fit Analysis") }),
1795
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-table", children: [
1796
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-header", children: [
1797
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-area", children: t("Area") }),
1798
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-you", children: t("You") }),
1799
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-chart", children: t("Chart") }),
1800
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: t("Fit") })
1801
+ ] }),
1802
+ sizingResult.matchDetails.map((m, i) => /* @__PURE__ */ jsxs("div", { className: `ps-tryon-sr-fit-row ps-fit-${m.fit}`, children: [
1803
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-area", children: m.measurement }),
1804
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-you", children: m.userValue }),
1805
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-chart", children: m.chartRange }),
1806
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: /* @__PURE__ */ jsxs("span", { className: `ps-tryon-sr-fit-badge ps-fit-${m.fit}`, children: [
1807
+ m.fit === "good" ? "✓" : m.fit === "tight" ? "↑" : "↓",
1808
+ " ",
1809
+ m.fit === "good" ? t("within range") : m.fit === "tight" ? t("may be snug") : t("may be loose")
1810
+ ] }) })
1811
+ ] }, i))
1812
+ ] })
1813
+ ] });
1814
+ if (sizingResult.reasoning) return /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-reasoning", children: /* @__PURE__ */ jsx("p", { children: sizingResult.reasoning }) });
1815
+ return null;
1816
+ })(),
1679
1817
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-ctas", children: [
1680
1818
  /* @__PURE__ */ jsxs("button", { className: "ps-tryon-cta", onClick: () => setView("upload"), children: [
1681
1819
  t("See how it looks on you"),
@@ -2376,6 +2514,25 @@ const STYLES = `
2376
2514
  .ps-tryon-sr-intl { }
2377
2515
  .ps-tryon-sr-label { font-size: 0.78vw; font-weight: 700; color: #999; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 0.52vw; }
2378
2516
  .ps-tryon-sr-label-hint { font-weight: 400; text-transform: none; letter-spacing: 0; color: #666; font-style: italic; }
2517
+ .ps-tryon-sr-intl-primary { display: flex; flex-wrap: wrap; gap: 0.42vw; margin-bottom: 0.52vw; }
2518
+ .ps-tryon-sr-intl-card {
2519
+ flex: 1; min-width: 3.5vw; display: flex; flex-direction: column; align-items: center;
2520
+ padding: 0.57vw 0.42vw; border: 1.5px solid #333; border-radius: 0.63vw;
2521
+ background: #1a1b1a; transition: border-color 0.2s;
2522
+ }
2523
+ .ps-tryon-sr-intl-card:hover { border-color: #555; }
2524
+ .ps-tryon-sr-intl-card-val { font-size: 1.04vw; font-weight: 800; color: #fff; line-height: 1.2; }
2525
+ .ps-tryon-sr-intl-card-code { font-size: 0.57vw; font-weight: 700; color: #666; text-transform: uppercase; letter-spacing: 0.08em; margin-top: 0.1vw; }
2526
+
2527
+ .ps-tryon-sr-intl-more { }
2528
+ .ps-tryon-sr-intl-more-btn {
2529
+ font-size: 0.73vw; color: #bb945c; font-weight: 600; cursor: pointer;
2530
+ list-style: none; margin-bottom: 0.42vw; font-family: inherit;
2531
+ }
2532
+ .ps-tryon-sr-intl-more-btn::-webkit-details-marker { display: none; }
2533
+ .ps-tryon-sr-intl-more-btn::before { content: "▸ "; }
2534
+ .ps-tryon-sr-intl-more[open] .ps-tryon-sr-intl-more-btn::before { content: "▾ "; }
2535
+
2379
2536
  .ps-tryon-sr-intl-grid { display: flex; flex-wrap: wrap; gap: 0.42vw; }
2380
2537
  .ps-tryon-sr-intl-item {
2381
2538
  display: flex; align-items: center; border: 1.5px solid #333; border-radius: 0.52vw; overflow: hidden;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primestyleai/tryon",
3
- "version": "3.15.0",
3
+ "version": "3.17.0",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",