@primestyleai/tryon 4.1.1 → 4.2.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.
@@ -314,6 +314,8 @@ const en = {
314
314
  "Edit values": "Edit values",
315
315
  "Based on your updated measurements, size": "Based on your updated measurements, size",
316
316
  "may be a better fit": "may be a better fit",
317
+ "Updated to": "Updated to",
318
+ "Your Measurements": "Your Measurements",
317
319
  "Fit Analysis": "Fit Analysis",
318
320
  "Show more": "Show more",
319
321
  "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-aIBOmApE.js";
2
- import { P, b, T, d, r } from "./index-aIBOmApE.js";
1
+ import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-CILUifQv.js";
2
+ import { P, b, T, d, r } from "./index-CILUifQv.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-aIBOmApE.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-CILUifQv.js";
5
5
  const HEADER_ALIASES = {
6
6
  // ── Size label columns (skipped during field derivation) ──
7
7
  size: "__size__",
@@ -453,22 +453,6 @@ function lbsToKg(lbs) {
453
453
  function ftInToCm(ft, inch) {
454
454
  return +(ft * 30.48 + inch * 2.54).toFixed(1);
455
455
  }
456
- function SvgIcon({ d, size = 18, strokeWidth = 2 }) {
457
- return /* @__PURE__ */ jsx(
458
- "svg",
459
- {
460
- width: size,
461
- height: size,
462
- viewBox: "0 0 24 24",
463
- fill: "none",
464
- stroke: "currentColor",
465
- strokeWidth,
466
- strokeLinecap: "round",
467
- strokeLinejoin: "round",
468
- children: /* @__PURE__ */ jsx("path", { d })
469
- }
470
- );
471
- }
472
456
  function CameraIcon({ size = 18 }) {
473
457
  return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [
474
458
  /* @__PURE__ */ 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" }),
@@ -873,6 +857,8 @@ function PrimeStyleTryonInner({
873
857
  if (sizeGuide.sections) payload.sizeGuide = { ...sizeGuide, sections: sizeGuide.sections };
874
858
  }
875
859
  payload.sizingUnit = sizingUnit;
860
+ console.log("[PS-SDK] sizeGuide state:", JSON.stringify({ found: sizeGuide?.found, hasHeaders: !!sizeGuide?.headers, hasRows: !!sizeGuide?.rows, headers: sizeGuide?.headers, rowCount: sizeGuide?.rows?.length }));
861
+ console.log("[PS-SDK] payload.sizeGuide:", payload.sizeGuide ? "present" : "MISSING");
876
862
  if (sizingMethod === "exact") {
877
863
  const m = { gender: formRef.current.gender || "male", sizingUnit };
878
864
  const numericKeys = /* @__PURE__ */ new Set();
@@ -1508,28 +1494,19 @@ function PrimeStyleTryonInner({
1508
1494
  }
1509
1495
  function SizeResultView() {
1510
1496
  const unitLbl = sizingUnit === "cm" ? t("cm") : t("in");
1511
- const [editing, setEditing] = useState(false);
1512
1497
  const [editVals, setEditVals] = useState({});
1513
- const [compareSize, setCompareSize] = useState("");
1514
- const cleanChart = (s) => {
1515
- const nums = s.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map((n) => parseFloat(n)).filter((n) => !isNaN(n));
1516
- if (nums.length === 0) return s;
1517
- if (nums.length === 1) return `${nums[0]}`;
1518
- return `${nums[0]}–${nums[1]}`;
1498
+ const cleanNum = (s) => {
1499
+ const ns = s.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map((n) => parseFloat(n)).filter((n) => !isNaN(n));
1500
+ return ns.length === 1 ? `${ns[0]}` : ns.length > 1 ? `${ns[0]}–${ns[1]}` : s;
1501
+ };
1502
+ const pNum = (s) => {
1503
+ const n = parseFloat(s.replace(/[^\d.]/g, ""));
1504
+ return isNaN(n) ? 0 : n;
1505
+ };
1506
+ const pRange = (s) => {
1507
+ const ns = s.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map(Number).filter((n) => !isNaN(n));
1508
+ return ns.length ? { min: Math.min(...ns), max: Math.max(...ns) } : { min: 0, max: 0 };
1519
1509
  };
1520
- const recSize = useMemo(() => {
1521
- if (sizingResult?.recommendedSize) return sizingResult.recommendedSize;
1522
- if (sizeGuide?.rows && sizeGuide.headers) {
1523
- const sc = sizeGuide.headers.findIndex((h) => /size|taglia|größe|taille/i.test(h.trim()));
1524
- const idx = sc >= 0 ? sc : 0;
1525
- for (const row of sizeGuide.rows) {
1526
- const label = row[idx]?.trim();
1527
- if (label && /^(XXS|XS|S|M|L|XL|XXL|XXXL|\d{1,3})$/i.test(label)) return label;
1528
- }
1529
- }
1530
- return "";
1531
- }, [sizingResult, sizeGuide]);
1532
- const selSize = compareSize || recSize;
1533
1510
  const sizeColIdx = useMemo(() => {
1534
1511
  if (!sizeGuide?.headers || !sizeGuide?.rows) return -1;
1535
1512
  const byName = sizeGuide.headers.findIndex((h) => /size|taglia|größe|taille/i.test(h.trim()));
@@ -1543,14 +1520,6 @@ function PrimeStyleTryonInner({
1543
1520
  if (sizeColIdx < 0 || !sizeGuide?.rows) return [];
1544
1521
  return sizeGuide.rows.map((r) => r[sizeColIdx]?.trim()).filter(Boolean);
1545
1522
  }, [sizeGuide, sizeColIdx]);
1546
- const pNum = (s) => {
1547
- const n = parseFloat(s.replace(/[^\d.]/g, ""));
1548
- return isNaN(n) ? 0 : n;
1549
- };
1550
- const pRange = (s) => {
1551
- const ns = s.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map(Number).filter((n) => !isNaN(n));
1552
- return ns.length ? { min: Math.min(...ns), max: Math.max(...ns) } : { min: 0, max: 0 };
1553
- };
1554
1523
  const chartRangeFor = useCallback((measurement, size) => {
1555
1524
  if (!sizeGuide?.headers || !sizeGuide?.rows || sizeColIdx < 0) return null;
1556
1525
  const mc = sizeGuide.headers.findIndex((h) => {
@@ -1561,6 +1530,29 @@ function PrimeStyleTryonInner({
1561
1530
  const row = sizeGuide.rows.find((r) => r[sizeColIdx]?.trim() === size);
1562
1531
  return row?.[mc] ? { range: row[mc], ...pRange(row[mc]) } : null;
1563
1532
  }, [sizeGuide, sizeColIdx]);
1533
+ const bestSize = useMemo(() => {
1534
+ if (!sizingResult?.matchDetails?.length || !allSizes.length) return sizingResult?.recommendedSize || "";
1535
+ if (!Object.keys(editVals).length) return sizingResult?.recommendedSize || "";
1536
+ let best = "";
1537
+ let bestScore = -1;
1538
+ for (const size of allSizes) {
1539
+ let score = 0;
1540
+ for (const m of sizingResult.matchDetails) {
1541
+ const edited = editVals[m.measurement];
1542
+ const userNum = edited !== void 0 && edited !== "" ? parseFloat(edited) : pNum(m.userValue);
1543
+ const alt = chartRangeFor(m.measurement, size);
1544
+ if (alt && userNum >= alt.min && userNum <= alt.max) score++;
1545
+ }
1546
+ if (score > bestScore) {
1547
+ bestScore = score;
1548
+ best = size;
1549
+ }
1550
+ }
1551
+ return best || sizingResult?.recommendedSize || "";
1552
+ }, [editVals, sizingResult, allSizes, chartRangeFor]);
1553
+ const origSize = sizingResult?.recommendedSize || "";
1554
+ const activeSize = bestSize || origSize;
1555
+ const sizeChanged = Object.keys(editVals).length > 0 && bestSize !== origSize;
1564
1556
  const fitRows = useMemo(() => {
1565
1557
  if (!sizingResult?.matchDetails?.length) return [];
1566
1558
  return sizingResult.matchDetails.map((m) => {
@@ -1569,8 +1561,8 @@ function PrimeStyleTryonInner({
1569
1561
  const userNum = edited !== void 0 && edited !== "" ? parseFloat(edited) : origNum;
1570
1562
  let { min: rMin, max: rMax } = pRange(m.chartRange);
1571
1563
  let chartLabel = m.chartRange;
1572
- if (compareSize && compareSize !== recSize) {
1573
- const alt = chartRangeFor(m.measurement, compareSize);
1564
+ if (activeSize !== origSize) {
1565
+ const alt = chartRangeFor(m.measurement, activeSize);
1574
1566
  if (alt) {
1575
1567
  chartLabel = alt.range;
1576
1568
  rMin = alt.min;
@@ -1578,129 +1570,88 @@ function PrimeStyleTryonInner({
1578
1570
  }
1579
1571
  }
1580
1572
  const fit = userNum >= rMin && userNum <= rMax ? "good" : userNum < rMin ? "tight" : "loose";
1581
- return { area: m.measurement, userNum, chartLabel: cleanChart(chartLabel), fit };
1573
+ return { area: m.measurement, userNum, chartLabel: cleanNum(chartLabel), fit };
1582
1574
  });
1583
- }, [sizingResult, compareSize, editVals, chartRangeFor, recSize]);
1584
- const suggestedSize = useMemo(() => {
1585
- if (!Object.keys(editVals).length || !sizingResult?.matchDetails?.length || !allSizes.length) return null;
1586
- let bestSize = "";
1587
- let bestScore = -1;
1588
- for (const size of allSizes) {
1589
- let score = 0;
1590
- for (const m of sizingResult.matchDetails) {
1591
- const edited = editVals[m.measurement];
1592
- const userNum = edited !== void 0 && edited !== "" ? parseFloat(edited) : pNum(m.userValue);
1593
- const alt = chartRangeFor(m.measurement, size);
1594
- if (alt && userNum >= alt.min && userNum <= alt.max) score++;
1595
- }
1596
- if (score > bestScore) {
1597
- bestScore = score;
1598
- bestSize = size;
1599
- }
1600
- }
1601
- return bestSize && bestSize !== recSize ? bestSize : null;
1602
- }, [editVals, sizingResult, allSizes, recSize, chartRangeFor]);
1575
+ }, [sizingResult, editVals, chartRangeFor, activeSize, origSize]);
1603
1576
  const allIntlSizes = useMemo(() => {
1604
1577
  const backendIntl = sizingResult?.internationalSizes || {};
1605
- const upper = recSize.toUpperCase().trim();
1606
- const fromTable = SIZE_CONVERSIONS[upper] || SIZE_CONVERSIONS[recSize] || {};
1578
+ const upper = activeSize.toUpperCase().trim();
1579
+ const fromTable = SIZE_CONVERSIONS[upper] || SIZE_CONVERSIONS[activeSize] || {};
1607
1580
  return { ...fromTable, ...backendIntl };
1608
- }, [sizingResult, recSize]);
1609
- const confLabel = sizingResult?.confidence === "high" ? t("High Confidence") : sizingResult?.confidence === "medium" ? t("Medium Confidence") : t("Low Confidence");
1581
+ }, [sizingResult, activeSize]);
1610
1582
  return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr", children: [
1611
1583
  sizingLoading && !sizingResult && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-loading", children: [
1612
1584
  /* @__PURE__ */ jsx("div", { className: "ps-tryon-size-loading-spinner" }),
1613
1585
  /* @__PURE__ */ jsx("p", { children: t("Analyzing your size...") })
1614
1586
  ] }),
1615
1587
  sizingResult && /* @__PURE__ */ jsxs(Fragment, { children: [
1616
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero", children: [
1617
- recSize ? /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-badge", children: recSize }) : /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-badge ps-tryon-sr-hero-badge-icon", children: /* @__PURE__ */ jsx(RulerIcon, { size: 22 }) }),
1618
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero-info", children: [
1619
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-title", children: t("Your Size") }),
1620
- /* @__PURE__ */ jsx("div", { className: `ps-tryon-sr-hero-conf ps-conf-${sizingResult.confidence}`, children: confLabel })
1621
- ] })
1622
- ] }),
1623
- suggestedSize && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-suggestion", onClick: () => {
1624
- setCompareSize(suggestedSize);
1588
+ /* @__PURE__ */ jsxs("button", { className: "ps-tryon-back", onClick: () => {
1589
+ setView("sizing-form");
1590
+ setEditVals({});
1625
1591
  }, children: [
1626
- /* @__PURE__ */ jsx(SparkleIcon, { size: 14 }),
1627
- /* @__PURE__ */ jsxs("span", { children: [
1628
- t("Based on your updated measurements, size"),
1629
- " ",
1630
- /* @__PURE__ */ jsx("strong", { children: suggestedSize }),
1631
- " ",
1632
- t("may be a better fit")
1633
- ] }),
1634
- /* @__PURE__ */ jsx(ArrowRightIcon, {})
1635
- ] }),
1636
- Object.keys(allIntlSizes).length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl", children: [
1637
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-section-title", children: t("Your size in other countries") }),
1638
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-intl-grid", children: Object.entries(allIntlSizes).map(([code, val]) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl-card", children: [
1639
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-val", children: val }),
1640
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-code", children: code })
1641
- ] }, code)) })
1592
+ /* @__PURE__ */ jsx(ArrowLeftIcon, {}),
1593
+ " ",
1594
+ t("Back")
1642
1595
  ] }),
1643
- fitRows.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit", children: [
1644
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-top", children: [
1645
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-section-title", children: t("Fit Analysis") }),
1646
- /* @__PURE__ */ jsxs("button", { className: "ps-tryon-sr-edit-btn", onClick: () => setEditing(!editing), children: [
1647
- /* @__PURE__ */ jsx(SvgIcon, { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7", size: 12 }),
1648
- editing ? t("Done") : t("Edit values")
1596
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-split", children: [
1597
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-left", children: [
1598
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero", children: [
1599
+ activeSize ? /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-badge", children: activeSize }) : /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-badge ps-tryon-sr-hero-badge-icon", children: /* @__PURE__ */ jsx(RulerIcon, { size: 22 }) }),
1600
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero-info", children: [
1601
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-title", children: t("Your Size") }),
1602
+ /* @__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") })
1603
+ ] })
1604
+ ] }),
1605
+ sizeChanged && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-size-changed", children: [
1606
+ /* @__PURE__ */ jsx(SparkleIcon, { size: 13 }),
1607
+ " ",
1608
+ t("Updated to"),
1609
+ " ",
1610
+ /* @__PURE__ */ jsx("strong", { children: activeSize })
1611
+ ] }),
1612
+ Object.keys(allIntlSizes).length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl", children: [
1613
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-section-title", children: t("Your size in other countries") }),
1614
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-intl-grid", children: Object.entries(allIntlSizes).map(([code, val]) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl-card", children: [
1615
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-val", children: val }),
1616
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-code", children: code })
1617
+ ] }, code)) })
1649
1618
  ] })
