@spilki/widget 1.0.34 โ†’ 1.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"widget.umd.js","sources":["../src/ui/bubble.ts","../src/core/utils.ts","../src/ui/panel.ts","../src/core/transport.ts","../src/core/state.ts","../src/core/jwt.ts","../src/index.ts"],"sourcesContent":["import type { PositionOption } from \"../types\";\n\n/**\n * Controller for the floating chat bubble element.\n * Manages a Shadow DOM-isolated toggle button.\n */\nexport interface BubbleController {\n mount(): void;\n destroy(): void;\n setOpen(open: boolean): void;\n setBadge(count: number): void;\n element: HTMLDivElement;\n}\n\ninterface BubbleOptions {\n color: string;\n position: PositionOption;\n onClick(): void;\n}\n\nconst TEMPLATE = `\n <style>\n :host {\n all: initial;\n position: fixed;\n z-index: 2147483000;\n bottom: 24px;\n }\n :host([data-position=\"bottom-right\"]) {\n right: 24px;\n }\n :host([data-position=\"bottom-left\"]) {\n left: 24px;\n }\n button {\n all: unset;\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 56px;\n height: 56px;\n border-radius: 999px;\n cursor: pointer;\n background: var(--spilki-accent);\n color: #fff;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);\n transition: transform 0.18s ease, box-shadow 0.18s ease;\n }\n button:focus-visible {\n outline: 2px solid #fff;\n outline-offset: 3px;\n }\n button:hover {\n transform: translateY(-1px);\n box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25);\n }\n .icon {\n width: 28px;\n height: 28px;\n }\n .icon svg {\n width: 100%;\n height: 100%;\n }\n .badge {\n position: absolute;\n top: -4px;\n right: -4px;\n min-width: 20px;\n height: 20px;\n border-radius: 999px;\n background: #ef4444;\n color: #fff;\n font: 600 11px/20px system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n text-align: center;\n padding: 0 6px;\n box-sizing: border-box;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n }\n .badge[hidden] {\n display: none;\n }\n </style>\n <button type=\"button\" aria-label=\"Open chat\">\n <span class=\"icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5 6a3 3 0 0 1 3-3h16a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H14l-5 6v-6H8a3 3 0 0 1-3-3V6Z\" fill=\"currentColor\"/>\n </svg>\n </span>\n <span class=\"badge\" hidden aria-hidden=\"true\">0</span>\n </button>\n`;\n\n/**\n * Creates an isolated chat bubble toggle with Shadow DOM.\n * @param options - Bubble configuration (color, position, click handler).\n * @returns Controller to mount, destroy, and toggle the bubble.\n */\nexport function createBubble(options: BubbleOptions): BubbleController {\n const host = document.createElement(\"div\");\n host.setAttribute(\"part\", \"bubble-root\");\n host.setAttribute(\"data-position\", options.position);\n host.style.setProperty(\"--spilki-accent\", options.color);\n\n const shadow = host.attachShadow({ mode: \"open\" });\n shadow.innerHTML = TEMPLATE;\n const button = shadow.querySelector(\"button\")!;\n const badge = shadow.querySelector(\".badge\") as HTMLSpanElement;\n button.addEventListener(\"click\", () => options.onClick());\n\n const controller: BubbleController = {\n element: host,\n mount() {\n document.body.appendChild(host);\n },\n destroy() {\n host.remove();\n },\n setOpen(open: boolean) {\n button.setAttribute(\"aria-expanded\", String(open));\n button.setAttribute(\"aria-label\", open ? \"Close chat\" : \"Open chat\");\n },\n setBadge(count: number) {\n const safeCount = Math.max(0, count);\n const isOpen = button.getAttribute(\"aria-expanded\") === \"true\";\n if (safeCount === 0) {\n badge.toggleAttribute(\"hidden\", true);\n badge.textContent = \"0\";\n if (!isOpen) button.setAttribute(\"aria-label\", \"Open chat\");\n return;\n }\n badge.toggleAttribute(\"hidden\", false);\n badge.textContent = safeCount > 99 ? \"99+\" : String(safeCount);\n if (!isOpen) {\n button.setAttribute(\"aria-label\", `Open chat, ${safeCount} unread`);\n }\n }\n };\n return controller;\n}\n","import type {InitOptions, WidgetI18n} from \"../types\";\n\nexport const DEFAULT_API_BASE = \"https://api.spilki.app\";\nexport const SESSION_PREFIX = \"spilki-widget\";\n\nconst DEFAULT_I18N: WidgetI18n = {\n welcome: \"Hi! I'm your assistant.\",\n placeholder: \"Type a messageโ€ฆ\",\n sendLabel: \"Send\",\n typing: \"Assistant is typing\",\n thinking: \"Assistant is thinking\",\n searching: \"Searching\",\n executingTool: \"Working on it\",\n offline: \"Unable to connect. Please try again later.\",\n title: \"Spilki Assistant\"\n};\n\nexport const DEFAULT_OPTIONS: Required<\n Pick<InitOptions, \"position\" | \"theme\" | \"color\" | \"welcome\" | \"persist\" | \"sound\">\n> & { apiBase: string; i18n: WidgetI18n } = {\n apiBase: DEFAULT_API_BASE,\n position: \"bottom-right\",\n theme: \"auto\",\n color: \"#6366f1\",\n welcome: DEFAULT_I18N.welcome,\n persist: true,\n sound: true,\n i18n: DEFAULT_I18N\n};\n\nexport type NormalizedOptions = InitOptions & {\n apiBase: string;\n position: InitOptions[\"position\"];\n theme: InitOptions[\"theme\"];\n color: string;\n welcome: string;\n persist: boolean;\n sound: boolean;\n i18n: WidgetI18n;\n};\n\nexport function mergeOptions(options: InitOptions): NormalizedOptions {\n const mergedI18n = {...DEFAULT_I18N, ...(options.i18n ?? {})};\n return {\n ...DEFAULT_OPTIONS,\n ...options,\n apiBase: options.apiBase ?? DEFAULT_OPTIONS.apiBase,\n i18n: mergedI18n,\n welcome: options.welcome ?? mergedI18n.welcome,\n position: options.position ?? DEFAULT_OPTIONS.position,\n theme: options.theme ?? DEFAULT_OPTIONS.theme,\n color: options.color ?? DEFAULT_OPTIONS.color,\n persist: options.persist ?? DEFAULT_OPTIONS.persist,\n sound: options.sound ?? DEFAULT_OPTIONS.sound\n } as NormalizedOptions;\n}\n\nexport function storageKey(org: string): string {\n return `${SESSION_PREFIX}:${org}`;\n}\n\nexport function getPersistedSession(\n org: string,\n): string | null {\n try {\n return localStorage.getItem(storageKey(org));\n } catch (error) {\n console.warn(\"SpilkiWidget: Unable to read localStorage\", error);\n return null;\n }\n}\n\nexport function persistSession(\n org: string,\n sessionId: string\n): void {\n try {\n localStorage.setItem(storageKey(org), sessionId);\n } catch (error) {\n console.warn(\"SpilkiWidget: Unable to persist session\", error);\n }\n}\n\nexport function clearSession(org: string): void {\n try {\n localStorage.removeItem(storageKey(org));\n } catch (error) {\n console.warn(\"SpilkiWidget: Unable to clear session\", error);\n }\n}\n\nexport function createId(prefix = \"msg\"): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return `${prefix}-${Math.random().toString(16).slice(2)}`;\n}\n\nexport function prefersDark(): boolean {\n return window.matchMedia?.(\"(prefers-color-scheme: dark)\").matches ?? false;\n}\n\nexport function getTheme(theme: string | undefined): \"light\" | \"dark\" {\n if (theme === \"light\" || theme === \"dark\") return theme;\n return prefersDark() ? \"dark\" : \"light\";\n}\n\nexport function clampMessages<T>(messages: T[], limit = 30): T[] {\n return messages.slice(-limit);\n}\n\nexport function warnAllowedOrigins(hint: string[] | undefined): void {\n if (!hint || hint.length === 0) return;\n const origin = window.location.origin;\n if (!hint.includes(origin)) {\n console.warn(\n `SpilkiWidget: current origin ${origin} not in allowedOriginsHint: ${hint.join(\", \")}`\n );\n }\n}\n\nconst MONTH_NAMES = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\n\nexport function formatConversationTs(ts: number): string {\n const d = new Date(ts);\n const now = new Date();\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const time = `${pad(d.getHours())}:${pad(d.getMinutes())}`;\n\n const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();\n const yesterdayStart = todayStart - 86400000;\n const msgDayStart = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();\n\n if (msgDayStart === todayStart) return `Today at ${time}`;\n if (msgDayStart === yesterdayStart) return `Yesterday at ${time}`;\n return `${MONTH_NAMES[d.getMonth()]} ${d.getDate()}, ${time}`;\n}\n","import type { AgentEventType, Message, SuggestedReply, WidgetI18n } from \"../types\";\nimport type { ConversationGroup } from \"../core/state\";\nimport { formatConversationTs } from \"../core/utils\";\nimport styles from \"./styles.css?inline\";\n\nconst RELATIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000;\nconst CONSECUTIVE_TIMESTAMP_MS = 2 * 60 * 1000;\nconst SCROLL_SHOW_THRESHOLD = 200;\n\nconst EMOJIS = [\n \"๐Ÿ˜€\", \"๐Ÿ˜‚\", \"๐Ÿคฃ\", \"๐Ÿ˜Š\", \"๐Ÿ˜\", \"๐Ÿฅฐ\", \"๐Ÿ˜˜\", \"๐Ÿ˜‰\", \"๐Ÿค”\", \"๐Ÿ˜\",\n \"๐Ÿ˜Ž\", \"๐Ÿค—\", \"๐Ÿ˜ข\", \"๐Ÿ˜ญ\", \"๐Ÿ˜ก\", \"๐Ÿคฏ\", \"๐Ÿ˜ฑ\", \"๐Ÿฅณ\", \"๐Ÿ˜ด\", \"๐Ÿคฎ\",\n \"๐Ÿ‘\", \"๐Ÿ‘Ž\", \"๐Ÿ‘‹\", \"๐Ÿค\", \"๐Ÿ™\", \"๐Ÿ’ช\", \"โœŒ๏ธ\", \"๐Ÿคž\", \"๐Ÿ‘\", \"๐Ÿซถ\",\n \"โค๏ธ\", \"๐Ÿ”ฅ\", \"โญ\", \"๐Ÿ’ฏ\", \"โœ…\", \"โŒ\", \"โšก\", \"๐ŸŽ‰\", \"๐Ÿ’ฐ\", \"๐Ÿ“ฆ\",\n \"๐Ÿ“…\", \"๐Ÿ“\", \"๐Ÿ“ž\", \"๐Ÿ’ฌ\", \"๐Ÿ””\", \"๐Ÿ”—\", \"๐Ÿ“ธ\", \"๐ŸŽต\", \"โ˜•\", \"๐Ÿ•\",\n] as const;\n\ninterface RenderedMessage {\n message: Message;\n timeEl: HTMLSpanElement;\n}\n\ninterface PanelOptions {\n color: string;\n theme: \"light\" | \"dark\";\n position: \"bottom-right\" | \"bottom-left\";\n i18n: WidgetI18n;\n onClose(): void;\n onSend(text: string): void;\n}\n\n// #7: escape HTML to prevent XSS via i18n values\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/**\n * Chat panel UI component rendered inside a Shadow DOM.\n * Manages message display, input, accessibility, and focus trapping.\n */\nexport class Panel {\n private readonly host: HTMLDivElement;\n private readonly shadow: ShadowRoot;\n private readonly messagesEl: HTMLDivElement;\n private readonly typingEl: HTMLDivElement;\n private readonly input: HTMLTextAreaElement;\n private readonly offlineEl: HTMLDivElement;\n private readonly sendButton: HTMLButtonElement;\n private readonly emojiButton: HTMLButtonElement;\n private readonly emojiPickerEl: HTMLDivElement;\n private readonly scrollBottomButton: HTMLButtonElement;\n private readonly scrollBottomCountEl: HTMLSpanElement;\n private readonly closeButton: HTMLButtonElement;\n private readonly suggestedRepliesEl: HTMLDivElement;\n private readonly relativeTimeFormat = new Intl.RelativeTimeFormat(undefined, {\n numeric: \"auto\",\n });\n private readonly absoluteTimeFormat = new Intl.DateTimeFormat(undefined, {\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false,\n });\n private readonly dateSeparatorFormat = new Intl.DateTimeFormat(undefined, {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n });\n private readonly renderedMessages: RenderedMessage[] = [];\n private readonly focusable: HTMLElement[] = [];\n private readonly seenIds = new Set<string>();\n private lastTimelineMessage: Message | null = null;\n private scrollUnreadCount = 0;\n private emojiPickerOpen = false;\n private open = false;\n\n // #4: stored listener references for cleanup\n private readonly handleCloseClick: () => void;\n private readonly handleSendClick: () => void;\n private readonly handleInputKeydown: (e: KeyboardEvent) => void;\n private readonly handleShadowKeydown: (e: Event) => void;\n private readonly handleFocusin: () => void;\n private readonly handleMessagesScroll: () => void;\n private readonly handleScrollBottomClick: () => void;\n private readonly handleEmojiClick: () => void;\n private readonly handleDocumentPointerDown: (e: PointerEvent) => void;\n\n constructor(private readonly options: PanelOptions) {\n this.host = document.createElement(\"div\");\n this.host.setAttribute(\"part\", \"panel-root\");\n this.host.style.position = \"fixed\";\n this.host.style.bottom = \"96px\";\n this.host.style[options.position === \"bottom-right\" ? \"right\" : \"left\"] = \"24px\";\n this.host.style.width = \"360px\";\n this.host.style.maxWidth = \"calc(100vw - 32px)\";\n this.host.style.height = \"520px\";\n this.host.style.display = \"none\";\n this.host.style.zIndex = \"2147483001\";\n\n this.shadow = this.host.attachShadow({ mode: \"open\" });\n\n // #7: escape all i18n values before HTML interpolation\n const safeTitle = escapeHtml(options.i18n.title);\n const safeTyping = escapeHtml(options.i18n.typing);\n const safeOffline = escapeHtml(options.i18n.offline);\n const safePlaceholder = escapeHtml(options.i18n.placeholder);\n const safeSendLabel = escapeHtml(options.i18n.sendLabel);\n\n this.shadow.innerHTML = `\n <style>${styles}</style>\n <div class=\"wrapper\" role=\"dialog\" aria-modal=\"true\" aria-label=\"${safeTitle}\">\n <header>\n <h1><span class=\"status-dot\" aria-hidden=\"true\"></span>${safeTitle}</h1>\n <button class=\"close\" type=\"button\" aria-label=\"Close\">ร—</button>\n </header>\n <div class=\"messages\" part=\"messages\" role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\"></div>\n <button class=\"scroll-bottom\" type=\"button\" aria-label=\"Scroll to latest messages\">\n <span class=\"scroll-arrow\" aria-hidden=\"true\">โ–ผ</span>\n <span class=\"scroll-count\" hidden>0</span>\n </button>\n <div class=\"typing\" hidden aria-live=\"polite\" aria-atomic=\"true\">${safeTyping}</div>\n <div class=\"offline\" hidden aria-live=\"assertive\" aria-atomic=\"true\">${safeOffline}</div>\n <div class=\"suggested-replies\" role=\"group\" hidden aria-label=\"Suggested replies\"></div>\n <div class=\"emoji-picker\" aria-label=\"Emoji picker\"></div>\n <div class=\"input-area\">\n <div class=\"input-wrap\">\n <textarea rows=\"1\" placeholder=\"${safePlaceholder}\" aria-label=\"${safePlaceholder}\"></textarea>\n <div class=\"input-actions\">\n <button class=\"emoji-btn\" type=\"button\" aria-label=\"Insert emoji\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M8 14s1.5 2 4 2 4-2 4-2\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <circle cx=\"9\" cy=\"10\" r=\"1\" fill=\"currentColor\"/>\n <circle cx=\"15\" cy=\"10\" r=\"1\" fill=\"currentColor\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"send-btn\" aria-label=\"${safeSendLabel}\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" fill=\"currentColor\"/>\n </svg>\n </button>\n </div>\n </div>\n </div>\n </div>\n `;\n\n this.host.dataset.theme = options.theme;\n this.host.style.setProperty(\"--spilki-accent\", options.color);\n\n this.messagesEl = this.shadow.querySelector(\".messages\") as HTMLDivElement;\n this.typingEl = this.shadow.querySelector(\".typing\") as HTMLDivElement;\n this.input = this.shadow.querySelector(\"textarea\") as HTMLTextAreaElement;\n this.offlineEl = this.shadow.querySelector(\".offline\") as HTMLDivElement;\n this.sendButton = this.shadow.querySelector(\".send-btn\") as HTMLButtonElement;\n this.emojiButton = this.shadow.querySelector(\".emoji-btn\") as HTMLButtonElement;\n this.emojiPickerEl = this.shadow.querySelector(\".emoji-picker\") as HTMLDivElement;\n this.scrollBottomButton = this.shadow.querySelector(\".scroll-bottom\") as HTMLButtonElement;\n this.scrollBottomCountEl = this.shadow.querySelector(\".scroll-count\") as HTMLSpanElement;\n this.closeButton = this.shadow.querySelector(\"header .close\") as HTMLButtonElement;\n this.suggestedRepliesEl = this.shadow.querySelector(\".suggested-replies\") as HTMLDivElement;\n\n // #4: bind and store listener references\n this.handleCloseClick = () => this.options.onClose();\n this.handleSendClick = () => this.send();\n this.handleInputKeydown = (event: KeyboardEvent) => {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n this.send();\n } else if (event.key === \"Escape\") {\n if (this.emojiPickerOpen) {\n event.stopPropagation();\n this.closeEmojiPicker();\n return;\n }\n this.options.onClose();\n }\n };\n this.handleShadowKeydown = (event: Event) => {\n const keyboard = event as KeyboardEvent;\n if (keyboard.key === \"Escape\") {\n if (this.emojiPickerOpen) {\n keyboard.preventDefault();\n this.closeEmojiPicker();\n return;\n }\n this.options.onClose();\n }\n if (keyboard.key === \"Tab\") {\n this.trapFocus(keyboard);\n }\n };\n this.handleFocusin = () => this.collectFocusable();\n this.handleMessagesScroll = () => this.updateScrollBottomState();\n this.handleScrollBottomClick = () => {\n this.scrollToBottom();\n this.resetScrollUnreadCount();\n };\n this.handleEmojiClick = () => {\n this.toggleEmojiPicker();\n };\n this.handleDocumentPointerDown = (event: PointerEvent) => {\n if (!this.emojiPickerOpen) return;\n const path = event.composedPath();\n if (path.includes(this.emojiPickerEl) || path.includes(this.emojiButton)) return;\n this.closeEmojiPicker();\n };\n\n this.closeButton.addEventListener(\"click\", this.handleCloseClick);\n this.sendButton.addEventListener(\"click\", this.handleSendClick);\n this.emojiButton.addEventListener(\"click\", this.handleEmojiClick);\n this.scrollBottomButton.addEventListener(\"click\", this.handleScrollBottomClick);\n this.input.addEventListener(\"keydown\", this.handleInputKeydown);\n this.messagesEl.addEventListener(\"scroll\", this.handleMessagesScroll);\n this.shadow.addEventListener(\"keydown\", this.handleShadowKeydown);\n this.shadow.addEventListener(\"focusin\", this.handleFocusin);\n document.addEventListener(\"pointerdown\", this.handleDocumentPointerDown, true);\n\n this.renderEmojiPicker();\n this.updateScrollBottomState();\n this.collectFocusable();\n }\n\n mount(): void {\n document.body.appendChild(this.host);\n }\n\n // #4: remove all event listeners before removing DOM\n destroy(): void {\n this.closeButton.removeEventListener(\"click\", this.handleCloseClick);\n this.sendButton.removeEventListener(\"click\", this.handleSendClick);\n this.emojiButton.removeEventListener(\"click\", this.handleEmojiClick);\n this.scrollBottomButton.removeEventListener(\"click\", this.handleScrollBottomClick);\n this.input.removeEventListener(\"keydown\", this.handleInputKeydown);\n this.messagesEl.removeEventListener(\"scroll\", this.handleMessagesScroll);\n this.shadow.removeEventListener(\"keydown\", this.handleShadowKeydown);\n this.shadow.removeEventListener(\"focusin\", this.handleFocusin);\n document.removeEventListener(\"pointerdown\", this.handleDocumentPointerDown, true);\n this.host.remove();\n }\n\n show(): void {\n if (this.open) return;\n this.open = true;\n this.host.style.display = \"block\";\n this.focusInput();\n }\n\n hide(): void {\n if (!this.open) return;\n this.open = false;\n this.closeEmojiPicker();\n this.host.style.display = \"none\";\n }\n\n focusInput(): void {\n queueMicrotask(() => {\n this.input.focus();\n });\n }\n\n updateTheme(theme: \"light\" | \"dark\"): void {\n this.host.dataset.theme = theme;\n }\n\n // #16: rebuild seen IDs on full re-render\n updateMessages(messages: Message[]): void {\n this.messagesEl.innerHTML = \"\";\n this.seenIds.clear();\n this.renderedMessages.length = 0;\n this.lastTimelineMessage = null;\n messages.forEach((message) => {\n this.seenIds.add(message.id);\n this.appendTimelineMessage(message);\n });\n this.updateTimestampVisibility();\n this.scrollToBottom();\n this.resetScrollUnreadCount();\n this.collectFocusable();\n }\n\n // Render conversation groups with collapsed history separators\n renderWithConversations(historyGroups: ConversationGroup[], currentMessages: Message[]): void {\n this.messagesEl.innerHTML = \"\";\n this.seenIds.clear();\n this.renderedMessages.length = 0;\n this.lastTimelineMessage = null;\n\n for (const group of historyGroups) {\n group.messages.forEach((msg) => this.seenIds.add(msg.id));\n const historyContainer = this.createHistoryContainer(group.messages);\n const lastTs = group.messages[group.messages.length - 1].ts;\n const separator = this.createSeparatorElement(lastTs, historyContainer);\n this.messagesEl.appendChild(historyContainer);\n this.messagesEl.appendChild(separator);\n }\n\n currentMessages.forEach((msg) => {\n this.seenIds.add(msg.id);\n this.appendTimelineMessage(msg);\n });\n\n this.updateTimestampVisibility();\n this.scrollToBottom();\n this.resetScrollUnreadCount();\n this.collectFocusable();\n }\n\n // #16: deduplicate; #19: smart scroll; #21: update focus trap\n appendMessage(message: Message): void {\n if (this.seenIds.has(message.id)) return;\n const wasScrolledUp = this.isScrolledUp();\n\n this.seenIds.add(message.id);\n this.appendTimelineMessage(message);\n this.updateTimestampVisibility();\n\n if (wasScrolledUp) {\n this.scrollUnreadCount += 1;\n this.updateScrollBottomState();\n } else {\n this.scrollToBottom();\n this.resetScrollUnreadCount();\n }\n\n this.collectFocusable();\n }\n\n setSuggestedReplies(replies: SuggestedReply[]): void {\n if (replies.length === 0) {\n this.hideSuggestedReplies();\n return;\n }\n this.suggestedRepliesEl.replaceChildren();\n replies.forEach((reply) => {\n const chip = document.createElement(\"button\");\n chip.className = \"suggested-chip\";\n chip.type = \"button\";\n chip.textContent = reply.text;\n chip.addEventListener(\"click\", () => {\n this.options.onSend(reply.payload ?? reply.text);\n this.hideSuggestedReplies();\n });\n this.suggestedRepliesEl.appendChild(chip);\n });\n this.suggestedRepliesEl.toggleAttribute(\"hidden\", false);\n this.collectFocusable();\n }\n\n hideSuggestedReplies(): void {\n this.suggestedRepliesEl.toggleAttribute(\"hidden\", true);\n this.suggestedRepliesEl.replaceChildren();\n this.collectFocusable();\n this.focusInput();\n }\n\n setAgentActivity(eventType: AgentEventType | null, i18n: WidgetI18n): void {\n if (eventType === null) {\n this.typingEl.toggleAttribute(\"hidden\", true);\n return;\n }\n const textMap: Record<AgentEventType, string> = {\n TYPING: i18n.typing,\n THINKING: i18n.thinking,\n SEARCHING: i18n.searching,\n EXECUTING_TOOL: i18n.executingTool,\n };\n this.typingEl.textContent = textMap[eventType] ?? i18n.typing;\n this.typingEl.toggleAttribute(\"hidden\", false);\n }\n\n setTyping(active: boolean): void {\n this.typingEl.toggleAttribute(\"hidden\", !active);\n }\n\n setOffline(active: boolean): void {\n this.offlineEl.toggleAttribute(\"hidden\", !active);\n }\n\n clearInput(): void {\n this.input.value = \"\";\n this.input.dispatchEvent(new Event(\"input\"));\n }\n\n private send(): void {\n const text = this.input.value.trim();\n if (!text) return;\n this.options.onSend(text);\n this.clearInput();\n this.hideSuggestedReplies();\n }\n\n private createMessageElement(message: Message): { item: HTMLDivElement; timeEl: HTMLSpanElement } {\n const item = document.createElement(\"div\");\n item.className = `message ${message.author}`;\n item.setAttribute(\"data-author\", message.author);\n item.setAttribute(\"role\", \"article\");\n item.setAttribute(\"aria-label\", message.author === \"user\" ? \"You\" : \"Assistant\");\n\n const bubble = document.createElement(\"div\");\n bubble.className = \"bubble\";\n bubble.textContent = message.text;\n\n const timeEl = document.createElement(\"span\");\n timeEl.className = \"msg-time\";\n timeEl.textContent = this.formatMessageTimestamp(message.ts);\n\n item.appendChild(bubble);\n item.appendChild(timeEl);\n return { item, timeEl };\n }\n\n private createSeparatorElement(ts: number, historyContainer: HTMLDivElement): HTMLDivElement {\n const el = document.createElement(\"div\");\n el.className = \"conversation-separator\";\n el.setAttribute(\"role\", \"button\");\n el.setAttribute(\"tabindex\", \"0\");\n el.setAttribute(\"aria-expanded\", \"false\");\n el.setAttribute(\"aria-label\", \"Show previous conversation\");\n el.textContent = formatConversationTs(ts);\n\n const toggle = () => {\n const expanded = historyContainer.classList.toggle(\"expanded\");\n el.setAttribute(\"aria-expanded\", String(expanded));\n el.setAttribute(\"aria-label\",\n expanded ? \"Hide previous conversation\" : \"Show previous conversation\");\n };\n\n el.addEventListener(\"click\", toggle);\n el.addEventListener(\"keydown\", (e: Event) => {\n const ke = e as KeyboardEvent;\n if (ke.key === \"Enter\" || ke.key === \" \") {\n ke.preventDefault();\n toggle();\n }\n });\n\n return el;\n }\n\n private createHistoryContainer(messages: Message[]): HTMLDivElement {\n const container = document.createElement(\"div\");\n container.className = \"conversation-history\";\n let previous: Message | null = null;\n\n messages.forEach((msg) => {\n this.appendDateSeparatorIfNeeded(previous, msg, container);\n const { item } = this.createMessageElement(msg);\n container.appendChild(item);\n previous = msg;\n });\n\n return container;\n }\n\n private appendTimelineMessage(message: Message): void {\n this.appendDateSeparatorIfNeeded(this.lastTimelineMessage, message, this.messagesEl);\n const rendered = this.createMessageElement(message);\n this.messagesEl.appendChild(rendered.item);\n this.renderedMessages.push({ message, timeEl: rendered.timeEl });\n this.lastTimelineMessage = message;\n }\n\n private appendDateSeparatorIfNeeded(\n previous: Message | null,\n current: Message,\n container: HTMLElement,\n ): void {\n if (!previous || this.isSameDay(previous.ts, current.ts)) {\n return;\n }\n\n const separator = document.createElement(\"div\");\n separator.className = \"date-separator\";\n separator.textContent = this.formatDateSeparator(current.ts);\n container.appendChild(separator);\n }\n\n private updateTimestampVisibility(): void {\n for (let index = 0; index < this.renderedMessages.length; index += 1) {\n const current = this.renderedMessages[index];\n const next = this.renderedMessages[index + 1];\n const hideTimestamp =\n Boolean(next) &&\n next.message.author === current.message.author &&\n next.message.ts - current.message.ts <= CONSECUTIVE_TIMESTAMP_MS &&\n next.message.ts >= current.message.ts;\n\n current.timeEl.toggleAttribute(\"hidden\", hideTimestamp);\n current.timeEl.textContent = this.formatMessageTimestamp(current.message.ts);\n }\n }\n\n private formatMessageTimestamp(ts: number): string {\n const now = Date.now();\n const age = now - ts;\n if (age < RELATIVE_THRESHOLD_MS) {\n const minutes = Math.max(1, Math.round(age / (60 * 1000)));\n if (minutes < 60) {\n return this.relativeTimeFormat.format(-minutes, \"minute\");\n }\n const hours = Math.max(1, Math.round(minutes / 60));\n return this.relativeTimeFormat.format(-hours, \"hour\");\n }\n return this.absoluteTimeFormat.format(new Date(ts));\n }\n\n private formatDateSeparator(ts: number): string {\n const date = new Date(ts);\n const today = this.startOfDay(Date.now());\n const messageDay = this.startOfDay(ts);\n if (messageDay === today) {\n return \"Today\";\n }\n if (messageDay === today - RELATIVE_THRESHOLD_MS) {\n return \"Yesterday\";\n }\n return this.dateSeparatorFormat.format(date);\n }\n\n private isSameDay(leftTs: number, rightTs: number): boolean {\n return this.startOfDay(leftTs) === this.startOfDay(rightTs);\n }\n\n private startOfDay(ts: number): number {\n const date = new Date(ts);\n return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();\n }\n\n private isScrolledUp(): boolean {\n return this.distanceFromBottom() > SCROLL_SHOW_THRESHOLD;\n }\n\n private distanceFromBottom(): number {\n return this.messagesEl.scrollHeight - this.messagesEl.scrollTop - this.messagesEl.clientHeight;\n }\n\n scrollToFirstUnread(lastReadTs: number): void {\n if (lastReadTs <= 0) {\n this.scrollToBottom();\n return;\n }\n const firstUnread = this.renderedMessages.find(\n (r) => r.message.author === \"bot\" && r.message.ts > lastReadTs,\n );\n if (!firstUnread) {\n this.scrollToBottom();\n return;\n }\n const container = firstUnread.timeEl.closest(\".message\");\n if (container) {\n container.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n }\n }\n\n private scrollToBottom(): void {\n if (typeof this.messagesEl.scrollTo === \"function\") {\n this.messagesEl.scrollTo({ top: this.messagesEl.scrollHeight, behavior: \"smooth\" });\n return;\n }\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight;\n }\n\n private updateScrollBottomState(): void {\n const scrolledUp = this.isScrolledUp();\n this.scrollBottomButton.classList.toggle(\"visible\", scrolledUp);\n this.scrollBottomButton.setAttribute(\"aria-hidden\", String(!scrolledUp));\n this.scrollBottomButton.tabIndex = scrolledUp ? 0 : -1;\n\n if (!scrolledUp) {\n this.resetScrollUnreadCount();\n }\n\n const hasUnread = this.scrollUnreadCount > 0;\n this.scrollBottomCountEl.toggleAttribute(\"hidden\", !hasUnread);\n this.scrollBottomCountEl.textContent = hasUnread\n ? this.scrollUnreadCount > 99\n ? \"99+\"\n : String(this.scrollUnreadCount)\n : \"0\";\n }\n\n private resetScrollUnreadCount(): void {\n if (this.scrollUnreadCount === 0) return;\n this.scrollUnreadCount = 0;\n this.updateScrollBottomState();\n }\n\n private toggleEmojiPicker(): void {\n if (this.emojiPickerOpen) {\n this.closeEmojiPicker();\n return;\n }\n this.emojiPickerOpen = true;\n this.emojiPickerEl.style.display = \"grid\";\n this.emojiButton.setAttribute(\"aria-expanded\", \"true\");\n this.collectFocusable();\n }\n\n private closeEmojiPicker(): void {\n if (!this.emojiPickerOpen) return;\n this.emojiPickerOpen = false;\n this.emojiPickerEl.style.display = \"none\";\n this.emojiButton.setAttribute(\"aria-expanded\", \"false\");\n this.collectFocusable();\n }\n\n private renderEmojiPicker(): void {\n this.emojiPickerEl.replaceChildren();\n EMOJIS.forEach((emoji) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"emoji-option\";\n button.textContent = emoji;\n button.setAttribute(\"aria-label\", `Insert ${emoji}`);\n button.addEventListener(\"click\", () => {\n this.insertEmojiAtCursor(emoji);\n this.closeEmojiPicker();\n });\n this.emojiPickerEl.appendChild(button);\n });\n }\n\n private insertEmojiAtCursor(emoji: string): void {\n const value = this.input.value;\n const start = this.input.selectionStart ?? value.length;\n const end = this.input.selectionEnd ?? value.length;\n this.input.value = `${value.slice(0, start)}${emoji}${value.slice(end)}`;\n const cursor = start + emoji.length;\n this.input.setSelectionRange(cursor, cursor);\n this.input.dispatchEvent(new Event(\"input\", { bubbles: true }));\n this.input.focus();\n }\n\n private collectFocusable(): void {\n const items = this.shadow.querySelectorAll<HTMLElement>(\n 'button, textarea, [href], [tabindex]:not([tabindex=\"-1\"])'\n );\n this.focusable.length = 0;\n items.forEach((el) => {\n if (!el.hasAttribute(\"disabled\") && !el.hasAttribute(\"hidden\") && el.tabIndex >= 0) {\n this.focusable.push(el);\n }\n });\n }\n\n private trapFocus(event: KeyboardEvent): void {\n if (this.focusable.length === 0) return;\n const first = this.focusable[0];\n const last = this.focusable[this.focusable.length - 1];\n const active = this.shadow.activeElement as HTMLElement;\n if (event.shiftKey && active === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n}\n","import type {AgentEventPayload, ConnectResponse, Message, SendPayload, TransportKind, WidgetUser} from \"../types\";\nimport {clampMessages} from \"./utils\";\n\ninterface TransportHandlers {\n onOpen(kind: TransportKind): void;\n\n onMessage(message: Message): void;\n\n onTyping(typing: boolean): void;\n\n onAgentEvent(event: AgentEventPayload): void;\n\n onError(error: Error): void;\n}\n\ninterface TransportOptions {\n apiBase: string;\n accessToken?: string;\n org: string;\n sessionId?: string | null;\n}\n\ntype IncomingPayload =\n | { type: \"message\"; payload: Message }\n | { type: \"typing\"; payload: { active: boolean } }\n | { type: \"AGENT_EVENT\"; payload: AgentEventPayload }\n | Message;\n\nconst RETRY_BASE = 1500;\nconst RETRY_MAX = 8000;\n\nexport class TransportManager {\n private sessionId: string | null = null;\n private currentKind: TransportKind | null = null;\n private ws?: WebSocket;\n private sseAbort?: AbortController;\n private pollTimer?: number;\n private retryTimer?: number; // #8 #9: track retry timer\n private stopped = false;\n private backoff = RETRY_BASE;\n private connectPromise: Promise<ConnectResponse> | null = null; // #3: connect lock\n private readonly handlers: TransportHandlers;\n private readonly options: TransportOptions;\n\n // #5 #6: stored handler refs for cleanup\n private wsOnOpen?: () => void;\n private wsOnMessage?: (event: MessageEvent) => void;\n private wsOnClose?: () => void;\n private wsOnError?: () => void;\n\n constructor(options: TransportOptions, handlers: TransportHandlers) {\n this.options = options;\n this.handlers = handlers;\n }\n\n private user?: WidgetUser;\n\n setAccessToken(token?: string) {\n this.options.accessToken = token;\n }\n\n setUser(user: WidgetUser) {\n const sanitized: Record<string, string> = {};\n for (const [key, value] of Object.entries(user)) {\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed) sanitized[key] = trimmed;\n }\n }\n this.user = sanitized as unknown as WidgetUser;\n\n if (this.sessionId) {\n this.sendIdentify().catch((err) =>\n this.handlers.onError(err as Error)\n );\n }\n }\n\n private async sendIdentify(): Promise<void> {\n const url = `${this.options.apiBase.replace(/\\/$/, \"\")}/widget/identify`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(this.options.accessToken ? {\"X-Authorization\": `Bearer ${this.options.accessToken}`} : {})\n },\n body: JSON.stringify({\n sessionId: this.sessionId,\n user: this.user\n })\n });\n if (!res.ok) {\n throw new Error(`SpilkiWidget: identify failed (${res.status})`);\n }\n }\n\n get kind(): TransportKind | null {\n return this.currentKind;\n }\n\n get activeSession(): string | null {\n return this.sessionId ?? this.options.sessionId ?? null;\n }\n\n // #3: connect lock โ€” return in-flight promise if already connecting\n async connect(): Promise<ConnectResponse> {\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = this.doConnect();\n try {\n return await this.connectPromise;\n } finally {\n this.connectPromise = null;\n }\n }\n\n private async doConnect(): Promise<ConnectResponse> {\n this.stopped = false;\n const connectUrl = `${this.options.apiBase.replace(/\\/$/, \"\")}/widget/session`;\n const currentSession = this.sessionId ?? this.options.sessionId ?? undefined;\n const body = {\n organisationId: this.options.org,\n sessionId: currentSession,\n userAgent: typeof navigator !== \"undefined\" ? navigator.userAgent : \"\",\n referrer: typeof document !== \"undefined\" ? document.referrer : \"\",\n origin: typeof window !== \"undefined\" ? window.location.origin : \"\",\n ...(this.user ? {user: this.user} : {})\n };\n\n const res = await fetch(connectUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(this.options.accessToken ? {\"X-Authorization\": `Bearer ${this.options.accessToken}`} : {})\n },\n body: JSON.stringify(body)\n });\n\n if (!res.ok) {\n throw new Error(`SpilkiWidget: connect failed (${res.status})`);\n }\n\n const data = (await res.json()) as ConnectResponse;\n this.sessionId = data.sessionId;\n this.options.sessionId = data.sessionId;\n this.backoff = RETRY_BASE;\n await this.startTransport(data);\n return data;\n }\n\n async send(text: string, messageId?: string): Promise<void> {\n const payload: SendPayload = {\n sessionId: this.sessionId ?? this.options.sessionId ?? \"\",\n text,\n ...(messageId ? {messageId} : {}),\n ...(this.user?.userId ? {userId: this.user.userId} : {})\n };\n\n if (!payload.sessionId) {\n throw new Error(\"SpilkiWidget: missing session id\");\n }\n\n if (this.currentKind === \"ws\" && this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({type: \"message\", payload}));\n return;\n }\n\n const url = `${this.options.apiBase.replace(/\\/$/, \"\")}/widget/message`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Authorization\": `Bearer ${this.options.accessToken}`\n },\n body: JSON.stringify(payload)\n });\n\n if (!res.ok) {\n throw new Error(`SpilkiWidget: send failed (${res.status})`);\n }\n }\n\n // #1: accept resetBackoff param; #8 #9: clear retryTimer\n stop(resetBackoff = true): void {\n this.stopped = true;\n if (resetBackoff) {\n this.backoff = RETRY_BASE;\n }\n\n // #8 #9: clear pending retry timeout\n if (this.retryTimer) {\n clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n\n // #5: remove WS listeners before closing\n if (this.ws) {\n if (this.wsOnOpen) this.ws.removeEventListener(\"open\", this.wsOnOpen);\n if (this.wsOnMessage) this.ws.removeEventListener(\"message\", this.wsOnMessage);\n if (this.wsOnClose) this.ws.removeEventListener(\"close\", this.wsOnClose);\n if (this.wsOnError) this.ws.removeEventListener(\"error\", this.wsOnError);\n this.ws.close();\n this.ws = undefined;\n }\n this.wsOnOpen = this.wsOnMessage = this.wsOnClose = this.wsOnError = undefined;\n\n // #6: abort fetch-based SSE stream\n if (this.sseAbort) {\n this.sseAbort.abort();\n this.sseAbort = undefined;\n }\n\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = undefined;\n }\n this.currentKind = null;\n }\n\n private async startTransport(connect: ConnectResponse): Promise<void> {\n if (this.stopped) return;\n const attempts: Array<() => Promise<void>> = [];\n if (connect.wsUrl) attempts.push(() => this.startWs(connect.wsUrl!));\n if (connect.sseUrl) attempts.push(() => this.startSse(connect.sseUrl!));\n if (connect.pollUrl) attempts.push(() => this.startPoll(connect.pollUrl!));\n\n for (const attempt of attempts) {\n try {\n await attempt();\n return;\n } catch (error) {\n this.handlers.onError(error as Error);\n }\n }\n throw new Error(\"SpilkiWidget: unable to establish transport\");\n }\n\n // #5: store named handler refs for WS cleanup\n private startWs(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n const ws = new WebSocket(url);\n this.ws = ws;\n\n this.wsOnOpen = () => {\n if (this.stopped) {\n ws.close();\n return;\n }\n this.currentKind = \"ws\";\n this.handlers.onOpen(\"ws\");\n this.backoff = RETRY_BASE;\n resolve();\n };\n this.wsOnMessage = (event: MessageEvent) => this.handleIncoming(event.data);\n this.wsOnClose = () => {\n if (this.stopped) return;\n this.retryFallback(\"ws\");\n };\n this.wsOnError = () => {\n this.handlers.onError(new Error(\"SpilkiWidget: websocket error\"));\n if (ws.readyState !== WebSocket.OPEN) {\n reject(new Error(\"WebSocket failed\"));\n }\n };\n\n ws.addEventListener(\"open\", this.wsOnOpen);\n ws.addEventListener(\"message\", this.wsOnMessage);\n ws.addEventListener(\"close\", this.wsOnClose);\n ws.addEventListener(\"error\", this.wsOnError);\n } catch (error) {\n reject(error as Error);\n }\n });\n }\n\n // SPLK-271: fetch-based SSE with X-Authorization header (EventSource can't send headers)\n private startSse(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const abort = new AbortController();\n this.sseAbort = abort;\n\n fetch(url, {\n headers: {\n \"Accept\": \"text/event-stream\",\n \"X-Authorization\": `Bearer ${this.options.accessToken}`\n },\n signal: abort.signal\n })\n .then((res) => {\n if (!res.ok || !res.body) {\n reject(new Error(\"SSE failed\"));\n return;\n }\n if (this.stopped) {\n abort.abort();\n return;\n }\n this.currentKind = \"sse\";\n this.handlers.onOpen(\"sse\");\n this.backoff = RETRY_BASE;\n resolve();\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n const read = (): void => {\n reader.read().then(({done, value}) => {\n if (done || this.stopped) {\n if (!this.stopped) {\n this.handlers.onError(new Error(\"SpilkiWidget: SSE stream ended\"));\n this.retryFallback(\"sse\");\n }\n return;\n }\n\n buffer += decoder.decode(value, {stream: true});\n const events = buffer.split(\"\\n\\n\");\n buffer = events.pop() ?? \"\";\n for (const event of events) {\n for (const line of event.split(\"\\n\")) {\n if (line.startsWith(\"data:\")) {\n this.handleIncoming(line.slice(5).trim());\n }\n }\n }\n read();\n }).catch((err: unknown) => {\n if (abort.signal.aborted) return;\n this.handlers.onError(err as Error);\n if (!this.stopped) this.retryFallback(\"sse\");\n });\n };\n read();\n })\n .catch((err: unknown) => {\n if (abort.signal.aborted) return;\n reject(err as Error);\n });\n });\n }\n\n // #15: add auth header to poll fetch\n private async startPoll(url: string): Promise<void> {\n this.currentKind = \"poll\";\n this.handlers.onOpen(\"poll\");\n this.backoff = RETRY_BASE;\n const poll = async () => {\n if (this.stopped) return;\n try {\n const res = await fetch(url, {\n headers: {\n \"X-Authorization\": `Bearer ${this.options.accessToken}`\n }\n });\n if (!res.ok) throw new Error(`Poll failed ${res.status}`);\n const messages = (await res.json()) as Message[];\n clampMessages(messages).forEach((message) => this.handlers.onMessage(message));\n this.backoff = RETRY_BASE;\n } catch (error) {\n this.handlers.onError(error as Error);\n this.backoff = Math.min(this.backoff * 1.5, RETRY_MAX);\n } finally {\n if (!this.stopped) {\n this.pollTimer = window.setTimeout(poll, this.backoff);\n }\n }\n };\n await poll();\n }\n\n // #1: preserve backoff by calling stop(false); #8: check stopped before reconnect\n private retryFallback(failed: TransportKind): void {\n if (this.stopped) return;\n this.stop(false); // #1: don't reset backoff\n this.backoff = Math.min(this.backoff * 1.5, RETRY_MAX);\n this.retryTimer = window.setTimeout(() => { // #8 #9: store timer ID\n if (this.stopped) return; // #8: respect user's stop()\n this.handlers.onError(new Error(`SpilkiWidget: retrying after ${failed}`));\n this.connect().catch((error) => this.handlers.onError(error));\n }, this.backoff);\n }\n\n // #17: don't display raw garbage on parse failure\n private handleIncoming(raw: string): void {\n try {\n const data = JSON.parse(raw) as IncomingPayload;\n if (Array.isArray(data)) {\n data.forEach((item) => this.dispatchIncoming(item));\n return;\n }\n this.dispatchIncoming(data);\n } catch {\n console.error(\"SpilkiWidget: failed to parse incoming message\", raw);\n }\n }\n\n private dispatchIncoming(data: IncomingPayload): void {\n if (\"type\" in data) {\n if (data.type === \"message\") {\n this.handlers.onMessage(data.payload);\n } else if (data.type === \"typing\") {\n this.handlers.onTyping(Boolean(data.payload?.active));\n } else if (data.type === \"AGENT_EVENT\" && isAgentEventPayload(data.payload)) {\n this.handlers.onAgentEvent(data.payload);\n }\n return;\n }\n this.handlers.onMessage(data as Message);\n }\n}\n\nconst VALID_EVENT_TYPES = new Set([\"TYPING\", \"THINKING\", \"SEARCHING\", \"EXECUTING_TOOL\"]);\n\nfunction isAgentEventPayload(p: unknown): p is AgentEventPayload {\n if (typeof p !== \"object\" || p === null) return false;\n const obj = p as Record<string, unknown>;\n return VALID_EVENT_TYPES.has(obj.eventType as string) && typeof obj.active === \"boolean\";\n}\n","import {clampMessages, createId} from \"./utils\";\nimport type {AgentEventType, Message} from \"../types\";\n\nexport const INACTIVITY_TIMEOUT = 30 * 60 * 1000; // 30 minutes\nexport const AGENT_EVENT_TIMEOUT = 30_000; // 30 seconds auto-clear\n\nexport interface ConversationGroup {\n startTs: number;\n messages: Message[];\n}\n\ntype Listener = () => void;\n\nexport interface WidgetStateSnapshot {\n isOpen: boolean;\n agentActivity: AgentEventType | null;\n isConnected: boolean;\n messages: Message[];\n}\n\nconst HISTORY_LIMIT = 30;\n\nexport class WidgetState {\n private listeners = new Set<Listener>();\n private historyKey: string;\n private sessionKey: string;\n private tokenKey: string;\n private activityKey: string;\n private lastReadKey: string;\n private readonly persist: boolean;\n private activityTimer: ReturnType<typeof setTimeout> | null = null;\n private activeEvents = new Set<AgentEventType>();\n private state: WidgetStateSnapshot = {\n isOpen: false,\n agentActivity: null,\n isConnected: false,\n messages: []\n };\n\n constructor(\n private readonly org: string,\n options: { persist: boolean }\n ) {\n this.historyKey = `spilki-history:${org}`;\n this.sessionKey = `spilki-session:${org}`;\n this.tokenKey = `spilki-token:${org}`;\n this.activityKey = `spilki-activity:${org}`;\n this.lastReadKey = `spilki-lastread:${org}`;\n this.persist = options.persist;\n this.state.messages = this.loadMessages();\n }\n\n get snapshot(): WidgetStateSnapshot {\n return {\n isOpen: this.state.isOpen,\n agentActivity: this.state.agentActivity,\n isConnected: this.state.isConnected,\n messages: this.state.messages\n };\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n open(): void {\n if (!this.state.isOpen) {\n this.state.isOpen = true;\n this.emit();\n }\n }\n\n close(): void {\n if (this.state.isOpen) {\n this.state.isOpen = false;\n this.emit();\n }\n }\n\n handleAgentEvent(eventType: AgentEventType, active: boolean): void {\n if (active) {\n this.activeEvents.add(eventType);\n } else {\n this.activeEvents.delete(eventType);\n }\n this.updateDisplayedActivity();\n }\n\n setTyping(value: boolean): void {\n this.handleAgentEvent(\"TYPING\", value);\n }\n\n private updateDisplayedActivity(): void {\n // Always re-arm timeout while any event is active (even if display didn't change)\n if (this.activityTimer) {\n clearTimeout(this.activityTimer);\n this.activityTimer = null;\n }\n\n if (this.activeEvents.size > 0) {\n this.activityTimer = setTimeout(() => {\n this.activeEvents.clear();\n this.state.agentActivity = null;\n this.activityTimer = null;\n this.emit();\n }, AGENT_EVENT_TIMEOUT);\n }\n\n const display = this.resolveTopActivity();\n if (this.state.agentActivity === display) return;\n this.state.agentActivity = display;\n this.emit();\n }\n\n private resolveTopActivity(): AgentEventType | null {\n if (this.activeEvents.size === 0) return null;\n const priority: AgentEventType[] = [\n \"EXECUTING_TOOL\",\n \"SEARCHING\",\n \"THINKING\",\n \"TYPING\",\n ];\n return priority.find((e) => this.activeEvents.has(e)) ?? null;\n }\n\n setConnected(value: boolean): void {\n if (this.state.isConnected !== value) {\n this.state.isConnected = value;\n this.emit();\n }\n }\n\n addMessage(message: Omit<Message, \"id\" | \"ts\"> & Partial<Pick<Message, \"id\" | \"ts\">>): Message | null {\n const full: Message = {\n id: message.id ?? createId(\"msg\"),\n ts: message.ts ?? Date.now(),\n author: message.author,\n text: message.text\n };\n if (this.state.messages.some((m) => m.id === full.id)) {\n return null;\n }\n this.state.messages = clampMessages([...this.state.messages, full], HISTORY_LIMIT);\n this.persistMessages();\n this.touchActivity();\n this.emit();\n return full;\n }\n\n setMessages(messages: Message[]): void {\n this.state.messages = clampMessages(messages, HISTORY_LIMIT);\n this.persistMessages();\n this.emit();\n }\n\n clearMessages(): void {\n this.state.messages = [];\n this.persistMessages();\n this.emit();\n }\n\n get sessionId(): string | null {\n if (!this.persist) return null;\n try {\n return localStorage.getItem(this.sessionKey);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to get item\", error);\n return null;\n }\n }\n\n persistSession(sessionId: string): void {\n if (!this.persist) return;\n try {\n localStorage.setItem(this.sessionKey, sessionId);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to set item\", error);\n }\n }\n\n clearSession(): void {\n if (!this.persist) return;\n try {\n localStorage.removeItem(this.sessionKey);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to remove item\", error);\n }\n }\n\n get accessToken(): string | null {\n if (!this.persist) return null;\n try {\n return localStorage.getItem(this.tokenKey);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to get item\", error);\n return null;\n }\n }\n\n persistAccessToken(token: string): void {\n if (!this.persist) return;\n try {\n localStorage.setItem(this.tokenKey, token);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to set item\", error);\n }\n }\n\n clearAccessToken(): void {\n if (!this.persist) return;\n try {\n localStorage.removeItem(this.tokenKey);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to remove item\", error);\n }\n }\n\n get lastActivityTs(): number {\n if (!this.persist) return 0;\n try {\n const raw = localStorage.getItem(this.activityKey);\n return raw ? Number(raw) : 0;\n } catch {\n return 0;\n }\n }\n\n touchActivity(): void {\n if (!this.persist) return;\n try {\n localStorage.setItem(this.activityKey, String(Date.now()));\n } catch (error) {\n console.error(\"SpilkiWidget: unable to set item\", error);\n }\n }\n\n get lastReadTs(): number {\n if (!this.persist) return 0;\n try {\n const raw = localStorage.getItem(this.lastReadKey);\n return raw ? Number(raw) : 0;\n } catch {\n return 0;\n }\n }\n\n markRead(): void {\n if (!this.persist) return;\n try {\n localStorage.setItem(this.lastReadKey, String(Date.now()));\n } catch { /* empty */ }\n }\n\n countUnread(): number {\n const cutoff = this.lastReadTs;\n if (cutoff === 0) return 0;\n return this.state.messages.filter((m) => m.author === \"bot\" && m.ts > cutoff).length;\n }\n\n isSessionExpired(): boolean {\n const last = this.lastActivityTs;\n if (last === 0) return false;\n return Date.now() - last >= INACTIVITY_TIMEOUT;\n }\n\n getConversationGroups(): ConversationGroup[] {\n const messages = this.state.messages;\n if (messages.length === 0) return [];\n\n const groups: ConversationGroup[] = [];\n let current: ConversationGroup = { startTs: messages[0].ts, messages: [messages[0]] };\n\n for (let i = 1; i < messages.length; i++) {\n if (messages[i].ts - messages[i - 1].ts >= INACTIVITY_TIMEOUT) {\n groups.push(current);\n current = { startTs: messages[i].ts, messages: [messages[i]] };\n } else {\n current.messages.push(messages[i]);\n }\n }\n groups.push(current);\n return groups;\n }\n\n private emit(): void {\n this.listeners.forEach((listener) => listener());\n }\n\n private persistMessages(): void {\n if (!this.persist) return;\n try {\n localStorage.setItem(this.historyKey, JSON.stringify(this.state.messages));\n } catch (error) {\n console.error(\"SpilkiWidget: unable to set item\", error);\n }\n }\n\n private loadMessages(): Message[] {\n if (!this.persist) return [];\n try {\n const raw = localStorage.getItem(this.historyKey);\n if (!raw) return [];\n const parsed = JSON.parse(raw) as Message[];\n return Array.isArray(parsed) ? clampMessages(parsed, HISTORY_LIMIT) : [];\n } catch (error) {\n console.error(\"SpilkiWidget: unable to load messages\", error);\n return [];\n }\n }\n}\n","interface JwtPayload {\n exp?: number;\n\n [key: string]: unknown;\n}\n\nfunction decodePart(part: string): string {\n const normalized = part.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), \"=\");\n if (typeof atob === \"function\") {\n return decodeURIComponent(\n Array.prototype.map\n .call(atob(padded), (c: string) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)\n .join(\"\")\n );\n }\n const buffer = (globalThis as {\n Buffer?: { from(input: string, encoding: string): { toString(enc: string): string } }\n }).Buffer;\n if (buffer) {\n return buffer.from(padded, \"base64\").toString(\"utf8\");\n }\n throw new Error(\"SpilkiWidget: no base64 decoder available\");\n}\n\n/**\n * Decode and parse a JWT token payload.\n * @param token - Raw JWT string.\n * @returns Parsed payload or `null` on failure.\n */\nexport function parseJwt<T extends JwtPayload = JwtPayload>(token: string): T | null {\n if (!token) return null;\n const parts = token.split(\".\");\n if (parts.length < 2) return null;\n try {\n const payload = decodePart(parts[1]);\n return JSON.parse(payload) as T;\n } catch (error) {\n console.error(\"SpilkiWidget: unable to parse JWT\", error);\n return null;\n }\n}\n\n/**\n * Check whether a JWT token is expired or will expire within 60 seconds.\n * Tokens with missing or non-numeric `exp` claims are treated as expired.\n * @param token - Raw JWT string.\n */\n\nexport function isExpired(token: string): boolean {\n const payload = parseJwt(token);\n if (!payload?.exp || typeof payload.exp !== \"number\") return true;\n const now = Math.floor(Date.now() / 1000);\n return payload.exp < now + 60;\n}\n","import {createBubble} from \"./ui/bubble\";\nimport {Panel} from \"./ui/panel\";\nimport {TransportManager} from \"./core/transport\";\nimport {WidgetState} from \"./core/state\";\nimport type {NormalizedOptions} from \"./core/utils\";\nimport {getTheme, mergeOptions, warnAllowedOrigins} from \"./core/utils\";\nimport {isExpired} from \"./core/jwt\";\nimport type {InitOptions, Message, SuggestedReply, TransportKind, WidgetAccessTokenResponse, WidgetHooks, WidgetUser} from \"./types\";\n\nexport interface SpilkiWidgetInstance {\n open(): void;\n\n close(): void;\n\n identify(user: WidgetUser): void;\n\n destroy(): void;\n\n transport?: TransportKind | null;\n}\n\nconst DEFAULT_HOOKS: WidgetHooks = {\n onOpen() {\n },\n onClose() {\n },\n onMessage() {\n },\n onError() {\n },\n onTransportChange() {\n }\n};\n\nasync function installAndGetAccessToken(apiBase: string, installationToken: string, organisationId: string): Promise<string> {\n const url = `${apiBase.replace(/\\/$/, \"\")}/widget/install`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\"Content-Type\": \"application/json\", Origin: window.location.origin},\n body: JSON.stringify({token: installationToken, organisationId: organisationId}),\n });\n if (!res.ok) throw new Error(`SpilkiWidget: install failed (${res.status})`);\n const data = (await res.json()) as WidgetAccessTokenResponse;\n return data.accessToken;\n}\n\nasync function refreshAccessToken(apiBase: string, oldToken: string): Promise<string> {\n const url = `${apiBase.replace(/\\/$/, \"\")}/widget/refresh`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\"X-Authorization\": `Bearer ${oldToken}`, Origin: window.location.origin}\n });\n if (!res.ok) throw new Error(`SpilkiWidget: refresh failed (${res.status})`);\n const data = (await res.json()) as WidgetAccessTokenResponse;\n return data.accessToken;\n}\n\ntype AudioWindow = Window & {\n webkitAudioContext?: typeof AudioContext;\n};\n\nfunction createNotificationPlayer(enabled: boolean): {\n markUserInteraction(): void;\n play(): void;\n destroy(): void;\n} {\n let unlocked = false;\n let audioContext: AudioContext | null = null;\n const listeners: Array<{ type: \"pointerdown\" | \"keydown\"; listener: () => void }> = [];\n\n const getAudioContext = (): AudioContext | null => {\n if (!enabled) return null;\n if (audioContext) return audioContext;\n try {\n const ctor = window.AudioContext ?? (window as AudioWindow).webkitAudioContext;\n if (!ctor) return null;\n audioContext = new ctor();\n } catch {\n return null;\n }\n return audioContext;\n };\n\n const markUserInteraction = (): void => {\n unlocked = true;\n try {\n const context = getAudioContext();\n if (context?.state === \"suspended\") {\n void context.resume().catch(() => undefined);\n }\n } catch { /* empty */ }\n };\n\n const armInteractionUnlock = (type: \"pointerdown\" | \"keydown\"): void => {\n const listener = (): void => {\n markUserInteraction();\n window.removeEventListener(type, listener, true);\n };\n listeners.push({type, listener});\n window.addEventListener(type, listener, {capture: true, passive: true});\n };\n\n armInteractionUnlock(\"pointerdown\");\n armInteractionUnlock(\"keydown\");\n\n return {\n markUserInteraction,\n play() {\n if (!enabled || !unlocked) return;\n const context = getAudioContext();\n if (!context) return;\n if (context.state !== \"running\") return;\n\n const masterGain = context.createGain();\n masterGain.gain.value = 0.15;\n masterGain.connect(context.destination);\n\n const scheduleTone = (frequency: number, startAt: number, duration: number): void => {\n const oscillator = context.createOscillator();\n const gain = context.createGain();\n\n oscillator.type = \"sine\";\n oscillator.frequency.setValueAtTime(frequency, startAt);\n\n gain.gain.setValueAtTime(0.0001, startAt);\n gain.gain.exponentialRampToValueAtTime(1, startAt + 0.012);\n gain.gain.exponentialRampToValueAtTime(0.0001, startAt + duration);\n\n oscillator.connect(gain);\n gain.connect(masterGain);\n\n oscillator.start(startAt);\n oscillator.stop(startAt + duration + 0.02);\n };\n\n const now = context.currentTime;\n scheduleTone(880, now, 0.08);\n scheduleTone(1175, now + 0.09, 0.08);\n },\n destroy() {\n listeners.forEach(({type, listener}) => {\n window.removeEventListener(type, listener, true);\n });\n listeners.length = 0;\n if (audioContext) {\n void audioContext.close().catch(() => undefined);\n }\n audioContext = null;\n }\n };\n}\n\nexport function initSpilkiWidget(rawOptions: InitOptions): SpilkiWidgetInstance {\n if (!rawOptions.org) {\n throw new Error(\"SpilkiWidget: org is required\");\n }\n\n const options: NormalizedOptions = mergeOptions(rawOptions);\n warnAllowedOrigins(options.allowedOriginsHint);\n\n const hooks: WidgetHooks = {...DEFAULT_HOOKS, ...(options.hooks ?? {})};\n const state = new WidgetState(options.org, {persist: options.persist});\n const notificationPlayer = createNotificationPlayer(options.sound);\n\n let accessToken = state.accessToken ?? undefined;\n let refreshPromise: Promise<void> | null = null;\n\n const syncUnreadBadge = (): void => {\n bubble.setBadge(state.countUnread());\n };\n\n const markAllRead = (): void => {\n state.markRead();\n bubble.setBadge(0);\n };\n\n const ensureAccessToken = async () => {\n if (refreshPromise) return refreshPromise;\n refreshPromise = (async () => {\n try {\n if (accessToken && isExpired(accessToken)) {\n try {\n accessToken = await refreshAccessToken(options.apiBase, accessToken);\n state.persistAccessToken(accessToken);\n transport.setAccessToken(accessToken);\n return;\n } catch {\n // Refresh failed (token expired beyond server tolerance) โ€” fall through to re-install\n accessToken = undefined;\n state.clearAccessToken();\n }\n }\n\n if (!accessToken) {\n if (!rawOptions.installationToken) throw new Error(\"SpilkiWidget: missing installationToken\");\n if (!rawOptions.org) throw new Error(\"SpilkiWidget: missing org\");\n accessToken = await installAndGetAccessToken(options.apiBase, rawOptions.installationToken, rawOptions.org);\n state.persistAccessToken(accessToken);\n transport.setAccessToken(accessToken);\n }\n } finally {\n refreshPromise = null;\n }\n })();\n return refreshPromise;\n };\n const bubble = createBubble({\n color: options.color!,\n position: options.position ?? \"bottom-right\",\n onClick: () => {\n const snap = state.snapshot;\n if (snap.isOpen) {\n state.close();\n hooks.onClose();\n } else {\n notificationPlayer.markUserInteraction();\n state.open();\n markAllRead();\n hooks.onOpen();\n }\n }\n });\n\n const panel = new Panel({\n color: options.color!,\n theme: getTheme(options.theme),\n position: options.position ?? \"bottom-right\",\n i18n: options.i18n,\n onClose: () => {\n state.close();\n hooks.onClose();\n },\n onSend: (text) => {\n const message = state.addMessage({author: \"user\", text});\n if (!message) return;\n panel.appendMessage(message);\n ensureAccessToken()\n .then(() => transport.send(text, message.id))\n .catch((error) => {\n hooks.onError(error as Error);\n state.setConnected(false);\n panel.setOffline(true);\n });\n }\n });\n\n const transport = new TransportManager(\n {\n apiBase: options.apiBase,\n accessToken,\n org: options.org,\n sessionId: state.sessionId\n },\n {\n onOpen(kind) {\n instance.transport = kind;\n hooks.onTransportChange(kind);\n state.setConnected(true);\n panel.setOffline(false);\n },\n onMessage(message) {\n const incoming = message as Message & {\n suggestedReplies?: SuggestedReply[];\n };\n const dynamicReplies = incoming.suggestedReplies;\n\n const stored = state.addMessage(message);\n if (!stored) return;\n panel.appendMessage(stored);\n\n if (\n stored.author === 'bot' &&\n dynamicReplies &&\n dynamicReplies.length > 0\n ) {\n panel.setSuggestedReplies(dynamicReplies);\n }\n\n if (!state.snapshot.isOpen && stored.author === 'bot') {\n syncUnreadBadge();\n notificationPlayer.play();\n }\n hooks.onMessage(stored);\n },\n onTyping(active) {\n state.setTyping(active);\n },\n onAgentEvent(event) {\n state.handleAgentEvent(event.eventType, event.active);\n },\n onError(error) {\n hooks.onError(error);\n state.setConnected(false);\n if (state.snapshot.isOpen) {\n panel.setOffline(true);\n }\n }\n }\n );\n\n bubble.mount();\n panel.mount();\n\n const initialMessages = state.snapshot.messages;\n const expired = state.isSessionExpired();\n const groups = state.getConversationGroups();\n\n if (initialMessages.length === 0 && options.welcome) {\n const welcome: Message = {\n id: \"welcome\",\n author: \"bot\",\n text: options.welcome,\n ts: Date.now()\n };\n state.addMessage(welcome);\n panel.appendMessage(welcome);\n } else if (expired && initialMessages.length > 0) {\n // Session expired: all messages are collapsed history, fresh start\n panel.renderWithConversations(groups, []);\n } else if (groups.length > 1) {\n // Active session, multiple conversation groups: latest is current\n panel.renderWithConversations(groups.slice(0, -1), groups[groups.length - 1].messages);\n } else {\n panel.updateMessages(initialMessages);\n }\n\n let wasOpen = false;\n const unsubscribe = state.subscribe(() => {\n const snap = state.snapshot;\n bubble.setOpen(snap.isOpen);\n panel.setAgentActivity(snap.agentActivity, options.i18n);\n panel.setOffline(!snap.isConnected);\n panel.updateTheme(getTheme(options.theme));\n if (snap.isOpen) {\n if (!wasOpen) {\n const hadUnread = state.countUnread() > 0;\n const readCutoff = state.lastReadTs;\n markAllRead();\n if (hadUnread) {\n panel.scrollToFirstUnread(readCutoff);\n }\n }\n wasOpen = true;\n panel.show();\n } else {\n wasOpen = false;\n panel.hide();\n }\n });\n\n const showChipsOnConnect = initialMessages.length === 0 || expired;\n syncUnreadBadge();\n\n ensureAccessToken()\n .then(() =>\n transport.connect().then((response) => {\n if (options.persist) {\n state.persistSession(response.sessionId);\n }\n state.setConnected(true);\n hooks.onTransportChange(transport.kind ?? \"ws\");\n\n const replies = response.suggestedReplies ?? [];\n if (replies.length > 0 && showChipsOnConnect) {\n panel.setSuggestedReplies(replies);\n }\n })\n )\n .catch((error) => {\n hooks.onError(error);\n state.setConnected(false);\n panel.setOffline(true);\n });\n\n const instance: SpilkiWidgetInstance = {\n transport: null,\n open() {\n notificationPlayer.markUserInteraction();\n state.open();\n markAllRead();\n hooks.onOpen();\n },\n close() {\n state.close();\n hooks.onClose();\n },\n identify(user: WidgetUser) {\n transport.setUser(user);\n },\n destroy() {\n unsubscribe();\n transport.stop();\n notificationPlayer.destroy();\n bubble.destroy();\n panel.destroy();\n if (activeInstance === instance) activeInstance = null;\n }\n };\n\n return instance;\n}\n\n/* โœ… RESTORED โ€” UNCHANGED */\nexport function autoInit(): void {\n if (typeof document === \"undefined\") return;\n const script = document.currentScript as HTMLScriptElement | null;\n if (!script) return;\n const dataset = script.dataset;\n if (dataset.autoinit === \"false\") return;\n const org = dataset.org;\n if (!org) {\n console.error(\"SpilkiWidget: data-org and is required for auto init\");\n return;\n }\n activeInstance = initSpilkiWidget({\n org,\n installationToken: dataset.installationToken,\n apiBase: dataset.apiBase,\n position: (dataset.position as InitOptions[\"position\"]) ?? undefined,\n theme: (dataset.theme as InitOptions[\"theme\"]) ?? undefined\n });\n}\n\ndeclare global {\n interface Window {\n SpilkiWidget?: {\n init?: (options: InitOptions) => SpilkiWidgetInstance;\n identify?: (user: WidgetUser) => void;\n };\n }\n}\n\nlet activeInstance: SpilkiWidgetInstance | null = null;\n\nif (typeof window !== \"undefined\") {\n window.SpilkiWidget = window.SpilkiWidget || {};\n window.SpilkiWidget.init = (options: InitOptions) => {\n activeInstance = initSpilkiWidget(options);\n return activeInstance;\n };\n window.SpilkiWidget.identify = (user: WidgetUser) => {\n if (activeInstance) {\n activeInstance.identify(user);\n } else {\n console.warn('SpilkiWidget: call init() before identify()');\n }\n };\n}\n\nautoInit();\n\nexport type {InitOptions, Message, WidgetUser} from \"./types\";\n"],"names":["TEMPLATE","createBubble","options","host","shadow","button","badge","open","count","safeCount","isOpen","DEFAULT_API_BASE","DEFAULT_I18N","DEFAULT_OPTIONS","mergeOptions","mergedI18n","_a","_b","_c","_d","_e","_f","_g","_h","createId","prefix","prefersDark","getTheme","theme","clampMessages","messages","limit","warnAllowedOrigins","hint","origin","MONTH_NAMES","formatConversationTs","ts","d","now","pad","n","time","todayStart","yesterdayStart","msgDayStart","RELATIVE_THRESHOLD_MS","CONSECUTIVE_TIMESTAMP_MS","SCROLL_SHOW_THRESHOLD","EMOJIS","escapeHtml","s","Panel","safeTitle","safeTyping","safeOffline","safePlaceholder","safeSendLabel","styles","event","keyboard","path","message","historyGroups","currentMessages","group","msg","historyContainer","lastTs","separator","wasScrolledUp","replies","reply","chip","eventType","i18n","textMap","active","text","item","bubble","timeEl","el","toggle","expanded","e","ke","container","previous","rendered","current","index","next","hideTimestamp","age","minutes","hours","date","today","messageDay","leftTs","rightTs","lastReadTs","firstUnread","r","scrolledUp","hasUnread","emoji","value","start","end","cursor","items","first","last","RETRY_BASE","RETRY_MAX","TransportManager","handlers","token","user","sanitized","key","trimmed","err","url","res","connectUrl","currentSession","body","data","messageId","payload","resetBackoff","connect","attempts","attempt","error","resolve","reject","ws","abort","reader","decoder","buffer","read","done","events","line","poll","failed","raw","isAgentEventPayload","VALID_EVENT_TYPES","p","obj","INACTIVITY_TIMEOUT","AGENT_EVENT_TIMEOUT","HISTORY_LIMIT","WidgetState","org","listener","display","full","m","sessionId","cutoff","groups","parsed","decodePart","part","normalized","padded","c","parseJwt","parts","isExpired","DEFAULT_HOOKS","installAndGetAccessToken","apiBase","installationToken","organisationId","refreshAccessToken","oldToken","createNotificationPlayer","enabled","unlocked","audioContext","listeners","getAudioContext","ctor","markUserInteraction","context","armInteractionUnlock","type","masterGain","scheduleTone","frequency","startAt","duration","oscillator","gain","initSpilkiWidget","rawOptions","hooks","state","notificationPlayer","accessToken","refreshPromise","syncUnreadBadge","markAllRead","ensureAccessToken","transport","panel","kind","instance","dynamicReplies","stored","initialMessages","expired","welcome","wasOpen","unsubscribe","snap","hadUnread","readCutoff","showChipsOnConnect","response","activeInstance","autoInit","script","dataset"],"mappings":"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"}
1
+ {"version":3,"file":"widget.umd.js","sources":["../src/ui/bubble.ts","../src/core/utils.ts","../src/ui/panel.ts","../src/core/transport.ts","../src/core/state.ts","../src/core/jwt.ts","../src/index.ts"],"sourcesContent":["import type { PositionOption } from \"../types\";\n\n/**\n * Controller for the floating chat bubble element.\n * Manages a Shadow DOM-isolated toggle button.\n */\nexport interface BubbleController {\n mount(): void;\n destroy(): void;\n setOpen(open: boolean): void;\n setBadge(count: number): void;\n element: HTMLDivElement;\n}\n\ninterface BubbleOptions {\n color: string;\n position: PositionOption;\n onClick(): void;\n}\n\nconst TEMPLATE = `\n <style>\n :host {\n all: initial;\n position: fixed;\n z-index: 2147483000;\n bottom: 24px;\n }\n :host([data-position=\"bottom-right\"]) {\n right: 24px;\n }\n :host([data-position=\"bottom-left\"]) {\n left: 24px;\n }\n button {\n all: unset;\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 56px;\n height: 56px;\n border-radius: 999px;\n cursor: pointer;\n background: var(--spilki-accent);\n color: #fff;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);\n transition: transform 0.18s ease, box-shadow 0.18s ease;\n }\n button:focus-visible {\n outline: 2px solid #fff;\n outline-offset: 3px;\n }\n button:hover {\n transform: translateY(-1px);\n box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25);\n }\n .icon {\n width: 28px;\n height: 28px;\n }\n .icon svg {\n width: 100%;\n height: 100%;\n }\n .badge {\n position: absolute;\n top: -4px;\n right: -4px;\n min-width: 20px;\n height: 20px;\n border-radius: 999px;\n background: #ef4444;\n color: #fff;\n font: 600 11px/20px system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n text-align: center;\n padding: 0 6px;\n box-sizing: border-box;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n }\n .badge[hidden] {\n display: none;\n }\n </style>\n <button type=\"button\" aria-label=\"Open chat\">\n <span class=\"icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5 6a3 3 0 0 1 3-3h16a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H14l-5 6v-6H8a3 3 0 0 1-3-3V6Z\" fill=\"currentColor\"/>\n </svg>\n </span>\n <span class=\"badge\" hidden aria-hidden=\"true\">0</span>\n </button>\n`;\n\n/**\n * Creates an isolated chat bubble toggle with Shadow DOM.\n * @param options - Bubble configuration (color, position, click handler).\n * @returns Controller to mount, destroy, and toggle the bubble.\n */\nexport function createBubble(options: BubbleOptions): BubbleController {\n const host = document.createElement(\"div\");\n host.setAttribute(\"part\", \"bubble-root\");\n host.setAttribute(\"data-position\", options.position);\n host.style.setProperty(\"--spilki-accent\", options.color);\n\n const shadow = host.attachShadow({ mode: \"open\" });\n shadow.innerHTML = TEMPLATE;\n const button = shadow.querySelector(\"button\")!;\n const badge = shadow.querySelector(\".badge\") as HTMLSpanElement;\n button.addEventListener(\"click\", () => options.onClick());\n\n const controller: BubbleController = {\n element: host,\n mount() {\n document.body.appendChild(host);\n },\n destroy() {\n host.remove();\n },\n setOpen(open: boolean) {\n button.setAttribute(\"aria-expanded\", String(open));\n button.setAttribute(\"aria-label\", open ? \"Close chat\" : \"Open chat\");\n },\n setBadge(count: number) {\n const safeCount = Math.max(0, count);\n const isOpen = button.getAttribute(\"aria-expanded\") === \"true\";\n if (safeCount === 0) {\n badge.toggleAttribute(\"hidden\", true);\n badge.textContent = \"0\";\n if (!isOpen) button.setAttribute(\"aria-label\", \"Open chat\");\n return;\n }\n badge.toggleAttribute(\"hidden\", false);\n badge.textContent = safeCount > 99 ? \"99+\" : String(safeCount);\n if (!isOpen) {\n button.setAttribute(\"aria-label\", `Open chat, ${safeCount} unread`);\n }\n }\n };\n return controller;\n}\n","import type {InitOptions, WidgetI18n} from \"../types\";\n\nexport const DEFAULT_API_BASE = \"https://api.spilki.app\";\nexport const SESSION_PREFIX = \"spilki-widget\";\n\nconst DEFAULT_I18N: WidgetI18n = {\n welcome: \"Hi! I'm your assistant.\",\n placeholder: \"Type a messageโ€ฆ\",\n sendLabel: \"Send\",\n typing: \"Assistant is typing\",\n thinking: \"Assistant is thinking\",\n searching: \"Searching\",\n executingTool: \"Working on it\",\n offline: \"Unable to connect. Please try again later.\",\n title: \"Spilki Assistant\"\n};\n\nexport const DEFAULT_OPTIONS: Required<\n Pick<InitOptions, \"position\" | \"theme\" | \"color\" | \"welcome\" | \"persist\" | \"sound\">\n> & { apiBase: string; i18n: WidgetI18n } = {\n apiBase: DEFAULT_API_BASE,\n position: \"bottom-right\",\n theme: \"auto\",\n color: \"#6366f1\",\n welcome: DEFAULT_I18N.welcome,\n persist: true,\n sound: true,\n i18n: DEFAULT_I18N\n};\n\nexport type NormalizedOptions = InitOptions & {\n apiBase: string;\n position: InitOptions[\"position\"];\n theme: InitOptions[\"theme\"];\n color: string;\n welcome: string;\n persist: boolean;\n sound: boolean;\n i18n: WidgetI18n;\n};\n\nexport function mergeOptions(options: InitOptions): NormalizedOptions {\n const mergedI18n = {...DEFAULT_I18N, ...(options.i18n ?? {})};\n return {\n ...DEFAULT_OPTIONS,\n ...options,\n apiBase: options.apiBase ?? DEFAULT_OPTIONS.apiBase,\n i18n: mergedI18n,\n welcome: options.welcome ?? mergedI18n.welcome,\n position: options.position ?? DEFAULT_OPTIONS.position,\n theme: options.theme ?? DEFAULT_OPTIONS.theme,\n color: options.color ?? DEFAULT_OPTIONS.color,\n persist: options.persist ?? DEFAULT_OPTIONS.persist,\n sound: options.sound ?? DEFAULT_OPTIONS.sound\n } as NormalizedOptions;\n}\n\nexport function storageKey(org: string): string {\n return `${SESSION_PREFIX}:${org}`;\n}\n\nexport function getPersistedSession(\n org: string,\n): string | null {\n try {\n return localStorage.getItem(storageKey(org));\n } catch (error) {\n console.warn(\"SpilkiWidget: Unable to read localStorage\", error);\n return null;\n }\n}\n\nexport function persistSession(\n org: string,\n sessionId: string\n): void {\n try {\n localStorage.setItem(storageKey(org), sessionId);\n } catch (error) {\n console.warn(\"SpilkiWidget: Unable to persist session\", error);\n }\n}\n\nexport function clearSession(org: string): void {\n try {\n localStorage.removeItem(storageKey(org));\n } catch (error) {\n console.warn(\"SpilkiWidget: Unable to clear session\", error);\n }\n}\n\nexport function createId(prefix = \"msg\"): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return `${prefix}-${Math.random().toString(16).slice(2)}`;\n}\n\nexport function prefersDark(): boolean {\n return window.matchMedia?.(\"(prefers-color-scheme: dark)\").matches ?? false;\n}\n\nexport function getTheme(theme: string | undefined): \"light\" | \"dark\" {\n if (theme === \"light\" || theme === \"dark\") return theme;\n return prefersDark() ? \"dark\" : \"light\";\n}\n\nexport function clampMessages<T>(messages: T[], limit = 30): T[] {\n return messages.slice(-limit);\n}\n\nexport function warnAllowedOrigins(hint: string[] | undefined): void {\n if (!hint || hint.length === 0) return;\n const origin = window.location.origin;\n if (!hint.includes(origin)) {\n console.warn(\n `SpilkiWidget: current origin ${origin} not in allowedOriginsHint: ${hint.join(\", \")}`\n );\n }\n}\n\nconst MONTH_NAMES = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\n\nexport function formatConversationTs(ts: number): string {\n const d = new Date(ts);\n const now = new Date();\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const time = `${pad(d.getHours())}:${pad(d.getMinutes())}`;\n\n const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();\n const yesterdayStart = todayStart - 86400000;\n const msgDayStart = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();\n\n if (msgDayStart === todayStart) return `Today at ${time}`;\n if (msgDayStart === yesterdayStart) return `Yesterday at ${time}`;\n return `${MONTH_NAMES[d.getMonth()]} ${d.getDate()}, ${time}`;\n}\n","import type { AgentEventType, Message, SuggestedReply, WidgetI18n } from \"../types\";\nimport type { ConversationGroup } from \"../core/state\";\nimport { formatConversationTs } from \"../core/utils\";\nimport styles from \"./styles.css?inline\";\n\nconst RELATIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000;\nconst CONSECUTIVE_TIMESTAMP_MS = 2 * 60 * 1000;\nconst SCROLL_SHOW_THRESHOLD = 200;\n\nconst EMOJIS = [\n \"๐Ÿ˜€\", \"๐Ÿ˜‚\", \"๐Ÿคฃ\", \"๐Ÿ˜Š\", \"๐Ÿ˜\", \"๐Ÿฅฐ\", \"๐Ÿ˜˜\", \"๐Ÿ˜‰\", \"๐Ÿค”\", \"๐Ÿ˜\",\n \"๐Ÿ˜Ž\", \"๐Ÿค—\", \"๐Ÿ˜ข\", \"๐Ÿ˜ญ\", \"๐Ÿ˜ก\", \"๐Ÿคฏ\", \"๐Ÿ˜ฑ\", \"๐Ÿฅณ\", \"๐Ÿ˜ด\", \"๐Ÿคฎ\",\n \"๐Ÿ‘\", \"๐Ÿ‘Ž\", \"๐Ÿ‘‹\", \"๐Ÿค\", \"๐Ÿ™\", \"๐Ÿ’ช\", \"โœŒ๏ธ\", \"๐Ÿคž\", \"๐Ÿ‘\", \"๐Ÿซถ\",\n \"โค๏ธ\", \"๐Ÿ”ฅ\", \"โญ\", \"๐Ÿ’ฏ\", \"โœ…\", \"โŒ\", \"โšก\", \"๐ŸŽ‰\", \"๐Ÿ’ฐ\", \"๐Ÿ“ฆ\",\n \"๐Ÿ“…\", \"๐Ÿ“\", \"๐Ÿ“ž\", \"๐Ÿ’ฌ\", \"๐Ÿ””\", \"๐Ÿ”—\", \"๐Ÿ“ธ\", \"๐ŸŽต\", \"โ˜•\", \"๐Ÿ•\",\n] as const;\n\ninterface RenderedMessage {\n message: Message;\n timeEl: HTMLSpanElement;\n}\n\ninterface PanelOptions {\n color: string;\n theme: \"light\" | \"dark\";\n position: \"bottom-right\" | \"bottom-left\";\n i18n: WidgetI18n;\n onClose(): void;\n onSend(text: string): void;\n}\n\n// #7: escape HTML to prevent XSS via i18n values\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/**\n * Chat panel UI component rendered inside a Shadow DOM.\n * Manages message display, input, accessibility, and focus trapping.\n */\nexport class Panel {\n private readonly host: HTMLDivElement;\n private readonly shadow: ShadowRoot;\n private readonly messagesEl: HTMLDivElement;\n private readonly typingEl: HTMLDivElement;\n private readonly input: HTMLTextAreaElement;\n private readonly offlineEl: HTMLDivElement;\n private readonly sendButton: HTMLButtonElement;\n private readonly emojiButton: HTMLButtonElement;\n private readonly emojiPickerEl: HTMLDivElement;\n private readonly scrollBottomButton: HTMLButtonElement;\n private readonly scrollBottomCountEl: HTMLSpanElement;\n private readonly closeButton: HTMLButtonElement;\n private readonly suggestedRepliesEl: HTMLDivElement;\n private readonly relativeTimeFormat = new Intl.RelativeTimeFormat(undefined, {\n numeric: \"auto\",\n });\n private readonly absoluteTimeFormat = new Intl.DateTimeFormat(undefined, {\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false,\n });\n private readonly dateSeparatorFormat = new Intl.DateTimeFormat(undefined, {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n });\n private readonly renderedMessages: RenderedMessage[] = [];\n private readonly focusable: HTMLElement[] = [];\n private readonly seenIds = new Set<string>();\n private lastTimelineMessage: Message | null = null;\n private scrollUnreadCount = 0;\n private emojiPickerOpen = false;\n private open = false;\n\n // #4: stored listener references for cleanup\n private readonly handleCloseClick: () => void;\n private readonly handleSendClick: () => void;\n private readonly handleInputKeydown: (e: KeyboardEvent) => void;\n private readonly handleShadowKeydown: (e: Event) => void;\n private readonly handleFocusin: () => void;\n private readonly handleMessagesScroll: () => void;\n private readonly handleScrollBottomClick: () => void;\n private readonly handleEmojiClick: () => void;\n private readonly handleDocumentPointerDown: (e: PointerEvent) => void;\n\n constructor(private readonly options: PanelOptions) {\n this.host = document.createElement(\"div\");\n this.host.setAttribute(\"part\", \"panel-root\");\n this.host.style.position = \"fixed\";\n this.host.style.bottom = \"96px\";\n this.host.style[options.position === \"bottom-right\" ? \"right\" : \"left\"] = \"24px\";\n this.host.style.width = \"360px\";\n this.host.style.maxWidth = \"calc(100vw - 32px)\";\n this.host.style.height = \"520px\";\n this.host.style.display = \"none\";\n this.host.style.zIndex = \"2147483001\";\n\n this.shadow = this.host.attachShadow({ mode: \"open\" });\n\n // #7: escape all i18n values before HTML interpolation\n const safeTitle = escapeHtml(options.i18n.title);\n const safeTyping = escapeHtml(options.i18n.typing);\n const safeOffline = escapeHtml(options.i18n.offline);\n const safePlaceholder = escapeHtml(options.i18n.placeholder);\n const safeSendLabel = escapeHtml(options.i18n.sendLabel);\n\n this.shadow.innerHTML = `\n <style>${styles}</style>\n <div class=\"wrapper\" role=\"dialog\" aria-modal=\"true\" aria-label=\"${safeTitle}\">\n <header>\n <h1><span class=\"status-dot\" aria-hidden=\"true\"></span>${safeTitle}</h1>\n <button class=\"close\" type=\"button\" aria-label=\"Close\">ร—</button>\n </header>\n <div class=\"messages\" part=\"messages\" role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\"></div>\n <button class=\"scroll-bottom\" type=\"button\" aria-label=\"Scroll to latest messages\">\n <span class=\"scroll-arrow\" aria-hidden=\"true\">โ–ผ</span>\n <span class=\"scroll-count\" hidden>0</span>\n </button>\n <div class=\"typing\" hidden aria-live=\"polite\" aria-atomic=\"true\">${safeTyping}</div>\n <div class=\"offline\" hidden aria-live=\"assertive\" aria-atomic=\"true\">${safeOffline}</div>\n <div class=\"suggested-replies\" role=\"group\" hidden aria-label=\"Suggested replies\"></div>\n <div class=\"emoji-picker\" aria-label=\"Emoji picker\"></div>\n <div class=\"input-area\">\n <div class=\"input-wrap\">\n <textarea rows=\"1\" placeholder=\"${safePlaceholder}\" aria-label=\"${safePlaceholder}\"></textarea>\n <div class=\"input-actions\">\n <button class=\"emoji-btn\" type=\"button\" aria-label=\"Insert emoji\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M8 14s1.5 2 4 2 4-2 4-2\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <circle cx=\"9\" cy=\"10\" r=\"1\" fill=\"currentColor\"/>\n <circle cx=\"15\" cy=\"10\" r=\"1\" fill=\"currentColor\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"send-btn\" aria-label=\"${safeSendLabel}\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" fill=\"currentColor\"/>\n </svg>\n </button>\n </div>\n </div>\n </div>\n </div>\n `;\n\n this.host.dataset.theme = options.theme;\n this.host.style.setProperty(\"--spilki-accent\", options.color);\n\n this.messagesEl = this.shadow.querySelector(\".messages\") as HTMLDivElement;\n this.typingEl = this.shadow.querySelector(\".typing\") as HTMLDivElement;\n this.input = this.shadow.querySelector(\"textarea\") as HTMLTextAreaElement;\n this.offlineEl = this.shadow.querySelector(\".offline\") as HTMLDivElement;\n this.sendButton = this.shadow.querySelector(\".send-btn\") as HTMLButtonElement;\n this.emojiButton = this.shadow.querySelector(\".emoji-btn\") as HTMLButtonElement;\n this.emojiPickerEl = this.shadow.querySelector(\".emoji-picker\") as HTMLDivElement;\n this.scrollBottomButton = this.shadow.querySelector(\".scroll-bottom\") as HTMLButtonElement;\n this.scrollBottomCountEl = this.shadow.querySelector(\".scroll-count\") as HTMLSpanElement;\n this.closeButton = this.shadow.querySelector(\"header .close\") as HTMLButtonElement;\n this.suggestedRepliesEl = this.shadow.querySelector(\".suggested-replies\") as HTMLDivElement;\n\n // #4: bind and store listener references\n this.handleCloseClick = () => this.options.onClose();\n this.handleSendClick = () => this.send();\n this.handleInputKeydown = (event: KeyboardEvent) => {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n this.send();\n } else if (event.key === \"Escape\") {\n if (this.emojiPickerOpen) {\n event.stopPropagation();\n this.closeEmojiPicker();\n return;\n }\n this.options.onClose();\n }\n };\n this.handleShadowKeydown = (event: Event) => {\n const keyboard = event as KeyboardEvent;\n if (keyboard.key === \"Escape\") {\n if (this.emojiPickerOpen) {\n keyboard.preventDefault();\n this.closeEmojiPicker();\n return;\n }\n this.options.onClose();\n }\n if (keyboard.key === \"Tab\") {\n this.trapFocus(keyboard);\n }\n };\n this.handleFocusin = () => this.collectFocusable();\n this.handleMessagesScroll = () => this.updateScrollBottomState();\n this.handleScrollBottomClick = () => {\n this.scrollToBottom();\n this.resetScrollUnreadCount();\n };\n this.handleEmojiClick = () => {\n this.toggleEmojiPicker();\n };\n this.handleDocumentPointerDown = (event: PointerEvent) => {\n if (!this.emojiPickerOpen) return;\n const path = event.composedPath();\n if (path.includes(this.emojiPickerEl) || path.includes(this.emojiButton)) return;\n this.closeEmojiPicker();\n };\n\n this.closeButton.addEventListener(\"click\", this.handleCloseClick);\n this.sendButton.addEventListener(\"click\", this.handleSendClick);\n this.emojiButton.addEventListener(\"click\", this.handleEmojiClick);\n this.scrollBottomButton.addEventListener(\"click\", this.handleScrollBottomClick);\n this.input.addEventListener(\"keydown\", this.handleInputKeydown);\n this.messagesEl.addEventListener(\"scroll\", this.handleMessagesScroll);\n this.shadow.addEventListener(\"keydown\", this.handleShadowKeydown);\n this.shadow.addEventListener(\"focusin\", this.handleFocusin);\n document.addEventListener(\"pointerdown\", this.handleDocumentPointerDown, true);\n\n this.renderEmojiPicker();\n this.updateScrollBottomState();\n this.collectFocusable();\n }\n\n mount(): void {\n document.body.appendChild(this.host);\n }\n\n // #4: remove all event listeners before removing DOM\n destroy(): void {\n this.closeButton.removeEventListener(\"click\", this.handleCloseClick);\n this.sendButton.removeEventListener(\"click\", this.handleSendClick);\n this.emojiButton.removeEventListener(\"click\", this.handleEmojiClick);\n this.scrollBottomButton.removeEventListener(\"click\", this.handleScrollBottomClick);\n this.input.removeEventListener(\"keydown\", this.handleInputKeydown);\n this.messagesEl.removeEventListener(\"scroll\", this.handleMessagesScroll);\n this.shadow.removeEventListener(\"keydown\", this.handleShadowKeydown);\n this.shadow.removeEventListener(\"focusin\", this.handleFocusin);\n document.removeEventListener(\"pointerdown\", this.handleDocumentPointerDown, true);\n this.host.remove();\n }\n\n show(): void {\n if (this.open) return;\n this.open = true;\n this.host.style.display = \"block\";\n this.focusInput();\n }\n\n hide(): void {\n if (!this.open) return;\n this.open = false;\n this.closeEmojiPicker();\n this.host.style.display = \"none\";\n }\n\n focusInput(): void {\n queueMicrotask(() => {\n this.input.focus();\n });\n }\n\n updateTheme(theme: \"light\" | \"dark\"): void {\n this.host.dataset.theme = theme;\n }\n\n // #16: rebuild seen IDs on full re-render\n updateMessages(messages: Message[]): void {\n this.messagesEl.innerHTML = \"\";\n this.seenIds.clear();\n this.renderedMessages.length = 0;\n this.lastTimelineMessage = null;\n messages.forEach((message) => {\n this.seenIds.add(message.id);\n this.appendTimelineMessage(message);\n });\n this.updateTimestampVisibility();\n this.scrollToBottom();\n this.resetScrollUnreadCount();\n this.collectFocusable();\n }\n\n // Render conversation groups with collapsed history separators\n renderWithConversations(historyGroups: ConversationGroup[], currentMessages: Message[]): void {\n this.messagesEl.innerHTML = \"\";\n this.seenIds.clear();\n this.renderedMessages.length = 0;\n this.lastTimelineMessage = null;\n\n for (const group of historyGroups) {\n group.messages.forEach((msg) => this.seenIds.add(msg.id));\n const historyContainer = this.createHistoryContainer(group.messages);\n const lastTs = group.messages[group.messages.length - 1].ts;\n const separator = this.createSeparatorElement(lastTs, historyContainer);\n this.messagesEl.appendChild(historyContainer);\n this.messagesEl.appendChild(separator);\n }\n\n currentMessages.forEach((msg) => {\n this.seenIds.add(msg.id);\n this.appendTimelineMessage(msg);\n });\n\n this.updateTimestampVisibility();\n this.scrollToBottom();\n this.resetScrollUnreadCount();\n this.collectFocusable();\n }\n\n // #16: deduplicate; #19: smart scroll; #21: update focus trap\n appendMessage(message: Message): void {\n if (this.seenIds.has(message.id)) return;\n const wasScrolledUp = this.isScrolledUp();\n\n this.seenIds.add(message.id);\n this.appendTimelineMessage(message);\n this.updateTimestampVisibility();\n\n if (wasScrolledUp) {\n this.scrollUnreadCount += 1;\n this.updateScrollBottomState();\n } else {\n this.scrollToBottom();\n this.resetScrollUnreadCount();\n }\n\n this.collectFocusable();\n }\n\n setSuggestedReplies(replies: SuggestedReply[]): void {\n if (replies.length === 0) {\n this.hideSuggestedReplies();\n return;\n }\n this.suggestedRepliesEl.replaceChildren();\n replies.forEach((reply) => {\n const chip = document.createElement(\"button\");\n chip.className = \"suggested-chip\";\n chip.type = \"button\";\n chip.textContent = reply.text;\n chip.addEventListener(\"click\", () => {\n this.options.onSend(reply.payload ?? reply.text);\n this.hideSuggestedReplies();\n });\n this.suggestedRepliesEl.appendChild(chip);\n });\n this.suggestedRepliesEl.toggleAttribute(\"hidden\", false);\n this.collectFocusable();\n }\n\n hideSuggestedReplies(): void {\n this.suggestedRepliesEl.toggleAttribute(\"hidden\", true);\n this.suggestedRepliesEl.replaceChildren();\n this.collectFocusable();\n this.focusInput();\n }\n\n setAgentActivity(eventType: AgentEventType | null, i18n: WidgetI18n): void {\n if (eventType === null) {\n this.typingEl.toggleAttribute(\"hidden\", true);\n return;\n }\n const textMap: Record<AgentEventType, string> = {\n TYPING: i18n.typing,\n THINKING: i18n.thinking,\n SEARCHING: i18n.searching,\n EXECUTING_TOOL: i18n.executingTool,\n };\n this.typingEl.textContent = textMap[eventType] ?? i18n.typing;\n this.typingEl.toggleAttribute(\"hidden\", false);\n }\n\n setTyping(active: boolean): void {\n this.typingEl.toggleAttribute(\"hidden\", !active);\n }\n\n setOffline(active: boolean): void {\n this.offlineEl.toggleAttribute(\"hidden\", !active);\n }\n\n clearInput(): void {\n this.input.value = \"\";\n this.input.dispatchEvent(new Event(\"input\"));\n }\n\n private send(): void {\n const text = this.input.value.trim();\n if (!text) return;\n this.options.onSend(text);\n this.clearInput();\n this.hideSuggestedReplies();\n }\n\n private createMessageElement(message: Message): { item: HTMLDivElement; timeEl: HTMLSpanElement } {\n const item = document.createElement(\"div\");\n item.className = `message ${message.author}`;\n item.setAttribute(\"data-author\", message.author);\n item.setAttribute(\"role\", \"article\");\n item.setAttribute(\"aria-label\", message.author === \"user\" ? \"You\" : \"Assistant\");\n\n const bubble = document.createElement(\"div\");\n bubble.className = \"bubble\";\n bubble.textContent = message.text;\n\n const timeEl = document.createElement(\"span\");\n timeEl.className = \"msg-time\";\n timeEl.textContent = this.formatMessageTimestamp(message.ts);\n\n item.appendChild(bubble);\n item.appendChild(timeEl);\n return { item, timeEl };\n }\n\n private createSeparatorElement(ts: number, historyContainer: HTMLDivElement): HTMLDivElement {\n const el = document.createElement(\"div\");\n el.className = \"conversation-separator\";\n el.setAttribute(\"role\", \"button\");\n el.setAttribute(\"tabindex\", \"0\");\n el.setAttribute(\"aria-expanded\", \"false\");\n el.setAttribute(\"aria-label\", \"Show previous conversation\");\n el.textContent = formatConversationTs(ts);\n\n const toggle = () => {\n const expanded = historyContainer.classList.toggle(\"expanded\");\n el.setAttribute(\"aria-expanded\", String(expanded));\n el.setAttribute(\"aria-label\",\n expanded ? \"Hide previous conversation\" : \"Show previous conversation\");\n };\n\n el.addEventListener(\"click\", toggle);\n el.addEventListener(\"keydown\", (e: Event) => {\n const ke = e as KeyboardEvent;\n if (ke.key === \"Enter\" || ke.key === \" \") {\n ke.preventDefault();\n toggle();\n }\n });\n\n return el;\n }\n\n private createHistoryContainer(messages: Message[]): HTMLDivElement {\n const container = document.createElement(\"div\");\n container.className = \"conversation-history\";\n let previous: Message | null = null;\n\n messages.forEach((msg) => {\n this.appendDateSeparatorIfNeeded(previous, msg, container);\n const { item } = this.createMessageElement(msg);\n container.appendChild(item);\n previous = msg;\n });\n\n return container;\n }\n\n private appendTimelineMessage(message: Message): void {\n this.appendDateSeparatorIfNeeded(this.lastTimelineMessage, message, this.messagesEl);\n const rendered = this.createMessageElement(message);\n this.messagesEl.appendChild(rendered.item);\n this.renderedMessages.push({ message, timeEl: rendered.timeEl });\n this.lastTimelineMessage = message;\n }\n\n private appendDateSeparatorIfNeeded(\n previous: Message | null,\n current: Message,\n container: HTMLElement,\n ): void {\n if (!previous || this.isSameDay(previous.ts, current.ts)) {\n return;\n }\n\n const separator = document.createElement(\"div\");\n separator.className = \"date-separator\";\n separator.textContent = this.formatDateSeparator(current.ts);\n container.appendChild(separator);\n }\n\n private updateTimestampVisibility(): void {\n for (let index = 0; index < this.renderedMessages.length; index += 1) {\n const current = this.renderedMessages[index];\n const next = this.renderedMessages[index + 1];\n const hideTimestamp =\n Boolean(next) &&\n next.message.author === current.message.author &&\n next.message.ts - current.message.ts <= CONSECUTIVE_TIMESTAMP_MS &&\n next.message.ts >= current.message.ts;\n\n current.timeEl.toggleAttribute(\"hidden\", hideTimestamp);\n current.timeEl.textContent = this.formatMessageTimestamp(current.message.ts);\n }\n }\n\n private formatMessageTimestamp(ts: number): string {\n const now = Date.now();\n const age = now - ts;\n if (age < RELATIVE_THRESHOLD_MS) {\n const minutes = Math.max(1, Math.round(age / (60 * 1000)));\n if (minutes < 60) {\n return this.relativeTimeFormat.format(-minutes, \"minute\");\n }\n const hours = Math.max(1, Math.round(minutes / 60));\n return this.relativeTimeFormat.format(-hours, \"hour\");\n }\n return this.absoluteTimeFormat.format(new Date(ts));\n }\n\n private formatDateSeparator(ts: number): string {\n const date = new Date(ts);\n const today = this.startOfDay(Date.now());\n const messageDay = this.startOfDay(ts);\n if (messageDay === today) {\n return \"Today\";\n }\n if (messageDay === today - RELATIVE_THRESHOLD_MS) {\n return \"Yesterday\";\n }\n return this.dateSeparatorFormat.format(date);\n }\n\n private isSameDay(leftTs: number, rightTs: number): boolean {\n return this.startOfDay(leftTs) === this.startOfDay(rightTs);\n }\n\n private startOfDay(ts: number): number {\n const date = new Date(ts);\n return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();\n }\n\n private isScrolledUp(): boolean {\n return this.distanceFromBottom() > SCROLL_SHOW_THRESHOLD;\n }\n\n private distanceFromBottom(): number {\n return this.messagesEl.scrollHeight - this.messagesEl.scrollTop - this.messagesEl.clientHeight;\n }\n\n scrollToFirstUnread(lastReadTs: number): void {\n if (lastReadTs <= 0) {\n this.scrollToBottom();\n return;\n }\n const firstUnread = this.renderedMessages.find(\n (r) => r.message.author === \"bot\" && r.message.ts > lastReadTs,\n );\n if (!firstUnread) {\n this.scrollToBottom();\n return;\n }\n const container = firstUnread.timeEl.closest(\".message\");\n if (container) {\n container.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n }\n }\n\n private scrollToBottom(): void {\n if (typeof this.messagesEl.scrollTo === \"function\") {\n this.messagesEl.scrollTo({ top: this.messagesEl.scrollHeight, behavior: \"smooth\" });\n return;\n }\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight;\n }\n\n private updateScrollBottomState(): void {\n const scrolledUp = this.isScrolledUp();\n this.scrollBottomButton.classList.toggle(\"visible\", scrolledUp);\n this.scrollBottomButton.setAttribute(\"aria-hidden\", String(!scrolledUp));\n this.scrollBottomButton.tabIndex = scrolledUp ? 0 : -1;\n\n if (!scrolledUp) {\n this.resetScrollUnreadCount();\n }\n\n const hasUnread = this.scrollUnreadCount > 0;\n this.scrollBottomCountEl.toggleAttribute(\"hidden\", !hasUnread);\n this.scrollBottomCountEl.textContent = hasUnread\n ? this.scrollUnreadCount > 99\n ? \"99+\"\n : String(this.scrollUnreadCount)\n : \"0\";\n }\n\n private resetScrollUnreadCount(): void {\n if (this.scrollUnreadCount === 0) return;\n this.scrollUnreadCount = 0;\n this.updateScrollBottomState();\n }\n\n private toggleEmojiPicker(): void {\n if (this.emojiPickerOpen) {\n this.closeEmojiPicker();\n return;\n }\n this.emojiPickerOpen = true;\n this.emojiPickerEl.style.display = \"grid\";\n this.emojiButton.setAttribute(\"aria-expanded\", \"true\");\n this.collectFocusable();\n }\n\n private closeEmojiPicker(): void {\n if (!this.emojiPickerOpen) return;\n this.emojiPickerOpen = false;\n this.emojiPickerEl.style.display = \"none\";\n this.emojiButton.setAttribute(\"aria-expanded\", \"false\");\n this.collectFocusable();\n }\n\n private renderEmojiPicker(): void {\n this.emojiPickerEl.replaceChildren();\n EMOJIS.forEach((emoji) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"emoji-option\";\n button.textContent = emoji;\n button.setAttribute(\"aria-label\", `Insert ${emoji}`);\n button.addEventListener(\"click\", () => {\n this.insertEmojiAtCursor(emoji);\n this.closeEmojiPicker();\n });\n this.emojiPickerEl.appendChild(button);\n });\n }\n\n private insertEmojiAtCursor(emoji: string): void {\n const value = this.input.value;\n const start = this.input.selectionStart ?? value.length;\n const end = this.input.selectionEnd ?? value.length;\n this.input.value = `${value.slice(0, start)}${emoji}${value.slice(end)}`;\n const cursor = start + emoji.length;\n this.input.setSelectionRange(cursor, cursor);\n this.input.dispatchEvent(new Event(\"input\", { bubbles: true }));\n this.input.focus();\n }\n\n private collectFocusable(): void {\n const items = this.shadow.querySelectorAll<HTMLElement>(\n 'button, textarea, [href], [tabindex]:not([tabindex=\"-1\"])'\n );\n this.focusable.length = 0;\n items.forEach((el) => {\n if (!el.hasAttribute(\"disabled\") && !el.hasAttribute(\"hidden\") && el.tabIndex >= 0) {\n this.focusable.push(el);\n }\n });\n }\n\n private trapFocus(event: KeyboardEvent): void {\n if (this.focusable.length === 0) return;\n const first = this.focusable[0];\n const last = this.focusable[this.focusable.length - 1];\n const active = this.shadow.activeElement as HTMLElement;\n if (event.shiftKey && active === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n}\n","import type {AgentEventPayload, ConnectResponse, Message, SendPayload, TransportKind, WidgetUser} from \"../types\";\nimport {clampMessages} from \"./utils\";\n\ninterface TransportHandlers {\n onOpen(kind: TransportKind): void;\n\n onMessage(message: Message): void;\n\n onTyping(typing: boolean): void;\n\n onAgentEvent(event: AgentEventPayload): void;\n\n onError(error: Error): void;\n}\n\ninterface TransportOptions {\n apiBase: string;\n accessToken?: string;\n org: string;\n sessionId?: string | null;\n}\n\ntype IncomingPayload =\n | { type: \"message\"; payload: Message }\n | { type: \"typing\"; payload: { active: boolean } }\n | { type: \"AGENT_EVENT\"; payload: AgentEventPayload }\n | Message;\n\nconst RETRY_BASE = 1500;\nconst RETRY_MAX = 8000;\n\nexport class TransportManager {\n private sessionId: string | null = null;\n private currentKind: TransportKind | null = null;\n private ws?: WebSocket;\n private sseAbort?: AbortController;\n private pollTimer?: number;\n private retryTimer?: number; // #8 #9: track retry timer\n private stopped = false;\n private backoff = RETRY_BASE;\n private connectPromise: Promise<ConnectResponse> | null = null; // #3: connect lock\n private readonly handlers: TransportHandlers;\n private readonly options: TransportOptions;\n\n // #5 #6: stored handler refs for cleanup\n private wsOnOpen?: () => void;\n private wsOnMessage?: (event: MessageEvent) => void;\n private wsOnClose?: () => void;\n private wsOnError?: () => void;\n\n constructor(options: TransportOptions, handlers: TransportHandlers) {\n this.options = options;\n this.handlers = handlers;\n }\n\n private user?: WidgetUser;\n\n setAccessToken(token?: string) {\n this.options.accessToken = token;\n }\n\n setUser(user: WidgetUser) {\n const sanitized: Record<string, string> = {};\n for (const [key, value] of Object.entries(user)) {\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed) sanitized[key] = trimmed;\n }\n }\n this.user = sanitized as unknown as WidgetUser;\n\n if (this.sessionId) {\n this.sendIdentify().catch((err) =>\n this.handlers.onError(err as Error)\n );\n }\n }\n\n private async sendIdentify(): Promise<void> {\n const url = `${this.options.apiBase.replace(/\\/$/, \"\")}/widget/identify`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(this.options.accessToken ? {\"X-Authorization\": `Bearer ${this.options.accessToken}`} : {})\n },\n body: JSON.stringify({\n sessionId: this.sessionId,\n user: this.user\n })\n });\n if (!res.ok) {\n throw new Error(`SpilkiWidget: identify failed (${res.status})`);\n }\n }\n\n get kind(): TransportKind | null {\n return this.currentKind;\n }\n\n get activeSession(): string | null {\n return this.sessionId ?? this.options.sessionId ?? null;\n }\n\n // #3: connect lock โ€” return in-flight promise if already connecting\n async connect(): Promise<ConnectResponse> {\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = this.doConnect();\n try {\n return await this.connectPromise;\n } finally {\n this.connectPromise = null;\n }\n }\n\n private async doConnect(): Promise<ConnectResponse> {\n this.stopped = false;\n const connectUrl = `${this.options.apiBase.replace(/\\/$/, \"\")}/widget/session`;\n const currentSession = this.sessionId ?? this.options.sessionId ?? undefined;\n const body = {\n organisationId: this.options.org,\n sessionId: currentSession,\n userAgent: typeof navigator !== \"undefined\" ? navigator.userAgent : \"\",\n referrer: typeof document !== \"undefined\" ? document.referrer : \"\",\n origin: typeof window !== \"undefined\" ? window.location.origin : \"\",\n ...(this.user ? {user: this.user} : {})\n };\n\n const res = await fetch(connectUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(this.options.accessToken ? {\"X-Authorization\": `Bearer ${this.options.accessToken}`} : {})\n },\n body: JSON.stringify(body)\n });\n\n if (!res.ok) {\n throw new Error(`SpilkiWidget: connect failed (${res.status})`);\n }\n\n const data = (await res.json()) as ConnectResponse;\n this.sessionId = data.sessionId;\n this.options.sessionId = data.sessionId;\n this.backoff = RETRY_BASE;\n await this.startTransport(data);\n return data;\n }\n\n async send(text: string, messageId?: string): Promise<void> {\n const payload: SendPayload = {\n sessionId: this.sessionId ?? this.options.sessionId ?? \"\",\n text,\n ...(messageId ? {messageId} : {}),\n ...(this.user?.userId ? {userId: this.user.userId} : {})\n };\n\n if (!payload.sessionId) {\n throw new Error(\"SpilkiWidget: missing session id\");\n }\n\n if (this.currentKind === \"ws\" && this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({type: \"message\", payload}));\n return;\n }\n\n const url = `${this.options.apiBase.replace(/\\/$/, \"\")}/widget/message`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Authorization\": `Bearer ${this.options.accessToken}`\n },\n body: JSON.stringify(payload)\n });\n\n if (!res.ok) {\n throw new Error(`SpilkiWidget: send failed (${res.status})`);\n }\n }\n\n // #1: accept resetBackoff param; #8 #9: clear retryTimer\n stop(resetBackoff = true): void {\n this.stopped = true;\n if (resetBackoff) {\n this.backoff = RETRY_BASE;\n }\n\n // #8 #9: clear pending retry timeout\n if (this.retryTimer) {\n clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n\n // #5: remove WS listeners before closing\n if (this.ws) {\n if (this.wsOnOpen) this.ws.removeEventListener(\"open\", this.wsOnOpen);\n if (this.wsOnMessage) this.ws.removeEventListener(\"message\", this.wsOnMessage);\n if (this.wsOnClose) this.ws.removeEventListener(\"close\", this.wsOnClose);\n if (this.wsOnError) this.ws.removeEventListener(\"error\", this.wsOnError);\n this.ws.close();\n this.ws = undefined;\n }\n this.wsOnOpen = this.wsOnMessage = this.wsOnClose = this.wsOnError = undefined;\n\n // #6: abort fetch-based SSE stream\n if (this.sseAbort) {\n this.sseAbort.abort();\n this.sseAbort = undefined;\n }\n\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = undefined;\n }\n this.currentKind = null;\n }\n\n private async startTransport(connect: ConnectResponse): Promise<void> {\n if (this.stopped) return;\n const attempts: Array<() => Promise<void>> = [];\n if (connect.wsUrl) attempts.push(() => this.startWs(connect.wsUrl!));\n if (connect.sseUrl) attempts.push(() => this.startSse(connect.sseUrl!));\n if (connect.pollUrl) attempts.push(() => this.startPoll(connect.pollUrl!));\n\n for (const attempt of attempts) {\n try {\n await attempt();\n return;\n } catch (error) {\n this.handlers.onError(error as Error);\n }\n }\n throw new Error(\"SpilkiWidget: unable to establish transport\");\n }\n\n // #5: store named handler refs for WS cleanup\n private startWs(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n const ws = new WebSocket(url);\n this.ws = ws;\n\n this.wsOnOpen = () => {\n if (this.stopped) {\n ws.close();\n return;\n }\n this.currentKind = \"ws\";\n this.handlers.onOpen(\"ws\");\n this.backoff = RETRY_BASE;\n resolve();\n };\n this.wsOnMessage = (event: MessageEvent) => this.handleIncoming(event.data);\n this.wsOnClose = () => {\n if (this.stopped) return;\n this.retryFallback(\"ws\");\n };\n this.wsOnError = () => {\n this.handlers.onError(new Error(\"SpilkiWidget: websocket error\"));\n if (ws.readyState !== WebSocket.OPEN) {\n reject(new Error(\"WebSocket failed\"));\n }\n };\n\n ws.addEventListener(\"open\", this.wsOnOpen);\n ws.addEventListener(\"message\", this.wsOnMessage);\n ws.addEventListener(\"close\", this.wsOnClose);\n ws.addEventListener(\"error\", this.wsOnError);\n } catch (error) {\n reject(error as Error);\n }\n });\n }\n\n // SPLK-271: fetch-based SSE with X-Authorization header (EventSource can't send headers)\n private startSse(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const abort = new AbortController();\n this.sseAbort = abort;\n\n fetch(url, {\n headers: {\n \"Accept\": \"text/event-stream\",\n \"X-Authorization\": `Bearer ${this.options.accessToken}`\n },\n signal: abort.signal\n })\n .then((res) => {\n if (!res.ok || !res.body) {\n reject(new Error(\"SSE failed\"));\n return;\n }\n if (this.stopped) {\n abort.abort();\n return;\n }\n this.currentKind = \"sse\";\n this.handlers.onOpen(\"sse\");\n this.backoff = RETRY_BASE;\n resolve();\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n const read = (): void => {\n reader.read().then(({done, value}) => {\n if (done || this.stopped) {\n if (!this.stopped) {\n this.handlers.onError(new Error(\"SpilkiWidget: SSE stream ended\"));\n this.retryFallback(\"sse\");\n }\n return;\n }\n\n buffer += decoder.decode(value, {stream: true});\n const events = buffer.split(\"\\n\\n\");\n buffer = events.pop() ?? \"\";\n for (const event of events) {\n for (const line of event.split(\"\\n\")) {\n if (line.startsWith(\"data:\")) {\n this.handleIncoming(line.slice(5).trim());\n }\n }\n }\n read();\n }).catch((err: unknown) => {\n if (abort.signal.aborted) return;\n this.handlers.onError(err as Error);\n if (!this.stopped) this.retryFallback(\"sse\");\n });\n };\n read();\n })\n .catch((err: unknown) => {\n if (abort.signal.aborted) return;\n reject(err as Error);\n });\n });\n }\n\n // #15: add auth header to poll fetch\n private async startPoll(url: string): Promise<void> {\n this.currentKind = \"poll\";\n this.handlers.onOpen(\"poll\");\n this.backoff = RETRY_BASE;\n const poll = async () => {\n if (this.stopped) return;\n try {\n const res = await fetch(url, {\n headers: {\n \"X-Authorization\": `Bearer ${this.options.accessToken}`\n }\n });\n if (!res.ok) throw new Error(`Poll failed ${res.status}`);\n const messages = (await res.json()) as Message[];\n clampMessages(messages).forEach((message) => this.handlers.onMessage(message));\n this.backoff = RETRY_BASE;\n } catch (error) {\n this.handlers.onError(error as Error);\n this.backoff = Math.min(this.backoff * 1.5, RETRY_MAX);\n } finally {\n if (!this.stopped) {\n this.pollTimer = window.setTimeout(poll, this.backoff);\n }\n }\n };\n await poll();\n }\n\n // #1: preserve backoff by calling stop(false); #8: check stopped before reconnect\n private retryFallback(failed: TransportKind): void {\n if (this.stopped) return;\n this.stop(false); // #1: don't reset backoff\n this.backoff = Math.min(this.backoff * 1.5, RETRY_MAX);\n this.retryTimer = window.setTimeout(() => { // #8 #9: store timer ID\n if (this.stopped) return; // #8: respect user's stop()\n this.handlers.onError(new Error(`SpilkiWidget: retrying after ${failed}`));\n this.connect().catch((error) => this.handlers.onError(error));\n }, this.backoff);\n }\n\n // #17: don't display raw garbage on parse failure\n private handleIncoming(raw: string): void {\n try {\n const data = JSON.parse(raw) as IncomingPayload;\n if (Array.isArray(data)) {\n data.forEach((item) => this.dispatchIncoming(item));\n return;\n }\n this.dispatchIncoming(data);\n } catch {\n console.error(\"SpilkiWidget: failed to parse incoming message\", raw);\n }\n }\n\n private dispatchIncoming(data: IncomingPayload): void {\n if (\"type\" in data) {\n if (data.type === \"message\") {\n this.handlers.onMessage(data.payload);\n } else if (data.type === \"typing\") {\n this.handlers.onTyping(Boolean(data.payload?.active));\n } else if (data.type === \"AGENT_EVENT\" && isAgentEventPayload(data.payload)) {\n this.handlers.onAgentEvent(data.payload);\n }\n return;\n }\n this.handlers.onMessage(data as Message);\n }\n}\n\nconst VALID_EVENT_TYPES = new Set([\"TYPING\", \"THINKING\", \"SEARCHING\", \"EXECUTING_TOOL\"]);\n\nfunction isAgentEventPayload(p: unknown): p is AgentEventPayload {\n if (typeof p !== \"object\" || p === null) return false;\n const obj = p as Record<string, unknown>;\n return VALID_EVENT_TYPES.has(obj.eventType as string);\n}\n","import {clampMessages, createId} from \"./utils\";\nimport type {AgentEventType, Message} from \"../types\";\n\nexport const INACTIVITY_TIMEOUT = 30 * 60 * 1000; // 30 minutes\nexport const AGENT_EVENT_TIMEOUT = 30_000; // 30 seconds auto-clear\n\nexport interface ConversationGroup {\n startTs: number;\n messages: Message[];\n}\n\ntype Listener = () => void;\n\nexport interface WidgetStateSnapshot {\n isOpen: boolean;\n agentActivity: AgentEventType | null;\n isConnected: boolean;\n messages: Message[];\n}\n\nconst HISTORY_LIMIT = 30;\n\nexport class WidgetState {\n private listeners = new Set<Listener>();\n private historyKey: string;\n private sessionKey: string;\n private tokenKey: string;\n private activityKey: string;\n private lastReadKey: string;\n private readonly persist: boolean;\n private activityTimer: ReturnType<typeof setTimeout> | null = null;\n private state: WidgetStateSnapshot = {\n isOpen: false,\n agentActivity: null,\n isConnected: false,\n messages: []\n };\n\n constructor(\n private readonly org: string,\n options: { persist: boolean }\n ) {\n this.historyKey = `spilki-history:${org}`;\n this.sessionKey = `spilki-session:${org}`;\n this.tokenKey = `spilki-token:${org}`;\n this.activityKey = `spilki-activity:${org}`;\n this.lastReadKey = `spilki-lastread:${org}`;\n this.persist = options.persist;\n this.state.messages = this.loadMessages();\n }\n\n get snapshot(): WidgetStateSnapshot {\n return {\n isOpen: this.state.isOpen,\n agentActivity: this.state.agentActivity,\n isConnected: this.state.isConnected,\n messages: this.state.messages\n };\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n open(): void {\n if (!this.state.isOpen) {\n this.state.isOpen = true;\n this.emit();\n }\n }\n\n close(): void {\n if (this.state.isOpen) {\n this.state.isOpen = false;\n this.emit();\n }\n }\n\n handleAgentEvent(eventType: AgentEventType): void {\n if (this.activityTimer) {\n clearTimeout(this.activityTimer);\n this.activityTimer = null;\n }\n this.activityTimer = setTimeout(() => {\n this.state.agentActivity = null;\n this.activityTimer = null;\n this.emit();\n }, AGENT_EVENT_TIMEOUT);\n\n if (this.state.agentActivity === eventType) return;\n this.state.agentActivity = eventType;\n this.emit();\n }\n\n clearAgentActivity(): void {\n if (!this.resetAgentActivity()) return;\n this.emit();\n }\n\n private resetAgentActivity(): boolean {\n if (this.activityTimer) {\n clearTimeout(this.activityTimer);\n this.activityTimer = null;\n }\n if (this.state.agentActivity === null) return false;\n this.state.agentActivity = null;\n return true;\n }\n\n setConnected(value: boolean): void {\n if (this.state.isConnected !== value) {\n this.state.isConnected = value;\n this.emit();\n }\n }\n\n addMessage(message: Omit<Message, \"id\" | \"ts\"> & Partial<Pick<Message, \"id\" | \"ts\">>): Message | null {\n const full: Message = {\n id: message.id ?? createId(\"msg\"),\n ts: message.ts ?? Date.now(),\n author: message.author,\n text: message.text\n };\n if (this.state.messages.some((m) => m.id === full.id)) {\n return null;\n }\n this.state.messages = clampMessages([...this.state.messages, full], HISTORY_LIMIT);\n this.resetAgentActivity();\n this.persistMessages();\n this.touchActivity();\n this.emit();\n return full;\n }\n\n setMessages(messages: Message[]): void {\n this.state.messages = clampMessages(messages, HISTORY_LIMIT);\n this.persistMessages();\n this.emit();\n }\n\n clearMessages(): void {\n this.state.messages = [];\n this.persistMessages();\n this.emit();\n }\n\n get sessionId(): string | null {\n if (!this.persist) return null;\n try {\n return localStorage.getItem(this.sessionKey);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to get item\", error);\n return null;\n }\n }\n\n persistSession(sessionId: string): void {\n if (!this.persist) return;\n try {\n localStorage.setItem(this.sessionKey, sessionId);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to set item\", error);\n }\n }\n\n clearSession(): void {\n if (!this.persist) return;\n try {\n localStorage.removeItem(this.sessionKey);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to remove item\", error);\n }\n }\n\n get accessToken(): string | null {\n if (!this.persist) return null;\n try {\n return localStorage.getItem(this.tokenKey);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to get item\", error);\n return null;\n }\n }\n\n persistAccessToken(token: string): void {\n if (!this.persist) return;\n try {\n localStorage.setItem(this.tokenKey, token);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to set item\", error);\n }\n }\n\n clearAccessToken(): void {\n if (!this.persist) return;\n try {\n localStorage.removeItem(this.tokenKey);\n } catch (error) {\n console.error(\"SpilkiWidget: unable to remove item\", error);\n }\n }\n\n get lastActivityTs(): number {\n if (!this.persist) return 0;\n try {\n const raw = localStorage.getItem(this.activityKey);\n return raw ? Number(raw) : 0;\n } catch {\n return 0;\n }\n }\n\n touchActivity(): void {\n if (!this.persist) return;\n try {\n localStorage.setItem(this.activityKey, String(Date.now()));\n } catch (error) {\n console.error(\"SpilkiWidget: unable to set item\", error);\n }\n }\n\n get lastReadTs(): number {\n if (!this.persist) return 0;\n try {\n const raw = localStorage.getItem(this.lastReadKey);\n return raw ? Number(raw) : 0;\n } catch {\n return 0;\n }\n }\n\n markRead(): void {\n if (!this.persist) return;\n try {\n localStorage.setItem(this.lastReadKey, String(Date.now()));\n } catch { /* empty */ }\n }\n\n countUnread(): number {\n const cutoff = this.lastReadTs;\n if (cutoff === 0) return 0;\n return this.state.messages.filter((m) => m.author === \"bot\" && m.ts > cutoff).length;\n }\n\n isSessionExpired(): boolean {\n const last = this.lastActivityTs;\n if (last === 0) return false;\n return Date.now() - last >= INACTIVITY_TIMEOUT;\n }\n\n getConversationGroups(): ConversationGroup[] {\n const messages = this.state.messages;\n if (messages.length === 0) return [];\n\n const groups: ConversationGroup[] = [];\n let current: ConversationGroup = { startTs: messages[0].ts, messages: [messages[0]] };\n\n for (let i = 1; i < messages.length; i++) {\n if (messages[i].ts - messages[i - 1].ts >= INACTIVITY_TIMEOUT) {\n groups.push(current);\n current = { startTs: messages[i].ts, messages: [messages[i]] };\n } else {\n current.messages.push(messages[i]);\n }\n }\n groups.push(current);\n return groups;\n }\n\n private emit(): void {\n this.listeners.forEach((listener) => listener());\n }\n\n private persistMessages(): void {\n if (!this.persist) return;\n try {\n localStorage.setItem(this.historyKey, JSON.stringify(this.state.messages));\n } catch (error) {\n console.error(\"SpilkiWidget: unable to set item\", error);\n }\n }\n\n private loadMessages(): Message[] {\n if (!this.persist) return [];\n try {\n const raw = localStorage.getItem(this.historyKey);\n if (!raw) return [];\n const parsed = JSON.parse(raw) as Message[];\n return Array.isArray(parsed) ? clampMessages(parsed, HISTORY_LIMIT) : [];\n } catch (error) {\n console.error(\"SpilkiWidget: unable to load messages\", error);\n return [];\n }\n }\n}\n","interface JwtPayload {\n exp?: number;\n\n [key: string]: unknown;\n}\n\nfunction decodePart(part: string): string {\n const normalized = part.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), \"=\");\n if (typeof atob === \"function\") {\n return decodeURIComponent(\n Array.prototype.map\n .call(atob(padded), (c: string) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)\n .join(\"\")\n );\n }\n const buffer = (globalThis as {\n Buffer?: { from(input: string, encoding: string): { toString(enc: string): string } }\n }).Buffer;\n if (buffer) {\n return buffer.from(padded, \"base64\").toString(\"utf8\");\n }\n throw new Error(\"SpilkiWidget: no base64 decoder available\");\n}\n\n/**\n * Decode and parse a JWT token payload.\n * @param token - Raw JWT string.\n * @returns Parsed payload or `null` on failure.\n */\nexport function parseJwt<T extends JwtPayload = JwtPayload>(token: string): T | null {\n if (!token) return null;\n const parts = token.split(\".\");\n if (parts.length < 2) return null;\n try {\n const payload = decodePart(parts[1]);\n return JSON.parse(payload) as T;\n } catch (error) {\n console.error(\"SpilkiWidget: unable to parse JWT\", error);\n return null;\n }\n}\n\n/**\n * Check whether a JWT token is expired or will expire within 60 seconds.\n * Tokens with missing or non-numeric `exp` claims are treated as expired.\n * @param token - Raw JWT string.\n */\n\nexport function isExpired(token: string): boolean {\n const payload = parseJwt(token);\n if (!payload?.exp || typeof payload.exp !== \"number\") return true;\n const now = Math.floor(Date.now() / 1000);\n return payload.exp < now + 60;\n}\n","import {createBubble} from \"./ui/bubble\";\nimport {Panel} from \"./ui/panel\";\nimport {TransportManager} from \"./core/transport\";\nimport {WidgetState} from \"./core/state\";\nimport type {NormalizedOptions} from \"./core/utils\";\nimport {getTheme, mergeOptions, warnAllowedOrigins} from \"./core/utils\";\nimport {isExpired} from \"./core/jwt\";\nimport type {InitOptions, Message, SuggestedReply, TransportKind, WidgetAccessTokenResponse, WidgetHooks, WidgetUser} from \"./types\";\n\nexport interface SpilkiWidgetInstance {\n open(): void;\n\n close(): void;\n\n identify(user: WidgetUser): void;\n\n destroy(): void;\n\n transport?: TransportKind | null;\n}\n\nconst DEFAULT_HOOKS: WidgetHooks = {\n onOpen() {\n },\n onClose() {\n },\n onMessage() {\n },\n onError() {\n },\n onTransportChange() {\n }\n};\n\nasync function installAndGetAccessToken(apiBase: string, installationToken: string, organisationId: string): Promise<string> {\n const url = `${apiBase.replace(/\\/$/, \"\")}/widget/install`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\"Content-Type\": \"application/json\", Origin: window.location.origin},\n body: JSON.stringify({token: installationToken, organisationId: organisationId}),\n });\n if (!res.ok) throw new Error(`SpilkiWidget: install failed (${res.status})`);\n const data = (await res.json()) as WidgetAccessTokenResponse;\n return data.accessToken;\n}\n\nasync function refreshAccessToken(apiBase: string, oldToken: string): Promise<string> {\n const url = `${apiBase.replace(/\\/$/, \"\")}/widget/refresh`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\"X-Authorization\": `Bearer ${oldToken}`, Origin: window.location.origin}\n });\n if (!res.ok) throw new Error(`SpilkiWidget: refresh failed (${res.status})`);\n const data = (await res.json()) as WidgetAccessTokenResponse;\n return data.accessToken;\n}\n\ntype AudioWindow = Window & {\n webkitAudioContext?: typeof AudioContext;\n};\n\nfunction createNotificationPlayer(enabled: boolean): {\n markUserInteraction(): void;\n play(): void;\n destroy(): void;\n} {\n let unlocked = false;\n let audioContext: AudioContext | null = null;\n const listeners: Array<{ type: \"pointerdown\" | \"keydown\"; listener: () => void }> = [];\n\n const getAudioContext = (): AudioContext | null => {\n if (!enabled) return null;\n if (audioContext) return audioContext;\n try {\n const ctor = window.AudioContext ?? (window as AudioWindow).webkitAudioContext;\n if (!ctor) return null;\n audioContext = new ctor();\n } catch {\n return null;\n }\n return audioContext;\n };\n\n const markUserInteraction = (): void => {\n unlocked = true;\n try {\n const context = getAudioContext();\n if (context?.state === \"suspended\") {\n void context.resume().catch(() => undefined);\n }\n } catch { /* empty */ }\n };\n\n const armInteractionUnlock = (type: \"pointerdown\" | \"keydown\"): void => {\n const listener = (): void => {\n markUserInteraction();\n window.removeEventListener(type, listener, true);\n };\n listeners.push({type, listener});\n window.addEventListener(type, listener, {capture: true, passive: true});\n };\n\n armInteractionUnlock(\"pointerdown\");\n armInteractionUnlock(\"keydown\");\n\n return {\n markUserInteraction,\n play() {\n if (!enabled || !unlocked) return;\n const context = getAudioContext();\n if (!context) return;\n if (context.state !== \"running\") return;\n\n const masterGain = context.createGain();\n masterGain.gain.value = 0.15;\n masterGain.connect(context.destination);\n\n const scheduleTone = (frequency: number, startAt: number, duration: number): void => {\n const oscillator = context.createOscillator();\n const gain = context.createGain();\n\n oscillator.type = \"sine\";\n oscillator.frequency.setValueAtTime(frequency, startAt);\n\n gain.gain.setValueAtTime(0.0001, startAt);\n gain.gain.exponentialRampToValueAtTime(1, startAt + 0.012);\n gain.gain.exponentialRampToValueAtTime(0.0001, startAt + duration);\n\n oscillator.connect(gain);\n gain.connect(masterGain);\n\n oscillator.start(startAt);\n oscillator.stop(startAt + duration + 0.02);\n };\n\n const now = context.currentTime;\n scheduleTone(880, now, 0.08);\n scheduleTone(1175, now + 0.09, 0.08);\n },\n destroy() {\n listeners.forEach(({type, listener}) => {\n window.removeEventListener(type, listener, true);\n });\n listeners.length = 0;\n if (audioContext) {\n void audioContext.close().catch(() => undefined);\n }\n audioContext = null;\n }\n };\n}\n\nexport function initSpilkiWidget(rawOptions: InitOptions): SpilkiWidgetInstance {\n if (!rawOptions.org) {\n throw new Error(\"SpilkiWidget: org is required\");\n }\n\n const options: NormalizedOptions = mergeOptions(rawOptions);\n warnAllowedOrigins(options.allowedOriginsHint);\n\n const hooks: WidgetHooks = {...DEFAULT_HOOKS, ...(options.hooks ?? {})};\n const state = new WidgetState(options.org, {persist: options.persist});\n const notificationPlayer = createNotificationPlayer(options.sound);\n\n let accessToken = state.accessToken ?? undefined;\n let refreshPromise: Promise<void> | null = null;\n\n const syncUnreadBadge = (): void => {\n bubble.setBadge(state.countUnread());\n };\n\n const markAllRead = (): void => {\n state.markRead();\n bubble.setBadge(0);\n };\n\n const ensureAccessToken = async () => {\n if (refreshPromise) return refreshPromise;\n refreshPromise = (async () => {\n try {\n if (accessToken && isExpired(accessToken)) {\n try {\n accessToken = await refreshAccessToken(options.apiBase, accessToken);\n state.persistAccessToken(accessToken);\n transport.setAccessToken(accessToken);\n return;\n } catch {\n // Refresh failed (token expired beyond server tolerance) โ€” fall through to re-install\n accessToken = undefined;\n state.clearAccessToken();\n }\n }\n\n if (!accessToken) {\n if (!rawOptions.installationToken) throw new Error(\"SpilkiWidget: missing installationToken\");\n if (!rawOptions.org) throw new Error(\"SpilkiWidget: missing org\");\n accessToken = await installAndGetAccessToken(options.apiBase, rawOptions.installationToken, rawOptions.org);\n state.persistAccessToken(accessToken);\n transport.setAccessToken(accessToken);\n }\n } finally {\n refreshPromise = null;\n }\n })();\n return refreshPromise;\n };\n const bubble = createBubble({\n color: options.color!,\n position: options.position ?? \"bottom-right\",\n onClick: () => {\n const snap = state.snapshot;\n if (snap.isOpen) {\n state.close();\n hooks.onClose();\n } else {\n notificationPlayer.markUserInteraction();\n state.open();\n markAllRead();\n hooks.onOpen();\n }\n }\n });\n\n const panel = new Panel({\n color: options.color!,\n theme: getTheme(options.theme),\n position: options.position ?? \"bottom-right\",\n i18n: options.i18n,\n onClose: () => {\n state.close();\n hooks.onClose();\n },\n onSend: (text) => {\n const message = state.addMessage({author: \"user\", text});\n if (!message) return;\n panel.appendMessage(message);\n ensureAccessToken()\n .then(() => transport.send(text, message.id))\n .catch((error) => {\n hooks.onError(error as Error);\n state.setConnected(false);\n panel.setOffline(true);\n });\n }\n });\n\n const transport = new TransportManager(\n {\n apiBase: options.apiBase,\n accessToken,\n org: options.org,\n sessionId: state.sessionId\n },\n {\n onOpen(kind) {\n instance.transport = kind;\n hooks.onTransportChange(kind);\n state.setConnected(true);\n panel.setOffline(false);\n },\n onMessage(message) {\n const incoming = message as Message & {\n suggestedReplies?: SuggestedReply[];\n };\n const dynamicReplies = incoming.suggestedReplies;\n\n const stored = state.addMessage(message);\n if (!stored) return;\n panel.appendMessage(stored);\n\n if (\n stored.author === 'bot' &&\n dynamicReplies &&\n dynamicReplies.length > 0\n ) {\n panel.setSuggestedReplies(dynamicReplies);\n }\n\n if (!state.snapshot.isOpen && stored.author === 'bot') {\n syncUnreadBadge();\n notificationPlayer.play();\n }\n hooks.onMessage(stored);\n },\n onTyping() {\n state.handleAgentEvent(\"TYPING\");\n },\n onAgentEvent(event) {\n state.handleAgentEvent(event.eventType);\n },\n onError(error) {\n hooks.onError(error);\n state.setConnected(false);\n if (state.snapshot.isOpen) {\n panel.setOffline(true);\n }\n }\n }\n );\n\n bubble.mount();\n panel.mount();\n\n const initialMessages = state.snapshot.messages;\n const expired = state.isSessionExpired();\n const groups = state.getConversationGroups();\n\n if (initialMessages.length === 0 && options.welcome) {\n const welcome: Message = {\n id: \"welcome\",\n author: \"bot\",\n text: options.welcome,\n ts: Date.now()\n };\n state.addMessage(welcome);\n panel.appendMessage(welcome);\n } else if (expired && initialMessages.length > 0) {\n // Session expired: all messages are collapsed history, fresh start\n panel.renderWithConversations(groups, []);\n } else if (groups.length > 1) {\n // Active session, multiple conversation groups: latest is current\n panel.renderWithConversations(groups.slice(0, -1), groups[groups.length - 1].messages);\n } else {\n panel.updateMessages(initialMessages);\n }\n\n let wasOpen = false;\n const unsubscribe = state.subscribe(() => {\n const snap = state.snapshot;\n bubble.setOpen(snap.isOpen);\n panel.setAgentActivity(snap.agentActivity, options.i18n);\n panel.setOffline(!snap.isConnected);\n panel.updateTheme(getTheme(options.theme));\n if (snap.isOpen) {\n if (!wasOpen) {\n const hadUnread = state.countUnread() > 0;\n const readCutoff = state.lastReadTs;\n markAllRead();\n if (hadUnread) {\n panel.scrollToFirstUnread(readCutoff);\n }\n }\n wasOpen = true;\n panel.show();\n } else {\n wasOpen = false;\n panel.hide();\n }\n });\n\n const showChipsOnConnect = initialMessages.length === 0 || expired;\n syncUnreadBadge();\n\n ensureAccessToken()\n .then(() =>\n transport.connect().then((response) => {\n if (options.persist) {\n state.persistSession(response.sessionId);\n }\n state.setConnected(true);\n hooks.onTransportChange(transport.kind ?? \"ws\");\n\n const replies = response.suggestedReplies ?? [];\n if (replies.length > 0 && showChipsOnConnect) {\n panel.setSuggestedReplies(replies);\n }\n })\n )\n .catch((error) => {\n hooks.onError(error);\n state.setConnected(false);\n panel.setOffline(true);\n });\n\n const instance: SpilkiWidgetInstance = {\n transport: null,\n open() {\n notificationPlayer.markUserInteraction();\n state.open();\n markAllRead();\n hooks.onOpen();\n },\n close() {\n state.close();\n hooks.onClose();\n },\n identify(user: WidgetUser) {\n transport.setUser(user);\n },\n destroy() {\n unsubscribe();\n transport.stop();\n notificationPlayer.destroy();\n bubble.destroy();\n panel.destroy();\n if (activeInstance === instance) activeInstance = null;\n }\n };\n\n return instance;\n}\n\n/* โœ… RESTORED โ€” UNCHANGED */\nexport function autoInit(): void {\n if (typeof document === \"undefined\") return;\n const script = document.currentScript as HTMLScriptElement | null;\n if (!script) return;\n const dataset = script.dataset;\n if (dataset.autoinit === \"false\") return;\n const org = dataset.org;\n if (!org) {\n console.error(\"SpilkiWidget: data-org and is required for auto init\");\n return;\n }\n activeInstance = initSpilkiWidget({\n org,\n installationToken: dataset.installationToken,\n apiBase: dataset.apiBase,\n position: (dataset.position as InitOptions[\"position\"]) ?? undefined,\n theme: (dataset.theme as InitOptions[\"theme\"]) ?? undefined\n });\n}\n\ndeclare global {\n interface Window {\n SpilkiWidget?: {\n init?: (options: InitOptions) => SpilkiWidgetInstance;\n identify?: (user: WidgetUser) => void;\n };\n }\n}\n\nlet activeInstance: SpilkiWidgetInstance | null = null;\n\nif (typeof window !== \"undefined\") {\n window.SpilkiWidget = window.SpilkiWidget || {};\n window.SpilkiWidget.init = (options: InitOptions) => {\n activeInstance = initSpilkiWidget(options);\n return activeInstance;\n };\n window.SpilkiWidget.identify = (user: WidgetUser) => {\n if (activeInstance) {\n activeInstance.identify(user);\n } else {\n console.warn('SpilkiWidget: call init() before identify()');\n }\n };\n}\n\nautoInit();\n\nexport type {InitOptions, Message, WidgetUser} from \"./types\";\n"],"names":["TEMPLATE","createBubble","options","host","shadow","button","badge","open","count","safeCount","isOpen","DEFAULT_API_BASE","DEFAULT_I18N","DEFAULT_OPTIONS","mergeOptions","mergedI18n","_a","_b","_c","_d","_e","_f","_g","_h","createId","prefix","prefersDark","getTheme","theme","clampMessages","messages","limit","warnAllowedOrigins","hint","origin","MONTH_NAMES","formatConversationTs","ts","d","now","pad","n","time","todayStart","yesterdayStart","msgDayStart","RELATIVE_THRESHOLD_MS","CONSECUTIVE_TIMESTAMP_MS","SCROLL_SHOW_THRESHOLD","EMOJIS","escapeHtml","s","Panel","safeTitle","safeTyping","safeOffline","safePlaceholder","safeSendLabel","styles","event","keyboard","path","message","historyGroups","currentMessages","group","msg","historyContainer","lastTs","separator","wasScrolledUp","replies","reply","chip","eventType","i18n","textMap","active","text","item","bubble","timeEl","el","toggle","expanded","e","ke","container","previous","rendered","current","index","next","hideTimestamp","age","minutes","hours","date","today","messageDay","leftTs","rightTs","lastReadTs","firstUnread","r","scrolledUp","hasUnread","emoji","value","start","end","cursor","items","first","last","RETRY_BASE","RETRY_MAX","TransportManager","handlers","token","user","sanitized","key","trimmed","err","url","res","connectUrl","currentSession","body","data","messageId","payload","resetBackoff","connect","attempts","attempt","error","resolve","reject","ws","abort","reader","decoder","buffer","read","done","events","line","poll","failed","raw","isAgentEventPayload","VALID_EVENT_TYPES","p","obj","INACTIVITY_TIMEOUT","AGENT_EVENT_TIMEOUT","HISTORY_LIMIT","WidgetState","org","listener","full","m","sessionId","cutoff","groups","parsed","decodePart","part","normalized","padded","c","parseJwt","parts","isExpired","DEFAULT_HOOKS","installAndGetAccessToken","apiBase","installationToken","organisationId","refreshAccessToken","oldToken","createNotificationPlayer","enabled","unlocked","audioContext","listeners","getAudioContext","ctor","markUserInteraction","context","armInteractionUnlock","type","masterGain","scheduleTone","frequency","startAt","duration","oscillator","gain","initSpilkiWidget","rawOptions","hooks","state","notificationPlayer","accessToken","refreshPromise","syncUnreadBadge","markAllRead","ensureAccessToken","transport","panel","kind","instance","dynamicReplies","stored","initialMessages","expired","welcome","wasOpen","unsubscribe","snap","hadUnread","readCutoff","showChipsOnConnect","response","activeInstance","autoInit","script","dataset"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spilki/widget",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "description": "Embeddable Spilki assistant widget",
5
5
  "type": "module",
6
6
  "main": "dist/widget.umd.js",