@gengage/assistant-fe 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/chat/api.d.ts.map +1 -1
  2. package/dist/chat/chat-presentation-state.d.ts +54 -0
  3. package/dist/chat/chat-presentation-state.d.ts.map +1 -0
  4. package/dist/chat/components/AIGroupingCards.d.ts.map +1 -1
  5. package/dist/chat/components/AISuggestedSearchCards.d.ts.map +1 -1
  6. package/dist/chat/components/AITopPicks.d.ts.map +1 -1
  7. package/dist/chat/components/ChatDrawer.d.ts +46 -0
  8. package/dist/chat/components/ChatDrawer.d.ts.map +1 -1
  9. package/dist/chat/components/GroundingReviewCard.d.ts.map +1 -1
  10. package/dist/chat/components/ThumbnailsColumn.d.ts +2 -0
  11. package/dist/chat/components/ThumbnailsColumn.d.ts.map +1 -1
  12. package/dist/chat/index.d.ts +10 -0
  13. package/dist/chat/index.d.ts.map +1 -1
  14. package/dist/chat/locales/en.d.ts.map +1 -1
  15. package/dist/chat/locales/tr.d.ts.map +1 -1
  16. package/dist/chat/types.d.ts +7 -1
  17. package/dist/chat/types.d.ts.map +1 -1
  18. package/dist/chat/utils/chat-presentation-debug.d.ts +20 -0
  19. package/dist/chat/utils/chat-presentation-debug.d.ts.map +1 -0
  20. package/dist/chat/utils/get-chat-scroll-element.d.ts +11 -0
  21. package/dist/chat/utils/get-chat-scroll-element.d.ts.map +1 -0
  22. package/dist/chat.cjs +1 -1
  23. package/dist/chat.iife.js +22 -22
  24. package/dist/chat.iife.js.map +1 -1
  25. package/dist/chat.js +10 -6
  26. package/dist/common/action-router.d.ts.map +1 -1
  27. package/dist/common/ga-datalayer.d.ts.map +1 -1
  28. package/dist/common/index.d.ts +2 -0
  29. package/dist/common/index.d.ts.map +1 -1
  30. package/dist/common/protocol-adapter.d.ts.map +1 -1
  31. package/dist/common/suggested-search-keywords.d.ts +19 -0
  32. package/dist/common/suggested-search-keywords.d.ts.map +1 -0
  33. package/dist/common.cjs +1 -1
  34. package/dist/common.js +50 -48
  35. package/dist/index-BLq9Phmq.js +5101 -0
  36. package/dist/index-BLq9Phmq.js.map +1 -0
  37. package/dist/{index-DjdiNtXX.cjs → index-BeP3ETm1.cjs} +2 -2
  38. package/dist/{index-DjdiNtXX.cjs.map → index-BeP3ETm1.cjs.map} +1 -1
  39. package/dist/index-BwBJkYGY.cjs +13 -0
  40. package/dist/index-BwBJkYGY.cjs.map +1 -0
  41. package/dist/{index-hUs4nTXp.js → index-ZO_kMfUR.js} +3 -3
  42. package/dist/{index-hUs4nTXp.js.map → index-ZO_kMfUR.js.map} +1 -1
  43. package/dist/index.cjs +1 -1
  44. package/dist/index.js +5 -5
  45. package/dist/native.cjs +1 -1
  46. package/dist/native.iife.js +28 -28
  47. package/dist/native.iife.js.map +1 -1
  48. package/dist/native.js +1 -1
  49. package/dist/qna/index.d.ts.map +1 -1
  50. package/dist/qna.cjs +1 -1
  51. package/dist/qna.cjs.map +1 -1
  52. package/dist/qna.iife.js +6 -6
  53. package/dist/qna.iife.js.map +1 -1
  54. package/dist/qna.js +8 -8
  55. package/dist/qna.js.map +1 -1
  56. package/dist/schemas-CaaT5BuE.cjs +86 -0
  57. package/dist/schemas-CaaT5BuE.cjs.map +1 -0
  58. package/dist/{schemas-C7h8aelU.js → schemas-DR_GaPQj.js} +895 -873
  59. package/dist/schemas-DR_GaPQj.js.map +1 -0
  60. package/dist/simrel/index.d.ts.map +1 -1
  61. package/dist/simrel/locales/en.d.ts.map +1 -1
  62. package/dist/simrel/locales/tr.d.ts.map +1 -1
  63. package/dist/simrel/types.d.ts +4 -0
  64. package/dist/simrel/types.d.ts.map +1 -1
  65. package/dist/simrel.cjs +1 -1
  66. package/dist/simrel.cjs.map +1 -1
  67. package/dist/simrel.iife.js +7 -7
  68. package/dist/simrel.iife.js.map +1 -1
  69. package/dist/simrel.js +29 -25
  70. package/dist/simrel.js.map +1 -1
  71. package/package.json +2 -1
  72. package/dist/index-BQVIyEOM.cjs +0 -13
  73. package/dist/index-BQVIyEOM.cjs.map +0 -1
  74. package/dist/index-Cdmhc_35.js +0 -4783
  75. package/dist/index-Cdmhc_35.js.map +0 -1
  76. package/dist/schemas-C7h8aelU.js.map +0 -1
  77. package/dist/schemas-DXvpS-Yg.cjs +0 -86
  78. package/dist/schemas-DXvpS-Yg.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-BQVIyEOM.cjs","sources":["../src/common/uuidv7.ts","../src/common/communication-bridge.ts","../src/common/action-router.ts","../src/chat/attachment-utils.ts","../src/chat/api.ts","../src/chat/locales/tr.ts","../src/chat/locales/en.ts","../src/chat/locales/index.ts","../src/common/voice-input.ts","../src/chat/components/KvkkBanner.ts","../src/chat/components/PanelTopBar.ts","../src/chat/components/ThumbnailsColumn.ts","../src/chat/components/ChatDrawer.ts","../src/chat/components/Launcher.ts","../src/common/tts-player.ts","../src/chat/components/ComparisonTable.ts","../src/chat/components/ReviewHighlights.ts","../src/chat/components/AITopPicks.ts","../src/chat/components/GroundingReviewCard.ts","../src/chat/components/AIGroupingCards.ts","../src/chat/components/AISuggestedSearchCards.ts","../src/chat/components/FloatingComparisonButton.ts","../src/chat/components/ProsAndCons.ts","../src/chat/components/CategoriesContainer.ts","../src/chat/components/HandoffNotice.ts","../src/chat/components/ProductSummaryCard.ts","../src/chat/components/renderUISpec.ts","../src/chat/components/typewriter.ts","../src/chat/components/productMentionLinker.ts","../src/chat/components/actionClassifier.ts","../src/chat/components/ChoicePrompter.ts","../src/common/indexed-db.ts","../src/chat/extendedModeManager.ts","../src/chat/panel-manager.ts","../src/chat/session-persistence.ts","../src/chat/kvkk.ts","../src/chat/catalog.ts","../src/chat/index.ts"],"sourcesContent":["/**\n * Zero-dependency UUIDv7 generator.\n *\n * 48-bit Unix-ms timestamp + random bits, version nibble 0x7, variant 0b10.\n * Lexicographically sortable — critical for thread ordering (threadA < threadB).\n */\n\nexport function uuidv7(): string {\n const now = Date.now();\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n\n // 48-bit timestamp (big-endian) in bytes 0–5\n bytes[0] = (now / 2 ** 40) & 0xff;\n bytes[1] = (now / 2 ** 32) & 0xff;\n bytes[2] = (now / 2 ** 24) & 0xff;\n bytes[3] = (now / 2 ** 16) & 0xff;\n bytes[4] = (now / 2 ** 8) & 0xff;\n bytes[5] = now & 0xff;\n\n // Version 7: set high nibble of byte 6\n bytes[6] = (bytes[6]! & 0x0f) | 0x70;\n\n // Variant 10xx: set high bits of byte 8\n bytes[8] = (bytes[8]! & 0x3f) | 0x80;\n\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","/**\n * Two-way communication bridge between the host page and embedded widgets.\n *\n * Host -> Widget: The host sends `window.postMessage({ gengage, type, payload })`.\n * The bridge validates the origin, checks the namespace, and routes to handlers.\n *\n * Widget -> Host: The bridge dispatches a `CustomEvent('gengage:bridge:message')`\n * on window so the host can listen without tight coupling.\n *\n * Built-in message types:\n * - 'addToCart' -- host confirms cart addition\n * - 'navigate' -- host navigates to URL\n * - 'openChat' -- programmatic open\n * - 'closeChat' -- programmatic close\n * - 'getContext' -- host requests current context\n */\n\nexport interface BridgeMessage {\n type: string;\n payload?: unknown;\n}\n\nexport interface CommunicationBridgeOptions {\n /** Widget namespace for message identification (e.g. 'chat', 'qna'). */\n namespace: string;\n /** Allowed origins for postMessage security (default: ['*']). */\n allowedOrigins?: string[];\n /** Callback when a message is received from the host. */\n onMessage?: (msg: BridgeMessage) => void;\n}\n\ntype BridgeHandler = (payload: unknown) => void;\n\nexport class CommunicationBridge {\n private readonly _namespace: string;\n private readonly _allowedOrigins: readonly string[];\n private readonly _onMessage: ((msg: BridgeMessage) => void) | undefined;\n private readonly _handlers = new Map<string, Set<BridgeHandler>>();\n private readonly _messageListener: (event: MessageEvent) => void;\n private _destroyed = false;\n\n constructor(options: CommunicationBridgeOptions) {\n this._namespace = options.namespace;\n // Default to same-origin for postMessage security. Customers who need\n // cross-origin iframe communication can pass allowedOrigins: ['*'] or a\n // specific list of origins via ChatWidgetConfig.allowedOrigins.\n this._allowedOrigins = options.allowedOrigins ?? [location.origin];\n this._onMessage = options.onMessage;\n\n if (this._allowedOrigins.includes('*') && typeof import.meta !== 'undefined' && import.meta.env?.DEV) {\n console.warn('[gengage] postMessage bridge using wildcard origin. Set allowedOrigins for production security.');\n }\n\n this._messageListener = (event: MessageEvent) => this._handlePostMessage(event);\n window.addEventListener('message', this._messageListener);\n }\n\n /** Send a message to the host page via CustomEvent on window. */\n send(type: string, payload?: unknown): void {\n if (this._destroyed) return;\n\n const detail: { namespace: string; type: string; payload?: unknown } = {\n namespace: this._namespace,\n type,\n };\n if (payload !== undefined) {\n detail.payload = payload;\n }\n\n window.dispatchEvent(\n new CustomEvent('gengage:bridge:message', {\n detail,\n bubbles: false,\n }),\n );\n }\n\n /**\n * Register a handler for a specific message type.\n * Returns an unsubscribe function.\n */\n on(type: string, handler: BridgeHandler): () => void {\n if (!this._handlers.has(type)) {\n this._handlers.set(type, new Set());\n }\n // The Map.get is guaranteed non-null after the set above\n const handlers = this._handlers.get(type)!;\n handlers.add(handler);\n\n return () => {\n handlers.delete(handler);\n if (handlers.size === 0) {\n this._handlers.delete(type);\n }\n };\n }\n\n /** Clean up all event listeners and handlers. */\n destroy(): void {\n if (this._destroyed) return;\n this._destroyed = true;\n window.removeEventListener('message', this._messageListener);\n this._handlers.clear();\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private _handlePostMessage(event: MessageEvent): void {\n if (this._destroyed) return;\n\n // Validate origin\n if (!this._isOriginAllowed(event.origin)) return;\n\n // Validate message shape: must be an object with { gengage, type }\n const data: unknown = event.data;\n if (!isValidBridgeData(data)) return;\n\n // Only process messages targeting this namespace\n if (data.gengage !== this._namespace) return;\n\n const msg: BridgeMessage = { type: data.type };\n if (data.payload !== undefined) {\n msg.payload = data.payload;\n }\n\n // Invoke the general onMessage callback\n this._onMessage?.(msg);\n\n // Route to type-specific handlers\n const handlers = this._handlers.get(msg.type);\n if (handlers) {\n for (const handler of handlers) {\n handler(msg.payload);\n }\n }\n }\n\n private _isOriginAllowed(origin: string): boolean {\n if (this._allowedOrigins.includes('*')) return true;\n return this._allowedOrigins.includes(origin);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Shape guard for incoming postMessage data. */\nfunction isValidBridgeData(data: unknown): data is { gengage: string; type: string; payload?: unknown } {\n if (typeof data !== 'object' || data === null) return false;\n const obj = data as Record<string, unknown>;\n return typeof obj['gengage'] === 'string' && typeof obj['type'] === 'string';\n}\n","import type { UnknownActionPolicy } from './config-schema.js';\nimport type { ActionPayload, StreamEventAction } from './types.js';\nimport { isSafeUrl } from './safe-html.js';\nimport { debugLog } from './debug.js';\n\nexport interface HostActionHandlers {\n openChat?: (payload?: ActionPayload | unknown) => void;\n navigate?: (params: { url: string; newTab?: boolean }) => void;\n saveSession?: (params: { sessionId: string; sku: string }) => void;\n addToCart?: (params: { sku: string; quantity: number; cartCode: string }) => void;\n scriptCall?: (params: { name: string; payload?: Record<string, unknown> }) => void;\n unknown?: (action: StreamEventAction['action']) => void;\n}\n\nexport interface ActionRouterOptions {\n allowScriptCall?: boolean;\n unknownActionPolicy?: UnknownActionPolicy;\n logger?: Pick<Console, 'warn' | 'error' | 'debug'>;\n}\n\nconst defaultLogger: Pick<Console, 'warn' | 'error' | 'debug'> = console;\n\nexport function routeStreamAction(\n event: StreamEventAction,\n handlers: HostActionHandlers,\n options: ActionRouterOptions = {},\n): void {\n const action = event.action;\n const logger = options.logger ?? defaultLogger;\n debugLog('action', `routing action: ${action.kind}`, action);\n\n switch (action.kind) {\n case 'open_chat': {\n handlers.openChat?.(action.payload);\n return;\n }\n case 'navigate': {\n if (typeof action.url !== 'string') {\n handleUnknownAction(action, handlers, options, logger);\n return;\n }\n const newTab = typeof action.newTab === 'boolean' ? action.newTab : undefined;\n if (handlers.navigate) {\n handlers.navigate({ url: action.url, ...(newTab !== undefined && { newTab }) });\n return;\n }\n defaultNavigate(action.url, newTab);\n return;\n }\n case 'save_session': {\n if (typeof action.sessionId !== 'string' || typeof action.sku !== 'string') {\n handleUnknownAction(action, handlers, options, logger);\n return;\n }\n handlers.saveSession?.({ sessionId: action.sessionId, sku: action.sku });\n return;\n }\n case 'add_to_cart': {\n if (\n typeof action.sku !== 'string' ||\n typeof action.quantity !== 'number' ||\n typeof action.cartCode !== 'string'\n ) {\n handleUnknownAction(action, handlers, options, logger);\n return;\n }\n handlers.addToCart?.({\n sku: action.sku,\n quantity: action.quantity,\n cartCode: action.cartCode,\n });\n return;\n }\n case 'script_call': {\n if (options.allowScriptCall === false) {\n handleUnknownAction(action, handlers, options, logger);\n return;\n }\n if (typeof action.name !== 'string') {\n handleUnknownAction(action, handlers, options, logger);\n return;\n }\n const payload = isRecord(action.payload) ? action.payload : undefined;\n handlers.scriptCall?.({ name: action.name, ...(payload !== undefined && { payload }) });\n return;\n }\n default: {\n handleUnknownAction(action, handlers, options, logger);\n }\n }\n}\n\nfunction handleUnknownAction(\n action: StreamEventAction['action'],\n handlers: HostActionHandlers,\n options: ActionRouterOptions,\n logger: Pick<Console, 'warn' | 'error' | 'debug'>,\n): void {\n const policy = options.unknownActionPolicy ?? 'log-and-ignore';\n if (policy === 'delegate') {\n handlers.unknown?.(action);\n if (!handlers.unknown) {\n logger.warn('[gengage] Unknown action received without delegate handler', action);\n }\n return;\n }\n\n if (policy === 'throw') {\n throw new Error(`[gengage] Unknown action kind: ${(action as { kind?: unknown }).kind}`);\n }\n\n logger.warn('[gengage] Unknown action ignored', action);\n}\n\nfunction defaultNavigate(url: string, newTab?: boolean): void {\n if (typeof window === 'undefined') return;\n if (!isSafeUrl(url)) {\n console.warn('[gengage] Blocked navigation to unsafe URL:', url);\n return;\n }\n if (newTab) {\n window.open(url, '_blank', 'noopener,noreferrer');\n return;\n }\n window.location.href = url;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","export const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/png', 'image/webp'] as const;\nexport const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB\n\nexport type ValidationResult = { ok: true } | { ok: false; reason: 'invalid_type' | 'too_large' };\n\nexport function validateImageFile(file: File): ValidationResult {\n if (!(ALLOWED_MIME_TYPES as readonly string[]).includes(file.type)) {\n return { ok: false, reason: 'invalid_type' };\n }\n if (file.size > MAX_FILE_SIZE) {\n return { ok: false, reason: 'too_large' };\n }\n return { ok: true };\n}\n","import { consumeStream } from '../common/streaming.js';\nimport { adaptBackendEvent } from '../common/protocol-adapter.js';\nimport { buildChatEndpointUrl } from '../common/api-paths.js';\nimport type { StreamEvent, UISpec, PageContext } from '../common/types.js';\nimport type { ChatTransportConfig } from '../common/api-paths.js';\n\nexport interface BackendRequestMeta {\n outputLanguage: string;\n parentUrl: string;\n windowWidth: string;\n windowHeight: string;\n selfUrl: string;\n id: string;\n userId: string;\n appId: string;\n threads: unknown[];\n createdAt: string;\n kvkkApproved: boolean;\n voiceEnabled: boolean;\n threadId: string;\n isControlGroup: boolean;\n isMobile: boolean;\n viewId?: string;\n}\n\nexport interface ProcessActionRequest {\n account_id: string;\n session_id: string;\n correlation_id: string;\n user_id?: string;\n view_id?: string;\n\n /**\n * @deprecated Use top-level `type` and `payload` instead.\n * Kept for one release cycle of backward compatibility.\n */\n action?: {\n title: string;\n type: string;\n payload?: unknown;\n };\n\n /** Backend action type identifier (preferred over `action.type`). */\n type?: string;\n /** Arbitrary action data passed to the backend (preferred over `action.payload`). */\n payload?: unknown;\n\n sku?: string;\n page_type?: string;\n locale?: string;\n meta?: BackendRequestMeta;\n context?: {\n messages?: Array<{ role: string; content: string }>;\n [key: string]: unknown;\n };\n}\n\nexport interface ActionEnrichmentContext {\n pageContext?: PageContext | undefined;\n backendContext?: import('../common/types.js').BackendContext | null | undefined;\n isMobile?: boolean | undefined;\n}\n\n/**\n * Enriches action payloads with fields the backend expects.\n * Only adds fields that are not already present in the payload.\n */\nexport function enrichActionPayload(\n action: { title: string; type: string; payload?: unknown },\n ctx: ActionEnrichmentContext,\n): { title: string; type: string; payload?: unknown } {\n const type = action.type;\n const existing =\n action.payload != null && typeof action.payload === 'object' && !Array.isArray(action.payload)\n ? (action.payload as Record<string, unknown>)\n : {};\n\n // Helper: only set if not already present\n const merge = (additions: Record<string, unknown>): Record<string, unknown> => {\n const result = { ...existing };\n for (const [key, value] of Object.entries(additions)) {\n if (!(key in result)) {\n result[key] = value;\n }\n }\n return result;\n };\n\n switch (type) {\n case 'inputText': {\n const additions: Record<string, unknown> = {\n is_launcher: 0,\n };\n if (ctx.pageContext?.extra) additions['page_details'] = ctx.pageContext.extra;\n // is_suggested_text may already be set by adapter — don't overwrite\n if (!('is_suggested_text' in existing)) additions['is_suggested_text'] = 0;\n return { ...action, payload: merge(additions) };\n }\n\n case 'findSimilar': {\n const additions: Record<string, unknown> = {\n is_launcher: 0,\n };\n if (action.title) additions['text'] = action.title;\n if (action.title) additions['input'] = action.title;\n return { ...action, payload: merge(additions) };\n }\n\n case 'getComparisonTable': {\n // sku_list should already be set; no-op if present\n return action;\n }\n\n case 'addToCart': {\n const additions: Record<string, unknown> = {};\n if (!('error_message' in existing)) additions['error_message'] = '';\n return { ...action, payload: merge(additions) };\n }\n\n case 'reviewSummary': {\n const additions: Record<string, unknown> = {};\n if (ctx.pageContext?.sku && !('sku' in existing)) {\n additions['sku'] = ctx.pageContext.sku;\n }\n if (Object.keys(additions).length === 0) return action;\n return { ...action, payload: merge(additions) };\n }\n\n default:\n return action;\n }\n}\n\nexport interface StreamCallbacks {\n onTextChunk: (\n content: string,\n isFinal: boolean,\n extra?: {\n productMentions?: Array<{ sku: string; short_name: string }> | undefined;\n skuToProductItem?: Record<string, Record<string, unknown>> | undefined;\n conversationMode?: string | undefined;\n },\n ) => void;\n onUISpec: (spec: UISpec, widget: string, panelHint?: 'panel') => void;\n onAction: (event: StreamEvent) => void;\n onMetadata: (event: StreamEvent) => void;\n onError: (err: Error) => void;\n onDone: () => void;\n}\n\n/**\n * Action type mapping.\n * The backend's ActionType enum uses `inputText` for user messages,\n * while the frontend uses `user_message`. Map at the boundary.\n */\nconst ACTION_TYPE_MAP: Record<string, string> = {\n user_message: 'inputText',\n};\n\n/**\n * Builds the request body for `/chat/process_action`.\n *\n * Backend expects `type` and `payload` at the top level —\n * matching chat_api.py's current schema.\n */\nfunction buildRequestBody(request: ProcessActionRequest): string {\n const { action, type: flatType, payload: flatPayload, ...rest } = request;\n // Prefer top-level type/payload; fall back to deprecated action wrapper.\n const rawType = flatType ?? action?.type ?? 'inputText';\n const rawPayload = flatPayload ?? action?.payload;\n const mappedType = ACTION_TYPE_MAP[rawType] ?? rawType;\n const body: Record<string, unknown> = {\n ...rest,\n type: mappedType,\n };\n if (rawPayload !== undefined) {\n // Backend expects payload as an object. Wrap string payloads for compatibility.\n body.payload = typeof rawPayload === 'string' ? { text: rawPayload } : rawPayload;\n }\n return JSON.stringify(body);\n}\n\nexport function sendChatMessage(\n request: ProcessActionRequest,\n callbacks: StreamCallbacks,\n transport: ChatTransportConfig,\n): AbortController {\n const url = buildChatEndpointUrl('process_action', transport);\n const controller = new AbortController();\n\n const run = async (): Promise<void> => {\n try {\n const requestBody = buildRequestBody(request);\n\n // Use FormData only when an attachment is present; otherwise send JSON.\n const useFormData = transport.attachment !== undefined;\n\n let fetchInit: RequestInit;\n if (useFormData) {\n const formData = new FormData();\n formData.append('request', requestBody);\n if (transport.attachment !== undefined) {\n formData.append('attachment', transport.attachment);\n }\n fetchInit = {\n method: 'POST',\n body: formData,\n signal: controller.signal,\n };\n } else {\n fetchInit = {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: requestBody,\n signal: controller.signal,\n };\n }\n\n const response = await fetch(url, fetchInit);\n\n if (!response.ok) {\n let detail = response.statusText;\n try {\n const body = await response.json();\n const msg =\n (body as Record<string, unknown>).detail ??\n (body as Record<string, unknown>).message ??\n (body as Record<string, unknown>).error;\n if (typeof msg === 'string') detail = msg;\n } catch {\n /* body not JSON — keep statusText */\n }\n callbacks.onError(new Error(`HTTP ${response.status}: ${detail}`));\n return;\n }\n\n // Guard against double-fire: onDone must fire exactly once\n let doneFired = false;\n const fireDone = () => {\n if (doneFired) return;\n doneFired = true;\n callbacks.onDone();\n };\n\n await consumeStream(response, {\n onEvent: (event: StreamEvent) => {\n const normalized = adaptBackendEvent(event as unknown as Record<string, unknown>);\n\n if (!normalized) return;\n\n switch (normalized.type) {\n case 'text_chunk':\n callbacks.onTextChunk(normalized.content, normalized.final === true, {\n productMentions: normalized.productMentions,\n skuToProductItem: normalized.skuToProductItem,\n conversationMode: normalized.conversationMode,\n });\n break;\n case 'ui_spec':\n callbacks.onUISpec(normalized.spec, normalized.widget, normalized.panelHint);\n break;\n case 'action':\n callbacks.onAction(normalized);\n break;\n case 'metadata':\n callbacks.onMetadata(normalized);\n break;\n case 'error':\n callbacks.onError(new Error(normalized.message));\n break;\n case 'done':\n fireDone();\n break;\n }\n },\n onError: callbacks.onError,\n signal: controller.signal,\n });\n\n // Fallback: if stream completed without a chatStreamEnd event (backend\n // bug or truncated response), ensure onDone fires so the widget doesn't\n // get stuck with a spinning typing indicator.\n fireDone();\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n callbacks.onError(err instanceof Error ? err : new Error(String(err)));\n }\n };\n\n void run();\n return controller;\n}\n","import type { ChatI18n } from '../types.js';\n\nexport const CHAT_I18N_TR: ChatI18n = {\n headerTitle: 'Ürün Uzmanı',\n inputPlaceholder: 'Ürün ara, soru sor',\n sendButton: 'Gönder',\n closeButton: 'Kapat',\n openButton: 'Sohbeti aç',\n newChatButton: 'Yeni sohbet',\n poweredBy: 'Powered by Gengage',\n errorMessage: 'Bir hata oluştu. Lütfen tekrar deneyin.',\n retryButton: 'Tekrar Dene',\n loadingMessage: 'Düşünüyorum...',\n productCtaLabel: 'İncele',\n attachImageButton: 'Resim ekle',\n removeAttachmentButton: 'Resmi kaldır',\n invalidFileType: 'Sadece JPEG, PNG ve WebP dosyaları destekleniyor.',\n fileTooLarge: \"Dosya boyutu 5 MB'dan küçük olmalıdır.\",\n aiTopPicksTitle: 'Sizin İçin En İyiler',\n roleWinner: 'En Beğendiğim',\n roleBestValue: 'En Uygun Fiyatlı',\n roleBestAlternative: 'En İyi Alternatif',\n viewDetails: 'Detayları Gör',\n groundingReviewCta: 'Yorumları Oku',\n groundingReviewSubtitle: '{count} yorum mevcut',\n variantsLabel: 'Varyantlar',\n sortRelated: 'Önerilen',\n sortPriceAsc: 'Fiyat ↑',\n sortPriceDesc: 'Fiyat ↓',\n compareSelected: 'Karşılaştır',\n compareMinHint: 'En az 2 ürün seçin',\n panelTitleProductDetails: 'Ürün Detayı',\n panelTitleSimilarProducts: 'Benzer Ürünler',\n panelTitleComparisonResults: 'Karşılaştırma Sonuçları',\n panelTitleCategories: 'Kategoriler',\n panelTitleSearchResults: 'Arama Sonuçları',\n inStockLabel: 'Stokta',\n outOfStockLabel: 'Tükendi',\n findSimilarLabel: 'Benzerlerini Bul',\n choicePrompterHeading: 'Kararsız mı kaldın?',\n choicePrompterSuggestion: 'Ürünleri seçip karşılaştırabilirsin',\n choicePrompterCta: 'Seç ve Karşılaştır',\n viewMoreLabel: 'Daha Fazla Göster',\n similarProductsLabel: 'Benzer Ürünler',\n addToCartButton: 'Sepete Ekle',\n shareButton: 'Paylaş',\n productInfoTab: 'Ürün Bilgileri',\n specificationsTab: 'Teknik Özellikler',\n recommendedChoiceLabel: 'Önerilen Seçim',\n highlightsLabel: 'Öne Çıkan Özellikler',\n keyDifferencesLabel: 'Temel Farklar',\n specialCasesLabel: 'Özel Durumlar İçin',\n emptyReviewsMessage: 'Yorum özeti bulunamadı.',\n closeAriaLabel: 'Kapat',\n startChatLabel: 'Sohbete Başla',\n voiceButton: 'Sesli giriş',\n voiceListening: 'Dinleniyor...',\n voiceNotSupported: 'Sesli giriş bu tarayıcıda desteklenmiyor.',\n voicePermissionDenied: 'Mikrofon erişimi reddedildi.',\n voiceError: 'Sesli giriş hatası.',\n handoffHeading: 'Destek temsilcisine aktarılıyor',\n productNotFoundMessage: 'Bu ürün bilgisi şu an kullanılamıyor. Başka bir konuda yardımcı olabilirim.',\n stopGenerating: 'Yanıtı Durdur',\n offlineMessage: 'Çevrimdışısınız — bağlantı kurulduğunda mesajlar gönderilecek.',\n stillWorkingMessage: 'İsteğiniz üzerinde hâlâ çalışılıyor...',\n cartAriaLabel: 'Sepetim',\n favoritesAriaLabel: 'Favorilerim',\n showPanelAriaLabel: 'Sonuçları Göster',\n addToFavoritesLabel: 'Favorilere ekle',\n customerReviewsTitle: 'Müşteri Yorumları',\n togglePanelAriaLabel: 'Paneli aç/kapat',\n chatMessagesAriaLabel: 'Sohbet mesajları',\n suggestionsAriaLabel: 'Öneriler',\n moreSuggestionsAriaLabel: 'Daha fazla öneri',\n rollbackAriaLabel: 'Bu mesaja geri dön',\n backAriaLabel: 'Geri',\n forwardAriaLabel: 'İleri',\n dismissAriaLabel: 'Kapat',\n cartAddErrorMessage: 'Üzgünüm sepete ekleyemedim, bir sorunla karşılaştım.',\n reviewFilterAll: 'Tümü',\n reviewFilterPositive: 'Olumlu',\n reviewFilterNegative: 'Olumsuz',\n decreaseLabel: 'Azalt',\n increaseLabel: 'Artır',\n tryAgainButton: 'Tekrar dene',\n askSomethingElseButton: 'Başka bir şey sor',\n accountInactiveMessage: 'Bu hesap şu an aktif değil. Lütfen daha sonra tekrar deneyin.',\n favoritesPageTitle: 'Favorilerim',\n emptyFavoritesMessage: 'Henüz favori ürün yok. Bir ürünü kalp ikonuna tıklayarak favorilere ekleyebilirsin.',\n};\n","import type { ChatI18n } from '../types.js';\n\nexport const CHAT_I18N_EN: ChatI18n = {\n headerTitle: 'Product Expert',\n inputPlaceholder: 'Search products, ask questions',\n sendButton: 'Send',\n closeButton: 'Close',\n openButton: 'Open chat',\n newChatButton: 'New chat',\n poweredBy: 'Powered by Gengage',\n errorMessage: 'Something went wrong. Please try again.',\n retryButton: 'Retry',\n loadingMessage: 'Thinking...',\n productCtaLabel: 'View',\n attachImageButton: 'Attach image',\n removeAttachmentButton: 'Remove image',\n invalidFileType: 'Only JPEG, PNG and WebP files are supported.',\n fileTooLarge: 'File must be smaller than 5 MB.',\n aiTopPicksTitle: 'Top Picks for You',\n roleWinner: 'Top Pick',\n roleBestValue: 'Best Value',\n roleBestAlternative: 'Best Alternative',\n viewDetails: 'View Details',\n groundingReviewCta: 'Read Reviews',\n groundingReviewSubtitle: '{count} reviews available',\n variantsLabel: 'Variants',\n sortRelated: 'Related',\n sortPriceAsc: 'Price ↑',\n sortPriceDesc: 'Price ↓',\n compareSelected: 'Compare',\n compareMinHint: 'Select at least 2 products',\n panelTitleProductDetails: 'Product Details',\n panelTitleSimilarProducts: 'Similar Products',\n panelTitleComparisonResults: 'Comparison Results',\n panelTitleCategories: 'Categories',\n panelTitleSearchResults: 'Search Results',\n inStockLabel: 'In Stock',\n outOfStockLabel: 'Out of Stock',\n findSimilarLabel: 'Find Similar',\n choicePrompterHeading: \"Can't decide?\",\n choicePrompterSuggestion: 'Select products to compare them',\n choicePrompterCta: 'Select & Compare',\n viewMoreLabel: 'Show More',\n similarProductsLabel: 'Similar Products',\n addToCartButton: 'Add to Cart',\n shareButton: 'Share',\n productInfoTab: 'Product Info',\n specificationsTab: 'Specifications',\n recommendedChoiceLabel: 'Recommended Choice',\n highlightsLabel: 'Key Highlights',\n keyDifferencesLabel: 'Key Differences',\n specialCasesLabel: 'For Special Cases',\n emptyReviewsMessage: 'No review summary found.',\n closeAriaLabel: 'Close',\n startChatLabel: 'Start Chat',\n voiceButton: 'Voice input',\n voiceListening: 'Listening...',\n voiceNotSupported: 'Voice input is not supported in this browser.',\n voicePermissionDenied: 'Microphone access denied.',\n voiceError: 'Voice input error.',\n handoffHeading: 'Transferring to a support agent',\n productNotFoundMessage: 'Product information is currently unavailable. I can help with something else.',\n stopGenerating: 'Stop generating',\n offlineMessage: \"You're offline — messages will send when you reconnect.\",\n stillWorkingMessage: 'Still working on your request...',\n cartAriaLabel: 'My cart',\n favoritesAriaLabel: 'My favorites',\n showPanelAriaLabel: 'Show Results',\n addToFavoritesLabel: 'Add to favorites',\n customerReviewsTitle: 'Customer Reviews',\n togglePanelAriaLabel: 'Toggle panel',\n chatMessagesAriaLabel: 'Chat messages',\n suggestionsAriaLabel: 'Suggestions',\n moreSuggestionsAriaLabel: 'More suggestions',\n rollbackAriaLabel: 'Rollback to this message',\n backAriaLabel: 'Back',\n forwardAriaLabel: 'Forward',\n dismissAriaLabel: 'Dismiss',\n cartAddErrorMessage: \"Sorry, I couldn't add that to your cart. Something went wrong.\",\n reviewFilterAll: 'All',\n reviewFilterPositive: 'Positive',\n reviewFilterNegative: 'Negative',\n decreaseLabel: 'Decrease',\n increaseLabel: 'Increase',\n tryAgainButton: 'Try again',\n askSomethingElseButton: 'Ask something else',\n accountInactiveMessage: 'This account is currently inactive. Please try again later.',\n favoritesPageTitle: 'My Favorites',\n emptyFavoritesMessage: 'No favorites yet. Heart a product to save it here.',\n};\n","import type { ChatI18n } from '../types.js';\nimport { CHAT_I18N_TR } from './tr.js';\nimport { CHAT_I18N_EN } from './en.js';\n\nfunction normalizeLocale(locale?: string): string {\n if (!locale) return 'tr';\n return locale.toLowerCase().split('-')[0] ?? 'tr';\n}\n\nexport function resolveChatLocale(locale?: string): ChatI18n {\n switch (normalizeLocale(locale)) {\n case 'en':\n return CHAT_I18N_EN;\n default:\n return CHAT_I18N_TR;\n }\n}\n\nexport { CHAT_I18N_TR, CHAT_I18N_EN };\n","/**\n * Browser-native Web Speech API voice input.\n *\n * Uses the SpeechRecognition API for real-time speech-to-text.\n * The frontend sends transcribed text directly — no audio blobs\n * are sent to the backend. This replaces server-side Groq Whisper.\n *\n * Supports:\n * - Real-time transcription with interim results\n * - Turkish (`tr-TR`) and English (`en-US`) language support\n * - Auto-submit on silence (configurable timeout)\n * - Microphone permission handling with descriptive errors\n *\n * Browser support:\n * - Chrome 33+, Edge 79+, Safari 14.1+ (via webkitSpeechRecognition)\n * - Firefox: NOT SUPPORTED (no SpeechRecognition API)\n */\n\n// ---------------------------------------------------------------------------\n// Web Speech API types (not in lib.dom.d.ts for all TS targets)\n// ---------------------------------------------------------------------------\n\ninterface SpeechRecognitionEvent extends Event {\n readonly resultIndex: number;\n readonly results: SpeechRecognitionResultList;\n}\n\ninterface SpeechRecognitionResultList {\n readonly length: number;\n item(index: number): SpeechRecognitionResult;\n [index: number]: SpeechRecognitionResult;\n}\n\ninterface SpeechRecognitionResult {\n readonly isFinal: boolean;\n readonly length: number;\n item(index: number): SpeechRecognitionAlternative;\n [index: number]: SpeechRecognitionAlternative;\n}\n\ninterface SpeechRecognitionAlternative {\n readonly transcript: string;\n readonly confidence: number;\n}\n\ninterface SpeechRecognitionErrorEvent extends Event {\n readonly error: string;\n readonly message: string;\n}\n\ninterface SpeechRecognitionConstructor {\n new (): SpeechRecognitionInstance;\n}\n\ninterface SpeechRecognitionInstance extends EventTarget {\n continuous: boolean;\n interimResults: boolean;\n lang: string;\n maxAlternatives: number;\n onresult: ((event: SpeechRecognitionEvent) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEvent) => void) | null;\n onend: (() => void) | null;\n onstart: (() => void) | null;\n onspeechend: (() => void) | null;\n onaudiostart: (() => void) | null;\n start(): void;\n stop(): void;\n abort(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport type VoiceInputState = 'idle' | 'listening' | 'error';\n\nexport type VoiceInputErrorCode =\n | 'not-supported'\n | 'not-allowed'\n | 'no-microphone'\n | 'no-speech'\n | 'network'\n | 'aborted'\n | 'unknown';\n\nexport interface VoiceInputCallbacks {\n /** Called with interim transcript while the user speaks. */\n onInterim?: (text: string) => void;\n /** Called with final transcript when a phrase is recognized. */\n onFinal?: (text: string) => void;\n /** Called when auto-submit fires (silence timeout reached with final text). */\n onAutoSubmit?: (text: string) => void;\n /** Called when voice input state changes. */\n onStateChange?: (state: VoiceInputState) => void;\n /** Called on recognition error. */\n onError?: (code: VoiceInputErrorCode, message: string) => void;\n}\n\nexport interface VoiceInputOptions {\n /** BCP 47 language tag. Default: 'tr-TR'. */\n lang?: string;\n /** Silence duration in ms before auto-submit. Default: 1500. */\n silenceTimeoutMs?: number;\n /** Whether to auto-submit on silence. Default: true. */\n autoSubmit?: boolean;\n}\n\n/**\n * Check whether the browser supports the Web Speech API.\n */\nexport function isVoiceInputSupported(): boolean {\n return getSpeechRecognitionConstructor() !== null;\n}\n\nfunction getSpeechRecognitionConstructor(): SpeechRecognitionConstructor | null {\n const w = globalThis as unknown as Record<string, unknown>;\n return (w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null) as SpeechRecognitionConstructor | null;\n}\n\nexport class VoiceInput {\n private recognition: SpeechRecognitionInstance | null = null;\n private _state: VoiceInputState = 'idle';\n private silenceTimer: ReturnType<typeof setTimeout> | null = null;\n private accumulatedTranscript = '';\n private readonly callbacks: VoiceInputCallbacks;\n private readonly lang: string;\n private readonly silenceTimeoutMs: number;\n private readonly autoSubmit: boolean;\n private intentionalStop = false;\n private _lastRestartAt = 0;\n\n constructor(callbacks: VoiceInputCallbacks, options?: VoiceInputOptions) {\n this.callbacks = callbacks;\n this.lang = options?.lang ?? 'tr-TR';\n this.silenceTimeoutMs = options?.silenceTimeoutMs ?? 1500;\n this.autoSubmit = options?.autoSubmit ?? true;\n }\n\n get state(): VoiceInputState {\n return this._state;\n }\n\n /**\n * Start listening. Requests microphone permission on first call.\n */\n start(): void {\n if (this._state === 'listening') return;\n\n const Ctor = getSpeechRecognitionConstructor();\n if (!Ctor) {\n this.setState('error');\n this.callbacks.onError?.('not-supported', 'Web Speech API is not supported in this browser.');\n return;\n }\n\n // Require secure context (HTTPS)\n if (typeof globalThis.isSecureContext !== 'undefined' && !globalThis.isSecureContext) {\n this.setState('error');\n this.callbacks.onError?.('not-allowed', 'Voice input requires HTTPS.');\n return;\n }\n\n this.accumulatedTranscript = '';\n this.intentionalStop = false;\n\n const recognition = new Ctor();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = this.lang;\n recognition.maxAlternatives = 1;\n\n recognition.onstart = () => {\n this.setState('listening');\n };\n\n recognition.onresult = (event: SpeechRecognitionEvent) => {\n this.clearSilenceTimer();\n\n let interim = '';\n let latestFinal = '';\n\n for (let i = event.resultIndex; i < event.results.length; i++) {\n const result = event.results[i];\n if (!result) continue;\n const alt = result[0];\n if (!alt) continue;\n if (result.isFinal) {\n latestFinal += alt.transcript;\n } else {\n interim += alt.transcript;\n }\n }\n\n if (latestFinal) {\n this.accumulatedTranscript += latestFinal;\n this.callbacks.onFinal?.(this.accumulatedTranscript);\n }\n\n if (interim) {\n this.callbacks.onInterim?.(this.accumulatedTranscript + interim);\n }\n\n // Start silence timer for auto-submit\n if (this.autoSubmit && this.accumulatedTranscript) {\n this.startSilenceTimer();\n }\n };\n\n recognition.onerror = (event: SpeechRecognitionErrorEvent) => {\n const code = mapErrorCode(event.error);\n // 'no-speech' and 'aborted' during intentional stop are not real errors\n if (this.intentionalStop && (event.error === 'no-speech' || event.error === 'aborted')) {\n return;\n }\n this.setState('error');\n this.callbacks.onError?.(code, event.message || event.error);\n };\n\n recognition.onend = () => {\n this.clearSilenceTimer();\n // Auto-restart if still in listening state (browser may stop recognition arbitrarily)\n if (this._state === 'listening' && !this.intentionalStop) {\n const now = Date.now();\n // Prevent rapid restart loop on Chrome — if onend fires within 500ms\n // of the last restart, the browser is refusing to stay active.\n if (now - this._lastRestartAt < 500) {\n this.setState('idle');\n return;\n }\n this._lastRestartAt = now;\n try {\n recognition.start();\n } catch {\n this.setState('idle');\n }\n return;\n }\n this.setState('idle');\n };\n\n this.recognition = recognition;\n\n try {\n recognition.start();\n } catch {\n this.setState('error');\n this.callbacks.onError?.('unknown', 'Failed to start speech recognition.');\n }\n }\n\n /**\n * Stop listening. Returns the accumulated transcript.\n */\n stop(): string {\n this.intentionalStop = true;\n this.clearSilenceTimer();\n if (this.recognition) {\n try {\n this.recognition.stop();\n } catch {\n // Already stopped\n }\n this.recognition = null;\n }\n this.setState('idle');\n return this.accumulatedTranscript;\n }\n\n /**\n * Abort listening. Discards any accumulated transcript.\n */\n abort(): void {\n this.intentionalStop = true;\n this.clearSilenceTimer();\n this.accumulatedTranscript = '';\n if (this.recognition) {\n try {\n this.recognition.abort();\n } catch {\n // Already aborted\n }\n this.recognition = null;\n }\n this.setState('idle');\n }\n\n /** Destroy the instance and release resources. */\n destroy(): void {\n this.abort();\n }\n\n private setState(state: VoiceInputState): void {\n if (this._state !== state) {\n this._state = state;\n this.callbacks.onStateChange?.(state);\n }\n }\n\n private startSilenceTimer(): void {\n this.clearSilenceTimer();\n this.silenceTimer = setTimeout(() => {\n const text = this.stop();\n if (text.trim()) {\n this.callbacks.onAutoSubmit?.(text.trim());\n }\n }, this.silenceTimeoutMs);\n }\n\n private clearSilenceTimer(): void {\n if (this.silenceTimer !== null) {\n clearTimeout(this.silenceTimer);\n this.silenceTimer = null;\n }\n }\n}\n\nfunction mapErrorCode(error: string): VoiceInputErrorCode {\n switch (error) {\n case 'not-allowed':\n return 'not-allowed';\n case 'no-speech':\n return 'no-speech';\n case 'audio-capture':\n return 'no-microphone';\n case 'network':\n return 'network';\n case 'aborted':\n return 'aborted';\n default:\n return 'unknown';\n }\n}\n","import { sanitizeHtml } from '../../common/safe-html.js';\n\nexport interface KvkkBannerOptions {\n htmlContent: string;\n onDismiss: () => void;\n closeAriaLabel?: string;\n}\n\nexport function createKvkkBanner(options: KvkkBannerOptions): HTMLElement {\n const banner = document.createElement('div');\n banner.className = 'gengage-chat-kvkk-banner';\n banner.setAttribute('role', 'alert');\n\n const content = document.createElement('div');\n content.className = 'gengage-chat-kvkk-content';\n content.innerHTML = sanitizeHtml(options.htmlContent);\n banner.appendChild(content);\n\n const dismiss = document.createElement('button');\n dismiss.className = 'gengage-chat-kvkk-dismiss';\n dismiss.type = 'button';\n dismiss.setAttribute('aria-label', options.closeAriaLabel ?? 'Close privacy notice');\n dismiss.textContent = '\\u00D7';\n dismiss.addEventListener('click', options.onDismiss);\n banner.appendChild(dismiss);\n\n return banner;\n}\n","/**\n * PanelTopBar — navigation bar at the top of the panel pane.\n *\n * Shows back/forward arrow buttons and a title derived from the current\n * panel content type.\n */\n\nexport interface PanelTopBarOptions {\n onBack: () => void;\n onForward: () => void;\n backAriaLabel?: string;\n forwardAriaLabel?: string;\n}\n\nexport class PanelTopBar {\n private _el: HTMLElement;\n private _backBtn: HTMLButtonElement;\n private _forwardBtn: HTMLButtonElement;\n private _titleEl: HTMLElement;\n\n constructor(options: PanelTopBarOptions) {\n this._el = document.createElement('div');\n this._el.className = 'gengage-chat-panel-topbar';\n\n this._backBtn = document.createElement('button');\n this._backBtn.className = 'gengage-chat-panel-topbar-back';\n this._backBtn.type = 'button';\n this._backBtn.disabled = true;\n this._backBtn.setAttribute('aria-label', options.backAriaLabel ?? 'Back');\n this._backBtn.title = options.backAriaLabel ?? 'Back';\n this._backBtn.textContent = '\\u2190'; // ←\n this._backBtn.addEventListener('click', () => options.onBack());\n\n this._titleEl = document.createElement('span');\n this._titleEl.className = 'gengage-chat-panel-topbar-title';\n\n this._forwardBtn = document.createElement('button');\n this._forwardBtn.className = 'gengage-chat-panel-topbar-forward';\n this._forwardBtn.type = 'button';\n this._forwardBtn.disabled = true;\n this._forwardBtn.setAttribute('aria-label', options.forwardAriaLabel ?? 'Forward');\n this._forwardBtn.title = options.forwardAriaLabel ?? 'Forward';\n this._forwardBtn.textContent = '\\u2192'; // →\n this._forwardBtn.addEventListener('click', () => options.onForward());\n\n this._el.appendChild(this._backBtn);\n this._el.appendChild(this._titleEl);\n this._el.appendChild(this._forwardBtn);\n }\n\n update(canBack: boolean, canForward: boolean, title: string): void {\n this._backBtn.disabled = !canBack;\n this._forwardBtn.disabled = !canForward;\n this._titleEl.textContent = title;\n }\n\n getElement(): HTMLElement {\n return this._el;\n }\n\n getTitle(): string {\n return this._titleEl.textContent ?? '';\n }\n}\n","/**\n * Vertical strip of product thumbnails along the right edge of the panel (results) pane.\n *\n * Clicking a thumbnail dispatches a rollback to the thread where that product was shown.\n */\n\nimport { isSafeImageUrl } from '../../common/safe-html.js';\n\nexport interface ThumbnailEntry {\n sku: string;\n imageUrl: string;\n threadId: string;\n}\n\nexport interface ThumbnailsColumnOptions {\n onThumbnailClick: (threadId: string) => void;\n}\n\nexport class ThumbnailsColumn {\n private readonly _el: HTMLElement;\n private readonly _onThumbnailClick: (threadId: string) => void;\n\n constructor(options: ThumbnailsColumnOptions) {\n this._onThumbnailClick = options.onThumbnailClick;\n\n this._el = document.createElement('div');\n this._el.className = 'gengage-chat-thumbnails-column';\n this._el.style.display = 'none';\n }\n\n getElement(): HTMLElement {\n return this._el;\n }\n\n setEntries(entries: ThumbnailEntry[]): void {\n // Deduplicate by SKU (keep first occurrence)\n const seen = new Set<string>();\n const unique: ThumbnailEntry[] = [];\n for (const entry of entries) {\n if (!seen.has(entry.sku)) {\n seen.add(entry.sku);\n unique.push(entry);\n }\n }\n\n this._el.innerHTML = '';\n\n for (const entry of unique) {\n const thumb = document.createElement('button');\n thumb.type = 'button';\n thumb.className = 'gengage-chat-thumbnail-btn';\n thumb.title = entry.sku;\n\n if (isSafeImageUrl(entry.imageUrl)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-thumbnail-img';\n img.src = entry.imageUrl;\n img.alt = entry.sku;\n img.width = 40;\n img.height = 40;\n thumb.appendChild(img);\n }\n\n thumb.addEventListener('click', () => {\n this._onThumbnailClick(entry.threadId);\n });\n\n this._el.appendChild(thumb);\n }\n }\n\n show(): void {\n this._el.style.display = '';\n }\n\n hide(): void {\n this._el.style.display = 'none';\n }\n}\n","import type { ChatI18n, ChatMessage } from '../types.js';\nimport { sanitizeHtml, isSafeImageUrl } from '../../common/safe-html.js';\nimport { CHAT_I18N_TR } from '../locales/index.js';\nimport { VoiceInput, isVoiceInputSupported } from '../../common/voice-input.js';\nimport { createKvkkBanner } from './KvkkBanner.js';\nimport { PanelTopBar } from './PanelTopBar.js';\nimport { ThumbnailsColumn } from './ThumbnailsColumn.js';\nimport type { ThumbnailEntry } from './ThumbnailsColumn.js';\n\n/** Generic fallback icon (right-arrow) used when a pill specifies an icon name not in the map. */\nconst DEFAULT_ACTION_ICON =\n '<svg viewBox=\"0 0 16 16\" class=\"gengage-chat-icon\"><path d=\"M3 8h10M9 4l4 4-4 4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>';\n\n/** SVG icon map for suggested action chips/pills. Keys match backend icon names. */\nconst SUGGESTED_ACTION_ICONS: Record<string, string> = {\n search:\n '<svg viewBox=\"0 0 16 16\" class=\"gengage-chat-icon\"><circle cx=\"6.5\" cy=\"6.5\" r=\"5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><line x1=\"10\" y1=\"10\" x2=\"15\" y2=\"15\" stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>',\n review:\n '<svg viewBox=\"0 0 16 16\" class=\"gengage-chat-icon\"><polygon points=\"8,1 10,6 15,6 11,9 12.5,14 8,11 3.5,14 5,9 1,6 6,6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>',\n info: '<svg viewBox=\"0 0 16 16\" class=\"gengage-chat-icon\"><circle cx=\"8\" cy=\"8\" r=\"7\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><line x1=\"8\" y1=\"7\" x2=\"8\" y2=\"12\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"8\" cy=\"4.5\" r=\"0.8\" fill=\"currentColor\"/></svg>',\n similar:\n '<svg viewBox=\"0 0 16 16\" class=\"gengage-chat-icon\"><rect x=\"1\" y=\"3\" width=\"6\" height=\"6\" rx=\"1\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><rect x=\"9\" y=\"3\" width=\"6\" height=\"6\" rx=\"1\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>',\n};\n\nexport { SUGGESTED_ACTION_ICONS, DEFAULT_ACTION_ICON };\n\nexport interface ChatDrawerOptions {\n i18n: ChatI18n;\n onSend: (text: string, attachment?: File) => void;\n /** Callback fired when the cart icon button in the header is clicked. */\n onCartClick?: (() => void) | undefined;\n onClose: () => void;\n onAttachment?: (file: File) => void;\n onPanelToggle?: () => void;\n onRollback?: (messageId: string) => void;\n headerTitle?: string | undefined;\n headerAvatarUrl?: string | undefined;\n /** Launcher image URL — used as avatar fallback when headerAvatarUrl is not set. */\n launcherImageUrl?: string | undefined;\n headerBadge?: string | undefined;\n /** URL for the cart icon link in the header (e.g. \"/sepetim\"). */\n /** @deprecated Use onCartClick instead. If set, the cart button will navigate to this URL. */\n headerCartUrl?: string | undefined;\n /** @deprecated Favorites button is always shown. */\n headerFavoritesToggle?: boolean | undefined;\n onFavoritesClick?: (() => void) | undefined;\n /** Callback fired when the panel back button is clicked. */\n onPanelBack?: (() => void) | undefined;\n /** Callback fired when the panel forward button is clicked. */\n onPanelForward?: (() => void) | undefined;\n /**\n * Fired when the user drags the mobile handle and releases.\n * 'half' | 'full' → switch to that snap position.\n * 'close' → close the drawer.\n */\n onMobileSnap?: ((state: 'half' | 'full' | 'close') => void) | undefined;\n /** Returns the current mobile sheet state so the drag handler knows which snap to target. */\n getMobileState?: (() => 'half' | 'full') | undefined;\n /** Returns true when the chat is displayed as a mobile bottom-sheet. Used to keep the side-panel back button always enabled. */\n getMobileViewport?: (() => boolean) | undefined;\n /** Callback fired when a product thumbnail is clicked (for thread rollback). */\n onThumbnailClick?: ((threadId: string) => void) | undefined;\n /** Callback fired when a link in bot HTML is clicked. */\n onLinkClick?: ((url: string) => void) | undefined;\n /** Enable voice input (Web Speech API STT). Default: false. */\n voiceEnabled?: boolean | undefined;\n /** BCP 47 language for speech recognition. Default: 'tr-TR'. */\n voiceLang?: string | undefined;\n /** Callback fired when the \"New Chat\" button is clicked. */\n onNewChat?: (() => void) | undefined;\n}\n\nconst DEFAULT_I18N: ChatI18n = CHAT_I18N_TR;\n\nexport class ChatDrawer {\n private root: HTMLElement;\n private messagesEl: HTMLElement;\n private inputEl: HTMLTextAreaElement;\n private sendBtn: HTMLButtonElement;\n private i18n: ChatI18n;\n private onSend: (text: string, attachment?: File) => void;\n private _panelEl: HTMLElement;\n private _panelVisible = false;\n private _panelCollapsed = false;\n private _dividerEl: HTMLElement;\n private _onPanelToggle: (() => void) | undefined = undefined;\n private _pendingAttachment: File | null = null;\n private _fileInput: HTMLInputElement;\n private _previewStrip: HTMLElement;\n private _previewName: HTMLElement;\n private _onAttachment: ((file: File) => void) | undefined = undefined;\n private _onRollback: ((messageId: string) => void) | undefined = undefined;\n private _onLinkClick: ((url: string) => void) | undefined = undefined;\n private _pillsEl: HTMLElement;\n private _kvkkSlot: HTMLElement;\n private _panelTopBar: PanelTopBar;\n private _userScrolledUp = false;\n private _scrollLockedUntil = 0;\n private _inputChipsEl: HTMLElement;\n private _thumbnailsColumn: ThumbnailsColumn;\n private _panelFloatingEl: HTMLElement;\n private _favBadgeEl: HTMLElement | null = null;\n private _thinkingSteps: string[] = [];\n private _firstBotMessageIds: Set<string> = new Set();\n private _voiceInput: VoiceInput | null = null;\n private _micBtn: HTMLButtonElement | null = null;\n private _voiceEnabled = false;\n private _voiceLang = 'tr-TR';\n private _ignoreNextDividerClick = false;\n private readonly _cleanups: Array<() => void> = [];\n private _focusTrapHandler: ((e: KeyboardEvent) => void) | null = null;\n private _previouslyFocusedElement: HTMLElement | null = null;\n private _stillWorkingTimer: ReturnType<typeof setTimeout> | null = null;\n private _conversationEl: HTMLElement | null = null;\n private readonly _options: ChatDrawerOptions;\n private _reopenPanelBtn: HTMLButtonElement | null = null;\n\n constructor(container: HTMLElement, options: ChatDrawerOptions) {\n this._options = options;\n this.i18n = { ...DEFAULT_I18N, ...options.i18n };\n this.onSend = options.onSend;\n if (options.onPanelToggle !== undefined) {\n this._onPanelToggle = options.onPanelToggle;\n }\n if (options.onAttachment !== undefined) {\n this._onAttachment = options.onAttachment;\n }\n if (options.onRollback !== undefined) {\n this._onRollback = options.onRollback;\n }\n if (options.onLinkClick !== undefined) {\n this._onLinkClick = options.onLinkClick;\n }\n if (options.voiceEnabled) {\n this._voiceEnabled = true;\n }\n if (options.voiceLang !== undefined) {\n this._voiceLang = options.voiceLang;\n }\n\n this.root = document.createElement('div');\n this.root.className = 'gengage-chat-drawer';\n this.root.setAttribute('role', 'dialog');\n this.root.setAttribute('aria-label', this.i18n.headerTitle ?? 'Chat');\n this.root.setAttribute('aria-modal', 'true');\n\n const descId = 'gengage-chat-dialog-desc';\n const descEl = document.createElement('span');\n descEl.id = descId;\n descEl.className = 'gengage-sr-only';\n descEl.textContent = this.i18n.headerTitle ?? 'AI shopping assistant';\n this.root.appendChild(descEl);\n this.root.setAttribute('aria-describedby', descId);\n\n // Mobile drag handle — pill indicator at the very top of the sheet\n {\n const handleEl = document.createElement('div');\n handleEl.className = 'gengage-chat-drawer-handle';\n handleEl.setAttribute('aria-hidden', 'true');\n this.root.appendChild(handleEl);\n\n const SNAP_THRESHOLD = 72; // px to trigger a snap to the next position\n let dragStartY = 0;\n let dragDelta = 0;\n let dragging = false;\n\n const onHandleTouchStart = (e: TouchEvent) => {\n if (!(this._options.getMobileViewport?.() ?? window.innerWidth <= 768)) return;\n const t = e.changedTouches?.[0];\n if (!t) return;\n dragStartY = t.clientY;\n dragDelta = 0;\n dragging = true;\n this.root.style.transition = 'none';\n };\n\n const onHandleTouchMove = (e: TouchEvent) => {\n if (!dragging) return;\n const t = e.changedTouches?.[0];\n if (!t) return;\n dragDelta = t.clientY - dragStartY;\n // Clamp: don't allow pulling upward past the current top\n const currentState = options.getMobileState?.() ?? 'full';\n const clampedDelta =\n currentState === 'full'\n ? Math.max(0, dragDelta) // full → only drag down\n : dragDelta; // half → allow both directions\n e.preventDefault(); // prevent body scroll\n this.root.style.transform = `translateY(${clampedDelta}px)`;\n };\n\n const onHandleTouchEnd = () => {\n if (!dragging) return;\n dragging = false;\n const currentState = options.getMobileState?.() ?? 'full';\n\n let nextState: 'half' | 'full' | 'close';\n if (dragDelta > SNAP_THRESHOLD) {\n nextState = currentState === 'full' ? 'half' : 'close';\n } else if (dragDelta < -SNAP_THRESHOLD && currentState === 'half') {\n nextState = 'full';\n } else {\n nextState = currentState; // snap back\n }\n\n // Re-enable transition before state change so CSS animates smoothly\n this.root.style.transition = '';\n if (nextState === 'close') {\n // Animate drawer off-screen before calling close\n this.root.style.transform = 'translateY(100%)';\n setTimeout(() => {\n this.root.style.transform = '';\n options.onMobileSnap?.('close');\n }, 280);\n } else {\n this.root.style.transform = '';\n options.onMobileSnap?.(nextState);\n }\n dragDelta = 0;\n };\n\n const onHandleTouchCancel = () => {\n if (!dragging) return;\n dragging = false;\n dragDelta = 0;\n this.root.style.transition = '';\n this.root.style.transform = '';\n };\n\n handleEl.addEventListener('touchstart', onHandleTouchStart, { passive: true });\n handleEl.addEventListener('touchmove', onHandleTouchMove, { passive: false });\n handleEl.addEventListener('touchend', onHandleTouchEnd, { passive: true });\n handleEl.addEventListener('touchcancel', onHandleTouchCancel, { passive: true });\n this._cleanups.push(() => {\n handleEl.removeEventListener('touchstart', onHandleTouchStart);\n handleEl.removeEventListener('touchmove', onHandleTouchMove);\n handleEl.removeEventListener('touchend', onHandleTouchEnd);\n handleEl.removeEventListener('touchcancel', onHandleTouchCancel);\n });\n }\n\n // Header — branded dark bar\n const header = document.createElement('div');\n header.className = 'gengage-chat-header';\n\n const headerLeft = document.createElement('div');\n headerLeft.className = 'gengage-chat-header-left';\n\n const avatarUrl = options.headerAvatarUrl ?? options.launcherImageUrl;\n if (avatarUrl) {\n const avatar = document.createElement('img');\n avatar.className = 'gengage-chat-header-avatar';\n avatar.src = avatarUrl;\n avatar.alt = options.headerTitle ?? 'Assistant';\n headerLeft.appendChild(avatar);\n }\n\n const headerInfo = document.createElement('div');\n headerInfo.className = 'gengage-chat-header-info';\n\n const titleRow = document.createElement('div');\n titleRow.className = 'gengage-chat-header-title-row';\n const title = document.createElement('span');\n title.className = 'gengage-chat-header-title';\n title.textContent = options.headerTitle ?? this.i18n.headerTitle ?? 'Product Expert';\n titleRow.appendChild(title);\n\n if (options.headerBadge) {\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-header-badge';\n badge.textContent = options.headerBadge;\n titleRow.appendChild(badge);\n }\n headerInfo.appendChild(titleRow);\n\n const powered = document.createElement('a');\n powered.className = 'gengage-chat-header-powered';\n powered.href = 'https://gengage.ai/';\n powered.target = '_blank';\n powered.rel = 'noopener noreferrer';\n powered.innerHTML = `<svg viewBox=\"0 0 16 16\" fill=\"currentColor\"><path d=\"M8 1a7 7 0 110 14A7 7 0 018 1zm0 1.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM7 4.5h2v4H7v-4zm0 5h2v2H7v-2z\"/></svg>Powered by Gengage`;\n headerInfo.appendChild(powered);\n\n headerLeft.appendChild(headerInfo);\n header.appendChild(headerLeft);\n\n const headerRight = document.createElement('div');\n headerRight.className = 'gengage-chat-header-right';\n\n // Reopen-panel button — shown on mobile when the side panel is hidden but has content\n {\n const reopenBtn = document.createElement('button');\n reopenBtn.type = 'button';\n reopenBtn.className = 'gengage-chat-header-btn gengage-chat-header-btn--reopen-panel';\n reopenBtn.setAttribute('aria-label', this.i18n.showPanelAriaLabel);\n reopenBtn.innerHTML = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><line x1=\"9\" y1=\"3\" x2=\"9\" y2=\"21\"/></svg>`;\n reopenBtn.addEventListener('click', () => this._showMobilePanelFromBtn());\n headerRight.appendChild(reopenBtn);\n this._reopenPanelBtn = reopenBtn;\n }\n\n // Cart button — always a <button> so the onCartClick callback is always invoked\n // (handles session persistence before navigation when headerCartUrl is set).\n {\n const cartBtn = document.createElement('button');\n cartBtn.type = 'button';\n cartBtn.className = 'gengage-chat-header-btn';\n cartBtn.setAttribute('aria-label', this.i18n.cartAriaLabel);\n cartBtn.innerHTML = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"9\" cy=\"21\" r=\"1\"/><circle cx=\"20\" cy=\"21\" r=\"1\"/><path d=\"M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6\"/></svg>`;\n cartBtn.addEventListener('click', () => options.onCartClick?.());\n headerRight.appendChild(cartBtn);\n }\n\n // New Chat button (optional — reset conversation)\n if (options.onNewChat) {\n const newChatBtn = document.createElement('button');\n newChatBtn.className = 'gengage-chat-header-btn gengage-chat-new-chat';\n newChatBtn.type = 'button';\n newChatBtn.setAttribute('aria-label', this.i18n.newChatButton);\n newChatBtn.title = this.i18n.newChatButton;\n newChatBtn.innerHTML = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 20h9\"/><path d=\"M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z\"/></svg>`;\n newChatBtn.addEventListener('click', () => options.onNewChat?.());\n headerRight.appendChild(newChatBtn);\n }\n\n const closeBtn = document.createElement('button');\n closeBtn.className = 'gengage-chat-close';\n closeBtn.type = 'button';\n closeBtn.setAttribute('aria-label', this.i18n.closeButton);\n closeBtn.innerHTML = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`;\n closeBtn.addEventListener('click', options.onClose);\n\n // Favorites button — always visible, placed just before the close button\n {\n const favBtn = document.createElement('button');\n favBtn.className = 'gengage-chat-header-btn gengage-chat-header-btn--fav';\n favBtn.type = 'button';\n favBtn.setAttribute('aria-label', this.i18n.favoritesAriaLabel);\n favBtn.setAttribute('aria-pressed', 'false');\n favBtn.innerHTML = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z\"/></svg>`;\n\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-header-fav-badge';\n badge.setAttribute('aria-hidden', 'true');\n badge.style.display = 'none';\n favBtn.appendChild(badge);\n this._favBadgeEl = badge;\n\n favBtn.addEventListener('click', () => {\n const pressed = favBtn.getAttribute('aria-pressed') === 'true';\n favBtn.setAttribute('aria-pressed', String(!pressed));\n options.onFavoritesClick?.();\n });\n headerRight.appendChild(favBtn);\n }\n\n headerRight.appendChild(closeBtn);\n header.appendChild(headerRight);\n\n // Body: flex container for panel + conversation\n const body = document.createElement('div');\n body.className = 'gengage-chat-body';\n\n // Panel (hidden by default)\n this._panelEl = document.createElement('div');\n this._panelEl.className = 'gengage-chat-panel';\n\n // Panel top bar (navigation)\n this._panelTopBar = new PanelTopBar({\n onBack: () => options.onPanelBack?.(),\n onForward: () => options.onPanelForward?.(),\n backAriaLabel: this.i18n.backAriaLabel,\n forwardAriaLabel: this.i18n.forwardAriaLabel,\n });\n this._panelEl.appendChild(this._panelTopBar.getElement());\n\n // Panel scroll affordance — bottom fade gradient when content is scrollable\n const onPanelScroll = () => this._updateScrollAffordance();\n this._panelEl.addEventListener('scroll', onPanelScroll, { passive: true });\n this._cleanups.push(() => this._panelEl.removeEventListener('scroll', onPanelScroll));\n\n body.appendChild(this._panelEl);\n\n // Divider between panel and conversation\n this._dividerEl = document.createElement('div');\n this._dividerEl.className = 'gengage-chat-panel-divider gengage-chat-panel-divider--hidden';\n this._dividerEl.setAttribute('role', 'separator');\n this._dividerEl.setAttribute('aria-label', this.i18n.togglePanelAriaLabel);\n this._dividerEl.setAttribute('title', this.i18n.togglePanelAriaLabel);\n const chevron = document.createElement('button');\n chevron.className = 'gengage-chat-panel-divider-toggle';\n chevron.type = 'button';\n chevron.setAttribute('aria-label', this.i18n.togglePanelAriaLabel);\n chevron.setAttribute('title', this.i18n.togglePanelAriaLabel);\n chevron.textContent = '\\u00BB'; // » (collapse right)\n chevron.addEventListener('click', () => {\n if (this._ignoreNextDividerClick) {\n this._ignoreNextDividerClick = false;\n return;\n }\n this.togglePanel();\n this._onPanelToggle?.();\n });\n let touchStartX: number | null = null;\n let touchStartY: number | null = null;\n const swipeThreshold = 24;\n const onDividerTouchStart = (event: TouchEvent) => {\n if (!(this._options.getMobileViewport?.() ?? window.innerWidth <= 768)) return;\n const touch = event.changedTouches?.[0];\n if (!touch) return;\n touchStartX = touch.clientX;\n touchStartY = touch.clientY;\n };\n const onDividerTouchEnd = (event: TouchEvent) => {\n if (!(this._options.getMobileViewport?.() ?? window.innerWidth <= 768)) return;\n if (touchStartX === null || touchStartY === null) return;\n const touch = event.changedTouches?.[0];\n if (!touch) return;\n\n const deltaX = touch.clientX - touchStartX;\n const deltaY = touch.clientY - touchStartY;\n touchStartX = null;\n touchStartY = null;\n\n // Vertical swipe only. Swipe up collapses panel, swipe down expands.\n if (Math.abs(deltaY) < swipeThreshold || Math.abs(deltaY) < Math.abs(deltaX)) return;\n const nextCollapsed = deltaY < 0;\n if (nextCollapsed === this._panelCollapsed) return;\n\n this._ignoreNextDividerClick = true;\n this.setPanelCollapsed(nextCollapsed);\n this._onPanelToggle?.();\n };\n this._dividerEl.addEventListener('touchstart', onDividerTouchStart, { passive: true });\n this._dividerEl.addEventListener('touchend', onDividerTouchEnd, { passive: true });\n this._cleanups.push(() => {\n this._dividerEl.removeEventListener('touchstart', onDividerTouchStart);\n this._dividerEl.removeEventListener('touchend', onDividerTouchEnd);\n });\n this._dividerEl.appendChild(chevron);\n body.appendChild(this._dividerEl);\n\n // Conversation wrapper — header lives inside so it only spans chat width\n const conversation = document.createElement('div');\n conversation.className = 'gengage-chat-conversation';\n this._conversationEl = conversation;\n conversation.appendChild(header);\n\n // Offline status bar (hidden by default, shown when navigator.onLine === false)\n const offlineBar = document.createElement('div');\n offlineBar.className = 'gengage-chat-offline-bar';\n offlineBar.setAttribute('role', 'status');\n offlineBar.setAttribute('aria-live', 'polite');\n offlineBar.textContent = this.i18n.offlineMessage;\n if (typeof navigator !== 'undefined' && !navigator.onLine) {\n offlineBar.classList.add('gengage-chat-offline-bar--visible');\n }\n conversation.appendChild(offlineBar);\n\n const onOffline = () => offlineBar.classList.add('gengage-chat-offline-bar--visible');\n const onOnline = () => offlineBar.classList.remove('gengage-chat-offline-bar--visible');\n window.addEventListener('offline', onOffline);\n window.addEventListener('online', onOnline);\n this._cleanups.push(() => {\n window.removeEventListener('offline', onOffline);\n window.removeEventListener('online', onOnline);\n });\n\n // KVKK banner slot (inserted above messages)\n this._kvkkSlot = document.createElement('div');\n this._kvkkSlot.className = 'gengage-chat-kvkk-slot';\n conversation.appendChild(this._kvkkSlot);\n\n // Messages area\n this.messagesEl = document.createElement('div');\n this.messagesEl.className = 'gengage-chat-messages';\n this.messagesEl.setAttribute('role', 'log');\n this.messagesEl.setAttribute('aria-live', 'polite');\n this.messagesEl.setAttribute('aria-atomic', 'false');\n this.messagesEl.setAttribute('aria-label', this.i18n.chatMessagesAriaLabel);\n\n // Track user scroll position to avoid auto-scrolling when reading history\n let scrollRafPending = false;\n const onMessagesScroll = () => {\n if (scrollRafPending) return;\n scrollRafPending = true;\n requestAnimationFrame(() => {\n scrollRafPending = false;\n const { scrollTop, scrollHeight, clientHeight } = this.messagesEl;\n this._userScrolledUp = scrollHeight - scrollTop - clientHeight > 10;\n });\n };\n this.messagesEl.addEventListener('scroll', onMessagesScroll, { passive: true });\n this._cleanups.push(() => {\n this.messagesEl.removeEventListener('scroll', onMessagesScroll);\n });\n\n conversation.appendChild(this.messagesEl);\n\n // Thumbnails column (right edge of panel — quick-scroll shortcuts for search results)\n this._thumbnailsColumn = new ThumbnailsColumn({\n onThumbnailClick: (threadId) => options.onThumbnailClick?.(threadId),\n });\n this._panelEl.appendChild(this._thumbnailsColumn.getElement());\n\n // Floating overlay: sticky zero-height anchor so absolutely-positioned overlays\n // (e.g. ChoicePrompter) stay fixed to the panel's visible area regardless of scroll.\n this._panelFloatingEl = document.createElement('div');\n this._panelFloatingEl.className = 'gengage-chat-panel-float';\n this._panelEl.appendChild(this._panelFloatingEl);\n\n // Suggestion pills row (between messages and input)\n this._pillsEl = document.createElement('div');\n this._pillsEl.className = 'gengage-chat-pills';\n this._pillsEl.setAttribute('role', 'toolbar');\n this._pillsEl.setAttribute('aria-label', this.i18n.suggestionsAriaLabel);\n this._pillsEl.style.display = 'none';\n\n const pillsScroll = document.createElement('div');\n pillsScroll.className = 'gengage-chat-pills-scroll';\n this._pillsEl.appendChild(pillsScroll);\n\n const pillsArrow = document.createElement('button');\n pillsArrow.className = 'gengage-chat-pills-arrow';\n pillsArrow.type = 'button';\n pillsArrow.setAttribute('aria-label', this.i18n.moreSuggestionsAriaLabel);\n pillsArrow.textContent = '\\u203A'; // › single right-pointing angle\n pillsArrow.addEventListener('click', () => {\n pillsScroll.scrollBy({ left: 150, behavior: 'smooth' });\n });\n this._pillsEl.appendChild(pillsArrow);\n\n // Hide arrow when fully scrolled\n let pillsRafPending = false;\n const onPillsScroll = () => {\n if (pillsRafPending) return;\n pillsRafPending = true;\n requestAnimationFrame(() => {\n pillsRafPending = false;\n const atEnd = pillsScroll.scrollLeft + pillsScroll.clientWidth >= pillsScroll.scrollWidth - 4;\n pillsArrow.style.display = atEnd ? 'none' : '';\n });\n };\n pillsScroll.addEventListener('scroll', onPillsScroll, { passive: true });\n this._cleanups.push(() => {\n pillsScroll.removeEventListener('scroll', onPillsScroll);\n });\n\n conversation.appendChild(this._pillsEl);\n\n // Input-area chips (compact chips above input for search/info/review/similar)\n this._inputChipsEl = document.createElement('div');\n this._inputChipsEl.className = 'gengage-chat-input-chips';\n this._inputChipsEl.style.display = 'none';\n conversation.appendChild(this._inputChipsEl);\n\n // Input area\n const inputArea = document.createElement('div');\n inputArea.className = 'gengage-chat-input-area';\n\n this.inputEl = document.createElement('textarea');\n this.inputEl.className = 'gengage-chat-input';\n this.inputEl.rows = 1;\n this.inputEl.placeholder = this.i18n.inputPlaceholder;\n\n // Auto-expand on desktop as user types (capped at 120px)\n this.inputEl.addEventListener('input', () => {\n requestAnimationFrame(() => {\n this.inputEl.style.height = 'auto';\n this.inputEl.style.height = `${Math.min(this.inputEl.scrollHeight, 120)}px`;\n });\n this._updateSendEnabled();\n });\n\n // Enter submits; Shift+Enter inserts newline on desktop only\n this.inputEl.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n const isMobile = this._options.getMobileViewport?.() ?? window.innerWidth <= 768;\n if (isMobile || !e.shiftKey) {\n e.preventDefault();\n this._submit();\n }\n // else: Shift+Enter on desktop → natural newline (no preventDefault)\n }\n });\n\n this.inputEl.addEventListener('paste', (e) => {\n const file = e.clipboardData?.files[0];\n if (file && file.type.startsWith('image/')) {\n e.preventDefault();\n if (this._onAttachment) {\n this._onAttachment(file);\n } else {\n this.stageAttachment(file);\n }\n }\n });\n\n // Hidden file input\n this._fileInput = document.createElement('input');\n this._fileInput.type = 'file';\n this._fileInput.accept = 'image/jpeg,image/png,image/webp';\n this._fileInput.style.display = 'none';\n this._fileInput.addEventListener('change', () => {\n const file = this._fileInput.files?.[0];\n if (file) {\n if (this._onAttachment) {\n this._onAttachment(file);\n } else {\n this.stageAttachment(file);\n }\n }\n this._fileInput.value = '';\n });\n\n // Attach button with camera SVG\n const attachBtn = document.createElement('button');\n attachBtn.className = 'gengage-chat-attach-btn';\n attachBtn.type = 'button';\n attachBtn.setAttribute('aria-label', this.i18n.attachImageButton);\n attachBtn.innerHTML = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z\"/><circle cx=\"12\" cy=\"13\" r=\"4\"/></svg>`;\n attachBtn.addEventListener('click', () => this._fileInput.click());\n\n // Attachment preview strip (hidden by default)\n this._previewStrip = document.createElement('div');\n this._previewStrip.className = 'gengage-chat-attachment-preview gengage-chat-attachment-preview--hidden';\n const previewThumb = document.createElement('img');\n previewThumb.className = 'gengage-chat-attachment-preview-thumb';\n previewThumb.alt = '';\n this._previewName = document.createElement('span');\n this._previewName.className = 'gengage-chat-attachment-name';\n const removeBtn = document.createElement('button');\n removeBtn.className = 'gengage-chat-attachment-remove';\n removeBtn.type = 'button';\n removeBtn.setAttribute('aria-label', this.i18n.removeAttachmentButton);\n removeBtn.textContent = '\\u00D7'; // multiplication sign (x)\n removeBtn.addEventListener('click', () => this.clearAttachment());\n this._previewStrip.appendChild(previewThumb);\n this._previewStrip.appendChild(this._previewName);\n this._previewStrip.appendChild(removeBtn);\n\n this.sendBtn = document.createElement('button');\n this.sendBtn.className = 'gengage-chat-send';\n this.sendBtn.type = 'button';\n this.sendBtn.disabled = true;\n this.sendBtn.setAttribute('aria-label', this.i18n.sendButton);\n this.sendBtn.innerHTML = `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\"/></svg>`;\n this.sendBtn.addEventListener('click', () => this._submit());\n\n // Drag-and-drop on input area\n inputArea.addEventListener('dragover', (e) => {\n e.preventDefault();\n inputArea.classList.add('gengage-chat-input-area--dragover');\n });\n inputArea.addEventListener('dragleave', () => {\n inputArea.classList.remove('gengage-chat-input-area--dragover');\n });\n inputArea.addEventListener('drop', (e) => {\n e.preventDefault();\n inputArea.classList.remove('gengage-chat-input-area--dragover');\n const file = e.dataTransfer?.files[0];\n if (file) {\n if (this._onAttachment) {\n this._onAttachment(file);\n } else {\n this.stageAttachment(file);\n }\n }\n });\n\n // Build pill container: [camera] [input] [mic?] [send]\n const pill = document.createElement('div');\n pill.className = 'gengage-chat-input-pill';\n pill.appendChild(attachBtn);\n pill.appendChild(this.inputEl);\n\n // Voice input mic button (Web Speech API STT)\n if (this._voiceEnabled && isVoiceInputSupported()) {\n this._micBtn = document.createElement('button');\n this._micBtn.className = 'gengage-chat-mic-btn';\n this._micBtn.type = 'button';\n this._micBtn.setAttribute('aria-label', this.i18n.voiceButton);\n this._micBtn.innerHTML =\n '<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">' +\n '<path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"/>' +\n '<path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>' +\n '<line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"/>' +\n '<line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\"/>' +\n '</svg>';\n this._micBtn.addEventListener('click', () => this._toggleVoice());\n pill.appendChild(this._micBtn);\n\n this._voiceInput = new VoiceInput(\n {\n onInterim: (text) => {\n this.inputEl.value = text;\n this.inputEl.style.height = 'auto';\n this.inputEl.style.height = `${Math.min(this.inputEl.scrollHeight, 120)}px`;\n },\n onFinal: (text) => {\n this.inputEl.value = text;\n },\n onAutoSubmit: (text) => {\n this.inputEl.value = text;\n this._micBtn?.classList.remove('gengage-chat-mic-btn--active');\n this._submit();\n },\n onStateChange: (state) => {\n if (state === 'listening') {\n this._micBtn?.classList.add('gengage-chat-mic-btn--active');\n } else {\n this._micBtn?.classList.remove('gengage-chat-mic-btn--active');\n }\n },\n onError: (_code, _message) => {\n this._micBtn?.classList.remove('gengage-chat-mic-btn--active');\n },\n },\n { lang: this._voiceLang },\n );\n }\n\n pill.appendChild(this.sendBtn);\n\n inputArea.appendChild(this._previewStrip);\n inputArea.appendChild(this._fileInput);\n inputArea.appendChild(pill);\n conversation.appendChild(inputArea);\n\n body.appendChild(conversation);\n this.root.appendChild(body);\n\n // Horizontal swipe to toggle panel on mobile (GAP-101)\n this._setupHorizontalSwipe(conversation);\n this._setupHorizontalSwipe(this._panelEl);\n\n // Footer\n const footer = document.createElement('div');\n footer.className = 'gengage-chat-footer';\n footer.textContent = this.i18n.poweredBy;\n this.root.appendChild(footer);\n\n // Escape key to close drawer\n const escapeHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n options.onClose();\n }\n };\n this.root.addEventListener('keydown', escapeHandler);\n this._cleanups.push(() => this.root.removeEventListener('keydown', escapeHandler));\n\n container.appendChild(this.root);\n }\n\n addMessage(message: ChatMessage): void {\n const bubble = document.createElement('div');\n bubble.className = `gengage-chat-bubble gengage-chat-bubble--${message.role}`;\n bubble.setAttribute('role', 'listitem');\n bubble.dataset['messageId'] = message.id;\n if (message.threadId) {\n bubble.dataset['threadId'] = message.threadId;\n }\n\n if (this._firstBotMessageIds.has(message.id)) {\n bubble.classList.add('gengage-chat-bubble--first');\n }\n\n if (message.attachment) {\n const thumbEl = document.createElement('img');\n thumbEl.className = 'gengage-chat-attachment-thumb';\n const blobUrl = URL.createObjectURL(message.attachment);\n thumbEl.src = blobUrl;\n thumbEl.alt = message.attachment.name;\n // Revoke blob URL once image loads (or errors) to free memory\n thumbEl.addEventListener('load', () => URL.revokeObjectURL(blobUrl), { once: true });\n thumbEl.addEventListener('error', () => URL.revokeObjectURL(blobUrl), { once: true });\n bubble.insertBefore(thumbEl, bubble.firstChild);\n }\n\n if (message.content) {\n const text = document.createElement('div');\n text.className = 'gengage-chat-bubble-text';\n if (message.role === 'assistant') {\n text.innerHTML = sanitizeHtml(message.content);\n // Intercept all links in bot HTML\n if (this._onLinkClick) {\n const links = text.querySelectorAll('a[href]');\n for (const link of links) {\n link.addEventListener('click', (e) => {\n e.preventDefault();\n const href = link.getAttribute('href');\n if (href) {\n this._onLinkClick?.(href);\n }\n });\n }\n }\n } else {\n text.textContent = message.content; // User messages: always safe textContent\n }\n bubble.appendChild(text);\n }\n\n // Add rollback button to user message bubbles\n if (message.role === 'user' && this._onRollback) {\n const rollbackBtn = document.createElement('button');\n rollbackBtn.className = 'gengage-chat-rollback-btn';\n rollbackBtn.type = 'button';\n rollbackBtn.setAttribute('aria-label', this.i18n.rollbackAriaLabel);\n rollbackBtn.title = this.i18n.rollbackAriaLabel;\n rollbackBtn.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/></svg>`;\n rollbackBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n this._onRollback?.(message.id);\n });\n bubble.appendChild(rollbackBtn);\n }\n\n this.messagesEl.appendChild(bubble);\n this._scrollToBottom(message.role === 'user');\n }\n\n showTypingIndicator(searchText?: string): void {\n this.removeTypingIndicator();\n const container = document.createElement('div');\n container.className = 'gengage-chat-typing';\n container.dataset['typing'] = 'true';\n\n if (this._thinkingSteps.length > 0) {\n // Render accumulated thinking steps\n this._renderThinkingStepsInto(container);\n } else {\n // Default 3-dot animation\n const indicator = document.createElement('div');\n indicator.className = 'gengage-chat-typing-dots';\n for (let i = 0; i < 3; i++) indicator.appendChild(document.createElement('span'));\n container.appendChild(indicator);\n if (searchText) {\n const sparkle = document.createElement('span');\n sparkle.className = 'gengage-chat-typing-sparkle';\n sparkle.textContent = '\\u2728'; // sparkle\n container.appendChild(sparkle);\n\n const text = document.createElement('span');\n text.className = 'gengage-chat-typing-text';\n text.textContent = searchText;\n container.appendChild(text);\n }\n }\n\n this.messagesEl.appendChild(container);\n this._scrollToBottom(true);\n\n // Start \"still working\" timer — shows a hint after 10s with no text chunks\n this._clearStillWorkingTimer();\n this._stillWorkingTimer = setTimeout(() => {\n this._stillWorkingTimer = null;\n const typing = this.messagesEl.querySelector('.gengage-chat-typing');\n if (!typing) return;\n // Only add if not already present\n if (typing.querySelector('.gengage-chat-still-working')) return;\n const hint = document.createElement('div');\n hint.className = 'gengage-chat-still-working';\n hint.textContent = this.i18n.stillWorkingMessage;\n typing.appendChild(hint);\n this._scrollToBottom(true);\n }, 10_000);\n }\n\n /** Accumulate a new thinking step (shown as a checklist in the typing indicator). */\n addThinkingStep(text: string): void {\n this._thinkingSteps.push(text);\n this._renderThinkingSteps();\n }\n\n removeTypingIndicator(): void {\n this._clearStillWorkingTimer();\n const existing = this.messagesEl.querySelector('.gengage-chat-typing');\n existing?.remove();\n this._thinkingSteps = [];\n this.hideStopButton();\n }\n\n private _clearStillWorkingTimer(): void {\n if (this._stillWorkingTimer !== null) {\n clearTimeout(this._stillWorkingTimer);\n this._stillWorkingTimer = null;\n }\n }\n\n /** Show a \"Stop generating\" button below the typing indicator. */\n showStopButton(onStop: () => void): void {\n this.hideStopButton();\n const btn = document.createElement('button');\n btn.className = 'gengage-chat-stop-btn';\n btn.type = 'button';\n btn.setAttribute('aria-label', this.i18n.stopGenerating);\n // Square stop icon + label\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-stop-icon';\n icon.setAttribute('aria-hidden', 'true');\n btn.appendChild(icon);\n const label = document.createElement('span');\n label.textContent = this.i18n.stopGenerating;\n btn.appendChild(label);\n btn.addEventListener('click', () => {\n this.hideStopButton();\n onStop();\n });\n this.messagesEl.appendChild(btn);\n this._scrollToBottom(true);\n }\n\n /** Remove the stop-generating button if present. */\n hideStopButton(): void {\n const existing = this.messagesEl.querySelector('.gengage-chat-stop-btn');\n existing?.remove();\n }\n\n showError(message?: string, onRetry?: () => void): void {\n const errEl = document.createElement('div');\n errEl.className = 'gengage-chat-error';\n errEl.setAttribute('role', 'alert');\n const textEl = document.createElement('span');\n textEl.textContent = message ?? this.i18n.errorMessage;\n errEl.appendChild(textEl);\n\n if (onRetry) {\n const retryBtn = document.createElement('button');\n retryBtn.className = 'gengage-chat-error-retry';\n retryBtn.textContent = this.i18n.retryButton ?? 'Retry';\n retryBtn.addEventListener('click', () => {\n errEl.remove();\n onRetry();\n });\n errEl.appendChild(retryBtn);\n }\n\n this.messagesEl.appendChild(errEl);\n this._scrollToBottom(true);\n }\n\n /** Show error with recovery action pills (\"Try again\" + \"Ask something else\"). */\n showErrorWithRecovery(message: string, actions: { onRetry: () => void; onNewQuestion: () => void }): void {\n this.showError(message);\n this.setPills([\n { label: this.i18n.tryAgainButton, onAction: actions.onRetry },\n { label: this.i18n.askSomethingElseButton, onAction: actions.onNewQuestion },\n ]);\n }\n\n clearMessages(): void {\n this.messagesEl.innerHTML = '';\n }\n\n /** Replace suggestion pills. Pass empty array to hide. */\n setPills(\n pills: Array<{ label: string; onAction: () => void; icon?: string; image?: string; description?: string }>,\n ): void {\n const scroll = this._pillsEl.querySelector('.gengage-chat-pills-scroll');\n if (!scroll) return;\n while (scroll.firstChild) scroll.removeChild(scroll.firstChild);\n\n if (pills.length === 0) {\n this._pillsEl.style.display = 'none';\n return;\n }\n\n this._pillsEl.style.display = '';\n for (const pill of pills) {\n const btn = document.createElement('button');\n btn.className = pill.image ? 'gengage-chat-pill gengage-chat-pill--rich' : 'gengage-chat-pill';\n btn.type = 'button';\n\n if (pill.icon) {\n const svgHtml = SUGGESTED_ACTION_ICONS[pill.icon] ?? DEFAULT_ACTION_ICON;\n const iconSpan = document.createElement('span');\n iconSpan.className = 'gengage-chat-pill-icon';\n iconSpan.innerHTML = svgHtml;\n btn.appendChild(iconSpan);\n }\n\n if (pill.image && isSafeImageUrl(pill.image)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-pill-img';\n img.src = pill.image;\n img.alt = '';\n btn.appendChild(img);\n }\n\n const textWrap = document.createElement('span');\n textWrap.className = 'gengage-chat-pill-text';\n textWrap.textContent = pill.label;\n btn.appendChild(textWrap);\n\n if (pill.description) {\n const desc = document.createElement('span');\n desc.className = 'gengage-chat-pill-desc';\n const descId = `pill-desc-${Math.random().toString(36).slice(2, 9)}`;\n desc.id = descId;\n desc.textContent = pill.description;\n btn.appendChild(desc);\n btn.setAttribute('aria-describedby', descId);\n }\n\n btn.addEventListener('click', () => pill.onAction());\n scroll.appendChild(btn);\n }\n\n // Show/hide arrow based on overflow\n const arrow = this._pillsEl.querySelector('.gengage-chat-pills-arrow') as HTMLElement | null;\n if (arrow) {\n requestAnimationFrame(() => {\n arrow.style.display = scroll.scrollWidth > scroll.clientWidth ? '' : 'none';\n });\n }\n }\n\n focusInput(): void {\n this.inputEl.focus();\n }\n\n showKvkkBanner(html: string, onDismiss: () => void): void {\n this._kvkkSlot.innerHTML = '';\n const banner = createKvkkBanner({ htmlContent: html, onDismiss, closeAriaLabel: this.i18n.closeAriaLabel });\n this._kvkkSlot.appendChild(banner);\n }\n\n hideKvkkBanner(): void {\n this._kvkkSlot.innerHTML = '';\n }\n\n getElement(): HTMLElement {\n return this.root;\n }\n\n /** Stage a file attachment for sending. Shows preview. */\n stageAttachment(file: File): void {\n this._pendingAttachment = file;\n this._previewName.textContent = file.name;\n const thumb = this._previewStrip.querySelector('.gengage-chat-attachment-preview-thumb') as HTMLImageElement;\n if (thumb) {\n // Revoke previous blob URL to prevent memory leak\n if (thumb.src && thumb.src.startsWith('blob:')) {\n URL.revokeObjectURL(thumb.src);\n }\n thumb.src = URL.createObjectURL(file);\n }\n this._previewStrip.classList.remove('gengage-chat-attachment-preview--hidden');\n this._updateSendEnabled();\n }\n\n /** Remove the staged attachment and hide preview. */\n clearAttachment(): void {\n const thumb = this._previewStrip.querySelector('.gengage-chat-attachment-preview-thumb') as HTMLImageElement;\n if (thumb?.src) {\n URL.revokeObjectURL(thumb.src);\n thumb.src = '';\n }\n this._pendingAttachment = null;\n this._previewStrip.classList.add('gengage-chat-attachment-preview--hidden');\n this._updateSendEnabled();\n }\n\n /** Get the currently staged attachment file, or null. */\n getPendingAttachment(): File | null {\n return this._pendingAttachment;\n }\n\n /** Replace panel content and show the panel. */\n setPanelContent(el: HTMLElement): void {\n // Brief crossfade transition when swapping panel content\n this._panelEl.classList.add('gengage-chat-panel--transitioning');\n this._panelEl.innerHTML = '';\n this._panelEl.appendChild(this._panelTopBar.getElement());\n this._panelEl.appendChild(el);\n this._panelEl.appendChild(this._panelFloatingEl);\n this._dividerEl.classList.remove('gengage-chat-panel-divider--hidden');\n if (!this._panelVisible) {\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n this.root.classList.add('gengage-chat-drawer--with-panel');\n }\n if (this._panelCollapsed) {\n this._panelEl.classList.add('gengage-chat-panel--collapsed');\n }\n requestAnimationFrame(() => {\n this._panelEl.classList.remove('gengage-chat-panel--transitioning');\n this._updateScrollAffordance();\n });\n // New content always reopens the panel — hide the reopen button\n if (this._reopenPanelBtn) this._reopenPanelBtn.style.display = 'none';\n }\n\n /** Append content to the panel without replacing existing content. */\n appendPanelContent(el: HTMLElement): void {\n this._panelEl.insertBefore(el, this._panelFloatingEl);\n this._dividerEl.classList.remove('gengage-chat-panel-divider--hidden');\n if (!this._panelVisible) {\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n this.root.classList.add('gengage-chat-drawer--with-panel');\n }\n }\n\n /** Return the panel element's content child (after topbar), or null. */\n getPanelContentElement(): HTMLElement | null {\n const children = this._panelEl.children;\n for (let i = 0; i < children.length; i++) {\n const child = children[i] as HTMLElement;\n if (\n child.classList.contains('gengage-chat-panel-topbar') ||\n child.classList.contains('gengage-chat-thumbnails-column') ||\n child.classList.contains('gengage-chat-panel-float')\n ) {\n continue;\n }\n return child;\n }\n return null;\n }\n\n /** Whether the panel is currently visible (may be empty). */\n isPanelVisible(): boolean {\n return this._panelVisible;\n }\n\n /** Whether the panel is currently visible and has rendered content (beyond topbar + thumbnails column). */\n hasPanelContent(): boolean {\n return this._panelVisible && this.getPanelContentElement() !== null;\n }\n\n /** Whether panel currently shows loading skeleton blocks. */\n isPanelLoading(): boolean {\n return this._panelEl.querySelector('.gengage-chat-panel-skeleton') !== null;\n }\n\n /** Show loading skeleton in the panel. Variant depends on contentType hint. */\n showPanelLoading(contentType?: string): void {\n this._dividerEl.classList.remove('gengage-chat-panel-divider--hidden');\n this._panelEl.innerHTML = '';\n this._panelEl.appendChild(this._panelTopBar.getElement());\n const skeleton = document.createElement('div');\n skeleton.className = 'gengage-chat-panel-skeleton';\n\n switch (contentType) {\n case 'productDetails': {\n // Tall image placeholder + text lines\n const imgBlock = document.createElement('div');\n imgBlock.className = 'gengage-chat-panel-skeleton-block gengage-chat-panel-skeleton-block--image';\n skeleton.appendChild(imgBlock);\n for (let i = 0; i < 3; i++) {\n const line = document.createElement('div');\n line.className = 'gengage-chat-panel-skeleton-block gengage-chat-panel-skeleton-block--text';\n skeleton.appendChild(line);\n }\n break;\n }\n case 'productList':\n case 'groupList': {\n // 2x3 grid of small card placeholders\n const grid = document.createElement('div');\n grid.className = 'gengage-chat-panel-skeleton-grid';\n for (let i = 0; i < 6; i++) {\n const card = document.createElement('div');\n card.className = 'gengage-chat-panel-skeleton-block gengage-chat-panel-skeleton-block--card';\n grid.appendChild(card);\n }\n skeleton.appendChild(grid);\n break;\n }\n case 'comparisonTable': {\n // Table-like rows\n for (let i = 0; i < 4; i++) {\n const row = document.createElement('div');\n row.className = 'gengage-chat-panel-skeleton-block gengage-chat-panel-skeleton-block--row';\n skeleton.appendChild(row);\n }\n break;\n }\n default: {\n // Generic: 3 blocks (existing behavior)\n for (let i = 0; i < 3; i++) {\n const block = document.createElement('div');\n block.className = 'gengage-chat-panel-skeleton-block';\n skeleton.appendChild(block);\n }\n break;\n }\n }\n\n this._panelEl.appendChild(skeleton);\n this._panelEl.appendChild(this._panelFloatingEl);\n if (!this._panelVisible) {\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n this.root.classList.add('gengage-chat-drawer--with-panel');\n }\n }\n\n /** Update the panel top bar navigation state. */\n updatePanelTopBar(canBack: boolean, canForward: boolean, title: string): void {\n // On mobile the back button always closes the side-panel overlay, so keep it active\n const isMobile = this._options.getMobileViewport?.() ?? false;\n this._panelTopBar.update(isMobile ? true : canBack, canForward, title);\n }\n\n getPanelTopBarTitle(): string {\n return this._panelTopBar.getTitle();\n }\n\n /** Update the favorites badge count. Pass 0 to hide the badge. */\n updateFavoritesBadge(count: number): void {\n if (!this._favBadgeEl) return;\n if (count > 0) {\n this._favBadgeEl.textContent = count > 99 ? '99+' : String(count);\n this._favBadgeEl.style.display = '';\n } else {\n this._favBadgeEl.style.display = 'none';\n }\n }\n\n /**\n * Hide the panel and clear its content. Always hides — even in force-expanded mode.\n * Callers: _hideDrawer (stale panel cleanup), stream onDone (loading skeleton cleanup),\n * thread navigation (no snapshot to restore). All require full hide.\n * Keeps `_panelCollapsed` untouched so user collapse preference survives future panel renders.\n */\n clearPanel(): void {\n this._panelEl.innerHTML = '';\n this._panelEl.appendChild(this._panelTopBar.getElement());\n this._panelEl.appendChild(this._panelFloatingEl);\n this._panelVisible = false;\n this._panelEl.classList.remove('gengage-chat-panel--visible', 'gengage-chat-panel--collapsed');\n this.root.classList.remove('gengage-chat-drawer--with-panel');\n this._dividerEl.classList.add('gengage-chat-panel-divider--hidden');\n if (this._reopenPanelBtn) this._reopenPanelBtn.style.display = 'none';\n }\n\n /**\n * On mobile: hide the side panel overlay without clearing its content.\n * Shows the reopen button in the header so the user can slide the panel back in.\n */\n hideMobilePanel(): void {\n if (!this._panelVisible) return;\n this._panelVisible = false;\n this._panelEl.classList.remove('gengage-chat-panel--visible');\n if (this._reopenPanelBtn) this._reopenPanelBtn.style.display = 'flex';\n }\n\n private _showMobilePanelFromBtn(): void {\n if (this._panelVisible) return;\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n if (this._reopenPanelBtn) this._reopenPanelBtn.style.display = 'none';\n }\n\n /** Expand panel without locking — user can still toggle via divider. */\n expandPanel(): void {\n this._panelCollapsed = false;\n this._panelEl.classList.remove('gengage-chat-panel--collapsed');\n if (!this._panelVisible) {\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n this.root.classList.add('gengage-chat-drawer--with-panel');\n }\n }\n\n /**\n * Ensure the panel starts expanded (panelMode: 'expanded').\n * Users can still collapse/expand via the divider chevron.\n */\n setForceExpanded(): void {\n this._panelCollapsed = false;\n this._panelEl.classList.remove('gengage-chat-panel--collapsed');\n // Show panel immediately even if empty\n if (!this._panelVisible) {\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n this.root.classList.add('gengage-chat-drawer--with-panel');\n }\n this._dividerEl.classList.remove('gengage-chat-panel-divider--hidden');\n }\n\n /** Update scroll affordance (bottom fade gradient) on the panel. */\n private _updateScrollAffordance(): void {\n const panel = this._panelEl;\n const atBottom = panel.scrollTop + panel.clientHeight >= panel.scrollHeight - 10;\n panel.classList.toggle('gengage-chat-panel--has-scroll', !atBottom && panel.scrollHeight > panel.clientHeight);\n }\n\n /** Horizontal swipe on conversation/panel areas to toggle the panel (mobile only). */\n private _setupHorizontalSwipe(el: HTMLElement): void {\n let startX = 0;\n let startY = 0;\n\n const onTouchStart = (e: TouchEvent) => {\n if (window.innerWidth > 768) return;\n const t = e.touches[0];\n if (!t) return;\n startX = t.clientX;\n startY = t.clientY;\n };\n\n const onTouchEnd = (e: TouchEvent) => {\n if (window.innerWidth > 768) return;\n const t = e.changedTouches[0];\n if (!t) return;\n const dx = t.clientX - startX;\n const dy = t.clientY - startY;\n // Only trigger if horizontal movement > 50px and dominant direction\n if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy) * 2) {\n this.togglePanel();\n this._onPanelToggle?.();\n }\n };\n\n el.addEventListener('touchstart', onTouchStart, { passive: true });\n el.addEventListener('touchend', onTouchEnd, { passive: true });\n this._cleanups.push(() => {\n el.removeEventListener('touchstart', onTouchStart);\n el.removeEventListener('touchend', onTouchEnd);\n });\n }\n\n /** Toggle panel between collapsed and expanded. */\n togglePanel(): void {\n this.setPanelCollapsed(!this._panelCollapsed);\n }\n\n /** Whether the panel is currently collapsed by the user. */\n isPanelCollapsed(): boolean {\n return this._panelCollapsed;\n }\n\n /** Programmatically set panel collapsed state. */\n setPanelCollapsed(collapsed: boolean): void {\n this._panelCollapsed = collapsed;\n if (collapsed) {\n this._panelEl.classList.add('gengage-chat-panel--collapsed');\n } else {\n this._panelEl.classList.remove('gengage-chat-panel--collapsed');\n }\n const chevronBtn = this._dividerEl.querySelector('.gengage-chat-panel-divider-toggle');\n if (chevronBtn) {\n chevronBtn.textContent = collapsed ? '\\u00AB' : '\\u00BB'; // « (expand left) or » (collapse right)\n }\n }\n\n /** Save panel collapsed state to sessionStorage. */\n persistPanelState(accountId: string): void {\n try {\n const key = `gengage:panel:${accountId}`;\n if (this._panelCollapsed) {\n sessionStorage.setItem(key, 'collapsed');\n } else {\n sessionStorage.removeItem(key);\n }\n } catch {\n // sessionStorage may be unavailable in restricted environments\n }\n }\n\n /** Restore panel collapsed state from sessionStorage. Returns true when restored as collapsed. */\n restorePanelState(accountId: string): boolean {\n try {\n const key = `gengage:panel:${accountId}`;\n if (sessionStorage.getItem(key) === 'collapsed') {\n this._panelCollapsed = true;\n return true;\n }\n } catch {\n // sessionStorage may be unavailable in restricted environments\n }\n return false;\n }\n\n /** Re-render thinking steps inside the existing typing indicator container. */\n private _renderThinkingSteps(): void {\n const existing = this.messagesEl.querySelector('[data-typing=\"true\"]') as HTMLElement | null;\n if (!existing) {\n // No typing indicator yet — create one with the steps\n this.showTypingIndicator();\n return;\n }\n // Clear and re-render\n existing.innerHTML = '';\n this._renderThinkingStepsInto(existing);\n this._scrollToBottom(false);\n }\n\n /** Render the accumulated thinking-step checklist into a container element. */\n private _renderThinkingStepsInto(container: HTMLElement): void {\n const list = document.createElement('div');\n list.className = 'gengage-chat-thinking-steps';\n\n for (let i = 0; i < this._thinkingSteps.length; i++) {\n const step = document.createElement('div');\n step.className = 'gengage-chat-thinking-step';\n\n const marker = document.createElement('span');\n marker.className = 'gengage-chat-thinking-step-marker';\n\n if (i < this._thinkingSteps.length - 1) {\n // Completed step\n marker.textContent = '\\u2713'; // ✓\n marker.classList.add('gengage-chat-thinking-step-marker--done');\n } else {\n // Current step (last one — still in progress)\n marker.textContent = '\\u25CF'; // ●\n marker.classList.add('gengage-chat-thinking-step-marker--active');\n }\n\n step.appendChild(marker);\n\n const text = document.createElement('span');\n text.className = 'gengage-chat-thinking-step-text';\n text.textContent = this._thinkingSteps[i]!;\n step.appendChild(text);\n\n list.appendChild(step);\n }\n\n container.appendChild(list);\n }\n\n private _updateSendEnabled(): void {\n const hasContent = this.inputEl.value.trim().length > 0 || this._pendingAttachment !== null;\n this.sendBtn.disabled = !hasContent;\n }\n\n private _submit(): void {\n const text = this.inputEl.value.trim();\n const attachment = this._pendingAttachment;\n if (!text && !attachment) return;\n this.onSend(text, attachment ?? undefined);\n this.inputEl.value = '';\n this.inputEl.style.height = 'auto'; // Reset textarea height after submit\n this.clearAttachment();\n this._updateSendEnabled();\n }\n\n private _toggleVoice(): void {\n if (!this._voiceInput) return;\n if (this._voiceInput.state === 'listening') {\n const text = this._voiceInput.stop();\n if (text.trim()) {\n this.inputEl.value = text;\n this._submit();\n }\n } else {\n this.inputEl.value = '';\n this._voiceInput.start();\n }\n }\n\n /** Lock auto-scroll for 500ms after session history restore to prevent visual jump. */\n lockScrollForRestore(): void {\n this._scrollLockedUntil = Date.now() + 500;\n }\n\n /** Scroll to bottom only if user hasn't scrolled up. Force=true always scrolls. */\n private _scrollToBottom(force = false): void {\n if (!force && this._userScrolledUp) return;\n if (!force && Date.now() < this._scrollLockedUntil) return;\n requestAnimationFrame(() => {\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight;\n this._userScrolledUp = false;\n });\n }\n\n /** Public method for typewriter ticks — scrolls only if user is near bottom. */\n scrollToBottomIfNeeded(): void {\n this._scrollToBottom(false);\n }\n\n /** Update a bot message's text content in the DOM (e.g. for fallback messages). */\n updateBotMessage(messageId: string, html: string): void {\n const bubble = this.messagesEl.querySelector(`[data-message-id=\"${CSS.escape(messageId)}\"]`);\n if (!bubble) return;\n let textEl = bubble.querySelector('.gengage-chat-bubble-text');\n if (!textEl) {\n textEl = document.createElement('div');\n textEl.className = 'gengage-chat-bubble-text';\n bubble.appendChild(textEl);\n }\n textEl.innerHTML = sanitizeHtml(html);\n this._scrollToBottom(false);\n }\n\n /** Mark a message as the first bot message in its thread (for special styling). */\n markFirstBotMessage(messageId: string): void {\n this._firstBotMessageIds.add(messageId);\n const bubble = this.messagesEl.querySelector(`[data-message-id=\"${CSS.escape(messageId)}\"]`);\n if (bubble) {\n bubble.classList.add('gengage-chat-bubble--first');\n }\n }\n\n /** Scroll to the first message of the last thread (for restore targeting). */\n scrollToLastThread(): void {\n const allBubbles = this.messagesEl.querySelectorAll('[data-thread-id]');\n if (allBubbles.length === 0) {\n this._scrollToBottom(true);\n return;\n }\n const lastThreadId = allBubbles[allBubbles.length - 1]!.getAttribute('data-thread-id');\n if (!lastThreadId) {\n this._scrollToBottom(true);\n return;\n }\n const target = this.messagesEl.querySelector(`[data-thread-id=\"${CSS.escape(lastThreadId)}\"]`);\n if (target) {\n requestAnimationFrame(() => {\n target.scrollIntoView({ block: 'start', behavior: 'auto' });\n this._userScrolledUp = false;\n });\n } else {\n this._scrollToBottom(true);\n }\n }\n\n /** Set compact input-area chips (search/info/review shortcuts above input). */\n setInputAreaChips(chips: Array<{ label: string; onAction: () => void; icon?: string }>): void {\n this._inputChipsEl.innerHTML = '';\n if (chips.length === 0) {\n this._inputChipsEl.style.display = 'none';\n return;\n }\n this._inputChipsEl.style.display = '';\n for (const chip of chips) {\n const btn = document.createElement('button');\n btn.className = 'gengage-chat-input-chip';\n btn.type = 'button';\n\n // Icon (SVG from icon map, falls back to generic arrow for unknown names)\n if (chip.icon) {\n const svgHtml = SUGGESTED_ACTION_ICONS[chip.icon] ?? DEFAULT_ACTION_ICON;\n const iconSpan = document.createElement('span');\n iconSpan.className = 'gengage-chat-input-chip-icon';\n iconSpan.innerHTML = svgHtml;\n btn.appendChild(iconSpan);\n }\n\n const label = document.createElement('span');\n label.textContent = chip.label;\n btn.appendChild(label);\n\n btn.addEventListener('click', () => chip.onAction());\n this._inputChipsEl.appendChild(btn);\n }\n }\n\n /** Clear input-area chips. */\n clearInputAreaChips(): void {\n this._inputChipsEl.innerHTML = '';\n this._inputChipsEl.style.display = 'none';\n }\n\n setThumbnails(entries: ThumbnailEntry[]): void {\n this._thumbnailsColumn.setEntries(entries);\n if (entries.length > 0) {\n this._thumbnailsColumn.show();\n } else {\n this._thumbnailsColumn.hide();\n }\n }\n\n hideThumbnails(): void {\n this._thumbnailsColumn.hide();\n }\n\n /** Activate focus trap — Tab/Shift+Tab cycles within the drawer. */\n trapFocus(): void {\n this._previouslyFocusedElement = document.activeElement as HTMLElement | null;\n this.releaseFocus();\n\n const handler = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n const focusable = this.root.querySelectorAll<HTMLElement>(\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n );\n if (focusable.length === 0) return;\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n\n // Use getRootNode() to resolve activeElement inside Shadow DOM\n const rootNode = this.root.getRootNode();\n const active = rootNode instanceof ShadowRoot ? rootNode.activeElement : document.activeElement;\n\n if (e.shiftKey) {\n if (active === first || !this.root.contains(active)) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (active === last || !this.root.contains(active)) {\n e.preventDefault();\n first.focus();\n }\n }\n };\n\n this._focusTrapHandler = handler;\n this.root.addEventListener('keydown', handler);\n }\n\n /** Release the focus trap and restore previously focused element. */\n releaseFocus(): void {\n if (this._focusTrapHandler) {\n this.root.removeEventListener('keydown', this._focusTrapHandler);\n this._focusTrapHandler = null;\n }\n if (this._previouslyFocusedElement) {\n try {\n this._previouslyFocusedElement.focus();\n } catch {\n // Element may no longer be in the DOM\n }\n this._previouslyFocusedElement = null;\n }\n }\n\n /** Clean up event listeners and child resources (VoiceInput, timers). */\n destroy(): void {\n this.releaseFocus();\n this._clearStillWorkingTimer();\n for (const cleanup of this._cleanups) cleanup();\n this._cleanups.length = 0;\n this._voiceInput?.destroy();\n this._voiceInput = null;\n }\n}\n","export interface LauncherOptions {\n onClick: () => void;\n svgMarkup?: string;\n /** Full-size image URL — renders launcher as an image button (no circular bg). */\n imageUrl?: string;\n ariaLabel?: string;\n hideMobile?: boolean;\n mobileBreakpoint?: number;\n tooltip?: string;\n}\n\n/**\n * Result of createLauncher — the container wraps the button and exposes\n * content-area slots where the QNA widget or host page can inject engagement\n * actions (buying-hesitation questions, \"Find Similar\" buttons, etc.).\n */\nexport interface LauncherElements {\n /** Outer container — append this to the DOM. */\n container: HTMLElement;\n /** The clickable FAB button. */\n button: HTMLButtonElement;\n /** Slot above the button (primary QNA actions). */\n contentArea: HTMLElement;\n /** Slot below the button (secondary content). */\n contentAreaBottom: HTMLElement;\n}\n\nconst DEFAULT_SVG = `<svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"3\" y=\"7\" width=\"18\" height=\"13\" rx=\"3\" fill=\"currentColor\" opacity=\"0.15\"/>\n <rect x=\"3\" y=\"7\" width=\"18\" height=\"13\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <circle cx=\"9\" cy=\"13\" r=\"1.5\" fill=\"currentColor\"/>\n <circle cx=\"15\" cy=\"13\" r=\"1.5\" fill=\"currentColor\"/>\n <path d=\"M9.5 17C10.3 17.6 11.1 18 12 18C12.9 18 13.7 17.6 14.5 17\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <path d=\"M12 7V4\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <circle cx=\"12\" cy=\"3\" r=\"1\" fill=\"currentColor\"/>\n <path d=\"M1 12V14\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <path d=\"M23 12V14\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n</svg>`;\n\nexport function createLauncher(options: LauncherOptions): LauncherElements {\n // Container holds content areas + button\n const container = document.createElement('div');\n container.className = 'gengage-chat-launcher-container';\n\n // Content area above button (QNA actions, buying-hesitation questions)\n const contentArea = document.createElement('div');\n contentArea.className = 'gengage-chat-launcher-content-area';\n container.appendChild(contentArea);\n\n // The FAB button\n const button = document.createElement('button');\n button.type = 'button';\n button.setAttribute('aria-label', options.ariaLabel ?? 'Open chat');\n\n if (options.imageUrl) {\n button.className = 'gengage-chat-launcher gengage-chat-launcher--image-mode';\n const img = document.createElement('img');\n img.src = options.imageUrl;\n img.alt = '';\n img.draggable = false;\n button.appendChild(img);\n } else {\n button.className = 'gengage-chat-launcher';\n button.innerHTML = options.svgMarkup ?? DEFAULT_SVG;\n }\n\n if (options.tooltip !== undefined) {\n const tooltipEl = document.createElement('span');\n tooltipEl.className = 'gengage-chat-launcher-tooltip';\n tooltipEl.textContent = options.tooltip;\n button.appendChild(tooltipEl);\n }\n\n if (options.hideMobile) {\n container.dataset['hideMobile'] = '1';\n }\n if (options.mobileBreakpoint !== undefined) {\n container.dataset['mobileBreakpoint'] = String(options.mobileBreakpoint);\n }\n\n button.addEventListener('click', options.onClick);\n container.appendChild(button);\n\n // Content area below button (secondary content)\n const contentAreaBottom = document.createElement('div');\n contentAreaBottom.className = 'gengage-chat-launcher-content-area-bottom';\n container.appendChild(contentAreaBottom);\n\n return { container, button, contentArea, contentAreaBottom };\n}\n","const ALLOWED_AUDIO_TYPES = new Set([\n 'audio/ogg',\n 'audio/mpeg',\n 'audio/mp3',\n 'audio/wav',\n 'audio/webm',\n 'audio/aac',\n 'audio/mp4',\n]);\n\n/** Active audio elements tracked for bulk cleanup. */\nconst activeAudioElements = new Set<HTMLAudioElement>();\n\n/** Release an audio element: pause, revoke, and remove from tracking set. */\nfunction releaseAudio(audio: HTMLAudioElement): void {\n audio.pause();\n audio.removeAttribute('src');\n audio.load(); // Releases the media resource\n activeAudioElements.delete(audio);\n}\n\n/** Returned by `playTtsAudio` on successful playback initiation. */\nexport interface AudioHandle {\n /** Stop playback immediately. Safe to call multiple times. */\n stop: () => void;\n}\n\n/**\n * Plays a base64-encoded audio clip.\n * Returns an `AudioHandle` that can stop playback, or `null` if playback\n * could not be initiated (blocked by browser, unsupported environment, etc.).\n */\nexport function playTtsAudio(base64: string, contentType = 'audio/ogg'): AudioHandle | null {\n // Strip parameters like '; codecs=opus' before checking allowlist\n const baseType = contentType.split(';')[0]!.trim();\n if (!ALLOWED_AUDIO_TYPES.has(baseType)) return null;\n try {\n const audio = new Audio(`data:${contentType};base64,${base64}`);\n activeAudioElements.add(audio);\n audio.addEventListener('ended', () => releaseAudio(audio), { once: true });\n audio.play().catch(() => {\n // Autoplay blocked by browser — release immediately\n releaseAudio(audio);\n });\n return {\n stop: () => releaseAudio(audio),\n };\n } catch {\n // Unsupported environment\n return null;\n }\n}\n\n/** Stop and release all active TTS audio elements. */\nexport function destroyAllTtsAudio(): void {\n for (const audio of activeAudioElements) {\n releaseAudio(audio);\n }\n}\n","/**\n * ComparisonTable — renders a product comparison table with recommended pick,\n * attribute rows, highlights, and optional special cases.\n *\n * XSS safety: All text is set via textContent. Image URLs are validated\n * for safe protocols before being assigned to img.src.\n */\n\nimport { sanitizeHtml, isSafeImageUrl } from '../../common/safe-html.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport type { PriceFormatConfig } from '../../common/price-formatter.js';\n\n/**\n * Fallback display names for common e-commerce product attributes.\n * Used when the backend sends raw field names (e.g., \"screen_size\")\n * and no locale-specific criteriaLabels map is provided via i18n.\n */\nconst CRITERIA_DISPLAY_NAMES: Record<string, string> = {\n screen_size: 'Screen Size',\n weight: 'Weight',\n battery_capacity: 'Battery Capacity',\n battery_life: 'Battery Life',\n storage: 'Storage',\n memory: 'Memory',\n ram: 'RAM',\n processor: 'Processor',\n camera: 'Camera',\n resolution: 'Resolution',\n display_type: 'Display Type',\n refresh_rate: 'Refresh Rate',\n color: 'Color',\n material: 'Material',\n dimensions: 'Dimensions',\n warranty: 'Warranty',\n connectivity: 'Connectivity',\n water_resistance: 'Water Resistance',\n operating_system: 'Operating System',\n brand: 'Brand',\n model: 'Model',\n price: 'Price',\n energy_class: 'Energy Class',\n noise_level: 'Noise Level',\n capacity: 'Capacity',\n power: 'Power',\n voltage: 'Voltage',\n width: 'Width',\n height: 'Height',\n depth: 'Depth',\n};\n\n/**\n * Map a raw criteria field name to a human-readable label.\n * Checks locale-specific `criteriaLabels` first (from i18n), then the\n * built-in fallback map, then applies a formatting heuristic.\n */\nexport function formatCriteriaName(rawName: string, criteriaLabels?: Record<string, string>): string {\n return (\n criteriaLabels?.[rawName] ??\n CRITERIA_DISPLAY_NAMES[rawName] ??\n rawName.replace(/_/g, ' ').replace(/^\\w/, (c) => c.toUpperCase())\n );\n}\n\nexport interface ComparisonProduct {\n sku: string;\n name: string;\n price: string;\n imageUrl?: string | undefined;\n rating?: number | undefined;\n reviewCount?: number | undefined;\n}\n\nexport interface ComparisonAttribute {\n label: string;\n values: string[];\n}\n\nexport interface ComparisonTableI18n {\n comparisonHeading?: string;\n recommendedChoiceLabel?: string;\n highlightsLabel?: string;\n keyDifferencesLabel?: string;\n specialCasesLabel?: string;\n addToCartButton?: string;\n /** Locale-specific attribute display names (e.g., { screen_size: 'Screen Size' }). */\n criteriaLabels?: Record<string, string>;\n}\n\nexport interface ComparisonTableOptions {\n recommended: ComparisonProduct;\n products: ComparisonProduct[];\n attributes: ComparisonAttribute[];\n highlights: string[];\n specialCases?: string[] | undefined;\n onProductClick: (sku: string) => void;\n onAddToCart?: ((sku: string) => void) | undefined;\n recommendedText?: string | undefined;\n winnerHits?: Record<string, { positive?: string[]; negative?: string[] }> | undefined;\n productActions?: Record<string, { title: string; type: string; payload?: unknown }> | undefined;\n keyDifferencesHtml?: string | undefined;\n i18n?: ComparisonTableI18n | undefined;\n pricing?: PriceFormatConfig | undefined;\n}\n\nexport function renderComparisonTable(options: ComparisonTableOptions): HTMLElement {\n const { recommended, products, attributes, highlights, specialCases, onProductClick, i18n } = options;\n\n const container = document.createElement('div');\n container.className = 'gengage-chat-comparison';\n container.setAttribute('role', 'dialog');\n container.setAttribute('aria-label', i18n?.comparisonHeading ?? 'Comparison Results');\n\n // Heading\n const heading = document.createElement('h3');\n heading.className = 'gengage-chat-comparison-heading';\n heading.textContent = i18n?.comparisonHeading ?? 'COMPARISON RESULTS';\n container.appendChild(heading);\n\n // Recommended card\n if (recommended) {\n const recCard = document.createElement('div');\n recCard.className = 'gengage-chat-comparison-recommended';\n\n const recLabel = document.createElement('div');\n recLabel.className = 'gengage-chat-comparison-recommended-label';\n recLabel.textContent = i18n?.recommendedChoiceLabel ?? 'Recommended Choice';\n recCard.appendChild(recLabel);\n\n const recBody = document.createElement('div');\n recBody.className = 'gengage-chat-comparison-recommended-body';\n\n if (recommended.imageUrl && isSafeImageUrl(recommended.imageUrl)) {\n const img = document.createElement('img');\n img.src = recommended.imageUrl;\n img.alt = recommended.name;\n img.loading = 'lazy';\n img.addEventListener(\n 'error',\n () => {\n img.style.display = 'none';\n },\n { once: true },\n );\n recBody.appendChild(img);\n }\n\n const info = document.createElement('div');\n info.className = 'gengage-chat-comparison-recommended-info';\n const title = document.createElement('div');\n title.className = 'gengage-chat-comparison-recommended-title';\n title.textContent = recommended.name;\n info.appendChild(title);\n const price = document.createElement('div');\n price.className = 'gengage-chat-comparison-recommended-price';\n price.textContent = formatPrice(recommended.price, options.pricing);\n info.appendChild(price);\n recBody.appendChild(info);\n\n recBody.addEventListener('click', () => {\n onProductClick(recommended.sku);\n });\n recBody.style.cursor = 'pointer';\n\n recCard.appendChild(recBody);\n\n // Highlights\n if (highlights.length > 0) {\n const hlSection = document.createElement('div');\n hlSection.className = 'gengage-chat-comparison-highlights';\n const hlLabel = document.createElement('div');\n hlLabel.className = 'gengage-chat-comparison-highlights-label';\n hlLabel.textContent = i18n?.highlightsLabel ?? 'Key Highlights';\n hlSection.appendChild(hlLabel);\n const ul = document.createElement('ul');\n for (const hl of highlights) {\n const li = document.createElement('li');\n li.textContent = hl;\n ul.appendChild(li);\n }\n hlSection.appendChild(ul);\n recCard.appendChild(hlSection);\n }\n\n // Recommended choice explanation\n if (options.recommendedText) {\n const recExplanation = document.createElement('div');\n recExplanation.className = 'gengage-chat-comparison-recommended-text';\n recExplanation.innerHTML = sanitizeHtml(options.recommendedText);\n recCard.appendChild(recExplanation);\n }\n\n container.appendChild(recCard);\n }\n\n // Key Differences section\n if (options.keyDifferencesHtml) {\n const kdSection = document.createElement('div');\n kdSection.className = 'gengage-chat-comparison-key-differences';\n const kdHeading = document.createElement('h4');\n kdHeading.textContent = i18n?.keyDifferencesLabel ?? 'Key Differences';\n kdSection.appendChild(kdHeading);\n const kdContent = document.createElement('div');\n kdContent.className = 'gengage-chat-comparison-key-differences-content';\n kdContent.innerHTML = sanitizeHtml(formatKeyDifferences(options.keyDifferencesHtml));\n kdSection.appendChild(kdContent);\n container.appendChild(kdSection);\n }\n\n // Special cases (expandable)\n if (specialCases && specialCases.length > 0) {\n const special = document.createElement('details');\n special.className = 'gengage-chat-comparison-special';\n const summary = document.createElement('summary');\n summary.textContent = i18n?.specialCasesLabel ?? 'For Special Cases';\n special.appendChild(summary);\n const list = document.createElement('ul');\n for (const sc of specialCases) {\n appendSpecialCaseListItems(list, sc);\n }\n if (list.childElementCount > 0) {\n special.appendChild(list);\n }\n container.appendChild(special);\n }\n\n // Comparison table\n if (products.length > 0 && attributes.length > 0) {\n const table = document.createElement('table');\n table.className = 'gengage-chat-comparison-table';\n\n // Header row: empty cell + product columns\n const thead = document.createElement('thead');\n const headerRow = document.createElement('tr');\n const emptyTh = document.createElement('th');\n headerRow.appendChild(emptyTh);\n for (const product of products) {\n const th = document.createElement('th');\n if (product.sku === recommended?.sku) {\n th.className = 'gengage-chat-comparison-selected';\n }\n if (product.imageUrl && isSafeImageUrl(product.imageUrl)) {\n const img = document.createElement('img');\n img.src = product.imageUrl;\n img.alt = product.name;\n img.loading = 'lazy';\n img.addEventListener(\n 'error',\n () => {\n img.style.display = 'none';\n },\n { once: true },\n );\n th.appendChild(img);\n }\n const name = document.createElement('div');\n name.textContent = product.name;\n th.appendChild(name);\n const prc = document.createElement('div');\n prc.className = 'gengage-chat-comparison-table-price';\n prc.textContent = formatPrice(product.price, options.pricing);\n th.appendChild(prc);\n headerRow.appendChild(th);\n }\n thead.appendChild(headerRow);\n table.appendChild(thead);\n\n // Attribute rows\n const tbody = document.createElement('tbody');\n for (const attr of attributes) {\n const row = document.createElement('tr');\n const labelTd = document.createElement('td');\n labelTd.className = 'gengage-chat-comparison-label';\n labelTd.textContent = formatCriteriaName(attr.label, i18n?.criteriaLabels);\n row.appendChild(labelTd);\n for (let i = 0; i < attr.values.length; i++) {\n const td = document.createElement('td');\n if (products[i]?.sku === recommended?.sku) {\n td.className = 'gengage-chat-comparison-selected';\n }\n td.textContent = attr.values[i] ?? '';\n row.appendChild(td);\n }\n tbody.appendChild(row);\n }\n table.appendChild(tbody);\n container.appendChild(table);\n }\n\n // View product buttons\n if (options.productActions) {\n const btnRow = document.createElement('div');\n btnRow.className = 'gengage-chat-comparison-product-actions';\n for (const product of products) {\n const action = options.productActions[product.sku];\n if (action) {\n const btn = document.createElement('button');\n btn.className = 'gengage-chat-comparison-view-btn';\n btn.type = 'button';\n btn.textContent = product.name;\n btn.addEventListener('click', () => onProductClick(product.sku));\n btnRow.appendChild(btn);\n }\n }\n if (btnRow.childElementCount > 0) {\n container.appendChild(btnRow);\n }\n }\n\n // Focus trap: keep Tab cycling within the comparison dialog\n container.addEventListener('keydown', (e) => {\n if (e.key !== 'Tab') return;\n const focusables = container.querySelectorAll<HTMLElement>(\n 'button, [href], input, [tabindex]:not([tabindex=\"-1\"])',\n );\n if (focusables.length === 0) return;\n const first = focusables[0]!;\n const last = focusables[focusables.length - 1]!;\n if (e.shiftKey && document.activeElement === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n });\n\n return container;\n}\n\nfunction formatKeyDifferences(text: string): string {\n // If the backend already sent HTML with list elements, pass through as-is\n if (/<[uo]l[\\s>]/i.test(text) || /<li[\\s>]/i.test(text)) return text;\n const lines = text.split('\\n').filter((l) => l.trim());\n if (lines.length <= 1) return text;\n return '<ul>' + lines.map((l) => `<li>${l.trim()}</li>`).join('') + '</ul>';\n}\n\nfunction appendSpecialCaseListItems(list: HTMLUListElement, raw: string): void {\n const sanitized = sanitizeHtml(raw);\n if (!sanitized) return;\n\n const template = document.createElement('template');\n template.innerHTML = sanitized;\n const nestedItems = Array.from(template.content.querySelectorAll('li'));\n if (nestedItems.length > 0) {\n for (const nestedItem of nestedItems) {\n const li = document.createElement('li');\n li.innerHTML = sanitizeHtml(nestedItem.innerHTML);\n list.appendChild(li);\n }\n return;\n }\n\n const li = document.createElement('li');\n if (looksLikeHtml(raw)) {\n li.innerHTML = sanitized;\n } else {\n li.textContent = raw;\n }\n list.appendChild(li);\n}\n\nfunction looksLikeHtml(text: string): boolean {\n return /<\\/?[a-z][\\s\\S]*>/i.test(text);\n}\n","/**\n * ReviewHighlights — renders review summary cards with filter tabs and tag pills.\n *\n * Tag pills are clickable filters: clicking a pill shows only reviews with that tag.\n * Sentiment tabs (All/Positive/Negative) work independently alongside tag filtering.\n * By default the first tag pill is selected, showing its reviews.\n *\n * All text is set via textContent — no innerHTML, no XSS surface.\n */\n\nimport type { UIElement } from '../../common/types.js';\n\ninterface ReviewItem {\n review_class?: string;\n review_text?: string;\n review_rating?: string | number;\n review_tag?: string;\n}\n\nexport function renderReviewHighlights(\n element: UIElement,\n options?: {\n emptyReviewsMessage?: string | undefined;\n reviewFilterAll?: string | undefined;\n reviewFilterPositive?: string | undefined;\n reviewFilterNegative?: string | undefined;\n },\n): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-review-highlights';\n\n const reviews = element.props?.['reviews'];\n if (!Array.isArray(reviews) || reviews.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'gengage-chat-review-empty';\n empty.textContent = options?.emptyReviewsMessage ?? 'No review summary found.';\n container.appendChild(empty);\n return container;\n }\n\n const items: ReviewItem[] = reviews.filter(\n (r): r is Record<string, unknown> => r !== null && typeof r === 'object',\n ) as ReviewItem[];\n\n // Count sentiments\n const counts = { all: items.length, positive: 0, negative: 0, neutral: 0 };\n for (const item of items) {\n if (item.review_class === 'positive') counts.positive++;\n else if (item.review_class === 'negative') counts.negative++;\n else counts.neutral++;\n }\n\n // Build unique tag list (ordered by first occurrence)\n const tagCounts = new Map<string, { count: number; sentiment: string }>();\n for (const item of items) {\n if (typeof item.review_tag === 'string' && item.review_tag.length > 0) {\n const existing = tagCounts.get(item.review_tag);\n if (existing) {\n existing.count++;\n } else {\n tagCounts.set(item.review_tag, { count: 1, sentiment: item.review_class ?? 'neutral' });\n }\n }\n }\n\n const firstTag = tagCounts.size > 0 ? (tagCounts.keys().next().value as string) : null;\n\n // State\n let activeSentiment = 'all';\n let activeTag: string | null = firstTag;\n\n // --- Sentiment filter tabs ---\n const tabBar = document.createElement('div');\n tabBar.className = 'gengage-chat-review-tabs';\n\n const allLabel = options?.reviewFilterAll ?? 'All';\n const positiveLabel = options?.reviewFilterPositive ?? 'Positive';\n const negativeLabel = options?.reviewFilterNegative ?? 'Negative';\n\n const sentimentFilters: Array<{ label: string; filter: string }> = [\n { label: `${allLabel} (${counts.all})`, filter: 'all' },\n ];\n if (counts.positive > 0)\n sentimentFilters.push({ label: `${positiveLabel} (${counts.positive})`, filter: 'positive' });\n if (counts.negative > 0)\n sentimentFilters.push({ label: `${negativeLabel} (${counts.negative})`, filter: 'negative' });\n\n // --- Review items container ---\n const itemsContainer = document.createElement('div');\n itemsContainer.className = 'gengage-chat-review-items';\n\n function renderItems(): void {\n while (itemsContainer.firstChild) itemsContainer.removeChild(itemsContainer.firstChild);\n\n const filtered = items.filter((i) => {\n const sentimentOk = activeSentiment === 'all' || i.review_class === activeSentiment;\n const tagOk = activeTag === null || i.review_tag === activeTag;\n return sentimentOk && tagOk;\n });\n\n for (const review of filtered) {\n const item = document.createElement('article');\n item.className = 'gengage-chat-review-item';\n const cls = review.review_class;\n if (cls === 'positive' || cls === 'negative' || cls === 'neutral') {\n item.dataset['tone'] = cls;\n }\n\n if (typeof review.review_tag === 'string' && review.review_tag.length > 0) {\n const tagEl = document.createElement('div');\n tagEl.className = 'gengage-chat-review-tag';\n tagEl.textContent = review.review_tag;\n item.appendChild(tagEl);\n }\n\n if (typeof review.review_text === 'string' && review.review_text.length > 0) {\n const textEl = document.createElement('div');\n textEl.className = 'gengage-chat-review-text';\n textEl.textContent = review.review_text;\n item.appendChild(textEl);\n }\n\n if (review.review_rating !== undefined && String(review.review_rating).length > 0) {\n const ratingEl = document.createElement('div');\n ratingEl.className = 'gengage-chat-review-rating';\n ratingEl.textContent = String(review.review_rating);\n item.appendChild(ratingEl);\n }\n\n itemsContainer.appendChild(item);\n }\n }\n\n for (const f of sentimentFilters) {\n const tab = document.createElement('button');\n tab.className = 'gengage-chat-review-tab';\n tab.type = 'button';\n tab.textContent = f.label;\n if (f.filter === activeSentiment) tab.classList.add('gengage-chat-review-tab--active');\n\n tab.addEventListener('click', () => {\n activeSentiment = f.filter;\n for (const t of tabBar.querySelectorAll('.gengage-chat-review-tab')) {\n t.classList.toggle('gengage-chat-review-tab--active', t === tab);\n }\n renderItems();\n });\n tabBar.appendChild(tab);\n }\n\n container.appendChild(tabBar);\n\n // --- Tag pills (clickable filters) ---\n if (tagCounts.size > 0) {\n const pillsRow = document.createElement('div');\n pillsRow.className = 'gengage-chat-review-pills';\n\n for (const [tag, data] of tagCounts) {\n const pill = document.createElement('button');\n pill.type = 'button';\n pill.className = 'gengage-chat-review-pill';\n pill.dataset['tone'] = data.sentiment;\n pill.dataset['tag'] = tag;\n if (tag === activeTag) pill.classList.add('gengage-chat-review-pill--active');\n\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-review-pill-icon';\n icon.textContent = data.sentiment === 'positive' ? '\\u2713' : data.sentiment === 'negative' ? '\\u2715' : '\\u25CF';\n pill.appendChild(icon);\n\n const tagText = document.createElement('span');\n tagText.textContent = tag;\n pill.appendChild(tagText);\n\n if (data.count > 1) {\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-review-pill-count';\n badge.textContent = String(data.count);\n pill.appendChild(badge);\n }\n\n pill.addEventListener('click', () => {\n // Toggle: clicking active tag deselects (shows all)\n activeTag = activeTag === tag ? null : tag;\n for (const p of pillsRow.querySelectorAll('.gengage-chat-review-pill')) {\n const isActive = activeTag !== null && (p as HTMLElement).dataset['tag'] === activeTag;\n p.classList.toggle('gengage-chat-review-pill--active', isActive);\n }\n renderItems();\n });\n\n pillsRow.appendChild(pill);\n }\n container.appendChild(pillsRow);\n }\n\n renderItems();\n container.appendChild(itemsContainer);\n\n return container;\n}\n","/**\n * AI Top Picks renderer.\n *\n * Renders rich AI-curated product suggestion cards with:\n * - Winner card (vertical, primary border, badge, large image)\n * - Compact cards (horizontal, smaller)\n * - Sentiment label chips (green/red/gray)\n * - Expert quality scores, review quotes\n */\n\nimport type { UIElement, ActionPayload } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport { isSafeImageUrl, safeSetAttribute } from '../../common/safe-html.js';\nimport { clampDiscount, addImageErrorHandler } from '../../common/product-utils.js';\n\ninterface SentimentLabel {\n label: string;\n sentiment?: 'positive' | 'negative' | 'neutral';\n}\n\ninterface AITopPickItem {\n product: Record<string, unknown>;\n role?: string;\n reason?: string;\n labels?: SentimentLabel[];\n expertQualityScore?: number;\n reviewHighlight?: string;\n action?: ActionPayload;\n}\n\nfunction resolveActionSku(item: AITopPickItem): string | null {\n const productSku = item.product['sku'];\n if (typeof productSku === 'string' && productSku.length > 0) return productSku;\n const payload = item.action?.payload;\n if (payload && typeof payload === 'object' && 'sku' in payload && typeof payload.sku === 'string') {\n return payload.sku;\n }\n return null;\n}\n\nconst ROLE_LABELS: Record<string, string> = {\n winner: 'roleWinner',\n best_value: 'roleBestValue',\n best_alternative: 'roleBestAlternative',\n};\n\nfunction getRoleLabel(role: string | undefined, i18n: ChatUISpecRenderContext['i18n']): string | null {\n if (!role || !i18n) return null;\n const key = ROLE_LABELS[role];\n if (!key) return role;\n return (i18n as Record<string, string>)[key] ?? role;\n}\n\nexport function renderAITopPicks(element: UIElement, ctx: ChatUISpecRenderContext): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-ai-top-picks';\n\n const suggestions = (element.props?.['suggestions'] ?? []) as AITopPickItem[];\n if (suggestions.length === 0) return container;\n\n // Title\n const title = document.createElement('h3');\n title.className = 'gengage-chat-ai-top-picks-title';\n title.textContent = ctx.i18n?.aiTopPicksTitle ?? 'Top Picks';\n container.appendChild(title);\n\n const cardsWrap = document.createElement('div');\n cardsWrap.className = 'gengage-chat-ai-top-picks-cards';\n\n for (let i = 0; i < suggestions.length; i++) {\n const suggestion = suggestions[i]!;\n const isWinner = suggestion.role === 'winner' || i === 0;\n const card = isWinner ? renderTopPickCard(suggestion, ctx) : renderCompactCard(suggestion, ctx);\n cardsWrap.appendChild(card);\n }\n\n container.appendChild(cardsWrap);\n return container;\n}\n\nfunction renderTopPickCard(item: AITopPickItem, ctx: ChatUISpecRenderContext): HTMLElement {\n const card = document.createElement('div');\n card.className = 'gengage-chat-ai-toppick-card gengage-chat-ai-toppick-card--winner';\n\n // Badge\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-ai-toppick-badge';\n badge.textContent = getRoleLabel(item.role, ctx.i18n) ?? ctx.i18n?.roleWinner ?? 'TOP MATCH';\n card.appendChild(badge);\n\n const product = item.product;\n\n // Discount badge\n const discountPercent = product['discountPercent'] as number | undefined;\n if (typeof discountPercent === 'number' && discountPercent > 0) {\n const discountBadge = document.createElement('span');\n discountBadge.className = 'gengage-chat-ai-toppick-discount-badge';\n discountBadge.textContent = `%${clampDiscount(discountPercent)}`;\n card.appendChild(discountBadge);\n }\n\n // Image\n const imageUrl = product['imageUrl'] as string | undefined;\n if (imageUrl && isSafeImageUrl(imageUrl)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-ai-toppick-img';\n safeSetAttribute(img, 'src', imageUrl);\n img.loading = 'lazy';\n img.alt = (product['name'] as string) || 'Product image';\n addImageErrorHandler(img);\n card.appendChild(img);\n }\n\n // Body\n const body = document.createElement('div');\n body.className = 'gengage-chat-ai-toppick-body';\n\n const name = product['name'] as string | undefined;\n if (name) {\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-ai-toppick-name';\n nameEl.textContent = name;\n body.appendChild(nameEl);\n }\n\n // Reason text\n if (item.reason) {\n const reasonEl = document.createElement('div');\n reasonEl.className = 'gengage-chat-ai-toppick-reason';\n reasonEl.textContent = item.reason;\n body.appendChild(reasonEl);\n }\n\n // Sentiment chips\n if (item.labels && item.labels.length > 0) {\n body.appendChild(renderSentimentChips(item.labels));\n }\n\n // Expert quality score — normalize to 0-10 scale\n if (typeof item.expertQualityScore === 'number') {\n const score = document.createElement('div');\n score.className = 'gengage-chat-ai-toppick-score';\n let displayScore = item.expertQualityScore;\n let maxScale = 10;\n if (displayScore > 10) {\n // Percentage-style score (e.g. 92) → normalize to x/10\n displayScore = Math.round(displayScore) / 10;\n } else if (displayScore <= 5) {\n maxScale = 5;\n }\n score.textContent = `${displayScore}/${maxScale}`;\n body.appendChild(score);\n }\n\n // Review highlight quote\n if (item.reviewHighlight) {\n const review = document.createElement('blockquote');\n review.className = 'gengage-chat-ai-toppick-review';\n review.textContent = item.reviewHighlight;\n body.appendChild(review);\n }\n\n // Price\n const price = product['price'] as string | undefined;\n const originalPrice = product['originalPrice'] as string | undefined;\n if (price) {\n const priceRow = document.createElement('div');\n priceRow.className = 'gengage-chat-ai-toppick-price';\n if (originalPrice && originalPrice !== price) {\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-ai-toppick-original-price';\n orig.textContent = formatPrice(originalPrice, ctx.pricing);\n priceRow.appendChild(orig);\n priceRow.appendChild(document.createTextNode(' '));\n }\n const current = document.createElement('span');\n current.textContent = formatPrice(price, ctx.pricing);\n priceRow.appendChild(current);\n body.appendChild(priceRow);\n }\n\n card.appendChild(body);\n\n // CTA button\n if (item.action) {\n const sku = resolveActionSku(item);\n const url = (product['url'] as string) ?? '';\n\n // Spinner overlay (hidden by default)\n const spinner = document.createElement('div');\n spinner.className = 'gengage-chat-ai-toppick-spinner';\n spinner.style.display = sku && ctx.topPicksLoadingSku === sku ? '' : 'none';\n card.appendChild(spinner);\n\n const cta = document.createElement('button');\n cta.className = 'gengage-chat-ai-toppick-cta';\n cta.type = 'button';\n cta.textContent = ctx.i18n?.viewDetails ?? 'View Details';\n cta.addEventListener('click', () => {\n if (item.action?.type === 'findSimilar' && sku && ctx.onProductClick) {\n ctx.onProductClick({ sku, url });\n return;\n }\n ctx.onAction(item.action!);\n });\n card.appendChild(cta);\n }\n\n return card;\n}\n\nfunction renderCompactCard(item: AITopPickItem, ctx: ChatUISpecRenderContext): HTMLElement {\n const card = document.createElement('div');\n card.className = 'gengage-chat-ai-toppick-card gengage-chat-ai-toppick-card--compact';\n\n const product = item.product;\n\n // Discount badge\n const discountPercent = product['discountPercent'] as number | undefined;\n if (typeof discountPercent === 'number' && discountPercent > 0) {\n const discountBadge = document.createElement('span');\n discountBadge.className = 'gengage-chat-ai-toppick-discount-badge';\n discountBadge.textContent = `%${clampDiscount(discountPercent)}`;\n card.appendChild(discountBadge);\n }\n\n // Image\n const imageUrl = product['imageUrl'] as string | undefined;\n if (imageUrl && isSafeImageUrl(imageUrl)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-ai-toppick-img';\n safeSetAttribute(img, 'src', imageUrl);\n img.loading = 'lazy';\n img.alt = (product['name'] as string) || 'Product image';\n addImageErrorHandler(img);\n card.appendChild(img);\n }\n\n // Body\n const body = document.createElement('div');\n body.className = 'gengage-chat-ai-toppick-body';\n\n // Role label\n const roleLabel = getRoleLabel(item.role, ctx.i18n);\n if (roleLabel) {\n const roleEl = document.createElement('div');\n roleEl.className = 'gengage-chat-ai-toppick-role';\n roleEl.textContent = roleLabel;\n body.appendChild(roleEl);\n }\n\n const name = product['name'] as string | undefined;\n if (name) {\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-ai-toppick-name';\n nameEl.textContent = name;\n body.appendChild(nameEl);\n }\n\n // Reason text\n if (item.reason) {\n const reasonEl = document.createElement('div');\n reasonEl.className = 'gengage-chat-ai-toppick-reason';\n reasonEl.textContent = item.reason;\n body.appendChild(reasonEl);\n }\n\n // Sentiment chips\n if (item.labels && item.labels.length > 0) {\n body.appendChild(renderSentimentChips(item.labels));\n }\n\n // Price\n const price = product['price'] as string | undefined;\n if (price) {\n const priceEl = document.createElement('div');\n priceEl.className = 'gengage-chat-ai-toppick-price';\n priceEl.textContent = formatPrice(price, ctx.pricing);\n body.appendChild(priceEl);\n }\n\n card.appendChild(body);\n\n // CTA\n if (item.action) {\n const sku = resolveActionSku(item);\n const url = (product['url'] as string) ?? '';\n\n // Spinner overlay (hidden by default)\n const spinner = document.createElement('div');\n spinner.className = 'gengage-chat-ai-toppick-spinner';\n spinner.style.display = sku && ctx.topPicksLoadingSku === sku ? '' : 'none';\n card.appendChild(spinner);\n\n const cta = document.createElement('button');\n cta.className = 'gengage-chat-ai-toppick-cta';\n cta.type = 'button';\n cta.textContent = ctx.i18n?.viewDetails ?? 'View Details';\n cta.addEventListener('click', () => {\n if (item.action?.type === 'findSimilar' && sku && ctx.onProductClick) {\n ctx.onProductClick({ sku, url });\n return;\n }\n ctx.onAction(item.action!);\n });\n card.appendChild(cta);\n }\n\n return card;\n}\n\nfunction renderSentimentChips(labels: SentimentLabel[]): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-ai-toppick-labels';\n for (const label of labels) {\n const chip = document.createElement('span');\n chip.className = 'gengage-chat-ai-toppick-label';\n chip.dataset['sentiment'] = label.sentiment ?? 'neutral';\n chip.textContent = label.label;\n container.appendChild(chip);\n }\n return container;\n}\n","/**\n * Grounding Review Card renderer.\n *\n * Renders a clickable card for review grounding data with title,\n * review count, and a CTA arrow. The entire card is clickable.\n *\n * XSS safety: All text is set via textContent. No innerHTML.\n */\n\nimport type { UIElement, ActionPayload } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\n\nexport function renderGroundingReviewCard(element: UIElement, ctx: ChatUISpecRenderContext): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-grounding-review';\n\n const props = element.props ?? {};\n const title = props['title'] as string | undefined;\n const reviewCount = props['reviewCount'] as string | undefined;\n const action = props['action'] as ActionPayload | undefined;\n const ctaLabel = ctx.i18n?.groundingReviewCta ?? 'Read Reviews';\n\n // Icon\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-grounding-review-icon';\n icon.textContent = '\\u{1F4CB}'; // clipboard\n container.appendChild(icon);\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-grounding-review-body';\n\n // Title\n const titleEl = document.createElement('div');\n titleEl.className = 'gengage-chat-grounding-review-title';\n titleEl.textContent = title ?? ctx.i18n?.customerReviewsTitle ?? 'Customer Reviews';\n body.appendChild(titleEl);\n\n // Subtitle (review count)\n if (reviewCount) {\n const subtitle = document.createElement('div');\n subtitle.className = 'gengage-chat-grounding-review-subtitle';\n const template = ctx.i18n?.groundingReviewSubtitle ?? '{count} yorum mevcut';\n subtitle.textContent = template.replace('{count}', reviewCount);\n body.appendChild(subtitle);\n }\n\n container.appendChild(body);\n\n // CTA arrow\n const cta = document.createElement('span');\n cta.className = 'gengage-chat-grounding-review-cta';\n cta.textContent = `${ctaLabel} \\u2192`;\n container.appendChild(cta);\n\n // Make entire card clickable\n if (action) {\n container.style.cursor = 'pointer';\n container.addEventListener('click', () => ctx.onAction(action));\n }\n\n return container;\n}\n","/**\n * AI Grouping Cards renderer.\n *\n * Desktop: compact card with image, name, and labels.\n * Mobile: simple button list with arrow prefix (images hidden via CSS).\n *\n * XSS safety: All text is set via textContent. Image URLs are validated\n * for safe protocols. No innerHTML.\n */\n\nimport type { UIElement, ActionPayload } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\nimport { isSafeImageUrl } from '../../common/safe-html.js';\n\ninterface GroupingEntry {\n name: string;\n image?: string;\n description?: string;\n labels?: string[];\n action: ActionPayload;\n}\n\nfunction normalizeGroupingAction(entry: GroupingEntry): ActionPayload {\n if (entry.action.type !== 'findSimilar') return entry.action;\n const payload =\n entry.action.payload && typeof entry.action.payload === 'object'\n ? (entry.action.payload as Record<string, unknown>)\n : null;\n const text =\n (typeof payload?.['input'] === 'string' && payload['input'].trim()) ||\n (typeof payload?.['text'] === 'string' && payload['text'].trim()) ||\n entry.name.trim();\n if (!text) return entry.action;\n\n const normalizedPayload: Record<string, unknown> = {\n text,\n is_suggested_text: 1,\n };\n if (typeof payload?.['sku'] === 'string' && payload['sku'].trim()) {\n normalizedPayload['sku'] = payload['sku'];\n }\n if (Array.isArray(payload?.['group_skus'])) {\n const groupSkus = payload['group_skus'].filter((sku): sku is string => typeof sku === 'string' && sku.length > 0);\n if (groupSkus.length > 0) normalizedPayload['group_skus'] = groupSkus;\n }\n return {\n title: entry.action.title,\n type: 'inputText',\n payload: normalizedPayload,\n };\n}\n\nexport function renderAIGroupingCards(element: UIElement, ctx: ChatUISpecRenderContext): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-grouping-cards';\n\n const entries = (element.props?.['entries'] ?? []) as GroupingEntry[];\n if (entries.length === 0) return container;\n\n for (const entry of entries) {\n const card = document.createElement('div');\n card.className = 'gengage-chat-grouping-card';\n card.style.cursor = 'pointer';\n card.addEventListener('click', () => ctx.onAction(normalizeGroupingAction(entry)));\n\n // Image (20x20 on desktop, hidden on mobile via CSS)\n if (entry.image && isSafeImageUrl(entry.image)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-grouping-card-img';\n img.src = entry.image;\n img.alt = entry.name;\n img.width = 20;\n img.height = 20;\n card.appendChild(img);\n }\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-grouping-card-body';\n\n const nameEl = document.createElement('span');\n nameEl.className = 'gengage-chat-grouping-card-name';\n nameEl.textContent = entry.name;\n body.appendChild(nameEl);\n\n if (entry.description) {\n const desc = document.createElement('span');\n desc.className = 'gengage-chat-grouping-card-desc';\n desc.textContent = entry.description;\n body.appendChild(desc);\n }\n\n if (entry.labels && entry.labels.length > 0) {\n const labelsEl = document.createElement('span');\n labelsEl.className = 'gengage-chat-grouping-card-labels';\n labelsEl.textContent = entry.labels.slice(0, 3).join(' \\u00B7 ');\n body.appendChild(labelsEl);\n }\n\n card.appendChild(body);\n\n // Mobile prefix arrow (shown via CSS only on mobile)\n const arrow = document.createElement('span');\n arrow.className = 'gengage-chat-grouping-card-arrow';\n arrow.textContent = '\\u21B3';\n card.insertBefore(arrow, card.firstChild);\n\n container.appendChild(card);\n }\n\n return container;\n}\n","/**\n * AI Suggested Search Cards renderer.\n *\n * Cards with image, short_name, detailed_user_message, and why_different.\n * Clicking a card dispatches the associated action.\n *\n * XSS safety: All text is set via textContent. Image URLs are validated\n * for safe protocols. No innerHTML.\n */\n\nimport type { UIElement, ActionPayload } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\nimport { isSafeImageUrl } from '../../common/safe-html.js';\n\ninterface SuggestedSearchEntry {\n shortName: string;\n detailedMessage?: string;\n whyDifferent?: string;\n image?: string;\n action: ActionPayload;\n}\n\nexport function renderAISuggestedSearchCards(element: UIElement, ctx: ChatUISpecRenderContext): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-suggested-search-cards';\n\n const entries = (element.props?.['entries'] ?? []) as SuggestedSearchEntry[];\n if (entries.length === 0) return container;\n\n for (const entry of entries) {\n const card = document.createElement('div');\n card.className = 'gengage-chat-suggested-search-card';\n card.style.cursor = 'pointer';\n card.addEventListener('click', () => ctx.onAction(entry.action));\n\n // Image (40x40)\n if (entry.image && isSafeImageUrl(entry.image)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-suggested-search-card-img';\n img.src = entry.image;\n img.alt = entry.shortName;\n img.width = 40;\n img.height = 40;\n card.appendChild(img);\n }\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-suggested-search-card-body';\n\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-suggested-search-card-name';\n nameEl.textContent = entry.shortName;\n body.appendChild(nameEl);\n\n if (entry.detailedMessage) {\n const desc = document.createElement('div');\n desc.className = 'gengage-chat-suggested-search-card-desc';\n desc.textContent = entry.detailedMessage;\n body.appendChild(desc);\n }\n\n if (entry.whyDifferent) {\n const diff = document.createElement('div');\n diff.className = 'gengage-chat-suggested-search-card-diff';\n diff.textContent = entry.whyDifferent;\n body.appendChild(diff);\n }\n\n card.appendChild(body);\n container.appendChild(card);\n }\n\n return container;\n}\n","/**\n * Floating comparison button.\n *\n * Renders a fixed-position button at the bottom of the product grid wrapper\n * that appears when 2+ products are selected for comparison.\n * Click dispatches a `getComparisonTable` action with the selected SKUs.\n *\n * XSS safety: All text set via textContent.\n */\n\nimport type { ChatUISpecRenderContext } from '../types.js';\n\n/**\n * Creates a floating comparison button element.\n * Only meaningful when `selectedSkus.length >= 2`.\n *\n * @param selectedSkus - Array of selected product SKU strings\n * @param ctx - Render context with onAction and i18n\n * @returns The button element\n */\nexport function renderFloatingComparisonButton(selectedSkus: string[], ctx: ChatUISpecRenderContext): HTMLElement {\n const button = document.createElement('button');\n button.className = 'gengage-chat-comparison-floating-btn';\n button.type = 'button';\n\n const label = ctx.i18n?.compareSelected ?? 'Compare';\n button.textContent = `${label} (${selectedSkus.length})`;\n\n button.addEventListener('click', () => {\n ctx.onAction({\n title: label,\n type: 'getComparisonTable',\n payload: { sku_list: [...selectedSkus] },\n });\n });\n\n return button;\n}\n","/**\n * Renders a Pros & Cons list for a product.\n *\n * XSS safety: All text is set via textContent. No innerHTML.\n */\n\nexport function renderProsAndCons(element: { props?: Record<string, unknown> }): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-pros-cons';\n\n const productName = element.props?.['productName'] as string | undefined;\n if (productName) {\n const heading = document.createElement('h4');\n heading.className = 'gengage-chat-pros-cons-heading';\n heading.textContent = productName;\n container.appendChild(heading);\n }\n\n const pros = element.props?.['pros'] as string[] | undefined;\n const cons = element.props?.['cons'] as string[] | undefined;\n\n if (pros && pros.length > 0) {\n const prosList = document.createElement('ul');\n prosList.className = 'gengage-chat-pros-cons-list';\n for (const pro of pros) {\n const li = document.createElement('li');\n li.className = 'gengage-chat-pros-cons-item';\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-pros-cons-icon gengage-chat-pros-cons-icon--pro';\n icon.textContent = '\\u2713';\n li.appendChild(icon);\n const text = document.createElement('span');\n text.textContent = pro;\n li.appendChild(text);\n prosList.appendChild(li);\n }\n container.appendChild(prosList);\n }\n\n if (cons && cons.length > 0) {\n const consList = document.createElement('ul');\n consList.className = 'gengage-chat-pros-cons-list';\n for (const con of cons) {\n const li = document.createElement('li');\n li.className = 'gengage-chat-pros-cons-item';\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-pros-cons-icon gengage-chat-pros-cons-icon--con';\n icon.textContent = '\\u2717';\n li.appendChild(icon);\n const text = document.createElement('span');\n text.textContent = con;\n li.appendChild(text);\n consList.appendChild(li);\n }\n container.appendChild(consList);\n }\n\n return container;\n}\n","/**\n * CategoriesContainer — renders grouped product lists with tab navigation\n * and optional filter tags.\n *\n * Backend sends `groupList` with `{ group_list: [{ group_name, product_list }], filter_tags }`.\n * The protocol adapter normalizes this into a CategoriesContainer UISpec element.\n *\n * XSS safety: All text is set via textContent. No innerHTML.\n */\n\nimport type { UIElement } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\nimport type { NormalizedProduct } from '../../common/protocol-adapter.js';\nimport { isSafeImageUrl } from '../../common/safe-html.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport { addImageErrorHandler } from '../../common/product-utils.js';\n\ninterface GroupData {\n groupName: string;\n products: NormalizedProduct[];\n}\n\ninterface FilterTagData {\n title: string;\n action?: { title: string; type: string; payload?: unknown };\n}\n\nexport function renderCategoriesContainer(element: UIElement, context: ChatUISpecRenderContext): HTMLElement {\n const groups = (element.props?.['groups'] as GroupData[] | undefined) ?? [];\n const filterTags = (element.props?.['filterTags'] as FilterTagData[] | undefined) ?? [];\n\n const container = document.createElement('div');\n container.className = 'gengage-chat-categories';\n\n if (groups.length === 0) return container;\n\n // Tab bar — WAI-ARIA tablist pattern\n const tabBar = document.createElement('div');\n tabBar.className = 'gengage-chat-categories-tabs';\n tabBar.setAttribute('role', 'tablist');\n\n const tabs: HTMLButtonElement[] = [];\n const panels: HTMLElement[] = [];\n\n const activateTab = (index: number): void => {\n for (let j = 0; j < tabs.length; j++) {\n const isActive = j === index;\n tabs[j]!.classList.toggle('gengage-chat-categories-tab--active', isActive);\n tabs[j]!.setAttribute('aria-selected', String(isActive));\n tabs[j]!.tabIndex = isActive ? 0 : -1;\n panels[j]!.style.display = isActive ? '' : 'none';\n }\n };\n\n for (let i = 0; i < groups.length; i++) {\n const group = groups[i]!;\n const tabId = `gengage-cat-tab-${i}`;\n const panelId = `gengage-cat-panel-${i}`;\n\n // Tab button\n const tab = document.createElement('button');\n tab.className = 'gengage-chat-categories-tab';\n tab.type = 'button';\n tab.id = tabId;\n tab.setAttribute('role', 'tab');\n tab.setAttribute('aria-controls', panelId);\n tab.setAttribute('aria-selected', String(i === 0));\n tab.tabIndex = i === 0 ? 0 : -1;\n if (i === 0) tab.classList.add('gengage-chat-categories-tab--active');\n tab.textContent = group.groupName;\n\n tab.addEventListener('click', () => activateTab(i));\n tab.addEventListener('keydown', (e: KeyboardEvent) => {\n let next = -1;\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n next = (i + 1) % groups.length;\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n next = (i - 1 + groups.length) % groups.length;\n } else if (e.key === 'Home') {\n next = 0;\n } else if (e.key === 'End') {\n next = groups.length - 1;\n }\n if (next >= 0) {\n e.preventDefault();\n activateTab(next);\n tabs[next]!.focus();\n }\n });\n\n tabs.push(tab);\n tabBar.appendChild(tab);\n\n // Product grid panel\n const panel = document.createElement('div');\n panel.className = 'gengage-chat-categories-grid';\n panel.id = panelId;\n panel.setAttribute('role', 'tabpanel');\n panel.setAttribute('aria-labelledby', tabId);\n if (i !== 0) panel.style.display = 'none';\n\n for (const product of group.products) {\n const card = renderCategoryProductCard(product, context);\n panel.appendChild(card);\n }\n\n panels.push(panel);\n }\n\n container.appendChild(tabBar);\n for (const panel of panels) container.appendChild(panel);\n\n // Filter tags\n if (filterTags.length > 0) {\n const tagsContainer = document.createElement('div');\n tagsContainer.className = 'gengage-chat-categories-filter-tags';\n\n for (const tag of filterTags) {\n const tagBtn = document.createElement('button');\n tagBtn.className = 'gengage-chat-categories-filter-tag';\n tagBtn.type = 'button';\n tagBtn.textContent = tag.title;\n if (tag.action) {\n tagBtn.addEventListener('click', () => {\n context.onAction(tag.action!);\n });\n }\n tagsContainer.appendChild(tagBtn);\n }\n\n container.appendChild(tagsContainer);\n }\n\n return container;\n}\n\nfunction renderCategoryProductCard(product: NormalizedProduct, ctx: ChatUISpecRenderContext): HTMLElement {\n const card = document.createElement('div');\n card.className = 'gengage-chat-product-card';\n\n if (product.imageUrl && isSafeImageUrl(product.imageUrl)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-product-card-img';\n img.src = product.imageUrl;\n img.alt = product.name;\n img.loading = 'lazy';\n addImageErrorHandler(img);\n card.appendChild(img);\n }\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-product-card-body';\n\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-product-card-name';\n nameEl.textContent = product.name;\n body.appendChild(nameEl);\n\n if (product.price) {\n const priceEl = document.createElement('div');\n priceEl.className = 'gengage-chat-product-card-price';\n priceEl.textContent = formatPrice(product.price, ctx.pricing);\n body.appendChild(priceEl);\n }\n\n card.appendChild(body);\n\n // Click → show product details\n if (ctx.onProductSelect) {\n card.style.cursor = 'pointer';\n card.addEventListener('click', () => {\n ctx.onProductSelect?.(product as unknown as Record<string, unknown>);\n });\n }\n\n return card;\n}\n","/**\n * Renders a handoff notice when the backend escalates to a human agent.\n *\n * XSS safety: All text is set via textContent. No innerHTML.\n */\n\nimport type { ChatUISpecRenderContext } from '../types.js';\n\nexport function renderHandoffNotice(\n element: { props?: Record<string, unknown> },\n context: ChatUISpecRenderContext,\n): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-handoff-notice';\n container.setAttribute('role', 'alert');\n\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-handoff-notice-icon';\n icon.textContent = '\\u{1F464}'; // 👤\n icon.setAttribute('aria-hidden', 'true');\n container.appendChild(icon);\n\n const heading = document.createElement('h4');\n heading.className = 'gengage-chat-handoff-notice-heading';\n heading.textContent = context.i18n?.handoffHeading ?? 'Transferring to a support agent';\n container.appendChild(heading);\n\n const summary = element.props?.['summary'] as string | undefined;\n if (summary) {\n const summaryEl = document.createElement('p');\n summaryEl.className = 'gengage-chat-handoff-notice-summary';\n summaryEl.textContent = summary;\n container.appendChild(summaryEl);\n }\n\n return container;\n}\n","/**\n * Compact horizontal product card for inline chat-pane rendering.\n *\n * Production parity: mirrors the prior engine's `LaunchSingleProduct` component.\n * Renders when `productDetails` arrives — the full ProductDetailsPanel goes\n * to the left panel while this compact summary appears inline in chat messages.\n *\n * Layout: [image 64×64] [name · rating · price] [View link]\n */\n\nimport type { UIElement } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport { isSafeUrl, safeSetAttribute } from '../../common/safe-html.js';\nimport { addImageErrorHandler, createStarRatingElement } from '../../common/product-utils.js';\n\nexport function renderProductSummaryCard(element: UIElement, ctx: ChatUISpecRenderContext): HTMLElement {\n const product = (element.props?.['product'] ?? element.props) as Record<string, unknown> | undefined;\n\n const card = document.createElement('div');\n card.className = 'gengage-chat-product-summary';\n if (!product) return card;\n\n // Make entire card clickable to open product in panel\n card.style.cursor = 'pointer';\n card.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('a')) return;\n ctx.onProductSelect?.(product);\n });\n\n // --- Image (left side) ---\n const imageUrl = product['imageUrl'] as string | undefined;\n if (imageUrl && isSafeUrl(imageUrl)) {\n const imgWrap = document.createElement('div');\n imgWrap.className = 'gengage-chat-product-summary__image';\n const img = document.createElement('img');\n img.loading = 'lazy';\n safeSetAttribute(img, 'src', imageUrl);\n const name = product['name'] as string | undefined;\n img.alt = name || 'Product image';\n addImageErrorHandler(img);\n imgWrap.appendChild(img);\n card.appendChild(imgWrap);\n }\n\n // --- Content (right side) ---\n const content = document.createElement('div');\n content.className = 'gengage-chat-product-summary__content';\n\n // Product name (brand + name)\n const brand = product['brand'] as string | undefined;\n const name = product['name'] as string | undefined;\n if (name) {\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-product-summary__name';\n // Only prepend brand if name doesn't already start with it\n const needsBrand = brand && !name.toLowerCase().startsWith(brand.toLowerCase());\n const fullName = needsBrand ? `${brand} ${name}` : name;\n nameEl.textContent = fullName;\n nameEl.title = fullName;\n content.appendChild(nameEl);\n }\n\n // Rating\n const rating = product['rating'];\n const reviewCount = product['reviewCount'];\n if (typeof rating === 'number' && Number.isFinite(rating) && rating > 0) {\n const ratingRow = document.createElement('div');\n ratingRow.className = 'gengage-chat-product-summary__rating';\n ratingRow.appendChild(createStarRatingElement(rating));\n if (typeof reviewCount === 'number' && Number.isFinite(reviewCount)) {\n const count = document.createElement('span');\n count.className = 'gengage-chat-product-summary__review-count';\n count.textContent = ` (${reviewCount})`;\n ratingRow.appendChild(count);\n }\n content.appendChild(ratingRow);\n }\n\n // Price row\n const price = product['price'] as string | undefined;\n const originalPrice = product['originalPrice'] as string | undefined;\n if (price) {\n const priceRow = document.createElement('div');\n priceRow.className = 'gengage-chat-product-summary__price';\n if (originalPrice && originalPrice !== price) {\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-product-summary__price-original';\n orig.textContent = formatPrice(originalPrice, ctx.pricing);\n priceRow.appendChild(orig);\n priceRow.appendChild(document.createTextNode(' '));\n }\n const current = document.createElement('span');\n current.className = 'gengage-chat-product-summary__price-current';\n current.textContent = formatPrice(price, ctx.pricing);\n priceRow.appendChild(current);\n content.appendChild(priceRow);\n }\n\n card.appendChild(content);\n\n // --- CTA link (right edge) ---\n const url = product['url'] as string | undefined;\n if (url && isSafeUrl(url)) {\n const cta = document.createElement('a');\n cta.className = 'gengage-chat-product-summary__cta';\n safeSetAttribute(cta, 'href', url);\n safeSetAttribute(cta, 'target', '_blank');\n safeSetAttribute(cta, 'rel', 'noopener noreferrer');\n cta.textContent = ctx.i18n?.productCtaLabel ?? 'View';\n card.appendChild(cta);\n }\n\n return card;\n}\n","/**\n * Renders a json-render UISpec into DOM elements.\n *\n * XSS safety: All text is set via textContent. URLs are validated for safe protocols.\n * No innerHTML is used anywhere in this module.\n */\n\nimport type { UISpec, UIElement, ActionPayload } from '../../common/types.js';\nimport { renderUISpecWithRegistry } from '../../common/renderer/index.js';\nimport type { UISpecDomRegistry, UISpecDomUnknownRenderer } from '../../common/renderer/index.js';\nimport type { ChatUISpecRenderContext, ProductSortState } from '../types.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport type { PriceFormatConfig } from '../../common/price-formatter.js';\nimport { renderComparisonTable } from './ComparisonTable.js';\nimport type { ComparisonProduct, ComparisonAttribute } from './ComparisonTable.js';\nimport { renderReviewHighlights as renderReviewHighlightsComponent } from './ReviewHighlights.js';\nimport { renderAITopPicks } from './AITopPicks.js';\nimport { renderGroundingReviewCard } from './GroundingReviewCard.js';\nimport { renderAIGroupingCards } from './AIGroupingCards.js';\nimport { renderAISuggestedSearchCards } from './AISuggestedSearchCards.js';\nimport { renderFloatingComparisonButton } from './FloatingComparisonButton.js';\nimport { renderProsAndCons } from './ProsAndCons.js';\nimport { renderCategoriesContainer } from './CategoriesContainer.js';\nimport { renderHandoffNotice } from './HandoffNotice.js';\nimport { renderProductSummaryCard } from './ProductSummaryCard.js';\nimport { createQuantityStepper } from '../../common/quantity-stepper.js';\nimport { isSafeUrl, safeSetAttribute } from '../../common/safe-html.js';\nimport {\n clampRating,\n clampDiscount,\n addImageErrorHandler,\n createStarRatingElement,\n} from '../../common/product-utils.js';\n\nexport type UISpecRenderContext = ChatUISpecRenderContext;\n\nexport type ChatUISpecRegistry = UISpecDomRegistry<UISpecRenderContext>;\n\nexport type { PriceFormatConfig };\n\n/** @deprecated Use context.isMobile instead. Kept as fallback for custom renderers. */\nfunction isMobileViewport(): boolean {\n return window.innerWidth < 768;\n}\n\nconst DEFAULT_CHAT_UI_SPEC_REGISTRY: ChatUISpecRegistry = {\n ActionButtons: ({ element, context }) => renderActionButtons(element, context),\n ActionButton: ({ element, context }) => renderActionButton(element, context),\n ProductCard: ({ element, context }) => renderProductCard(element, context),\n ProductDetailsPanel: ({ element, context }) => renderProductDetailsPanel(element, context),\n ProductGrid: ({ element, spec, renderElement, context }) => renderProductGrid(element, spec, renderElement, context),\n ReviewHighlights: ({ element, context }) =>\n renderReviewHighlightsComponent(element, {\n emptyReviewsMessage: context.i18n?.emptyReviewsMessage,\n reviewFilterAll: context.i18n?.reviewFilterAll,\n reviewFilterPositive: context.i18n?.reviewFilterPositive,\n reviewFilterNegative: context.i18n?.reviewFilterNegative,\n }),\n ComparisonTable: ({ element, context }) => renderComparisonTableElement(element, context),\n AITopPicks: ({ element, context }) => renderAITopPicks(element, context),\n GroundingReviewCard: ({ element, context }) => renderGroundingReviewCard(element, context),\n AIGroupingCards: ({ element, context }) => renderAIGroupingCards(element, context),\n AISuggestedSearchCards: ({ element, context }) => renderAISuggestedSearchCards(element, context),\n ProsAndCons: ({ element }) => renderProsAndCons(element),\n CategoriesContainer: ({ element, context }) => renderCategoriesContainer(element, context),\n HandoffNotice: ({ element, context }) => renderHandoffNotice(element, context),\n ProductSummaryCard: ({ element, context }) => renderProductSummaryCard(element, context),\n Divider: ({ element }) => renderDivider(element),\n};\n\nexport const defaultChatUnknownUISpecRenderer: UISpecDomUnknownRenderer<UISpecRenderContext> = ({\n element,\n renderElement,\n}) => {\n if (import.meta.env?.DEV) {\n console.warn(`[gengage] Unknown ui_spec component type: ${element.type}`);\n }\n if (!element.children || element.children.length === 0) {\n return null;\n }\n const wrapper = document.createElement('div');\n for (const childId of element.children) {\n const rendered = renderElement(childId);\n if (rendered) wrapper.appendChild(rendered);\n }\n return wrapper;\n};\n\nexport function createDefaultChatUISpecRegistry(): ChatUISpecRegistry {\n return { ...DEFAULT_CHAT_UI_SPEC_REGISTRY };\n}\n\nexport function renderUISpec(\n spec: UISpec,\n ctx: UISpecRenderContext,\n registry = DEFAULT_CHAT_UI_SPEC_REGISTRY,\n unknownRenderer: UISpecDomUnknownRenderer<UISpecRenderContext> = defaultChatUnknownUISpecRenderer,\n): HTMLElement {\n return renderUISpecWithRegistry({\n spec,\n context: ctx,\n registry,\n containerClassName: 'gengage-chat-uispec',\n unknownRenderer,\n });\n}\n\nfunction renderActionButtons(element: UIElement, ctx: UISpecRenderContext): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-action-buttons';\n\n const buttons = element.props?.['buttons'] as Array<{ label: string; action: ActionPayload }> | undefined;\n\n if (buttons) {\n for (const btn of buttons) {\n const button = document.createElement('button');\n button.className = 'gengage-chat-action-btn';\n button.textContent = btn.label;\n button.addEventListener('click', () => ctx.onAction(btn.action));\n container.appendChild(button);\n }\n }\n\n return container;\n}\n\nfunction renderActionButton(element: UIElement, ctx: UISpecRenderContext): HTMLElement {\n const button = document.createElement('button');\n button.className = 'gengage-chat-action-btn';\n const label = element.props?.['label'];\n if (typeof label === 'string') button.textContent = label;\n const action = element.props?.['action'] as ActionPayload | undefined;\n if (action) {\n button.addEventListener('click', () => ctx.onAction(action));\n }\n return button;\n}\n\nfunction renderProductCard(element: UIElement, ctx: UISpecRenderContext): HTMLElement {\n const card = document.createElement('div');\n card.className = 'gengage-chat-product-card';\n\n // Product data may be nested under `product` prop (adapter) or flat in props\n const product = (element.props?.['product'] ?? element.props) as Record<string, unknown> | undefined;\n if (!product) return card;\n\n // Store SKU as data attribute for comparison mode DOM refresh\n const productSku = product['sku'] as string | undefined;\n if (productSku) card.dataset['sku'] = productSku;\n\n // Make card clickable to show detail in panel (disabled in comparison select mode)\n if (ctx.onProductSelect) {\n card.style.cursor = 'pointer';\n card.addEventListener('click', (e) => {\n // Check live DOM: if card is inside a comparison wrapper, mode is active\n if (card.parentElement?.classList.contains('gengage-chat-comparison-select-wrapper')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-product-card-atc')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-product-card-cta')) return;\n ctx.onProductSelect?.(product);\n });\n }\n\n const imageUrl = product['imageUrl'] as string | undefined;\n if (imageUrl && isSafeUrl(imageUrl)) {\n const imgWrapper = document.createElement('div');\n imgWrapper.className = 'gengage-chat-product-card-img-wrapper';\n\n const img = document.createElement('img');\n img.className = 'gengage-chat-product-card-img';\n img.loading = 'lazy';\n safeSetAttribute(img, 'src', imageUrl);\n const name = product['name'] as string | undefined;\n if (name) img.alt = name;\n addImageErrorHandler(img);\n imgWrapper.appendChild(img);\n\n // Discount badge (top-left of image)\n const discountPercent = product['discountPercent'] as number | undefined;\n if (typeof discountPercent === 'number' && discountPercent > 0) {\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-product-card-discount-badge';\n badge.textContent = `%${clampDiscount(discountPercent)}`;\n imgWrapper.appendChild(badge);\n }\n\n // \"Find Similar\" hover pill on image\n const findSimilarSku = product['sku'] as string | undefined;\n if (findSimilarSku) {\n const pill = document.createElement('button');\n pill.className = 'gengage-chat-find-similar-pill';\n pill.type = 'button';\n pill.textContent = ctx.i18n?.findSimilarLabel ?? 'Find Similar';\n pill.addEventListener('click', (e) => {\n e.stopPropagation();\n ctx.onAction({\n title: ctx.i18n?.findSimilarLabel ?? 'Find Similar',\n type: 'findSimilar',\n payload: { sku: findSimilarSku, ...(imageUrl ? { image_url: imageUrl } : {}) },\n });\n });\n imgWrapper.appendChild(pill);\n }\n\n // Favorite heart toggle\n const favSku = product['sku'] as string | undefined;\n if (favSku && ctx.onFavoriteToggle) {\n const heart = document.createElement('button');\n heart.className = 'gengage-chat-favorite-btn';\n heart.type = 'button';\n heart.setAttribute('aria-label', ctx.i18n?.addToFavoritesLabel ?? 'Add to favorites');\n const isFav = ctx.favoritedSkus?.has(favSku) ?? false;\n if (isFav) heart.classList.add('gengage-chat-favorite-btn--active');\n const svgFill = isFav ? 'currentColor' : 'none';\n heart.innerHTML = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"${svgFill}\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z\"/></svg>`;\n heart.addEventListener('click', (e) => {\n e.stopPropagation();\n heart.classList.toggle('gengage-chat-favorite-btn--active');\n const svg = heart.querySelector('svg');\n if (svg) {\n svg.setAttribute(\n 'fill',\n heart.classList.contains('gengage-chat-favorite-btn--active') ? 'currentColor' : 'none',\n );\n }\n ctx.onFavoriteToggle!(favSku, product);\n });\n imgWrapper.appendChild(heart);\n }\n\n card.appendChild(imgWrapper);\n }\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-product-card-body';\n\n const brand = product['brand'] as string | undefined;\n if (brand) {\n const brandEl = document.createElement('div');\n brandEl.className = 'gengage-chat-product-card-brand';\n brandEl.textContent = brand;\n body.appendChild(brandEl);\n }\n\n const name = product['name'] as string | undefined;\n if (name) {\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-product-card-name';\n nameEl.textContent = name;\n nameEl.title = name;\n body.appendChild(nameEl);\n }\n\n const rating = product['rating'];\n const reviewCount = product['reviewCount'];\n if (typeof rating === 'number' && Number.isFinite(rating) && rating > 0) {\n const ratingRow = document.createElement('div');\n ratingRow.className = 'gengage-chat-product-card-rating';\n ratingRow.appendChild(createStarRatingElement(rating));\n if (typeof reviewCount === 'number' && Number.isFinite(reviewCount)) {\n const count = document.createElement('span');\n count.className = 'gengage-chat-product-card-review-count';\n count.textContent = ` (${reviewCount})`;\n ratingRow.appendChild(count);\n }\n body.appendChild(ratingRow);\n }\n\n const price = product['price'] as string | undefined;\n const originalPrice = product['originalPrice'] as string | undefined;\n const priceAsync = product['price_async'] as boolean | undefined;\n\n if (priceAsync === true) {\n const priceRow = document.createElement('div');\n priceRow.className = 'gengage-chat-product-card-price';\n const skeleton = document.createElement('span');\n skeleton.className = 'gengage-chat-price-skeleton';\n priceRow.appendChild(skeleton);\n body.appendChild(priceRow);\n // Replace skeleton with actual price after delay\n setTimeout(() => {\n if (!skeleton.parentElement) return; // Element removed from DOM\n if (price && parseFloat(price) > 0) {\n skeleton.replaceWith(document.createTextNode(formatPrice(price, ctx.pricing)));\n } else {\n skeleton.remove();\n }\n }, 300);\n } else if (price && parseFloat(price) > 0) {\n const priceRow = document.createElement('div');\n priceRow.className = 'gengage-chat-product-card-price';\n if (originalPrice && originalPrice !== price) {\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-product-card-original-price';\n orig.textContent = formatPrice(originalPrice, ctx.pricing);\n priceRow.appendChild(orig);\n priceRow.appendChild(document.createTextNode(' '));\n }\n const current = document.createElement('span');\n current.textContent = formatPrice(price, ctx.pricing);\n priceRow.appendChild(current);\n body.appendChild(priceRow);\n }\n\n // Stock indicator\n const inStock = product['inStock'];\n if (typeof inStock === 'boolean') {\n const stock = document.createElement('div');\n stock.className = `gengage-chat-product-card-stock ${inStock ? 'is-in-stock' : 'is-out-of-stock'}`;\n stock.textContent = inStock\n ? (ctx.i18n?.inStockLabel ?? 'In Stock')\n : (ctx.i18n?.outOfStockLabel ?? 'Out of Stock');\n body.appendChild(stock);\n }\n\n // Promotion badges (e.g. \"Free Shipping\", \"Flash Sale\") — max 3\n const promotions = product['promotions'] as string[] | undefined;\n if (promotions && promotions.length > 0) {\n const promoBadges = document.createElement('div');\n promoBadges.className = 'gengage-chat-product-card-promos';\n for (const promo of promotions.slice(0, 3)) {\n if (!promo || /%(0(\\.0+)?)\\s/.test(promo)) continue; // skip zero-value badges\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-product-card-promo-badge';\n badge.textContent = promo;\n badge.title = promo;\n promoBadges.appendChild(badge);\n }\n if (promoBadges.childElementCount > 0) body.appendChild(promoBadges);\n }\n\n card.appendChild(body);\n\n const url = product['url'] as string | undefined;\n const sku = product['sku'] as string | undefined;\n const action = element.props?.['action'] as ActionPayload | undefined;\n\n if (action) {\n const cta = document.createElement('button');\n cta.className = 'gengage-chat-product-card-cta';\n cta.type = 'button';\n cta.textContent = action.title || ctx.i18n?.productCtaLabel || 'View';\n cta.addEventListener('click', (e) => {\n if (card.parentElement?.classList.contains('gengage-chat-comparison-select-wrapper')) {\n e.stopPropagation();\n return;\n }\n ctx.onAction(action);\n });\n card.appendChild(cta);\n } else if (url && isSafeUrl(url)) {\n const cta = document.createElement('a');\n cta.className = 'gengage-chat-product-card-cta';\n safeSetAttribute(cta, 'href', url);\n safeSetAttribute(cta, 'target', '_blank');\n safeSetAttribute(cta, 'rel', 'noopener noreferrer');\n cta.textContent = ctx.i18n?.productCtaLabel ?? 'View';\n cta.addEventListener('click', (e) => {\n if (card.parentElement?.classList.contains('gengage-chat-comparison-select-wrapper')) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n if (ctx.onProductClick && sku) {\n e.preventDefault();\n ctx.onProductClick({ sku, url });\n }\n });\n card.appendChild(cta);\n }\n\n // Add to cart stepper (shown on search result cards when cartCode is available and in stock)\n const cartCode = product['cartCode'] as string | undefined;\n if (cartCode && sku && inStock !== false) {\n const stepper = createQuantityStepper({\n compact: true,\n label: ctx.i18n?.addToCartButton ?? 'Add to Cart',\n decreaseLabel: ctx.i18n?.decreaseLabel,\n increaseLabel: ctx.i18n?.increaseLabel,\n onSubmit: (quantity) => {\n ctx.onAction({\n title: ctx.i18n?.addToCartButton ?? 'Add to Cart',\n type: 'addToCart',\n payload: { sku, cartCode, quantity },\n });\n },\n });\n stepper.classList.add('gengage-chat-product-card-atc');\n card.appendChild(stepper);\n }\n\n // Wrap with checkbox overlay when comparison select mode is active\n if (ctx.comparisonSelectMode && sku && ctx.onToggleComparisonSku) {\n const wrapper = document.createElement('div');\n wrapper.className = 'gengage-chat-comparison-select-wrapper';\n\n const checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.className = 'gengage-chat-comparison-checkbox';\n checkbox.checked = ctx.comparisonSelectedSkus?.includes(sku) ?? false;\n checkbox.addEventListener('change', () => {\n ctx.onToggleComparisonSku?.(sku);\n });\n\n // Clicking anywhere on the card toggles comparison selection — no product detail navigation.\n // Do NOT manually flip checkbox.checked here: onToggleComparisonSku triggers\n // _refreshComparisonUI which syncs checkbox state from the canonical Set.\n wrapper.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.gengage-chat-comparison-checkbox')) return;\n e.stopPropagation();\n ctx.onToggleComparisonSku?.(sku);\n });\n\n wrapper.appendChild(checkbox);\n wrapper.appendChild(card);\n return wrapper;\n }\n\n return card;\n}\n\n/* clampRating, clampDiscount, addImageErrorHandler, renderStarRating\n are imported from ../../common/product-utils.js */\n\nfunction renderProductDetailsPanel(element: UIElement, ctx: UISpecRenderContext): HTMLElement {\n const panel = document.createElement('article');\n panel.className = 'gengage-chat-product-details-panel';\n\n const product = (element.props?.['product'] ?? element.props) as Record<string, unknown> | undefined;\n if (!product) return panel;\n\n // Image gallery or single image\n const images = product['images'] as string[] | undefined;\n const imageUrl = product['imageUrl'] as string | undefined;\n\n const detailsSku = product['sku'] as string | undefined;\n\n if (images && images.length > 1) {\n // Gallery with thumbnails\n const media = document.createElement('div');\n media.className =\n 'gengage-chat-product-details-media gengage-chat-product-details-gallery gengage-chat-product-details-img-wrap';\n\n const mainImg = document.createElement('img');\n mainImg.className = 'gengage-chat-product-details-img';\n const firstSafe = images.find((u) => isSafeUrl(u));\n if (firstSafe) safeSetAttribute(mainImg, 'src', firstSafe);\n const name = product['name'] as string | undefined;\n if (name) mainImg.alt = name;\n addImageErrorHandler(mainImg);\n media.appendChild(mainImg);\n\n const thumbStrip = document.createElement('div');\n thumbStrip.className = 'gengage-chat-product-gallery-thumbs';\n\n const MAX_VISIBLE_THUMBNAILS = 6;\n const safeImages = images.filter((u): u is string => !!u && isSafeUrl(u));\n let activeThumb: HTMLElement | null = null;\n let activeThumbIdx = 0;\n for (let i = 0; i < safeImages.length; i++) {\n const imgUrl = safeImages[i]!;\n if (i >= MAX_VISIBLE_THUMBNAILS) break;\n const thumb = document.createElement('img');\n thumb.className = 'gengage-chat-product-gallery-thumb';\n if (i === 0) {\n thumb.classList.add('gengage-chat-product-gallery-thumb--active');\n activeThumb = thumb;\n }\n safeSetAttribute(thumb, 'src', imgUrl);\n thumb.alt = `${name ?? 'Product'} ${i + 1}`;\n thumb.width = 48;\n thumb.height = 48;\n addImageErrorHandler(thumb);\n thumb.addEventListener('click', () => {\n safeSetAttribute(mainImg, 'src', imgUrl);\n if (activeThumb) activeThumb.classList.remove('gengage-chat-product-gallery-thumb--active');\n thumb.classList.add('gengage-chat-product-gallery-thumb--active');\n activeThumb = thumb;\n activeThumbIdx = i;\n });\n thumbStrip.appendChild(thumb);\n }\n\n // \"+N more\" indicator when thumbnails exceed limit\n if (safeImages.length > MAX_VISIBLE_THUMBNAILS) {\n const more = document.createElement('span');\n more.className = 'gengage-chat-product-gallery-thumb-more';\n more.textContent = `+${safeImages.length - MAX_VISIBLE_THUMBNAILS}`;\n thumbStrip.appendChild(more);\n }\n\n // Touch swipe gesture for gallery navigation\n let touchStartX = 0;\n const SWIPE_THRESHOLD = 50;\n\n mainImg.addEventListener(\n 'touchstart',\n (e: TouchEvent) => {\n touchStartX = e.changedTouches[0]!.clientX;\n },\n { passive: true },\n );\n\n mainImg.addEventListener('touchend', (e: TouchEvent) => {\n const touchEndX = e.changedTouches[0]!.clientX;\n const diff = touchStartX - touchEndX;\n if (Math.abs(diff) < SWIPE_THRESHOLD) return;\n\n const nextIdx =\n diff > 0\n ? Math.min(activeThumbIdx + 1, safeImages.length - 1) // swipe left → next\n : Math.max(activeThumbIdx - 1, 0); // swipe right → prev\n\n if (nextIdx !== activeThumbIdx) {\n const nextUrl = safeImages[nextIdx];\n if (nextUrl) {\n safeSetAttribute(mainImg, 'src', nextUrl);\n // Update active thumb highlight if within visible range\n const thumbEls = thumbStrip.querySelectorAll('.gengage-chat-product-gallery-thumb');\n if (activeThumb) activeThumb.classList.remove('gengage-chat-product-gallery-thumb--active');\n if (nextIdx < MAX_VISIBLE_THUMBNAILS && thumbEls[nextIdx]) {\n (thumbEls[nextIdx] as HTMLElement).classList.add('gengage-chat-product-gallery-thumb--active');\n activeThumb = thumbEls[nextIdx] as HTMLElement;\n } else {\n activeThumb = null;\n }\n activeThumbIdx = nextIdx;\n }\n }\n });\n\n media.appendChild(thumbStrip);\n\n // \"Find Similar\" hover pill on gallery image\n if (detailsSku) {\n const pill = document.createElement('button');\n pill.className = 'gengage-chat-find-similar-pill';\n pill.type = 'button';\n pill.textContent = ctx.i18n?.findSimilarLabel ?? 'Find Similar';\n pill.addEventListener('click', () => {\n ctx.onAction({\n title: ctx.i18n?.findSimilarLabel ?? 'Find Similar',\n type: 'findSimilar',\n payload: { sku: detailsSku, ...(firstSafe ? { image_url: firstSafe } : {}) },\n });\n });\n media.appendChild(pill);\n }\n\n panel.appendChild(media);\n } else if (imageUrl && isSafeUrl(imageUrl)) {\n // Single image fallback\n const media = document.createElement('div');\n media.className = 'gengage-chat-product-details-media gengage-chat-product-details-img-wrap';\n const img = document.createElement('img');\n img.className = 'gengage-chat-product-details-img';\n img.loading = 'lazy';\n safeSetAttribute(img, 'src', imageUrl);\n addImageErrorHandler(img);\n const name = product['name'] as string | undefined;\n if (name) img.alt = name;\n media.appendChild(img);\n\n // \"Find Similar\" hover pill on single image\n if (detailsSku) {\n const pill = document.createElement('button');\n pill.className = 'gengage-chat-find-similar-pill';\n pill.type = 'button';\n pill.textContent = ctx.i18n?.findSimilarLabel ?? 'Find Similar';\n pill.addEventListener('click', () => {\n ctx.onAction({\n title: ctx.i18n?.findSimilarLabel ?? 'Find Similar',\n type: 'findSimilar',\n payload: { sku: detailsSku, ...(imageUrl ? { image_url: imageUrl } : {}) },\n });\n });\n media.appendChild(pill);\n }\n\n panel.appendChild(media);\n }\n\n const content = document.createElement('div');\n content.className = 'gengage-chat-product-details-content';\n\n const name = product['name'] as string | undefined;\n if (name) {\n const title = document.createElement('h3');\n title.className = 'gengage-chat-product-details-title';\n title.textContent = name;\n title.title = name;\n content.appendChild(title);\n }\n\n const rating = product['rating'];\n const reviewCount = product['reviewCount'];\n if (typeof rating === 'number' && Number.isFinite(rating) && rating > 0) {\n const ratingRow = document.createElement('div');\n ratingRow.className = 'gengage-chat-product-details-rating';\n ratingRow.textContent = `\\u2605 ${clampRating(rating).toFixed(1)}`;\n if (typeof reviewCount === 'number' && Number.isFinite(reviewCount)) {\n const count = document.createElement('span');\n count.className = 'gengage-chat-product-details-review-count';\n count.textContent = ` (${reviewCount})`;\n ratingRow.appendChild(count);\n }\n content.appendChild(ratingRow);\n }\n\n const price = product['price'] as string | undefined;\n const originalPrice = product['originalPrice'] as string | undefined;\n const priceAsync = product['price_async'] as boolean | undefined;\n\n if (priceAsync === true) {\n const priceRow = document.createElement('div');\n priceRow.className = 'gengage-chat-product-details-price';\n const skeleton = document.createElement('span');\n skeleton.className = 'gengage-chat-price-skeleton';\n priceRow.appendChild(skeleton);\n content.appendChild(priceRow);\n // Replace skeleton with actual price after delay\n setTimeout(() => {\n if (!skeleton.parentElement) return; // Element removed from DOM\n if (price && parseFloat(price) > 0) {\n const currentPrice = document.createElement('span');\n currentPrice.className = 'gengage-chat-product-details-current-price';\n currentPrice.textContent = formatPrice(price, ctx.pricing);\n skeleton.replaceWith(currentPrice);\n } else {\n skeleton.remove();\n }\n }, 300);\n } else if (price && parseFloat(price) > 0) {\n const priceRow = document.createElement('div');\n priceRow.className = 'gengage-chat-product-details-price';\n if (originalPrice && originalPrice !== price) {\n const oldPrice = document.createElement('span');\n oldPrice.className = 'gengage-chat-product-details-original-price';\n oldPrice.textContent = formatPrice(originalPrice, ctx.pricing);\n priceRow.appendChild(oldPrice);\n priceRow.appendChild(document.createTextNode(' '));\n }\n const currentPrice = document.createElement('span');\n currentPrice.className = 'gengage-chat-product-details-current-price';\n currentPrice.textContent = formatPrice(price, ctx.pricing);\n priceRow.appendChild(currentPrice);\n content.appendChild(priceRow);\n }\n\n const inStock = product['inStock'];\n if (typeof inStock === 'boolean') {\n const stock = document.createElement('div');\n stock.className = `gengage-chat-product-details-stock ${inStock ? 'is-in-stock' : 'is-out-of-stock'}`;\n stock.textContent = inStock\n ? (ctx.i18n?.inStockLabel ?? 'In Stock')\n : (ctx.i18n?.outOfStockLabel ?? 'Out of Stock');\n content.appendChild(stock);\n }\n\n // Promotion badges (e.g. \"Free Shipping\", \"Flash Sale\") — max 3\n const promotions = product['promotions'] as string[] | undefined;\n if (promotions && promotions.length > 0) {\n const promoBadges = document.createElement('div');\n promoBadges.className = 'gengage-chat-product-details-promos';\n for (const promo of promotions.slice(0, 3)) {\n if (!promo || /%(0(\\.0+)?)\\s/.test(promo)) continue; // skip zero-value badges\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-product-details-promo-badge';\n badge.textContent = promo;\n badge.title = promo;\n promoBadges.appendChild(badge);\n }\n if (promoBadges.childElementCount > 0) content.appendChild(promoBadges);\n }\n\n // Variant selector\n const variants = product['variants'] as Array<Record<string, unknown>> | undefined;\n if (variants && variants.length > 0) {\n const variantSection = document.createElement('div');\n variantSection.className = 'gengage-chat-product-variants';\n\n const variantLabel = document.createElement('div');\n variantLabel.className = 'gengage-chat-product-variants-label';\n variantLabel.textContent = ctx.i18n?.variantsLabel ?? 'Variants';\n variantSection.appendChild(variantLabel);\n\n const variantList = document.createElement('div');\n variantList.className = 'gengage-chat-product-variants-list';\n\n for (const variant of variants) {\n const variantValue = variant['value'] as string | undefined;\n const variantName =\n variantValue ?? (variant['name'] as string | undefined) ?? (variant['variant_name'] as string | undefined);\n const variantSku = variant['sku'] as string | undefined;\n if (!variantName && !variantSku) continue;\n\n const btn = document.createElement('button');\n btn.className = 'gengage-chat-product-variant-btn';\n btn.type = 'button';\n\n const labelText = variantName ?? variantSku ?? '';\n const variantPrice = variant['price'] as number | string | undefined;\n if (variantPrice && String(variantPrice) !== String(price)) {\n btn.textContent = `${labelText} - ${formatPrice(String(variantPrice), ctx.pricing)}`;\n } else {\n btn.textContent = labelText;\n }\n\n if (variantSku) {\n btn.addEventListener('click', () => {\n ctx.onAction({\n title: labelText,\n type: 'launchVariant',\n payload: { sku: variantSku },\n });\n });\n }\n variantList.appendChild(btn);\n }\n\n variantSection.appendChild(variantList);\n content.appendChild(variantSection);\n }\n\n const sku = product['sku'] as string | undefined;\n const cartCode = product['cartCode'] as string | undefined;\n\n const actionRow = document.createElement('div');\n actionRow.className = 'gengage-chat-product-details-actions';\n\n const action = element.props?.['action'] as ActionPayload | undefined;\n if (action) {\n const actionBtn = document.createElement('button');\n actionBtn.className = 'gengage-chat-product-details-cta';\n actionBtn.type = 'button';\n actionBtn.textContent = action.title || ctx.i18n?.productCtaLabel || 'View';\n actionBtn.addEventListener('click', () => ctx.onAction(action));\n actionRow.appendChild(actionBtn);\n } else {\n const url = product['url'] as string | undefined;\n if (url && isSafeUrl(url)) {\n const cta = document.createElement('a');\n cta.className = 'gengage-chat-product-details-cta';\n safeSetAttribute(cta, 'href', url);\n safeSetAttribute(cta, 'target', '_blank');\n safeSetAttribute(cta, 'rel', 'noopener noreferrer');\n cta.textContent = ctx.i18n?.productCtaLabel ?? 'View';\n cta.addEventListener('click', (e) => {\n if (ctx.onProductClick && sku) {\n e.preventDefault();\n ctx.onProductClick({ sku, url });\n }\n });\n actionRow.appendChild(cta);\n }\n }\n\n // Add to Cart stepper — shown when the product has a cartCode and is in stock\n if (cartCode && sku && inStock !== false) {\n const stepper = createQuantityStepper({\n compact: false,\n label: ctx.i18n?.addToCartButton ?? 'Add to Cart',\n decreaseLabel: ctx.i18n?.decreaseLabel,\n increaseLabel: ctx.i18n?.increaseLabel,\n onSubmit: (quantity) => {\n ctx.onAction({\n title: ctx.i18n?.addToCartButton ?? 'Add to Cart',\n type: 'addToCart',\n payload: { sku, cartCode, quantity },\n });\n },\n });\n stepper.classList.add('gengage-chat-product-details-atc-stepper');\n actionRow.appendChild(stepper);\n }\n\n // Share button — copies product URL or triggers native share\n const shareUrl = product['url'] as string | undefined;\n if (shareUrl && isSafeUrl(shareUrl)) {\n const shareBtn = document.createElement('button');\n shareBtn.className = 'gengage-chat-product-details-share';\n shareBtn.type = 'button';\n const shareLabel = ctx.i18n?.shareButton ?? 'Share';\n shareBtn.title = shareLabel;\n shareBtn.setAttribute('aria-label', shareLabel);\n const svgNS = 'http://www.w3.org/2000/svg';\n const svg = document.createElementNS(svgNS, 'svg');\n svg.setAttribute('width', '18');\n svg.setAttribute('height', '18');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('fill', 'none');\n svg.setAttribute('stroke', 'currentColor');\n svg.setAttribute('stroke-width', '2');\n svg.setAttribute('stroke-linecap', 'round');\n svg.setAttribute('stroke-linejoin', 'round');\n function addCircle(cx: string, cy: string): void {\n const c = document.createElementNS(svgNS, 'circle');\n c.setAttribute('cx', cx);\n c.setAttribute('cy', cy);\n c.setAttribute('r', '3');\n svg.appendChild(c);\n }\n function addLine(x1: string, y1: string, x2: string, y2: string): void {\n const l = document.createElementNS(svgNS, 'line');\n l.setAttribute('x1', x1);\n l.setAttribute('y1', y1);\n l.setAttribute('x2', x2);\n l.setAttribute('y2', y2);\n svg.appendChild(l);\n }\n addCircle('18', '5');\n addCircle('6', '12');\n addCircle('18', '19');\n addLine('8.59', '13.51', '15.42', '17.49');\n addLine('15.41', '6.51', '8.59', '10.49');\n shareBtn.appendChild(svg);\n shareBtn.addEventListener('click', async () => {\n const productName = product['name'] as string | undefined;\n try {\n if (navigator.share) {\n await navigator.share({ title: productName ?? '', url: shareUrl });\n } else if (navigator.clipboard) {\n await navigator.clipboard.writeText(shareUrl);\n shareBtn.classList.add('gengage-chat-product-details-share--copied');\n setTimeout(() => shareBtn.classList.remove('gengage-chat-product-details-share--copied'), 1500);\n }\n } catch {\n // Share cancelled or clipboard write denied — ignore\n }\n });\n actionRow.appendChild(shareBtn);\n }\n\n if (actionRow.childElementCount > 0) {\n content.appendChild(actionRow);\n }\n\n panel.appendChild(content);\n\n // Product detail tabs: \"Product Info\" / \"Specifications\"\n const description = product['description'] as string | undefined;\n const specifications = product['specifications'] as\n | Record<string, string>\n | Array<{ key: string; value: string }>\n | undefined;\n if (description || specifications) {\n panel.appendChild(renderProductDetailTabs(description, specifications, ctx));\n }\n\n return panel;\n}\n\nfunction renderProductDetailTabs(\n description: string | undefined,\n specifications: Record<string, string> | Array<{ key: string; value: string }> | undefined,\n ctx: UISpecRenderContext,\n): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-product-detail-tabs';\n\n const tabBar = document.createElement('div');\n tabBar.className = 'gengage-chat-product-detail-tab-bar';\n\n const tabPanels: HTMLElement[] = [];\n\n // Product Info tab\n if (description) {\n const tab = document.createElement('button');\n tab.className = 'gengage-chat-product-detail-tab gengage-chat-product-detail-tab--active';\n tab.type = 'button';\n tab.textContent = ctx.i18n?.productInfoTab ?? 'Product Info';\n tabBar.appendChild(tab);\n\n const panel = document.createElement('div');\n panel.className = 'gengage-chat-product-detail-tab-panel';\n panel.textContent = description;\n tabPanels.push(panel);\n }\n\n // Specifications tab\n if (specifications) {\n const tab = document.createElement('button');\n tab.className = `gengage-chat-product-detail-tab${!description ? ' gengage-chat-product-detail-tab--active' : ''}`;\n tab.type = 'button';\n tab.textContent = ctx.i18n?.specificationsTab ?? 'Specifications';\n tabBar.appendChild(tab);\n\n const panel = document.createElement('div');\n panel.className = 'gengage-chat-product-detail-tab-panel';\n if (!description) {\n // Show specs by default if no description\n } else {\n panel.style.display = 'none';\n }\n\n const table = document.createElement('table');\n table.className = 'gengage-chat-product-specs-table';\n const entries = Array.isArray(specifications)\n ? specifications\n : Object.entries(specifications).map(([key, value]) => ({ key, value }));\n for (const entry of entries) {\n const row = document.createElement('tr');\n const keyCell = document.createElement('td');\n keyCell.className = 'gengage-chat-product-specs-key';\n keyCell.textContent = entry.key;\n const valCell = document.createElement('td');\n valCell.className = 'gengage-chat-product-specs-value';\n valCell.textContent = entry.value;\n row.appendChild(keyCell);\n row.appendChild(valCell);\n table.appendChild(row);\n }\n panel.appendChild(table);\n tabPanels.push(panel);\n }\n\n // Wire up tab switching\n const tabs = tabBar.querySelectorAll('.gengage-chat-product-detail-tab');\n tabs.forEach((tabEl, idx) => {\n tabEl.addEventListener('click', () => {\n tabs.forEach((t) => t.classList.remove('gengage-chat-product-detail-tab--active'));\n tabEl.classList.add('gengage-chat-product-detail-tab--active');\n tabPanels.forEach((p, pIdx) => {\n p.style.display = pIdx === idx ? '' : 'none';\n });\n });\n });\n\n container.appendChild(tabBar);\n for (const p of tabPanels) container.appendChild(p);\n return container;\n}\n\nfunction getSortedChildIds(childIds: string[], spec: UISpec, sort?: ProductSortState): string[] {\n if (!sort || sort.type === 'related') return childIds;\n\n const withPrice = childIds.map((id) => {\n const el = spec.elements[id];\n const product = el?.props?.['product'] as Record<string, unknown> | undefined;\n const price = product ? Number(product['price']) : NaN;\n return { id, price: Number.isFinite(price) ? price : Infinity };\n });\n\n withPrice.sort((a, b) => {\n if (a.price === Infinity && b.price === Infinity) return 0;\n if (a.price === Infinity) return 1;\n if (b.price === Infinity) return -1;\n return sort.direction === 'desc' ? b.price - a.price : a.price - b.price;\n });\n\n return withPrice.map((x) => x.id);\n}\n\nfunction resortGrid(grid: HTMLElement, childIds: string[], spec: UISpec, sort: ProductSortState): void {\n const sorted = getSortedChildIds(childIds, spec, sort);\n // Build map from element ID data attribute to DOM element\n const childMap = new Map<string, HTMLElement>();\n for (const child of Array.from(grid.children) as HTMLElement[]) {\n const elId = child.dataset['elementId'];\n if (elId) childMap.set(elId, child);\n }\n\n for (const id of sorted) {\n const el = childMap.get(id);\n if (el) grid.appendChild(el);\n }\n}\n\nfunction renderProductGrid(\n element: UIElement,\n spec: UISpec,\n renderElement: (elementId: string) => HTMLElement | null,\n ctx?: UISpecRenderContext,\n): HTMLElement {\n const wrapper = document.createElement('div');\n wrapper.className = 'gengage-chat-product-grid-wrapper';\n\n const childIds = element.children ?? [];\n\n // Sort toolbar (only when >1 children and context has sort support)\n if (childIds.length > 1 && ctx?.onSortChange) {\n const toolbar = document.createElement('div');\n toolbar.className = 'gengage-chat-product-sort-toolbar';\n\n const sort = ctx.productSort ?? { type: 'related' as const };\n\n const buttons: Array<{ label: string; sortState: ProductSortState }> = [\n { label: ctx.i18n?.sortRelated ?? 'Related', sortState: { type: 'related' } },\n { label: ctx.i18n?.sortPriceAsc ?? 'Price ↑', sortState: { type: 'price', direction: 'asc' } },\n { label: ctx.i18n?.sortPriceDesc ?? 'Price ↓', sortState: { type: 'price', direction: 'desc' } },\n ];\n\n for (const btn of buttons) {\n const button = document.createElement('button');\n button.className = 'gengage-chat-product-sort-btn';\n button.type = 'button';\n const isActive = sort.type === btn.sortState.type && sort.direction === btn.sortState.direction;\n if (isActive) button.classList.add('gengage-chat-product-sort-btn--active');\n button.textContent = btn.label;\n button.addEventListener('click', () => {\n ctx.onSortChange?.(btn.sortState);\n resortGrid(grid, childIds, spec, btn.sortState);\n toolbar\n .querySelectorAll('.gengage-chat-product-sort-btn')\n .forEach((b) => b.classList.remove('gengage-chat-product-sort-btn--active'));\n button.classList.add('gengage-chat-product-sort-btn--active');\n });\n toolbar.appendChild(button);\n }\n\n // Comparison toggle button (only if onToggleComparisonSku is provided)\n if (ctx.onToggleComparisonSku) {\n const separator = document.createElement('div');\n separator.className = 'gengage-chat-product-sort-separator';\n toolbar.appendChild(separator);\n\n const compareBtn = document.createElement('button');\n compareBtn.className = 'gengage-chat-comparison-toggle-btn';\n compareBtn.type = 'button';\n if (ctx.comparisonSelectMode) {\n compareBtn.classList.add('gengage-chat-comparison-toggle-btn--active');\n }\n // Hide compare button during streaming — revealed on stream end with fade-in\n if (ctx.isStreaming) {\n compareBtn.classList.add('gengage-chat-comparison-toggle-btn--hidden');\n }\n compareBtn.textContent = ctx.i18n?.compareSelected ?? 'Compare';\n compareBtn.addEventListener('click', () => {\n // Toggle is handled by the parent — dispatched via onToggleComparisonSku with empty string\n // to signal mode toggle (convention: empty sku = toggle mode)\n ctx.onToggleComparisonSku?.('');\n });\n toolbar.appendChild(compareBtn);\n }\n\n wrapper.appendChild(toolbar);\n }\n\n const grid = document.createElement('div');\n grid.className = 'gengage-chat-product-grid';\n\n const sortedIds = getSortedChildIds(childIds, spec, ctx?.productSort);\n for (const childId of sortedIds) {\n if (!spec.elements[childId]) continue;\n const rendered = renderElement(childId);\n if (rendered) {\n rendered.dataset['elementId'] = childId;\n grid.appendChild(rendered);\n }\n }\n\n // Mobile variant: horizontal scroll\n if (ctx?.isMobile ?? isMobileViewport()) {\n grid.classList.add('gengage-chat-product-grid--mobile');\n }\n\n wrapper.appendChild(grid);\n\n // \"View More\" button (only when endOfList is not true)\n const endOfList = element.props?.['endOfList'] as boolean | undefined;\n if (endOfList !== true && childIds.length > 0) {\n const viewMoreBtn = document.createElement('button');\n viewMoreBtn.className = 'gengage-chat-product-grid-view-more';\n viewMoreBtn.type = 'button';\n viewMoreBtn.textContent = ctx?.i18n?.viewMoreLabel ?? 'Show More';\n viewMoreBtn.addEventListener('click', () => {\n ctx?.onAction({ title: 'More', type: 'moreProductList', payload: {} });\n });\n wrapper.appendChild(viewMoreBtn);\n }\n\n // Floating comparison button (visible when 2+ products are selected)\n if (ctx?.comparisonSelectMode && ctx.comparisonSelectedSkus && ctx.comparisonSelectedSkus.length >= 2) {\n const floatingBtn = renderFloatingComparisonButton(ctx.comparisonSelectedSkus, ctx);\n wrapper.appendChild(floatingBtn);\n }\n\n return wrapper;\n}\n\nfunction renderComparisonTableElement(element: UIElement, ctx: UISpecRenderContext): HTMLElement {\n const props = element.props ?? {};\n const keyDifferencesHtml = props['keyDifferencesHtml'] as string | undefined;\n const recommended = props['recommended'] as ComparisonProduct | undefined;\n const products = (props['products'] as ComparisonProduct[] | undefined) ?? [];\n const attributes = (props['attributes'] as ComparisonAttribute[] | undefined) ?? [];\n const highlights = (props['highlights'] as string[] | undefined) ?? [];\n const specialCases = props['specialCases'] as string[] | undefined;\n const recommendedText = props['recommendedText'] as string | undefined;\n const winnerHits = props['winnerHits'] as Record<string, { positive?: string[]; negative?: string[] }> | undefined;\n const productActions = props['productActions'] as\n | Record<string, { title: string; type: string; payload?: unknown }>\n | undefined;\n\n if (!recommended) {\n const fallback = document.createElement('div');\n return fallback;\n }\n\n const options: import('./ComparisonTable.js').ComparisonTableOptions = {\n recommended,\n products,\n attributes,\n highlights,\n specialCases,\n onProductClick: (sku) => {\n ctx.onProductClick?.({ sku, url: '' });\n },\n pricing: ctx.pricing,\n };\n if (recommendedText !== undefined) options.recommendedText = recommendedText;\n if (winnerHits !== undefined) options.winnerHits = winnerHits;\n if (productActions !== undefined) options.productActions = productActions;\n if (keyDifferencesHtml !== undefined) options.keyDifferencesHtml = keyDifferencesHtml;\n if (ctx.i18n) {\n options.i18n = {\n comparisonHeading: ctx.i18n.panelTitleComparisonResults,\n recommendedChoiceLabel: ctx.i18n.recommendedChoiceLabel,\n highlightsLabel: ctx.i18n.highlightsLabel,\n keyDifferencesLabel: ctx.i18n.keyDifferencesLabel,\n specialCasesLabel: ctx.i18n.specialCasesLabel,\n addToCartButton: ctx.i18n.addToCartButton,\n };\n }\n\n const el = renderComparisonTable(options);\n\n // Mobile variant\n if (ctx.isMobile ?? isMobileViewport()) {\n el.classList.add('gengage-chat-comparison--mobile');\n }\n\n return el;\n}\n\nfunction renderDivider(element: UIElement): HTMLElement {\n const hr = document.createElement('hr');\n hr.className = 'gengage-chat-divider';\n const label = element.props?.['label'] as string | undefined;\n if (label) {\n const wrapper = document.createElement('div');\n wrapper.className = 'gengage-chat-divider-wrapper';\n const labelEl = document.createElement('span');\n labelEl.className = 'gengage-chat-divider-label';\n labelEl.textContent = label;\n wrapper.appendChild(hr);\n wrapper.appendChild(labelEl);\n return wrapper;\n }\n return hr;\n}\n","/**\n * Block-by-block HTML typewriter effect.\n *\n * Parses sanitised HTML into top-level blocks and reveals them one at a time\n * with a configurable stagger delay. Respects prefers-reduced-motion.\n */\n\nexport interface TypewriterOptions {\n container: HTMLElement;\n html: string;\n /** Delay in ms between block reveals (default: 30). */\n delayMs?: number;\n /** Called after each block is revealed — useful for scroll tracking. */\n onTick?: () => void;\n /** Called when all blocks have been revealed. */\n onComplete?: () => void;\n}\n\nexport interface TypewriterHandle {\n /** Skip animation and show all content immediately. */\n complete(): void;\n /** Cancel animation, leave content as-is. */\n cancel(): void;\n readonly isRunning: boolean;\n}\n\nconst BLOCK_ELEMENTS = new Set([\n 'P',\n 'DIV',\n 'H1',\n 'H2',\n 'H3',\n 'H4',\n 'H5',\n 'H6',\n 'LI',\n 'UL',\n 'OL',\n 'BLOCKQUOTE',\n 'PRE',\n 'TABLE',\n 'SECTION',\n 'HR',\n 'FIGURE',\n 'FIGCAPTION',\n 'DL',\n 'DT',\n 'DD',\n]);\n\n/**\n * Split parsed DOM children into logical blocks for reveal animation.\n * Block-level elements each become their own block.\n * Adjacent inline/text nodes are grouped together.\n */\nfunction splitIntoBlocks(nodes: NodeList): Node[][] {\n const blocks: Node[][] = [];\n let currentInline: Node[] = [];\n\n for (const node of nodes) {\n if (node.nodeType === Node.ELEMENT_NODE && BLOCK_ELEMENTS.has((node as Element).tagName)) {\n // Flush any pending inline group\n if (currentInline.length > 0) {\n blocks.push(currentInline);\n currentInline = [];\n }\n blocks.push([node]);\n } else {\n currentInline.push(node);\n }\n }\n\n if (currentInline.length > 0) {\n blocks.push(currentInline);\n }\n\n return blocks;\n}\n\nfunction containsTable(nodes: Node[][]): boolean {\n for (const block of nodes) {\n for (const node of block) {\n if (\n node.nodeType === Node.ELEMENT_NODE &&\n ((node as Element).tagName === 'TABLE' || (node as Element).querySelector?.('table'))\n ) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\nexport function typewriteHtml(options: TypewriterOptions): TypewriterHandle {\n const { container, html, delayMs = 30, onTick, onComplete } = options;\n\n // Parse HTML into DOM nodes\n const template = document.createElement('template');\n template.innerHTML = html;\n const blocks = splitIntoBlocks(template.content.childNodes);\n\n // Skip animation for: reduced motion, single block, contains table, empty\n if (prefersReducedMotion() || blocks.length <= 1 || containsTable(blocks)) {\n container.innerHTML = html;\n onComplete?.();\n return { complete() {}, cancel() {}, isRunning: false };\n }\n\n // Clear container and start reveal\n container.innerHTML = '';\n let currentIndex = 0;\n let timerId: ReturnType<typeof setTimeout> | null = null;\n let running = true;\n\n function revealNext(): void {\n if (!running || currentIndex >= blocks.length) {\n running = false;\n onComplete?.();\n return;\n }\n\n const block = blocks[currentIndex]!;\n const wrapper = document.createElement('span');\n wrapper.className = 'gengage-chat-typewriter-block';\n for (const node of block) {\n wrapper.appendChild(node.cloneNode(true));\n }\n container.appendChild(wrapper);\n\n currentIndex++;\n onTick?.();\n\n if (currentIndex < blocks.length) {\n timerId = setTimeout(revealNext, delayMs);\n } else {\n running = false;\n onComplete?.();\n }\n }\n\n // Start the first reveal immediately\n revealNext();\n\n return {\n complete() {\n if (!running) return;\n if (timerId !== null) clearTimeout(timerId);\n running = false;\n container.innerHTML = html;\n onComplete?.();\n },\n cancel() {\n if (timerId !== null) clearTimeout(timerId);\n running = false;\n },\n get isRunning() {\n return running;\n },\n };\n}\n","/**\n * Product mention linker.\n *\n * After sanitiseHtml() renders bot text, this module walks text nodes and\n * wraps product name occurrences with clickable links that dispatch a\n * `launchSingleProduct` action.\n *\n * XSS safety: Uses DOM text-node manipulation only — no innerHTML.\n */\n\nexport interface ProductMention {\n sku: string;\n short_name: string;\n}\n\nexport interface ProductMentionLinkerOptions {\n container: HTMLElement;\n mentions: ProductMention[];\n onProductClick: (sku: string) => void;\n}\n\nfunction isWordChar(char: string | undefined): boolean {\n return char !== undefined && /[\\p{L}\\p{N}_]/u.test(char);\n}\n\n/**\n * Walk text nodes in `container` and wrap occurrences of each mention's\n * `short_name` with a clickable `<a>` element.\n *\n * Only the first occurrence of each mention is linked to avoid visual clutter.\n */\nexport function linkProductMentions(options: ProductMentionLinkerOptions): void {\n const { container, mentions, onProductClick } = options;\n if (mentions.length === 0) return;\n\n // Build a map of lowercase short_name → mention for case-insensitive matching\n const mentionMap = new Map<string, ProductMention>();\n for (const m of mentions) {\n if (m.short_name.length === 0) continue;\n mentionMap.set(m.short_name.toLowerCase(), m);\n }\n\n if (mentionMap.size === 0) return;\n\n // Process one mention at a time, re-walking the tree each time\n // (DOM mutations invalidate the walker)\n for (const [lowerName, mention] of mentionMap) {\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);\n let node = walker.nextNode();\n let found = false;\n\n while (node && !found) {\n const text = node.textContent ?? '';\n const idx = text.toLowerCase().indexOf(lowerName);\n if (idx === -1) {\n node = walker.nextNode();\n continue;\n }\n\n const prevChar = idx > 0 ? text[idx - 1] : undefined;\n const nextChar = text[idx + mention.short_name.length];\n if (isWordChar(prevChar) || isWordChar(nextChar)) {\n node = walker.nextNode();\n continue;\n }\n\n const before = text.slice(0, idx);\n const match = text.slice(idx, idx + mention.short_name.length);\n const after = text.slice(idx + mention.short_name.length);\n\n const parent = node.parentNode;\n if (!parent) {\n node = walker.nextNode();\n continue;\n }\n\n const link = document.createElement('a');\n link.className = 'gengage-product-mention';\n link.textContent = match;\n link.href = '#';\n link.addEventListener('click', (e) => {\n e.preventDefault();\n onProductClick(mention.sku);\n });\n\n if (before) parent.insertBefore(document.createTextNode(before), node);\n parent.insertBefore(link, node);\n if (after) parent.insertBefore(document.createTextNode(after), node);\n parent.removeChild(node);\n\n found = true;\n }\n }\n}\n","/**\n * Classifies suggested actions into input-area chips vs message-flow pills.\n *\n * Input-area chips: compact shortcuts above the input field (search, info, review, similar).\n * Message-flow pills: larger interactive cards in the suggestion row.\n */\n\nconst INPUT_AREA_ICONS = new Set(['search', 'info', 'review', 'similar']);\nconst INPUT_AREA_TYPES = new Set([\n 'quickAnswer',\n 'reviewSummary',\n 'searchDiscovery',\n 'launchDiscovery',\n 'exploreTogetherV2',\n]);\n\nexport function isInputAreaAction(btn: { icon?: string; action?: { type?: string } }): boolean {\n if (btn.icon && INPUT_AREA_ICONS.has(btn.icon)) return true;\n if (btn.action?.type && INPUT_AREA_TYPES.has(btn.action.type)) return true;\n return false;\n}\n","/**\n * Floating card that suggests the user try comparison mode.\n *\n * Shows in the panel bottom-right when a ProductGrid is displayed and\n * comparison mode is not active.\n */\n\nconst SESSION_STORAGE_KEY = 'gengage_choice_prompter_dismissed';\nconst GLOBAL_DISMISS_KEY = 'gengage_choice_prompter_dismissed_global';\n\nexport interface ChoicePrompterOptions {\n heading: string;\n suggestion: string;\n ctaLabel: string;\n threadId: string;\n onCtaClick: () => void;\n onDismiss?: () => void;\n dismissAriaLabel?: string;\n}\n\nexport function createChoicePrompter(options: ChoicePrompterOptions): HTMLElement {\n const card = document.createElement('div');\n card.className = 'gengage-chat-choice-prompter';\n\n const headingEl = document.createElement('div');\n headingEl.className = 'gengage-chat-choice-prompter-heading';\n headingEl.textContent = options.heading;\n card.appendChild(headingEl);\n\n const suggestionEl = document.createElement('div');\n suggestionEl.className = 'gengage-chat-choice-prompter-suggestion';\n suggestionEl.textContent = options.suggestion;\n card.appendChild(suggestionEl);\n\n const cta = document.createElement('button');\n cta.type = 'button';\n cta.className = 'gengage-chat-choice-prompter-cta';\n cta.textContent = options.ctaLabel;\n cta.addEventListener('click', () => {\n markDismissed(options.threadId);\n try {\n sessionStorage.setItem(GLOBAL_DISMISS_KEY, '1');\n } catch {\n /* */\n }\n card.remove();\n options.onCtaClick();\n });\n card.appendChild(cta);\n\n const dismiss = document.createElement('button');\n dismiss.type = 'button';\n dismiss.className = 'gengage-chat-choice-prompter-dismiss';\n dismiss.textContent = '\\u00D7'; // × close\n dismiss.setAttribute('aria-label', options.dismissAriaLabel ?? 'Dismiss');\n dismiss.addEventListener('click', () => {\n markDismissed(options.threadId);\n try {\n sessionStorage.setItem(GLOBAL_DISMISS_KEY, '1');\n } catch {\n /* */\n }\n card.remove();\n options.onDismiss?.();\n });\n card.appendChild(dismiss);\n\n return card;\n}\n\nexport function isChoicePrompterGloballyDismissed(): boolean {\n try {\n return sessionStorage.getItem(GLOBAL_DISMISS_KEY) === '1';\n } catch {\n return false;\n }\n}\n\nexport function isChoicePrompterDismissed(threadId: string): boolean {\n try {\n const raw = sessionStorage.getItem(SESSION_STORAGE_KEY);\n if (!raw) return false;\n const dismissed: string[] = JSON.parse(raw);\n return dismissed.includes(threadId);\n } catch {\n return false;\n }\n}\n\nfunction markDismissed(threadId: string): void {\n try {\n const raw = sessionStorage.getItem(SESSION_STORAGE_KEY);\n const dismissed: string[] = raw ? (JSON.parse(raw) as string[]) : [];\n if (!dismissed.includes(threadId)) {\n dismissed.push(threadId);\n }\n sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(dismissed));\n } catch {\n // sessionStorage unavailable — silently ignore\n }\n}\n","/**\n * IndexedDB persistence for chat sessions, backend context, and message payloads.\n *\n * Three stores:\n * - `sessions` — full session state (messages, thread cursors, timestamps)\n * - `context` — per-thread backend context snapshots (compound key)\n * - `payload` — message UISpec payloads (lean message pattern)\n *\n * All operations are best-effort: IndexedDB unavailability is non-fatal.\n */\n\nimport type { SerializableChatMessage } from '../chat/types.js';\nimport type { UISpec } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Data shapes stored in each object store\n// ---------------------------------------------------------------------------\n\nexport interface SessionData {\n userId: string;\n appId: string;\n sessionId: string;\n messages: SerializableChatMessage[];\n currentThreadId: string | null;\n lastThreadId: string | null;\n createdAt: string;\n /** Thread IDs that produced panel content, in creation order. */\n panelThreads?: string[] | undefined;\n /** Product thumbnails for ThumbnailsColumn. */\n thumbnailEntries?: Array<{ sku: string; imageUrl: string; threadId: string }> | undefined;\n /** Serialized panel HTML keyed by bot message ID (for panel snapshot restore). */\n panelSnapshotHtml?: Record<string, string> | undefined;\n /** SKU active when session was saved — prevents cross-SKU restore. */\n sku?: string | undefined;\n}\n\nexport interface ContextData {\n sessionId: string;\n threadId: string;\n context: import('./types.js').BackendContext;\n}\n\nexport interface PayloadData {\n threadId: string;\n messageId: string;\n uiSpec: UISpec;\n}\n\nexport interface FavoriteData {\n userId: string;\n appId: string;\n sku: string;\n name?: string | undefined;\n imageUrl?: string | undefined;\n price?: string | undefined;\n savedAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// GengageIndexedDB\n// ---------------------------------------------------------------------------\n\nconst DB_NAME = 'gengage_assistant';\nconst DB_VERSION = 3;\n\nconst STORE_SESSIONS = 'sessions';\nconst STORE_CONTEXT = 'context';\nconst STORE_PAYLOAD = 'payload';\nconst STORE_FAVORITES = 'favorites';\n\n/**\n * Wrap an IDBRequest in a Promise.\n */\nfunction requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\n/**\n * Wrap an IDBTransaction completion in a Promise.\n */\nfunction transactionComplete(tx: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n tx.onabort = () => reject(tx.error ?? new DOMException('Transaction aborted'));\n });\n}\n\nexport class GengageIndexedDB {\n private _db: IDBDatabase | null = null;\n private _dbName: string;\n private _version: number;\n\n constructor(dbName: string = DB_NAME, version: number = DB_VERSION) {\n this._dbName = dbName;\n this._version = version;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async open(): Promise<IDBDatabase> {\n if (this._db) return this._db;\n\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this._dbName, this._version);\n\n request.onupgradeneeded = (event) => {\n const db = request.result;\n const oldVersion = (event as IDBVersionChangeEvent).oldVersion;\n\n if (oldVersion < 1) {\n // Fresh install — create stores with v3 schema directly\n db.createObjectStore(STORE_SESSIONS, { keyPath: ['userId', 'appId', 'sessionId'] });\n db.createObjectStore(STORE_CONTEXT, { keyPath: ['sessionId', 'threadId'] });\n const payloadStore = db.createObjectStore(STORE_PAYLOAD, {\n keyPath: ['threadId', 'messageId'],\n });\n payloadStore.createIndex('threadId', 'threadId', { unique: false });\n db.createObjectStore(STORE_FAVORITES, { keyPath: ['userId', 'appId', 'sku'] });\n }\n\n if (oldVersion >= 1 && oldVersion < 2) {\n // Upgrade from v1 — drop old stores, recreate with new keys\n if (db.objectStoreNames.contains(STORE_SESSIONS)) db.deleteObjectStore(STORE_SESSIONS);\n if (db.objectStoreNames.contains(STORE_PAYLOAD)) db.deleteObjectStore(STORE_PAYLOAD);\n db.createObjectStore(STORE_SESSIONS, { keyPath: ['userId', 'appId', 'sessionId'] });\n const payloadStore = db.createObjectStore(STORE_PAYLOAD, {\n keyPath: ['threadId', 'messageId'],\n });\n payloadStore.createIndex('threadId', 'threadId', { unique: false });\n // Ensure context store exists (may be absent in early v1 schemas)\n if (!db.objectStoreNames.contains(STORE_CONTEXT)) {\n db.createObjectStore(STORE_CONTEXT, { keyPath: ['sessionId', 'threadId'] });\n }\n }\n\n if (oldVersion < 3) {\n if (!db.objectStoreNames.contains(STORE_FAVORITES)) {\n db.createObjectStore(STORE_FAVORITES, { keyPath: ['userId', 'appId', 'sku'] });\n }\n }\n };\n\n request.onsuccess = () => {\n this._db = request.result;\n resolve(this._db);\n };\n\n request.onerror = () => reject(request.error);\n });\n }\n\n close(): void {\n this._db?.close();\n this._db = null;\n }\n\n // -------------------------------------------------------------------------\n // Sessions\n // -------------------------------------------------------------------------\n\n async saveSession(data: SessionData): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_SESSIONS, 'readwrite');\n tx.objectStore(STORE_SESSIONS).put(data);\n await transactionComplete(tx);\n }\n\n async loadSession(userId: string, appId: string, sessionId: string): Promise<SessionData | null> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_SESSIONS, 'readonly');\n const result = await requestToPromise(tx.objectStore(STORE_SESSIONS).get([userId, appId, sessionId]));\n return (result as SessionData | undefined) ?? null;\n }\n\n // -------------------------------------------------------------------------\n // Context (compound key: [sessionId, threadId])\n // -------------------------------------------------------------------------\n\n async saveContext(data: ContextData): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_CONTEXT, 'readwrite');\n tx.objectStore(STORE_CONTEXT).put(data);\n await transactionComplete(tx);\n }\n\n async loadContext(sessionId: string, threadId: string): Promise<ContextData | null> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_CONTEXT, 'readonly');\n const result = await requestToPromise(tx.objectStore(STORE_CONTEXT).get([sessionId, threadId]));\n return (result as ContextData | undefined) ?? null;\n }\n\n /**\n * Delete all context entries for a session whose threadId is lexicographically\n * greater than the given threadId. Used during rollback to prune future branches.\n *\n * Thread IDs are UUIDv7 (lexicographically sortable by time), so string\n * comparison is sufficient.\n */\n async deleteContextsAfterThread(sessionId: string, threadId: string): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_CONTEXT, 'readwrite');\n const store = tx.objectStore(STORE_CONTEXT);\n\n // Open cursor over all entries and filter by sessionId + threadId comparison\n const request = store.openCursor();\n\n await new Promise<void>((resolve, reject) => {\n request.onsuccess = () => {\n const cursor = request.result;\n if (!cursor) {\n resolve();\n return;\n }\n const entry = cursor.value as ContextData;\n if (entry.sessionId === sessionId && entry.threadId > threadId) {\n try {\n cursor.delete();\n } catch {\n // cursor.delete() may fail on read-only or version-change transactions — non-fatal\n }\n }\n cursor.continue();\n };\n request.onerror = () => reject(request.error);\n });\n\n await transactionComplete(tx);\n }\n\n /**\n * Load the most recent context for a session (latest threadId).\n * Uses lexicographic ordering of UUIDv7 threadIds for chronological sort.\n */\n async loadLatestContext(sessionId: string): Promise<ContextData | null> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_CONTEXT, 'readonly');\n const store = tx.objectStore(STORE_CONTEXT);\n\n // UUIDv7 thread IDs are ASCII hex+hyphens, so \\uffff is a safe upper bound\n const range = IDBKeyRange.bound([sessionId, ''], [sessionId, '\\uffff']);\n\n return new Promise((resolve, reject) => {\n const request = store.openCursor(range, 'prev');\n request.onsuccess = () => {\n const cursor = request.result;\n resolve(cursor ? (cursor.value as ContextData) : null);\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n // -------------------------------------------------------------------------\n // Payload\n // -------------------------------------------------------------------------\n\n async savePayload(data: PayloadData): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_PAYLOAD, 'readwrite');\n tx.objectStore(STORE_PAYLOAD).put(data);\n await transactionComplete(tx);\n }\n\n async loadPayload(threadId: string, messageId: string): Promise<PayloadData | null> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_PAYLOAD, 'readonly');\n const result = await requestToPromise(tx.objectStore(STORE_PAYLOAD).get([threadId, messageId]));\n return (result as PayloadData | undefined) ?? null;\n }\n\n async loadPayloadsByThread(threadId: string): Promise<PayloadData[]> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_PAYLOAD, 'readonly');\n const index = tx.objectStore(STORE_PAYLOAD).index('threadId');\n const results: PayloadData[] = [];\n\n return new Promise((resolve, reject) => {\n const request = index.openCursor(IDBKeyRange.only(threadId));\n request.onsuccess = () => {\n const cursor = request.result;\n if (!cursor) {\n resolve(results);\n return;\n }\n results.push(cursor.value as PayloadData);\n cursor.continue();\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n // -------------------------------------------------------------------------\n // Favorites\n // -------------------------------------------------------------------------\n\n async saveFavorite(data: FavoriteData): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_FAVORITES, 'readwrite');\n tx.objectStore(STORE_FAVORITES).put(data);\n await transactionComplete(tx);\n }\n\n async removeFavorite(userId: string, appId: string, sku: string): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_FAVORITES, 'readwrite');\n tx.objectStore(STORE_FAVORITES).delete([userId, appId, sku]);\n await transactionComplete(tx);\n }\n\n async loadFavorites(userId: string, appId: string): Promise<FavoriteData[]> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_FAVORITES, 'readonly');\n const all = await requestToPromise(tx.objectStore(STORE_FAVORITES).getAll());\n return (all as FavoriteData[]).filter((f) => f.userId === userId && f.appId === appId);\n }\n\n async isFavorite(userId: string, appId: string, sku: string): Promise<boolean> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_FAVORITES, 'readonly');\n const result = await requestToPromise(tx.objectStore(STORE_FAVORITES).get([userId, appId, sku]));\n return result !== undefined;\n }\n\n // -------------------------------------------------------------------------\n // Internal\n // -------------------------------------------------------------------------\n\n private _requireDb(): IDBDatabase {\n if (!this._db) {\n throw new Error('GengageIndexedDB: database not open. Call open() first.');\n }\n return this._db;\n }\n}\n","/**\n * Extended Mode Manager — production lock-count system for panel extension.\n *\n * Controls whether the host page PDP area should be maximized or minimized\n * based on a combination of conditions: lock count, user visibility preference,\n * favorites mode, panel content type, and chat visibility.\n */\n\nexport type PanelContentType =\n | 'comparisonTable'\n | 'groupList'\n | 'productDetails'\n | 'productDetailsSimilars'\n | 'productList';\n\n/**\n * Base content types that always trigger panel extension.\n * `productDetails` is only added when `productDetailsInPanel` is true\n * (i.e. demo websites). Regular accounts show product details inline in ChatPane.\n */\nconst BASE_PANEL_CONTENT_TYPES: readonly PanelContentType[] = [\n 'comparisonTable',\n 'groupList',\n 'productDetailsSimilars',\n 'productList',\n];\n\nexport interface ExtendedModeManagerOptions {\n onChange: (extended: boolean) => void;\n /** Whether `productDetails` should trigger panel extension (default: false, true for demo sites). */\n productDetailsInPanel?: boolean;\n}\n\nexport class ExtendedModeManager {\n private _lockCount = 1; // starts locked\n private _hiddenByUser = false;\n private _lastPanelContentType: PanelContentType | null = null;\n private _chatShown = false;\n private _isFavoritesMode = false;\n private _lastExtended = false;\n private _onChange: (extended: boolean) => void;\n private _panelContentTypes: ReadonlySet<string>;\n\n constructor(options: ExtendedModeManagerOptions) {\n this._onChange = options.onChange;\n\n const types = new Set<string>(BASE_PANEL_CONTENT_TYPES);\n if (options.productDetailsInPanel) {\n types.add('productDetails');\n }\n this._panelContentTypes = types;\n }\n\n get isExtended(): boolean {\n return (\n this._lockCount === 0 &&\n !this._hiddenByUser &&\n !this._isFavoritesMode &&\n this._lastPanelContentType !== null &&\n this._panelContentTypes.has(this._lastPanelContentType) &&\n this._chatShown\n );\n }\n\n unlock(): void {\n if (this._lockCount > 0) {\n this._lockCount--;\n }\n this._checkStateChange();\n }\n\n lock(): void {\n this._lockCount++;\n this._checkStateChange();\n }\n\n setHiddenByUser(hidden: boolean): void {\n this._hiddenByUser = hidden;\n this._checkStateChange();\n }\n\n setChatShown(shown: boolean): void {\n this._chatShown = shown;\n this._checkStateChange();\n }\n\n setFavoritesMode(fav: boolean): void {\n this._isFavoritesMode = fav;\n this._checkStateChange();\n }\n\n setPanelContentType(type: PanelContentType | null): void {\n this._lastPanelContentType = type;\n this._checkStateChange();\n }\n\n private _checkStateChange(): void {\n const current = this.isExtended;\n if (current !== this._lastExtended) {\n this._lastExtended = current;\n this._onChange(current);\n }\n }\n}\n","/**\n * PanelManager — manages the two-pane panel state for the chat widget.\n *\n * Owns panel snapshots, snapshot types, active message highlighting,\n * panel thread navigation, topbar updates, and extended mode coordination.\n *\n * Extracted from chat/index.ts to improve cohesion and reduce file size.\n */\n\nimport type { ChatDrawer } from './components/ChatDrawer.js';\nimport type { CommunicationBridge } from '../common/communication-bridge.js';\nimport type { ExtendedModeManager, PanelContentType } from './extendedModeManager.js';\nimport type { ChatI18n } from './types.js';\nimport type { UISpec, UIElement } from '../common/types.js';\n\n/** Minimal interface the panel manager needs from its host widget. */\nexport interface PanelManagerDeps {\n drawer: () => ChatDrawer | null;\n shadow: () => ShadowRoot | null;\n currentThreadId: () => string | null;\n bridge: () => CommunicationBridge | null;\n extendedModeManager: () => ExtendedModeManager | null;\n i18n: () => ChatI18n;\n rollbackToThread: (threadId: string) => void;\n}\n\nexport class PanelManager {\n /** Panel content snapshots keyed by bot message ID for history navigation. */\n readonly snapshots = new Map<string, HTMLElement>();\n /** Rebuild functions keyed by message ID — produce fresh DOM with live event listeners. */\n private readonly _snapshotRebuilders = new Map<string, () => HTMLElement>();\n /** Component type for each panel snapshot (for topbar title restoration). */\n readonly snapshotTypes = new Map<string, string>();\n /** Currently active (highlighted) message ID in the chat pane. */\n activePanelMessageId: string | null = null;\n /** Current panel component type. */\n currentType: string | null = null;\n /** Thread IDs that have panel content, in order of creation. */\n threads: string[] = [];\n /** Action type that triggered the current stream (for panel title disambiguation). */\n lastActionType: string | null = null;\n\n constructor(private readonly deps: PanelManagerDeps) {}\n\n /**\n * Snapshot the current panel state for a message. Stores a rebuild function\n * (preferred — produces fresh DOM with live event listeners) alongside a\n * static DOM clone as fallback.\n */\n snapshotForMessage(messageId: string, rebuild?: (() => HTMLElement) | undefined): void {\n const drawer = this.deps.drawer();\n if (!drawer?.hasPanelContent()) return;\n // Never snapshot loading skeleton — it must not be persisted or restored\n if (drawer.isPanelLoading()) return;\n const contentEl = drawer.getPanelContentElement();\n if (!contentEl) return;\n const clone = contentEl.cloneNode(true) as HTMLElement;\n this.snapshots.set(messageId, clone);\n if (rebuild) this._snapshotRebuilders.set(messageId, rebuild);\n // Store the component type so topbar can be restored with the right title\n if (this.currentType) {\n this.snapshotTypes.set(messageId, this.currentType);\n }\n }\n\n /**\n * Attach a click handler to a bot message bubble so clicking it restores\n * the panel content that was active when that message was received.\n */\n attachClickHandler(messageId: string): void {\n const shadow = this.deps.shadow();\n const bubble = shadow?.querySelector(`[data-message-id=\"${CSS.escape(messageId)}\"]`);\n if (!bubble) return;\n (bubble as HTMLElement).style.cursor = 'pointer';\n bubble.addEventListener('click', () => this.restoreForMessage(messageId));\n }\n\n /**\n * Restore the panel content snapshot associated with a given message ID.\n * Highlights the active message and de-highlights the previous one.\n * Also restores the panel topbar title for the snapshot's component type.\n * Returns true if the snapshot was found and restored.\n */\n restoreForMessage(messageId: string): boolean {\n // Prefer rebuild function (live event listeners) over static DOM clone\n const rebuild = this._snapshotRebuilders.get(messageId);\n const snapshot = this.snapshots.get(messageId);\n if (!rebuild && !snapshot) return false;\n\n const shadow = this.deps.shadow();\n const drawer = this.deps.drawer();\n\n // De-highlight previous active message\n if (this.activePanelMessageId) {\n const prev = shadow?.querySelector(`[data-message-id=\"${CSS.escape(this.activePanelMessageId)}\"]`);\n prev?.classList.remove('gengage-chat-bubble--active');\n }\n\n // Highlight the clicked message\n const current = shadow?.querySelector(`[data-message-id=\"${CSS.escape(messageId)}\"]`);\n current?.classList.add('gengage-chat-bubble--active');\n this.activePanelMessageId = messageId;\n\n // Restore panel content — prefer rebuild for live interactions, fallback to clone\n const el = rebuild ? rebuild() : (snapshot!.cloneNode(true) as HTMLElement);\n drawer?.setPanelContent(el);\n\n // Restore component type and topbar\n const snapshotType = this.snapshotTypes.get(messageId);\n if (snapshotType) {\n this.currentType = snapshotType;\n this.updateTopBar(snapshotType);\n }\n return true;\n }\n\n /**\n * Send maximize-pdp / minify-pdp bridge messages with production-matching delays.\n * Called by the extended mode manager when panel extension state changes.\n */\n notifyExtension(extended: boolean): void {\n const bridge = this.deps.bridge();\n if (extended) {\n setTimeout(() => bridge?.send('maximize-pdp', {}), 350);\n } else {\n setTimeout(() => bridge?.send('minify-pdp', {}), 200);\n }\n }\n\n /**\n * Derive panel title from UISpec root element type using i18n strings.\n * When the backend provides a `panelTitle` prop, it takes precedence.\n */\n titleForComponent(componentType: string, backendTitle?: string): string {\n if (backendTitle) return backendTitle;\n const i18n = this.deps.i18n();\n switch (componentType) {\n case 'ProductDetailsPanel':\n return i18n.panelTitleProductDetails;\n case 'ProductGrid':\n // User-typed queries produce search results; product actions produce similar products\n return isSearchLikeActionType(this.lastActionType)\n ? i18n.panelTitleSearchResults\n : i18n.panelTitleSimilarProducts;\n case 'ComparisonTable':\n return i18n.panelTitleComparisonResults;\n case 'AIGroupingCards':\n return i18n.panelTitleCategories;\n default:\n return '';\n }\n }\n\n /**\n * Update the panel top bar navigation state and title.\n * When the backend provides a `panelTitle`, it takes precedence over i18n defaults.\n */\n updateTopBar(componentType: string, backendTitle?: string): void {\n const currentThreadId = this.deps.currentThreadId();\n if (!currentThreadId) return;\n const idx = this.threads.indexOf(currentThreadId);\n const canBack = idx > 0;\n const canForward = idx >= 0 && idx < this.threads.length - 1;\n const title = this.titleForComponent(componentType, backendTitle);\n this.deps.drawer()?.updatePanelTopBar(canBack, canForward, title);\n }\n\n /**\n * Set panel topbar title during loading (before actual panel content arrives).\n * Maps backend pending types to the same i18n titles used for final content.\n */\n updateTopBarForLoading(pendingType: string): void {\n const i18n = this.deps.i18n();\n const loadingTitleMap: Record<string, string> = {\n productDetails: i18n.panelTitleProductDetails,\n productList: isSearchLikeActionType(this.lastActionType)\n ? i18n.panelTitleSearchResults\n : i18n.panelTitleSimilarProducts,\n comparisonTable: i18n.panelTitleComparisonResults,\n groupList: i18n.panelTitleCategories,\n };\n const title = loadingTitleMap[pendingType] ?? '';\n if (title) {\n const currentThreadId = this.deps.currentThreadId();\n const idx = currentThreadId ? this.threads.indexOf(currentThreadId) : -1;\n const canBack = idx > 0;\n const canForward = idx >= 0 && idx < this.threads.length - 1;\n this.deps.drawer()?.updatePanelTopBar(canBack, canForward, title);\n }\n }\n\n /**\n * Map UISpec component types to PanelContentType for the extended mode manager.\n */\n updateExtendedMode(componentType: string): void {\n const mapping: Record<string, PanelContentType> = {\n ComparisonTable: 'comparisonTable',\n AIGroupingCards: 'groupList',\n ProductDetailsPanel: 'productDetails',\n ProductGrid: 'productList',\n };\n const panelType = mapping[componentType] ?? null;\n this.deps.extendedModeManager()?.setPanelContentType(panelType);\n }\n\n /** Navigate to the previous panel thread. */\n navigateBack(): void {\n const currentThreadId = this.deps.currentThreadId();\n if (!currentThreadId) return;\n const idx = this.threads.indexOf(currentThreadId);\n if (idx > 0) {\n const target = this.threads[idx - 1];\n if (target) this.deps.rollbackToThread(target);\n }\n }\n\n /** Navigate to the next panel thread. */\n navigateForward(): void {\n const currentThreadId = this.deps.currentThreadId();\n if (!currentThreadId) return;\n const idx = this.threads.indexOf(currentThreadId);\n if (idx >= 0 && idx < this.threads.length - 1) {\n const target = this.threads[idx + 1];\n if (target) this.deps.rollbackToThread(target);\n }\n }\n\n /**\n * Panel route shaping:\n * - product details => expanded LHS panel (`ProductDetailsPanel`)\n * - all other panel-routed specs keep their original component types\n */\n toPanelSpec(spec: UISpec): UISpec {\n const root = spec.elements[spec.root];\n if (!root || root.type !== 'ProductCard') return spec;\n\n const panelRoot: UIElement = {\n ...root,\n type: 'ProductDetailsPanel',\n };\n\n return {\n root: spec.root,\n elements: {\n ...spec.elements,\n [spec.root]: panelRoot,\n },\n };\n }\n\n destroy(): void {\n this.snapshots.clear();\n this._snapshotRebuilders.clear();\n this.snapshotTypes.clear();\n this.activePanelMessageId = null;\n this.currentType = null;\n this.threads = [];\n }\n}\n\nfunction isSearchLikeActionType(actionType: string | null): boolean {\n return actionType === 'user_message' || actionType === 'inputText';\n}\n\n/**\n * Decide how to apply incoming panel UISpec content.\n *\n * - `'replace'` — set as sole panel content (new search, comparison, product details)\n * - `'append'` — add below existing content (similar products grid after product details)\n * - `'appendSimilars'` — append via the dedicated similars helper\n */\nexport type PanelUpdateAction = 'replace' | 'append' | 'appendSimilars';\n\nexport function determinePanelUpdateAction(opts: {\n componentType: string;\n similarsAppend: boolean;\n currentPanelType: string | null;\n hasPanelContent: boolean;\n isPanelLoading: boolean;\n isFirstPanelContentInStream: boolean;\n}): PanelUpdateAction {\n // productDetailsSimilars: append to existing ProductDetailsPanel\n if (\n opts.similarsAppend &&\n opts.currentPanelType === 'ProductDetailsPanel' &&\n opts.hasPanelContent &&\n !opts.isPanelLoading\n ) {\n return 'appendSimilars';\n }\n\n // Append ProductGrid only when it follows other panel content in the SAME stream\n // (e.g. similar products below product details). A ProductGrid that is the first\n // panel content in a new stream (search results) must REPLACE.\n if (\n opts.componentType === 'ProductGrid' &&\n !opts.isFirstPanelContentInStream &&\n opts.hasPanelContent &&\n !opts.isPanelLoading\n ) {\n return 'append';\n }\n\n return 'replace';\n}\n","/**\n * SessionPersistence — IndexedDB persistence helpers for the chat widget.\n *\n * Encapsulates session save/load, payload caching, favorite toggling,\n * and URL navigation with pre-save.\n *\n * Extracted from chat/index.ts to improve cohesion and reduce file size.\n */\n\nimport type { GengageIndexedDB, FavoriteData } from '../common/indexed-db.js';\nimport type { CommunicationBridge } from '../common/communication-bridge.js';\nimport { isSafeUrl } from '../common/safe-html.js';\nimport type { BackendContext, UISpec } from '../common/types.js';\nimport type { ChatMessage, SerializableChatMessage } from './types.js';\nimport type { ThumbnailEntry } from './components/ThumbnailsColumn.js';\n\nexport type { FavoriteData };\n\nexport interface PersistSessionParams {\n userId: string;\n appId: string;\n sessionId: string;\n messages: ChatMessage[];\n currentThreadId: string | null;\n lastThreadId: string | null;\n chatCreatedAt: string;\n panelSnapshots: Map<string, HTMLElement>;\n panelThreads: string[];\n thumbnailEntries: ThumbnailEntry[];\n lastBackendContext: BackendContext | null;\n sku?: string | undefined;\n}\n\nexport class SessionPersistence {\n private _db: GengageIndexedDB | null;\n /** Favorited product SKUs (loaded from IDB). */\n readonly favoritedSkus: Set<string> = new Set();\n /** Full favorite data cache (loaded from IDB alongside favoritedSkus). */\n private _favoritesCache: Map<string, FavoriteData> = new Map();\n /** Async mutex: serializes persist() calls to prevent interleaved IDB writes. */\n private _persistLock: Promise<void> = Promise.resolve();\n\n constructor(db: GengageIndexedDB | null) {\n this._db = db;\n }\n\n get db(): GengageIndexedDB | null {\n return this._db;\n }\n\n set db(value: GengageIndexedDB | null) {\n this._db = value;\n }\n\n /**\n * Persist current session state to IndexedDB.\n * Called after each stream completion (onDone). Non-fatal on failure.\n */\n async persist(params: PersistSessionParams): Promise<void> {\n if (!this._db) return;\n // Serialize through mutex to prevent interleaved IDB writes\n const prev = this._persistLock;\n let unlock: () => void;\n this._persistLock = new Promise<void>((r) => {\n unlock = r;\n });\n await prev;\n try {\n await this._persistImpl(params);\n } finally {\n unlock!();\n }\n }\n\n private async _persistImpl(params: PersistSessionParams): Promise<void> {\n if (!this._db) return;\n\n const serializableMessages: SerializableChatMessage[] = params.messages.map((m) => {\n const sm: SerializableChatMessage = {\n id: m.id,\n role: m.role,\n timestamp: m.timestamp,\n status: m.status === 'streaming' ? 'done' : m.status,\n };\n if (m.threadId !== undefined) sm.threadId = m.threadId;\n if (m.content !== undefined) sm.content = m.content;\n if (m.silent) sm.silent = true;\n return sm;\n });\n\n // Serialize panel snapshots to HTML strings (never persist loading skeletons)\n const panelSnapshotHtml: Record<string, string> = {};\n for (const [msgId, el] of params.panelSnapshots) {\n if (el.querySelector('.gengage-chat-panel-skeleton')) continue;\n panelSnapshotHtml[msgId] = el.innerHTML;\n }\n\n await this._db.saveSession({\n userId: params.userId,\n appId: params.appId,\n sessionId: params.sessionId,\n messages: serializableMessages,\n currentThreadId: params.currentThreadId,\n lastThreadId: params.lastThreadId,\n createdAt: params.chatCreatedAt,\n panelThreads: params.panelThreads.length > 0 ? params.panelThreads : undefined,\n thumbnailEntries: params.thumbnailEntries.length > 0 ? params.thumbnailEntries : undefined,\n panelSnapshotHtml: Object.keys(panelSnapshotHtml).length > 0 ? panelSnapshotHtml : undefined,\n sku: params.sku,\n });\n\n // Save latest context snapshot for current thread\n if (params.lastBackendContext && params.currentThreadId) {\n await this._db.saveContext({\n sessionId: params.sessionId,\n threadId: params.currentThreadId,\n context: params.lastBackendContext,\n });\n }\n\n // Save UISpec payloads separately\n // Note: we intentionally do NOT delete m.uiSpec from the caller's live objects.\n // The caller's message array must remain intact for re-rendering and snapshot logic.\n for (const m of params.messages) {\n if (m.uiSpec && m.threadId) {\n await this._db.savePayload({\n threadId: m.threadId,\n messageId: m.id,\n uiSpec: m.uiSpec,\n });\n }\n }\n }\n\n /**\n * Persist session to IndexedDB, then navigate to the URL.\n * Sends an 'openURLInNewTab' bridge message so the host page can intercept\n * (e.g. for SPA routing), then navigates directly as fallback.\n *\n * Legacy compatibility: the prior engine navigated via window.location.href\n * after posting saveSessionAndOpenURL to the iframe. The clean-room runs in\n * the same window (Shadow DOM, not iframe), so it navigates directly.\n */\n async saveAndOpenURL(url: string, persistFn: () => Promise<void>, bridge: CommunicationBridge | null): Promise<void> {\n try {\n await persistFn();\n } catch {\n // Non-fatal — still navigate\n }\n // Notify host so SPAs can intercept (e.g. router.push instead of full reload).\n // NOTE: The bridge message is fire-and-forget. The subsequent location.href\n // assignment may trigger a page unload before the host processes the message.\n // This matches legacy behavior. SPAs that need to intercept\n // should handle the bridge event synchronously or call event.preventDefault()\n // on the gengage:navigate CustomEvent to suppress the fallback navigation.\n bridge?.send('openURLInNewTab', { url });\n if (isSafeUrl(url)) {\n window.location.href = url;\n }\n }\n\n /**\n * Load a UISpec payload from IndexedDB with retry logic.\n * Returns null if not found or all retries fail.\n */\n async loadPayload(threadId: string, messageId: string): Promise<UISpec | null> {\n if (!this._db) return null;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n const payload = await this._db.loadPayload(threadId, messageId);\n if (payload) return payload.uiSpec;\n } catch {\n // Retry after delay\n }\n if (attempt < 2) {\n await new Promise((r) => setTimeout(r, 100));\n }\n }\n return null;\n }\n\n /**\n * Load favorited SKUs from IDB into the in-memory set.\n */\n async loadFavorites(userId: string, appId: string): Promise<void> {\n if (!this._db) return;\n try {\n const favs = await this._db.loadFavorites(userId, appId);\n for (const f of favs) {\n this.favoritedSkus.add(f.sku);\n this._favoritesCache.set(f.sku, f);\n }\n } catch {\n // Non-fatal — continue without favorites\n }\n }\n\n /** Returns favorited products ordered newest-first. */\n getFavoriteProducts(): FavoriteData[] {\n return [...this._favoritesCache.values()].sort((a, b) => {\n return (b.savedAt ?? '').localeCompare(a.savedAt ?? '');\n });\n }\n\n /**\n * Toggle a product's favorited state in IDB and in-memory set.\n */\n async toggleFavorite(userId: string, appId: string, sku: string, product: Record<string, unknown>): Promise<void> {\n // Always update in-memory state first — IDB is optional (persistence only)\n if (this.favoritedSkus.has(sku)) {\n this.favoritedSkus.delete(sku);\n this._favoritesCache.delete(sku);\n if (this._db) await this._db.removeFavorite(userId, appId, sku);\n } else {\n const data: FavoriteData = {\n userId,\n appId,\n sku,\n name: product['name'] as string | undefined,\n imageUrl: product['imageUrl'] as string | undefined,\n price: product['price'] as string | undefined,\n savedAt: new Date().toISOString(),\n };\n this.favoritedSkus.add(sku);\n this._favoritesCache.set(sku, data);\n if (this._db) await this._db.saveFavorite(data);\n }\n }\n\n close(): void {\n this._db?.close();\n this._db = null;\n }\n}\n","/**\n * KVKK notice filtering and caching helpers.\n *\n * KVKK (Kişisel Verilerin Korunması Kanunu) is Turkey's data protection law.\n * When the backend streams a response containing a KVKK notice, we:\n * 1. Strip the KVKK block from the visible bot text\n * 2. Show a banner on first encounter (per account)\n * 3. Mark it as shown in localStorage to avoid repeat banners\n */\n\nconst KVKK_STORAGE_KEY = 'gengage_kvkk_shown';\nconst KVKK_TEXT_MARKERS = ['kvkk', 'kişisel veri', 'kisisel veri'];\nconst KVKK_LAW_NUMBER_RE = /\\b6698\\b/;\n\nexport function containsKvkk(html: string): boolean {\n const lower = html.toLowerCase();\n return KVKK_TEXT_MARKERS.some((m) => lower.includes(m)) || KVKK_LAW_NUMBER_RE.test(lower);\n}\n\nexport function isKvkkShown(accountId: string): boolean {\n try {\n return localStorage.getItem(`${KVKK_STORAGE_KEY}_${accountId}`) === '1';\n } catch {\n return false;\n }\n}\n\nexport function markKvkkShown(accountId: string): void {\n try {\n localStorage.setItem(`${KVKK_STORAGE_KEY}_${accountId}`, '1');\n } catch {\n // localStorage unavailable — silently ignore\n }\n}\n\n/**\n * Strip the KVKK portion from bot HTML.\n * KVKK is typically wrapped in a `<div style=\"...\">` at the start.\n * We remove the first block-level element that contains a KVKK marker.\n */\nexport function stripKvkkBlock(html: string): string {\n const doc = new DOMParser().parseFromString(html, 'text/html');\n const body = doc.body;\n const children = Array.from(body.children);\n for (const child of children) {\n if (containsKvkk(child.textContent ?? '')) {\n child.remove();\n break; // Only strip the first KVKK block\n }\n }\n return body.innerHTML.trim();\n}\n\nexport function extractKvkkBlock(html: string): string | null {\n const doc = new DOMParser().parseFromString(html, 'text/html');\n for (const child of Array.from(doc.body.children)) {\n if (containsKvkk(child.textContent ?? '')) {\n return child.outerHTML;\n }\n }\n return null;\n}\n\nconst LOCALE_TO_LANGUAGE: Record<string, string> = {\n tr: 'TURKISH',\n en: 'ENGLISH',\n de: 'GERMAN',\n fr: 'FRENCH',\n};\n\nexport function localeToOutputLanguage(locale?: string): string {\n if (!locale) return 'TURKISH';\n return LOCALE_TO_LANGUAGE[locale.toLowerCase().slice(0, 2)] ?? 'TURKISH';\n}\n","/**\n * Chat widget — json-render catalog definition.\n *\n * This catalog describes all UI components the chat widget understands.\n * The backend streams `ui_spec` events whose element types must match names\n * defined here. Component implementations live in ./registry.tsx.\n *\n * HOW IT WORKS:\n * 1. Backend streams NDJSON events including `ui_spec` events.\n * 2. Each `ui_spec` event contains a `spec: UISpec` field.\n * 3. The `<ChatRenderer>` component feeds specs into json-render's `<Renderer>`.\n * 4. json-render looks up the element `type` in the registry and renders it.\n *\n * CUSTOMISING:\n * Fork this repo, edit ./registry.tsx to swap in your own components.\n * The catalog schema below stays the same — only the visual implementation changes.\n *\n * See: https://github.com/vercel-labs/json-render\n */\n\nimport { z } from 'zod';\n\n// ---------------------------------------------------------------------------\n// Component schemas (Zod)\n// ---------------------------------------------------------------------------\n\nexport const MessageBubbleSchema = z.object({\n role: z.enum(['user', 'assistant']),\n content: z.string(),\n timestamp: z.number().optional(),\n});\n\nexport const ProductCardSchema = z.object({\n sku: z.string(),\n name: z.string(),\n imageUrl: z.string().url().optional(),\n price: z.string().optional(),\n originalPrice: z.string().optional(),\n url: z.string().url(),\n /** Override CTA label (default: \"View product\") */\n ctaLabel: z.string().optional(),\n});\n\nexport const ActionButtonsSchema = z.object({\n buttons: z.array(\n z.object({\n label: z.string(),\n /** Opaque action payload forwarded to the backend when clicked. */\n action: z.object({\n title: z.string(),\n type: z.string(),\n payload: z.unknown().optional(),\n }),\n }),\n ),\n});\n\nexport const TypingIndicatorSchema = z.object({});\n\nexport const DividerSchema = z.object({\n label: z.string().optional(),\n});\n\nconst ComparisonProductSchema = z.object({\n sku: z.string(),\n name: z.string(),\n price: z.string(),\n imageUrl: z.string().optional(),\n rating: z.number().optional(),\n reviewCount: z.number().optional(),\n});\n\nexport const ComparisonTableSchema = z.object({\n recommended: ComparisonProductSchema,\n products: z.array(ComparisonProductSchema),\n attributes: z.array(\n z.object({\n label: z.string(),\n values: z.array(z.string()),\n }),\n ),\n highlights: z.array(z.string()),\n specialCases: z.array(z.string()).optional(),\n recommendedText: z.string().optional(),\n winnerHits: z\n .record(\n z.string(),\n z.object({\n positive: z.array(z.string()).optional(),\n negative: z.array(z.string()).optional(),\n }),\n )\n .optional(),\n productActions: z\n .record(\n z.string(),\n z.object({\n title: z.string(),\n type: z.string(),\n payload: z.unknown().optional(),\n }),\n )\n .optional(),\n});\n\nexport const SentimentLabelSchema = z.object({\n label: z.string(),\n sentiment: z.enum(['positive', 'negative', 'neutral']).optional(),\n});\n\nexport const AITopPickItemSchema = z.object({\n product: z.record(z.string(), z.unknown()),\n role: z.string().optional(),\n reason: z.string().optional(),\n labels: z.array(SentimentLabelSchema).optional(),\n expertQualityScore: z.number().optional(),\n reviewHighlight: z.string().optional(),\n action: z\n .object({\n title: z.string(),\n type: z.string(),\n payload: z.unknown().optional(),\n })\n .optional(),\n});\n\nexport const AITopPicksSchema = z.object({\n suggestions: z.array(AITopPickItemSchema),\n});\n\nexport const GroundingReviewCardSchema = z.object({\n title: z.string().optional(),\n text: z.string().optional(),\n reviewCount: z.string().optional(),\n action: z.object({ title: z.string(), type: z.string(), payload: z.unknown().optional() }),\n});\n\nexport const AIGroupingCardsSchema = z.object({\n entries: z.array(\n z.object({\n name: z.string(),\n image: z.string().optional(),\n description: z.string().optional(),\n action: z.object({ title: z.string(), type: z.string(), payload: z.unknown().optional() }),\n }),\n ),\n});\n\nexport const AISuggestedSearchCardsSchema = z.object({\n entries: z.array(\n z.object({\n shortName: z.string(),\n detailedMessage: z.string().optional(),\n whyDifferent: z.string().optional(),\n image: z.string().optional(),\n action: z.object({ title: z.string(), type: z.string(), payload: z.unknown().optional() }),\n }),\n ),\n});\n\nconst ActionPayloadSchema = z.object({\n title: z.string(),\n type: z.string(),\n payload: z.unknown().optional(),\n});\n\nconst ProductVariantSchema = z.object({\n name: z.string().optional(),\n value: z.string().optional(),\n variant_name: z.string().optional(),\n sku: z.string().optional(),\n price: z.union([z.number(), z.string()]).optional(),\n});\n\nexport const ProductDetailsPanelSchema = z.object({\n product: z\n .object({\n sku: z.string().optional(),\n name: z.string().optional(),\n images: z.array(z.string()).optional(),\n imageUrl: z.string().optional(),\n rating: z.number().optional(),\n reviewCount: z.number().optional(),\n price: z.string().optional(),\n originalPrice: z.string().optional(),\n price_async: z.boolean().optional(),\n inStock: z.boolean().optional(),\n promotions: z.array(z.string()).optional(),\n variants: z.array(ProductVariantSchema).optional(),\n url: z.string().optional(),\n cartCode: z.string().optional(),\n description: z.string().optional(),\n specifications: z\n .union([z.record(z.string(), z.string()), z.array(z.object({ key: z.string(), value: z.string() }))])\n .optional(),\n })\n .optional(),\n action: ActionPayloadSchema.optional(),\n});\n\nexport const ProductGridSchema = z.object({\n endOfList: z.boolean().optional(),\n});\n\nconst ReviewItemSchema = z.object({\n review_class: z.string().optional(),\n review_text: z.string().optional(),\n review_rating: z.union([z.string(), z.number()]).optional(),\n review_tag: z.string().optional(),\n});\n\nexport const ReviewHighlightsSchema = z.object({\n reviews: z.array(ReviewItemSchema).optional(),\n});\n\nexport const ProsAndConsSchema = z.object({\n productName: z.string().optional(),\n pros: z.array(z.string()).optional(),\n cons: z.array(z.string()).optional(),\n});\n\nexport const CategoriesContainerSchema = z.object({\n groups: z\n .array(\n z.object({\n groupName: z.string(),\n products: z.array(z.record(z.string(), z.unknown())).optional(),\n }),\n )\n .optional(),\n filterTags: z\n .array(\n z.object({\n title: z.string(),\n action: ActionPayloadSchema.optional(),\n }),\n )\n .optional(),\n});\n\nexport const HandoffNoticeSchema = z.object({\n summary: z.string().optional(),\n products_discussed: z.array(z.string()).optional(),\n user_sentiment: z.string().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Catalog definition\n//\n// When @json-render/core ships a stable defineCatalog() API, replace this\n// plain object with the official defineCatalog() call. For now the schema\n// map acts as both documentation and runtime validation.\n// ---------------------------------------------------------------------------\n\nexport const chatCatalog = {\n components: {\n MessageBubble: {\n schema: MessageBubbleSchema,\n description: 'A single chat message bubble for user or assistant turns.',\n },\n ProductCard: {\n schema: ProductCardSchema,\n description: 'A product card rendered inline in the chat stream.',\n },\n ActionButtons: {\n schema: ActionButtonsSchema,\n description: 'A horizontal row of quick-reply action buttons.',\n },\n TypingIndicator: {\n schema: TypingIndicatorSchema,\n description: 'An animated indicator shown while the assistant is typing.',\n },\n Divider: {\n schema: DividerSchema,\n description: 'A horizontal rule with an optional label.',\n },\n ComparisonTable: {\n schema: ComparisonTableSchema,\n description: 'A product comparison table with recommended pick, attribute rows, and highlights.',\n },\n AITopPicks: {\n schema: AITopPicksSchema,\n description: 'Rich AI-curated product suggestion cards with roles, sentiment labels, scores, and review quotes.',\n },\n GroundingReviewCard: {\n schema: GroundingReviewCardSchema,\n description: 'A card showing review grounding data with review count and CTA.',\n },\n AIGroupingCards: {\n schema: AIGroupingCardsSchema,\n description: 'Category grouping cards with images and labels for product discovery.',\n },\n AISuggestedSearchCards: {\n schema: AISuggestedSearchCardsSchema,\n description: 'Suggested search cards with images, descriptions, and differentiation.',\n },\n ProductDetailsPanel: {\n schema: ProductDetailsPanelSchema,\n description: 'Full product detail view with images, specs, variants, and purchase actions.',\n },\n ProductGrid: {\n schema: ProductGridSchema,\n description: 'A scrollable grid of ProductCard children with optional \"more\" pagination.',\n },\n ReviewHighlights: {\n schema: ReviewHighlightsSchema,\n description: 'A list of highlighted customer reviews with sentiment and ratings.',\n },\n ProsAndCons: {\n schema: ProsAndConsSchema,\n description: 'A pros and cons list for a product.',\n },\n CategoriesContainer: {\n schema: CategoriesContainerSchema,\n description: 'Tabbed product groups with optional filter tag buttons.',\n },\n HandoffNotice: {\n schema: HandoffNoticeSchema,\n description: 'A notice shown when the conversation is escalated to a human agent.',\n },\n },\n} as const;\n\nexport type ChatCatalog = typeof chatCatalog;\nexport type ChatComponentName = keyof ChatCatalog['components'];\n","/**\n * Chat widget -- public entry point.\n *\n * Renders a floating launcher button + slide-in chat drawer inside Shadow DOM\n * for CSS isolation. Handles streaming NDJSON from the backend.\n */\n\nimport type { ActionPayload, PageContext, StreamEvent, StreamEventAction, UISpec } from '../common/types.js';\nimport type { ChatTransportConfig } from '../common/api-paths.js';\nimport type { ActionRouterOptions } from '../common/action-router.js';\nimport type { UISpecRenderHelpers } from '../common/renderer/index.js';\nimport type { BridgeMessage, CommunicationBridgeOptions } from '../common/communication-bridge.js';\nimport type { BackendRequestMeta } from './api.js';\nimport { mergeUISpecRegistry } from '../common/renderer/index.js';\nimport { BaseWidget } from '../common/widget-base.js';\nimport { dispatch } from '../common/events.js';\nimport { uuidv7 } from '../common/uuidv7.js';\nimport { CommunicationBridge } from '../common/communication-bridge.js';\nimport { routeStreamAction } from '../common/action-router.js';\nimport {\n streamStartEvent,\n streamChunkEvent,\n streamDoneEvent,\n streamErrorEvent,\n streamUiSpecEvent,\n llmUsageEvent,\n meteringIncrementEvent,\n chatHistorySnapshotEvent,\n} from '../common/analytics-events.js';\nimport { sanitizeHtml, isSafeUrl } from '../common/safe-html.js';\nimport { validateImageFile } from './attachment-utils.js';\nimport { sendChatMessage, enrichActionPayload } from './api.js';\nimport { ChatDrawer } from './components/ChatDrawer.js';\nimport { createLauncher } from './components/Launcher.js';\nimport type { LauncherElements } from './components/Launcher.js';\nimport { playTtsAudio } from '../common/tts-player.js';\nimport type { AudioHandle } from '../common/tts-player.js';\nimport {\n renderUISpec,\n createDefaultChatUISpecRegistry,\n defaultChatUnknownUISpecRenderer,\n} from './components/renderUISpec.js';\nimport type { TypewriterHandle } from './components/typewriter.js';\nimport { typewriteHtml } from './components/typewriter.js';\nimport { linkProductMentions } from './components/productMentionLinker.js';\nimport { isInputAreaAction } from './components/actionClassifier.js';\nimport type { ThumbnailEntry } from './components/ThumbnailsColumn.js';\nimport {\n createChoicePrompter,\n isChoicePrompterDismissed,\n isChoicePrompterGloballyDismissed,\n} from './components/ChoicePrompter.js';\nimport type {\n ChatWidgetConfig,\n ChatMessage,\n ChatI18n,\n ChatUISpecRenderContext,\n ChatUISpecRegistry,\n ProductSortState,\n} from './types.js';\nimport { GengageIndexedDB } from '../common/indexed-db.js';\nimport { CHAT_I18N_TR, resolveChatLocale } from './locales/index.js';\nimport { ExtendedModeManager } from './extendedModeManager.js';\nimport { PanelManager, determinePanelUpdateAction } from './panel-manager.js';\nimport { SessionPersistence } from './session-persistence.js';\nimport {\n containsKvkk,\n isKvkkShown,\n markKvkkShown,\n stripKvkkBlock,\n extractKvkkBlock,\n localeToOutputLanguage,\n} from './kvkk.js';\n\nimport chatStyles from './components/chat.css?inline';\nimport * as ga from '../common/ga-datalayer.js';\n\n/** Validate that a string is a safe CSS color value (no url(), expression(), etc.). */\nfunction isSafeCSSColor(value: string): boolean {\n if (value.length > 120) return false;\n // Only allow characters found in valid CSS colors: hex digits, letters, parens,\n // commas, dots, spaces, percent, slash (modern color syntax), and hyphens.\n return /^[a-zA-Z0-9#(),.\\s%/\\-]+$/.test(value);\n}\n\n/** Lightweight runtime check: value looks like an ActionPayload (has a string `type`). */\nfunction isActionLike(value: unknown): value is ActionPayload {\n return typeof value === 'object' && value !== null && typeof (value as Record<string, unknown>).type === 'string';\n}\n\n/**\n * Floating AI chat widget with streaming NDJSON responses, product cards, and comparison tables.\n *\n * @example\n * ```ts\n * import { GengageChat, bootstrapSession } from '@gengage/assistant-fe';\n *\n * const chat = new GengageChat();\n * await chat.init({\n * accountId: 'mystore',\n * middlewareUrl: 'https://chat.gengage.ai',\n * session: { sessionId: bootstrapSession() },\n * });\n * chat.open(); // Programmatically open the drawer\n * ```\n */\nexport class GengageChat extends BaseWidget<ChatWidgetConfig> {\n private _shadow: ShadowRoot | null = null;\n private _rootEl: HTMLElement | null = null;\n private _launcher: LauncherElements | null = null;\n private _drawer: ChatDrawer | null = null;\n private _bridge: CommunicationBridge | null = null;\n private _drawerVisible = false;\n private _messages: ChatMessage[] = [];\n // Bot text accumulation is now closure-local inside _sendAction to prevent\n // corruption when concurrent preservePanel streams write simultaneously.\n private _currentMessageId = 0;\n private _abortControllers = new Set<AbortController>();\n /** Current thread cursor — only messages with threadId <= this are visible. */\n private _currentThreadId: string | null = null;\n /** Most recent threadId ever created — used to detect branch points. */\n private _lastThreadId: string | null = null;\n /** Timestamp when the chat session was created (ISO 8601). */\n private _chatCreatedAt = '';\n /** Last backend-streamed context object — sent back with every request. */\n private _lastBackendContext: import('../common/types.js').BackendContext | null = null;\n private _productSort: ProductSortState = { type: 'related' };\n private _lastSku: string | undefined;\n private _comparisonSelectMode = false;\n private _comparisonSelectedSkus: string[] = [];\n /** SKUs of products the user has viewed across panel product grids. */\n private _viewedProductSkus = new Set<string>();\n private _thumbnailEntries: ThumbnailEntry[] = [];\n private _choicePrompterEl: HTMLElement | null = null;\n private _openState: 'full' | 'half' = 'full';\n private _mobileBreakpoint = 768;\n private _isMobileViewport = false;\n private _pdpLaunched = false;\n /** True while the initial silent PDP launch request is in flight. */\n private _pdpPrimingInFlight = false;\n /** User messages queued until silent PDP priming completes. */\n private _queuedUserMessages: Array<{ text: string; attachment?: File }> = [];\n private _productContextUnavailableSku: string | null = null;\n private _i18n: ChatI18n = CHAT_I18N_TR;\n private _extendedModeManager: ExtendedModeManager | null = null;\n /** Active typewriter animation handle — cancelled on new action or drawer close. */\n private _activeTypewriter: TypewriterHandle | null = null;\n /** Active TTS audio handle — stopped on new stream start or drawer close. */\n private _activeTtsHandle: AudioHandle | null = null;\n /** Active request thread ID — guards against stale stream events from cancelled requests. */\n private _activeRequestThreadId: string | null = null;\n /** Accumulated SKU → product item map from outputText events. */\n private _skuToProductItem: Record<string, Record<string, unknown>> = {};\n /** Current conversation mode from the latest outputText event. */\n private _conversationMode: string | null = null;\n /** Whether initialization (including IDB restore) has completed. */\n private _initComplete = false;\n /** Queue of actions received before init completes. Max 10, FIFO discard. */\n private _pendingActions: Array<{\n action: ActionPayload;\n options?: { silent?: boolean; attachment?: File } | undefined;\n }> = [];\n /** Supplemental context received from host via bridge (e.g. PDP detail context). */\n private _bridgeContext: Record<string, unknown> | null = null;\n /** Last cart quantity received from host via bridge. */\n private _cartQuantity: number | null = null;\n private _threadsWithFirstBot: Set<string> = new Set();\n /** Panel state manager (snapshots, topbar, navigation). */\n private _panel: PanelManager | null = null;\n /** Client-side panel navigation stack for local drilldowns (e.g. card → detail). Max 10 entries.\n * Stores rebuild info (UISpec or kind) instead of DOM clones so back navigation\n * produces fresh elements with live event listeners. */\n private _localPanelHistory: Array<{\n source: { kind: 'spec'; spec: import('../common/types.js').UISpec } | { kind: 'favorites' };\n title: string;\n }> = [];\n private static readonly _MAX_PANEL_HISTORY = 10;\n /** Tracks how the current panel content was produced, for history/error-recovery rebuild. */\n private _currentPanelSource:\n | { kind: 'spec'; spec: import('../common/types.js').UISpec }\n | { kind: 'favorites' }\n | null = null;\n /** IndexedDB session persistence manager. */\n private _session: SessionPersistence | null = null;\n /** Registered event callbacks (GA4 event hooks). Key = event name, value = set of callbacks. */\n private _eventCallbacks = new Map<string, Set<(detail: Record<string, unknown>) => boolean | Promise<boolean>>>();\n /** Last sent action+options for retry on error. */\n private _lastSentAction: {\n action: ActionPayload;\n options?: { silent?: boolean; attachment?: File; preservePanel?: boolean; isPdpPrime?: boolean } | undefined;\n } | null = null;\n /** Consecutive identical error counter for account-inactive detection. */\n private _consecutiveErrorCount = 0;\n /** Last error message text for deduplication. */\n private _lastErrorMessage = '';\n\n protected async onInit(config: ChatWidgetConfig): Promise<void> {\n this._i18n = this._resolveI18n(config);\n this._chatCreatedAt = new Date().toISOString();\n\n // Create Shadow DOM for CSS isolation\n this._shadow = this.root.attachShadow({ mode: 'open' });\n\n // Inject styles\n const style = document.createElement('style');\n style.textContent = chatStyles;\n this._shadow.appendChild(style);\n\n // Create root container\n const rootEl = document.createElement('div');\n rootEl.className = 'gengage-chat-root';\n this._rootEl = rootEl;\n this._shadow.appendChild(rootEl);\n\n // Create launcher (floating variant only — inline/overlay are triggered programmatically)\n const variant = config.variant ?? 'floating';\n if (variant === 'floating') {\n const launcherOpts: import('./components/Launcher.js').LauncherOptions = {\n onClick: () => this.open(),\n ariaLabel: this._i18n.openButton,\n };\n if (config.launcherImageUrl !== undefined) launcherOpts.imageUrl = config.launcherImageUrl;\n else if (config.launcherSvg !== undefined) launcherOpts.svgMarkup = config.launcherSvg;\n if (config.hideMobileLauncher !== undefined) launcherOpts.hideMobile = config.hideMobileLauncher;\n if (config.mobileBreakpoint !== undefined) launcherOpts.mobileBreakpoint = config.mobileBreakpoint;\n if (config.launcherTooltip !== undefined) launcherOpts.tooltip = config.launcherTooltip;\n this._launcher = createLauncher(launcherOpts);\n rootEl.appendChild(this._launcher.container);\n }\n\n // Overlay variant wraps drawer in a backdrop for full-screen modal\n if (variant === 'overlay') {\n rootEl.classList.add('gengage-chat--overlay');\n }\n\n // Create drawer (hidden initially for floating/overlay, visible for inline)\n const drawerContainer = document.createElement('div');\n rootEl.appendChild(drawerContainer);\n\n this._drawer = new ChatDrawer(drawerContainer, {\n i18n: this._i18n,\n onSend: (text, attachment) => this._sendMessage(text, attachment),\n onClose: () => this.close(),\n onAttachment: (file) => this._handleAttachment(file),\n onPanelToggle: () => {\n this._drawer?.persistPanelState(config.accountId);\n },\n onRollback: (messageId) => this._handleRollback(messageId),\n onPanelBack: () => this._navigatePanelBack(),\n onPanelForward: () => this._panel?.navigateForward(),\n headerTitle: config.headerTitle,\n headerAvatarUrl: config.headerAvatarUrl,\n launcherImageUrl: config.launcherImageUrl,\n headerBadge: config.headerBadge,\n headerCartUrl: config.headerCartUrl,\n headerFavoritesToggle: config.headerFavoritesToggle,\n onCartClick: () => {\n if (config.headerCartUrl) {\n this._saveSessionAndOpenURL(config.headerCartUrl);\n } else {\n config.onCartClick?.();\n }\n },\n onFavoritesClick: () => {\n ga.trackLikeList();\n config.onFavoritesClick?.();\n this._openFavoritesPanel();\n },\n getMobileState: () => this._openState ?? 'full',\n getMobileViewport: () => this._isMobileViewport,\n onMobileSnap: (state) => {\n if (state === 'close') {\n this.close();\n } else {\n this._openState = state;\n this._applyOpenStateClasses();\n }\n },\n onThumbnailClick: (threadId) => this._rollbackToThread(threadId),\n onLinkClick: (url) => {\n this._saveSessionAndOpenURL(url);\n },\n voiceEnabled: config.voiceEnabled,\n voiceLang: config.locale\n ? `${config.locale.split('-')[0] ?? 'tr'}-${(config.locale.split('-')[1] ?? config.locale.split('-')[0] ?? 'TR').toUpperCase()}`\n : undefined,\n onNewChat: () => {\n this._abortControllers.forEach((c) => c.abort());\n this._abortControllers.clear();\n this._activeTypewriter?.cancel();\n this._activeTypewriter = null;\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = null;\n this._messages = [];\n this._drawer?.clearMessages();\n this._currentThreadId = uuidv7();\n this._lastThreadId = this._currentThreadId;\n this._choicePrompterEl?.remove();\n this._choicePrompterEl = null;\n this._viewedProductSkus.clear();\n this._drawer?.clearPanel();\n this._consecutiveErrorCount = 0;\n this._lastErrorMessage = '';\n this._thumbnailEntries = [];\n this._drawer?.setThumbnails([]);\n this._panel!.snapshots.clear();\n this._panel!.threads = [];\n // Re-show welcome if configured\n this._showWelcomeIfNeeded();\n },\n });\n\n // Extended mode manager for host PDP maximize/minimize\n this._extendedModeManager = new ExtendedModeManager({\n onChange: (extended) => this._panel?.notifyExtension(extended),\n productDetailsInPanel: config.isDemoWebsite ?? false,\n });\n\n // Panel manager for snapshot/topbar/navigation state\n this._panel = new PanelManager({\n drawer: () => this._drawer,\n shadow: () => this._shadow,\n currentThreadId: () => this._currentThreadId,\n bridge: () => this._bridge,\n extendedModeManager: () => this._extendedModeManager,\n i18n: () => this._i18n,\n rollbackToThread: (tid) => this._rollbackToThread(tid),\n });\n\n // Hide drawer initially for floating/overlay variants\n if (variant !== 'inline') {\n this._drawer.getElement().classList.add('gengage-chat-drawer--hidden');\n }\n\n // Restore panel state from session\n const restoredCollapsedPanel = this._drawer.restorePanelState(config.accountId);\n\n // Panel mode: 'collapsed' starts hidden, 'expanded' starts open, 'auto' (default) expands when content arrives\n const panelMode = config.panelMode ?? 'auto';\n if (panelMode === 'collapsed') {\n this._drawer.setPanelCollapsed(true);\n } else if (panelMode === 'expanded') {\n this._drawer.setForceExpanded();\n } else if (!restoredCollapsedPanel) {\n // 'auto': panel starts collapsed — will expand when content arrives via stream\n // (prevents empty panel being visible on page load/session restore)\n }\n\n // Restore session if an explicit handoff signal exists (e.g. SimRel product navigation)\n const restoreSessionId = sessionStorage.getItem('gengage_restore_session_id');\n const restoreSku = sessionStorage.getItem('gengage_restore_sku');\n const hasHandoff = !!(restoreSessionId && restoreSku);\n if (hasHandoff) {\n sessionStorage.removeItem('gengage_restore_session_id');\n sessionStorage.removeItem('gengage_restore_sku');\n }\n\n // IndexedDB persistence — best-effort, non-fatal\n try {\n const idb = new GengageIndexedDB();\n await idb.open();\n this._session = new SessionPersistence(idb);\n await this._restoreFromIndexedDB(hasHandoff);\n } catch {\n // IndexedDB unavailable — continue without persistence\n this._session = new SessionPersistence(null);\n }\n\n // Register public API on window\n this._registerPublicAPI();\n\n // Apply mobileInitialState if configured\n if (config.mobileInitialState !== undefined) {\n this._openState = config.mobileInitialState;\n }\n this._mobileBreakpoint = config.mobileBreakpoint ?? 768;\n\n this._syncViewportState();\n const onResize = () => this._syncViewportState();\n window.addEventListener('resize', onResize, { passive: true });\n this.addCleanup(() => window.removeEventListener('resize', onResize));\n\n // iOS visualViewport keyboard handling\n if (window.visualViewport) {\n const onViewportResize = () => {\n if (!this._drawerVisible || !this._isMobileViewport) return;\n const el = this._drawer?.getElement();\n if (!el) return;\n const offset = window.innerHeight - (window.visualViewport?.height ?? window.innerHeight);\n el.style.setProperty('--gengage-keyboard-offset', `${Math.max(0, offset)}px`);\n };\n window.visualViewport.addEventListener('resize', onViewportResize);\n this.addCleanup(() => window.visualViewport?.removeEventListener('resize', onViewportResize));\n }\n\n // Inline variant starts visible\n if (variant === 'inline') {\n this._drawerVisible = true;\n this.isVisible = true;\n this._applyOpenStateClasses();\n }\n\n // Communication bridge for host ↔ widget messaging\n const bridgeOpts: CommunicationBridgeOptions = {\n namespace: 'chat',\n onMessage: (msg) => this._handleBridgeMessage(msg),\n };\n if (config.allowedOrigins !== undefined) bridgeOpts.allowedOrigins = config.allowedOrigins;\n this._bridge = new CommunicationBridge(bridgeOpts);\n\n // Track initial SKU for page-change detection\n this._lastSku = this.config.pageContext?.sku;\n\n // Mark init complete and drain pending actions queue\n this._initComplete = true;\n for (const pending of this._pendingActions) {\n this._sendAction(pending.action, pending.options);\n }\n this._pendingActions = [];\n\n dispatch('gengage:chat:ready', {});\n ga.trackInit('chat');\n config.onReady?.();\n }\n\n protected onUpdate(context: Partial<PageContext>): void {\n if (context.sku !== undefined && context.sku !== this._lastSku) {\n this._lastSku = context.sku;\n this._resetForNewPage();\n }\n }\n\n protected onShow(): void {\n this._showDrawer();\n this.emit('open');\n dispatch('gengage:chat:open', { state: this._openState });\n ga.trackShow('chat');\n this.config.onOpen?.();\n\n // Show welcome message on first open with empty history\n this._showWelcomeIfNeeded();\n\n // Auto-launch PDP context on first open when SKU is available\n if (!this._pdpLaunched && this.config.pageContext?.sku) {\n this._pdpLaunched = true;\n this._pdpPrimingInFlight = true;\n this._sendAction(\n {\n title: '',\n type: 'launchSingleProduct',\n payload: { sku: this.config.pageContext.sku },\n },\n { silent: true, isPdpPrime: true },\n );\n }\n }\n\n protected onHide(): void {\n // Floating mode should only hide the drawer, not the launcher button.\n // BaseWidget.hide() toggles `root.style.display = 'none'`, so undo it here.\n if ((this.config.variant ?? 'floating') === 'floating') {\n this.root.style.display = '';\n }\n this._hideDrawer();\n this.emit('close');\n dispatch('gengage:chat:close', {});\n this.config.onClose?.();\n }\n\n protected onDestroy(): void {\n this._abortControllers.forEach((c) => c.abort());\n this._abortControllers.clear();\n this._activeTypewriter?.cancel();\n this._activeTypewriter = null;\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = null;\n this._drawer?.destroy();\n this._drawer = null;\n this._bridge?.destroy();\n this._bridge = null;\n this._extendedModeManager = null;\n this._panel?.destroy();\n this._panel = null;\n this._session?.close();\n this._session = null;\n this._localPanelHistory = [];\n this._currentPanelSource = null;\n if (window.gengage) {\n delete window.gengage.chat;\n }\n if (this._shadow) {\n this._shadow.innerHTML = '';\n this._shadow = null;\n }\n this._rootEl = null;\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n open(options?: { state?: 'full' | 'half'; initialMessage?: string }): void {\n if (options?.state !== undefined) {\n this._openState = options.state;\n if (this._drawerVisible) {\n this._applyOpenStateClasses();\n }\n }\n this.show();\n if (options?.initialMessage !== undefined) {\n this._sendMessage(options.initialMessage);\n }\n }\n\n openWithAction(action: ActionPayload, options?: { sku?: string; state?: 'full' | 'half' }): void {\n if (options?.sku !== undefined) {\n this.update({ sku: options.sku });\n }\n // Mark PDP as launched since we're sending an explicit action\n this._pdpLaunched = true;\n // Action-triggered opens default to half-sheet on mobile\n if (options?.state !== undefined) {\n this._openState = options.state;\n } else if (this._isMobileViewport) {\n this._openState = 'half';\n }\n this.show();\n if (this._drawerVisible) {\n this._applyOpenStateClasses();\n }\n this._sendAction(action);\n }\n\n /** Send a user message programmatically (same flow as typing + submit). */\n sendMessage(text: string): void {\n this._sendMessage(text);\n }\n\n /** Send a backend action programmatically. */\n sendAction(action: ActionPayload, options?: { silent?: boolean }): void {\n this._sendAction(action, options);\n }\n\n close(): void {\n this.hide();\n }\n\n saveSession(sessionId: string, sku: string): void {\n sessionStorage.setItem('gengage_restore_session_id', sessionId);\n sessionStorage.setItem('gengage_restore_sku', sku);\n }\n\n get isOpen(): boolean {\n return this._drawerVisible;\n }\n\n /**\n * Register a callback for a GA4 event name (e.g. 'gengage-cart-add').\n * The callback receives the event detail and should return true (success) or false (failure).\n * For add-to-cart, failure triggers an error message in the chat.\n * @returns unsubscribe function\n */\n addCallback(\n eventName: string,\n callback: (detail: Record<string, unknown>) => boolean | Promise<boolean>,\n ): () => void {\n let set = this._eventCallbacks.get(eventName);\n if (!set) {\n set = new Set();\n this._eventCallbacks.set(eventName, set);\n }\n set.add(callback);\n return () => {\n set!.delete(callback);\n if (set!.size === 0) this._eventCallbacks.delete(eventName);\n };\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n /** Reset all chat state when navigating to a different SKU/page. */\n private _resetForNewPage(): void {\n // Invalidate any in-flight stream callbacks so they discard themselves\n // via the `threadId !== this._activeRequestThreadId` guard.\n this._activeRequestThreadId = null;\n // Abort in-flight streams\n this._abortControllers.forEach((c) => c.abort());\n this._abortControllers.clear();\n // Cancel active typewriter/TTS\n this._activeTypewriter?.cancel();\n this._activeTypewriter = null;\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = null;\n // Clear messages\n this._messages.length = 0;\n this._drawer?.clearMessages();\n // Clear panel\n this._drawer?.clearPanel();\n this._currentPanelSource = null;\n this._panel!.snapshots.clear();\n this._panel!.threads = [];\n // Clear thumbnails\n this._thumbnailEntries = [];\n this._drawer?.setThumbnails([]);\n // Reset comparison state\n this._comparisonSelectMode = false;\n this._comparisonSelectedSkus = [];\n this._viewedProductSkus.clear();\n // Reset thread cursors\n this._currentThreadId = null;\n this._lastThreadId = null;\n this._lastBackendContext = null;\n this._chatCreatedAt = new Date().toISOString();\n // Allow PDP auto-launch for new SKU\n this._pdpLaunched = false;\n this._pdpPrimingInFlight = false;\n this._queuedUserMessages = [];\n this._productContextUnavailableSku = null;\n }\n\n private _handleBridgeMessage(msg: BridgeMessage): void {\n switch (msg.type) {\n case 'openChat':\n this.open();\n break;\n case 'closeChat':\n this.close();\n break;\n case 'startNewChatWithLauncherAction': {\n // Start a new chat with a preset launcher action.\n // Legacy sends payload as the action directly: { type, title, requestDetails }\n // Clean-room also accepts nested: { action: { type, title, ... } }\n const payload = msg.payload as Record<string, unknown> | undefined;\n const nested = payload?.action;\n // Prefer nested shape, fall back to payload-is-action (legacy compat)\n const action = isActionLike(nested) ? nested : isActionLike(payload) ? payload : null;\n if (action) {\n this._sendAction(action, { silent: true });\n }\n this.open();\n break;\n }\n case 'startNewChatWithDetailContext': {\n // Start chat with product detail context (e.g., from PDP page).\n // Legacy shape: { action: {...}, sku: \"...\" }\n const ctx = msg.payload as Record<string, unknown> | undefined;\n if (ctx && typeof ctx === 'object') {\n this._bridgeContext = ctx;\n // Extract SKU and update page context so auto-PDP launch uses it\n const sku = ctx.sku as string | undefined;\n if (sku) {\n this.update({ sku });\n }\n // Extract and send the action (legacy sent action + premade context)\n if (isActionLike(ctx.action)) {\n this._pdpLaunched = true; // Skip auto-launch, explicit action takes priority\n this.open();\n this._sendAction(ctx.action);\n break;\n }\n }\n this.open();\n break;\n }\n case 'launcherAction': {\n // Send action to active chat from external trigger\n const payload = msg.payload as Record<string, unknown> | undefined;\n const action = payload?.action;\n if (action && typeof action === 'object' && 'type' in (action as Record<string, unknown>)) {\n this._sendAction(action as ActionPayload);\n }\n break;\n }\n case 'scrollToBottom':\n this._drawer?.scrollToBottomIfNeeded();\n break;\n case 'addToCardHandler': {\n // Host notifies widget of add-to-cart result\n this._bridge?.send('addToCardResult', msg.payload);\n break;\n }\n case 'cartQuantityHandler': {\n // Host sends updated cart quantity\n const payload = msg.payload as Record<string, unknown> | undefined;\n if (payload && 'quantity' in payload && typeof payload.quantity === 'number') {\n this._cartQuantity = payload.quantity;\n }\n break;\n }\n case 'minimizeRequestedByUser':\n this._extendedModeManager?.setHiddenByUser(true);\n break;\n case 'bgColorChange': {\n const payload = msg.payload as Record<string, unknown> | undefined;\n const color = payload?.color;\n if (typeof color === 'string' && isSafeCSSColor(color) && this._shadow) {\n (this._shadow.host as HTMLElement).style.setProperty('--gengage-chat-bg', color);\n }\n break;\n }\n default:\n break;\n }\n }\n\n private _registerPublicAPI(): void {\n if (!window.gengage) window.gengage = {};\n window.gengage.chat = {\n open: (opts) => this.open(opts),\n openWithAction: (action, opts) => this.openWithAction(action, opts),\n sendMessage: (text) => this.sendMessage(text),\n sendAction: (action, opts) => this.sendAction(action, opts),\n close: () => this.close(),\n saveSession: (sid, sku) => this.saveSession(sid, sku),\n get isOpen() {\n return false;\n }, // Placeholder, overridden below\n on: (event, handler) => this.on(event, handler),\n trackCheckout: (type, data) => this.trackCheckout(type, data),\n flushMeteringSummary: (data) => this.flushMeteringSummary(data),\n addCallback: (eventName, callback) => this.addCallback(eventName, callback),\n };\n // Fix isOpen getter to reflect actual state\n Object.defineProperty(window.gengage.chat, 'isOpen', {\n get: () => this._drawerVisible,\n });\n }\n\n private _showDrawer(): void {\n if (this._drawerVisible) return;\n this._drawerVisible = true;\n const el = this._drawer?.getElement();\n if (el) {\n el.classList.remove('gengage-chat-drawer--hidden');\n }\n this._applyOpenStateClasses();\n this._drawer?.trapFocus();\n if (!(this._isMobileViewport && this._openState === 'half')) {\n this._drawer?.focusInput();\n }\n this._extendedModeManager?.setChatShown(true);\n }\n\n /** Show welcome message and starter pills on first open with empty history. */\n private _showWelcomeIfNeeded(): void {\n if (this._messages.length !== 0 || !this.config.welcomeMessage) return;\n const welcomeMsg: ChatMessage = {\n id: uuidv7(),\n role: 'assistant',\n content: this.config.welcomeMessage,\n timestamp: Date.now(),\n status: 'done',\n };\n this._messages.push(welcomeMsg);\n this._drawer?.addMessage(welcomeMsg);\n if (this.config.welcomeActions?.length) {\n this._drawer?.setPills(\n this.config.welcomeActions.map((label) => ({\n label,\n onAction: () => this._sendMessage(label),\n })),\n );\n }\n }\n\n private _hideDrawer(): void {\n if (!this._drawerVisible) return;\n this._drawer?.releaseFocus();\n this._activeTypewriter?.cancel();\n this._activeTypewriter = null;\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = null;\n this._drawerVisible = false;\n // Reset mobile open state so next launcher open defaults to full-screen\n this._openState = 'full';\n // Don't clear panel — preserve it so reopening the drawer shows the same\n // panel state. clearPanel() is reserved for SKU/page resets and stale\n // loading skeleton cleanup after streams.\n const el = this._drawer?.getElement();\n if (el) {\n el.classList.add('gengage-chat-drawer--hidden');\n }\n this._applyOpenStateClasses();\n this._extendedModeManager?.setChatShown(false);\n }\n\n private _syncViewportState(): void {\n if (!this._rootEl) return;\n this._isMobileViewport = window.innerWidth <= this._mobileBreakpoint;\n this._rootEl.classList.toggle('gengage-chat-root--mobile', this._isMobileViewport);\n\n if (this._launcher) {\n const hideLauncher = this._isMobileViewport && this.config.hideMobileLauncher === true;\n this._launcher.container.classList.toggle('gengage-chat-launcher--hidden-mobile', hideLauncher);\n }\n\n this._applyOpenStateClasses();\n }\n\n private _applyOpenStateClasses(): void {\n if (!this._rootEl) return;\n const mobileHalf = this._drawerVisible && this._isMobileViewport && this._openState === 'half';\n const mobileFull = this._drawerVisible && this._isMobileViewport && this._openState === 'full';\n this._rootEl.classList.toggle('gengage-chat-root--open', this._drawerVisible);\n this._rootEl.classList.toggle('gengage-chat-root--mobile-half', mobileHalf);\n this._rootEl.classList.toggle('gengage-chat-root--mobile-full', mobileFull);\n }\n\n private _handleAttachment(file: File): void {\n const result = validateImageFile(file);\n if (!result.ok) {\n const message = result.reason === 'invalid_type' ? this._i18n.invalidFileType : this._i18n.fileTooLarge;\n dispatch('gengage:global:error', {\n message,\n source: 'chat' as const,\n });\n return;\n }\n this._drawer?.stageAttachment(file);\n }\n\n private _sendMessage(text: string, attachment?: File): void {\n if (this._pdpPrimingInFlight) {\n this._queuedUserMessages.push(attachment !== undefined ? { text, attachment } : { text });\n return;\n }\n\n ga.trackMessageSent();\n // Track conversation start on first user message in a new thread\n const hasUserMessages = this._messages.some((m) => m.role === 'user');\n if (!hasUserMessages) {\n ga.trackConversationStart();\n }\n // Image upload from Chat Input uses findSimilar (not inputText) per wire protocol\n const action: ActionPayload =\n attachment !== undefined\n ? { title: text, type: 'findSimilar', payload: text ? { text } : {} }\n : { title: text, type: 'user_message', payload: text };\n if (attachment !== undefined) {\n this._sendAction(action, { attachment });\n } else {\n this._sendAction(action);\n }\n }\n\n private _flushQueuedUserMessages(): void {\n if (this._pdpPrimingInFlight || this._queuedUserMessages.length === 0) return;\n const queued = [...this._queuedUserMessages];\n this._queuedUserMessages = [];\n for (const item of queued) {\n this._sendMessage(item.text, item.attachment);\n }\n }\n\n private _sendAction(\n action: ActionPayload,\n options?: { silent?: boolean; attachment?: File; preservePanel?: boolean; isPdpPrime?: boolean },\n ): void {\n // Cancel any running typewriter animation and TTS playback\n this._activeTypewriter?.cancel();\n this._activeTypewriter = null;\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = null;\n\n // Track last action for retry on error\n this._lastSentAction = { action, options };\n\n // Defer actions until init completes\n if (!this._initComplete) {\n if (this._pendingActions.length < 10) {\n this._pendingActions.push({ action, options });\n }\n return;\n }\n\n // Remove ChoicePrompter on new action\n this._choicePrompterEl?.remove();\n this._choicePrompterEl = null;\n\n // Clear comparison mode when starting a new request (unless preservePanel)\n if (!options?.preservePanel && this._comparisonSelectMode) {\n this._comparisonSelectMode = false;\n this._comparisonSelectedSkus = [];\n }\n\n // Clear local panel history on new requests (fresh panel context)\n if (!options?.preservePanel) {\n this._localPanelHistory = [];\n }\n\n // Branch deletion: if user is sending from a rewound position, prune future messages\n if (this._currentThreadId && this._lastThreadId && this._lastThreadId > this._currentThreadId) {\n const cutoff = this._currentThreadId;\n // Remove future messages from array\n const removed = this._messages.filter((m) => m.threadId !== undefined && m.threadId > cutoff);\n this._messages = this._messages.filter((m) => !m.threadId || m.threadId <= cutoff);\n // Remove their DOM nodes\n for (const msg of removed) {\n this._shadow?.querySelector(`[data-message-id=\"${CSS.escape(msg.id)}\"]`)?.remove();\n this._panel!.snapshots.delete(msg.id);\n this._panel!.snapshotTypes.delete(msg.id);\n }\n // Remove orphaned inline UISpec elements\n const orphanedUISpecs = this._shadow?.querySelectorAll(`[data-thread-id]`);\n orphanedUISpecs?.forEach((el) => {\n if (el instanceof HTMLElement && el.dataset['threadId'] && el.dataset['threadId'] > cutoff) {\n el.remove();\n }\n });\n }\n\n // Clear previous suggestion pills and input chips\n this._drawer?.setPills([]);\n this._drawer?.clearInputAreaChips();\n\n // Notify host that assistant is responding\n this._bridge?.send('isResponding', true);\n\n // Generate thread ID for this request-response cycle\n const threadId = uuidv7();\n this._currentThreadId = threadId;\n this._lastThreadId = threadId;\n // Preserve the active grid intent during product drilldowns. A product click\n // should not relabel an existing search-result panel as \"similar products\".\n if (this._panel && action.type !== 'launchSingleProduct') {\n this._panel.lastActionType = action.type;\n }\n // For preservePanel actions (like/addToCart), don't overwrite _activeRequestThreadId\n // to avoid silencing concurrent streams. Instead, track validity locally.\n const isPreservePanel = options?.preservePanel === true;\n const isPdpAutoLaunch =\n action.type === 'launchSingleProduct' && options?.silent === true && options?.isPdpPrime === true;\n if (!isPreservePanel) {\n this._activeRequestThreadId = threadId;\n }\n\n // Add user message to UI (skip for silent/auto-launch actions)\n if (!options?.silent) {\n const userText = typeof action.payload === 'string' ? action.payload : action.title;\n // Retry deduplication: skip adding a duplicate user bubble when retrying\n const lastMsg = this._messages.length > 0 ? this._messages[this._messages.length - 1] : undefined;\n const isDuplicate = lastMsg !== undefined && lastMsg.role === 'user' && lastMsg.content === userText;\n if (!isDuplicate) {\n const userMsg = this._createMessage('user', userText);\n userMsg.threadId = threadId;\n if (options?.attachment !== undefined) {\n userMsg.attachment = options.attachment;\n }\n this._drawer?.addMessage(userMsg);\n this._messages.push(userMsg);\n }\n }\n\n const shouldShortCircuitUnavailableContext =\n !options?.silent &&\n this._hasUnavailableProductContext() &&\n (action.type === 'user_message' || action.type === 'inputText');\n if (shouldShortCircuitUnavailableContext) {\n const fallback = this._i18n.productNotFoundMessage;\n const botMsg = this._createMessage('assistant', fallback);\n botMsg.threadId = threadId;\n botMsg.status = 'done';\n this._messages.push(botMsg);\n this._ensureAssistantMessageRendered(botMsg);\n this._drawer?.updateBotMessage(botMsg.id, fallback);\n this._bridge?.send('isResponding', false);\n this.emit('message', botMsg);\n this._persistToIndexedDB().catch(() => {\n /* non-fatal */\n });\n return;\n }\n\n // Preserve panel during the request — don't clear or show loading skeleton\n // until the backend explicitly signals new panel content (panelLoading event).\n // Captures the panel source (UISpec/kind) so it can be re-rendered with fresh\n // event listeners if the backend fails to deliver new panel content.\n let prePanelSource = this._currentPanelSource;\n let prePanelSourceCaptured = false;\n const capturePanelSourceIfNeeded = (): void => {\n if (prePanelSourceCaptured || options?.preservePanel) return;\n prePanelSource = this._currentPanelSource;\n prePanelSourceCaptured = true;\n };\n const restoreOrClearPanel = (): void => {\n if (!this._drawer?.isPanelLoading()) return;\n if (prePanelSource) {\n const ctx = this._buildRenderContext();\n const el =\n prePanelSource.kind === 'favorites'\n ? this._buildFavoritesPageEl()\n : this._renderUISpec(prePanelSource.spec, ctx);\n this._drawer.setPanelContent(el);\n this._currentPanelSource = prePanelSource;\n } else {\n this._drawer.clearPanel();\n this._currentPanelSource = null;\n }\n prePanelSource = null;\n };\n\n // Show typing indicator\n this._drawer?.showTypingIndicator();\n // Use a local text accumulator per stream invocation to prevent corruption\n // when multiple concurrent preservePanel streams write to the same state.\n let localBotText = '';\n\n // Create bot message placeholder\n const botMsg = this._createMessage('assistant', '');\n botMsg.threadId = threadId;\n botMsg.status = 'streaming';\n if (options?.silent) botMsg.silent = true;\n // Note: silent flag only skips the USER message above — bot response is always rendered\n this._messages.push(botMsg);\n\n // Abort previous request(s) — skip for preservePanel to avoid killing concurrent streams\n if (!options?.preservePanel) {\n this._abortControllers.forEach((c) => c.abort());\n this._abortControllers.clear();\n }\n\n const transport: ChatTransportConfig = {\n middlewareUrl: this.config.middlewareUrl,\n ...(this.config.accountId ? { accountId: this.config.accountId } : {}),\n };\n if (options?.attachment !== undefined) {\n transport.attachment = options.attachment;\n }\n\n const visibleMessages = this._getVisibleMessages();\n const chatHistory = visibleMessages\n // Keep assistant messages even when empty (panel-only responses) so the\n // backend sees the proper alternating user/model turn structure. Exclude\n // the current bot placeholder (just created, not yet populated).\n .filter((m) => m !== botMsg && (m.content || m.role === 'assistant'))\n .slice(-50)\n .map((m) => ({\n role: m.role === 'user' ? 'user' : 'model',\n content: m.content ?? '',\n }));\n\n // Build meta object for backend\n const meta: BackendRequestMeta = {\n outputLanguage: localeToOutputLanguage(this.config.locale),\n parentUrl: window.location.href,\n windowWidth: String(window.innerWidth),\n windowHeight: String(window.innerHeight),\n selfUrl: '',\n id: this.config.session?.sessionId ?? '',\n userId: this.config.session?.userId ?? '',\n appId: this.config.accountId,\n threads: [],\n createdAt: this._chatCreatedAt,\n kvkkApproved: isKvkkShown(this.config.accountId),\n voiceEnabled: this.config.voiceEnabled ?? false,\n threadId,\n isControlGroup: this.config.session?.abTestVariant === 'control',\n isMobile: this._isMobileViewport,\n };\n if (this.config.session?.viewId !== undefined) {\n meta.viewId = this.config.session.viewId;\n }\n\n // Enrich action payload with fields the backend expects\n const enrichedAction = enrichActionPayload(action, {\n pageContext: this.config.pageContext,\n backendContext: this._lastBackendContext,\n isMobile: this._isMobileViewport,\n });\n\n const request: import('./api.js').ProcessActionRequest = {\n account_id: this.config.accountId,\n session_id: this.config.session?.sessionId ?? '',\n correlation_id: this.config.session?.sessionId ?? '',\n type: enrichedAction.type,\n locale: this.config.locale ?? 'tr',\n meta,\n context: {\n // Spread backend context (panel, message_id, etc.) but preserve FE's\n // authoritative chatHistory — the backend's stale `messages` must not\n // overwrite the up-to-date conversation history built by the widget.\n ...(this._lastBackendContext ?? {}),\n messages: chatHistory,\n // Backend reads session_id from context.\n session_id: this.config.session?.sessionId ?? '',\n },\n };\n\n // Only set optional fields when values exist (exactOptionalPropertyTypes)\n if (this.config.session?.userId !== undefined) {\n request.user_id = this.config.session.userId;\n }\n if (this.config.session?.viewId !== undefined) {\n request.view_id = this.config.session.viewId;\n }\n if (enrichedAction.payload !== undefined) {\n request.payload = enrichedAction.payload;\n }\n if (this.config.pageContext?.sku !== undefined) {\n request.sku = this.config.pageContext.sku;\n }\n if (this.config.pageContext?.pageType !== undefined) {\n request.page_type = this.config.pageContext.pageType;\n }\n\n // Analytics tracking state for this request\n const requestId = crypto.randomUUID();\n const streamStart = Date.now();\n let chunkIndex = 0;\n let panelLoadingSeen = false;\n let panelContentReceived = false;\n\n this.track(\n streamStartEvent(this.analyticsContext(), {\n endpoint: 'process_action',\n request_id: requestId,\n widget: 'chat',\n }),\n );\n\n let streamController: AbortController | null = null;\n streamController = sendChatMessage(\n request,\n {\n onTextChunk: (content, isFinal, extra) => {\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n localBotText += content;\n this._drawer?.removeTypingIndicator();\n\n // Store enrichment data from outputText for downstream rendering\n if (extra?.skuToProductItem) {\n this._skuToProductItem = { ...this._skuToProductItem, ...extra.skuToProductItem };\n }\n if (extra?.conversationMode) {\n this._conversationMode = extra.conversationMode;\n }\n\n this.track(\n streamChunkEvent(this.analyticsContext(), {\n request_id: requestId,\n chunk_index: chunkIndex++,\n widget: 'chat',\n }),\n );\n\n if (!this._drawer) return;\n\n // KVKK filtering: always strip KVKK block from display text.\n // Show banner on first encounter only.\n let displayText = localBotText;\n if (isFinal && containsKvkk(displayText)) {\n const acctId = this.config.accountId;\n if (!isKvkkShown(acctId)) {\n const kvkkHtml = extractKvkkBlock(displayText);\n if (kvkkHtml) {\n this._drawer?.showKvkkBanner(kvkkHtml, () => {\n this._drawer?.hideKvkkBanner();\n });\n }\n markKvkkShown(acctId);\n }\n displayText = stripKvkkBlock(displayText);\n }\n\n // Check if we already have a bot bubble in the DOM (query by message ID, not :last-child)\n const existingBubble = this._shadow?.querySelector(\n `[data-message-id=\"${botMsg.id}\"] .gengage-chat-bubble-text`,\n );\n if (existingBubble) {\n existingBubble.innerHTML = sanitizeHtml(displayText);\n } else {\n botMsg.content = displayText;\n if (botMsg.role === 'assistant' && botMsg.threadId && !this._threadsWithFirstBot.has(botMsg.threadId)) {\n this._threadsWithFirstBot.add(botMsg.threadId);\n this._drawer.markFirstBotMessage(botMsg.id);\n }\n this._drawer.addMessage(botMsg);\n }\n\n if (isFinal) {\n botMsg.content = displayText;\n botMsg.status = 'done';\n ga.trackMessageReceived();\n\n // Apply typewriter animation to the final bot text\n const bubbleTextEl = this._shadow?.querySelector(\n `[data-message-id=\"${botMsg.id}\"] .gengage-chat-bubble-text`,\n ) as HTMLElement | null;\n if (bubbleTextEl) {\n this._activeTypewriter?.cancel();\n const mentions = extra?.productMentions;\n this._activeTypewriter = typewriteHtml({\n container: bubbleTextEl,\n html: sanitizeHtml(displayText),\n onTick: () => this._drawer?.scrollToBottomIfNeeded(),\n onComplete: () => {\n this._activeTypewriter = null;\n // Link product mentions after typewriter finishes\n if (mentions && mentions.length > 0 && bubbleTextEl) {\n linkProductMentions({\n container: bubbleTextEl,\n mentions,\n onProductClick: (sku) => {\n this._sendAction({\n title: mentions.find((m) => m.sku === sku)?.short_name ?? sku,\n type: 'launchSingleProduct',\n payload: { sku },\n });\n },\n });\n }\n },\n });\n }\n }\n },\n onUISpec: (spec, widget, panelHint) => {\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n if (widget !== 'chat') return;\n\n const rootElement = spec.elements[spec.root];\n const componentType = rootElement?.type ?? 'unknown';\n this.track(\n streamUiSpecEvent(this.analyticsContext(), {\n request_id: requestId,\n chunk_index: chunkIndex,\n component_type: componentType,\n widget: 'chat',\n }),\n );\n\n const renderContext = this._buildRenderContext();\n renderContext.isStreaming = true;\n\n // GA dataLayer: track component-specific events\n if (componentType === 'ComparisonTable') {\n const products = rootElement?.props?.['products'];\n ga.trackCompareReceived(Array.isArray(products) ? products.length : 0);\n }\n if (componentType === 'ProductGrid') {\n const childCount = rootElement?.children?.length ?? 0;\n ga.trackSearch(undefined, childCount);\n }\n\n const panelSpec = panelHint === 'panel' && this._panel ? this._panel.toPanelSpec(spec) : spec;\n const shouldRenderInline =\n !botMsg.silent &&\n (panelHint !== 'panel' || componentType === 'ProductCard') &&\n componentType !== 'ActionButtons'; // ActionButtons render as bottom pills only\n\n if (panelHint === 'panel' && this._panel) {\n const isFirstPanelContentInStream = !panelContentReceived;\n panelContentReceived = true;\n\n const panelAction = determinePanelUpdateAction({\n componentType,\n similarsAppend: rootElement?.props?.['similarsAppend'] === true,\n currentPanelType: this._panel.currentType,\n hasPanelContent: this._drawer?.hasPanelContent() ?? false,\n isPanelLoading: this._drawer?.isPanelLoading() ?? false,\n isFirstPanelContentInStream,\n });\n\n if (panelAction === 'appendSimilars') {\n this._appendSimilarsToPanel(panelSpec, renderContext);\n } else if (panelAction === 'append') {\n this._drawer?.appendPanelContent(this._renderUISpec(panelSpec, renderContext));\n } else {\n // Reset comparison state when new panel content replaces the grid\n this._comparisonSelectMode = false;\n this._comparisonSelectedSkus = [];\n this._drawer?.setPanelContent(this._renderUISpec(panelSpec, renderContext));\n this._currentPanelSource = { kind: 'spec', spec: panelSpec };\n this._panel.currentType = componentType;\n }\n\n if (componentType === 'ProductDetailsPanel' && action.type === 'launchSingleProduct') {\n this._clearUnavailableProductContext();\n }\n\n // Track panel thread and update topbar + extended mode\n if (botMsg.threadId && !this._panel.threads.includes(botMsg.threadId)) {\n this._panel.threads.push(botMsg.threadId);\n }\n // Use the primary panel type for title (don't let appended grids overwrite it).\n // Backend-provided panelTitle (e.g. search results title) takes precedence.\n const titleType = this._panel.currentType ?? componentType;\n const backendTitle = rootElement?.props?.['panelTitle'] as string | undefined;\n this._panel.updateTopBar(titleType, backendTitle);\n this._panel.updateExtendedMode(componentType);\n }\n\n // ProductDetailsPanel goes to the panel, but also render a compact\n // horizontal ProductSummaryCard in chat messages (production parity\n // with the prior engine's LaunchSingleProduct component).\n if (componentType === 'ProductDetailsPanel' && !botMsg.silent && panelHint === 'panel') {\n const product = rootElement?.props?.['product'] as Record<string, unknown> | undefined;\n if (product) {\n const inlineSpec: UISpec = {\n root: 'root',\n elements: {\n root: {\n type: 'ProductSummaryCard',\n props: { product },\n },\n },\n };\n const messagesContainer = this._shadow?.querySelector('.gengage-chat-messages');\n if (messagesContainer) {\n const inline = this._renderUISpec(inlineSpec, renderContext);\n if (botMsg.threadId) {\n inline.dataset['threadId'] = botMsg.threadId;\n }\n messagesContainer.appendChild(inline);\n inline.scrollIntoView({ behavior: 'auto', block: 'end' });\n }\n }\n }\n\n if (shouldRenderInline) {\n const messagesContainer = this._shadow?.querySelector('.gengage-chat-messages');\n if (messagesContainer) {\n const inline = this._renderUISpec(spec, renderContext);\n if (botMsg.threadId) {\n inline.dataset['threadId'] = botMsg.threadId;\n }\n messagesContainer.appendChild(inline);\n inline.scrollIntoView({ behavior: 'auto', block: 'end' });\n }\n }\n\n // Track product thumbnails for ThumbnailsColumn\n if ((componentType === 'ProductGrid' || componentType === 'ProductCard') && botMsg.threadId) {\n const childIds = rootElement?.children ?? [];\n const products =\n componentType === 'ProductGrid'\n ? (childIds\n .map((id) => spec.elements[id]?.props?.['product'] as Record<string, unknown> | undefined)\n .filter(Boolean) as Record<string, unknown>[])\n : ([rootElement?.props?.['product'] as Record<string, unknown> | undefined].filter(Boolean) as Record<\n string,\n unknown\n >[]);\n\n for (const product of products) {\n const sku = product['sku'] as string | undefined;\n const imageUrl = product['imageUrl'] as string | undefined;\n if (sku && imageUrl) {\n this._thumbnailEntries.push({ sku, imageUrl, threadId: botMsg.threadId });\n }\n if (sku) {\n this._viewedProductSkus.add(sku);\n }\n }\n this._drawer?.setThumbnails(this._thumbnailEntries);\n }\n\n // Send preview images to host for launcher thumbnails\n if (componentType === 'ProductGrid' || componentType === 'ProductDetailsPanel') {\n const previewProducts =\n componentType === 'ProductGrid'\n ? ((rootElement?.children ?? [])\n .map((id) => spec.elements[id]?.props?.['product'] as Record<string, unknown> | undefined)\n .filter(Boolean) as Record<string, unknown>[])\n : ([\n (rootElement?.props?.['product'] ?? rootElement?.props) as Record<string, unknown> | undefined,\n ].filter(Boolean) as Record<string, unknown>[]);\n const previewImageUrls = previewProducts\n .map((p) => p['imageUrl'] as string | undefined)\n .filter((url): url is string => typeof url === 'string')\n .slice(0, 5);\n if (previewImageUrls.length > 0) {\n this._bridge?.send('previewImages', { images: previewImageUrls });\n }\n }\n\n // Show ChoicePrompter when ProductGrid in panel, comparison mode is not active,\n // the user has viewed 2+ products, and hasn't dismissed for this thread\n if (\n componentType === 'ProductGrid' &&\n panelHint === 'panel' &&\n this._viewedProductSkus.size >= 2 &&\n !this._comparisonSelectMode &&\n !isChoicePrompterGloballyDismissed() &&\n !isChoicePrompterDismissed(this._currentThreadId ?? '')\n ) {\n this._choicePrompterEl?.remove();\n this._shadow?.querySelectorAll('.gengage-chat-choice-prompter').forEach((el) => el.remove());\n this._choicePrompterEl = createChoicePrompter({\n heading: this._i18n.choicePrompterHeading,\n suggestion: this._i18n.choicePrompterSuggestion,\n ctaLabel: this._i18n.choicePrompterCta,\n threadId: this._currentThreadId ?? '',\n dismissAriaLabel: this._i18n.dismissAriaLabel,\n onCtaClick: () => {\n this._comparisonSelectMode = true;\n this._choicePrompterEl = null;\n this._refreshComparisonUI();\n },\n onDismiss: () => {\n this._choicePrompterEl = null;\n },\n });\n // Mount in the panel float anchor — the prompter floats at the\n // bottom-right of the details pane (panel), not the conversation pane.\n const mountEl = this._shadow?.querySelector('.gengage-chat-panel-float');\n if (mountEl) {\n mountEl.appendChild(this._choicePrompterEl);\n\n // Dismiss ChoicePrompter when the mobile keyboard opens (viewport shrinks)\n if (this._isMobileViewport && window.visualViewport) {\n const prompterRef = this._choicePrompterEl;\n const dismissOnKeyboard = (): void => {\n const heightRatio = window.visualViewport!.height / window.innerHeight;\n if (heightRatio < 0.75) {\n prompterRef.remove();\n if (this._choicePrompterEl === prompterRef) {\n this._choicePrompterEl = null;\n }\n window.visualViewport!.removeEventListener('resize', dismissOnKeyboard);\n }\n };\n window.visualViewport.addEventListener('resize', dismissOnKeyboard);\n }\n } else {\n this._choicePrompterEl = null;\n }\n }\n\n // Extract suggestion pills / input-area chips from ActionButtons UISpec\n if (componentType === 'ActionButtons') {\n const buttons = rootElement?.props?.['buttons'] as\n | Array<{\n label: string;\n action: ActionPayload;\n icon?: string;\n image?: string;\n description?: string;\n }>\n | undefined;\n if (buttons && buttons.length > 0) {\n const inputChips: Array<{ label: string; icon?: string | undefined; action: ActionPayload }> = [];\n const pillButtons: typeof buttons = [];\n\n for (const btn of buttons) {\n if (isInputAreaAction(btn)) {\n const chip: { label: string; icon?: string | undefined; action: ActionPayload } = {\n label: btn.label,\n action: btn.action,\n };\n if (btn.icon) chip.icon = btn.icon;\n inputChips.push(chip);\n } else {\n pillButtons.push(btn);\n }\n }\n\n if (inputChips.length > 0) {\n this._drawer?.setInputAreaChips(\n inputChips.map((chip) => ({\n label: chip.label,\n onAction: () => this._sendAction(chip.action),\n ...(chip.icon ? { icon: chip.icon } : {}),\n })),\n );\n }\n\n if (pillButtons.length > 0) {\n this._drawer?.setPills(\n pillButtons.map((btn) => {\n const pill: {\n label: string;\n onAction: () => void;\n icon?: string;\n image?: string;\n description?: string;\n } = {\n label: btn.label,\n onAction: () => this._sendAction(btn.action),\n };\n if (btn.icon) pill.icon = btn.icon;\n if (btn.image) pill.image = btn.image;\n if (btn.description) pill.description = btn.description;\n return pill;\n }),\n );\n }\n }\n }\n\n botMsg.uiSpec = spec;\n },\n onAction: (event: StreamEvent) => {\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n if (event.type === 'action') {\n const routerOpts: ActionRouterOptions = {};\n if (this.config.actionHandling?.unknownActionPolicy !== undefined) {\n routerOpts.unknownActionPolicy = this.config.actionHandling.unknownActionPolicy;\n }\n if (this.config.actionHandling?.allowScriptCall !== undefined) {\n routerOpts.allowScriptCall = this.config.actionHandling.allowScriptCall;\n }\n routeStreamAction(\n event as StreamEventAction,\n {\n openChat: () => this.open(),\n navigate: (params) => {\n if (!isSafeUrl(params.url)) return;\n this._bridge?.send('navigate', params);\n if (params.newTab) {\n window.open(params.url, '_blank', 'noopener,noreferrer');\n } else {\n window.location.href = params.url;\n }\n },\n saveSession: (params) => this.saveSession(params.sessionId, params.sku),\n addToCart: (params) => {\n dispatch('gengage:similar:add-to-cart', params);\n },\n scriptCall: (params) => {\n dispatch('gengage:chat:script-call', params);\n this.config.onScriptCall?.(params);\n },\n },\n routerOpts,\n );\n }\n },\n onMetadata: (event: StreamEvent) => {\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n if (event.type === 'metadata' && event.meta) {\n // Store backend context for sending with next request\n if (\n event.meta.panel !== undefined ||\n event.meta.messages !== undefined ||\n event.meta.message_id !== undefined\n ) {\n this._lastBackendContext = event.meta as import('../common/types.js').BackendContext;\n }\n\n // Panel loading indicator\n if (event.meta.panelLoading) {\n panelLoadingSeen = true;\n panelContentReceived = false;\n // Snapshot current panel before replacing with skeleton\n capturePanelSourceIfNeeded();\n const pendingType =\n typeof event.meta.panelPendingType === 'string' ? event.meta.panelPendingType : undefined;\n if (this._panel) this._panel.currentType = null;\n this._drawer?.showPanelLoading(pendingType);\n // Set panel topbar title immediately so it's not an empty white bar\n if (pendingType) {\n this._panel?.updateTopBarForLoading(pendingType);\n }\n }\n\n // Optional voice payload emitted by backend when voiceEnabled is true.\n if (event.meta.voice) {\n // Dispatch cancelable event — host can call preventDefault() to suppress built-in playback\n const voiceEvent = new CustomEvent('gengage:chat:voice', {\n detail: { payload: event.meta.voice },\n bubbles: false,\n cancelable: true,\n });\n const allowed = window.dispatchEvent(voiceEvent);\n ga.trackVoiceInput();\n\n // Built-in TTS playback (skipped if host called preventDefault)\n if (allowed) {\n const voicePayload = event.meta.voice as { audio_base64?: string; content_type?: string };\n if (voicePayload.audio_base64) {\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = playTtsAudio(\n voicePayload.audio_base64,\n voicePayload.content_type ?? 'audio/ogg',\n );\n }\n }\n }\n\n if (event.meta.redirectTarget || event.meta.redirect) {\n dispatch('gengage:chat:redirect', {\n target: event.meta.redirectTarget ?? null,\n payload: event.meta.redirect ?? null,\n });\n }\n\n // Analyze animation — show panel loading skeleton with pulse\n if (event.meta.analyzeAnimation) {\n panelLoadingSeen = true;\n panelContentReceived = false;\n capturePanelSourceIfNeeded();\n if (this._panel) this._panel.currentType = null;\n this._drawer?.showPanelLoading();\n // Default to product details title during analyze\n this._panel?.updateTopBarForLoading('productDetails');\n }\n\n // Thinking step messages — accumulate as checklist in typing indicator\n if (event.meta.loading && typeof event.meta.loadingText === 'string') {\n this._drawer?.addThinkingStep(event.meta.loadingText);\n this._bridge?.send('loadingMessage', { text: event.meta.loadingText });\n }\n\n // Forward visitor engagement data to host page\n if (event.meta.visitorDataResponse) {\n this._bridge?.send('engagingMessage', event.meta.visitorDataResponse);\n }\n\n // Forward form events to host via bridge (Otokoc-specific)\n if (event.meta.formType) {\n this._bridge?.send('glovOtokoc', {\n type: event.meta.formType,\n data: event.meta.formPayload,\n });\n }\n\n // Forward launcher content to host via bridge\n if (event.meta.launcherContent) {\n this._bridge?.send('launcherContent', event.meta.launcherContent);\n }\n\n dispatch('gengage:chat:metadata', { payload: event.meta });\n\n // Extract LLM usage from metadata if present\n const meta = event.meta;\n if (typeof meta.prompt_tokens === 'number' && typeof meta.completion_tokens === 'number') {\n this.track(\n llmUsageEvent(this.analyticsContext(), {\n model: event.model ?? 'unknown',\n prompt_tokens: meta.prompt_tokens as number,\n completion_tokens: meta.completion_tokens as number,\n total_tokens:\n (meta.total_tokens as number) ??\n (meta.prompt_tokens as number) + (meta.completion_tokens as number),\n }),\n );\n }\n }\n },\n onError: (err) => {\n if (streamController) this._abortControllers.delete(streamController);\n // Skip error handling for aborted/superseded requests (including when no active request)\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n this._bridge?.send('isResponding', false);\n this._bridge?.send('loadingMessage', { text: null });\n this._drawer?.removeTypingIndicator();\n // Capture panel state before resetting — needed for error gating below\n const hadPanelContent = panelContentReceived;\n if (panelLoadingSeen && !panelContentReceived) restoreOrClearPanel();\n panelLoadingSeen = false;\n panelContentReceived = false;\n // Skip error toast when the stream already delivered user-facing content\n // (bot text, panel content, or silent auto-launch). Showing a generic\n // error on top of an already-rendered assistant response is confusing.\n const hasContent =\n botMsg.silent ||\n (botMsg.content != null && botMsg.content.length > 0) ||\n localBotText.length > 0 ||\n hadPanelContent;\n if (!hasContent) {\n if (isPdpAutoLaunch || this._hasUnavailableProductContext()) {\n // Show soft fallback instead of generic error for auto-launch\n const fallback = this._i18n.productNotFoundMessage;\n botMsg.content = fallback;\n botMsg.status = 'done';\n this._ensureAssistantMessageRendered(botMsg);\n this._drawer?.updateBotMessage(botMsg.id, fallback);\n this._markUnavailableProductContext();\n } else {\n // Show error inline in the chat — not as a global toast.\n // The user is in an active conversation; a toast on top of the\n // chat is confusing and can overlap with prior bot messages.\n this.emit('error', err);\n\n // Track consecutive identical errors — repeated failures suggest\n // the account is inactive or backend is unreachable.\n const errMsg = err.message;\n if (errMsg === this._lastErrorMessage) {\n this._consecutiveErrorCount++;\n } else {\n this._consecutiveErrorCount = 1;\n this._lastErrorMessage = errMsg;\n }\n\n if (this._consecutiveErrorCount >= 2) {\n // Escalate: show account-inactive message with recovery pills\n this._drawer?.showErrorWithRecovery(this._i18n.accountInactiveMessage, {\n onRetry: () => {\n if (this._lastSentAction) {\n this._sendAction(this._lastSentAction.action, this._lastSentAction.options);\n }\n },\n onNewQuestion: () => {\n this._drawer?.focusInput();\n },\n });\n } else {\n // First error: show standard error with retry + recovery pills\n this._drawer?.showErrorWithRecovery(this._i18n.errorMessage, {\n onRetry: () => {\n if (this._lastSentAction) {\n this._sendAction(this._lastSentAction.action, this._lastSentAction.options);\n }\n },\n onNewQuestion: () => {\n this._drawer?.focusInput();\n },\n });\n }\n }\n }\n if (isPdpAutoLaunch) {\n this._pdpPrimingInFlight = false;\n this._flushQueuedUserMessages();\n }\n // Only overwrite status if message hasn't already completed (isFinal text chunk sets 'done')\n if (botMsg.status === 'streaming') {\n botMsg.status = 'error';\n }\n\n this.track(\n streamErrorEvent(this.analyticsContext(), {\n request_id: requestId,\n error_code: 'STREAM_ERROR',\n error_message: err.message,\n widget: 'chat',\n }),\n );\n },\n onDone: () => {\n if (streamController) this._abortControllers.delete(streamController);\n // Skip cleanup for aborted/superseded requests\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n this._activeRequestThreadId = null;\n // Reset consecutive error counter on successful stream completion\n this._consecutiveErrorCount = 0;\n this._lastErrorMessage = '';\n this._bridge?.send('isResponding', false);\n this._bridge?.send('loadingMessage', { text: null });\n this._drawer?.removeTypingIndicator();\n if (panelLoadingSeen && !panelContentReceived) restoreOrClearPanel();\n panelLoadingSeen = false;\n panelContentReceived = false;\n // Detect failed PDP auto-launch: silent launch action that produced\n // no visible content (no bot text, no panel). Show a soft fallback\n // message so the user isn't left with an empty chat.\n if (isPdpAutoLaunch && !localBotText && !panelContentReceived) {\n const fallback = this._i18n.productNotFoundMessage;\n botMsg.content = fallback;\n this._ensureAssistantMessageRendered(botMsg);\n this._drawer?.updateBotMessage(botMsg.id, fallback);\n this._markUnavailableProductContext();\n }\n if (isPdpAutoLaunch) {\n this._pdpPrimingInFlight = false;\n this._flushQueuedUserMessages();\n }\n\n if (botMsg.status === 'streaming') {\n botMsg.status = 'done';\n ga.trackMessageReceived();\n }\n\n // Reveal the comparison toggle button (hidden during streaming) with fade-in\n const hiddenCompareBtn = this._shadow?.querySelector('.gengage-chat-comparison-toggle-btn--hidden');\n if (hiddenCompareBtn) {\n hiddenCompareBtn.classList.remove('gengage-chat-comparison-toggle-btn--hidden');\n hiddenCompareBtn.classList.add('gengage-chat-comparison-toggle-btn--reveal');\n }\n\n this.emit('message', botMsg);\n\n // Snapshot current panel content for this message's history.\n // Pass a rebuild function so restored panels have live event listeners.\n const panelSource = this._currentPanelSource;\n this._panel?.snapshotForMessage(\n botMsg.id,\n panelSource\n ? () => {\n const ctx = this._buildRenderContext();\n return panelSource.kind === 'favorites'\n ? this._buildFavoritesPageEl()\n : this._renderUISpec(panelSource.spec, ctx);\n }\n : undefined,\n );\n // Make the bot message bubble clickable to restore its panel state\n this._panel?.attachClickHandler(botMsg.id);\n\n this.track(\n streamDoneEvent(this.analyticsContext(), {\n request_id: requestId,\n latency_ms: Date.now() - streamStart,\n chunk_count: chunkIndex,\n widget: 'chat',\n }),\n );\n\n this.track(\n meteringIncrementEvent(this.analyticsContext(), {\n meter_key: 'chat_request',\n quantity: 1,\n unit: 'request',\n }),\n );\n\n this.track(\n chatHistorySnapshotEvent(this.analyticsContext(), {\n message_count: this._messages.length,\n history_ref: this.config.session?.sessionId ?? '',\n redaction_level: 'none',\n }),\n );\n\n // Persist session to IndexedDB (fire-and-forget)\n this._persistToIndexedDB().catch(() => {\n /* non-fatal */\n });\n },\n },\n transport,\n );\n this._abortControllers.add(streamController);\n\n // Show \"Stop generating\" button for user-visible streams\n if (!options?.silent && !isPreservePanel) {\n const ctrl = streamController;\n this._drawer?.showStopButton(() => {\n ctrl.abort();\n this._abortControllers.delete(ctrl);\n this._drawer?.removeTypingIndicator();\n this._bridge?.send('isResponding', false);\n this._bridge?.send('loadingMessage', { text: null });\n if (botMsg.status === 'streaming') {\n botMsg.status = 'done';\n }\n });\n }\n }\n\n /** Return messages visible at the current thread cursor. */\n private _getVisibleMessages(): ChatMessage[] {\n const msgs = this._messages.filter((m) => !m.silent);\n if (!this._currentThreadId) return msgs;\n const cutoff = this._currentThreadId;\n return msgs.filter((m) => !m.threadId || m.threadId <= cutoff);\n }\n\n /** Handle rollback-on-click from a user message bubble. */\n private _appendSimilarsToPanel(spec: UISpec, ctx: import('../chat/types.js').ChatUISpecRenderContext): void {\n if (!this._drawer) return;\n const panelEl = this._drawer.getPanelContentElement();\n if (!panelEl) return;\n const heading = document.createElement('h3');\n heading.className = 'gengage-chat-product-details-similars-heading';\n heading.textContent = this._i18n.similarProductsLabel ?? 'Similar Products';\n panelEl.appendChild(heading);\n const grid = this._renderUISpec(spec, ctx);\n grid.classList.add('gengage-chat-product-details-similars');\n panelEl.appendChild(grid);\n }\n\n private _handleRollback(messageId: string): void {\n const msg = this._messages.find((m) => m.id === messageId);\n if (!msg?.threadId) return;\n this._rollbackToThread(msg.threadId);\n }\n\n /** Rewind the conversation to the given thread. */\n private _rollbackToThread(threadId: string): void {\n this._currentThreadId = threadId;\n this._extendedModeManager?.setHiddenByUser(false);\n\n // Toggle visibility of messages after the cutoff\n for (const msg of this._messages) {\n const bubble = this._shadow?.querySelector(`[data-message-id=\"${CSS.escape(msg.id)}\"]`);\n if (!bubble) continue;\n if (msg.threadId && msg.threadId > threadId) {\n bubble.classList.add('gengage-chat-bubble--hidden');\n } else {\n bubble.classList.remove('gengage-chat-bubble--hidden');\n }\n }\n\n // Hide inline UISpec elements from future threads\n this._shadow?.querySelectorAll('[data-thread-id]').forEach((el) => {\n if (el instanceof HTMLElement && el.dataset['threadId'] && el.dataset['threadId'] > threadId) {\n el.classList.add('gengage-chat-bubble--hidden');\n } else if (el instanceof HTMLElement) {\n el.classList.remove('gengage-chat-bubble--hidden');\n }\n });\n\n // Restore panel snapshot from the target thread's bot message\n const targetBot = this._messages.find((m) => m.role === 'assistant' && m.threadId === threadId);\n const restored = targetBot ? this._panel?.restoreForMessage(targetBot.id) : false;\n if (!restored) {\n this._drawer?.clearPanel();\n this._currentPanelSource = null;\n }\n // Always update topbar navigation state for the new thread position\n const panelType = this._panel!.currentType ?? '';\n this._panel?.updateTopBar(panelType);\n\n // Clear suggestion pills (they belong to the latest thread)\n this._drawer?.setPills([]);\n\n // Load context from IndexedDB for the target thread so the next request\n // sends the correct historical context (not the latest one).\n if (this._session?.db && this.config.session?.sessionId) {\n this._session?.db\n .loadContext(this.config.session.sessionId, threadId)\n .then((ctx) => {\n if (ctx) this._lastBackendContext = ctx.context;\n })\n .catch(() => {\n /* non-fatal */\n });\n }\n\n // Prune future context entries from IndexedDB\n if (this._session?.db && this.config.session?.sessionId) {\n this._session?.db.deleteContextsAfterThread(this.config.session.sessionId, threadId).catch(() => {\n /* non-fatal */\n });\n }\n }\n\n // ---------------------------------------------------------------------------\n // IndexedDB persistence (delegates to SessionPersistence)\n // ---------------------------------------------------------------------------\n\n private async _persistToIndexedDB(): Promise<void> {\n if (!this._session || !this.config.session?.sessionId) return;\n await this._session.persist({\n userId: this.config.session.userId ?? '',\n appId: this.config.accountId,\n sessionId: this.config.session.sessionId,\n messages: this._messages,\n currentThreadId: this._currentThreadId,\n lastThreadId: this._lastThreadId,\n chatCreatedAt: this._chatCreatedAt,\n panelSnapshots: this._panel?.snapshots ?? new Map(),\n panelThreads: this._panel?.threads ?? [],\n thumbnailEntries: this._thumbnailEntries,\n lastBackendContext: this._lastBackendContext,\n sku: this.config.pageContext?.sku,\n });\n }\n\n private _isSameOriginUrl(url: string): boolean {\n try {\n if (!url.trim()) return false;\n const parsed = new URL(url, window.location.href);\n return parsed.origin === window.location.origin;\n } catch {\n return false;\n }\n }\n\n private _markUnavailableProductContext(): void {\n this._productContextUnavailableSku = this.config.pageContext?.sku ?? null;\n }\n\n private _clearUnavailableProductContext(): void {\n this._productContextUnavailableSku = null;\n }\n\n private _hasUnavailableProductContext(): boolean {\n const currentSku = this.config.pageContext?.sku;\n return currentSku !== undefined && currentSku.length > 0 && this._productContextUnavailableSku === currentSku;\n }\n\n private _ensureAssistantMessageRendered(msg: ChatMessage): void {\n const bubble = this._shadow?.querySelector(`[data-message-id=\"${CSS.escape(msg.id)}\"]`);\n if (bubble || !this._drawer) return;\n if (msg.role === 'assistant' && msg.threadId && !this._threadsWithFirstBot.has(msg.threadId)) {\n this._threadsWithFirstBot.add(msg.threadId);\n this._drawer.addMessage(msg);\n this._drawer.markFirstBotMessage(msg.id);\n return;\n }\n this._drawer.addMessage(msg);\n }\n\n private async _saveSessionAndOpenURL(url: string): Promise<void> {\n if (!this._session) return;\n await this._session.saveAndOpenURL(url, () => this._persistToIndexedDB(), this._bridge);\n }\n\n private async _loadPayload(threadId: string, messageId: string): Promise<import('../common/types.js').UISpec | null> {\n if (!this._session) return null;\n return this._session.loadPayload(threadId, messageId);\n }\n\n /**\n * Attempt to restore chat session from IndexedDB.\n * Always restores when IDB has session data for the current sessionId.\n * Best-effort — failures are silently ignored.\n */\n private async _restoreFromIndexedDB(shouldRestore: boolean): Promise<void> {\n if (!this._session?.db) return;\n const sessionId = this.config.session?.sessionId;\n if (!sessionId) return;\n\n const userId = this.config.session?.userId ?? '';\n const appId = this.config.accountId;\n\n // Always restore favorites (user preference, not session state)\n await this._session.loadFavorites(userId, appId);\n this._drawer?.updateFavoritesBadge(this._session.favoritedSkus.size);\n\n // Only restore chat state on explicit handoff (e.g. SimRel product navigation)\n if (!shouldRestore) return;\n\n const session = await this._session.db?.loadSession(userId, appId, sessionId);\n if (!session || session.messages.length === 0) return;\n\n // Don't restore a session saved for a different SKU\n const currentSku = this.config.pageContext?.sku;\n if (currentSku && session.sku && session.sku !== currentSku) return;\n\n // Prevent duplicate auto-launch: session already has messages, so PDP launch already happened\n this._pdpLaunched = true;\n\n // Lock auto-scroll during restore to prevent visual jump\n this._drawer?.lockScrollForRestore();\n\n // Restore thread cursors and creation timestamp\n this._currentThreadId = session.currentThreadId;\n this._lastThreadId = session.lastThreadId;\n this._chatCreatedAt = session.createdAt;\n\n // Restore panel threads and thumbnail entries\n if (session.panelThreads) {\n this._panel!.threads = session.panelThreads;\n }\n if (session.thumbnailEntries) {\n this._thumbnailEntries = session.thumbnailEntries;\n this._drawer?.setThumbnails(this._thumbnailEntries);\n }\n\n // Restore panel snapshots from serialized HTML (sanitize for defense-in-depth)\n if (session.panelSnapshotHtml) {\n for (const [msgId, html] of Object.entries(session.panelSnapshotHtml)) {\n const container = document.createElement('div');\n container.innerHTML = sanitizeHtml(html);\n this._panel!.snapshots.set(msgId, container);\n }\n }\n\n // Track highest message ID to avoid collisions with new messages\n let maxMsgNum = 0;\n\n // Replay messages into DOM\n for (const msg of session.messages) {\n const chatMsg: ChatMessage = {\n id: msg.id,\n role: msg.role,\n timestamp: msg.timestamp,\n status: msg.status,\n };\n if (msg.threadId !== undefined) chatMsg.threadId = msg.threadId;\n if (msg.content !== undefined) chatMsg.content = msg.content;\n if (msg.silent) chatMsg.silent = true;\n\n this._messages.push(chatMsg);\n\n // Skip silent messages from DOM rendering\n if (chatMsg.silent) continue;\n\n if (chatMsg.role === 'assistant' && chatMsg.threadId && !this._threadsWithFirstBot.has(chatMsg.threadId)) {\n this._threadsWithFirstBot.add(chatMsg.threadId);\n this._drawer?.markFirstBotMessage(chatMsg.id);\n }\n this._drawer?.addMessage(chatMsg);\n\n // Track message ID counter\n const idNum = parseInt(msg.id.replace('msg-', ''), 10);\n if (!isNaN(idNum) && idNum > maxMsgNum) maxMsgNum = idNum;\n\n // Re-render inline UISpec elements for bot messages (load from payload store)\n if (chatMsg.role === 'assistant' && chatMsg.threadId) {\n const uiSpec = await this._loadPayload(chatMsg.threadId, chatMsg.id);\n if (uiSpec) {\n chatMsg.uiSpec = uiSpec;\n this._restoreInlineUISpec(chatMsg);\n this._panel?.attachClickHandler(chatMsg.id);\n // Clear after render to maintain lean pattern\n delete chatMsg.uiSpec;\n }\n }\n }\n\n // Advance message ID counter past restored messages\n if (maxMsgNum > this._currentMessageId) {\n this._currentMessageId = maxMsgNum;\n }\n\n // Restore backend context with fallback chain\n if (this._currentThreadId) {\n let ctx = await this._session.db?.loadContext(sessionId, this._currentThreadId);\n if (!ctx) {\n ctx = await this._session.db?.loadLatestContext(sessionId);\n }\n if (ctx) this._lastBackendContext = ctx.context;\n }\n\n // Restore panel for the current thread's latest bot message\n if (this._currentThreadId) {\n const panelBot = [...this._messages]\n .reverse()\n .find((m) => m.role === 'assistant' && m.threadId === this._currentThreadId && !m.silent);\n if (panelBot && this._panel!.snapshots.has(panelBot.id)) {\n this._panel?.restoreForMessage(panelBot.id);\n }\n }\n\n // Apply thread visibility — hide messages from future threads\n if (this._currentThreadId) {\n const cutoff = this._currentThreadId;\n for (const msg of this._messages) {\n if (msg.threadId && msg.threadId > cutoff) {\n const bubble = this._shadow?.querySelector(`[data-message-id=\"${CSS.escape(msg.id)}\"]`);\n bubble?.classList.add('gengage-chat-bubble--hidden');\n }\n }\n this._shadow?.querySelectorAll('[data-thread-id]').forEach((el) => {\n if (el instanceof HTMLElement && el.dataset['threadId'] && el.dataset['threadId'] > cutoff) {\n el.classList.add('gengage-chat-bubble--hidden');\n }\n });\n }\n\n // Update panel topbar if we have panel threads\n if (this._panel!.threads.length > 0 && this._currentThreadId) {\n const lastPanelThread = this._panel!.threads[this._panel!.threads.length - 1];\n if (lastPanelThread) {\n const lastPanelBot = [...this._messages]\n .reverse()\n .find((m) => m.role === 'assistant' && m.threadId === lastPanelThread);\n if (lastPanelBot?.threadId) {\n const uiSpec = await this._loadPayload(lastPanelBot.threadId, lastPanelBot.id);\n if (uiSpec) {\n const rootEl = uiSpec.elements[uiSpec.root];\n if (rootEl) {\n this._panel?.updateTopBar(rootEl.type);\n }\n }\n }\n }\n }\n\n // After lockout expires, scroll to last thread boundary instead of absolute bottom\n setTimeout(() => {\n this._drawer?.scrollToLastThread();\n }, 550);\n }\n\n /**\n * Toggle comparison mode or individual SKU selection, then refresh the DOM.\n * Extracted so both the render-context callback and DOM-created checkboxes\n * share the same state-mutation + refresh path.\n */\n /**\n * Panel back navigation: pop local drilldown history first (e.g. card→detail),\n * then fall back to thread-level history.\n */\n private _navigatePanelBack(): void {\n const prev = this._localPanelHistory.pop();\n if (prev) {\n const ctx = this._buildRenderContext();\n const el =\n prev.source.kind === 'favorites' ? this._buildFavoritesPageEl() : this._renderUISpec(prev.source.spec, ctx);\n this._drawer?.setPanelContent(el);\n this._currentPanelSource = prev.source;\n const canBack = this._localPanelHistory.length > 0 || (this._panel?.threads.length ?? 0) > 1;\n this._drawer?.updatePanelTopBar(canBack, false, prev.title);\n return;\n }\n // On mobile, when there is no local history left, back = hide the side panel\n // (content is preserved so it can be reopened via the header button)\n if (this._isMobileViewport) {\n this._drawer?.hideMobilePanel();\n return;\n }\n this._panel?.navigateBack();\n }\n\n private _toggleComparisonSku(sku: string): void {\n if (sku === '') {\n this._comparisonSelectMode = !this._comparisonSelectMode;\n if (!this._comparisonSelectMode) {\n this._comparisonSelectedSkus = [];\n ga.trackCompareClear();\n }\n } else {\n const idx = this._comparisonSelectedSkus.indexOf(sku);\n if (idx >= 0) {\n this._comparisonSelectedSkus = this._comparisonSelectedSkus.filter((s) => s !== sku);\n } else {\n this._comparisonSelectedSkus = [...this._comparisonSelectedSkus, sku];\n ga.trackComparePreselection(sku);\n }\n }\n this._refreshComparisonUI();\n }\n\n /**\n * Refresh the panel DOM to reflect the current comparison state without\n * full re-render. Updates: toggle button active class, checkbox overlays\n * on product cards, and the floating comparison button.\n */\n private _refreshComparisonUI(): void {\n const panelEl = this._shadow?.querySelector('.gengage-chat-panel');\n if (!panelEl) return;\n\n const gridWrapper = panelEl.querySelector('.gengage-chat-product-grid-wrapper');\n if (!gridWrapper) return;\n const grid = gridWrapper.querySelector('.gengage-chat-product-grid');\n if (!grid) return;\n\n // 1. Toggle comparison button active state\n const toggleBtn = gridWrapper.querySelector('.gengage-chat-comparison-toggle-btn');\n if (toggleBtn) {\n toggleBtn.classList.toggle('gengage-chat-comparison-toggle-btn--active', this._comparisonSelectMode);\n }\n\n // 2. Add or remove checkbox overlays on product cards\n if (this._comparisonSelectMode) {\n const cards = grid.querySelectorAll<HTMLElement>('.gengage-chat-product-card[data-sku]');\n for (const card of cards) {\n if (card.parentElement?.classList.contains('gengage-chat-comparison-select-wrapper')) {\n // Already wrapped — sync checked state\n const cb = card.parentElement.querySelector<HTMLInputElement>('.gengage-chat-comparison-checkbox');\n if (cb) cb.checked = this._comparisonSelectedSkus.includes(card.dataset['sku']!);\n continue;\n }\n const sku = card.dataset['sku']!;\n const wrapper = document.createElement('div');\n wrapper.className = 'gengage-chat-comparison-select-wrapper';\n const checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.className = 'gengage-chat-comparison-checkbox';\n checkbox.checked = this._comparisonSelectedSkus.includes(sku);\n checkbox.addEventListener('change', () => {\n this._toggleComparisonSku(sku);\n });\n card.parentNode!.insertBefore(wrapper, card);\n wrapper.appendChild(checkbox);\n wrapper.appendChild(card);\n // Allow clicking anywhere on the card (not just the tiny checkbox) to toggle selection\n wrapper.style.cursor = 'pointer';\n wrapper.addEventListener('click', (e) => {\n // Avoid double-toggle when the checkbox itself is clicked\n if (e.target === checkbox) return;\n checkbox.checked = !checkbox.checked;\n this._toggleComparisonSku(sku);\n });\n }\n } else {\n // Remove all checkbox wrappers\n const wrappers = grid.querySelectorAll('.gengage-chat-comparison-select-wrapper');\n for (const wrapper of wrappers) {\n const card = wrapper.querySelector('.gengage-chat-product-card');\n if (card && wrapper.parentNode) {\n wrapper.parentNode.insertBefore(card, wrapper);\n wrapper.remove();\n }\n }\n }\n\n // 3. Update floating comparison button\n const existingFloating = gridWrapper.querySelector('.gengage-chat-comparison-floating-btn');\n if (this._comparisonSelectMode) {\n const count = this._comparisonSelectedSkus.length;\n const canCompare = count >= 2;\n const label = this._i18n.compareSelected ?? 'Compare';\n const text = canCompare ? `${label} (${count})` : (this._i18n.compareMinHint ?? 'Select at least 2 products');\n if (existingFloating) {\n existingFloating.textContent = text;\n (existingFloating as HTMLButtonElement).disabled = !canCompare;\n existingFloating.classList.toggle('gengage-chat-comparison-floating-btn--disabled', !canCompare);\n } else {\n const btn = document.createElement('button');\n btn.className = 'gengage-chat-comparison-floating-btn';\n btn.type = 'button';\n btn.textContent = text;\n btn.disabled = !canCompare;\n if (!canCompare) btn.classList.add('gengage-chat-comparison-floating-btn--disabled');\n btn.addEventListener('click', () => {\n if (this._comparisonSelectedSkus.length < 2) return;\n ga.trackCompareSelected(this._comparisonSelectedSkus);\n this._sendAction({\n title: label,\n type: 'getComparisonTable',\n payload: { sku_list: [...this._comparisonSelectedSkus] },\n });\n });\n gridWrapper.appendChild(btn);\n }\n } else {\n existingFloating?.remove();\n }\n }\n\n /**\n * Build a ChatUISpecRenderContext with all callbacks wired up.\n * Used both during streaming and during session restore.\n */\n private _buildRenderContext(): ChatUISpecRenderContext {\n const ctx: ChatUISpecRenderContext = {\n onAction: (action) => {\n ga.trackSuggestedQuestion(action.title, action.type);\n if (action.type === 'findSimilar') {\n const sku =\n typeof action.payload === 'object' && action.payload !== null && 'sku' in action.payload\n ? String((action.payload as Record<string, unknown>).sku)\n : '';\n ga.trackFindSimilars(sku);\n }\n if (action.type === 'getComparisonTable') {\n ga.trackCompareSelected(this._comparisonSelectedSkus);\n }\n // addToCart/like actions should preserve the current panel (product cards stay visible)\n const preservePanel = action.type === 'addToCart' || action.type === 'like';\n this._sendAction(action, preservePanel ? { preservePanel: true } : undefined);\n },\n onProductClick: (params) => {\n ga.trackProductDetail(params.sku);\n // Demo mode: load product in-chat via launchSingleProduct (no navigation)\n // Production mode: navigate to product page (chat auto-opens on new page)\n const shouldNavigate = this.config.isDemoWebsite !== true && this._isSameOriginUrl(params.url);\n if (!shouldNavigate) {\n this._sendAction({\n title: params.sku,\n type: 'launchSingleProduct',\n payload: { sku: params.sku },\n });\n } else {\n dispatch('gengage:similar:product-click', {\n sku: params.sku,\n url: params.url,\n sessionId: this.config.session?.sessionId ?? null,\n });\n this._saveSessionAndOpenURL(params.url);\n }\n },\n onAddToCart: (params) => {\n ga.trackCartAdd(params.sku, params.quantity);\n const detail = {\n ...params,\n sessionId: this.config.session?.sessionId ?? null,\n };\n dispatch('gengage:chat:add-to-cart', detail);\n this._bridge?.send('addToCart', params);\n void this._runEventCallbacks('gengage-cart-add', detail as unknown as Record<string, unknown>);\n // Send addToCart action to backend — preservePanel keeps current products visible\n this._sendAction(\n {\n title: this._i18n.addToCartButton ?? 'Add to Cart',\n type: 'addToCart',\n payload: { sku: params.sku, cart_code: params.cartCode, quantity: params.quantity },\n },\n { preservePanel: true },\n );\n },\n onProductSelect: (product) => {\n // Save current panel source to local history so back button can re-render it\n if (this._currentPanelSource) {\n const currentTitle = this._drawer?.getPanelTopBarTitle() ?? '';\n this._localPanelHistory.push({ source: this._currentPanelSource, title: currentTitle });\n if (this._localPanelHistory.length > GengageChat._MAX_PANEL_HISTORY) this._localPanelHistory.shift();\n }\n const detailSpec: import('../common/types.js').UISpec = {\n root: 'root',\n elements: {\n root: {\n type: 'ProductDetailsPanel',\n props: { product },\n },\n },\n };\n this._drawer?.setPanelContent(this._renderUISpec(detailSpec, ctx));\n this._currentPanelSource = { kind: 'spec', spec: detailSpec };\n this._drawer?.updatePanelTopBar(true, false, this._i18n.panelTitleProductDetails);\n },\n i18n: this._i18n,\n pricing: this.config.pricing,\n productSort: this._productSort,\n onSortChange: (sort) => {\n this._productSort = sort;\n },\n comparisonSelectMode: this._comparisonSelectMode,\n comparisonSelectedSkus: this._comparisonSelectedSkus,\n onToggleComparisonSku: (sku) => {\n this._toggleComparisonSku(sku);\n },\n favoritedSkus: this._session?.favoritedSkus ?? new Set(),\n onFavoriteToggle: (sku, product) => {\n const wasLiked = this._session?.favoritedSkus.has(sku) ?? false;\n void this._toggleFavorite(sku, product);\n // Only send like action to backend when adding, not when removing\n if (!wasLiked) {\n ga.trackLikeProduct(sku);\n const productName = (product['name'] as string | undefined) ?? sku;\n this._sendAction(\n {\n title: productName,\n type: 'like',\n payload: { sku },\n },\n { preservePanel: true },\n );\n }\n },\n isMobile: this._isMobileViewport,\n };\n return ctx;\n }\n\n private async _toggleFavorite(sku: string, product: Record<string, unknown>): Promise<void> {\n if (!this._session) return;\n const userId = this.config.session?.userId ?? '';\n const appId = this.config.accountId;\n await this._session.toggleFavorite(userId, appId, sku, product);\n this._drawer?.updateFavoritesBadge(this._session.favoritedSkus.size);\n }\n\n private _openFavoritesPanel(): void {\n if (!this._drawer) return;\n\n // Save current panel source to local history so back button can re-render it\n if (this._currentPanelSource) {\n const currentTitle = this._drawer.getPanelTopBarTitle() ?? '';\n this._localPanelHistory.push({ source: this._currentPanelSource, title: currentTitle });\n if (this._localPanelHistory.length > GengageChat._MAX_PANEL_HISTORY) this._localPanelHistory.shift();\n }\n\n this._drawer.setPanelContent(this._buildFavoritesPageEl());\n this._currentPanelSource = { kind: 'favorites' };\n this._drawer.updatePanelTopBar(true, false, this._i18n.favoritesPageTitle);\n }\n\n private _buildFavoritesPageEl(): HTMLElement {\n const favorites = this._session?.getFavoriteProducts() ?? [];\n\n if (favorites.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'gengage-chat-favorites-empty';\n\n const icon = document.createElement('div');\n icon.className = 'gengage-chat-favorites-empty-icon';\n icon.innerHTML = `<svg width=\"40\" height=\"40\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z\"/></svg>`;\n empty.appendChild(icon);\n\n const text = document.createElement('p');\n text.textContent = this._i18n.emptyFavoritesMessage;\n empty.appendChild(text);\n\n return empty;\n }\n\n // Convert favorites to product records and render as ProductGrid UISpec\n const elements: import('../common/types.js').UISpec['elements'] = {};\n const childKeys: string[] = [];\n\n for (const [i, fav] of favorites.entries()) {\n const key = `card_${i}`;\n childKeys.push(key);\n elements[key] = {\n type: 'ProductCard',\n props: {\n product: {\n sku: fav.sku,\n name: fav.name,\n imageUrl: fav.imageUrl,\n price: fav.price,\n } as Record<string, unknown>,\n },\n };\n }\n\n elements['grid'] = { type: 'ProductGrid', children: childKeys };\n\n const spec: import('../common/types.js').UISpec = { root: 'grid', elements };\n return this._renderUISpec(spec, this._buildRenderContext());\n }\n\n /**\n * Run registered callbacks for a GA4 event.\n * If any callback returns false or throws, handle the failure (e.g. show error for cart-add).\n */\n private async _runEventCallbacks(eventName: string, detail: Record<string, unknown>): Promise<void> {\n const callbacks = this._eventCallbacks.get(eventName);\n if (!callbacks || callbacks.size === 0) return;\n\n for (const cb of callbacks) {\n try {\n const result = cb(detail);\n const success = result instanceof Promise ? await result : result;\n if (success === false) {\n this._handleCallbackFailure(eventName, detail);\n return;\n }\n } catch {\n this._handleCallbackFailure(eventName, detail);\n return;\n }\n }\n }\n\n /**\n * Handle a callback failure — for add-to-cart, show an error message in chat.\n */\n private _handleCallbackFailure(eventName: string, _detail: Record<string, unknown>): void {\n if (eventName === 'gengage-cart-add') {\n const errorText = this._i18n.cartAddErrorMessage;\n const botMsg = this._createMessage('assistant', errorText);\n if (this._currentThreadId) botMsg.threadId = this._currentThreadId;\n this._messages.push(botMsg);\n this._drawer?.addMessage(botMsg);\n // Note: _sendAction is NOT called here — onAddToCart already sent the backend\n // request unconditionally. Sending again would duplicate the cart-add action.\n }\n }\n\n /**\n * Re-render inline UISpec elements for a restored bot message.\n * Inserts them into the messages container after the message bubble.\n */\n private _restoreInlineUISpec(chatMsg: ChatMessage): void {\n if (!chatMsg.uiSpec || !this._drawer) return;\n const spec = chatMsg.uiSpec;\n const rootElement = spec.elements[spec.root];\n if (!rootElement) return;\n\n const componentType = rootElement.type;\n\n // ActionButtons are rendered as pills/chips, not inline\n if (componentType === 'ActionButtons') return;\n\n // Panel-only components should not be rendered inline.\n // Note: panelHint is a StreamEvent property not stored in UISpec elements,\n // so we identify panel-only status by component type.\n // ProductDetailsPanel is panel-only but gets a compact ProductSummaryCard below.\n // ComparisonTable is always panel-only.\n // ProductGrid with similarsAppend is panel-appended, not inline.\n if (componentType === 'ComparisonTable') return;\n if (componentType === 'ProductGrid' && rootElement.props?.['similarsAppend'] === true) return;\n\n const renderContext = this._buildRenderContext();\n const messagesContainer = this._shadow?.querySelector('.gengage-chat-messages');\n if (!messagesContainer) return;\n\n // ProductDetailsPanel: synthesize a compact ProductSummaryCard for inline rendering\n if (componentType === 'ProductDetailsPanel') {\n const product = rootElement.props?.['product'] as Record<string, unknown> | undefined;\n if (!product) return;\n const inlineSpec: UISpec = {\n root: 'root',\n elements: { root: { type: 'ProductSummaryCard', props: { product } } },\n };\n const inline = this._renderUISpec(inlineSpec, renderContext);\n if (chatMsg.threadId) inline.dataset['threadId'] = chatMsg.threadId;\n messagesContainer.appendChild(inline);\n return;\n }\n\n const inline = this._renderUISpec(spec, renderContext);\n if (chatMsg.threadId) {\n inline.dataset['threadId'] = chatMsg.threadId;\n }\n messagesContainer.appendChild(inline);\n }\n\n private _createMessage(role: 'user' | 'assistant', content: string): ChatMessage {\n this._currentMessageId++;\n return {\n id: `msg-${this._currentMessageId}`,\n role,\n content,\n timestamp: Date.now(),\n status: 'done',\n };\n }\n\n private _resolveI18n(config: ChatWidgetConfig): ChatI18n {\n const base = resolveChatLocale(config.locale);\n return { ...base, ...config.i18n };\n }\n\n private _resolveUISpecRegistry(): ChatUISpecRegistry {\n const baseRegistry = createDefaultChatUISpecRegistry();\n return mergeUISpecRegistry(baseRegistry, this.config.renderer?.registry);\n }\n\n private _renderUISpec(spec: UISpec, context: ChatUISpecRenderContext): HTMLElement {\n const registry = this._resolveUISpecRegistry();\n const unknownRenderer = this.config.renderer?.unknownRenderer ?? defaultChatUnknownUISpecRenderer;\n const defaultRender = (inputSpec: UISpec, inputContext: ChatUISpecRenderContext) =>\n renderUISpec(inputSpec, inputContext, registry, unknownRenderer);\n\n const override = this.config.renderer?.renderUISpec;\n if (!override) return defaultRender(spec, context);\n\n const helpers: UISpecRenderHelpers<ChatUISpecRenderContext> = {\n registry,\n unknownRenderer,\n defaultRender,\n };\n return override(spec, context, helpers);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Convenience factory\n// ---------------------------------------------------------------------------\n\nexport function createChatWidget(): GengageChat {\n return new GengageChat();\n}\n\nexport type {\n ChatWidgetConfig,\n ChatMessage,\n ChatSession,\n ChatUIComponents,\n ChatI18n,\n ChatRendererConfig,\n ChatUISpecRenderContext,\n ChatUISpecRegistry,\n ProductSortState,\n SerializableChatMessage,\n} from './types.js';\nexport {\n renderUISpec,\n createDefaultChatUISpecRegistry,\n defaultChatUnknownUISpecRenderer,\n} from './components/renderUISpec.js';\nexport type { UISpecRenderContext } from './components/renderUISpec.js';\nexport { chatCatalog } from './catalog.js';\nexport type { ChatCatalog, ChatComponentName } from './catalog.js';\n"],"names":["uuidv7","now","bytes","hex","b","CommunicationBridge","options","_documentCurrentScript","event","type","payload","detail","handler","handlers","data","isValidBridgeData","msg","origin","obj","defaultLogger","routeStreamAction","action","logger","debugLog","handleUnknownAction","newTab","defaultNavigate","isRecord","policy","url","isSafeUrl","value","ALLOWED_MIME_TYPES","MAX_FILE_SIZE","validateImageFile","file","enrichActionPayload","ctx","existing","merge","additions","result","key","ACTION_TYPE_MAP","buildRequestBody","request","flatType","flatPayload","rest","rawType","rawPayload","mappedType","body","sendChatMessage","callbacks","transport","buildChatEndpointUrl","controller","requestBody","useFormData","fetchInit","formData","response","doneFired","fireDone","consumeStream","normalized","adaptBackendEvent","err","CHAT_I18N_TR","CHAT_I18N_EN","normalizeLocale","locale","resolveChatLocale","isVoiceInputSupported","getSpeechRecognitionConstructor","w","VoiceInput","Ctor","recognition","interim","latestFinal","i","alt","code","mapErrorCode","state","text","error","createKvkkBanner","banner","content","sanitizeHtml","dismiss","PanelTopBar","canBack","canForward","title","ThumbnailsColumn","entries","seen","unique","entry","thumb","isSafeImageUrl","img","DEFAULT_ACTION_ICON","SUGGESTED_ACTION_ICONS","DEFAULT_I18N","ChatDrawer","container","descId","descEl","handleEl","SNAP_THRESHOLD","dragStartY","dragDelta","dragging","onHandleTouchStart","e","t","onHandleTouchMove","clampedDelta","onHandleTouchEnd","currentState","nextState","onHandleTouchCancel","header","headerLeft","avatarUrl","avatar","headerInfo","titleRow","badge","powered","headerRight","reopenBtn","cartBtn","newChatBtn","closeBtn","favBtn","pressed","onPanelScroll","chevron","touchStartX","touchStartY","swipeThreshold","onDividerTouchStart","touch","onDividerTouchEnd","deltaX","deltaY","nextCollapsed","conversation","offlineBar","onOffline","onOnline","scrollRafPending","onMessagesScroll","scrollTop","scrollHeight","clientHeight","threadId","pillsScroll","pillsArrow","pillsRafPending","onPillsScroll","atEnd","inputArea","attachBtn","previewThumb","removeBtn","pill","_code","_message","footer","escapeHandler","message","bubble","thumbEl","blobUrl","links","link","href","rollbackBtn","searchText","indicator","sparkle","typing","hint","onStop","btn","icon","label","onRetry","errEl","textEl","retryBtn","actions","pills","scroll","svgHtml","iconSpan","textWrap","desc","arrow","html","onDismiss","el","children","child","contentType","skeleton","imgBlock","line","grid","card","row","block","isMobile","count","panel","atBottom","startX","startY","onTouchStart","onTouchEnd","dx","dy","collapsed","chevronBtn","accountId","list","step","marker","hasContent","attachment","force","messageId","allBubbles","lastThreadId","target","chips","chip","focusable","first","last","rootNode","active","cleanup","DEFAULT_SVG","createLauncher","contentArea","button","tooltipEl","contentAreaBottom","ALLOWED_AUDIO_TYPES","activeAudioElements","releaseAudio","audio","playTtsAudio","base64","baseType","CRITERIA_DISPLAY_NAMES","formatCriteriaName","rawName","criteriaLabels","c","renderComparisonTable","recommended","products","attributes","highlights","specialCases","onProductClick","i18n","heading","recCard","recLabel","recBody","info","price","formatPrice","hlSection","hlLabel","ul","hl","li","recExplanation","kdSection","kdHeading","kdContent","formatKeyDifferences","special","summary","sc","appendSpecialCaseListItems","table","thead","headerRow","emptyTh","product","th","name","prc","tbody","attr","labelTd","td","btnRow","focusables","lines","l","raw","sanitized","template","nestedItems","nestedItem","looksLikeHtml","renderReviewHighlights","element","reviews","empty","items","r","counts","item","tagCounts","firstTag","activeSentiment","activeTag","tabBar","allLabel","positiveLabel","negativeLabel","sentimentFilters","itemsContainer","renderItems","filtered","sentimentOk","tagOk","review","cls","tagEl","ratingEl","f","tab","pillsRow","tag","tagText","p","isActive","resolveActionSku","productSku","ROLE_LABELS","getRoleLabel","role","renderAITopPicks","suggestions","cardsWrap","suggestion","renderTopPickCard","renderCompactCard","discountPercent","discountBadge","clampDiscount","imageUrl","safeSetAttribute","addImageErrorHandler","nameEl","reasonEl","renderSentimentChips","score","displayScore","maxScale","originalPrice","priceRow","orig","current","sku","spinner","cta","roleLabel","roleEl","priceEl","labels","renderGroundingReviewCard","props","reviewCount","ctaLabel","titleEl","subtitle","normalizeGroupingAction","normalizedPayload","groupSkus","renderAIGroupingCards","labelsEl","renderAISuggestedSearchCards","diff","renderFloatingComparisonButton","selectedSkus","renderProsAndCons","productName","pros","cons","prosList","pro","consList","con","renderCategoriesContainer","context","groups","filterTags","tabs","panels","activateTab","index","j","group","tabId","panelId","next","renderCategoryProductCard","tagsContainer","tagBtn","renderHandoffNotice","summaryEl","renderProductSummaryCard","imgWrap","brand","fullName","rating","ratingRow","createStarRatingElement","isMobileViewport","DEFAULT_CHAT_UI_SPEC_REGISTRY","renderActionButtons","renderActionButton","renderProductCard","renderProductDetailsPanel","spec","renderElement","renderProductGrid","renderReviewHighlightsComponent","renderComparisonTableElement","renderDivider","defaultChatUnknownUISpecRenderer","wrapper","childId","rendered","createDefaultChatUISpecRegistry","renderUISpec","registry","unknownRenderer","renderUISpecWithRegistry","buttons","imgWrapper","findSimilarSku","favSku","heart","isFav","svgFill","svg","brandEl","inStock","stock","promotions","promoBadges","promo","cartCode","stepper","createQuantityStepper","quantity","checkbox","images","detailsSku","media","mainImg","firstSafe","u","thumbStrip","MAX_VISIBLE_THUMBNAILS","safeImages","activeThumb","activeThumbIdx","imgUrl","more","SWIPE_THRESHOLD","touchEndX","nextIdx","nextUrl","thumbEls","clampRating","currentPrice","oldPrice","variants","variantSection","variantLabel","variantList","variant","variantName","variantSku","labelText","variantPrice","actionRow","actionBtn","shareUrl","addCircle","cx","cy","svgNS","addLine","x1","y1","x2","y2","shareBtn","shareLabel","description","specifications","renderProductDetailTabs","tabPanels","keyCell","valCell","tabEl","idx","pIdx","getSortedChildIds","childIds","sort","withPrice","id","a","x","resortGrid","sorted","childMap","elId","toolbar","separator","compareBtn","sortedIds","viewMoreBtn","floatingBtn","keyDifferencesHtml","recommendedText","winnerHits","productActions","hr","labelEl","BLOCK_ELEMENTS","splitIntoBlocks","nodes","blocks","currentInline","node","containsTable","prefersReducedMotion","typewriteHtml","delayMs","onTick","onComplete","currentIndex","timerId","running","revealNext","isWordChar","char","linkProductMentions","mentions","mentionMap","m","lowerName","mention","walker","found","prevChar","nextChar","before","match","after","parent","INPUT_AREA_ICONS","INPUT_AREA_TYPES","isInputAreaAction","SESSION_STORAGE_KEY","GLOBAL_DISMISS_KEY","createChoicePrompter","headingEl","suggestionEl","markDismissed","isChoicePrompterGloballyDismissed","isChoicePrompterDismissed","dismissed","DB_NAME","DB_VERSION","STORE_SESSIONS","STORE_CONTEXT","STORE_PAYLOAD","STORE_FAVORITES","requestToPromise","resolve","reject","transactionComplete","tx","GengageIndexedDB","dbName","version","db","oldVersion","userId","appId","sessionId","cursor","store","range","results","BASE_PANEL_CONTENT_TYPES","ExtendedModeManager","types","hidden","shown","fav","PanelManager","deps","rebuild","drawer","contentEl","clone","snapshot","shadow","snapshotType","extended","bridge","componentType","backendTitle","isSearchLikeActionType","currentThreadId","pendingType","panelType","root","panelRoot","actionType","determinePanelUpdateAction","opts","SessionPersistence","params","prev","unlock","serializableMessages","sm","panelSnapshotHtml","msgId","persistFn","attempt","favs","KVKK_STORAGE_KEY","KVKK_TEXT_MARKERS","KVKK_LAW_NUMBER_RE","containsKvkk","lower","isKvkkShown","markKvkkShown","stripKvkkBlock","extractKvkkBlock","doc","LOCALE_TO_LANGUAGE","localeToOutputLanguage","MessageBubbleSchema","z.object","z.enum","z.string","z.number","ProductCardSchema","ActionButtonsSchema","z.array","z.unknown","TypingIndicatorSchema","DividerSchema","ComparisonProductSchema","ComparisonTableSchema","z.record","SentimentLabelSchema","AITopPickItemSchema","AITopPicksSchema","GroundingReviewCardSchema","AIGroupingCardsSchema","AISuggestedSearchCardsSchema","ActionPayloadSchema","ProductVariantSchema","z.union","ProductDetailsPanelSchema","z.boolean","ProductGridSchema","ReviewItemSchema","ReviewHighlightsSchema","ProsAndConsSchema","CategoriesContainerSchema","HandoffNoticeSchema","chatCatalog","isSafeCSSColor","isActionLike","_GengageChat","BaseWidget","config","style","chatStyles","rootEl","launcherOpts","drawerContainer","ga.trackLikeList","tid","panelMode","restoreSessionId","restoreSku","hasHandoff","idb","onResize","onViewportResize","offset","bridgeOpts","pending","dispatch","ga.trackInit","ga.trackShow","eventName","callback","set","nested","color","sid","welcomeMsg","hideLauncher","mobileHalf","mobileFull","ga.trackMessageSent","ga.trackConversationStart","queued","cutoff","removed","isPreservePanel","isPdpAutoLaunch","userText","lastMsg","userMsg","fallback","botMsg","prePanelSource","prePanelSourceCaptured","capturePanelSourceIfNeeded","restoreOrClearPanel","localBotText","chatHistory","meta","enrichedAction","requestId","streamStart","chunkIndex","panelLoadingSeen","panelContentReceived","streamStartEvent","streamController","isFinal","extra","streamChunkEvent","displayText","acctId","kvkkHtml","existingBubble","ga.trackMessageReceived","bubbleTextEl","widget","panelHint","rootElement","streamUiSpecEvent","renderContext","ga.trackCompareReceived","childCount","ga.trackSearch","panelSpec","shouldRenderInline","isFirstPanelContentInStream","panelAction","titleType","inlineSpec","messagesContainer","inline","previewImageUrls","mountEl","prompterRef","dismissOnKeyboard","inputChips","pillButtons","routerOpts","voiceEvent","allowed","ga.trackVoiceInput","voicePayload","llmUsageEvent","hadPanelContent","errMsg","streamErrorEvent","hiddenCompareBtn","panelSource","streamDoneEvent","meteringIncrementEvent","chatHistorySnapshotEvent","ctrl","msgs","panelEl","targetBot","currentSku","shouldRestore","session","maxMsgNum","chatMsg","idNum","uiSpec","panelBot","lastPanelThread","lastPanelBot","ga.trackCompareClear","s","ga.trackComparePreselection","gridWrapper","toggleBtn","cards","cb","wrappers","existingFloating","canCompare","ga.trackCompareSelected","ga.trackSuggestedQuestion","ga.trackFindSimilars","preservePanel","ga.trackProductDetail","ga.trackCartAdd","currentTitle","detailSpec","wasLiked","ga.trackLikeProduct","favorites","elements","childKeys","_detail","errorText","baseRegistry","mergeUISpecRegistry","defaultRender","inputSpec","inputContext","override","GengageChat","createChatWidget"],"mappings":"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"}