@primestyleai/tryon 3.17.0 → 3.18.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.
@@ -305,6 +305,9 @@ const en = {
305
305
  "Size Chart": "Size Chart",
306
306
  "tap to compare": "tap to compare",
307
307
  "Comparing size": "Comparing size",
308
+ "Compare with another size": "Compare with another size",
309
+ "recommended": "recommended",
310
+ "size": "size",
308
311
  "Fit Analysis": "Fit Analysis",
309
312
  "Show more": "Show more",
310
313
  "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-DvAzTRFF.js";
2
- import { P, b, T, d, r } from "./index-DvAzTRFF.js";
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";
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-DvAzTRFF.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-Lf4qO_jW.js";
5
5
  const HEADER_ALIASES = {
6
6
  // ── Size label columns (skipped during field derivation) ──
7
7
  size: "__size__",
@@ -1582,108 +1582,65 @@ function PrimeStyleTryonInner({
1582
1582
  ] }, `form-${formGender}-${sizingUnit}-${heightUnit}-${sizingCountry}-${formKey}`);
1583
1583
  }
1584
1584
  function SizeResultView() {
1585
- const [activeSize, setActiveSize] = useState(sizingResult?.recommendedSize || "");
1586
- const [editedValues, setEditedValues] = useState({});
1587
- const isCmResult = sizingUnit === "cm";
1588
- const chartData = useMemo(() => {
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("");
1594
+ const [editVals, setEditVals] = useState({});
1595
+ const parseNumFromDetail = (s) => {
1596
+ const n = parseFloat(s.replace(/[^\d.]/g, ""));
1597
+ return isNaN(n) ? 0 : n;
1598
+ };
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) };
1603
+ };
1604
+ const getChartRangeForSize = useCallback((measurement, size) => {
1589
1605
  if (!sizeGuide?.found || !sizeGuide.headers || !sizeGuide.rows) return null;
1590
- const INTL = /* @__PURE__ */ new Set(["eu", "us", "uk", "it", "fr", "de", "jp", "cn", "kr", "au", "br", "eur"]);
1591
- const MEAS_MAP = {
1592
- chest: "chest",
1593
- bust: "bust",
1594
- waist: "waist",
1595
- hips: "hips",
1596
- hip: "hips",
1597
- shoulder: "shoulderWidth",
1598
- shoulders: "shoulderWidth",
1599
- "shoulder width": "shoulderWidth",
1600
- sleeve: "sleeveLength",
1601
- "sleeve length": "sleeveLength",
1602
- inseam: "inseam",
1603
- neck: "neckCircumference",
1604
- "neck circumference": "neckCircumference",
1605
- foot: "footLengthCm",
1606
- "foot length": "footLengthCm"
1607
- };
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;
1616
- const measCols = [];
1617
- const intlCols = [];
1618
- sizeGuide.headers.forEach((h, i) => {
1619
- if (i === idx) return;
1620
- const lower = h.toLowerCase().trim().replace(/\s*\(.*\)/, "");
1621
- const clean = lower.replace(/\s*size\s*/gi, "").trim();
1622
- if (INTL.has(clean)) {
1623
- intlCols.push({ headerIdx: i, code: clean.toUpperCase() });
1624
- } else if (MEAS_MAP[clean]) {
1625
- measCols.push({ headerIdx: i, label: h.trim(), formKey: MEAS_MAP[clean] });
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 });
1630
- }
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();
1611
+ return a === b || a.includes(b) || b.includes(a);
1631
1612
  });
1632
- const sizes = sizeGuide.rows.map((row) => {
1633
- const label = row[idx] || "";
1634
- const measurements = {};
1635
- for (const col of measCols) {
1636
- const cell = row[col.headerIdx] || "";
1637
- const nums = cell.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map(Number).filter((n) => !isNaN(n));
1638
- if (nums.length > 0) {
1639
- measurements[col.formKey] = { raw: cell, min: Math.min(...nums), max: Math.max(...nums) };
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]);
1620
+ 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);
1633
+ if (alt) {
1634
+ chartRange = alt.range;
1635
+ rangeMin = alt.min;
1636
+ rangeMax = alt.max;
1640
1637
  }
1641
1638
  }
1642
- const intl = {};
1643
- for (const col of intlCols) {
1644
- if (row[col.headerIdx]) intl[col.code] = row[col.headerIdx];
1645
- }
1646
- return { label, measurements, intl };
1639
+ const fit = userNum >= rangeMin && userNum <= rangeMax ? "good" : userNum < rangeMin ? "tight" : "loose";
1640
+ return { measurement: m.measurement, userNum, userDisplay: `${userNum}`, chartRange, fit };
1647
1641
  });