1650
1619
  ] }),
1651
- allSizes.length > 1 && /* @__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: [
1652
- s,
1653
- s === recSize ? ` ${t("recommended")}` : ""
1654
- ] }, s)) }),
1655
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-fit-cards", children: fitRows.map((row, i) => /* @__PURE__ */ jsxs("div", { className: `ps-tryon-sr-fc ps-fit-${row.fit}`, children: [
1656
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fc-head", children: [
1657
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fc-area", children: row.area }),
1658
- /* @__PURE__ */ jsx("span", { className: `ps-tryon-sr-fc-badge ps-fit-${row.fit}`, children: row.fit === "good" ? `✓ ${t("within range")}` : row.fit === "tight" ? `↑ ${t("may be snug")}` : `↓ ${t("may be loose")}` })
1659
- ] }),
1660
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fc-body", children: [
1661
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fc-col", children: [
1662
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-fc-label", children: t("You") }),
1663
- editing ? /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fc-edit", children: [
1620
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-right", children: [
1621
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-section-title", children: t("Your Measurements") }),
1622
+ fitRows.map((row, i) => /* @__PURE__ */ jsxs("div", { className: `ps-tryon-sr-meas ps-fit-${row.fit}`, children: [
1623
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-meas-head", children: [
1624
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-meas-area", children: row.area }),
1625
+ /* @__PURE__ */ jsx("span", { className: `ps-tryon-sr-meas-badge ps-fit-${row.fit}`, children: row.fit === "good" ? `✓ ${t("within range")}` : row.fit === "tight" ? `↑ ${t("may be snug")}` : `↓ ${t("may be loose")}` })
1626
+ ] }),
1627
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-meas-row", children: [
1628
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-meas-input-wrap", children: [
1664
1629
  /* @__PURE__ */ jsx(
1665
1630
  "input",
1666
1631
  {
1667
1632
  type: "number",
1668
- className: "ps-tryon-sr-fc-input",
1633
+ className: "ps-tryon-sr-meas-input",
1669
1634
  value: editVals[row.area] !== void 0 ? editVals[row.area] : row.userNum,
1670
1635
  onChange: (e) => setEditVals((prev) => ({ ...prev, [row.area]: e.target.value }))
1671
1636
  }
1672
1637
  ),
1673
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fc-unit", children: unitLbl })
1674
- ] }) : /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fc-value", children: [
1675
- row.userNum,
1676
- " ",
1677
- unitLbl
1678
- ] })
1679
- ] }),
1680
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fc-col", children: [
1681
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fc-label", children: [
1682
- t("Chart"),
1683
- " (",
1684
- selSize,
1685
- ")"
1638
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-meas-unit", children: unitLbl })
1686
1639
  ] }),
