@neurodyn/react-ai-try-on 0.0.4 → 0.1.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.
- package/dist/index.d.ts +280 -10
- package/dist/index.js +737 -160
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -935,6 +935,10 @@ var __webpack_modules__ = {
|
|
|
935
935
|
height: auto;
|
|
936
936
|
}
|
|
937
937
|
|
|
938
|
+
.h-dvh {
|
|
939
|
+
height: 100dvh;
|
|
940
|
+
}
|
|
941
|
+
|
|
938
942
|
.h-full {
|
|
939
943
|
height: 100%;
|
|
940
944
|
}
|
|
@@ -943,10 +947,6 @@ var __webpack_modules__ = {
|
|
|
943
947
|
height: 1px;
|
|
944
948
|
}
|
|
945
949
|
|
|
946
|
-
.h-svh {
|
|
947
|
-
height: 100svh;
|
|
948
|
-
}
|
|
949
|
-
|
|
950
950
|
.max-h-\\(--radix-select-content-available-height\\) {
|
|
951
951
|
max-height: var(--radix-select-content-available-height);
|
|
952
952
|
}
|
|
@@ -955,12 +955,12 @@ var __webpack_modules__ = {
|
|
|
955
955
|
max-height: calc(var(--spacing) * 179);
|
|
956
956
|
}
|
|
957
957
|
|
|
958
|
-
.max-h-
|
|
959
|
-
max-height:
|
|
958
|
+
.max-h-dvh {
|
|
959
|
+
max-height: 100dvh;
|
|
960
960
|
}
|
|
961
961
|
|
|
962
|
-
.max-h-
|
|
963
|
-
max-height:
|
|
962
|
+
.max-h-none {
|
|
963
|
+
max-height: none;
|
|
964
964
|
}
|
|
965
965
|
|
|
966
966
|
.min-h-0 {
|
|
@@ -2045,6 +2045,10 @@ var __webpack_modules__ = {
|
|
|
2045
2045
|
padding-bottom: 5px;
|
|
2046
2046
|
}
|
|
2047
2047
|
|
|
2048
|
+
.pl-2 {
|
|
2049
|
+
padding-left: calc(var(--spacing) * 2);
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2048
2052
|
.pl-3 {
|
|
2049
2053
|
padding-left: calc(var(--spacing) * 3);
|
|
2050
2054
|
}
|
|
@@ -2707,6 +2711,10 @@ var __webpack_modules__ = {
|
|
|
2707
2711
|
-webkit-mask-size: 18% 18%;
|
|
2708
2712
|
}
|
|
2709
2713
|
|
|
2714
|
+
.paused {
|
|
2715
|
+
animation-play-state: paused;
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2710
2718
|
.running {
|
|
2711
2719
|
animation-play-state: running;
|
|
2712
2720
|
}
|
|
@@ -4479,6 +4487,10 @@ var __webpack_modules__ = {
|
|
|
4479
4487
|
color: var(--v-text-dark);
|
|
4480
4488
|
}
|
|
4481
4489
|
|
|
4490
|
+
.data-\\[variant\\=destructive\\]\\:\\[\\&_svg\\:not\\(\\[class\\*\\=text-\\]\\)\\]\\:text-inherit[data-variant="destructive"] svg:not([class*="text-"]) {
|
|
4491
|
+
color: inherit;
|
|
4492
|
+
}
|
|
4493
|
+
|
|
4482
4494
|
.\\[\\&_tr\\]\\:border-b tr {
|
|
4483
4495
|
border-bottom-style: var(--tw-border-style);
|
|
4484
4496
|
border-bottom-width: 1px;
|
|
@@ -9462,6 +9474,15 @@ function ThemeProvider({ children, defaultTheme = 'system', storageKey = 'v-them
|
|
|
9462
9474
|
});
|
|
9463
9475
|
}
|
|
9464
9476
|
var fontsinline = __webpack_require__("../ui/src/styles/fonts.css?inline");
|
|
9477
|
+
const externalTryOnClient = {
|
|
9478
|
+
requestUploadUrls: async ()=>{
|
|
9479
|
+
throw new Error('External generation handles its own uploads');
|
|
9480
|
+
},
|
|
9481
|
+
generateTryOn: async ()=>{
|
|
9482
|
+
throw new Error('External generation handler is required');
|
|
9483
|
+
},
|
|
9484
|
+
sendWidgetEvent: ()=>{}
|
|
9485
|
+
};
|
|
9465
9486
|
const DEFAULT_API_TIMEOUT = 30000;
|
|
9466
9487
|
function createDefaultClient(defaultBaseURL) {
|
|
9467
9488
|
return axios.create({
|
|
@@ -9859,6 +9880,17 @@ async function tryOnControllerCreateUploadUrls(params, config = {}) {
|
|
|
9859
9880
|
});
|
|
9860
9881
|
return res.data;
|
|
9861
9882
|
}
|
|
9883
|
+
async function tryOnControllerGenerateShopifyTryOn(data, config = {}) {
|
|
9884
|
+
const { client: request = node_client_node_client, ...requestConfig } = config;
|
|
9885
|
+
const requestData = data;
|
|
9886
|
+
const res = await request({
|
|
9887
|
+
method: "POST",
|
|
9888
|
+
url: "/public/try-on/shopify/generate",
|
|
9889
|
+
data: requestData,
|
|
9890
|
+
...requestConfig
|
|
9891
|
+
});
|
|
9892
|
+
return res.data;
|
|
9893
|
+
}
|
|
9862
9894
|
async function tryOnControllerGenerateTryOn(data, config = {}) {
|
|
9863
9895
|
const { client: request = node_client_node_client, ...requestConfig } = config;
|
|
9864
9896
|
const requestData = data;
|
|
@@ -10146,6 +10178,16 @@ function createTryOnClient({ apiKey } = {}) {
|
|
|
10146
10178
|
}) : createInternalTryOnClient();
|
|
10147
10179
|
}
|
|
10148
10180
|
const ns1 = {
|
|
10181
|
+
activationLoading: 'Checking this product…',
|
|
10182
|
+
activationLoadingSubtitle: 'One moment while the store confirms try-on for this item.',
|
|
10183
|
+
activationTemporarilyUnavailable: 'Try-on is paused for this store right now. Please check back later.',
|
|
10184
|
+
activationUnavailableTitle: 'Try-on is unavailable',
|
|
10185
|
+
externalPhotoRequired: 'Please upload your photo again.',
|
|
10186
|
+
externalNoTokens: 'The store has no tokens remaining.',
|
|
10187
|
+
externalProductUnavailable: 'Try-on is unavailable for this product.',
|
|
10188
|
+
externalServiceUnavailable: 'Try-on is unavailable. Please try again later.',
|
|
10189
|
+
externalTimeout: 'The request timed out. Please try again.',
|
|
10190
|
+
externalPhotoHint: 'Upload a clear photo of yourself. The store chooses the item used for your try-on.',
|
|
10149
10191
|
back: 'Back',
|
|
10150
10192
|
badge: 'Try it on',
|
|
10151
10193
|
confirmBadge: 'Almost there',
|
|
@@ -10235,7 +10277,7 @@ const ns1 = {
|
|
|
10235
10277
|
startUpload: 'Choose your photo',
|
|
10236
10278
|
termsJoiner: '&',
|
|
10237
10279
|
termsLink: 'Terms',
|
|
10238
|
-
termsPrefix: 'By using this service,
|
|
10280
|
+
termsPrefix: 'By using this service, you agree to',
|
|
10239
10281
|
privacyLink: 'privacy Policy',
|
|
10240
10282
|
upperBodyIntroBadArmsCrossed: 'Arms crossed hide the fit',
|
|
10241
10283
|
upperBodyIntroBadObjectBlockingTorso: 'Object blocks the torso',
|
|
@@ -10257,6 +10299,16 @@ const ns1 = {
|
|
|
10257
10299
|
wristIntroTitle: 'Give us a hand'
|
|
10258
10300
|
};
|
|
10259
10301
|
const ru_ns1 = {
|
|
10302
|
+
activationLoading: 'Проверяем товар…',
|
|
10303
|
+
activationLoadingSubtitle: 'Магазин подтверждает примерку для этого товара.',
|
|
10304
|
+
activationTemporarilyUnavailable: 'Примерка в этом магазине сейчас приостановлена. Загляните позже.',
|
|
10305
|
+
activationUnavailableTitle: 'Примерка недоступна',
|
|
10306
|
+
externalPhotoRequired: 'Загрузите фото заново.',
|
|
10307
|
+
externalNoTokens: 'У магазина закончились токены.',
|
|
10308
|
+
externalProductUnavailable: 'Примерка для этого товара недоступна.',
|
|
10309
|
+
externalServiceUnavailable: 'Примерка недоступна. Попробуйте позже.',
|
|
10310
|
+
externalTimeout: 'Время ожидания истекло. Попробуйте снова.',
|
|
10311
|
+
externalPhotoHint: 'Загрузите чёткое фото себя. Изображение товара для примерки определяется настройками магазина.',
|
|
10260
10312
|
back: 'Назад',
|
|
10261
10313
|
badge: 'Примерить',
|
|
10262
10314
|
confirmBadge: 'Почти готово',
|
|
@@ -10313,7 +10365,7 @@ const ru_ns1 = {
|
|
|
10313
10365
|
headwearIntroTitle: 'Загрузите фото головы и плеч',
|
|
10314
10366
|
introCta: 'Продолжить',
|
|
10315
10367
|
introBadge: 'Совет',
|
|
10316
|
-
myFits: '
|
|
10368
|
+
myFits: 'Мои образы',
|
|
10317
10369
|
lowerBodyIntroBadCoveredWaist: 'Свободная одежда скрывает линию талии',
|
|
10318
10370
|
lowerBodyIntroBadSitting: 'В сидячей позе ноги согнуты',
|
|
10319
10371
|
lowerBodyIntroGoodFullBody: 'Фото в полный рост',
|
|
@@ -10338,8 +10390,8 @@ const ru_ns1 = {
|
|
|
10338
10390
|
resultReset: 'Сохранить',
|
|
10339
10391
|
resultShare: 'Поделиться',
|
|
10340
10392
|
resultTitle: 'Вот как это выглядит на вас',
|
|
10341
|
-
resultTryAnother: '
|
|
10342
|
-
resultTryAnotherWithPrice: '
|
|
10393
|
+
resultTryAnother: 'Добавить в корзину',
|
|
10394
|
+
resultTryAnotherWithPrice: 'Добавить в корзину – {{price}}',
|
|
10343
10395
|
share: 'Поделиться',
|
|
10344
10396
|
startHistory: 'Смотреть прошлые результаты',
|
|
10345
10397
|
startTitle: 'Хотите увидеть это на себе?',
|
|
@@ -10389,18 +10441,29 @@ function createAiTryOnI18n(language) {
|
|
|
10389
10441
|
return instance;
|
|
10390
10442
|
}
|
|
10391
10443
|
const PROPERTY_BLOCK_RE = /@property\s+(--[^{\s]+)\s*\{([^}]*)\}/g;
|
|
10392
|
-
const INITIAL_VALUE_RE = /initial-value:([
|
|
10444
|
+
const INITIAL_VALUE_RE = /initial-value:([^;}]+)/i;
|
|
10445
|
+
const NON_INHERITING_RE = /inherits:\s*false(?:\s*;|\s*$)/i;
|
|
10393
10446
|
const ROOT_TO_HOST_SELECTOR_RE = /:root\s*,\s*:host|:root/g;
|
|
10447
|
+
const SHADOW_HOST_VISIBILITY_CSS = ':host{display:block!important;visibility:visible!important}';
|
|
10394
10448
|
function createShadowRootCssText(cssText) {
|
|
10395
10449
|
const cssForHost = cssText.replace(ROOT_TO_HOST_SELECTOR_RE, ':host');
|
|
10396
10450
|
const propertyBlocks = Array.from(cssText.matchAll(PROPERTY_BLOCK_RE));
|
|
10397
10451
|
const propertyDefaults = propertyBlocks.map(([, name, body])=>{
|
|
10398
|
-
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
return ` ${name}: ${initialValue};`;
|
|
10452
|
+
if (!NON_INHERITING_RE.test(body)) return null;
|
|
10453
|
+
const initialValue = body.match(INITIAL_VALUE_RE)?.[1].trim() ?? 'initial';
|
|
10454
|
+
return ` ${name}: ${initialValue};`;
|
|
10402
10455
|
}).filter((value)=>null !== value).join('\n');
|
|
10403
|
-
|
|
10456
|
+
if (!propertyDefaults) return cssForHost;
|
|
10457
|
+
return `${cssForHost}\n\n@layer base {
|
|
10458
|
+
:host,
|
|
10459
|
+
*,
|
|
10460
|
+
::before,
|
|
10461
|
+
::after,
|
|
10462
|
+
::backdrop,
|
|
10463
|
+
::file-selector-button {
|
|
10464
|
+
${propertyDefaults}
|
|
10465
|
+
}
|
|
10466
|
+
}`;
|
|
10404
10467
|
}
|
|
10405
10468
|
var styleinline = __webpack_require__("./src/style.css?inline");
|
|
10406
10469
|
const shadowRootCssText = createShadowRootCssText(styleinline.A);
|
|
@@ -18183,7 +18246,7 @@ function ResultImagePanel({ className, onOpenComparison, productLabel, productUr
|
|
|
18183
18246
|
});
|
|
18184
18247
|
}
|
|
18185
18248
|
const COMPARISON_DIALOG_BACKGROUND_CLASS_NAME = 'bg-[#212124E5]';
|
|
18186
|
-
function ComparisonDialog({ onAddToCart, onClose, onDeleteResult, onSaveResult, onShareResult, portalContainer, productName, productPrice, productUrl, result }) {
|
|
18249
|
+
function ComparisonDialog({ addToCartError, addToCartPending = false, onAddToCart, onClose, onDeleteResult, onSaveResult, onShareResult, portalContainer, productName, productPrice, productUrl, result }) {
|
|
18187
18250
|
const { t } = useTranslation();
|
|
18188
18251
|
const productLabel = productName ?? t('resultProductLink');
|
|
18189
18252
|
const normalizedProductPrice = normalizeProductPrice(productPrice);
|
|
@@ -18197,7 +18260,7 @@ function ComparisonDialog({ onAddToCart, onClose, onDeleteResult, onSaveResult,
|
|
|
18197
18260
|
open: true,
|
|
18198
18261
|
onOpenChange: (open)=>!open && onClose(),
|
|
18199
18262
|
children: /*#__PURE__*/ jsxs(DialogContent, {
|
|
18200
|
-
className: `dark fixed inset-0 left-0 top-0 flex h-
|
|
18263
|
+
className: `dark fixed inset-0 left-0 top-0 flex h-dvh max-h-none w-svw max-w-none translate-x-0 translate-y-0 flex-col rounded-none ${COMPARISON_DIALOG_BACKGROUND_CLASS_NAME} p-0 text-text-dark duration-200 sm:max-w-none sm:p-0`,
|
|
18201
18264
|
portalContainer: portalContainer,
|
|
18202
18265
|
showCloseButton: false,
|
|
18203
18266
|
children: [
|
|
@@ -18297,59 +18360,67 @@ function ComparisonDialog({ onAddToCart, onClose, onDeleteResult, onSaveResult,
|
|
|
18297
18360
|
]
|
|
18298
18361
|
})
|
|
18299
18362
|
}),
|
|
18300
|
-
/*#__PURE__*/
|
|
18301
|
-
className: "flex
|
|
18302
|
-
children:
|
|
18303
|
-
|
|
18304
|
-
|
|
18305
|
-
|
|
18306
|
-
|
|
18307
|
-
|
|
18308
|
-
|
|
18309
|
-
|
|
18310
|
-
|
|
18311
|
-
|
|
18312
|
-
|
|
18363
|
+
/*#__PURE__*/ jsxs("div", {
|
|
18364
|
+
className: "flex shrink-0 flex-col items-center gap-1 px-8 pb-6 pt-3",
|
|
18365
|
+
children: [
|
|
18366
|
+
addToCartError && /*#__PURE__*/ jsx_runtime_jsx("p", {
|
|
18367
|
+
className: "text-xs text-text-sub",
|
|
18368
|
+
role: "alert",
|
|
18369
|
+
children: addToCartError
|
|
18370
|
+
}),
|
|
18371
|
+
/*#__PURE__*/ jsxs("div", {
|
|
18372
|
+
className: "flex h-23 w-full max-w-104 items-start gap-2",
|
|
18373
|
+
children: [
|
|
18374
|
+
onAddToCart && /*#__PURE__*/ jsxs(button_Button, {
|
|
18375
|
+
className: "min-w-0 flex-1",
|
|
18376
|
+
disabled: addToCartPending,
|
|
18377
|
+
size: "lg",
|
|
18378
|
+
type: "button",
|
|
18379
|
+
variant: "contrast",
|
|
18380
|
+
onClick: onAddToCart,
|
|
18381
|
+
children: [
|
|
18382
|
+
/*#__PURE__*/ jsx_runtime_jsx(Cart, {
|
|
18383
|
+
"data-icon": "inline-start"
|
|
18384
|
+
}),
|
|
18385
|
+
/*#__PURE__*/ jsx_runtime_jsx("span", {
|
|
18386
|
+
className: "truncate",
|
|
18387
|
+
children: addToCartLabel
|
|
18388
|
+
})
|
|
18389
|
+
]
|
|
18390
|
+
}),
|
|
18391
|
+
/*#__PURE__*/ jsx_runtime_jsx(button_Button, {
|
|
18392
|
+
"aria-label": t('share'),
|
|
18393
|
+
size: "icon-lg",
|
|
18394
|
+
type: "button",
|
|
18395
|
+
variant: "outline",
|
|
18396
|
+
onClick: onShareResult,
|
|
18397
|
+
children: /*#__PURE__*/ jsx_runtime_jsx(Share, {
|
|
18398
|
+
"data-icon": "inline-start"
|
|
18399
|
+
})
|
|
18400
|
+
}),
|
|
18401
|
+
/*#__PURE__*/ jsx_runtime_jsx(button_Button, {
|
|
18402
|
+
"aria-label": t('download'),
|
|
18403
|
+
size: "icon-lg",
|
|
18404
|
+
type: "button",
|
|
18405
|
+
variant: "outline",
|
|
18406
|
+
onClick: onSaveResult,
|
|
18407
|
+
children: /*#__PURE__*/ jsx_runtime_jsx(Download, {
|
|
18408
|
+
"data-icon": "inline-start"
|
|
18409
|
+
})
|
|
18410
|
+
}),
|
|
18411
|
+
/*#__PURE__*/ jsx_runtime_jsx(button_Button, {
|
|
18412
|
+
"aria-label": t('reset'),
|
|
18413
|
+
size: "icon-lg",
|
|
18414
|
+
type: "button",
|
|
18415
|
+
variant: "outline",
|
|
18416
|
+
onClick: handleDelete,
|
|
18417
|
+
children: /*#__PURE__*/ jsx_runtime_jsx(EditorTrash, {
|
|
18313
18418
|
"data-icon": "inline-start"
|
|
18314
|
-
}),
|
|
18315
|
-
/*#__PURE__*/ jsx_runtime_jsx("span", {
|
|
18316
|
-
className: "truncate",
|
|
18317
|
-
children: addToCartLabel
|
|
18318
18419
|
})
|
|
18319
|
-
]
|
|
18320
|
-
}),
|
|
18321
|
-
/*#__PURE__*/ jsx_runtime_jsx(button_Button, {
|
|
18322
|
-
"aria-label": t('share'),
|
|
18323
|
-
size: "icon-lg",
|
|
18324
|
-
type: "button",
|
|
18325
|
-
variant: "outline",
|
|
18326
|
-
onClick: onShareResult,
|
|
18327
|
-
children: /*#__PURE__*/ jsx_runtime_jsx(Share, {
|
|
18328
|
-
"data-icon": "inline-start"
|
|
18329
|
-
})
|
|
18330
|
-
}),
|
|
18331
|
-
/*#__PURE__*/ jsx_runtime_jsx(button_Button, {
|
|
18332
|
-
"aria-label": t('download'),
|
|
18333
|
-
size: "icon-lg",
|
|
18334
|
-
type: "button",
|
|
18335
|
-
variant: "outline",
|
|
18336
|
-
onClick: onSaveResult,
|
|
18337
|
-
children: /*#__PURE__*/ jsx_runtime_jsx(Download, {
|
|
18338
|
-
"data-icon": "inline-start"
|
|
18339
|
-
})
|
|
18340
|
-
}),
|
|
18341
|
-
/*#__PURE__*/ jsx_runtime_jsx(button_Button, {
|
|
18342
|
-
"aria-label": t('reset'),
|
|
18343
|
-
size: "icon-lg",
|
|
18344
|
-
type: "button",
|
|
18345
|
-
variant: "outline",
|
|
18346
|
-
onClick: handleDelete,
|
|
18347
|
-
children: /*#__PURE__*/ jsx_runtime_jsx(EditorTrash, {
|
|
18348
|
-
"data-icon": "inline-start"
|
|
18349
18420
|
})
|
|
18350
|
-
|
|
18351
|
-
|
|
18352
|
-
|
|
18421
|
+
]
|
|
18422
|
+
})
|
|
18423
|
+
]
|
|
18353
18424
|
})
|
|
18354
18425
|
]
|
|
18355
18426
|
})
|
|
@@ -18392,7 +18463,7 @@ function Badge({ className, variant, size, asChild = false, ...props }) {
|
|
|
18392
18463
|
}
|
|
18393
18464
|
function TryOnShell({ className, ...props }) {
|
|
18394
18465
|
return /*#__PURE__*/ jsx_runtime_jsx("div", {
|
|
18395
|
-
className: utils_cn('flex max-h-
|
|
18466
|
+
className: utils_cn('flex max-h-dvh w-svw flex-col overflow-hidden rounded-t-3xl rounded-b-none bg-background-containers text-foreground shadow-2xl sm:max-h-[min(calc(100svh-var(--spacing)*8),--spacing(176))] sm:w-120 sm:rounded-3xl', className),
|
|
18396
18467
|
...props
|
|
18397
18468
|
});
|
|
18398
18469
|
}
|
|
@@ -18871,10 +18942,10 @@ function getAiTryOnGuidance(productType) {
|
|
|
18871
18942
|
}
|
|
18872
18943
|
function StartScreen({ view }) {
|
|
18873
18944
|
const { t } = useTranslation();
|
|
18874
|
-
const guidance = getAiTryOnGuidance(view.productType);
|
|
18945
|
+
const guidance = view.productType ? getAiTryOnGuidance(view.productType) : null;
|
|
18875
18946
|
return /*#__PURE__*/ jsxs(ScreenLayout, {
|
|
18876
18947
|
title: t('startTitle'),
|
|
18877
|
-
subtitle: t(guidance
|
|
18948
|
+
subtitle: t(guidance?.titleKey ?? 'externalPhotoHint'),
|
|
18878
18949
|
children: [
|
|
18879
18950
|
/*#__PURE__*/ jsxs(TryOnImagePanel, {
|
|
18880
18951
|
className: "mt-3",
|
|
@@ -18925,7 +18996,8 @@ function StartScreen({ view }) {
|
|
|
18925
18996
|
}
|
|
18926
18997
|
function IntroScreen({ view }) {
|
|
18927
18998
|
const { t } = useTranslation();
|
|
18928
|
-
const guidance = getAiTryOnGuidance(view.productType);
|
|
18999
|
+
const guidance = view.productType ? getAiTryOnGuidance(view.productType) : null;
|
|
19000
|
+
if (!guidance) return null;
|
|
18929
19001
|
return /*#__PURE__*/ jsxs(ScreenLayout, {
|
|
18930
19002
|
title: t(guidance.titleKey),
|
|
18931
19003
|
subtitle: t(guidance.subtitleKey),
|
|
@@ -19043,6 +19115,79 @@ function ConfirmScreen({ view }) {
|
|
|
19043
19115
|
]
|
|
19044
19116
|
});
|
|
19045
19117
|
}
|
|
19118
|
+
function ActivationLoadingScreen() {
|
|
19119
|
+
const { t } = useTranslation();
|
|
19120
|
+
return /*#__PURE__*/ jsx_runtime_jsx(ScreenLayout, {
|
|
19121
|
+
title: t('activationLoading'),
|
|
19122
|
+
subtitle: t('activationLoadingSubtitle'),
|
|
19123
|
+
children: /*#__PURE__*/ jsx_runtime_jsx(TryOnImagePanel, {
|
|
19124
|
+
className: "mt-3",
|
|
19125
|
+
children: /*#__PURE__*/ jsx_runtime_jsx(LoadingTexture, {
|
|
19126
|
+
animated: true,
|
|
19127
|
+
className: "left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
|
|
19128
|
+
})
|
|
19129
|
+
})
|
|
19130
|
+
});
|
|
19131
|
+
}
|
|
19132
|
+
function ActivationUnavailableScreen({ onClose, temporary }) {
|
|
19133
|
+
const { t } = useTranslation();
|
|
19134
|
+
return /*#__PURE__*/ jsx_runtime_jsx(ScreenLayout, {
|
|
19135
|
+
title: t('activationUnavailableTitle'),
|
|
19136
|
+
subtitle: t(temporary ? 'activationTemporarilyUnavailable' : 'externalProductUnavailable'),
|
|
19137
|
+
children: /*#__PURE__*/ jsxs(ActionStack, {
|
|
19138
|
+
className: "mt-4",
|
|
19139
|
+
children: [
|
|
19140
|
+
onClose && /*#__PURE__*/ jsx_runtime_jsx(button_Button, {
|
|
19141
|
+
className: "w-full",
|
|
19142
|
+
size: "lg",
|
|
19143
|
+
type: "button",
|
|
19144
|
+
variant: "contrast",
|
|
19145
|
+
onClick: onClose,
|
|
19146
|
+
children: t('close')
|
|
19147
|
+
}),
|
|
19148
|
+
/*#__PURE__*/ jsx_runtime_jsx(TermsCopy, {
|
|
19149
|
+
className: "text-text-sub"
|
|
19150
|
+
})
|
|
19151
|
+
]
|
|
19152
|
+
})
|
|
19153
|
+
});
|
|
19154
|
+
}
|
|
19155
|
+
function ActivationErrorScreen({ onClose, onRetry }) {
|
|
19156
|
+
const { t } = useTranslation();
|
|
19157
|
+
return /*#__PURE__*/ jsx_runtime_jsx(ScreenLayout, {
|
|
19158
|
+
title: t('errorTitle'),
|
|
19159
|
+
subtitle: t('externalServiceUnavailable'),
|
|
19160
|
+
children: /*#__PURE__*/ jsxs(ActionStack, {
|
|
19161
|
+
className: "mt-4",
|
|
19162
|
+
children: [
|
|
19163
|
+
onRetry && /*#__PURE__*/ jsxs(button_Button, {
|
|
19164
|
+
className: "w-full",
|
|
19165
|
+
size: "lg",
|
|
19166
|
+
type: "button",
|
|
19167
|
+
variant: "contrast",
|
|
19168
|
+
onClick: onRetry,
|
|
19169
|
+
children: [
|
|
19170
|
+
/*#__PURE__*/ jsx_runtime_jsx(Vizbl, {
|
|
19171
|
+
"data-icon": "inline-start"
|
|
19172
|
+
}),
|
|
19173
|
+
t('errorRetry')
|
|
19174
|
+
]
|
|
19175
|
+
}),
|
|
19176
|
+
onClose && /*#__PURE__*/ jsx_runtime_jsx(button_Button, {
|
|
19177
|
+
className: "w-full",
|
|
19178
|
+
size: "lg",
|
|
19179
|
+
type: "button",
|
|
19180
|
+
variant: "outline",
|
|
19181
|
+
onClick: onClose,
|
|
19182
|
+
children: t('close')
|
|
19183
|
+
}),
|
|
19184
|
+
/*#__PURE__*/ jsx_runtime_jsx(TermsCopy, {
|
|
19185
|
+
className: "text-text-sub"
|
|
19186
|
+
})
|
|
19187
|
+
]
|
|
19188
|
+
})
|
|
19189
|
+
});
|
|
19190
|
+
}
|
|
19046
19191
|
function ProcessingScreen({ view }) {
|
|
19047
19192
|
const { t } = useTranslation();
|
|
19048
19193
|
return /*#__PURE__*/ jsxs(ScreenLayout, {
|
|
@@ -19091,14 +19236,25 @@ function ProcessingScreen({ view }) {
|
|
|
19091
19236
|
]
|
|
19092
19237
|
});
|
|
19093
19238
|
}
|
|
19094
|
-
function ErrorScreen({ view }) {
|
|
19239
|
+
function ErrorScreen({ view, onClose }) {
|
|
19095
19240
|
const { t } = useTranslation();
|
|
19096
19241
|
const isFileTooLarge = 'upload-validation' === view.error.kind && 'file-too-large' === view.error.code;
|
|
19242
|
+
const externalErrors = {
|
|
19243
|
+
tokens: 'externalNoTokens',
|
|
19244
|
+
product: 'externalProductUnavailable',
|
|
19245
|
+
service: 'externalServiceUnavailable',
|
|
19246
|
+
timeout: 'externalTimeout',
|
|
19247
|
+
'photo-required': 'externalPhotoRequired'
|
|
19248
|
+
};
|
|
19249
|
+
const code = 'generation' === view.error.kind ? view.error.code : void 0;
|
|
19250
|
+
const externalKey = code ? externalErrors[code] : void 0;
|
|
19251
|
+
const canRetry = !code || 'service' === code || 'timeout' === code;
|
|
19252
|
+
const canChangePhoto = !code || 'photo-required' === code;
|
|
19097
19253
|
return /*#__PURE__*/ jsxs(ScreenLayout, {
|
|
19098
19254
|
title: t(isFileTooLarge ? 'errorFileTooLarge' : 'errorTitle'),
|
|
19099
|
-
subtitle: isFileTooLarge ? void 0 : t('errorSubtitle'),
|
|
19255
|
+
subtitle: isFileTooLarge ? void 0 : t(externalKey ?? 'errorSubtitle'),
|
|
19100
19256
|
children: [
|
|
19101
|
-
!isFileTooLarge && /*#__PURE__*/ jsxs(TryOnImagePanel, {
|
|
19257
|
+
!isFileTooLarge && !code && /*#__PURE__*/ jsxs(TryOnImagePanel, {
|
|
19102
19258
|
className: "mt-3",
|
|
19103
19259
|
children: [
|
|
19104
19260
|
/*#__PURE__*/ jsx_runtime_jsx(BlurredPhotoBackdrop, {
|
|
@@ -19146,7 +19302,7 @@ function ErrorScreen({ view }) {
|
|
|
19146
19302
|
/*#__PURE__*/ jsxs(ActionStack, {
|
|
19147
19303
|
className: "mt-4",
|
|
19148
19304
|
children: [
|
|
19149
|
-
/*#__PURE__*/ jsxs(button_Button, {
|
|
19305
|
+
canRetry && /*#__PURE__*/ jsxs(button_Button, {
|
|
19150
19306
|
className: "w-full",
|
|
19151
19307
|
size: "lg",
|
|
19152
19308
|
type: "button",
|
|
@@ -19159,7 +19315,7 @@ function ErrorScreen({ view }) {
|
|
|
19159
19315
|
t('errorRetry')
|
|
19160
19316
|
]
|
|
19161
19317
|
}),
|
|
19162
|
-
/*#__PURE__*/ jsxs(button_Button, {
|
|
19318
|
+
canChangePhoto && /*#__PURE__*/ jsxs(button_Button, {
|
|
19163
19319
|
className: "w-full",
|
|
19164
19320
|
size: "lg",
|
|
19165
19321
|
type: "button",
|
|
@@ -19172,6 +19328,14 @@ function ErrorScreen({ view }) {
|
|
|
19172
19328
|
t('errorDifferentPhoto')
|
|
19173
19329
|
]
|
|
19174
19330
|
}),
|
|
19331
|
+
code && onClose && /*#__PURE__*/ jsx_runtime_jsx(button_Button, {
|
|
19332
|
+
className: "w-full",
|
|
19333
|
+
size: "lg",
|
|
19334
|
+
type: "button",
|
|
19335
|
+
variant: "outline",
|
|
19336
|
+
onClick: onClose,
|
|
19337
|
+
children: t('close')
|
|
19338
|
+
}),
|
|
19175
19339
|
/*#__PURE__*/ jsx_runtime_jsx(TermsCopy, {
|
|
19176
19340
|
className: "text-text-sub"
|
|
19177
19341
|
})
|
|
@@ -19180,8 +19344,9 @@ function ErrorScreen({ view }) {
|
|
|
19180
19344
|
]
|
|
19181
19345
|
});
|
|
19182
19346
|
}
|
|
19183
|
-
function ResultScreen({ view }) {
|
|
19347
|
+
function ResultScreen({ view, showAddToCart = true, onAddToCart, addToCartPending = false, addToCartError }) {
|
|
19184
19348
|
const { t } = useTranslation();
|
|
19349
|
+
const addToCart = onAddToCart ?? view.addToCart;
|
|
19185
19350
|
const normalizedProductPrice = try_on_screens_normalizeProductPrice(view.productPrice);
|
|
19186
19351
|
const addToCartLabel = normalizedProductPrice ? t('resultTryAnotherWithPrice', {
|
|
19187
19352
|
price: normalizedProductPrice
|
|
@@ -19192,7 +19357,8 @@ function ResultScreen({ view }) {
|
|
|
19192
19357
|
className: "flex flex-col gap-4 px-6 pb-2 pt-4 sm:px-8",
|
|
19193
19358
|
children: view.resultHistory.map((historyItem, index)=>/*#__PURE__*/ jsx_runtime_jsx(ResultFitCard, {
|
|
19194
19359
|
compact: index > 0,
|
|
19195
|
-
onAddToCart:
|
|
19360
|
+
onAddToCart: showAddToCart ? addToCart : void 0,
|
|
19361
|
+
addToCartPending: addToCartPending,
|
|
19196
19362
|
productName: historyItem.productName ?? view.productName,
|
|
19197
19363
|
productPrice: historyItem.productPrice,
|
|
19198
19364
|
productUrl: historyItem.productUrl ?? view.productUrl,
|
|
@@ -19220,12 +19386,13 @@ function ResultScreen({ view }) {
|
|
|
19220
19386
|
/*#__PURE__*/ jsxs(ActionStack, {
|
|
19221
19387
|
className: "mt-4",
|
|
19222
19388
|
children: [
|
|
19223
|
-
/*#__PURE__*/ jsxs(button_Button, {
|
|
19389
|
+
showAddToCart && /*#__PURE__*/ jsxs(button_Button, {
|
|
19224
19390
|
className: "w-full",
|
|
19391
|
+
disabled: addToCartPending,
|
|
19225
19392
|
size: "lg",
|
|
19226
19393
|
type: "button",
|
|
19227
19394
|
variant: "contrast",
|
|
19228
|
-
onClick:
|
|
19395
|
+
onClick: addToCart,
|
|
19229
19396
|
children: [
|
|
19230
19397
|
/*#__PURE__*/ jsx_runtime_jsx(Cart, {
|
|
19231
19398
|
"data-icon": "inline-start"
|
|
@@ -19233,6 +19400,11 @@ function ResultScreen({ view }) {
|
|
|
19233
19400
|
addToCartLabel
|
|
19234
19401
|
]
|
|
19235
19402
|
}),
|
|
19403
|
+
addToCartError && /*#__PURE__*/ jsx_runtime_jsx("p", {
|
|
19404
|
+
className: "text-center text-xs text-text-sub",
|
|
19405
|
+
role: "alert",
|
|
19406
|
+
children: addToCartError
|
|
19407
|
+
}),
|
|
19236
19408
|
/*#__PURE__*/ jsxs("div", {
|
|
19237
19409
|
className: "grid grid-cols-3 gap-2",
|
|
19238
19410
|
children: [
|
|
@@ -19276,7 +19448,7 @@ function ResultScreen({ view }) {
|
|
|
19276
19448
|
]
|
|
19277
19449
|
});
|
|
19278
19450
|
}
|
|
19279
|
-
function ResultFitCard({ compact = false, onAddToCart, onDeleteResult, onOpenComparison, onSaveResult, onShareResult, productName, productPrice, productUrl, result }) {
|
|
19451
|
+
function ResultFitCard({ compact = false, onAddToCart, addToCartPending = false, onDeleteResult, onOpenComparison, onSaveResult, onShareResult, productName, productPrice, productUrl, result }) {
|
|
19280
19452
|
const { t } = useTranslation();
|
|
19281
19453
|
const productLabel = productName ?? t('resultProductLink');
|
|
19282
19454
|
const normalizedProductPrice = try_on_screens_normalizeProductPrice(productPrice);
|
|
@@ -19296,8 +19468,9 @@ function ResultFitCard({ compact = false, onAddToCart, onDeleteResult, onOpenCom
|
|
|
19296
19468
|
/*#__PURE__*/ jsxs("div", {
|
|
19297
19469
|
className: "flex gap-2",
|
|
19298
19470
|
children: [
|
|
19299
|
-
/*#__PURE__*/ jsxs(button_Button, {
|
|
19471
|
+
onAddToCart && /*#__PURE__*/ jsxs(button_Button, {
|
|
19300
19472
|
className: "min-w-0 flex-1",
|
|
19473
|
+
disabled: addToCartPending,
|
|
19301
19474
|
size: "default",
|
|
19302
19475
|
type: "button",
|
|
19303
19476
|
variant: "contrast",
|
|
@@ -19419,6 +19592,55 @@ function try_on_screens_normalizeProductPrice(value) {
|
|
|
19419
19592
|
const normalized = value?.trim();
|
|
19420
19593
|
return normalized || void 0;
|
|
19421
19594
|
}
|
|
19595
|
+
const IMAGE_SNIFF_BYTES = 16;
|
|
19596
|
+
const IMAGE_BRANDS = new Set([
|
|
19597
|
+
'avif',
|
|
19598
|
+
'avis',
|
|
19599
|
+
'heic',
|
|
19600
|
+
'heix',
|
|
19601
|
+
'heim',
|
|
19602
|
+
'heis',
|
|
19603
|
+
'hevc',
|
|
19604
|
+
'hevx',
|
|
19605
|
+
'mif1',
|
|
19606
|
+
'msf1'
|
|
19607
|
+
]);
|
|
19608
|
+
async function readGeneratedImage(response) {
|
|
19609
|
+
const blob = toBlob(response);
|
|
19610
|
+
if (!blob || blob.size <= 0) return {
|
|
19611
|
+
blob: null,
|
|
19612
|
+
errorText: null
|
|
19613
|
+
};
|
|
19614
|
+
const header = new Uint8Array(await blob.slice(0, IMAGE_SNIFF_BYTES).arrayBuffer());
|
|
19615
|
+
if (!isImageHeader(header)) return {
|
|
19616
|
+
blob: null,
|
|
19617
|
+
errorText: (await blob.text().catch(()=>'')).slice(0, 2000)
|
|
19618
|
+
};
|
|
19619
|
+
return {
|
|
19620
|
+
blob,
|
|
19621
|
+
errorText: null
|
|
19622
|
+
};
|
|
19623
|
+
}
|
|
19624
|
+
function toBlob(response) {
|
|
19625
|
+
if (response instanceof Blob) return response;
|
|
19626
|
+
if (response instanceof ArrayBuffer) return new Blob([
|
|
19627
|
+
response
|
|
19628
|
+
]);
|
|
19629
|
+
if (ArrayBuffer.isView(response)) return new Blob([
|
|
19630
|
+
new Uint8Array(response.buffer, response.byteOffset, response.byteLength)
|
|
19631
|
+
]);
|
|
19632
|
+
return null;
|
|
19633
|
+
}
|
|
19634
|
+
function isImageHeader(header) {
|
|
19635
|
+
const startsWith = (...bytes)=>bytes.every((byte, index)=>header[index] === byte);
|
|
19636
|
+
const at = (offset, ...bytes)=>bytes.every((byte, index)=>header[offset + index] === byte);
|
|
19637
|
+
return startsWith(0xFF, 0xD8, 0xFF) || startsWith(0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A) || startsWith(0x47, 0x49, 0x46, 0x38) || startsWith(0x42, 0x4D) || startsWith(0x52, 0x49, 0x46, 0x46) && at(8, 0x57, 0x45, 0x42, 0x50) || at(4, 0x66, 0x74, 0x79, 0x70) && IMAGE_BRANDS.has(brand(header));
|
|
19638
|
+
}
|
|
19639
|
+
function brand(header) {
|
|
19640
|
+
let value = '';
|
|
19641
|
+
for(let index = 8; index < 12; index++)value += String.fromCharCode(header[index] ?? 0);
|
|
19642
|
+
return value;
|
|
19643
|
+
}
|
|
19422
19644
|
const REQUIRED_UPLOAD_COUNT = 2;
|
|
19423
19645
|
function createNanoBananaAiTryOnProcessor({ createObjectUrl = defaultCreateObjectUrl, fetch: fetchImpl, productImageSrc, productType, tryOnClient, apiKey }) {
|
|
19424
19646
|
assertProductType(productType);
|
|
@@ -19437,8 +19659,12 @@ function createNanoBananaAiTryOnProcessor({ createObjectUrl = defaultCreateObjec
|
|
|
19437
19659
|
const [personUpload, productUpload] = resolveUploads(await client.requestUploadUrls(REQUIRED_UPLOAD_COUNT), REQUIRED_UPLOAD_COUNT);
|
|
19438
19660
|
context?.reportProgress(44);
|
|
19439
19661
|
await Promise.all([
|
|
19440
|
-
uploadImageToPresignedUrl(personUpload.uploadUrl, personImageBlob,
|
|
19441
|
-
|
|
19662
|
+
uploadImageToPresignedUrl(personUpload.uploadUrl, personImageBlob, {
|
|
19663
|
+
fetch: fetchImpl
|
|
19664
|
+
}),
|
|
19665
|
+
uploadImageToPresignedUrl(productUpload.uploadUrl, productImageBlob, {
|
|
19666
|
+
fetch: fetchImpl
|
|
19667
|
+
})
|
|
19442
19668
|
]);
|
|
19443
19669
|
context?.reportProgress(62);
|
|
19444
19670
|
const stopGenerationProgress = startGenerationProgress(context?.reportProgress);
|
|
@@ -19457,8 +19683,8 @@ function createNanoBananaAiTryOnProcessor({ createObjectUrl = defaultCreateObjec
|
|
|
19457
19683
|
stopGenerationProgress();
|
|
19458
19684
|
}
|
|
19459
19685
|
context?.reportProgress(96);
|
|
19460
|
-
const generatedImageBlob = await
|
|
19461
|
-
if (generatedImageBlob
|
|
19686
|
+
const { blob: generatedImageBlob } = await readGeneratedImage(response);
|
|
19687
|
+
if (!generatedImageBlob) throw new Error('AI try-on generation did not return an image');
|
|
19462
19688
|
context?.reportProgress(98);
|
|
19463
19689
|
const generatedImageSrc = createObjectUrl(generatedImageBlob);
|
|
19464
19690
|
if (!generatedImageSrc) throw new Error('AI try-on result image URL cannot be created');
|
|
@@ -19500,14 +19726,28 @@ function resolveFetch(fetchImpl) {
|
|
|
19500
19726
|
if ('function' == typeof fetch) return fetch;
|
|
19501
19727
|
throw new Error('Fetch API is not available for AI try-on image upload');
|
|
19502
19728
|
}
|
|
19503
|
-
async function uploadImageToPresignedUrl(uploadUrl, imageBlob,
|
|
19504
|
-
const
|
|
19505
|
-
|
|
19506
|
-
|
|
19729
|
+
async function uploadImageToPresignedUrl(uploadUrl, imageBlob, options = {}) {
|
|
19730
|
+
const { fetch: fetchImpl, signal, timeoutMs } = options;
|
|
19731
|
+
const controller = new AbortController();
|
|
19732
|
+
const abort = ()=>controller.abort(signal?.reason);
|
|
19733
|
+
const timer = void 0 === timeoutMs ? void 0 : setTimeout(()=>controller.abort(new DOMException('AI try-on image upload timed out', 'TimeoutError')), timeoutMs);
|
|
19734
|
+
if (signal?.aborted) abort();
|
|
19735
|
+
else signal?.addEventListener('abort', abort, {
|
|
19736
|
+
once: true
|
|
19507
19737
|
});
|
|
19508
|
-
|
|
19509
|
-
const
|
|
19510
|
-
|
|
19738
|
+
try {
|
|
19739
|
+
const response = await resolveFetch(fetchImpl)(uploadUrl, {
|
|
19740
|
+
body: imageBlob,
|
|
19741
|
+
method: 'PUT',
|
|
19742
|
+
signal: controller.signal
|
|
19743
|
+
});
|
|
19744
|
+
if (!response.ok) {
|
|
19745
|
+
const details = await response.text().catch(()=>'');
|
|
19746
|
+
throw new Error(`Nano Banana image upload failed with status ${response.status}${details ? `: ${details}` : ''}`);
|
|
19747
|
+
}
|
|
19748
|
+
} finally{
|
|
19749
|
+
clearTimeout(timer);
|
|
19750
|
+
signal?.removeEventListener('abort', abort);
|
|
19511
19751
|
}
|
|
19512
19752
|
}
|
|
19513
19753
|
async function resolveProductImageBlob(productImageSrc, fetchImpl) {
|
|
@@ -19529,24 +19769,6 @@ async function resolveBlobFromUrl(src, fetchImpl, message) {
|
|
|
19529
19769
|
}
|
|
19530
19770
|
return response.blob();
|
|
19531
19771
|
}
|
|
19532
|
-
async function resolveGeneratedImageBlob(response) {
|
|
19533
|
-
if (response instanceof Blob) return response.type ? response : new Blob([
|
|
19534
|
-
response
|
|
19535
|
-
], {
|
|
19536
|
-
type: 'image/jpeg'
|
|
19537
|
-
});
|
|
19538
|
-
if (response instanceof ArrayBuffer) return new Blob([
|
|
19539
|
-
response
|
|
19540
|
-
], {
|
|
19541
|
-
type: 'image/jpeg'
|
|
19542
|
-
});
|
|
19543
|
-
if ('string' == typeof response) return new Blob([
|
|
19544
|
-
response
|
|
19545
|
-
], {
|
|
19546
|
-
type: 'image/jpeg'
|
|
19547
|
-
});
|
|
19548
|
-
throw new Error('AI try-on generation did not return an image');
|
|
19549
|
-
}
|
|
19550
19772
|
function startGenerationProgress(reportProgress) {
|
|
19551
19773
|
if (!reportProgress) return ()=>{};
|
|
19552
19774
|
reportProgress(70);
|
|
@@ -19955,7 +20177,7 @@ class AiTryOnSession {
|
|
|
19955
20177
|
};
|
|
19956
20178
|
}
|
|
19957
20179
|
choosePhoto() {
|
|
19958
|
-
if (this.tipStore.consumeFirstView(this.state.productType)) {
|
|
20180
|
+
if (this.state.productType && this.tipStore.consumeFirstView(this.state.productType)) {
|
|
19959
20181
|
this.state = {
|
|
19960
20182
|
...this.state,
|
|
19961
20183
|
stage: 'intro'
|
|
@@ -19967,14 +20189,14 @@ class AiTryOnSession {
|
|
|
19967
20189
|
};
|
|
19968
20190
|
}
|
|
19969
20191
|
continueIntro() {
|
|
19970
|
-
this.tipStore.markSeen(this.state.productType);
|
|
20192
|
+
if (this.state.productType) this.tipStore.markSeen(this.state.productType);
|
|
19971
20193
|
return {
|
|
19972
20194
|
type: 'request-file-input'
|
|
19973
20195
|
};
|
|
19974
20196
|
}
|
|
19975
20197
|
requestClose() {
|
|
19976
20198
|
if ('intro' === this.state.stage) {
|
|
19977
|
-
this.tipStore.markSeen(this.state.productType);
|
|
20199
|
+
if (this.state.productType) this.tipStore.markSeen(this.state.productType);
|
|
19978
20200
|
this.goBack();
|
|
19979
20201
|
return null;
|
|
19980
20202
|
}
|
|
@@ -20118,12 +20340,15 @@ class AiTryOnSession {
|
|
|
20118
20340
|
};
|
|
20119
20341
|
return historyItem;
|
|
20120
20342
|
}
|
|
20121
|
-
failGeneration(message) {
|
|
20343
|
+
failGeneration(message, code) {
|
|
20122
20344
|
this.state = {
|
|
20123
20345
|
...this.state,
|
|
20124
20346
|
error: {
|
|
20125
20347
|
kind: 'generation',
|
|
20126
|
-
message
|
|
20348
|
+
message,
|
|
20349
|
+
...code ? {
|
|
20350
|
+
code
|
|
20351
|
+
} : {}
|
|
20127
20352
|
},
|
|
20128
20353
|
generationProduct: null,
|
|
20129
20354
|
stage: 'error'
|
|
@@ -20294,6 +20519,13 @@ function clampProgress(progress) {
|
|
|
20294
20519
|
function createPhotoId(file) {
|
|
20295
20520
|
return `${file.name}-${file.size}-${file.lastModified}`;
|
|
20296
20521
|
}
|
|
20522
|
+
class AiTryOnGenerationError extends Error {
|
|
20523
|
+
code;
|
|
20524
|
+
constructor(code){
|
|
20525
|
+
super(code), this.code = code;
|
|
20526
|
+
this.name = 'AiTryOnGenerationError';
|
|
20527
|
+
}
|
|
20528
|
+
}
|
|
20297
20529
|
function createAiTryOnInteractiveView(session, { addToCart, choosePhoto, deleteResult, generate, runSessionCommand, saveResult, shareResult, sync }) {
|
|
20298
20530
|
const currentView = session.view;
|
|
20299
20531
|
switch(currentView.stage){
|
|
@@ -20430,6 +20662,13 @@ function collectPhotoObjectUrl(photo) {
|
|
|
20430
20662
|
function useAiTryOnSession({ historyStore = indexedDbAiTryOnHistoryStore, objectUrls = browserAiTryOnObjectUrls, onRequestClose, onRequestFileInput, processPhoto, productImageSrc, productName, productPrice, productType, productUrl, tryOnClient, resultActions = browserAiTryOnResultActions, tipStore = browserAiTryOnTipStore }) {
|
|
20431
20663
|
const [, rerender] = (0, __rspack_external_react.useReducer)((value)=>value + 1, 0);
|
|
20432
20664
|
const trackedObjectUrls = (0, __rspack_external_react.useRef)(new Set());
|
|
20665
|
+
const mounted = (0, __rspack_external_react.useRef)(true);
|
|
20666
|
+
(0, __rspack_external_react.useEffect)(()=>{
|
|
20667
|
+
mounted.current = true;
|
|
20668
|
+
return ()=>{
|
|
20669
|
+
mounted.current = false;
|
|
20670
|
+
};
|
|
20671
|
+
}, []);
|
|
20433
20672
|
const createTrackedObjectUrl = (0, __rspack_external_react.useCallback)((blob)=>{
|
|
20434
20673
|
const src = objectUrls.createObjectUrl(blob);
|
|
20435
20674
|
if (src) trackedObjectUrls.current.add(src);
|
|
@@ -20455,12 +20694,16 @@ function useAiTryOnSession({ historyStore = indexedDbAiTryOnHistoryStore, object
|
|
|
20455
20694
|
productType,
|
|
20456
20695
|
productUrl
|
|
20457
20696
|
});
|
|
20458
|
-
const processSelectedPhoto = (0, __rspack_external_react.useMemo)(()=>
|
|
20697
|
+
const processSelectedPhoto = (0, __rspack_external_react.useMemo)(()=>{
|
|
20698
|
+
if (processPhoto) return processPhoto;
|
|
20699
|
+
assertProductType(productType);
|
|
20700
|
+
return createNanoBananaAiTryOnProcessor({
|
|
20459
20701
|
createObjectUrl: createTrackedObjectUrl,
|
|
20460
20702
|
productImageSrc,
|
|
20461
20703
|
productType,
|
|
20462
20704
|
tryOnClient
|
|
20463
|
-
})
|
|
20705
|
+
});
|
|
20706
|
+
}, [
|
|
20464
20707
|
createTrackedObjectUrl,
|
|
20465
20708
|
processPhoto,
|
|
20466
20709
|
productImageSrc,
|
|
@@ -20521,21 +20764,29 @@ function useAiTryOnSession({ historyStore = indexedDbAiTryOnHistoryStore, object
|
|
|
20521
20764
|
try {
|
|
20522
20765
|
const result = await processSelectedPhoto(photo, {
|
|
20523
20766
|
reportProgress: (progress)=>{
|
|
20767
|
+
if (!mounted.current) return;
|
|
20524
20768
|
session.reportGenerationProgress(progress);
|
|
20525
20769
|
sync();
|
|
20526
20770
|
}
|
|
20527
20771
|
});
|
|
20528
20772
|
await waitForRemainingProcessingDuration(processingStartedAt);
|
|
20773
|
+
if (!mounted.current) {
|
|
20774
|
+
persistHistoryItem(session.completeGeneration(result));
|
|
20775
|
+
if (result.after.src.startsWith('blob:')) objectUrls.revokeObjectUrl(result.after.src);
|
|
20776
|
+
return;
|
|
20777
|
+
}
|
|
20529
20778
|
const historyItem = session.completeGeneration(result);
|
|
20530
20779
|
trackResultObjectUrls(historyItem, trackedObjectUrls.current);
|
|
20531
20780
|
persistHistoryItem(historyItem);
|
|
20532
20781
|
sync();
|
|
20533
20782
|
} catch (error) {
|
|
20534
20783
|
await waitForRemainingProcessingDuration(processingStartedAt);
|
|
20535
|
-
|
|
20784
|
+
if (!mounted.current) return;
|
|
20785
|
+
session.failGeneration(error instanceof Error ? error.message : void 0, error instanceof AiTryOnGenerationError ? error.code : void 0);
|
|
20536
20786
|
sync();
|
|
20537
20787
|
}
|
|
20538
20788
|
}, [
|
|
20789
|
+
objectUrls,
|
|
20539
20790
|
persistHistoryItem,
|
|
20540
20791
|
processSelectedPhoto,
|
|
20541
20792
|
session,
|
|
@@ -20643,8 +20894,11 @@ function useAiTryOnSession({ historyStore = indexedDbAiTryOnHistoryStore, object
|
|
|
20643
20894
|
function waitForRemainingProcessingDuration(startedAt) {
|
|
20644
20895
|
return waitForProcessingDuration(getRemainingProcessingDuration(startedAt));
|
|
20645
20896
|
}
|
|
20646
|
-
|
|
20647
|
-
|
|
20897
|
+
const READY_ACTIVATION = {
|
|
20898
|
+
status: 'ready'
|
|
20899
|
+
};
|
|
20900
|
+
function AiTryOnRoot({ dialogPortalContainer, language, onClose, onAddToCart, processPhoto, productImageSrc, productName, productPrice, productType, productUrl, tryOnClient: providedTryOnClient, apiKey, showDialogCloseButton = false, showAddToCart = true, activation = READY_ACTIVATION, onRetryActivation }) {
|
|
20901
|
+
if (!processPhoto) assertProductType(productType);
|
|
20648
20902
|
const { i18n, t } = useTranslation();
|
|
20649
20903
|
const fileInputRef = (0, __rspack_external_react.useRef)(null);
|
|
20650
20904
|
const normalizedApiKey = normalizeApiKey(apiKey);
|
|
@@ -20672,7 +20926,22 @@ function AiTryOnRoot({ dialogPortalContainer, language, onClose, processPhoto, p
|
|
|
20672
20926
|
tryOnClient
|
|
20673
20927
|
});
|
|
20674
20928
|
const stage = session.view.stage;
|
|
20929
|
+
const closeDialog = onClose ?? session.requestClose;
|
|
20675
20930
|
const isMyFitsActive = 'result' === session.view.stage && session.view.myFitsOpen;
|
|
20931
|
+
const [cartPending, setCartPending] = (0, __rspack_external_react.useState)(false);
|
|
20932
|
+
const [cartError, setCartError] = (0, __rspack_external_react.useState)(void 0);
|
|
20933
|
+
const addToCart = (0, __rspack_external_react.useCallback)(()=>{
|
|
20934
|
+
if ('result' !== session.view.stage) return;
|
|
20935
|
+
session.view.addToCart();
|
|
20936
|
+
if (!onAddToCart) return;
|
|
20937
|
+
setCartPending(true);
|
|
20938
|
+
setCartError(void 0);
|
|
20939
|
+
Promise.resolve(onAddToCart()).catch((error)=>setCartError(error instanceof Error && error.message ? error.message : t('externalServiceUnavailable'))).finally(()=>setCartPending(false));
|
|
20940
|
+
}, [
|
|
20941
|
+
onAddToCart,
|
|
20942
|
+
session.view,
|
|
20943
|
+
t
|
|
20944
|
+
]);
|
|
20676
20945
|
function handleFileChange(event) {
|
|
20677
20946
|
const file = event.currentTarget.files?.[0];
|
|
20678
20947
|
if (file) session.selectFile(file);
|
|
@@ -20697,18 +20966,18 @@ function AiTryOnRoot({ dialogPortalContainer, language, onClose, processPhoto, p
|
|
|
20697
20966
|
]
|
|
20698
20967
|
}) : null;
|
|
20699
20968
|
return /*#__PURE__*/ jsxs(TryOnShell, {
|
|
20700
|
-
className: isMyFitsActive ? 'h-
|
|
20969
|
+
className: isMyFitsActive ? 'h-dvh max-h-dvh sm:h-230 sm:max-h-[calc(100svh-var(--spacing)*8)]' : void 0,
|
|
20701
20970
|
"data-ai-try-on-stage": stage,
|
|
20702
20971
|
children: [
|
|
20703
20972
|
/*#__PURE__*/ jsx_runtime_jsx(TryOnHeader, {
|
|
20704
|
-
badge: 'history' === stage ? null : t(getBadgeKey(stage)),
|
|
20973
|
+
badge: 'history' === stage || 'ready' !== activation.status ? null : t(getBadgeKey(stage)),
|
|
20705
20974
|
badgeTone: getBadgeTone(stage),
|
|
20706
20975
|
closeLabel: t('close'),
|
|
20707
|
-
leftSlot: leftSlot,
|
|
20708
|
-
middleSlot: middleSlot,
|
|
20976
|
+
leftSlot: 'ready' === activation.status ? leftSlot : null,
|
|
20977
|
+
middleSlot: 'ready' === activation.status ? middleSlot : null,
|
|
20709
20978
|
preferMiddleSlotFirst: 'result' === stage,
|
|
20710
20979
|
showCloseButton: showDialogCloseButton,
|
|
20711
|
-
onClose: session.requestClose
|
|
20980
|
+
onClose: 'ready' === activation.status ? session.requestClose : closeDialog
|
|
20712
20981
|
}),
|
|
20713
20982
|
/*#__PURE__*/ jsx_runtime_jsx("input", {
|
|
20714
20983
|
ref: fileInputRef,
|
|
@@ -20719,36 +20988,56 @@ function AiTryOnRoot({ dialogPortalContainer, language, onClose, processPhoto, p
|
|
|
20719
20988
|
}),
|
|
20720
20989
|
/*#__PURE__*/ jsxs(TryOnScreenTransition, {
|
|
20721
20990
|
children: [
|
|
20722
|
-
'
|
|
20723
|
-
|
|
20724
|
-
|
|
20725
|
-
|
|
20726
|
-
view: session.view
|
|
20727
|
-
}) : null,
|
|
20728
|
-
'history' === session.view.stage ? /*#__PURE__*/ jsx_runtime_jsx(HistoryScreen, {
|
|
20729
|
-
view: session.view
|
|
20730
|
-
}) : null,
|
|
20731
|
-
'confirm' === session.view.stage ? /*#__PURE__*/ jsx_runtime_jsx(ConfirmScreen, {
|
|
20732
|
-
view: session.view
|
|
20991
|
+
'loading' === activation.status ? /*#__PURE__*/ jsx_runtime_jsx(ActivationLoadingScreen, {}) : null,
|
|
20992
|
+
'unavailable' === activation.status ? /*#__PURE__*/ jsx_runtime_jsx(ActivationUnavailableScreen, {
|
|
20993
|
+
temporary: activation.temporary,
|
|
20994
|
+
onClose: closeDialog
|
|
20733
20995
|
}) : null,
|
|
20734
|
-
'
|
|
20735
|
-
|
|
20996
|
+
'error' === activation.status ? /*#__PURE__*/ jsx_runtime_jsx(ActivationErrorScreen, {
|
|
20997
|
+
onClose: closeDialog,
|
|
20998
|
+
onRetry: onRetryActivation
|
|
20736
20999
|
}) : null,
|
|
20737
|
-
'
|
|
20738
|
-
|
|
20739
|
-
|
|
20740
|
-
|
|
20741
|
-
|
|
20742
|
-
|
|
21000
|
+
'ready' !== activation.status ? null : /*#__PURE__*/ jsxs(Fragment, {
|
|
21001
|
+
children: [
|
|
21002
|
+
'start' === session.view.stage ? /*#__PURE__*/ jsx_runtime_jsx(StartScreen, {
|
|
21003
|
+
view: session.view
|
|
21004
|
+
}) : null,
|
|
21005
|
+
'intro' === session.view.stage ? /*#__PURE__*/ jsx_runtime_jsx(IntroScreen, {
|
|
21006
|
+
view: session.view
|
|
21007
|
+
}) : null,
|
|
21008
|
+
'history' === session.view.stage ? /*#__PURE__*/ jsx_runtime_jsx(HistoryScreen, {
|
|
21009
|
+
view: session.view
|
|
21010
|
+
}) : null,
|
|
21011
|
+
'confirm' === session.view.stage ? /*#__PURE__*/ jsx_runtime_jsx(ConfirmScreen, {
|
|
21012
|
+
view: session.view
|
|
21013
|
+
}) : null,
|
|
21014
|
+
'processing' === session.view.stage ? /*#__PURE__*/ jsx_runtime_jsx(ProcessingScreen, {
|
|
21015
|
+
view: session.view
|
|
21016
|
+
}) : null,
|
|
21017
|
+
'error' === session.view.stage ? /*#__PURE__*/ jsx_runtime_jsx(ErrorScreen, {
|
|
21018
|
+
view: session.view,
|
|
21019
|
+
onClose: session.requestClose
|
|
21020
|
+
}) : null,
|
|
21021
|
+
'result' === session.view.stage ? /*#__PURE__*/ jsx_runtime_jsx(ResultScreen, {
|
|
21022
|
+
view: session.view,
|
|
21023
|
+
showAddToCart: showAddToCart,
|
|
21024
|
+
onAddToCart: onAddToCart ? addToCart : void 0,
|
|
21025
|
+
addToCartPending: cartPending,
|
|
21026
|
+
addToCartError: cartError
|
|
21027
|
+
}) : null
|
|
21028
|
+
]
|
|
21029
|
+
})
|
|
20743
21030
|
]
|
|
20744
|
-
}, stage),
|
|
21031
|
+
}, 'ready' === activation.status ? stage : `activation-${activation.status}`),
|
|
20745
21032
|
session.comparison.open && /*#__PURE__*/ jsx_runtime_jsx(ComparisonDialog, {
|
|
20746
21033
|
productName: session.comparison.productName,
|
|
20747
21034
|
productPrice: session.comparison.productPrice,
|
|
20748
21035
|
productUrl: session.comparison.productUrl,
|
|
20749
21036
|
portalContainer: dialogPortalContainer ?? void 0,
|
|
20750
21037
|
result: session.comparison.result,
|
|
20751
|
-
|
|
21038
|
+
addToCartPending: cartPending,
|
|
21039
|
+
addToCartError: cartError,
|
|
21040
|
+
onAddToCart: showAddToCart ? onAddToCart ? addToCart : session.comparison.addToCart : void 0,
|
|
20752
21041
|
onClose: session.comparison.close,
|
|
20753
21042
|
onDeleteResult: session.comparison.deleteResult,
|
|
20754
21043
|
onSaveResult: session.comparison.saveResult,
|
|
@@ -20788,8 +21077,246 @@ function getBadgeTone(stage) {
|
|
|
20788
21077
|
configureNodeApiClient({
|
|
20789
21078
|
baseURL: "https://api.neurodyn.ai/"
|
|
20790
21079
|
});
|
|
20791
|
-
|
|
20792
|
-
|
|
21080
|
+
const SHOPIFY_REQUEST_TIMEOUT_MS = 30000;
|
|
21081
|
+
const SHOPIFY_UPLOAD_TIMEOUT_MS = 300000;
|
|
21082
|
+
const SHOPPER_UPLOAD_COUNT = 1;
|
|
21083
|
+
const MAX_ERROR_BODY = 2000;
|
|
21084
|
+
class ShopifyTryOnClient {
|
|
21085
|
+
createObjectUrl;
|
|
21086
|
+
operations;
|
|
21087
|
+
signal;
|
|
21088
|
+
session = null;
|
|
21089
|
+
constructor({ createObjectUrl = shopify_try_on_client_defaultCreateObjectUrl, operations, signal }){
|
|
21090
|
+
this.createObjectUrl = createObjectUrl;
|
|
21091
|
+
this.operations = operations;
|
|
21092
|
+
this.signal = signal;
|
|
21093
|
+
}
|
|
21094
|
+
async activate(target) {
|
|
21095
|
+
const response = await this.run(()=>this.operations.activate(target, this.baseConfig()));
|
|
21096
|
+
const { activation, session } = normalizeShopifyActivation(response);
|
|
21097
|
+
if (session) this.session = createWidgetSessionClient(session, {
|
|
21098
|
+
apiConfig: this.baseConfig(),
|
|
21099
|
+
issueWidgetToken: this.operations.issueWidgetToken
|
|
21100
|
+
});
|
|
21101
|
+
return activation;
|
|
21102
|
+
}
|
|
21103
|
+
createProcessPhoto(target) {
|
|
21104
|
+
return async (photo, context)=>{
|
|
21105
|
+
context?.reportProgress(4);
|
|
21106
|
+
const source = await resolveAiTryOnSourceBlob(photo);
|
|
21107
|
+
if (!source || source.size <= 0) throw new AiTryOnGenerationError('photo-required');
|
|
21108
|
+
context?.reportProgress(18);
|
|
21109
|
+
const [upload] = resolveUploads(await this.run(async ()=>this.operations.requestUploadUrls(SHOPPER_UPLOAD_COUNT, await this.requestConfig())), SHOPPER_UPLOAD_COUNT);
|
|
21110
|
+
context?.reportProgress(38);
|
|
21111
|
+
const uploadOptions = {
|
|
21112
|
+
signal: this.signal,
|
|
21113
|
+
timeoutMs: SHOPIFY_UPLOAD_TIMEOUT_MS
|
|
21114
|
+
};
|
|
21115
|
+
await this.run(()=>this.operations.uploadPhoto ? this.operations.uploadPhoto(upload.uploadUrl, source, uploadOptions) : uploadImageToPresignedUrl(upload.uploadUrl, source, uploadOptions));
|
|
21116
|
+
context?.reportProgress(58);
|
|
21117
|
+
const stopGenerationProgress = startGenerationProgress(context?.reportProgress);
|
|
21118
|
+
let response;
|
|
21119
|
+
try {
|
|
21120
|
+
response = await this.run(async ()=>this.operations.generate({
|
|
21121
|
+
...target,
|
|
21122
|
+
personImageKey: upload.key
|
|
21123
|
+
}, await this.requestConfig({
|
|
21124
|
+
responseType: 'blob',
|
|
21125
|
+
timeout: 90000
|
|
21126
|
+
})));
|
|
21127
|
+
} finally{
|
|
21128
|
+
stopGenerationProgress();
|
|
21129
|
+
}
|
|
21130
|
+
const { blob, errorText } = await readGeneratedImage(response);
|
|
21131
|
+
if (!blob) throw toGenerationError(errorText);
|
|
21132
|
+
context?.reportProgress(98);
|
|
21133
|
+
const src = this.createObjectUrl(blob);
|
|
21134
|
+
if (!src) throw new AiTryOnGenerationError('service');
|
|
21135
|
+
return {
|
|
21136
|
+
before: photo,
|
|
21137
|
+
after: {
|
|
21138
|
+
blob,
|
|
21139
|
+
id: `${photo.id}-shopify-result`,
|
|
21140
|
+
name: photo.name ? `${photo.name} result` : 'AI try-on result',
|
|
21141
|
+
size: blob.size,
|
|
21142
|
+
src,
|
|
21143
|
+
type: blob.type || 'image/jpeg'
|
|
21144
|
+
}
|
|
21145
|
+
};
|
|
21146
|
+
};
|
|
21147
|
+
}
|
|
21148
|
+
async run(operation) {
|
|
21149
|
+
try {
|
|
21150
|
+
return await operation();
|
|
21151
|
+
} catch (error) {
|
|
21152
|
+
if (error instanceof AiTryOnGenerationError || isCanceled(error, this.signal)) throw error;
|
|
21153
|
+
throw isTimeout(error) ? new AiTryOnGenerationError('timeout') : toGenerationError(await readRejectionBody(error));
|
|
21154
|
+
}
|
|
21155
|
+
}
|
|
21156
|
+
sendWidgetEvent(event) {
|
|
21157
|
+
const send = this.operations.sendWidgetEvent;
|
|
21158
|
+
if (!send) return;
|
|
21159
|
+
this.requestConfig().then((config)=>send({
|
|
21160
|
+
event,
|
|
21161
|
+
timestamp: new Date().toISOString()
|
|
21162
|
+
}, config)).catch(()=>{});
|
|
21163
|
+
}
|
|
21164
|
+
async requestUploadUrls() {
|
|
21165
|
+
throw new Error('Shopify generation handles its own uploads');
|
|
21166
|
+
}
|
|
21167
|
+
async generateTryOn() {
|
|
21168
|
+
throw new Error('Shopify generation runs through its own processor');
|
|
21169
|
+
}
|
|
21170
|
+
baseConfig() {
|
|
21171
|
+
return {
|
|
21172
|
+
signal: this.signal,
|
|
21173
|
+
timeout: SHOPIFY_REQUEST_TIMEOUT_MS
|
|
21174
|
+
};
|
|
21175
|
+
}
|
|
21176
|
+
async requestConfig(config = {}) {
|
|
21177
|
+
const merged = {
|
|
21178
|
+
...this.baseConfig(),
|
|
21179
|
+
...config
|
|
21180
|
+
};
|
|
21181
|
+
return this.session ? this.session.getApiConfigForRequest(merged) : merged;
|
|
21182
|
+
}
|
|
21183
|
+
}
|
|
21184
|
+
function createShopifyTryOnClient(options) {
|
|
21185
|
+
return new ShopifyTryOnClient(options);
|
|
21186
|
+
}
|
|
21187
|
+
function normalizeShopifyActivation(response) {
|
|
21188
|
+
const body = shopify_try_on_client_isRecord(response) ? response : {};
|
|
21189
|
+
const zoneType = 'string' == typeof body.zoneType ? body.zoneType : void 0;
|
|
21190
|
+
const photoUrl = shopify_try_on_client_normalizeString(body.photoUrl) || void 0;
|
|
21191
|
+
const session = normalizeWidgetSession(body.widgetSession);
|
|
21192
|
+
const hasSource = void 0 !== photoUrl;
|
|
21193
|
+
const available = true === body.available && null !== session && hasSource;
|
|
21194
|
+
const reason = available ? void 0 : readReason(body.unavailableReason) ?? (true !== body.available || hasSource ? void 0 : 'no_product_photo');
|
|
21195
|
+
return {
|
|
21196
|
+
activation: {
|
|
21197
|
+
available,
|
|
21198
|
+
photoUrl,
|
|
21199
|
+
reason,
|
|
21200
|
+
zoneType: isProductType(zoneType) ? zoneType : void 0
|
|
21201
|
+
},
|
|
21202
|
+
session: available ? session : null
|
|
21203
|
+
};
|
|
21204
|
+
}
|
|
21205
|
+
const UNAVAILABLE_REASONS = new Set([
|
|
21206
|
+
'not_enabled',
|
|
21207
|
+
'zone_not_configured',
|
|
21208
|
+
'no_product_photo',
|
|
21209
|
+
'tokens_exhausted'
|
|
21210
|
+
]);
|
|
21211
|
+
function readReason(value) {
|
|
21212
|
+
return 'string' == typeof value && UNAVAILABLE_REASONS.has(value) ? value : void 0;
|
|
21213
|
+
}
|
|
21214
|
+
function toGenerationError(errorText) {
|
|
21215
|
+
if (!errorText) return new AiTryOnGenerationError('service');
|
|
21216
|
+
try {
|
|
21217
|
+
const body = JSON.parse(errorText);
|
|
21218
|
+
const code = shopify_try_on_client_isRecord(body) ? body.code : void 0;
|
|
21219
|
+
if ('tokens' === code || 'product' === code || 'timeout' === code || 'service' === code) return new AiTryOnGenerationError(code);
|
|
21220
|
+
} catch {}
|
|
21221
|
+
return new AiTryOnGenerationError('service');
|
|
21222
|
+
}
|
|
21223
|
+
function normalizeWidgetSession(value) {
|
|
21224
|
+
if (!shopify_try_on_client_isRecord(value)) return null;
|
|
21225
|
+
const token = shopify_try_on_client_normalizeString(value.token);
|
|
21226
|
+
const sid = shopify_try_on_client_normalizeString(value.sid);
|
|
21227
|
+
const expiresIn = 'number' == typeof value.expiresIn ? value.expiresIn : NaN;
|
|
21228
|
+
return token && sid && Number.isFinite(expiresIn) && expiresIn > 0 ? {
|
|
21229
|
+
expiresIn,
|
|
21230
|
+
sid,
|
|
21231
|
+
token
|
|
21232
|
+
} : null;
|
|
21233
|
+
}
|
|
21234
|
+
function isCanceled(error, signal) {
|
|
21235
|
+
return signal?.aborted === true || 'AbortError' === errorName(error) || 'ERR_CANCELED' === shopify_try_on_client_errorCode(error);
|
|
21236
|
+
}
|
|
21237
|
+
function isTimeout(error) {
|
|
21238
|
+
return 'TimeoutError' === errorName(error) || 'ECONNABORTED' === shopify_try_on_client_errorCode(error) || 'ETIMEDOUT' === shopify_try_on_client_errorCode(error);
|
|
21239
|
+
}
|
|
21240
|
+
function errorName(error) {
|
|
21241
|
+
return shopify_try_on_client_isRecord(error) && 'string' == typeof error.name ? error.name : '';
|
|
21242
|
+
}
|
|
21243
|
+
function shopify_try_on_client_errorCode(error) {
|
|
21244
|
+
return shopify_try_on_client_isRecord(error) && 'string' == typeof error.code ? error.code : '';
|
|
21245
|
+
}
|
|
21246
|
+
async function readRejectionBody(error) {
|
|
21247
|
+
const response = shopify_try_on_client_isRecord(error) ? error.response : void 0;
|
|
21248
|
+
const data = shopify_try_on_client_isRecord(response) ? response.data : void 0;
|
|
21249
|
+
if (data instanceof Blob) return (await data.text().catch(()=>'')).slice(0, MAX_ERROR_BODY) || null;
|
|
21250
|
+
if ('string' == typeof data) return data.slice(0, MAX_ERROR_BODY) || null;
|
|
21251
|
+
if (shopify_try_on_client_isRecord(data)) return JSON.stringify(data).slice(0, MAX_ERROR_BODY);
|
|
21252
|
+
return null;
|
|
21253
|
+
}
|
|
21254
|
+
function shopify_try_on_client_isRecord(value) {
|
|
21255
|
+
return null !== value && 'object' == typeof value;
|
|
21256
|
+
}
|
|
21257
|
+
function shopify_try_on_client_normalizeString(value) {
|
|
21258
|
+
return 'string' == typeof value ? value.trim() : '';
|
|
21259
|
+
}
|
|
21260
|
+
function shopify_try_on_client_defaultCreateObjectUrl(blob) {
|
|
21261
|
+
return 'function' == typeof URL.createObjectURL ? URL.createObjectURL(blob) : '';
|
|
21262
|
+
}
|
|
21263
|
+
const SHOPIFY_PRODUCT_TYPE = 'ai-try-on-shopify';
|
|
21264
|
+
function createProxyRequest(baseURL) {
|
|
21265
|
+
const instance = axios.create({
|
|
21266
|
+
baseURL,
|
|
21267
|
+
timeout: SHOPIFY_REQUEST_TIMEOUT_MS
|
|
21268
|
+
});
|
|
21269
|
+
return async function(config) {
|
|
21270
|
+
const response = await instance.request(config);
|
|
21271
|
+
return {
|
|
21272
|
+
data: response.data,
|
|
21273
|
+
headers: response.headers,
|
|
21274
|
+
status: response.status,
|
|
21275
|
+
statusText: response.statusText
|
|
21276
|
+
};
|
|
21277
|
+
};
|
|
21278
|
+
}
|
|
21279
|
+
function createAppProxyOperations(basePath) {
|
|
21280
|
+
const mount = basePath.trim().replace(/\/+$/, '');
|
|
21281
|
+
if (!mount) return;
|
|
21282
|
+
const client = createProxyRequest(mount);
|
|
21283
|
+
return {
|
|
21284
|
+
activate: ({ productId, variantId }, config)=>tryOnControllerActivateTryOn({
|
|
21285
|
+
product_id: productId,
|
|
21286
|
+
variant_id: variantId,
|
|
21287
|
+
productType: SHOPIFY_PRODUCT_TYPE
|
|
21288
|
+
}, {
|
|
21289
|
+
...config,
|
|
21290
|
+
client
|
|
21291
|
+
}),
|
|
21292
|
+
generate: (data, config)=>tryOnControllerGenerateShopifyTryOn(data, {
|
|
21293
|
+
...config,
|
|
21294
|
+
client
|
|
21295
|
+
}),
|
|
21296
|
+
issueWidgetToken: (data, config)=>publicApiControllerIssueWidgetToken(data, {
|
|
21297
|
+
...config,
|
|
21298
|
+
client
|
|
21299
|
+
}),
|
|
21300
|
+
requestUploadUrls: (count, config)=>tryOnControllerCreateUploadUrls({
|
|
21301
|
+
count
|
|
21302
|
+
}, {
|
|
21303
|
+
...config,
|
|
21304
|
+
client
|
|
21305
|
+
}),
|
|
21306
|
+
sendWidgetEvent: (data, config)=>widgetEventsControllerCreateWidgetEvent(data, {
|
|
21307
|
+
...config,
|
|
21308
|
+
client
|
|
21309
|
+
})
|
|
21310
|
+
};
|
|
21311
|
+
}
|
|
21312
|
+
const LOADING_ACTIVATION = {
|
|
21313
|
+
status: 'loading'
|
|
21314
|
+
};
|
|
21315
|
+
const src_READY_ACTIVATION = {
|
|
21316
|
+
status: 'ready'
|
|
21317
|
+
};
|
|
21318
|
+
function AiTryOnDialog({ defaultOpen = false, language, onOpenChange, open, productImageSrc, productName, productPrice, productType, productUrl, apiKey, externalGeneration, externalActivation, externalAddToCart, externalClient, ...props }) {
|
|
21319
|
+
if (!externalGeneration) assertProductType(productType);
|
|
20793
21320
|
const i18n = (0, __rspack_external_react.useMemo)(()=>createAiTryOnI18n(language), [
|
|
20794
21321
|
language
|
|
20795
21322
|
]);
|
|
@@ -20799,20 +21326,58 @@ function AiTryOnDialog({ defaultOpen = false, language, onOpenChange, open, prod
|
|
|
20799
21326
|
const isControlled = void 0 !== open;
|
|
20800
21327
|
const currentOpen = isControlled ? open : internalOpen;
|
|
20801
21328
|
const normalizedApiKey = normalizeApiKey(apiKey);
|
|
20802
|
-
const tryOnClient = (0, __rspack_external_react.useMemo)(()=>createTryOnClient({
|
|
21329
|
+
const tryOnClient = (0, __rspack_external_react.useMemo)(()=>externalGeneration ? externalClient ?? externalTryOnClient : createTryOnClient({
|
|
20803
21330
|
apiKey: normalizedApiKey
|
|
20804
21331
|
}), [
|
|
20805
|
-
normalizedApiKey
|
|
21332
|
+
normalizedApiKey,
|
|
21333
|
+
externalGeneration,
|
|
21334
|
+
externalClient
|
|
21335
|
+
]);
|
|
21336
|
+
const [externalActivationState, setExternalActivationState] = (0, __rspack_external_react.useState)(LOADING_ACTIVATION);
|
|
21337
|
+
const [activationAttempt, setActivationAttempt] = (0, __rspack_external_react.useState)(0);
|
|
21338
|
+
const activation = externalActivation ? externalActivationState : src_READY_ACTIVATION;
|
|
21339
|
+
const activationRef = (0, __rspack_external_react.useRef)(externalActivation);
|
|
21340
|
+
const hasActivation = Boolean(externalActivation);
|
|
21341
|
+
(0, __rspack_external_react.useEffect)(()=>{
|
|
21342
|
+
activationRef.current = externalActivation;
|
|
21343
|
+
});
|
|
21344
|
+
(0, __rspack_external_react.useEffect)(()=>{
|
|
21345
|
+
const activate = activationRef.current;
|
|
21346
|
+
if (!activate) return;
|
|
21347
|
+
setExternalActivationState(LOADING_ACTIVATION);
|
|
21348
|
+
if (!currentOpen) return;
|
|
21349
|
+
let cancelled = false;
|
|
21350
|
+
activate().then((result)=>{
|
|
21351
|
+
if (!cancelled) setExternalActivationState(result.enabled ? {
|
|
21352
|
+
...result,
|
|
21353
|
+
status: 'ready'
|
|
21354
|
+
} : {
|
|
21355
|
+
status: 'unavailable',
|
|
21356
|
+
temporary: result.temporarilyUnavailable
|
|
21357
|
+
});
|
|
21358
|
+
}).catch(()=>{
|
|
21359
|
+
if (!cancelled) setExternalActivationState({
|
|
21360
|
+
status: 'error'
|
|
21361
|
+
});
|
|
21362
|
+
});
|
|
21363
|
+
return ()=>{
|
|
21364
|
+
cancelled = true;
|
|
21365
|
+
};
|
|
21366
|
+
}, [
|
|
21367
|
+
activationAttempt,
|
|
21368
|
+
currentOpen,
|
|
21369
|
+
hasActivation
|
|
20806
21370
|
]);
|
|
20807
21371
|
(0, __rspack_external_react.useEffect)(()=>{
|
|
20808
21372
|
if (!currentOpen) {
|
|
20809
21373
|
hasTrackedOpen.current = false;
|
|
20810
21374
|
return;
|
|
20811
21375
|
}
|
|
20812
|
-
if (hasTrackedOpen.current) return;
|
|
21376
|
+
if ('ready' !== activation.status || hasTrackedOpen.current) return;
|
|
20813
21377
|
hasTrackedOpen.current = true;
|
|
20814
21378
|
tryOnClient.sendWidgetEvent('widget_opened');
|
|
20815
21379
|
}, [
|
|
21380
|
+
activation.status,
|
|
20816
21381
|
currentOpen,
|
|
20817
21382
|
tryOnClient
|
|
20818
21383
|
]);
|
|
@@ -20831,6 +21396,9 @@ function AiTryOnDialog({ defaultOpen = false, language, onOpenChange, open, prod
|
|
|
20831
21396
|
zIndex: 2147483647
|
|
20832
21397
|
},
|
|
20833
21398
|
children: [
|
|
21399
|
+
/*#__PURE__*/ jsx_runtime_jsx("style", {
|
|
21400
|
+
children: SHADOW_HOST_VISIBILITY_CSS
|
|
21401
|
+
}),
|
|
20834
21402
|
/*#__PURE__*/ jsx_runtime_jsx("style", {
|
|
20835
21403
|
children: shadowRootCssText
|
|
20836
21404
|
}),
|
|
@@ -20853,6 +21421,10 @@ function AiTryOnDialog({ defaultOpen = false, language, onOpenChange, open, prod
|
|
|
20853
21421
|
productType: productType,
|
|
20854
21422
|
productUrl: productUrl,
|
|
20855
21423
|
tryOnClient: tryOnClient,
|
|
21424
|
+
externalGeneration: externalGeneration,
|
|
21425
|
+
externalAddToCart: externalAddToCart,
|
|
21426
|
+
activation: activation,
|
|
21427
|
+
onRetryActivation: ()=>setActivationAttempt((attempt)=>attempt + 1),
|
|
20856
21428
|
onRequestClose: ()=>handleOpenChange(false),
|
|
20857
21429
|
onOpenChange: handleOpenChange,
|
|
20858
21430
|
...props
|
|
@@ -20864,12 +21436,12 @@ function AiTryOnDialog({ defaultOpen = false, language, onOpenChange, open, prod
|
|
|
20864
21436
|
]
|
|
20865
21437
|
});
|
|
20866
21438
|
}
|
|
20867
|
-
function AiTryOnDialogContent({ language, onRequestClose, portalContainer, productImageSrc, productName, productPrice, productType, productUrl, tryOnClient, ...props }) {
|
|
21439
|
+
function AiTryOnDialogContent({ language, onRequestClose, portalContainer, productImageSrc, productName, productPrice, productType, productUrl, tryOnClient, externalGeneration, externalAddToCart, activation, onRetryActivation, ...props }) {
|
|
20868
21440
|
const { t } = useTranslation();
|
|
20869
21441
|
return /*#__PURE__*/ jsx_runtime_jsx(Dialog, {
|
|
20870
21442
|
...props,
|
|
20871
21443
|
children: /*#__PURE__*/ jsxs(DialogContent, {
|
|
20872
|
-
className: "inset-x-0 bottom-0 top-auto block max-h-
|
|
21444
|
+
className: "inset-x-0 bottom-0 top-auto block max-h-dvh w-svw max-w-none translate-x-0 translate-y-0 gap-0 overflow-hidden rounded-t-3xl rounded-b-none p-0 sm:inset-x-auto sm:right-4 sm:bottom-4 sm:left-auto sm:max-h-230 sm:w-120 sm:max-w-120 sm:rounded-3xl sm:p-0",
|
|
20873
21445
|
contentInsideOverlay: true,
|
|
20874
21446
|
portalContainer: portalContainer,
|
|
20875
21447
|
showCloseButton: false,
|
|
@@ -20891,12 +21463,17 @@ function AiTryOnDialogContent({ language, onRequestClose, portalContainer, produ
|
|
|
20891
21463
|
/*#__PURE__*/ jsx_runtime_jsx(AiTryOnRoot, {
|
|
20892
21464
|
dialogPortalContainer: portalContainer,
|
|
20893
21465
|
language: language,
|
|
20894
|
-
productImageSrc: productImageSrc,
|
|
21466
|
+
productImageSrc: 'ready' === activation.status && activation.productImageSrc || productImageSrc,
|
|
20895
21467
|
productName: productName,
|
|
20896
21468
|
productPrice: productPrice,
|
|
20897
|
-
productType: productType,
|
|
21469
|
+
productType: 'ready' === activation.status ? activation.productType ?? productType : productType,
|
|
20898
21470
|
productUrl: productUrl,
|
|
20899
21471
|
tryOnClient: tryOnClient,
|
|
21472
|
+
processPhoto: externalGeneration,
|
|
21473
|
+
activation: activation,
|
|
21474
|
+
onRetryActivation: onRetryActivation,
|
|
21475
|
+
onAddToCart: externalAddToCart,
|
|
21476
|
+
showAddToCart: !externalGeneration || Boolean(externalAddToCart),
|
|
20900
21477
|
showDialogCloseButton: true,
|
|
20901
21478
|
onClose: onRequestClose
|
|
20902
21479
|
})
|
|
@@ -20904,4 +21481,4 @@ function AiTryOnDialogContent({ language, onRequestClose, portalContainer, produ
|
|
|
20904
21481
|
})
|
|
20905
21482
|
});
|
|
20906
21483
|
}
|
|
20907
|
-
export { AiTryOnDialog, PRODUCT_TYPES, isProductType };
|
|
21484
|
+
export { AiTryOnDialog, AiTryOnGenerationError, PRODUCT_TYPES, SHOPIFY_REQUEST_TIMEOUT_MS, createAppProxyOperations, createShopifyTryOnClient, isProductType, uploadImageToPresignedUrl };
|