@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.
Files changed (3) hide show
  1. package/dist/index.d.ts +280 -10
  2. package/dist/index.js +737 -160
  3. package/package.json +2 -1
package/dist/index.d.ts CHANGED
@@ -2,34 +2,179 @@ import * as DialogPrimitive from '@radix-ui/react-dialog';
2
2
  import { JSX } from 'react/jsx-runtime';
3
3
  import * as React_2 from 'react';
4
4
 
5
- export declare function AiTryOnDialog({ defaultOpen, language, onOpenChange, open, productImageSrc, productName, productPrice, productType, productUrl, apiKey, ...props }: AiTryOnDialogProps): JSX.Element;
5
+ export declare type AiTryOnActivationState = {
6
+ status: 'loading';
7
+ } | ({
8
+ status: 'ready';
9
+ } & Partial<AiTryOnExternalActivation>) | {
10
+ status: 'unavailable';
11
+ temporary?: boolean;
12
+ } | {
13
+ status: 'error';
14
+ };
15
+
16
+ export declare function AiTryOnDialog({ defaultOpen, language, onOpenChange, open, productImageSrc, productName, productPrice, productType, productUrl, apiKey, externalGeneration, externalActivation, externalAddToCart, externalClient, ...props }: AiTryOnDialogProps): JSX.Element;
6
17
 
7
18
  declare interface AiTryOnDialogOwnProps extends Omit<DialogProps, 'children' | 'modal'> {
8
19
  /** UI language override. */
9
20
  language?: AiTryOnLanguage;
10
- /** Product image displayed in AI try-on dialog UI and sent to AI try-on generation. */
21
+ /** Display image; only the ordinary transport sends it to generation. External mode uses it as a preview. */
11
22
  productImageSrc: string;
12
23
  /** Product name for product preview alt text and product page CTA. */
13
24
  productName: string;
14
25
  /** Optional already-formatted product price shown in Add-to-Cart CTAs. */
15
26
  productPrice?: string;
16
- /** Canonical product category sent unchanged to AI try-on generation. */
17
- productType: ProductType;
18
27
  /** Product page URL opened from the result product CTA. */
19
28
  productUrl?: string;
20
- /** Optional merchant API key. A non-empty key selects Public Use; its absence selects Internal Use. */
29
+ }
30
+
31
+ export declare type AiTryOnDialogProps = AiTryOnDialogOwnProps & ({
32
+ productType: ProductType;
21
33
  apiKey?: string;
34
+ externalGeneration?: never;
35
+ externalActivation?: never;
36
+ externalClient?: never;
37
+ externalAddToCart?: never;
38
+ } | AiTryOnExternalProps);
39
+
40
+ /** What an external integration resolves before the shopper may upload a photo. */
41
+ export declare interface AiTryOnExternalActivation {
42
+ /** The integration's backend is the only authority on availability. */
43
+ enabled: boolean;
44
+ /** Set when the block should lift on its own, so the shopper is told to return. */
45
+ temporarilyUnavailable?: boolean;
46
+ /** Drives the existing photo guidance; absent means generic guidance. */
47
+ productType?: ProductType;
48
+ /** Replaces the storefront preview with the photo generation actually uses. */
49
+ productImageSrc?: string;
22
50
  }
23
51
 