1687
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fc-value", children: [
1640
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-meas-vs", children: "vs" }),
1641
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-meas-chart", children: [
1688
1642
  row.chartLabel,
1689
1643
  " ",
1690
1644
  unitLbl
1691
1645
  ] })
1692
1646
  ] })
1693
- ] })
1694
- ] }, i)) })
1647
+ ] }, i)),
1648
+ fitRows.length === 0 && sizingResult.reasoning && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-reasoning", children: /* @__PURE__ */ jsx("p", { children: sizingResult.reasoning }) })
1649
+ ] })
1695
1650
  ] }),
1696
- fitRows.length === 0 && sizingResult.reasoning && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-reasoning", children: /* @__PURE__ */ jsx("p", { children: sizingResult.reasoning }) }),
1697
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-ctas", children: [
1698
- /* @__PURE__ */ jsxs("button", { className: "ps-tryon-cta", onClick: () => setView("upload"), children: [
1699
- t("See how it looks on you"),
1700
- " ",
1701
- /* @__PURE__ */ jsx(ArrowRightIcon, {})
1702
- ] }),
1703
- /* @__PURE__ */ jsx("button", { className: "ps-tryon-btn-secondary", onClick: handleClose, children: t("Done") })
1651
+ /* @__PURE__ */ jsxs("button", { className: "ps-tryon-cta", onClick: () => setView("upload"), children: [
1652
+ t("See how it looks on you"),
1653
+ " ",
1654
+ /* @__PURE__ */ jsx(ArrowRightIcon, {})
1704
1655
  ] })
