@yassirbenmoussa/aicommerce-sdk 1.1.0 → 1.3.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 CHANGED
@@ -12,8 +12,12 @@ interface AICommerceConfig {
12
12
  }
13
13
  /** Chat message request */
14
14
  interface ChatRequest {
15
- /** The user's message */
16
- message: string;
15
+ /** The user's message (optional if audio is provided) */
16
+ message?: string;
17
+ /** Base64-encoded audio data for voice input */
18
+ audioBase64?: string;
19
+ /** MIME type of the audio (e.g., 'audio/webm', 'audio/wav') */
20
+ audioMimeType?: string;
17
21
  /** Optional session token for conversation continuity */
18
22
  sessionToken?: string;
19
23
  /** Optional context for better recommendations */
@@ -61,6 +65,8 @@ interface Product {
61
65
  currency?: string;
62
66
  /** Primary image URL */
63
67
  imageUrl?: string;
68
+ /** Image URL (backend consistency) */
69
+ image?: string;
64
70
  /** All image URLs */
65
71
  images?: string[];
66
72
  /** Product category */
@@ -92,6 +98,62 @@ interface APIError {
92
98
  type EventType = 'message' | 'products' | 'error' | 'session';
93
99
  /** Event callback */
94
100
  type EventCallback<T = unknown> = (data: T) => void;
101
+ /** Store configuration from API */
102
+ interface StoreConfig {
103
+ id: string;
104
+ name: string;
105
+ slug: string;
106
+ logo: string | null;
107
+ primaryColor: string;
108
+ chatBotName: string;
109
+ welcomeMessage: string;
110
+ }
111
+ /** Widget configuration options */
112
+ interface WidgetConfig {
113
+ /** Your API key from the AI Commerce dashboard */
114
+ apiKey: string;
115
+ /** Base URL for the API (defaults to current origin or https://api.aicommerce.dev) */
116
+ baseUrl?: string;
117
+ /** Widget position on screen */
118
+ position?: 'bottom-right' | 'bottom-left';
119
+ /** Theme mode */
120
+ theme?: 'light' | 'dark' | 'auto';
121
+ /** Custom primary color (overrides store setting) */
122
+ primaryColor?: string;
123
+ /** Custom welcome message (overrides store setting) */
124
+ welcomeMessage?: string;
125
+ /** Custom bot name (overrides store setting) */
126
+ botName?: string;
127
+ /** Z-index for the widget */
128
+ zIndex?: number;
129
+ /** Custom button text when minimized */
130
+ buttonText?: string;
131
+ /** Hide the launcher button (for custom triggers) */
132
+ hideLauncher?: boolean;
133
+ /** Callback when widget is opened */
134
+ onOpen?: () => void;
135
+ /** Callback when widget is closed */
136
+ onClose?: () => void;
137
+ /** Callback when a product is clicked */
138
+ onProductClick?: (product: Product) => void;
139
+ /** Callback when a message is sent */
140
+ onMessage?: (message: string, response: ChatResponse) => void;
141
+ }
142
+ /** Widget instance interface */
143
+ interface WidgetInstance {
144
+ /** Open the chat widget */
145
+ open: () => void;
146
+ /** Close the chat widget */
147
+ close: () => void;
148
+ /** Toggle the chat widget */
149
+ toggle: () => void;
150
+ /** Destroy the widget and remove from DOM */
151
+ destroy: () => void;
152
+ /** Send a message programmatically */
153
+ sendMessage: (message: string) => Promise<ChatResponse>;
154
+ /** Update widget configuration */
155
+ updateConfig: (config: Partial<WidgetConfig>) => void;
156
+ }
95
157
 
96
158
  /**
97
159
  * AI Commerce SDK Client
@@ -143,6 +205,27 @@ declare class AICommerce {
143
205
  * ```
144
206
  */
145
207
  chat(message: string | ChatRequest, context?: ChatContext): Promise<ChatResponse>;
208
+ /**
209
+ * Send an audio message and get product recommendations
210
+ *
211
+ * @param audioBlob - Audio blob (from MediaRecorder or file input)
212
+ * @param context - Optional context for better recommendations
213
+ * @returns Chat response with AI reply and products
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * // Record audio using MediaRecorder
218
+ * const mediaRecorder = new MediaRecorder(stream);
219
+ * const chunks: Blob[] = [];
220
+ * mediaRecorder.ondataavailable = (e) => chunks.push(e.data);
221
+ * mediaRecorder.onstop = async () => {
222
+ * const audioBlob = new Blob(chunks, { type: 'audio/webm' });
223
+ * const response = await client.chatWithAudio(audioBlob);
224
+ * console.log(response.reply);
225
+ * };
226
+ * ```
227
+ */
228
+ chatWithAudio(audioBlob: Blob, context?: ChatContext): Promise<ChatResponse>;
146
229
  /**
147
230
  * Create a new chat session
148
231
  *
@@ -161,6 +244,53 @@ declare class AICommerce {
161
244
  * Set a session token (for restoring sessions)
162
245
  */
163
246
  setSessionToken(token: string): void;
247
+ /**
248
+ * Products API namespace
249
+ */
250
+ readonly products: {
251
+ /**
252
+ * Create a new product
253
+ */
254
+ create: (product: CreateProductInput) => Promise<ProductResponse>;
255
+ /**
256
+ * Batch upsert products (create or update)
257
+ */
258
+ batchUpsert: (products: CreateProductInput[]) => Promise<BatchUpsertResponse>;
259
+ /**
260
+ * List products with pagination
261
+ */
262
+ list: (options?: ListProductsOptions) => Promise<ListProductsResponse>;
263
+ /**
264
+ * Get a single product by ID
265
+ */
266
+ get: (productId: string) => Promise<ProductResponse>;
267
+ /**
268
+ * Update a product
269
+ */
270
+ update: (productId: string, data: UpdateProductInput) => Promise<ProductResponse>;
271
+ /**
272
+ * Delete a product
273
+ */
274
+ delete: (productId: string) => Promise<{
275
+ success: boolean;
276
+ deleted: boolean;
277
+ }>;
278
+ };
279
+ /**
280
+ * Upload an image file
281
+ *
282
+ * @example
283
+ * ```typescript
284
+ * // Upload from File input
285
+ * const file = document.querySelector('input[type="file"]').files[0];
286
+ * const result = await client.upload(file);
287
+ * console.log(result.url);
288
+ *
289
+ * // Upload and associate with product
290
+ * const result = await client.upload(file, { productId: 'prod_123', isPrimary: true });
291
+ * ```
292
+ */
293
+ upload(file: File | Blob, options?: UploadOptions): Promise<UploadResponse>;
164
294
  /**
165
295
  * Static method for one-off chat requests
166
296
  *
@@ -179,6 +309,95 @@ declare class AICommerce {
179
309
  context?: ChatContext;
180
310
  }): Promise<ChatResponse>;
181
311
  }
312
+ interface CreateProductInput {
313
+ name: string;
314
+ slug: string;
315
+ description?: string;
316
+ sku?: string;
317
+ barcode?: string;
318
+ price: number;
319
+ compareAtPrice?: number;
320
+ currency?: string;
321
+ quantity?: number;
322
+ trackInventory?: boolean;
323
+ isActive?: boolean;
324
+ isFeatured?: boolean;
325
+ tags?: string;
326
+ externalId?: string;
327
+ categoryId?: string;
328
+ images?: Array<{
329
+ url: string;
330
+ alt?: string;
331
+ isPrimary?: boolean;
332
+ }>;
333
+ }
334
+ interface UpdateProductInput {
335
+ name?: string;
336
+ slug?: string;
337
+ description?: string | null;
338
+ sku?: string | null;
339
+ barcode?: string | null;
340
+ price?: number;
341
+ compareAtPrice?: number | null;
342
+ currency?: string;
343
+ quantity?: number;
344
+ trackInventory?: boolean;
345
+ isActive?: boolean;
346
+ isFeatured?: boolean;
347
+ tags?: string | null;
348
+ categoryId?: string | null;
349
+ }
350
+ interface ProductResponse {
351
+ success: boolean;
352
+ product: Product;
353
+ }
354
+ interface BatchUpsertResponse {
355
+ success: boolean;
356
+ processed: number;
357
+ errors: number;
358
+ results: Array<{
359
+ id: string;
360
+ slug: string;
361
+ status: 'created' | 'updated';
362
+ }>;
363
+ errorDetails?: Array<{
364
+ slug: string;
365
+ error: string;
366
+ }>;
367
+ }
368
+ interface ListProductsOptions {
369
+ page?: number;
370
+ perPage?: number;
371
+ search?: string;
372
+ categoryId?: string;
373
+ isActive?: boolean;
374
+ }
375
+ interface ListProductsResponse {
376
+ success: boolean;
377
+ data: Product[];
378
+ total: number;
379
+ page: number;
380
+ perPage: number;
381
+ totalPages: number;
382
+ }
383
+ interface UploadOptions {
384
+ folder?: string;
385
+ productId?: string;
386
+ isPrimary?: boolean;
387
+ }
388
+ interface UploadResponse {
389
+ success: boolean;
390
+ url: string;
391
+ key: string;
392
+ size: number;
393
+ contentType: string;
394
+ productImage?: {
395
+ id: string;
396
+ url: string;
397
+ alt: string | null;
398
+ isPrimary: boolean;
399
+ };
400
+ }
182
401
  /**
183
402
  * Custom error class for AI Commerce SDK
184
403
  */
@@ -188,6 +407,25 @@ declare class AICommerceError extends Error {
188
407
  constructor(message: string, code: string, status: number);
189
408
  }
190
409
 
410
+ /**
411
+ * AI Commerce Chat Widget
412
+ *
413
+ * Embeddable chat widget for e-commerce stores
414
+ * Uses store's primaryColor for theming
415
+ */
416
+
417
+ /**
418
+ * Create and initialize the AI Commerce chat widget
419
+ */
420
+ declare function createWidget(config: WidgetConfig): WidgetInstance;
421
+ /**
422
+ * Global widget initialization for script tag usage
423
+ */
424
+ declare const AICommerceWidget: {
425
+ init: typeof createWidget;
426
+ VERSION: string;
427
+ };
428
+
191
429
  /**
192
430
  * AI Commerce SDK
193
431
  *
@@ -198,7 +436,7 @@ declare class AICommerceError extends Error {
198
436
  *
199
437
  * @example
200
438
  * ```typescript
201
- * // npm usage
439
+ * // npm usage - Client API
202
440
  * import { AICommerce } from '@yassirbenmoussa/aicommerce-sdk';
203
441
  *
204
442
  * const client = new AICommerce({ apiKey: 'your-api-key' });
@@ -208,12 +446,13 @@ declare class AICommerceError extends Error {
208
446
  *
209
447
  * @example
210
448
  * ```html
211
- * <!-- Script tag usage -->
212
- * <script src="https://cdn.aicommerce.dev/sdk.min.js"></script>
449
+ * <!-- Widget usage -->
450
+ * <script src="https://cdn.aicommerce.dev/widget.min.js"></script>
213
451
  * <script>
214
- * const client = new AICommerce({ apiKey: 'your-api-key' });
215
- * client.chat('I need a laptop').then(response => {
216
- * console.log(response.products);
452
+ * AICommerceWidget.init({
453
+ * apiKey: 'your-api-key',
454
+ * position: 'bottom-right',
455
+ * theme: 'auto'
217
456
  * });
218
457
  * </script>
219
458
  * ```
@@ -221,4 +460,4 @@ declare class AICommerceError extends Error {
221
460
 
222
461
  declare const VERSION = "1.0.0";
223
462
 
224
- export { AICommerce, type AICommerceConfig, AICommerceError, type APIError, type ChatContext, type ChatRequest, type ChatResponse, type EventCallback, type EventType, type Product, type Session, VERSION };
463
+ export { AICommerce, type AICommerceConfig, AICommerceError, AICommerceWidget, type APIError, type ChatContext, type ChatRequest, type ChatResponse, type EventCallback, type EventType, type Product, type Session, type StoreConfig, VERSION, type WidgetConfig, type WidgetInstance, createWidget };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/index.ts"],"names":["client_exports","__export","AICommerce","AICommerceError","init_client","__esmMin","_AICommerce","config","url","endpoint","options","controller","timeoutId","response","errorData","error","message","context","request","token","_AICommerceError","code","status","VERSION"],"mappings":"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","file":"index.min.js","sourcesContent":["import type {\r\n AICommerceConfig,\r\n ChatRequest,\r\n ChatResponse,\r\n ChatContext,\r\n Session,\r\n APIError,\r\n} from './types';\r\n\r\n/**\r\n * AI Commerce SDK Client\r\n * \r\n * @example\r\n * ```typescript\r\n * import { AICommerce } from '@yassirbenmoussa/aicommerce-sdk';\r\n * \r\n * const client = new AICommerce({ apiKey: 'your-api-key' });\r\n * const response = await client.chat('I need a laptop under $1000');\r\n * console.log(response.products);\r\n * ```\r\n */\r\nexport class AICommerce {\r\n private readonly apiKey: string;\r\n private readonly baseUrl: string;\r\n private readonly timeout: number;\r\n private sessionToken: string | null = null;\r\n\r\n constructor(config: AICommerceConfig) {\r\n if (!config.apiKey) {\r\n throw new Error('AICommerce: apiKey is required');\r\n }\r\n\r\n this.apiKey = config.apiKey;\r\n this.baseUrl = this.normalizeUrl(config.baseUrl || this.detectBaseUrl());\r\n this.timeout = config.timeout || 30000;\r\n }\r\n\r\n /**\r\n * Detect the base URL based on environment\r\n */\r\n private detectBaseUrl(): string {\r\n // Browser environment\r\n if (typeof window !== 'undefined') {\r\n return window.location.origin;\r\n }\r\n // Node.js or default\r\n return 'https://api.aicommerce.dev';\r\n }\r\n\r\n /**\r\n * Normalize URL (remove trailing slash)\r\n */\r\n private normalizeUrl(url: string): string {\r\n return url.replace(/\\/$/, '');\r\n }\r\n\r\n /**\r\n * Make an API request\r\n */\r\n private async request<T>(\r\n endpoint: string,\r\n options: RequestInit = {}\r\n ): Promise<T> {\r\n const url = `${this.baseUrl}${endpoint}`;\r\n\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\r\n\r\n try {\r\n const response = await fetch(url, {\r\n ...options,\r\n signal: controller.signal,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-API-Key': this.apiKey,\r\n ...(this.sessionToken && { 'X-Session-Token': this.sessionToken }),\r\n ...options.headers,\r\n },\r\n });\r\n\r\n clearTimeout(timeoutId);\r\n\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({}));\r\n const error: APIError = {\r\n code: errorData.code || 'UNKNOWN_ERROR',\r\n message: errorData.message || errorData.error || `HTTP ${response.status}`,\r\n status: response.status,\r\n };\r\n throw new AICommerceError(error.message, error.code, error.status);\r\n }\r\n\r\n return response.json();\r\n } catch (error) {\r\n clearTimeout(timeoutId);\r\n\r\n if (error instanceof AICommerceError) {\r\n throw error;\r\n }\r\n\r\n if (error instanceof Error && error.name === 'AbortError') {\r\n throw new AICommerceError('Request timeout', 'TIMEOUT', 408);\r\n }\r\n\r\n throw new AICommerceError(\r\n error instanceof Error ? error.message : 'Unknown error',\r\n 'NETWORK_ERROR',\r\n 0\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Send a chat message and get product recommendations\r\n * \r\n * @param message - The user's message or full ChatRequest object\r\n * @param context - Optional context for better recommendations\r\n * @returns Chat response with AI reply and products\r\n * \r\n * @example\r\n * ```typescript\r\n * // Simple usage\r\n * const response = await client.chat('I need running shoes');\r\n * \r\n * // With context\r\n * const response = await client.chat('I need running shoes', {\r\n * budget: { max: 150 },\r\n * preferences: ['comfortable', 'lightweight']\r\n * });\r\n * ```\r\n */\r\n async chat(\r\n message: string | ChatRequest,\r\n context?: ChatContext\r\n ): Promise<ChatResponse> {\r\n const request: ChatRequest = typeof message === 'string'\r\n ? { message, context, sessionToken: this.sessionToken || undefined }\r\n : { ...message, sessionToken: message.sessionToken || this.sessionToken || undefined };\r\n\r\n const response = await this.request<ChatResponse>('/api/v1/chat', {\r\n method: 'POST',\r\n body: JSON.stringify(request),\r\n });\r\n\r\n // Store session token for follow-up messages\r\n if (response.sessionToken) {\r\n this.sessionToken = response.sessionToken;\r\n }\r\n\r\n return response;\r\n }\r\n\r\n /**\r\n * Create a new chat session\r\n * \r\n * @returns Session information with token\r\n */\r\n async createSession(): Promise<Session> {\r\n const response = await this.request<{ session: Session }>('/api/v1/chat/session', {\r\n method: 'POST',\r\n });\r\n\r\n this.sessionToken = response.session.token;\r\n return response.session;\r\n }\r\n\r\n /**\r\n * Clear the current session\r\n */\r\n clearSession(): void {\r\n this.sessionToken = null;\r\n }\r\n\r\n /**\r\n * Get the current session token\r\n */\r\n getSessionToken(): string | null {\r\n return this.sessionToken;\r\n }\r\n\r\n /**\r\n * Set a session token (for restoring sessions)\r\n */\r\n setSessionToken(token: string): void {\r\n this.sessionToken = token;\r\n }\r\n\r\n /**\r\n * Static method for one-off chat requests\r\n * \r\n * @example\r\n * ```typescript\r\n * const response = await AICommerce.quickChat({\r\n * apiKey: 'your-api-key',\r\n * message: 'I need a laptop'\r\n * });\r\n * ```\r\n */\r\n static async quickChat(options: {\r\n apiKey: string;\r\n message: string;\r\n baseUrl?: string;\r\n context?: ChatContext;\r\n }): Promise<ChatResponse> {\r\n const client = new AICommerce({\r\n apiKey: options.apiKey,\r\n baseUrl: options.baseUrl,\r\n });\r\n return client.chat(options.message, options.context);\r\n }\r\n}\r\n\r\n/**\r\n * Custom error class for AI Commerce SDK\r\n */\r\nexport class AICommerceError extends Error {\r\n readonly code: string;\r\n readonly status: number;\r\n\r\n constructor(message: string, code: string, status: number) {\r\n super(message);\r\n this.name = 'AICommerceError';\r\n this.code = code;\r\n this.status = status;\r\n\r\n // Maintain proper prototype chain\r\n Object.setPrototypeOf(this, AICommerceError.prototype);\r\n }\r\n}\r\n","/**\r\n * AI Commerce SDK\r\n * \r\n * AI-powered product recommendations for e-commerce\r\n * \r\n * @packageDocumentation\r\n * @module @yassirbenmoussa/aicommerce-sdk\r\n * \r\n * @example\r\n * ```typescript\r\n * // npm usage\r\n * import { AICommerce } from '@yassirbenmoussa/aicommerce-sdk';\r\n * \r\n * const client = new AICommerce({ apiKey: 'your-api-key' });\r\n * const response = await client.chat('I need a laptop');\r\n * console.log(response.products);\r\n * ```\r\n * \r\n * @example\r\n * ```html\r\n * <!-- Script tag usage -->\r\n * <script src=\"https://cdn.aicommerce.dev/sdk.min.js\"></script>\r\n * <script>\r\n * const client = new AICommerce({ apiKey: 'your-api-key' });\r\n * client.chat('I need a laptop').then(response => {\r\n * console.log(response.products);\r\n * });\r\n * </script>\r\n * ```\r\n */\r\n\r\n// Export main client\r\nexport { AICommerce, AICommerceError } from './client';\r\n\r\n// Export all types\r\nexport type {\r\n AICommerceConfig,\r\n ChatRequest,\r\n ChatResponse,\r\n ChatContext,\r\n Product,\r\n Session,\r\n APIError,\r\n EventType,\r\n EventCallback,\r\n} from './types';\r\n\r\n// Version\r\nexport const VERSION = '1.0.0';\r\n\r\n// For UMD builds, attach to window\r\nif (typeof window !== 'undefined') {\r\n // @ts-expect-error - Attaching to window for UMD\r\n window.AICommerce = require('./client').AICommerce;\r\n // @ts-expect-error - Attaching to window for UMD\r\n window.AICommerceError = require('./client').AICommerceError;\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/widget-styles.ts","../src/widget.ts","../src/index.ts"],"names":["client_exports","__export","AICommerce","AICommerceError","init_client","__esmMin","_AICommerce","config","product","products","options","params","query","productId","data","url","endpoint","controller","timeoutId","response","errorData","error","message","context","request","audioBlob","arrayBuffer","byte","token","file","formData","_AICommerceError","code","status","hexToRgb","hex","result","createWidgetStyles","primary","rgb","isLeft","injectStyles","css","style","existing","init_widget_styles","widget_exports","AICommerceWidget","createWidget","client","state","mediaRecorder","audioChunks","container","styleElement","resolvedConfig","fetchStoreConfig","baseUrl","detectBaseUrl","script","initialize","styles","render","html","msg","index","isRtl","isArabic","isUser","renderAudioPlayer","escapeHtml","formatPrice","attachEventListeners","messagesEl","height","formatTime","launcherEl","open","closeEl","close","inputEl","sendEl","e","handleSend","micEl","handleMicClick","card","m","p","slider","isDown","startX","scrollLeft","walk","player","audio","btn","bars","timeDisplay","a","otherBtn","progress","bar","i","waveform","rect","percent","stream","track","handleAudioSend","audioUrl","waveformBars","audioDuration","analyzeAudio","resolve","text","seconds","mins","secs","blob","audioContext","channelData","step","calculatedBars","start","end","sum","j","rms","toggle","destroy","updateConfig","newConfig","div","price","currency","symbol","init_widget","VERSION"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","file":"index.min.js","sourcesContent":["import type {\r\n AICommerceConfig,\r\n ChatRequest,\r\n ChatResponse,\r\n ChatContext,\r\n Session,\r\n APIError,\r\n Product,\r\n} from './types';\r\n\r\n/**\r\n * AI Commerce SDK Client\r\n * \r\n * @example\r\n * ```typescript\r\n * import { AICommerce } from '@yassirbenmoussa/aicommerce-sdk';\r\n * \r\n * const client = new AICommerce({ apiKey: 'your-api-key' });\r\n * const response = await client.chat('I need a laptop under $1000');\r\n * console.log(response.products);\r\n * ```\r\n */\r\nexport class AICommerce {\r\n private readonly apiKey: string;\r\n private readonly baseUrl: string;\r\n private readonly timeout: number;\r\n private sessionToken: string | null = null;\r\n\r\n constructor(config: AICommerceConfig) {\r\n if (!config.apiKey) {\r\n throw new Error('AICommerce: apiKey is required');\r\n }\r\n\r\n this.apiKey = config.apiKey;\r\n this.baseUrl = this.normalizeUrl(config.baseUrl || this.detectBaseUrl());\r\n this.timeout = config.timeout || 30000;\r\n }\r\n\r\n /**\r\n * Detect the base URL based on environment\r\n */\r\n private detectBaseUrl(): string {\r\n // Browser environment\r\n if (typeof window !== 'undefined') {\r\n return window.location.origin;\r\n }\r\n // Node.js or default\r\n return 'https://api.aicommerce.dev';\r\n }\r\n\r\n /**\r\n * Normalize URL (remove trailing slash)\r\n */\r\n private normalizeUrl(url: string): string {\r\n return url.replace(/\\/$/, '');\r\n }\r\n\r\n /**\r\n * Make an API request\r\n */\r\n private async request<T>(\r\n endpoint: string,\r\n options: RequestInit = {}\r\n ): Promise<T> {\r\n const url = `${this.baseUrl}${endpoint}`;\r\n\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\r\n\r\n try {\r\n const response = await fetch(url, {\r\n ...options,\r\n signal: controller.signal,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-API-Key': this.apiKey,\r\n ...(this.sessionToken && { 'X-Session-Token': this.sessionToken }),\r\n ...options.headers,\r\n },\r\n });\r\n\r\n clearTimeout(timeoutId);\r\n\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({}));\r\n const error: APIError = {\r\n code: errorData.code || 'UNKNOWN_ERROR',\r\n message: errorData.message || errorData.error || `HTTP ${response.status}`,\r\n status: response.status,\r\n };\r\n throw new AICommerceError(error.message, error.code, error.status);\r\n }\r\n\r\n return response.json();\r\n } catch (error) {\r\n clearTimeout(timeoutId);\r\n\r\n if (error instanceof AICommerceError) {\r\n throw error;\r\n }\r\n\r\n if (error instanceof Error && error.name === 'AbortError') {\r\n throw new AICommerceError('Request timeout', 'TIMEOUT', 408);\r\n }\r\n\r\n throw new AICommerceError(\r\n error instanceof Error ? error.message : 'Unknown error',\r\n 'NETWORK_ERROR',\r\n 0\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Send a chat message and get product recommendations\r\n * \r\n * @param message - The user's message or full ChatRequest object\r\n * @param context - Optional context for better recommendations\r\n * @returns Chat response with AI reply and products\r\n * \r\n * @example\r\n * ```typescript\r\n * // Simple usage\r\n * const response = await client.chat('I need running shoes');\r\n * \r\n * // With context\r\n * const response = await client.chat('I need running shoes', {\r\n * budget: { max: 150 },\r\n * preferences: ['comfortable', 'lightweight']\r\n * });\r\n * ```\r\n */\r\n async chat(\r\n message: string | ChatRequest,\r\n context?: ChatContext\r\n ): Promise<ChatResponse> {\r\n const request: ChatRequest = typeof message === 'string'\r\n ? { message, context, sessionToken: this.sessionToken || undefined }\r\n : { ...message, sessionToken: message.sessionToken || this.sessionToken || undefined };\r\n\r\n const response = await this.request<ChatResponse>('/api/v1/chat', {\r\n method: 'POST',\r\n body: JSON.stringify(request),\r\n });\r\n\r\n // Store session token for follow-up messages\r\n if (response.sessionToken) {\r\n this.sessionToken = response.sessionToken;\r\n }\r\n\r\n return response;\r\n }\r\n\r\n /**\r\n * Send an audio message and get product recommendations\r\n * \r\n * @param audioBlob - Audio blob (from MediaRecorder or file input)\r\n * @param context - Optional context for better recommendations\r\n * @returns Chat response with AI reply and products\r\n * \r\n * @example\r\n * ```typescript\r\n * // Record audio using MediaRecorder\r\n * const mediaRecorder = new MediaRecorder(stream);\r\n * const chunks: Blob[] = [];\r\n * mediaRecorder.ondataavailable = (e) => chunks.push(e.data);\r\n * mediaRecorder.onstop = async () => {\r\n * const audioBlob = new Blob(chunks, { type: 'audio/webm' });\r\n * const response = await client.chatWithAudio(audioBlob);\r\n * console.log(response.reply);\r\n * };\r\n * ```\r\n */\r\n async chatWithAudio(\r\n audioBlob: Blob,\r\n context?: ChatContext\r\n ): Promise<ChatResponse> {\r\n // Convert blob to base64\r\n const arrayBuffer = await audioBlob.arrayBuffer();\r\n const base64 = btoa(\r\n new Uint8Array(arrayBuffer).reduce(\r\n (data, byte) => data + String.fromCharCode(byte),\r\n ''\r\n )\r\n );\r\n\r\n const request: ChatRequest = {\r\n audioBase64: base64,\r\n audioMimeType: audioBlob.type || 'audio/webm',\r\n context,\r\n sessionToken: this.sessionToken || undefined,\r\n };\r\n\r\n const response = await this.request<ChatResponse>('/api/v1/chat', {\r\n method: 'POST',\r\n body: JSON.stringify(request),\r\n });\r\n\r\n // Store session token for follow-up messages\r\n if (response.sessionToken) {\r\n this.sessionToken = response.sessionToken;\r\n }\r\n\r\n return response;\r\n }\r\n\r\n /**\r\n * Create a new chat session\r\n * \r\n * @returns Session information with token\r\n */\r\n async createSession(): Promise<Session> {\r\n const response = await this.request<{ session: Session }>('/api/v1/chat/session', {\r\n method: 'POST',\r\n });\r\n\r\n this.sessionToken = response.session.token;\r\n return response.session;\r\n }\r\n\r\n /**\r\n * Clear the current session\r\n */\r\n clearSession(): void {\r\n this.sessionToken = null;\r\n }\r\n\r\n /**\r\n * Get the current session token\r\n */\r\n getSessionToken(): string | null {\r\n return this.sessionToken;\r\n }\r\n\r\n /**\r\n * Set a session token (for restoring sessions)\r\n */\r\n setSessionToken(token: string): void {\r\n this.sessionToken = token;\r\n }\r\n\r\n // ============================================\r\n // Products API\r\n // ============================================\r\n\r\n /**\r\n * Products API namespace\r\n */\r\n readonly products = {\r\n /**\r\n * Create a new product\r\n */\r\n create: async (product: CreateProductInput): Promise<ProductResponse> => {\r\n return this.request<ProductResponse>('/api/v1/products', {\r\n method: 'POST',\r\n body: JSON.stringify(product),\r\n });\r\n },\r\n\r\n /**\r\n * Batch upsert products (create or update)\r\n */\r\n batchUpsert: async (products: CreateProductInput[]): Promise<BatchUpsertResponse> => {\r\n return this.request<BatchUpsertResponse>('/api/v1/products', {\r\n method: 'POST',\r\n body: JSON.stringify({ products }),\r\n });\r\n },\r\n\r\n /**\r\n * List products with pagination\r\n */\r\n list: async (options?: ListProductsOptions): Promise<ListProductsResponse> => {\r\n const params = new URLSearchParams();\r\n if (options?.page) params.set('page', String(options.page));\r\n if (options?.perPage) params.set('perPage', String(options.perPage));\r\n if (options?.search) params.set('search', options.search);\r\n if (options?.categoryId) params.set('categoryId', options.categoryId);\r\n if (options?.isActive !== undefined) params.set('isActive', String(options.isActive));\r\n\r\n const query = params.toString();\r\n return this.request<ListProductsResponse>(`/api/v1/products${query ? `?${query}` : ''}`);\r\n },\r\n\r\n /**\r\n * Get a single product by ID\r\n */\r\n get: async (productId: string): Promise<ProductResponse> => {\r\n return this.request<ProductResponse>(`/api/v1/products/${productId}`);\r\n },\r\n\r\n /**\r\n * Update a product\r\n */\r\n update: async (productId: string, data: UpdateProductInput): Promise<ProductResponse> => {\r\n return this.request<ProductResponse>(`/api/v1/products/${productId}`, {\r\n method: 'PUT',\r\n body: JSON.stringify(data),\r\n });\r\n },\r\n\r\n /**\r\n * Delete a product\r\n */\r\n delete: async (productId: string): Promise<{ success: boolean; deleted: boolean }> => {\r\n return this.request<{ success: boolean; deleted: boolean }>(`/api/v1/products/${productId}`, {\r\n method: 'DELETE',\r\n });\r\n },\r\n };\r\n\r\n // ============================================\r\n // Upload API\r\n // ============================================\r\n\r\n /**\r\n * Upload an image file\r\n * \r\n * @example\r\n * ```typescript\r\n * // Upload from File input\r\n * const file = document.querySelector('input[type=\"file\"]').files[0];\r\n * const result = await client.upload(file);\r\n * console.log(result.url);\r\n * \r\n * // Upload and associate with product\r\n * const result = await client.upload(file, { productId: 'prod_123', isPrimary: true });\r\n * ```\r\n */\r\n async upload(file: File | Blob, options?: UploadOptions): Promise<UploadResponse> {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n\r\n if (options?.folder) formData.append('folder', options.folder);\r\n if (options?.productId) formData.append('productId', options.productId);\r\n if (options?.isPrimary) formData.append('isPrimary', 'true');\r\n\r\n const url = `${this.baseUrl}/api/v1/upload`;\r\n\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'X-API-Key': this.apiKey,\r\n },\r\n body: formData,\r\n });\r\n\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({}));\r\n throw new AICommerceError(\r\n errorData.message || errorData.error || `HTTP ${response.status}`,\r\n errorData.code || 'UPLOAD_ERROR',\r\n response.status\r\n );\r\n }\r\n\r\n return response.json();\r\n }\r\n\r\n /**\r\n * Static method for one-off chat requests\r\n * \r\n * @example\r\n * ```typescript\r\n * const response = await AICommerce.quickChat({\r\n * apiKey: 'your-api-key',\r\n * message: 'I need a laptop'\r\n * });\r\n * ```\r\n */\r\n static async quickChat(options: {\r\n apiKey: string;\r\n message: string;\r\n baseUrl?: string;\r\n context?: ChatContext;\r\n }): Promise<ChatResponse> {\r\n const client = new AICommerce({\r\n apiKey: options.apiKey,\r\n baseUrl: options.baseUrl,\r\n });\r\n return client.chat(options.message, options.context);\r\n }\r\n}\r\n\r\n// ============================================\r\n// Product Types\r\n// ============================================\r\n\r\nexport interface CreateProductInput {\r\n name: string;\r\n slug: string;\r\n description?: string;\r\n sku?: string;\r\n barcode?: string;\r\n price: number;\r\n compareAtPrice?: number;\r\n currency?: string;\r\n quantity?: number;\r\n trackInventory?: boolean;\r\n isActive?: boolean;\r\n isFeatured?: boolean;\r\n tags?: string;\r\n externalId?: string;\r\n categoryId?: string;\r\n images?: Array<{ url: string; alt?: string; isPrimary?: boolean }>;\r\n}\r\n\r\nexport interface UpdateProductInput {\r\n name?: string;\r\n slug?: string;\r\n description?: string | null;\r\n sku?: string | null;\r\n barcode?: string | null;\r\n price?: number;\r\n compareAtPrice?: number | null;\r\n currency?: string;\r\n quantity?: number;\r\n trackInventory?: boolean;\r\n isActive?: boolean;\r\n isFeatured?: boolean;\r\n tags?: string | null;\r\n categoryId?: string | null;\r\n}\r\n\r\nexport interface ProductResponse {\r\n success: boolean;\r\n product: Product;\r\n}\r\n\r\nexport interface BatchUpsertResponse {\r\n success: boolean;\r\n processed: number;\r\n errors: number;\r\n results: Array<{ id: string; slug: string; status: 'created' | 'updated' }>;\r\n errorDetails?: Array<{ slug: string; error: string }>;\r\n}\r\n\r\nexport interface ListProductsOptions {\r\n page?: number;\r\n perPage?: number;\r\n search?: string;\r\n categoryId?: string;\r\n isActive?: boolean;\r\n}\r\n\r\nexport interface ListProductsResponse {\r\n success: boolean;\r\n data: Product[];\r\n total: number;\r\n page: number;\r\n perPage: number;\r\n totalPages: number;\r\n}\r\n\r\nexport interface UploadOptions {\r\n folder?: string;\r\n productId?: string;\r\n isPrimary?: boolean;\r\n}\r\n\r\nexport interface UploadResponse {\r\n success: boolean;\r\n url: string;\r\n key: string;\r\n size: number;\r\n contentType: string;\r\n productImage?: {\r\n id: string;\r\n url: string;\r\n alt: string | null;\r\n isPrimary: boolean;\r\n };\r\n}\r\n\r\n/**\r\n * Custom error class for AI Commerce SDK\r\n */\r\nexport class AICommerceError extends Error {\r\n readonly code: string;\r\n readonly status: number;\r\n\r\n constructor(message: string, code: string, status: number) {\r\n super(message);\r\n this.name = 'AICommerceError';\r\n this.code = code;\r\n this.status = status;\r\n\r\n // Maintain proper prototype chain\r\n Object.setPrototypeOf(this, AICommerceError.prototype);\r\n }\r\n}\r\n","/**\r\n * Widget Styles (CSS-in-JS)\r\n * \r\n * Generates scoped CSS for the AI Commerce widget\r\n * Uses store's primaryColor for theming\r\n */\r\n\r\nimport type { WidgetConfig } from './types';\r\n\r\ntype ResolvedConfig = Required<Omit<WidgetConfig, 'onOpen' | 'onClose' | 'onProductClick' | 'onMessage'>>;\r\n\r\n/**\r\n * Convert hex to RGB\r\n */\r\nfunction hexToRgb(hex: string): { r: number; g: number; b: number } {\r\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\r\n return result\r\n ? {\r\n r: parseInt(result[1], 16),\r\n g: parseInt(result[2], 16),\r\n b: parseInt(result[3], 16),\r\n }\r\n : { r: 99, g: 102, b: 241 }; // Default indigo\r\n}\r\n\r\n/**\r\n * Create widget styles\r\n */\r\nexport function createWidgetStyles(config: ResolvedConfig): string {\r\n const primary = config.primaryColor;\r\n const rgb = hexToRgb(primary);\r\n const isLeft = config.position === 'bottom-left';\r\n\r\n return `\r\n/* AI Commerce Widget Styles */\r\n#aicommerce-widget {\r\n --aic-primary: ${primary};\r\n --aic-primary-rgb: ${rgb.r}, ${rgb.g}, ${rgb.b};\r\n --aic-primary-light: rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.1);\r\n --aic-primary-dark: rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.9);\r\n --aic-bg: #ffffff;\r\n --aic-bg-secondary: #f8fafc;\r\n --aic-text: #1e293b;\r\n --aic-text-secondary: #64748b;\r\n --aic-border: #e2e8f0;\r\n --aic-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\r\n --aic-radius: 16px;\r\n --aic-z-index: ${config.zIndex};\r\n \r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;\r\n font-size: 14px;\r\n line-height: 1.5;\r\n position: fixed;\r\n bottom: 20px;\r\n ${isLeft ? 'left: 20px;' : 'right: 20px;'}\r\n z-index: var(--aic-z-index);\r\n}\r\n\r\n/* Dark theme */\r\n#aicommerce-widget.aicommerce-theme-dark,\r\n@media (prefers-color-scheme: dark) {\r\n #aicommerce-widget.aicommerce-theme-auto {\r\n --aic-bg: #1e293b;\r\n --aic-bg-secondary: #0f172a;\r\n --aic-text: #f1f5f9;\r\n --aic-text-secondary: #94a3b8;\r\n --aic-border: #334155;\r\n }\r\n}\r\n\r\n/* Launcher Button */\r\n.aicommerce-launcher {\r\n width: 60px;\r\n height: 60px;\r\n border-radius: 50%;\r\n background: linear-gradient(135deg, var(--aic-primary), var(--aic-primary-dark));\r\n border: none;\r\n cursor: pointer;\r\n box-shadow: 0 4px 20px rgba(var(--aic-primary-rgb), 0.4);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n animation: aic-pulse 2s infinite;\r\n}\r\n\r\n.aicommerce-launcher:hover {\r\n transform: scale(1.1);\r\n box-shadow: 0 6px 30px rgba(var(--aic-primary-rgb), 0.5);\r\n}\r\n\r\n.aicommerce-launcher-icon {\r\n font-size: 24px;\r\n}\r\n\r\n.aicommerce-hidden {\r\n display: none !important;\r\n}\r\n\r\n@keyframes aic-pulse {\r\n 0%, 100% { box-shadow: 0 4px 20px rgba(var(--aic-primary-rgb), 0.4); }\r\n 50% { box-shadow: 0 4px 30px rgba(var(--aic-primary-rgb), 0.6); }\r\n}\r\n\r\n/* Chat Window */\r\n.aicommerce-chat {\r\n position: absolute;\r\n bottom: 0;\r\n ${isLeft ? 'left: 0;' : 'right: 0;'}\r\n width: 380px;\r\n max-width: calc(100vw - 40px);\r\n height: 600px;\r\n max-height: calc(100vh - 100px);\r\n background: var(--aic-bg);\r\n border-radius: var(--aic-radius);\r\n box-shadow: var(--aic-shadow);\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n transform-origin: bottom ${isLeft ? 'left' : 'right'};\r\n}\r\n\r\n.aicommerce-chat.aicommerce-closed {\r\n opacity: 0;\r\n transform: scale(0.9) translateY(20px);\r\n pointer-events: none;\r\n}\r\n\r\n.aicommerce-chat.aicommerce-open {\r\n opacity: 1;\r\n transform: scale(1) translateY(0);\r\n}\r\n\r\n/* Header */\r\n.aicommerce-header {\r\n background: linear-gradient(135deg, var(--aic-primary), var(--aic-primary-dark));\r\n color: white;\r\n padding: 16px 20px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n}\r\n\r\n.aicommerce-header-info {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n}\r\n\r\n.aicommerce-avatar {\r\n width: 40px;\r\n height: 40px;\r\n border-radius: 50%;\r\n background: rgba(255, 255, 255, 0.2);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 20px;\r\n overflow: hidden;\r\n}\r\n\r\n.aicommerce-avatar img {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n\r\n.aicommerce-header-text {\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.aicommerce-bot-name {\r\n font-weight: 600;\r\n font-size: 16px;\r\n}\r\n\r\n.aicommerce-status {\r\n font-size: 12px;\r\n opacity: 0.9;\r\n}\r\n\r\n.aicommerce-close {\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background: rgba(255, 255, 255, 0.2);\r\n border: none;\r\n color: white;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 16px;\r\n transition: background 0.2s;\r\n}\r\n\r\n.aicommerce-close:hover {\r\n background: rgba(255, 255, 255, 0.3);\r\n}\r\n\r\n/* Messages */\r\n.aicommerce-messages {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n background: var(--aic-bg-secondary);\r\n}\r\n\r\n.aicommerce-message {\r\n max-width: 85%;\r\n animation: aic-slide-in 0.3s ease-out;\r\n}\r\n\r\n.aicommerce-message.aicommerce-user {\r\n align-self: flex-end;\r\n}\r\n\r\n.aicommerce-message.aicommerce-assistant {\r\n align-self: flex-start;\r\n}\r\n\r\n.aicommerce-message-content {\r\n padding: 12px 16px;\r\n border-radius: 16px;\r\n line-height: 1.5;\r\n}\r\n\r\n.aicommerce-user .aicommerce-message-content {\r\n background: var(--aic-primary);\r\n color: white;\r\n border-bottom-right-radius: 4px;\r\n}\r\n\r\n.aicommerce-assistant .aicommerce-message-content {\r\n background: var(--aic-bg);\r\n color: var(--aic-text);\r\n border-bottom-left-radius: 4px;\r\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n@keyframes aic-slide-in {\r\n from { opacity: 0; transform: translateY(10px); }\r\n to { opacity: 1; transform: translateY(0); }\r\n}\r\n\r\n/* Typing Indicator */\r\n.aicommerce-typing {\r\n display: flex;\r\n gap: 4px;\r\n padding: 12px 16px;\r\n background: var(--aic-bg);\r\n border-radius: 16px;\r\n width: fit-content;\r\n}\r\n\r\n.aicommerce-typing span {\r\n width: 8px;\r\n height: 8px;\r\n background: var(--aic-text-secondary);\r\n border-radius: 50%;\r\n animation: aic-bounce 1.4s infinite ease-in-out;\r\n}\r\n\r\n.aicommerce-typing span:nth-child(1) { animation-delay: -0.32s; }\r\n.aicommerce-typing span:nth-child(2) { animation-delay: -0.16s; }\r\n\r\n@keyframes aic-bounce {\r\n 0%, 80%, 100% { transform: scale(0); }\r\n 40% { transform: scale(1); }\r\n}\r\n\r\n/* Product Cards */\r\n.aicommerce-products {\r\n display: flex;\r\n gap: 16px;\r\n margin-top: 12px;\r\n overflow-x: auto;\r\n padding-bottom: 16px;\r\n width: 100%;\r\n max-width: 100%;\r\n cursor: grab;\r\n user-select: none;\r\n -webkit-user-select: none;\r\n scrollbar-width: none; /* Firefox */\r\n}\r\n.aicommerce-products::-webkit-scrollbar {\r\n display: none; /* Chrome/Safari */\r\n}\r\n\r\n.aicommerce-product-card {\r\n flex-shrink: 0;\r\n width: 280px;\r\n background: var(--aic-bg);\r\n border-radius: 12px;\r\n overflow: hidden;\r\n cursor: pointer;\r\n transition: all 0.2s;\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\r\n}\r\n\r\n.aicommerce-product-card:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\r\n}\r\n\r\n.aicommerce-product-image {\r\n width: 100%;\r\n aspect-ratio: 16/9;\r\n height: auto;\r\n object-fit: cover;\r\n}\r\n\r\n.aicommerce-product-placeholder {\r\n width: 100%;\r\n aspect-ratio: 16/9;\r\n height: auto;\r\n background: var(--aic-bg-secondary);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 32px;\r\n}\r\n\r\n.aicommerce-product-info {\r\n padding: 10px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n}\r\n\r\n.aicommerce-product-name {\r\n font-weight: 500;\r\n font-size: 13px;\r\n color: var(--aic-text);\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.aicommerce-product-price {\r\n font-weight: 600;\r\n font-size: 14px;\r\n color: var(--aic-primary);\r\n}\r\n\r\n.aicommerce-product-desc {\r\n font-size: 12px;\r\n color: var(--aic-text-secondary);\r\n line-height: 1.4;\r\n display: -webkit-box;\r\n -webkit-line-clamp: 2;\r\n -webkit-box-orient: vertical;\r\n overflow: hidden;\r\n margin-top: 4px;\r\n}\r\n\r\n/* Audio Player */\r\n.aicommerce-audio-player {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n min-width: 240px;\r\n padding: 4px 0;\r\n}\r\n\r\n.aicommerce-audio-btn {\r\n width: 40px;\r\n height: 40px;\r\n border-radius: 50%;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border: none;\r\n cursor: pointer;\r\n transition: all 0.2s;\r\n background: rgba(255, 255, 255, 0.25);\r\n color: white;\r\n flex-shrink: 0;\r\n padding: 0;\r\n}\r\n\r\n.aicommerce-audio-btn:hover {\r\n background: rgba(255, 255, 255, 0.35);\r\n transform: scale(1.05);\r\n}\r\n\r\n.aicommerce-audio-btn:active {\r\n transform: scale(0.95);\r\n}\r\n\r\n/* Invert colors for assistant (since background is white/gray) */\r\n.aicommerce-assistant .aicommerce-audio-btn {\r\n background: var(--aic-primary);\r\n color: white;\r\n}\r\n.aicommerce-assistant .aicommerce-audio-btn:hover {\r\n background: var(--aic-primary-dark);\r\n}\r\n\r\n.aicommerce-audio-waveform {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n min-width: 0; /* Prevent overflow */\r\n}\r\n\r\n.aicommerce-waveform-bars {\r\n display: flex;\r\n align-items: center;\r\n gap: 2px;\r\n height: 24px;\r\n cursor: pointer;\r\n width: 100%;\r\n}\r\n\r\n.aicommerce-waveform-bar {\r\n width: 3px;\r\n border-radius: 2px;\r\n min-height: 3px;\r\n transition: background-color 0.1s;\r\n flex-shrink: 0;\r\n}\r\n\r\n.aicommerce-audio-time {\r\n display: flex;\r\n justify-content: space-between;\r\n font-size: 11px;\r\n font-weight: 500;\r\n}\r\n\r\n.aicommerce-user .aicommerce-audio-time {\r\n color: rgba(255, 255, 255, 0.8);\r\n}\r\n.aicommerce-assistant .aicommerce-audio-time {\r\n color: var(--aic-text-secondary);\r\n}\r\n\r\n/* RTL Support */\r\n.aicommerce-rtl {\r\n direction: rtl;\r\n text-align: right;\r\n}\r\n.aicommerce-ltr {\r\n direction: ltr;\r\n text-align: left;\r\n}\r\n\r\n/* Input Area */\r\n.aicommerce-input-container {\r\n padding: 16px 20px;\r\n background: var(--aic-bg);\r\n border-top: 1px solid var(--aic-border);\r\n display: flex;\r\n gap: 12px;\r\n}\r\n\r\n.aicommerce-input {\r\n flex: 1;\r\n padding: 12px 16px;\r\n border: 1px solid var(--aic-border);\r\n border-radius: 24px;\r\n background: var(--aic-bg-secondary);\r\n color: var(--aic-text);\r\n font-size: 14px;\r\n outline: none;\r\n transition: all 0.2s;\r\n}\r\n\r\n.aicommerce-input:focus {\r\n border-color: var(--aic-primary);\r\n box-shadow: 0 0 0 3px var(--aic-primary-light);\r\n}\r\n\r\n.aicommerce-input::placeholder {\r\n color: var(--aic-text-secondary);\r\n}\r\n\r\n.aicommerce-send {\r\n width: 44px;\r\n height: 44px;\r\n border-radius: 50%;\r\n background: var(--aic-primary);\r\n border: none;\r\n color: white;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: all 0.2s;\r\n}\r\n\r\n.aicommerce-send:hover:not(:disabled) {\r\n background: var(--aic-primary-dark);\r\n transform: scale(1.05);\r\n}\r\n\r\n.aicommerce-send:disabled {\r\n opacity: 0.6;\r\n cursor: not-allowed;\r\n}\r\n\r\n/* Microphone Button */\r\n.aicommerce-mic {\r\n width: 44px;\r\n height: 44px;\r\n border-radius: 50%;\r\n background: var(--aic-bg-secondary);\r\n border: 1px solid var(--aic-border);\r\n color: var(--aic-text-secondary);\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: all 0.2s;\r\n}\r\n\r\n.aicommerce-mic:hover:not(:disabled) {\r\n background: var(--aic-primary-light);\r\n border-color: var(--aic-primary);\r\n color: var(--aic-primary);\r\n}\r\n\r\n.aicommerce-mic.aicommerce-recording {\r\n background: #ef4444;\r\n border-color: #ef4444;\r\n color: white;\r\n animation: aic-recording-pulse 1s infinite;\r\n}\r\n\r\n.aicommerce-mic:disabled {\r\n opacity: 0.6;\r\n cursor: not-allowed;\r\n}\r\n\r\n@keyframes aic-recording-pulse {\r\n 0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }\r\n 50% { transform: scale(1.05); box-shadow: 0 0 0 8px rgba(239, 68, 68, 0); }\r\n}\r\n\r\n/* Mobile Responsive */\r\n@media (max-width: 420px) {\r\n #aicommerce-widget {\r\n bottom: 16px;\r\n ${isLeft ? 'left: 16px;' : 'right: 16px;'}\r\n }\r\n \r\n .aicommerce-chat {\r\n width: calc(100vw - 32px);\r\n height: calc(100vh - 100px);\r\n border-radius: 12px;\r\n }\r\n \r\n .aicommerce-launcher {\r\n width: 56px;\r\n height: 56px;\r\n }\r\n}\r\n\r\n/* Scrollbar */\r\n.aicommerce-messages::-webkit-scrollbar {\r\n width: 6px;\r\n}\r\n\r\n.aicommerce-messages::-webkit-scrollbar-track {\r\n background: transparent;\r\n}\r\n\r\n.aicommerce-messages::-webkit-scrollbar-thumb {\r\n background: var(--aic-border);\r\n border-radius: 3px;\r\n}\r\n\r\n.aicommerce-messages::-webkit-scrollbar-thumb:hover {\r\n background: var(--aic-text-secondary);\r\n}\r\n`;\r\n}\r\n\r\n/**\r\n * Inject styles into the document\r\n */\r\nexport function injectStyles(css: string): HTMLStyleElement {\r\n const style = document.createElement('style');\r\n style.id = 'aicommerce-widget-styles';\r\n style.textContent = css;\r\n\r\n // Remove existing styles\r\n const existing = document.getElementById('aicommerce-widget-styles');\r\n if (existing) {\r\n existing.remove();\r\n }\r\n\r\n document.head.appendChild(style);\r\n return style;\r\n}\r\n","/**\r\n * AI Commerce Chat Widget\r\n * \r\n * Embeddable chat widget for e-commerce stores\r\n * Uses store's primaryColor for theming\r\n */\r\n\r\nimport { AICommerce } from './client';\r\nimport type {\r\n WidgetConfig,\r\n WidgetInstance,\r\n StoreConfig,\r\n Product,\r\n ChatResponse\r\n} from './types';\r\nimport { createWidgetStyles, injectStyles } from './widget-styles';\r\n\r\n// Widget state\r\ninterface WidgetState {\r\n isOpen: boolean;\r\n isLoading: boolean;\r\n isRecording: boolean;\r\n messages: Array<{\r\n role: 'user' | 'assistant';\r\n content: string;\r\n products?: Product[];\r\n audioUrl?: string; // Voice message URL\r\n audioDuration?: number;\r\n waveformBars?: number[];\r\n }>;\r\n storeConfig: StoreConfig | null;\r\n}\r\n\r\n/**\r\n * Create and initialize the AI Commerce chat widget\r\n */\r\nexport function createWidget(config: WidgetConfig): WidgetInstance {\r\n // Validate config\r\n if (!config.apiKey) {\r\n throw new Error('AICommerceWidget: apiKey is required');\r\n }\r\n\r\n // Initialize client\r\n const client = new AICommerce({\r\n apiKey: config.apiKey,\r\n baseUrl: config.baseUrl,\r\n });\r\n\r\n // Widget state\r\n const state: WidgetState = {\r\n isOpen: false,\r\n isLoading: true,\r\n isRecording: false,\r\n messages: [],\r\n storeConfig: null,\r\n };\r\n\r\n // Audio recording state\r\n let mediaRecorder: MediaRecorder | null = null;\r\n let audioChunks: Blob[] = [];\r\n\r\n // DOM elements\r\n let container: HTMLDivElement | null = null;\r\n let launcher: HTMLButtonElement | null = null;\r\n let chatWindow: HTMLDivElement | null = null;\r\n let styleElement: HTMLStyleElement | null = null;\r\n\r\n // Resolved config (merged with store config)\r\n let resolvedConfig: Required<Omit<WidgetConfig, 'onOpen' | 'onClose' | 'onProductClick' | 'onMessage'>> &\r\n Pick<WidgetConfig, 'onOpen' | 'onClose' | 'onProductClick' | 'onMessage'>;\r\n\r\n /**\r\n * Fetch store configuration\r\n */\r\n async function fetchStoreConfig(): Promise<StoreConfig | null> {\r\n try {\r\n const baseUrl = config.baseUrl || detectBaseUrl();\r\n const response = await fetch(`${baseUrl}/api/v1/store`, {\r\n headers: { 'x-api-key': config.apiKey },\r\n });\r\n\r\n if (!response.ok) return null;\r\n\r\n const data = await response.json();\r\n return data.store;\r\n } catch (error) {\r\n console.error('Failed to fetch store config:', error);\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Detect base URL\r\n */\r\n function detectBaseUrl(): string {\r\n if (typeof window !== 'undefined') {\r\n // Check for data attribute on script tag\r\n const script = document.querySelector('script[data-aicommerce-url]');\r\n if (script) {\r\n return script.getAttribute('data-aicommerce-url') || '';\r\n }\r\n }\r\n return 'https://api.aicommerce.dev';\r\n }\r\n\r\n /**\r\n * Initialize the widget\r\n */\r\n async function initialize(): Promise<void> {\r\n // Fetch store config\r\n state.storeConfig = await fetchStoreConfig();\r\n\r\n // Merge configs\r\n resolvedConfig = {\r\n apiKey: config.apiKey,\r\n baseUrl: config.baseUrl || detectBaseUrl(),\r\n position: config.position || 'bottom-right',\r\n theme: config.theme || 'auto',\r\n primaryColor: config.primaryColor || state.storeConfig?.primaryColor || '#6366f1',\r\n welcomeMessage: config.welcomeMessage || state.storeConfig?.welcomeMessage || 'Hi! How can I help you find the perfect product today?',\r\n botName: config.botName || state.storeConfig?.chatBotName || 'Shopping Assistant',\r\n zIndex: config.zIndex || 9999,\r\n buttonText: config.buttonText || '💬',\r\n hideLauncher: config.hideLauncher || false,\r\n onOpen: config.onOpen,\r\n onClose: config.onClose,\r\n onProductClick: config.onProductClick,\r\n onMessage: config.onMessage,\r\n };\r\n\r\n // Inject styles\r\n const styles = createWidgetStyles(resolvedConfig);\r\n styleElement = injectStyles(styles);\r\n\r\n // Create container\r\n container = document.createElement('div');\r\n container.id = 'aicommerce-widget';\r\n container.className = `aicommerce-widget aicommerce-${resolvedConfig.position} aicommerce-theme-${resolvedConfig.theme}`;\r\n document.body.appendChild(container);\r\n\r\n // Render widget\r\n render();\r\n\r\n // Add welcome message\r\n state.messages.push({\r\n role: 'assistant',\r\n content: resolvedConfig.welcomeMessage,\r\n });\r\n state.isLoading = false;\r\n render();\r\n }\r\n\r\n /**\r\n * Render the widget\r\n */\r\n function render(): void {\r\n if (!container) return;\r\n\r\n const html = `\r\n ${!resolvedConfig.hideLauncher ? `\r\n <button class=\"aicommerce-launcher ${state.isOpen ? 'aicommerce-hidden' : ''}\" aria-label=\"Open chat\">\r\n <span class=\"aicommerce-launcher-icon\">${resolvedConfig.buttonText}</span>\r\n </button>\r\n ` : ''}\r\n \r\n <div class=\"aicommerce-chat ${state.isOpen ? 'aicommerce-open' : 'aicommerce-closed'}\">\r\n <div class=\"aicommerce-header\">\r\n <div class=\"aicommerce-header-info\">\r\n <div class=\"aicommerce-avatar\">\r\n ${state.storeConfig?.logo\r\n ? `<img src=\"${state.storeConfig.logo}\" alt=\"${resolvedConfig.botName}\" />`\r\n : `<span>🤖</span>`\r\n }\r\n </div>\r\n <div class=\"aicommerce-header-text\">\r\n <span class=\"aicommerce-bot-name\">${resolvedConfig.botName}</span>\r\n <span class=\"aicommerce-status\">Online</span>\r\n </div>\r\n </div>\r\n <button class=\"aicommerce-close\" aria-label=\"Close chat\">✕</button>\r\n </div>\r\n \r\n <div class=\"aicommerce-messages\">\r\n ${state.messages.map((msg, index) => {\r\n const isRtl = isArabic(msg.content);\r\n const isUser = msg.role === 'user';\r\n\r\n return `\r\n <div class=\"aicommerce-message aicommerce-${msg.role}\">\r\n <div class=\"aicommerce-message-content ${isRtl ? 'aicommerce-rtl' : 'aicommerce-ltr'}\">\r\n ${msg.audioUrl ? renderAudioPlayer(msg, index, isUser) : escapeHtml(msg.content)}\r\n </div>\r\n ${msg.products && msg.products.length > 0 ? `\r\n <div class=\"aicommerce-products\">\r\n ${msg.products.map(product => `\r\n <div class=\"aicommerce-product-card\" data-product-id=\"${product.id}\">\r\n ${(product.image || product.imageUrl) ? `\r\n <img src=\"${product.image || product.imageUrl}\" alt=\"${escapeHtml(product.name)}\" class=\"aicommerce-product-image\" />\r\n ` : `\r\n <div class=\"aicommerce-product-placeholder\">📦</div>\r\n `}\r\n <div class=\"aicommerce-product-info\">\r\n <span class=\"aicommerce-product-name\" title=\"${escapeHtml(product.name)}\">${escapeHtml(product.name)}</span>\r\n ${product.description ? `<p class=\"aicommerce-product-desc\">${escapeHtml(product.description)}</p>` : ''}\r\n <span class=\"aicommerce-product-price\">${formatPrice(product.price, product.currency)}</span>\r\n </div>\r\n </div>\r\n `).join('')}\r\n </div>\r\n ` : ''}\r\n </div>\r\n `}).join('')}\r\n ${state.isLoading ? `\r\n <div class=\"aicommerce-message aicommerce-assistant\">\r\n <div class=\"aicommerce-typing\">\r\n <span></span><span></span><span></span>\r\n </div>\r\n </div>\r\n ` : ''}\r\n </div>\r\n \r\n <div class=\"aicommerce-input-container\">\r\n <input \r\n type=\"text\" \r\n class=\"aicommerce-input\" \r\n placeholder=\"Type your message...\"\r\n ${state.isLoading || state.isRecording ? 'disabled' : ''}\r\n />\r\n <button class=\"aicommerce-mic ${state.isRecording ? 'aicommerce-recording' : ''}\" ${state.isLoading ? 'disabled' : ''} aria-label=\"${state.isRecording ? 'Stop recording' : 'Voice input'}\">\r\n ${state.isRecording ? `\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\"/>\r\n </svg>\r\n ` : `\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"/>\r\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\r\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"/>\r\n <line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\"/>\r\n </svg>\r\n `}\r\n </button>\r\n <button class=\"aicommerce-send\" ${state.isLoading || state.isRecording ? 'disabled' : ''} aria-label=\"Send message\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M22 2L11 13M22 2L15 22L11 13M22 2L2 9L11 13\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n `;\r\n\r\n container.innerHTML = html;\r\n\r\n // Attach event listeners\r\n attachEventListeners();\r\n\r\n // Scroll to bottom\r\n const messagesEl = container.querySelector('.aicommerce-messages');\r\n if (messagesEl) {\r\n messagesEl.scrollTop = messagesEl.scrollHeight;\r\n }\r\n }\r\n\r\n function renderAudioPlayer(msg: any, index: number, isUser: boolean): string {\r\n return `\r\n <div class=\"aicommerce-audio-player\" data-message-index=\"${index}\">\r\n <button class=\"aicommerce-audio-btn\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"2\"><polygon points=\"5 3 19 12 5 21 5 3\"></polygon></svg>\r\n </button>\r\n <div class=\"aicommerce-audio-waveform\">\r\n <div class=\"aicommerce-waveform-bars\">\r\n ${(msg.waveformBars || Array(40).fill(10)).map((height: number) => `\r\n <div class=\"aicommerce-waveform-bar\" style=\"height: ${height}%; background-color: ${isUser ? 'rgba(255,255,255,0.4)' : 'rgba(99,102,241,0.3)'}\"></div>\r\n `).join('')}\r\n </div>\r\n <div class=\"aicommerce-audio-time\">\r\n <span class=\"aicommerce-current-time\">0:00</span>\r\n <span>${formatTime(msg.audioDuration || 0)}</span>\r\n </div>\r\n </div>\r\n <audio src=\"${msg.audioUrl}\" preload=\"metadata\"></audio>\r\n </div>\r\n `;\r\n }\r\n\r\n /**\r\n * Attach event listeners\r\n */\r\n function attachEventListeners(): void {\r\n if (!container) return;\r\n\r\n // Launcher click\r\n const launcherEl = container.querySelector('.aicommerce-launcher');\r\n if (launcherEl) {\r\n launcherEl.addEventListener('click', () => open());\r\n }\r\n\r\n // Close button\r\n const closeEl = container.querySelector('.aicommerce-close');\r\n if (closeEl) {\r\n closeEl.addEventListener('click', () => close());\r\n }\r\n\r\n // Input\r\n const inputEl = container.querySelector('.aicommerce-input') as HTMLInputElement;\r\n const sendEl = container.querySelector('.aicommerce-send');\r\n\r\n if (inputEl) {\r\n inputEl.addEventListener('keypress', (e) => {\r\n if (e.key === 'Enter' && inputEl.value.trim()) {\r\n handleSend(inputEl.value.trim());\r\n inputEl.value = '';\r\n }\r\n });\r\n }\r\n\r\n if (sendEl && inputEl) {\r\n sendEl.addEventListener('click', () => {\r\n if (inputEl.value.trim()) {\r\n handleSend(inputEl.value.trim());\r\n inputEl.value = '';\r\n }\r\n });\r\n }\r\n\r\n // Microphone button\r\n const micEl = container.querySelector('.aicommerce-mic');\r\n if (micEl) {\r\n micEl.addEventListener('click', () => handleMicClick());\r\n }\r\n\r\n // Product clicks\r\n const productCards = container.querySelectorAll('.aicommerce-product-card');\r\n productCards.forEach(card => {\r\n card.addEventListener('click', () => {\r\n const productId = card.getAttribute('data-product-id');\r\n const product = state.messages\r\n .flatMap(m => m.products || [])\r\n .find(p => p.id === productId);\r\n\r\n if (product && resolvedConfig.onProductClick) {\r\n resolvedConfig.onProductClick(product);\r\n }\r\n });\r\n });\r\n\r\n // Product Sliders (Drag to Scroll)\r\n const sliders = container.querySelectorAll('.aicommerce-products') as NodeListOf<HTMLElement>;\r\n sliders.forEach(slider => {\r\n let isDown = false;\r\n let startX = 0;\r\n let scrollLeft = 0;\r\n\r\n slider.addEventListener('mousedown', (e) => {\r\n isDown = true;\r\n slider.style.cursor = 'grabbing';\r\n startX = e.pageX - slider.offsetLeft;\r\n scrollLeft = slider.scrollLeft;\r\n });\r\n slider.addEventListener('mouseleave', () => {\r\n isDown = false;\r\n slider.style.cursor = 'grab';\r\n });\r\n slider.addEventListener('mouseup', () => {\r\n isDown = false;\r\n slider.style.cursor = 'grab';\r\n });\r\n slider.addEventListener('mousemove', (e) => {\r\n if (!isDown) return;\r\n e.preventDefault();\r\n const x = e.pageX - slider.offsetLeft;\r\n const walk = (x - startX) * 2; // scroll-fast\r\n slider.scrollLeft = scrollLeft - walk;\r\n });\r\n });\r\n\r\n // Audio Players\r\n const audioPlayers = container.querySelectorAll('.aicommerce-audio-player');\r\n audioPlayers.forEach(player => {\r\n const audio = player.querySelector('audio');\r\n const btn = player.querySelector('.aicommerce-audio-btn');\r\n const bars = player.querySelectorAll('.aicommerce-waveform-bar');\r\n const timeDisplay = player.querySelector('.aicommerce-current-time');\r\n\r\n if (!audio || !btn) return;\r\n\r\n // Toggle Play\r\n btn.addEventListener('click', () => {\r\n const isPlaying = !audio.paused;\r\n\r\n if (!isPlaying) {\r\n // Pause all others\r\n container?.querySelectorAll('audio').forEach((a: HTMLAudioElement) => {\r\n if (a !== audio && !a.paused) {\r\n a.pause();\r\n const parent = a.closest('.aicommerce-audio-player');\r\n const otherBtn = parent?.querySelector('.aicommerce-audio-btn');\r\n if (otherBtn) otherBtn.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"2\"><polygon points=\"5 3 19 12 5 21 5 3\"></polygon></svg>`;\r\n }\r\n });\r\n\r\n audio.play();\r\n btn.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"6\" y=\"4\" width=\"4\" height=\"16\"></rect><rect x=\"14\" y=\"4\" width=\"4\" height=\"16\"></rect></svg>`;\r\n } else {\r\n audio.pause();\r\n btn.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"2\"><polygon points=\"5 3 19 12 5 21 5 3\"></polygon></svg>`;\r\n }\r\n });\r\n\r\n audio.addEventListener('timeupdate', () => {\r\n if (timeDisplay) timeDisplay.textContent = formatTime(audio.currentTime);\r\n\r\n if (audio.duration) {\r\n const progress = (audio.currentTime / audio.duration) * 100;\r\n bars.forEach((bar, i) => {\r\n const barPos = (i / bars.length) * 100;\r\n if (barPos <= progress) {\r\n (bar as HTMLElement).style.backgroundColor = player.closest('.aicommerce-user') ? 'rgba(255,255,255,1)' : 'var(--aic-primary)';\r\n } else {\r\n (bar as HTMLElement).style.backgroundColor = player.closest('.aicommerce-user') ? 'rgba(255,255,255,0.4)' : 'rgba(99,102,241,0.3)';\r\n }\r\n });\r\n }\r\n });\r\n\r\n audio.addEventListener('ended', () => {\r\n btn.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"2\"><polygon points=\"5 3 19 12 5 21 5 3\"></polygon></svg>`;\r\n });\r\n\r\n const waveform = player.querySelector('.aicommerce-waveform-bars');\r\n if (waveform) {\r\n waveform.addEventListener('click', (e: any) => {\r\n const rect = waveform.getBoundingClientRect();\r\n const x = e.clientX - rect.left;\r\n const percent = x / rect.width;\r\n if (audio.duration) {\r\n audio.currentTime = percent * audio.duration;\r\n }\r\n });\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Handle microphone button click\r\n */\r\n async function handleMicClick(): Promise<void> {\r\n if (state.isRecording) {\r\n // Stop recording\r\n if (mediaRecorder && mediaRecorder.state !== 'inactive') {\r\n mediaRecorder.stop();\r\n }\r\n } else {\r\n // Start recording\r\n try {\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\r\n audioChunks = [];\r\n\r\n mediaRecorder = new MediaRecorder(stream, {\r\n mimeType: MediaRecorder.isTypeSupported('audio/webm') ? 'audio/webm' : 'audio/mp4'\r\n });\r\n\r\n mediaRecorder.ondataavailable = (e) => {\r\n if (e.data.size > 0) {\r\n audioChunks.push(e.data);\r\n }\r\n };\r\n\r\n mediaRecorder.onstop = async () => {\r\n // Stop all tracks\r\n stream.getTracks().forEach(track => track.stop());\r\n\r\n if (audioChunks.length > 0) {\r\n const audioBlob = new Blob(audioChunks, { type: mediaRecorder?.mimeType || 'audio/webm' });\r\n await handleAudioSend(audioBlob);\r\n }\r\n\r\n state.isRecording = false;\r\n render();\r\n };\r\n\r\n mediaRecorder.start();\r\n state.isRecording = true;\r\n render();\r\n } catch (error) {\r\n console.error('Failed to start recording:', error);\r\n state.messages.push({\r\n role: 'assistant',\r\n content: 'Unable to access microphone. Please check your permissions.',\r\n });\r\n render();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Handle sending audio message\r\n */\r\n async function handleAudioSend(audioBlob: Blob): Promise<ChatResponse> {\r\n // Create audio URL for playback\r\n const audioUrl = URL.createObjectURL(audioBlob);\r\n\r\n // Analyze audio for waveform\r\n let waveformBars: number[] = Array(40).fill(10);\r\n let audioDuration = 0;\r\n\r\n try {\r\n waveformBars = await analyzeAudio(audioBlob);\r\n\r\n // Get duration\r\n const audio = new Audio(audioUrl);\r\n await new Promise<void>((resolve) => {\r\n audio.onloadedmetadata = () => {\r\n audioDuration = audio.duration;\r\n resolve();\r\n };\r\n audio.onerror = () => resolve();\r\n });\r\n } catch (e) {\r\n console.error(\"Audio analysis failed\", e);\r\n }\r\n\r\n // Add user message with audio\r\n state.messages.push({\r\n role: 'user',\r\n content: 'Voice message',\r\n audioUrl,\r\n audioDuration,\r\n waveformBars\r\n });\r\n state.isLoading = true;\r\n render();\r\n\r\n try {\r\n const response = await client.chatWithAudio(audioBlob);\r\n\r\n // Add assistant message\r\n state.messages.push({\r\n role: 'assistant',\r\n content: response.reply,\r\n products: response.products,\r\n });\r\n\r\n if (resolvedConfig.onMessage) {\r\n resolvedConfig.onMessage('Voice message', response);\r\n }\r\n\r\n return response;\r\n } catch (error) {\r\n state.messages.push({\r\n role: 'assistant',\r\n content: 'Sorry, I encountered an error processing your voice message. Please try again.',\r\n });\r\n throw error;\r\n } finally {\r\n state.isLoading = false;\r\n render();\r\n }\r\n }\r\n\r\n /**\r\n * Helpers\r\n */\r\n function isArabic(text: string): boolean {\r\n return /[\\u0600-\\u06FF\\u0750-\\u077F\\u08A0-\\u08FF\\uFB50-\\uFDFF\\uFE70-\\uFEFF]/.test(text);\r\n }\r\n\r\n function formatTime(seconds: number): string {\r\n const mins = Math.floor(seconds / 60);\r\n const secs = Math.floor(seconds % 60);\r\n return `${mins}:${secs.toString().padStart(2, '0')}`;\r\n }\r\n\r\n async function analyzeAudio(blob: Blob): Promise<number[]> {\r\n try {\r\n const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();\r\n const arrayBuffer = await blob.arrayBuffer();\r\n const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\r\n const channelData = audioBuffer.getChannelData(0);\r\n\r\n const bars = 40;\r\n const step = Math.floor(channelData.length / bars);\r\n const calculatedBars = [];\r\n\r\n for (let i = 0; i < bars; i++) {\r\n const start = i * step;\r\n const end = start + step;\r\n let sum = 0;\r\n for (let j = start; j < end; j++) {\r\n if (channelData[j]) sum += channelData[j] * channelData[j];\r\n }\r\n const rms = Math.sqrt(sum / step);\r\n const height = Math.min(100, Math.max(10, rms * 400));\r\n calculatedBars.push(height);\r\n }\r\n return calculatedBars;\r\n } catch (e) {\r\n console.error(\"Analysis error\", e);\r\n return Array.from({ length: 40 }, () => 20 + Math.random() * 60);\r\n }\r\n }\r\n\r\n /**\r\n * Handle sending a message\r\n */\r\n async function handleSend(message: string): Promise<ChatResponse> {\r\n // Add user message\r\n state.messages.push({ role: 'user', content: message });\r\n state.isLoading = true;\r\n render();\r\n\r\n try {\r\n const response = await client.chat(message);\r\n\r\n // Add assistant message\r\n state.messages.push({\r\n role: 'assistant',\r\n content: response.reply,\r\n products: response.products,\r\n });\r\n\r\n if (resolvedConfig.onMessage) {\r\n resolvedConfig.onMessage(message, response);\r\n }\r\n\r\n return response;\r\n } catch (error) {\r\n state.messages.push({\r\n role: 'assistant',\r\n content: 'Sorry, I encountered an error. Please try again.',\r\n });\r\n throw error;\r\n } finally {\r\n state.isLoading = false;\r\n render();\r\n }\r\n }\r\n\r\n /**\r\n * Open the widget\r\n */\r\n function open(): void {\r\n state.isOpen = true;\r\n render();\r\n resolvedConfig.onOpen?.();\r\n\r\n // Focus input\r\n setTimeout(() => {\r\n const input = container?.querySelector('.aicommerce-input') as HTMLInputElement;\r\n input?.focus();\r\n }, 100);\r\n }\r\n\r\n /**\r\n * Close the widget\r\n */\r\n function close(): void {\r\n state.isOpen = false;\r\n render();\r\n resolvedConfig.onClose?.();\r\n }\r\n\r\n /**\r\n * Toggle the widget\r\n */\r\n function toggle(): void {\r\n if (state.isOpen) {\r\n close();\r\n } else {\r\n open();\r\n }\r\n }\r\n\r\n /**\r\n * Destroy the widget\r\n */\r\n function destroy(): void {\r\n if (container) {\r\n container.remove();\r\n container = null;\r\n }\r\n if (styleElement) {\r\n styleElement.remove();\r\n styleElement = null;\r\n }\r\n }\r\n\r\n /**\r\n * Update configuration\r\n */\r\n function updateConfig(newConfig: Partial<WidgetConfig>): void {\r\n Object.assign(resolvedConfig, newConfig);\r\n\r\n if (newConfig.primaryColor) {\r\n const styles = createWidgetStyles(resolvedConfig);\r\n if (styleElement) {\r\n styleElement.textContent = styles;\r\n }\r\n }\r\n\r\n render();\r\n }\r\n\r\n // Utility functions\r\n function escapeHtml(text: string): string {\r\n const div = document.createElement('div');\r\n div.textContent = text;\r\n return div.innerHTML;\r\n }\r\n\r\n function formatPrice(price: number, currency?: string): string {\r\n const symbols: Record<string, string> = {\r\n USD: '$', EUR: '€', GBP: '£', MAD: 'DH',\r\n SAR: 'SAR', AED: 'AED', JPY: '¥', CNY: '¥',\r\n };\r\n const symbol = symbols[currency || 'USD'] || currency || '$';\r\n return `${price.toFixed(2)} ${symbol}`;\r\n }\r\n\r\n // Initialize and return instance\r\n initialize();\r\n\r\n return {\r\n open,\r\n close,\r\n toggle,\r\n destroy,\r\n sendMessage: handleSend,\r\n updateConfig,\r\n };\r\n}\r\n\r\n/**\r\n * Global widget initialization for script tag usage\r\n */\r\nexport const AICommerceWidget = {\r\n init: createWidget,\r\n VERSION: '1.0.0',\r\n};\r\n\r\n// Auto-attach to window for UMD builds\r\nif (typeof window !== 'undefined') {\r\n (window as unknown as Record<string, unknown>).AICommerceWidget = AICommerceWidget;\r\n}\r\n","/**\r\n * AI Commerce SDK\r\n * \r\n * AI-powered product recommendations for e-commerce\r\n * \r\n * @packageDocumentation\r\n * @module @yassirbenmoussa/aicommerce-sdk\r\n * \r\n * @example\r\n * ```typescript\r\n * // npm usage - Client API\r\n * import { AICommerce } from '@yassirbenmoussa/aicommerce-sdk';\r\n * \r\n * const client = new AICommerce({ apiKey: 'your-api-key' });\r\n * const response = await client.chat('I need a laptop');\r\n * console.log(response.products);\r\n * ```\r\n * \r\n * @example\r\n * ```html\r\n * <!-- Widget usage -->\r\n * <script src=\"https://cdn.aicommerce.dev/widget.min.js\"></script>\r\n * <script>\r\n * AICommerceWidget.init({\r\n * apiKey: 'your-api-key',\r\n * position: 'bottom-right',\r\n * theme: 'auto'\r\n * });\r\n * </script>\r\n * ```\r\n */\r\n\r\n// Export main client\r\nexport { AICommerce, AICommerceError } from './client';\r\n\r\n// Export widget\r\nexport { createWidget, AICommerceWidget } from './widget';\r\n\r\n// Export all types\r\nexport type {\r\n // Client types\r\n AICommerceConfig,\r\n ChatRequest,\r\n ChatResponse,\r\n ChatContext,\r\n Product,\r\n Session,\r\n APIError,\r\n EventType,\r\n EventCallback,\r\n // Widget types\r\n StoreConfig,\r\n WidgetConfig,\r\n WidgetInstance,\r\n} from './types';\r\n\r\n// Version\r\nexport const VERSION = '1.0.0';\r\n\r\n// For UMD builds, attach to window\r\nif (typeof window !== 'undefined') {\r\n // @ts-expect-error - Attaching to window for UMD\r\n window.AICommerce = require('./client').AICommerce;\r\n // @ts-expect-error - Attaching to window for UMD\r\n window.AICommerceError = require('./client').AICommerceError;\r\n // @ts-expect-error - Attaching to window for UMD\r\n window.AICommerceWidget = require('./widget').AICommerceWidget;\r\n}\r\n"]}