@cloudflare/ai-search-snippet 0.0.32 → 0.0.33

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.
@@ -1 +1 @@
1
- {"version":3,"file":"search-snippet.umd.js","sources":["../src/utils/loading-messages.ts","../src/utils/index.ts","../src/api/ai-search.ts","../src/constants.ts","../src/styles/chat.ts","../src/styles/theme.ts","../src/utils/markdown.ts","../src/components/chat-view.ts","../src/components/chat-bubble-snippet.ts","../src/components/chat-page-snippet.ts","../src/styles/search.ts","../src/components/search-bar-snippet.ts","../src/styles/modal.ts","../src/components/search-modal-snippet.ts"],"sourcesContent":["/**\n * Loading messages that cycle during search/streaming operations\n */\n\nexport const LOADING_MESSAGES = [\n 'Searching...',\n 'Digging through results...',\n 'Scanning the knowledge base...',\n 'Finding the best matches...',\n 'Sifting through the data...',\n 'Almost there...',\n 'Looking far and wide...',\n 'Connecting the dots...',\n 'Rummaging through pages...',\n 'Hunting down answers...',\n];\n\n/** Interval in ms between loading message changes */\nexport const LOADING_MESSAGE_INTERVAL_MS = 2500;\n","/**\n * Utility functions for the Search Snippet Library\n */\n\nimport { AISearchClient } from '../api/ai-search.ts';\n\nexport { LOADING_MESSAGE_INTERVAL_MS, LOADING_MESSAGES } from './loading-messages.ts';\n\n/**\n * Debounce function to limit API calls\n */\nexport type DebouncedFn<T extends (...args: unknown[]) => unknown> = ((\n ...args: Parameters<T>\n) => void) & { cancel: () => void };\n\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n func: T,\n wait: number\n): DebouncedFn<T> {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n\n function executedFunction(...args: Parameters<T>) {\n clearTimeout(timeout);\n timeout = setTimeout(() => {\n func(...args);\n }, wait);\n }\n\n executedFunction.cancel = () => clearTimeout(timeout);\n\n return executedFunction;\n}\n\n/**\n * Sanitize HTML to prevent XSS attacks\n */\nexport function sanitizeHTML(html: string): string {\n const div = document.createElement('div');\n div.textContent = html;\n return div.innerHTML;\n}\n\n/**\n * Escape HTML entities\n */\nexport function escapeHTML(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\n/**\n * Decode percent-encoded URLs for display\n */\nexport function formatDisplayUrl(url: string): string {\n try {\n return decodeURI(url);\n } catch {\n return url;\n }\n}\n\n/**\n * Decode HTML entities (e.g., &#38; -> &, &amp; -> &)\n */\nexport function decodeHTMLEntities(text: string): string {\n const doc = new DOMParser().parseFromString(text, 'text/html');\n return doc.documentElement.textContent || '';\n}\n\n/**\n * Format timestamp to readable date\n */\nexport function formatTimestamp(timestamp: number): string {\n const date = new Date(timestamp);\n const now = new Date();\n const diff = now.getTime() - date.getTime();\n\n // Less than a minute\n if (diff < 60000) {\n return 'Just now';\n }\n\n // Less than an hour\n if (diff < 3600000) {\n const minutes = Math.floor(diff / 60000);\n return `${minutes} ${minutes === 1 ? 'minute' : 'minutes'} ago`;\n }\n\n // Less than a day\n if (diff < 86400000) {\n const hours = Math.floor(diff / 3600000);\n return `${hours} ${hours === 1 ? 'hour' : 'hours'} ago`;\n }\n\n // Format as date\n return date.toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport function formatDate(timestamp: number): string {\n return new Date(timestamp).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n });\n}\n\n/**\n * Generate unique ID\n */\nexport function generateId(prefix = 'id'): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n}\n\n/**\n * Parse attributes from element\n */\nexport function parseAttribute(value: string | null, defaultValue: string): string {\n return value !== null ? value : defaultValue;\n}\n\nexport function parseBooleanAttribute(value: string | null, defaultValue: boolean): boolean {\n if (value === null) return defaultValue;\n return value === 'true' || value === '';\n}\n\nexport function parseNumberAttribute(value: string | null, defaultValue: number): number {\n if (value === null) return defaultValue;\n const parsed = Number.parseInt(value, 10);\n return Number.isNaN(parsed) ? defaultValue : parsed;\n}\n\n/**\n * Create custom event\n */\nexport function createCustomEvent<T>(name: string, detail: T): CustomEvent<T> {\n return new CustomEvent(name, {\n detail,\n bubbles: true,\n composed: true,\n cancelable: true,\n });\n}\n\n/**\n * Create API client\n */\nexport function createClient(apiUrl: string): AISearchClient {\n if (!apiUrl) {\n throw new Error('API URL is required');\n }\n\n return new AISearchClient(apiUrl);\n}\n","/**\n * NLWeb API Client\n * Handles all API communication with retry logic, streaming support, and request cancellation\n */\n\nimport type {\n AISearchAPIResponse,\n ChatOptions,\n ChatTextResponse,\n ChatTypes,\n RequestState,\n SearchError,\n SearchOptions,\n SearchRequestOptions,\n SearchResult,\n} from '../types/index.ts';\nimport { decodeHTMLEntities } from '../utils/index.ts';\n\ntype RequestOperation = 'ai-search' | 'search' | 'chat/completions';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction deepMergeRecords(\n ...records: Array<Record<string, unknown> | undefined>\n): Record<string, unknown> {\n const merged: Record<string, unknown> = {};\n\n for (const record of records) {\n if (!record) {\n continue;\n }\n\n for (const [key, value] of Object.entries(record)) {\n const currentValue = merged[key];\n\n if (isRecord(currentValue) && isRecord(value)) {\n merged[key] = deepMergeRecords(currentValue, value);\n } else {\n merged[key] = value;\n }\n }\n }\n\n return merged;\n}\n\nfunction buildRequestUrl(\n endpoint: string,\n queryParams: SearchRequestOptions['queryParams'] | undefined\n): string {\n if (!isRecord(queryParams)) {\n return endpoint;\n }\n\n const searchParams = new URLSearchParams();\n\n for (const [key, value] of Object.entries(queryParams)) {\n if (value === undefined || value === null) {\n continue;\n }\n\n searchParams.append(key, String(value));\n }\n\n const query = searchParams.toString();\n\n if (!query) {\n return endpoint;\n }\n\n const hashIndex = endpoint.indexOf('#');\n const path = hashIndex === -1 ? endpoint : endpoint.slice(0, hashIndex);\n const hash = hashIndex === -1 ? '' : endpoint.slice(hashIndex);\n const separator = path.includes('?') ? '&' : '?';\n\n return `${path}${separator}${query}${hash}`;\n}\n\nfunction normalizeHeaders(\n headers: SearchRequestOptions['headers'] | undefined\n): Record<string, string> {\n if (!isRecord(headers)) {\n return {};\n }\n\n const normalizedHeaders: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined || value === null) {\n continue;\n }\n\n normalizedHeaders[key] = String(value);\n }\n\n return normalizedHeaders;\n}\n\nfunction normalizeBody(\n body: SearchRequestOptions['body'] | undefined\n): Record<string, unknown> | undefined {\n return isRecord(body) ? body : undefined;\n}\n\nexport class AISearchClient {\n activeRequests: Map<string, RequestState> = new Map();\n baseUrl: string;\n\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n }\n\n private request(\n body: Record<string, unknown>,\n operation: RequestOperation,\n signal?: AbortSignal,\n requestOptions?: SearchRequestOptions\n ): Promise<Response> {\n const sourceHeader = operation === 'search' ? 'snippet-search' : 'snippet-chat-completions';\n const url = buildRequestUrl(`${this.baseUrl}/${operation}`, requestOptions?.queryParams);\n\n return fetch(url, {\n method: 'POST',\n body: JSON.stringify(deepMergeRecords(normalizeBody(requestOptions?.body), body)),\n headers: {\n ...normalizeHeaders(requestOptions?.headers),\n 'Content-Type': 'application/json',\n 'cf-ai-search-source': sourceHeader,\n },\n signal,\n });\n }\n\n /**\n * Performs a search query with optional streaming\n */\n async search(query: string, options: Omit<SearchOptions, 'query'> = {}): Promise<SearchResult[]> {\n const requestId = this.generateRequestId();\n const controller = new AbortController();\n const signal = options.signal || controller.signal;\n\n this.registerRequest(requestId, controller);\n\n try {\n const response = await this.request(\n {\n messages: [{ role: 'user', content: query }],\n stream: false,\n ai_search_options: {\n retrieval: {\n metadata_only: true,\n max_results: options.maxResults ?? 30,\n },\n },\n },\n 'search',\n signal,\n options.request\n );\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error('Response body is empty');\n }\n const result: AISearchAPIResponse = await response.json();\n if (result.success && result.result) {\n return result.result.chunks.map(\n (chunk) =>\n ({\n type: 'result',\n id: chunk.id,\n title: decodeHTMLEntities(chunk.item.metadata?.title),\n description: chunk.item.metadata?.description\n ? decodeHTMLEntities(chunk.item.metadata?.description)\n : '',\n timestamp: chunk.item.timestamp ?? undefined,\n url: chunk.item.key,\n image: chunk.item.metadata?.image || undefined,\n metadata: chunk.item.metadata as unknown as Record<string, unknown>,\n }) satisfies SearchResult\n );\n }\n\n if (result.success === false) {\n // @ts-expect-error need to check this\n throw new Error(result.error);\n }\n throw new Error('Unknown error');\n } finally {\n this.unregisterRequest(requestId);\n }\n }\n\n async *searchStream(\n query: string,\n options: Omit<SearchOptions, 'query'> = {}\n ): AsyncGenerator<SearchResult | SearchError, void, undefined> {\n const requestId = this.generateRequestId();\n const controller = new AbortController();\n const signal = options.signal || controller.signal;\n\n this.registerRequest(requestId, controller);\n\n const response = await this.request(\n {\n messages: [{ role: 'user', content: query }],\n stream: true,\n ...(options.maxResults !== undefined && {\n max_results: options.maxResults,\n }),\n },\n 'ai-search',\n signal,\n options.request\n );\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n if (!response.body) {\n throw new Error('Response body is empty');\n }\n\n let chunks = '';\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n const chunk = decoder.decode(value, { stream: true });\n chunks += chunk;\n }\n\n const result: string = chunks\n .replaceAll('data: ', '')\n .trim()\n .split('\\n\\n')\n .map((chunk) => {\n return JSON.parse(chunk) as { response: string };\n })\n .map((chunk) => chunk.response)\n .join('');\n\n yield {\n type: 'result',\n id: '',\n title: '',\n description: result,\n url: '',\n metadata: {},\n };\n }\n\n async *chat(query: string, options?: ChatOptions): AsyncGenerator<ChatTypes, void, undefined> {\n const controller = new AbortController();\n const signal = options?.signal || controller.signal;\n // const prevQueries: string[] = JSON.parse(localStorage.getItem('prevQueries') || '[]');\n // prevQueries.push(query);\n // localStorage.setItem('prevQueries', JSON.stringify(prevQueries));\n const response = await this.request(\n {\n messages: [{ role: 'user', content: query }],\n stream: false,\n },\n 'chat/completions',\n signal\n );\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n if (!response.body) {\n throw new Error('Response body is empty');\n }\n const result = (await response.json()) as {\n choices: {\n message: {\n content: string;\n };\n }[];\n };\n\n yield {\n type: 'text',\n message: result.choices.map((choice) => choice.message.content).join(''),\n } satisfies ChatTextResponse;\n\n // for (const item of result.data) {\n // yield {\n // type: 'result',\n // id: item.filename,\n // title: item.filename,\n // description: item.content.text,\n // url: item.filename,\n // metadata: item.attributes\n // ,\n // } satisfies ChatResult;\n // }\n\n return;\n }\n\n /**\n * Cancels an active request by ID\n */\n cancelRequest(requestId: string): void {\n const request = this.activeRequests.get(requestId);\n if (request) {\n request.controller.abort();\n this.unregisterRequest(requestId);\n }\n }\n\n /**\n * Cancels all active requests\n */\n cancelAllRequests(): void {\n for (const [requestId] of this.activeRequests) {\n this.cancelRequest(requestId);\n }\n }\n\n /**\n * Register an active request\n */\n private registerRequest(id: string, controller: AbortController): void {\n this.activeRequests.set(id, {\n id,\n controller,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Unregister a completed request\n */\n private unregisterRequest(id: string): void {\n this.activeRequests.delete(id);\n }\n\n /**\n * Generate unique request ID\n */\n private generateRequestId(): string {\n return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n}\n","/**\n * Cloudflare AI Search branding constants\n */\n\nexport const CLOUDFLARE_LOGO_SVG = `<svg width=\"32\" height=\"10\" viewBox=\"0 0 412 186\" xmlns=\"http://www.w3.org/2000/svg\" aria-label=\"Cloudflare\" role=\"img\">\n <path fill=\"#f38020\" d=\"m280.8395,183.31456c11,-26 -4,-38 -19,-38l-148,-2c-4,0 -4,-6 1,-7l150,-2c17,-1 37,-15 43,-33c0,0 10,-21 9,-24a97,97 0 0 0 -187,-11c-38,-25 -78,9 -69,46c-48,3 -65,46 -60,72c0,1 1,2 3,2l274,0c1,0 3,-1 3,-3z\"/>\n <path fill=\"#faae40\" d=\"m330.8395,81.31456c-4,0 -6,-1 -7,1l-5,21c-5,16 3,30 20,31l32,2c4,0 4,6 -1,7l-33,1c-36,4 -46,39 -46,39c0,2 0,3 2,3l113,0l3,-2a81,81 0 0 0 -78,-103\"/>\n</svg>`;\n\nexport const CLOUDFLARE_SEARCH_URL = 'https://workers.cloudflare.com/product/ai-search';\n\nexport const POWERED_BY_BRANDING = `Powered by <a href=\"${CLOUDFLARE_SEARCH_URL}\" target=\"_blank\" rel=\"noopener noreferrer\">Cloudflare AI Search ${CLOUDFLARE_LOGO_SVG}</a>`;\n","/**\n * Chat mode specific styles\n */\n\nexport const chatStyles = `\n/* Chat container */\n.chat-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow-y: auto;\n}\n\n/* Messages area */\n.chat-messages {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: var(--search-snippet-spacing-md);\n display: flex;\n flex-direction: column;\n gap: var(--search-snippet-spacing-md);\n}\n\n.chat-messages::-webkit-scrollbar {\n width: 8px;\n}\n\n.chat-messages::-webkit-scrollbar-track {\n background: var(--search-snippet-surface);\n}\n\n.chat-messages::-webkit-scrollbar-thumb {\n background: var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n}\n\n.chat-messages::-webkit-scrollbar-thumb:hover {\n background: var(--search-snippet-text-secondary);\n}\n\n/* Message */\n.chat-message {\n display: flex;\n gap: var(--search-snippet-spacing-sm);\n max-width: 85%;\n animation: slideIn var(--search-snippet-animation-duration) ease-out;\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.chat-message-user {\n align-self: flex-end;\n flex-direction: row-reverse;\n}\n\n.chat-message-assistant {\n align-self: flex-start;\n}\n\n.chat-message-system {\n align-self: center;\n max-width: 100%;\n}\n\n/* Message avatar */\n.chat-message-avatar {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n font-size: var(--search-snippet-font-size-sm);\n font-weight: var(--search-snippet-font-weight-bold);\n background: var(--search-snippet-primary-color);\n color: white;\n}\n\n.chat-message-assistant .chat-message-avatar {\n background: var(--search-snippet-surface);\n color: var(--search-snippet-text-color);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n/* Message content */\n.chat-message-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: var(--search-snippet-spacing-xs);\n max-width: 100%;\n}\n\n.chat-message-content ol, .chat-message-content ul {\n margin-inline-start: 16px;\n}\n\n.chat-message-content ol li, .chat-message-content ul li {\n padding-inline-start: 0;\n}\n\n.chat-message-bubble {\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n border-radius: var(--search-snippet-border-radius);\n word-wrap: break-word;\n overflow-wrap: break-word;\n}\n\n.chat-message-user .chat-message-bubble {\n background: var(--search-snippet-user-message-bg);\n color: var(--search-snippet-user-message-text);\n border-top-right-radius: var(--search-snippet-spacing-xs);\n}\n\n.chat-message-assistant .chat-message-bubble {\n background: var(--search-snippet-assistant-message-bg);\n color: var(--search-snippet-assistant-message-text);\n border-top-left-radius: var(--search-snippet-spacing-xs);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n.chat-message-system .chat-message-bubble {\n background: var(--search-snippet-system-message-bg);\n color: var(--search-snippet-system-message-text);\n text-align: center;\n font-size: var(--search-snippet-font-size-sm);\n padding: var(--search-snippet-spacing-xs) var(--search-snippet-spacing-md);\n}\n\n.chat-message-text {\n font-size: var(--search-snippet-font-size-base);\n line-height: 1.5;\n white-space: wrap;\n}\n.chat-message-text li{\n padding-inline-start: var(--search-snippet-spacing-md);\n}\n\n.chat-message-metadata {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n}\n\n.chat-message-user .chat-message-metadata {\n justify-content: flex-end;\n}\n\n.chat-message-time {\n opacity: 0.7;\n}\n\n/* Streaming indicator */\n.chat-streaming {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-xs);\n}\n\n.chat-streaming-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: currentColor;\n animation: pulse 1.4s ease-in-out infinite;\n}\n\n.chat-streaming-dot:nth-child(2) {\n animation-delay: 0.2s;\n}\n\n.chat-streaming-dot:nth-child(3) {\n animation-delay: 0.4s;\n}\n\n@keyframes pulse {\n 0%, 80%, 100% {\n opacity: 0.3;\n transform: scale(0.8);\n }\n 40% {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n.chat-streaming .loading-text {\n margin-left: var(--search-snippet-spacing-xs);\n}\n\n/* Input area */\n.chat-input-area {\n padding: var(--search-snippet-spacing-md);\n border-top: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n background: var(--search-snippet-surface);\n}\n\n.chat-input-wrapper {\n display: flex;\n gap: var(--search-snippet-spacing-sm);\n align-items: flex-end;\n}\n\n.chat-input {\n flex: 1;\n min-height: var(--search-snippet-input-height);\n max-height: 120px;\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n line-height: var(--search-snippet-line-height);\n color: var(--search-snippet-text-color);\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n outline: none;\n resize: vertical;\n transition: var(--search-snippet-transition);\n}\n\n.chat-input:focus {\n border-color: var(--search-snippet-primary-color);\n box-shadow: 0 0 0 3px var(--search-snippet-focus-ring);\n}\n\n.chat-input::placeholder {\n color: var(--search-snippet-text-secondary);\n}\n\n.chat-input:disabled {\n background: var(--search-snippet-surface);\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.chat-send-button {\n flex-shrink: 0;\n height: var(--search-snippet-input-height);\n padding: 0 var(--search-snippet-spacing-lg);\n}\n\n.chat-send-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* Empty chat state */\n.chat-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--search-snippet-spacing-xxl);\n gap: var(--search-snippet-spacing-md);\n color: var(--search-snippet-text-secondary);\n text-align: center;\n height: 100%;\n}\n\n.chat-empty-icon {\n width: 64px;\n height: 64px;\n opacity: 0.5;\n}\n\n.chat-empty-title {\n font-size: var(--search-snippet-font-size-lg);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n}\n\n.chat-empty-description {\n font-size: var(--search-snippet-font-size-sm);\n}\n\n/* Code blocks in messages */\n.chat-message-bubble pre {\n background: var(--search-snippet-surface);\n padding: var(--search-snippet-spacing-sm);\n border-radius: var(--search-snippet-border-radius);\n overflow-x: auto;\n font-family: var(--search-snippet-font-family-mono);\n font-size: var(--search-snippet-font-size-sm);\n margin: var(--search-snippet-spacing-xs) 0;\n}\n\n.chat-message-bubble code {\n font-family: var(--search-snippet-font-family-mono);\n font-size: 0.9em;\n background: var(--search-snippet-surface);\n padding: 2px 4px;\n border-radius: var(--search-snippet-border-radius);\n}\n\n.chat-message-bubble pre code {\n background: none;\n padding: 0;\n}\n\n/* Links in messages */\n.chat-message-bubble a {\n color: var(--search-snippet-primary-color);\n text-decoration: underline;\n}\n\n.chat-message-bubble a:hover {\n text-decoration: none;\n}\n`;\n","/**\n * CSS Theme with comprehensive CSS custom properties\n */\n\nexport const baseStyles = `\n:host {\n /* Colors - Light Mode */\n --search-snippet-primary-color: #2563eb;\n --search-snippet-primary-hover: #0f51dfff;\n --search-snippet-background: #ffffff;\n --search-snippet-surface: #f8f9fa;\n --search-snippet-text-color: #212529;\n --search-snippet-text-secondary: #6c757d;\n --search-snippet-text-description: #495057;\n --search-snippet-border-color: #dee2e6;\n --search-snippet-hover-background: #f1f3f5;\n --search-snippet-focus-ring: #0066cc40;\n --search-snippet-error-color: #dc3545;\n --search-snippet-error-background: #f8d7da;\n --search-snippet-success-color: #28a745;\n --search-snippet-success-background: #d4edda;\n --search-snippet-warning-color: #ffc107;\n --search-snippet-warning-background: #fff3cd;\n \n /* Message Colors */\n --search-snippet-user-message-bg: #0066cc;\n --search-snippet-user-message-text: #ffffff;\n --search-snippet-assistant-message-bg: #f1f3f5;\n --search-snippet-assistant-message-text: #212529;\n --search-snippet-system-message-bg: #fff3cd;\n --search-snippet-system-message-text: #856404;\n \n /* Typography */\n --search-snippet-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', \n 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', \n 'Segoe UI Emoji', 'Segoe UI Symbol';\n --search-snippet-font-family-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;\n --search-snippet-font-size-base: 14px;\n --search-snippet-font-size-sm: 12px;\n --search-snippet-font-size-lg: 16px;\n --search-snippet-font-size-xl: 18px;\n --search-snippet-line-height: 1.5;\n --search-snippet-font-weight-normal: 400;\n --search-snippet-font-weight-medium: 500;\n --search-snippet-font-weight-bold: 600;\n \n /* Spacing */\n --search-snippet-spacing-xs: 4px;\n --search-snippet-spacing-sm: 8px;\n --search-snippet-spacing-md: 12px;\n --search-snippet-spacing-lg: 16px;\n --search-snippet-spacing-xl: 24px;\n --search-snippet-spacing-xxl: 32px;\n \n /* Sizing */\n --search-snippet-width: 100%;\n --search-snippet-max-width: 100%;\n --search-snippet-min-width: 320px;\n --search-snippet-max-height: 600px;\n --search-snippet-input-height: 44px;\n --search-snippet-button-height: 36px;\n --search-snippet-icon-size: 20px;\n \n /* Border */\n --search-snippet-border-width: 1px;\n --search-snippet-border-radius: 18px;\n \n /* Shadows */\n --search-snippet-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n --search-snippet-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n --search-snippet-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15);\n --search-snippet-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.2);\n --search-snippet-shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);\n \n /* Animation */\n --search-snippet-transition-fast: 150ms ease;\n --search-snippet-transition: 200ms ease;\n --search-snippet-transition-slow: 300ms ease;\n --search-snippet-animation-duration: 0.2s;\n \n /* Z-index */\n --search-snippet-z-dropdown: 1000;\n --search-snippet-z-modal: 1050;\n --search-snippet-z-popover: 1060;\n --search-snippet-z-tooltip: 1070;\n \n /* Layout */\n display: block;\n width: var(--search-snippet-width);\n max-width: var(--search-snippet-max-width);\n min-width: var(--search-snippet-min-width);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n line-height: var(--search-snippet-line-height);\n color: var(--search-snippet-text-color);\n\n\n /* Search */\n --search-snippet-icon-size: 20px;\n --search-snippet-icon-margin-left: 6px;\n --search-snippet-result-item-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n\n /* Chat Bubble */\n --chat-bubble-button-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);\n --chat-bubble-window-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);\n --chat-bubble-button-size: 60px;\n --chat-bubble-button-radius: 50%;\n --chat-bubble-button-icon-size: 28px;\n --chat-bubble-button-icon-color: white;\n --chat-bubble-button-bottom: 20px;\n --chat-bubble-button-right: 20px;\n --chat-bubble-button-z-index: 9999;\n --chat-bubble-position: fixed;\n\n\n}\n\n:host(:not([theme=\"dark\"])) {\n /* Colors - Light Mode */\n --search-snippet-primary-color: #2563eb;\n --search-snippet-primary-hover: #0f51dfff;\n --search-snippet-background: #ffffff;\n --search-snippet-surface: #f8f9fa;\n --search-snippet-text-color: #212529;\n --search-snippet-text-secondary: #6c757d;\n --search-snippet-text-description: #495057;\n --search-snippet-border-color: #dee2e6;\n --search-snippet-hover-background: #f1f3f5;\n --search-snippet-focus-ring: #0066cc40;\n --search-snippet-error-color: #dc3545;\n --search-snippet-error-background: #f8d7da;\n --search-snippet-success-color: #28a745;\n --search-snippet-success-background: #d4edda;\n --search-snippet-warning-color: #ffc107;\n --search-snippet-warning-background: #fff3cd;\n \n /* Message Colors */\n --search-snippet-user-message-bg: #0066cc;\n --search-snippet-user-message-text: #ffffff;\n --search-snippet-assistant-message-bg: #f1f3f5;\n --search-snippet-assistant-message-text: #212529;\n --search-snippet-system-message-bg: #fff3cd;\n --search-snippet-system-message-text: #856404;\n}\n\n/* Dark Mode */\n@media (prefers-color-scheme: dark) {\n :host(:not([theme=\"light\"])) {\n --search-snippet-primary-color: #2563eb;\n --search-snippet-primary-hover: #0f51dfff;\n --search-snippet-background: #1a1b1e;\n --search-snippet-surface: #25262b;\n --search-snippet-text-color: #c1c2c5;\n --search-snippet-text-secondary: #909296;\n --search-snippet-text-description: #adb5bd;\n --search-snippet-border-color: #373a40;\n --search-snippet-hover-background: #2c2e33;\n --search-snippet-focus-ring: #4dabf740;\n --search-snippet-error-color: #ff6b6b;\n --search-snippet-error-background: #3d1f1f;\n --search-snippet-success-color: #51cf66;\n --search-snippet-success-background: #1f3d24;\n --search-snippet-warning-color: #ffd43b;\n --search-snippet-warning-background: #3d3419;\n \n --search-snippet-user-message-bg: #4dabf7;\n --search-snippet-user-message-text: #1a1b1e;\n --search-snippet-assistant-message-bg: #2c2e33;\n --search-snippet-assistant-message-text: #c1c2c5;\n --search-snippet-system-message-bg: #3d3419;\n --search-snippet-system-message-text: #ffd43b;\n color-scheme: dark;\n }\n}\n\n/* Auto theme support */\n:host([theme=\"light\"]) {\n color-scheme: light;\n}\n\n\n/* Base reset */\n* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n/* Container */\n.container {\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n box-shadow: var(--search-snippet-shadow);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n/* Header */\n.header {\n padding: var(--search-snippet-spacing-md);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n background: var(--search-snippet-surface);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--search-snippet-spacing-md);\n}\n\n.header-title {\n font-size: var(--search-snippet-font-size-lg);\n font-weight: var(--search-snippet-font-weight-bold);\n color: var(--search-snippet-text-color);\n}\n\n/* Input */\n.input-wrapper {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n}\n\n.input {\n width: 100%;\n height: var(--search-snippet-input-height);\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n line-height: var(--search-snippet-line-height);\n color: var(--search-snippet-text-color);\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n outline: none;\n transition: var(--search-snippet-transition);\n}\n\n.input:focus {\n border-color: var(--search-snippet-primary-color);\n box-shadow: 0 0 0 3px var(--search-snippet-focus-ring);\n}\n\n.input::placeholder {\n color: var(--search-snippet-text-secondary);\n}\n\n.input:disabled {\n background: var(--search-snippet-surface);\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n/* Button */\n.button {\n height: var(--search-snippet-button-height);\n padding: 0 var(--search-snippet-spacing-lg);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: #ffffff;\n background: var(--search-snippet-primary-color);\n border: none;\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n outline: none;\n transition: var(--search-snippet-transition);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-sm);\n white-space: nowrap;\n}\n\n.button:hover:not(:disabled) {\n background: var(--search-snippet-primary-hover);\n}\n\n.button:focus-visible {\n box-shadow: 0 0 0 3px var(--search-snippet-focus-ring);\n}\n\n.button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.button-secondary {\n background: var(--search-snippet-surface);\n color: var(--search-snippet-text-color);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n.button-secondary:hover:not(:disabled) {\n background: var(--search-snippet-hover-background);\n}\n\n/* Content area */\n.content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: var(--search-snippet-spacing-md);\n}\n\n/* Scrollbar styling */\n.content::-webkit-scrollbar {\n width: 8px;\n}\n\n.content::-webkit-scrollbar-track {\n background: var(--search-snippet-surface);\n}\n\n.content::-webkit-scrollbar-thumb {\n background: var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n}\n\n.content::-webkit-scrollbar-thumb:hover {\n background: var(--search-snippet-text-secondary);\n}\n\n/* Loading spinner */\n.loading {\n display: inline-block;\n width: var(--search-snippet-icon-size);\n height: var(--search-snippet-icon-size);\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: spin 0.6s linear infinite;\n}\n\n@keyframes spin {\n to { transform: rotate(360deg); }\n}\n\n/* Loading message animation */\n@keyframes loading-message-in {\n from {\n opacity: 0;\n transform: translateY(8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.loading-text {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n}\n\n.loading-text-animate {\n animation: loading-message-in 0.3s ease-out;\n}\n\n/* Error message */\n.error {\n padding: var(--search-snippet-spacing-md);\n color: var(--search-snippet-error-color);\n background: var(--search-snippet-error-background);\n border-radius: var(--search-snippet-border-radius);\n font-size: var(--search-snippet-font-size-sm);\n}\n\n/* Empty state */\n.empty {\n padding: var(--search-snippet-spacing-xl);\n text-align: center;\n color: var(--search-snippet-text-secondary);\n}\n\n/* Accessibility */\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n/* Focus visible polyfill */\n.focus-visible:focus {\n outline: 2px solid var(--search-snippet-primary-color);\n outline-offset: 2px;\n}\n\n/* Powered by branding - block style (for sidebars) */\n.powered-by {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-xs);\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n background: var(--search-snippet-surface);\n border-top: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n margin-top: auto;\n flex-shrink: 0;\n}\n\n.powered-by svg {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n}\n\n.powered-by a,\n.powered-by-inline a {\n color: var(--search-snippet-text-secondary);\n text-decoration: none;\n transition: color var(--search-snippet-transition-fast);\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.powered-by a:hover,\n.powered-by-inline a:hover {\n color: var(--search-snippet-primary-color);\n}\n\n/* Powered by branding - inline style (for headers/subtle placement) */\n.powered-by-inline {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n padding: var(--search-snippet-spacing-xs) 0;\n text-align: center;\n}\n`;\n","/**\n * Converts markdown text to HTML\n * Supports: headers, bold, italic, links, lists, code blocks, inline code, blockquotes, and horizontal rules\n */\nexport function markdownToHtml(markdown: string): string {\n let html = markdown;\n\n // Escape HTML characters first to prevent XSS\n html = escapeHtml(html);\n\n // Process code blocks first (to protect from other transformations)\n html = html.replace(/```([\\s\\S]*?)```/g, (_, code) => `<pre><code>${code.trim()}</code></pre>`);\n\n // Split into lines for block-level processing\n const lines = html.split('\\n');\n const processedLines: string[] = [];\n let inList = false;\n let listType = '';\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // Headers (h1-h6)\n const headerMatch = line.match(/^(#{1,6})\\s+(.+)$/);\n if (headerMatch) {\n const level = headerMatch[1].length;\n const content = headerMatch[2];\n processedLines.push(`<h${level}>${processInlineMarkdown(content)}</h${level}>`);\n continue;\n }\n\n // Horizontal rule\n if (line.match(/^---+$/)) {\n processedLines.push('<hr />');\n continue;\n }\n\n // Blockquote\n if (line.match(/^>\\s+/)) {\n const content = line.replace(/^>\\s+/, '');\n processedLines.push(`<blockquote>${processInlineMarkdown(content)}</blockquote>`);\n continue;\n }\n\n // Unordered list\n const ulMatch = line.match(/^[-*]\\s+(.+)$/);\n if (ulMatch) {\n if (!inList || listType !== 'ul') {\n if (inList) processedLines.push(`</${listType}>`);\n processedLines.push('<ul>');\n inList = true;\n listType = 'ul';\n }\n processedLines.push(`<li>${processInlineMarkdown(ulMatch[1])}</li>`);\n continue;\n }\n\n // Ordered list\n const olMatch = line.match(/^\\d+\\.\\s+(.+)$/);\n if (olMatch) {\n if (!inList || listType !== 'ol') {\n if (inList) processedLines.push(`</${listType}>`);\n processedLines.push('<ol>');\n inList = true;\n listType = 'ol';\n }\n processedLines.push(`<li>${processInlineMarkdown(olMatch[1])}</li>`);\n continue;\n }\n\n // Close list if we're no longer in one\n if (inList) {\n processedLines.push(`</${listType}>`);\n inList = false;\n listType = '';\n }\n\n // Empty line\n if (line.trim() === '') {\n processedLines.push('<br />');\n continue;\n }\n\n // Regular paragraph\n processedLines.push(`<p>${processInlineMarkdown(line)}</p>`);\n }\n\n // Close any open list\n if (inList) {\n processedLines.push(`</${listType}>`);\n }\n\n return processedLines.join('\\n');\n}\n\n/**\n * Process inline markdown elements (bold, italic, links, inline code)\n */\nfunction processInlineMarkdown(text: string): string {\n let result = text;\n\n // Inline code (before other inline elements)\n result = result.replace(/`([^`]+)`/g, '<code>$1</code>');\n\n // Bold and italic (***text***)\n result = result.replace(/\\*\\*\\*(.+?)\\*\\*\\*/g, '<strong><em>$1</em></strong>');\n result = result.replace(/___(.+?)___/g, '<strong><em>$1</em></strong>');\n\n // Bold (**text** or __text__)\n result = result.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');\n result = result.replace(/__(.+?)__/g, '<strong>$1</strong>');\n\n // Italic (*text* or _text_)\n result = result.replace(/\\*(.+?)\\*/g, '<em>$1</em>');\n result = result.replace(/_(.+?)_/g, '<em>$1</em>');\n\n // Links [text](url)\n result = result.replace(\n /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>'\n );\n\n return result;\n}\n\n/**\n * Escape HTML characters to prevent XSS\n */\nfunction escapeHtml(text: string): string {\n const htmlEntities: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n };\n\n return text.replace(/[&<>\"']/g, (char) => htmlEntities[char] || char);\n}\n","/**\n * ChatView Component\n * Handles chat interface with streaming support\n */\n\nimport type { AISearchClient } from '../api/ai-search.ts';\nimport type { SearchSnippetProps } from '../types/index.ts';\nimport {\n createCustomEvent,\n escapeHTML,\n formatTimestamp,\n generateId,\n LOADING_MESSAGE_INTERVAL_MS,\n LOADING_MESSAGES,\n} from '../utils/index.ts';\nimport { markdownToHtml } from '../utils/markdown.ts';\nexport interface Message {\n id: string;\n role: 'user' | 'assistant' | 'system';\n content: string;\n timestamp: number;\n metadata?: Record<string, unknown>;\n}\nexport class ChatView {\n private container: HTMLElement;\n private client: AISearchClient;\n private props: SearchSnippetProps;\n private inputElement: HTMLTextAreaElement | null = null;\n private messagesContainer: HTMLElement | null = null;\n private sendButton: HTMLButtonElement | null = null;\n private messages: Message[] = [];\n private isStreaming = false;\n private currentStreamingMessageId: string | null = null;\n private loadingMessageInterval: ReturnType<typeof setInterval> | null = null;\n private loadingMessageIndex = 0;\n\n // Event handler references for cleanup\n private handleInputResize: ((e: Event) => void) | null = null;\n private handleInputKeydown: ((e: KeyboardEvent) => void) | null = null;\n private handleSendClick: (() => void) | null = null;\n\n constructor(container: HTMLElement, client: AISearchClient, props: SearchSnippetProps) {\n this.container = container;\n this.client = client;\n this.props = props;\n\n this.render();\n this.attachEventListeners();\n }\n\n /**\n * Render the chat interface\n */\n private render(): void {\n this.container.innerHTML = `\n <div class=\"chat-container\">\n <div class=\"chat-messages\">\n <div class=\"chat-empty\">\n <svg class=\"chat-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n <div class=\"chat-empty-title\">Start a Conversation</div>\n <div class=\"chat-empty-description\">\n Send a message to begin chatting\n </div>\n </div>\n </div>\n <div class=\"chat-input-area\">\n <div class=\"chat-input-wrapper\">\n <textarea\n class=\"chat-input\"\n placeholder=\"${escapeHTML(this.props.placeholder || 'Type a message...')}\"\n aria-label=\"Chat message input\"\n style=\"height: 40px;\"\n rows=\"1\"\n ></textarea>\n <button class=\"button chat-send-button\" aria-label=\"Send message\">\n <span>Send</span>\n </button>\n </div>\n </div>\n </div>\n `;\n\n this.messagesContainer = this.container.querySelector('.chat-messages');\n this.inputElement = this.container.querySelector('.chat-input');\n this.sendButton = this.container.querySelector('.chat-send-button');\n }\n\n /**\n * Attach event listeners\n */\n private attachEventListeners(): void {\n if (!this.inputElement || !this.sendButton) return;\n\n // Auto-resize textarea\n this.handleInputResize = (e: Event) => {\n const target = e.target as HTMLTextAreaElement;\n target.style.height = 'auto';\n target.style.height = `${target.scrollHeight}px`;\n };\n this.inputElement.addEventListener('input', this.handleInputResize);\n\n // Enter to send (Shift+Enter for new line)\n this.handleInputKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n this.handleSendMessage();\n }\n };\n this.inputElement.addEventListener('keydown', this.handleInputKeydown);\n\n // Send button click\n this.handleSendClick = () => {\n this.handleSendMessage();\n };\n this.sendButton.addEventListener('click', this.handleSendClick);\n }\n\n /**\n * Handle send message\n */\n private async handleSendMessage(): Promise<void> {\n if (!this.inputElement || this.isStreaming) return;\n\n const content = this.inputElement.value.trim();\n if (content.length === 0) return;\n\n // Clear input\n this.inputElement.value = '';\n this.inputElement.style.height = 'auto';\n\n // Send message\n await this.sendMessage(content);\n }\n\n /**\n * Send a message\n */\n public async sendMessage(content: string): Promise<void> {\n // Add user message\n const userMessage: Message = {\n id: generateId('msg'),\n role: 'user',\n content,\n timestamp: Date.now(),\n };\n\n this.addMessage(userMessage);\n this.renderMessages(true);\n this.setStreamingState(true);\n\n // Create placeholder for assistant response\n const assistantMessageId = generateId('msg');\n const assistantMessage: Message = {\n id: assistantMessageId,\n role: 'assistant',\n content: '',\n timestamp: Date.now(),\n };\n\n this.addMessage(assistantMessage);\n this.currentStreamingMessageId = assistantMessageId;\n this.renderMessages(true);\n\n try {\n // Stream the response\n const stream = this.client.chat(content);\n\n let fullContent = '';\n\n for await (const chunk of stream) {\n if (chunk.type === 'text' && chunk.message) {\n fullContent += chunk.message;\n this.updateStreamingMessage(assistantMessageId, fullContent);\n } else if (chunk.type === 'error') {\n this.showErrorInMessage(assistantMessageId, chunk.message || 'Unknown error');\n break;\n }\n // else if (chunk.type === 'done') {\n // break;\n // }\n }\n\n // Update final message\n const messageIndex = this.messages.findIndex((m) => m.id === assistantMessageId);\n if (messageIndex !== -1) {\n this.messages[messageIndex].content = fullContent;\n }\n\n // Emit message event\n this.container.dispatchEvent(createCustomEvent('message', { message: assistantMessage }));\n } catch (error) {\n this.showErrorInMessage(assistantMessageId, (error as Error).message);\n\n // Emit error event\n this.container.dispatchEvent(\n createCustomEvent('error', {\n error: {\n message: (error as Error).message,\n code: 'CHAT_ERROR',\n },\n })\n );\n } finally {\n this.setStreamingState(false);\n this.renderMessages();\n this.currentStreamingMessageId = null;\n }\n }\n\n /**\n * Add a message to the chat\n */\n private addMessage(message: Message): void {\n this.messages.push(message);\n this.renderMessages();\n }\n\n /**\n * Update streaming message content\n */\n private updateStreamingMessage(messageId: string, content: string): void {\n const messageIndex = this.messages.findIndex((m) => m.id === messageId);\n if (messageIndex !== -1) {\n this.messages[messageIndex].content = content;\n this.renderMessages(true);\n }\n }\n\n /**\n * Show error in message\n */\n private showErrorInMessage(messageId: string, error: string): void {\n const messageIndex = this.messages.findIndex((m) => m.id === messageId);\n if (messageIndex !== -1) {\n this.messages[messageIndex].content = `Error: ${error}`;\n this.renderMessages();\n }\n }\n\n /**\n * Render all messages\n */\n private renderMessages(isStreaming = false): void {\n if (!this.messagesContainer) return;\n\n if (this.messages.length === 0) {\n this.messagesContainer.innerHTML = `\n <div class=\"chat-empty\">\n <svg class=\"chat-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n <div class=\"chat-empty-title\">Start a Conversation</div>\n <div class=\"chat-empty-description\">\n Send a message to begin chatting\n </div>\n </div>\n `;\n return;\n }\n\n const messagesHTML = this.messages\n .map((message) =>\n this.renderMessage(message, isStreaming && message.id === this.currentStreamingMessageId)\n )\n .join('');\n\n this.messagesContainer.innerHTML = messagesHTML;\n\n // Scroll to bottom\n this.scrollToBottom();\n }\n\n /**\n * Render a single message\n */\n private renderMessage(message: Message, isStreaming = false): string {\n const roleClass = `chat-message-${message.role}`;\n const avatar = message.role === 'user' ? 'U' : 'AI';\n\n return `\n <div class=\"chat-message ${roleClass}\">\n <div class=\"chat-message-avatar\">${avatar}</div>\n <div class=\"chat-message-content\">\n <div class=\"chat-message-bubble\">\n ${message.content ? `<div class=\"chat-message-text\">${markdownToHtml(message.content)}</div>` : ''}\n ${isStreaming ? `<div class=\"chat-streaming\"><span class=\"chat-streaming-dot\"></span><span class=\"chat-streaming-dot\"></span><span class=\"chat-streaming-dot\"></span><span class=\"loading-text\">${LOADING_MESSAGES[this.loadingMessageIndex]}</span></div>` : ''}\n </div>\n <div class=\"chat-message-metadata\">\n <span class=\"chat-message-time\">${formatTimestamp(message.timestamp)}</span>\n </div>\n </div>\n </div>\n `;\n }\n\n /**\n * Scroll to bottom of messages\n */\n private scrollToBottom(): void {\n if (!this.messagesContainer) return;\n\n requestAnimationFrame(() => {\n if (this.messagesContainer) {\n this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;\n }\n });\n }\n\n /**\n * Set streaming state\n */\n private setStreamingState(streaming: boolean): void {\n this.isStreaming = streaming;\n\n if (this.inputElement) {\n this.inputElement.disabled = streaming;\n }\n\n if (this.sendButton) {\n this.sendButton.disabled = streaming;\n this.sendButton.innerHTML = streaming ? '<div class=\"loading\"></div>' : '<span>Send</span>';\n }\n\n if (streaming) {\n this.startLoadingMessages();\n } else {\n this.clearLoadingMessages();\n }\n }\n\n private startLoadingMessages(): void {\n this.loadingMessageIndex = Math.floor(Math.random() * LOADING_MESSAGES.length);\n this.loadingMessageInterval = setInterval(() => {\n this.loadingMessageIndex = (this.loadingMessageIndex + 1) % LOADING_MESSAGES.length;\n if (this.isStreaming) {\n this.renderMessages(true);\n }\n }, LOADING_MESSAGE_INTERVAL_MS);\n }\n\n private clearLoadingMessages(): void {\n if (this.loadingMessageInterval) {\n clearInterval(this.loadingMessageInterval);\n this.loadingMessageInterval = null;\n }\n }\n\n /**\n * Get all messages\n */\n public getMessages(): Message[] {\n return [...this.messages];\n }\n\n /**\n * Clear all messages\n */\n public clearMessages(): void {\n this.messages = [];\n this.renderMessages();\n }\n\n /**\n * Set messages (for restoring history)\n */\n public setMessages(messages: Message[]): void {\n this.messages = [...messages];\n this.renderMessages();\n }\n\n /**\n * Destroy and cleanup\n */\n public destroy(): void {\n this.clearLoadingMessages();\n\n if (this.isStreaming) {\n this.client.cancelAllRequests();\n }\n\n // Remove event listeners\n if (this.inputElement) {\n if (this.handleInputResize) {\n this.inputElement.removeEventListener('input', this.handleInputResize);\n }\n if (this.handleInputKeydown) {\n this.inputElement.removeEventListener('keydown', this.handleInputKeydown);\n }\n }\n\n if (this.sendButton && this.handleSendClick) {\n this.sendButton.removeEventListener('click', this.handleSendClick);\n }\n\n // Clear handler references\n this.handleInputResize = null;\n this.handleInputKeydown = null;\n this.handleSendClick = null;\n }\n}\n","/**\n * Chat Bubble Snippet\n * A floating chat widget that expands from a bubble button\n * Fixed position in bottom-right corner\n */\n\nimport type { AISearchClient } from '../api/ai-search.ts';\nimport { POWERED_BY_BRANDING } from '../constants.ts';\nimport { chatStyles } from '../styles/chat.ts';\nimport { baseStyles } from '../styles/theme.ts';\nimport type { SearchSnippetProps } from '../types/index.ts';\nimport {\n createClient,\n createCustomEvent,\n parseAttribute,\n parseBooleanAttribute,\n} from '../utils/index.ts';\nimport type { Message } from './chat-view.ts';\nimport { ChatView } from './chat-view.ts';\n\nconst COMPONENT_NAME = 'chat-bubble-snippet';\n\nexport class ChatBubbleSnippet extends HTMLElement {\n private shadow: ShadowRoot;\n private client: AISearchClient | null = null;\n private chatView: ChatView | null = null;\n private container: HTMLElement | null = null;\n private isExpanded = false;\n private isMinimized = false;\n\n // Event handler references for cleanup\n private handleBubbleClick: (() => void) | null = null;\n private handleCloseClick: (() => void) | null = null;\n private handleMinimizeClick: (() => void) | null = null;\n private handleClearClick: (() => void) | null = null;\n\n static get observedAttributes() {\n return ['api-url', 'placeholder', 'theme', 'hide-branding'] as const;\n }\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback(): void {\n this.render();\n this.initializeClient();\n this.dispatchEvent(createCustomEvent('ready', undefined));\n }\n\n disconnectedCallback(): void {\n this.cleanup();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n if (name === 'api-url') {\n this.initializeClient();\n } else if (name === 'theme') {\n // Theme changes are handled automatically by CSS :host([theme]) selectors\n this.updateTheme(newValue);\n }\n }\n\n private getProps(): SearchSnippetProps {\n return {\n apiUrl: parseAttribute(this.getAttribute('api-url'), ''),\n placeholder: parseAttribute(this.getAttribute('placeholder'), 'Type a message...'),\n theme: parseAttribute(this.getAttribute('theme'), 'auto') as 'light' | 'dark' | 'auto',\n hideBranding: parseBooleanAttribute(this.getAttribute('hide-branding'), false),\n };\n }\n\n private initializeClient(): void {\n const props = this.getProps();\n\n if (!props.apiUrl) {\n console.error('ChatBubbleSnippet: api-url attribute is required');\n this.client = null;\n return;\n }\n\n try {\n this.client = createClient(props.apiUrl);\n } catch (error) {\n console.error('ChatBubbleSnippet:', error);\n }\n }\n\n private render(): void {\n const style = document.createElement('style');\n style.textContent = `${baseStyles}\\n${chatStyles}\\n${this.getBubbleStyles()}`;\n\n this.container = document.createElement('div');\n this.container.className = 'chat-bubble-widget';\n this.container.innerHTML = this.getBaseHTML();\n\n this.shadow.innerHTML = '';\n this.shadow.appendChild(style);\n this.shadow.appendChild(this.container);\n\n this.attachEventListeners();\n }\n\n private getBubbleStyles(): string {\n return `\n .chat-bubble-widget {\n position: var(--chat-bubble-position);\n bottom: var(--chat-bubble-button-bottom);\n right: var(--chat-bubble-button-right);\n z-index: var(--chat-bubble-button-z-index);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n }\n\n .bubble-button {\n width: var(--chat-bubble-button-size);\n height: var(--chat-bubble-button-size);\n border-radius: var(--chat-bubble-button-radius);\n background: var(--search-snippet-primary-color);\n border: none;\n cursor: pointer;\n box-shadow: var(--chat-bubble-button-shadow);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.3s ease;\n position: relative;\n }\n\n .bubble-button:hover {\n background: var(--search-snippet-primary-hover);\n transform: scale(1.05);\n }\n\n .bubble-button svg {\n width: var(--chat-bubble-button-icon-size);\n height: var(--chat-bubble-button-icon-size);\n color: var(--chat-bubble-button-icon-color);\n }\n\n .bubble-button.hidden {\n opacity: 0;\n pointer-events: none;\n transform: scale(0);\n }\n\n .chat-window {\n position: absolute;\n bottom: 0;\n right: 0;\n width: 380px;\n height: 500px;\n background: var(--search-snippet-background);\n border-radius: var(--search-snippet-border-radius);\n box-shadow: var(--chat-bubble-window-shadow);\n display: flex;\n flex-direction: column;\n opacity: 0;\n transform: scale(0.8) translateY(20px);\n transform-origin: bottom right;\n transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);\n pointer-events: none;\n overflow: hidden;\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n }\n\n .chat-window.expanded {\n opacity: 1;\n transform: scale(1) translateY(0);\n pointer-events: auto;\n }\n\n .chat-window.minimized {\n height: 58px;\n overflow: hidden;\n }\n\n .chat-header {\n background: var(--search-snippet-surface);\n padding: var(--search-snippet-spacing-md);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n }\n\n .chat-header-title {\n font-weight: var(--search-snippet-font-weight-bold);\n color: var(--search-snippet-text-color);\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n font-size: var(--search-snippet-font-size-lg);\n }\n\n .chat-header-title svg {\n width: 20px;\n height: 20px;\n }\n\n .chat-header-actions {\n display: flex;\n gap: var(--search-snippet-spacing-xs);\n }\n\n .icon-button {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background var(--search-snippet-transition-fast);\n color: var(--search-snippet-text-color);\n }\n\n .icon-button:hover {\n background: var(--search-snippet-hover-background);\n }\n\n .icon-button svg {\n width: 18px;\n height: 18px;\n }\n\n .chat-content {\n flex: 1;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n @media (max-width: 480px) {\n .chat-window {\n width: calc(100vw - 40px);\n max-width: 400px;\n }\n }\n `;\n }\n\n private getBaseHTML(): string {\n const props = this.getProps();\n const brandingHTML = props.hideBranding\n ? ''\n : `<div class=\"powered-by\">${POWERED_BY_BRANDING}</div>`;\n\n return `\n <button class=\"bubble-button\" aria-label=\"Open chat\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n </button>\n <div class=\"chat-window\">\n <div class=\"chat-header\">\n <div class=\"chat-header-title\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n <span>Chat</span>\n </div>\n <div class=\"chat-header-actions\">\n <button class=\"icon-button clear-button\" aria-label=\"Clear history\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <polyline points=\"3 6 5 6 21 6\"></polyline>\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path>\n </svg>\n </button>\n <button class=\"icon-button minimize-button\" aria-label=\"Minimize\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"></line>\n </svg>\n </button>\n <button class=\"icon-button close-button\" aria-label=\"Close\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\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 </button>\n </div>\n </div>\n <div class=\"chat-content\"></div>\n ${brandingHTML}\n </div>\n `;\n }\n\n private attachEventListeners(): void {\n const bubbleButton = this.shadow.querySelector('.bubble-button');\n const closeButton = this.shadow.querySelector('.close-button');\n const minimizeButton = this.shadow.querySelector('.minimize-button');\n const clearButton = this.shadow.querySelector('.clear-button');\n\n this.handleBubbleClick = () => this.toggleChat();\n this.handleCloseClick = () => this.closeChat();\n this.handleMinimizeClick = () => this.toggleMinimize();\n this.handleClearClick = () => this.clearChat();\n\n bubbleButton?.addEventListener('click', this.handleBubbleClick);\n closeButton?.addEventListener('click', this.handleCloseClick);\n minimizeButton?.addEventListener('click', this.handleMinimizeClick);\n clearButton?.addEventListener('click', this.handleClearClick);\n }\n\n private removeEventListeners(): void {\n const bubbleButton = this.shadow.querySelector('.bubble-button');\n const closeButton = this.shadow.querySelector('.close-button');\n const minimizeButton = this.shadow.querySelector('.minimize-button');\n const clearButton = this.shadow.querySelector('.clear-button');\n\n if (this.handleBubbleClick) {\n bubbleButton?.removeEventListener('click', this.handleBubbleClick);\n }\n if (this.handleCloseClick) {\n closeButton?.removeEventListener('click', this.handleCloseClick);\n }\n if (this.handleMinimizeClick) {\n minimizeButton?.removeEventListener('click', this.handleMinimizeClick);\n }\n if (this.handleClearClick) {\n clearButton?.removeEventListener('click', this.handleClearClick);\n }\n\n // Clear handler references\n this.handleBubbleClick = null;\n this.handleCloseClick = null;\n this.handleMinimizeClick = null;\n this.handleClearClick = null;\n }\n\n private toggleChat(): void {\n this.isExpanded = !this.isExpanded;\n const bubbleButton = this.shadow.querySelector('.bubble-button');\n const chatWindow = this.shadow.querySelector('.chat-window');\n\n if (this.isExpanded) {\n bubbleButton?.classList.add('hidden');\n chatWindow?.classList.add('expanded');\n this.initializeChatView();\n } else {\n bubbleButton?.classList.remove('hidden');\n chatWindow?.classList.remove('expanded');\n }\n }\n\n private closeChat(): void {\n this.isExpanded = false;\n this.isMinimized = false;\n const bubbleButton = this.shadow.querySelector('.bubble-button');\n const chatWindow = this.shadow.querySelector('.chat-window');\n\n bubbleButton?.classList.remove('hidden');\n chatWindow?.classList.remove('expanded', 'minimized');\n }\n\n private toggleMinimize(): void {\n this.isMinimized = !this.isMinimized;\n const chatWindow = this.shadow.querySelector('.chat-window');\n\n if (this.isMinimized) {\n chatWindow?.classList.add('minimized');\n } else {\n chatWindow?.classList.remove('minimized');\n }\n }\n\n private initializeChatView(): void {\n if (this.chatView) return;\n\n const chatContent = this.shadow.querySelector('.chat-content') as HTMLElement;\n if (!chatContent) return;\n\n if (!this.client) {\n chatContent.innerHTML = `\n <div style=\"padding: 16px; color: var(--search-snippet-error-color, #ef4444); font-family: var(--search-snippet-font-family, sans-serif); font-size: var(--search-snippet-font-size-base, 14px);\">\n <strong>Error:</strong> The <code>api-url</code> attribute is required. Please provide a valid API URL.\n </div>\n `;\n return;\n }\n\n const props = this.getProps();\n this.chatView = new ChatView(chatContent, this.client, props);\n }\n\n private updateTheme(theme: string | null): void {\n // CSS :host([theme]) selectors handle theming automatically\n // For 'auto' mode, remove the attribute to let @media (prefers-color-scheme) work\n const validTheme = theme === 'light' || theme === 'dark' ? theme : null;\n\n if (\n validTheme === null &&\n this.hasAttribute('theme') &&\n this.getAttribute('theme') !== 'auto'\n ) {\n this.removeAttribute('theme');\n }\n }\n\n private cleanup(): void {\n this.removeEventListeners();\n\n if (this.client) {\n this.client.cancelAllRequests();\n }\n\n if (this.chatView) {\n this.chatView.destroy();\n }\n }\n\n // Public API\n public clearChat(): void {\n this.chatView?.clearMessages();\n }\n\n public async sendMessage(content: string): Promise<void> {\n if (this.chatView) {\n await this.chatView.sendMessage(content);\n }\n }\n\n public getMessages(): Message[] {\n return this.chatView?.getMessages() || [];\n }\n}\n\n// Register the custom element\nif (!customElements.get(COMPONENT_NAME)) {\n customElements.define(COMPONENT_NAME, ChatBubbleSnippet);\n}\n","/**\n * Chat Page Snippet\n * A full-page chat interface with history support\n */\n\nimport type { AISearchClient } from '../api/ai-search.ts';\nimport { POWERED_BY_BRANDING } from '../constants.ts';\nimport { chatStyles } from '../styles/chat.ts';\nimport { baseStyles } from '../styles/theme.ts';\nimport type { SearchSnippetProps } from '../types/index.ts';\nimport {\n createClient,\n createCustomEvent,\n parseAttribute,\n parseBooleanAttribute,\n} from '../utils/index.ts';\nimport type { Message } from './chat-view.ts';\nimport { ChatView } from './chat-view.ts';\n\nconst COMPONENT_NAME = 'chat-page-snippet';\nconst STORAGE_KEY = 'chat-page-sessions';\n\ninterface ChatSession {\n id: string;\n title: string;\n messages: Message[];\n createdAt: number;\n updatedAt: number;\n}\n\nexport class ChatPageSnippet extends HTMLElement {\n private shadow: ShadowRoot;\n private client: AISearchClient | null = null;\n private chatView: ChatView | null = null;\n private container: HTMLElement | null = null;\n private sessions: ChatSession[] = [];\n private currentSessionId: string | null = null;\n private sidebarCollapsed = false;\n\n // Event handler references for cleanup\n private handleClearClick: (() => void) | null = null;\n private handleNewChatClick: (() => void) | null = null;\n private handleToggleSidebarClick: (() => void) | null = null;\n private handleChatListClick: ((e: Event) => void) | null = null;\n private handleMessageEvent: (() => void) | null = null;\n\n static get observedAttributes() {\n return ['api-url', 'placeholder', 'theme', 'hide-branding'] as const;\n }\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n this.loadSessions();\n }\n\n connectedCallback(): void {\n this.render();\n this.initializeClient();\n this.setupView();\n this.dispatchEvent(createCustomEvent('ready', undefined));\n }\n\n disconnectedCallback(): void {\n this.saveCurrentSession();\n this.cleanup();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n if (name === 'api-url') {\n this.initializeClient();\n this.setupView();\n } else if (name === 'theme') {\n // Theme changes are handled automatically by CSS :host([theme]) selectors\n this.updateTheme(newValue);\n }\n }\n\n private getProps(): SearchSnippetProps {\n return {\n apiUrl: parseAttribute(this.getAttribute('api-url'), ''),\n placeholder: parseAttribute(this.getAttribute('placeholder'), 'Type a message...'),\n theme: parseAttribute(this.getAttribute('theme'), 'auto') as 'light' | 'dark' | 'auto',\n hideBranding: parseBooleanAttribute(this.getAttribute('hide-branding'), false),\n };\n }\n\n private initializeClient(): void {\n const props = this.getProps();\n\n if (!props.apiUrl) {\n console.error('ChatPageSnippet: api-url attribute is required');\n this.client = null;\n return;\n }\n\n try {\n this.client = createClient(props.apiUrl);\n } catch (error) {\n console.error('ChatPageSnippet:', error);\n }\n }\n\n private render(): void {\n const style = document.createElement('style');\n style.textContent = `${baseStyles}\\n${chatStyles}\\n${this.getPageStyles()}`;\n\n this.container = document.createElement('div');\n this.container.className = 'chat-page-container';\n this.container.innerHTML = this.getBaseHTML();\n\n this.shadow.innerHTML = '';\n this.shadow.appendChild(style);\n this.shadow.appendChild(this.container);\n\n this.attachEventListeners();\n }\n\n private getPageStyles(): string {\n return `\n :host {\n display: block;\n width: 100%;\n height: 100vh;\n }\n\n .chat-page-container {\n display: flex;\n height: 100%;\n background: var(--search-snippet-background);\n }\n\n /* Sidebar styles */\n .chat-sidebar {\n width: 280px;\n min-width: 280px;\n background: var(--search-snippet-surface);\n border-right: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n display: flex;\n flex-direction: column;\n transition: var(--search-snippet-transition);\n overflow: hidden;\n }\n\n .chat-sidebar.collapsed {\n width: 0;\n min-width: 0;\n border-right: none;\n }\n\n .sidebar-header {\n padding: var(--search-snippet-spacing-lg);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n height: 69px;\n }\n\n .sidebar-title {\n font-size: var(--search-snippet-font-size-lg);\n font-weight: var(--search-snippet-font-weight-bold);\n color: var(--search-snippet-text-color);\n }\n\n .new-chat-button {\n width: 100%;\n height: var(--search-snippet-button-height);\n margin: var(--search-snippet-spacing-md) var(--search-snippet-spacing-lg);\n padding: 0 var(--search-snippet-spacing-lg);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: #fff;\n background: var(--search-snippet-primary-color);\n border: none;\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n outline: none;\n transition: var(--search-snippet-transition);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-sm);\n box-sizing: border-box;\n width: calc(100% - var(--search-snippet-spacing-lg) * 2);\n }\n\n .new-chat-button:hover {\n opacity: 0.9;\n }\n\n .new-chat-button svg {\n width: 16px;\n height: 16px;\n }\n\n .chat-list {\n flex: 1;\n overflow-y: auto;\n padding: var(--search-snippet-spacing-sm);\n }\n\n .chat-list-item {\n display: flex;\n align-items: center;\n padding: var(--search-snippet-spacing-md) var(--search-snippet-spacing-lg);\n margin-bottom: var(--search-snippet-spacing-xs);\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n transition: var(--search-snippet-transition);\n gap: var(--search-snippet-spacing-sm);\n }\n\n .chat-list-item:hover {\n background: var(--search-snippet-hover-background);\n }\n\n .chat-list-item.active {\n background: var(--search-snippet-hover-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n }\n\n .chat-list-item-content {\n flex: 1;\n min-width: 0;\n }\n\n .chat-list-item-title {\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .chat-list-item-date {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n margin-top: 2px;\n }\n\n .chat-list-item-delete {\n opacity: 0;\n background: none;\n border: none;\n padding: var(--search-snippet-spacing-xs);\n cursor: pointer;\n color: var(--search-snippet-text-secondary);\n border-radius: var(--search-snippet-border-radius-sm);\n transition: var(--search-snippet-transition);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .chat-list-item:hover .chat-list-item-delete {\n opacity: 1;\n }\n\n .chat-list-item-delete:hover {\n background: var(--search-snippet-error-background, rgba(239, 68, 68, 0.1));\n color: var(--search-snippet-error-color, #ef4444);\n }\n\n .chat-list-item-delete svg {\n width: 14px;\n height: 14px;\n }\n\n .chat-list-empty {\n padding: var(--search-snippet-spacing-xl);\n text-align: center;\n color: var(--search-snippet-text-secondary);\n font-size: var(--search-snippet-font-size-sm);\n }\n\n /* Main content area */\n .chat-main {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n }\n\n .chat-page-header {\n background: var(--search-snippet-surface);\n padding: var(--search-snippet-spacing-lg);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n }\n\n .chat-page-header-left {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-md);\n }\n\n .toggle-sidebar-button {\n width: 36px;\n height: 36px;\n padding: 0;\n font-family: var(--search-snippet-font-family);\n color: var(--search-snippet-text-color);\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n outline: none;\n transition: var(--search-snippet-transition);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n .toggle-sidebar-button:hover {\n background: var(--search-snippet-hover-background);\n }\n\n .toggle-sidebar-button svg {\n width: 18px;\n height: 18px;\n }\n\n .chat-page-header-title {\n font-size: var(--search-snippet-font-size-xl);\n font-weight: var(--search-snippet-font-weight-bold);\n color: var(--search-snippet-text-color);\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-md);\n }\n\n .chat-page-header-title svg {\n width: 28px;\n height: 28px;\n }\n\n .chat-page-header-actions {\n display: flex;\n gap: var(--search-snippet-spacing-sm);\n }\n\n .header-button {\n height: var(--search-snippet-button-height);\n padding: 0 var(--search-snippet-spacing-lg);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n outline: none;\n transition: var(--search-snippet-transition);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-sm);\n }\n\n .header-button:hover {\n background: var(--search-snippet-hover-background);\n }\n\n .header-button svg {\n width: 16px;\n height: 16px;\n }\n\n .chat-page-content {\n flex: 1;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n width: 100%;\n }\n\n .container {\n border: none;\n box-shadow: none;\n height: 100%;\n width: 100%;\n background: var(--search-snippet-background);\n border-radius: 0;\n }\n `;\n }\n\n private getBaseHTML(): string {\n const props = this.getProps();\n const brandingHTML = props.hideBranding\n ? ''\n : `<div class=\"powered-by\">${POWERED_BY_BRANDING}</div>`;\n\n return `\n <div class=\"chat-sidebar\">\n <div class=\"sidebar-header\">\n <span class=\"sidebar-title\">History</span>\n </div>\n <button class=\"new-chat-button\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12h14\"></path>\n </svg>\n New Chat\n </button>\n <div class=\"chat-list\"></div>\n ${brandingHTML}\n </div>\n <div class=\"chat-main\">\n <div class=\"chat-page-header\">\n <div class=\"chat-page-header-left\">\n <button class=\"toggle-sidebar-button\" title=\"Toggle sidebar\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M3 12h18M3 6h18M3 18h18\"></path>\n </svg>\n </button>\n <div class=\"chat-page-header-title\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n <span>Chat</span>\n </div>\n </div>\n <div class=\"chat-page-header-actions\">\n <button class=\"header-button clear-button\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path>\n </svg>\n Clear Chat\n </button>\n </div>\n </div>\n <div class=\"chat-page-content\">\n <div class=\"container\"></div>\n </div>\n </div>\n `;\n }\n\n private attachEventListeners(): void {\n const clearButton = this.shadow.querySelector('.clear-button');\n const newChatButton = this.shadow.querySelector('.new-chat-button');\n const toggleSidebarButton = this.shadow.querySelector('.toggle-sidebar-button');\n const chatList = this.shadow.querySelector('.chat-list');\n\n this.handleClearClick = () => this.clearCurrentChat();\n this.handleNewChatClick = () => this.createNewChat();\n this.handleToggleSidebarClick = () => this.toggleSidebar();\n this.handleChatListClick = (e: Event) => this.onChatListClick(e);\n\n clearButton?.addEventListener('click', this.handleClearClick);\n newChatButton?.addEventListener('click', this.handleNewChatClick);\n toggleSidebarButton?.addEventListener('click', this.handleToggleSidebarClick);\n chatList?.addEventListener('click', this.handleChatListClick);\n }\n\n private removeEventListeners(): void {\n const clearButton = this.shadow.querySelector('.clear-button');\n const newChatButton = this.shadow.querySelector('.new-chat-button');\n const toggleSidebarButton = this.shadow.querySelector('.toggle-sidebar-button');\n const chatList = this.shadow.querySelector('.chat-list');\n const chatContent = this.shadow.querySelector('.container') as HTMLElement;\n\n if (this.handleClearClick) {\n clearButton?.removeEventListener('click', this.handleClearClick);\n }\n if (this.handleNewChatClick) {\n newChatButton?.removeEventListener('click', this.handleNewChatClick);\n }\n if (this.handleToggleSidebarClick) {\n toggleSidebarButton?.removeEventListener('click', this.handleToggleSidebarClick);\n }\n if (this.handleChatListClick) {\n chatList?.removeEventListener('click', this.handleChatListClick);\n }\n if (this.handleMessageEvent && chatContent) {\n chatContent.removeEventListener('message', this.handleMessageEvent);\n }\n\n // Clear handler references\n this.handleClearClick = null;\n this.handleNewChatClick = null;\n this.handleToggleSidebarClick = null;\n this.handleChatListClick = null;\n this.handleMessageEvent = null;\n }\n\n private setupView(): void {\n const chatContent = this.shadow.querySelector('.container') as HTMLElement;\n\n if (!this.client) {\n if (chatContent) {\n chatContent.innerHTML = `\n <div style=\"padding: 16px; color: var(--search-snippet-error-color, #ef4444); font-family: var(--search-snippet-font-family, sans-serif); font-size: var(--search-snippet-font-size-base, 14px);\">\n <strong>Error:</strong> The <code>api-url</code> attribute is required. Please provide a valid API URL.\n </div>\n `;\n }\n return;\n }\n\n if (!chatContent) return;\n\n const props = this.getProps();\n this.chatView = new ChatView(chatContent, this.client, props);\n\n // Load current session or create new one\n if (this.sessions.length === 0) {\n this.createNewChat();\n } else {\n // Load the most recent session\n const lastSession = this.sessions[0];\n this.switchToSession(lastSession.id);\n }\n\n // Listen for new messages to save session\n this.handleMessageEvent = () => {\n this.saveCurrentSession();\n this.updateSessionTitle();\n this.renderChatList();\n };\n chatContent.addEventListener('message', this.handleMessageEvent);\n\n this.renderChatList();\n }\n\n private generateSessionId(): string {\n return `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n }\n\n private loadSessions(): void {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) {\n this.sessions = JSON.parse(stored);\n // Sort by updatedAt descending\n this.sessions.sort((a, b) => b.updatedAt - a.updatedAt);\n }\n } catch (error) {\n console.error('Failed to load chat sessions:', error);\n }\n }\n\n private saveSessions(): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(this.sessions));\n } catch (error) {\n console.error('Failed to save chat sessions:', error);\n }\n }\n\n private saveCurrentSession(): void {\n if (!this.currentSessionId || !this.chatView) return;\n\n const sessionIndex = this.sessions.findIndex((s) => s.id === this.currentSessionId);\n if (sessionIndex !== -1) {\n this.sessions[sessionIndex].messages = this.chatView.getMessages();\n this.sessions[sessionIndex].updatedAt = Date.now();\n this.saveSessions();\n }\n }\n\n private updateSessionTitle(): void {\n if (!this.currentSessionId) return;\n\n const session = this.sessions.find((s) => s.id === this.currentSessionId);\n if (session && session.messages.length > 0 && session.title === 'New Chat') {\n const firstUserMessage = session.messages.find((m) => m.role === 'user');\n if (firstUserMessage) {\n session.title =\n firstUserMessage.content.slice(0, 50) +\n (firstUserMessage.content.length > 50 ? '...' : '');\n this.saveSessions();\n }\n }\n }\n\n private createNewChat(): void {\n // Save current session first\n this.saveCurrentSession();\n\n const newSession: ChatSession = {\n id: this.generateSessionId(),\n title: 'New Chat',\n messages: [],\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n this.sessions.unshift(newSession);\n this.currentSessionId = newSession.id;\n this.saveSessions();\n\n // Clear the chat view\n this.chatView?.clearMessages();\n this.renderChatList();\n }\n\n private switchToSession(sessionId: string): void {\n if (sessionId === this.currentSessionId) return;\n\n // Save current session first\n this.saveCurrentSession();\n\n const session = this.sessions.find((s) => s.id === sessionId);\n if (session && this.chatView) {\n this.currentSessionId = sessionId;\n this.chatView.setMessages(session.messages);\n this.renderChatList();\n }\n }\n\n private deleteSession(sessionId: string): void {\n const sessionIndex = this.sessions.findIndex((s) => s.id === sessionId);\n if (sessionIndex === -1) return;\n\n this.sessions.splice(sessionIndex, 1);\n this.saveSessions();\n\n // If we deleted the current session, switch to another or create new\n if (sessionId === this.currentSessionId) {\n if (this.sessions.length > 0) {\n this.switchToSession(this.sessions[0].id);\n } else {\n this.createNewChat();\n }\n }\n\n this.renderChatList();\n }\n\n private clearCurrentChat(): void {\n if (!this.currentSessionId) return;\n\n const session = this.sessions.find((s) => s.id === this.currentSessionId);\n if (session) {\n session.messages = [];\n session.title = 'New Chat';\n session.updatedAt = Date.now();\n this.saveSessions();\n }\n\n this.chatView?.clearMessages();\n this.renderChatList();\n }\n\n private toggleSidebar(): void {\n this.sidebarCollapsed = !this.sidebarCollapsed;\n const sidebar = this.shadow.querySelector('.chat-sidebar');\n sidebar?.classList.toggle('collapsed', this.sidebarCollapsed);\n }\n\n private onChatListClick(e: Event): void {\n const target = e.target as HTMLElement;\n\n // Handle delete button click\n const deleteButton = target.closest('.chat-list-item-delete');\n if (deleteButton) {\n e.stopPropagation();\n const sessionId = deleteButton.getAttribute('data-session-id');\n if (sessionId) {\n this.deleteSession(sessionId);\n }\n return;\n }\n\n // Handle chat item click\n const chatItem = target.closest('.chat-list-item');\n if (chatItem) {\n const sessionId = chatItem.getAttribute('data-session-id');\n if (sessionId) {\n this.switchToSession(sessionId);\n }\n }\n }\n\n private renderChatList(): void {\n const chatList = this.shadow.querySelector('.chat-list');\n if (!chatList) return;\n\n if (this.sessions.length === 0) {\n chatList.innerHTML = '<div class=\"chat-list-empty\">No chats yet</div>';\n return;\n }\n\n chatList.innerHTML = this.sessions.map((session) => this.renderChatListItem(session)).join('');\n }\n\n private renderChatListItem(session: ChatSession): string {\n const isActive = session.id === this.currentSessionId;\n const date = this.formatDate(session.updatedAt);\n\n return `\n <div class=\"chat-list-item ${isActive ? 'active' : ''}\" data-session-id=\"${session.id}\">\n <div class=\"chat-list-item-content\">\n <div class=\"chat-list-item-title\">${this.escapeHTML(session.title)}</div>\n <div class=\"chat-list-item-date\">${date}</div>\n </div>\n <button class=\"chat-list-item-delete\" data-session-id=\"${session.id}\" title=\"Delete chat\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path>\n </svg>\n </button>\n </div>\n `;\n }\n\n private formatDate(timestamp: number): string {\n const date = new Date(timestamp);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffDays === 0) {\n return date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });\n } else if (diffDays === 1) {\n return 'Yesterday';\n } else if (diffDays < 7) {\n return date.toLocaleDateString(undefined, { weekday: 'long' });\n } else {\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n }\n\n private escapeHTML(str: string): string {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n private updateTheme(theme: string | null): void {\n // CSS :host([theme]) selectors handle theming automatically\n // For 'auto' mode, remove the attribute to let @media (prefers-color-scheme) work\n const validTheme = theme === 'light' || theme === 'dark' ? theme : null;\n\n if (\n validTheme === null &&\n this.hasAttribute('theme') &&\n this.getAttribute('theme') !== 'auto'\n ) {\n this.removeAttribute('theme');\n }\n }\n\n private cleanup(): void {\n this.removeEventListeners();\n\n if (this.client) {\n this.client.cancelAllRequests();\n }\n\n if (this.chatView) {\n this.chatView.destroy();\n }\n }\n\n // Public API\n public clearChat(): void {\n this.clearCurrentChat();\n }\n\n public async sendMessage(content: string): Promise<void> {\n if (this.chatView) {\n await this.chatView.sendMessage(content);\n this.saveCurrentSession();\n }\n }\n\n public getMessages(): Message[] {\n return this.chatView?.getMessages() || [];\n }\n\n public getSessions(): ChatSession[] {\n return [...this.sessions];\n }\n\n public getCurrentSession(): ChatSession | null {\n return this.sessions.find((s) => s.id === this.currentSessionId) || null;\n }\n}\n\n// Register the custom element\nif (!customElements.get(COMPONENT_NAME)) {\n customElements.define(COMPONENT_NAME, ChatPageSnippet);\n}\n","/**\n * Search mode specific styles\n */\n\nexport const searchStyles = `\n/* Search view states */\n.search-view {\n transition: var(--search-snippet-transition-slow);\n background: var(--search-snippet-background);\n border-radius: var(--search-snippet-border-radius);\n padding: 0px;\n}\n\n.search-view-collapsed {\n max-height: 60px;\n}\n\n.search-view-expanded {\n max-height: var(--search-snippet-max-height);\n}\n\n\n.search-icon {\n width: var(--search-snippet-icon-size);\n height: var(--search-snippet-icon-size);\n margin-left: var(--search-snippet-icon-margin-left);\n color: var(--search-snippet-text-color);\n}\n\n/* Search input wrapper */\n.search-input-wrapper {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n overflow: hidden;\n transition: max-width var(--search-snippet-transition-slow), \n opacity var(--search-snippet-transition);\n padding: var(--search-snippet-spacing-sm);\n border-radius: var(--search-snippet-border-radius);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n\n\n.search-input {\n flex: 1;\n border: none;\n outline: none;\n background: transparent;\n color: var(--search-snippet-text-color);\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n box-shadow: none;\n padding: 0;\n}\n\n.search-input::placeholder {\n color: var(--search-snippet-text-secondary);\n}\n\n.search-view:has(.search-input:not(:placeholder-shown)) .search-input-wrapper, .search-view:has(.search-input:not(:placeholder-shown)) {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.search-view:focus-within {\n border-color: var(--search-snippet-primary-color);\n box-shadow: inset 0 0 0 3px var(--search-snippet-focus-ring);\n}\n\n.search-view:has(.search-input:not(:placeholder-shown)) .search-content {\n max-height: 600px;\n opacity: 1;\n overflow-y: auto;\n padding: 8px;\n}\n\n.search-submit-button {\n flex-shrink: 0;\n \n border-radius: max(var(--search-snippet-button-min-border-radius, 4px), calc(var(--search-snippet-border-radius) - var(--search-snippet-spacing-sm)))\n}\n\n/* Search content */\n.search-content {\n max-height: 0;\n opacity: 0;\n transition: max-height var(--search-snippet-transition-slow),\n opacity var(--search-snippet-transition);\n position: absolute;\n width: 100%;\n background: var(--search-snippet-background);\n border-bottom-left-radius: var(--search-snippet-border-radius);\n border-bottom-right-radius: var(--search-snippet-border-radius);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-top: none;\n}\n\n.search-content::-webkit-scrollbar {\n width: 8px;\n height: 100px;\n}\n\n.search-content::-webkit-scrollbar-track {\n background: var(--search-snippet-surface);\n \n}\n\n.search-content::-webkit-scrollbar-thumb {\n background: var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n}\n\n.search-content::-webkit-scrollbar-thumb:hover {\n background: var(--search-snippet-text-secondary);\n}\n\n.container {\n overflow: unset;\n position: relative;\n border: none;\n}\n\n.container:has(.search-input:not(:placeholder-shown)) {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n\n/* Override header for search mode */\n\n/* Search results */\n.search-results {\n display: flex;\n flex-direction: column;\n gap: var(--search-snippet-spacing-sm);\n}\n\na.search-result-item {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: var(--search-snippet-spacing-md);\n padding: var(--search-snippet-spacing-md);\n background: var(--search-snippet-surface);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n transition: var(--search-snippet-transition);\n text-decoration: none;\n color: inherit;\n}\n\n/* Image thumbnail container */\n.search-result-image-container {\n flex-shrink: 0;\n width: 64px;\n height: 64px;\n border-radius: calc(var(--search-snippet-border-radius) - 4px);\n overflow: hidden;\n position: relative;\n}\n\n.search-result-image {\n width: 100%;\n height: 100%;\n object-fit: contain;\n opacity: 0;\n transition: opacity var(--search-snippet-transition);\n}\n\n.search-result-image.loaded {\n opacity: 1;\n}\n\n/* Loading shimmer */\n.search-result-image-loading {\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 90deg,\n var(--search-snippet-surface) 25%,\n var(--search-snippet-border-color) 50%,\n var(--search-snippet-surface) 75%\n );\n background-size: 200% 100%;\n animation: search-image-shimmer 1.5s infinite;\n}\n\n@keyframes search-image-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* Placeholder icon */\n.search-result-image-placeholder {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--search-snippet-text-secondary);\n opacity: 0.5;\n}\n\n.search-result-image-placeholder svg {\n width: 24px;\n height: 24px;\n}\n\n/* Content wrapper */\n.search-result-content {\n flex: 1;\n min-width: 0;\n}\n\na.search-result-item:hover {\n background: var(--search-snippet-hover-background);\n border-color: var(--search-snippet-primary-color);\n transform: translateY(-1px);\n box-shadow: var(--search-snippet-result-item-shadow);\n}\n\na.search-result-item:focus-visible {\n outline: 2px solid var(--search-snippet-primary-color);\n outline-offset: 2px;\n}\n\n.search-result-title {\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n margin-bottom: var(--search-snippet-spacing-xs);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.search-result-snippet {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-description);\n line-height: 1.6;\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.search-result-metadata {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n margin-top: var(--search-snippet-spacing-xs);\n min-width: 0;\n}\n\n.search-result-url {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-primary-color);\n text-decoration: none;\n display: block;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.search-result-url-empty {\n visibility: hidden;\n}\n\n.search-result-date {\n font-size: 12px;\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-secondary);\n text-align: right;\n flex-shrink: 0;\n}\n\n.search-result-url:hover {\n text-decoration: underline;\n}\n\n/* Search header */\n.search-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: var(--search-snippet-spacing-md);\n padding-bottom: var(--search-snippet-spacing-sm);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n.search-count {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n}\n\n/* Search footer */\n.search-footer {\n padding: var(--search-snippet-spacing-md);\n padding-bottom: var(--search-snippet-spacing-xs);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-sm);\n}\n\n/* See more link */\n.search-see-more {\n display: inline-flex;\n align-items: center;\n gap: var(--search-snippet-spacing-xs);\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-primary-color);\n text-decoration: none;\n font-weight: var(--search-snippet-font-weight-medium);\n transition: color var(--search-snippet-transition-fast);\n}\n\n.search-see-more:hover {\n text-decoration: underline;\n}\n\n/* Loading state for search */\n.search-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--search-snippet-spacing-xxl);\n gap: var(--search-snippet-spacing-md);\n color: var(--search-snippet-text-secondary);\n}\n\n/* Empty search state */\n.search-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--search-snippet-spacing-xxl);\n gap: var(--search-snippet-spacing-md);\n color: var(--search-snippet-text-secondary);\n text-align: center;\n}\n\n.search-empty-icon {\n width: 64px;\n height: 64px;\n opacity: 0.5;\n}\n\n.search-empty-title {\n font-size: var(--search-snippet-font-size-lg);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n}\n\n.search-empty-description {\n font-size: var(--search-snippet-font-size-sm);\n}\n\n/* Highlight matching text */\n.search-highlight {\n background: var(--search-snippet-warning-background);\n color: var(--search-snippet-warning-color);\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: var(--search-snippet-font-weight-medium);\n}\n`;\n","/**\n * Search Bar Snippet\n * A search bar with results display\n */\n\nimport type { AISearchClient } from '../api/ai-search.ts';\nimport { POWERED_BY_BRANDING } from '../constants.ts';\nimport { searchStyles } from '../styles/search.ts';\nimport { baseStyles } from '../styles/theme.ts';\nimport type { SearchRequestOptions, SearchResult, SearchSnippetProps } from '../types/index.ts';\nimport {\n createClient,\n createCustomEvent,\n debounce,\n escapeHTML,\n formatDate,\n formatDisplayUrl,\n LOADING_MESSAGE_INTERVAL_MS,\n LOADING_MESSAGES,\n parseAttribute,\n parseBooleanAttribute,\n parseNumberAttribute,\n} from '../utils/index.ts';\n\nconst COMPONENT_NAME = 'search-bar-snippet';\nconst DEFAULT_DISPLAY_RESULTS = 10;\nconst REQUEST_MAX_RESULTS = 100;\n\nexport class SearchBarSnippet extends HTMLElement {\n private shadow: ShadowRoot;\n private client: AISearchClient | null = null;\n private container: HTMLElement | null = null;\n private inputElement: HTMLInputElement | null = null;\n private resultsContainer: HTMLElement | null = null;\n private searchButton: HTMLButtonElement | null = null;\n private debouncedSearch: ((query: string) => void) | null = null;\n private currentSearchController: AbortController | null = null;\n private loadingMessageInterval: ReturnType<typeof setInterval> | null = null;\n private loadingMessageIndex = 0;\n\n // Event handler references for cleanup\n private handleInputChange: ((e: Event) => void) | null = null;\n private handleInputKeydownEnter: ((e: KeyboardEvent) => void) | null = null;\n private handleInputKeydownEscape: ((e: KeyboardEvent) => void) | null = null;\n private handleSearchButtonClick: (() => void) | null = null;\n\n static get observedAttributes() {\n return [\n 'api-url',\n 'placeholder',\n 'max-results',\n 'debounce-ms',\n 'theme',\n 'hide-branding',\n 'show-url',\n 'show-date',\n 'hide-thumbnails',\n 'see-more',\n 'request-options',\n ] as const;\n }\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback(): void {\n this.initializeClient();\n this.render();\n this.dispatchEvent(createCustomEvent('ready', undefined));\n }\n\n disconnectedCallback(): void {\n this.cleanup();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n if (name === 'api-url') {\n this.initializeClient();\n } else if (name === 'theme') {\n // Theme changes are handled automatically by CSS :host([theme]) selectors\n // But we trigger this to ensure any dynamic updates are applied\n this.updateTheme(newValue);\n }\n }\n\n private getProps(): SearchSnippetProps {\n return {\n apiUrl: parseAttribute(this.getAttribute('api-url'), ''),\n placeholder: parseAttribute(this.getAttribute('placeholder'), 'Search...'),\n maxResults: parseNumberAttribute(this.getAttribute('max-results'), 10),\n debounceMs: parseNumberAttribute(this.getAttribute('debounce-ms'), 300),\n theme: parseAttribute(this.getAttribute('theme'), 'auto') as 'light' | 'dark' | 'auto',\n hideBranding: parseBooleanAttribute(this.getAttribute('hide-branding'), false),\n showUrl: parseBooleanAttribute(this.getAttribute('show-url'), false),\n showDate: parseBooleanAttribute(this.getAttribute('show-date'), false),\n hideThumbnails: parseBooleanAttribute(this.getAttribute('hide-thumbnails'), false),\n seeMore: parseAttribute(this.getAttribute('see-more'), ''),\n };\n }\n\n private getRequestOptions(): SearchRequestOptions | undefined {\n const rawRequestOptions = this.getAttribute('request-options');\n\n if (!rawRequestOptions) {\n return undefined;\n }\n\n try {\n const parsedRequestOptions = JSON.parse(rawRequestOptions) as unknown;\n\n if (\n parsedRequestOptions === null ||\n typeof parsedRequestOptions !== 'object' ||\n Array.isArray(parsedRequestOptions)\n ) {\n throw new Error('request-options must be a JSON object');\n }\n\n return parsedRequestOptions as SearchRequestOptions;\n } catch (error) {\n console.error('SearchBarSnippet: invalid request-options attribute', error);\n return undefined;\n }\n }\n\n private initializeClient(): void {\n const props = this.getProps();\n\n if (!props.apiUrl) {\n console.error('SearchBarSnippet: api-url attribute is required');\n this.client = null;\n this.showMissingApiUrlError();\n return;\n }\n\n try {\n this.client = createClient(props.apiUrl);\n } catch (error) {\n console.error('SearchBarSnippet:', error);\n }\n }\n\n private render(): void {\n const props = this.getProps();\n\n // Create debounced search function\n const searchFn = (query: string) => this.performSearch(query);\n this.debouncedSearch = debounce(\n searchFn as (...args: unknown[]) => unknown,\n props.debounceMs || 400\n ) as (query: string) => void;\n\n const style = document.createElement('style');\n style.textContent = `${baseStyles}\\n${searchStyles}`;\n\n this.container = document.createElement('div');\n this.container.className = 'container';\n this.container.innerHTML = `\n <div class=\"search-view\"> \n <div class=\"search-input-wrapper\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"search-icon\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\"><path d=\"M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z\"/></svg>\n <input\n type=\"text\"\n name=\"search-input\"\n class=\"search-input\"\n placeholder=\"${escapeHTML(props.placeholder || 'Search...')}\"\n aria-label=\"Search input\"\n autocomplete=\"off\"\n />\n <button class=\"button search-submit-button\" aria-label=\"Search\">\n <span>Search</span>\n </button>\n </div>\n <div class=\"search-content\">\n <div class=\"search-results-wrapper\">\n <!-- Results will be inserted here -->\n </div>\n </div>\n </div>\n `;\n\n this.shadow.innerHTML = '';\n this.shadow.appendChild(style);\n this.shadow.appendChild(this.container);\n\n // Get references to elements\n this.inputElement = this.container.querySelector('.search-input');\n this.resultsContainer = this.container.querySelector('.search-results-wrapper');\n this.searchButton = this.container.querySelector('.search-submit-button');\n\n this.attachEventListeners();\n\n // Show error immediately if api-url was missing when the component was connected\n if (!this.client) {\n this.showMissingApiUrlError();\n }\n }\n\n private attachEventListeners(): void {\n if (!this.inputElement) return;\n\n // Input event for real-time search\n this.handleInputChange = (e: Event) => {\n const target = e.target as HTMLInputElement;\n const query = target.value.trim();\n\n if (query.length > 0 && this.debouncedSearch) {\n this.debouncedSearch(query);\n } else {\n this.showEmptyState();\n }\n };\n this.inputElement.addEventListener('input', this.handleInputChange);\n\n // Enter key to search immediately\n this.handleInputKeydownEnter = (e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n const query = (e.target as HTMLInputElement).value.trim();\n if (query.length > 0) {\n this.performSearch(query);\n }\n }\n };\n this.inputElement.addEventListener('keydown', this.handleInputKeydownEnter);\n\n this.handleInputKeydownEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this.inputElement) {\n this.inputElement.value = '';\n }\n };\n window.addEventListener('keydown', this.handleInputKeydownEscape);\n\n // Search button click\n if (this.searchButton) {\n this.handleSearchButtonClick = () => {\n const query = this.inputElement?.value.trim() || '';\n if (query.length > 0) {\n this.performSearch(query);\n }\n };\n this.searchButton.addEventListener('click', this.handleSearchButtonClick);\n }\n }\n\n private async performSearch(query: string): Promise<void> {\n if (!this.client) {\n this.showMissingApiUrlError();\n return;\n }\n\n // Cancel any existing request before starting a new one\n if (this.currentSearchController) {\n this.currentSearchController.abort();\n this.currentSearchController = null;\n }\n\n // Create new controller for this request\n this.currentSearchController = new AbortController();\n this.showLoadingState();\n\n try {\n const results = await this.client.search(query, {\n signal: this.currentSearchController.signal,\n maxResults: REQUEST_MAX_RESULTS,\n request: this.getRequestOptions(),\n });\n const props = this.getProps();\n const visibleResults = results.slice(0, props.maxResults || DEFAULT_DISPLAY_RESULTS);\n this.displayResults(visibleResults, query, results.length);\n } catch (error) {\n // Don't show error state for cancelled requests\n if ((error as Error).name === 'AbortError') {\n return;\n }\n this.showErrorState((error as Error).message);\n } finally {\n this.currentSearchController = null;\n }\n }\n\n private displayResults(\n results: SearchResult[],\n query: string,\n totalResults = results.length\n ): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n if (results.length === 0) {\n this.showNoResultsState(query);\n return;\n }\n const props = this.getProps();\n const brandingHTML = props.hideBranding\n ? ''\n : `<div class=\"powered-by-inline\">${POWERED_BY_BRANDING}</div>`;\n const hasMoreResults = totalResults > results.length;\n const resultsCountLabel = hasMoreResults\n ? `Showing ${results.length} of ${totalResults} results`\n : `Found ${totalResults} result${totalResults === 1 ? '' : 's'}`;\n\n const seeMoreHTML =\n props.seeMore && hasMoreResults\n ? `<div class=\"search-footer\">\n <a href=\"${escapeHTML(props.seeMore + encodeURIComponent(query))}\" class=\"search-see-more\">\n <span>See more results</span>\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14\"/><path d=\"m12 5 7 7-7 7\"/></svg>\n </a>\n </div>`\n : '';\n\n const resultsHTML = `\n <div class=\"search-header\">\n <div class=\"search-count\">\n ${resultsCountLabel}\n </div>\n ${brandingHTML}\n </div>\n <div class=\"search-results\">\n ${results.map((result) => this.renderResult(result)).join('')}\n </div>\n ${seeMoreHTML}\n `;\n\n this.resultsContainer.innerHTML = resultsHTML;\n\n // Attach click handlers to results\n this.attachResultHandlers();\n }\n\n private renderResult(result: SearchResult): string {\n const props = this.getProps();\n const imageHTML = props.hideThumbnails\n ? ''\n : this.renderResultImage(result.image, result.title);\n const href = result.url ? escapeHTML(result.url) : '#';\n const displayUrl = result.url ? escapeHTML(formatDisplayUrl(result.url)) : '';\n const timestampHTML =\n props.showDate && result.timestamp !== undefined\n ? `<div class=\"search-result-date\">${escapeHTML(formatDate(result.timestamp))}</div>`\n : '';\n const metadataHTML =\n (props.showUrl && result.url) || timestampHTML\n ? `<div class=\"search-result-metadata\">\n ${props.showUrl && result.url ? `<span class=\"search-result-url\">${displayUrl}</span>` : '<span class=\"search-result-url search-result-url-empty\"></span>'}\n ${timestampHTML}\n </div>`\n : '';\n\n return `\n <a href=\"${href}\" class=\"search-result-item\" data-result-id=\"${escapeHTML(result.url || '')}\">\n ${imageHTML}\n <div class=\"search-result-content\">\n <div class=\"search-result-title\">${escapeHTML(result.title || '')}</div>\n <div class=\"search-result-snippet\">${escapeHTML(result.description || '')}</div>\n ${metadataHTML}\n </div>\n </a>\n `;\n }\n\n private renderResultImage(imageUrl: string | undefined, alt: string): string {\n const placeholderSVG = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/></svg>`;\n\n if (!imageUrl) {\n return `\n <div class=\"search-result-image-container\">\n <div class=\"search-result-image-placeholder\">${placeholderSVG}</div>\n </div>\n `;\n }\n\n return `\n <div class=\"search-result-image-container\">\n <div class=\"search-result-image-loading\"></div>\n <div class=\"search-result-image-placeholder\" style=\"display: none;\">${placeholderSVG}</div>\n <img \n class=\"search-result-image\" \n src=\"${escapeHTML(imageUrl)}\" \n alt=\"${escapeHTML(alt)}\"\n loading=\"lazy\"\n />\n </div>\n `;\n }\n\n private attachResultHandlers(): void {\n const resultItems = this.container?.querySelectorAll('.search-result-item');\n if (!resultItems) return;\n\n // Handle clicks on results without URLs (prevent default anchor behavior)\n for (const item of resultItems) {\n const href = item.getAttribute('href');\n if (href === '#') {\n item.addEventListener('click', (e) => {\n e.preventDefault();\n });\n }\n }\n\n // Image load/error handlers\n const images = this.container?.querySelectorAll('.search-result-image');\n images?.forEach((img) => {\n img.addEventListener('load', () => {\n img.classList.add('loaded');\n const container = img.closest('.search-result-image-container');\n container?.querySelector('.search-result-image-loading')?.remove();\n });\n\n img.addEventListener('error', () => {\n const container = img.closest('.search-result-image-container');\n container?.querySelector('.search-result-image-loading')?.remove();\n const placeholder = container?.querySelector(\n '.search-result-image-placeholder'\n ) as HTMLElement;\n if (placeholder) placeholder.style.display = 'flex';\n (img as HTMLElement).style.display = 'none';\n });\n });\n }\n\n private showLoadingState(): void {\n if (!this.resultsContainer) return;\n\n this.clearLoadingInterval();\n this.loadingMessageIndex = Math.floor(Math.random() * LOADING_MESSAGES.length);\n\n this.resultsContainer.innerHTML = `\n <div class=\"search-loading\">\n <div class=\"loading\" aria-label=\"Loading\"></div>\n <div class=\"loading-text loading-text-animate\">${LOADING_MESSAGES[this.loadingMessageIndex]}</div>\n </div>\n `;\n\n this.startLoadingInterval();\n }\n\n private startLoadingInterval(): void {\n this.loadingMessageInterval = setInterval(() => {\n this.loadingMessageIndex = (this.loadingMessageIndex + 1) % LOADING_MESSAGES.length;\n const textEl = this.resultsContainer?.querySelector('.loading-text');\n if (textEl) {\n textEl.classList.remove('loading-text-animate');\n void (textEl as HTMLElement).offsetWidth;\n textEl.textContent = LOADING_MESSAGES[this.loadingMessageIndex];\n textEl.classList.add('loading-text-animate');\n }\n }, LOADING_MESSAGE_INTERVAL_MS);\n }\n\n private clearLoadingInterval(): void {\n if (this.loadingMessageInterval) {\n clearInterval(this.loadingMessageInterval);\n this.loadingMessageInterval = null;\n }\n }\n\n private showEmptyState(): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n this.resultsContainer.innerHTML = `\n <div class=\"search-empty\">\n <svg class=\"search-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"></circle>\n <path d=\"m21 21-4.35-4.35\"></path>\n </svg>\n <div class=\"search-empty-title\">Start Searching</div>\n <div class=\"search-empty-description\">\n Enter a query to search for results\n </div>\n </div>\n `;\n }\n\n private showNoResultsState(query: string): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n this.resultsContainer.innerHTML = `\n <div class=\"search-empty\">\n <svg class=\"search-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"></circle>\n <line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"></line>\n </svg>\n <div class=\"search-empty-title\">No Results Found</div>\n <div class=\"search-empty-description\">\n No results found for \"${escapeHTML(query)}\"\n </div>\n </div>\n `;\n }\n\n private showErrorState(message: string): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n this.resultsContainer.innerHTML = `\n <div class=\"error\">\n <strong>Error:</strong> ${escapeHTML(message)}\n </div>\n `;\n }\n\n private showMissingApiUrlError(): void {\n if (this.resultsContainer) {\n this.showErrorState('The api-url attribute is required. Please provide a valid API URL.');\n }\n }\n\n private updateTheme(theme: string | null): void {\n // CSS custom properties via :host([theme]) selectors handle the actual theming\n // This method is here for any additional theme-related logic if needed\n const validTheme = theme === 'light' || theme === 'dark' || theme === 'auto' ? theme : 'auto';\n\n // Ensure the attribute is set on the host for CSS selectors\n if (validTheme === 'auto') {\n // Let the @media (prefers-color-scheme) handle it\n this.removeAttribute('theme');\n } else {\n this.setAttribute('theme', validTheme);\n }\n }\n\n private cleanup(): void {\n this.clearLoadingInterval();\n\n // Cancel any in-flight search request\n if (this.currentSearchController) {\n this.currentSearchController.abort();\n this.currentSearchController = null;\n }\n\n if (this.client) {\n this.client.cancelAllRequests();\n }\n\n // Remove event listeners\n if (this.inputElement) {\n if (this.handleInputChange) {\n this.inputElement.removeEventListener('input', this.handleInputChange);\n }\n if (this.handleInputKeydownEnter) {\n this.inputElement.removeEventListener('keydown', this.handleInputKeydownEnter);\n }\n if (this.handleInputKeydownEscape) {\n window.removeEventListener('keydown', this.handleInputKeydownEscape);\n }\n }\n\n if (this.searchButton && this.handleSearchButtonClick) {\n this.searchButton.removeEventListener('click', this.handleSearchButtonClick);\n }\n\n // Clear handler references\n this.handleInputChange = null;\n this.handleInputKeydownEnter = null;\n this.handleInputKeydownEscape = null;\n this.handleSearchButtonClick = null;\n }\n\n // Public API\n public async search(query: string): Promise<void> {\n await this.performSearch(query);\n }\n}\n\n// Register the custom element\nif (!customElements.get(COMPONENT_NAME)) {\n customElements.define(COMPONENT_NAME, SearchBarSnippet);\n}\n","/**\n * Modal search combobox specific styles\n */\n\nexport const modalStyles = `\n/* Modal backdrop */\n.modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n z-index: var(--search-snippet-z-modal);\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--search-snippet-transition), visibility var(--search-snippet-transition);\n}\n\n.modal-backdrop.open {\n opacity: 1;\n visibility: visible;\n}\n\n/* Modal container */\n.modal-container {\n position: fixed;\n top: 15%;\n left: 50%;\n transform: translateX(-50%) scale(0.95);\n width: 90%;\n max-width: 600px;\n max-height: 70vh;\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n box-shadow: var(--search-snippet-shadow-lg);\n z-index: calc(var(--search-snippet-z-modal) + 1);\n display: flex;\n flex-direction: column;\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--search-snippet-transition), \n visibility var(--search-snippet-transition),\n transform var(--search-snippet-transition);\n}\n\n.modal-container.open {\n opacity: 1;\n visibility: visible;\n transform: translateX(-50%) scale(1);\n}\n\n/* Modal header with search input */\n.modal-header {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n padding: var(--search-snippet-spacing-md);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n.modal-search-icon {\n width: var(--search-snippet-icon-size);\n height: var(--search-snippet-icon-size);\n color: var(--search-snippet-text-secondary);\n flex-shrink: 0;\n}\n\n.modal-search-input {\n flex: 1;\n border: none;\n outline: none;\n background: transparent;\n color: var(--search-snippet-text-color);\n font-size: var(--search-snippet-font-size-lg);\n font-family: var(--search-snippet-font-family);\n font-weight: var(--search-snippet-font-weight-normal);\n padding: var(--search-snippet-spacing-xs) 0;\n}\n\n.modal-search-input::placeholder {\n color: var(--search-snippet-text-secondary);\n}\n\n.modal-shortcut-hint {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-xs);\n color: var(--search-snippet-text-secondary);\n font-size: var(--search-snippet-font-size-sm);\n flex-shrink: 0;\n}\n\n.modal-kbd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 22px;\n padding: 0 var(--search-snippet-spacing-xs);\n background: var(--search-snippet-surface);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: 4px;\n font-size: var(--search-snippet-font-size-sm);\n font-family: var(--search-snippet-font-family);\n color: var(--search-snippet-text-secondary);\n}\n\n/* Modal content (results area) */\n.modal-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: var(--search-snippet-spacing-sm);\n}\n\n.modal-content::-webkit-scrollbar {\n width: 8px;\n}\n\n.modal-content::-webkit-scrollbar-track {\n background: var(--search-snippet-surface);\n}\n\n.modal-content::-webkit-scrollbar-thumb {\n background: var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n}\n\n.modal-content::-webkit-scrollbar-thumb:hover {\n background: var(--search-snippet-text-secondary);\n}\n\n/* Results list */\n.modal-results {\n display: flex;\n flex-direction: column;\n gap: var(--search-snippet-spacing-xs);\n}\n\na.modal-result-item {\n padding: var(--search-snippet-spacing-md);\n background: transparent;\n border: var(--search-snippet-border-width) solid transparent;\n border-radius: calc(var(--search-snippet-border-radius) - 4px);\n cursor: pointer;\n transition: var(--search-snippet-transition-fast);\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: var(--search-snippet-spacing-md);\n text-decoration: none;\n color: inherit;\n}\n\n/* Image thumbnail container */\n.modal-result-image-container {\n flex-shrink: 0;\n width: 48px;\n height: 48px;\n border-radius: 6px;\n overflow: hidden;\n position: relative;\n}\n\n.modal-result-image {\n width: 100%;\n height: 100%;\n object-fit: contain;\n opacity: 0;\n transition: opacity var(--search-snippet-transition);\n}\n\n.modal-result-image.loaded {\n opacity: 1;\n}\n\n/* Loading shimmer */\n.modal-result-image-loading {\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 90deg,\n var(--search-snippet-surface) 25%,\n var(--search-snippet-border-color) 50%,\n var(--search-snippet-surface) 75%\n );\n background-size: 200% 100%;\n animation: modal-image-shimmer 1.5s infinite;\n}\n\n@keyframes modal-image-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* Placeholder icon */\n.modal-result-image-placeholder {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--search-snippet-text-secondary);\n opacity: 0.5;\n}\n\n.modal-result-image-placeholder svg {\n width: 20px;\n height: 20px;\n}\n\n/* Content wrapper */\n.modal-result-content {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: var(--search-snippet-spacing-xs);\n}\n\na.modal-result-item:hover,\na.modal-result-item.active {\n background: var(--search-snippet-hover-background);\n border-color: var(--search-snippet-border-color);\n}\n\na.modal-result-item.active {\n border-color: var(--search-snippet-primary-color);\n background: var(--search-snippet-focus-ring);\n}\n\na.modal-result-item:focus-visible {\n outline: 2px solid var(--search-snippet-primary-color);\n outline-offset: -2px;\n}\n\n.modal-result-title {\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.modal-result-description {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-description);\n line-height: 1.5;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.modal-result-metadata {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n min-width: 0;\n}\n\n.modal-result-url {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-primary-color);\n text-decoration: none;\n display: block;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.modal-result-url-empty {\n visibility: hidden;\n}\n\n.modal-result-date {\n font-size: 12px;\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-secondary);\n text-align: right;\n flex-shrink: 0;\n}\n\n.modal-result-url:hover {\n text-decoration: underline;\n}\n\n/* Result group header */\n.modal-group-header {\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n font-size: var(--search-snippet-font-size-sm);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n/* Loading state */\n.modal-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--search-snippet-spacing-xxl);\n gap: var(--search-snippet-spacing-md);\n color: var(--search-snippet-text-secondary);\n}\n\n/* Empty state */\n.modal-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--search-snippet-spacing-xxl);\n gap: var(--search-snippet-spacing-md);\n color: var(--search-snippet-text-secondary);\n text-align: center;\n}\n\n.modal-empty-icon {\n width: 48px;\n height: 48px;\n opacity: 0.5;\n}\n\n.modal-empty-title {\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n}\n\n.modal-empty-description {\n font-size: var(--search-snippet-font-size-sm);\n}\n\n/* Footer */\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n border-top: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n background: var(--search-snippet-surface);\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n border-radius: 0 0 var(--search-snippet-border-radius) var(--search-snippet-border-radius);\n}\n\n.modal-footer-hints {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-md);\n}\n\n.modal-footer-hint {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-xs);\n}\n\n.modal-footer-hint .modal-kbd {\n min-width: 20px;\n height: 20px;\n font-size: 11px;\n}\n\n/* Results count */\n.modal-results-count {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n}\n\n/* Powered by in modal footer */\n.modal-footer .powered-by-inline {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n}\n\n.modal-footer .powered-by-inline a {\n color: var(--search-snippet-text-secondary);\n text-decoration: none;\n transition: color var(--search-snippet-transition-fast);\n}\n\n.modal-footer .powered-by-inline a:hover {\n color: var(--search-snippet-primary-color);\n}\n\n/* See more link */\n.modal-see-more {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-xs);\n padding: var(--search-snippet-spacing-md);\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-primary-color);\n text-decoration: none;\n font-weight: var(--search-snippet-font-weight-medium);\n transition: background var(--search-snippet-transition-fast);\n padding-bottom: var(--search-snippet-spacing-xs);\n}\n\n.modal-see-more:hover {\n background: var(--search-snippet-hover);\n text-decoration: underline;\n}\n\n/* Responsive adjustments */\n@media (max-width: 640px) {\n .modal-container {\n top: 10%;\n width: 95%;\n max-height: 80vh;\n }\n\n .modal-footer-hints {\n display: none;\n }\n}\n\n/* Animation for modal open */\n@keyframes modal-slide-in {\n from {\n opacity: 0;\n transform: translateX(-50%) scale(0.95) translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) scale(1) translateY(0);\n }\n}\n\n.modal-container.open {\n animation: modal-slide-in var(--search-snippet-transition) ease-out;\n}\n`;\n","/**\n * Search Modal Snippet\n * A modal combobox search component with keyboard navigation\n * Opens with Cmd/Ctrl+K shortcut by default\n */\n\nimport type { AISearchClient } from '../api/ai-search.ts';\nimport { POWERED_BY_BRANDING } from '../constants.ts';\nimport { modalStyles } from '../styles/modal.ts';\nimport { baseStyles } from '../styles/theme.ts';\nimport type { SearchRequestOptions, SearchResult, SearchSnippetProps } from '../types/index.ts';\nimport {\n createClient,\n createCustomEvent,\n debounce,\n escapeHTML,\n formatDate,\n formatDisplayUrl,\n LOADING_MESSAGE_INTERVAL_MS,\n LOADING_MESSAGES,\n parseAttribute,\n parseBooleanAttribute,\n parseNumberAttribute,\n} from '../utils/index.ts';\n\nconst COMPONENT_NAME = 'search-modal-snippet';\nconst DEFAULT_DISPLAY_RESULTS = 10;\nconst REQUEST_MAX_RESULTS = 100;\n\nexport interface SearchModalProps extends SearchSnippetProps {\n /** Keyboard shortcut key (default: 'k') */\n shortcut?: string;\n /** Whether to use meta key (Cmd on Mac) or ctrl key */\n useMetaKey?: boolean;\n}\n\nexport class SearchModalSnippet extends HTMLElement {\n private shadow: ShadowRoot;\n private client: AISearchClient | null = null;\n private backdrop: HTMLElement | null = null;\n private modal: HTMLElement | null = null;\n private inputElement: HTMLInputElement | null = null;\n private resultsContainer: HTMLElement | null = null;\n private footerCount: HTMLElement | null = null;\n private isOpen = false;\n private results: SearchResult[] = [];\n private activeIndex = -1;\n private debouncedSearch: (((query: string) => void) & { cancel: () => void }) | null = null;\n private currentSearchController: AbortController | null = null;\n private loadingMessageInterval: ReturnType<typeof setInterval> | null = null;\n private loadingMessageIndex = 0;\n\n // Event handler references for cleanup\n private handleGlobalKeydown: ((e: KeyboardEvent) => void) | null = null;\n private handleInputChange: ((e: Event) => void) | null = null;\n private handleInputKeydown: ((e: KeyboardEvent) => void) | null = null;\n private handleBackdropClick: ((e: MouseEvent) => void) | null = null;\n\n // Scroll lock state\n private savedBodyStyles: {\n overflow: string;\n position: string;\n top: string;\n width: string;\n } | null = null;\n private savedHtmlOverflow: string | null = null;\n\n static get observedAttributes() {\n return [\n 'api-url',\n 'placeholder',\n 'max-results',\n 'theme',\n 'shortcut',\n 'use-meta-key',\n 'debounce-ms',\n 'hide-branding',\n 'show-url',\n 'show-date',\n 'hide-thumbnails',\n 'see-more',\n 'request-options',\n ] as const;\n }\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback(): void {\n this.initializeClient();\n this.render();\n this.attachGlobalKeyboardShortcut();\n this.dispatchEvent(createCustomEvent('ready', undefined));\n }\n\n disconnectedCallback(): void {\n this.cleanup();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n if (name === 'api-url') {\n this.initializeClient();\n } else if (name === 'theme') {\n this.updateTheme(newValue);\n }\n }\n\n private getProps(): SearchModalProps {\n return {\n apiUrl: parseAttribute(this.getAttribute('api-url'), ''),\n placeholder: parseAttribute(this.getAttribute('placeholder'), 'Search...'),\n maxResults: parseNumberAttribute(this.getAttribute('max-results'), 10),\n debounceMs: parseNumberAttribute(this.getAttribute('debounce-ms'), 300),\n theme: parseAttribute(this.getAttribute('theme'), 'auto') as 'light' | 'dark' | 'auto',\n shortcut: parseAttribute(this.getAttribute('shortcut'), 'k'),\n useMetaKey: this.getAttribute('use-meta-key') !== 'false',\n hideBranding: parseBooleanAttribute(this.getAttribute('hide-branding'), false),\n showUrl: parseBooleanAttribute(this.getAttribute('show-url'), false),\n showDate: parseBooleanAttribute(this.getAttribute('show-date'), false),\n hideThumbnails: parseBooleanAttribute(this.getAttribute('hide-thumbnails'), false),\n seeMore: parseAttribute(this.getAttribute('see-more'), ''),\n };\n }\n\n private getRequestOptions(): SearchRequestOptions | undefined {\n const rawRequestOptions = this.getAttribute('request-options');\n\n if (!rawRequestOptions) {\n return undefined;\n }\n\n try {\n const parsedRequestOptions = JSON.parse(rawRequestOptions) as unknown;\n\n if (\n parsedRequestOptions === null ||\n typeof parsedRequestOptions !== 'object' ||\n Array.isArray(parsedRequestOptions)\n ) {\n throw new Error('request-options must be a JSON object');\n }\n\n return parsedRequestOptions as SearchRequestOptions;\n } catch (error) {\n console.error('SearchModalSnippet: invalid request-options attribute', error);\n return undefined;\n }\n }\n\n private initializeClient(): void {\n const props = this.getProps();\n\n if (!props.apiUrl) {\n console.error('SearchModalSnippet: api-url attribute is required');\n this.client = null;\n this.showMissingApiUrlError();\n return;\n }\n\n try {\n this.client = createClient(props.apiUrl);\n } catch (error) {\n console.error('SearchModalSnippet:', error);\n }\n }\n\n private render(): void {\n const props = this.getProps();\n\n // Create debounced search function\n const searchFn = (query: string) => this.performSearch(query);\n this.debouncedSearch = debounce(\n searchFn as (...args: unknown[]) => unknown,\n props.debounceMs || 300\n );\n\n const style = document.createElement('style');\n style.textContent = `${baseStyles}\\n${modalStyles}`;\n\n const brandingHTML = props.hideBranding\n ? ''\n : `<div class=\"powered-by-inline\">${POWERED_BY_BRANDING}</div>`;\n\n const container = document.createElement('div');\n container.innerHTML = `\n <div class=\"modal-backdrop\" role=\"presentation\"></div>\n <div class=\"modal-container\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"modal-title\">\n <div class=\"modal-header\">\n <svg class=\"modal-search-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -960 960 960\" fill=\"currentColor\">\n <path d=\"M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z\"/>\n </svg>\n <input\n type=\"text\"\n class=\"modal-search-input\"\n placeholder=\"${escapeHTML(props.placeholder || 'Search...')}\"\n aria-label=\"Search\"\n aria-autocomplete=\"list\"\n aria-controls=\"modal-results-list\"\n aria-expanded=\"false\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n />\n </div>\n <div class=\"modal-content\">\n <div class=\"modal-results\" id=\"modal-results-list\" role=\"listbox\" aria-label=\"Search results\">\n ${this.renderEmptyState()}\n </div>\n </div>\n <div class=\"modal-footer\">\n <div class=\"modal-footer-hints\">\n <div class=\"modal-footer-hint\">\n <kbd class=\"modal-kbd\">↑</kbd>\n <kbd class=\"modal-kbd\">↓</kbd>\n <span>Navigate</span>\n </div>\n <div class=\"modal-footer-hint\">\n <kbd class=\"modal-kbd\">↵</kbd>\n <span>Select</span>\n </div>\n <div class=\"modal-footer-hint\">\n <kbd class=\"modal-kbd\">Esc</kbd>\n <span>Close</span>\n </div>\n </div>\n ${brandingHTML}\n </div>\n </div>\n `;\n\n this.shadow.innerHTML = '';\n this.shadow.appendChild(style);\n this.shadow.appendChild(container);\n\n // Get references to elements\n this.backdrop = this.shadow.querySelector('.modal-backdrop');\n this.modal = this.shadow.querySelector('.modal-container');\n this.inputElement = this.shadow.querySelector('.modal-search-input');\n this.resultsContainer = this.shadow.querySelector('.modal-results');\n this.footerCount = this.shadow.querySelector('.modal-results-count');\n\n this.attachEventListeners();\n\n // Show error immediately if api-url was missing when the component was connected\n if (!this.client) {\n this.showMissingApiUrlError();\n }\n }\n\n private attachGlobalKeyboardShortcut(): void {\n const props = this.getProps();\n const shortcutKey = props.shortcut?.toLowerCase() || 'k';\n\n this.handleGlobalKeydown = (e: KeyboardEvent) => {\n // Check for shortcut to open modal\n const modifierPressed = props.useMetaKey ? e.metaKey || e.ctrlKey : e.ctrlKey;\n\n if (modifierPressed && e.key.toLowerCase() === shortcutKey && !this.isOpen) {\n e.preventDefault();\n this.open();\n }\n };\n\n document.addEventListener('keydown', this.handleGlobalKeydown);\n }\n\n private attachEventListeners(): void {\n if (!this.inputElement || !this.backdrop) return;\n\n // Input change event\n this.handleInputChange = (e: Event) => {\n const target = e.target as HTMLInputElement;\n const query = target.value.trim();\n\n if (query.length > 0 && this.debouncedSearch) {\n this.debouncedSearch(query);\n } else {\n this.debouncedSearch?.cancel();\n this.currentSearchController?.abort();\n this.results = [];\n this.activeIndex = -1;\n this.showEmptyState();\n }\n };\n this.inputElement.addEventListener('input', this.handleInputChange);\n\n // Keyboard navigation\n this.handleInputKeydown = (e: KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n this.navigateResults(1);\n break;\n case 'ArrowUp':\n e.preventDefault();\n this.navigateResults(-1);\n break;\n case 'Enter':\n e.preventDefault();\n this.selectActiveResult();\n break;\n case 'Escape':\n e.preventDefault();\n this.close();\n break;\n }\n };\n this.inputElement.addEventListener('keydown', this.handleInputKeydown);\n\n // Backdrop click to close\n this.handleBackdropClick = (e: MouseEvent) => {\n if (e.target === this.backdrop) {\n this.close();\n }\n };\n this.backdrop.addEventListener('click', this.handleBackdropClick);\n }\n\n private navigateResults(direction: number): void {\n if (this.results.length === 0) return;\n\n const newIndex = this.activeIndex + direction;\n\n if (newIndex < 0) {\n this.activeIndex = this.results.length - 1;\n } else if (newIndex >= this.results.length) {\n this.activeIndex = 0;\n } else {\n this.activeIndex = newIndex;\n }\n\n this.updateActiveResult();\n }\n\n private updateActiveResult(): void {\n const items = this.resultsContainer?.querySelectorAll('.modal-result-item');\n if (!items) return;\n\n items.forEach((item, index) => {\n if (index === this.activeIndex) {\n item.classList.add('active');\n item.setAttribute('aria-selected', 'true');\n // Scroll into view if needed\n (item as HTMLElement).scrollIntoView({ block: 'nearest' });\n } else {\n item.classList.remove('active');\n item.setAttribute('aria-selected', 'false');\n }\n });\n\n // Update aria-activedescendant\n if (this.inputElement && this.activeIndex >= 0) {\n this.inputElement.setAttribute('aria-activedescendant', `result-${this.activeIndex}`);\n } else if (this.inputElement) {\n this.inputElement.removeAttribute('aria-activedescendant');\n }\n }\n\n private selectActiveResult(): void {\n if (this.activeIndex < 0 || this.activeIndex >= this.results.length) {\n // If no result selected but there's a query, perform immediate search\n const query = this.inputElement?.value.trim();\n if (query && query.length > 0) {\n this.performSearch(query);\n }\n return;\n }\n\n const result = this.results[this.activeIndex];\n this.dispatchEvent(\n createCustomEvent('result-select', {\n result,\n index: this.activeIndex,\n })\n );\n\n // Navigate to URL - click the active link element to trigger navigation\n const activeItem = this.resultsContainer?.querySelector(\n `.modal-result-item[data-index=\"${this.activeIndex}\"]`\n ) as HTMLAnchorElement | null;\n\n if (activeItem && result.url) {\n activeItem.click();\n }\n\n this.close();\n }\n\n private async performSearch(query: string): Promise<void> {\n if (!this.client) {\n this.showMissingApiUrlError();\n return;\n }\n\n // Cancel any existing request before starting a new one\n if (this.currentSearchController) {\n this.currentSearchController.abort();\n this.currentSearchController = null;\n }\n\n // Create new controller for this request\n this.currentSearchController = new AbortController();\n this.showLoadingState();\n\n try {\n const results = await this.client.search(query, {\n signal: this.currentSearchController.signal,\n maxResults: REQUEST_MAX_RESULTS,\n request: this.getRequestOptions(),\n });\n const props = this.getProps();\n this.results = results.slice(0, props.maxResults || DEFAULT_DISPLAY_RESULTS);\n this.activeIndex = this.results.length > 0 ? 0 : -1;\n this.displayResults(this.results, query, results.length);\n } catch (error) {\n // Don't show error state for cancelled requests\n if ((error as Error).name === 'AbortError') {\n return;\n }\n this.showErrorState((error as Error).message);\n } finally {\n this.currentSearchController = null;\n }\n }\n\n private displayResults(\n results: SearchResult[],\n query: string,\n totalResults = results.length\n ): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n if (results.length === 0) {\n this.showNoResultsState(query);\n return;\n }\n\n const props = this.getProps();\n const resultsHTML = results.map((result, index) => this.renderResult(result, index)).join('');\n const hasMoreResults = totalResults > results.length;\n const resultsCountLabel = hasMoreResults\n ? `Showing ${results.length} of ${totalResults} results`\n : `${totalResults} result${totalResults === 1 ? '' : 's'}`;\n const seeMoreHTML =\n props.seeMore && hasMoreResults\n ? `<a href=\"${escapeHTML(props.seeMore + encodeURIComponent(query))}\" class=\"modal-see-more\">\n <span>See more results</span>\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14\"/><path d=\"m12 5 7 7-7 7\"/></svg>\n </a>`\n : '';\n\n this.resultsContainer.innerHTML = resultsHTML + seeMoreHTML;\n\n // Update footer count\n if (this.footerCount) {\n this.footerCount.textContent = resultsCountLabel;\n }\n\n // Update aria-expanded\n if (this.inputElement) {\n this.inputElement.setAttribute('aria-expanded', 'true');\n }\n\n // Attach click handlers\n this.attachResultHandlers();\n\n // Update active state\n this.updateActiveResult();\n }\n\n private renderResult(result: SearchResult, index: number): string {\n const props = this.getProps();\n const imageHTML = props.hideThumbnails\n ? ''\n : this.renderResultImage(result.image, result.title);\n const href = result.url ? escapeHTML(result.url) : '#';\n const displayUrl = result.url ? escapeHTML(formatDisplayUrl(result.url)) : '';\n const timestampHTML =\n props.showDate && result.timestamp !== undefined\n ? `<div class=\"modal-result-date\">${escapeHTML(formatDate(result.timestamp))}</div>`\n : '';\n const metadataHTML =\n (props.showUrl && result.url) || timestampHTML\n ? `<div class=\"modal-result-metadata\">\n ${props.showUrl && result.url ? `<span class=\"modal-result-url\">${displayUrl}</span>` : '<span class=\"modal-result-url modal-result-url-empty\"></span>'}\n ${timestampHTML}\n </div>`\n : '';\n\n return `\n <a \n href=\"${href}\"\n class=\"modal-result-item${index === this.activeIndex ? ' active' : ''}\" \n role=\"option\" \n id=\"result-${index}\"\n aria-selected=\"${index === this.activeIndex}\"\n tabindex=\"-1\"\n data-index=\"${index}\"\n data-url=\"${escapeHTML(result.url || '')}\"\n >\n ${imageHTML}\n <div class=\"modal-result-content\">\n <div class=\"modal-result-title\">${escapeHTML(result.title || '')}</div>\n ${result.description ? `<div class=\"modal-result-description\">${escapeHTML(result.description)}</div>` : ''}\n ${metadataHTML}\n </div>\n </a>\n `;\n }\n\n private renderResultImage(imageUrl: string | undefined, alt: string): string {\n const placeholderSVG = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/></svg>`;\n\n if (!imageUrl) {\n return `\n <div class=\"modal-result-image-container\">\n <div class=\"modal-result-image-placeholder\">${placeholderSVG}</div>\n </div>\n `;\n }\n\n return `\n <div class=\"modal-result-image-container\">\n <div class=\"modal-result-image-loading\"></div>\n <div class=\"modal-result-image-placeholder\" style=\"display: none;\">${placeholderSVG}</div>\n <img \n class=\"modal-result-image\" \n src=\"${escapeHTML(imageUrl)}\" \n alt=\"${escapeHTML(alt)}\"\n loading=\"lazy\"\n />\n </div>\n `;\n }\n\n private attachResultHandlers(): void {\n const items = this.resultsContainer?.querySelectorAll('.modal-result-item');\n if (!items) return;\n\n items.forEach((item, index) => {\n // Handle clicks on results without URLs (prevent default anchor behavior)\n const href = item.getAttribute('href');\n if (href === '#') {\n item.addEventListener('click', (e) => {\n e.preventDefault();\n });\n }\n\n item.addEventListener('mouseenter', () => {\n this.activeIndex = index;\n this.updateActiveResult();\n });\n });\n\n // Image load/error handlers\n const images = this.resultsContainer?.querySelectorAll('.modal-result-image');\n images?.forEach((img) => {\n img.addEventListener('load', () => {\n img.classList.add('loaded');\n const container = img.closest('.modal-result-image-container');\n container?.querySelector('.modal-result-image-loading')?.remove();\n });\n\n img.addEventListener('error', () => {\n const container = img.closest('.modal-result-image-container');\n container?.querySelector('.modal-result-image-loading')?.remove();\n const placeholder = container?.querySelector(\n '.modal-result-image-placeholder'\n ) as HTMLElement;\n if (placeholder) placeholder.style.display = 'flex';\n (img as HTMLElement).style.display = 'none';\n });\n });\n }\n\n private renderEmptyState(): string {\n return `\n <div class=\"modal-empty\">\n <svg class=\"modal-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"></circle>\n <path d=\"m21 21-4.35-4.35\"></path>\n </svg>\n <div class=\"modal-empty-description\">Start typing to search</div>\n </div>\n `;\n }\n\n private showEmptyState(): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n this.resultsContainer.innerHTML = this.renderEmptyState();\n\n if (this.footerCount) {\n this.footerCount.textContent = '';\n }\n\n if (this.inputElement) {\n this.inputElement.setAttribute('aria-expanded', 'false');\n }\n }\n\n private showLoadingState(): void {\n if (!this.resultsContainer) return;\n\n this.clearLoadingInterval();\n this.loadingMessageIndex = Math.floor(Math.random() * LOADING_MESSAGES.length);\n\n this.resultsContainer.innerHTML = `\n <div class=\"modal-loading\">\n <div class=\"loading\" aria-label=\"Loading\"></div>\n <div class=\"loading-text loading-text-animate\">${LOADING_MESSAGES[this.loadingMessageIndex]}</div>\n </div>\n `;\n\n if (this.footerCount) {\n this.footerCount.textContent = LOADING_MESSAGES[this.loadingMessageIndex];\n }\n\n this.startLoadingInterval();\n }\n\n private startLoadingInterval(): void {\n this.loadingMessageInterval = setInterval(() => {\n this.loadingMessageIndex = (this.loadingMessageIndex + 1) % LOADING_MESSAGES.length;\n const textEl = this.resultsContainer?.querySelector('.loading-text');\n if (textEl) {\n textEl.classList.remove('loading-text-animate');\n void (textEl as HTMLElement).offsetWidth;\n textEl.textContent = LOADING_MESSAGES[this.loadingMessageIndex];\n textEl.classList.add('loading-text-animate');\n }\n if (this.footerCount) {\n this.footerCount.textContent = LOADING_MESSAGES[this.loadingMessageIndex];\n }\n }, LOADING_MESSAGE_INTERVAL_MS);\n }\n\n private clearLoadingInterval(): void {\n if (this.loadingMessageInterval) {\n clearInterval(this.loadingMessageInterval);\n this.loadingMessageInterval = null;\n }\n }\n\n private showNoResultsState(query: string): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n this.resultsContainer.innerHTML = `\n <div class=\"modal-empty\">\n <svg class=\"modal-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"></circle>\n <line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"></line>\n </svg>\n <div class=\"modal-empty-title\">No results found</div>\n <div class=\"modal-empty-description\">No results for \"${escapeHTML(query)}\"</div>\n </div>\n `;\n\n if (this.footerCount) {\n this.footerCount.textContent = '0 results';\n }\n\n if (this.inputElement) {\n this.inputElement.setAttribute('aria-expanded', 'false');\n }\n }\n\n private showErrorState(message: string): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n this.resultsContainer.innerHTML = `\n <div class=\"error\">\n <strong>Error:</strong> ${escapeHTML(message)}\n </div>\n `;\n\n if (this.footerCount) {\n this.footerCount.textContent = 'Error';\n }\n }\n\n private showMissingApiUrlError(): void {\n if (this.resultsContainer) {\n this.showErrorState('The api-url attribute is required. Please provide a valid API URL.');\n }\n }\n\n private updateTheme(theme: string | null): void {\n const validTheme = theme === 'light' || theme === 'dark' || theme === 'auto' ? theme : 'auto';\n\n if (validTheme === 'auto') {\n this.removeAttribute('theme');\n } else {\n this.setAttribute('theme', validTheme);\n }\n }\n\n private lockBodyScroll(): void {\n // Save current body styles\n const scrollY = window.scrollY;\n this.savedBodyStyles = {\n overflow: document.body.style.overflow,\n position: document.body.style.position,\n top: document.body.style.top,\n width: document.body.style.width,\n };\n this.savedHtmlOverflow = document.documentElement.style.overflow;\n\n // Apply scroll lock styles to both html and body for cross-browser support\n document.documentElement.style.overflow = 'hidden';\n document.body.style.overflow = 'hidden';\n document.body.style.position = 'fixed';\n document.body.style.top = `-${scrollY}px`;\n document.body.style.width = '100%';\n }\n\n private unlockBodyScroll(): void {\n if (!this.savedBodyStyles) return;\n\n // Get the scroll position from the top style\n const scrollY = Math.abs(Number.parseInt(document.body.style.top || '0', 10));\n\n // Restore original styles\n document.documentElement.style.overflow = this.savedHtmlOverflow || '';\n document.body.style.overflow = this.savedBodyStyles.overflow;\n document.body.style.position = this.savedBodyStyles.position;\n document.body.style.top = this.savedBodyStyles.top;\n document.body.style.width = this.savedBodyStyles.width;\n\n // Restore scroll position\n window.scrollTo(0, scrollY);\n\n this.savedBodyStyles = null;\n this.savedHtmlOverflow = null;\n }\n\n private cleanup(): void {\n this.clearLoadingInterval();\n\n // Cancel any in-flight search request\n if (this.currentSearchController) {\n this.currentSearchController.abort();\n this.currentSearchController = null;\n }\n\n // Remove global keyboard listener\n if (this.handleGlobalKeydown) {\n document.removeEventListener('keydown', this.handleGlobalKeydown);\n this.handleGlobalKeydown = null;\n }\n\n // Remove element event listeners\n if (this.inputElement) {\n if (this.handleInputChange) {\n this.inputElement.removeEventListener('input', this.handleInputChange);\n }\n if (this.handleInputKeydown) {\n this.inputElement.removeEventListener('keydown', this.handleInputKeydown);\n }\n }\n\n if (this.backdrop && this.handleBackdropClick) {\n this.backdrop.removeEventListener('click', this.handleBackdropClick);\n }\n\n // Clear handler references\n this.handleInputChange = null;\n this.handleInputKeydown = null;\n this.handleBackdropClick = null;\n\n // Cancel any pending requests\n if (this.client) {\n this.client.cancelAllRequests();\n }\n }\n\n // Public API\n\n /**\n * Open the search modal\n */\n public open(): void {\n if (this.isOpen) return;\n\n this.isOpen = true;\n this.backdrop?.classList.add('open');\n this.modal?.classList.add('open');\n\n // Focus input after animation completes\n // Use double rAF to ensure DOM has updated and transitions have started\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n this.inputElement?.focus();\n });\n });\n\n // Prevent body scroll (save current styles to restore later)\n this.lockBodyScroll();\n\n this.dispatchEvent(createCustomEvent('open', undefined));\n }\n\n /**\n * Close the search modal\n */\n public close(): void {\n if (!this.isOpen) return;\n\n this.isOpen = false;\n this.backdrop?.classList.remove('open');\n this.modal?.classList.remove('open');\n\n // Clear state\n if (this.inputElement) {\n this.inputElement.value = '';\n }\n this.results = [];\n this.activeIndex = -1;\n this.showEmptyState();\n\n // Restore body scroll\n this.unlockBodyScroll();\n\n this.dispatchEvent(createCustomEvent('close', undefined));\n }\n\n /**\n * Toggle the search modal open/closed\n */\n public toggle(): void {\n if (this.isOpen) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /**\n * Perform a search programmatically\n */\n public async search(query: string): Promise<void> {\n if (!this.isOpen) {\n this.open();\n }\n\n if (this.inputElement) {\n this.inputElement.value = query;\n }\n\n await this.performSearch(query);\n }\n\n /**\n * Get current search results\n */\n public getResults(): SearchResult[] {\n return [...this.results];\n }\n\n /**\n * Check if modal is currently open\n */\n public isModalOpen(): boolean {\n return this.isOpen;\n }\n}\n\n// Register the custom element\nif (!customElements.get(COMPONENT_NAME)) {\n customElements.define(COMPONENT_NAME, SearchModalSnippet);\n}\n"],"names":["LOADING_MESSAGES","debounce","func","wait","timeout","executedFunction","args","escapeHTML","text","div","formatDisplayUrl","url","decodeHTMLEntities","formatTimestamp","timestamp","date","diff","minutes","hours","formatDate","generateId","prefix","parseAttribute","value","defaultValue","parseBooleanAttribute","parseNumberAttribute","parsed","createCustomEvent","name","detail","createClient","apiUrl","AISearchClient","isRecord","deepMergeRecords","records","merged","record","key","currentValue","buildRequestUrl","endpoint","queryParams","searchParams","query","hashIndex","path","hash","separator","normalizeHeaders","headers","normalizedHeaders","normalizeBody","body","baseUrl","__publicField","operation","signal","requestOptions","sourceHeader","options","requestId","controller","response","result","chunk","chunks","reader","decoder","done","choice","request","id","POWERED_BY_BRANDING","chatStyles","baseStyles","markdownToHtml","markdown","html","escapeHtml","_","code","lines","processedLines","inList","listType","i","line","headerMatch","level","content","processInlineMarkdown","ulMatch","olMatch","htmlEntities","char","ChatView","container","client","props","e","target","userMessage","assistantMessageId","assistantMessage","stream","fullContent","messageIndex","m","error","message","messageId","isStreaming","messagesHTML","roleClass","avatar","streaming","messages","COMPONENT_NAME","ChatBubbleSnippet","oldValue","newValue","style","bubbleButton","closeButton","minimizeButton","clearButton","chatWindow","chatContent","theme","STORAGE_KEY","ChatPageSnippet","newChatButton","toggleSidebarButton","chatList","lastSession","stored","a","b","sessionIndex","s","session","firstUserMessage","newSession","sessionId","deleteButton","chatItem","isActive","diffMs","diffDays","str","searchStyles","DEFAULT_DISPLAY_RESULTS","REQUEST_MAX_RESULTS","SearchBarSnippet","rawRequestOptions","parsedRequestOptions","searchFn","results","visibleResults","totalResults","brandingHTML","hasMoreResults","resultsCountLabel","seeMoreHTML","resultsHTML","imageHTML","href","displayUrl","timestampHTML","metadataHTML","imageUrl","alt","placeholderSVG","resultItems","item","img","placeholder","textEl","validTheme","modalStyles","SearchModalSnippet","shortcutKey","direction","newIndex","items","index","activeItem","scrollY"],"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,ECAbC,EAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,ECqBrB9D,EAAiB,uBACjBkC,GAA0B,GAC1BC,GAAsB,IASrB,MAAM4B,UAA2B,WAAY,CAiDlD,aAAc,CACZ,MAAA,EAjDM9H,EAAA,eACAA,EAAA,cAAgC,MAChCA,EAAA,gBAA+B,MAC/BA,EAAA,aAA4B,MAC5BA,EAAA,oBAAwC,MACxCA,EAAA,wBAAuC,MACvCA,EAAA,mBAAkC,MAClCA,EAAA,cAAS,IACTA,EAAA,eAA0B,CAAA,GAC1BA,EAAA,mBAAc,IACdA,EAAA,uBAA+E,MAC/EA,EAAA,+BAAkD,MAClDA,EAAA,8BAAgE,MAChEA,EAAA,2BAAsB,GAGtBA,EAAA,2BAA2D,MAC3DA,EAAA,yBAAiD,MACjDA,EAAA,0BAA0D,MAC1DA,EAAA,2BAAwD,MAGxDA,EAAA,uBAKG,MACHA,EAAA,yBAAmC,MAsBzC,KAAK,OAAS,KAAK,aAAa,CAAE,KAAM,OAAQ,CAClD,CArBA,WAAW,oBAAqB,CAC9B,MAAO,CACL,UACA,cACA,cACA,QACA,WACA,eACA,cACA,gBACA,WACA,YACA,kBACA,WACA,iBAAA,CAEJ,CAOA,mBAA0B,CACxB,KAAK,iBAAA,EACL,KAAK,OAAA,EACL,KAAK,6BAAA,EACL,KAAK,cAAc5B,EAAkB,QAAS,MAAS,CAAC,CAC1D,CAEA,sBAA6B,CAC3B,KAAK,QAAA,CACP,CAEA,yBAAyBC,EAAc4F,EAAyBC,EAA+B,CACzFD,IAAaC,IAEb7F,IAAS,UACX,KAAK,iBAAA,EACIA,IAAS,SAClB,KAAK,YAAY6F,CAAQ,EAE7B,CAEQ,UAA6B,CACnC,MAAO,CACL,OAAQpG,EAAe,KAAK,aAAa,SAAS,EAAG,EAAE,EACvD,YAAaA,EAAe,KAAK,aAAa,aAAa,EAAG,WAAW,EACzE,WAAYI,EAAqB,KAAK,aAAa,aAAa,EAAG,EAAE,EACrE,WAAYA,EAAqB,KAAK,aAAa,aAAa,EAAG,GAAG,EACtE,MAAOJ,EAAe,KAAK,aAAa,OAAO,EAAG,MAAM,EACxD,SAAUA,EAAe,KAAK,aAAa,UAAU,EAAG,GAAG,EAC3D,WAAY,KAAK,aAAa,cAAc,IAAM,QAClD,aAAcG,EAAsB,KAAK,aAAa,eAAe,EAAG,EAAK,EAC7E,QAASA,EAAsB,KAAK,aAAa,UAAU,EAAG,EAAK,EACnE,SAAUA,EAAsB,KAAK,aAAa,WAAW,EAAG,EAAK,EACrE,eAAgBA,EAAsB,KAAK,aAAa,iBAAiB,EAAG,EAAK,EACjF,QAASH,EAAe,KAAK,aAAa,UAAU,EAAG,EAAE,CAAA,CAE7D,CAEQ,mBAAsD,CAC5D,MAAMsI,EAAoB,KAAK,aAAa,iBAAiB,EAE7D,GAAKA,EAIL,GAAI,CACF,MAAMC,EAAuB,KAAK,MAAMD,CAAiB,EAEzD,GACEC,IAAyB,MACzB,OAAOA,GAAyB,UAChC,MAAM,QAAQA,CAAoB,EAElC,MAAM,IAAI,MAAM,uCAAuC,EAGzD,OAAOA,CACT,OAAS/C,EAAO,CACd,QAAQ,MAAM,wDAAyDA,CAAK,EAC5E,MACF,CACF,CAEQ,kBAAyB,CAC/B,MAAMV,EAAQ,KAAK,SAAA,EAEnB,GAAI,CAACA,EAAM,OAAQ,CACjB,QAAQ,MAAM,mDAAmD,EACjE,KAAK,OAAS,KACd,KAAK,uBAAA,EACL,MACF,CAEA,GAAI,CACF,KAAK,OAASrE,EAAaqE,EAAM,MAAM,CACzC,OAASU,EAAO,CACd,QAAQ,MAAM,sBAAuBA,CAAK,CAC5C,CACF,CAEQ,QAAe,CACrB,MAAMV,EAAQ,KAAK,SAAA,EAGb0D,EAAYjH,GAAkB,KAAK,cAAcA,CAAK,EAC5D,KAAK,gBAAkB5C,EACrB6J,EACA1D,EAAM,YAAc,GAAA,EAGtB,MAAMuB,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,YAAc,GAAG/C,CAAU;AAAA,EAAKyG,EAAW,GAEjD,MAAMnB,EAAe9D,EAAM,aACvB,GACA,kCAAkC1B,CAAmB,SAEnDwB,EAAY,SAAS,cAAc,KAAK,EAC9CA,EAAU,UAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAUC3F,EAAW6F,EAAM,aAAe,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAWzD,KAAK,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAmBzB8D,CAAY;AAAA;AAAA;AAAA,MAKpB,KAAK,OAAO,UAAY,GACxB,KAAK,OAAO,YAAYvC,CAAK,EAC7B,KAAK,OAAO,YAAYzB,CAAS,EAGjC,KAAK,SAAW,KAAK,OAAO,cAAc,iBAAiB,EAC3D,KAAK,MAAQ,KAAK,OAAO,cAAc,kBAAkB,EACzD,KAAK,aAAe,KAAK,OAAO,cAAc,qBAAqB,EACnE,KAAK,iBAAmB,KAAK,OAAO,cAAc,gBAAgB,EAClE,KAAK,YAAc,KAAK,OAAO,cAAc,sBAAsB,EAEnE,KAAK,qBAAA,EAGA,KAAK,QACR,KAAK,uBAAA,CAET,CAEQ,8BAAqC,CAC3C,MAAME,EAAQ,KAAK,SAAA,EACbmF,EAAcnF,EAAM,UAAU,YAAA,GAAiB,IAErD,KAAK,oBAAuBC,GAAqB,EAEvBD,EAAM,YAAaC,EAAE,SAAWA,EAAE,UAEnCA,EAAE,IAAI,YAAA,IAAkBkF,GAAe,CAAC,KAAK,SAClElF,EAAE,eAAA,EACF,KAAK,KAAA,EAET,EAEA,SAAS,iBAAiB,UAAW,KAAK,mBAAmB,CAC/D,CAEQ,sBAA6B,CAC/B,CAAC,KAAK,cAAgB,CAAC,KAAK,WAGhC,KAAK,kBAAqB,GAAa,CAErC,MAAMxD,EADS,EAAE,OACI,MAAM,KAAA,EAEvBA,EAAM,OAAS,GAAK,KAAK,gBAC3B,KAAK,gBAAgBA,CAAK,GAE1B,KAAK,iBAAiB,OAAA,EACtB,KAAK,yBAAyB,MAAA,EAC9B,KAAK,QAAU,CAAA,EACf,KAAK,YAAc,GACnB,KAAK,eAAA,EAET,EACA,KAAK,aAAa,iBAAiB,QAAS,KAAK,iBAAiB,EAGlE,KAAK,mBAAsB,GAAqB,CAC9C,OAAQ,EAAE,IAAA,CACR,IAAK,YACH,EAAE,eAAA,EACF,KAAK,gBAAgB,CAAC,EACtB,MACF,IAAK,UACH,EAAE,eAAA,EACF,KAAK,gBAAgB,EAAE,EACvB,MACF,IAAK,QACH,EAAE,eAAA,EACF,KAAK,mBAAA,EACL,MACF,IAAK,SACH,EAAE,eAAA,EACF,KAAK,MAAA,EACL,KAAA,CAEN,EACA,KAAK,aAAa,iBAAiB,UAAW,KAAK,kBAAkB,EAGrE,KAAK,oBAAuB,GAAkB,CACxC,EAAE,SAAW,KAAK,UACpB,KAAK,MAAA,CAET,EACA,KAAK,SAAS,iBAAiB,QAAS,KAAK,mBAAmB,EAClE,CAEQ,gBAAgB2I,EAAyB,CAC/C,GAAI,KAAK,QAAQ,SAAW,EAAG,OAE/B,MAAMC,EAAW,KAAK,YAAcD,EAEhCC,EAAW,EACb,KAAK,YAAc,KAAK,QAAQ,OAAS,EAChCA,GAAY,KAAK,QAAQ,OAClC,KAAK,YAAc,EAEnB,KAAK,YAAcA,EAGrB,KAAK,mBAAA,CACP,CAEQ,oBAA2B,CACjC,MAAMC,EAAQ,KAAK,kBAAkB,iBAAiB,oBAAoB,EACrEA,IAELA,EAAM,QAAQ,CAACV,EAAMW,IAAU,CACzBA,IAAU,KAAK,aACjBX,EAAK,UAAU,IAAI,QAAQ,EAC3BA,EAAK,aAAa,gBAAiB,MAAM,EAExCA,EAAqB,eAAe,CAAE,MAAO,UAAW,IAEzDA,EAAK,UAAU,OAAO,QAAQ,EAC9BA,EAAK,aAAa,gBAAiB,OAAO,EAE9C,CAAC,EAGG,KAAK,cAAgB,KAAK,aAAe,EAC3C,KAAK,aAAa,aAAa,wBAAyB,UAAU,KAAK,WAAW,EAAE,EAC3E,KAAK,cACd,KAAK,aAAa,gBAAgB,uBAAuB,EAE7D,CAEQ,oBAA2B,CACjC,GAAI,KAAK,YAAc,GAAK,KAAK,aAAe,KAAK,QAAQ,OAAQ,CAEnE,MAAMnI,EAAQ,KAAK,cAAc,MAAM,KAAA,EACnCA,GAASA,EAAM,OAAS,GAC1B,KAAK,cAAcA,CAAK,EAE1B,MACF,CAEA,MAAMoB,EAAS,KAAK,QAAQ,KAAK,WAAW,EAC5C,KAAK,cACHrC,EAAkB,gBAAiB,CACjC,OAAAqC,EACA,MAAO,KAAK,WAAA,CACb,CAAA,EAIH,MAAM2H,EAAa,KAAK,kBAAkB,cACxC,kCAAkC,KAAK,WAAW,IAAA,EAGhDA,GAAc3H,EAAO,KACvB2H,EAAW,MAAA,EAGb,KAAK,MAAA,CACP,CAEA,MAAc,cAAc/I,EAA8B,CACxD,GAAI,CAAC,KAAK,OAAQ,CAChB,KAAK,uBAAA,EACL,MACF,CAGI,KAAK,0BACP,KAAK,wBAAwB,MAAA,EAC7B,KAAK,wBAA0B,MAIjC,KAAK,wBAA0B,IAAI,gBACnC,KAAK,iBAAA,EAEL,GAAI,CACF,MAAMkH,EAAU,MAAM,KAAK,OAAO,OAAOlH,EAAO,CAC9C,OAAQ,KAAK,wBAAwB,OACrC,WAAY6G,GACZ,QAAS,KAAK,kBAAA,CAAkB,CACjC,EACKtD,EAAQ,KAAK,SAAA,EACnB,KAAK,QAAU2D,EAAQ,MAAM,EAAG3D,EAAM,YAAcqD,EAAuB,EAC3E,KAAK,YAAc,KAAK,QAAQ,OAAS,EAAI,EAAI,GACjD,KAAK,eAAe,KAAK,QAAS5G,EAAOkH,EAAQ,MAAM,CACzD,OAASjD,EAAO,CAEd,GAAKA,EAAgB,OAAS,aAC5B,OAEF,KAAK,eAAgBA,EAAgB,OAAO,CAC9C,QAAA,CACE,KAAK,wBAA0B,IACjC,CACF,CAEQ,eACNiD,EACAlH,EACAoH,EAAeF,EAAQ,OACjB,CAEN,GADA,KAAK,qBAAA,EACD,CAAC,KAAK,iBAAkB,OAE5B,GAAIA,EAAQ,SAAW,EAAG,CACxB,KAAK,mBAAmBlH,CAAK,EAC7B,MACF,CAEA,MAAMuD,EAAQ,KAAK,SAAA,EACbkE,EAAcP,EAAQ,IAAI,CAAC9F,EAAQ0H,IAAU,KAAK,aAAa1H,EAAQ0H,CAAK,CAAC,EAAE,KAAK,EAAE,EACtFxB,EAAiBF,EAAeF,EAAQ,OACxCK,EAAoBD,EACtB,WAAWJ,EAAQ,MAAM,OAAOE,CAAY,WAC5C,GAAGA,CAAY,UAAUA,IAAiB,EAAI,GAAK,GAAG,GACpDI,EACJjE,EAAM,SAAW+D,EACb,YAAY5J,EAAW6F,EAAM,QAAU,mBAAmBvD,CAAK,CAAC,CAAC;AAAA;AAAA;AAAA,gBAIjE,GAEN,KAAK,iBAAiB,UAAYyH,EAAcD,EAG5C,KAAK,cACP,KAAK,YAAY,YAAcD,GAI7B,KAAK,cACP,KAAK,aAAa,aAAa,gBAAiB,MAAM,EAIxD,KAAK,qBAAA,EAGL,KAAK,mBAAA,CACP,CAEQ,aAAanG,EAAsB0H,EAAuB,CAChE,MAAMvF,EAAQ,KAAK,SAAA,EACbmE,EAAYnE,EAAM,eACpB,GACA,KAAK,kBAAkBnC,EAAO,MAAOA,EAAO,KAAK,EAC/CuG,EAAOvG,EAAO,IAAM1D,EAAW0D,EAAO,GAAG,EAAI,IAC7CwG,EAAaxG,EAAO,IAAM1D,EAAWG,EAAiBuD,EAAO,GAAG,CAAC,EAAI,GACrEyG,EACJtE,EAAM,UAAYnC,EAAO,YAAc,OACnC,kCAAkC1D,EAAWY,EAAW8C,EAAO,SAAS,CAAC,CAAC,SAC1E,GACA0G,EACHvE,EAAM,SAAWnC,EAAO,KAAQyG,EAC7B;AAAA,cACItE,EAAM,SAAWnC,EAAO,IAAM,kCAAkCwG,CAAU,UAAY,+DAA+D;AAAA,cACrJC,CAAa;AAAA,kBAEjB,GAEN,MAAO;AAAA;AAAA,gBAEKF,CAAI;AAAA,kCACcmB,IAAU,KAAK,YAAc,UAAY,EAAE;AAAA;AAAA,qBAExDA,CAAK;AAAA,yBACDA,IAAU,KAAK,WAAW;AAAA;AAAA,sBAE7BA,CAAK;AAAA,oBACPpL,EAAW0D,EAAO,KAAO,EAAE,CAAC;AAAA;AAAA,UAEtCsG,CAAS;AAAA;AAAA,4CAEyBhK,EAAW0D,EAAO,OAAS,EAAE,CAAC;AAAA,YAC9DA,EAAO,YAAc,yCAAyC1D,EAAW0D,EAAO,WAAW,CAAC,SAAW,EAAE;AAAA,YACzG0G,CAAY;AAAA;AAAA;AAAA,KAItB,CAEQ,kBAAkBC,EAA8BC,EAAqB,CAC3E,MAAMC,EAAiB,6SAEvB,OAAKF,EAQE;AAAA;AAAA;AAAA,6EAGkEE,CAAc;AAAA;AAAA;AAAA,iBAG1EvK,EAAWqK,CAAQ,CAAC;AAAA,iBACpBrK,EAAWsK,CAAG,CAAC;AAAA;AAAA;AAAA;AAAA,MAdnB;AAAA;AAAA,wDAE2CC,CAAc;AAAA;AAAA,OAiBpE,CAEQ,sBAA6B,CACnC,MAAMY,EAAQ,KAAK,kBAAkB,iBAAiB,oBAAoB,EAC1E,GAAI,CAACA,EAAO,OAEZA,EAAM,QAAQ,CAACV,EAAMW,IAAU,CAEhBX,EAAK,aAAa,MAAM,IACxB,KACXA,EAAK,iBAAiB,QAAU3E,GAAM,CACpCA,EAAE,eAAA,CACJ,CAAC,EAGH2E,EAAK,iBAAiB,aAAc,IAAM,CACxC,KAAK,YAAcW,EACnB,KAAK,mBAAA,CACP,CAAC,CACH,CAAC,EAGc,KAAK,kBAAkB,iBAAiB,qBAAqB,GACpE,QAASV,GAAQ,CACvBA,EAAI,iBAAiB,OAAQ,IAAM,CACjCA,EAAI,UAAU,IAAI,QAAQ,EACRA,EAAI,QAAQ,+BAA+B,GAClD,cAAc,6BAA6B,GAAG,OAAA,CAC3D,CAAC,EAEDA,EAAI,iBAAiB,QAAS,IAAM,CAClC,MAAM/E,EAAY+E,EAAI,QAAQ,+BAA+B,EAC7D/E,GAAW,cAAc,6BAA6B,GAAG,OAAA,EACzD,MAAMgF,EAAchF,GAAW,cAC7B,iCAAA,EAEEgF,IAAaA,EAAY,MAAM,QAAU,QAC5CD,EAAoB,MAAM,QAAU,MACvC,CAAC,CACH,CAAC,CACH,CAEQ,kBAA2B,CACjC,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAST,CAEQ,gBAAuB,CAC7B,KAAK,qBAAA,EACA,KAAK,mBACV,KAAK,iBAAiB,UAAY,KAAK,iBAAA,EAEnC,KAAK,cACP,KAAK,YAAY,YAAc,IAG7B,KAAK,cACP,KAAK,aAAa,aAAa,gBAAiB,OAAO,EAE3D,CAEQ,kBAAyB,CAC1B,KAAK,mBAEV,KAAK,qBAAA,EACL,KAAK,oBAAsB,KAAK,MAAM,KAAK,OAAA,EAAWjL,EAAiB,MAAM,EAE7E,KAAK,iBAAiB,UAAY;AAAA;AAAA;AAAA,yDAGmBA,EAAiB,KAAK,mBAAmB,CAAC;AAAA;AAAA,MAI3F,KAAK,cACP,KAAK,YAAY,YAAcA,EAAiB,KAAK,mBAAmB,GAG1E,KAAK,qBAAA,EACP,CAEQ,sBAA6B,CACnC,KAAK,uBAAyB,YAAY,IAAM,CAC9C,KAAK,qBAAuB,KAAK,oBAAsB,GAAKA,EAAiB,OAC7E,MAAMmL,EAAS,KAAK,kBAAkB,cAAc,eAAe,EAC/DA,IACFA,EAAO,UAAU,OAAO,sBAAsB,EACxCA,EAAuB,YAC7BA,EAAO,YAAcnL,EAAiB,KAAK,mBAAmB,EAC9DmL,EAAO,UAAU,IAAI,sBAAsB,GAEzC,KAAK,cACP,KAAK,YAAY,YAAcnL,EAAiB,KAAK,mBAAmB,EAE5E,EAAG,IAA2B,CAChC,CAEQ,sBAA6B,CAC/B,KAAK,yBACP,cAAc,KAAK,sBAAsB,EACzC,KAAK,uBAAyB,KAElC,CAEQ,mBAAmB6C,EAAqB,CAC9C,KAAK,qBAAA,EACA,KAAK,mBAEV,KAAK,iBAAiB,UAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+DAOyBtC,EAAWsC,CAAK,CAAC;AAAA;AAAA,MAIxE,KAAK,cACP,KAAK,YAAY,YAAc,aAG7B,KAAK,cACP,KAAK,aAAa,aAAa,gBAAiB,OAAO,EAE3D,CAEQ,eAAekE,EAAuB,CAC5C,KAAK,qBAAA,EACA,KAAK,mBAEV,KAAK,iBAAiB,UAAY;AAAA;AAAA,kCAEJxG,EAAWwG,CAAO,CAAC;AAAA;AAAA,MAI7C,KAAK,cACP,KAAK,YAAY,YAAc,SAEnC,CAEQ,wBAA+B,CACjC,KAAK,kBACP,KAAK,eAAe,oEAAoE,CAE5F,CAEQ,YAAYmB,EAA4B,CAC9C,MAAMkD,EAAalD,IAAU,SAAWA,IAAU,QAAUA,IAAU,OAASA,EAAQ,OAEnFkD,IAAe,OACjB,KAAK,gBAAgB,OAAO,EAE5B,KAAK,aAAa,QAASA,CAAU,CAEzC,CAEQ,gBAAuB,CAE7B,MAAMS,EAAU,OAAO,QACvB,KAAK,gBAAkB,CACrB,SAAU,SAAS,KAAK,MAAM,SAC9B,SAAU,SAAS,KAAK,MAAM,SAC9B,IAAK,SAAS,KAAK,MAAM,IACzB,MAAO,SAAS,KAAK,MAAM,KAAA,EAE7B,KAAK,kBAAoB,SAAS,gBAAgB,MAAM,SAGxD,SAAS,gBAAgB,MAAM,SAAW,SAC1C,SAAS,KAAK,MAAM,SAAW,SAC/B,SAAS,KAAK,MAAM,SAAW,QAC/B,SAAS,KAAK,MAAM,IAAM,IAAIA,CAAO,KACrC,SAAS,KAAK,MAAM,MAAQ,MAC9B,CAEQ,kBAAyB,CAC/B,GAAI,CAAC,KAAK,gBAAiB,OAG3B,MAAMA,EAAU,KAAK,IAAI,OAAO,SAAS,SAAS,KAAK,MAAM,KAAO,IAAK,EAAE,CAAC,EAG5E,SAAS,gBAAgB,MAAM,SAAW,KAAK,mBAAqB,GACpE,SAAS,KAAK,MAAM,SAAW,KAAK,gBAAgB,SACpD,SAAS,KAAK,MAAM,SAAW,KAAK,gBAAgB,SACpD,SAAS,KAAK,MAAM,IAAM,KAAK,gBAAgB,IAC/C,SAAS,KAAK,MAAM,MAAQ,KAAK,gBAAgB,MAGjD,OAAO,SAAS,EAAGA,CAAO,EAE1B,KAAK,gBAAkB,KACvB,KAAK,kBAAoB,IAC3B,CAEQ,SAAgB,CACtB,KAAK,qBAAA,EAGD,KAAK,0BACP,KAAK,wBAAwB,MAAA,EAC7B,KAAK,wBAA0B,MAI7B,KAAK,sBACP,SAAS,oBAAoB,UAAW,KAAK,mBAAmB,EAChE,KAAK,oBAAsB,MAIzB,KAAK,eACH,KAAK,mBACP,KAAK,aAAa,oBAAoB,QAAS,KAAK,iBAAiB,EAEnE,KAAK,oBACP,KAAK,aAAa,oBAAoB,UAAW,KAAK,kBAAkB,GAIxE,KAAK,UAAY,KAAK,qBACxB,KAAK,SAAS,oBAAoB,QAAS,KAAK,mBAAmB,EAIrE,KAAK,kBAAoB,KACzB,KAAK,mBAAqB,KAC1B,KAAK,oBAAsB,KAGvB,KAAK,QACP,KAAK,OAAO,kBAAA,CAEhB,CAOO,MAAa,CACd,KAAK,SAET,KAAK,OAAS,GACd,KAAK,UAAU,UAAU,IAAI,MAAM,EACnC,KAAK,OAAO,UAAU,IAAI,MAAM,EAIhC,sBAAsB,IAAM,CAC1B,sBAAsB,IAAM,CAC1B,KAAK,cAAc,MAAA,CACrB,CAAC,CACH,CAAC,EAGD,KAAK,eAAA,EAEL,KAAK,cAAcjK,EAAkB,OAAQ,MAAS,CAAC,EACzD,CAKO,OAAc,CACd,KAAK,SAEV,KAAK,OAAS,GACd,KAAK,UAAU,UAAU,OAAO,MAAM,EACtC,KAAK,OAAO,UAAU,OAAO,MAAM,EAG/B,KAAK,eACP,KAAK,aAAa,MAAQ,IAE5B,KAAK,QAAU,CAAA,EACf,KAAK,YAAc,GACnB,KAAK,eAAA,EAGL,KAAK,iBAAA,EAEL,KAAK,cAAcA,EAAkB,QAAS,MAAS,CAAC,EAC1D,CAKO,QAAe,CAChB,KAAK,OACP,KAAK,MAAA,EAEL,KAAK,KAAA,CAET,CAKA,MAAa,OAAOiB,EAA8B,CAC3C,KAAK,QACR,KAAK,KAAA,EAGH,KAAK,eACP,KAAK,aAAa,MAAQA,GAG5B,MAAM,KAAK,cAAcA,CAAK,CAChC,CAKO,YAA6B,CAClC,MAAO,CAAC,GAAG,KAAK,OAAO,CACzB,CAKO,aAAuB,CAC5B,OAAO,KAAK,MACd,CACF,CAGK,eAAe,IAAI0E,CAAc,GACpC,eAAe,OAAOA,EAAgB+D,CAAkB"}
1
+ {"version":3,"file":"search-snippet.umd.js","sources":["../src/utils/loading-messages.ts","../src/utils/index.ts","../src/api/ai-search.ts","../src/constants.ts","../src/styles/chat.ts","../src/styles/theme.ts","../src/utils/markdown.ts","../src/components/chat-view.ts","../src/components/chat-bubble-snippet.ts","../src/components/chat-page-snippet.ts","../src/styles/search.ts","../src/components/search-bar-snippet.ts","../src/styles/modal.ts","../src/components/search-modal-snippet.ts"],"sourcesContent":["/**\n * Loading messages that cycle during search/streaming operations\n */\n\nexport const LOADING_MESSAGES = [\n 'Searching...',\n 'Digging through results...',\n 'Scanning the knowledge base...',\n 'Finding the best matches...',\n 'Sifting through the data...',\n 'Almost there...',\n 'Looking far and wide...',\n 'Connecting the dots...',\n 'Rummaging through pages...',\n 'Hunting down answers...',\n];\n\n/** Interval in ms between loading message changes */\nexport const LOADING_MESSAGE_INTERVAL_MS = 2500;\n","/**\n * Utility functions for the Search Snippet Library\n */\n\nimport { AISearchClient } from '../api/ai-search.ts';\n\nexport { LOADING_MESSAGE_INTERVAL_MS, LOADING_MESSAGES } from './loading-messages.ts';\n\n/**\n * Debounce function to limit API calls\n */\nexport type DebouncedFn<T extends (...args: unknown[]) => unknown> = ((\n ...args: Parameters<T>\n) => void) & { cancel: () => void };\n\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n func: T,\n wait: number\n): DebouncedFn<T> {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n\n function executedFunction(...args: Parameters<T>) {\n clearTimeout(timeout);\n timeout = setTimeout(() => {\n func(...args);\n }, wait);\n }\n\n executedFunction.cancel = () => clearTimeout(timeout);\n\n return executedFunction;\n}\n\n/**\n * Sanitize HTML to prevent XSS attacks\n */\nexport function sanitizeHTML(html: string): string {\n const div = document.createElement('div');\n div.textContent = html;\n return div.innerHTML;\n}\n\n/**\n * Escape HTML entities\n */\nexport function escapeHTML(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\n/**\n * Decode percent-encoded URLs for display\n */\nexport function formatDisplayUrl(url: string): string {\n try {\n return decodeURI(url);\n } catch {\n return url;\n }\n}\n\n/**\n * Decode HTML entities (e.g., &#38; -> &, &amp; -> &)\n */\nexport function decodeHTMLEntities(text: string): string {\n const doc = new DOMParser().parseFromString(text, 'text/html');\n return doc.documentElement.textContent || '';\n}\n\n/**\n * Format timestamp to readable date\n */\nexport function formatTimestamp(timestamp: number): string {\n const date = new Date(timestamp);\n const now = new Date();\n const diff = now.getTime() - date.getTime();\n\n // Less than a minute\n if (diff < 60000) {\n return 'Just now';\n }\n\n // Less than an hour\n if (diff < 3600000) {\n const minutes = Math.floor(diff / 60000);\n return `${minutes} ${minutes === 1 ? 'minute' : 'minutes'} ago`;\n }\n\n // Less than a day\n if (diff < 86400000) {\n const hours = Math.floor(diff / 3600000);\n return `${hours} ${hours === 1 ? 'hour' : 'hours'} ago`;\n }\n\n // Format as date\n return date.toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport function formatDate(timestamp: number): string {\n return new Date(timestamp).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n });\n}\n\n/**\n * Generate unique ID\n */\nexport function generateId(prefix = 'id'): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n}\n\n/**\n * Parse attributes from element\n */\nexport function parseAttribute(value: string | null, defaultValue: string): string {\n return value !== null ? value : defaultValue;\n}\n\nexport function parseBooleanAttribute(value: string | null, defaultValue: boolean): boolean {\n if (value === null) return defaultValue;\n return value === 'true' || value === '';\n}\n\nexport function parseNumberAttribute(value: string | null, defaultValue: number): number {\n if (value === null) return defaultValue;\n const parsed = Number.parseInt(value, 10);\n return Number.isNaN(parsed) ? defaultValue : parsed;\n}\n\n/**\n * Create custom event\n */\nexport function createCustomEvent<T>(name: string, detail: T): CustomEvent<T> {\n return new CustomEvent(name, {\n detail,\n bubbles: true,\n composed: true,\n cancelable: true,\n });\n}\n\n/**\n * Create API client\n */\nexport function createClient(apiUrl: string): AISearchClient {\n if (!apiUrl) {\n throw new Error('API URL is required');\n }\n\n return new AISearchClient(apiUrl);\n}\n","/**\n * NLWeb API Client\n * Handles all API communication with retry logic, streaming support, and request cancellation\n */\n\nimport type {\n AISearchAPIResponse,\n ChatOptions,\n ChatTextResponse,\n ChatTypes,\n RequestState,\n SearchError,\n SearchOptions,\n SearchRequestOptions,\n SearchResult,\n} from '../types/index.ts';\nimport { decodeHTMLEntities } from '../utils/index.ts';\n\ntype RequestOperation = 'ai-search' | 'search' | 'chat/completions';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction deepMergeRecords(\n ...records: Array<Record<string, unknown> | undefined>\n): Record<string, unknown> {\n const merged: Record<string, unknown> = {};\n\n for (const record of records) {\n if (!record) {\n continue;\n }\n\n for (const [key, value] of Object.entries(record)) {\n const currentValue = merged[key];\n\n if (isRecord(currentValue) && isRecord(value)) {\n merged[key] = deepMergeRecords(currentValue, value);\n } else {\n merged[key] = value;\n }\n }\n }\n\n return merged;\n}\n\nfunction buildRequestUrl(\n endpoint: string,\n queryParams: SearchRequestOptions['queryParams'] | undefined\n): string {\n if (!isRecord(queryParams)) {\n return endpoint;\n }\n\n const searchParams = new URLSearchParams();\n\n for (const [key, value] of Object.entries(queryParams)) {\n if (value === undefined || value === null) {\n continue;\n }\n\n searchParams.append(key, String(value));\n }\n\n const query = searchParams.toString();\n\n if (!query) {\n return endpoint;\n }\n\n const hashIndex = endpoint.indexOf('#');\n const path = hashIndex === -1 ? endpoint : endpoint.slice(0, hashIndex);\n const hash = hashIndex === -1 ? '' : endpoint.slice(hashIndex);\n const separator = path.includes('?') ? '&' : '?';\n\n return `${path}${separator}${query}${hash}`;\n}\n\nfunction normalizeHeaders(\n headers: SearchRequestOptions['headers'] | undefined\n): Record<string, string> {\n if (!isRecord(headers)) {\n return {};\n }\n\n const normalizedHeaders: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined || value === null) {\n continue;\n }\n\n normalizedHeaders[key] = String(value);\n }\n\n return normalizedHeaders;\n}\n\nfunction normalizeBody(\n body: SearchRequestOptions['body'] | undefined\n): Record<string, unknown> | undefined {\n return isRecord(body) ? body : undefined;\n}\n\nexport class AISearchClient {\n activeRequests: Map<string, RequestState> = new Map();\n baseUrl: string;\n\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n }\n\n private request(\n body: Record<string, unknown>,\n operation: RequestOperation,\n signal?: AbortSignal,\n requestOptions?: SearchRequestOptions\n ): Promise<Response> {\n const sourceHeader = operation === 'search' ? 'snippet-search' : 'snippet-chat-completions';\n const url = buildRequestUrl(`${this.baseUrl}/${operation}`, requestOptions?.queryParams);\n\n return fetch(url, {\n method: 'POST',\n body: JSON.stringify(deepMergeRecords(normalizeBody(requestOptions?.body), body)),\n headers: {\n ...normalizeHeaders(requestOptions?.headers),\n 'Content-Type': 'application/json',\n 'cf-ai-search-source': sourceHeader,\n },\n signal,\n });\n }\n\n /**\n * Performs a search query with optional streaming\n */\n async search(query: string, options: Omit<SearchOptions, 'query'> = {}): Promise<SearchResult[]> {\n const requestId = this.generateRequestId();\n const controller = new AbortController();\n const signal = options.signal || controller.signal;\n\n this.registerRequest(requestId, controller);\n\n try {\n const response = await this.request(\n {\n messages: [{ role: 'user', content: query }],\n stream: false,\n ai_search_options: {\n retrieval: {\n metadata_only: true,\n max_num_results: options.maxResults ?? 30,\n },\n },\n },\n 'search',\n signal,\n options.request\n );\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error('Response body is empty');\n }\n const result: AISearchAPIResponse = await response.json();\n if (result.success && result.result) {\n return result.result.chunks.map(\n (chunk) =>\n ({\n type: 'result',\n id: chunk.id,\n title: decodeHTMLEntities(chunk.item.metadata?.title),\n description: chunk.item.metadata?.description\n ? decodeHTMLEntities(chunk.item.metadata?.description)\n : '',\n timestamp: chunk.item.timestamp ?? undefined,\n url: chunk.item.key,\n image: chunk.item.metadata?.image || undefined,\n metadata: chunk.item.metadata as unknown as Record<string, unknown>,\n }) satisfies SearchResult\n );\n }\n\n if (result.success === false) {\n // @ts-expect-error need to check this\n throw new Error(result.error);\n }\n throw new Error('Unknown error');\n } finally {\n this.unregisterRequest(requestId);\n }\n }\n\n async *searchStream(\n query: string,\n options: Omit<SearchOptions, 'query'> = {}\n ): AsyncGenerator<SearchResult | SearchError, void, undefined> {\n const requestId = this.generateRequestId();\n const controller = new AbortController();\n const signal = options.signal || controller.signal;\n\n this.registerRequest(requestId, controller);\n\n const response = await this.request(\n {\n messages: [{ role: 'user', content: query }],\n stream: true,\n ...(options.maxResults !== undefined && {\n max_num_results: options.maxResults,\n }),\n },\n 'ai-search',\n signal,\n options.request\n );\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n if (!response.body) {\n throw new Error('Response body is empty');\n }\n\n let chunks = '';\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n const chunk = decoder.decode(value, { stream: true });\n chunks += chunk;\n }\n\n const result: string = chunks\n .replaceAll('data: ', '')\n .trim()\n .split('\\n\\n')\n .map((chunk) => {\n return JSON.parse(chunk) as { response: string };\n })\n .map((chunk) => chunk.response)\n .join('');\n\n yield {\n type: 'result',\n id: '',\n title: '',\n description: result,\n url: '',\n metadata: {},\n };\n }\n\n async *chat(query: string, options?: ChatOptions): AsyncGenerator<ChatTypes, void, undefined> {\n const controller = new AbortController();\n const signal = options?.signal || controller.signal;\n // const prevQueries: string[] = JSON.parse(localStorage.getItem('prevQueries') || '[]');\n // prevQueries.push(query);\n // localStorage.setItem('prevQueries', JSON.stringify(prevQueries));\n const response = await this.request(\n {\n messages: [{ role: 'user', content: query }],\n stream: false,\n },\n 'chat/completions',\n signal\n );\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n if (!response.body) {\n throw new Error('Response body is empty');\n }\n const result = (await response.json()) as {\n choices: {\n message: {\n content: string;\n };\n }[];\n };\n\n yield {\n type: 'text',\n message: result.choices.map((choice) => choice.message.content).join(''),\n } satisfies ChatTextResponse;\n\n // for (const item of result.data) {\n // yield {\n // type: 'result',\n // id: item.filename,\n // title: item.filename,\n // description: item.content.text,\n // url: item.filename,\n // metadata: item.attributes\n // ,\n // } satisfies ChatResult;\n // }\n\n return;\n }\n\n /**\n * Cancels an active request by ID\n */\n cancelRequest(requestId: string): void {\n const request = this.activeRequests.get(requestId);\n if (request) {\n request.controller.abort();\n this.unregisterRequest(requestId);\n }\n }\n\n /**\n * Cancels all active requests\n */\n cancelAllRequests(): void {\n for (const [requestId] of this.activeRequests) {\n this.cancelRequest(requestId);\n }\n }\n\n /**\n * Register an active request\n */\n private registerRequest(id: string, controller: AbortController): void {\n this.activeRequests.set(id, {\n id,\n controller,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Unregister a completed request\n */\n private unregisterRequest(id: string): void {\n this.activeRequests.delete(id);\n }\n\n /**\n * Generate unique request ID\n */\n private generateRequestId(): string {\n return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n}\n","/**\n * Cloudflare AI Search branding constants\n */\n\nexport const CLOUDFLARE_LOGO_SVG = `<svg width=\"32\" height=\"10\" viewBox=\"0 0 412 186\" xmlns=\"http://www.w3.org/2000/svg\" aria-label=\"Cloudflare\" role=\"img\">\n <path fill=\"#f38020\" d=\"m280.8395,183.31456c11,-26 -4,-38 -19,-38l-148,-2c-4,0 -4,-6 1,-7l150,-2c17,-1 37,-15 43,-33c0,0 10,-21 9,-24a97,97 0 0 0 -187,-11c-38,-25 -78,9 -69,46c-48,3 -65,46 -60,72c0,1 1,2 3,2l274,0c1,0 3,-1 3,-3z\"/>\n <path fill=\"#faae40\" d=\"m330.8395,81.31456c-4,0 -6,-1 -7,1l-5,21c-5,16 3,30 20,31l32,2c4,0 4,6 -1,7l-33,1c-36,4 -46,39 -46,39c0,2 0,3 2,3l113,0l3,-2a81,81 0 0 0 -78,-103\"/>\n</svg>`;\n\nexport const CLOUDFLARE_SEARCH_URL = 'https://workers.cloudflare.com/product/ai-search';\n\nexport const POWERED_BY_BRANDING = `Powered by <a href=\"${CLOUDFLARE_SEARCH_URL}\" target=\"_blank\" rel=\"noopener noreferrer\">Cloudflare AI Search ${CLOUDFLARE_LOGO_SVG}</a>`;\n","/**\n * Chat mode specific styles\n */\n\nexport const chatStyles = `\n/* Chat container */\n.chat-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow-y: auto;\n}\n\n/* Messages area */\n.chat-messages {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: var(--search-snippet-spacing-md);\n display: flex;\n flex-direction: column;\n gap: var(--search-snippet-spacing-md);\n}\n\n.chat-messages::-webkit-scrollbar {\n width: 8px;\n}\n\n.chat-messages::-webkit-scrollbar-track {\n background: var(--search-snippet-surface);\n}\n\n.chat-messages::-webkit-scrollbar-thumb {\n background: var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n}\n\n.chat-messages::-webkit-scrollbar-thumb:hover {\n background: var(--search-snippet-text-secondary);\n}\n\n/* Message */\n.chat-message {\n display: flex;\n gap: var(--search-snippet-spacing-sm);\n max-width: 85%;\n animation: slideIn var(--search-snippet-animation-duration) ease-out;\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.chat-message-user {\n align-self: flex-end;\n flex-direction: row-reverse;\n}\n\n.chat-message-assistant {\n align-self: flex-start;\n}\n\n.chat-message-system {\n align-self: center;\n max-width: 100%;\n}\n\n/* Message avatar */\n.chat-message-avatar {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n font-size: var(--search-snippet-font-size-sm);\n font-weight: var(--search-snippet-font-weight-bold);\n background: var(--search-snippet-primary-color);\n color: white;\n}\n\n.chat-message-assistant .chat-message-avatar {\n background: var(--search-snippet-surface);\n color: var(--search-snippet-text-color);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n/* Message content */\n.chat-message-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: var(--search-snippet-spacing-xs);\n max-width: 100%;\n}\n\n.chat-message-content ol, .chat-message-content ul {\n margin-inline-start: 16px;\n}\n\n.chat-message-content ol li, .chat-message-content ul li {\n padding-inline-start: 0;\n}\n\n.chat-message-bubble {\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n border-radius: var(--search-snippet-border-radius);\n word-wrap: break-word;\n overflow-wrap: break-word;\n}\n\n.chat-message-user .chat-message-bubble {\n background: var(--search-snippet-user-message-bg);\n color: var(--search-snippet-user-message-text);\n border-top-right-radius: var(--search-snippet-spacing-xs);\n}\n\n.chat-message-assistant .chat-message-bubble {\n background: var(--search-snippet-assistant-message-bg);\n color: var(--search-snippet-assistant-message-text);\n border-top-left-radius: var(--search-snippet-spacing-xs);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n.chat-message-system .chat-message-bubble {\n background: var(--search-snippet-system-message-bg);\n color: var(--search-snippet-system-message-text);\n text-align: center;\n font-size: var(--search-snippet-font-size-sm);\n padding: var(--search-snippet-spacing-xs) var(--search-snippet-spacing-md);\n}\n\n.chat-message-text {\n font-size: var(--search-snippet-font-size-base);\n line-height: 1.5;\n white-space: wrap;\n}\n.chat-message-text li{\n padding-inline-start: var(--search-snippet-spacing-md);\n}\n\n.chat-message-metadata {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n}\n\n.chat-message-user .chat-message-metadata {\n justify-content: flex-end;\n}\n\n.chat-message-time {\n opacity: 0.7;\n}\n\n/* Streaming indicator */\n.chat-streaming {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-xs);\n}\n\n.chat-streaming-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: currentColor;\n animation: pulse 1.4s ease-in-out infinite;\n}\n\n.chat-streaming-dot:nth-child(2) {\n animation-delay: 0.2s;\n}\n\n.chat-streaming-dot:nth-child(3) {\n animation-delay: 0.4s;\n}\n\n@keyframes pulse {\n 0%, 80%, 100% {\n opacity: 0.3;\n transform: scale(0.8);\n }\n 40% {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n.chat-streaming .loading-text {\n margin-left: var(--search-snippet-spacing-xs);\n}\n\n/* Input area */\n.chat-input-area {\n padding: var(--search-snippet-spacing-md);\n border-top: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n background: var(--search-snippet-surface);\n}\n\n.chat-input-wrapper {\n display: flex;\n gap: var(--search-snippet-spacing-sm);\n align-items: flex-end;\n}\n\n.chat-input {\n flex: 1;\n min-height: var(--search-snippet-input-height);\n max-height: 120px;\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n line-height: var(--search-snippet-line-height);\n color: var(--search-snippet-text-color);\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n outline: none;\n resize: vertical;\n transition: var(--search-snippet-transition);\n}\n\n.chat-input:focus {\n border-color: var(--search-snippet-primary-color);\n box-shadow: 0 0 0 3px var(--search-snippet-focus-ring);\n}\n\n.chat-input::placeholder {\n color: var(--search-snippet-text-secondary);\n}\n\n.chat-input:disabled {\n background: var(--search-snippet-surface);\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.chat-send-button {\n flex-shrink: 0;\n height: var(--search-snippet-input-height);\n padding: 0 var(--search-snippet-spacing-lg);\n}\n\n.chat-send-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* Empty chat state */\n.chat-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--search-snippet-spacing-xxl);\n gap: var(--search-snippet-spacing-md);\n color: var(--search-snippet-text-secondary);\n text-align: center;\n height: 100%;\n}\n\n.chat-empty-icon {\n width: 64px;\n height: 64px;\n opacity: 0.5;\n}\n\n.chat-empty-title {\n font-size: var(--search-snippet-font-size-lg);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n}\n\n.chat-empty-description {\n font-size: var(--search-snippet-font-size-sm);\n}\n\n/* Code blocks in messages */\n.chat-message-bubble pre {\n background: var(--search-snippet-surface);\n padding: var(--search-snippet-spacing-sm);\n border-radius: var(--search-snippet-border-radius);\n overflow-x: auto;\n font-family: var(--search-snippet-font-family-mono);\n font-size: var(--search-snippet-font-size-sm);\n margin: var(--search-snippet-spacing-xs) 0;\n}\n\n.chat-message-bubble code {\n font-family: var(--search-snippet-font-family-mono);\n font-size: 0.9em;\n background: var(--search-snippet-surface);\n padding: 2px 4px;\n border-radius: var(--search-snippet-border-radius);\n}\n\n.chat-message-bubble pre code {\n background: none;\n padding: 0;\n}\n\n/* Links in messages */\n.chat-message-bubble a {\n color: var(--search-snippet-primary-color);\n text-decoration: underline;\n}\n\n.chat-message-bubble a:hover {\n text-decoration: none;\n}\n`;\n","/**\n * CSS Theme with comprehensive CSS custom properties\n */\n\nexport const baseStyles = `\n:host {\n /* Colors - Light Mode */\n --search-snippet-primary-color: #2563eb;\n --search-snippet-primary-hover: #0f51dfff;\n --search-snippet-background: #ffffff;\n --search-snippet-surface: #f8f9fa;\n --search-snippet-text-color: #212529;\n --search-snippet-text-secondary: #6c757d;\n --search-snippet-text-description: #495057;\n --search-snippet-border-color: #dee2e6;\n --search-snippet-hover-background: #f1f3f5;\n --search-snippet-focus-ring: #0066cc40;\n --search-snippet-error-color: #dc3545;\n --search-snippet-error-background: #f8d7da;\n --search-snippet-success-color: #28a745;\n --search-snippet-success-background: #d4edda;\n --search-snippet-warning-color: #ffc107;\n --search-snippet-warning-background: #fff3cd;\n \n /* Message Colors */\n --search-snippet-user-message-bg: #0066cc;\n --search-snippet-user-message-text: #ffffff;\n --search-snippet-assistant-message-bg: #f1f3f5;\n --search-snippet-assistant-message-text: #212529;\n --search-snippet-system-message-bg: #fff3cd;\n --search-snippet-system-message-text: #856404;\n \n /* Typography */\n --search-snippet-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', \n 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', \n 'Segoe UI Emoji', 'Segoe UI Symbol';\n --search-snippet-font-family-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;\n --search-snippet-font-size-base: 14px;\n --search-snippet-font-size-sm: 12px;\n --search-snippet-font-size-lg: 16px;\n --search-snippet-font-size-xl: 18px;\n --search-snippet-line-height: 1.5;\n --search-snippet-font-weight-normal: 400;\n --search-snippet-font-weight-medium: 500;\n --search-snippet-font-weight-bold: 600;\n \n /* Spacing */\n --search-snippet-spacing-xs: 4px;\n --search-snippet-spacing-sm: 8px;\n --search-snippet-spacing-md: 12px;\n --search-snippet-spacing-lg: 16px;\n --search-snippet-spacing-xl: 24px;\n --search-snippet-spacing-xxl: 32px;\n \n /* Sizing */\n --search-snippet-width: 100%;\n --search-snippet-max-width: 100%;\n --search-snippet-min-width: 320px;\n --search-snippet-max-height: 600px;\n --search-snippet-input-height: 44px;\n --search-snippet-button-height: 36px;\n --search-snippet-icon-size: 20px;\n \n /* Border */\n --search-snippet-border-width: 1px;\n --search-snippet-border-radius: 18px;\n \n /* Shadows */\n --search-snippet-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n --search-snippet-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n --search-snippet-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15);\n --search-snippet-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.2);\n --search-snippet-shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);\n \n /* Animation */\n --search-snippet-transition-fast: 150ms ease;\n --search-snippet-transition: 200ms ease;\n --search-snippet-transition-slow: 300ms ease;\n --search-snippet-animation-duration: 0.2s;\n \n /* Z-index */\n --search-snippet-z-dropdown: 1000;\n --search-snippet-z-modal: 1050;\n --search-snippet-z-popover: 1060;\n --search-snippet-z-tooltip: 1070;\n \n /* Layout */\n display: block;\n width: var(--search-snippet-width);\n max-width: var(--search-snippet-max-width);\n min-width: var(--search-snippet-min-width);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n line-height: var(--search-snippet-line-height);\n color: var(--search-snippet-text-color);\n\n\n /* Search */\n --search-snippet-icon-size: 20px;\n --search-snippet-icon-margin-left: 6px;\n --search-snippet-result-item-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n\n /* Chat Bubble */\n --chat-bubble-button-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);\n --chat-bubble-window-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);\n --chat-bubble-button-size: 60px;\n --chat-bubble-button-radius: 50%;\n --chat-bubble-button-icon-size: 28px;\n --chat-bubble-button-icon-color: white;\n --chat-bubble-button-bottom: 20px;\n --chat-bubble-button-right: 20px;\n --chat-bubble-button-z-index: 9999;\n --chat-bubble-position: fixed;\n\n\n}\n\n:host(:not([theme=\"dark\"])) {\n /* Colors - Light Mode */\n --search-snippet-primary-color: #2563eb;\n --search-snippet-primary-hover: #0f51dfff;\n --search-snippet-background: #ffffff;\n --search-snippet-surface: #f8f9fa;\n --search-snippet-text-color: #212529;\n --search-snippet-text-secondary: #6c757d;\n --search-snippet-text-description: #495057;\n --search-snippet-border-color: #dee2e6;\n --search-snippet-hover-background: #f1f3f5;\n --search-snippet-focus-ring: #0066cc40;\n --search-snippet-error-color: #dc3545;\n --search-snippet-error-background: #f8d7da;\n --search-snippet-success-color: #28a745;\n --search-snippet-success-background: #d4edda;\n --search-snippet-warning-color: #ffc107;\n --search-snippet-warning-background: #fff3cd;\n \n /* Message Colors */\n --search-snippet-user-message-bg: #0066cc;\n --search-snippet-user-message-text: #ffffff;\n --search-snippet-assistant-message-bg: #f1f3f5;\n --search-snippet-assistant-message-text: #212529;\n --search-snippet-system-message-bg: #fff3cd;\n --search-snippet-system-message-text: #856404;\n}\n\n/* Dark Mode */\n@media (prefers-color-scheme: dark) {\n :host(:not([theme=\"light\"])) {\n --search-snippet-primary-color: #2563eb;\n --search-snippet-primary-hover: #0f51dfff;\n --search-snippet-background: #1a1b1e;\n --search-snippet-surface: #25262b;\n --search-snippet-text-color: #c1c2c5;\n --search-snippet-text-secondary: #909296;\n --search-snippet-text-description: #adb5bd;\n --search-snippet-border-color: #373a40;\n --search-snippet-hover-background: #2c2e33;\n --search-snippet-focus-ring: #4dabf740;\n --search-snippet-error-color: #ff6b6b;\n --search-snippet-error-background: #3d1f1f;\n --search-snippet-success-color: #51cf66;\n --search-snippet-success-background: #1f3d24;\n --search-snippet-warning-color: #ffd43b;\n --search-snippet-warning-background: #3d3419;\n \n --search-snippet-user-message-bg: #4dabf7;\n --search-snippet-user-message-text: #1a1b1e;\n --search-snippet-assistant-message-bg: #2c2e33;\n --search-snippet-assistant-message-text: #c1c2c5;\n --search-snippet-system-message-bg: #3d3419;\n --search-snippet-system-message-text: #ffd43b;\n color-scheme: dark;\n }\n}\n\n/* Auto theme support */\n:host([theme=\"light\"]) {\n color-scheme: light;\n}\n\n\n/* Base reset */\n* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n/* Container */\n.container {\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n box-shadow: var(--search-snippet-shadow);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n/* Header */\n.header {\n padding: var(--search-snippet-spacing-md);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n background: var(--search-snippet-surface);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--search-snippet-spacing-md);\n}\n\n.header-title {\n font-size: var(--search-snippet-font-size-lg);\n font-weight: var(--search-snippet-font-weight-bold);\n color: var(--search-snippet-text-color);\n}\n\n/* Input */\n.input-wrapper {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n}\n\n.input {\n width: 100%;\n height: var(--search-snippet-input-height);\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n line-height: var(--search-snippet-line-height);\n color: var(--search-snippet-text-color);\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n outline: none;\n transition: var(--search-snippet-transition);\n}\n\n.input:focus {\n border-color: var(--search-snippet-primary-color);\n box-shadow: 0 0 0 3px var(--search-snippet-focus-ring);\n}\n\n.input::placeholder {\n color: var(--search-snippet-text-secondary);\n}\n\n.input:disabled {\n background: var(--search-snippet-surface);\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n/* Button */\n.button {\n height: var(--search-snippet-button-height);\n padding: 0 var(--search-snippet-spacing-lg);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: #ffffff;\n background: var(--search-snippet-primary-color);\n border: none;\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n outline: none;\n transition: var(--search-snippet-transition);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-sm);\n white-space: nowrap;\n}\n\n.button:hover:not(:disabled) {\n background: var(--search-snippet-primary-hover);\n}\n\n.button:focus-visible {\n box-shadow: 0 0 0 3px var(--search-snippet-focus-ring);\n}\n\n.button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.button-secondary {\n background: var(--search-snippet-surface);\n color: var(--search-snippet-text-color);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n.button-secondary:hover:not(:disabled) {\n background: var(--search-snippet-hover-background);\n}\n\n/* Content area */\n.content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: var(--search-snippet-spacing-md);\n}\n\n/* Scrollbar styling */\n.content::-webkit-scrollbar {\n width: 8px;\n}\n\n.content::-webkit-scrollbar-track {\n background: var(--search-snippet-surface);\n}\n\n.content::-webkit-scrollbar-thumb {\n background: var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n}\n\n.content::-webkit-scrollbar-thumb:hover {\n background: var(--search-snippet-text-secondary);\n}\n\n/* Loading spinner */\n.loading {\n display: inline-block;\n width: var(--search-snippet-icon-size);\n height: var(--search-snippet-icon-size);\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: spin 0.6s linear infinite;\n}\n\n@keyframes spin {\n to { transform: rotate(360deg); }\n}\n\n/* Loading message animation */\n@keyframes loading-message-in {\n from {\n opacity: 0;\n transform: translateY(8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.loading-text {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n}\n\n.loading-text-animate {\n animation: loading-message-in 0.3s ease-out;\n}\n\n/* Error message */\n.error {\n padding: var(--search-snippet-spacing-md);\n color: var(--search-snippet-error-color);\n background: var(--search-snippet-error-background);\n border-radius: var(--search-snippet-border-radius);\n font-size: var(--search-snippet-font-size-sm);\n}\n\n/* Empty state */\n.empty {\n padding: var(--search-snippet-spacing-xl);\n text-align: center;\n color: var(--search-snippet-text-secondary);\n}\n\n/* Accessibility */\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n/* Focus visible polyfill */\n.focus-visible:focus {\n outline: 2px solid var(--search-snippet-primary-color);\n outline-offset: 2px;\n}\n\n/* Powered by branding - block style (for sidebars) */\n.powered-by {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-xs);\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n background: var(--search-snippet-surface);\n border-top: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n margin-top: auto;\n flex-shrink: 0;\n}\n\n.powered-by svg {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n}\n\n.powered-by a,\n.powered-by-inline a {\n color: var(--search-snippet-text-secondary);\n text-decoration: none;\n transition: color var(--search-snippet-transition-fast);\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.powered-by a:hover,\n.powered-by-inline a:hover {\n color: var(--search-snippet-primary-color);\n}\n\n/* Powered by branding - inline style (for headers/subtle placement) */\n.powered-by-inline {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n padding: var(--search-snippet-spacing-xs) 0;\n text-align: center;\n}\n`;\n","/**\n * Converts markdown text to HTML\n * Supports: headers, bold, italic, links, lists, code blocks, inline code, blockquotes, and horizontal rules\n */\nexport function markdownToHtml(markdown: string): string {\n let html = markdown;\n\n // Escape HTML characters first to prevent XSS\n html = escapeHtml(html);\n\n // Process code blocks first (to protect from other transformations)\n html = html.replace(/```([\\s\\S]*?)```/g, (_, code) => `<pre><code>${code.trim()}</code></pre>`);\n\n // Split into lines for block-level processing\n const lines = html.split('\\n');\n const processedLines: string[] = [];\n let inList = false;\n let listType = '';\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // Headers (h1-h6)\n const headerMatch = line.match(/^(#{1,6})\\s+(.+)$/);\n if (headerMatch) {\n const level = headerMatch[1].length;\n const content = headerMatch[2];\n processedLines.push(`<h${level}>${processInlineMarkdown(content)}</h${level}>`);\n continue;\n }\n\n // Horizontal rule\n if (line.match(/^---+$/)) {\n processedLines.push('<hr />');\n continue;\n }\n\n // Blockquote\n if (line.match(/^>\\s+/)) {\n const content = line.replace(/^>\\s+/, '');\n processedLines.push(`<blockquote>${processInlineMarkdown(content)}</blockquote>`);\n continue;\n }\n\n // Unordered list\n const ulMatch = line.match(/^[-*]\\s+(.+)$/);\n if (ulMatch) {\n if (!inList || listType !== 'ul') {\n if (inList) processedLines.push(`</${listType}>`);\n processedLines.push('<ul>');\n inList = true;\n listType = 'ul';\n }\n processedLines.push(`<li>${processInlineMarkdown(ulMatch[1])}</li>`);\n continue;\n }\n\n // Ordered list\n const olMatch = line.match(/^\\d+\\.\\s+(.+)$/);\n if (olMatch) {\n if (!inList || listType !== 'ol') {\n if (inList) processedLines.push(`</${listType}>`);\n processedLines.push('<ol>');\n inList = true;\n listType = 'ol';\n }\n processedLines.push(`<li>${processInlineMarkdown(olMatch[1])}</li>`);\n continue;\n }\n\n // Close list if we're no longer in one\n if (inList) {\n processedLines.push(`</${listType}>`);\n inList = false;\n listType = '';\n }\n\n // Empty line\n if (line.trim() === '') {\n processedLines.push('<br />');\n continue;\n }\n\n // Regular paragraph\n processedLines.push(`<p>${processInlineMarkdown(line)}</p>`);\n }\n\n // Close any open list\n if (inList) {\n processedLines.push(`</${listType}>`);\n }\n\n return processedLines.join('\\n');\n}\n\n/**\n * Process inline markdown elements (bold, italic, links, inline code)\n */\nfunction processInlineMarkdown(text: string): string {\n let result = text;\n\n // Inline code (before other inline elements)\n result = result.replace(/`([^`]+)`/g, '<code>$1</code>');\n\n // Bold and italic (***text***)\n result = result.replace(/\\*\\*\\*(.+?)\\*\\*\\*/g, '<strong><em>$1</em></strong>');\n result = result.replace(/___(.+?)___/g, '<strong><em>$1</em></strong>');\n\n // Bold (**text** or __text__)\n result = result.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');\n result = result.replace(/__(.+?)__/g, '<strong>$1</strong>');\n\n // Italic (*text* or _text_)\n result = result.replace(/\\*(.+?)\\*/g, '<em>$1</em>');\n result = result.replace(/_(.+?)_/g, '<em>$1</em>');\n\n // Links [text](url)\n result = result.replace(\n /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>'\n );\n\n return result;\n}\n\n/**\n * Escape HTML characters to prevent XSS\n */\nfunction escapeHtml(text: string): string {\n const htmlEntities: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n };\n\n return text.replace(/[&<>\"']/g, (char) => htmlEntities[char] || char);\n}\n","/**\n * ChatView Component\n * Handles chat interface with streaming support\n */\n\nimport type { AISearchClient } from '../api/ai-search.ts';\nimport type { SearchSnippetProps } from '../types/index.ts';\nimport {\n createCustomEvent,\n escapeHTML,\n formatTimestamp,\n generateId,\n LOADING_MESSAGE_INTERVAL_MS,\n LOADING_MESSAGES,\n} from '../utils/index.ts';\nimport { markdownToHtml } from '../utils/markdown.ts';\nexport interface Message {\n id: string;\n role: 'user' | 'assistant' | 'system';\n content: string;\n timestamp: number;\n metadata?: Record<string, unknown>;\n}\nexport class ChatView {\n private container: HTMLElement;\n private client: AISearchClient;\n private props: SearchSnippetProps;\n private inputElement: HTMLTextAreaElement | null = null;\n private messagesContainer: HTMLElement | null = null;\n private sendButton: HTMLButtonElement | null = null;\n private messages: Message[] = [];\n private isStreaming = false;\n private currentStreamingMessageId: string | null = null;\n private loadingMessageInterval: ReturnType<typeof setInterval> | null = null;\n private loadingMessageIndex = 0;\n\n // Event handler references for cleanup\n private handleInputResize: ((e: Event) => void) | null = null;\n private handleInputKeydown: ((e: KeyboardEvent) => void) | null = null;\n private handleSendClick: (() => void) | null = null;\n\n constructor(container: HTMLElement, client: AISearchClient, props: SearchSnippetProps) {\n this.container = container;\n this.client = client;\n this.props = props;\n\n this.render();\n this.attachEventListeners();\n }\n\n /**\n * Render the chat interface\n */\n private render(): void {\n this.container.innerHTML = `\n <div class=\"chat-container\">\n <div class=\"chat-messages\">\n <div class=\"chat-empty\">\n <svg class=\"chat-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n <div class=\"chat-empty-title\">Start a Conversation</div>\n <div class=\"chat-empty-description\">\n Send a message to begin chatting\n </div>\n </div>\n </div>\n <div class=\"chat-input-area\">\n <div class=\"chat-input-wrapper\">\n <textarea\n class=\"chat-input\"\n placeholder=\"${escapeHTML(this.props.placeholder || 'Type a message...')}\"\n aria-label=\"Chat message input\"\n style=\"height: 40px;\"\n rows=\"1\"\n ></textarea>\n <button class=\"button chat-send-button\" aria-label=\"Send message\">\n <span>Send</span>\n </button>\n </div>\n </div>\n </div>\n `;\n\n this.messagesContainer = this.container.querySelector('.chat-messages');\n this.inputElement = this.container.querySelector('.chat-input');\n this.sendButton = this.container.querySelector('.chat-send-button');\n }\n\n /**\n * Attach event listeners\n */\n private attachEventListeners(): void {\n if (!this.inputElement || !this.sendButton) return;\n\n // Auto-resize textarea\n this.handleInputResize = (e: Event) => {\n const target = e.target as HTMLTextAreaElement;\n target.style.height = 'auto';\n target.style.height = `${target.scrollHeight}px`;\n };\n this.inputElement.addEventListener('input', this.handleInputResize);\n\n // Enter to send (Shift+Enter for new line)\n this.handleInputKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n this.handleSendMessage();\n }\n };\n this.inputElement.addEventListener('keydown', this.handleInputKeydown);\n\n // Send button click\n this.handleSendClick = () => {\n this.handleSendMessage();\n };\n this.sendButton.addEventListener('click', this.handleSendClick);\n }\n\n /**\n * Handle send message\n */\n private async handleSendMessage(): Promise<void> {\n if (!this.inputElement || this.isStreaming) return;\n\n const content = this.inputElement.value.trim();\n if (content.length === 0) return;\n\n // Clear input\n this.inputElement.value = '';\n this.inputElement.style.height = 'auto';\n\n // Send message\n await this.sendMessage(content);\n }\n\n /**\n * Send a message\n */\n public async sendMessage(content: string): Promise<void> {\n // Add user message\n const userMessage: Message = {\n id: generateId('msg'),\n role: 'user',\n content,\n timestamp: Date.now(),\n };\n\n this.addMessage(userMessage);\n this.renderMessages(true);\n this.setStreamingState(true);\n\n // Create placeholder for assistant response\n const assistantMessageId = generateId('msg');\n const assistantMessage: Message = {\n id: assistantMessageId,\n role: 'assistant',\n content: '',\n timestamp: Date.now(),\n };\n\n this.addMessage(assistantMessage);\n this.currentStreamingMessageId = assistantMessageId;\n this.renderMessages(true);\n\n try {\n // Stream the response\n const stream = this.client.chat(content);\n\n let fullContent = '';\n\n for await (const chunk of stream) {\n if (chunk.type === 'text' && chunk.message) {\n fullContent += chunk.message;\n this.updateStreamingMessage(assistantMessageId, fullContent);\n } else if (chunk.type === 'error') {\n this.showErrorInMessage(assistantMessageId, chunk.message || 'Unknown error');\n break;\n }\n // else if (chunk.type === 'done') {\n // break;\n // }\n }\n\n // Update final message\n const messageIndex = this.messages.findIndex((m) => m.id === assistantMessageId);\n if (messageIndex !== -1) {\n this.messages[messageIndex].content = fullContent;\n }\n\n // Emit message event\n this.container.dispatchEvent(createCustomEvent('message', { message: assistantMessage }));\n } catch (error) {\n this.showErrorInMessage(assistantMessageId, (error as Error).message);\n\n // Emit error event\n this.container.dispatchEvent(\n createCustomEvent('error', {\n error: {\n message: (error as Error).message,\n code: 'CHAT_ERROR',\n },\n })\n );\n } finally {\n this.setStreamingState(false);\n this.renderMessages();\n this.currentStreamingMessageId = null;\n }\n }\n\n /**\n * Add a message to the chat\n */\n private addMessage(message: Message): void {\n this.messages.push(message);\n this.renderMessages();\n }\n\n /**\n * Update streaming message content\n */\n private updateStreamingMessage(messageId: string, content: string): void {\n const messageIndex = this.messages.findIndex((m) => m.id === messageId);\n if (messageIndex !== -1) {\n this.messages[messageIndex].content = content;\n this.renderMessages(true);\n }\n }\n\n /**\n * Show error in message\n */\n private showErrorInMessage(messageId: string, error: string): void {\n const messageIndex = this.messages.findIndex((m) => m.id === messageId);\n if (messageIndex !== -1) {\n this.messages[messageIndex].content = `Error: ${error}`;\n this.renderMessages();\n }\n }\n\n /**\n * Render all messages\n */\n private renderMessages(isStreaming = false): void {\n if (!this.messagesContainer) return;\n\n if (this.messages.length === 0) {\n this.messagesContainer.innerHTML = `\n <div class=\"chat-empty\">\n <svg class=\"chat-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n <div class=\"chat-empty-title\">Start a Conversation</div>\n <div class=\"chat-empty-description\">\n Send a message to begin chatting\n </div>\n </div>\n `;\n return;\n }\n\n const messagesHTML = this.messages\n .map((message) =>\n this.renderMessage(message, isStreaming && message.id === this.currentStreamingMessageId)\n )\n .join('');\n\n this.messagesContainer.innerHTML = messagesHTML;\n\n // Scroll to bottom\n this.scrollToBottom();\n }\n\n /**\n * Render a single message\n */\n private renderMessage(message: Message, isStreaming = false): string {\n const roleClass = `chat-message-${message.role}`;\n const avatar = message.role === 'user' ? 'U' : 'AI';\n\n return `\n <div class=\"chat-message ${roleClass}\">\n <div class=\"chat-message-avatar\">${avatar}</div>\n <div class=\"chat-message-content\">\n <div class=\"chat-message-bubble\">\n ${message.content ? `<div class=\"chat-message-text\">${markdownToHtml(message.content)}</div>` : ''}\n ${isStreaming ? `<div class=\"chat-streaming\"><span class=\"chat-streaming-dot\"></span><span class=\"chat-streaming-dot\"></span><span class=\"chat-streaming-dot\"></span><span class=\"loading-text\">${LOADING_MESSAGES[this.loadingMessageIndex]}</span></div>` : ''}\n </div>\n <div class=\"chat-message-metadata\">\n <span class=\"chat-message-time\">${formatTimestamp(message.timestamp)}</span>\n </div>\n </div>\n </div>\n `;\n }\n\n /**\n * Scroll to bottom of messages\n */\n private scrollToBottom(): void {\n if (!this.messagesContainer) return;\n\n requestAnimationFrame(() => {\n if (this.messagesContainer) {\n this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;\n }\n });\n }\n\n /**\n * Set streaming state\n */\n private setStreamingState(streaming: boolean): void {\n this.isStreaming = streaming;\n\n if (this.inputElement) {\n this.inputElement.disabled = streaming;\n }\n\n if (this.sendButton) {\n this.sendButton.disabled = streaming;\n this.sendButton.innerHTML = streaming ? '<div class=\"loading\"></div>' : '<span>Send</span>';\n }\n\n if (streaming) {\n this.startLoadingMessages();\n } else {\n this.clearLoadingMessages();\n }\n }\n\n private startLoadingMessages(): void {\n this.loadingMessageIndex = Math.floor(Math.random() * LOADING_MESSAGES.length);\n this.loadingMessageInterval = setInterval(() => {\n this.loadingMessageIndex = (this.loadingMessageIndex + 1) % LOADING_MESSAGES.length;\n if (this.isStreaming) {\n this.renderMessages(true);\n }\n }, LOADING_MESSAGE_INTERVAL_MS);\n }\n\n private clearLoadingMessages(): void {\n if (this.loadingMessageInterval) {\n clearInterval(this.loadingMessageInterval);\n this.loadingMessageInterval = null;\n }\n }\n\n /**\n * Get all messages\n */\n public getMessages(): Message[] {\n return [...this.messages];\n }\n\n /**\n * Clear all messages\n */\n public clearMessages(): void {\n this.messages = [];\n this.renderMessages();\n }\n\n /**\n * Set messages (for restoring history)\n */\n public setMessages(messages: Message[]): void {\n this.messages = [...messages];\n this.renderMessages();\n }\n\n /**\n * Destroy and cleanup\n */\n public destroy(): void {\n this.clearLoadingMessages();\n\n if (this.isStreaming) {\n this.client.cancelAllRequests();\n }\n\n // Remove event listeners\n if (this.inputElement) {\n if (this.handleInputResize) {\n this.inputElement.removeEventListener('input', this.handleInputResize);\n }\n if (this.handleInputKeydown) {\n this.inputElement.removeEventListener('keydown', this.handleInputKeydown);\n }\n }\n\n if (this.sendButton && this.handleSendClick) {\n this.sendButton.removeEventListener('click', this.handleSendClick);\n }\n\n // Clear handler references\n this.handleInputResize = null;\n this.handleInputKeydown = null;\n this.handleSendClick = null;\n }\n}\n","/**\n * Chat Bubble Snippet\n * A floating chat widget that expands from a bubble button\n * Fixed position in bottom-right corner\n */\n\nimport type { AISearchClient } from '../api/ai-search.ts';\nimport { POWERED_BY_BRANDING } from '../constants.ts';\nimport { chatStyles } from '../styles/chat.ts';\nimport { baseStyles } from '../styles/theme.ts';\nimport type { SearchSnippetProps } from '../types/index.ts';\nimport {\n createClient,\n createCustomEvent,\n parseAttribute,\n parseBooleanAttribute,\n} from '../utils/index.ts';\nimport type { Message } from './chat-view.ts';\nimport { ChatView } from './chat-view.ts';\n\nconst COMPONENT_NAME = 'chat-bubble-snippet';\n\nexport class ChatBubbleSnippet extends HTMLElement {\n private shadow: ShadowRoot;\n private client: AISearchClient | null = null;\n private chatView: ChatView | null = null;\n private container: HTMLElement | null = null;\n private isExpanded = false;\n private isMinimized = false;\n\n // Event handler references for cleanup\n private handleBubbleClick: (() => void) | null = null;\n private handleCloseClick: (() => void) | null = null;\n private handleMinimizeClick: (() => void) | null = null;\n private handleClearClick: (() => void) | null = null;\n\n static get observedAttributes() {\n return ['api-url', 'placeholder', 'theme', 'hide-branding'] as const;\n }\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback(): void {\n this.render();\n this.initializeClient();\n this.dispatchEvent(createCustomEvent('ready', undefined));\n }\n\n disconnectedCallback(): void {\n this.cleanup();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n if (name === 'api-url') {\n this.initializeClient();\n } else if (name === 'theme') {\n // Theme changes are handled automatically by CSS :host([theme]) selectors\n this.updateTheme(newValue);\n }\n }\n\n private getProps(): SearchSnippetProps {\n return {\n apiUrl: parseAttribute(this.getAttribute('api-url'), ''),\n placeholder: parseAttribute(this.getAttribute('placeholder'), 'Type a message...'),\n theme: parseAttribute(this.getAttribute('theme'), 'auto') as 'light' | 'dark' | 'auto',\n hideBranding: parseBooleanAttribute(this.getAttribute('hide-branding'), false),\n };\n }\n\n private initializeClient(): void {\n const props = this.getProps();\n\n if (!props.apiUrl) {\n console.error('ChatBubbleSnippet: api-url attribute is required');\n this.client = null;\n return;\n }\n\n try {\n this.client = createClient(props.apiUrl);\n } catch (error) {\n console.error('ChatBubbleSnippet:', error);\n }\n }\n\n private render(): void {\n const style = document.createElement('style');\n style.textContent = `${baseStyles}\\n${chatStyles}\\n${this.getBubbleStyles()}`;\n\n this.container = document.createElement('div');\n this.container.className = 'chat-bubble-widget';\n this.container.innerHTML = this.getBaseHTML();\n\n this.shadow.innerHTML = '';\n this.shadow.appendChild(style);\n this.shadow.appendChild(this.container);\n\n this.attachEventListeners();\n }\n\n private getBubbleStyles(): string {\n return `\n .chat-bubble-widget {\n position: var(--chat-bubble-position);\n bottom: var(--chat-bubble-button-bottom);\n right: var(--chat-bubble-button-right);\n z-index: var(--chat-bubble-button-z-index);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n }\n\n .bubble-button {\n width: var(--chat-bubble-button-size);\n height: var(--chat-bubble-button-size);\n border-radius: var(--chat-bubble-button-radius);\n background: var(--search-snippet-primary-color);\n border: none;\n cursor: pointer;\n box-shadow: var(--chat-bubble-button-shadow);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.3s ease;\n position: relative;\n }\n\n .bubble-button:hover {\n background: var(--search-snippet-primary-hover);\n transform: scale(1.05);\n }\n\n .bubble-button svg {\n width: var(--chat-bubble-button-icon-size);\n height: var(--chat-bubble-button-icon-size);\n color: var(--chat-bubble-button-icon-color);\n }\n\n .bubble-button.hidden {\n opacity: 0;\n pointer-events: none;\n transform: scale(0);\n }\n\n .chat-window {\n position: absolute;\n bottom: 0;\n right: 0;\n width: 380px;\n height: 500px;\n background: var(--search-snippet-background);\n border-radius: var(--search-snippet-border-radius);\n box-shadow: var(--chat-bubble-window-shadow);\n display: flex;\n flex-direction: column;\n opacity: 0;\n transform: scale(0.8) translateY(20px);\n transform-origin: bottom right;\n transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);\n pointer-events: none;\n overflow: hidden;\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n }\n\n .chat-window.expanded {\n opacity: 1;\n transform: scale(1) translateY(0);\n pointer-events: auto;\n }\n\n .chat-window.minimized {\n height: 58px;\n overflow: hidden;\n }\n\n .chat-header {\n background: var(--search-snippet-surface);\n padding: var(--search-snippet-spacing-md);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n }\n\n .chat-header-title {\n font-weight: var(--search-snippet-font-weight-bold);\n color: var(--search-snippet-text-color);\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n font-size: var(--search-snippet-font-size-lg);\n }\n\n .chat-header-title svg {\n width: 20px;\n height: 20px;\n }\n\n .chat-header-actions {\n display: flex;\n gap: var(--search-snippet-spacing-xs);\n }\n\n .icon-button {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background var(--search-snippet-transition-fast);\n color: var(--search-snippet-text-color);\n }\n\n .icon-button:hover {\n background: var(--search-snippet-hover-background);\n }\n\n .icon-button svg {\n width: 18px;\n height: 18px;\n }\n\n .chat-content {\n flex: 1;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n @media (max-width: 480px) {\n .chat-window {\n width: calc(100vw - 40px);\n max-width: 400px;\n }\n }\n `;\n }\n\n private getBaseHTML(): string {\n const props = this.getProps();\n const brandingHTML = props.hideBranding\n ? ''\n : `<div class=\"powered-by\">${POWERED_BY_BRANDING}</div>`;\n\n return `\n <button class=\"bubble-button\" aria-label=\"Open chat\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n </button>\n <div class=\"chat-window\">\n <div class=\"chat-header\">\n <div class=\"chat-header-title\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n <span>Chat</span>\n </div>\n <div class=\"chat-header-actions\">\n <button class=\"icon-button clear-button\" aria-label=\"Clear history\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <polyline points=\"3 6 5 6 21 6\"></polyline>\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path>\n </svg>\n </button>\n <button class=\"icon-button minimize-button\" aria-label=\"Minimize\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"></line>\n </svg>\n </button>\n <button class=\"icon-button close-button\" aria-label=\"Close\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\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 </button>\n </div>\n </div>\n <div class=\"chat-content\"></div>\n ${brandingHTML}\n </div>\n `;\n }\n\n private attachEventListeners(): void {\n const bubbleButton = this.shadow.querySelector('.bubble-button');\n const closeButton = this.shadow.querySelector('.close-button');\n const minimizeButton = this.shadow.querySelector('.minimize-button');\n const clearButton = this.shadow.querySelector('.clear-button');\n\n this.handleBubbleClick = () => this.toggleChat();\n this.handleCloseClick = () => this.closeChat();\n this.handleMinimizeClick = () => this.toggleMinimize();\n this.handleClearClick = () => this.clearChat();\n\n bubbleButton?.addEventListener('click', this.handleBubbleClick);\n closeButton?.addEventListener('click', this.handleCloseClick);\n minimizeButton?.addEventListener('click', this.handleMinimizeClick);\n clearButton?.addEventListener('click', this.handleClearClick);\n }\n\n private removeEventListeners(): void {\n const bubbleButton = this.shadow.querySelector('.bubble-button');\n const closeButton = this.shadow.querySelector('.close-button');\n const minimizeButton = this.shadow.querySelector('.minimize-button');\n const clearButton = this.shadow.querySelector('.clear-button');\n\n if (this.handleBubbleClick) {\n bubbleButton?.removeEventListener('click', this.handleBubbleClick);\n }\n if (this.handleCloseClick) {\n closeButton?.removeEventListener('click', this.handleCloseClick);\n }\n if (this.handleMinimizeClick) {\n minimizeButton?.removeEventListener('click', this.handleMinimizeClick);\n }\n if (this.handleClearClick) {\n clearButton?.removeEventListener('click', this.handleClearClick);\n }\n\n // Clear handler references\n this.handleBubbleClick = null;\n this.handleCloseClick = null;\n this.handleMinimizeClick = null;\n this.handleClearClick = null;\n }\n\n private toggleChat(): void {\n this.isExpanded = !this.isExpanded;\n const bubbleButton = this.shadow.querySelector('.bubble-button');\n const chatWindow = this.shadow.querySelector('.chat-window');\n\n if (this.isExpanded) {\n bubbleButton?.classList.add('hidden');\n chatWindow?.classList.add('expanded');\n this.initializeChatView();\n } else {\n bubbleButton?.classList.remove('hidden');\n chatWindow?.classList.remove('expanded');\n }\n }\n\n private closeChat(): void {\n this.isExpanded = false;\n this.isMinimized = false;\n const bubbleButton = this.shadow.querySelector('.bubble-button');\n const chatWindow = this.shadow.querySelector('.chat-window');\n\n bubbleButton?.classList.remove('hidden');\n chatWindow?.classList.remove('expanded', 'minimized');\n }\n\n private toggleMinimize(): void {\n this.isMinimized = !this.isMinimized;\n const chatWindow = this.shadow.querySelector('.chat-window');\n\n if (this.isMinimized) {\n chatWindow?.classList.add('minimized');\n } else {\n chatWindow?.classList.remove('minimized');\n }\n }\n\n private initializeChatView(): void {\n if (this.chatView) return;\n\n const chatContent = this.shadow.querySelector('.chat-content') as HTMLElement;\n if (!chatContent) return;\n\n if (!this.client) {\n chatContent.innerHTML = `\n <div style=\"padding: 16px; color: var(--search-snippet-error-color, #ef4444); font-family: var(--search-snippet-font-family, sans-serif); font-size: var(--search-snippet-font-size-base, 14px);\">\n <strong>Error:</strong> The <code>api-url</code> attribute is required. Please provide a valid API URL.\n </div>\n `;\n return;\n }\n\n const props = this.getProps();\n this.chatView = new ChatView(chatContent, this.client, props);\n }\n\n private updateTheme(theme: string | null): void {\n // CSS :host([theme]) selectors handle theming automatically\n // For 'auto' mode, remove the attribute to let @media (prefers-color-scheme) work\n const validTheme = theme === 'light' || theme === 'dark' ? theme : null;\n\n if (\n validTheme === null &&\n this.hasAttribute('theme') &&\n this.getAttribute('theme') !== 'auto'\n ) {\n this.removeAttribute('theme');\n }\n }\n\n private cleanup(): void {\n this.removeEventListeners();\n\n if (this.client) {\n this.client.cancelAllRequests();\n }\n\n if (this.chatView) {\n this.chatView.destroy();\n }\n }\n\n // Public API\n public clearChat(): void {\n this.chatView?.clearMessages();\n }\n\n public async sendMessage(content: string): Promise<void> {\n if (this.chatView) {\n await this.chatView.sendMessage(content);\n }\n }\n\n public getMessages(): Message[] {\n return this.chatView?.getMessages() || [];\n }\n}\n\n// Register the custom element\nif (!customElements.get(COMPONENT_NAME)) {\n customElements.define(COMPONENT_NAME, ChatBubbleSnippet);\n}\n","/**\n * Chat Page Snippet\n * A full-page chat interface with history support\n */\n\nimport type { AISearchClient } from '../api/ai-search.ts';\nimport { POWERED_BY_BRANDING } from '../constants.ts';\nimport { chatStyles } from '../styles/chat.ts';\nimport { baseStyles } from '../styles/theme.ts';\nimport type { SearchSnippetProps } from '../types/index.ts';\nimport {\n createClient,\n createCustomEvent,\n parseAttribute,\n parseBooleanAttribute,\n} from '../utils/index.ts';\nimport type { Message } from './chat-view.ts';\nimport { ChatView } from './chat-view.ts';\n\nconst COMPONENT_NAME = 'chat-page-snippet';\nconst STORAGE_KEY = 'chat-page-sessions';\n\ninterface ChatSession {\n id: string;\n title: string;\n messages: Message[];\n createdAt: number;\n updatedAt: number;\n}\n\nexport class ChatPageSnippet extends HTMLElement {\n private shadow: ShadowRoot;\n private client: AISearchClient | null = null;\n private chatView: ChatView | null = null;\n private container: HTMLElement | null = null;\n private sessions: ChatSession[] = [];\n private currentSessionId: string | null = null;\n private sidebarCollapsed = false;\n\n // Event handler references for cleanup\n private handleClearClick: (() => void) | null = null;\n private handleNewChatClick: (() => void) | null = null;\n private handleToggleSidebarClick: (() => void) | null = null;\n private handleChatListClick: ((e: Event) => void) | null = null;\n private handleMessageEvent: (() => void) | null = null;\n\n static get observedAttributes() {\n return ['api-url', 'placeholder', 'theme', 'hide-branding'] as const;\n }\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n this.loadSessions();\n }\n\n connectedCallback(): void {\n this.render();\n this.initializeClient();\n this.setupView();\n this.dispatchEvent(createCustomEvent('ready', undefined));\n }\n\n disconnectedCallback(): void {\n this.saveCurrentSession();\n this.cleanup();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n if (name === 'api-url') {\n this.initializeClient();\n this.setupView();\n } else if (name === 'theme') {\n // Theme changes are handled automatically by CSS :host([theme]) selectors\n this.updateTheme(newValue);\n }\n }\n\n private getProps(): SearchSnippetProps {\n return {\n apiUrl: parseAttribute(this.getAttribute('api-url'), ''),\n placeholder: parseAttribute(this.getAttribute('placeholder'), 'Type a message...'),\n theme: parseAttribute(this.getAttribute('theme'), 'auto') as 'light' | 'dark' | 'auto',\n hideBranding: parseBooleanAttribute(this.getAttribute('hide-branding'), false),\n };\n }\n\n private initializeClient(): void {\n const props = this.getProps();\n\n if (!props.apiUrl) {\n console.error('ChatPageSnippet: api-url attribute is required');\n this.client = null;\n return;\n }\n\n try {\n this.client = createClient(props.apiUrl);\n } catch (error) {\n console.error('ChatPageSnippet:', error);\n }\n }\n\n private render(): void {\n const style = document.createElement('style');\n style.textContent = `${baseStyles}\\n${chatStyles}\\n${this.getPageStyles()}`;\n\n this.container = document.createElement('div');\n this.container.className = 'chat-page-container';\n this.container.innerHTML = this.getBaseHTML();\n\n this.shadow.innerHTML = '';\n this.shadow.appendChild(style);\n this.shadow.appendChild(this.container);\n\n this.attachEventListeners();\n }\n\n private getPageStyles(): string {\n return `\n :host {\n display: block;\n width: 100%;\n height: 100vh;\n }\n\n .chat-page-container {\n display: flex;\n height: 100%;\n background: var(--search-snippet-background);\n }\n\n /* Sidebar styles */\n .chat-sidebar {\n width: 280px;\n min-width: 280px;\n background: var(--search-snippet-surface);\n border-right: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n display: flex;\n flex-direction: column;\n transition: var(--search-snippet-transition);\n overflow: hidden;\n }\n\n .chat-sidebar.collapsed {\n width: 0;\n min-width: 0;\n border-right: none;\n }\n\n .sidebar-header {\n padding: var(--search-snippet-spacing-lg);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n height: 69px;\n }\n\n .sidebar-title {\n font-size: var(--search-snippet-font-size-lg);\n font-weight: var(--search-snippet-font-weight-bold);\n color: var(--search-snippet-text-color);\n }\n\n .new-chat-button {\n width: 100%;\n height: var(--search-snippet-button-height);\n margin: var(--search-snippet-spacing-md) var(--search-snippet-spacing-lg);\n padding: 0 var(--search-snippet-spacing-lg);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: #fff;\n background: var(--search-snippet-primary-color);\n border: none;\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n outline: none;\n transition: var(--search-snippet-transition);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-sm);\n box-sizing: border-box;\n width: calc(100% - var(--search-snippet-spacing-lg) * 2);\n }\n\n .new-chat-button:hover {\n opacity: 0.9;\n }\n\n .new-chat-button svg {\n width: 16px;\n height: 16px;\n }\n\n .chat-list {\n flex: 1;\n overflow-y: auto;\n padding: var(--search-snippet-spacing-sm);\n }\n\n .chat-list-item {\n display: flex;\n align-items: center;\n padding: var(--search-snippet-spacing-md) var(--search-snippet-spacing-lg);\n margin-bottom: var(--search-snippet-spacing-xs);\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n transition: var(--search-snippet-transition);\n gap: var(--search-snippet-spacing-sm);\n }\n\n .chat-list-item:hover {\n background: var(--search-snippet-hover-background);\n }\n\n .chat-list-item.active {\n background: var(--search-snippet-hover-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n }\n\n .chat-list-item-content {\n flex: 1;\n min-width: 0;\n }\n\n .chat-list-item-title {\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .chat-list-item-date {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n margin-top: 2px;\n }\n\n .chat-list-item-delete {\n opacity: 0;\n background: none;\n border: none;\n padding: var(--search-snippet-spacing-xs);\n cursor: pointer;\n color: var(--search-snippet-text-secondary);\n border-radius: var(--search-snippet-border-radius-sm);\n transition: var(--search-snippet-transition);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .chat-list-item:hover .chat-list-item-delete {\n opacity: 1;\n }\n\n .chat-list-item-delete:hover {\n background: var(--search-snippet-error-background, rgba(239, 68, 68, 0.1));\n color: var(--search-snippet-error-color, #ef4444);\n }\n\n .chat-list-item-delete svg {\n width: 14px;\n height: 14px;\n }\n\n .chat-list-empty {\n padding: var(--search-snippet-spacing-xl);\n text-align: center;\n color: var(--search-snippet-text-secondary);\n font-size: var(--search-snippet-font-size-sm);\n }\n\n /* Main content area */\n .chat-main {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n }\n\n .chat-page-header {\n background: var(--search-snippet-surface);\n padding: var(--search-snippet-spacing-lg);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n }\n\n .chat-page-header-left {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-md);\n }\n\n .toggle-sidebar-button {\n width: 36px;\n height: 36px;\n padding: 0;\n font-family: var(--search-snippet-font-family);\n color: var(--search-snippet-text-color);\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n outline: none;\n transition: var(--search-snippet-transition);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n .toggle-sidebar-button:hover {\n background: var(--search-snippet-hover-background);\n }\n\n .toggle-sidebar-button svg {\n width: 18px;\n height: 18px;\n }\n\n .chat-page-header-title {\n font-size: var(--search-snippet-font-size-xl);\n font-weight: var(--search-snippet-font-weight-bold);\n color: var(--search-snippet-text-color);\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-md);\n }\n\n .chat-page-header-title svg {\n width: 28px;\n height: 28px;\n }\n\n .chat-page-header-actions {\n display: flex;\n gap: var(--search-snippet-spacing-sm);\n }\n\n .header-button {\n height: var(--search-snippet-button-height);\n padding: 0 var(--search-snippet-spacing-lg);\n font-family: var(--search-snippet-font-family);\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n outline: none;\n transition: var(--search-snippet-transition);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-sm);\n }\n\n .header-button:hover {\n background: var(--search-snippet-hover-background);\n }\n\n .header-button svg {\n width: 16px;\n height: 16px;\n }\n\n .chat-page-content {\n flex: 1;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n width: 100%;\n }\n\n .container {\n border: none;\n box-shadow: none;\n height: 100%;\n width: 100%;\n background: var(--search-snippet-background);\n border-radius: 0;\n }\n `;\n }\n\n private getBaseHTML(): string {\n const props = this.getProps();\n const brandingHTML = props.hideBranding\n ? ''\n : `<div class=\"powered-by\">${POWERED_BY_BRANDING}</div>`;\n\n return `\n <div class=\"chat-sidebar\">\n <div class=\"sidebar-header\">\n <span class=\"sidebar-title\">History</span>\n </div>\n <button class=\"new-chat-button\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12h14\"></path>\n </svg>\n New Chat\n </button>\n <div class=\"chat-list\"></div>\n ${brandingHTML}\n </div>\n <div class=\"chat-main\">\n <div class=\"chat-page-header\">\n <div class=\"chat-page-header-left\">\n <button class=\"toggle-sidebar-button\" title=\"Toggle sidebar\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M3 12h18M3 6h18M3 18h18\"></path>\n </svg>\n </button>\n <div class=\"chat-page-header-title\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\n </svg>\n <span>Chat</span>\n </div>\n </div>\n <div class=\"chat-page-header-actions\">\n <button class=\"header-button clear-button\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path>\n </svg>\n Clear Chat\n </button>\n </div>\n </div>\n <div class=\"chat-page-content\">\n <div class=\"container\"></div>\n </div>\n </div>\n `;\n }\n\n private attachEventListeners(): void {\n const clearButton = this.shadow.querySelector('.clear-button');\n const newChatButton = this.shadow.querySelector('.new-chat-button');\n const toggleSidebarButton = this.shadow.querySelector('.toggle-sidebar-button');\n const chatList = this.shadow.querySelector('.chat-list');\n\n this.handleClearClick = () => this.clearCurrentChat();\n this.handleNewChatClick = () => this.createNewChat();\n this.handleToggleSidebarClick = () => this.toggleSidebar();\n this.handleChatListClick = (e: Event) => this.onChatListClick(e);\n\n clearButton?.addEventListener('click', this.handleClearClick);\n newChatButton?.addEventListener('click', this.handleNewChatClick);\n toggleSidebarButton?.addEventListener('click', this.handleToggleSidebarClick);\n chatList?.addEventListener('click', this.handleChatListClick);\n }\n\n private removeEventListeners(): void {\n const clearButton = this.shadow.querySelector('.clear-button');\n const newChatButton = this.shadow.querySelector('.new-chat-button');\n const toggleSidebarButton = this.shadow.querySelector('.toggle-sidebar-button');\n const chatList = this.shadow.querySelector('.chat-list');\n const chatContent = this.shadow.querySelector('.container') as HTMLElement;\n\n if (this.handleClearClick) {\n clearButton?.removeEventListener('click', this.handleClearClick);\n }\n if (this.handleNewChatClick) {\n newChatButton?.removeEventListener('click', this.handleNewChatClick);\n }\n if (this.handleToggleSidebarClick) {\n toggleSidebarButton?.removeEventListener('click', this.handleToggleSidebarClick);\n }\n if (this.handleChatListClick) {\n chatList?.removeEventListener('click', this.handleChatListClick);\n }\n if (this.handleMessageEvent && chatContent) {\n chatContent.removeEventListener('message', this.handleMessageEvent);\n }\n\n // Clear handler references\n this.handleClearClick = null;\n this.handleNewChatClick = null;\n this.handleToggleSidebarClick = null;\n this.handleChatListClick = null;\n this.handleMessageEvent = null;\n }\n\n private setupView(): void {\n const chatContent = this.shadow.querySelector('.container') as HTMLElement;\n\n if (!this.client) {\n if (chatContent) {\n chatContent.innerHTML = `\n <div style=\"padding: 16px; color: var(--search-snippet-error-color, #ef4444); font-family: var(--search-snippet-font-family, sans-serif); font-size: var(--search-snippet-font-size-base, 14px);\">\n <strong>Error:</strong> The <code>api-url</code> attribute is required. Please provide a valid API URL.\n </div>\n `;\n }\n return;\n }\n\n if (!chatContent) return;\n\n const props = this.getProps();\n this.chatView = new ChatView(chatContent, this.client, props);\n\n // Load current session or create new one\n if (this.sessions.length === 0) {\n this.createNewChat();\n } else {\n // Load the most recent session\n const lastSession = this.sessions[0];\n this.switchToSession(lastSession.id);\n }\n\n // Listen for new messages to save session\n this.handleMessageEvent = () => {\n this.saveCurrentSession();\n this.updateSessionTitle();\n this.renderChatList();\n };\n chatContent.addEventListener('message', this.handleMessageEvent);\n\n this.renderChatList();\n }\n\n private generateSessionId(): string {\n return `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n }\n\n private loadSessions(): void {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) {\n this.sessions = JSON.parse(stored);\n // Sort by updatedAt descending\n this.sessions.sort((a, b) => b.updatedAt - a.updatedAt);\n }\n } catch (error) {\n console.error('Failed to load chat sessions:', error);\n }\n }\n\n private saveSessions(): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(this.sessions));\n } catch (error) {\n console.error('Failed to save chat sessions:', error);\n }\n }\n\n private saveCurrentSession(): void {\n if (!this.currentSessionId || !this.chatView) return;\n\n const sessionIndex = this.sessions.findIndex((s) => s.id === this.currentSessionId);\n if (sessionIndex !== -1) {\n this.sessions[sessionIndex].messages = this.chatView.getMessages();\n this.sessions[sessionIndex].updatedAt = Date.now();\n this.saveSessions();\n }\n }\n\n private updateSessionTitle(): void {\n if (!this.currentSessionId) return;\n\n const session = this.sessions.find((s) => s.id === this.currentSessionId);\n if (session && session.messages.length > 0 && session.title === 'New Chat') {\n const firstUserMessage = session.messages.find((m) => m.role === 'user');\n if (firstUserMessage) {\n session.title =\n firstUserMessage.content.slice(0, 50) +\n (firstUserMessage.content.length > 50 ? '...' : '');\n this.saveSessions();\n }\n }\n }\n\n private createNewChat(): void {\n // Save current session first\n this.saveCurrentSession();\n\n const newSession: ChatSession = {\n id: this.generateSessionId(),\n title: 'New Chat',\n messages: [],\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n this.sessions.unshift(newSession);\n this.currentSessionId = newSession.id;\n this.saveSessions();\n\n // Clear the chat view\n this.chatView?.clearMessages();\n this.renderChatList();\n }\n\n private switchToSession(sessionId: string): void {\n if (sessionId === this.currentSessionId) return;\n\n // Save current session first\n this.saveCurrentSession();\n\n const session = this.sessions.find((s) => s.id === sessionId);\n if (session && this.chatView) {\n this.currentSessionId = sessionId;\n this.chatView.setMessages(session.messages);\n this.renderChatList();\n }\n }\n\n private deleteSession(sessionId: string): void {\n const sessionIndex = this.sessions.findIndex((s) => s.id === sessionId);\n if (sessionIndex === -1) return;\n\n this.sessions.splice(sessionIndex, 1);\n this.saveSessions();\n\n // If we deleted the current session, switch to another or create new\n if (sessionId === this.currentSessionId) {\n if (this.sessions.length > 0) {\n this.switchToSession(this.sessions[0].id);\n } else {\n this.createNewChat();\n }\n }\n\n this.renderChatList();\n }\n\n private clearCurrentChat(): void {\n if (!this.currentSessionId) return;\n\n const session = this.sessions.find((s) => s.id === this.currentSessionId);\n if (session) {\n session.messages = [];\n session.title = 'New Chat';\n session.updatedAt = Date.now();\n this.saveSessions();\n }\n\n this.chatView?.clearMessages();\n this.renderChatList();\n }\n\n private toggleSidebar(): void {\n this.sidebarCollapsed = !this.sidebarCollapsed;\n const sidebar = this.shadow.querySelector('.chat-sidebar');\n sidebar?.classList.toggle('collapsed', this.sidebarCollapsed);\n }\n\n private onChatListClick(e: Event): void {\n const target = e.target as HTMLElement;\n\n // Handle delete button click\n const deleteButton = target.closest('.chat-list-item-delete');\n if (deleteButton) {\n e.stopPropagation();\n const sessionId = deleteButton.getAttribute('data-session-id');\n if (sessionId) {\n this.deleteSession(sessionId);\n }\n return;\n }\n\n // Handle chat item click\n const chatItem = target.closest('.chat-list-item');\n if (chatItem) {\n const sessionId = chatItem.getAttribute('data-session-id');\n if (sessionId) {\n this.switchToSession(sessionId);\n }\n }\n }\n\n private renderChatList(): void {\n const chatList = this.shadow.querySelector('.chat-list');\n if (!chatList) return;\n\n if (this.sessions.length === 0) {\n chatList.innerHTML = '<div class=\"chat-list-empty\">No chats yet</div>';\n return;\n }\n\n chatList.innerHTML = this.sessions.map((session) => this.renderChatListItem(session)).join('');\n }\n\n private renderChatListItem(session: ChatSession): string {\n const isActive = session.id === this.currentSessionId;\n const date = this.formatDate(session.updatedAt);\n\n return `\n <div class=\"chat-list-item ${isActive ? 'active' : ''}\" data-session-id=\"${session.id}\">\n <div class=\"chat-list-item-content\">\n <div class=\"chat-list-item-title\">${this.escapeHTML(session.title)}</div>\n <div class=\"chat-list-item-date\">${date}</div>\n </div>\n <button class=\"chat-list-item-delete\" data-session-id=\"${session.id}\" title=\"Delete chat\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path>\n </svg>\n </button>\n </div>\n `;\n }\n\n private formatDate(timestamp: number): string {\n const date = new Date(timestamp);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffDays === 0) {\n return date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });\n } else if (diffDays === 1) {\n return 'Yesterday';\n } else if (diffDays < 7) {\n return date.toLocaleDateString(undefined, { weekday: 'long' });\n } else {\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n }\n\n private escapeHTML(str: string): string {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n private updateTheme(theme: string | null): void {\n // CSS :host([theme]) selectors handle theming automatically\n // For 'auto' mode, remove the attribute to let @media (prefers-color-scheme) work\n const validTheme = theme === 'light' || theme === 'dark' ? theme : null;\n\n if (\n validTheme === null &&\n this.hasAttribute('theme') &&\n this.getAttribute('theme') !== 'auto'\n ) {\n this.removeAttribute('theme');\n }\n }\n\n private cleanup(): void {\n this.removeEventListeners();\n\n if (this.client) {\n this.client.cancelAllRequests();\n }\n\n if (this.chatView) {\n this.chatView.destroy();\n }\n }\n\n // Public API\n public clearChat(): void {\n this.clearCurrentChat();\n }\n\n public async sendMessage(content: string): Promise<void> {\n if (this.chatView) {\n await this.chatView.sendMessage(content);\n this.saveCurrentSession();\n }\n }\n\n public getMessages(): Message[] {\n return this.chatView?.getMessages() || [];\n }\n\n public getSessions(): ChatSession[] {\n return [...this.sessions];\n }\n\n public getCurrentSession(): ChatSession | null {\n return this.sessions.find((s) => s.id === this.currentSessionId) || null;\n }\n}\n\n// Register the custom element\nif (!customElements.get(COMPONENT_NAME)) {\n customElements.define(COMPONENT_NAME, ChatPageSnippet);\n}\n","/**\n * Search mode specific styles\n */\n\nexport const searchStyles = `\n/* Search view states */\n.search-view {\n transition: var(--search-snippet-transition-slow);\n background: var(--search-snippet-background);\n border-radius: var(--search-snippet-border-radius);\n padding: 0px;\n}\n\n.search-view-collapsed {\n max-height: 60px;\n}\n\n.search-view-expanded {\n max-height: var(--search-snippet-max-height);\n}\n\n\n.search-icon {\n width: var(--search-snippet-icon-size);\n height: var(--search-snippet-icon-size);\n margin-left: var(--search-snippet-icon-margin-left);\n color: var(--search-snippet-text-color);\n}\n\n/* Search input wrapper */\n.search-input-wrapper {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n overflow: hidden;\n transition: max-width var(--search-snippet-transition-slow), \n opacity var(--search-snippet-transition);\n padding: var(--search-snippet-spacing-sm);\n border-radius: var(--search-snippet-border-radius);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n\n\n.search-input {\n flex: 1;\n border: none;\n outline: none;\n background: transparent;\n color: var(--search-snippet-text-color);\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n box-shadow: none;\n padding: 0;\n}\n\n.search-input::placeholder {\n color: var(--search-snippet-text-secondary);\n}\n\n.search-view:has(.search-input:not(:placeholder-shown)) .search-input-wrapper, .search-view:has(.search-input:not(:placeholder-shown)) {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.search-view:focus-within {\n border-color: var(--search-snippet-primary-color);\n box-shadow: inset 0 0 0 3px var(--search-snippet-focus-ring);\n}\n\n.search-view:has(.search-input:not(:placeholder-shown)) .search-content {\n max-height: 600px;\n opacity: 1;\n overflow-y: auto;\n padding: 8px;\n}\n\n.search-submit-button {\n flex-shrink: 0;\n \n border-radius: max(var(--search-snippet-button-min-border-radius, 4px), calc(var(--search-snippet-border-radius) - var(--search-snippet-spacing-sm)))\n}\n\n/* Search content */\n.search-content {\n max-height: 0;\n opacity: 0;\n transition: max-height var(--search-snippet-transition-slow),\n opacity var(--search-snippet-transition);\n position: absolute;\n width: 100%;\n background: var(--search-snippet-background);\n border-bottom-left-radius: var(--search-snippet-border-radius);\n border-bottom-right-radius: var(--search-snippet-border-radius);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-top: none;\n}\n\n.search-content::-webkit-scrollbar {\n width: 8px;\n height: 100px;\n}\n\n.search-content::-webkit-scrollbar-track {\n background: var(--search-snippet-surface);\n \n}\n\n.search-content::-webkit-scrollbar-thumb {\n background: var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n}\n\n.search-content::-webkit-scrollbar-thumb:hover {\n background: var(--search-snippet-text-secondary);\n}\n\n.container {\n overflow: unset;\n position: relative;\n border: none;\n}\n\n.container:has(.search-input:not(:placeholder-shown)) {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n\n/* Override header for search mode */\n\n/* Search results */\n.search-results {\n display: flex;\n flex-direction: column;\n gap: var(--search-snippet-spacing-sm);\n}\n\na.search-result-item {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: var(--search-snippet-spacing-md);\n padding: var(--search-snippet-spacing-md);\n background: var(--search-snippet-surface);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n cursor: pointer;\n transition: var(--search-snippet-transition);\n text-decoration: none;\n color: inherit;\n}\n\n/* Image thumbnail container */\n.search-result-image-container {\n flex-shrink: 0;\n width: 64px;\n height: 64px;\n border-radius: calc(var(--search-snippet-border-radius) - 4px);\n overflow: hidden;\n position: relative;\n}\n\n.search-result-image {\n width: 100%;\n height: 100%;\n object-fit: contain;\n opacity: 0;\n transition: opacity var(--search-snippet-transition);\n}\n\n.search-result-image.loaded {\n opacity: 1;\n}\n\n/* Loading shimmer */\n.search-result-image-loading {\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 90deg,\n var(--search-snippet-surface) 25%,\n var(--search-snippet-border-color) 50%,\n var(--search-snippet-surface) 75%\n );\n background-size: 200% 100%;\n animation: search-image-shimmer 1.5s infinite;\n}\n\n@keyframes search-image-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* Placeholder icon */\n.search-result-image-placeholder {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--search-snippet-text-secondary);\n opacity: 0.5;\n}\n\n.search-result-image-placeholder svg {\n width: 24px;\n height: 24px;\n}\n\n/* Content wrapper */\n.search-result-content {\n flex: 1;\n min-width: 0;\n}\n\na.search-result-item:hover {\n background: var(--search-snippet-hover-background);\n border-color: var(--search-snippet-primary-color);\n transform: translateY(-1px);\n box-shadow: var(--search-snippet-result-item-shadow);\n}\n\na.search-result-item:focus-visible {\n outline: 2px solid var(--search-snippet-primary-color);\n outline-offset: 2px;\n}\n\n.search-result-title {\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n margin-bottom: var(--search-snippet-spacing-xs);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.search-result-snippet {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-description);\n line-height: 1.6;\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.search-result-metadata {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n margin-top: var(--search-snippet-spacing-xs);\n min-width: 0;\n}\n\n.search-result-url {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-primary-color);\n text-decoration: none;\n display: block;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.search-result-url-empty {\n visibility: hidden;\n}\n\n.search-result-date {\n font-size: 12px;\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-secondary);\n text-align: right;\n flex-shrink: 0;\n}\n\n.search-result-url:hover {\n text-decoration: underline;\n}\n\n/* Search header */\n.search-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: var(--search-snippet-spacing-md);\n padding-bottom: var(--search-snippet-spacing-sm);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n.search-count {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n}\n\n/* Search footer */\n.search-footer {\n padding: var(--search-snippet-spacing-md);\n padding-bottom: var(--search-snippet-spacing-xs);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-sm);\n}\n\n/* See more link */\n.search-see-more {\n display: inline-flex;\n align-items: center;\n gap: var(--search-snippet-spacing-xs);\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-primary-color);\n text-decoration: none;\n font-weight: var(--search-snippet-font-weight-medium);\n transition: color var(--search-snippet-transition-fast);\n}\n\n.search-see-more:hover {\n text-decoration: underline;\n}\n\n/* Loading state for search */\n.search-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--search-snippet-spacing-xxl);\n gap: var(--search-snippet-spacing-md);\n color: var(--search-snippet-text-secondary);\n}\n\n/* Empty search state */\n.search-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--search-snippet-spacing-xxl);\n gap: var(--search-snippet-spacing-md);\n color: var(--search-snippet-text-secondary);\n text-align: center;\n}\n\n.search-empty-icon {\n width: 64px;\n height: 64px;\n opacity: 0.5;\n}\n\n.search-empty-title {\n font-size: var(--search-snippet-font-size-lg);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n}\n\n.search-empty-description {\n font-size: var(--search-snippet-font-size-sm);\n}\n\n/* Highlight matching text */\n.search-highlight {\n background: var(--search-snippet-warning-background);\n color: var(--search-snippet-warning-color);\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: var(--search-snippet-font-weight-medium);\n}\n`;\n","/**\n * Search Bar Snippet\n * A search bar with results display\n */\n\nimport type { AISearchClient } from '../api/ai-search.ts';\nimport { POWERED_BY_BRANDING } from '../constants.ts';\nimport { searchStyles } from '../styles/search.ts';\nimport { baseStyles } from '../styles/theme.ts';\nimport type { SearchRequestOptions, SearchResult, SearchSnippetProps } from '../types/index.ts';\nimport {\n createClient,\n createCustomEvent,\n debounce,\n escapeHTML,\n formatDate,\n formatDisplayUrl,\n LOADING_MESSAGE_INTERVAL_MS,\n LOADING_MESSAGES,\n parseAttribute,\n parseBooleanAttribute,\n parseNumberAttribute,\n} from '../utils/index.ts';\n\nconst COMPONENT_NAME = 'search-bar-snippet';\nconst DEFAULT_DISPLAY_RESULTS = 10;\nconst REQUEST_MAX_RESULTS = 100;\n\nexport class SearchBarSnippet extends HTMLElement {\n private shadow: ShadowRoot;\n private client: AISearchClient | null = null;\n private container: HTMLElement | null = null;\n private inputElement: HTMLInputElement | null = null;\n private resultsContainer: HTMLElement | null = null;\n private searchButton: HTMLButtonElement | null = null;\n private debouncedSearch: ((query: string) => void) | null = null;\n private currentSearchController: AbortController | null = null;\n private loadingMessageInterval: ReturnType<typeof setInterval> | null = null;\n private loadingMessageIndex = 0;\n\n // Event handler references for cleanup\n private handleInputChange: ((e: Event) => void) | null = null;\n private handleInputKeydownEnter: ((e: KeyboardEvent) => void) | null = null;\n private handleInputKeydownEscape: ((e: KeyboardEvent) => void) | null = null;\n private handleSearchButtonClick: (() => void) | null = null;\n\n static get observedAttributes() {\n return [\n 'api-url',\n 'placeholder',\n 'max-results',\n 'debounce-ms',\n 'theme',\n 'hide-branding',\n 'show-url',\n 'show-date',\n 'hide-thumbnails',\n 'see-more',\n 'request-options',\n ] as const;\n }\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback(): void {\n this.initializeClient();\n this.render();\n this.dispatchEvent(createCustomEvent('ready', undefined));\n }\n\n disconnectedCallback(): void {\n this.cleanup();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n if (name === 'api-url') {\n this.initializeClient();\n } else if (name === 'theme') {\n // Theme changes are handled automatically by CSS :host([theme]) selectors\n // But we trigger this to ensure any dynamic updates are applied\n this.updateTheme(newValue);\n }\n }\n\n private getProps(): SearchSnippetProps {\n return {\n apiUrl: parseAttribute(this.getAttribute('api-url'), ''),\n placeholder: parseAttribute(this.getAttribute('placeholder'), 'Search...'),\n maxResults: parseNumberAttribute(this.getAttribute('max-results'), 10),\n debounceMs: parseNumberAttribute(this.getAttribute('debounce-ms'), 300),\n theme: parseAttribute(this.getAttribute('theme'), 'auto') as 'light' | 'dark' | 'auto',\n hideBranding: parseBooleanAttribute(this.getAttribute('hide-branding'), false),\n showUrl: parseBooleanAttribute(this.getAttribute('show-url'), false),\n showDate: parseBooleanAttribute(this.getAttribute('show-date'), false),\n hideThumbnails: parseBooleanAttribute(this.getAttribute('hide-thumbnails'), false),\n seeMore: parseAttribute(this.getAttribute('see-more'), ''),\n };\n }\n\n private getRequestOptions(): SearchRequestOptions | undefined {\n const rawRequestOptions = this.getAttribute('request-options');\n\n if (!rawRequestOptions) {\n return undefined;\n }\n\n try {\n const parsedRequestOptions = JSON.parse(rawRequestOptions) as unknown;\n\n if (\n parsedRequestOptions === null ||\n typeof parsedRequestOptions !== 'object' ||\n Array.isArray(parsedRequestOptions)\n ) {\n throw new Error('request-options must be a JSON object');\n }\n\n return parsedRequestOptions as SearchRequestOptions;\n } catch (error) {\n console.error('SearchBarSnippet: invalid request-options attribute', error);\n return undefined;\n }\n }\n\n private initializeClient(): void {\n const props = this.getProps();\n\n if (!props.apiUrl) {\n console.error('SearchBarSnippet: api-url attribute is required');\n this.client = null;\n this.showMissingApiUrlError();\n return;\n }\n\n try {\n this.client = createClient(props.apiUrl);\n } catch (error) {\n console.error('SearchBarSnippet:', error);\n }\n }\n\n private render(): void {\n const props = this.getProps();\n\n // Create debounced search function\n const searchFn = (query: string) => this.performSearch(query);\n this.debouncedSearch = debounce(\n searchFn as (...args: unknown[]) => unknown,\n props.debounceMs || 400\n ) as (query: string) => void;\n\n const style = document.createElement('style');\n style.textContent = `${baseStyles}\\n${searchStyles}`;\n\n this.container = document.createElement('div');\n this.container.className = 'container';\n this.container.innerHTML = `\n <div class=\"search-view\"> \n <div class=\"search-input-wrapper\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"search-icon\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\"><path d=\"M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z\"/></svg>\n <input\n type=\"text\"\n name=\"search-input\"\n class=\"search-input\"\n placeholder=\"${escapeHTML(props.placeholder || 'Search...')}\"\n aria-label=\"Search input\"\n autocomplete=\"off\"\n />\n <button class=\"button search-submit-button\" aria-label=\"Search\">\n <span>Search</span>\n </button>\n </div>\n <div class=\"search-content\">\n <div class=\"search-results-wrapper\">\n <!-- Results will be inserted here -->\n </div>\n </div>\n </div>\n `;\n\n this.shadow.innerHTML = '';\n this.shadow.appendChild(style);\n this.shadow.appendChild(this.container);\n\n // Get references to elements\n this.inputElement = this.container.querySelector('.search-input');\n this.resultsContainer = this.container.querySelector('.search-results-wrapper');\n this.searchButton = this.container.querySelector('.search-submit-button');\n\n this.attachEventListeners();\n\n // Show error immediately if api-url was missing when the component was connected\n if (!this.client) {\n this.showMissingApiUrlError();\n }\n }\n\n private attachEventListeners(): void {\n if (!this.inputElement) return;\n\n // Input event for real-time search\n this.handleInputChange = (e: Event) => {\n const target = e.target as HTMLInputElement;\n const query = target.value.trim();\n\n if (query.length > 0 && this.debouncedSearch) {\n this.debouncedSearch(query);\n } else {\n this.showEmptyState();\n }\n };\n this.inputElement.addEventListener('input', this.handleInputChange);\n\n // Enter key to search immediately\n this.handleInputKeydownEnter = (e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n const query = (e.target as HTMLInputElement).value.trim();\n if (query.length > 0) {\n this.performSearch(query);\n }\n }\n };\n this.inputElement.addEventListener('keydown', this.handleInputKeydownEnter);\n\n this.handleInputKeydownEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this.inputElement) {\n this.inputElement.value = '';\n }\n };\n window.addEventListener('keydown', this.handleInputKeydownEscape);\n\n // Search button click\n if (this.searchButton) {\n this.handleSearchButtonClick = () => {\n const query = this.inputElement?.value.trim() || '';\n if (query.length > 0) {\n this.performSearch(query);\n }\n };\n this.searchButton.addEventListener('click', this.handleSearchButtonClick);\n }\n }\n\n private async performSearch(query: string): Promise<void> {\n if (!this.client) {\n this.showMissingApiUrlError();\n return;\n }\n\n // Cancel any existing request before starting a new one\n if (this.currentSearchController) {\n this.currentSearchController.abort();\n this.currentSearchController = null;\n }\n\n // Create new controller for this request\n this.currentSearchController = new AbortController();\n this.showLoadingState();\n\n try {\n const results = await this.client.search(query, {\n signal: this.currentSearchController.signal,\n maxResults: REQUEST_MAX_RESULTS,\n request: this.getRequestOptions(),\n });\n const props = this.getProps();\n const visibleResults = results.slice(0, props.maxResults || DEFAULT_DISPLAY_RESULTS);\n this.displayResults(visibleResults, query, results.length);\n } catch (error) {\n // Don't show error state for cancelled requests\n if ((error as Error).name === 'AbortError') {\n return;\n }\n this.showErrorState((error as Error).message);\n } finally {\n this.currentSearchController = null;\n }\n }\n\n private displayResults(\n results: SearchResult[],\n query: string,\n totalResults = results.length\n ): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n if (results.length === 0) {\n this.showNoResultsState(query);\n return;\n }\n const props = this.getProps();\n const brandingHTML = props.hideBranding\n ? ''\n : `<div class=\"powered-by-inline\">${POWERED_BY_BRANDING}</div>`;\n const hasMoreResults = totalResults > results.length;\n const resultsCountLabel = hasMoreResults\n ? `Showing ${results.length} of ${totalResults} results`\n : `Found ${totalResults} result${totalResults === 1 ? '' : 's'}`;\n\n const seeMoreHTML =\n props.seeMore && hasMoreResults\n ? `<div class=\"search-footer\">\n <a href=\"${escapeHTML(props.seeMore + encodeURIComponent(query))}\" class=\"search-see-more\">\n <span>See more results</span>\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14\"/><path d=\"m12 5 7 7-7 7\"/></svg>\n </a>\n </div>`\n : '';\n\n const resultsHTML = `\n <div class=\"search-header\">\n <div class=\"search-count\">\n ${resultsCountLabel}\n </div>\n ${brandingHTML}\n </div>\n <div class=\"search-results\">\n ${results.map((result) => this.renderResult(result)).join('')}\n </div>\n ${seeMoreHTML}\n `;\n\n this.resultsContainer.innerHTML = resultsHTML;\n\n // Attach click handlers to results\n this.attachResultHandlers();\n }\n\n private renderResult(result: SearchResult): string {\n const props = this.getProps();\n const imageHTML = props.hideThumbnails\n ? ''\n : this.renderResultImage(result.image, result.title);\n const href = result.url ? escapeHTML(result.url) : '#';\n const displayUrl = result.url ? escapeHTML(formatDisplayUrl(result.url)) : '';\n const timestampHTML =\n props.showDate && result.timestamp !== undefined\n ? `<div class=\"search-result-date\">${escapeHTML(formatDate(result.timestamp))}</div>`\n : '';\n const metadataHTML =\n (props.showUrl && result.url) || timestampHTML\n ? `<div class=\"search-result-metadata\">\n ${props.showUrl && result.url ? `<span class=\"search-result-url\">${displayUrl}</span>` : '<span class=\"search-result-url search-result-url-empty\"></span>'}\n ${timestampHTML}\n </div>`\n : '';\n\n return `\n <a href=\"${href}\" class=\"search-result-item\" data-result-id=\"${escapeHTML(result.url || '')}\">\n ${imageHTML}\n <div class=\"search-result-content\">\n <div class=\"search-result-title\">${escapeHTML(result.title || '')}</div>\n <div class=\"search-result-snippet\">${escapeHTML(result.description || '')}</div>\n ${metadataHTML}\n </div>\n </a>\n `;\n }\n\n private renderResultImage(imageUrl: string | undefined, alt: string): string {\n const placeholderSVG = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/></svg>`;\n\n if (!imageUrl) {\n return `\n <div class=\"search-result-image-container\">\n <div class=\"search-result-image-placeholder\">${placeholderSVG}</div>\n </div>\n `;\n }\n\n return `\n <div class=\"search-result-image-container\">\n <div class=\"search-result-image-loading\"></div>\n <div class=\"search-result-image-placeholder\" style=\"display: none;\">${placeholderSVG}</div>\n <img \n class=\"search-result-image\" \n src=\"${escapeHTML(imageUrl)}\" \n alt=\"${escapeHTML(alt)}\"\n loading=\"lazy\"\n />\n </div>\n `;\n }\n\n private attachResultHandlers(): void {\n const resultItems = this.container?.querySelectorAll('.search-result-item');\n if (!resultItems) return;\n\n // Handle clicks on results without URLs (prevent default anchor behavior)\n for (const item of resultItems) {\n const href = item.getAttribute('href');\n if (href === '#') {\n item.addEventListener('click', (e) => {\n e.preventDefault();\n });\n }\n }\n\n // Image load/error handlers\n const images = this.container?.querySelectorAll('.search-result-image');\n images?.forEach((img) => {\n img.addEventListener('load', () => {\n img.classList.add('loaded');\n const container = img.closest('.search-result-image-container');\n container?.querySelector('.search-result-image-loading')?.remove();\n });\n\n img.addEventListener('error', () => {\n const container = img.closest('.search-result-image-container');\n container?.querySelector('.search-result-image-loading')?.remove();\n const placeholder = container?.querySelector(\n '.search-result-image-placeholder'\n ) as HTMLElement;\n if (placeholder) placeholder.style.display = 'flex';\n (img as HTMLElement).style.display = 'none';\n });\n });\n }\n\n private showLoadingState(): void {\n if (!this.resultsContainer) return;\n\n this.clearLoadingInterval();\n this.loadingMessageIndex = Math.floor(Math.random() * LOADING_MESSAGES.length);\n\n this.resultsContainer.innerHTML = `\n <div class=\"search-loading\">\n <div class=\"loading\" aria-label=\"Loading\"></div>\n <div class=\"loading-text loading-text-animate\">${LOADING_MESSAGES[this.loadingMessageIndex]}</div>\n </div>\n `;\n\n this.startLoadingInterval();\n }\n\n private startLoadingInterval(): void {\n this.loadingMessageInterval = setInterval(() => {\n this.loadingMessageIndex = (this.loadingMessageIndex + 1) % LOADING_MESSAGES.length;\n const textEl = this.resultsContainer?.querySelector('.loading-text');\n if (textEl) {\n textEl.classList.remove('loading-text-animate');\n void (textEl as HTMLElement).offsetWidth;\n textEl.textContent = LOADING_MESSAGES[this.loadingMessageIndex];\n textEl.classList.add('loading-text-animate');\n }\n }, LOADING_MESSAGE_INTERVAL_MS);\n }\n\n private clearLoadingInterval(): void {\n if (this.loadingMessageInterval) {\n clearInterval(this.loadingMessageInterval);\n this.loadingMessageInterval = null;\n }\n }\n\n private showEmptyState(): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n this.resultsContainer.innerHTML = `\n <div class=\"search-empty\">\n <svg class=\"search-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"></circle>\n <path d=\"m21 21-4.35-4.35\"></path>\n </svg>\n <div class=\"search-empty-title\">Start Searching</div>\n <div class=\"search-empty-description\">\n Enter a query to search for results\n </div>\n </div>\n `;\n }\n\n private showNoResultsState(query: string): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n this.resultsContainer.innerHTML = `\n <div class=\"search-empty\">\n <svg class=\"search-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"></circle>\n <line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"></line>\n </svg>\n <div class=\"search-empty-title\">No Results Found</div>\n <div class=\"search-empty-description\">\n No results found for \"${escapeHTML(query)}\"\n </div>\n </div>\n `;\n }\n\n private showErrorState(message: string): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n this.resultsContainer.innerHTML = `\n <div class=\"error\">\n <strong>Error:</strong> ${escapeHTML(message)}\n </div>\n `;\n }\n\n private showMissingApiUrlError(): void {\n if (this.resultsContainer) {\n this.showErrorState('The api-url attribute is required. Please provide a valid API URL.');\n }\n }\n\n private updateTheme(theme: string | null): void {\n // CSS custom properties via :host([theme]) selectors handle the actual theming\n // This method is here for any additional theme-related logic if needed\n const validTheme = theme === 'light' || theme === 'dark' || theme === 'auto' ? theme : 'auto';\n\n // Ensure the attribute is set on the host for CSS selectors\n if (validTheme === 'auto') {\n // Let the @media (prefers-color-scheme) handle it\n this.removeAttribute('theme');\n } else {\n this.setAttribute('theme', validTheme);\n }\n }\n\n private cleanup(): void {\n this.clearLoadingInterval();\n\n // Cancel any in-flight search request\n if (this.currentSearchController) {\n this.currentSearchController.abort();\n this.currentSearchController = null;\n }\n\n if (this.client) {\n this.client.cancelAllRequests();\n }\n\n // Remove event listeners\n if (this.inputElement) {\n if (this.handleInputChange) {\n this.inputElement.removeEventListener('input', this.handleInputChange);\n }\n if (this.handleInputKeydownEnter) {\n this.inputElement.removeEventListener('keydown', this.handleInputKeydownEnter);\n }\n if (this.handleInputKeydownEscape) {\n window.removeEventListener('keydown', this.handleInputKeydownEscape);\n }\n }\n\n if (this.searchButton && this.handleSearchButtonClick) {\n this.searchButton.removeEventListener('click', this.handleSearchButtonClick);\n }\n\n // Clear handler references\n this.handleInputChange = null;\n this.handleInputKeydownEnter = null;\n this.handleInputKeydownEscape = null;\n this.handleSearchButtonClick = null;\n }\n\n // Public API\n public async search(query: string): Promise<void> {\n await this.performSearch(query);\n }\n}\n\n// Register the custom element\nif (!customElements.get(COMPONENT_NAME)) {\n customElements.define(COMPONENT_NAME, SearchBarSnippet);\n}\n","/**\n * Modal search combobox specific styles\n */\n\nexport const modalStyles = `\n/* Modal backdrop */\n.modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n z-index: var(--search-snippet-z-modal);\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--search-snippet-transition), visibility var(--search-snippet-transition);\n}\n\n.modal-backdrop.open {\n opacity: 1;\n visibility: visible;\n}\n\n/* Modal container */\n.modal-container {\n position: fixed;\n top: 15%;\n left: 50%;\n transform: translateX(-50%) scale(0.95);\n width: 90%;\n max-width: 600px;\n max-height: 70vh;\n background: var(--search-snippet-background);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n box-shadow: var(--search-snippet-shadow-lg);\n z-index: calc(var(--search-snippet-z-modal) + 1);\n display: flex;\n flex-direction: column;\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--search-snippet-transition), \n visibility var(--search-snippet-transition),\n transform var(--search-snippet-transition);\n}\n\n.modal-container.open {\n opacity: 1;\n visibility: visible;\n transform: translateX(-50%) scale(1);\n}\n\n/* Modal header with search input */\n.modal-header {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n padding: var(--search-snippet-spacing-md);\n border-bottom: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n}\n\n.modal-search-icon {\n width: var(--search-snippet-icon-size);\n height: var(--search-snippet-icon-size);\n color: var(--search-snippet-text-secondary);\n flex-shrink: 0;\n}\n\n.modal-search-input {\n flex: 1;\n border: none;\n outline: none;\n background: transparent;\n color: var(--search-snippet-text-color);\n font-size: var(--search-snippet-font-size-lg);\n font-family: var(--search-snippet-font-family);\n font-weight: var(--search-snippet-font-weight-normal);\n padding: var(--search-snippet-spacing-xs) 0;\n}\n\n.modal-search-input::placeholder {\n color: var(--search-snippet-text-secondary);\n}\n\n.modal-shortcut-hint {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-xs);\n color: var(--search-snippet-text-secondary);\n font-size: var(--search-snippet-font-size-sm);\n flex-shrink: 0;\n}\n\n.modal-kbd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 22px;\n padding: 0 var(--search-snippet-spacing-xs);\n background: var(--search-snippet-surface);\n border: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n border-radius: 4px;\n font-size: var(--search-snippet-font-size-sm);\n font-family: var(--search-snippet-font-family);\n color: var(--search-snippet-text-secondary);\n}\n\n/* Modal content (results area) */\n.modal-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: var(--search-snippet-spacing-sm);\n}\n\n.modal-content::-webkit-scrollbar {\n width: 8px;\n}\n\n.modal-content::-webkit-scrollbar-track {\n background: var(--search-snippet-surface);\n}\n\n.modal-content::-webkit-scrollbar-thumb {\n background: var(--search-snippet-border-color);\n border-radius: var(--search-snippet-border-radius);\n}\n\n.modal-content::-webkit-scrollbar-thumb:hover {\n background: var(--search-snippet-text-secondary);\n}\n\n/* Results list */\n.modal-results {\n display: flex;\n flex-direction: column;\n gap: var(--search-snippet-spacing-xs);\n}\n\na.modal-result-item {\n padding: var(--search-snippet-spacing-md);\n background: transparent;\n border: var(--search-snippet-border-width) solid transparent;\n border-radius: calc(var(--search-snippet-border-radius) - 4px);\n cursor: pointer;\n transition: var(--search-snippet-transition-fast);\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: var(--search-snippet-spacing-md);\n text-decoration: none;\n color: inherit;\n}\n\n/* Image thumbnail container */\n.modal-result-image-container {\n flex-shrink: 0;\n width: 48px;\n height: 48px;\n border-radius: 6px;\n overflow: hidden;\n position: relative;\n}\n\n.modal-result-image {\n width: 100%;\n height: 100%;\n object-fit: contain;\n opacity: 0;\n transition: opacity var(--search-snippet-transition);\n}\n\n.modal-result-image.loaded {\n opacity: 1;\n}\n\n/* Loading shimmer */\n.modal-result-image-loading {\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 90deg,\n var(--search-snippet-surface) 25%,\n var(--search-snippet-border-color) 50%,\n var(--search-snippet-surface) 75%\n );\n background-size: 200% 100%;\n animation: modal-image-shimmer 1.5s infinite;\n}\n\n@keyframes modal-image-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* Placeholder icon */\n.modal-result-image-placeholder {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--search-snippet-text-secondary);\n opacity: 0.5;\n}\n\n.modal-result-image-placeholder svg {\n width: 20px;\n height: 20px;\n}\n\n/* Content wrapper */\n.modal-result-content {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: var(--search-snippet-spacing-xs);\n}\n\na.modal-result-item:hover,\na.modal-result-item.active {\n background: var(--search-snippet-hover-background);\n border-color: var(--search-snippet-border-color);\n}\n\na.modal-result-item.active {\n border-color: var(--search-snippet-primary-color);\n background: var(--search-snippet-focus-ring);\n}\n\na.modal-result-item:focus-visible {\n outline: 2px solid var(--search-snippet-primary-color);\n outline-offset: -2px;\n}\n\n.modal-result-title {\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.modal-result-description {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-description);\n line-height: 1.5;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.modal-result-metadata {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-sm);\n min-width: 0;\n}\n\n.modal-result-url {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-primary-color);\n text-decoration: none;\n display: block;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.modal-result-url-empty {\n visibility: hidden;\n}\n\n.modal-result-date {\n font-size: 12px;\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-secondary);\n text-align: right;\n flex-shrink: 0;\n}\n\n.modal-result-url:hover {\n text-decoration: underline;\n}\n\n/* Result group header */\n.modal-group-header {\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n font-size: var(--search-snippet-font-size-sm);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n/* Loading state */\n.modal-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--search-snippet-spacing-xxl);\n gap: var(--search-snippet-spacing-md);\n color: var(--search-snippet-text-secondary);\n}\n\n/* Empty state */\n.modal-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--search-snippet-spacing-xxl);\n gap: var(--search-snippet-spacing-md);\n color: var(--search-snippet-text-secondary);\n text-align: center;\n}\n\n.modal-empty-icon {\n width: 48px;\n height: 48px;\n opacity: 0.5;\n}\n\n.modal-empty-title {\n font-size: var(--search-snippet-font-size-base);\n font-weight: var(--search-snippet-font-weight-medium);\n color: var(--search-snippet-text-color);\n}\n\n.modal-empty-description {\n font-size: var(--search-snippet-font-size-sm);\n}\n\n/* Footer */\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--search-snippet-spacing-sm) var(--search-snippet-spacing-md);\n border-top: var(--search-snippet-border-width) solid var(--search-snippet-border-color);\n background: var(--search-snippet-surface);\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n border-radius: 0 0 var(--search-snippet-border-radius) var(--search-snippet-border-radius);\n}\n\n.modal-footer-hints {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-md);\n}\n\n.modal-footer-hint {\n display: flex;\n align-items: center;\n gap: var(--search-snippet-spacing-xs);\n}\n\n.modal-footer-hint .modal-kbd {\n min-width: 20px;\n height: 20px;\n font-size: 11px;\n}\n\n/* Results count */\n.modal-results-count {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n}\n\n/* Powered by in modal footer */\n.modal-footer .powered-by-inline {\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-text-secondary);\n}\n\n.modal-footer .powered-by-inline a {\n color: var(--search-snippet-text-secondary);\n text-decoration: none;\n transition: color var(--search-snippet-transition-fast);\n}\n\n.modal-footer .powered-by-inline a:hover {\n color: var(--search-snippet-primary-color);\n}\n\n/* See more link */\n.modal-see-more {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--search-snippet-spacing-xs);\n padding: var(--search-snippet-spacing-md);\n font-size: var(--search-snippet-font-size-sm);\n color: var(--search-snippet-primary-color);\n text-decoration: none;\n font-weight: var(--search-snippet-font-weight-medium);\n transition: background var(--search-snippet-transition-fast);\n padding-bottom: var(--search-snippet-spacing-xs);\n}\n\n.modal-see-more:hover {\n background: var(--search-snippet-hover);\n text-decoration: underline;\n}\n\n/* Responsive adjustments */\n@media (max-width: 640px) {\n .modal-container {\n top: 10%;\n width: 95%;\n max-height: 80vh;\n }\n\n .modal-footer-hints {\n display: none;\n }\n}\n\n/* Animation for modal open */\n@keyframes modal-slide-in {\n from {\n opacity: 0;\n transform: translateX(-50%) scale(0.95) translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) scale(1) translateY(0);\n }\n}\n\n.modal-container.open {\n animation: modal-slide-in var(--search-snippet-transition) ease-out;\n}\n`;\n","/**\n * Search Modal Snippet\n * A modal combobox search component with keyboard navigation\n * Opens with Cmd/Ctrl+K shortcut by default\n */\n\nimport type { AISearchClient } from '../api/ai-search.ts';\nimport { POWERED_BY_BRANDING } from '../constants.ts';\nimport { modalStyles } from '../styles/modal.ts';\nimport { baseStyles } from '../styles/theme.ts';\nimport type { SearchRequestOptions, SearchResult, SearchSnippetProps } from '../types/index.ts';\nimport {\n createClient,\n createCustomEvent,\n debounce,\n escapeHTML,\n formatDate,\n formatDisplayUrl,\n LOADING_MESSAGE_INTERVAL_MS,\n LOADING_MESSAGES,\n parseAttribute,\n parseBooleanAttribute,\n parseNumberAttribute,\n} from '../utils/index.ts';\n\nconst COMPONENT_NAME = 'search-modal-snippet';\nconst DEFAULT_DISPLAY_RESULTS = 10;\nconst REQUEST_MAX_RESULTS = 100;\n\nexport interface SearchModalProps extends SearchSnippetProps {\n /** Keyboard shortcut key (default: 'k') */\n shortcut?: string;\n /** Whether to use meta key (Cmd on Mac) or ctrl key */\n useMetaKey?: boolean;\n}\n\nexport class SearchModalSnippet extends HTMLElement {\n private shadow: ShadowRoot;\n private client: AISearchClient | null = null;\n private backdrop: HTMLElement | null = null;\n private modal: HTMLElement | null = null;\n private inputElement: HTMLInputElement | null = null;\n private resultsContainer: HTMLElement | null = null;\n private footerCount: HTMLElement | null = null;\n private isOpen = false;\n private results: SearchResult[] = [];\n private activeIndex = -1;\n private debouncedSearch: (((query: string) => void) & { cancel: () => void }) | null = null;\n private currentSearchController: AbortController | null = null;\n private loadingMessageInterval: ReturnType<typeof setInterval> | null = null;\n private loadingMessageIndex = 0;\n\n // Event handler references for cleanup\n private handleGlobalKeydown: ((e: KeyboardEvent) => void) | null = null;\n private handleInputChange: ((e: Event) => void) | null = null;\n private handleInputKeydown: ((e: KeyboardEvent) => void) | null = null;\n private handleBackdropClick: ((e: MouseEvent) => void) | null = null;\n\n // Scroll lock state\n private savedBodyStyles: {\n overflow: string;\n position: string;\n top: string;\n width: string;\n } | null = null;\n private savedHtmlOverflow: string | null = null;\n\n static get observedAttributes() {\n return [\n 'api-url',\n 'placeholder',\n 'max-results',\n 'theme',\n 'shortcut',\n 'use-meta-key',\n 'debounce-ms',\n 'hide-branding',\n 'show-url',\n 'show-date',\n 'hide-thumbnails',\n 'see-more',\n 'request-options',\n ] as const;\n }\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback(): void {\n this.initializeClient();\n this.render();\n this.attachGlobalKeyboardShortcut();\n this.dispatchEvent(createCustomEvent('ready', undefined));\n }\n\n disconnectedCallback(): void {\n this.cleanup();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n if (name === 'api-url') {\n this.initializeClient();\n } else if (name === 'theme') {\n this.updateTheme(newValue);\n }\n }\n\n private getProps(): SearchModalProps {\n return {\n apiUrl: parseAttribute(this.getAttribute('api-url'), ''),\n placeholder: parseAttribute(this.getAttribute('placeholder'), 'Search...'),\n maxResults: parseNumberAttribute(this.getAttribute('max-results'), 10),\n debounceMs: parseNumberAttribute(this.getAttribute('debounce-ms'), 300),\n theme: parseAttribute(this.getAttribute('theme'), 'auto') as 'light' | 'dark' | 'auto',\n shortcut: parseAttribute(this.getAttribute('shortcut'), 'k'),\n useMetaKey: this.getAttribute('use-meta-key') !== 'false',\n hideBranding: parseBooleanAttribute(this.getAttribute('hide-branding'), false),\n showUrl: parseBooleanAttribute(this.getAttribute('show-url'), false),\n showDate: parseBooleanAttribute(this.getAttribute('show-date'), false),\n hideThumbnails: parseBooleanAttribute(this.getAttribute('hide-thumbnails'), false),\n seeMore: parseAttribute(this.getAttribute('see-more'), ''),\n };\n }\n\n private getRequestOptions(): SearchRequestOptions | undefined {\n const rawRequestOptions = this.getAttribute('request-options');\n\n if (!rawRequestOptions) {\n return undefined;\n }\n\n try {\n const parsedRequestOptions = JSON.parse(rawRequestOptions) as unknown;\n\n if (\n parsedRequestOptions === null ||\n typeof parsedRequestOptions !== 'object' ||\n Array.isArray(parsedRequestOptions)\n ) {\n throw new Error('request-options must be a JSON object');\n }\n\n return parsedRequestOptions as SearchRequestOptions;\n } catch (error) {\n console.error('SearchModalSnippet: invalid request-options attribute', error);\n return undefined;\n }\n }\n\n private initializeClient(): void {\n const props = this.getProps();\n\n if (!props.apiUrl) {\n console.error('SearchModalSnippet: api-url attribute is required');\n this.client = null;\n this.showMissingApiUrlError();\n return;\n }\n\n try {\n this.client = createClient(props.apiUrl);\n } catch (error) {\n console.error('SearchModalSnippet:', error);\n }\n }\n\n private render(): void {\n const props = this.getProps();\n\n // Create debounced search function\n const searchFn = (query: string) => this.performSearch(query);\n this.debouncedSearch = debounce(\n searchFn as (...args: unknown[]) => unknown,\n props.debounceMs || 300\n );\n\n const style = document.createElement('style');\n style.textContent = `${baseStyles}\\n${modalStyles}`;\n\n const brandingHTML = props.hideBranding\n ? ''\n : `<div class=\"powered-by-inline\">${POWERED_BY_BRANDING}</div>`;\n\n const container = document.createElement('div');\n container.innerHTML = `\n <div class=\"modal-backdrop\" role=\"presentation\"></div>\n <div class=\"modal-container\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"modal-title\">\n <div class=\"modal-header\">\n <svg class=\"modal-search-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -960 960 960\" fill=\"currentColor\">\n <path d=\"M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z\"/>\n </svg>\n <input\n type=\"text\"\n class=\"modal-search-input\"\n placeholder=\"${escapeHTML(props.placeholder || 'Search...')}\"\n aria-label=\"Search\"\n aria-autocomplete=\"list\"\n aria-controls=\"modal-results-list\"\n aria-expanded=\"false\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n />\n </div>\n <div class=\"modal-content\">\n <div class=\"modal-results\" id=\"modal-results-list\" role=\"listbox\" aria-label=\"Search results\">\n ${this.renderEmptyState()}\n </div>\n </div>\n <div class=\"modal-footer\">\n <div class=\"modal-footer-hints\">\n <div class=\"modal-footer-hint\">\n <kbd class=\"modal-kbd\">↑</kbd>\n <kbd class=\"modal-kbd\">↓</kbd>\n <span>Navigate</span>\n </div>\n <div class=\"modal-footer-hint\">\n <kbd class=\"modal-kbd\">↵</kbd>\n <span>Select</span>\n </div>\n <div class=\"modal-footer-hint\">\n <kbd class=\"modal-kbd\">Esc</kbd>\n <span>Close</span>\n </div>\n </div>\n ${brandingHTML}\n </div>\n </div>\n `;\n\n this.shadow.innerHTML = '';\n this.shadow.appendChild(style);\n this.shadow.appendChild(container);\n\n // Get references to elements\n this.backdrop = this.shadow.querySelector('.modal-backdrop');\n this.modal = this.shadow.querySelector('.modal-container');\n this.inputElement = this.shadow.querySelector('.modal-search-input');\n this.resultsContainer = this.shadow.querySelector('.modal-results');\n this.footerCount = this.shadow.querySelector('.modal-results-count');\n\n this.attachEventListeners();\n\n // Show error immediately if api-url was missing when the component was connected\n if (!this.client) {\n this.showMissingApiUrlError();\n }\n }\n\n private attachGlobalKeyboardShortcut(): void {\n const props = this.getProps();\n const shortcutKey = props.shortcut?.toLowerCase() || 'k';\n\n this.handleGlobalKeydown = (e: KeyboardEvent) => {\n // Check for shortcut to open modal\n const modifierPressed = props.useMetaKey ? e.metaKey || e.ctrlKey : e.ctrlKey;\n\n if (modifierPressed && e.key.toLowerCase() === shortcutKey && !this.isOpen) {\n e.preventDefault();\n this.open();\n }\n };\n\n document.addEventListener('keydown', this.handleGlobalKeydown);\n }\n\n private attachEventListeners(): void {\n if (!this.inputElement || !this.backdrop) return;\n\n // Input change event\n this.handleInputChange = (e: Event) => {\n const target = e.target as HTMLInputElement;\n const query = target.value.trim();\n\n if (query.length > 0 && this.debouncedSearch) {\n this.debouncedSearch(query);\n } else {\n this.debouncedSearch?.cancel();\n this.currentSearchController?.abort();\n this.results = [];\n this.activeIndex = -1;\n this.showEmptyState();\n }\n };\n this.inputElement.addEventListener('input', this.handleInputChange);\n\n // Keyboard navigation\n this.handleInputKeydown = (e: KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n this.navigateResults(1);\n break;\n case 'ArrowUp':\n e.preventDefault();\n this.navigateResults(-1);\n break;\n case 'Enter':\n e.preventDefault();\n this.selectActiveResult();\n break;\n case 'Escape':\n e.preventDefault();\n this.close();\n break;\n }\n };\n this.inputElement.addEventListener('keydown', this.handleInputKeydown);\n\n // Backdrop click to close\n this.handleBackdropClick = (e: MouseEvent) => {\n if (e.target === this.backdrop) {\n this.close();\n }\n };\n this.backdrop.addEventListener('click', this.handleBackdropClick);\n }\n\n private navigateResults(direction: number): void {\n if (this.results.length === 0) return;\n\n const newIndex = this.activeIndex + direction;\n\n if (newIndex < 0) {\n this.activeIndex = this.results.length - 1;\n } else if (newIndex >= this.results.length) {\n this.activeIndex = 0;\n } else {\n this.activeIndex = newIndex;\n }\n\n this.updateActiveResult();\n }\n\n private updateActiveResult(): void {\n const items = this.resultsContainer?.querySelectorAll('.modal-result-item');\n if (!items) return;\n\n items.forEach((item, index) => {\n if (index === this.activeIndex) {\n item.classList.add('active');\n item.setAttribute('aria-selected', 'true');\n // Scroll into view if needed\n (item as HTMLElement).scrollIntoView({ block: 'nearest' });\n } else {\n item.classList.remove('active');\n item.setAttribute('aria-selected', 'false');\n }\n });\n\n // Update aria-activedescendant\n if (this.inputElement && this.activeIndex >= 0) {\n this.inputElement.setAttribute('aria-activedescendant', `result-${this.activeIndex}`);\n } else if (this.inputElement) {\n this.inputElement.removeAttribute('aria-activedescendant');\n }\n }\n\n private selectActiveResult(): void {\n if (this.activeIndex < 0 || this.activeIndex >= this.results.length) {\n // If no result selected but there's a query, perform immediate search\n const query = this.inputElement?.value.trim();\n if (query && query.length > 0) {\n this.performSearch(query);\n }\n return;\n }\n\n const result = this.results[this.activeIndex];\n this.dispatchEvent(\n createCustomEvent('result-select', {\n result,\n index: this.activeIndex,\n })\n );\n\n // Navigate to URL - click the active link element to trigger navigation\n const activeItem = this.resultsContainer?.querySelector(\n `.modal-result-item[data-index=\"${this.activeIndex}\"]`\n ) as HTMLAnchorElement | null;\n\n if (activeItem && result.url) {\n activeItem.click();\n }\n\n this.close();\n }\n\n private async performSearch(query: string): Promise<void> {\n if (!this.client) {\n this.showMissingApiUrlError();\n return;\n }\n\n // Cancel any existing request before starting a new one\n if (this.currentSearchController) {\n this.currentSearchController.abort();\n this.currentSearchController = null;\n }\n\n // Create new controller for this request\n this.currentSearchController = new AbortController();\n this.showLoadingState();\n\n try {\n const results = await this.client.search(query, {\n signal: this.currentSearchController.signal,\n maxResults: REQUEST_MAX_RESULTS,\n request: this.getRequestOptions(),\n });\n const props = this.getProps();\n this.results = results.slice(0, props.maxResults || DEFAULT_DISPLAY_RESULTS);\n this.activeIndex = this.results.length > 0 ? 0 : -1;\n this.displayResults(this.results, query, results.length);\n } catch (error) {\n // Don't show error state for cancelled requests\n if ((error as Error).name === 'AbortError') {\n return;\n }\n this.showErrorState((error as Error).message);\n } finally {\n this.currentSearchController = null;\n }\n }\n\n private displayResults(\n results: SearchResult[],\n query: string,\n totalResults = results.length\n ): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n if (results.length === 0) {\n this.showNoResultsState(query);\n return;\n }\n\n const props = this.getProps();\n const resultsHTML = results.map((result, index) => this.renderResult(result, index)).join('');\n const hasMoreResults = totalResults > results.length;\n const resultsCountLabel = hasMoreResults\n ? `Showing ${results.length} of ${totalResults} results`\n : `${totalResults} result${totalResults === 1 ? '' : 's'}`;\n const seeMoreHTML =\n props.seeMore && hasMoreResults\n ? `<a href=\"${escapeHTML(props.seeMore + encodeURIComponent(query))}\" class=\"modal-see-more\">\n <span>See more results</span>\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14\"/><path d=\"m12 5 7 7-7 7\"/></svg>\n </a>`\n : '';\n\n this.resultsContainer.innerHTML = resultsHTML + seeMoreHTML;\n\n // Update footer count\n if (this.footerCount) {\n this.footerCount.textContent = resultsCountLabel;\n }\n\n // Update aria-expanded\n if (this.inputElement) {\n this.inputElement.setAttribute('aria-expanded', 'true');\n }\n\n // Attach click handlers\n this.attachResultHandlers();\n\n // Update active state\n this.updateActiveResult();\n }\n\n private renderResult(result: SearchResult, index: number): string {\n const props = this.getProps();\n const imageHTML = props.hideThumbnails\n ? ''\n : this.renderResultImage(result.image, result.title);\n const href = result.url ? escapeHTML(result.url) : '#';\n const displayUrl = result.url ? escapeHTML(formatDisplayUrl(result.url)) : '';\n const timestampHTML =\n props.showDate && result.timestamp !== undefined\n ? `<div class=\"modal-result-date\">${escapeHTML(formatDate(result.timestamp))}</div>`\n : '';\n const metadataHTML =\n (props.showUrl && result.url) || timestampHTML\n ? `<div class=\"modal-result-metadata\">\n ${props.showUrl && result.url ? `<span class=\"modal-result-url\">${displayUrl}</span>` : '<span class=\"modal-result-url modal-result-url-empty\"></span>'}\n ${timestampHTML}\n </div>`\n : '';\n\n return `\n <a \n href=\"${href}\"\n class=\"modal-result-item${index === this.activeIndex ? ' active' : ''}\" \n role=\"option\" \n id=\"result-${index}\"\n aria-selected=\"${index === this.activeIndex}\"\n tabindex=\"-1\"\n data-index=\"${index}\"\n data-url=\"${escapeHTML(result.url || '')}\"\n >\n ${imageHTML}\n <div class=\"modal-result-content\">\n <div class=\"modal-result-title\">${escapeHTML(result.title || '')}</div>\n ${result.description ? `<div class=\"modal-result-description\">${escapeHTML(result.description)}</div>` : ''}\n ${metadataHTML}\n </div>\n </a>\n `;\n }\n\n private renderResultImage(imageUrl: string | undefined, alt: string): string {\n const placeholderSVG = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/></svg>`;\n\n if (!imageUrl) {\n return `\n <div class=\"modal-result-image-container\">\n <div class=\"modal-result-image-placeholder\">${placeholderSVG}</div>\n </div>\n `;\n }\n\n return `\n <div class=\"modal-result-image-container\">\n <div class=\"modal-result-image-loading\"></div>\n <div class=\"modal-result-image-placeholder\" style=\"display: none;\">${placeholderSVG}</div>\n <img \n class=\"modal-result-image\" \n src=\"${escapeHTML(imageUrl)}\" \n alt=\"${escapeHTML(alt)}\"\n loading=\"lazy\"\n />\n </div>\n `;\n }\n\n private attachResultHandlers(): void {\n const items = this.resultsContainer?.querySelectorAll('.modal-result-item');\n if (!items) return;\n\n items.forEach((item, index) => {\n // Handle clicks on results without URLs (prevent default anchor behavior)\n const href = item.getAttribute('href');\n if (href === '#') {\n item.addEventListener('click', (e) => {\n e.preventDefault();\n });\n }\n\n item.addEventListener('mouseenter', () => {\n this.activeIndex = index;\n this.updateActiveResult();\n });\n });\n\n // Image load/error handlers\n const images = this.resultsContainer?.querySelectorAll('.modal-result-image');\n images?.forEach((img) => {\n img.addEventListener('load', () => {\n img.classList.add('loaded');\n const container = img.closest('.modal-result-image-container');\n container?.querySelector('.modal-result-image-loading')?.remove();\n });\n\n img.addEventListener('error', () => {\n const container = img.closest('.modal-result-image-container');\n container?.querySelector('.modal-result-image-loading')?.remove();\n const placeholder = container?.querySelector(\n '.modal-result-image-placeholder'\n ) as HTMLElement;\n if (placeholder) placeholder.style.display = 'flex';\n (img as HTMLElement).style.display = 'none';\n });\n });\n }\n\n private renderEmptyState(): string {\n return `\n <div class=\"modal-empty\">\n <svg class=\"modal-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"></circle>\n <path d=\"m21 21-4.35-4.35\"></path>\n </svg>\n <div class=\"modal-empty-description\">Start typing to search</div>\n </div>\n `;\n }\n\n private showEmptyState(): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n this.resultsContainer.innerHTML = this.renderEmptyState();\n\n if (this.footerCount) {\n this.footerCount.textContent = '';\n }\n\n if (this.inputElement) {\n this.inputElement.setAttribute('aria-expanded', 'false');\n }\n }\n\n private showLoadingState(): void {\n if (!this.resultsContainer) return;\n\n this.clearLoadingInterval();\n this.loadingMessageIndex = Math.floor(Math.random() * LOADING_MESSAGES.length);\n\n this.resultsContainer.innerHTML = `\n <div class=\"modal-loading\">\n <div class=\"loading\" aria-label=\"Loading\"></div>\n <div class=\"loading-text loading-text-animate\">${LOADING_MESSAGES[this.loadingMessageIndex]}</div>\n </div>\n `;\n\n if (this.footerCount) {\n this.footerCount.textContent = LOADING_MESSAGES[this.loadingMessageIndex];\n }\n\n this.startLoadingInterval();\n }\n\n private startLoadingInterval(): void {\n this.loadingMessageInterval = setInterval(() => {\n this.loadingMessageIndex = (this.loadingMessageIndex + 1) % LOADING_MESSAGES.length;\n const textEl = this.resultsContainer?.querySelector('.loading-text');\n if (textEl) {\n textEl.classList.remove('loading-text-animate');\n void (textEl as HTMLElement).offsetWidth;\n textEl.textContent = LOADING_MESSAGES[this.loadingMessageIndex];\n textEl.classList.add('loading-text-animate');\n }\n if (this.footerCount) {\n this.footerCount.textContent = LOADING_MESSAGES[this.loadingMessageIndex];\n }\n }, LOADING_MESSAGE_INTERVAL_MS);\n }\n\n private clearLoadingInterval(): void {\n if (this.loadingMessageInterval) {\n clearInterval(this.loadingMessageInterval);\n this.loadingMessageInterval = null;\n }\n }\n\n private showNoResultsState(query: string): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n this.resultsContainer.innerHTML = `\n <div class=\"modal-empty\">\n <svg class=\"modal-empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"></circle>\n <line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"></line>\n </svg>\n <div class=\"modal-empty-title\">No results found</div>\n <div class=\"modal-empty-description\">No results for \"${escapeHTML(query)}\"</div>\n </div>\n `;\n\n if (this.footerCount) {\n this.footerCount.textContent = '0 results';\n }\n\n if (this.inputElement) {\n this.inputElement.setAttribute('aria-expanded', 'false');\n }\n }\n\n private showErrorState(message: string): void {\n this.clearLoadingInterval();\n if (!this.resultsContainer) return;\n\n this.resultsContainer.innerHTML = `\n <div class=\"error\">\n <strong>Error:</strong> ${escapeHTML(message)}\n </div>\n `;\n\n if (this.footerCount) {\n this.footerCount.textContent = 'Error';\n }\n }\n\n private showMissingApiUrlError(): void {\n if (this.resultsContainer) {\n this.showErrorState('The api-url attribute is required. Please provide a valid API URL.');\n }\n }\n\n private updateTheme(theme: string | null): void {\n const validTheme = theme === 'light' || theme === 'dark' || theme === 'auto' ? theme : 'auto';\n\n if (validTheme === 'auto') {\n this.removeAttribute('theme');\n } else {\n this.setAttribute('theme', validTheme);\n }\n }\n\n private lockBodyScroll(): void {\n // Save current body styles\n const scrollY = window.scrollY;\n this.savedBodyStyles = {\n overflow: document.body.style.overflow,\n position: document.body.style.position,\n top: document.body.style.top,\n width: document.body.style.width,\n };\n this.savedHtmlOverflow = document.documentElement.style.overflow;\n\n // Apply scroll lock styles to both html and body for cross-browser support\n document.documentElement.style.overflow = 'hidden';\n document.body.style.overflow = 'hidden';\n document.body.style.position = 'fixed';\n document.body.style.top = `-${scrollY}px`;\n document.body.style.width = '100%';\n }\n\n private unlockBodyScroll(): void {\n if (!this.savedBodyStyles) return;\n\n // Get the scroll position from the top style\n const scrollY = Math.abs(Number.parseInt(document.body.style.top || '0', 10));\n\n // Restore original styles\n document.documentElement.style.overflow = this.savedHtmlOverflow || '';\n document.body.style.overflow = this.savedBodyStyles.overflow;\n document.body.style.position = this.savedBodyStyles.position;\n document.body.style.top = this.savedBodyStyles.top;\n document.body.style.width = this.savedBodyStyles.width;\n\n // Restore scroll position\n window.scrollTo(0, scrollY);\n\n this.savedBodyStyles = null;\n this.savedHtmlOverflow = null;\n }\n\n private cleanup(): void {\n this.clearLoadingInterval();\n\n // Cancel any in-flight search request\n if (this.currentSearchController) {\n this.currentSearchController.abort();\n this.currentSearchController = null;\n }\n\n // Remove global keyboard listener\n if (this.handleGlobalKeydown) {\n document.removeEventListener('keydown', this.handleGlobalKeydown);\n this.handleGlobalKeydown = null;\n }\n\n // Remove element event listeners\n if (this.inputElement) {\n if (this.handleInputChange) {\n this.inputElement.removeEventListener('input', this.handleInputChange);\n }\n if (this.handleInputKeydown) {\n this.inputElement.removeEventListener('keydown', this.handleInputKeydown);\n }\n }\n\n if (this.backdrop && this.handleBackdropClick) {\n this.backdrop.removeEventListener('click', this.handleBackdropClick);\n }\n\n // Clear handler references\n this.handleInputChange = null;\n this.handleInputKeydown = null;\n this.handleBackdropClick = null;\n\n // Cancel any pending requests\n if (this.client) {\n this.client.cancelAllRequests();\n }\n }\n\n // Public API\n\n /**\n * Open the search modal\n */\n public open(): void {\n if (this.isOpen) return;\n\n this.isOpen = true;\n this.backdrop?.classList.add('open');\n this.modal?.classList.add('open');\n\n // Focus input after animation completes\n // Use double rAF to ensure DOM has updated and transitions have started\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n this.inputElement?.focus();\n });\n });\n\n // Prevent body scroll (save current styles to restore later)\n this.lockBodyScroll();\n\n this.dispatchEvent(createCustomEvent('open', undefined));\n }\n\n /**\n * Close the search modal\n */\n public close(): void {\n if (!this.isOpen) return;\n\n this.isOpen = false;\n this.backdrop?.classList.remove('open');\n this.modal?.classList.remove('open');\n\n // Clear state\n if (this.inputElement) {\n this.inputElement.value = '';\n }\n this.results = [];\n this.activeIndex = -1;\n this.showEmptyState();\n\n // Restore body scroll\n this.unlockBodyScroll();\n\n this.dispatchEvent(createCustomEvent('close', undefined));\n }\n\n /**\n * Toggle the search modal open/closed\n */\n public toggle(): void {\n if (this.isOpen) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /**\n * Perform a search programmatically\n */\n public async search(query: string): Promise<void> {\n if (!this.isOpen) {\n this.open();\n }\n\n if (this.inputElement) {\n this.inputElement.value = query;\n }\n\n await this.performSearch(query);\n }\n\n /**\n * Get current search results\n */\n public getResults(): SearchResult[] {\n return [...this.results];\n }\n\n /**\n * Check if modal is currently open\n */\n public isModalOpen(): boolean {\n return this.isOpen;\n }\n}\n\n// Register the custom element\nif (!customElements.get(COMPONENT_NAME)) {\n customElements.define(COMPONENT_NAME, SearchModalSnippet);\n}\n"],"names":["LOADING_MESSAGES","debounce","func","wait","timeout","executedFunction","args","escapeHTML","text","div","formatDisplayUrl","url","decodeHTMLEntities","formatTimestamp","timestamp","date","diff","minutes","hours","formatDate","generateId","prefix","parseAttribute","value","defaultValue","parseBooleanAttribute","parseNumberAttribute","parsed","createCustomEvent","name","detail","createClient","apiUrl","AISearchClient","isRecord","deepMergeRecords","records","merged","record","key","currentValue","buildRequestUrl","endpoint","queryParams","searchParams","query","hashIndex","path","hash","separator","normalizeHeaders","headers","normalizedHeaders","normalizeBody","body","baseUrl","__publicField","operation","signal","requestOptions","sourceHeader","options","requestId","controller","response","result","chunk","chunks","reader","decoder","done","choice","request","id","POWERED_BY_BRANDING","chatStyles","baseStyles","markdownToHtml","markdown","html","escapeHtml","_","code","lines","processedLines","inList","listType","i","line","headerMatch","level","content","processInlineMarkdown","ulMatch","olMatch","htmlEntities","char","ChatView","container","client","props","e","target","userMessage","assistantMessageId","assistantMessage","stream","fullContent","messageIndex","m","error","message","messageId","isStreaming","messagesHTML","roleClass","avatar","streaming","messages","COMPONENT_NAME","ChatBubbleSnippet","oldValue","newValue","style","bubbleButton","closeButton","minimizeButton","clearButton","chatWindow","chatContent","theme","STORAGE_KEY","ChatPageSnippet","newChatButton","toggleSidebarButton","chatList","lastSession","stored","a","b","sessionIndex","s","session","firstUserMessage","newSession","sessionId","deleteButton","chatItem","isActive","diffMs","diffDays","str","searchStyles","DEFAULT_DISPLAY_RESULTS","REQUEST_MAX_RESULTS","SearchBarSnippet","rawRequestOptions","parsedRequestOptions","searchFn","results","visibleResults","totalResults","brandingHTML","hasMoreResults","resultsCountLabel","seeMoreHTML","resultsHTML","imageHTML","href","displayUrl","timestampHTML","metadataHTML","imageUrl","alt","placeholderSVG","resultItems","item","img","placeholder","textEl","validTheme","modalStyles","SearchModalSnippet","shortcutKey","direction","newIndex","items","index","activeItem","scrollY"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,ECAbC,EAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,ECAnB,SAASC,EAAeC,EAA0B,CACvD,IAAIC,EAAOD,EAGXC,EAAOC,EAAWD,CAAI,EAGtBA,EAAOA,EAAK,QAAQ,oBAAqB,CAACE,EAAGC,IAAS,cAAcA,EAAK,KAAA,CAAM,eAAe,EAG9F,MAAMC,EAAQJ,EAAK,MAAM;AAAA,CAAI,EACvBK,EAA2B,CAAA,EACjC,IAAIC,EAAS,GACTC,EAAW,GAEf,QAASC,EAAI,EAAGA,EAAIJ,EAAM,OAAQI,IAAK,CACrC,MAAMC,EAAOL,EAAMI,CAAC,EAGdE,EAAcD,EAAK,MAAM,mBAAmB,EAClD,GAAIC,EAAa,CACf,MAAMC,EAAQD,EAAY,CAAC,EAAE,OACvBE,EAAUF,EAAY,CAAC,EAC7BL,EAAe,KAAK,KAAKM,CAAK,IAAIE,EAAsBD,CAAO,CAAC,MAAMD,CAAK,GAAG,EAC9E,QACF,CAGA,GAAIF,EAAK,MAAM,QAAQ,EAAG,CACxBJ,EAAe,KAAK,QAAQ,EAC5B,QACF,CAGA,GAAII,EAAK,MAAM,OAAO,EAAG,CACvB,MAAMG,EAAUH,EAAK,QAAQ,QAAS,EAAE,EACxCJ,EAAe,KAAK,eAAeQ,EAAsBD,CAAO,CAAC,eAAe,EAChF,QACF,CAGA,MAAME,EAAUL,EAAK,MAAM,eAAe,EAC1C,GAAIK,EAAS,EACP,CAACR,GAAUC,IAAa,QACtBD,GAAQD,EAAe,KAAK,KAAKE,CAAQ,GAAG,EAChDF,EAAe,KAAK,MAAM,EAC1BC,EAAS,GACTC,EAAW,MAEbF,EAAe,KAAK,OAAOQ,EAAsBC,EAAQ,CAAC,CAAC,CAAC,OAAO,EACnE,QACF,CAGA,MAAMC,EAAUN,EAAK,MAAM,gBAAgB,EAC3C,GAAIM,EAAS,EACP,CAACT,GAAUC,IAAa,QACtBD,GAAQD,EAAe,KAAK,KAAKE,CAAQ,GAAG,EAChDF,EAAe,KAAK,MAAM,EAC1BC,EAAS,GACTC,EAAW,MAEbF,EAAe,KAAK,OAAOQ,EAAsBE,EAAQ,CAAC,CAAC,CAAC,OAAO,EACnE,QACF,CAUA,GAPIT,IACFD,EAAe,KAAK,KAAKE,CAAQ,GAAG,EACpCD,EAAS,GACTC,EAAW,IAITE,EAAK,KAAA,IAAW,GAAI,CACtBJ,EAAe,KAAK,QAAQ,EAC5B,QACF,CAGAA,EAAe,KAAK,MAAMQ,EAAsBJ,CAAI,CAAC,MAAM,CAC7D,CAGA,OAAIH,GACFD,EAAe,KAAK,KAAKE,CAAQ,GAAG,EAG/BF,EAAe,KAAK;AAAA,CAAI,CACjC,CAKA,SAASQ,EAAsBpF,EAAsB,CACnD,IAAIyD,EAASzD,EAGb,OAAAyD,EAASA,EAAO,QAAQ,aAAc,iBAAiB,EAGvDA,EAASA,EAAO,QAAQ,qBAAsB,8BAA8B,EAC5EA,EAASA,EAAO,QAAQ,eAAgB,8BAA8B,EAGtEA,EAASA,EAAO,QAAQ,iBAAkB,qBAAqB,EAC/DA,EAASA,EAAO,QAAQ,aAAc,qBAAqB,EAG3DA,EAASA,EAAO,QAAQ,aAAc,aAAa,EACnDA,EAASA,EAAO,QAAQ,WAAY,aAAa,EAGjDA,EAASA,EAAO,QACd,2BACA,+DAAA,EAGKA,CACT,CAKA,SAASe,EAAWxE,EAAsB,CACxC,MAAMuF,EAAuC,CAC3C,IAAK,QACL,IAAK,OACL,IAAK,OACL,IAAK,SACL,IAAK,OAAA,EAGP,OAAOvF,EAAK,QAAQ,WAAawF,GAASD,EAAaC,CAAI,GAAKA,CAAI,CACtE,CCnHO,MAAMC,CAAS,CAkBpB,YAAYC,EAAwBC,EAAwBC,EAA2B,CAjB/E5C,EAAA,kBACAA,EAAA,eACAA,EAAA,cACAA,EAAA,oBAA2C,MAC3CA,EAAA,yBAAwC,MACxCA,EAAA,kBAAuC,MACvCA,EAAA,gBAAsB,CAAA,GACtBA,EAAA,mBAAc,IACdA,EAAA,iCAA2C,MAC3CA,EAAA,8BAAgE,MAChEA,EAAA,2BAAsB,GAGtBA,EAAA,yBAAiD,MACjDA,EAAA,0BAA0D,MAC1DA,EAAA,uBAAuC,MAG7C,KAAK,UAAY0C,EACjB,KAAK,OAASC,EACd,KAAK,MAAQC,EAEb,KAAK,OAAA,EACL,KAAK,qBAAA,CACP,CAKQ,QAAe,CACrB,KAAK,UAAU,UAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAiBF7F,EAAW,KAAK,MAAM,aAAe,mBAAmB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAalF,KAAK,kBAAoB,KAAK,UAAU,cAAc,gBAAgB,EACtE,KAAK,aAAe,KAAK,UAAU,cAAc,aAAa,EAC9D,KAAK,WAAa,KAAK,UAAU,cAAc,mBAAmB,CACpE,CAKQ,sBAA6B,CAC/B,CAAC,KAAK,cAAgB,CAAC,KAAK,aAGhC,KAAK,kBAAqB8F,GAAa,CACrC,MAAMC,EAASD,EAAE,OACjBC,EAAO,MAAM,OAAS,OACtBA,EAAO,MAAM,OAAS,GAAGA,EAAO,YAAY,IAC9C,EACA,KAAK,aAAa,iBAAiB,QAAS,KAAK,iBAAiB,EAGlE,KAAK,mBAAsBD,GAAqB,CAC1CA,EAAE,MAAQ,SAAW,CAACA,EAAE,WAC1BA,EAAE,eAAA,EACF,KAAK,kBAAA,EAET,EACA,KAAK,aAAa,iBAAiB,UAAW,KAAK,kBAAkB,EAGrE,KAAK,gBAAkB,IAAM,CAC3B,KAAK,kBAAA,CACP,EACA,KAAK,WAAW,iBAAiB,QAAS,KAAK,eAAe,EAChE,CAKA,MAAc,mBAAmC,CAC/C,GAAI,CAAC,KAAK,cAAgB,KAAK,YAAa,OAE5C,MAAMV,EAAU,KAAK,aAAa,MAAM,KAAA,EACpCA,EAAQ,SAAW,IAGvB,KAAK,aAAa,MAAQ,GAC1B,KAAK,aAAa,MAAM,OAAS,OAGjC,MAAM,KAAK,YAAYA,CAAO,EAChC,CAKA,MAAa,YAAYA,EAAgC,CAEvD,MAAMY,EAAuB,CAC3B,GAAInF,EAAW,KAAK,EACpB,KAAM,OACN,QAAAuE,EACA,UAAW,KAAK,IAAA,CAAI,EAGtB,KAAK,WAAWY,CAAW,EAC3B,KAAK,eAAe,EAAI,EACxB,KAAK,kBAAkB,EAAI,EAG3B,MAAMC,EAAqBpF,EAAW,KAAK,EACrCqF,EAA4B,CAChC,GAAID,EACJ,KAAM,YACN,QAAS,GACT,UAAW,KAAK,IAAA,CAAI,EAGtB,KAAK,WAAWC,CAAgB,EAChC,KAAK,0BAA4BD,EACjC,KAAK,eAAe,EAAI,EAExB,GAAI,CAEF,MAAME,EAAS,KAAK,OAAO,KAAKf,CAAO,EAEvC,IAAIgB,EAAc,GAElB,gBAAiBzC,KAASwC,EACxB,GAAIxC,EAAM,OAAS,QAAUA,EAAM,QACjCyC,GAAezC,EAAM,QACrB,KAAK,uBAAuBsC,EAAoBG,CAAW,UAClDzC,EAAM,OAAS,QAAS,CACjC,KAAK,mBAAmBsC,EAAoBtC,EAAM,SAAW,eAAe,EAC5E,KACF,CAOF,MAAM0C,EAAe,KAAK,SAAS,UAAWC,GAAMA,EAAE,KAAOL,CAAkB,EAC3EI,IAAiB,KACnB,KAAK,SAASA,CAAY,EAAE,QAAUD,GAIxC,KAAK,UAAU,cAAc/E,EAAkB,UAAW,CAAE,QAAS6E,CAAA,CAAkB,CAAC,CAC1F,OAASK,EAAO,CACd,KAAK,mBAAmBN,EAAqBM,EAAgB,OAAO,EAGpE,KAAK,UAAU,cACblF,EAAkB,QAAS,CACzB,MAAO,CACL,QAAUkF,EAAgB,QAC1B,KAAM,YAAA,CACR,CACD,CAAA,CAEL,QAAA,CACE,KAAK,kBAAkB,EAAK,EAC5B,KAAK,eAAA,EACL,KAAK,0BAA4B,IACnC,CACF,CAKQ,WAAWC,EAAwB,CACzC,KAAK,SAAS,KAAKA,CAAO,EAC1B,KAAK,eAAA,CACP,CAKQ,uBAAuBC,EAAmBrB,EAAuB,CACvE,MAAMiB,EAAe,KAAK,SAAS,UAAWC,GAAMA,EAAE,KAAOG,CAAS,EAClEJ,IAAiB,KACnB,KAAK,SAASA,CAAY,EAAE,QAAUjB,EACtC,KAAK,eAAe,EAAI,EAE5B,CAKQ,mBAAmBqB,EAAmBF,EAAqB,CACjE,MAAMF,EAAe,KAAK,SAAS,UAAWC,GAAMA,EAAE,KAAOG,CAAS,EAClEJ,IAAiB,KACnB,KAAK,SAASA,CAAY,EAAE,QAAU,UAAUE,CAAK,GACrD,KAAK,eAAA,EAET,CAKQ,eAAeG,EAAc,GAAa,CAChD,GAAI,CAAC,KAAK,kBAAmB,OAE7B,GAAI,KAAK,SAAS,SAAW,EAAG,CAC9B,KAAK,kBAAkB,UAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAWnC,MACF,CAEA,MAAMC,EAAe,KAAK,SACvB,IAAKH,GACJ,KAAK,cAAcA,EAASE,GAAeF,EAAQ,KAAO,KAAK,yBAAyB,CAAA,EAEzF,KAAK,EAAE,EAEV,KAAK,kBAAkB,UAAYG,EAGnC,KAAK,eAAA,CACP,CAKQ,cAAcH,EAAkBE,EAAc,GAAe,CACnE,MAAME,EAAY,gBAAgBJ,EAAQ,IAAI,GACxCK,EAASL,EAAQ,OAAS,OAAS,IAAM,KAE/C,MAAO;AAAA,iCACsBI,CAAS;AAAA,2CACCC,CAAM;AAAA;AAAA;AAAA,cAGnCL,EAAQ,QAAU,kCAAkClC,EAAekC,EAAQ,OAAO,CAAC,SAAW,EAAE;AAAA,cAChGE,EAAc,kLAAkLjH,EAAiB,KAAK,mBAAmB,CAAC,gBAAkB,EAAE;AAAA;AAAA;AAAA,8CAG9Na,EAAgBkG,EAAQ,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA,KAK9E,CAKQ,gBAAuB,CACxB,KAAK,mBAEV,sBAAsB,IAAM,CACtB,KAAK,oBACP,KAAK,kBAAkB,UAAY,KAAK,kBAAkB,aAE9D,CAAC,CACH,CAKQ,kBAAkBM,EAA0B,CAClD,KAAK,YAAcA,EAEf,KAAK,eACP,KAAK,aAAa,SAAWA,GAG3B,KAAK,aACP,KAAK,WAAW,SAAWA,EAC3B,KAAK,WAAW,UAAYA,EAAY,8BAAgC,qBAGtEA,EACF,KAAK,qBAAA,EAEL,KAAK,qBAAA,CAET,CAEQ,sBAA6B,CACnC,KAAK,oBAAsB,KAAK,MAAM,KAAK,OAAA,EAAWrH,EAAiB,MAAM,EAC7E,KAAK,uBAAyB,YAAY,IAAM,CAC9C,KAAK,qBAAuB,KAAK,oBAAsB,GAAKA,EAAiB,OACzE,KAAK,aACP,KAAK,eAAe,EAAI,CAE5B,EAAG,IAA2B,CAChC,CAEQ,sBAA6B,CAC/B,KAAK,yBACP,cAAc,KAAK,sBAAsB,EACzC,KAAK,uBAAyB,KAElC,CAKO,aAAyB,CAC9B,MAAO,CAAC,GAAG,KAAK,QAAQ,CAC1B,CAKO,eAAsB,CAC3B,KAAK,SAAW,CAAA,EAChB,KAAK,eAAA,CACP,CAKO,YAAYsH,EAA2B,CAC5C,KAAK,SAAW,CAAC,GAAGA,CAAQ,EAC5B,KAAK,eAAA,CACP,CAKO,SAAgB,CACrB,KAAK,qBAAA,EAED,KAAK,aACP,KAAK,OAAO,kBAAA,EAIV,KAAK,eACH,KAAK,mBACP,KAAK,aAAa,oBAAoB,QAAS,KAAK,iBAAiB,EAEnE,KAAK,oBACP,KAAK,aAAa,oBAAoB,UAAW,KAAK,kBAAkB,GAIxE,KAAK,YAAc,KAAK,iBAC1B,KAAK,WAAW,oBAAoB,QAAS,KAAK,eAAe,EAInE,KAAK,kBAAoB,KACzB,KAAK,mBAAqB,KAC1B,KAAK,gBAAkB,IACzB,CACF,CC7XA,MAAMC,EAAiB,sBAEhB,MAAMC,UAA0B,WAAY,CAkBjD,aAAc,CACZ,MAAA,EAlBMhE,EAAA,eACAA,EAAA,cAAgC,MAChCA,EAAA,gBAA4B,MAC5BA,EAAA,iBAAgC,MAChCA,EAAA,kBAAa,IACbA,EAAA,mBAAc,IAGdA,EAAA,yBAAyC,MACzCA,EAAA,wBAAwC,MACxCA,EAAA,2BAA2C,MAC3CA,EAAA,wBAAwC,MAQ9C,KAAK,OAAS,KAAK,aAAa,CAAE,KAAM,OAAQ,CAClD,CAPA,WAAW,oBAAqB,CAC9B,MAAO,CAAC,UAAW,cAAe,QAAS,eAAe,CAC5D,CAOA,mBAA0B,CACxB,KAAK,OAAA,EACL,KAAK,iBAAA,EACL,KAAK,cAAc5B,EAAkB,QAAS,MAAS,CAAC,CAC1D,CAEA,sBAA6B,CAC3B,KAAK,QAAA,CACP,CAEA,yBAAyBC,EAAc4F,EAAyBC,EAA+B,CACzFD,IAAaC,IAEb7F,IAAS,UACX,KAAK,iBAAA,EACIA,IAAS,SAElB,KAAK,YAAY6F,CAAQ,EAE7B,CAEQ,UAA+B,CACrC,MAAO,CACL,OAAQpG,EAAe,KAAK,aAAa,SAAS,EAAG,EAAE,EACvD,YAAaA,EAAe,KAAK,aAAa,aAAa,EAAG,mBAAmB,EACjF,MAAOA,EAAe,KAAK,aAAa,OAAO,EAAG,MAAM,EACxD,aAAcG,EAAsB,KAAK,aAAa,eAAe,EAAG,EAAK,CAAA,CAEjF,CAEQ,kBAAyB,CAC/B,MAAM2E,EAAQ,KAAK,SAAA,EAEnB,GAAI,CAACA,EAAM,OAAQ,CACjB,QAAQ,MAAM,kDAAkD,EAChE,KAAK,OAAS,KACd,MACF,CAEA,GAAI,CACF,KAAK,OAASrE,EAAaqE,EAAM,MAAM,CACzC,OAASU,EAAO,CACd,QAAQ,MAAM,qBAAsBA,CAAK,CAC3C,CACF,CAEQ,QAAe,CACrB,MAAMa,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,YAAc,GAAG/C,CAAU;AAAA,EAAKD,CAAU;AAAA,EAAK,KAAK,iBAAiB,GAE3E,KAAK,UAAY,SAAS,cAAc,KAAK,EAC7C,KAAK,UAAU,UAAY,qBAC3B,KAAK,UAAU,UAAY,KAAK,YAAA,EAEhC,KAAK,OAAO,UAAY,GACxB,KAAK,OAAO,YAAYgD,CAAK,EAC7B,KAAK,OAAO,YAAY,KAAK,SAAS,EAEtC,KAAK,qBAAA,CACP,CAEQ,iBAA0B,CAChC,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KA2IT,CAEQ,aAAsB,CAM5B,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UALO,KAAK,SAAA,EACQ,aACvB,GACA,2BAA2BjD,CAAmB,QAqChC;AAAA;AAAA,KAGpB,CAEQ,sBAA6B,CACnC,MAAMkD,EAAe,KAAK,OAAO,cAAc,gBAAgB,EACzDC,EAAc,KAAK,OAAO,cAAc,eAAe,EACvDC,EAAiB,KAAK,OAAO,cAAc,kBAAkB,EAC7DC,EAAc,KAAK,OAAO,cAAc,eAAe,EAE7D,KAAK,kBAAoB,IAAM,KAAK,WAAA,EACpC,KAAK,iBAAmB,IAAM,KAAK,UAAA,EACnC,KAAK,oBAAsB,IAAM,KAAK,eAAA,EACtC,KAAK,iBAAmB,IAAM,KAAK,UAAA,EAEnCH,GAAc,iBAAiB,QAAS,KAAK,iBAAiB,EAC9DC,GAAa,iBAAiB,QAAS,KAAK,gBAAgB,EAC5DC,GAAgB,iBAAiB,QAAS,KAAK,mBAAmB,EAClEC,GAAa,iBAAiB,QAAS,KAAK,gBAAgB,CAC9D,CAEQ,sBAA6B,CACnC,MAAMH,EAAe,KAAK,OAAO,cAAc,gBAAgB,EACzDC,EAAc,KAAK,OAAO,cAAc,eAAe,EACvDC,EAAiB,KAAK,OAAO,cAAc,kBAAkB,EAC7DC,EAAc,KAAK,OAAO,cAAc,eAAe,EAEzD,KAAK,mBACPH,GAAc,oBAAoB,QAAS,KAAK,iBAAiB,EAE/D,KAAK,kBACPC,GAAa,oBAAoB,QAAS,KAAK,gBAAgB,EAE7D,KAAK,qBACPC,GAAgB,oBAAoB,QAAS,KAAK,mBAAmB,EAEnE,KAAK,kBACPC,GAAa,oBAAoB,QAAS,KAAK,gBAAgB,EAIjE,KAAK,kBAAoB,KACzB,KAAK,iBAAmB,KACxB,KAAK,oBAAsB,KAC3B,KAAK,iBAAmB,IAC1B,CAEQ,YAAmB,CACzB,KAAK,WAAa,CAAC,KAAK,WACxB,MAAMH,EAAe,KAAK,OAAO,cAAc,gBAAgB,EACzDI,EAAa,KAAK,OAAO,cAAc,cAAc,EAEvD,KAAK,YACPJ,GAAc,UAAU,IAAI,QAAQ,EACpCI,GAAY,UAAU,IAAI,UAAU,EACpC,KAAK,mBAAA,IAELJ,GAAc,UAAU,OAAO,QAAQ,EACvCI,GAAY,UAAU,OAAO,UAAU,EAE3C,CAEQ,WAAkB,CACxB,KAAK,WAAa,GAClB,KAAK,YAAc,GACnB,MAAMJ,EAAe,KAAK,OAAO,cAAc,gBAAgB,EACzDI,EAAa,KAAK,OAAO,cAAc,cAAc,EAE3DJ,GAAc,UAAU,OAAO,QAAQ,EACvCI,GAAY,UAAU,OAAO,WAAY,WAAW,CACtD,CAEQ,gBAAuB,CAC7B,KAAK,YAAc,CAAC,KAAK,YACzB,MAAMA,EAAa,KAAK,OAAO,cAAc,cAAc,EAEvD,KAAK,YACPA,GAAY,UAAU,IAAI,WAAW,EAErCA,GAAY,UAAU,OAAO,WAAW,CAE5C,CAEQ,oBAA2B,CACjC,GAAI,KAAK,SAAU,OAEnB,MAAMC,EAAc,KAAK,OAAO,cAAc,eAAe,EAC7D,GAAI,CAACA,EAAa,OAElB,GAAI,CAAC,KAAK,OAAQ,CAChBA,EAAY,UAAY;AAAA;AAAA;AAAA;AAAA,QAKxB,MACF,CAEA,MAAM7B,EAAQ,KAAK,SAAA,EACnB,KAAK,SAAW,IAAIH,EAASgC,EAAa,KAAK,OAAQ7B,CAAK,CAC9D,CAEQ,YAAY8B,EAA4B,EAG3BA,IAAU,SAAWA,IAAU,OAASA,EAAQ,QAGlD,MACf,KAAK,aAAa,OAAO,GACzB,KAAK,aAAa,OAAO,IAAM,QAE/B,KAAK,gBAAgB,OAAO,CAEhC,CAEQ,SAAgB,CACtB,KAAK,qBAAA,EAED,KAAK,QACP,KAAK,OAAO,kBAAA,EAGV,KAAK,UACP,KAAK,SAAS,QAAA,CAElB,CAGO,WAAkB,CACvB,KAAK,UAAU,cAAA,CACjB,CAEA,MAAa,YAAYvC,EAAgC,CACnD,KAAK,UACP,MAAM,KAAK,SAAS,YAAYA,CAAO,CAE3C,CAEO,aAAyB,CAC9B,OAAO,KAAK,UAAU,YAAA,GAAiB,CAAA,CACzC,CACF,CAGK,eAAe,IAAI4B,CAAc,GACpC,eAAe,OAAOA,EAAgBC,CAAiB,ECjazD,MAAMD,EAAiB,oBACjBY,EAAc,qBAUb,MAAMC,UAAwB,WAAY,CAoB/C,aAAc,CACZ,MAAA,EApBM5E,EAAA,eACAA,EAAA,cAAgC,MAChCA,EAAA,gBAA4B,MAC5BA,EAAA,iBAAgC,MAChCA,EAAA,gBAA0B,CAAA,GAC1BA,EAAA,wBAAkC,MAClCA,EAAA,wBAAmB,IAGnBA,EAAA,wBAAwC,MACxCA,EAAA,0BAA0C,MAC1CA,EAAA,gCAAgD,MAChDA,EAAA,2BAAmD,MACnDA,EAAA,0BAA0C,MAQhD,KAAK,OAAS,KAAK,aAAa,CAAE,KAAM,OAAQ,EAChD,KAAK,aAAA,CACP,CARA,WAAW,oBAAqB,CAC9B,MAAO,CAAC,UAAW,cAAe,QAAS,eAAe,CAC5D,CAQA,mBAA0B,CACxB,KAAK,OAAA,EACL,KAAK,iBAAA,EACL,KAAK,UAAA,EACL,KAAK,cAAc5B,EAAkB,QAAS,MAAS,CAAC,CAC1D,CAEA,sBAA6B,CAC3B,KAAK,mBAAA,EACL,KAAK,QAAA,CACP,CAEA,yBAAyBC,EAAc4F,EAAyBC,EAA+B,CACzFD,IAAaC,IAEb7F,IAAS,WACX,KAAK,iBAAA,EACL,KAAK,UAAA,GACIA,IAAS,SAElB,KAAK,YAAY6F,CAAQ,EAE7B,CAEQ,UAA+B,CACrC,MAAO,CACL,OAAQpG,EAAe,KAAK,aAAa,SAAS,EAAG,EAAE,EACvD,YAAaA,EAAe,KAAK,aAAa,aAAa,EAAG,mBAAmB,EACjF,MAAOA,EAAe,KAAK,aAAa,OAAO,EAAG,MAAM,EACxD,aAAcG,EAAsB,KAAK,aAAa,eAAe,EAAG,EAAK,CAAA,CAEjF,CAEQ,kBAAyB,CAC/B,MAAM2E,EAAQ,KAAK,SAAA,EAEnB,GAAI,CAACA,EAAM,OAAQ,CACjB,QAAQ,MAAM,gDAAgD,EAC9D,KAAK,OAAS,KACd,MACF,CAEA,GAAI,CACF,KAAK,OAASrE,EAAaqE,EAAM,MAAM,CACzC,OAASU,EAAO,CACd,QAAQ,MAAM,mBAAoBA,CAAK,CACzC,CACF,CAEQ,QAAe,CACrB,MAAMa,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,YAAc,GAAG/C,CAAU;AAAA,EAAKD,CAAU;AAAA,EAAK,KAAK,eAAe,GAEzE,KAAK,UAAY,SAAS,cAAc,KAAK,EAC7C,KAAK,UAAU,UAAY,sBAC3B,KAAK,UAAU,UAAY,KAAK,YAAA,EAEhC,KAAK,OAAO,UAAY,GACxB,KAAK,OAAO,YAAYgD,CAAK,EAC7B,KAAK,OAAO,YAAY,KAAK,SAAS,EAEtC,KAAK,qBAAA,CACP,CAEQ,eAAwB,CAC9B,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAkRT,CAEQ,aAAsB,CAM5B,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UALO,KAAK,SAAA,EACQ,aACvB,GACA,2BAA2BjD,CAAmB,QAchC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KA+BpB,CAEQ,sBAA6B,CACnC,MAAMqD,EAAc,KAAK,OAAO,cAAc,eAAe,EACvDM,EAAgB,KAAK,OAAO,cAAc,kBAAkB,EAC5DC,EAAsB,KAAK,OAAO,cAAc,wBAAwB,EACxEC,EAAW,KAAK,OAAO,cAAc,YAAY,EAEvD,KAAK,iBAAmB,IAAM,KAAK,iBAAA,EACnC,KAAK,mBAAqB,IAAM,KAAK,cAAA,EACrC,KAAK,yBAA2B,IAAM,KAAK,cAAA,EAC3C,KAAK,oBAAuBlC,GAAa,KAAK,gBAAgBA,CAAC,EAE/D0B,GAAa,iBAAiB,QAAS,KAAK,gBAAgB,EAC5DM,GAAe,iBAAiB,QAAS,KAAK,kBAAkB,EAChEC,GAAqB,iBAAiB,QAAS,KAAK,wBAAwB,EAC5EC,GAAU,iBAAiB,QAAS,KAAK,mBAAmB,CAC9D,CAEQ,sBAA6B,CACnC,MAAMR,EAAc,KAAK,OAAO,cAAc,eAAe,EACvDM,EAAgB,KAAK,OAAO,cAAc,kBAAkB,EAC5DC,EAAsB,KAAK,OAAO,cAAc,wBAAwB,EACxEC,EAAW,KAAK,OAAO,cAAc,YAAY,EACjDN,EAAc,KAAK,OAAO,cAAc,YAAY,EAEtD,KAAK,kBACPF,GAAa,oBAAoB,QAAS,KAAK,gBAAgB,EAE7D,KAAK,oBACPM,GAAe,oBAAoB,QAAS,KAAK,kBAAkB,EAEjE,KAAK,0BACPC,GAAqB,oBAAoB,QAAS,KAAK,wBAAwB,EAE7E,KAAK,qBACPC,GAAU,oBAAoB,QAAS,KAAK,mBAAmB,EAE7D,KAAK,oBAAsBN,GAC7BA,EAAY,oBAAoB,UAAW,KAAK,kBAAkB,EAIpE,KAAK,iBAAmB,KACxB,KAAK,mBAAqB,KAC1B,KAAK,yBAA2B,KAChC,KAAK,oBAAsB,KAC3B,KAAK,mBAAqB,IAC5B,CAEQ,WAAkB,CACxB,MAAMA,EAAc,KAAK,OAAO,cAAc,YAAY,EAE1D,GAAI,CAAC,KAAK,OAAQ,CACZA,IACFA,EAAY,UAAY;AAAA;AAAA;AAAA;AAAA,WAM1B,MACF,CAEA,GAAI,CAACA,EAAa,OAElB,MAAM7B,EAAQ,KAAK,SAAA,EAInB,GAHA,KAAK,SAAW,IAAIH,EAASgC,EAAa,KAAK,OAAQ7B,CAAK,EAGxD,KAAK,SAAS,SAAW,EAC3B,KAAK,cAAA,MACA,CAEL,MAAMoC,EAAc,KAAK,SAAS,CAAC,EACnC,KAAK,gBAAgBA,EAAY,EAAE,CACrC,CAGA,KAAK,mBAAqB,IAAM,CAC9B,KAAK,mBAAA,EACL,KAAK,mBAAA,EACL,KAAK,eAAA,CACP,EACAP,EAAY,iBAAiB,UAAW,KAAK,kBAAkB,EAE/D,KAAK,eAAA,CACP,CAEQ,mBAA4B,CAClC,MAAO,WAAW,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,UAAU,EAAG,CAAC,CAAC,EAC5E,CAEQ,cAAqB,CAC3B,GAAI,CACF,MAAMQ,EAAS,aAAa,QAAQN,CAAW,EAC3CM,IACF,KAAK,SAAW,KAAK,MAAMA,CAAM,EAEjC,KAAK,SAAS,KAAK,CAACC,EAAGC,IAAMA,EAAE,UAAYD,EAAE,SAAS,EAE1D,OAAS5B,EAAO,CACd,QAAQ,MAAM,gCAAiCA,CAAK,CACtD,CACF,CAEQ,cAAqB,CAC3B,GAAI,CACF,aAAa,QAAQqB,EAAa,KAAK,UAAU,KAAK,QAAQ,CAAC,CACjE,OAASrB,EAAO,CACd,QAAQ,MAAM,gCAAiCA,CAAK,CACtD,CACF,CAEQ,oBAA2B,CACjC,GAAI,CAAC,KAAK,kBAAoB,CAAC,KAAK,SAAU,OAE9C,MAAM8B,EAAe,KAAK,SAAS,UAAWC,GAAMA,EAAE,KAAO,KAAK,gBAAgB,EAC9ED,IAAiB,KACnB,KAAK,SAASA,CAAY,EAAE,SAAW,KAAK,SAAS,YAAA,EACrD,KAAK,SAASA,CAAY,EAAE,UAAY,KAAK,IAAA,EAC7C,KAAK,aAAA,EAET,CAEQ,oBAA2B,CACjC,GAAI,CAAC,KAAK,iBAAkB,OAE5B,MAAME,EAAU,KAAK,SAAS,KAAMD,GAAMA,EAAE,KAAO,KAAK,gBAAgB,EACxE,GAAIC,GAAWA,EAAQ,SAAS,OAAS,GAAKA,EAAQ,QAAU,WAAY,CAC1E,MAAMC,EAAmBD,EAAQ,SAAS,KAAMjC,GAAMA,EAAE,OAAS,MAAM,EACnEkC,IACFD,EAAQ,MACNC,EAAiB,QAAQ,MAAM,EAAG,EAAE,GACnCA,EAAiB,QAAQ,OAAS,GAAK,MAAQ,IAClD,KAAK,aAAA,EAET,CACF,CAEQ,eAAsB,CAE5B,KAAK,mBAAA,EAEL,MAAMC,EAA0B,CAC9B,GAAI,KAAK,kBAAA,EACT,MAAO,WACP,SAAU,CAAA,EACV,UAAW,KAAK,IAAA,EAChB,UAAW,KAAK,IAAA,CAAI,EAGtB,KAAK,SAAS,QAAQA,CAAU,EAChC,KAAK,iBAAmBA,EAAW,GACnC,KAAK,aAAA,EAGL,KAAK,UAAU,cAAA,EACf,KAAK,eAAA,CACP,CAEQ,gBAAgBC,EAAyB,CAC/C,GAAIA,IAAc,KAAK,iBAAkB,OAGzC,KAAK,mBAAA,EAEL,MAAMH,EAAU,KAAK,SAAS,KAAM,GAAM,EAAE,KAAOG,CAAS,EACxDH,GAAW,KAAK,WAClB,KAAK,iBAAmBG,EACxB,KAAK,SAAS,YAAYH,EAAQ,QAAQ,EAC1C,KAAK,eAAA,EAET,CAEQ,cAAcG,EAAyB,CAC7C,MAAML,EAAe,KAAK,SAAS,UAAW,GAAM,EAAE,KAAOK,CAAS,EAClEL,IAAiB,KAErB,KAAK,SAAS,OAAOA,EAAc,CAAC,EACpC,KAAK,aAAA,EAGDK,IAAc,KAAK,mBACjB,KAAK,SAAS,OAAS,EACzB,KAAK,gBAAgB,KAAK,SAAS,CAAC,EAAE,EAAE,EAExC,KAAK,cAAA,GAIT,KAAK,eAAA,EACP,CAEQ,kBAAyB,CAC/B,GAAI,CAAC,KAAK,iBAAkB,OAE5B,MAAMH,EAAU,KAAK,SAAS,KAAMD,GAAMA,EAAE,KAAO,KAAK,gBAAgB,EACpEC,IACFA,EAAQ,SAAW,CAAA,EACnBA,EAAQ,MAAQ,WAChBA,EAAQ,UAAY,KAAK,IAAA,EACzB,KAAK,aAAA,GAGP,KAAK,UAAU,cAAA,EACf,KAAK,eAAA,CACP,CAEQ,eAAsB,CAC5B,KAAK,iBAAmB,CAAC,KAAK,iBACd,KAAK,OAAO,cAAc,eAAe,GAChD,UAAU,OAAO,YAAa,KAAK,gBAAgB,CAC9D,CAEQ,gBAAgB,EAAgB,CACtC,MAAMxC,EAAS,EAAE,OAGX4C,EAAe5C,EAAO,QAAQ,wBAAwB,EAC5D,GAAI4C,EAAc,CAChB,EAAE,gBAAA,EACF,MAAMD,EAAYC,EAAa,aAAa,iBAAiB,EACzDD,GACF,KAAK,cAAcA,CAAS,EAE9B,MACF,CAGA,MAAME,EAAW7C,EAAO,QAAQ,iBAAiB,EACjD,GAAI6C,EAAU,CACZ,MAAMF,EAAYE,EAAS,aAAa,iBAAiB,EACrDF,GACF,KAAK,gBAAgBA,CAAS,CAElC,CACF,CAEQ,gBAAuB,CAC7B,MAAMV,EAAW,KAAK,OAAO,cAAc,YAAY,EACvD,GAAKA,EAEL,IAAI,KAAK,SAAS,SAAW,EAAG,CAC9BA,EAAS,UAAY,kDACrB,MACF,CAEAA,EAAS,UAAY,KAAK,SAAS,IAAKO,GAAY,KAAK,mBAAmBA,CAAO,CAAC,EAAE,KAAK,EAAE,EAC/F,CAEQ,mBAAmBA,EAA8B,CACvD,MAAMM,EAAWN,EAAQ,KAAO,KAAK,iBAC/B/H,EAAO,KAAK,WAAW+H,EAAQ,SAAS,EAE9C,MAAO;AAAA,mCACwBM,EAAW,SAAW,EAAE,sBAAsBN,EAAQ,EAAE;AAAA;AAAA,8CAE7C,KAAK,WAAWA,EAAQ,KAAK,CAAC;AAAA,6CAC/B/H,CAAI;AAAA;AAAA,iEAEgB+H,EAAQ,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAOzE,CAEQ,WAAWhI,EAA2B,CAC5C,MAAMC,EAAO,IAAI,KAAKD,CAAS,EAEzBuI,MADU,KAAA,EACG,QAAA,EAAYtI,EAAK,QAAA,EAC9BuI,EAAW,KAAK,MAAMD,GAAU,IAAO,GAAK,GAAK,GAAG,EAE1D,OAAIC,IAAa,EACRvI,EAAK,mBAAmB,OAAW,CAAE,KAAM,UAAW,OAAQ,UAAW,EACvEuI,IAAa,EACf,YACEA,EAAW,EACbvI,EAAK,mBAAmB,OAAW,CAAE,QAAS,OAAQ,EAEtDA,EAAK,mBAAmB,OAAW,CAAE,MAAO,QAAS,IAAK,UAAW,CAEhF,CAEQ,WAAWwI,EAAqB,CACtC,MAAM9I,EAAM,SAAS,cAAc,KAAK,EACxC,OAAAA,EAAI,YAAc8I,EACX9I,EAAI,SACb,CAEQ,YAAYyH,EAA4B,EAG3BA,IAAU,SAAWA,IAAU,OAASA,EAAQ,QAGlD,MACf,KAAK,aAAa,OAAO,GACzB,KAAK,aAAa,OAAO,IAAM,QAE/B,KAAK,gBAAgB,OAAO,CAEhC,CAEQ,SAAgB,CACtB,KAAK,qBAAA,EAED,KAAK,QACP,KAAK,OAAO,kBAAA,EAGV,KAAK,UACP,KAAK,SAAS,QAAA,CAElB,CAGO,WAAkB,CACvB,KAAK,iBAAA,CACP,CAEA,MAAa,YAAYvC,EAAgC,CACnD,KAAK,WACP,MAAM,KAAK,SAAS,YAAYA,CAAO,EACvC,KAAK,mBAAA,EAET,CAEO,aAAyB,CAC9B,OAAO,KAAK,UAAU,YAAA,GAAiB,CAAA,CACzC,CAEO,aAA6B,CAClC,MAAO,CAAC,GAAG,KAAK,QAAQ,CAC1B,CAEO,mBAAwC,CAC7C,OAAO,KAAK,SAAS,KAAMkD,GAAMA,EAAE,KAAO,KAAK,gBAAgB,GAAK,IACtE,CACF,CAGK,eAAe,IAAItB,CAAc,GACpC,eAAe,OAAOA,EAAgBa,CAAe,ECpxBhD,MAAMoB,EAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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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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"}