1648
- return { sizes: sizes.filter((s) => s.label.trim()), measCols, intlCols };
1649
- }, [sizeGuide]);
1650
- const getUserVal = useCallback((formKey2) => {
1651
- const edited = editedValues[formKey2];
1652
- if (edited !== void 0 && edited !== "") {
1653
- const v2 = parseFloat(edited);
1654
- return isNaN(v2) ? null : isCmResult ? v2 : inToCm(v2);
1655
- }
1656
- const raw = formRef.current[formKey2];
1657
- if (!raw) return null;
1658
- const v = parseFloat(raw);
1659
- return isNaN(v) ? null : isCmResult ? v : inToCm(v);
1660
- }, [editedValues, isCmResult]);
1661
- const activeFit = useMemo(() => {
1662
- if (!chartData) return null;
1663
- const sizeRow = chartData.sizes.find((s) => s.label === activeSize);
1664
- if (!sizeRow) return null;
1665
- const details = [];
1666
- for (const col of chartData.measCols) {
1667
- const range = sizeRow.measurements[col.formKey];
1668
- if (!range) continue;
1669
- const userVal = getUserVal(col.formKey);
1670
- if (userVal === null) continue;
1671
- const fit = userVal >= range.min && userVal <= range.max ? "good" : userVal < range.min ? "tight" : "loose";
1672
- details.push({ label: col.label, formKey: col.formKey, userVal, chartMin: range.min, chartMax: range.max, chartRaw: range.raw, fit });
1673
- }
1674
- return details;
1675
- }, [chartData, activeSize, getUserVal]);
1676
- const activeIntl = useMemo(() => {
1677
- if (!chartData) return sizingResult?.internationalSizes || {};
1678
- const sizeRow = chartData.sizes.find((s) => s.label === activeSize);
1679
- return sizeRow?.intl || sizingResult?.internationalSizes || {};
1680
- }, [chartData, activeSize, sizingResult]);
1681
- const fmtRange = (min, max) => {
1682
- if (isCmResult) return min === max ? `${Math.round(min)}` : `${Math.round(min)}–${Math.round(max)}`;
1683
- return min === max ? `${cmToIn(min)}` : `${cmToIn(min)}–${cmToIn(max)}`;
1684
- };
1685
- const unitLabel = isCmResult ? t("cm") : t("in");
1686
- const isRecommended = activeSize === sizingResult?.recommendedSize;
1642
+ }, [sizingResult, compareSize, editVals, getChartRangeForSize]);
1643
+ const intlSizes = sizingResult?.internationalSizes || {};
1687
1644
  return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr", children: [
1688
1645
  sizingLoading && !sizingResult && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-loading", children: [
1689
1646
  /* @__PURE__ */ jsx("div", { className: "ps-tryon-size-loading-spinner" }),
@@ -1697,123 +1654,65 @@ function PrimeStyleTryonInner({
1697
1654
  /* @__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") })
1698
1655
  ] })
1699
1656
  ] }),
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
- })(),
1724
- chartData && chartData.sizes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-sizes", children: [
1657
+ 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") }),
1659
+ /* @__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
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-val", children: val }),
1661
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-code", children: code })
1662
+ ] }, code)) })
1663
+ ] }),
1664
+ allSizes.length > 1 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-compare", children: [
1665
+ /* @__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: [
1725
1680
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-label", children: [
1726
- t("Size Chart"),
1727
- " ",
1728
- /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-label-hint", children: [
1729
- "",
1730
- t("tap to compare")
1681
+ t("Fit Analysis"),
1682
+ compareSize && compareSize !== sizingResult.recommendedSize && /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-label-hint", children: [
1683
+ " ",
1684
+ t("size"),
1685
+ " ",
1686
+ compareSize
1731
1687
  ] })
1732
1688
  ] }),
1733
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-size-row", children: chartData.sizes.map((s) => /* @__PURE__ */ jsxs(
1734
- "button",
1735
- {
1736
- onClick: () => setActiveSize(s.label),
1737
- className: `ps-tryon-sr-size-chip${s.label === activeSize ? " ps-active" : ""}${s.label === sizingResult.recommendedSize ? " ps-recommended" : ""}`,
1738
- children: [
1739
- s.label,
1740
- s.label === sizingResult.recommendedSize && /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-rec-dot" })
1741
- ]
1742
- },
1743
- s.label
1744
- )) }),
1745
- !isRecommended && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-comparing", children: [
1746
- t("Comparing size"),
1747
- " ",
1748
- /* @__PURE__ */ jsx("strong", { children: activeSize })
1749
- ] })
1750
- ] }),
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") })
1772
- ] }),
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(
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") })
1695
+ ] }),
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(
1776
1700
  "input",
1777
1701
  {
1778
1702
  type: "number",
1779
1703
  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 }))
1704
+ value: editVals[row.measurement] !== void 0 ? editVals[row.measurement] : row.userNum,
1705
+ onChange: (e) => setEditVals((prev) => ({ ...prev, [row.measurement]: e.target.value }))
1782
1706
  }
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") })
1707
+ ),
1708
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-unit", children: unitLbl })
1801
1709
  ] }),
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
- })(),
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
+ ] })
1714
+ ] }),
1715
+ fitRows.length === 0 && sizingResult.reasoning && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-reasoning", children: /* @__PURE__ */ jsx("p", { children: sizingResult.reasoning }) }),
1817
1716
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-ctas", children: [
1818
1717
  /* @__PURE__ */ jsxs("button", { className: "ps-tryon-cta", onClick: () => setView("upload"), children: [
1819
1718
  t("See how it looks on you"),
@@ -2596,6 +2495,19 @@ const STYLES = `
2596
2495
  .ps-tryon-sr-fit-input::-webkit-outer-spin-button,
2597
2496
  .ps-tryon-sr-fit-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
2598
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; }
2499
+
2500
+ /* Compare dropdown */
2501
+ .ps-tryon-sr-compare { }
2502
+ .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;
2504
+ background: #1a1b1a; color: #fff; font-size: 0.83vw; font-weight: 600; font-family: inherit;
2505
+ appearance: none; -webkit-appearance: none; cursor: pointer; outline: none;
2506
+ 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");
2507
+ background-repeat: no-repeat; background-position: right 0.73vw center;
2508
+ }
2509
+ .ps-tryon-sr-compare-select:focus { border-color: #bb945c; }
2510
+ .ps-tryon-sr-compare-select option { background: #1a1b1a; color: #fff; }
2599
2511
 
2600
2512
  .ps-tryon-sr-fit-badge {
2601
2513
  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.17.0",
3
+ "version": "3.18.0",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",