24
- export declare interface AiTryOnDialogProps extends AiTryOnDialogOwnProps {
52
+ /** External integrations may resolve product data before the shopper may act. */
53
+ declare interface AiTryOnExternalProps {
54
+ externalGeneration: AiTryOnProcessPhoto;
55
+ /** Runs on every opening; until it resolves the dialog shows a loading screen. */
56
+ externalActivation?: () => Promise<AiTryOnExternalActivation>;
57
+ /** Transport for the open event. Without it external mode stays analytics-free. */
58
+ externalClient?: AiTryOnTransportClient;
59
+ /**
60
+ * Really adds the tried-on item to the host's cart. Supplying it brings the cart
61
+ * button back for external hosts, which otherwise only have a button that reports
62
+ * the intent and adds nothing.
63
+ */
64
+ externalAddToCart?: () => Promise<void> | void;
65
+ /** Optional fallback category; activation overrides it. */
66
+ productType?: ProductType;
67
+ apiKey?: never;
25
68
  }
26
69
 
70
+ export declare class AiTryOnGenerationError extends Error {
71
+ readonly code: AiTryOnGenerationErrorCode;
72
+ constructor(code: AiTryOnGenerationErrorCode);
73
+ }
74
+
75
+ /** Safe, transport-independent failures that may be presented to the shopper. */
76
+ export declare type AiTryOnGenerationErrorCode = 'tokens' | 'product' | 'service' | 'timeout' | 'photo-required';
77
+
27
78
  declare type AiTryOnLanguage = keyof typeof resources;
28
79
 
80
+ export declare interface AiTryOnPhoto {
81
+ blob?: Blob;
82
+ file?: File;
83
+ id: string;
84
+ lastModified?: number;
85
+ name?: string;
86
+ size?: number;
87
+ src: string;
88
+ type?: string;
89
+ }
90
+
91
+ declare interface AiTryOnProcessingContext {
92
+ reportProgress: (progress: number) => void;
93
+ }
94
+
95
+ export declare type AiTryOnProcessPhoto = (photo: AiTryOnPhoto, context?: AiTryOnProcessingContext) => Promise<AiTryOnResult>;
96
+
97
+ export declare interface AiTryOnResult {
98
+ after: AiTryOnPhoto;
99
+ before: AiTryOnPhoto;
100
+ createdAt?: number;
101
+ id?: string;
102
+ productImageSrc?: string;
103
+ productName?: string;
104
+ productPrice?: string;
105
+ productUrl?: string;
106
+ }
107
+
108
+ /**
109
+ * Transport-agnostic AI try-on client.
110
+ *
111
+ * Public Use and Internal Use each provide one implementation. The transport is
112
+ * selected once, before the first request, from the normalized API key
113
+ * (see {@link normalizeApiKey}); individual operations never choose their own
114
+ * endpoint. Per ADR-0002, a Public Use failure never falls back to Internal Use,
115
+ * and Internal Use never activates a Public Widget Session or emits Public
116
+ * Widget Analytics.
117
+ */
118
+ declare interface AiTryOnTransportClient {
119
+ /** Request presigned upload URLs for the shopper and product images. */
120
+ requestUploadUrls: (count: number) => Promise<unknown>;
121
+ /** Call the unified synchronous try-on generation operation. */
122
+ generateTryOn: (data: TryOnGenerationRequest, options?: TryOnGenerationOptions) => Promise<unknown>;
123
+ /** Fire-and-forget Public Widget Analytics; a no-op in Internal Use. */
124
+ sendWidgetEvent: (event: AiTryOnWidgetEventName) => void;
125
+ }
126
+
127
+ declare interface AiTryOnUploadOptions {
128
+ fetch?: typeof fetch;
129
+ signal?: AbortSignal;
130
+ /** Omitted by the ordinary processor, which has never bounded its uploads. */
131
+ timeoutMs?: number;
132
+ }
133
+
134
+ declare type AiTryOnWidgetEventName = 'widget_opened' | 'add_to_cart_from_widget';
135
+
136
+ /**
137
+ * Production operations for a storefront, or none without a mount.
138
+ *
139
+ * `basePath` is the App Proxy mount — `/apps/<subpath>` — and is supplied by the
140
+ * browser bundle rather than read from `import.meta.env` here: this package's
141
+ * build does not substitute `PUBLIC_*` vars, so a read at this level compiles to
142
+ * an empty string. Kept root-relative so it resolves against the shop's own
143
+ * domain from any product URL, including localised ones.
144
+ */
145
+ export declare function createAppProxyOperations(basePath: string): ShopifyTryOnOperations | undefined;
146
+
147
+ export declare function createShopifyTryOnClient(options: ShopifyTryOnClientOptions): ShopifyTryOnClient;
148
+
29
149
  declare type DialogProps = React_2.ComponentProps<typeof DialogPrimitive.Root>
30
150
 
31
151
  export declare function isProductType(value: unknown): value is ProductType;
32
152
 
153
+ declare type IssueWidgetToken = (data: {
154
+ token?: string;
155
+ }, config?: WidgetSessionApiConfig) => Promise<IssueWidgetTokenResponseDtoOutput>;
156
+
157
+ /**
158
+ * Generated by Kubb (https://kubb.dev/).
159
+ * Do not edit manually.
160
+ */
161
+ declare type IssueWidgetTokenResponseDtoOutput = {
162
+ /**
163
+ * @type string
164
+ */
165
+ token: string;
166
+ /**
167
+ * @type string
168
+ */
169
+ sid: string;
170
+ /**
171
+ * @minLength -9007199254740991
172
+ * @maxLength 9007199254740991
173
+ * @type integer
174
+ */
175
+ expiresIn: number;
176
+ };
177
+
33
178
  export declare const PRODUCT_TYPES: readonly ["full-body", "upper-body", "lower-body", "footwear", "headwear", "eyewear", "neck", "wrist", "bag", "waist"];
34
179
 
35
180
  export declare type ProductType = typeof PRODUCT_TYPES[number];
@@ -37,6 +182,16 @@ export declare type ProductType = typeof PRODUCT_TYPES[number];
37
182
  declare const resources: {
38
183
  readonly en: {
39
184
  readonly ns1: {
185
+ readonly activationLoading: "Checking this product…";
186
+ readonly activationLoadingSubtitle: "One moment while the store confirms try-on for this item.";
187
+ readonly activationTemporarilyUnavailable: "Try-on is paused for this store right now. Please check back later.";
188
+ readonly activationUnavailableTitle: "Try-on is unavailable";
189
+ readonly externalPhotoRequired: "Please upload your photo again.";
190
+ readonly externalNoTokens: "The store has no tokens remaining.";
191
+ readonly externalProductUnavailable: "Try-on is unavailable for this product.";
192
+ readonly externalServiceUnavailable: "Try-on is unavailable. Please try again later.";
193
+ readonly externalTimeout: "The request timed out. Please try again.";
194
+ readonly externalPhotoHint: "Upload a clear photo of yourself. The store chooses the item used for your try-on.";
40
195
  readonly back: "Back";
41
196
  readonly badge: "Try it on";
42
197
  readonly confirmBadge: "Almost there";
@@ -126,7 +281,7 @@ declare const resources: {
126
281
  readonly startUpload: "Choose your photo";
127
282
  readonly termsJoiner: "&";
128
283
  readonly termsLink: "Terms";
129
- readonly termsPrefix: "By using this service, your agree to";
284
+ readonly termsPrefix: "By using this service, you agree to";
130
285
  readonly privacyLink: "privacy Policy";
131
286
  readonly upperBodyIntroBadArmsCrossed: "Arms crossed hide the fit";
132
287
  readonly upperBodyIntroBadObjectBlockingTorso: "Object blocks the torso";
@@ -150,6 +305,16 @@ declare const resources: {
150
305
  };
151
306
  readonly ru: {
152
307
  readonly ns1: {
308
+ readonly activationLoading: "Проверяем товар…";
309
+ readonly activationLoadingSubtitle: "Магазин подтверждает примерку для этого товара.";
310
+ readonly activationTemporarilyUnavailable: "Примерка в этом магазине сейчас приостановлена. Загляните позже.";
311
+ readonly activationUnavailableTitle: "Примерка недоступна";
312
+ readonly externalPhotoRequired: "Загрузите фото заново.";
313
+ readonly externalNoTokens: "У магазина закончились токены.";
314
+ readonly externalProductUnavailable: "Примерка для этого товара недоступна.";
315
+ readonly externalServiceUnavailable: "Примерка недоступна. Попробуйте позже.";
316
+ readonly externalTimeout: "Время ожидания истекло. Попробуйте снова.";
317
+ readonly externalPhotoHint: "Загрузите чёткое фото себя. Изображение товара для примерки определяется настройками магазина.";
153
318
  readonly back: "Назад";
154
319
  readonly badge: "Примерить";
155
320
  readonly confirmBadge: "Почти готово";
@@ -206,7 +371,7 @@ declare const resources: {
206
371
  readonly headwearIntroTitle: "Загрузите фото головы и плеч";
207
372
  readonly introCta: "Продолжить";
208
373
  readonly introBadge: "Совет";
209
- readonly myFits: "My fits";
374
+ readonly myFits: "Мои образы";
210
375
  readonly lowerBodyIntroBadCoveredWaist: "Свободная одежда скрывает линию талии";
211
376
  readonly lowerBodyIntroBadSitting: "В сидячей позе ноги согнуты";
212
377
  readonly lowerBodyIntroGoodFullBody: "Фото в полный рост";
@@ -231,8 +396,8 @@ declare const resources: {
231
396
  readonly resultReset: "Сохранить";
232
397
  readonly resultShare: "Поделиться";
233
398
  readonly resultTitle: "Вот как это выглядит на вас";
234
- readonly resultTryAnother: "Add To Cart";
235
- readonly resultTryAnotherWithPrice: "Add To Cart – {{price}}";
399
+ readonly resultTryAnother: "Добавить в корзину";
400
+ readonly resultTryAnotherWithPrice: "Добавить в корзину – {{price}}";
236
401
  readonly share: "Поделиться";
237
402
  readonly startHistory: "Смотреть прошлые результаты";
238
403
  readonly startTitle: "Хотите увидеть это на себе?";
@@ -263,4 +428,109 @@ declare const resources: {
263
428
  };
264
429
  };
265
430
 
431
+ /** The isolated client does not inherit the shared client's default, so it is set here. */
432
+ export declare const SHOPIFY_REQUEST_TIMEOUT_MS = 30000;
433
+
434
+ export declare interface ShopifyActivation {
435
+ /** The backend decides availability; the frontend only refuses a malformed answer. */
436
+ available: boolean;
437
+ reason?: ShopifyUnavailableReason;
438
+ /** Canonical zone slug, when the backend resolved one. Drives the photo guidance. */
439
+ zoneType?: ProductType;
440
+ /** The photo the backend generates from, replacing the storefront preview. */
441
+ photoUrl?: string;
442
+ }
443
+
444
+ /**
445
+ * One instance per dialog opening: it activates the product, holds the Widget
446
+ * Session, runs generation and emits the open event. Uploads and generation are
447
+ * reached through {@link createProcessPhoto}, never through the transport
448
+ * surface — the widget must not drive Shopify requests with the ordinary shapes.
449
+ */
450
+ declare class ShopifyTryOnClient implements AiTryOnTransportClient {
451
+ private readonly createObjectUrl;
452
+ private readonly operations;
453
+ private readonly signal;
454
+ private session;
455
+ constructor({ createObjectUrl, operations, signal }: ShopifyTryOnClientOptions);
456
+ activate(target: ShopifyTryOnTarget): Promise<ShopifyActivation>;
457
+ createProcessPhoto(target: ShopifyTryOnTarget): AiTryOnProcessPhoto;
458
+ /**
459
+ * Every Shopify request funnels through here so a rejected HTTP status reaches
460
+ * the shopper as a stable code instead of a generic service failure.
461
+ */
462
+ private run;
463
+ sendWidgetEvent(event: AiTryOnWidgetEventName): void;
464
+ /** Shopify uploads and generation go through {@link createProcessPhoto}. */
465
+ requestUploadUrls(): Promise<never>;
466
+ generateTryOn(): Promise<never>;
467
+ private baseConfig;
468
+ private requestConfig;
469
+ }
470
+
471
+ declare interface ShopifyTryOnClientOptions {
472
+ createObjectUrl?: (blob: Blob) => string;
473
+ operations: ShopifyTryOnOperations;
474
+ signal?: AbortSignal;
475
+ }
476
+
477
+ /**
478
+ * HTTP seam. Stage 1 supplies test operations; stage 2 supplies the generated
479
+ * `@vizbl/api` methods bound to an isolated client on the App Proxy path.
480
+ */
481
+ export declare interface ShopifyTryOnOperations {
482
+ activate: (target: ShopifyTryOnTarget, config: WidgetSessionApiConfig) => Promise<unknown>;
483
+ requestUploadUrls: (count: number, config: WidgetSessionApiConfig) => Promise<unknown>;
484
+ generate: (data: ShopifyTryOnTarget & {
485
+ personImageKey: string;
486
+ }, config: WidgetSessionApiConfig) => Promise<unknown>;
487
+ /**
488
+ * Refreshes the Widget Session. Required: without it the session client falls
489
+ * back to the shared cabinet API instance and the refresh leaves the proxy.
490
+ */
491
+ issueWidgetToken: IssueWidgetToken;
492
+ /** Overrides the direct PUT to storage; the demo uses it to keep photos local. */
493
+ uploadPhoto?: (uploadUrl: string, photo: Blob, options: {
494
+ signal?: AbortSignal;
495
+ timeoutMs: number;
496
+ }) => Promise<void>;
497
+ sendWidgetEvent?: (data: {
498
+ event: AiTryOnWidgetEventName;
499
+ timestamp: string;
500
+ }, config: WidgetSessionApiConfig) => Promise<unknown>;
501
+ }
502
+
503
+ /**
504
+ * What the shopper is trying on. The variant is optional: a product page without
505
+ * a variant picker has none, and the backend then resolves the product's photo.
506
+ */
507
+ export declare interface ShopifyTryOnTarget {
508
+ productId: string;
509
+ variantId?: string;
510
+ }
511
+
512
+ /** Why the backend refused, so the storefront can tell "come back later" from "not set up". */
513
+ declare type ShopifyUnavailableReason = 'not_enabled' | 'zone_not_configured' | 'no_product_photo' | 'tokens_exhausted';
514
+
515
+ declare interface TryOnGenerationOptions {
516
+ timeout?: number;
517
+ }
518
+
519
+ declare interface TryOnGenerationRequest {
520
+ /** Validated canonical Product Type, sent unchanged to the unified generation operation. */
521
+ category: ProductType;
522
+ personImage: string;
523
+ productImages: string[];
524
+ }
525
+
526
+ export declare function uploadImageToPresignedUrl(uploadUrl: string, imageBlob: Blob, options?: AiTryOnUploadOptions): Promise<void>;
527
+
528
+ declare interface WidgetSessionApiConfig {
529
+ baseURL?: string;
530
+ headers?: Record<string, string | undefined>;
531
+ signal?: AbortSignal;
532
+ timeout?: number;
533
+ responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream';
534
+ }
535
+
266
536
  export { }