@primestyleai/tryon 5.10.186 → 5.10.188

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.
@@ -6979,7 +6979,7 @@ const es = {
6979
6979
  "Virtual Try-On": "Probador Virtual",
6980
6980
  "Find Your Size & See It On You": "Encuentra tu talla y pruébatelo",
6981
6981
  "See Your Fit": "Ver tu ajuste",
6982
- "Get the perfect fit, then try it on virtually": "Encuentra el ajuste perfecto y pruébatelo virtualmente",
6982
+ "Check your size, then try it on virtually": "Comprueba tu talla y pruébatelo virtualmente",
6983
6983
  "Get Your Size": "Tu talla",
6984
6984
  "Instant fit recommendation": "Recomendación de ajuste instantánea",
6985
6985
  "Try It On": "Pruébatelo",
@@ -7014,8 +7014,9 @@ const es = {
7014
7014
  "Edit measurements": "Editar medidas",
7015
7015
  "Size guide": "Guía de tallas",
7016
7016
  "Your fit": "Tu ajuste",
7017
+ "Your Size Recommendation": "Tu recomendación de talla",
7017
7018
  "Your measure": "Tu medida",
7018
- "perfect fit": "ajuste perfecto",
7019
+ "within range": "dentro del rango",
7019
7020
  "too tight": "demasiado ajustado",
7020
7021
  "tight": "ajustado",
7021
7022
  "a bit tight": "un poco ajustado",
@@ -7236,7 +7237,7 @@ const fr = {
7236
7237
  "Virtual Try-On": "Essayage Virtuel",
7237
7238
  "Find Your Size & See It On You": "Trouvez votre taille et essayez-le",
7238
7239
  "See Your Fit": "Voir votre ajustement",
7239
- "Get the perfect fit, then try it on virtually": "Trouvez la coupe parfaite, puis essayez-le virtuellement",
7240
+ "Check your size, then try it on virtually": "Vérifiez votre taille, puis essayez-le virtuellement",
7240
7241
  "Get Your Size": "Votre taille",
7241
7242
  "Instant fit recommendation": "Recommandation de coupe instantanée",
7242
7243
  "Try It On": "Essayer",
@@ -7271,8 +7272,9 @@ const fr = {
7271
7272
  "Edit measurements": "Modifier les mesures",
7272
7273
  "Size guide": "Guide des tailles",
7273
7274
  "Your fit": "Votre coupe",
7275
+ "Your Size Recommendation": "Votre recommandation de taille",
7274
7276
  "Your measure": "Votre mesure",
7275
- "perfect fit": "coupe parfaite",
7277
+ "within range": "dans la plage",
7276
7278
  "too tight": "trop serré",
7277
7279
  "tight": "serré",
7278
7280
  "a bit tight": "un peu serré",
@@ -7493,7 +7495,7 @@ const de = {
7493
7495
  "Virtual Try-On": "Virtuelle Anprobe",
7494
7496
  "Find Your Size & See It On You": "Finden Sie Ihre Größe und probieren Sie es an",
7495
7497
  "See Your Fit": "Ihre Passform ansehen",
7496
- "Get the perfect fit, then try it on virtually": "Finden Sie die perfekte Passform und probieren Sie es virtuell an",
7498
+ "Check your size, then try it on virtually": "Prüfen Sie Ihre Größe und probieren Sie es virtuell an",
7497
7499
  "Get Your Size": "Ihre Größe",
7498
7500
  "Instant fit recommendation": "Sofortige Passformempfehlung",
7499
7501
  "Try It On": "Anprobieren",
@@ -7528,8 +7530,9 @@ const de = {
7528
7530
  "Edit measurements": "Maße bearbeiten",
7529
7531
  "Size guide": "Größentabelle",
7530
7532
  "Your fit": "Ihre Passform",
7533
+ "Your Size Recommendation": "Ihre Größenempfehlung",
7531
7534
  "Your measure": "Ihr Maß",
7532
- "perfect fit": "perfekte Passform",
7535
+ "within range": "im Bereich",
7533
7536
  "too tight": "zu eng",
7534
7537
  "tight": "eng",
7535
7538
  "a bit tight": "etwas eng",
@@ -7750,7 +7753,7 @@ const it = {
7750
7753
  "Virtual Try-On": "Prova Virtuale",
7751
7754
  "Find Your Size & See It On You": "Trova la tua taglia e provalo",
7752
7755
  "See Your Fit": "Vedi la vestibilità",
7753
- "Get the perfect fit, then try it on virtually": "Trova la vestibilità perfetta, poi provalo virtualmente",
7756
+ "Check your size, then try it on virtually": "Controlla la tua taglia, poi provalo virtualmente",
7754
7757
  "Get Your Size": "La tua taglia",
7755
7758
  "Instant fit recommendation": "Raccomandazione vestibilità istantanea",
7756
7759
  "Try It On": "Provalo",
@@ -7785,8 +7788,9 @@ const it = {
7785
7788
  "Edit measurements": "Modifica misure",
7786
7789
  "Size guide": "Guida alle taglie",
7787
7790
  "Your fit": "La tua vestibilità",
7791
+ "Your Size Recommendation": "La tua taglia consigliata",
7788
7792
  "Your measure": "La tua misura",
7789
- "perfect fit": "vestibilità perfetta",
7793
+ "within range": "entro l'intervallo",
7790
7794
  "too tight": "troppo stretto",
7791
7795
  "tight": "stretto",
7792
7796
  "a bit tight": "un po' stretto",
@@ -8007,7 +8011,7 @@ const pt$1 = {
8007
8011
  "Virtual Try-On": "Provador Virtual",
8008
8012
  "Find Your Size & See It On You": "Encontre seu tamanho e experimente",
8009
8013
  "See Your Fit": "Veja seu ajuste",
8010
- "Get the perfect fit, then try it on virtually": "Encontre o caimento perfeito e experimente virtualmente",
8014
+ "Check your size, then try it on virtually": "Confira seu tamanho e experimente virtualmente",
8011
8015
  "Get Your Size": "Seu tamanho",
8012
8016
  "Instant fit recommendation": "Recomendação de caimento instantânea",
8013
8017
  "Try It On": "Experimentar",
@@ -8042,8 +8046,9 @@ const pt$1 = {
8042
8046
  "Edit measurements": "Editar medidas",
8043
8047
  "Size guide": "Guia de tamanhos",
8044
8048
  "Your fit": "Seu caimento",
8049
+ "Your Size Recommendation": "Sua recomendação de tamanho",
8045
8050
  "Your measure": "Sua medida",
8046
- "perfect fit": "caimento perfeito",
8051
+ "within range": "dentro da faixa",
8047
8052
  "too tight": "muito apertado",
8048
8053
  "tight": "apertado",
8049
8054
  "a bit tight": "um pouco apertado",
@@ -8264,7 +8269,7 @@ const ja = {
8264
8269
  "Virtual Try-On": "バーチャル試着",
8265
8270
  "Find Your Size & See It On You": "あなたのサイズを見つけて試着",
8266
8271
  "See Your Fit": "フィット感を見る",
8267
- "Get the perfect fit, then try it on virtually": "最適なフィットを見つけて、バーチャルで試着しましょう",
8272
+ "Check your size, then try it on virtually": "サイズを確認して、バーチャルで試着しましょう",
8268
8273
  "Get Your Size": "サイズを確認",
8269
8274
  "Instant fit recommendation": "瞬時にフィット提案",
8270
8275
  "Try It On": "試着する",
@@ -8299,8 +8304,9 @@ const ja = {
8299
8304
  "Edit measurements": "寸法を編集",
8300
8305
  "Size guide": "サイズガイド",
8301
8306
  "Your fit": "あなたのフィット",
8307
+ "Your Size Recommendation": "サイズのおすすめ",
8302
8308
  "Your measure": "あなたの寸法",
8303
- "perfect fit": "ぴったり",
8309
+ "within range": "範囲内",
8304
8310
  "too tight": "きつすぎる",
8305
8311
  "tight": "きつい",
8306
8312
  "a bit tight": "少しきつい",
@@ -8521,7 +8527,7 @@ const zh = {
8521
8527
  "Virtual Try-On": "虚拟试穿",
8522
8528
  "Find Your Size & See It On You": "找到你的尺码并试穿",
8523
8529
  "See Your Fit": "查看合身效果",
8524
- "Get the perfect fit, then try it on virtually": "找到最佳合身度,然后虚拟试穿",
8530
+ "Check your size, then try it on virtually": "确认尺码,然后虚拟试穿",
8525
8531
  "Get Your Size": "获取尺码",
8526
8532
  "Instant fit recommendation": "即时合身推荐",
8527
8533
  "Try It On": "试穿",
@@ -8556,8 +8562,9 @@ const zh = {
8556
8562
  "Edit measurements": "编辑尺寸",
8557
8563
  "Size guide": "尺码指南",
8558
8564
  "Your fit": "你的合身度",
8565
+ "Your Size Recommendation": "你的尺码建议",
8559
8566
  "Your measure": "你的尺寸",
8560
- "perfect fit": "完美合身",
8567
+ "within range": "在范围内",
8561
8568
  "too tight": "太紧",
8562
8569
  "tight": "偏紧",
8563
8570
  "a bit tight": "略紧",
@@ -8778,7 +8785,7 @@ const ko = {
8778
8785
  "Virtual Try-On": "가상 피팅",
8779
8786
  "Find Your Size & See It On You": "사이즈를 찾고 입어보세요",
8780
8787
  "See Your Fit": "핏 확인하기",
8781
- "Get the perfect fit, then try it on virtually": "완벽한 핏을 찾고 가상으로 입어보세요",
8788
+ "Check your size, then try it on virtually": "사이즈를 확인하고 가상으로 입어보세요",
8782
8789
  "Get Your Size": "사이즈 확인",
8783
8790
  "Instant fit recommendation": "즉시 핏 추천",
8784
8791
  "Try It On": "입어보기",
@@ -8813,8 +8820,9 @@ const ko = {
8813
8820
  "Edit measurements": "치수 수정",
8814
8821
  "Size guide": "사이즈 가이드",
8815
8822
  "Your fit": "나의 핏",
8823
+ "Your Size Recommendation": "사이즈 추천",
8816
8824
  "Your measure": "내 치수",
8817
- "perfect fit": "완벽한 ",
8825
+ "within range": "범위 ",
8818
8826
  "too tight": "너무 타이트",
8819
8827
  "tight": "타이트",
8820
8828
  "a bit tight": "약간 타이트",
@@ -9035,7 +9043,7 @@ const ar = {
9035
9043
  "Virtual Try-On": "تجربة افتراضية",
9036
9044
  "Find Your Size & See It On You": "اعثر على مقاسك وجرّبه",
9037
9045
  "See Your Fit": "شاهد الملاءمة",
9038
- "Get the perfect fit, then try it on virtually": "اعثر على المقاس المثالي ثم جرّبه افتراضياً",
9046
+ "Check your size, then try it on virtually": "تحقق من مقاسك ثم جرّبه افتراضياً",
9039
9047
  "Get Your Size": "مقاسك",
9040
9048
  "Instant fit recommendation": "توصية فورية بالمقاس",
9041
9049
  "Try It On": "جرّبه",
@@ -9070,8 +9078,9 @@ const ar = {
9070
9078
  "Edit measurements": "تعديل القياسات",
9071
9079
  "Size guide": "دليل المقاسات",
9072
9080
  "Your fit": "ملاءمتك",
9081
+ "Your Size Recommendation": "توصية المقاس",
9073
9082
  "Your measure": "قياسك",
9074
- "perfect fit": "مقاس مثالي",
9083
+ "within range": "ضمن النطاق",
9075
9084
  "too tight": "ضيق جداً",
9076
9085
  "tight": "ضيق",
9077
9086
  "a bit tight": "ضيق قليلاً",
@@ -9465,6 +9474,8 @@ class ApiClient {
9465
9474
  if (category && category !== "apparel") body.category = category;
9466
9475
  if (context?.productId) body.productId = context.productId;
9467
9476
  if (context?.productTitle) body.productTitle = context.productTitle;
9477
+ if (context?.productCategory) body.productCategory = context.productCategory;
9478
+ if (context?.productSubcategory) body.productSubcategory = context.productSubcategory;
9468
9479
  if (context?.productFitType) body.productFitType = context.productFitType;
9469
9480
  if (context?.productType) body.productType = context.productType;
9470
9481
  if (context?.productTags?.length) body.productTags = context.productTags;
@@ -10280,6 +10291,9 @@ function formatUserMeasurementValue(measurement, value) {
10280
10291
  if (!isUnitlessShoeSizeMeasurement(measurement)) return value;
10281
10292
  return value.replace(/\s*(cm|mm|in|inch|inches)\b/ig, "").trim();
10282
10293
  }
10294
+ function normalizePromptSizeLabel(size) {
10295
+ return String(size || "").trim().replace(/\s+[–—-]\s+(?:UK|US|EU|IT|FR|DE|ES|JP|CN|KR|AU|BR)\s+.+$/i, "").replace(/\s*\((?:UK|US|EU|IT|FR|DE|ES|JP|CN|KR|AU|BR)\s+[^)]*\)\s*$/i, "").trim();
10296
+ }
10283
10297
  function computeFit(userValue, chartRange, unit) {
10284
10298
  const targetUnit = normalizeUnit(unit) || detectUnitFromText(chartRange) || "in";
10285
10299
  const chartUnit = detectUnitFromText(chartRange);
@@ -10340,7 +10354,7 @@ function buildSilhouetteContext(sizingResult, sizeGuide, selectedSizeOverride, u
10340
10354
  if (!sizingResult && !sizeGuide && !userHeight && !userWeight) return void 0;
10341
10355
  const out = {};
10342
10356
  const promptUnit = normalizeUnit(sizingResult?.unit) || detectUnitFromText(sizingResult?.matchDetails?.[0]?.userValue) || detectUnitFromText(Object.values(sizingResult?.sections || {})[0]?.matchDetails?.[0]?.userValue) || "in";
10343
- const baseSize = (selectedSizeOverride || sizingResult?.recommendedSize || "").toString().trim();
10357
+ const baseSize = normalizePromptSizeLabel(selectedSizeOverride || sizingResult?.recommendedSize || "");
10344
10358
  if (userHeight) out.userHeight = userHeight;
10345
10359
  if (userWeight) out.userWeight = userWeight;
10346
10360
  let chartRowLength = null;
@@ -10390,7 +10404,7 @@ function buildSilhouetteContext(sizingResult, sizeGuide, selectedSizeOverride, u
10390
10404
  const labelParts = [];
10391
10405
  const measurementParts = [];
10392
10406
  for (const [secName, secResult] of sectionEntries) {
10393
- const secSize = (secResult?.recommendedSize ?? "").toString().trim();
10407
+ const secSize = normalizePromptSizeLabel(secResult?.recommendedSize ?? "");
10394
10408
  if (!secSize) continue;
10395
10409
  const cleanSec = /\bsize\s*$/i.test(secName) ? secName.replace(/\s*\bsize\s*$/i, "").trim() : secName;
10396
10410
  labelParts.push(`${cleanSec} ${secSize}`);
@@ -10496,6 +10510,24 @@ function lsSet(key, value) {
10496
10510
  } catch {
10497
10511
  }
10498
10512
  }
10513
+ function lsRemove(key) {
10514
+ try {
10515
+ localStorage.removeItem(LS_PREFIX + key);
10516
+ } catch {
10517
+ }
10518
+ }
10519
+ function clearProfileLocalStorage() {
10520
+ lsRemove(PROFILES_KEY);
10521
+ lsRemove(ACTIVE_PROFILE_KEY);
10522
+ lsRemove("profile_completion_draft");
10523
+ try {
10524
+ localStorage.removeItem(PROFILES_KEY);
10525
+ localStorage.removeItem(ACTIVE_PROFILE_KEY);
10526
+ localStorage.removeItem("profile_completion_draft");
10527
+ } catch {
10528
+ }
10529
+ emitStorageChange("profile-clear");
10530
+ }
10499
10531
  function getProfiles() {
10500
10532
  return lsGet(PROFILES_KEY, []);
10501
10533
  }
@@ -10510,19 +10542,6 @@ function setActiveProfileId(id2) {
10510
10542
  lsSet(ACTIVE_PROFILE_KEY, id2);
10511
10543
  emitStorageChange("active-profile");
10512
10544
  }
10513
- function getActiveProfile() {
10514
- const profiles = getProfiles();
10515
- if (profiles.length === 0) return null;
10516
- const activeId = getActiveProfileId();
10517
- if (activeId) {
10518
- const found = profiles.find((p2) => p2.id === activeId);
10519
- if (found) return found;
10520
- }
10521
- const sorted = [...profiles].sort(
10522
- (a, b) => (b.lastUsedAt || b.createdAt || 0) - (a.lastUsedAt || a.createdAt || 0)
10523
- );
10524
- return sorted[0] || null;
10525
- }
10526
10545
  function updateProfile(id2, patch) {
10527
10546
  const profiles = getProfiles();
10528
10547
  const idx = profiles.findIndex((p2) => p2.id === id2);
@@ -10937,6 +10956,179 @@ function getApiUrl(override) {
10937
10956
  }
10938
10957
  return envUrl || "http://localhost:4000";
10939
10958
  }
10959
+ const SESSION_KEY = "primestyle_profile_session";
10960
+ const AUTH_MESSAGE_TYPE = "PRIMESTYLE_SDK_AUTH";
10961
+ const POPUP_TIMEOUT_MS = 12e4;
10962
+ const POLL_INTERVAL_MS = 900;
10963
+ function isTrustedAuthOrigin(eventOrigin, expectedOrigin) {
10964
+ if (eventOrigin === expectedOrigin) return true;
10965
+ try {
10966
+ const host = new URL(eventOrigin).hostname;
10967
+ return host === "localhost" || host === "127.0.0.1" || host.endsWith(".primestyleai.com") || host.endsWith(".myaifitting.com");
10968
+ } catch {
10969
+ return false;
10970
+ }
10971
+ }
10972
+ function readStorage() {
10973
+ if (typeof window === "undefined") return null;
10974
+ try {
10975
+ return window.localStorage;
10976
+ } catch {
10977
+ return null;
10978
+ }
10979
+ }
10980
+ function createAuthRequestId() {
10981
+ try {
10982
+ const randomId = window.crypto?.randomUUID?.();
10983
+ if (randomId) return randomId;
10984
+ } catch {
10985
+ }
10986
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
10987
+ }
10988
+ async function readAuthStatus(statusUrl) {
10989
+ const response = await fetch(statusUrl.toString(), {
10990
+ method: "GET",
10991
+ credentials: "omit",
10992
+ cache: "no-store"
10993
+ });
10994
+ if (!response.ok) return null;
10995
+ const data = await response.json();
10996
+ return data.status === "done" ? data : null;
10997
+ }
10998
+ function getStoredProfileSession() {
10999
+ const storage = readStorage();
11000
+ if (!storage) return null;
11001
+ try {
11002
+ const raw = storage.getItem(SESSION_KEY);
11003
+ if (!raw) return null;
11004
+ const parsed = JSON.parse(raw);
11005
+ return parsed?.accessToken ? parsed : null;
11006
+ } catch {
11007
+ return null;
11008
+ }
11009
+ }
11010
+ function setStoredProfileSession(session) {
11011
+ const storage = readStorage();
11012
+ if (!storage) return;
11013
+ try {
11014
+ storage.setItem(SESSION_KEY, JSON.stringify(session));
11015
+ } catch {
11016
+ }
11017
+ }
11018
+ function clearStoredProfileSession() {
11019
+ const storage = readStorage();
11020
+ if (!storage) return;
11021
+ try {
11022
+ storage.removeItem(SESSION_KEY);
11023
+ } catch {
11024
+ }
11025
+ }
11026
+ function startSocialProfileLogin(provider, apiUrl) {
11027
+ if (typeof window === "undefined") {
11028
+ return Promise.reject(new Error("Social login must run in the browser."));
11029
+ }
11030
+ const baseUrl = getApiUrl(apiUrl);
11031
+ const backendOrigin = new URL(baseUrl).origin;
11032
+ const authRequestId = createAuthRequestId();
11033
+ const startUrl = new URL(`${baseUrl.replace(/\/$/, "")}/api/sdk/v1/auth/${provider}/start`);
11034
+ startUrl.searchParams.set("origin", window.location.origin);
11035
+ startUrl.searchParams.set("requestId", authRequestId);
11036
+ const statusUrl = new URL(`${baseUrl.replace(/\/$/, "")}/api/sdk/v1/auth/${provider}/status`);
11037
+ statusUrl.searchParams.set("origin", window.location.origin);
11038
+ statusUrl.searchParams.set("requestId", authRequestId);
11039
+ const popup = window.open(
11040
+ startUrl.toString(),
11041
+ "primestyle-profile-login",
11042
+ "width=520,height=720"
11043
+ );
11044
+ if (!popup) {
11045
+ return Promise.reject(new Error("Popup was blocked. Please allow popups and try again."));
11046
+ }
11047
+ return new Promise((resolve, reject) => {
11048
+ let settled = false;
11049
+ const cleanup = () => {
11050
+ window.removeEventListener("message", onMessage);
11051
+ window.clearInterval(statusTimer);
11052
+ window.clearTimeout(timeout);
11053
+ };
11054
+ const finish = (fn) => {
11055
+ if (settled) return;
11056
+ settled = true;
11057
+ cleanup();
11058
+ fn();
11059
+ };
11060
+ const onMessage = (event) => {
11061
+ if (!isTrustedAuthOrigin(event.origin, backendOrigin)) return;
11062
+ const data = event.data;
11063
+ if (!data || data.type !== AUTH_MESSAGE_TYPE) return;
11064
+ if (!data.ok || !data.accessToken) {
11065
+ finish(() => reject(new Error(data.error || "Social login failed.")));
11066
+ return;
11067
+ }
11068
+ finish(() => resolve({
11069
+ accessToken: data.accessToken,
11070
+ isNewUser: Boolean(data.isNewUser),
11071
+ signedInAt: Date.now()
11072
+ }));
11073
+ };
11074
+ const handleAuthPayload = (data) => {
11075
+ if (!data.ok || !data.accessToken) {
11076
+ finish(() => reject(new Error(data.error || "Social login failed.")));
11077
+ return;
11078
+ }
11079
+ finish(() => resolve({
11080
+ accessToken: data.accessToken,
11081
+ isNewUser: Boolean(data.isNewUser),
11082
+ signedInAt: Date.now()
11083
+ }));
11084
+ };
11085
+ const statusTimer = window.setInterval(() => {
11086
+ readAuthStatus(statusUrl).then((data) => {
11087
+ if (data) handleAuthPayload(data);
11088
+ }).catch(() => {
11089
+ });
11090
+ }, POLL_INTERVAL_MS);
11091
+ const timeout = window.setTimeout(() => {
11092
+ finish(() => reject(new Error("Login did not finish. Please close the sign-in window and try again.")));
11093
+ }, POPUP_TIMEOUT_MS);
11094
+ window.addEventListener("message", onMessage);
11095
+ });
11096
+ }
11097
+ function endpoint(apiUrl) {
11098
+ return `${getApiUrl(apiUrl).replace(/\/$/, "")}/api/sdk/v1/profiles`;
11099
+ }
11100
+ function normalizeStore(data) {
11101
+ return {
11102
+ profiles: Array.isArray(data.profiles) ? data.profiles : [],
11103
+ activeProfileId: data.activeProfileId ?? null
11104
+ };
11105
+ }
11106
+ async function parseResponse(response) {
11107
+ const data = await response.json().catch(() => ({}));
11108
+ if (!response.ok) {
11109
+ const message = typeof data.message === "string" ? data.message : "Profile sync failed.";
11110
+ throw new Error(message);
11111
+ }
11112
+ return data;
11113
+ }
11114
+ async function fetchRemoteProfiles(apiUrl, accessToken) {
11115
+ const response = await fetch(endpoint(apiUrl), {
11116
+ method: "GET",
11117
+ headers: { Authorization: `Bearer ${accessToken}` }
11118
+ });
11119
+ return normalizeStore(await parseResponse(response));
11120
+ }
11121
+ async function saveRemoteProfiles(apiUrl, accessToken, profiles, activeProfileId) {
11122
+ const response = await fetch(endpoint(apiUrl), {
11123
+ method: "PUT",
11124
+ headers: {
11125
+ Authorization: `Bearer ${accessToken}`,
11126
+ "Content-Type": "application/json"
11127
+ },
11128
+ body: JSON.stringify({ profiles, activeProfileId })
11129
+ });
11130
+ return normalizeStore(await parseResponse(response));
11131
+ }
10940
11132
  let cachedMP = null;
10941
11133
  const MP_CACHE_TTL_MS = 6e4;
10942
11134
  function setCachedMediaPipe(landmarks) {
@@ -10962,9 +11154,26 @@ async function recommendForProduct(input) {
10962
11154
  const t0 = Date.now();
10963
11155
  log("ENTER", { productId: input.productId, apiUrl: input.apiUrl, skipCache: !!input.skipCache });
10964
11156
  const sessionId = getOrCreateSessionId();
10965
- const profile = input.profile ?? getActiveProfile();
11157
+ const apiKey = input.apiKey ?? getApiKey();
11158
+ const apiUrl = (input.apiUrl ?? getApiUrl()).replace(/\/+$/, "");
11159
+ const session = getStoredProfileSession();
11160
+ if (!session) {
11161
+ log("no signed-in profile session — returning null");
11162
+ return null;
11163
+ }
11164
+ let remoteProfiles = [];
11165
+ let activeProfileId = null;
11166
+ try {
11167
+ const remoteStore = await fetchRemoteProfiles(apiUrl, session.accessToken);
11168
+ remoteProfiles = remoteStore.profiles;
11169
+ activeProfileId = remoteStore.activeProfileId;
11170
+ } catch (error) {
11171
+ log("remote profile fetch failed — returning null", error);
11172
+ return null;
11173
+ }
11174
+ const profile = input.profile && input.profile.id === activeProfileId ? input.profile : activeProfileId ? remoteProfiles.find((p2) => p2.id === activeProfileId) ?? null : null;
10966
11175
  if (!profile) {
10967
- log("no active profile — returning null");
11176
+ log("no selected backend profile — returning null");
10968
11177
  return null;
10969
11178
  }
10970
11179
  log("profile resolved", {
@@ -11017,8 +11226,6 @@ async function recommendForProduct(input) {
11017
11226
  }
11018
11227
  }
11019
11228
  log(`cache MISS — calling backend (elapsed in pre-flight: ${Date.now() - t0}ms)`);
11020
- const apiKey = input.apiKey ?? getApiKey();
11021
- const apiUrl = (input.apiUrl ?? getApiUrl()).replace(/\/+$/, "");
11022
11229
  let sizeGuide = null;
11023
11230
  if (input.sizeGuideData != null) {
11024
11231
  const tSg = Date.now();
@@ -11063,6 +11270,34 @@ async function recommendForProduct(input) {
11063
11270
  if (value != null) measurements[key] = value;
11064
11271
  }
11065
11272
  }
11273
+ const legacyKnownMeasurements = {};
11274
+ for (const key of [
11275
+ "chest",
11276
+ "bust",
11277
+ "waist",
11278
+ "hips",
11279
+ "shoulderWidth",
11280
+ "sleeveLength",
11281
+ "inseam",
11282
+ "neckCircumference",
11283
+ "thighCircumference",
11284
+ "wristCircumference",
11285
+ "footLengthCm"
11286
+ ]) {
11287
+ const value = profile[key];
11288
+ if (typeof value === "number" && value > 0) {
11289
+ measurements[key] = value;
11290
+ legacyKnownMeasurements[key] = value;
11291
+ }
11292
+ }
11293
+ if (profile.customMeasurements) {
11294
+ for (const [key, value] of Object.entries(profile.customMeasurements)) {
11295
+ if (typeof value === "number" && value > 0) {
11296
+ measurements[key] = value;
11297
+ legacyKnownMeasurements[key] = value;
11298
+ }
11299
+ }
11300
+ }
11066
11301
  if (profile.height != null) measurements.height = profile.height;
11067
11302
  if (profile.weight != null) measurements.weight = profile.weight;
11068
11303
  if (profile.heightUnit) measurements.heightUnit = profile.heightUnit;
@@ -11099,6 +11334,8 @@ async function recommendForProduct(input) {
11099
11334
  }
11100
11335
  if (profile.knownMeasurements) {
11101
11336
  payload.knownMeasurements = profile.knownMeasurements;
11337
+ } else if (Object.keys(legacyKnownMeasurements).length > 0) {
11338
+ payload.knownMeasurements = legacyKnownMeasurements;
11102
11339
  }
11103
11340
  if (sizeGuide && sizeGuide.found) {
11104
11341
  payload.sizeGuide = sizeGuide;
@@ -11146,7 +11383,7 @@ async function recommendForProduct(input) {
11146
11383
  }
11147
11384
  ])
11148
11385
  ) : void 0;
11149
- addSizeToHistory(profile.id, {
11386
+ const newHistoryEntry = {
11150
11387
  productId: input.productId,
11151
11388
  productTitle: input.productTitle,
11152
11389
  productImage: input.productImage,
@@ -11156,6 +11393,15 @@ async function recommendForProduct(input) {
11156
11393
  sectionsFull,
11157
11394
  recommendedLength: result.recommendedLength || void 0,
11158
11395
  savedAt: Date.now()
11396
+ };
11397
+ const updatedProfiles = remoteProfiles.map((p2) => {
11398
+ if (p2.id !== profile.id) return p2;
11399
+ const history = (p2.sizeHistory || []).filter((h) => h.productId !== input.productId);
11400
+ history.unshift(newHistoryEntry);
11401
+ return { ...p2, sizeHistory: history.slice(0, 50), lastUsedAt: Date.now() };
11402
+ });
11403
+ void saveRemoteProfiles(apiUrl, session.accessToken, updatedProfiles, activeProfileId).catch((error) => {
11404
+ log("remote size-history save failed", error);
11159
11405
  });
11160
11406
  setLastSizeSelection({
11161
11407
  productId: input.productId,
@@ -11345,179 +11591,6 @@ async function pickBestGarmentImage(images) {
11345
11591
  for (const s of scored) console.log(`[ps-sdk:garment-pick] ${s.score.toString().padStart(4, " ")} ${s.url}`);
11346
11592
  return best;
11347
11593
  }
11348
- const SESSION_KEY = "primestyle_profile_session";
11349
- const AUTH_MESSAGE_TYPE = "PRIMESTYLE_SDK_AUTH";
11350
- const POPUP_TIMEOUT_MS = 12e4;
11351
- const POLL_INTERVAL_MS = 900;
11352
- function isTrustedAuthOrigin(eventOrigin, expectedOrigin) {
11353
- if (eventOrigin === expectedOrigin) return true;
11354
- try {
11355
- const host = new URL(eventOrigin).hostname;
11356
- return host === "localhost" || host === "127.0.0.1" || host.endsWith(".primestyleai.com") || host.endsWith(".myaifitting.com");
11357
- } catch {
11358
- return false;
11359
- }
11360
- }
11361
- function readStorage() {
11362
- if (typeof window === "undefined") return null;
11363
- try {
11364
- return window.localStorage;
11365
- } catch {
11366
- return null;
11367
- }
11368
- }
11369
- function createAuthRequestId() {
11370
- try {
11371
- const randomId = window.crypto?.randomUUID?.();
11372
- if (randomId) return randomId;
11373
- } catch {
11374
- }
11375
- return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
11376
- }
11377
- async function readAuthStatus(statusUrl) {
11378
- const response = await fetch(statusUrl.toString(), {
11379
- method: "GET",
11380
- credentials: "omit",
11381
- cache: "no-store"
11382
- });
11383
- if (!response.ok) return null;
11384
- const data = await response.json();
11385
- return data.status === "done" ? data : null;
11386
- }
11387
- function getStoredProfileSession() {
11388
- const storage = readStorage();
11389
- if (!storage) return null;
11390
- try {
11391
- const raw = storage.getItem(SESSION_KEY);
11392
- if (!raw) return null;
11393
- const parsed = JSON.parse(raw);
11394
- return parsed?.accessToken ? parsed : null;
11395
- } catch {
11396
- return null;
11397
- }
11398
- }
11399
- function setStoredProfileSession(session) {
11400
- const storage = readStorage();
11401
- if (!storage) return;
11402
- try {
11403
- storage.setItem(SESSION_KEY, JSON.stringify(session));
11404
- } catch {
11405
- }
11406
- }
11407
- function clearStoredProfileSession() {
11408
- const storage = readStorage();
11409
- if (!storage) return;
11410
- try {
11411
- storage.removeItem(SESSION_KEY);
11412
- } catch {
11413
- }
11414
- }
11415
- function startSocialProfileLogin(provider, apiUrl) {
11416
- if (typeof window === "undefined") {
11417
- return Promise.reject(new Error("Social login must run in the browser."));
11418
- }
11419
- const baseUrl = getApiUrl(apiUrl);
11420
- const backendOrigin = new URL(baseUrl).origin;
11421
- const authRequestId = createAuthRequestId();
11422
- const startUrl = new URL(`${baseUrl.replace(/\/$/, "")}/api/sdk/v1/auth/${provider}/start`);
11423
- startUrl.searchParams.set("origin", window.location.origin);
11424
- startUrl.searchParams.set("requestId", authRequestId);
11425
- const statusUrl = new URL(`${baseUrl.replace(/\/$/, "")}/api/sdk/v1/auth/${provider}/status`);
11426
- statusUrl.searchParams.set("origin", window.location.origin);
11427
- statusUrl.searchParams.set("requestId", authRequestId);
11428
- const popup = window.open(
11429
- startUrl.toString(),
11430
- "primestyle-profile-login",
11431
- "width=520,height=720"
11432
- );
11433
- if (!popup) {
11434
- return Promise.reject(new Error("Popup was blocked. Please allow popups and try again."));
11435
- }
11436
- return new Promise((resolve, reject) => {
11437
- let settled = false;
11438
- const cleanup = () => {
11439
- window.removeEventListener("message", onMessage);
11440
- window.clearInterval(statusTimer);
11441
- window.clearTimeout(timeout);
11442
- };
11443
- const finish = (fn) => {
11444
- if (settled) return;
11445
- settled = true;
11446
- cleanup();
11447
- fn();
11448
- };
11449
- const onMessage = (event) => {
11450
- if (!isTrustedAuthOrigin(event.origin, backendOrigin)) return;
11451
- const data = event.data;
11452
- if (!data || data.type !== AUTH_MESSAGE_TYPE) return;
11453
- if (!data.ok || !data.accessToken) {
11454
- finish(() => reject(new Error(data.error || "Social login failed.")));
11455
- return;
11456
- }
11457
- finish(() => resolve({
11458
- accessToken: data.accessToken,
11459
- isNewUser: Boolean(data.isNewUser),
11460
- signedInAt: Date.now()
11461
- }));
11462
- };
11463
- const handleAuthPayload = (data) => {
11464
- if (!data.ok || !data.accessToken) {
11465
- finish(() => reject(new Error(data.error || "Social login failed.")));
11466
- return;
11467
- }
11468
- finish(() => resolve({
11469
- accessToken: data.accessToken,
11470
- isNewUser: Boolean(data.isNewUser),
11471
- signedInAt: Date.now()
11472
- }));
11473
- };
11474
- const statusTimer = window.setInterval(() => {
11475
- readAuthStatus(statusUrl).then((data) => {
11476
- if (data) handleAuthPayload(data);
11477
- }).catch(() => {
11478
- });
11479
- }, POLL_INTERVAL_MS);
11480
- const timeout = window.setTimeout(() => {
11481
- finish(() => reject(new Error("Login did not finish. Please close the sign-in window and try again.")));
11482
- }, POPUP_TIMEOUT_MS);
11483
- window.addEventListener("message", onMessage);
11484
- });
11485
- }
11486
- function endpoint(apiUrl) {
11487
- return `${getApiUrl(apiUrl).replace(/\/$/, "")}/api/sdk/v1/profiles`;
11488
- }
11489
- function normalizeStore(data) {
11490
- return {
11491
- profiles: Array.isArray(data.profiles) ? data.profiles : [],
11492
- activeProfileId: data.activeProfileId ?? null
11493
- };
11494
- }
11495
- async function parseResponse(response) {
11496
- const data = await response.json().catch(() => ({}));
11497
- if (!response.ok) {
11498
- const message = typeof data.message === "string" ? data.message : "Profile sync failed.";
11499
- throw new Error(message);
11500
- }
11501
- return data;
11502
- }
11503
- async function fetchRemoteProfiles(apiUrl, accessToken) {
11504
- const response = await fetch(endpoint(apiUrl), {
11505
- method: "GET",
11506
- headers: { Authorization: `Bearer ${accessToken}` }
11507
- });
11508
- return normalizeStore(await parseResponse(response));
11509
- }
11510
- async function saveRemoteProfiles(apiUrl, accessToken, profiles, activeProfileId) {
11511
- const response = await fetch(endpoint(apiUrl), {
11512
- method: "PUT",
11513
- headers: {
11514
- Authorization: `Bearer ${accessToken}`,
11515
- "Content-Type": "application/json"
11516
- },
11517
- body: JSON.stringify({ profiles, activeProfileId })
11518
- });
11519
- return normalizeStore(await parseResponse(response));
11520
- }
11521
11594
  function normalizeProfilePhotoSource(value) {
11522
11595
  if (!value) return null;
11523
11596
  if (/^https?:\/\//i.test(value)) return value;
@@ -16611,7 +16684,7 @@ const STYLES = `
16611
16684
  to { opacity: 1; }
16612
16685
  }
16613
16686
 
16614
- /* Minimal "Using <profile> · start fresh" hint above the first input */
16687
+ /* Active profile status above body details */
16615
16688
  .ps-bp-profile-hint {
16616
16689
  margin: 0; padding: 0;
16617
16690
  text-align: center;
@@ -16631,6 +16704,65 @@ const STYLES = `
16631
16704
  cursor: pointer; padding: 0;
16632
16705
  }
16633
16706
  .ps-bp-profile-hint-link:hover { color: var(--ps-text-secondary); }
16707
+ .ps-bp-profile-card {
16708
+ display: flex;
16709
+ align-items: center;
16710
+ justify-content: space-between;
16711
+ gap: clamp(10px, 0.9vw, 18px);
16712
+ width: min(100%, 520px);
16713
+ margin: 0 auto clamp(10px, 0.9vw, 18px);
16714
+ padding: clamp(10px, 0.85vw, 16px) clamp(12px, 1vw, 20px);
16715
+ border: 1px solid rgba(33, 84, 239, 0.18);
16716
+ border-radius: clamp(8px, 0.7vw, 12px);
16717
+ background: linear-gradient(135deg, rgba(33, 84, 239, 0.08), rgba(255, 255, 255, 0.96));
16718
+ box-shadow: 0 10px 28px -24px rgba(33, 84, 239, 0.55);
16719
+ }
16720
+ .ps-bp-profile-card-copy {
16721
+ display: flex;
16722
+ flex-direction: column;
16723
+ gap: clamp(2px, 0.18vw, 4px);
16724
+ min-width: 0;
16725
+ }
16726
+ .ps-bp-profile-card-eyebrow {
16727
+ font-size: clamp(8px, 0.55vw, 10px);
16728
+ font-weight: 800;
16729
+ letter-spacing: 0.14em;
16730
+ text-transform: uppercase;
16731
+ color: var(--ps-accent);
16732
+ }
16733
+ .ps-bp-profile-card-copy strong {
16734
+ font-size: clamp(13px, 0.95vw, 17px);
16735
+ line-height: 1.1;
16736
+ color: var(--ps-text-primary);
16737
+ overflow: hidden;
16738
+ text-overflow: ellipsis;
16739
+ white-space: nowrap;
16740
+ }
16741
+ .ps-bp-profile-card-copy span:last-child {
16742
+ font-size: clamp(10px, 0.68vw, 12px);
16743
+ line-height: 1.35;
16744
+ color: var(--ps-text-secondary);
16745
+ }
16746
+ .ps-bp-profile-card-action {
16747
+ flex-shrink: 0;
16748
+ border: 1px solid rgba(33, 84, 239, 0.22);
16749
+ background: #FFFFFF;
16750
+ color: var(--ps-accent);
16751
+ border-radius: 999px;
16752
+ padding: clamp(7px, 0.55vw, 10px) clamp(10px, 0.8vw, 16px);
16753
+ font-family: inherit;
16754
+ font-size: clamp(9px, 0.62vw, 11px);
16755
+ font-weight: 800;
16756
+ letter-spacing: 0.08em;
16757
+ text-transform: uppercase;
16758
+ cursor: pointer;
16759
+ transition: border-color 0.15s, background 0.15s, transform 0.15s;
16760
+ }
16761
+ .ps-bp-profile-card-action:hover {
16762
+ border-color: var(--ps-accent);
16763
+ background: rgba(33, 84, 239, 0.06);
16764
+ }
16765
+ .ps-bp-profile-card-action:active { transform: scale(0.98); }
16634
16766
 
16635
16767
  /* Typography */
16636
16768
  .ps-bp-title {
@@ -19259,6 +19391,9 @@ const STYLES = `
19259
19391
  background: var(--ps-bg-primary);
19260
19392
  flex-shrink: 0;
19261
19393
  }
19394
+ .ps-cpw-footer-no-back {
19395
+ justify-content: flex-end;
19396
+ }
19262
19397
  .ps-cpw-back-btn {
19263
19398
  background: none; border: none;
19264
19399
  color: var(--ps-text-secondary);
@@ -19902,6 +20037,7 @@ const STYLES = `
19902
20037
  display: flex; flex-direction: column;
19903
20038
  gap: max(14px, 1.2vw);
19904
20039
  min-width: 0; width: 100%;
20040
+ padding-bottom: clamp(78px, 5.4vw, 104px);
19905
20041
  }
19906
20042
 
19907
20043
  /* Basics list — height / weight / age, inline icon + label + value */
@@ -20137,12 +20273,12 @@ const STYLES = `
20137
20273
  gap: clamp(8px, 0.7vw, 14px);
20138
20274
  position: sticky;
20139
20275
  bottom: 0;
20140
- background: var(--ps-bg-primary);
20276
+ background: color-mix(in srgb, var(--ps-bg-primary) 96%, #FFFFFF);
20141
20277
  border-top: 1px solid var(--ps-border-subtle);
20142
- padding-top: clamp(8px, 0.7vw, 14px);
20143
- padding-bottom: clamp(8px, 0.7vw, 14px);
20144
- z-index: 2;
20278
+ padding: clamp(10px, 0.8vw, 16px) clamp(4px, 0.35vw, 8px) calc(clamp(10px, 0.8vw, 16px) + env(safe-area-inset-bottom, 0px));
20279
+ z-index: 5;
20145
20280
  margin-top: auto;
20281
+ box-shadow: 0 -14px 30px -28px rgba(17, 24, 39, 0.35);
20146
20282
  }
20147
20283
  .ps-pmv-actions-right {
20148
20284
  display: flex; align-items: center;
@@ -20154,7 +20290,7 @@ const STYLES = `
20154
20290
  background: none;
20155
20291
  border: 1px solid var(--ps-border-color);
20156
20292
  border-radius: clamp(4px, 0.35vw, 8px);
20157
- padding: clamp(6px, 0.55vw, 12px) clamp(10px, 0.9vw, 20px);
20293
+ padding: clamp(6px, 0.55vw, 12px) clamp(13px, 1.05vw, 24px);
20158
20294
  font-family: inherit;
20159
20295
  font-size: clamp(9px, 0.65vw, 12px);
20160
20296
  font-weight: 600;
@@ -20176,7 +20312,7 @@ const STYLES = `
20176
20312
  background: var(--ps-accent); color: #FFFFFF;
20177
20313
  border: none;
20178
20314
  border-radius: clamp(4px, 0.35vw, 8px);
20179
- padding: clamp(7px, 0.65vw, 14px) clamp(12px, 1vw, 22px);
20315
+ padding: clamp(7px, 0.65vw, 14px) clamp(15px, 1.15vw, 26px);
20180
20316
  font-family: inherit;
20181
20317
  font-size: clamp(9px, 0.65vw, 12px);
20182
20318
  font-weight: 700;
@@ -21824,7 +21960,7 @@ function WelcomeView({
21824
21960
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-welcome-sparkle", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SparkleIcon, { size: 20 }) })
21825
21961
  ] }),
21826
21962
  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "ps-tryon-welcome-title", children: t2("See Your Fit") }),
21827
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ps-tryon-welcome-sub", children: t2("Get the perfect fit, then try it on virtually") })
21963
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ps-tryon-welcome-sub", children: t2("Check your size, then try it on virtually") })
21828
21964
  ] }),
21829
21965
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-features", children: [
21830
21966
  { icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RulerIcon$1, { size: 22 }), title: t2("Get Your Size"), desc: t2("Instant fit recommendation") },
@@ -22155,7 +22291,7 @@ function MobileScanningView({
22155
22291
  { title: t2("FINALIZING RESULT"), desc: t2("Almost done — preparing your recommendation."), viewfinderText: t2("FINALIZING") }
22156
22292
  ] : [
22157
22293
  { title: t2("DETECTING POSE"), desc: t2("Identifying body landmarks from your photo."), viewfinderText: t2("DETECTING POSE") },
22158
- { title: t2("SCANNING FRAME"), desc: t2("Our AI is mapping your proportions to calculate the perfect fit."), viewfinderText: t2("SCANNING FRAME") },
22294
+ { title: t2("SCANNING FRAME"), desc: t2("Our AI is mapping your proportions for a size recommendation."), viewfinderText: t2("SCANNING FRAME") },
22159
22295
  { title: t2("ANALYZING BODY"), desc: t2("Measuring shoulders, chest, waist and hips."), viewfinderText: t2("ANALYZING") },
22160
22296
  { title: t2("MATCHING SIZE"), desc: t2("Comparing your measurements to the size guide."), viewfinderText: t2("MATCHING SIZE") },
22161
22297
  { title: t2("FINALIZING RESULT"), desc: t2("Almost done — preparing your recommendation."), viewfinderText: t2("FINALIZING") }
@@ -23168,10 +23304,10 @@ const cellValFn = (row, colIdx, header) => {
23168
23304
  }
23169
23305
  return "";
23170
23306
  };
23171
- const fitLabelFn = (fit, t2) => fit === "good" ? t2("perfect fit") : fit === "too-tight" ? t2("too tight") : fit === "tight" ? t2("tight") : fit === "a-bit-tight" ? t2("a bit tight") : fit === "too-loose" ? t2("too loose") : fit === "loose" ? t2("loose") : t2("a bit loose");
23307
+ const fitLabelFn = (fit, t2) => fit === "good" ? t2("within range") : fit === "too-tight" ? t2("too tight") : fit === "tight" ? t2("tight") : fit === "a-bit-tight" ? t2("a bit tight") : fit === "too-loose" ? t2("too loose") : fit === "loose" ? t2("loose") : t2("a bit loose");
23172
23308
  const accessoryFitLabelFn = (fit, t2) => fit === "good" ? t2("within range") : fitLabelFn(fit, t2);
23173
23309
  const lengthFitLabelFn = (fit, t2) => {
23174
- if (fit === "good") return t2("perfect fit");
23310
+ if (fit === "good") return t2("within range");
23175
23311
  if (fit === "too-short" || fit === "too-tight") return t2("too short");
23176
23312
  if (fit === "short" || fit === "tight") return t2("short");
23177
23313
  if (fit === "a-bit-short" || fit === "a-bit-tight") return t2("a bit short");
@@ -24929,7 +25065,7 @@ function SizeResultView({
24929
25065
  ] }),
24930
25066
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-v2-panel ps-tryon-v2-result-panel", children: [
24931
25067
  profileCompletionCta ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-v2-profile-head ps-expanded", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProfileCompletionCta, { onClick: profileCompletionCta.onClick, placement: "header", t: t2 }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-v2-result-copy", children: [
24932
- /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "ps-tryon-v2-title", children: t2("Your Perfect Fit") }),
25068
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "ps-tryon-v2-title", children: t2("Your Size Recommendation") }),
24933
25069
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ps-tryon-v2-subtitle", children: t2("Tap any section for detailed breakdown") })
24934
25070
  ] }),
24935
25071
  mismatchNotice,
@@ -25186,7 +25322,7 @@ function SizeResultView({
25186
25322
  /* CARD VIEW — clickable summary card + gallery strip */
25187
25323
  /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
25188
25324
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-v2-result-head", children: profileCompletionCta ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-v2-profile-head ps-expanded", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProfileCompletionCta, { onClick: profileCompletionCta.onClick, placement: "header", t: t2 }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-v2-result-copy", children: [
25189
- /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "ps-tryon-v2-title", children: guideOnlyResult ? t2("Product Size Guide") : t2("Your Perfect Fit") }),
25325
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "ps-tryon-v2-title", children: guideOnlyResult ? t2("Product Size Guide") : t2("Your Size Recommendation") }),
25190
25326
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ps-tryon-v2-subtitle", children: guideOnlyResult ? t2("Tap the card to view product measurements") : t2("Tap the card for detailed breakdown") })
25191
25327
  ] }) }),
25192
25328
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-v2-sep" }),
@@ -26048,7 +26184,7 @@ function ProcessingView({
26048
26184
  }
26049
26185
  }, []);
26050
26186
  const aiFacts = [
26051
- t2("Our model is analyzing 150+ body landmarks for the perfect fit"),
26187
+ t2("Our model is analyzing 150+ body landmarks for your size recommendation"),
26052
26188
  t2("Calibrating fabric drape against your body proportions"),
26053
26189
  t2("Cross-checking fit against millions of garment patterns"),
26054
26190
  t2("Rendering shadows and highlights to match your photo's lighting")
@@ -26151,20 +26287,22 @@ function ProcessingView({
26151
26287
  function NoChartView({
26152
26288
  productImage,
26153
26289
  productTitle,
26290
+ reason = "no-chart",
26154
26291
  onTryOn,
26155
26292
  onClose,
26156
26293
  t: t2
26157
26294
  }) {
26158
26295
  const isMobile = useIsMobile();
26296
+ const isNoMatch = reason === "no-match";
26159
26297
  const RightColumn = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-no-chart-content", children: [
26160
26298
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-no-chart-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 24 24", width: "44", height: "44", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
26161
26299
  /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 17l6 6 12-12-6-6L3 17z" }),
26162
26300
  /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14 4l-3 3M16 6l-2 2M18 8l-2 2M11 7l-2 2M13 9l-2 2M15 11l-2 2" })
26163
26301
  ] }) }),
26164
- /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "ps-tryon-no-chart-title", children: t2("No size chart available") }),
26165
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ps-tryon-no-chart-msg", children: t2("The merchant hasn't uploaded sizing data for this product yet. You can still see how it looks on you with a virtual try-on.") }),
26302
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "ps-tryon-no-chart-title", children: isNoMatch ? t2("No matching size available") : t2("No size chart available") }),
26303
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ps-tryon-no-chart-msg", children: isNoMatch ? t2("This product's size chart doesn't include a reliable fit for your measurements.") : t2("The merchant hasn't uploaded sizing data for this product yet. You can still see how it looks on you with a virtual try-on.") }),
26166
26304
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-no-chart-actions", children: [
26167
- /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: "ps-tryon-no-chart-cta", onClick: onTryOn, children: [
26305
+ !isNoMatch && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: "ps-tryon-no-chart-cta", onClick: onTryOn, children: [
26168
26306
  t2("See how it looks on you"),
26169
26307
  " →"
26170
26308
  ] }),
@@ -26348,10 +26486,11 @@ function ProgressiveStep({
26348
26486
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-cpw-card-row", children })
26349
26487
  ] }, stepKey);
26350
26488
  }
26351
- function CreateProfileWizard({ onSave, onCancel, initialMode, initialDraft, apiUrl, apiKey, onPhotoPreview, onEstimate, t: t2 }) {
26489
+ function CreateProfileWizard({ onSave, onCancel, initialMode, initialDraft, directAnalysisFlow = false, apiUrl, apiKey, onPhotoPreview, onEstimate, t: t2 }) {
26352
26490
  const seededUnit = draftUnit(initialDraft);
26353
26491
  const seededHeight = draftHeightParts(initialDraft, seededUnit);
26354
26492
  const seededGender = initialDraft?.gender === "female" ? "female" : "male";
26493
+ const seededPhotoRef = reactExports.useRef(initialDraft?.photoBase64 || initialDraft?.photoUrl || null);
26355
26494
  const [mode, setMode] = reactExports.useState(initialMode ?? null);
26356
26495
  const [manualStep, setManualStep] = reactExports.useState("identity");
26357
26496
  const [imageStep, setImageStep] = reactExports.useState("name-photo");
@@ -26370,15 +26509,19 @@ function CreateProfileWizard({ onSave, onCancel, initialMode, initialDraft, apiU
26370
26509
  const [weightVal, setWeightVal] = reactExports.useState(draftDisplayNumber(initialDraft?.weight));
26371
26510
  const [ageVal, setAgeVal] = reactExports.useState(draftDisplayNumber(initialDraft?.age));
26372
26511
  const [error, setError] = reactExports.useState("");
26373
- const [photoBase64, setPhotoBase64] = reactExports.useState(null);
26512
+ const [photoBase64, setPhotoBase64] = reactExports.useState(() => seededPhotoRef.current);
26374
26513
  const [photoUploading, setPhotoUploading] = reactExports.useState(false);
26375
26514
  const [photoStatus, setPhotoStatus] = reactExports.useState("");
26376
26515
  const [photoRejection, setPhotoRejection] = reactExports.useState(null);
26516
+ reactExports.useEffect(() => {
26517
+ if (seededPhotoRef.current) onPhotoPreview?.(seededPhotoRef.current);
26518
+ }, []);
26377
26519
  const [scanLandmarks, setScanLandmarks] = reactExports.useState(null);
26378
26520
  const [scanImgDims, setScanImgDims] = reactExports.useState({ w: 800, h: 1200 });
26379
26521
  const [scanStageIdx, setScanStageIdx] = reactExports.useState(0);
26380
26522
  const [photoHelpOpen, setPhotoHelpOpen] = reactExports.useState(false);
26381
26523
  const [uploadHoverCpw, setUploadHoverCpw] = reactExports.useState(false);
26524
+ const directAutoStartedRef = reactExports.useRef(false);
26382
26525
  reactExports.useEffect(() => {
26383
26526
  if (imageStep !== "calculating" || !photoBase64) return;
26384
26527
  let cancelled = false;
@@ -26677,7 +26820,7 @@ function CreateProfileWizard({ onSave, onCancel, initialMode, initialDraft, apiU
26677
26820
  return;
26678
26821
  }
26679
26822
  const a = parseFloat(ageVal);
26680
- if (isWomen && (!bandSize || !cupSize)) {
26823
+ if (isWomen && !directAnalysisFlow && (!bandSize || !cupSize)) {
26681
26824
  setError(t2("Please select your bra band and cup size"));
26682
26825
  return;
26683
26826
  }
@@ -26727,6 +26870,15 @@ function CreateProfileWizard({ onSave, onCancel, initialMode, initialDraft, apiU
26727
26870
  }
26728
26871
  onSave(payload);
26729
26872
  };
26873
+ reactExports.useEffect(() => {
26874
+ if (!directAnalysisFlow || directAutoStartedRef.current) return;
26875
+ if (mode !== "image" || imageStep !== "name-photo") return;
26876
+ const hasHeight = unit === "in" ? parseFloat(heightFt) > 0 || parseFloat(heightInch) > 0 : parseFloat(heightVal) > 0;
26877
+ const hasWeight = parseFloat(weightVal) > 0;
26878
+ if (!photoBase64 || !name.trim() || !hasHeight || !hasWeight) return;
26879
+ directAutoStartedRef.current = true;
26880
+ void advanceImage();
26881
+ }, [directAnalysisFlow, mode, imageStep, photoBase64, name, heightFt, heightInch, heightVal, weightVal, unit]);
26730
26882
  const goBackImage = () => {
26731
26883
  setError("");
26732
26884
  if (imageStep === "details") {
@@ -26768,9 +26920,12 @@ function CreateProfileWizard({ onSave, onCancel, initialMode, initialDraft, apiU
26768
26920
  if (photoBase64) pct += 50;
26769
26921
  return pct;
26770
26922
  })();
26771
- const hideGlobalBack = mode === "image" && imageStep === "name-photo";
26772
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `ps-cpw-root${hideGlobalBack ? " ps-cpw-hide-global-back" : ""}`, children: [
26773
- !(mode === "image" && imageStep === "name-photo") && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-cpw-step-head", children: [
26923
+ const hideGlobalBack = mode === "image" && imageStep === "name-photo" && !directAnalysisFlow;
26924
+ const hideStepHeader = directAnalysisFlow && mode === "image";
26925
+ const hideFooter = directAnalysisFlow && mode === "image" && imageStep === "calculating" && estimating;
26926
+ const hideFooterBack = directAnalysisFlow && mode === "image";
26927
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `ps-cpw-root${hideGlobalBack ? " ps-cpw-hide-global-back" : ""}${directAnalysisFlow ? " ps-cpw-direct-flow" : ""}`, children: [
26928
+ !hideStepHeader && !(mode === "image" && imageStep === "name-photo") && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-cpw-step-head", children: [
26774
26929
  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "ps-cpw-step-title", children: headerLabel }),
26775
26930
  mode != null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-cpw-progress", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-cpw-progress-track", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-cpw-progress-fill", style: { width: `${progressPct}%` } }) }) })
26776
26931
  ] }),
@@ -27698,7 +27853,7 @@ function CreateProfileWizard({ onSave, onCancel, initialMode, initialDraft, apiU
27698
27853
  mode === "image" && imageStep === "calculating" && (() => {
27699
27854
  const stages = [
27700
27855
  { title: t2("DETECTING POSE"), desc: t2("Identifying body landmarks from your photo.") },
27701
- { title: t2("SCANNING FRAME"), desc: t2("Our AI is mapping your proportions to calculate the perfect fit.") },
27856
+ { title: t2("SCANNING FRAME"), desc: t2("Our AI is mapping your proportions for a size recommendation.") },
27702
27857
  { title: t2("ANALYZING BODY"), desc: t2("Measuring shoulders, chest, waist and hips.") },
27703
27858
  { title: t2("MATCHING SIZE"), desc: t2("Comparing your measurements to the size guide.") },
27704
27859
  { title: t2("FINALIZING RESULT"), desc: t2("Almost done — preparing your recommendation.") }
@@ -27786,13 +27941,13 @@ function CreateProfileWizard({ onSave, onCancel, initialMode, initialDraft, apiU
27786
27941
  ] }) }, "image-calculating");
27787
27942
  })()
27788
27943
  ] }),
27789
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-cpw-footer", children: [
27790
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-cpw-back-btn", onClick: handleBack, children: mode == null ? t2("Cancel") : `← ${t2("Back")}` }),
27944
+ !hideFooter && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `ps-cpw-footer${hideFooterBack ? " ps-cpw-footer-no-back" : ""}`, children: [
27945
+ !hideFooterBack && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-cpw-back-btn", onClick: handleBack, children: mode == null ? t2("Cancel") : `← ${t2("Back")}` }),
27791
27946
  mode === "manual" && !isAutoAdvanceStep && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-cpw-next-btn", onClick: advanceManual, children: isLastManualStep ? t2("CALCULATE MY FIT") : t2("Continue") }),
27792
27947
  mode === "image" && imageStep === "name-photo" && (() => {
27793
27948
  const heightOk = unit === "in" ? parseFloat(heightFt) > 0 || parseFloat(heightInch) > 0 : parseFloat(heightVal) > 0;
27794
27949
  const weightOk = parseFloat(weightVal) > 0;
27795
- const braOk = !isWomen || !!bandSize && !!cupSize;
27950
+ const braOk = !isWomen || directAnalysisFlow || !!bandSize && !!cupSize;
27796
27951
  const nameOk = !!name.trim();
27797
27952
  const photoOk = !!photoBase64;
27798
27953
  const analyzing = photoUploading;
@@ -28213,6 +28368,8 @@ function MySizingProfilesView({
28213
28368
  const goBack = () => {
28214
28369
  if (creating) {
28215
28370
  setCreating(false);
28371
+ onProfileDraftConsumed?.();
28372
+ onPhotoPreview?.(null);
28216
28373
  return;
28217
28374
  }
28218
28375
  if (viewingId !== null) {
@@ -28221,7 +28378,7 @@ function MySizingProfilesView({
28221
28378
  }
28222
28379
  };
28223
28380
  onRegisterBackInterceptor(canGoBack, goBack);
28224
- }, [creating, viewingId, onRegisterBackInterceptor]);
28381
+ }, [creating, viewingId, onRegisterBackInterceptor, onProfileDraftConsumed, onPhotoPreview]);
28225
28382
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msp-root", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msp-scroll", children: viewingProfile ? /* @__PURE__ */ jsxRuntimeExports.jsx(
28226
28383
  ProfileMeasurementsView,
28227
28384
  {
@@ -28246,6 +28403,7 @@ function MySizingProfilesView({
28246
28403
  apiKey,
28247
28404
  initialMode: initialCreateDraft ? "image" : void 0,
28248
28405
  initialDraft: initialCreateDraft,
28406
+ directAnalysisFlow: !!initialCreateDraft,
28249
28407
  onSave: (data) => {
28250
28408
  onSaveNewProfile(data);
28251
28409
  onProfileDraftConsumed?.();
@@ -28482,14 +28640,13 @@ function BasicsStepMobile({
28482
28640
  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "ps-bpm-title", children: t2("Body Measurements") }),
28483
28641
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ps-bpm-subtitle", children: t2("Enter your details for a bespoke size recommendation") })
28484
28642
  ] }),
28485
- activeProfileName && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "ps-bp-profile-hint", children: [
28486
- t2("Using"),
28487
- " ",
28488
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: activeProfileName }),
28489
- onStartFresh && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
28490
- " · ",
28491
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-bp-profile-hint-link", onClick: onStartFresh, children: t2("start fresh") })
28492
- ] })
28643
+ activeProfileName && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-profile-card", children: [
28644
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-profile-card-copy", children: [
28645
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-bp-profile-card-eyebrow", children: t2("Active Profile") }),
28646
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: activeProfileName }),
28647
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t2("Using saved measurements for this fit.") })
28648
+ ] }),
28649
+ onStartFresh && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-bp-profile-card-action", onClick: onStartFresh, children: t2("Start Fresh") })
28493
28650
  ] }),
28494
28651
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bpm-toggle", children: [
28495
28652
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -29549,6 +29706,9 @@ function BodyProfileView({
29549
29706
  formRef.current.weight = String(weightVal);
29550
29707
  formRef.current.gender = gender;
29551
29708
  if (age) formRef.current.age = age;
29709
+ if (chestProfile) formRef.current.chestProfile = chestProfile;
29710
+ if (midsectionProfile) formRef.current.midsectionProfile = midsectionProfile;
29711
+ if (hipProfile) formRef.current.hipProfile = hipProfile;
29552
29712
  if (bandSize) formRef.current.bandSize = bandSize;
29553
29713
  if (cupSize) formRef.current.cupSize = cupSize;
29554
29714
  if (braSizeRegion) formRef.current.braSizeRegion = braSizeRegion;
@@ -29605,6 +29765,9 @@ function BodyProfileView({
29605
29765
  weightUnit: isShoeReferenceMode ? "kg" : wUnit,
29606
29766
  gender,
29607
29767
  ...ageForSubmit ? { age: ageForSubmit } : {},
29768
+ ...chestProfile ? { chestProfile } : {},
29769
+ ...midsectionProfile ? { midsectionProfile } : {},
29770
+ ...hipProfile ? { hipProfile } : {},
29608
29771
  ...currentShoeReference ? {
29609
29772
  extraMeasurements: {
29610
29773
  referenceShoeBrandId: currentShoeReference.brandId,
@@ -30502,14 +30665,13 @@ function BodyProfileView({
30502
30665
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: `ps-bp-system-btn${isImperialMode ? " ps-bp-system-active" : ""}`, onClick: photoSwitchToImperial, type: "button", children: t2("Imperial") })
30503
30666
  ] }),
30504
30667
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginTop: "auto", marginBottom: "auto" }, children: [
30505
- activeProfileName && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "ps-bp-profile-hint", style: { textAlign: "center", margin: "0 0 0.5vw" }, children: [
30506
- t2("Using"),
30507
- " ",
30508
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: activeProfileName }),
30509
- onStartFresh && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
30510
- " · ",
30511
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-bp-profile-hint-link", onClick: handleClearFromProfile, children: t2("start fresh") })
30512
- ] })
30668
+ activeProfileName && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-profile-card", children: [
30669
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-profile-card-copy", children: [
30670
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-bp-profile-card-eyebrow", children: t2("Active Profile") }),
30671
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: activeProfileName }),
30672
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t2("Using saved measurements for this fit.") })
30673
+ ] }),
30674
+ onStartFresh && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-bp-profile-card-action", onClick: handleClearFromProfile, children: t2("Start Fresh") })
30513
30675
  ] }),
30514
30676
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-inline-fields", children: [
30515
30677
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-inline-row", children: [
@@ -30811,14 +30973,13 @@ function BodyProfileView({
30811
30973
  }
30812
30974
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-step ps-bp-step-enter", children: [
30813
30975
  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "ps-bp-title", children: basicOnly || simplePhotoOnly ? t2("Your Details") : t2("Body Measurements") }),
30814
- activeProfileName && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "ps-bp-profile-hint", style: { textAlign: "center", margin: "0 0 0.5vw" }, children: [
30815
- t2("Using"),
30816
- " ",
30817
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: activeProfileName }),
30818
- onStartFresh && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
30819
- " · ",
30820
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-bp-profile-hint-link", onClick: handleClearFromProfile, children: t2("start fresh") })
30821
- ] })
30976
+ activeProfileName && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-profile-card", children: [
30977
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-profile-card-copy", children: [
30978
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-bp-profile-card-eyebrow", children: t2("Active Profile") }),
30979
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: activeProfileName }),
30980
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t2("Using saved measurements for this fit.") })
30981
+ ] }),
30982
+ onStartFresh && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-bp-profile-card-action", onClick: handleClearFromProfile, children: t2("Start Fresh") })
30822
30983
  ] }),
30823
30984
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-system-toggle", children: [
30824
30985
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: `ps-bp-system-btn${!isImperialMode ? " ps-bp-system-active" : ""}`, onClick: switchToMetric, type: "button", children: t2("Metric") }),
@@ -32594,6 +32755,7 @@ function PrimeStyleTryonInner({
32594
32755
  setCssReady(true);
32595
32756
  }, []);
32596
32757
  const [view, setView] = reactExports.useState(initialView ?? "idle");
32758
+ const [noSizeReason, setNoSizeReason] = reactExports.useState("no-chart");
32597
32759
  const [selectedFile, setSelectedFile] = reactExports.useState(null);
32598
32760
  const [previewUrl, setPreviewUrl] = reactExports.useState(null);
32599
32761
  const [resultImageUrl, setResultImageUrl] = reactExports.useState(null);
@@ -32673,7 +32835,9 @@ function PrimeStyleTryonInner({
32673
32835
  const [estimationLoading, setEstimationLoading] = reactExports.useState(false);
32674
32836
  const [profiles, setProfiles] = reactExports.useState(() => lsGet("profiles", []));
32675
32837
  const [profileSession, setProfileSession] = reactExports.useState(() => getStoredProfileSession());
32676
- const [profileCompletionDraft, setProfileCompletionDraft] = reactExports.useState(() => lsGet(PROFILE_COMPLETION_DRAFT_KEY, null));
32838
+ const profileSessionRef = reactExports.useRef(profileSession);
32839
+ const suppressNextProfilePersistRef = reactExports.useRef(false);
32840
+ const [profileCompletionDraft, setProfileCompletionDraft] = reactExports.useState(() => getStoredProfileSession() ? null : lsGet(PROFILE_COMPLETION_DRAFT_KEY, null));
32677
32841
  const [profileAuthError, setProfileAuthError] = reactExports.useState(null);
32678
32842
  const [profileAuthLoadingProvider, setProfileAuthLoadingProvider] = reactExports.useState(null);
32679
32843
  const [history, setHistory] = reactExports.useState(() => lsGet("history", []).map(normalizeHistoryEntry));
@@ -32692,8 +32856,11 @@ function PrimeStyleTryonInner({
32692
32856
  const [deleteConfirmId, setDeleteConfirmId] = reactExports.useState(null);
32693
32857
  const setActiveProfileId$1 = reactExports.useCallback((id2) => {
32694
32858
  setActiveProfileIdState(id2);
32695
- setActiveProfileId(id2);
32859
+ if (!profileSessionRef.current) setActiveProfileId(id2);
32696
32860
  }, []);
32861
+ reactExports.useEffect(() => {
32862
+ profileSessionRef.current = profileSession;
32863
+ }, [profileSession]);
32697
32864
  const [profileSaved, setProfileSaved] = reactExports.useState(false);
32698
32865
  const [drawer, setDrawer] = reactExports.useState(null);
32699
32866
  const [profileDetail, setProfileDetail] = reactExports.useState(null);
@@ -32709,13 +32876,15 @@ function PrimeStyleTryonInner({
32709
32876
  profilesGoBackRef.current = goBack;
32710
32877
  }, []);
32711
32878
  const clearProfileCompletionDraft = reactExports.useCallback(() => {
32712
- lsSet(PROFILE_COMPLETION_DRAFT_KEY, null);
32879
+ lsRemove(PROFILE_COMPLETION_DRAFT_KEY);
32713
32880
  setProfileCompletionDraft(null);
32714
32881
  }, []);
32715
32882
  const handleCompleteProfileFromResult = reactExports.useCallback(() => {
32716
32883
  const f2 = formRef.current || {};
32717
32884
  const active = profiles.find((p2) => p2.id === activeProfileId) || null;
32718
32885
  const gender = f2.gender === "female" || active?.gender === "female" ? "female" : "male";
32886
+ const profilePhoto = f2.photoBase64 || f2.bodyImage || active?.photoBase64 || active?.photoUrl;
32887
+ const normalizedProfilePhoto = profilePhoto ? normalizeProfilePhotoSource(profilePhoto) : null;
32719
32888
  const customMeasurements = {};
32720
32889
  const wristCircumference = positiveNumber(f2.wristCircumference);
32721
32890
  if (wristCircumference != null) customMeasurements.wristCircumference = wristCircumference;
@@ -32737,33 +32906,36 @@ function PrimeStyleTryonInner({
32737
32906
  bandSize: f2.bandSize || active?.bandSize,
32738
32907
  cupSize: f2.cupSize || active?.cupSize,
32739
32908
  braSizeRegion: f2.braSizeRegion || active?.braSizeRegion || "US",
32909
+ ...normalizedProfilePhoto ? { photoBase64: normalizedProfilePhoto } : {},
32740
32910
  ...Object.keys(customMeasurements).length > 0 ? { customMeasurements } : {}
32741
32911
  };
32742
- lsSet(PROFILE_COMPLETION_DRAFT_KEY, draft);
32912
+ if (profileSessionRef.current) lsRemove(PROFILE_COMPLETION_DRAFT_KEY);
32913
+ else lsSet(PROFILE_COMPLETION_DRAFT_KEY, draft);
32743
32914
  setProfileCompletionDraft(draft);
32744
32915
  setProfileAuthError(null);
32745
32916
  setView("profiles");
32746
32917
  }, [activeProfileId, effectiveProductId, heightUnit, productTitle, profiles, sizingUnit, weightUnit]);
32747
- const applyProfileStore = reactExports.useCallback((nextProfiles, nextActiveProfileId) => {
32748
- saveProfiles(nextProfiles);
32918
+ const applyProfileStore = reactExports.useCallback((nextProfiles, nextActiveProfileId, persistLocal = !profileSessionRef.current) => {
32919
+ if (persistLocal) saveProfiles(nextProfiles);
32749
32920
  setProfiles(nextProfiles);
32750
- setActiveProfileId$1(nextActiveProfileId);
32921
+ setActiveProfileIdState(nextActiveProfileId);
32922
+ if (persistLocal) setActiveProfileId(nextActiveProfileId);
32751
32923
  profileSyncSnapshotRef.current = JSON.stringify({ profiles: nextProfiles, activeProfileId: nextActiveProfileId });
32752
- }, [setActiveProfileId$1]);
32924
+ }, []);
32753
32925
  const hydrateProfileStore = reactExports.useCallback(async (session, seedProfiles, seedActiveProfileId) => {
32754
32926
  profileSyncHydratingRef.current = true;
32755
32927
  try {
32756
32928
  const remoteStore = await fetchRemoteProfiles(apiUrl, session.accessToken);
32757
32929
  if (remoteStore.profiles.length > 0) {
32758
- applyProfileStore(remoteStore.profiles, remoteStore.activeProfileId);
32930
+ applyProfileStore(remoteStore.profiles, remoteStore.activeProfileId, false);
32759
32931
  return;
32760
32932
  }
32761
32933
  if (seedProfiles.length > 0) {
32762
32934
  const savedStore = await saveRemoteProfiles(apiUrl, session.accessToken, seedProfiles, seedActiveProfileId);
32763
- applyProfileStore(savedStore.profiles, savedStore.activeProfileId);
32935
+ applyProfileStore(savedStore.profiles, savedStore.activeProfileId, false);
32764
32936
  return;
32765
32937
  }
32766
- applyProfileStore([], null);
32938
+ applyProfileStore([], null, false);
32767
32939
  } finally {
32768
32940
  profileSyncHydratingRef.current = false;
32769
32941
  }
@@ -32774,6 +32946,7 @@ function PrimeStyleTryonInner({
32774
32946
  try {
32775
32947
  const session = await startSocialProfileLogin(provider, apiUrl);
32776
32948
  setStoredProfileSession(session);
32949
+ profileSessionRef.current = session;
32777
32950
  setProfileSession(session);
32778
32951
  await hydrateProfileStore(session, profiles, activeProfileId);
32779
32952
  } catch (error) {
@@ -32784,10 +32957,28 @@ function PrimeStyleTryonInner({
32784
32957
  }, [activeProfileId, apiUrl, hydrateProfileStore, profiles, t2]);
32785
32958
  const handleProfileLogout = reactExports.useCallback(() => {
32786
32959
  clearStoredProfileSession();
32960
+ clearProfileLocalStorage();
32961
+ suppressNextProfilePersistRef.current = true;
32962
+ profileSessionRef.current = null;
32787
32963
  setProfileSession(null);
32964
+ setProfiles([]);
32965
+ setActiveProfileIdState(null);
32966
+ setProfileCompletionDraft(null);
32967
+ setProfileSaved(false);
32968
+ setProfileDetail(null);
32969
+ formRef.current = {};
32970
+ setFormKey((k2) => k2 + 1);
32971
+ if (previewUrl) URL.revokeObjectURL(previewUrl);
32972
+ setSelectedFile(null);
32973
+ selectedFileRef.current = null;
32974
+ setPreviewUrl(null);
32975
+ setBodyLandmarks(null);
32976
+ setFaceLandmarks(null);
32977
+ setEstimatedValues(null);
32978
+ setEstimatingProfileIds(/* @__PURE__ */ new Set());
32788
32979
  setProfileAuthError(null);
32789
32980
  setProfileAuthLoadingProvider(null);
32790
- }, []);
32981
+ }, [previewUrl]);
32791
32982
  const fileInputRef = reactExports.useRef(null);
32792
32983
  const apiRef = reactExports.useRef(null);
32793
32984
  const sseRef = reactExports.useRef(null);
@@ -33070,6 +33261,11 @@ function PrimeStyleTryonInner({
33070
33261
  };
33071
33262
  }, [history]);
33072
33263
  reactExports.useEffect(() => {
33264
+ if (suppressNextProfilePersistRef.current) {
33265
+ suppressNextProfilePersistRef.current = false;
33266
+ return;
33267
+ }
33268
+ if (profileSessionRef.current) return;
33073
33269
  lsSet("profiles", profiles);
33074
33270
  }, [profiles]);
33075
33271
  reactExports.useEffect(() => {
@@ -33098,7 +33294,10 @@ function PrimeStyleTryonInner({
33098
33294
  };
33099
33295
  }, [activeProfileId, apiUrl, applyProfileStore, profileSession, profiles, t2]);
33100
33296
  reactExports.useEffect(() => {
33101
- const handler = () => setProfiles(lsGet("profiles", []));
33297
+ const handler = () => {
33298
+ if (profileSessionRef.current) return;
33299
+ setProfiles(lsGet("profiles", []));
33300
+ };
33102
33301
  window.addEventListener(PS_STORAGE_CHANGE_EVENT, handler);
33103
33302
  return () => window.removeEventListener(PS_STORAGE_CHANGE_EVENT, handler);
33104
33303
  }, []);
@@ -33146,6 +33345,14 @@ function PrimeStyleTryonInner({
33146
33345
  return 1;
33147
33346
  }
33148
33347
  }, [view]);
33348
+ const updateProfilesForCurrentSession = reactExports.useCallback((updater) => {
33349
+ setProfiles((prev) => {
33350
+ const source = profileSessionRef.current ? prev : lsGet("profiles", prev);
33351
+ const next = updater(source);
33352
+ if (!profileSessionRef.current) saveProfiles(next);
33353
+ return next;
33354
+ });
33355
+ }, []);
33149
33356
  const persistResultToProfile = reactExports.useCallback(
33150
33357
  (formData, recommendation, options) => {
33151
33358
  let targetId = activeProfileId;
@@ -33155,27 +33362,63 @@ function PrimeStyleTryonInner({
33155
33362
  return;
33156
33363
  }
33157
33364
  if (targetId && formData.gender === "female" && (formData.bandSize || formData.cupSize || formData.braSizeRegion)) {
33158
- updateProfile(targetId, {
33365
+ const patch = {
33159
33366
  ...formData.bandSize ? { bandSize: formData.bandSize } : {},
33160
33367
  ...formData.cupSize ? { cupSize: formData.cupSize } : {},
33161
33368
  ...formData.braSizeRegion ? { braSizeRegion: formData.braSizeRegion } : {}
33162
- });
33163
- setProfiles(lsGet("profiles", []));
33369
+ };
33370
+ if (profileSessionRef.current) {
33371
+ updateProfilesForCurrentSession((list) => list.map((p2) => p2.id === targetId ? { ...p2, ...patch, lastEditedAt: Date.now() } : p2));
33372
+ } else {
33373
+ updateProfile(targetId, patch);
33374
+ setProfiles(lsGet("profiles", []));
33375
+ }
33164
33376
  }
33377
+ const persistMeasurements = (est, unit) => {
33378
+ if (!targetId) return;
33379
+ if (profileSessionRef.current) {
33380
+ updateProfilesForCurrentSession((list) => list.map((p2) => p2.id === targetId ? {
33381
+ ...p2,
33382
+ measurements: est,
33383
+ measurementsUnit: unit,
33384
+ lastEditedAt: Date.now()
33385
+ } : p2));
33386
+ } else {
33387
+ updateProfileMeasurements(targetId, est, unit);
33388
+ setProfiles(lsGet("profiles", []));
33389
+ }
33390
+ };
33391
+ const persistSizeHistory = (entry) => {
33392
+ if (!targetId) return;
33393
+ if (profileSessionRef.current) {
33394
+ updateProfilesForCurrentSession((list) => list.map((p2) => {
33395
+ if (p2.id !== targetId) return p2;
33396
+ const sizeHistory = (p2.sizeHistory || []).filter((h) => h.productId !== entry.productId);
33397
+ sizeHistory.unshift(entry);
33398
+ return {
33399
+ ...p2,
33400
+ sizeHistory: sizeHistory.slice(0, 50),
33401
+ lastUsedAt: Date.now()
33402
+ };
33403
+ }));
33404
+ } else {
33405
+ addSizeToHistory(targetId, entry);
33406
+ setProfiles(lsGet("profiles", []));
33407
+ }
33408
+ };
33165
33409
  if (options?.skipBodyEstimate) {
33166
33410
  console.log("[ps-sdk:persist] skipping body estimates — face/head flow (no body context)");
33167
33411
  } else if (targetId && recommendation?.estimates) {
33168
33412
  const est = recommendation.estimates;
33169
33413
  const unit = recommendation.estimatesUnit || "cm";
33170
- updateProfileMeasurements(targetId, est, unit);
33171
- setProfiles(lsGet("profiles", []));
33414
+ persistMeasurements(est, unit);
33172
33415
  }
33173
33416
  setEstimationDone(true);
33174
33417
  if (recommendation?.recommendedSize && targetId) {
33175
33418
  const sectionsMap = recommendation.sections ? Object.fromEntries(
33176
33419
  Object.entries(recommendation.sections).map(([name, sec]) => [name, sec.recommendedSize])
33177
33420
  ) : void 0;
33178
- addSizeToHistory(targetId, {
33421
+ persistSizeHistory({
33179
33422
  productId: effectiveProductId,
33180
33423
  productTitle,
33181
33424
  productImage,
@@ -33184,10 +33427,9 @@ function PrimeStyleTryonInner({
33184
33427
  sections: sectionsMap,
33185
33428
  savedAt: Date.now()
33186
33429
  });
33187
- setProfiles(lsGet("profiles", []));
33188
33430
  }
33189
33431
  },
33190
- [activeProfileId, profiles, productImage, productTitle, effectiveProductId]
33432
+ [activeProfileId, profiles, productImage, productTitle, effectiveProductId, updateProfilesForCurrentSession]
33191
33433
  );
33192
33434
  const snapSubmitRef = reactExports.useRef(null);
33193
33435
  const [confirmProfile, setConfirmProfile] = reactExports.useState(null);
@@ -33196,7 +33438,7 @@ function PrimeStyleTryonInner({
33196
33438
  const profileWeight = p2.weight ?? p2.weightKg ?? 0;
33197
33439
  const hasStored = !!p2.measurements && Object.keys(p2.measurements).length > 0;
33198
33440
  const storedPhoto = p2.photoUrl || p2.photoBase64;
33199
- if (!hasStored && storedPhoto && profileHeight > 0 && snapSubmitRef.current) {
33441
+ if (storedPhoto && profileHeight > 0 && snapSubmitRef.current) {
33200
33442
  try {
33201
33443
  const dataUrl = await profilePhotoToDataUrl(storedPhoto);
33202
33444
  const blob = await fetch(dataUrl).then((r2) => r2.blob());
@@ -33210,6 +33452,7 @@ function PrimeStyleTryonInner({
33210
33452
  weightUnit: p2.weightUnit || "kg",
33211
33453
  gender: p2.gender,
33212
33454
  age: p2.age,
33455
+ ...hasStored && p2.measurements ? { knownMeasurements: p2.measurements } : {},
33213
33456
  ...snapBraFields({
33214
33457
  gender: p2.gender,
33215
33458
  bandSize: p2.bandSize,
@@ -33253,6 +33496,7 @@ function PrimeStyleTryonInner({
33253
33496
  }
33254
33497
  }
33255
33498
  setView("size-result");
33499
+ const minVisible = new Promise((resolve) => setTimeout(resolve, 6e3));
33256
33500
  recommendForProduct({
33257
33501
  productId: effectiveProductId,
33258
33502
  productTitle,
@@ -33271,7 +33515,9 @@ function PrimeStyleTryonInner({
33271
33515
  if (res?.raw) setSizingResult(res.raw);
33272
33516
  setEstimationDone(true);
33273
33517
  }).catch(() => {
33274
- }).finally(() => setSizingLoading(false));
33518
+ }).finally(() => {
33519
+ void minVisible.then(() => setSizingLoading(false));
33520
+ });
33275
33521
  }, [effectiveProductId, productTitle, productImage, productCategory, productSubcategory, resolvedProductFitType, productType, productTagsList, productDescription, sizeGuideData, apiUrl, previewUrl]);
33276
33522
  const handleUseActiveProfile = reactExports.useCallback(async () => {
33277
33523
  const p2 = profiles.find((x2) => x2.id === activeProfileId);
@@ -33493,6 +33739,8 @@ function PrimeStyleTryonInner({
33493
33739
  }, [sizeGuide, formGender]);
33494
33740
  const submitSizing = reactExports.useCallback(async (methodOverride) => {
33495
33741
  if (!apiRef.current) return;
33742
+ setActiveSection(null);
33743
+ noFitFoundRef.current = false;
33496
33744
  const method = methodOverride || sizingMethod;
33497
33745
  const baseUrl = getApiUrl(apiUrl);
33498
33746
  const key = getApiKey();
@@ -33553,6 +33801,12 @@ function PrimeStyleTryonInner({
33553
33801
  if (resp.ok) {
33554
33802
  const data = await resp.json();
33555
33803
  await minVisible;
33804
+ if (data?.found === false) {
33805
+ setNoSizeReason(data?.reasoning === "NO_SIZE_CHART" ? "no-chart" : "no-match");
33806
+ setView("no-chart");
33807
+ setEstimationDone(true);
33808
+ return;
33809
+ }
33556
33810
  setSizingResult(data);
33557
33811
  onComplete?.(data);
33558
33812
  } else {
@@ -33614,6 +33868,10 @@ function PrimeStyleTryonInner({
33614
33868
  if (formRef.current.shoeUS) m2.shoeUS = formRef.current.shoeUS;
33615
33869
  if (formRef.current.shoeUK) m2.shoeUK = formRef.current.shoeUK;
33616
33870
  if (formRef.current.fitPreference) m2.fitPreference = formRef.current.fitPreference;
33871
+ if (formRef.current.bodyType) m2.bodyType = formRef.current.bodyType;
33872
+ if (formRef.current.chestProfile) m2.chestProfile = formRef.current.chestProfile;
33873
+ if (formRef.current.midsectionProfile) m2.midsectionProfile = formRef.current.midsectionProfile;
33874
+ if (formRef.current.hipProfile) m2.hipProfile = formRef.current.hipProfile;
33617
33875
  payload.measurements = m2;
33618
33876
  console.log("[PS-SDK] FINAL measurements:", JSON.stringify(m2));
33619
33877
  } else {
@@ -33652,7 +33910,8 @@ function PrimeStyleTryonInner({
33652
33910
  if (res.ok) {
33653
33911
  const data = await res.json();
33654
33912
  console.log("[PS-SDK] Sizing recommend RESULT:", JSON.stringify(data));
33655
- if (data?.found === false && data?.reasoning === "NO_SIZE_CHART") {
33913
+ if (data?.found === false) {
33914
+ setNoSizeReason(data?.reasoning === "NO_SIZE_CHART" ? "no-chart" : "no-match");
33656
33915
  setView("no-chart");
33657
33916
  setEstimationDone(true);
33658
33917
  return;
@@ -33741,6 +34000,7 @@ function PrimeStyleTryonInner({
33741
34000
  setFaceLandmarks(null);
33742
34001
  setSizingMethod("quick");
33743
34002
  setSizingLoading(true);
34003
+ setActiveSection(null);
33744
34004
  setView("size-result");
33745
34005
  submitSizing("quick");
33746
34006
  return;
@@ -33759,6 +34019,7 @@ function PrimeStyleTryonInner({
33759
34019
  setResultImageUrl(null);
33760
34020
  currentHistoryEntryIdRef.current = null;
33761
34021
  currentTryOnJobIdRef.current = null;
34022
+ setActiveSection(null);
33762
34023
  setRestoredProductImage(null);
33763
34024
  setRestoredProductImages(null);
33764
34025
  setRestoredProductCarouselItems(null);
@@ -33810,6 +34071,8 @@ function PrimeStyleTryonInner({
33810
34071
  formRef.current.weightUnit = data.weightUnit;
33811
34072
  formRef.current.gender = data.gender;
33812
34073
  if (data.age != null) formRef.current.age = String(data.age);
34074
+ formRef.current.bodyImage = data.photoBase64;
34075
+ formRef.current.photoBase64 = data.photoBase64;
33813
34076
  const braFields = snapBraFields(data);
33814
34077
  if (braFields.bandSize) formRef.current.bandSize = braFields.bandSize;
33815
34078
  if (braFields.cupSize) formRef.current.cupSize = braFields.cupSize;
@@ -33834,6 +34097,7 @@ function PrimeStyleTryonInner({
33834
34097
  historyTryonSavedRef.current = false;
33835
34098
  currentHistoryEntryIdRef.current = null;
33836
34099
  currentTryOnJobIdRef.current = null;
34100
+ setActiveSection(null);
33837
34101
  setSizingLoading(true);
33838
34102
  setEstimationDone(false);
33839
34103
  setView("size-result");
@@ -33880,6 +34144,14 @@ function PrimeStyleTryonInner({
33880
34144
  });
33881
34145
  if (recRes.ok) {
33882
34146
  const recData = await recRes.json();
34147
+ if (recData?.found === false) {
34148
+ await minVisible2;
34149
+ setNoSizeReason(recData?.reasoning === "NO_SIZE_CHART" ? "no-chart" : "no-match");
34150
+ setView("no-chart");
34151
+ setEstimationDone(true);
34152
+ setSizingLoading(false);
34153
+ return;
34154
+ }
33883
34155
  setSizingResult(recData);
33884
34156
  onComplete?.(recData);
33885
34157
  persistResultToProfile(
@@ -33999,6 +34271,7 @@ function PrimeStyleTryonInner({
33999
34271
  if (recRes.ok) {
34000
34272
  const recData = await recRes.json();
34001
34273
  if (recData?.found === false && recData?.reasoning === "NO_SIZE_CHART") {
34274
+ setNoSizeReason("no-chart");
34002
34275
  setView("no-chart");
34003
34276
  setEstimationDone(true);
34004
34277
  setSizingLoading(false);
@@ -34012,8 +34285,13 @@ function PrimeStyleTryonInner({
34012
34285
  noFitFoundRef.current = true;
34013
34286
  setTryOnProcessing(false);
34014
34287
  setTryOnStartedAt(null);
34288
+ setNoSizeReason("no-match");
34015
34289
  setSizingResult({ ...recData, found: false });
34016
34290
  onComplete?.({ ...recData, found: false });
34291
+ setView("no-chart");
34292
+ setEstimationDone(true);
34293
+ setSizingLoading(false);
34294
+ return;
34017
34295
  } else {
34018
34296
  setSizingResult(recData);
34019
34297
  onComplete?.(recData);
@@ -34174,6 +34452,8 @@ function PrimeStyleTryonInner({
34174
34452
  {
34175
34453
  productId: effectiveProductId,
34176
34454
  productTitle,
34455
+ productCategory,
34456
+ productSubcategory,
34177
34457
  productFitType: resolvedProductFitType,
34178
34458
  productType,
34179
34459
  productTags: productTagsList,
@@ -34242,7 +34522,7 @@ function PrimeStyleTryonInner({
34242
34522
  setView("error");
34243
34523
  onError?.({ message, code });
34244
34524
  }
34245
- }, [selectedFile, productImage, effectiveProductImages, garmentReferenceImage, productTitle, resolvedProductFitType, productType, productTagsList, productDescription, productMaterial, measurementType, sizingResult, sizeGuide, apiUrl, onProcessing, onError, handleVtoUpdate]);
34525
+ }, [selectedFile, productImage, effectiveProductImages, garmentReferenceImage, productTitle, productCategory, productSubcategory, resolvedProductFitType, productType, productTagsList, productDescription, productMaterial, measurementType, sizingResult, sizeGuide, apiUrl, onProcessing, onError, handleVtoUpdate]);
34246
34526
  reactExports.useEffect(() => {
34247
34527
  if (view !== "size-result") {
34248
34528
  autoTryOnFiredRef.current = false;
@@ -34989,12 +35269,31 @@ function PrimeStyleTryonInner({
34989
35269
  setView("body-profile");
34990
35270
  },
34991
35271
  onSaveProfileMeasurements: (id2, measurements, unit) => {
34992
- updateProfileMeasurements(id2, measurements, unit ?? profiles.find((x2) => x2.id === id2)?.measurementsUnit ?? "cm");
34993
- setProfiles(lsGet("profiles", []));
35272
+ const nextUnit = unit ?? profiles.find((x2) => x2.id === id2)?.measurementsUnit ?? "cm";
35273
+ if (profileSessionRef.current) {
35274
+ updateProfilesForCurrentSession((list) => list.map((p2) => p2.id === id2 ? {
35275
+ ...p2,
35276
+ measurements,
35277
+ measurementsUnit: nextUnit,
35278
+ lastEditedAt: Date.now()
35279
+ } : p2));
35280
+ } else {
35281
+ updateProfileMeasurements(id2, measurements, nextUnit);
35282
+ setProfiles(lsGet("profiles", []));
35283
+ }
34994
35284
  },
34995
35285
  onSaveBraSize: (id2, bandSize, cupSize) => {
34996
- updateProfile(id2, { bandSize, cupSize });
34997
- setProfiles(lsGet("profiles", []));
35286
+ if (profileSessionRef.current) {
35287
+ updateProfilesForCurrentSession((list) => list.map((p2) => p2.id === id2 ? {
35288
+ ...p2,
35289
+ bandSize,
35290
+ cupSize,
35291
+ lastEditedAt: Date.now()
35292
+ } : p2));
35293
+ } else {
35294
+ updateProfile(id2, { bandSize, cupSize });
35295
+ setProfiles(lsGet("profiles", []));
35296
+ }
34998
35297
  },
34999
35298
  onEditProfile: (p2) => {
35000
35299
  setProfileDetail(p2);
@@ -35067,13 +35366,24 @@ function PrimeStyleTryonInner({
35067
35366
  bodyLandmarks: landmarks ?? void 0
35068
35367
  })).then((est) => {
35069
35368
  if (est) {
35070
- updateProfileMeasurements(newProfile.id, est.estimates, est.unit);
35369
+ if (profileSessionRef.current) {
35370
+ updateProfilesForCurrentSession((list) => list.map((p2) => p2.id === newProfile.id ? {
35371
+ ...p2,
35372
+ measurements: est.estimates,
35373
+ measurementsUnit: est.unit,
35374
+ lastEditedAt: Date.now()
35375
+ } : p2));
35376
+ } else {
35377
+ updateProfileMeasurements(newProfile.id, est.estimates, est.unit);
35378
+ }
35071
35379
  if (est.userEstimates) {
35072
- const all = lsGet("profiles", []);
35073
- const idx = all.findIndex((p2) => p2.id === newProfile.id);
35074
- if (idx >= 0) {
35380
+ const userEstimates = est.userEstimates;
35381
+ updateProfilesForCurrentSession((list) => {
35382
+ const idx = list.findIndex((p2) => p2.id === newProfile.id);
35383
+ if (idx < 0) return list;
35384
+ const all = [...list];
35075
35385
  const target = all[idx];
35076
- const u2 = est.userEstimates;
35386
+ const u2 = userEstimates;
35077
35387
  const patched = { ...target };
35078
35388
  if (u2.height && !(target.height || target.heightCm)) {
35079
35389
  patched.height = u2.height;
@@ -35089,10 +35399,10 @@ function PrimeStyleTryonInner({
35089
35399
  patched.age = u2.age;
35090
35400
  }
35091
35401
  all[idx] = patched;
35092
- lsSet("profiles", all);
35093
- }
35402
+ return all;
35403
+ });
35094
35404
  }
35095
- setProfiles(lsGet("profiles", []));
35405
+ if (!profileSessionRef.current) setProfiles(lsGet("profiles", []));
35096
35406
  }
35097
35407
  }).catch(() => {
35098
35408
  }).finally(() => {
@@ -35105,9 +35415,8 @@ function PrimeStyleTryonInner({
35105
35415
  }
35106
35416
  },
35107
35417
  onDeleteProfile: (id2) => {
35108
- setProfiles((prev) => prev.filter((p2) => p2.id !== id2));
35418
+ updateProfilesForCurrentSession((prev) => prev.filter((p2) => p2.id !== id2));
35109
35419
  if (activeProfileId === id2) setActiveProfileId$1(null);
35110
- lsSet("profiles", lsGet("profiles", []).filter((p2) => p2.id !== id2));
35111
35420
  },
35112
35421
  onRequestDelete: (id2) => setDeleteConfirmId(id2),
35113
35422
  onLogout: handleProfileLogout,
@@ -35141,6 +35450,7 @@ function PrimeStyleTryonInner({
35141
35450
  {
35142
35451
  productImage,
35143
35452
  productTitle,
35453
+ reason: noSizeReason,
35144
35454
  onTryOn: () => setView("photo-guide"),
35145
35455
  onClose: onClose ?? (() => {
35146
35456
  }),
@@ -35367,9 +35677,7 @@ function PrimeStyleTryonInner({
35367
35677
  {
35368
35678
  onConfirm: () => {
35369
35679
  const id2 = deleteConfirmId;
35370
- const updated = lsGet("profiles", []).filter((p2) => p2.id !== id2);
35371
- lsSet("profiles", updated);
35372
- setProfiles(updated);
35680
+ updateProfilesForCurrentSession((prev) => prev.filter((p2) => p2.id !== id2));
35373
35681
  if (activeProfileId === id2) setActiveProfileId$1(null);
35374
35682
  setDeleteConfirmId(null);
35375
35683
  },