@neurodyn/ai-try-on 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ai-try-on.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AiTryOnDialog, PRODUCT_TYPES, isProductType } from "@neurodyn/react-ai-try-on";
1
+ import { AiTryOnDialog, PRODUCT_TYPES } from "@neurodyn/react-ai-try-on";
2
2
  import { createElement } from "react";
3
3
  import { createRoot } from "react-dom/client";
4
4
  const DIALOG_ROOT_ID_PREFIX = 'neurodyn-ai-try-on-dialog-root';
@@ -43,54 +43,177 @@ class AiTryOnDialogRenderer {
43
43
  }));
44
44
  }
45
45
  }
46
- const AI_TRY_ON_TRIGGER_ATTRIBUTE = 'data-neurodyn-ai-try-on-dialog';
47
- const AI_TRY_ON_TRIGGER_SELECTOR = `[${AI_TRY_ON_TRIGGER_ATTRIBUTE}]`;
48
- const AI_TRY_ON_LANGUAGE_ATTRIBUTE = 'data-neurodyn-ai-try-on-language';
49
- const AI_TRY_ON_PRODUCT_IMAGE_SRC_ATTRIBUTE = 'data-neurodyn-ai-try-on-product-image-src';
50
- const AI_TRY_ON_PRODUCT_NAME_ATTRIBUTE = 'data-neurodyn-ai-try-on-product-name';
51
- const AI_TRY_ON_PRODUCT_PRICE_ATTRIBUTE = 'data-neurodyn-ai-try-on-product-price';
52
- const AI_TRY_ON_PRODUCT_URL_ATTRIBUTE = 'data-neurodyn-ai-try-on-product-url';
53
- const AI_TRY_ON_PRODUCT_TYPE_ATTRIBUTE = 'data-neurodyn-ai-try-on-product-type';
54
- const AI_TRY_ON_API_KEY_ATTRIBUTE = 'data-neurodyn-ai-try-on-api-key';
55
- function readTriggerProps(trigger, props = {}) {
46
+ function contract_attribute(name, parser, details) {
47
+ return {
48
+ kind: 'attribute',
49
+ name,
50
+ parse: parser.parse,
51
+ values: parser.values,
52
+ ...details
53
+ };
54
+ }
55
+ function optionOnly(reason) {
56
56
  return {
57
- language: void 0 !== props.language ? props.language : readLanguageAttribute(trigger, AI_TRY_ON_LANGUAGE_ATTRIBUTE),
58
- productImageSrc: void 0 !== props.productImageSrc ? props.productImageSrc : readRequiredAttribute(trigger, AI_TRY_ON_PRODUCT_IMAGE_SRC_ATTRIBUTE),
59
- productName: void 0 !== props.productName ? props.productName : readRequiredAttribute(trigger, AI_TRY_ON_PRODUCT_NAME_ATTRIBUTE),
60
- productPrice: void 0 !== props.productPrice ? normalizeOptionalValue(props.productPrice) : readOptionalAttribute(trigger, AI_TRY_ON_PRODUCT_PRICE_ATTRIBUTE),
61
- productUrl: void 0 !== props.productUrl ? props.productUrl : readOptionalAttribute(trigger, AI_TRY_ON_PRODUCT_URL_ATTRIBUTE),
62
- productType: readProductType(void 0 !== props.productType ? props.productType : trigger.getAttribute(AI_TRY_ON_PRODUCT_TYPE_ATTRIBUTE)),
63
- apiKey: normalizeApiKey(void 0 !== props.apiKey ? props.apiKey : trigger.getAttribute(AI_TRY_ON_API_KEY_ATTRIBUTE))
57
+ kind: 'option',
58
+ reason
64
59
  };
65
60
  }
66
- function readProductType(value) {
67
- if (!isProductType(value)) {
68
- const received = 'string' == typeof value && value.length > 0 ? ` "${value}"` : '';
69
- throw new Error(`AI try-on product type from ${AI_TRY_ON_PRODUCT_TYPE_ATTRIBUTE} must be one of: ${PRODUCT_TYPES.join(', ')}; received${received || ' no value'}`);
61
+ function defineTriggerContract() {
62
+ return (contract)=>contract;
63
+ }
64
+ function contract_attributeEntries(contract) {
65
+ return Object.entries(contract.fields).filter((entry)=>'attribute' === entry[1].kind);
66
+ }
67
+ function attributeName(contract, key) {
68
+ const field = contract.fields[key];
69
+ return `${contract.prefix}${field.name}`;
70
+ }
71
+ function triggerSelector(contract) {
72
+ return `[${contract.markers[0].name}]`;
73
+ }
74
+ function readRawAttribute(trigger, name, legacyNames = []) {
75
+ const value = trigger.getAttribute(name);
76
+ if (0 === legacyNames.length || value?.trim()) return value;
77
+ for (const legacyName of legacyNames){
78
+ const legacyValue = trigger.getAttribute(legacyName);
79
+ if (legacyValue?.trim()) return legacyValue;
70
80
  }
71
81
  return value;
72
82
  }
73
- function readLanguageAttribute(trigger, name) {
74
- const value = trigger.getAttribute(name);
75
- return 'en' === value || 'ru' === value ? value : void 0;
83
+ function readTriggerAttributes(trigger, contract, options = {}) {
84
+ const values = {};
85
+ for (const [key, field] of contract_attributeEntries(contract)){
86
+ const name = `${contract.prefix}${field.name}`;
87
+ const option = options[key];
88
+ if (void 0 !== option) {
89
+ values[key] = field.normalizesOptions ? field.parse(option, {
90
+ attribute: name
91
+ }) : option;
92
+ continue;
93
+ }
94
+ values[key] = field.parse(readRawAttribute(trigger, name, field.legacyNames), {
95
+ attribute: name
96
+ });
97
+ }
98
+ return values;
76
99
  }
77
- function readOptionalAttribute(trigger, name) {
78
- const value = trigger.getAttribute(name);
79
- return normalizeOptionalValue(value);
100
+ function trimmedText() {
101
+ return {
102
+ parse: (raw)=>raw?.trim() || void 0
103
+ };
80
104
  }
81
- function normalizeApiKey(value) {
82
- const apiKey = value?.trim();
83
- return apiKey || void 0;
105
+ function requiredText(missing) {
106
+ return {
107
+ parse: (raw, { attribute })=>{
108
+ const value = raw?.trim();
109
+ if (!value) throw missing(attribute);
110
+ return value;
111
+ }
112
+ };
84
113
  }
85
- function normalizeOptionalValue(value) {
86
- const normalized = value?.trim();
87
- return normalized || void 0;
114
+ function oneOf(values) {
115
+ return {
116
+ parse: (raw)=>values.includes(raw ?? '') ? raw : void 0,
117
+ values
118
+ };
88
119
  }
89
- function readRequiredAttribute(trigger, name) {
90
- const value = readOptionalAttribute(trigger, name);
91
- if (!value) throw new Error(`AI try-on dialog trigger is missing ${name}`);
92
- return value;
120
+ function custom(parse, values) {
121
+ return {
122
+ parse,
123
+ values
124
+ };
125
+ }
126
+ function listOf() {
127
+ return (values)=>values;
128
+ }
129
+ const AI_TRY_ON_PRODUCT_TYPES = listOf()([
130
+ 'full-body',
131
+ 'upper-body',
132
+ 'lower-body',
133
+ 'footwear',
134
+ 'headwear',
135
+ 'eyewear',
136
+ 'neck',
137
+ 'wrist',
138
+ 'bag',
139
+ 'waist'
140
+ ]);
141
+ const AI_TRY_ON_LANGUAGES = listOf()([
142
+ 'en',
143
+ 'ru'
144
+ ]);
145
+ function isProductType(value) {
146
+ return 'string' == typeof value && AI_TRY_ON_PRODUCT_TYPES.includes(value);
147
+ }
148
+ const EXTERNAL_ONLY = 'External integrations only, such as the Shopify block.';
149
+ const aiTryOnContract = defineTriggerContract()({
150
+ prefix: 'data-neurodyn-ai-try-on-',
151
+ markers: [
152
+ {
153
+ name: 'data-neurodyn-ai-try-on-dialog',
154
+ description: 'Marks a dialog trigger for the embed build.'
155
+ }
156
+ ],
157
+ fields: {
158
+ productType: contract_attribute('product-type', custom((value, { attribute })=>{
159
+ if (!isProductType(value)) {
160
+ const received = 'string' == typeof value && value.length > 0 ? ` "${value}"` : '';
161
+ throw new Error(`AI try-on product type from ${attribute} must be one of: ${AI_TRY_ON_PRODUCT_TYPES.join(', ')}; received${received || ' no value'}`);
162
+ }
163
+ return value;
164
+ }, AI_TRY_ON_PRODUCT_TYPES), {
165
+ required: true,
166
+ normalizesOptions: true,
167
+ description: 'Product category.'
168
+ }),
169
+ productName: contract_attribute('product-name', requiredText((name)=>new Error(`AI try-on dialog trigger is missing ${name}`)), {
170
+ required: true,
171
+ description: 'Product name shown in the dialog.'
172
+ }),
173
+ productImageSrc: contract_attribute('product-image-src', requiredText((name)=>new Error(`AI try-on dialog trigger is missing ${name}`)), {
174
+ required: true,
175
+ description: 'Product image URL.'
176
+ }),
177
+ productPrice: contract_attribute('product-price', trimmedText(), {
178
+ required: false,
179
+ normalizesOptions: true,
180
+ description: 'Already-formatted product price.'
181
+ }),
182
+ productUrl: contract_attribute('product-url', trimmedText(), {
183
+ required: false,
184
+ description: 'Product page URL opened from the result.'
185
+ }),
186
+ language: contract_attribute('language', oneOf(AI_TRY_ON_LANGUAGES), {
187
+ required: false,
188
+ defaultValue: 'Browser language',
189
+ description: 'UI language override.'
190
+ }),
191
+ apiKey: contract_attribute('api-key', trimmedText(), {
192
+ required: true,
193
+ normalizesOptions: true,
194
+ description: 'Public API key.'
195
+ }),
196
+ open: optionOnly('The embed build owns the open state.'),
197
+ defaultOpen: optionOnly('The embed build owns the open state.'),
198
+ onOpenChange: optionOnly('The embed build owns the open state.'),
199
+ externalGeneration: optionOnly(EXTERNAL_ONLY),
200
+ externalActivation: optionOnly(EXTERNAL_ONLY),
201
+ externalClient: optionOnly(EXTERNAL_ONLY),
202
+ externalAddToCart: optionOnly(EXTERNAL_ONLY)
203
+ }
204
+ });
205
+ function readTriggerProps(trigger, props = {}) {
206
+ return readTriggerAttributes(trigger, aiTryOnContract, props);
93
207
  }
208
+ const AI_TRY_ON_TRIGGER_ATTRIBUTE = aiTryOnContract.markers["0"].name;
209
+ const AI_TRY_ON_TRIGGER_SELECTOR = triggerSelector(aiTryOnContract);
210
+ const AI_TRY_ON_LANGUAGE_ATTRIBUTE = attributeName(aiTryOnContract, 'language');
211
+ const AI_TRY_ON_PRODUCT_IMAGE_SRC_ATTRIBUTE = attributeName(aiTryOnContract, 'productImageSrc');
212
+ const AI_TRY_ON_PRODUCT_NAME_ATTRIBUTE = attributeName(aiTryOnContract, 'productName');
213
+ const AI_TRY_ON_PRODUCT_PRICE_ATTRIBUTE = attributeName(aiTryOnContract, 'productPrice');
214
+ const AI_TRY_ON_PRODUCT_URL_ATTRIBUTE = attributeName(aiTryOnContract, 'productUrl');
215
+ const AI_TRY_ON_PRODUCT_TYPE_ATTRIBUTE = attributeName(aiTryOnContract, 'productType');
216
+ attributeName(aiTryOnContract, 'apiKey');
94
217
  class src_AiTryOnDialog {
95
218
  destroyed = false;
96
219
  props;
@@ -0,0 +1,154 @@
1
+ import type { AiTryOnDialog } from '@neurodyn/react-ai-try-on';
2
+ import type { ComponentProps } from 'react';
3
+
4
+ export declare const AI_TRY_ON_EMBED_SCRIPT_URL = "https://cdn.neurodyn.ai/@neurodyn/ai-try-on/dist/ai-try-on.embed.min.js";
5
+
6
+ export declare const AI_TRY_ON_LANGUAGES: readonly ["en", "ru"];
7
+
8
+ export declare const AI_TRY_ON_PRODUCT_TYPES: readonly ["full-body", "upper-body", "lower-body", "footwear", "headwear", "eyewear", "neck", "wrist", "bag", "waist"];
9
+
10
+ /** The eyewear trigger the docs show as an example. */
11
+ export declare const AI_TRY_ON_TRIGGER_EXAMPLE: {
12
+ readonly productType: "eyewear";
13
+ readonly productName: "Sample Glasses";
14
+ readonly productImageSrc: "https://example.com/products/glasses.jpg";
15
+ readonly apiKey: "...";
16
+ };
17
+
18
+ export declare const aiTryOnContract: TriggerContract<AiTryOnTriggerDialogProps, {
19
+ readonly productType: TriggerAttribute<"full-body" | "upper-body" | "lower-body" | "footwear" | "headwear" | "eyewear" | "neck" | "wrist" | "bag" | "waist">;
20
+ readonly productName: TriggerAttribute<string>;
21
+ readonly productImageSrc: TriggerAttribute<string>;
22
+ readonly productPrice: TriggerAttribute<string | undefined>;
23
+ readonly productUrl: TriggerAttribute<string | undefined>;
24
+ readonly language: TriggerAttribute<"en" | "ru" | undefined>;
25
+ readonly apiKey: TriggerAttribute<string | undefined>;
26
+ readonly open: OptionOnly;
27
+ readonly defaultOpen: OptionOnly;
28
+ readonly onOpenChange: OptionOnly;
29
+ readonly externalGeneration: OptionOnly;
30
+ readonly externalActivation: OptionOnly;
31
+ readonly externalClient: OptionOnly;
32
+ readonly externalAddToCart: OptionOnly;
33
+ }>;
34
+
35
+ /**
36
+ * The React dialog's props in ordinary mode, which is what the trigger
37
+ * renders. External mode (the Shopify block) has a contract of its own.
38
+ */
39
+ export declare interface AiTryOnTriggerDialogProps extends Extract<ComponentProps<typeof AiTryOnDialog>, {
40
+ externalGeneration?: never;
41
+ }> {
42
+ }
43
+
44
+ export declare type AiTryOnTriggerRow = TriggerSnippetRow<keyof AiTryOnTriggerValues>;
45
+
46
+ export declare type AiTryOnTriggerValues = TriggerSnippetValues<typeof aiTryOnContract.fields>;
47
+
48
+ /** What the reader hands a parser besides the raw value. */
49
+ declare interface AttributeParseContext {
50
+ /** Full attribute name, e.g. `data-neurodyn-room-viewer-api-key`. */
51
+ attribute: string
52
+ }
53
+
54
+ /**
55
+ * Whether an attribute must be set: always, never, or only while another
56
+ * attribute holds one of the listed values (`src` while `type` is `wallart`).
57
+ * `when` names a field of the same contract.
58
+ */
59
+ declare type AttributeRequirement
60
+ = | boolean
61
+ | { readonly when: string, readonly is: readonly string[] }
62
+
63
+ /** A copy-paste trigger button with the given attributes, in contract order and escaped. */
64
+ export declare function buildAiTryOnTrigger(values: AiTryOnTriggerValues, options: TriggerSnippetOptions): string;
65
+
66
+ /** The tag that loads the self-initializing embed build from `src`. */
67
+ export declare function buildEmbedScript(src: string): string;
68
+
69
+ /**
70
+ * The rows describing a trigger built from the same values, for the table
71
+ * beside the snippet; `extra` adds attributes the snippet leaves out.
72
+ */
73
+ export declare function describeAiTryOnTrigger(values: AiTryOnTriggerValues, extra?: readonly (keyof AiTryOnTriggerValues)[]): AiTryOnTriggerRow[];
74
+
75
+ /** A widget option that has no attribute: callbacks, controlled state, sessions. */
76
+ declare interface OptionOnly {
77
+ readonly kind: 'option'
78
+ /** Why the option is programmatic only. */
79
+ readonly reason: string
80
+ }
81
+
82
+ /** What an attribute's parser produces. */
83
+ declare type ParsedAttributeValue<Field> = Field extends TriggerAttribute<infer Value> ? Value : never
84
+
85
+ /** A widget option a merchant can set with a `data-*` attribute on the trigger. */
86
+ declare interface TriggerAttribute<Value> {
87
+ readonly kind: 'attribute'
88
+ /** Attribute name without the widget prefix, e.g. `api-key`. */
89
+ readonly name: string
90
+ readonly parse: (raw: string | null, context: AttributeParseContext) => Value
91
+ /** The accepted values, listed in the docs. */
92
+ readonly values?: readonly string[]
93
+ /** What the docs and the cabinets' snippets ask for; the reader does not enforce it. */
94
+ readonly required: AttributeRequirement
95
+ /** The default as Markdown for the docs, e.g. `` `cm` `` or `User locale`. */
96
+ readonly defaultValue?: string
97
+ /** One or two sentences for the docs; accepted values are appended from `values`. */
98
+ readonly description: string
99
+ /** Full attribute names read, in order, while the primary one is absent or blank. */
100
+ readonly legacyNames?: readonly string[]
101
+ /** Runs an explicitly passed programmatic option through `parse` as well. */
102
+ readonly normalizesOptions?: boolean
103
+ }
104
+
105
+ /** Keys of the fields that are attributes. */
106
+ declare type TriggerAttributeKey<Fields> = {
107
+ [Key in keyof Fields]: Fields[Key] extends TriggerAttribute<any> ? Key : never
108
+ }[keyof Fields] & string
109
+
110
+ declare interface TriggerContract<Options, Fields extends TriggerFields<Options> = TriggerFields<Options>> {
111
+ /** Prefix shared by the widget's option attributes, e.g. `data-neurodyn-room-viewer-`. */
112
+ readonly prefix: string
113
+ /** The first marker is the one snippets use. */
114
+ readonly markers: readonly [TriggerMarker, ...TriggerMarker[]]
115
+ readonly fields: Fields
116
+ }
117
+
118
+ /**
119
+ * One entry per widget option, none optional: an option added to the widget's
120
+ * props without an entry here fails type-checking until it is given an
121
+ * attribute or marked option-only.
122
+ */
123
+ declare type TriggerFields<Options> = {
124
+ readonly [Key in keyof Options]-?: TriggerAttribute<Options[Key] | undefined> | OptionOnly
125
+ }
126
+
127
+ /** A marker attribute that selects trigger elements, e.g. `data-neurodyn-room-viewer-dialog`. */
128
+ declare interface TriggerMarker {
129
+ readonly name: string
130
+ readonly description: string
131
+ }
132
+
133
+ export declare interface TriggerSnippetOptions {
134
+ /** The button text. */
135
+ label: string
136
+ }
137
+
138
+ /** One attribute of a snippet, for the table next to it. */
139
+ declare interface TriggerSnippetRow<Key extends string> {
140
+ /** The field key, or `null` for the marker attribute. */
141
+ key: Key | null
142
+ attribute: string
143
+ required: boolean
144
+ }
145
+
146
+ /**
147
+ * Values for a copy-paste trigger: only the contract's attributes, each typed
148
+ * as its option. Placeholders such as `PUBLIC_WIDGET_KEY` fit string options.
149
+ */
150
+ declare type TriggerSnippetValues<Fields> = {
151
+ [Key in TriggerAttributeKey<Fields>]?: Exclude<ParsedAttributeValue<Fields[Key]>, undefined | null>
152
+ }
153
+
154
+ export { }
@@ -0,0 +1,195 @@
1
+ function contract_attribute(name, parser, details) {
2
+ return {
3
+ kind: 'attribute',
4
+ name,
5
+ parse: parser.parse,
6
+ values: parser.values,
7
+ ...details
8
+ };
9
+ }
10
+ function optionOnly(reason) {
11
+ return {
12
+ kind: 'option',
13
+ reason
14
+ };
15
+ }
16
+ function defineTriggerContract() {
17
+ return (contract)=>contract;
18
+ }
19
+ function contract_attributeEntries(contract) {
20
+ return Object.entries(contract.fields).filter((entry)=>'attribute' === entry[1].kind);
21
+ }
22
+ function escapeHtml(value) {
23
+ return value.replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
24
+ }
25
+ function isAttributeRequired(requirement, values) {
26
+ if ('boolean' == typeof requirement) return requirement;
27
+ const current = values[requirement.when];
28
+ return null != current && requirement.is.includes(String(current));
29
+ }
30
+ function snippetEntries(contract, values) {
31
+ return contract_attributeEntries(contract).flatMap(([key, field])=>{
32
+ const value = values[key];
33
+ return null == value ? [] : [
34
+ {
35
+ key,
36
+ field,
37
+ value
38
+ }
39
+ ];
40
+ });
41
+ }
42
+ function snippet_buildTriggerSnippet(contract, values, { label }) {
43
+ const lines = [
44
+ '<button',
45
+ ' type="button"',
46
+ ` ${contract.markers[0].name}`
47
+ ];
48
+ for (const { field, value } of snippetEntries(contract, values))lines.push(` ${contract.prefix}${field.name}="${escapeHtml(String(value))}"`);
49
+ lines.push('>', ` ${escapeHtml(label)}`, '</button>');
50
+ return lines.join('\n');
51
+ }
52
+ function describeTriggerSnippet(contract, values, extra = []) {
53
+ const present = snippetEntries(contract, values);
54
+ const presentKeys = new Set(present.map(({ key })=>key));
55
+ const fields = contract.fields;
56
+ const row = (key, field)=>({
57
+ key,
58
+ attribute: `${contract.prefix}${field.name}`,
59
+ required: isAttributeRequired(field.required, values)
60
+ });
61
+ return [
62
+ {
63
+ key: null,
64
+ attribute: contract.markers[0].name,
65
+ required: true
66
+ },
67
+ ...present.map(({ key, field })=>row(key, field)),
68
+ ...extra.filter((key)=>!presentKeys.has(key)).map((key)=>row(key, fields[key]))
69
+ ];
70
+ }
71
+ function snippet_buildEmbedScript(src) {
72
+ return `<script defer src="${escapeHtml(src)}"></script>`;
73
+ }
74
+ function trimmedText() {
75
+ return {
76
+ parse: (raw)=>raw?.trim() || void 0
77
+ };
78
+ }
79
+ function requiredText(missing) {
80
+ return {
81
+ parse: (raw, { attribute })=>{
82
+ const value = raw?.trim();
83
+ if (!value) throw missing(attribute);
84
+ return value;
85
+ }
86
+ };
87
+ }
88
+ function oneOf(values) {
89
+ return {
90
+ parse: (raw)=>values.includes(raw ?? '') ? raw : void 0,
91
+ values
92
+ };
93
+ }
94
+ function custom(parse, values) {
95
+ return {
96
+ parse,
97
+ values
98
+ };
99
+ }
100
+ function listOf() {
101
+ return (values)=>values;
102
+ }
103
+ const AI_TRY_ON_PRODUCT_TYPES = listOf()([
104
+ 'full-body',
105
+ 'upper-body',
106
+ 'lower-body',
107
+ 'footwear',
108
+ 'headwear',
109
+ 'eyewear',
110
+ 'neck',
111
+ 'wrist',
112
+ 'bag',
113
+ 'waist'
114
+ ]);
115
+ const AI_TRY_ON_LANGUAGES = listOf()([
116
+ 'en',
117
+ 'ru'
118
+ ]);
119
+ const AI_TRY_ON_EMBED_SCRIPT_URL = 'https://cdn.neurodyn.ai/@neurodyn/ai-try-on/dist/ai-try-on.embed.min.js';
120
+ function isProductType(value) {
121
+ return 'string' == typeof value && AI_TRY_ON_PRODUCT_TYPES.includes(value);
122
+ }
123
+ const EXTERNAL_ONLY = 'External integrations only, such as the Shopify block.';
124
+ const aiTryOnContract = defineTriggerContract()({
125
+ prefix: 'data-neurodyn-ai-try-on-',
126
+ markers: [
127
+ {
128
+ name: 'data-neurodyn-ai-try-on-dialog',
129
+ description: 'Marks a dialog trigger for the embed build.'
130
+ }
131
+ ],
132
+ fields: {
133
+ productType: contract_attribute('product-type', custom((value, { attribute })=>{
134
+ if (!isProductType(value)) {
135
+ const received = 'string' == typeof value && value.length > 0 ? ` "${value}"` : '';
136
+ throw new Error(`AI try-on product type from ${attribute} must be one of: ${AI_TRY_ON_PRODUCT_TYPES.join(', ')}; received${received || ' no value'}`);
137
+ }
138
+ return value;
139
+ }, AI_TRY_ON_PRODUCT_TYPES), {
140
+ required: true,
141
+ normalizesOptions: true,
142
+ description: 'Product category.'
143
+ }),
144
+ productName: contract_attribute('product-name', requiredText((name)=>new Error(`AI try-on dialog trigger is missing ${name}`)), {
145
+ required: true,
146
+ description: 'Product name shown in the dialog.'
147
+ }),
148
+ productImageSrc: contract_attribute('product-image-src', requiredText((name)=>new Error(`AI try-on dialog trigger is missing ${name}`)), {
149
+ required: true,
150
+ description: 'Product image URL.'
151
+ }),
152
+ productPrice: contract_attribute('product-price', trimmedText(), {
153
+ required: false,
154
+ normalizesOptions: true,
155
+ description: 'Already-formatted product price.'
156
+ }),
157
+ productUrl: contract_attribute('product-url', trimmedText(), {
158
+ required: false,
159
+ description: 'Product page URL opened from the result.'
160
+ }),
161
+ language: contract_attribute('language', oneOf(AI_TRY_ON_LANGUAGES), {
162
+ required: false,
163
+ defaultValue: 'Browser language',
164
+ description: 'UI language override.'
165
+ }),
166
+ apiKey: contract_attribute('api-key', trimmedText(), {
167
+ required: true,
168
+ normalizesOptions: true,
169
+ description: 'Public API key.'
170
+ }),
171
+ open: optionOnly('The embed build owns the open state.'),
172
+ defaultOpen: optionOnly('The embed build owns the open state.'),
173
+ onOpenChange: optionOnly('The embed build owns the open state.'),
174
+ externalGeneration: optionOnly(EXTERNAL_ONLY),
175
+ externalActivation: optionOnly(EXTERNAL_ONLY),
176
+ externalClient: optionOnly(EXTERNAL_ONLY),
177
+ externalAddToCart: optionOnly(EXTERNAL_ONLY)
178
+ }
179
+ });
180
+ const AI_TRY_ON_TRIGGER_EXAMPLE = {
181
+ productType: 'eyewear',
182
+ productName: 'Sample Glasses',
183
+ productImageSrc: 'https://example.com/products/glasses.jpg',
184
+ apiKey: '...'
185
+ };
186
+ function buildAiTryOnTrigger(values, options) {
187
+ return snippet_buildTriggerSnippet(aiTryOnContract, values, options);
188
+ }
189
+ function describeAiTryOnTrigger(values, extra = []) {
190
+ return describeTriggerSnippet(aiTryOnContract, values, extra);
191
+ }
192
+ function contract_buildEmbedScript(src) {
193
+ return snippet_buildEmbedScript(src);
194
+ }
195
+ export { AI_TRY_ON_EMBED_SCRIPT_URL, AI_TRY_ON_LANGUAGES, AI_TRY_ON_PRODUCT_TYPES, AI_TRY_ON_TRIGGER_EXAMPLE, aiTryOnContract, buildAiTryOnTrigger, contract_buildEmbedScript as buildEmbedScript, describeAiTryOnTrigger };
package/dist/index.d.ts CHANGED
@@ -1,22 +1,22 @@
1
1
  import { PRODUCT_TYPES } from '@neurodyn/react-ai-try-on';
2
2
  import { ProductType } from '@neurodyn/react-ai-try-on';
3
3
 
4
- export declare const AI_TRY_ON_LANGUAGE_ATTRIBUTE = "data-neurodyn-ai-try-on-language";
4
+ export declare const AI_TRY_ON_LANGUAGE_ATTRIBUTE: string;
5
5
 
6
- export declare const AI_TRY_ON_PRODUCT_IMAGE_SRC_ATTRIBUTE = "data-neurodyn-ai-try-on-product-image-src";
6
+ export declare const AI_TRY_ON_PRODUCT_IMAGE_SRC_ATTRIBUTE: string;
7
7
 
8
- export declare const AI_TRY_ON_PRODUCT_NAME_ATTRIBUTE = "data-neurodyn-ai-try-on-product-name";
8
+ export declare const AI_TRY_ON_PRODUCT_NAME_ATTRIBUTE: string;
9
9
 
10
- export declare const AI_TRY_ON_PRODUCT_PRICE_ATTRIBUTE = "data-neurodyn-ai-try-on-product-price";
10
+ export declare const AI_TRY_ON_PRODUCT_PRICE_ATTRIBUTE: string;
11
11
 
12
12
  /** Canonical Product Type driving per-type generation. */
13
- export declare const AI_TRY_ON_PRODUCT_TYPE_ATTRIBUTE = "data-neurodyn-ai-try-on-product-type";
13
+ export declare const AI_TRY_ON_PRODUCT_TYPE_ATTRIBUTE: string;
14
14
 
15
- export declare const AI_TRY_ON_PRODUCT_URL_ATTRIBUTE = "data-neurodyn-ai-try-on-product-url";
15
+ export declare const AI_TRY_ON_PRODUCT_URL_ATTRIBUTE: string;
16
16
 
17
- export declare const AI_TRY_ON_TRIGGER_ATTRIBUTE = "data-neurodyn-ai-try-on-dialog";
17
+ export declare const AI_TRY_ON_TRIGGER_ATTRIBUTE: string;
18
18
 
19
- export declare const AI_TRY_ON_TRIGGER_SELECTOR = "[data-neurodyn-ai-try-on-dialog]";
19
+ export declare const AI_TRY_ON_TRIGGER_SELECTOR: string;
20
20
 
21
21
  export declare class AiTryOnDialog {
22
22
  private destroyed;