@primestyleai/tryon 3.18.0 → 3.19.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.
@@ -308,6 +308,9 @@ const en = {
308
308
  "Compare with another size": "Compare with another size",
309
309
  "recommended": "recommended",
310
310
  "size": "size",
311
+ "Your size in other countries": "Your size in other countries",
312
+ "Showing fit for size": "Showing fit for size",
313
+ "Back to": "Back to",
311
314
  "Fit Analysis": "Fit Analysis",
312
315
  "Show more": "Show more",
313
316
  "Done": "Done",
@@ -1,5 +1,5 @@
1
- import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-Lf4qO_jW.js";
2
- import { P, b, T, d, r } from "./index-Lf4qO_jW.js";
1
+ import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-DmAF7P54.js";
2
+ import { P, b, T, d, r } from "./index-DmAF7P54.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-Lf4qO_jW.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-DmAF7P54.js";
5
5
  const HEADER_ALIASES = {
6
6
  // ── Size label columns (skipped during field derivation) ──
7
7
  size: "__size__",
@@ -424,9 +424,6 @@ function detectLocale() {
424
424
  function isImperial(locale) {
425
425
  return locale === "US" || locale === "UK";
426
426
  }
427
- function cmToIn(cm) {
428
- return +(cm / 2.54).toFixed(1);
429
- }
430
427
  function inToCm(inches) {
431
428
  return +(inches * 2.54).toFixed(1);
432
429
  }
@@ -826,115 +823,7 @@ function PrimeStyleTryonInner({
826
823
  }
827
824
  return formGender === "female" ? FALLBACK_FIELDS_FEMALE : FALLBACK_FIELDS_MALE;
828
825
  }, [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]);
931
826
  const submitSizing = useCallback(async () => {
932
- const localResult = computeSizingLocally();
933
- if (localResult) {
934
- setSizingResult(localResult);
935
- setSizingLoading(false);
936
- return;
937
- }
938
827
  if (!apiRef.current) return;
939
828
  const baseUrl = getApiUrl(apiUrl);
940
829
  const key = getApiKey();
@@ -993,7 +882,7 @@ function PrimeStyleTryonInner({
993
882
  } finally {
994
883
  setSizingLoading(false);
995
884
  }
996
- }, [apiUrl, sizingMethod, sizingCountry, heightUnit, weightUnit, sizingUnit, sizeGuide, productTitle, dynamicFields, computeSizingLocally]);
885
+ }, [apiUrl, sizingMethod, sizingCountry, heightUnit, weightUnit, sizingUnit, sizeGuide, productTitle, dynamicFields]);
997
886
  const handleTryOnSubmit = useCallback(async () => {
998
887
  if (!selectedFile || !apiRef.current || !sseRef.current) {
999
888
  const msg = !apiRef.current ? t("SDK not configured. Please provide an API key.") : t("Something went wrong");
@@ -1582,65 +1471,65 @@ function PrimeStyleTryonInner({
1582
1471
  ] }, `form-${formGender}-${sizingUnit}-${heightUnit}-${sizingCountry}-${formKey}`);
1583
1472
  }
1584
1473
  function SizeResultView() {
1585
- const isCmR = sizingUnit === "cm";
1586
- const unitLbl = isCmR ? t("cm") : t("in");
1587
- const allSizes = useMemo(() => {
1588
- if (!sizeGuide?.found || !sizeGuide.headers || !sizeGuide.rows) return [];
1589
- const idx = sizeGuide.headers.findIndex((h) => /^size$/i.test(h.trim()));
1590
- const col = idx >= 0 ? idx : 0;
1591
- return sizeGuide.rows.map((r) => r[col]?.trim()).filter(Boolean);
1592
- }, [sizeGuide]);
1593
- const [compareSize, setCompareSize] = useState("");
1474
+ const unitLbl = sizingUnit === "cm" ? t("cm") : t("in");
1594
1475
  const [editVals, setEditVals] = useState({});
1595
- const parseNumFromDetail = (s) => {
1476
+ const [compareSize, setCompareSize] = useState("");
1477
+ const sizeColIdx = useMemo(() => {
1478
+ if (!sizeGuide?.headers || !sizeGuide?.rows) return -1;
1479
+ const byName = sizeGuide.headers.findIndex((h) => /size|taglia|größe|taille/i.test(h.trim()));
1480
+ if (byName >= 0) return byName;
1481
+ for (let c = 0; c < sizeGuide.headers.length; c++) {
1482
+ const vals = sizeGuide.rows.map((r) => r[c]?.trim() || "");
1483
+ if (vals.some((v) => /^(XXS|XS|S|M|L|XL|XXL|XXXL|ONE SIZE|\d{1,2})$/i.test(v))) return c;
1484
+ }
1485
+ return 0;
1486
+ }, [sizeGuide]);
1487
+ const allSizes = useMemo(() => {
1488
+ if (sizeColIdx < 0 || !sizeGuide?.rows) return [];
1489
+ return sizeGuide.rows.map((r) => r[sizeColIdx]?.trim()).filter(Boolean);
1490
+ }, [sizeGuide, sizeColIdx]);
1491
+ const pNum = (s) => {
1596
1492
  const n = parseFloat(s.replace(/[^\d.]/g, ""));
1597
1493
  return isNaN(n) ? 0 : n;
1598
1494
  };
1599
- const parseRangeFromDetail = (s) => {
1600
- const nums = s.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map(Number).filter((n) => !isNaN(n));
1601
- if (nums.length === 0) return { min: 0, max: 0 };
1602
- return { min: Math.min(...nums), max: Math.max(...nums) };
1495
+ const pRange = (s) => {
1496
+ const ns = s.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map(Number).filter((n) => !isNaN(n));
1497
+ return ns.length ? { min: Math.min(...ns), max: Math.max(...ns) } : { min: 0, max: 0 };
1603
1498
  };
1604
- const getChartRangeForSize = useCallback((measurement, size) => {
1605
- if (!sizeGuide?.found || !sizeGuide.headers || !sizeGuide.rows) return null;
1606
- const sizeCol = sizeGuide.headers.findIndex((h) => /^size$/i.test(h.trim()));
1607
- const sc = sizeCol >= 0 ? sizeCol : 0;
1608
- const measCol = sizeGuide.headers.findIndex((h) => {
1609
- const a = h.toLowerCase().trim();
1610
- const b = measurement.toLowerCase().trim();
1499
+ const chartRangeFor = useCallback((measurement, size) => {
1500
+ if (!sizeGuide?.headers || !sizeGuide?.rows || sizeColIdx < 0) return null;
1501
+ const mc = sizeGuide.headers.findIndex((h) => {
1502
+ const a = h.toLowerCase().trim(), b = measurement.toLowerCase().trim();
1611
1503
  return a === b || a.includes(b) || b.includes(a);
1612
1504
  });
1613
- if (measCol < 0) return null;
1614
- const row = sizeGuide.rows.find((r) => r[sc]?.trim() === size);
1615
- if (!row || !row[measCol]) return null;
1616
- const cell = row[measCol];
1617
- const parsed = parseRangeFromDetail(cell);
1618
- return { range: cell, ...parsed };
1619
- }, [sizeGuide]);
1505
+ if (mc < 0) return null;
1506
+ const row = sizeGuide.rows.find((r) => r[sizeColIdx]?.trim() === size);
1507
+ return row?.[mc] ? { range: row[mc], ...pRange(row[mc]) } : null;
1508
+ }, [sizeGuide, sizeColIdx]);
1620
1509
  const fitRows = useMemo(() => {
1621
- const base = sizingResult?.matchDetails;
1622
- if (!base || base.length === 0) return [];
1623
- compareSize || sizingResult?.recommendedSize || "";
1624
- return base.map((m) => {
1625
- const editedRaw = editVals[m.measurement];
1626
- const origNum = parseNumFromDetail(m.userValue);
1627
- const userNum = editedRaw !== void 0 && editedRaw !== "" ? parseFloat(editedRaw) : origNum;
1628
- let chartRange = m.chartRange;
1629
- let rangeMin = parseRangeFromDetail(m.chartRange).min;
1630
- let rangeMax = parseRangeFromDetail(m.chartRange).max;
1631
- if (compareSize && compareSize !== sizingResult?.recommendedSize) {
1632
- const alt = getChartRangeForSize(m.measurement, compareSize);
1510
+ if (!sizingResult?.matchDetails?.length) return [];
1511
+ compareSize || sizingResult.recommendedSize || "";
1512
+ return sizingResult.matchDetails.map((m) => {
1513
+ const origNum = pNum(m.userValue);
1514
+ const edited = editVals[m.measurement];
1515
+ const userNum = edited !== void 0 && edited !== "" ? parseFloat(edited) : origNum;
1516
+ let { min: rMin, max: rMax } = pRange(m.chartRange);
1517
+ let chartLabel = m.chartRange;
1518
+ if (compareSize && compareSize !== sizingResult.recommendedSize) {
1519
+ const alt = chartRangeFor(m.measurement, compareSize);
1633
1520
  if (alt) {
1634
- chartRange = alt.range;
1635
- rangeMin = alt.min;
1636
- rangeMax = alt.max;
1521
+ chartLabel = alt.range;
1522
+ rMin = alt.min;
1523
+ rMax = alt.max;
1637
1524
  }
1638
1525
  }
1639
- const fit = userNum >= rangeMin && userNum <= rangeMax ? "good" : userNum < rangeMin ? "tight" : "loose";
1640
- return { measurement: m.measurement, userNum, userDisplay: `${userNum}`, chartRange, fit };
1526
+ const fit = userNum >= rMin && userNum <= rMax ? "good" : userNum < rMin ? "tight" : "loose";
1527
+ return { area: m.measurement, userNum, chartLabel, fit };
1641
1528
  });
1642
- }, [sizingResult, compareSize, editVals, getChartRangeForSize]);
1529
+ }, [sizingResult, compareSize, editVals, chartRangeFor]);
1643
1530
  const intlSizes = sizingResult?.internationalSizes || {};
1531
+ const recSize = sizingResult?.recommendedSize || "";
1532
+ const selSize = compareSize || recSize;
1644
1533
  return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr", children: [
1645
1534
  sizingLoading && !sizingResult && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-loading", children: [
1646
1535
  /* @__PURE__ */ jsx("div", { className: "ps-tryon-size-loading-spinner" }),
@@ -1648,14 +1537,17 @@ function PrimeStyleTryonInner({
1648
1537
  ] }),
1649
1538
  sizingResult && /* @__PURE__ */ jsxs(Fragment, { children: [
1650
1539
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero", children: [
1651
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-badge", children: sizingResult.recommendedSize }),
1540
+ recSize && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-badge", children: recSize }),
1652
1541
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero-info", children: [
1653
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-title", children: t("Your Size") }),
1542
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero-title", children: [
1543
+ t("Your Size"),
1544
+ recSize ? `: ${recSize}` : ""
1545
+ ] }),
1654
1546
  /* @__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") })
1655
1547
  ] })
1656
1548
  ] }),
1657
1549
  Object.keys(intlSizes).length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl", children: [
1658
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Equivalent Sizes") }),
1550
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Your size in other countries") }),
1659
1551
  /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-intl-primary", children: Object.entries(intlSizes).map(([code, val]) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl-card", children: [
1660
1552
  /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-val", children: val }),
1661
1553
  /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-code", children: code })
@@ -1663,54 +1555,55 @@ function PrimeStyleTryonInner({
1663
1555
  ] }),
1664
1556
  allSizes.length > 1 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-compare", children: [
1665
1557
  /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Compare with another size") }),
1666
- /* @__PURE__ */ jsx(
1667
- "select",
1668
- {
1669
- className: "ps-tryon-sr-compare-select",
1670
- value: compareSize || sizingResult.recommendedSize,
1671
- onChange: (e) => setCompareSize(e.target.value),
1672
- children: allSizes.map((s) => /* @__PURE__ */ jsxs("option", { value: s, children: [
1673
- s,
1674
- s === sizingResult.recommendedSize ? ` ${t("recommended")}` : ""
1675
- ] }, s))
1676
- }
1677
- )
1678
- ] }),
1679
- fitRows.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit", children: [
1680
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-label", children: [
1681
- t("Fit Analysis"),
1682
- compareSize && compareSize !== sizingResult.recommendedSize && /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-label-hint", children: [
1683
- " — ",
1684
- t("size"),
1558
+ /* @__PURE__ */ jsx("select", { className: "ps-tryon-sr-compare-select", value: selSize, onChange: (e) => setCompareSize(e.target.value), children: allSizes.map((s) => /* @__PURE__ */ jsxs("option", { value: s, children: [
1559
+ s,
1560
+ s === recSize ? ` ★ ${t("recommended")}` : ""
1561
+ ] }, s)) }),
1562
+ compareSize && compareSize !== recSize && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-compare-note", children: [
1563
+ t("Showing fit for size"),
1564
+ " ",
1565
+ /* @__PURE__ */ jsx("strong", { children: compareSize }),
1566
+ /* @__PURE__ */ jsxs("button", { className: "ps-tryon-sr-compare-reset", onClick: () => setCompareSize(""), children: [
1567
+ t("Back to"),
1685
1568
  " ",
1686
- compareSize
1569
+ recSize
1687
1570
  ] })
1688
- ] }),
1689
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-table", children: [
1690
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-header", children: [
1691
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-area", children: t("Area") }),
1692
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-you", children: t("You") }),
1693
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-chart", children: t("Chart") }),
1694
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: t("Fit") })
1571
+ ] })
1572
+ ] }),
1573
+ fitRows.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit", children: [
1574
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Fit Analysis") }),
1575
+ fitRows.map((row, i) => /* @__PURE__ */ jsxs("div", { className: `ps-tryon-sr-fit-card ps-fit-${row.fit}`, children: [
1576
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-card-top", children: [
1577
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-area", children: row.area }),
1578
+ /* @__PURE__ */ jsx("span", { className: `ps-tryon-sr-fit-badge ps-fit-${row.fit}`, children: row.fit === "good" ? `✓ ${t("within range")}` : row.fit === "tight" ? `↑ ${t("may be snug")}` : `↓ ${t("may be loose")}` })
1695
1579
  ] }),
1696
- fitRows.map((row, i) => /* @__PURE__ */ jsxs("div", { className: `ps-tryon-sr-fit-row ps-fit-${row.fit}`, children: [
1697
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-area", children: row.measurement }),
1698
- /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-fit-col-you", children: [
1699
- /* @__PURE__ */ jsx(
1700
- "input",
1701
- {
1702
- type: "number",
1703
- className: "ps-tryon-sr-fit-input",
1704
- value: editVals[row.measurement] !== void 0 ? editVals[row.measurement] : row.userNum,
1705
- onChange: (e) => setEditVals((prev) => ({ ...prev, [row.measurement]: e.target.value }))
1706
- }
1707
- ),
1708
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-unit", children: unitLbl })
1580
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-card-bottom", children: [
1581
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-val", children: [
1582
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-val-label", children: t("You") }),
1583
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-val-input-wrap", children: [
1584
+ /* @__PURE__ */ jsx(
1585
+ "input",
1586
+ {
1587
+ type: "number",
1588
+ className: "ps-tryon-sr-fit-input",
1589
+ value: editVals[row.area] !== void 0 ? editVals[row.area] : row.userNum,
1590
+ onChange: (e) => setEditVals((prev) => ({ ...prev, [row.area]: e.target.value }))
1591
+ }
1592
+ ),
1593
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-unit", children: unitLbl })
1594
+ ] })
1709
1595
  ] }),
