@huskel/sdk 0.4.0 → 0.4.1

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/api.ts","../src/client.ts","../src/hooks/useHuskel.ts","../src/hooks/useSearch.ts","../src/components/HuskelProvider.tsx","../src/hooks/useIngest.ts","../src/hooks/usePageIngest.ts","../src/hooks/useChat.ts","../src/components/SearchBar.tsx","../src/components/Sparkle.tsx","../src/components/ChatWidget.tsx","../src/components/AIChatButton.tsx"],"sourcesContent":["export { initHuskel, getHuskelClient, HuskelClient } from './client';\r\nexport { HuskelAPI } from './api';\r\nexport { useHuskel } from './hooks/useHuskel';\r\nexport { useSearch } from './hooks/useSearch';\r\nexport { useIngest } from './hooks/useIngest';\r\nexport { usePageIngest } from './hooks/usePageIngest';\r\nexport { useChat } from './hooks/useChat';\r\nexport type { ChatMessage, ChatSource } from './hooks/useChat';\r\nexport { SearchBar } from './components/SearchBar';\r\nexport { Sparkle } from './components/Sparkle';\r\nexport { ChatWidget } from './components/ChatWidget';\r\nexport { AIChatButton } from './components/AIChatButton';\r\nexport { HuskelProvider } from './components/HuskelProvider';\r\nexport type {\r\n Product,\r\n RawProductInput,\r\n HuskelConfig,\r\n SearchRequest,\r\n SearchResult,\r\n SearchResponse,\r\n IngestResponse,\r\n HuskelError,\r\n} from './types';\r\n","import { Product, SearchResponse, IngestResponse, HuskelError } from './types';\r\n\r\nconst MAX_RETRIES = 3;\r\nconst RETRY_DELAYS = [500, 1000, 2000]; // ms\r\n\r\nfunction log(level: 'info' | 'warn' | 'error', msg: string, data?: unknown) {\r\n const prefix = '[Huskel]';\r\n if (level === 'error') console.error(prefix, msg, data ?? '');\r\n else if (level === 'warn') console.warn(prefix, msg, data ?? '');\r\n else console.log(prefix, msg, data ?? '');\r\n}\r\n\r\nasync function sleep(ms: number) {\r\n return new Promise(r => setTimeout(r, ms));\r\n}\r\n\r\nexport class HuskelAPI {\r\n constructor(\r\n private apiUrl: string,\r\n private siteId: string,\r\n private apiToken: string,\r\n private getShopperId?: () => string | undefined,\r\n private getSessionId?: () => string | undefined\r\n ) {}\r\n\r\n private async post<T>(path: string, body: unknown, attempt = 0): Promise<T> {\r\n const url = `${this.apiUrl}${path}`;\r\n\r\n try {\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n 'X-Huskel-Token': this.apiToken,\r\n 'X-Huskel-Site': this.siteId,\r\n };\r\n\r\n const shopperId = this.getShopperId?.();\r\n if (shopperId) {\r\n headers['X-Huskel-Shopper-Id'] = shopperId;\r\n }\r\n\r\n const sessionId = this.getSessionId?.();\r\n if (sessionId) {\r\n headers['X-Huskel-Session-Id'] = sessionId;\r\n }\r\n\r\n const res = await fetch(url, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body),\r\n });\r\n\r\n if (!res.ok) {\r\n const text = await res.text();\r\n const err: HuskelError = { status: res.status, message: text };\r\n\r\n // Don't retry 4xx — developer errors\r\n if (res.status >= 400 && res.status < 500) {\r\n log('error', `${path} failed [${res.status}]`, text);\r\n throw err;\r\n }\r\n\r\n // Retry 5xx\r\n if (attempt < MAX_RETRIES - 1) {\r\n log('warn', `${path} [${res.status}] retrying (${attempt + 1}/${MAX_RETRIES})...`);\r\n await sleep(RETRY_DELAYS[attempt]);\r\n return this.post(path, body, attempt + 1);\r\n }\r\n\r\n log('error', `${path} failed after ${MAX_RETRIES} attempts`, err);\r\n throw err;\r\n }\r\n\r\n return res.json();\r\n } catch (e) {\r\n // Network error (offline, DNS, etc.)\r\n if ((e as HuskelError).status === undefined) {\r\n if (attempt < MAX_RETRIES - 1) {\r\n log('warn', `${path} network error, retrying (${attempt + 1}/${MAX_RETRIES})...`);\r\n await sleep(RETRY_DELAYS[attempt]);\r\n return this.post(path, body, attempt + 1);\r\n }\r\n log('error', `${path} unreachable after ${MAX_RETRIES} attempts`);\r\n }\r\n throw e;\r\n }\r\n }\r\n\r\n async ingest(product: Product): Promise<IngestResponse> {\r\n log('info', 'ingesting product', product.name);\r\n return this.post('/ingest', { siteId: this.siteId, product });\r\n }\r\n\r\n async ingestBatch(products: Product[]): Promise<IngestResponse> {\r\n log('info', `ingesting batch of ${products.length} products`);\r\n return this.post('/ingest/batch', { siteId: this.siteId, products });\r\n }\r\n\r\n async search(query: string, limit = 10): Promise<SearchResponse> {\r\n log('info', 'search query', query);\r\n return this.post('/search', { query, siteId: this.siteId, limit });\r\n }\r\n\r\n // Pure vector search — no LLM, instant results. This is what the SearchBar uses.\r\n async searchVector(query: string, limit = 10): Promise<SearchResponse> {\r\n return this.post('/search/vector', { query, siteId: this.siteId, limit });\r\n }\r\n\r\n // Autocomplete — pure in-memory Trie, <1ms, no Upstash call. Only true prefix matches.\r\n async searchAutocomplete(query: string, limit = 8): Promise<SearchResponse> {\r\n return this.post('/search/autocomplete', { query, siteId: this.siteId, limit });\r\n }\r\n\r\n // LLM chat — conversational search with history context.\r\n async chat(query: string, history: Array<{ role: 'user' | 'assistant'; content: string }> = []): Promise<{ answer: string; sources: any[] }> {\r\n log('info', 'chat query', query);\r\n return this.post('/chat', { query, siteId: this.siteId, history });\r\n }\r\n}\r\n","import { HuskelConfig, Product, RawProductInput } from './types';\r\nimport { HuskelAPI } from './api';\r\n\r\nfunction getEnvVar(key: string): string | undefined {\r\n if (typeof globalThis !== 'undefined') {\r\n const g = globalThis as any;\r\n if (g.process && g.process.env) {\r\n return g.process.env[key];\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\nfunction mapRawProduct(input: RawProductInput): Product | null {\r\n const name = input.name || input.title || input.productName || '';\r\n \r\n let price = '';\r\n let priceNumeric: number | undefined = undefined;\r\n\r\n if (input.price !== undefined) {\r\n if (typeof input.price === 'number') {\r\n priceNumeric = input.price;\r\n price = String(input.price);\r\n } else {\r\n price = input.price;\r\n const num = parseFloat(input.price.replace(/[^0-9.]/g, ''));\r\n priceNumeric = isNaN(num) ? undefined : num;\r\n }\r\n }\r\n if (input.priceNumeric !== undefined) {\r\n priceNumeric = input.priceNumeric;\r\n }\r\n\r\n let url = input.url || '';\r\n if (!url && typeof window !== 'undefined') {\r\n url = window.location.href;\r\n }\r\n\r\n let slug = input.slug || input.id || input.productId || '';\r\n if (!slug && url) {\r\n slug = url.split('/').filter(Boolean).pop() || '';\r\n }\r\n if (!slug && name) {\r\n slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');\r\n }\r\n\r\n let images: string[] = [];\r\n if (input.images) {\r\n images = input.images;\r\n } else if (input.image) {\r\n images = [input.image];\r\n } else if (input.thumbnail) {\r\n images = [input.thumbnail];\r\n }\r\n\r\n if (!name) {\r\n console.warn('[Huskel] Validation warning: Product name/title is missing. Skipping:', input);\r\n return null;\r\n }\r\n if (!price) {\r\n console.warn('[Huskel] Validation warning: Product price is missing. Skipping:', input);\r\n return null;\r\n }\r\n if (!url) {\r\n console.warn('[Huskel] Validation warning: Product URL is missing. Skipping:', input);\r\n return null;\r\n }\r\n\r\n return {\r\n name,\r\n price,\r\n url,\r\n brand: input.brand,\r\n description: input.description,\r\n originalPrice: input.originalPrice,\r\n discount: input.discount,\r\n currency: input.currency ?? 'KES',\r\n stock: input.stock,\r\n availability: input.availability,\r\n rating: input.rating,\r\n reviewCount: input.reviewCount,\r\n category: input.category,\r\n subCategory: input.subCategory,\r\n tags: input.tags,\r\n images: images.length > 0 ? images : undefined,\r\n specs: input.specs,\r\n priceNumeric,\r\n slug,\r\n };\r\n}\r\n\r\nfunction generateUUID(): string {\r\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\r\n return crypto.randomUUID();\r\n }\r\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n}\r\n\r\nexport class HuskelClient {\r\n readonly api: HuskelAPI;\r\n private ingestQueue: Product[] = [];\r\n private ingestTimer: ReturnType<typeof setTimeout> | null = null;\r\n private ingestedUrls = new Set<string>();\r\n private onlineHandler: (() => void) | null = null;\r\n private shopperId?: string;\r\n private sessionId: string = '';\r\n\r\n private static INGEST_CACHE_KEY = 'huskel_ingested_v1';\r\n private static INGEST_CACHE_TTL = 24 * 60 * 60 * 1000; // 24h\r\n\r\n private loadIngestedCache() {\r\n if (typeof window === 'undefined') return;\r\n try {\r\n const raw = localStorage.getItem(HuskelClient.INGEST_CACHE_KEY);\r\n if (!raw) return;\r\n const { ts, urls }: { ts: number; urls: string[] } = JSON.parse(raw);\r\n if (Date.now() - ts > HuskelClient.INGEST_CACHE_TTL) {\r\n localStorage.removeItem(HuskelClient.INGEST_CACHE_KEY);\r\n return;\r\n }\r\n this.ingestedUrls = new Set(urls);\r\n } catch { /* ignore */ }\r\n }\r\n\r\n private saveIngestedCache() {\r\n if (typeof window === 'undefined') return;\r\n try {\r\n localStorage.setItem(\r\n HuskelClient.INGEST_CACHE_KEY,\r\n JSON.stringify({ ts: Date.now(), urls: [...this.ingestedUrls] })\r\n );\r\n } catch { /* ignore */ }\r\n }\r\n\r\n constructor(config: HuskelConfig) {\r\n const siteId = config.siteId || getEnvVar('NEXT_PUBLIC_HUSKEL_SITE_ID') || '';\r\n const apiUrl = config.apiUrl || getEnvVar('NEXT_PUBLIC_HUSKEL_API_URL') || '';\r\n const apiToken = config.apiToken || getEnvVar('NEXT_PUBLIC_HUSKEL_API_TOKEN') || '';\r\n\r\n // Runtime validation — fail loudly so misconfiguration is never silent\r\n if (!siteId) console.error('[Huskel] Missing siteId. Set it via <HuskelProvider siteId=\"...\"> or NEXT_PUBLIC_HUSKEL_SITE_ID.');\r\n if (!apiUrl) console.error('[Huskel] Missing apiUrl. Set it via <HuskelProvider apiUrl=\"...\"> or NEXT_PUBLIC_HUSKEL_API_URL.');\r\n if (!apiToken) console.error('[Huskel] Missing apiToken. Set it via <HuskelProvider apiToken=\"...\"> or NEXT_PUBLIC_HUSKEL_API_TOKEN.');\r\n\r\n this.shopperId = config.shopperId;\r\n this.initSession();\r\n this.loadIngestedCache();\r\n\r\n this.api = new HuskelAPI(\r\n apiUrl,\r\n siteId,\r\n apiToken,\r\n () => this.shopperId,\r\n () => this.sessionId\r\n );\r\n instance = this;\r\n\r\n if (typeof window !== 'undefined') {\r\n this.onlineHandler = () => {\r\n console.log('[Huskel] Connectivity restored, flushing queued ingestions.');\r\n this.flushQueue();\r\n };\r\n window.addEventListener('online', this.onlineHandler);\r\n }\r\n }\r\n\r\n setShopperId(id: string | undefined) {\r\n this.shopperId = id;\r\n }\r\n\r\n getShopperId(): string | undefined {\r\n return this.shopperId;\r\n }\r\n\r\n getSessionId(): string {\r\n return this.sessionId;\r\n }\r\n\r\n private initSession() {\r\n if (typeof window !== 'undefined' && window.sessionStorage) {\r\n try {\r\n let sid = window.sessionStorage.getItem('huskel_session_id');\r\n if (!sid) {\r\n sid = generateUUID();\r\n window.sessionStorage.setItem('huskel_session_id', sid);\r\n }\r\n this.sessionId = sid;\r\n return;\r\n } catch (e) {\r\n // Fallback if sessionStorage is disabled or private mode\r\n }\r\n }\r\n this.sessionId = generateUUID();\r\n }\r\n\r\n destroy() {\r\n if (typeof window !== 'undefined' && this.onlineHandler) {\r\n window.removeEventListener('online', this.onlineHandler);\r\n this.onlineHandler = null;\r\n }\r\n if (this.ingestTimer) {\r\n clearTimeout(this.ingestTimer);\r\n this.ingestTimer = null;\r\n }\r\n if (instance === this) instance = null;\r\n }\r\n\r\n async queueIngest(rawProduct: RawProductInput): Promise<void> {\r\n const product = mapRawProduct(rawProduct);\r\n if (!product) return;\r\n\r\n if (this.ingestedUrls.has(product.url)) {\r\n return; // already indexed in this session or today — skip\r\n }\r\n this.ingestedUrls.add(product.url);\r\n this.saveIngestedCache();\r\n\r\n this.ingestQueue.push(product);\r\n this.scheduleFlush();\r\n }\r\n\r\n async queueIngestBatch(rawProducts: RawProductInput[]): Promise<void> {\r\n rawProducts.forEach(p => {\r\n const product = mapRawProduct(p);\r\n if (!product) return;\r\n\r\n if (this.ingestedUrls.has(product.url)) {\r\n return;\r\n }\r\n this.ingestedUrls.add(product.url);\r\n this.ingestQueue.push(product);\r\n });\r\n\r\n if (this.ingestQueue.length > 0) {\r\n this.saveIngestedCache();\r\n this.scheduleFlush();\r\n }\r\n }\r\n\r\n private scheduleFlush() {\r\n if (this.ingestTimer) return;\r\n this.ingestTimer = setTimeout(() => {\r\n this.flushQueue();\r\n }, 300);\r\n }\r\n\r\n private async flushQueue() {\r\n this.ingestTimer = null;\r\n if (this.ingestQueue.length === 0) return;\r\n\r\n if (typeof navigator !== 'undefined' && !navigator.onLine) {\r\n console.warn('[Huskel] Browser offline. Postponing ingestion.');\r\n return;\r\n }\r\n\r\n const batch = [...this.ingestQueue];\r\n this.ingestQueue = [];\r\n\r\n try {\r\n await this.api.ingestBatch(batch);\r\n } catch (e: any) {\r\n if (e.status && e.status >= 400 && e.status < 500) {\r\n console.error('[Huskel] Ingestion discarded due to client error:', e.message);\r\n return;\r\n }\r\n\r\n // Re-queue and schedule another flush so items are not stuck forever\r\n console.warn('[Huskel] Ingestion failed. Re-queuing to retry.', e);\r\n this.ingestQueue = [...batch, ...this.ingestQueue];\r\n this.scheduleFlush();\r\n }\r\n }\r\n}\r\n\r\nlet instance: HuskelClient | null = null;\r\n\r\nexport function initHuskel(config: HuskelConfig): HuskelClient {\r\n instance = new HuskelClient(config);\r\n return instance;\r\n}\r\n\r\nexport function getHuskelClient(): HuskelClient {\r\n if (!instance) {\r\n const siteId = getEnvVar('NEXT_PUBLIC_HUSKEL_SITE_ID');\r\n const apiUrl = getEnvVar('NEXT_PUBLIC_HUSKEL_API_URL');\r\n const apiToken = getEnvVar('NEXT_PUBLIC_HUSKEL_API_TOKEN');\r\n\r\n if (siteId && apiUrl && apiToken) {\r\n instance = new HuskelClient({ siteId, apiUrl, apiToken });\r\n } else {\r\n throw new Error('[Huskel] Call initHuskel() or set NEXT_PUBLIC_HUSKEL_* environment variables before using the client.');\r\n }\r\n }\r\n return instance;\r\n}\r\n","import { useRef } from 'react';\r\nimport { HuskelConfig } from '../types';\r\nimport { HuskelClient, initHuskel } from '../client';\r\n\r\n/**\r\n * @deprecated Use <HuskelProvider> instead to avoid SSR issues.\r\n */\r\nexport function useHuskel(config: HuskelConfig): HuskelClient {\r\n const clientRef = useRef<HuskelClient | null>(null);\r\n\r\n if (!clientRef.current) {\r\n console.warn('[Huskel] useHuskel() is deprecated. Please wrap your application in <HuskelProvider> instead.');\r\n clientRef.current = initHuskel(config);\r\n }\r\n\r\n return clientRef.current;\r\n}\r\n","import { useState, useCallback, useRef } from 'react';\nimport { SearchResult } from '../types';\nimport { useHuskelContext } from '../components/HuskelProvider';\n\ninterface UseSearchReturn {\n results: SearchResult[];\n loading: boolean;\n error: string | null;\n search: (query: string, limit?: number) => Promise<void>;\n clear: () => void;\n}\n\nexport function useSearch(): UseSearchReturn {\n const client = useHuskelContext();\n const [results, setResults] = useState<SearchResult[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n // Generation counter so stale responses from any in-flight requests don't overwrite newer ones\n const genRef = useRef(0);\n\n const search = useCallback(async (query: string, limit = 8) => {\n if (!query.trim()) { setResults([]); setLoading(false); return; }\n const gen = ++genRef.current;\n // No loading spinner for autocomplete — it's so fast it would just flicker\n setError(null);\n try {\n // searchAutocomplete = pure in-memory Trie, <1ms, no Upstash\n const res = await client.api.searchAutocomplete(query, limit);\n if (gen === genRef.current) {\n setResults(res.results ?? []);\n }\n } catch (e: unknown) {\n if (gen === genRef.current) {\n setError((e as Error).message ?? 'Search failed');\n }\n } finally {\n if (gen === genRef.current) setLoading(false);\n }\n }, [client]);\n\n const clear = useCallback(() => {\n genRef.current++;\n setResults([]);\n setError(null);\n setLoading(false);\n }, []);\n\n return { results, loading, error, search, clear };\n}\n","'use client';\r\n\r\nimport React, { createContext, useContext, useEffect, useRef } from 'react';\r\nimport { HuskelClient, getHuskelClient } from '../client';\r\nimport { HuskelConfig } from '../types';\r\n\r\nexport const HuskelContext = createContext<HuskelClient | null>(null);\r\n\r\ninterface HuskelProviderProps extends HuskelConfig {\r\n children: React.ReactNode;\r\n}\r\n\r\nexport function HuskelProvider({ siteId, apiUrl, apiToken, shopperId, children }: HuskelProviderProps) {\r\n const clientRef = useRef<HuskelClient | null>(null);\r\n\r\n if (!clientRef.current) {\r\n clientRef.current = new HuskelClient({ siteId, apiUrl, apiToken, shopperId });\r\n }\r\n\r\n // Update shopperId dynamically when it changes (e.g., shopper logs in/out)\r\n useEffect(() => {\r\n clientRef.current?.setShopperId(shopperId);\r\n }, [shopperId]);\r\n\r\n // Clean up the online listener and timers when the provider unmounts\r\n // (prevents leaks during hot module reload and React StrictMode double-mount)\r\n useEffect(() => {\r\n return () => {\r\n clientRef.current?.destroy();\r\n };\r\n }, []);\r\n\r\n return (\r\n <HuskelContext.Provider value={clientRef.current}>\r\n {children}\r\n </HuskelContext.Provider>\r\n );\r\n}\r\n\r\nexport function useHuskelContext(): HuskelClient {\r\n const context = useContext(HuskelContext);\r\n if (!context) {\r\n return getHuskelClient();\r\n }\r\n return context;\r\n}\r\n\r\n","import { useCallback, useState } from 'react';\r\nimport { RawProductInput } from '../types';\r\nimport { useHuskelContext } from '../components/HuskelProvider';\r\n\r\ninterface UseIngestReturn {\r\n ingest: (product: RawProductInput) => Promise<void>;\r\n ingestBatch: (products: RawProductInput[]) => Promise<void>;\r\n loading: boolean;\r\n error: string | null;\r\n}\r\n\r\nexport function useIngest(): UseIngestReturn {\r\n const client = useHuskelContext();\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const ingest = useCallback(async (product: RawProductInput) => {\r\n setLoading(true);\r\n setError(null);\r\n try {\r\n await client.queueIngest(product);\r\n } catch (e: unknown) {\r\n setError((e as Error).message ?? 'Ingest failed');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [client]);\r\n\r\n const ingestBatch = useCallback(async (products: RawProductInput[]) => {\r\n if (!products.length) return;\r\n setLoading(true);\r\n setError(null);\r\n try {\r\n await client.queueIngestBatch(products);\r\n } catch (e: unknown) {\r\n setError((e as Error).message ?? 'Batch ingest failed');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [client]);\r\n\r\n return { ingest, ingestBatch, loading, error };\r\n}\r\n","import { useEffect, useRef } from 'react';\nimport { RawProductInput } from '../types';\nimport { getHuskelClient } from '../client';\n\n/**\n * usePageIngest — drop this into any product page component.\n * The moment a customer's browser renders the page, the product is\n * automatically captured and queued for ingestion into the vector index.\n *\n * No configuration needed beyond <HuskelProvider> in your layout.\n *\n * @example\n * // Product detail page — Next.js or React\n * export function ProductPage({ product }) {\n * usePageIngest({\n * name: product.title,\n * price: product.price,\n * url: window.location.href,\n * images: [product.thumbnail],\n * category: product.category,\n * });\n * return <div>...</div>;\n * }\n */\nexport function usePageIngest(product: RawProductInput | null | undefined): void {\n // Use url as the stable key — avoids re-ingesting on unrelated re-renders\n const ingestedRef = useRef<string | null>(null);\n\n useEffect(() => {\n if (!product) return;\n\n // Resolve URL — falls back to window.location if not provided\n const url =\n product.url ||\n (typeof window !== 'undefined' ? window.location.href : '');\n\n // Guard: only ingest once per URL per component lifecycle\n if (ingestedRef.current === url) return;\n ingestedRef.current = url;\n\n try {\n getHuskelClient().queueIngest({ ...product, url });\n } catch {\n // Client not initialised — silently skip\n }\n }, [product?.url ?? product?.name]);\n}\n","import { useState, useCallback, useRef } from 'react';\nimport { useHuskelContext } from '../components/HuskelProvider';\n\nexport interface ChatMessage {\n role: 'user' | 'assistant';\n content: string;\n}\n\nexport interface ChatSource {\n id?: string;\n name: string;\n price?: string;\n currency?: string;\n category?: string;\n url?: string;\n image?: string;\n}\n\ninterface UseChatReturn {\n messages: ChatMessage[];\n sources: ChatSource[];\n loading: boolean;\n error: string | null;\n send: (query: string) => Promise<void>;\n reset: () => void;\n}\n\nexport function useChat(): UseChatReturn {\n const client = useHuskelContext();\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [sources, setSources] = useState<ChatSource[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n const send = useCallback(async (query: string) => {\n if (!query.trim() || loading) return;\n abortRef.current?.abort();\n abortRef.current = new AbortController();\n\n // Optimistically add the user message\n const userMsg: ChatMessage = { role: 'user', content: query };\n setMessages(prev => [...prev, userMsg]);\n setLoading(true);\n setError(null);\n\n try {\n // Build history from current messages (exclude the one we just added — server will see it as query)\n const history = messages.map(m => ({ role: m.role, content: m.content }));\n const res = await client.api.chat(query, history);\n\n const assistantMsg: ChatMessage = { role: 'assistant', content: res.answer };\n setMessages(prev => [...prev, assistantMsg]);\n setSources(res.sources ?? []);\n } catch (e: any) {\n setError(e?.message ?? 'Chat request failed');\n // Remove the optimistic user message on failure\n setMessages(prev => prev.slice(0, -1));\n } finally {\n setLoading(false);\n }\n }, [client, messages, loading]);\n\n const reset = useCallback(() => {\n setMessages([]);\n setSources([]);\n setError(null);\n }, []);\n\n return { messages, sources, loading, error, send, reset };\n}\n","import React, { useState, useEffect, useRef } from 'react';\nimport { useSearch } from '../hooks/useSearch';\nimport { SearchResult } from '../types';\n\ninterface SearchBarProps {\n placeholder?: string;\n limit?: number;\n /** Debounce in ms — default 80 for near-instant feel */\n debounceMs?: number;\n onSelect?: (result: SearchResult) => void;\n className?: string;\n inputClassName?: string;\n dropdownClassName?: string;\n renderResult?: (result: SearchResult) => React.ReactNode;\n}\n\n/* ─── Minimal adaptive styles ─────────────────────────────────────────────── */\nconst CSS = `\n /* ── Light mode defaults (CSS custom properties) ── */\n .hsk-sb-wrap {\n --hsk-bg: #fff;\n --hsk-border: #d1d5db;\n --hsk-text: #111;\n --hsk-muted: #9ca3af;\n --hsk-hover: #f3f4f6;\n --hsk-drop-shadow: 0 8px 30px rgba(0,0,0,.12);\n position: relative;\n width: 100%;\n font-family: inherit;\n }\n\n /* ── Dark mode overrides at wrap level so ALL children inherit ── */\n @media (prefers-color-scheme: dark) {\n .hsk-sb-wrap {\n --hsk-bg: #1a1a1b;\n --hsk-border: #2a2a2d;\n --hsk-text: #f3f3f2;\n --hsk-muted: #666;\n --hsk-hover: #1e1e1f;\n --hsk-drop-shadow: 0 12px 40px rgba(0,0,0,.5);\n }\n }\n\n /* ── Input ── */\n .hsk-sb-input {\n width: 100%;\n padding: 10px 16px 10px 40px;\n font-size: 14px;\n border-radius: 9999px;\n border: 1.5px solid var(--hsk-border);\n outline: none;\n box-sizing: border-box;\n background: var(--hsk-bg);\n color: var(--hsk-text);\n transition: border-color .15s, box-shadow .15s;\n font-family: inherit;\n }\n .hsk-sb-input::placeholder { color: var(--hsk-muted); }\n .hsk-sb-input:focus {\n border-color: #ff6a33;\n box-shadow: 0 0 0 3px rgba(255,106,51,.12);\n }\n .hsk-sb-icon {\n position: absolute;\n left: 14px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--hsk-muted);\n pointer-events: none;\n display: flex;\n align-items: center;\n }\n\n /* ── Dropdown ── */\n .hsk-sb-drop {\n position: absolute;\n top: calc(100% + 6px);\n left: 0; right: 0;\n background: var(--hsk-bg);\n border: 1px solid var(--hsk-border);\n border-radius: 12px;\n box-shadow: var(--hsk-drop-shadow);\n z-index: 9999;\n overflow: hidden;\n padding: 6px 0;\n }\n\n /* ── Row ── */\n .hsk-sb-row {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 9px 16px;\n cursor: pointer;\n transition: background .1s;\n }\n .hsk-sb-row:hover { background: var(--hsk-hover); }\n .hsk-sb-row-icon {\n color: var(--hsk-muted);\n flex-shrink: 0;\n display: flex;\n align-items: center;\n }\n .hsk-sb-row-body { flex: 1; min-width: 0; }\n .hsk-sb-row-title {\n font-size: 13px;\n font-weight: 500;\n color: var(--hsk-text);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 1.3;\n }\n .hsk-sb-row-sub {\n font-size: 11px;\n color: var(--hsk-muted);\n margin-top: 2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .hsk-sb-empty {\n padding: 14px 16px;\n font-size: 13px;\n color: var(--hsk-muted);\n }\n\n /* ── Thin accent bar while loading (non-intrusive, no text) ── */\n .hsk-sb-loading-bar {\n height: 2px;\n background: linear-gradient(90deg, transparent, #ff6a33, transparent);\n background-size: 200% 100%;\n animation: hsk-sweep 0.9s linear infinite;\n position: absolute;\n top: 0; left: 0; right: 0;\n }\n @keyframes hsk-sweep {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n\n /* ── Staggered fade-in ── */\n .hsk-sb-fade { animation: hsk-fin .1s ease-out both; }\n @keyframes hsk-fin { from { opacity:0; transform:translateY(3px); } to { opacity:1; transform:none; } }\n`;\n\n/* SVG search glass — pure inline so no icon dependency */\nconst SearchIcon = () => (\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <circle cx=\"8.5\" cy=\"8.5\" r=\"5.5\"/>\n <line x1=\"13\" y1=\"13\" x2=\"18\" y2=\"18\"/>\n </svg>\n);\n\nexport function SearchBar({\n placeholder = 'Search products…',\n limit = 10,\n debounceMs = 80,\n onSelect,\n className,\n inputClassName,\n dropdownClassName,\n renderResult,\n}: SearchBarProps) {\n const [query, setQuery] = useState('');\n const [open, setOpen] = useState(false);\n const { results, loading, search, clear } = useSearch();\n const timer = useRef<ReturnType<typeof setTimeout>>();\n const wrap = useRef<HTMLDivElement>(null);\n\n /* Debounce search — but keep stale results visible between calls */\n useEffect(() => {\n clearTimeout(timer.current);\n if (!query.trim()) { clear(); setOpen(false); return; }\n setOpen(true); // open immediately (stale results show while fetching)\n timer.current = setTimeout(() => { search(query, limit); }, debounceMs);\n return () => clearTimeout(timer.current);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [query]);\n\n /* Click-outside to close */\n useEffect(() => {\n const h = (e: MouseEvent) => {\n if (wrap.current && !wrap.current.contains(e.target as Node)) setOpen(false);\n };\n document.addEventListener('mousedown', h);\n return () => document.removeEventListener('mousedown', h);\n }, []);\n\n const handleSelect = (r: SearchResult) => {\n setOpen(false);\n setQuery(r.product.name);\n onSelect?.(r);\n };\n\n const showDrop = open && query.trim().length > 0;\n\n return (\n <>\n <style>{CSS}</style>\n <div className={`hsk-sb-wrap ${className ?? ''}`} ref={wrap}>\n <span className=\"hsk-sb-icon\"><SearchIcon /></span>\n <input\n className={`hsk-sb-input ${inputClassName ?? ''}`}\n type=\"text\"\n value={query}\n placeholder={placeholder}\n onChange={e => setQuery(e.target.value)}\n onFocus={() => results.length > 0 && query.trim() && setOpen(true)}\n autoComplete=\"off\"\n spellCheck={false}\n />\n {showDrop && (\n <div className={`hsk-sb-drop ${dropdownClassName ?? ''}`} style={{ position: 'absolute' }}>\n {loading && <div className=\"hsk-sb-loading-bar\" />}\n\n {results.length === 0 && !loading && (\n <div className=\"hsk-sb-empty\">No results for &ldquo;{query}&rdquo;</div>\n )}\n\n {results.map((r, i) => (\n renderResult ? (\n <div\n key={r.id}\n onClick={() => handleSelect(r)}\n className=\"hsk-sb-fade\"\n style={{ animationDelay: `${i * 18}ms` }}\n >\n {renderResult(r)}\n </div>\n ) : (\n <div\n key={r.id}\n className=\"hsk-sb-row hsk-sb-fade\"\n style={{ animationDelay: `${i * 18}ms` }}\n onClick={() => handleSelect(r)}\n >\n <span className=\"hsk-sb-row-icon\"><SearchIcon /></span>\n <div className=\"hsk-sb-row-body\">\n <div className=\"hsk-sb-row-title\">{r.product.name}</div>\n {(r.product.category || r.product.brand) && (\n <div className=\"hsk-sb-row-sub\">\n {r.product.category ?? r.product.brand}\n </div>\n )}\n </div>\n </div>\n )\n ))}\n </div>\n )}\n </div>\n </>\n );\n}\n","import React, { useState, useEffect, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useSearch } from '../hooks/useSearch';\nimport { SearchResult } from '../types';\n\ninterface SparkleProps {\n productName: string;\n limit?: number;\n onResult?: (results: SearchResult[]) => void;\n /** Override the backdrop colour (any CSS colour/gradient) */\n backdropColor?: string;\n /** Override backdrop blur — e.g. \"8px\" or 8 */\n backdropBlur?: string | number;\n /** Extra classes on the trigger button */\n className?: string;\n /** Called when user clicks a result — return false to prevent default navigation */\n onNavigate?: (result: SearchResult) => boolean | void;\n}\n\n/* ─── Styles ──────────────────────────────────────────────────────────────── */\nconst CSS = `\n /* ── Trigger button ── */\n .hsk-sp-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: 1px solid var(--hsk-sp-border, rgba(255,106,51,.35));\n background: var(--hsk-sp-bg, rgba(255,106,51,.08));\n color: #ff6a33;\n cursor: pointer;\n font-size: 15px;\n line-height: 1;\n transition: background .15s, border-color .15s, transform .12s;\n flex-shrink: 0;\n padding: 0;\n }\n .hsk-sp-btn:hover {\n background: rgba(255,106,51,.18);\n border-color: rgba(255,106,51,.7);\n transform: scale(1.1);\n }\n .hsk-sp-btn:active { transform: scale(.92); }\n\n /* ── Backdrop ── */\n .hsk-sp-backdrop {\n position: fixed;\n inset: 0;\n z-index: 99998;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding: clamp(48px, 10vh, 96px) 16px 40px;\n animation: hsk-bd-in .2s ease-out both;\n overflow-y: auto;\n }\n @keyframes hsk-bd-in { from { opacity:0; } to { opacity:1; } }\n\n /* ── Modal card ── */\n .hsk-sp-card {\n width: 100%;\n max-width: 600px;\n border-radius: 18px;\n overflow: hidden;\n animation: hsk-card-in .24s cubic-bezier(.34,1.36,.64,1) both;\n flex-shrink: 0;\n /* light */\n background: var(--hsk-modal-card-bg, #fff);\n border: 1px solid var(--hsk-modal-card-border, rgba(0,0,0,.08));\n box-shadow: 0 32px 80px rgba(0,0,0,.18), 0 2px 8px rgba(0,0,0,.06);\n }\n @keyframes hsk-card-in {\n from { opacity:0; transform: scale(.96) translateY(-12px); }\n to { opacity:1; transform: scale(1) translateY(0); }\n }\n\n /* ── Header ── */\n .hsk-sp-header {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 18px 20px 14px;\n border-bottom: 1px solid var(--hsk-modal-divide, rgba(0,0,0,.07));\n }\n .hsk-sp-header-icon { font-size: 18px; color: #ff6a33; flex-shrink: 0; }\n .hsk-sp-header-body { flex: 1; min-width: 0; }\n .hsk-sp-header-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--hsk-modal-text, #111);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .hsk-sp-header-sub {\n font-size: 11px;\n color: var(--hsk-modal-muted, #888);\n margin-top: 2px;\n }\n .hsk-sp-close {\n width: 30px; height: 30px;\n border-radius: 8px;\n border: 1px solid var(--hsk-modal-divide, rgba(0,0,0,.1));\n background: none;\n color: var(--hsk-modal-muted, #888);\n cursor: pointer;\n font-size: 18px;\n display: flex; align-items: center; justify-content: center;\n transition: all .15s;\n flex-shrink: 0;\n }\n .hsk-sp-close:hover { border-color: #ff6a33; color: #ff6a33; }\n\n /* ── Thin loading accent bar ── */\n .hsk-sp-bar {\n height: 2px;\n background: linear-gradient(90deg, transparent 0%, #ff6a33 40%, #ffaa80 60%, transparent 100%);\n background-size: 200% 100%;\n animation: hsk-bar .9s linear infinite;\n }\n @keyframes hsk-bar { 0%{background-position:200% 0} 100%{background-position:-200% 0} }\n\n /* ── Results list ── */\n .hsk-sp-results { padding: 10px 12px; display: flex; flex-direction: column; gap: 8px; }\n .hsk-sp-empty { padding: 40px; text-align: center; font-size: 13px; color: var(--hsk-modal-muted, #aaa); }\n\n /* ── Result card (toast-inspired: slides up from bottom) ── */\n .hsk-sp-item {\n display: flex;\n gap: 14px;\n padding: 14px;\n border-radius: 12px;\n border: 1px solid var(--hsk-modal-item-border, rgba(0,0,0,.07));\n background: var(--hsk-modal-item-bg, #f9f9f9);\n animation: hsk-toast-up .28s cubic-bezier(.22,.68,0,1.2) both;\n overflow: hidden;\n }\n @keyframes hsk-toast-up {\n from { opacity:0; transform: translateY(18px) scale(.97); }\n to { opacity:1; transform: translateY(0) scale(1); }\n }\n\n /* image */\n .hsk-sp-img-wrap {\n width: 72px; height: 72px;\n border-radius: 10px;\n background: #fff;\n border: 1px solid var(--hsk-modal-divide, rgba(0,0,0,.07));\n flex-shrink: 0;\n overflow: hidden;\n display: flex; align-items: center; justify-content: center;\n padding: 6px;\n }\n .hsk-sp-img-wrap img { max-width: 100%; max-height: 100%; object-fit: contain; }\n .hsk-sp-img-placeholder { font-size: 26px; }\n\n /* body */\n .hsk-sp-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }\n .hsk-sp-item-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--hsk-modal-text, #111);\n line-height: 1.35;\n overflow: hidden;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n .hsk-sp-item-cat {\n font-size: 11px;\n font-weight: 600;\n color: #ff6a33;\n text-transform: uppercase;\n letter-spacing: .05em;\n }\n .hsk-sp-item-price-row { display: flex; align-items: baseline; gap: 8px; margin-top: 2px; }\n .hsk-sp-item-price {\n font-size: 18px;\n font-weight: 700;\n color: var(--hsk-modal-text, #111);\n }\n .hsk-sp-item-currency { font-size: 12px; color: var(--hsk-modal-muted, #888); }\n\n /* actions */\n .hsk-sp-actions { display: flex; gap: 6px; margin-top: 8px; }\n .hsk-sp-action {\n flex: 1;\n padding: 7px 10px;\n border-radius: 8px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n border: 1px solid transparent;\n transition: all .15s;\n text-align: center;\n font-family: inherit;\n }\n .hsk-sp-action-primary {\n background: #ff6a33;\n color: #fff;\n border-color: #ff6a33;\n }\n .hsk-sp-action-primary:hover { background: #e55d2a; }\n .hsk-sp-action-secondary {\n background: var(--hsk-action-sec-bg, rgba(0,0,0,.06));\n color: var(--hsk-modal-muted, #666);\n border-color: var(--hsk-modal-divide, rgba(0,0,0,.1));\n }\n .hsk-sp-action-secondary:hover {\n background: var(--hsk-action-sec-bg-hover, rgba(0,0,0,.1));\n color: var(--hsk-modal-text, #333);\n }\n\n /* ── Footer ── */\n .hsk-sp-footer {\n padding: 12px 20px;\n border-top: 1px solid var(--hsk-modal-divide, rgba(0,0,0,.07));\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .hsk-sp-badge {\n font-size: 10px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;\n color: #ff6a33;\n background: rgba(255,106,51,.1);\n border: 1px solid rgba(255,106,51,.25);\n padding: 2px 8px;\n border-radius: 999px;\n }\n .hsk-sp-esc { font-size: 11px; color: var(--hsk-modal-muted, #bbb); margin-left: auto; }\n\n /* ── Dark mode ── */\n @media (prefers-color-scheme: dark) {\n .hsk-sp-card {\n --hsk-modal-card-bg: #111112;\n --hsk-modal-card-border: rgba(255,255,255,.07);\n --hsk-modal-text: #f3f3f2;\n --hsk-modal-muted: #666;\n --hsk-modal-divide: rgba(255,255,255,.07);\n --hsk-modal-item-bg: #1a1a1b;\n --hsk-modal-item-border: rgba(255,255,255,.06);\n --hsk-action-sec-bg: rgba(255,255,255,.07);\n --hsk-action-sec-bg-hover: rgba(255,255,255,.12);\n }\n .hsk-sp-img-wrap { background: #242425; border-color: rgba(255,255,255,.08); }\n }\n`;\n\n/* ── Modal ────────────────────────────────────────────────────────────────── */\ninterface ModalProps {\n productName: string;\n limit: number;\n backdropColor?: string;\n backdropBlur?: string | number;\n onClose: () => void;\n onNavigate?: (r: SearchResult) => boolean | void;\n onResult?: (results: SearchResult[]) => void;\n}\n\nfunction SparkleModal({ productName, limit, backdropColor, backdropBlur, onClose, onNavigate, onResult }: ModalProps) {\n const { results, loading, search } = useSearch();\n const initiated = useRef(false);\n\n /* auto-search on open */\n useEffect(() => {\n if (!initiated.current) { initiated.current = true; search(productName, limit); }\n }, []);\n\n /* fire callback */\n useEffect(() => { if (results.length > 0) onResult?.(results); }, [results]);\n\n /* Escape key */\n useEffect(() => {\n const h = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };\n document.addEventListener('keydown', h);\n return () => document.removeEventListener('keydown', h);\n }, []);\n\n const blurVal = typeof backdropBlur === 'number' ? `${backdropBlur}px` : (backdropBlur ?? '16px');\n const bg = backdropColor ?? undefined; // undefined → CSS decides per mode\n\n const handleNav = (r: SearchResult) => {\n const prevent = onNavigate?.(r);\n if (prevent !== false) {\n onClose();\n if (r.product.url) window.location.href = r.product.url;\n }\n };\n\n return (\n <>\n <style>{CSS}</style>\n {/* Backdrop — colour adapts to mode unless dev overrides */}\n <div\n className=\"hsk-sp-backdrop\"\n onClick={onClose}\n style={{\n backdropFilter: `blur(${blurVal})`,\n WebkitBackdropFilter: `blur(${blurVal})`,\n background: bg ?? undefined,\n /* CSS handles light/dark via @media if bg not forced */\n }}\n >\n {/* Inject mode-aware backdrop colour if not overridden */}\n {!bg && (\n <style>{`\n @media (prefers-color-scheme: dark) { .hsk-sp-backdrop { background: rgba(0,0,0,.80); } }\n @media (prefers-color-scheme: light) { .hsk-sp-backdrop { background: rgba(240,240,245,.70); } }\n `}</style>\n )}\n\n <div className=\"hsk-sp-card\" onClick={e => e.stopPropagation()}>\n {/* Header */}\n <div className=\"hsk-sp-header\">\n <span className=\"hsk-sp-header-icon\">✦</span>\n <div className=\"hsk-sp-header-body\">\n <div className=\"hsk-sp-header-title\">Similar to &ldquo;{productName}&rdquo;</div>\n <div className=\"hsk-sp-header-sub\">AI vector similarity · instant results</div>\n </div>\n <button className=\"hsk-sp-close\" onClick={onClose} aria-label=\"Close\">×</button>\n </div>\n\n {/* Loading bar (thin, non-intrusive) */}\n {loading && <div className=\"hsk-sp-bar\" />}\n\n {/* Results */}\n <div className=\"hsk-sp-results\">\n {!loading && results.length === 0 && (\n <div className=\"hsk-sp-empty\">No similar products found.</div>\n )}\n\n {results.map((r, i) => {\n const price = parseFloat(r.product.price?.replace(/[^0-9.]/g, '') || '0');\n const currency = r.product.currency ?? 'KES';\n return (\n <div\n key={r.id}\n className=\"hsk-sp-item\"\n style={{ animationDelay: `${i * 55}ms` }}\n >\n {/* Image */}\n <div className=\"hsk-sp-img-wrap\">\n {r.product.images?.[0]\n ? <img src={r.product.images[0]} alt={r.product.name} />\n : <span className=\"hsk-sp-img-placeholder\">🛍</span>\n }\n </div>\n\n {/* Body */}\n <div className=\"hsk-sp-item-body\">\n {r.product.category && (\n <div className=\"hsk-sp-item-cat\">{r.product.category}</div>\n )}\n <div className=\"hsk-sp-item-name\">{r.product.name}</div>\n <div className=\"hsk-sp-item-price-row\">\n <span className=\"hsk-sp-item-currency\">{currency}</span>\n <span className=\"hsk-sp-item-price\">{price.toLocaleString()}</span>\n </div>\n {/* Actions */}\n <div className=\"hsk-sp-actions\">\n <button\n className=\"hsk-sp-action hsk-sp-action-primary\"\n onClick={() => handleNav(r)}\n >\n View Product\n </button>\n <button\n className=\"hsk-sp-action hsk-sp-action-secondary\"\n onClick={() => onClose()}\n >\n Add to Cart\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Footer */}\n <div className=\"hsk-sp-footer\">\n <span className=\"hsk-sp-badge\">✦ Huskel AI</span>\n <span className=\"hsk-sp-esc\">Esc to close</span>\n </div>\n </div>\n </div>\n </>\n );\n}\n\n/* ── Exported component ───────────────────────────────────────────────────── */\nexport function Sparkle({ productName, limit = 8, onResult, backdropColor, backdropBlur, className, onNavigate }: SparkleProps) {\n const [open, setOpen] = useState(false);\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => { setMounted(true); }, []);\n\n return (\n <>\n <style>{CSS}</style>\n <button\n className={`hsk-sp-btn ${className ?? ''}`}\n onClick={() => setOpen(true)}\n title=\"Find similar products\"\n aria-label=\"Find similar products\"\n >\n ✦\n </button>\n {open && mounted && createPortal(\n <SparkleModal\n productName={productName}\n limit={limit}\n backdropColor={backdropColor}\n backdropBlur={backdropBlur}\n onClose={() => setOpen(false)}\n onResult={onResult}\n onNavigate={onNavigate}\n />,\n document.body\n )}\n </>\n );\n}\n","import React, { useState, useRef, useEffect } from 'react';\nimport { useChat, ChatMessage, ChatSource } from '../hooks/useChat';\n\ninterface ChatWidgetProps {\n placeholder?: string;\n title?: string;\n className?: string;\n /** Called when user clicks a product link */\n onSelectSource?: (source: ChatSource) => void;\n}\n\nconst S = `\n .hsk-chat-widget{display:flex;flex-direction:column;height:100%;min-height:320px;font-family:inherit;background:#0f0f10;border:1px solid #2a2a2d;border-radius:12px;overflow:hidden}\n .hsk-chat-header{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid #1e1e1f;background:#111112;flex-shrink:0}\n .hsk-chat-title{font-size:14px;font-weight:600;color:#f3f3f2}\n .hsk-chat-badge{font-size:10px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:#ff6a33;background:#ff6a3315;border:1px solid #ff6a3330;padding:2px 8px;border-radius:20px}\n .hsk-chat-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px;scroll-behavior:smooth}\n .hsk-chat-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:8px;color:#555;font-size:13px;text-align:center;padding:24px}\n .hsk-chat-empty-icon{font-size:28px;margin-bottom:4px}\n .hsk-msg-row{display:flex;gap:8px;align-items:flex-start}\n .hsk-msg-row.user{flex-direction:row-reverse}\n .hsk-msg-avatar{width:28px;height:28px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700}\n .hsk-msg-avatar.ai{background:#ff6a3320;border:1px solid #ff6a3340;color:#ff6a33}\n .hsk-msg-avatar.user{background:#2a2a2d;color:#9a9aa1}\n .hsk-msg-bubble{max-width:78%;padding:10px 14px;border-radius:12px;font-size:13px;line-height:1.6}\n .hsk-msg-bubble.ai{background:#171718;border:1px solid #2a2a2d;color:#e8e8e7;border-radius:4px 12px 12px 12px}\n .hsk-msg-bubble.user{background:#ff6a33;color:#fff;border-radius:12px 4px 12px 12px}\n .hsk-sources{margin-top:10px;display:flex;flex-direction:column;gap:6px}\n .hsk-source-card{display:flex;align-items:center;gap:10px;padding:8px 10px;background:#1a1a1b;border:1px solid #252527;border-radius:8px;cursor:pointer;transition:border-color 0.15s}\n .hsk-source-card:hover{border-color:#ff6a3360}\n .hsk-source-img{width:36px;height:36px;object-fit:cover;border-radius:4px;background:#fff}\n .hsk-source-name{font-size:12px;font-weight:500;color:#e8e8e7;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n .hsk-source-price{font-size:11px;color:#ff6a33;font-weight:700;margin-top:2px}\n .hsk-typing{display:flex;gap:4px;align-items:center;padding:10px 14px;background:#171718;border:1px solid #2a2a2d;border-radius:4px 12px 12px 12px;width:fit-content}\n .hsk-typing-dot{width:6px;height:6px;background:#ff6a33;border-radius:50%;animation:hsk-chat-bounce 1.2s infinite}\n .hsk-typing-dot:nth-child(2){animation-delay:0.2s}\n .hsk-typing-dot:nth-child(3){animation-delay:0.4s}\n @keyframes hsk-chat-bounce{0%,100%{opacity:0.3;transform:translateY(0)}50%{opacity:1;transform:translateY(-4px)}}\n .hsk-chat-input-area{display:flex;align-items:center;gap:8px;padding:12px 14px;border-top:1px solid #1e1e1f;background:#111112;flex-shrink:0}\n .hsk-chat-input{flex:1;background:#1a1a1b;border:1px solid #2a2a2d;border-radius:8px;padding:9px 14px;font-size:13px;color:#f3f3f2;outline:none;font-family:inherit;transition:border-color 0.2s;resize:none;min-height:38px;max-height:120px;line-height:1.5}\n .hsk-chat-input::placeholder{color:#555}\n .hsk-chat-input:focus{border-color:#ff6a33}\n .hsk-chat-send{width:34px;height:34px;border-radius:8px;background:#ff6a33;border:none;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:16px;transition:opacity 0.15s,transform 0.1s}\n .hsk-chat-send:hover{opacity:0.88}\n .hsk-chat-send:active{transform:scale(0.93)}\n .hsk-chat-send:disabled{opacity:0.4;cursor:not-allowed}\n .hsk-chat-reset{font-size:11px;color:#555;cursor:pointer;padding:0 4px;transition:color 0.15s;background:none;border:none;font-family:inherit}\n .hsk-chat-reset:hover{color:#ff6a33}\n`;\n\nfunction SourceCard({ source, onSelect }: { source: ChatSource; onSelect?: (s: ChatSource) => void }) {\n return (\n <div className=\"hsk-source-card\" onClick={() => onSelect?.(source)}>\n {source.image && <img src={source.image} alt={source.name} className=\"hsk-source-img\" />}\n <div style={{ flex: 1, minWidth: 0 }}>\n <div className=\"hsk-source-name\">{source.name}</div>\n {source.price && (\n <div className=\"hsk-source-price\">{source.currency ?? 'KES'} {source.price}</div>\n )}\n </div>\n </div>\n );\n}\n\nexport function ChatWidget({ placeholder = 'Ask about anything in our store…', title = 'AI Shopping Assistant', className, onSelectSource }: ChatWidgetProps) {\n const { messages, sources, loading, error, send, reset } = useChat();\n const [input, setInput] = useState('');\n const bottomRef = useRef<HTMLDivElement>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n // Scroll to bottom on new messages\n useEffect(() => {\n bottomRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages, loading]);\n\n const handleSend = async () => {\n const q = input.trim();\n if (!q || loading) return;\n setInput('');\n if (textareaRef.current) textareaRef.current.style.height = 'auto';\n await send(q);\n };\n\n const handleKey = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n };\n\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInput(e.target.value);\n // Auto-grow textarea\n const t = e.target;\n t.style.height = 'auto';\n t.style.height = Math.min(t.scrollHeight, 120) + 'px';\n };\n\n return (\n <>\n <style>{S}</style>\n <div className={`hsk-chat-widget ${className ?? ''}`}>\n {/* Header */}\n <div className=\"hsk-chat-header\">\n <span style={{ fontSize: 16, color: '#ff6a33' }}>✦</span>\n <span className=\"hsk-chat-title\">{title}</span>\n <span className=\"hsk-chat-badge\">AI</span>\n {messages.length > 0 && (\n <button className=\"hsk-chat-reset\" onClick={reset} style={{ marginLeft: 'auto' }}>\n Clear\n </button>\n )}\n </div>\n\n {/* Messages */}\n <div className=\"hsk-chat-messages\">\n {messages.length === 0 ? (\n <div className=\"hsk-chat-empty\">\n <div className=\"hsk-chat-empty-icon\">✦</div>\n <div>Ask me anything about our products</div>\n <div style={{ fontSize: 12, color: '#444', marginTop: 4 }}>\n \"Find me headphones under KSh 5,000\" · \"Gift ideas for a chef\"\n </div>\n </div>\n ) : (\n messages.map((msg, idx) => (\n <div key={idx}>\n <div className={`hsk-msg-row ${msg.role}`}>\n <div className={`hsk-msg-avatar ${msg.role === 'assistant' ? 'ai' : 'user'}`}>\n {msg.role === 'assistant' ? '✦' : '↑'}\n </div>\n <div className={`hsk-msg-bubble ${msg.role === 'assistant' ? 'ai' : 'user'}`}>\n {msg.content}\n </div>\n </div>\n {/* Show sources after the last assistant message */}\n {msg.role === 'assistant' && idx === messages.length - 1 && sources.length > 0 && (\n <div style={{ marginLeft: 36 }}>\n <div className=\"hsk-sources\">\n {sources.map((src, si) => (\n <SourceCard key={si} source={src} onSelect={onSelectSource} />\n ))}\n </div>\n </div>\n )}\n </div>\n ))\n )}\n\n {/* Typing indicator */}\n {loading && (\n <div className=\"hsk-msg-row\">\n <div className=\"hsk-msg-avatar ai\">✦</div>\n <div className=\"hsk-typing\">\n <div className=\"hsk-typing-dot\" />\n <div className=\"hsk-typing-dot\" />\n <div className=\"hsk-typing-dot\" />\n </div>\n </div>\n )}\n\n {error && (\n <div style={{ fontSize: 12, color: '#ef4444', textAlign: 'center', padding: 8 }}>\n {error}\n </div>\n )}\n <div ref={bottomRef} />\n </div>\n\n {/* Input */}\n <div className=\"hsk-chat-input-area\">\n <textarea\n ref={textareaRef}\n className=\"hsk-chat-input\"\n value={input}\n onChange={handleInput}\n onKeyDown={handleKey}\n placeholder={placeholder}\n rows={1}\n disabled={loading}\n />\n <button\n className=\"hsk-chat-send\"\n onClick={handleSend}\n disabled={!input.trim() || loading}\n aria-label=\"Send\"\n >\n ↑\n </button>\n </div>\n </div>\n </>\n );\n}\n","'use client';\n\nimport React, { useState, useEffect, useRef, useCallback } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useChat, ChatMessage, ChatSource } from '../hooks/useChat';\n\ninterface AIChatButtonProps {\n label?: string;\n title?: string;\n placeholder?: string;\n backdropColor?: string;\n backdropBlur?: string | number;\n className?: string;\n onSelectSource?: (source: ChatSource) => void;\n}\n\n/* ─── Styles ──────────────────────────────────────────────────────────────── */\nconst CSS = `\n /* ── Trigger button ── */\n .hsk-cb-btn {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n padding: 8px 16px;\n border-radius: 9999px;\n border: 1px solid rgba(255,106,51,.4);\n background: rgba(255,106,51,.1);\n color: #ff6a33;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: background .15s, border-color .15s, transform .12s, box-shadow .15s;\n font-family: inherit;\n white-space: nowrap;\n }\n .hsk-cb-btn:hover {\n background: rgba(255,106,51,.18);\n border-color: rgba(255,106,51,.7);\n box-shadow: 0 4px 16px rgba(255,106,51,.2);\n }\n .hsk-cb-btn:active { transform: scale(.95); }\n .hsk-cb-btn-icon { font-size: 15px; line-height: 1; }\n\n /* ── Full-screen overlay ── */\n .hsk-cb-overlay {\n position: fixed;\n inset: 0;\n z-index: 99999;\n display: flex;\n flex-direction: column;\n animation: hsk-overlay-in .2s ease-out both;\n }\n @keyframes hsk-overlay-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n /* ── Panel (Claude-style, centered column) ── */\n .hsk-cb-panel {\n position: relative;\n display: flex;\n flex-direction: column;\n height: 100%;\n max-width: 780px;\n width: 100%;\n margin: 0 auto;\n animation: hsk-panel-in .28s cubic-bezier(.34,1.2,.64,1) both;\n }\n @keyframes hsk-panel-in {\n from { opacity: 0; transform: translateY(24px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n /* ── Top bar ── */\n .hsk-cb-topbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 28px 12px;\n flex-shrink: 0;\n }\n .hsk-cb-topbar-left {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .hsk-cb-topbar-icon {\n font-size: 22px;\n color: #ff6a33;\n line-height: 1;\n animation: hsk-sparkle-spin 6s linear infinite;\n }\n @keyframes hsk-sparkle-spin {\n 0%,100% { transform: rotate(0deg) scale(1); }\n 25% { transform: rotate(15deg) scale(1.1); }\n 75% { transform: rotate(-10deg) scale(.95); }\n }\n .hsk-cb-topbar-title {\n font-size: 16px;\n font-weight: 700;\n color: var(--hsk-chat-text, #111);\n letter-spacing: -.01em;\n }\n .hsk-cb-topbar-sub {\n font-size: 12px;\n color: var(--hsk-chat-muted, #888);\n margin-top: 2px;\n }\n .hsk-cb-topbar-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .hsk-cb-topbar-btn {\n height: 34px;\n padding: 0 14px;\n border-radius: 8px;\n border: 1px solid var(--hsk-chat-divide, rgba(0,0,0,.1));\n background: none;\n color: var(--hsk-chat-muted, #888);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all .15s;\n font-family: inherit;\n }\n .hsk-cb-topbar-btn:hover {\n border-color: #ff6a33;\n color: #ff6a33;\n }\n .hsk-cb-close {\n width: 34px; height: 34px;\n border-radius: 8px;\n border: 1px solid var(--hsk-chat-divide, rgba(0,0,0,.1));\n background: none;\n color: var(--hsk-chat-muted, #888);\n cursor: pointer;\n font-size: 20px;\n display: flex; align-items: center; justify-content: center;\n transition: all .15s;\n flex-shrink: 0;\n font-family: inherit;\n line-height: 1;\n }\n .hsk-cb-close:hover { border-color: #ff6a33; color: #ff6a33; }\n\n /* ── Messages scroll area ── */\n .hsk-cb-msgs {\n flex: 1;\n overflow-y: auto;\n padding: 8px 28px 0;\n display: flex;\n flex-direction: column;\n gap: 0;\n scroll-behavior: smooth;\n scrollbar-width: thin;\n scrollbar-color: var(--hsk-chat-divide, rgba(0,0,0,.1)) transparent;\n }\n\n /* ── Empty / welcome state ── */\n .hsk-cb-empty {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 20px;\n padding: 60px 32px;\n text-align: center;\n }\n .hsk-cb-empty-icon {\n font-size: 48px;\n color: #ff6a33;\n animation: hsk-sparkle-spin 4s linear infinite;\n }\n .hsk-cb-empty-title {\n font-size: 26px;\n font-weight: 700;\n color: var(--hsk-chat-text, #111);\n letter-spacing: -.02em;\n }\n .hsk-cb-empty-sub {\n font-size: 14px;\n color: var(--hsk-chat-muted, #888);\n line-height: 1.7;\n max-width: 380px;\n }\n .hsk-cb-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n justify-content: center;\n margin-top: 4px;\n }\n .hsk-cb-chip {\n padding: 8px 16px;\n border-radius: 9999px;\n border: 1px solid var(--hsk-chat-divide, rgba(0,0,0,.1));\n background: var(--hsk-chat-source-bg, rgba(0,0,0,.03));\n color: var(--hsk-chat-text, #333);\n font-size: 13px;\n cursor: pointer;\n transition: all .15s;\n font-family: inherit;\n }\n .hsk-cb-chip:hover {\n border-color: #ff6a33;\n color: #ff6a33;\n background: rgba(255,106,51,.06);\n }\n\n /* ── Message rows ── */\n .hsk-cb-msg-group {\n padding: 20px 0;\n border-bottom: 1px solid var(--hsk-chat-divide, rgba(0,0,0,.05));\n animation: hsk-msg-in .22s ease-out both;\n }\n .hsk-cb-msg-group:last-child { border-bottom: none; }\n @keyframes hsk-msg-in {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n /* User message */\n .hsk-cb-user-msg {\n display: flex;\n justify-content: flex-end;\n margin-bottom: 20px;\n }\n .hsk-cb-user-bubble {\n background: #ff6a33;\n color: #fff;\n padding: 12px 20px;\n border-radius: 22px 22px 6px 22px;\n font-size: 15px;\n line-height: 1.6;\n max-width: 72%;\n font-weight: 500;\n }\n\n /* AI message - no bubble, just clean text like Claude */\n .hsk-cb-ai-msg {\n display: flex;\n align-items: flex-start;\n gap: 14px;\n }\n .hsk-cb-ai-icon {\n width: 28px; height: 28px;\n border-radius: 50%;\n background: rgba(255,106,51,.12);\n border: 1px solid rgba(255,106,51,.25);\n color: #ff6a33;\n font-size: 13px;\n display: flex; align-items: center; justify-content: center;\n flex-shrink: 0;\n margin-top: 2px;\n }\n .hsk-cb-ai-body { flex: 1; min-width: 0; }\n .hsk-cb-ai-text {\n font-size: 15px;\n line-height: 1.75;\n color: var(--hsk-chat-text, #111);\n white-space: pre-wrap;\n }\n\n /* ── Sources horizontal carousel ── */\n .hsk-cb-sources-wrap {\n position: relative;\n margin-top: 20px;\n }\n .hsk-cb-sources {\n display: flex;\n flex-direction: row;\n gap: 14px;\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-bottom: 4px;\n }\n .hsk-cb-sources::-webkit-scrollbar { display: none; }\n /* Feathered right edge */\n .hsk-cb-sources-fade {\n position: absolute;\n right: 0; top: 0; bottom: 4px;\n width: 90px;\n pointer-events: none;\n }\n /* Scroll-next pill */\n .hsk-cb-sources-next {\n position: absolute;\n right: 10px;\n top: 50%;\n transform: translateY(-50%);\n width: 30px; height: 30px;\n border-radius: 50%;\n border: 1px solid var(--hsk-chat-divide, rgba(0,0,0,.12));\n background: var(--hsk-chat-bg, #0e0e0f);\n color: var(--hsk-chat-text, #eee);\n cursor: pointer;\n font-size: 16px;\n display: flex; align-items: center; justify-content: center;\n box-shadow: 0 2px 12px rgba(0,0,0,.2);\n transition: all .15s;\n z-index: 3;\n font-family: inherit;\n line-height: 1;\n }\n .hsk-cb-sources-next:hover { border-color: #ff6a33; color: #ff6a33; }\n /* Card: no border, no bg, no radius — clean dark canvas */\n .hsk-cb-source {\n flex: 0 0 188px;\n scroll-snap-align: start;\n border-radius: 0;\n border: none;\n background: transparent;\n cursor: pointer;\n transition: transform .14s, opacity .14s;\n animation: hsk-card-in .26s ease-out both;\n overflow: visible;\n }\n @keyframes hsk-card-in {\n from { opacity: 0; transform: translateX(16px); }\n to { opacity: 1; transform: none; }\n }\n .hsk-cb-source:hover { transform: translateY(-3px); opacity: .92; }\n .hsk-cb-src-imgwrap {\n width: 188px;\n height: 188px;\n overflow: hidden;\n border-radius: 0;\n display: block;\n }\n .hsk-cb-src-imgwrap img {\n width: 100%; height: 100%;\n object-fit: cover;\n transition: transform .22s;\n display: block;\n }\n .hsk-cb-source:hover .hsk-cb-src-imgwrap img { transform: scale(1.05); }\n .hsk-cb-src-imgwrap-empty {\n width: 188px;\n height: 188px;\n background: var(--hsk-chat-divide, rgba(255,255,255,.06));\n display: flex; align-items: center; justify-content: center;\n color: var(--hsk-chat-muted, #555);\n font-size: 32px;\n }\n .hsk-cb-src-info {\n padding: 8px 2px 0;\n }\n .hsk-cb-src-name {\n font-size: 13px;\n font-weight: 600;\n color: var(--hsk-chat-text, #eee);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n .hsk-cb-src-price {\n font-size: 13px;\n color: #ff6a33;\n font-weight: 700;\n margin-top: 3px;\n }\n\n /* ── Selected product inline card ── */\n .hsk-cb-selected-product {\n display: flex;\n align-items: flex-start;\n gap: 14px;\n margin-top: 16px;\n padding: 14px;\n border: 1px solid var(--hsk-chat-divide, rgba(255,255,255,.08));\n border-left: 3px solid #ff6a33;\n background: var(--hsk-chat-source-bg, rgba(255,255,255,.03));\n cursor: pointer;\n transition: border-color .15s;\n animation: hsk-msg-in .2s ease-out both;\n }\n .hsk-cb-selected-product:hover { border-left-color: rgba(255,106,51,.6); }\n .hsk-cb-selected-img {\n width: 64px; height: 64px;\n object-fit: cover;\n flex-shrink: 0;\n }\n .hsk-cb-selected-info { flex: 1; min-width: 0; }\n .hsk-cb-selected-name {\n font-size: 13px; font-weight: 700;\n color: var(--hsk-chat-text, #eee);\n margin-bottom: 3px;\n }\n .hsk-cb-selected-price {\n font-size: 13px; color: #ff6a33; font-weight: 700;\n }\n\n /* ── Typing indicator ── */\n .hsk-cb-typing-row {\n display: flex;\n align-items: flex-start;\n gap: 14px;\n padding: 20px 0;\n }\n .hsk-cb-typing {\n display: flex;\n gap: 5px;\n padding: 14px 18px;\n }\n .hsk-cb-dot {\n width: 7px; height: 7px;\n border-radius: 50%;\n background: var(--hsk-chat-muted, #ccc);\n animation: hsk-dot-pulse 1.2s ease-in-out infinite;\n }\n .hsk-cb-dot:nth-child(2) { animation-delay: .18s; }\n .hsk-cb-dot:nth-child(3) { animation-delay: .36s; }\n @keyframes hsk-dot-pulse {\n 0%,100% { opacity: .3; transform: scale(.75); }\n 50% { opacity: 1; transform: scale(1); }\n }\n\n /* ── Input area ── */\n .hsk-cb-input-wrap {\n padding: 16px 28px 28px;\n flex-shrink: 0;\n }\n .hsk-cb-input-box {\n display: flex;\n align-items: flex-end;\n gap: 10px;\n background: var(--hsk-chat-input-bg, rgba(0,0,0,.04));\n border: 1.5px solid var(--hsk-chat-divide, rgba(0,0,0,.1));\n border-radius: 18px;\n padding: 14px 14px 14px 20px;\n transition: border-color .15s, box-shadow .15s;\n }\n .hsk-cb-input-box:focus-within {\n border-color: #ff6a33;\n box-shadow: 0 0 0 3px rgba(255,106,51,.1);\n }\n .hsk-cb-textarea {\n flex: 1;\n background: transparent;\n border: none;\n outline: none;\n resize: none;\n font-size: 15px;\n color: var(--hsk-chat-text, #111);\n min-height: 24px;\n max-height: 140px;\n line-height: 1.55;\n font-family: inherit;\n }\n .hsk-cb-textarea::placeholder { color: var(--hsk-chat-muted, #aaa); }\n .hsk-cb-send {\n width: 38px; height: 38px;\n border-radius: 10px;\n background: #ff6a33;\n border: none;\n color: #fff;\n cursor: pointer;\n font-size: 18px;\n display: flex; align-items: center; justify-content: center;\n flex-shrink: 0;\n transition: opacity .15s, transform .1s, background .15s;\n font-family: inherit;\n }\n .hsk-cb-send:hover { opacity: .88; }\n .hsk-cb-send:active { transform: scale(.9); }\n .hsk-cb-send:disabled { opacity: .3; cursor: not-allowed; background: var(--hsk-chat-muted, #ccc); }\n .hsk-cb-hint {\n text-align: center;\n font-size: 11px;\n color: var(--hsk-chat-muted, #bbb);\n margin-top: 10px;\n }\n\n /* ── Error ── */\n .hsk-cb-error {\n margin: 8px 0;\n padding: 10px 14px;\n border-radius: 10px;\n background: rgba(239,68,68,.08);\n border: 1px solid rgba(239,68,68,.2);\n color: #ef4444;\n font-size: 13px;\n }\n\n /* ── Dark mode ── */\n @media (prefers-color-scheme: dark) {\n .hsk-cb-overlay {\n --hsk-chat-bg: #0e0e0f;\n --hsk-chat-text: #f0efed;\n --hsk-chat-muted: #555;\n --hsk-chat-divide: rgba(255,255,255,.07);\n --hsk-chat-input-bg: rgba(255,255,255,.05);\n --hsk-chat-source-bg: rgba(255,255,255,.04);\n --hsk-fade-bg: #0e0e0f;\n }\n .hsk-cb-overlay {\n background: rgba(0,0,0,.92) !important;\n }\n }\n @media (prefers-color-scheme: light) {\n .hsk-cb-overlay {\n --hsk-chat-bg: #fafafa;\n --hsk-chat-text: #111;\n --hsk-chat-muted: #999;\n --hsk-chat-divide: rgba(0,0,0,.08);\n --hsk-chat-input-bg: rgba(0,0,0,.04);\n --hsk-chat-source-bg: rgba(0,0,0,.025);\n --hsk-fade-bg: #fafafa;\n }\n .hsk-cb-overlay {\n background: rgba(240,240,244,.88) !important;\n }\n }\n`;\n\n/* ─── Suggestion chips ────────────────────────────────────────────────────── */\nconst CHIPS = [\n 'Cheapest smartphone',\n 'Smart TV under KSh 20,000',\n 'Noise-cancelling headphones',\n 'Best laptop for students',\n];\n\n/* ─── SourcesCarousel ─────────────────────────────────────────────────────── */\ninterface SourcesCarouselProps {\n sources: ChatSource[];\n onSelectSource?: (src: ChatSource) => void;\n}\n\nfunction SourcesCarousel({ sources, onSelectSource }: SourcesCarouselProps) {\n const railRef = useRef<HTMLDivElement>(null);\n const [showNext, setShowNext] = useState(false);\n\n const measure = useCallback(() => {\n const el = railRef.current;\n if (!el) return;\n // Hide next button when fully scrolled to end\n const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 8;\n setShowNext(el.scrollWidth > el.clientWidth + 4 && !atEnd);\n }, []);\n\n useEffect(() => {\n measure();\n const el = railRef.current;\n if (!el) return;\n const ro = new ResizeObserver(measure);\n ro.observe(el);\n el.addEventListener('scroll', measure, { passive: true });\n return () => { ro.disconnect(); el.removeEventListener('scroll', measure); };\n }, [measure, sources]);\n\n const scrollNext = () => {\n railRef.current?.scrollBy({ left: 170, behavior: 'smooth' });\n };\n\n return (\n <div className=\"hsk-cb-sources-wrap\">\n <div className=\"hsk-cb-sources\" ref={railRef}>\n {sources.map((src, si) => (\n <div\n key={si}\n className=\"hsk-cb-source\"\n style={{ animationDelay: `${si * 50}ms` }}\n onClick={() => onSelectSource?.(src)}\n >\n {src.image ? (\n <div className=\"hsk-cb-src-imgwrap\">\n <img src={src.image} alt={src.name} loading=\"lazy\" />\n </div>\n ) : (\n <div className=\"hsk-cb-src-imgwrap-empty\">✦</div>\n )}\n <div className=\"hsk-cb-src-info\">\n <div className=\"hsk-cb-src-name\">{src.name}</div>\n {src.price && (\n <div className=\"hsk-cb-src-price\">\n {src.currency ?? 'KES'}{' '}\n {parseFloat(src.price.replace(/[^0-9.]/g, '') || '0').toLocaleString()}\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n {showNext && (\n <>\n <div\n className=\"hsk-cb-sources-fade\"\n style={{ background: 'linear-gradient(to right, transparent, var(--hsk-fade-bg, #0e0e0f))' }}\n />\n <button className=\"hsk-cb-sources-next\" onClick={scrollNext} aria-label=\"See more\">›</button>\n </>\n )}\n </div>\n );\n}\n\n/* ── Chat Modal ────────────────────────────────────────────────────────────── */\ninterface ChatModalProps extends Pick<AIChatButtonProps, 'title' | 'placeholder' | 'backdropColor' | 'backdropBlur' | 'onSelectSource'> {\n onClose: () => void;\n}\n\nfunction ChatModal({\n title = 'AI Shopping Assistant',\n placeholder = 'Ask me anything — gifts, budget, use case…',\n backdropColor,\n backdropBlur,\n onClose,\n onSelectSource,\n}: ChatModalProps) {\n const { messages, sources, loading, error, send, reset } = useChat();\n const [input, setInput] = useState('');\n const [selectedProduct, setSelectedProduct] = useState<ChatSource | null>(null);\n const bottomRef = useRef<HTMLDivElement>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages, loading, selectedProduct]);\n\n useEffect(() => {\n const h = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };\n document.addEventListener('keydown', h);\n return () => document.removeEventListener('keydown', h);\n }, []);\n\n const handleSourceClick = (src: ChatSource) => {\n setSelectedProduct(src);\n onSelectSource?.(src);\n // Auto-ask LLM to describe the selected product in detail\n const q = `Tell me more about the ${src.name}${src.price ? ` (${src.currency ?? 'KES'} ${src.price})` : ''} — what are its key specs, who is it best for, and is it worth buying?`;\n send(q);\n };\n\n const handleSend = async (text?: string) => {\n const q = (text ?? input).trim();\n if (!q || loading) return;\n setSelectedProduct(null);\n setInput('');\n if (textareaRef.current) {\n textareaRef.current.style.height = 'auto';\n }\n await send(q);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); }\n };\n\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInput(e.target.value);\n const t = e.target;\n t.style.height = 'auto';\n t.style.height = `${Math.min(t.scrollHeight, 140)}px`;\n };\n\n const blurVal = typeof backdropBlur === 'number' ? `${backdropBlur}px` : (backdropBlur ?? '20px');\n\n return (\n <>\n <style>{CSS}</style>\n <div\n className=\"hsk-cb-overlay\"\n onClick={onClose}\n style={{\n backdropFilter: `blur(${blurVal})`,\n WebkitBackdropFilter: `blur(${blurVal})`,\n ...(backdropColor ? { background: backdropColor } : {}),\n }}\n >\n <div className=\"hsk-cb-panel\" onClick={e => e.stopPropagation()}>\n\n {/* Top bar */}\n <div className=\"hsk-cb-topbar\">\n <div className=\"hsk-cb-topbar-left\">\n <span className=\"hsk-cb-topbar-icon\">✦</span>\n <div>\n <div className=\"hsk-cb-topbar-title\">{title}</div>\n <div className=\"hsk-cb-topbar-sub\">Powered by Huskel AI · searches the whole catalogue</div>\n </div>\n </div>\n <div className=\"hsk-cb-topbar-actions\">\n {messages.length > 0 && (\n <button className=\"hsk-cb-topbar-btn\" onClick={reset}>Clear chat</button>\n )}\n <button className=\"hsk-cb-close\" onClick={onClose} aria-label=\"Close\">×</button>\n </div>\n </div>\n\n {/* Messages */}\n <div className=\"hsk-cb-msgs\">\n {messages.length === 0 ? (\n <div className=\"hsk-cb-empty\">\n <div className=\"hsk-cb-empty-icon\">✦</div>\n <div className=\"hsk-cb-empty-title\">What can I help you find?</div>\n <div className=\"hsk-cb-empty-sub\">\n Ask about products, budgets, gift ideas, specs — I'll search the entire catalogue for you.\n </div>\n <div className=\"hsk-cb-chips\">\n {CHIPS.map(chip => (\n <button\n key={chip}\n className=\"hsk-cb-chip\"\n onClick={() => handleSend(chip)}\n >\n {chip}\n </button>\n ))}\n </div>\n </div>\n ) : (\n messages.map((msg: ChatMessage, idx: number) => {\n const isLast = idx === messages.length - 1;\n const isUser = msg.role === 'user';\n return (\n <div key={idx} className=\"hsk-cb-msg-group\">\n {isUser ? (\n <div className=\"hsk-cb-user-msg\">\n <div className=\"hsk-cb-user-bubble\">{msg.content}</div>\n </div>\n ) : (\n <div className=\"hsk-cb-ai-msg\">\n <div className=\"hsk-cb-ai-icon\">✦</div>\n <div className=\"hsk-cb-ai-body\">\n <div className=\"hsk-cb-ai-text\">{msg.content}</div>\n\n {/* Sources as horizontal carousel — only after latest assistant reply */}\n {isLast && sources.length > 0 && (\n <SourcesCarousel\n sources={sources}\n onSelectSource={handleSourceClick}\n />\n )}\n </div>\n </div>\n )}\n </div>\n );\n })\n )}\n\n {/* Selected product pinned card — shows while LLM fetches details */}\n {selectedProduct && loading && (\n <div\n className=\"hsk-cb-selected-product\"\n onClick={() => selectedProduct.url && window.open(selectedProduct.url, '_blank')}\n >\n {selectedProduct.image && (\n <img className=\"hsk-cb-selected-img\" src={selectedProduct.image} alt={selectedProduct.name} />\n )}\n <div className=\"hsk-cb-selected-info\">\n <div className=\"hsk-cb-selected-name\">{selectedProduct.name}</div>\n {selectedProduct.price && (\n <div className=\"hsk-cb-selected-price\">\n {selectedProduct.currency ?? 'KES'} {parseFloat((selectedProduct.price ?? '').replace(/[^0-9.]/g, '') || '0').toLocaleString()}\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Typing dots */}\n {loading && (\n <div className=\"hsk-cb-typing-row\">\n <div className=\"hsk-cb-ai-icon\">✦</div>\n <div className=\"hsk-cb-typing\">\n <div className=\"hsk-cb-dot\" />\n <div className=\"hsk-cb-dot\" />\n <div className=\"hsk-cb-dot\" />\n </div>\n </div>\n )}\n\n {error && <div className=\"hsk-cb-error\">{error}</div>}\n <div ref={bottomRef} style={{ height: 1 }} />\n </div>\n\n {/* Input */}\n <div className=\"hsk-cb-input-wrap\">\n <div className=\"hsk-cb-input-box\">\n <textarea\n ref={textareaRef}\n className=\"hsk-cb-textarea\"\n value={input}\n onChange={handleInput}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n rows={1}\n disabled={loading}\n autoFocus\n />\n <button\n className=\"hsk-cb-send\"\n onClick={() => handleSend()}\n disabled={!input.trim() || loading}\n aria-label=\"Send\"\n >\n ↑\n </button>\n </div>\n <div className=\"hsk-cb-hint\">Huskel AI · searches the whole catalogue in real time</div>\n </div>\n\n </div>\n </div>\n </>\n );\n}\n\n/* ── Exported AIChatButton ────────────────────────────────────────────────── */\nexport function AIChatButton({\n label,\n title,\n placeholder,\n backdropColor,\n backdropBlur,\n className,\n onSelectSource,\n}: AIChatButtonProps) {\n const [open, setOpen] = useState(false);\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => { setMounted(true); }, []);\n\n return (\n <>\n <style>{CSS}</style>\n <button\n className={`hsk-cb-btn ${className ?? ''}`}\n onClick={() => setOpen(true)}\n aria-label=\"Open AI chat\"\n >\n <span className=\"hsk-cb-btn-icon\">✦</span>\n {label !== undefined ? label : null}\n </button>\n {open && mounted && createPortal(\n <ChatModal\n title={title}\n placeholder={placeholder}\n backdropColor={backdropColor}\n backdropBlur={backdropBlur}\n onClose={() => setOpen(false)}\n onSelectSource={onSelectSource}\n />,\n document.body\n )}\n </>\n );\n}\n"],"mappings":"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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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["import_react","import_react","import_react","import_react","import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","CSS","import_react","import_jsx_runtime","import_react","import_react_dom","import_jsx_runtime","CSS","_a"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/api.ts","../src/client.ts","../src/hooks/useHuskel.ts","../src/hooks/useSearch.ts","../src/components/HuskelProvider.tsx","../src/hooks/useIngest.ts","../src/hooks/usePageIngest.ts","../src/hooks/useChat.ts","../src/components/SearchBar.tsx","../src/components/Sparkle.tsx","../src/components/ChatWidget.tsx","../src/components/AIChatButton.tsx"],"sourcesContent":["import './styles.css';\r\nexport { initHuskel, getHuskelClient, HuskelClient } from './client';\r\nexport { HuskelAPI } from './api';\r\nexport { useHuskel } from './hooks/useHuskel';\r\nexport { useSearch } from './hooks/useSearch';\r\nexport { useIngest } from './hooks/useIngest';\r\nexport { usePageIngest } from './hooks/usePageIngest';\r\nexport { useChat } from './hooks/useChat';\r\nexport type { ChatMessage, ChatSource } from './hooks/useChat';\r\nexport { SearchBar } from './components/SearchBar';\r\nexport { Sparkle } from './components/Sparkle';\r\nexport { ChatWidget } from './components/ChatWidget';\r\nexport { AIChatButton } from './components/AIChatButton';\r\nexport { HuskelProvider } from './components/HuskelProvider';\r\nexport type {\r\n Product,\r\n RawProductInput,\r\n HuskelConfig,\r\n SearchRequest,\r\n SearchResult,\r\n SearchResponse,\r\n IngestResponse,\r\n HuskelError,\r\n} from './types';\r\n","import { Product, SearchResponse, IngestResponse, HuskelError } from './types';\r\n\r\nconst MAX_RETRIES = 3;\r\nconst RETRY_DELAYS = [500, 1000, 2000]; // ms\r\n\r\nfunction log(level: 'info' | 'warn' | 'error', msg: string, data?: unknown) {\r\n const prefix = '[Huskel]';\r\n if (level === 'error') console.error(prefix, msg, data ?? '');\r\n else if (level === 'warn') console.warn(prefix, msg, data ?? '');\r\n else console.log(prefix, msg, data ?? '');\r\n}\r\n\r\nasync function sleep(ms: number) {\r\n return new Promise(r => setTimeout(r, ms));\r\n}\r\n\r\nexport class HuskelAPI {\r\n constructor(\r\n private apiUrl: string,\r\n private siteId: string,\r\n private apiToken: string,\r\n private getShopperId?: () => string | undefined,\r\n private getSessionId?: () => string | undefined\r\n ) {}\r\n\r\n private async post<T>(path: string, body: unknown, attempt = 0): Promise<T> {\r\n const url = `${this.apiUrl}${path}`;\r\n\r\n try {\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n 'X-Huskel-Token': this.apiToken,\r\n 'X-Huskel-Site': this.siteId,\r\n };\r\n\r\n const shopperId = this.getShopperId?.();\r\n if (shopperId) {\r\n headers['X-Huskel-Shopper-Id'] = shopperId;\r\n }\r\n\r\n const sessionId = this.getSessionId?.();\r\n if (sessionId) {\r\n headers['X-Huskel-Session-Id'] = sessionId;\r\n }\r\n\r\n const res = await fetch(url, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body),\r\n });\r\n\r\n if (!res.ok) {\r\n const text = await res.text();\r\n const err: HuskelError = { status: res.status, message: text };\r\n\r\n // Don't retry 4xx — developer errors\r\n if (res.status >= 400 && res.status < 500) {\r\n log('error', `${path} failed [${res.status}]`, text);\r\n throw err;\r\n }\r\n\r\n // Retry 5xx\r\n if (attempt < MAX_RETRIES - 1) {\r\n log('warn', `${path} [${res.status}] retrying (${attempt + 1}/${MAX_RETRIES})...`);\r\n await sleep(RETRY_DELAYS[attempt]);\r\n return this.post(path, body, attempt + 1);\r\n }\r\n\r\n log('error', `${path} failed after ${MAX_RETRIES} attempts`, err);\r\n throw err;\r\n }\r\n\r\n return res.json();\r\n } catch (e) {\r\n // Network error (offline, DNS, etc.)\r\n if ((e as HuskelError).status === undefined) {\r\n if (attempt < MAX_RETRIES - 1) {\r\n log('warn', `${path} network error, retrying (${attempt + 1}/${MAX_RETRIES})...`);\r\n await sleep(RETRY_DELAYS[attempt]);\r\n return this.post(path, body, attempt + 1);\r\n }\r\n log('error', `${path} unreachable after ${MAX_RETRIES} attempts`);\r\n }\r\n throw e;\r\n }\r\n }\r\n\r\n async ingest(product: Product): Promise<IngestResponse> {\r\n log('info', 'ingesting product', product.name);\r\n return this.post('/ingest', { siteId: this.siteId, product });\r\n }\r\n\r\n async ingestBatch(products: Product[]): Promise<IngestResponse> {\r\n log('info', `ingesting batch of ${products.length} products`);\r\n return this.post('/ingest/batch', { siteId: this.siteId, products });\r\n }\r\n\r\n async search(query: string, limit = 10): Promise<SearchResponse> {\r\n log('info', 'search query', query);\r\n return this.post('/search', { query, siteId: this.siteId, limit });\r\n }\r\n\r\n // Pure vector search — no LLM, instant results. This is what the SearchBar uses.\r\n async searchVector(query: string, limit = 10): Promise<SearchResponse> {\r\n return this.post('/search/vector', { query, siteId: this.siteId, limit });\r\n }\r\n\r\n // Autocomplete — pure in-memory Trie, <1ms, no Upstash call. Only true prefix matches.\r\n async searchAutocomplete(query: string, limit = 8): Promise<SearchResponse> {\r\n return this.post('/search/autocomplete', { query, siteId: this.siteId, limit });\r\n }\r\n\r\n // LLM chat — conversational search with history context.\r\n async chat(query: string, history: Array<{ role: 'user' | 'assistant'; content: string }> = []): Promise<{ answer: string; sources: any[] }> {\r\n log('info', 'chat query', query);\r\n return this.post('/chat', { query, siteId: this.siteId, history });\r\n }\r\n}\r\n","import { HuskelConfig, Product, RawProductInput } from './types';\r\nimport { HuskelAPI } from './api';\r\n\r\nfunction getEnvVar(key: string): string | undefined {\r\n if (typeof globalThis !== 'undefined') {\r\n const g = globalThis as any;\r\n if (g.process && g.process.env) {\r\n return g.process.env[key];\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\nfunction mapRawProduct(input: RawProductInput): Product | null {\r\n const name = input.name || input.title || input.productName || '';\r\n \r\n let price = '';\r\n let priceNumeric: number | undefined = undefined;\r\n\r\n if (input.price !== undefined) {\r\n if (typeof input.price === 'number') {\r\n priceNumeric = input.price;\r\n price = String(input.price);\r\n } else {\r\n price = input.price;\r\n const num = parseFloat(input.price.replace(/[^0-9.]/g, ''));\r\n priceNumeric = isNaN(num) ? undefined : num;\r\n }\r\n }\r\n if (input.priceNumeric !== undefined) {\r\n priceNumeric = input.priceNumeric;\r\n }\r\n\r\n let url = input.url || '';\r\n if (!url && typeof window !== 'undefined') {\r\n url = window.location.href;\r\n }\r\n\r\n let slug = input.slug || input.id || input.productId || '';\r\n if (!slug && url) {\r\n slug = url.split('/').filter(Boolean).pop() || '';\r\n }\r\n if (!slug && name) {\r\n slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');\r\n }\r\n\r\n let images: string[] = [];\r\n if (input.images) {\r\n images = input.images;\r\n } else if (input.image) {\r\n images = [input.image];\r\n } else if (input.thumbnail) {\r\n images = [input.thumbnail];\r\n }\r\n\r\n if (!name) {\r\n console.warn('[Huskel] Validation warning: Product name/title is missing. Skipping:', input);\r\n return null;\r\n }\r\n if (!price) {\r\n console.warn('[Huskel] Validation warning: Product price is missing. Skipping:', input);\r\n return null;\r\n }\r\n if (!url) {\r\n console.warn('[Huskel] Validation warning: Product URL is missing. Skipping:', input);\r\n return null;\r\n }\r\n\r\n return {\r\n name,\r\n price,\r\n url,\r\n brand: input.brand,\r\n description: input.description,\r\n originalPrice: input.originalPrice,\r\n discount: input.discount,\r\n currency: input.currency ?? 'KES',\r\n stock: input.stock,\r\n availability: input.availability,\r\n rating: input.rating,\r\n reviewCount: input.reviewCount,\r\n category: input.category,\r\n subCategory: input.subCategory,\r\n tags: input.tags,\r\n images: images.length > 0 ? images : undefined,\r\n specs: input.specs,\r\n priceNumeric,\r\n slug,\r\n };\r\n}\r\n\r\nfunction generateUUID(): string {\r\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\r\n return crypto.randomUUID();\r\n }\r\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n}\r\n\r\nexport class HuskelClient {\r\n readonly api: HuskelAPI;\r\n private ingestQueue: Product[] = [];\r\n private ingestTimer: ReturnType<typeof setTimeout> | null = null;\r\n private ingestedUrls = new Set<string>();\r\n private onlineHandler: (() => void) | null = null;\r\n private shopperId?: string;\r\n private sessionId: string = '';\r\n\r\n private static INGEST_CACHE_KEY = 'huskel_ingested_v1';\r\n private static INGEST_CACHE_TTL = 24 * 60 * 60 * 1000; // 24h\r\n\r\n private loadIngestedCache() {\r\n if (typeof window === 'undefined') return;\r\n try {\r\n const raw = localStorage.getItem(HuskelClient.INGEST_CACHE_KEY);\r\n if (!raw) return;\r\n const { ts, urls }: { ts: number; urls: string[] } = JSON.parse(raw);\r\n if (Date.now() - ts > HuskelClient.INGEST_CACHE_TTL) {\r\n localStorage.removeItem(HuskelClient.INGEST_CACHE_KEY);\r\n return;\r\n }\r\n this.ingestedUrls = new Set(urls);\r\n } catch { /* ignore */ }\r\n }\r\n\r\n private saveIngestedCache() {\r\n if (typeof window === 'undefined') return;\r\n try {\r\n localStorage.setItem(\r\n HuskelClient.INGEST_CACHE_KEY,\r\n JSON.stringify({ ts: Date.now(), urls: [...this.ingestedUrls] })\r\n );\r\n } catch { /* ignore */ }\r\n }\r\n\r\n constructor(config: HuskelConfig) {\r\n const siteId = config.siteId || getEnvVar('NEXT_PUBLIC_HUSKEL_SITE_ID') || '';\r\n const apiUrl = config.apiUrl || getEnvVar('NEXT_PUBLIC_HUSKEL_API_URL') || '';\r\n const apiToken = config.apiToken || getEnvVar('NEXT_PUBLIC_HUSKEL_API_TOKEN') || '';\r\n\r\n // Runtime validation — fail loudly so misconfiguration is never silent\r\n if (!siteId) console.error('[Huskel] Missing siteId. Set it via <HuskelProvider siteId=\"...\"> or NEXT_PUBLIC_HUSKEL_SITE_ID.');\r\n if (!apiUrl) console.error('[Huskel] Missing apiUrl. Set it via <HuskelProvider apiUrl=\"...\"> or NEXT_PUBLIC_HUSKEL_API_URL.');\r\n if (!apiToken) console.error('[Huskel] Missing apiToken. Set it via <HuskelProvider apiToken=\"...\"> or NEXT_PUBLIC_HUSKEL_API_TOKEN.');\r\n\r\n this.shopperId = config.shopperId;\r\n this.initSession();\r\n this.loadIngestedCache();\r\n\r\n this.api = new HuskelAPI(\r\n apiUrl,\r\n siteId,\r\n apiToken,\r\n () => this.shopperId,\r\n () => this.sessionId\r\n );\r\n instance = this;\r\n\r\n if (typeof window !== 'undefined') {\r\n this.onlineHandler = () => {\r\n console.log('[Huskel] Connectivity restored, flushing queued ingestions.');\r\n this.flushQueue();\r\n };\r\n window.addEventListener('online', this.onlineHandler);\r\n }\r\n }\r\n\r\n setShopperId(id: string | undefined) {\r\n this.shopperId = id;\r\n }\r\n\r\n getShopperId(): string | undefined {\r\n return this.shopperId;\r\n }\r\n\r\n getSessionId(): string {\r\n return this.sessionId;\r\n }\r\n\r\n private initSession() {\r\n if (typeof window !== 'undefined' && window.sessionStorage) {\r\n try {\r\n let sid = window.sessionStorage.getItem('huskel_session_id');\r\n if (!sid) {\r\n sid = generateUUID();\r\n window.sessionStorage.setItem('huskel_session_id', sid);\r\n }\r\n this.sessionId = sid;\r\n return;\r\n } catch (e) {\r\n // Fallback if sessionStorage is disabled or private mode\r\n }\r\n }\r\n this.sessionId = generateUUID();\r\n }\r\n\r\n destroy() {\r\n if (typeof window !== 'undefined' && this.onlineHandler) {\r\n window.removeEventListener('online', this.onlineHandler);\r\n this.onlineHandler = null;\r\n }\r\n if (this.ingestTimer) {\r\n clearTimeout(this.ingestTimer);\r\n this.ingestTimer = null;\r\n }\r\n if (instance === this) instance = null;\r\n }\r\n\r\n async queueIngest(rawProduct: RawProductInput): Promise<void> {\r\n const product = mapRawProduct(rawProduct);\r\n if (!product) return;\r\n\r\n if (this.ingestedUrls.has(product.url)) {\r\n return; // already indexed in this session or today — skip\r\n }\r\n this.ingestedUrls.add(product.url);\r\n this.saveIngestedCache();\r\n\r\n this.ingestQueue.push(product);\r\n this.scheduleFlush();\r\n }\r\n\r\n async queueIngestBatch(rawProducts: RawProductInput[]): Promise<void> {\r\n rawProducts.forEach(p => {\r\n const product = mapRawProduct(p);\r\n if (!product) return;\r\n\r\n if (this.ingestedUrls.has(product.url)) {\r\n return;\r\n }\r\n this.ingestedUrls.add(product.url);\r\n this.ingestQueue.push(product);\r\n });\r\n\r\n if (this.ingestQueue.length > 0) {\r\n this.saveIngestedCache();\r\n this.scheduleFlush();\r\n }\r\n }\r\n\r\n private scheduleFlush() {\r\n if (this.ingestTimer) return;\r\n this.ingestTimer = setTimeout(() => {\r\n this.flushQueue();\r\n }, 300);\r\n }\r\n\r\n private async flushQueue() {\r\n this.ingestTimer = null;\r\n if (this.ingestQueue.length === 0) return;\r\n\r\n if (typeof navigator !== 'undefined' && !navigator.onLine) {\r\n console.warn('[Huskel] Browser offline. Postponing ingestion.');\r\n return;\r\n }\r\n\r\n const batch = [...this.ingestQueue];\r\n this.ingestQueue = [];\r\n\r\n try {\r\n await this.api.ingestBatch(batch);\r\n } catch (e: any) {\r\n if (e.status && e.status >= 400 && e.status < 500) {\r\n console.error('[Huskel] Ingestion discarded due to client error:', e.message);\r\n return;\r\n }\r\n\r\n // Re-queue and schedule another flush so items are not stuck forever\r\n console.warn('[Huskel] Ingestion failed. Re-queuing to retry.', e);\r\n this.ingestQueue = [...batch, ...this.ingestQueue];\r\n this.scheduleFlush();\r\n }\r\n }\r\n}\r\n\r\nlet instance: HuskelClient | null = null;\r\n\r\nexport function initHuskel(config: HuskelConfig): HuskelClient {\r\n instance = new HuskelClient(config);\r\n return instance;\r\n}\r\n\r\nexport function getHuskelClient(): HuskelClient {\r\n if (!instance) {\r\n const siteId = getEnvVar('NEXT_PUBLIC_HUSKEL_SITE_ID');\r\n const apiUrl = getEnvVar('NEXT_PUBLIC_HUSKEL_API_URL');\r\n const apiToken = getEnvVar('NEXT_PUBLIC_HUSKEL_API_TOKEN');\r\n\r\n if (siteId && apiUrl && apiToken) {\r\n instance = new HuskelClient({ siteId, apiUrl, apiToken });\r\n } else {\r\n throw new Error('[Huskel] Call initHuskel() or set NEXT_PUBLIC_HUSKEL_* environment variables before using the client.');\r\n }\r\n }\r\n return instance;\r\n}\r\n","import { useRef } from 'react';\r\nimport { HuskelConfig } from '../types';\r\nimport { HuskelClient, initHuskel } from '../client';\r\n\r\n/**\r\n * @deprecated Use <HuskelProvider> instead to avoid SSR issues.\r\n */\r\nexport function useHuskel(config: HuskelConfig): HuskelClient {\r\n const clientRef = useRef<HuskelClient | null>(null);\r\n\r\n if (!clientRef.current) {\r\n console.warn('[Huskel] useHuskel() is deprecated. Please wrap your application in <HuskelProvider> instead.');\r\n clientRef.current = initHuskel(config);\r\n }\r\n\r\n return clientRef.current;\r\n}\r\n","import { useState, useCallback, useRef } from 'react';\nimport { SearchResult } from '../types';\nimport { useHuskelContext } from '../components/HuskelProvider';\n\ninterface UseSearchReturn {\n results: SearchResult[];\n loading: boolean;\n error: string | null;\n search: (query: string, limit?: number) => Promise<void>;\n clear: () => void;\n}\n\nexport function useSearch(): UseSearchReturn {\n const client = useHuskelContext();\n const [results, setResults] = useState<SearchResult[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n // Generation counter so stale responses from any in-flight requests don't overwrite newer ones\n const genRef = useRef(0);\n\n const search = useCallback(async (query: string, limit = 8) => {\n if (!query.trim()) { setResults([]); setLoading(false); return; }\n const gen = ++genRef.current;\n // No loading spinner for autocomplete — it's so fast it would just flicker\n setError(null);\n try {\n // searchAutocomplete = pure in-memory Trie, <1ms, no Upstash\n const res = await client.api.searchAutocomplete(query, limit);\n if (gen === genRef.current) {\n setResults(res.results ?? []);\n }\n } catch (e: unknown) {\n if (gen === genRef.current) {\n setError((e as Error).message ?? 'Search failed');\n }\n } finally {\n if (gen === genRef.current) setLoading(false);\n }\n }, [client]);\n\n const clear = useCallback(() => {\n genRef.current++;\n setResults([]);\n setError(null);\n setLoading(false);\n }, []);\n\n return { results, loading, error, search, clear };\n}\n","'use client';\r\n\r\nimport React, { createContext, useContext, useEffect, useRef } from 'react';\r\nimport { HuskelClient, getHuskelClient } from '../client';\r\nimport { HuskelConfig } from '../types';\r\n\r\nexport const HuskelContext = createContext<HuskelClient | null>(null);\r\n\r\ninterface HuskelProviderProps extends HuskelConfig {\r\n children: React.ReactNode;\r\n}\r\n\r\nexport function HuskelProvider({ siteId, apiUrl, apiToken, shopperId, children }: HuskelProviderProps) {\r\n const clientRef = useRef<HuskelClient | null>(null);\r\n\r\n if (!clientRef.current) {\r\n clientRef.current = new HuskelClient({ siteId, apiUrl, apiToken, shopperId });\r\n }\r\n\r\n // Update shopperId dynamically when it changes (e.g., shopper logs in/out)\r\n useEffect(() => {\r\n clientRef.current?.setShopperId(shopperId);\r\n }, [shopperId]);\r\n\r\n // Clean up the online listener and timers when the provider unmounts\r\n // (prevents leaks during hot module reload and React StrictMode double-mount)\r\n useEffect(() => {\r\n return () => {\r\n clientRef.current?.destroy();\r\n };\r\n }, []);\r\n\r\n return (\r\n <HuskelContext.Provider value={clientRef.current}>\r\n {children}\r\n </HuskelContext.Provider>\r\n );\r\n}\r\n\r\nexport function useHuskelContext(): HuskelClient {\r\n const context = useContext(HuskelContext);\r\n if (!context) {\r\n return getHuskelClient();\r\n }\r\n return context;\r\n}\r\n\r\n","import { useCallback, useState } from 'react';\r\nimport { RawProductInput } from '../types';\r\nimport { useHuskelContext } from '../components/HuskelProvider';\r\n\r\ninterface UseIngestReturn {\r\n ingest: (product: RawProductInput) => Promise<void>;\r\n ingestBatch: (products: RawProductInput[]) => Promise<void>;\r\n loading: boolean;\r\n error: string | null;\r\n}\r\n\r\nexport function useIngest(): UseIngestReturn {\r\n const client = useHuskelContext();\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const ingest = useCallback(async (product: RawProductInput) => {\r\n setLoading(true);\r\n setError(null);\r\n try {\r\n await client.queueIngest(product);\r\n } catch (e: unknown) {\r\n setError((e as Error).message ?? 'Ingest failed');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [client]);\r\n\r\n const ingestBatch = useCallback(async (products: RawProductInput[]) => {\r\n if (!products.length) return;\r\n setLoading(true);\r\n setError(null);\r\n try {\r\n await client.queueIngestBatch(products);\r\n } catch (e: unknown) {\r\n setError((e as Error).message ?? 'Batch ingest failed');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [client]);\r\n\r\n return { ingest, ingestBatch, loading, error };\r\n}\r\n","import { useEffect, useRef } from 'react';\nimport { RawProductInput } from '../types';\nimport { getHuskelClient } from '../client';\n\n/**\n * usePageIngest — drop this into any product page component.\n * The moment a customer's browser renders the page, the product is\n * automatically captured and queued for ingestion into the vector index.\n *\n * No configuration needed beyond <HuskelProvider> in your layout.\n *\n * @example\n * // Product detail page — Next.js or React\n * export function ProductPage({ product }) {\n * usePageIngest({\n * name: product.title,\n * price: product.price,\n * url: window.location.href,\n * images: [product.thumbnail],\n * category: product.category,\n * });\n * return <div>...</div>;\n * }\n */\nexport function usePageIngest(product: RawProductInput | null | undefined): void {\n // Use url as the stable key — avoids re-ingesting on unrelated re-renders\n const ingestedRef = useRef<string | null>(null);\n\n useEffect(() => {\n if (!product) return;\n\n // Resolve URL — falls back to window.location if not provided\n const url =\n product.url ||\n (typeof window !== 'undefined' ? window.location.href : '');\n\n // Guard: only ingest once per URL per component lifecycle\n if (ingestedRef.current === url) return;\n ingestedRef.current = url;\n\n try {\n getHuskelClient().queueIngest({ ...product, url });\n } catch {\n // Client not initialised — silently skip\n }\n }, [product?.url ?? product?.name]);\n}\n","import { useState, useCallback, useRef } from 'react';\nimport { useHuskelContext } from '../components/HuskelProvider';\n\nexport interface ChatMessage {\n role: 'user' | 'assistant';\n content: string;\n}\n\nexport interface ChatSource {\n id?: string;\n name: string;\n price?: string;\n currency?: string;\n category?: string;\n url?: string;\n image?: string;\n}\n\ninterface UseChatReturn {\n messages: ChatMessage[];\n sources: ChatSource[];\n loading: boolean;\n error: string | null;\n send: (query: string) => Promise<void>;\n reset: () => void;\n}\n\nexport function useChat(): UseChatReturn {\n const client = useHuskelContext();\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [sources, setSources] = useState<ChatSource[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n const send = useCallback(async (query: string) => {\n if (!query.trim() || loading) return;\n abortRef.current?.abort();\n abortRef.current = new AbortController();\n\n // Optimistically add the user message\n const userMsg: ChatMessage = { role: 'user', content: query };\n setMessages(prev => [...prev, userMsg]);\n setLoading(true);\n setError(null);\n\n try {\n // Build history from current messages (exclude the one we just added — server will see it as query)\n const history = messages.map(m => ({ role: m.role, content: m.content }));\n const res = await client.api.chat(query, history);\n\n const assistantMsg: ChatMessage = { role: 'assistant', content: res.answer };\n setMessages(prev => [...prev, assistantMsg]);\n setSources(res.sources ?? []);\n } catch (e: any) {\n setError(e?.message ?? 'Chat request failed');\n // Remove the optimistic user message on failure\n setMessages(prev => prev.slice(0, -1));\n } finally {\n setLoading(false);\n }\n }, [client, messages, loading]);\n\n const reset = useCallback(() => {\n setMessages([]);\n setSources([]);\n setError(null);\n }, []);\n\n return { messages, sources, loading, error, send, reset };\n}\n","import React, { useState, useEffect, useRef } from 'react';\nimport { useSearch } from '../hooks/useSearch';\nimport { SearchResult } from '../types';\n\nexport interface SearchBarProps {\n placeholder?: string;\n limit?: number;\n /** Debounce in ms — default 80 for near-instant feel */\n debounceMs?: number;\n onSelect?: (result: SearchResult) => void;\n className?: string;\n inputClassName?: string;\n dropdownClassName?: string;\n renderResult?: (result: SearchResult) => React.ReactNode;\n theme?: {\n primaryColor?: string;\n backgroundColor?: string;\n textColor?: string;\n fontFamily?: string;\n };\n classNames?: {\n root?: string;\n input?: string;\n dropdown?: string;\n row?: string;\n };\n}\n\n/* SVG search glass — pure inline so no icon dependency */\nconst SearchIcon = () => (\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <circle cx=\"8.5\" cy=\"8.5\" r=\"5.5\"/>\n <line x1=\"13\" y1=\"13\" x2=\"18\" y2=\"18\"/>\n </svg>\n);\n\nexport function SearchBar({\n placeholder = 'Search products…',\n limit = 10,\n debounceMs = 80,\n onSelect,\n className,\n inputClassName,\n dropdownClassName,\n renderResult,\n theme,\n classNames = {},\n}: SearchBarProps) {\n const [query, setQuery] = useState('');\n const [open, setOpen] = useState(false);\n const { results, loading, search, clear } = useSearch();\n const timer = useRef<ReturnType<typeof setTimeout>>();\n const wrap = useRef<HTMLDivElement>(null);\n\n /* Debounce search — but keep stale results visible between calls */\n useEffect(() => {\n clearTimeout(timer.current);\n if (!query.trim()) { clear(); setOpen(false); return; }\n setOpen(true); // open immediately (stale results show while fetching)\n timer.current = setTimeout(() => { search(query, limit); }, debounceMs);\n return () => clearTimeout(timer.current);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [query]);\n\n /* Click-outside to close */\n useEffect(() => {\n const h = (e: MouseEvent) => {\n if (wrap.current && !wrap.current.contains(e.target as Node)) setOpen(false);\n };\n document.addEventListener('mousedown', h);\n return () => document.removeEventListener('mousedown', h);\n }, []);\n\n const handleSelect = (r: SearchResult) => {\n setOpen(false);\n setQuery(r.product.name);\n onSelect?.(r);\n };\n\n const showDrop = open && query.trim().length > 0;\n\n const customStyles = {\n ...(theme?.primaryColor && { '--hsk-primary': theme.primaryColor }),\n ...(theme?.backgroundColor && { '--hsk-bg': theme.backgroundColor }),\n ...(theme?.textColor && { '--hsk-text': theme.textColor }),\n ...(theme?.fontFamily && { '--hsk-font': theme.fontFamily }),\n } as React.CSSProperties;\n\n return (\n <div className={`hsk-sb-wrap ${classNames.root || ''} ${className || ''}`} ref={wrap} style={customStyles}>\n <span className=\"hsk-sb-icon\"><SearchIcon /></span>\n <input\n className={`hsk-sb-input ${classNames.input || ''} ${inputClassName || ''}`}\n type=\"text\"\n value={query}\n placeholder={placeholder}\n onChange={e => setQuery(e.target.value)}\n onFocus={() => results.length > 0 && query.trim() && setOpen(true)}\n autoComplete=\"off\"\n spellCheck={false}\n />\n {showDrop && (\n <div className={`hsk-sb-drop ${classNames.dropdown || ''} ${dropdownClassName || ''}`} style={{ position: 'absolute' }}>\n {loading && <div className=\"hsk-sb-loading-bar\" />}\n\n {results.length === 0 && !loading && (\n <div className=\"hsk-sb-empty\">No results for &ldquo;{query}&rdquo;</div>\n )}\n\n {results.map((r, i) => (\n renderResult ? (\n <div\n key={r.id}\n onClick={() => handleSelect(r)}\n className=\"hsk-sb-fade\"\n style={{ animationDelay: `${i * 18}ms` }}\n >\n {renderResult(r)}\n </div>\n ) : (\n <div\n key={r.id}\n className={`hsk-sb-row hsk-sb-fade ${classNames.row || ''}`}\n style={{ animationDelay: `${i * 18}ms` }}\n onClick={() => handleSelect(r)}\n >\n <span className=\"hsk-sb-row-icon\"><SearchIcon /></span>\n <div className=\"hsk-sb-row-body\">\n <div className=\"hsk-sb-row-title\">{r.product.name}</div>\n {(r.product.category || r.product.brand) && (\n <div className=\"hsk-sb-row-sub\">\n {r.product.category ?? r.product.brand}\n </div>\n )}\n </div>\n </div>\n )\n ))}\n </div>\n )}\n </div>\n );\n}\n","import React, { useState, useEffect, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useSearch } from '../hooks/useSearch';\nimport { SearchResult } from '../types';\n\nexport interface SparkleProps {\n productName: string;\n limit?: number;\n onResult?: (results: SearchResult[]) => void;\n /** Override the backdrop colour (any CSS colour/gradient) */\n backdropColor?: string;\n /** Override backdrop blur — e.g. \"8px\" or 8 */\n backdropBlur?: string | number;\n /** Extra classes on the trigger button */\n className?: string;\n /** Called when user clicks a result — return false to prevent default navigation */\n onNavigate?: (result: SearchResult) => boolean | void;\n theme?: {\n primaryColor?: string;\n backgroundColor?: string;\n textColor?: string;\n fontFamily?: string;\n };\n classNames?: {\n button?: string;\n backdrop?: string;\n card?: string;\n item?: string;\n };\n}\n\nconst SparkleIcon = ({ className }: { className?: string }) => (\n <svg className={className} width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z\"/>\n </svg>\n);\n\nconst CloseIcon = () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n);\n\n/* ── Modal ────────────────────────────────────────────────────────────────── */\ninterface ModalProps extends Pick<SparkleProps, 'productName' | 'limit' | 'backdropColor' | 'backdropBlur' | 'onNavigate' | 'onResult' | 'theme' | 'classNames'> {\n onClose: () => void;\n}\n\nfunction SparkleModal({ \n productName, \n limit, \n backdropColor, \n backdropBlur, \n onClose, \n onNavigate, \n onResult,\n theme,\n classNames = {},\n}: ModalProps) {\n const { results, loading, search } = useSearch();\n const initiated = useRef(false);\n\n /* auto-search on open */\n useEffect(() => {\n if (!initiated.current) { initiated.current = true; search(productName, limit); }\n }, []);\n\n /* fire callback */\n useEffect(() => { if (results.length > 0) onResult?.(results); }, [results]);\n\n /* Escape key */\n useEffect(() => {\n const h = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };\n document.addEventListener('keydown', h);\n return () => document.removeEventListener('keydown', h);\n }, []);\n\n const blurVal = typeof backdropBlur === 'number' ? `${backdropBlur}px` : (backdropBlur ?? '16px');\n const bg = backdropColor ?? undefined;\n\n const handleNav = (r: SearchResult) => {\n const prevent = onNavigate?.(r);\n if (prevent !== false) {\n onClose();\n if (r.product.url) window.location.href = r.product.url;\n }\n };\n\n const customStyles = {\n ...(theme?.primaryColor && { '--hsk-primary': theme.primaryColor }),\n ...(theme?.backgroundColor && { '--hsk-bg': theme.backgroundColor }),\n ...(theme?.textColor && { '--hsk-text': theme.textColor }),\n ...(theme?.fontFamily && { '--hsk-font': theme.fontFamily }),\n } as React.CSSProperties;\n\n return (\n <div\n className={`hsk-sp-backdrop ${classNames.backdrop || ''}`}\n onClick={onClose}\n style={{\n backdropFilter: `blur(${blurVal})`,\n WebkitBackdropFilter: `blur(${blurVal})`,\n background: bg ?? undefined,\n ...customStyles,\n }}\n >\n <div className={`hsk-sp-card ${classNames.card || ''}`} onClick={e => e.stopPropagation()}>\n <div className=\"hsk-sp-header\">\n <span className=\"hsk-sp-header-icon\" style={{ display: 'flex', alignItems: 'center' }}>\n <SparkleIcon />\n </span>\n <div className=\"hsk-sp-header-body\">\n <div className=\"hsk-sp-header-title\">Similar to &ldquo;{productName}&rdquo;</div>\n <div className=\"hsk-sp-header-sub\">AI vector similarity · instant results</div>\n </div>\n <button className=\"hsk-sp-close\" onClick={onClose} aria-label=\"Close\">\n <CloseIcon />\n </button>\n </div>\n\n {loading && <div className=\"hsk-sp-bar\" />}\n\n <div className=\"hsk-sp-results\">\n {!loading && results.length === 0 && (\n <div className=\"hsk-sp-empty\">No similar products found.</div>\n )}\n\n {results.map((r, i) => {\n const price = parseFloat(r.product.price?.replace(/[^0-9.]/g, '') || '0');\n const currency = r.product.currency ?? 'KES';\n return (\n <div\n key={r.id}\n className={`hsk-sp-item ${classNames.item || ''}`}\n style={{ animationDelay: `${i * 55}ms` }}\n >\n <div className=\"hsk-sp-img-wrap\">\n {r.product.images?.[0]\n ? <img src={r.product.images[0]} alt={r.product.name} />\n : <span className=\"hsk-sp-img-placeholder\">🛍</span>\n }\n </div>\n\n <div className=\"hsk-sp-item-body\">\n {r.product.category && (\n <div className=\"hsk-sp-item-cat\">{r.product.category}</div>\n )}\n <div className=\"hsk-sp-item-name\">{r.product.name}</div>\n <div className=\"hsk-sp-item-price-row\">\n <span className=\"hsk-sp-item-currency\">{currency}</span>\n <span className=\"hsk-sp-item-price\">{price.toLocaleString()}</span>\n </div>\n <div className=\"hsk-sp-actions\">\n <button\n className=\"hsk-sp-action hsk-sp-action-primary\"\n onClick={() => handleNav(r)}\n >\n View Product\n </button>\n <button\n className=\"hsk-sp-action hsk-sp-action-secondary\"\n onClick={() => onClose()}\n >\n Add to Cart\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n <div className=\"hsk-sp-footer\">\n <span className=\"hsk-sp-badge\" style={{ display: 'inline-flex', alignItems: 'center', gap: '4px' }}>\n <SparkleIcon /> Huskel AI\n </span>\n <span className=\"hsk-sp-esc\">Esc to close</span>\n </div>\n </div>\n </div>\n );\n}\n\n/* ── Exported component ───────────────────────────────────────────────────── */\nexport function Sparkle({ \n productName, \n limit = 8, \n onResult, \n backdropColor, \n backdropBlur, \n className, \n onNavigate,\n theme,\n classNames = {},\n}: SparkleProps) {\n const [open, setOpen] = useState(false);\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => { setMounted(true); }, []);\n\n const customStyles = {\n ...(theme?.primaryColor && { '--hsk-primary': theme.primaryColor }),\n ...(theme?.backgroundColor && { '--hsk-bg': theme.backgroundColor }),\n ...(theme?.textColor && { '--hsk-text': theme.textColor }),\n ...(theme?.fontFamily && { '--hsk-font': theme.fontFamily }),\n } as React.CSSProperties;\n\n return (\n <>\n <button\n className={`hsk-sp-btn ${classNames.button || ''} ${className || ''}`}\n onClick={() => setOpen(true)}\n style={customStyles}\n title=\"Find similar products\"\n aria-label=\"Find similar products\"\n >\n <SparkleIcon />\n </button>\n {open && mounted && createPortal(\n <SparkleModal\n productName={productName}\n limit={limit}\n backdropColor={backdropColor}\n backdropBlur={backdropBlur}\n onClose={() => setOpen(false)}\n onResult={onResult}\n onNavigate={onNavigate}\n theme={theme}\n classNames={classNames}\n />,\n document.body\n )}\n </>\n );\n}\n","import React, { useState, useRef, useEffect } from 'react';\nimport { useChat, ChatMessage, ChatSource } from '../hooks/useChat';\n\n// Better Prop Interface for SDKs\nexport interface ChatWidgetProps {\n title?: string;\n placeholder?: string;\n emptyStateText?: string;\n emptyStateSuggestions?: string;\n defaultCurrency?: string;\n className?: string;\n \n // Allow overriding styles via standard CSS variables\n theme?: {\n primaryColor?: string;\n backgroundColor?: string;\n textColor?: string;\n fontFamily?: string;\n };\n \n // Allow targeting specific elements with custom classes (e.g. Tailwind)\n classNames?: {\n root?: string;\n header?: string;\n messageBubble?: string;\n input?: string;\n };\n \n onSelectSource?: (source: ChatSource) => void;\n}\n\n// Simple SVG Icons instead of text characters\nconst SparkleIcon = () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z\"/>\n </svg>\n);\n\nconst ArrowUpIcon = () => (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m5 12 7-7 7 7\"/>\n <path d=\"M12 19V5\"/>\n </svg>\n);\n\nfunction SourceCard({ source, defaultCurrency, onSelect }: { source: ChatSource; defaultCurrency: string; onSelect?: (s: ChatSource) => void }) {\n return (\n <div className=\"hsk-source-card\" onClick={() => onSelect?.(source)}>\n {source.image && <img src={source.image} alt={source.name} className=\"hsk-source-img\" />}\n <div style={{ flex: 1, minWidth: 0 }}>\n <div className=\"hsk-source-name\">{source.name}</div>\n {source.price && (\n <div className=\"hsk-source-price\">{source.currency ?? defaultCurrency} {source.price}</div>\n )}\n </div>\n </div>\n );\n}\n\nexport function ChatWidget({ \n title = 'AI Shopping Assistant',\n placeholder = 'Ask about anything in our store…', \n emptyStateText = 'Ask me anything about our products',\n emptyStateSuggestions = '\"Find me headphones under KSh 5,000\" · \"Gift ideas\"',\n defaultCurrency = 'KES',\n className,\n theme,\n classNames = {},\n onSelectSource \n}: ChatWidgetProps) {\n const { messages, sources, loading, error, send, reset } = useChat();\n const [input, setInput] = useState('');\n const bottomRef = useRef<HTMLDivElement>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n useEffect(() => {\n bottomRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages, loading]);\n\n const handleSend = async () => {\n const q = input.trim();\n if (!q || loading) return;\n setInput('');\n if (textareaRef.current) textareaRef.current.style.height = 'auto';\n await send(q);\n };\n\n const handleKey = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n };\n\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInput(e.target.value);\n const t = e.target;\n t.style.height = 'auto';\n t.style.height = Math.min(t.scrollHeight, 120) + 'px';\n };\n\n // Apply custom CSS variables inline to the root wrapper\n const customStyles = {\n ...(theme?.primaryColor && { '--hsk-primary': theme.primaryColor }),\n ...(theme?.backgroundColor && { '--hsk-bg': theme.backgroundColor }),\n ...(theme?.textColor && { '--hsk-text': theme.textColor }),\n ...(theme?.fontFamily && { '--hsk-font': theme.fontFamily }),\n } as React.CSSProperties;\n\n return (\n <div \n className={`hsk-chat-widget ${classNames.root || ''} ${className || ''}`} \n style={customStyles}\n >\n <div className={`hsk-chat-header ${classNames.header || ''}`}>\n <span className=\"hsk-chat-header-icon\"><SparkleIcon /></span>\n <span className=\"hsk-chat-title\">{title}</span>\n <span className=\"hsk-chat-badge\">AI</span>\n {messages.length > 0 && (\n <button className=\"hsk-chat-reset\" onClick={reset} style={{ marginLeft: 'auto' }}>Clear</button>\n )}\n </div>\n\n <div className=\"hsk-chat-messages\">\n {messages.length === 0 ? (\n <div className=\"hsk-chat-empty\">\n <div className=\"hsk-chat-empty-icon\"><SparkleIcon /></div>\n <div>{emptyStateText}</div>\n <div className=\"hsk-chat-empty-suggestions\">{emptyStateSuggestions}</div>\n </div>\n ) : (\n messages.map((msg, idx) => (\n <div key={idx}>\n <div className={`hsk-msg-row ${msg.role}`}>\n <div className={`hsk-msg-avatar ${msg.role === 'assistant' ? 'ai' : 'user'}`}>\n {msg.role === 'assistant' ? <SparkleIcon /> : 'U'}\n </div>\n <div className={`hsk-msg-bubble ${msg.role} ${classNames.messageBubble || ''}`}>\n {msg.content}\n </div>\n </div>\n {msg.role === 'assistant' && idx === messages.length - 1 && sources.length > 0 && (\n <div className=\"hsk-sources-container\">\n <div className=\"hsk-sources\">\n {sources.map((src, si) => (\n <SourceCard key={si} source={src} defaultCurrency={defaultCurrency} onSelect={onSelectSource} />\n ))}\n </div>\n </div>\n )}\n </div>\n ))\n )}\n\n {loading && (\n <div className=\"hsk-msg-row\">\n <div className=\"hsk-msg-avatar ai\"><SparkleIcon /></div>\n <div className=\"hsk-typing\">\n <div className=\"hsk-typing-dot\" /><div className=\"hsk-typing-dot\" /><div className=\"hsk-typing-dot\" />\n </div>\n </div>\n )}\n\n {error && <div className=\"hsk-chat-error\">{error}</div>}\n <div ref={bottomRef} />\n </div>\n\n <div className=\"hsk-chat-input-area\">\n <textarea\n ref={textareaRef}\n className={`hsk-chat-input ${classNames.input || ''}`}\n value={input}\n onChange={handleInput}\n onKeyDown={handleKey}\n placeholder={placeholder}\n rows={1}\n disabled={loading}\n />\n <button\n className=\"hsk-chat-send\"\n onClick={handleSend}\n disabled={!input.trim() || loading}\n aria-label=\"Send message\"\n >\n <ArrowUpIcon />\n </button>\n </div>\n </div>\n );\n}\n\n","'use client';\n\nimport React, { useState, useEffect, useRef, useCallback } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useChat, ChatMessage, ChatSource } from '../hooks/useChat';\n\nexport interface AIChatButtonProps {\n label?: string;\n title?: string;\n placeholder?: string;\n backdropColor?: string;\n backdropBlur?: string | number;\n className?: string;\n onSelectSource?: (source: ChatSource) => void;\n defaultCurrency?: string;\n chips?: string[];\n theme?: {\n primaryColor?: string;\n backgroundColor?: string;\n textColor?: string;\n fontFamily?: string;\n };\n classNames?: {\n button?: string;\n overlay?: string;\n panel?: string;\n input?: string;\n sendButton?: string;\n };\n}\n\nconst SparkleIcon = ({ className }: { className?: string }) => (\n <svg className={className} width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z\"/>\n </svg>\n);\n\nconst ArrowUpIcon = () => (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m5 12 7-7 7 7\"/>\n <path d=\"M12 19V5\"/>\n </svg>\n);\n\nconst CloseIcon = () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n);\n\nconst ChevronRightIcon = () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\"/>\n </svg>\n);\n\nconst DEFAULT_CHIPS = [\n 'Cheapest smartphone',\n 'Smart TV under KSh 20,000',\n 'Noise-cancelling headphones',\n 'Best laptop for students',\n];\n\ninterface SourcesCarouselProps {\n sources: ChatSource[];\n defaultCurrency: string;\n onSelectSource?: (src: ChatSource) => void;\n}\n\nfunction SourcesCarousel({ sources, defaultCurrency, onSelectSource }: SourcesCarouselProps) {\n const railRef = useRef<HTMLDivElement>(null);\n const [showNext, setShowNext] = useState(false);\n\n const measure = useCallback(() => {\n const el = railRef.current;\n if (!el) return;\n const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 8;\n setShowNext(el.scrollWidth > el.clientWidth + 4 && !atEnd);\n }, []);\n\n useEffect(() => {\n measure();\n const el = railRef.current;\n if (!el) return;\n const ro = new ResizeObserver(measure);\n ro.observe(el);\n el.addEventListener('scroll', measure, { passive: true });\n return () => { ro.disconnect(); el.removeEventListener('scroll', measure); };\n }, [measure, sources]);\n\n const scrollNext = () => {\n railRef.current?.scrollBy({ left: 170, behavior: 'smooth' });\n };\n\n return (\n <div className=\"hsk-cb-sources-wrap\">\n <div className=\"hsk-cb-sources\" ref={railRef}>\n {sources.map((src, si) => (\n <div\n key={si}\n className=\"hsk-cb-source\"\n style={{ animationDelay: `${si * 50}ms` }}\n onClick={() => onSelectSource?.(src)}\n >\n {src.image ? (\n <div className=\"hsk-cb-src-imgwrap\">\n <img src={src.image} alt={src.name} loading=\"lazy\" />\n </div>\n ) : (\n <div className=\"hsk-cb-src-imgwrap-empty\">\n <SparkleIcon />\n </div>\n )}\n <div className=\"hsk-cb-src-info\">\n <div className=\"hsk-cb-src-name\">{src.name}</div>\n {src.price && (\n <div className=\"hsk-cb-src-price\">\n {src.currency ?? defaultCurrency}{' '}\n {parseFloat(src.price.replace(/[^0-9.]/g, '') || '0').toLocaleString()}\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n {showNext && (\n <>\n <div\n className=\"hsk-cb-sources-fade\"\n style={{ background: 'linear-gradient(to right, transparent, var(--hsk-fade-bg, #0e0e0f))' }}\n />\n <button className=\"hsk-cb-sources-next\" onClick={scrollNext} aria-label=\"See more\">\n <ChevronRightIcon />\n </button>\n </>\n )}\n </div>\n );\n}\n\ninterface ChatModalProps extends Pick<AIChatButtonProps, 'title' | 'placeholder' | 'backdropColor' | 'backdropBlur' | 'onSelectSource' | 'defaultCurrency' | 'chips' | 'theme' | 'classNames'> {\n onClose: () => void;\n}\n\nfunction ChatModal({\n title = 'AI Shopping Assistant',\n placeholder = 'Ask me anything — gifts, budget, use case…',\n backdropColor,\n backdropBlur,\n onClose,\n onSelectSource,\n defaultCurrency = 'KES',\n chips = DEFAULT_CHIPS,\n theme,\n classNames = {},\n}: ChatModalProps) {\n const { messages, sources, loading, error, send, reset } = useChat();\n const [input, setInput] = useState('');\n const [selectedProduct, setSelectedProduct] = useState<ChatSource | null>(null);\n const bottomRef = useRef<HTMLDivElement>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages, loading, selectedProduct]);\n\n useEffect(() => {\n const h = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };\n document.addEventListener('keydown', h);\n return () => document.removeEventListener('keydown', h);\n }, []);\n\n const handleSourceClick = (src: ChatSource) => {\n setSelectedProduct(src);\n onSelectSource?.(src);\n const q = `Tell me more about the ${src.name}${src.price ? ` (${src.currency ?? defaultCurrency} ${src.price})` : ''} — what are its key specs, who is it best for, and is it worth buying?`;\n send(q);\n };\n\n const handleSend = async (text?: string) => {\n const q = (text ?? input).trim();\n if (!q || loading) return;\n setSelectedProduct(null);\n setInput('');\n if (textareaRef.current) {\n textareaRef.current.style.height = 'auto';\n }\n await send(q);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); }\n };\n\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInput(e.target.value);\n const t = e.target;\n t.style.height = 'auto';\n t.style.height = `${Math.min(t.scrollHeight, 140)}px`;\n };\n\n const blurVal = typeof backdropBlur === 'number' ? `${backdropBlur}px` : (backdropBlur ?? '20px');\n\n const customStyles = {\n ...(theme?.primaryColor && { '--hsk-primary': theme.primaryColor }),\n ...(theme?.backgroundColor && { '--hsk-bg': theme.backgroundColor }),\n ...(theme?.textColor && { '--hsk-text': theme.textColor }),\n ...(theme?.fontFamily && { '--hsk-font': theme.fontFamily }),\n } as React.CSSProperties;\n\n return (\n <div\n className={`hsk-cb-overlay ${classNames.overlay || ''}`}\n onClick={onClose}\n style={{\n backdropFilter: `blur(${blurVal})`,\n WebkitBackdropFilter: `blur(${blurVal})`,\n ...(backdropColor ? { background: backdropColor } : {}),\n ...customStyles,\n }}\n >\n <div className={`hsk-cb-panel ${classNames.panel || ''}`} onClick={e => e.stopPropagation()}>\n\n {/* Top bar */}\n <div className=\"hsk-cb-topbar\">\n <div className=\"hsk-cb-topbar-left\">\n <span className=\"hsk-cb-topbar-icon\" style={{ display: 'flex', alignItems: 'center' }}>\n <SparkleIcon />\n </span>\n <div>\n <div className=\"hsk-cb-topbar-title\">{title}</div>\n <div className=\"hsk-cb-topbar-sub\">Powered by Huskel AI · searches the whole catalogue</div>\n </div>\n </div>\n <div className=\"hsk-cb-topbar-actions\">\n {messages.length > 0 && (\n <button className=\"hsk-cb-topbar-btn\" onClick={reset}>Clear chat</button>\n )}\n <button className=\"hsk-cb-close\" onClick={onClose} aria-label=\"Close\">\n <CloseIcon />\n </button>\n </div>\n </div>\n\n {/* Messages */}\n <div className=\"hsk-cb-msgs\">\n {messages.length === 0 ? (\n <div className=\"hsk-cb-empty\">\n <div className=\"hsk-cb-empty-icon\" style={{ display: 'flex', alignItems: 'center' }}>\n <SparkleIcon />\n </div>\n <div className=\"hsk-cb-empty-title\">What can I help you find?</div>\n <div className=\"hsk-cb-empty-sub\">\n Ask about products, budgets, gift ideas, specs — I'll search the entire catalogue for you.\n </div>\n <div className=\"hsk-cb-chips\">\n {chips.map(chip => (\n <button\n key={chip}\n className=\"hsk-cb-chip\"\n onClick={() => handleSend(chip)}\n >\n {chip}\n </button>\n ))}\n </div>\n </div>\n ) : (\n messages.map((msg: ChatMessage, idx: number) => {\n const isLast = idx === messages.length - 1;\n const isUser = msg.role === 'user';\n return (\n <div key={idx} className=\"hsk-cb-msg-group\">\n {isUser ? (\n <div className=\"hsk-cb-user-msg\">\n <div className=\"hsk-cb-user-bubble\">{msg.content}</div>\n </div>\n ) : (\n <div className=\"hsk-cb-ai-msg\">\n <div className=\"hsk-cb-ai-icon\" style={{ display: 'flex', alignItems: 'center' }}>\n <SparkleIcon />\n </div>\n <div className=\"hsk-cb-ai-body\">\n <div className=\"hsk-cb-ai-text\">{msg.content}</div>\n\n {/* Sources as horizontal carousel — only after latest assistant reply */}\n {isLast && sources.length > 0 && (\n <SourcesCarousel\n sources={sources}\n defaultCurrency={defaultCurrency}\n onSelectSource={handleSourceClick}\n />\n )}\n </div>\n </div>\n )}\n </div>\n );\n })\n )}\n\n {/* Selected product pinned card — shows while LLM fetches details */}\n {selectedProduct && loading && (\n <div\n className=\"hsk-cb-selected-product\"\n onClick={() => selectedProduct.url && window.open(selectedProduct.url, '_blank')}\n >\n {selectedProduct.image && (\n <img className=\"hsk-cb-selected-img\" src={selectedProduct.image} alt={selectedProduct.name} />\n )}\n <div className=\"hsk-cb-selected-info\">\n <div className=\"hsk-cb-selected-name\">{selectedProduct.name}</div>\n {selectedProduct.price && (\n <div className=\"hsk-cb-selected-price\">\n {selectedProduct.currency ?? defaultCurrency} {parseFloat((selectedProduct.price ?? '').replace(/[^0-9.]/g, '') || '0').toLocaleString()}\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Typing dots */}\n {loading && (\n <div className=\"hsk-cb-typing-row\">\n <div className=\"hsk-cb-ai-icon\" style={{ display: 'flex', alignItems: 'center' }}>\n <SparkleIcon />\n </div>\n <div className=\"hsk-cb-typing\">\n <div className=\"hsk-cb-dot\" />\n <div className=\"hsk-cb-dot\" />\n <div className=\"hsk-cb-dot\" />\n </div>\n </div>\n )}\n\n {error && <div className=\"hsk-cb-error\">{error}</div>}\n <div ref={bottomRef} style={{ height: 1 }} />\n </div>\n\n {/* Input */}\n <div className=\"hsk-cb-input-wrap\">\n <div className=\"hsk-cb-input-box\">\n <textarea\n ref={textareaRef}\n className={`hsk-cb-textarea ${classNames.input || ''}`}\n value={input}\n onChange={handleInput}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n rows={1}\n disabled={loading}\n autoFocus\n />\n <button\n className={`hsk-cb-send ${classNames.sendButton || ''}`}\n onClick={() => handleSend()}\n disabled={!input.trim() || loading}\n aria-label=\"Send message\"\n >\n <ArrowUpIcon />\n </button>\n </div>\n <div className=\"hsk-cb-hint\">Huskel AI · searches the whole catalogue in real time</div>\n </div>\n\n </div>\n </div>\n );\n}\n\nexport function AIChatButton({\n label,\n title,\n placeholder,\n backdropColor,\n backdropBlur,\n className,\n onSelectSource,\n defaultCurrency,\n chips,\n theme,\n classNames = {},\n}: AIChatButtonProps) {\n const [open, setOpen] = useState(false);\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => { setMounted(true); }, []);\n\n const customStyles = {\n ...(theme?.primaryColor && { '--hsk-primary': theme.primaryColor }),\n ...(theme?.backgroundColor && { '--hsk-bg': theme.backgroundColor }),\n ...(theme?.textColor && { '--hsk-text': theme.textColor }),\n ...(theme?.fontFamily && { '--hsk-font': theme.fontFamily }),\n } as React.CSSProperties;\n\n return (\n <>\n <button\n className={`hsk-cb-btn ${classNames.button || ''} ${className || ''}`}\n onClick={() => setOpen(true)}\n style={customStyles}\n aria-label=\"Open AI chat\"\n >\n <span className=\"hsk-cb-btn-icon\" style={{ display: 'flex', alignItems: 'center' }}>\n <SparkleIcon />\n </span>\n {label !== undefined ? label : null}\n </button>\n {open && mounted && createPortal(\n <ChatModal\n title={title}\n placeholder={placeholder}\n backdropColor={backdropColor}\n backdropBlur={backdropBlur}\n onClose={() => setOpen(false)}\n onSelectSource={onSelectSource}\n defaultCurrency={defaultCurrency}\n chips={chips}\n theme={theme}\n classNames={classNames}\n />,\n document.body\n )}\n </>\n );\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_react","import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","SparkleIcon","import_react","import_react_dom","import_jsx_runtime","SparkleIcon","ArrowUpIcon","CloseIcon","_a"]}