1705
1656
  ] })
1706
1657
  ] });
@@ -1920,7 +1871,7 @@ function PrimeStyleTryonInner({
1920
1871
  ] }),
1921
1872
  view !== "idle" && /* @__PURE__ */ jsx("div", { className: cx("ps-tryon-overlay", cn.overlay), onClick: (e) => {
1922
1873
  if (e.target === e.currentTarget) handleClose();
1923
- }, children: /* @__PURE__ */ jsxs("div", { className: cx(`ps-tryon-modal${view === "result" && resultImageUrl && sizingResult ? " ps-tryon-modal-wide" : ""}`, cn.modal), onClick: (e) => e.stopPropagation(), children: [
1874
+ }, children: /* @__PURE__ */ jsxs("div", { className: cx(`ps-tryon-modal${view === "result" && resultImageUrl && sizingResult || view === "size-result" ? " ps-tryon-modal-wide" : ""}`, cn.modal), onClick: (e) => e.stopPropagation(), children: [
1924
1875
  /* @__PURE__ */ jsxs("div", { className: cx("ps-tryon-header", cn.header), children: [
1925
1876
  /* @__PURE__ */ jsx("span", { className: cx("ps-tryon-title", cn.title), children: t("Virtual Try-On") }),
1926
1877
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-header-actions", children: [
@@ -2390,21 +2341,25 @@ const STYLES = `
2390
2341
  .ps-tryon-sr-hero-conf { font-size: 0.73vw; font-weight: 600; }
2391
2342
  .ps-conf-high { color: #4ade80; } .ps-conf-medium { color: #bb945c; } .ps-conf-low { color: #ef4444; }
2392
2343
 
2393
- /* Suggestion banner */
2394
- .ps-tryon-sr-suggestion {
2395
- display: flex; align-items: center; gap: 0.52vw;
2396
- padding: 0.62vw 0.83vw; border-radius: 0.52vw;
2397
- background: linear-gradient(135deg, rgba(187,148,92,0.1), rgba(187,148,92,0.04));
2398
- border: 1px solid rgba(187,148,92,0.25);
2399
- font-size: 0.73vw; color: #d6ba7d; cursor: pointer;
2400
- animation: ps-fade-up 0.3s ease both; transition: background 0.2s;
2401
- }
2402
- .ps-tryon-sr-suggestion:hover { background: rgba(187,148,92,0.14); }
2403
- .ps-tryon-sr-suggestion svg { stroke: #bb945c; flex-shrink: 0; }
2404
- .ps-tryon-sr-suggestion strong { color: #fff; }
2405
2344
  .ps-tryon-sr-hero-badge-icon { background: linear-gradient(135deg, #333, #444); }
2406
2345
  .ps-tryon-sr-hero-badge-icon svg { stroke: #bb945c; }
2407
2346
 
2347
+ /* Size changed notice */
2348
+ .ps-tryon-sr-size-changed {
2349
+ display: flex; align-items: center; gap: 0.36vw;
2350
+ padding: 0.42vw 0.68vw; border-radius: 0.42vw;
2351
+ background: rgba(187,148,92,0.08); border: 1px solid rgba(187,148,92,0.2);
2352
+ font-size: 0.73vw; color: #d6ba7d; margin-top: 0.52vw;
2353
+ animation: ps-fade-up 0.25s ease both;
2354
+ }
2355
+ .ps-tryon-sr-size-changed svg { stroke: #bb945c; }
2356
+ .ps-tryon-sr-size-changed strong { color: #fff; }
2357
+
2358
+ /* Split layout */
2359
+ .ps-tryon-sr-split { display: flex; gap: 1.5vw; min-height: 0; }
2360
+ .ps-tryon-sr-left { flex: 0 0 45%; display: flex; flex-direction: column; gap: 1vw; }
2361
+ .ps-tryon-sr-right { flex: 1; display: flex; flex-direction: column; gap: 0.52vw; }
2362
+
2408
2363
  /* Section titles */
2409
2364
  .ps-tryon-sr-section-title { font-size: 0.73vw; font-weight: 700; color: #666; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 0.52vw; }
2410
2365
 
@@ -2446,82 +2401,44 @@ const STYLES = `
2446
2401
  .ps-tryon-sr-comparing { font-size: 0.73vw; color: #bb945c; margin-top: 0.42vw; }
2447
2402
  .ps-tryon-sr-comparing strong { color: #d6ba7d; }
2448
2403
 
2449
- /* Fit analysis */
2450
- .ps-tryon-sr-fit { }
2451
- .ps-tryon-sr-fit-top {
2452
- display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.52vw;
2453
- }
2454
- .ps-tryon-sr-edit-btn {
2455
- display: inline-flex; align-items: center; gap: 0.31vw;
2456
- padding: 0.31vw 0.73vw; border: 1px solid rgba(187,148,92,0.4); border-radius: 0.42vw;
2457
- background: rgba(187,148,92,0.06); color: #bb945c; font-size: 0.68vw; font-weight: 600;
2458
- cursor: pointer; font-family: inherit; transition: all 0.2s;
2459
- }
2460
- .ps-tryon-sr-edit-btn:hover { background: rgba(187,148,92,0.12); border-color: #bb945c; }
2461
- .ps-tryon-sr-edit-btn svg { stroke: currentColor; }
2462
-
2463
- .ps-tryon-sr-fit-cards { display: flex; flex-direction: column; gap: 0.42vw; }
2464
-
2465
- .ps-tryon-sr-fc {
2466
- border: 1px solid #282828; border-radius: 0.63vw; overflow: hidden;
2404
+ /* Measurement rows (right column) */
2405
+ .ps-tryon-sr-meas {
2406
+ border: 1px solid #282828; border-radius: 0.57vw; overflow: hidden;
2467
2407
  transition: border-color 0.2s;
2468
2408
  }
2469
- .ps-tryon-sr-fc.ps-fit-good { border-left: 3px solid #4ade80; }
2470
- .ps-tryon-sr-fc.ps-fit-tight { border-left: 3px solid #f59e0b; }
2471
- .ps-tryon-sr-fc.ps-fit-loose { border-left: 3px solid #60a5fa; }
2472
-
2473
- .ps-tryon-sr-fc-head {
2409
+ .ps-tryon-sr-meas.ps-fit-good { border-left: 3px solid #4ade80; }
2410
+ .ps-tryon-sr-meas.ps-fit-tight { border-left: 3px solid #f59e0b; }
2411
+ .ps-tryon-sr-meas.ps-fit-loose { border-left: 3px solid #60a5fa; }
2412
+ .ps-tryon-sr-meas-head {
2474
2413
  display: flex; align-items: center; justify-content: space-between;
2475
- padding: 0.57vw 0.83vw; background: #161616;
2476
- }
2477
- .ps-tryon-sr-fc-area { font-size: 0.83vw; font-weight: 700; color: #fff; }
2478
-
2479
- .ps-tryon-sr-fc-badge {
2480
- display: inline-flex; align-items: center; gap: 0.21vw; padding: 0.21vw 0.52vw;
2481
- border-radius: 0.31vw; font-size: 0.63vw; font-weight: 700; white-space: nowrap;
2482
- }
2483
- .ps-tryon-sr-fc-badge.ps-fit-good { background: rgba(74,222,128,0.08); color: #4ade80; }
2484
- .ps-tryon-sr-fc-badge.ps-fit-tight { background: rgba(245,158,11,0.08); color: #f59e0b; }
2485
- .ps-tryon-sr-fc-badge.ps-fit-loose { background: rgba(96,165,250,0.08); color: #60a5fa; }
2486
-
2487
- .ps-tryon-sr-fc-body {
2488
- display: flex; gap: 1.5vw; padding: 0.52vw 0.83vw 0.62vw;
2489
- }
2490
- .ps-tryon-sr-fc-col { flex: 1; }
2491
- .ps-tryon-sr-fc-label { font-size: 0.52vw; font-weight: 700; color: #555; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 0.15vw; }
2492
- .ps-tryon-sr-fc-value { font-size: 0.88vw; font-weight: 600; color: #ccc; }
2493
- .ps-tryon-sr-fc-edit { display: flex; align-items: center; gap: 0.26vw; }
2494
- .ps-tryon-sr-fc-input {
2495
- width: 4.2vw; padding: 0.26vw 0.42vw; border: 1.5px solid #444; border-radius: 0.36vw;
2496
- background: #0c0c0d; color: #fff; font-size: 0.88vw; font-weight: 600; font-family: inherit;
2414
+ padding: 0.47vw 0.73vw; background: #161616;
2415
+ }
2416
+ .ps-tryon-sr-meas-area { font-size: 0.78vw; font-weight: 700; color: #fff; }
2417
+ .ps-tryon-sr-meas-badge {
2418
+ display: inline-flex; align-items: center; gap: 0.21vw; padding: 0.16vw 0.47vw;
2419
+ border-radius: 0.26vw; font-size: 0.57vw; font-weight: 700; white-space: nowrap;
2420
+ }
2421
+ .ps-tryon-sr-meas-badge.ps-fit-good { background: rgba(74,222,128,0.08); color: #4ade80; }
2422
+ .ps-tryon-sr-meas-badge.ps-fit-tight { background: rgba(245,158,11,0.08); color: #f59e0b; }
2423
+ .ps-tryon-sr-meas-badge.ps-fit-loose { background: rgba(96,165,250,0.08); color: #60a5fa; }
2424
+ .ps-tryon-sr-meas-row {
2425
+ display: flex; align-items: center; gap: 0.52vw; padding: 0.47vw 0.73vw;
2426
+ }
2427
+ .ps-tryon-sr-meas-input-wrap { display: flex; align-items: center; gap: 0.21vw; }
2428
+ .ps-tryon-sr-meas-input {
2429
+ width: 3.8vw; padding: 0.26vw 0.36vw; border: 1.5px solid #444; border-radius: 0.31vw;
2430
+ background: #0c0c0d; color: #fff; font-size: 0.83vw; font-weight: 600; font-family: inherit;
2497
2431
  outline: none; text-align: center; -moz-appearance: textfield;
2498
2432
  transition: border-color 0.2s, background 0.2s;
2499
2433
  }
2500
- .ps-tryon-sr-fc-input::-webkit-outer-spin-button,
2501
- .ps-tryon-sr-fc-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
2502
- .ps-tryon-sr-fc-input:focus { border-color: #bb945c; background: #1a1a1a; }
2503
- .ps-tryon-sr-fc-unit { font-size: 0.63vw; color: #555; }
2434
+ .ps-tryon-sr-meas-input::-webkit-outer-spin-button,
2435
+ .ps-tryon-sr-meas-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
2436
+ .ps-tryon-sr-meas-input:focus { border-color: #bb945c; background: #1a1a1a; }
2437
+ .ps-tryon-sr-meas-unit { font-size: 0.63vw; color: #555; }
2438
+ .ps-tryon-sr-meas-vs { font-size: 0.63vw; color: #444; font-weight: 700; }
2439
+ .ps-tryon-sr-meas-chart { font-size: 0.83vw; font-weight: 600; color: #888; }
2504
2440
 
2505
- /* Compare dropdown */
2506
- .ps-tryon-sr-compare { }
2507
- .ps-tryon-sr-compare-select {
2508
- width: 100%; padding: 0.57vw 2vw 0.57vw 0.83vw; border: 1.5px solid #333; border-radius: 0.57vw;
2509
- background: #1a1b1a; color: #fff; font-size: 0.83vw; font-weight: 600; font-family: inherit;
2510
- appearance: none; -webkit-appearance: none; cursor: pointer; outline: none;
2511
- 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");
2512
- background-repeat: no-repeat; background-position: right 0.73vw center;
2513
- }
2514
- .ps-tryon-sr-compare-select:focus { border-color: #bb945c; }
2515
- .ps-tryon-sr-compare-select option { background: #1a1b1a; color: #fff; }
2516
- .ps-tryon-sr-compare-note {
2517
- font-size: 0.73vw; color: #bb945c; margin-top: 0.42vw; display: flex; align-items: center; gap: 0.52vw;
2518
- }
2519
- .ps-tryon-sr-compare-reset {
2520
- background: none; border: 1px solid #bb945c; color: #bb945c; padding: 0.16vw 0.52vw;
2521
- border-radius: 0.31vw; font-size: 0.63vw; font-weight: 600; cursor: pointer;
2522
- font-family: inherit; transition: all 0.2s;
2523
- }
2524
- .ps-tryon-sr-compare-reset:hover { background: rgba(187,148,92,0.1); }
2441
+ /* (compare dropdown removed — recalculation is automatic) */
2525
2442
 
2526
2443
  .ps-tryon-sr-fit-badge {
2527
2444
  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": "4.1.1",
3
+ "version": "4.2.0",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",
@@ -53,5 +53,8 @@
53
53
  "terser": "^5.31.0",
54
54
  "typescript": "^5.5.0",
55
55
  "vite": "^5.4.0"
56
+ },
57
+ "dependencies": {
58
+ "@primestyleai/tryon": "^4.1.2"
56
59
  }
57
60
  }