1710
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-chart", children: row.chartRange }),
1711
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: /* @__PURE__ */ jsx("span", { className: `ps-tryon-sr-fit-badge ps-fit-${row.fit}`, children: row.fit === "good" ? "✓ " + t("within range") : row.fit === "tight" ? "↑ " + t("may be snug") : "↓ " + t("may be loose") }) })
1712
- ] }, i))
1713
- ] })
1596
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-val", children: [
1597
+ /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-fit-val-label", children: [
1598
+ t("Chart"),
1599
+ " (",
1600
+ selSize,
1601
+ ")"
1602
+ ] }),
1603
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-val-text", children: row.chartLabel })
1604
+ ] })
1605
+ ] })
1606
+ ] }, i))
1714
1607
  ] }),
1715
1608
  fitRows.length === 0 && sizingResult.reasoning && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-reasoning", children: /* @__PURE__ */ jsx("p", { children: sizingResult.reasoning }) }),
1716
1609
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-ctas", children: [
@@ -2463,44 +2356,49 @@ const STYLES = `
2463
2356
  .ps-tryon-sr-comparing { font-size: 0.73vw; color: #bb945c; margin-top: 0.42vw; }
2464
2357
  .ps-tryon-sr-comparing strong { color: #d6ba7d; }
2465
2358
 
2466
- /* Fit analysis table */
2467
- .ps-tryon-sr-fit { }
2468
- .ps-tryon-sr-fit-table { border: 1.5px solid #333; border-radius: 0.73vw; overflow: hidden; }
2469
- .ps-tryon-sr-fit-header {
2470
- display: flex; padding: 0.57vw 0.83vw; background: #1a1b1a; border-bottom: 1px solid #333;
2471
- }
2472
- .ps-tryon-sr-fit-header span {
2473
- font-size: 0.68vw; font-weight: 700; color: #666; text-transform: uppercase; letter-spacing: 0.06em;
2474
- }
2475
- .ps-tryon-sr-fit-row {
2476
- display: flex; align-items: center; padding: 0.57vw 0.83vw;
2477
- border-bottom: 1px solid #222; transition: background 0.15s;
2478
- }
2479
- .ps-tryon-sr-fit-row:last-child { border-bottom: none; }
2480
- .ps-tryon-sr-fit-row:hover { background: rgba(255,255,255,0.02); }
2481
- .ps-tryon-sr-fit-row.ps-fit-good { border-left: 3px solid #4ade80; }
2482
- .ps-tryon-sr-fit-row.ps-fit-tight { border-left: 3px solid #f59e0b; }
2483
- .ps-tryon-sr-fit-row.ps-fit-loose { border-left: 3px solid #60a5fa; }
2484
-
2485
- .ps-tryon-sr-fit-col-area { flex: 0 0 28%; font-size: 0.78vw; font-weight: 600; color: #fff; }
2486
- .ps-tryon-sr-fit-col-you { flex: 0 0 22%; }
2487
- .ps-tryon-sr-fit-col-chart { flex: 0 0 22%; font-size: 0.78vw; color: #999; font-weight: 500; }
2488
- .ps-tryon-sr-fit-col-fit { flex: 1; text-align: right; }
2359
+ /* Fit analysis cards */
2360
+ .ps-tryon-sr-fit { display: flex; flex-direction: column; gap: 0.52vw; }
2361
+ .ps-tryon-sr-fit-card {
2362
+ border: 1.5px solid #333; border-radius: 0.63vw; overflow: hidden; transition: border-color 0.2s;
2363
+ }
2364
+ .ps-tryon-sr-fit-card.ps-fit-good { border-left: 3px solid #4ade80; }
2365
+ .ps-tryon-sr-fit-card.ps-fit-tight { border-left: 3px solid #f59e0b; }
2366
+ .ps-tryon-sr-fit-card.ps-fit-loose { border-left: 3px solid #60a5fa; }
2367
+ .ps-tryon-sr-fit-card-top {
2368
+ display: flex; align-items: center; justify-content: space-between;
2369
+ padding: 0.52vw 0.83vw; background: #1a1b1a;
2370
+ }
2371
+ .ps-tryon-sr-fit-area { font-size: 0.83vw; font-weight: 600; color: #fff; }
2372
+ .ps-tryon-sr-fit-card-bottom {
2373
+ display: flex; gap: 1vw; padding: 0.52vw 0.83vw;
2374
+ }
2375
+ .ps-tryon-sr-fit-val { flex: 1; }
2376
+ .ps-tryon-sr-fit-val-label { font-size: 0.57vw; font-weight: 700; color: #666; text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 0.21vw; display: block; }
2377
+ .ps-tryon-sr-fit-val-input-wrap { display: flex; align-items: center; gap: 0.26vw; }
2378
+ .ps-tryon-sr-fit-val-text { font-size: 0.83vw; font-weight: 600; color: #ccc; }
2489
2379
 
2490
2380
  .ps-tryon-sr-fit-input {
2491
- width: 3.5vw; padding: 0.26vw 0.42vw; border: 1.5px solid #333; border-radius: 0.36vw;
2492
- background: #111211; color: #fff; font-size: 0.78vw; font-weight: 600; font-family: inherit;
2381
+ width: 4vw; padding: 0.31vw 0.42vw; border: 1.5px solid #444; border-radius: 0.36vw;
2382
+ background: #0c0c0d; color: #fff; font-size: 0.83vw; font-weight: 600; font-family: inherit;
2493
2383
  outline: none; text-align: center; -moz-appearance: textfield;
2494
2384
  }
2495
2385
  .ps-tryon-sr-fit-input::-webkit-outer-spin-button,
2496
2386
  .ps-tryon-sr-fit-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
2497
- .ps-tryon-sr-fit-input:focus { border-color: #bb945c; }
2498
- .ps-tryon-sr-fit-unit { font-size: 0.57vw; color: #666; margin-left: 0.2vw; }
2387
+ .ps-tryon-sr-fit-input:focus { border-color: #bb945c; background: #1a1b1a; }
2388
+ .ps-tryon-sr-fit-unit { font-size: 0.68vw; color: #666; }
2389
+
2390
+ .ps-tryon-sr-fit-badge {
2391
+ display: inline-flex; align-items: center; gap: 0.26vw; padding: 0.26vw 0.57vw;
2392
+ border-radius: 0.36vw; font-size: 0.68vw; font-weight: 700; white-space: nowrap;
2393
+ }
2394
+ .ps-tryon-sr-fit-badge.ps-fit-good { background: rgba(74,222,128,0.1); color: #4ade80; }
2395
+ .ps-tryon-sr-fit-badge.ps-fit-tight { background: rgba(245,158,11,0.1); color: #f59e0b; }
2396
+ .ps-tryon-sr-fit-badge.ps-fit-loose { background: rgba(96,165,250,0.1); color: #60a5fa; }
2499
2397
 
2500
2398
  /* Compare dropdown */
2501
2399
  .ps-tryon-sr-compare { }
2502
2400
  .ps-tryon-sr-compare-select {
2503
- width: 100%; padding: 0.52vw 2vw 0.52vw 0.73vw; border: 1.5px solid #333; border-radius: 0.52vw;
2401
+ width: 100%; padding: 0.57vw 2vw 0.57vw 0.83vw; border: 1.5px solid #333; border-radius: 0.57vw;
2504
2402
  background: #1a1b1a; color: #fff; font-size: 0.83vw; font-weight: 600; font-family: inherit;
2505
2403
  appearance: none; -webkit-appearance: none; cursor: pointer; outline: none;
2506
2404
  background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%23999' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
@@ -2508,6 +2406,15 @@ const STYLES = `
2508
2406
  }
2509
2407
  .ps-tryon-sr-compare-select:focus { border-color: #bb945c; }
2510
2408
  .ps-tryon-sr-compare-select option { background: #1a1b1a; color: #fff; }
2409
+ .ps-tryon-sr-compare-note {
2410
+ font-size: 0.73vw; color: #bb945c; margin-top: 0.42vw; display: flex; align-items: center; gap: 0.52vw;
2411
+ }
2412
+ .ps-tryon-sr-compare-reset {
2413
+ background: none; border: 1px solid #bb945c; color: #bb945c; padding: 0.16vw 0.52vw;
2414
+ border-radius: 0.31vw; font-size: 0.63vw; font-weight: 600; cursor: pointer;
2415
+ font-family: inherit; transition: all 0.2s;
2416
+ }
2417
+ .ps-tryon-sr-compare-reset:hover { background: rgba(187,148,92,0.1); }
2511
2418
 
2512
2419
  .ps-tryon-sr-fit-badge {
2513
2420
  display: inline-flex; align-items: center; gap: 0.26vw; padding: 0.21vw 0.52vw;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primestyleai/tryon",
3
- "version": "3.18.0",
3
+ "version": "3.19.0",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",