@spilki/widget 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -25
- package/dist/bootstrap.es.js +68 -67
- package/dist/bootstrap.es.js.map +1 -1
- package/dist/bootstrap.umd.js +3 -3
- package/dist/bootstrap.umd.js.map +1 -1
- package/dist/core/state.d.ts +1 -2
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/transport.d.ts +1 -1
- package/dist/core/transport.d.ts.map +1 -1
- package/dist/core/utils.d.ts +4 -4
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/widget.es.js +72 -71
- package/dist/widget.es.js.map +1 -1
- package/dist/widget.umd.js +4 -4
- package/dist/widget.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/widget.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget.umd.js","sources":["../src/ui/bubble.ts","../src/ui/panel.ts","../src/core/utils.ts","../src/core/transport.ts","../src/core/state.ts","../src/core/jwt.ts","../src/index.ts"],"sourcesContent":["import type { PositionOption } from \"../types\";\n\nexport interface BubbleController {\n mount(): void;\n destroy(): void;\n setOpen(open: boolean): 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 }\n button {\n all: unset;\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 </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 </button>\n`;\n\nexport function createBubble(options: BubbleOptions): BubbleController {\n const host = document.createElement(\"div\");\n host.setAttribute(\"part\", \"bubble-root\");\n host.style.setProperty(\"--spilki-accent\", options.color);\n host.style.position = \"fixed\";\n host.style.bottom = \"24px\";\n host.style[options.position === \"bottom-right\" ? \"right\" : \"left\"] = \"24px\";\n\n const shadow = host.attachShadow({ mode: \"open\" });\n shadow.innerHTML = TEMPLATE;\n const button = shadow.querySelector(\"button\")!;\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 }\n };\n return controller;\n}\n","import type { Message, WidgetI18n } from \"../types\";\nimport styles from \"./styles.css?inline\";\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\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 focusable: HTMLElement[] = [];\n private open = false;\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 this.shadow.innerHTML = `\n <style>${styles}</style>\n <div class=\"wrapper\" role=\"dialog\" aria-modal=\"true\" aria-label=\"${options.i18n.title}\">\n <header>\n <h1><span class=\"status-dot\" aria-hidden=\"true\"></span>${options.i18n.title}</h1>\n <button class=\"close\" type=\"button\" aria-label=\"Close\">×</button>\n </header>\n <div class=\"messages\" part=\"messages\"></div>\n <div class=\"typing\" hidden>${options.i18n.typing}</div>\n <div class=\"offline\" hidden>${options.i18n.offline}</div>\n <div class=\"input-area\">\n <textarea rows=\"2\" placeholder=\"${options.i18n.placeholder}\" aria-label=\"${options.i18n.placeholder}\"></textarea>\n <button type=\"button\">${options.i18n.sendLabel}</button>\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(\".input-area button\") as HTMLButtonElement;\n\n const closeButton = this.shadow.querySelector(\"header .close\") as HTMLButtonElement;\n closeButton.addEventListener(\"click\", () => this.options.onClose());\n this.sendButton.addEventListener(\"click\", () => this.send());\n\n this.input.addEventListener(\"keydown\", (event: KeyboardEvent) => {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n this.send();\n } else if (event.key === \"Escape\") {\n this.options.onClose();\n }\n });\n\n this.shadow.addEventListener(\"keydown\", (event) => {\n const keyboard = event as KeyboardEvent;\n if (keyboard.key === \"Escape\") {\n this.options.onClose();\n }\n if (keyboard.key === \"Tab\") {\n this.trapFocus(keyboard);\n }\n });\n\n this.shadow.addEventListener(\"focusin\", () => this.collectFocusable());\n this.collectFocusable();\n }\n\n mount(): void {\n document.body.appendChild(this.host);\n }\n\n destroy(): void {\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.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 updateMessages(messages: Message[]): void {\n this.messagesEl.innerHTML = \"\";\n messages.forEach((message) => {\n const item = document.createElement(\"div\");\n item.className = `message ${message.author}`;\n item.setAttribute(\"data-author\", message.author);\n const bubble = document.createElement(\"div\");\n bubble.className = \"bubble\";\n bubble.textContent = message.text;\n item.appendChild(bubble);\n this.messagesEl.appendChild(item);\n });\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight;\n }\n\n appendMessage(message: Message): void {\n const item = document.createElement(\"div\");\n item.className = `message ${message.author}`;\n item.setAttribute(\"data-author\", message.author);\n const bubble = document.createElement(\"div\");\n bubble.className = \"bubble\";\n bubble.textContent = message.text;\n item.appendChild(bubble);\n this.messagesEl.appendChild(item);\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight;\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 }\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\")) {\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 {InitOptions, WidgetI18n} from \"../types\";\n\nexport const DEFAULT_API_BASE = \"https://api.spilki.ai\";\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 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\">\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 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 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 } as NormalizedOptions;\n}\n\nexport function storageKey(org: string, assistant: string): string {\n return `${SESSION_PREFIX}:${org}:${assistant}`;\n}\n\nexport function getPersistedSession(\n org: string,\n assistant: string\n): string | null {\n try {\n return localStorage.getItem(storageKey(org, assistant));\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 assistant: string,\n sessionId: string\n): void {\n try {\n localStorage.setItem(storageKey(org, assistant), sessionId);\n } catch (error) {\n console.warn(\"SpilkiWidget: Unable to persist session\", error);\n }\n}\n\nexport function clearSession(org: string, assistant: string): void {\n try {\n localStorage.removeItem(storageKey(org, assistant));\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","import type {ConnectResponse, Message, SendPayload, TransportKind} 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 onError(error: Error): void;\n}\n\ninterface TransportOptions {\n apiBase: string;\n accessToken?: string;\n org: string;\n assistant: string;\n sessionId?: string | null;\n}\n\ntype IncomingPayload =\n | { type: \"message\"; payload: Message }\n | { type: \"typing\"; payload: { active: boolean } }\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 sse?: EventSource;\n private pollTimer?: number;\n private stopped = false;\n private backoff = RETRY_BASE;\n private readonly handlers: TransportHandlers;\n private readonly options: TransportOptions;\n\n constructor(options: TransportOptions, handlers: TransportHandlers) {\n this.options = options;\n this.handlers = handlers;\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 async connect(): Promise<ConnectResponse> {\n this.stopped = false;\n const connectUrl = `${this.options.apiBase.replace(/\\/$/, \"\")}/widget/connect`;\n const currentSession = this.sessionId ?? this.options.sessionId ?? undefined;\n const body = {\n organisationId: this.options.org,\n assistantId: this.options.assistant,\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 };\n\n const res = await fetch(connectUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(this.options.accessToken ? {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): Promise<void> {\n const payload: SendPayload = {\n sessionId: this.sessionId ?? this.options.sessionId ?? \"\",\n text\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 ...(this.options.accessToken ? {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 stop(): void {\n this.stopped = true;\n this.backoff = RETRY_BASE;\n if (this.ws) {\n this.ws.close();\n this.ws = undefined;\n }\n if (this.sse) {\n this.sse.close();\n this.sse = undefined;\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 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 ws.addEventListener(\"open\", () => {\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 ws.addEventListener(\"message\", (event) => this.handleIncoming(event.data));\n ws.addEventListener(\"close\", () => {\n if (this.stopped) return;\n this.retryFallback(\"ws\");\n });\n ws.addEventListener(\"error\", () => {\n this.handlers.onError(new Error(\"SpilkiWidget: websocket error\"));\n if (ws.readyState !== WebSocket.OPEN) {\n reject(new Error(\"WebSocket failed\"));\n }\n });\n } catch (error) {\n reject(error as Error);\n }\n });\n }\n\n private startSse(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (typeof EventSource === \"undefined\") {\n reject(new Error(\"SSE not supported\"));\n return;\n }\n const sse = new EventSource(url);\n this.sse = sse;\n let opened = false;\n sse.addEventListener(\"open\", () => {\n opened = true;\n if (this.stopped) {\n sse.close();\n return;\n }\n this.currentKind = \"sse\";\n this.handlers.onOpen(\"sse\");\n this.backoff = RETRY_BASE;\n resolve();\n });\n sse.addEventListener(\"message\", (event) => this.handleIncoming(event.data));\n sse.addEventListener(\"error\", () => {\n if (!opened) {\n reject(new Error(\"SSE failed\"));\n return;\n }\n this.handlers.onError(new Error(\"SpilkiWidget: SSE error\"));\n if (this.stopped) return;\n this.retryFallback(\"sse\");\n });\n });\n }\n\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 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 private retryFallback(failed: TransportKind): void {\n if (this.stopped) return;\n this.stop();\n this.backoff = Math.min(this.backoff * 1.5, RETRY_MAX);\n setTimeout(() => {\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 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 const message: Message = {\n id: `${Date.now()}`,\n author: \"bot\",\n text: raw,\n ts: Date.now()\n };\n this.handlers.onMessage(message);\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 }\n return;\n }\n this.handlers.onMessage(data as Message);\n }\n}\n","import {clampMessages, createId} from \"./utils\";\nimport type {Message} from \"../types\";\n\ntype Listener = () => void;\n\nexport interface WidgetStateSnapshot {\n isOpen: boolean;\n isTyping: boolean;\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 readonly persist: boolean;\n private state: WidgetStateSnapshot = {\n isOpen: false,\n isTyping: false,\n isConnected: false,\n messages: []\n };\n\n constructor(\n private readonly org: string,\n private readonly assistant: string,\n options: { persist: boolean }\n ) {\n this.historyKey = `spilki-history:${org}:${assistant}`;\n this.sessionKey = `spilki-session:${org}:${assistant}`;\n this.tokenKey = `spilki-token:${org}:${assistant}`;\n this.persist = options.persist;\n this.state.messages = this.loadMessages();\n }\n\n get snapshot(): WidgetStateSnapshot {\n return {...this.state, messages: [...this.state.messages]};\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 setTyping(value: boolean): void {\n if (this.state.isTyping !== value) {\n this.state.isTyping = value;\n this.emit();\n }\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 {\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 this.state.messages = clampMessages([...this.state.messages, full], HISTORY_LIMIT);\n this.persistMessages();\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 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\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\nexport function isExpired(token: string): boolean {\n const payload = parseJwt(token);\n if (!payload?.exp) return false;\n const now = Math.floor(Date.now() / 1000);\n return payload.exp < now;\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, TransportKind, WidgetAccessTokenResponse, WidgetHooks} from \"./types\";\n\nexport interface SpilkiWidgetInstance {\n open(): void;\n\n close(): 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): 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})\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: {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\nexport function initSpilkiWidget(rawOptions: InitOptions): SpilkiWidgetInstance {\n if (!rawOptions.org || !rawOptions.assistant) {\n throw new Error(\"SpilkiWidget: org and assistant are 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, options.assistant, {persist: options.persist});\n\n let accessToken = state.accessToken ?? undefined;\n\n const ensureAccessToken = async () => {\n if (!accessToken) {\n if (!rawOptions.installationToken) throw new Error(\"SpilkiWidget: missing installationToken\");\n accessToken = await installAndGetAccessToken(options.apiBase, rawOptions.installationToken);\n state.persistAccessToken(accessToken);\n return;\n }\n if (isExpired(accessToken)) {\n accessToken = await refreshAccessToken(options.apiBase, accessToken);\n state.persistAccessToken(accessToken);\n }\n };\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 state.open();\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 panel.appendMessage(message);\n ensureAccessToken()\n .then(() => transport.send(text))\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 assistant: options.assistant,\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 stored = state.addMessage(message);\n panel.appendMessage(stored);\n hooks.onMessage(stored);\n },\n onTyping(active) {\n state.setTyping(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 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 {\n panel.updateMessages(initialMessages);\n }\n\n const unsubscribe = state.subscribe(() => {\n const snap = state.snapshot;\n bubble.setOpen(snap.isOpen);\n panel.setTyping(snap.isTyping);\n panel.setOffline(!snap.isConnected);\n panel.updateTheme(getTheme(options.theme));\n if (snap.isOpen) {\n panel.show();\n } else {\n panel.hide();\n }\n });\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 )\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 state.open();\n hooks.onOpen();\n },\n close() {\n state.close();\n hooks.onClose();\n },\n destroy() {\n unsubscribe();\n transport.stop();\n bubble.destroy();\n panel.destroy();\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 const assistant = dataset.assistant;\n if (!org || !assistant) {\n console.error(\"SpilkiWidget: data-org and data-assistant are required for auto init\");\n return;\n }\n initSpilkiWidget({\n org,\n assistant,\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\nif (typeof window !== \"undefined\") {\n const globalAny = window as any;\n globalAny.SpilkiWidget = globalAny.SpilkiWidget || {};\n globalAny.SpilkiWidget.init = (options: InitOptions) => initSpilkiWidget(options);\n}\n\nautoInit();\n\nexport type {InitOptions, Message} from \"./types\";\n"],"names":["TEMPLATE","createBubble","options","host","shadow","button","open","Panel","styles","event","keyboard","theme","messages","message","item","bubble","active","text","items","el","first","last","DEFAULT_API_BASE","DEFAULT_I18N","DEFAULT_OPTIONS","mergeOptions","mergedI18n","_a","_b","_c","_d","_e","_f","_g","createId","prefix","prefersDark","getTheme","clampMessages","limit","warnAllowedOrigins","hint","origin","RETRY_BASE","RETRY_MAX","TransportManager","handlers","connectUrl","currentSession","body","res","data","payload","url","connect","attempts","attempt","error","resolve","reject","ws","sse","opened","poll","failed","raw","HISTORY_LIMIT","WidgetState","org","assistant","listener","value","full","sessionId","token","parsed","decodePart","part","normalized","padded","c","buffer","parseJwt","parts","isExpired","now","DEFAULT_HOOKS","installAndGetAccessToken","apiBase","installationToken","refreshAccessToken","oldToken","initSpilkiWidget","rawOptions","hooks","state","accessToken","ensureAccessToken","panel","transport","kind","instance","stored","initialMessages","welcome","unsubscribe","snap","response","autoInit","script","dataset","globalAny"],"mappings":"sPAeA,MAAMA,EAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+CV,SAASC,EAAaC,EAA0C,CACrE,MAAMC,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,aAAa,OAAQ,aAAa,EACvCA,EAAK,MAAM,YAAY,kBAAmBD,EAAQ,KAAK,EACvDC,EAAK,MAAM,SAAW,QACtBA,EAAK,MAAM,OAAS,OACpBA,EAAK,MAAMD,EAAQ,WAAa,eAAiB,QAAU,MAAM,EAAI,OAErE,MAAME,EAASD,EAAK,aAAa,CAAE,KAAM,OAAQ,EACjDC,EAAO,UAAYJ,EACnB,MAAMK,EAASD,EAAO,cAAc,QAAQ,EAC5C,OAAAC,EAAO,iBAAiB,QAAS,IAAMH,EAAQ,SAAS,EAEnB,CACnC,QAASC,EACT,OAAQ,CACN,SAAS,KAAK,YAAYA,CAAI,CAChC,EACA,SAAU,CACRA,EAAK,OAAA,CACP,EACA,QAAQG,EAAe,CACrBD,EAAO,aAAa,gBAAiB,OAAOC,CAAI,CAAC,CACnD,CAAA,CAGJ,y6GC5EO,MAAMC,CAAM,CAWjB,YAA6BL,EAAuB,CAAvB,KAAA,QAAAA,EAH7B,KAAiB,UAA2B,CAAA,EAC5C,KAAQ,KAAO,GAGb,KAAK,KAAO,SAAS,cAAc,KAAK,EACxC,KAAK,KAAK,aAAa,OAAQ,YAAY,EAC3C,KAAK,KAAK,MAAM,SAAW,QAC3B,KAAK,KAAK,MAAM,OAAS,OACzB,KAAK,KAAK,MAAMA,EAAQ,WAAa,eAAiB,QAAU,MAAM,EAAI,OAC1E,KAAK,KAAK,MAAM,MAAQ,QACxB,KAAK,KAAK,MAAM,SAAW,qBAC3B,KAAK,KAAK,MAAM,OAAS,QACzB,KAAK,KAAK,MAAM,QAAU,OAC1B,KAAK,KAAK,MAAM,OAAS,aAEzB,KAAK,OAAS,KAAK,KAAK,aAAa,CAAE,KAAM,OAAQ,EACrD,KAAK,OAAO,UAAY;AAAA,eACbM,CAAM;AAAA,yEACoDN,EAAQ,KAAK,KAAK;AAAA;AAAA,mEAExBA,EAAQ,KAAK,KAAK;AAAA;AAAA;AAAA;AAAA,qCAIhDA,EAAQ,KAAK,MAAM;AAAA,sCAClBA,EAAQ,KAAK,OAAO;AAAA;AAAA,4CAEdA,EAAQ,KAAK,WAAW,iBAAiBA,EAAQ,KAAK,WAAW;AAAA,kCAC3EA,EAAQ,KAAK,SAAS;AAAA;AAAA;AAAA,MAKpD,KAAK,KAAK,QAAQ,MAAQA,EAAQ,MAClC,KAAK,KAAK,MAAM,YAAY,kBAAmBA,EAAQ,KAAK,EAE5D,KAAK,WAAa,KAAK,OAAO,cAAc,WAAW,EACvD,KAAK,SAAW,KAAK,OAAO,cAAc,SAAS,EACnD,KAAK,MAAQ,KAAK,OAAO,cAAc,UAAU,EACjD,KAAK,UAAY,KAAK,OAAO,cAAc,UAAU,EACrD,KAAK,WAAa,KAAK,OAAO,cAAc,oBAAoB,EAE5C,KAAK,OAAO,cAAc,eAAe,EACjD,iBAAiB,QAAS,IAAM,KAAK,QAAQ,SAAS,EAClE,KAAK,WAAW,iBAAiB,QAAS,IAAM,KAAK,MAAM,EAE3D,KAAK,MAAM,iBAAiB,UAAYO,GAAyB,CAC3DA,EAAM,MAAQ,SAAW,CAACA,EAAM,UAClCA,EAAM,eAAA,EACN,KAAK,KAAA,GACIA,EAAM,MAAQ,UACvB,KAAK,QAAQ,QAAA,CAEjB,CAAC,EAED,KAAK,OAAO,iBAAiB,UAAYA,GAAU,CACjD,MAAMC,EAAWD,EACbC,EAAS,MAAQ,UACnB,KAAK,QAAQ,QAAA,EAEXA,EAAS,MAAQ,OACnB,KAAK,UAAUA,CAAQ,CAE3B,CAAC,EAED,KAAK,OAAO,iBAAiB,UAAW,IAAM,KAAK,kBAAkB,EACrE,KAAK,iBAAA,CACP,CAEA,OAAc,CACZ,SAAS,KAAK,YAAY,KAAK,IAAI,CACrC,CAEA,SAAgB,CACd,KAAK,KAAK,OAAA,CACZ,CAEA,MAAa,CACP,KAAK,OACT,KAAK,KAAO,GACZ,KAAK,KAAK,MAAM,QAAU,QAC1B,KAAK,WAAA,EACP,CAEA,MAAa,CACN,KAAK,OACV,KAAK,KAAO,GACZ,KAAK,KAAK,MAAM,QAAU,OAC5B,CAEA,YAAmB,CACjB,eAAe,IAAM,CACnB,KAAK,MAAM,MAAA,CACb,CAAC,CACH,CAEA,YAAYC,EAA+B,CACzC,KAAK,KAAK,QAAQ,MAAQA,CAC5B,CAEA,eAAeC,EAA2B,CACxC,KAAK,WAAW,UAAY,GAC5BA,EAAS,QAASC,GAAY,CAC5B,MAAMC,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,WAAWD,EAAQ,MAAM,GAC1CC,EAAK,aAAa,cAAeD,EAAQ,MAAM,EAC/C,MAAME,EAAS,SAAS,cAAc,KAAK,EAC3CA,EAAO,UAAY,SACnBA,EAAO,YAAcF,EAAQ,KAC7BC,EAAK,YAAYC,CAAM,EACvB,KAAK,WAAW,YAAYD,CAAI,CAClC,CAAC,EACD,KAAK,WAAW,UAAY,KAAK,WAAW,YAC9C,CAEA,cAAcD,EAAwB,CACpC,MAAMC,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,WAAWD,EAAQ,MAAM,GAC1CC,EAAK,aAAa,cAAeD,EAAQ,MAAM,EAC/C,MAAME,EAAS,SAAS,cAAc,KAAK,EAC3CA,EAAO,UAAY,SACnBA,EAAO,YAAcF,EAAQ,KAC7BC,EAAK,YAAYC,CAAM,EACvB,KAAK,WAAW,YAAYD,CAAI,EAChC,KAAK,WAAW,UAAY,KAAK,WAAW,YAC9C,CAEA,UAAUE,EAAuB,CAC/B,KAAK,SAAS,gBAAgB,SAAU,CAACA,CAAM,CACjD,CAEA,WAAWA,EAAuB,CAChC,KAAK,UAAU,gBAAgB,SAAU,CAACA,CAAM,CAClD,CAEA,YAAmB,CACjB,KAAK,MAAM,MAAQ,GACnB,KAAK,MAAM,cAAc,IAAI,MAAM,OAAO,CAAC,CAC7C,CAEQ,MAAa,CACnB,MAAMC,EAAO,KAAK,MAAM,MAAM,KAAA,EACzBA,IACL,KAAK,QAAQ,OAAOA,CAAI,EACxB,KAAK,WAAA,EACP,CAEQ,kBAAyB,CAC/B,MAAMC,EAAQ,KAAK,OAAO,iBACxB,2DAAA,EAEF,KAAK,UAAU,OAAS,EACxBA,EAAM,QAASC,GAAO,CACfA,EAAG,aAAa,UAAU,GAC7B,KAAK,UAAU,KAAKA,CAAE,CAE1B,CAAC,CACH,CAEQ,UAAUV,EAA4B,CAC5C,GAAI,KAAK,UAAU,SAAW,EAAG,OACjC,MAAMW,EAAQ,KAAK,UAAU,CAAC,EACxBC,EAAO,KAAK,UAAU,KAAK,UAAU,OAAS,CAAC,EAC/CL,EAAS,KAAK,OAAO,cACvBP,EAAM,UAAYO,IAAWI,GAC/BX,EAAM,eAAA,EACNY,EAAK,MAAA,GACI,CAACZ,EAAM,UAAYO,IAAWK,IACvCZ,EAAM,eAAA,EACNW,EAAM,MAAA,EAEV,CACF,CC9LO,MAAME,EAAmB,wBAG1BC,EAA2B,CAC7B,QAAS,0BACT,YAAa,kBACb,UAAW,OACX,OAAQ,uBACR,QAAS,6CACT,MAAO,kBACX,EAEaC,EAE+B,CACxC,QAASF,EACT,SAAU,eACV,MAAO,OACP,MAAO,UACP,QAASC,EAAa,QACtB,QAAS,GACT,KAAMA,CACV,EAYO,SAASE,EAAavB,EAAyC,mBAClE,MAAMwB,EAAa,CAAC,GAAGH,EAAc,IAAII,EAAAzB,EAAQ,OAAR,KAAAyB,EAAgB,EAAC,EAC1D,MAAO,CACH,GAAGH,EACH,GAAGtB,EACH,SAAS0B,EAAA1B,EAAQ,UAAR,KAAA0B,EAAmBJ,EAAgB,QAC5C,KAAME,EACN,SAASG,EAAA3B,EAAQ,UAAR,KAAA2B,EAAmBH,EAAW,QACvC,UAAUI,EAAA5B,EAAQ,WAAR,KAAA4B,EAAoBN,EAAgB,SAC9C,OAAOO,EAAA7B,EAAQ,QAAR,KAAA6B,EAAiBP,EAAgB,MACxC,OAAOQ,EAAA9B,EAAQ,QAAR,KAAA8B,EAAiBR,EAAgB,MACxC,SAASS,EAAA/B,EAAQ,UAAR,KAAA+B,EAAmBT,EAAgB,OAAA,CAEpD,CAsCO,SAASU,EAASC,EAAS,MAAe,CAC7C,OAAI,OAAO,QAAW,aAAe,OAAO,WACjC,OAAO,WAAA,EAEX,GAAGA,CAAM,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,EAC3D,CAEO,SAASC,GAAuB,SACnC,OAAOR,GAAAD,EAAA,OAAO,aAAP,YAAAA,EAAA,YAAoB,gCAAgC,UAApD,KAAAC,EAA+D,EAC1E,CAEO,SAASS,EAAS1B,EAA6C,CAClE,OAAIA,IAAU,SAAWA,IAAU,OAAeA,EAC3CyB,EAAA,EAAgB,OAAS,OACpC,CAEO,SAASE,EAAiB1B,EAAe2B,EAAQ,GAAS,CAC7D,OAAO3B,EAAS,MAAM,CAAC2B,CAAK,CAChC,CAEO,SAASC,EAAmBC,EAAkC,CACjE,GAAI,CAACA,GAAQA,EAAK,SAAW,EAAG,OAChC,MAAMC,EAAS,OAAO,SAAS,OAC1BD,EAAK,SAASC,CAAM,GACrB,QAAQ,KACJ,gCAAgCA,CAAM,+BAA+BD,EAAK,KAAK,IAAI,CAAC,EAAA,CAGhG,CCzFA,MAAME,EAAa,KACbC,EAAY,IAEX,MAAMC,CAAiB,CAW1B,YAAY3C,EAA2B4C,EAA6B,CAVpE,KAAQ,UAA2B,KACnC,KAAQ,YAAoC,KAI5C,KAAQ,QAAU,GAClB,KAAQ,QAAUH,EAKd,KAAK,QAAUzC,EACf,KAAK,SAAW4C,CACpB,CAEA,IAAI,MAA6B,CAC7B,OAAO,KAAK,WAChB,CAEA,IAAI,eAA+B,SAC/B,OAAOlB,GAAAD,EAAA,KAAK,YAAL,KAAAA,EAAkB,KAAK,QAAQ,YAA/B,KAAAC,EAA4C,IACvD,CAEA,MAAM,SAAoC,SACtC,KAAK,QAAU,GACf,MAAMmB,EAAa,GAAG,KAAK,QAAQ,QAAQ,QAAQ,MAAO,EAAE,CAAC,kBACvDC,GAAiBpB,GAAAD,EAAA,KAAK,YAAL,KAAAA,EAAkB,KAAK,QAAQ,YAA/B,KAAAC,EAA4C,OAC7DqB,EAAO,CACT,eAAgB,KAAK,QAAQ,IAC7B,YAAa,KAAK,QAAQ,UAC1B,UAAWD,EACX,UAAW,OAAO,WAAc,YAAc,UAAU,UAAY,GACpE,SAAU,OAAO,UAAa,YAAc,SAAS,SAAW,GAChE,OAAQ,OAAO,QAAW,YAAc,OAAO,SAAS,OAAS,EAAA,EAG/DE,EAAM,MAAM,MAAMH,EAAY,CAChC,OAAQ,OACR,QAAS,CACL,eAAgB,mBAChB,GAAI,KAAK,QAAQ,YAAc,CAAC,cAAe,UAAU,KAAK,QAAQ,WAAW,EAAA,EAAM,CAAA,CAAC,EAE5F,KAAM,KAAK,UAAUE,CAAI,CAAA,CAC5B,EAED,GAAI,CAACC,EAAI,GACL,MAAM,IAAI,MAAM,iCAAiCA,EAAI,MAAM,GAAG,EAGlE,MAAMC,EAAQ,MAAMD,EAAI,KAAA,EACxB,YAAK,UAAYC,EAAK,UACtB,KAAK,QAAQ,UAAYA,EAAK,UAC9B,KAAK,QAAUR,EACf,MAAM,KAAK,eAAeQ,CAAI,EACvBA,CACX,CAEA,MAAM,KAAKlC,EAA6B,SACpC,MAAMmC,EAAuB,CACzB,WAAWxB,GAAAD,EAAA,KAAK,YAAL,KAAAA,EAAkB,KAAK,QAAQ,YAA/B,KAAAC,EAA4C,GACvD,KAAAX,CAAA,EAGJ,GAAI,CAACmC,EAAQ,UACT,MAAM,IAAI,MAAM,kCAAkC,EAGtD,GAAI,KAAK,cAAgB,MAAQ,KAAK,IAAM,KAAK,GAAG,aAAe,UAAU,KAAM,CAC/E,KAAK,GAAG,KAAK,KAAK,UAAU,CAAC,KAAM,UAAW,QAAAA,CAAA,CAAQ,CAAC,EACvD,MACJ,CAEA,MAAMC,EAAM,GAAG,KAAK,QAAQ,QAAQ,QAAQ,MAAO,EAAE,CAAC,kBAChDH,EAAM,MAAM,MAAMG,EAAK,CACzB,OAAQ,OACR,QAAS,CACL,eAAgB,mBAChB,GAAI,KAAK,QAAQ,YAAc,CAAC,cAAe,UAAU,KAAK,QAAQ,WAAW,EAAA,EAAM,CAAA,CAAC,EAE5F,KAAM,KAAK,UAAUD,CAAO,CAAA,CAC/B,EAED,GAAI,CAACF,EAAI,GACL,MAAM,IAAI,MAAM,8BAA8BA,EAAI,MAAM,GAAG,CAEnE,CAEA,MAAa,CACT,KAAK,QAAU,GACf,KAAK,QAAUP,EACX,KAAK,KACL,KAAK,GAAG,MAAA,EACR,KAAK,GAAK,QAEV,KAAK,MACL,KAAK,IAAI,MAAA,EACT,KAAK,IAAM,QAEX,KAAK,YACL,aAAa,KAAK,SAAS,EAC3B,KAAK,UAAY,QAErB,KAAK,YAAc,IACvB,CAEA,MAAc,eAAeW,EAAyC,CAClE,GAAI,KAAK,QAAS,OAClB,MAAMC,EAAuC,CAAA,EACzCD,EAAQ,OAAOC,EAAS,KAAK,IAAM,KAAK,QAAQD,EAAQ,KAAM,CAAC,EAC/DA,EAAQ,QAAQC,EAAS,KAAK,IAAM,KAAK,SAASD,EAAQ,MAAO,CAAC,EAClEA,EAAQ,SAASC,EAAS,KAAK,IAAM,KAAK,UAAUD,EAAQ,OAAQ,CAAC,EAEzE,UAAWE,KAAWD,EAClB,GAAI,CACA,MAAMC,EAAA,EACN,MACJ,OAASC,EAAO,CACZ,KAAK,SAAS,QAAQA,CAAc,CACxC,CAEJ,MAAM,IAAI,MAAM,6CAA6C,CACjE,CAEQ,QAAQJ,EAA4B,CACxC,OAAO,IAAI,QAAQ,CAACK,EAASC,IAAW,CACpC,GAAI,CACA,MAAMC,EAAK,IAAI,UAAUP,CAAG,EAC5B,KAAK,GAAKO,EACVA,EAAG,iBAAiB,OAAQ,IAAM,CAC9B,GAAI,KAAK,QAAS,CACdA,EAAG,MAAA,EACH,MACJ,CACA,KAAK,YAAc,KACnB,KAAK,SAAS,OAAO,IAAI,EACzB,KAAK,QAAUjB,EACfe,EAAA,CACJ,CAAC,EACDE,EAAG,iBAAiB,UAAYnD,GAAU,KAAK,eAAeA,EAAM,IAAI,CAAC,EACzEmD,EAAG,iBAAiB,QAAS,IAAM,CAC3B,KAAK,SACT,KAAK,cAAc,IAAI,CAC3B,CAAC,EACDA,EAAG,iBAAiB,QAAS,IAAM,CAC/B,KAAK,SAAS,QAAQ,IAAI,MAAM,+BAA+B,CAAC,EAC5DA,EAAG,aAAe,UAAU,MAC5BD,EAAO,IAAI,MAAM,kBAAkB,CAAC,CAE5C,CAAC,CACL,OAASF,EAAO,CACZE,EAAOF,CAAc,CACzB,CACJ,CAAC,CACL,CAEQ,SAASJ,EAA4B,CACzC,OAAO,IAAI,QAAQ,CAACK,EAASC,IAAW,CACpC,GAAI,OAAO,aAAgB,YAAa,CACpCA,EAAO,IAAI,MAAM,mBAAmB,CAAC,EACrC,MACJ,CACA,MAAME,EAAM,IAAI,YAAYR,CAAG,EAC/B,KAAK,IAAMQ,EACX,IAAIC,EAAS,GACbD,EAAI,iBAAiB,OAAQ,IAAM,CAE/B,GADAC,EAAS,GACL,KAAK,QAAS,CACdD,EAAI,MAAA,EACJ,MACJ,CACA,KAAK,YAAc,MACnB,KAAK,SAAS,OAAO,KAAK,EAC1B,KAAK,QAAUlB,EACfe,EAAA,CACJ,CAAC,EACDG,EAAI,iBAAiB,UAAYpD,GAAU,KAAK,eAAeA,EAAM,IAAI,CAAC,EAC1EoD,EAAI,iBAAiB,QAAS,IAAM,CAChC,GAAI,CAACC,EAAQ,CACTH,EAAO,IAAI,MAAM,YAAY,CAAC,EAC9B,MACJ,CACA,KAAK,SAAS,QAAQ,IAAI,MAAM,yBAAyB,CAAC,EACtD,MAAK,SACT,KAAK,cAAc,KAAK,CAC5B,CAAC,CACL,CAAC,CACL,CAEA,MAAc,UAAUN,EAA4B,CAChD,KAAK,YAAc,OACnB,KAAK,SAAS,OAAO,MAAM,EAC3B,KAAK,QAAUV,EACf,MAAMoB,EAAO,SAAY,CACrB,GAAI,MAAK,QACT,GAAI,CACA,MAAMb,EAAM,MAAM,MAAMG,CAAG,EAC3B,GAAI,CAACH,EAAI,GAAI,MAAM,IAAI,MAAM,eAAeA,EAAI,MAAM,EAAE,EACxD,MAAMtC,EAAY,MAAMsC,EAAI,KAAA,EAC5BZ,EAAc1B,CAAQ,EAAE,QAASC,GAAY,KAAK,SAAS,UAAUA,CAAO,CAAC,EAC7E,KAAK,QAAU8B,CACnB,OAASc,EAAO,CACZ,KAAK,SAAS,QAAQA,CAAc,EACpC,KAAK,QAAU,KAAK,IAAI,KAAK,QAAU,IAAKb,CAAS,CACzD,QAAA,CACS,KAAK,UACN,KAAK,UAAY,OAAO,WAAWmB,EAAM,KAAK,OAAO,EAE7D,CACJ,EACA,MAAMA,EAAA,CACV,CAEQ,cAAcC,EAA6B,CAC3C,KAAK,UACT,KAAK,KAAA,EACL,KAAK,QAAU,KAAK,IAAI,KAAK,QAAU,IAAKpB,CAAS,EACrD,WAAW,IAAM,CACb,KAAK,SAAS,QAAQ,IAAI,MAAM,gCAAgCoB,CAAM,EAAE,CAAC,EACzE,KAAK,UAAU,MAAOP,GAAU,KAAK,SAAS,QAAQA,CAAK,CAAC,CAChE,EAAG,KAAK,OAAO,EACnB,CAEQ,eAAeQ,EAAmB,CACtC,GAAI,CACA,MAAMd,EAAO,KAAK,MAAMc,CAAG,EAC3B,GAAI,MAAM,QAAQd,CAAI,EAAG,CACrBA,EAAK,QAASrC,GAAS,KAAK,iBAAiBA,CAAI,CAAC,EAClD,MACJ,CACA,KAAK,iBAAiBqC,CAAI,CAC9B,MAAQ,CACJ,MAAMtC,EAAmB,CACrB,GAAI,GAAG,KAAK,IAAA,CAAK,GACjB,OAAQ,MACR,KAAMoD,EACN,GAAI,KAAK,IAAA,CAAI,EAEjB,KAAK,SAAS,UAAUpD,CAAO,CACnC,CACJ,CAEQ,iBAAiBsC,EAA6B,OAClD,GAAI,SAAUA,EAAM,CACZA,EAAK,OAAS,UACd,KAAK,SAAS,UAAUA,EAAK,OAAO,EAC7BA,EAAK,OAAS,UACrB,KAAK,SAAS,SAAS,IAAQxB,EAAAwB,EAAK,UAAL,MAAAxB,EAAc,OAAO,EAExD,MACJ,CACA,KAAK,SAAS,UAAUwB,CAAe,CAC3C,CACJ,CC9QA,MAAMe,EAAgB,GAEf,MAAMC,CAAY,CAarB,YACqBC,EACAC,EACjBnE,EACF,CAHmB,KAAA,IAAAkE,EACA,KAAA,UAAAC,EAdrB,KAAQ,cAAgB,IAKxB,KAAQ,MAA6B,CACjC,OAAQ,GACR,SAAU,GACV,YAAa,GACb,SAAU,CAAA,CAAC,EAQX,KAAK,WAAa,kBAAkBD,CAAG,IAAIC,CAAS,GACpD,KAAK,WAAa,kBAAkBD,CAAG,IAAIC,CAAS,GACpD,KAAK,SAAW,gBAAgBD,CAAG,IAAIC,CAAS,GAChD,KAAK,QAAUnE,EAAQ,QACvB,KAAK,MAAM,SAAW,KAAK,aAAA,CAC/B,CAEA,IAAI,UAAgC,CAChC,MAAO,CAAC,GAAG,KAAK,MAAO,SAAU,CAAC,GAAG,KAAK,MAAM,QAAQ,CAAA,CAC5D,CAEA,UAAUoE,EAAgC,CACtC,YAAK,UAAU,IAAIA,CAAQ,EACpB,IAAM,KAAK,UAAU,OAAOA,CAAQ,CAC/C,CAEA,MAAa,CACJ,KAAK,MAAM,SACZ,KAAK,MAAM,OAAS,GACpB,KAAK,KAAA,EAEb,CAEA,OAAc,CACN,KAAK,MAAM,SACX,KAAK,MAAM,OAAS,GACpB,KAAK,KAAA,EAEb,CAEA,UAAUC,EAAsB,CACxB,KAAK,MAAM,WAAaA,IACxB,KAAK,MAAM,SAAWA,EACtB,KAAK,KAAA,EAEb,CAEA,aAAaA,EAAsB,CAC3B,KAAK,MAAM,cAAgBA,IAC3B,KAAK,MAAM,YAAcA,EACzB,KAAK,KAAA,EAEb,CAEA,WAAW1D,EAAoF,SAC3F,MAAM2D,EAAgB,CAClB,IAAI7C,EAAAd,EAAQ,KAAR,KAAAc,EAAcO,EAAS,KAAK,EAChC,IAAIN,EAAAf,EAAQ,KAAR,KAAAe,EAAc,KAAK,IAAA,EACvB,OAAQf,EAAQ,OAChB,KAAMA,EAAQ,IAAA,EAElB,YAAK,MAAM,SAAWyB,EAAc,CAAC,GAAG,KAAK,MAAM,SAAUkC,CAAI,EAAGN,CAAa,EACjF,KAAK,gBAAA,EACL,KAAK,KAAA,EACEM,CACX,CAEA,YAAY5D,EAA2B,CACnC,KAAK,MAAM,SAAW0B,EAAc1B,EAAUsD,CAAa,EAC3D,KAAK,gBAAA,EACL,KAAK,KAAA,CACT,CAEA,eAAsB,CAClB,KAAK,MAAM,SAAW,CAAA,EACtB,KAAK,gBAAA,EACL,KAAK,KAAA,CACT,CAEA,IAAI,WAA2B,CAC3B,GAAI,CAAC,KAAK,QAAS,OAAO,KAC1B,GAAI,CACA,OAAO,aAAa,QAAQ,KAAK,UAAU,CAC/C,OAAST,EAAO,CACZ,eAAQ,MAAM,mCAAoCA,CAAK,EAChD,IACX,CACJ,CAEA,eAAegB,EAAyB,CACpC,GAAK,KAAK,QACV,GAAI,CACA,aAAa,QAAQ,KAAK,WAAYA,CAAS,CACnD,OAAShB,EAAO,CACZ,QAAQ,MAAM,mCAAoCA,CAAK,CAC3D,CACJ,CAEA,cAAqB,CACjB,GAAK,KAAK,QACV,GAAI,CACA,aAAa,WAAW,KAAK,UAAU,CAC3C,OAASA,EAAO,CACZ,QAAQ,MAAM,sCAAuCA,CAAK,CAC9D,CACJ,CAEA,IAAI,aAA6B,CAC7B,GAAI,CAAC,KAAK,QAAS,OAAO,KAC1B,GAAI,CACA,OAAO,aAAa,QAAQ,KAAK,QAAQ,CAC7C,OAASA,EAAO,CACZ,eAAQ,MAAM,mCAAoCA,CAAK,EAChD,IACX,CACJ,CAEA,mBAAmBiB,EAAqB,CACpC,GAAK,KAAK,QACV,GAAI,CACA,aAAa,QAAQ,KAAK,SAAUA,CAAK,CAC7C,OAASjB,EAAO,CACZ,QAAQ,MAAM,mCAAoCA,CAAK,CAC3D,CACJ,CAEA,kBAAyB,CACrB,GAAK,KAAK,QACV,GAAI,CACA,aAAa,WAAW,KAAK,QAAQ,CACzC,OAASA,EAAO,CACZ,QAAQ,MAAM,sCAAuCA,CAAK,CAC9D,CACJ,CAEQ,MAAa,CACjB,KAAK,UAAU,QAASa,GAAaA,GAAU,CACnD,CAEQ,iBAAwB,CAC5B,GAAK,KAAK,QACV,GAAI,CACA,aAAa,QAAQ,KAAK,WAAY,KAAK,UAAU,KAAK,MAAM,QAAQ,CAAC,CAC7E,OAASb,EAAO,CACZ,QAAQ,MAAM,mCAAoCA,CAAK,CAC3D,CACJ,CAEQ,cAA0B,CAC9B,GAAI,CAAC,KAAK,QAAS,MAAO,CAAA,EAC1B,GAAI,CACA,MAAMQ,EAAM,aAAa,QAAQ,KAAK,UAAU,EAChD,GAAI,CAACA,EAAK,MAAO,CAAA,EACjB,MAAMU,EAAS,KAAK,MAAMV,CAAG,EAC7B,OAAO,MAAM,QAAQU,CAAM,EAAIrC,EAAcqC,EAAQT,CAAa,EAAI,CAAA,CAC1E,OAAST,EAAO,CACZ,eAAQ,MAAM,wCAAyCA,CAAK,EACrD,CAAA,CACX,CACJ,CACJ,CChLA,SAASmB,EAAWC,EAAsB,CACtC,MAAMC,EAAaD,EAAK,QAAQ,KAAM,GAAG,EAAE,QAAQ,KAAM,GAAG,EACtDE,EAASD,EAAW,OAAOA,EAAW,QAAW,EAAKA,EAAW,OAAS,GAAM,EAAI,GAAG,EAC7F,GAAI,OAAO,MAAS,WAChB,OAAO,mBACH,MAAM,UAAU,IACX,KAAK,KAAKC,CAAM,EAAIC,GAAc,IAAI,KAAKA,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EACrF,KAAK,EAAE,CAAA,EAGpB,MAAMC,EAAU,WAEb,OACH,GAAIA,EACA,OAAOA,EAAO,KAAKF,EAAQ,QAAQ,EAAE,SAAS,MAAM,EAExD,MAAM,IAAI,MAAM,2CAA2C,CAC/D,CAEO,SAASG,EAA4CR,EAAyB,CACjF,GAAI,CAACA,EAAO,OAAO,KACnB,MAAMS,EAAQT,EAAM,MAAM,GAAG,EAC7B,GAAIS,EAAM,OAAS,EAAG,OAAO,KAC7B,GAAI,CACA,MAAM/B,EAAUwB,EAAWO,EAAM,CAAC,CAAC,EACnC,OAAO,KAAK,MAAM/B,CAAO,CAC7B,OAASK,EAAO,CACZ,eAAQ,MAAM,oCAAqCA,CAAK,EACjD,IACX,CACJ,CAEO,SAAS2B,EAAUV,EAAwB,CAC9C,MAAMtB,EAAU8B,EAASR,CAAK,EAC9B,GAAI,EAACtB,GAAA,MAAAA,EAAS,KAAK,MAAO,GAC1B,MAAMiC,EAAM,KAAK,MAAM,KAAK,IAAA,EAAQ,GAAI,EACxC,OAAOjC,EAAQ,IAAMiC,CACzB,CCxBA,MAAMC,EAA6B,CAC/B,QAAS,CACT,EACA,SAAU,CACV,EACA,WAAY,CACZ,EACA,SAAU,CACV,EACA,mBAAoB,CACpB,CACJ,EAEA,eAAeC,EAAyBC,EAAiBC,EAA4C,CACjG,MAAMpC,EAAM,GAAGmC,EAAQ,QAAQ,MAAO,EAAE,CAAC,kBACnCtC,EAAM,MAAM,MAAMG,EAAK,CACzB,OAAQ,OACR,QAAS,CAAC,eAAgB,mBAAoB,OAAQ,OAAO,SAAS,MAAA,EACtE,KAAM,KAAK,UAAU,CAAC,MAAOoC,EAAkB,CAAA,CAClD,EACD,GAAI,CAACvC,EAAI,GAAI,MAAM,IAAI,MAAM,iCAAiCA,EAAI,MAAM,GAAG,EAE3E,OADc,MAAMA,EAAI,KAAA,GACZ,WAChB,CAEA,eAAewC,EAAmBF,EAAiBG,EAAmC,CAClF,MAAMtC,EAAM,GAAGmC,EAAQ,QAAQ,MAAO,EAAE,CAAC,kBACnCtC,EAAM,MAAM,MAAMG,EAAK,CACzB,OAAQ,OACR,QAAS,CAAC,cAAe,UAAUsC,CAAQ,GAAI,OAAQ,OAAO,SAAS,MAAA,CAAM,CAChF,EACD,GAAI,CAACzC,EAAI,GAAI,MAAM,IAAI,MAAM,iCAAiCA,EAAI,MAAM,GAAG,EAE3E,OADc,MAAMA,EAAI,KAAA,GACZ,WAChB,CAEO,SAAS0C,EAAiBC,EAA+C,aAC5E,GAAI,CAACA,EAAW,KAAO,CAACA,EAAW,UAC/B,MAAM,IAAI,MAAM,8CAA8C,EAGlE,MAAM3F,EAA6BuB,EAAaoE,CAAU,EAC1DrD,EAAmBtC,EAAQ,kBAAkB,EAE7C,MAAM4F,EAAqB,CAAC,GAAGR,EAAe,IAAI3D,EAAAzB,EAAQ,QAAR,KAAAyB,EAAiB,EAAC,EAC9DoE,EAAQ,IAAI5B,EAAYjE,EAAQ,IAAKA,EAAQ,UAAW,CAAC,QAASA,EAAQ,OAAA,CAAQ,EAExF,IAAI8F,GAAcpE,EAAAmE,EAAM,cAAN,KAAAnE,EAAqB,OAEvC,MAAMqE,EAAoB,SAAY,CAClC,GAAI,CAACD,EAAa,CACd,GAAI,CAACH,EAAW,kBAAmB,MAAM,IAAI,MAAM,yCAAyC,EAC5FG,EAAc,MAAMT,EAAyBrF,EAAQ,QAAS2F,EAAW,iBAAiB,EAC1FE,EAAM,mBAAmBC,CAAW,EACpC,MACJ,CACIZ,EAAUY,CAAW,IACrBA,EAAc,MAAMN,EAAmBxF,EAAQ,QAAS8F,CAAW,EACnED,EAAM,mBAAmBC,CAAW,EAE5C,EAEMjF,EAASd,EAAa,CACxB,MAAOC,EAAQ,MACf,UAAU2B,EAAA3B,EAAQ,WAAR,KAAA2B,EAAoB,eAC9B,QAAS,IAAM,CACEkE,EAAM,SACV,QACLA,EAAM,MAAA,EACND,EAAM,QAAA,IAENC,EAAM,KAAA,EACND,EAAM,OAAA,EAEd,CAAA,CACH,EAEKI,EAAQ,IAAI3F,EAAM,CACpB,MAAOL,EAAQ,MACf,MAAOmC,EAASnC,EAAQ,KAAK,EAC7B,UAAU4B,EAAA5B,EAAQ,WAAR,KAAA4B,EAAoB,eAC9B,KAAM5B,EAAQ,KACd,QAAS,IAAM,CACX6F,EAAM,MAAA,EACND,EAAM,QAAA,CACV,EACA,OAAS7E,GAAS,CACd,MAAMJ,EAAUkF,EAAM,WAAW,CAAC,OAAQ,OAAQ,KAAA9E,EAAK,EACvDiF,EAAM,cAAcrF,CAAO,EAC3BoF,EAAA,EACK,KAAK,IAAME,EAAU,KAAKlF,CAAI,CAAC,EAC/B,MAAOwC,GAAU,CACdqC,EAAM,QAAQrC,CAAc,EAC5BsC,EAAM,aAAa,EAAK,EACxBG,EAAM,WAAW,EAAI,CACzB,CAAC,CACT,CAAA,CACH,EAEKC,EAAY,IAAItD,EAClB,CACI,QAAS3C,EAAQ,QACjB,YAAA8F,EACA,IAAK9F,EAAQ,IACb,UAAWA,EAAQ,UACnB,UAAW6F,EAAM,SAAA,EAErB,CACI,OAAOK,EAAM,CACTC,EAAS,UAAYD,EACrBN,EAAM,kBAAkBM,CAAI,EAC5BL,EAAM,aAAa,EAAI,EACvBG,EAAM,WAAW,EAAK,CAC1B,EACA,UAAUrF,EAAS,CACf,MAAMyF,EAASP,EAAM,WAAWlF,CAAO,EACvCqF,EAAM,cAAcI,CAAM,EAC1BR,EAAM,UAAUQ,CAAM,CAC1B,EACA,SAAStF,EAAQ,CACb+E,EAAM,UAAU/E,CAAM,CAC1B,EACA,QAAQyC,EAAO,CACXqC,EAAM,QAAQrC,CAAK,EACnBsC,EAAM,aAAa,EAAK,EACpBA,EAAM,SAAS,QACfG,EAAM,WAAW,EAAI,CAE7B,CAAA,CACJ,EAGJnF,EAAO,MAAA,EACPmF,EAAM,MAAA,EAEN,MAAMK,EAAkBR,EAAM,SAAS,SACvC,GAAIQ,EAAgB,SAAW,GAAKrG,EAAQ,QAAS,CACjD,MAAMsG,EAAmB,CACrB,GAAI,UACJ,OAAQ,MACR,KAAMtG,EAAQ,QACd,GAAI,KAAK,IAAA,CAAI,EAEjB6F,EAAM,WAAWS,CAAO,EACxBN,EAAM,cAAcM,CAAO,CAC/B,MACIN,EAAM,eAAeK,CAAe,EAGxC,MAAME,EAAcV,EAAM,UAAU,IAAM,CACtC,MAAMW,EAAOX,EAAM,SACnBhF,EAAO,QAAQ2F,EAAK,MAAM,EAC1BR,EAAM,UAAUQ,EAAK,QAAQ,EAC7BR,EAAM,WAAW,CAACQ,EAAK,WAAW,EAClCR,EAAM,YAAY7D,EAASnC,EAAQ,KAAK,CAAC,EACrCwG,EAAK,OACLR,EAAM,KAAA,EAENA,EAAM,KAAA,CAEd,CAAC,EAEDD,EAAA,EACK,KAAK,IACFE,EAAU,QAAA,EAAU,KAAMQ,GAAa,OAC/BzG,EAAQ,SACR6F,EAAM,eAAeY,EAAS,SAAS,EAE3CZ,EAAM,aAAa,EAAI,EACvBD,EAAM,mBAAkBnE,EAAAwE,EAAU,OAAV,KAAAxE,EAAkB,IAAI,CAClD,CAAC,CAAA,EAEJ,MAAO8B,GAAU,CACdqC,EAAM,QAAQrC,CAAK,EACnBsC,EAAM,aAAa,EAAK,EACxBG,EAAM,WAAW,EAAI,CACzB,CAAC,EAEL,MAAMG,EAAiC,CACnC,UAAW,KACX,MAAO,CACHN,EAAM,KAAA,EACND,EAAM,OAAA,CACV,EACA,OAAQ,CACJC,EAAM,MAAA,EACND,EAAM,QAAA,CACV,EACA,SAAU,CACNW,EAAA,EACAN,EAAU,KAAA,EACVpF,EAAO,QAAA,EACPmF,EAAM,QAAA,CACV,CAAA,EAGJ,OAAOG,CACX,CAGO,SAASO,GAAiB,SAC7B,GAAI,OAAO,UAAa,YAAa,OACrC,MAAMC,EAAS,SAAS,cACxB,GAAI,CAACA,EAAQ,OACb,MAAMC,EAAUD,EAAO,QACvB,GAAIC,EAAQ,WAAa,QAAS,OAClC,MAAM1C,EAAM0C,EAAQ,IACdzC,EAAYyC,EAAQ,UAC1B,GAAI,CAAC1C,GAAO,CAACC,EAAW,CACpB,QAAQ,MAAM,sEAAsE,EACpF,MACJ,CACAuB,EAAiB,CACb,IAAAxB,EACA,UAAAC,EACA,kBAAmByC,EAAQ,kBAC3B,QAASA,EAAQ,QACjB,UAAWnF,EAAAmF,EAAQ,WAAR,KAAAnF,EAAgD,OAC3D,OAAQC,EAAAkF,EAAQ,QAAR,KAAAlF,EAA0C,MAAA,CACrD,CACL,CAEA,GAAI,OAAO,QAAW,YAAa,CAC/B,MAAMmF,EAAY,OAClBA,EAAU,aAAeA,EAAU,cAAgB,CAAA,EACnDA,EAAU,aAAa,KAAQ7G,GAAyB0F,EAAiB1F,CAAO,CACpF,CAEA0G,EAAA"}
|
|
1
|
+
{"version":3,"file":"widget.umd.js","sources":["../src/ui/bubble.ts","../src/ui/panel.ts","../src/core/utils.ts","../src/core/transport.ts","../src/core/state.ts","../src/core/jwt.ts","../src/index.ts"],"sourcesContent":["import type { PositionOption } from \"../types\";\n\nexport interface BubbleController {\n mount(): void;\n destroy(): void;\n setOpen(open: boolean): 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 }\n button {\n all: unset;\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 </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 </button>\n`;\n\nexport function createBubble(options: BubbleOptions): BubbleController {\n const host = document.createElement(\"div\");\n host.setAttribute(\"part\", \"bubble-root\");\n host.style.setProperty(\"--spilki-accent\", options.color);\n host.style.position = \"fixed\";\n host.style.bottom = \"24px\";\n host.style[options.position === \"bottom-right\" ? \"right\" : \"left\"] = \"24px\";\n\n const shadow = host.attachShadow({ mode: \"open\" });\n shadow.innerHTML = TEMPLATE;\n const button = shadow.querySelector(\"button\")!;\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 }\n };\n return controller;\n}\n","import type { Message, WidgetI18n } from \"../types\";\nimport styles from \"./styles.css?inline\";\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\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 focusable: HTMLElement[] = [];\n private open = false;\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 this.shadow.innerHTML = `\n <style>${styles}</style>\n <div class=\"wrapper\" role=\"dialog\" aria-modal=\"true\" aria-label=\"${options.i18n.title}\">\n <header>\n <h1><span class=\"status-dot\" aria-hidden=\"true\"></span>${options.i18n.title}</h1>\n <button class=\"close\" type=\"button\" aria-label=\"Close\">×</button>\n </header>\n <div class=\"messages\" part=\"messages\"></div>\n <div class=\"typing\" hidden>${options.i18n.typing}</div>\n <div class=\"offline\" hidden>${options.i18n.offline}</div>\n <div class=\"input-area\">\n <textarea rows=\"2\" placeholder=\"${options.i18n.placeholder}\" aria-label=\"${options.i18n.placeholder}\"></textarea>\n <button type=\"button\">${options.i18n.sendLabel}</button>\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(\".input-area button\") as HTMLButtonElement;\n\n const closeButton = this.shadow.querySelector(\"header .close\") as HTMLButtonElement;\n closeButton.addEventListener(\"click\", () => this.options.onClose());\n this.sendButton.addEventListener(\"click\", () => this.send());\n\n this.input.addEventListener(\"keydown\", (event: KeyboardEvent) => {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n this.send();\n } else if (event.key === \"Escape\") {\n this.options.onClose();\n }\n });\n\n this.shadow.addEventListener(\"keydown\", (event) => {\n const keyboard = event as KeyboardEvent;\n if (keyboard.key === \"Escape\") {\n this.options.onClose();\n }\n if (keyboard.key === \"Tab\") {\n this.trapFocus(keyboard);\n }\n });\n\n this.shadow.addEventListener(\"focusin\", () => this.collectFocusable());\n this.collectFocusable();\n }\n\n mount(): void {\n document.body.appendChild(this.host);\n }\n\n destroy(): void {\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.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 updateMessages(messages: Message[]): void {\n this.messagesEl.innerHTML = \"\";\n messages.forEach((message) => {\n const item = document.createElement(\"div\");\n item.className = `message ${message.author}`;\n item.setAttribute(\"data-author\", message.author);\n const bubble = document.createElement(\"div\");\n bubble.className = \"bubble\";\n bubble.textContent = message.text;\n item.appendChild(bubble);\n this.messagesEl.appendChild(item);\n });\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight;\n }\n\n appendMessage(message: Message): void {\n const item = document.createElement(\"div\");\n item.className = `message ${message.author}`;\n item.setAttribute(\"data-author\", message.author);\n const bubble = document.createElement(\"div\");\n bubble.className = \"bubble\";\n bubble.textContent = message.text;\n item.appendChild(bubble);\n this.messagesEl.appendChild(item);\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight;\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 }\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\")) {\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 {InitOptions, WidgetI18n} from \"../types\";\n\nexport const DEFAULT_API_BASE = \"https://api.spilki.ai\";\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 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\">\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 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 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 } 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","import type {ConnectResponse, Message, SendPayload, TransportKind} 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 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 | 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 sse?: EventSource;\n private pollTimer?: number;\n private stopped = false;\n private backoff = RETRY_BASE;\n private readonly handlers: TransportHandlers;\n private readonly options: TransportOptions;\n\n constructor(options: TransportOptions, handlers: TransportHandlers) {\n this.options = options;\n this.handlers = handlers;\n }\n\n setAccessToken(token?: string) {\n this.options.accessToken = token;\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 async connect(): 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 };\n\n const res = await fetch(connectUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(this.options.accessToken ? {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): Promise<void> {\n const payload: SendPayload = {\n sessionId: this.sessionId ?? this.options.sessionId ?? \"\",\n text\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 ...(this.options.accessToken ? {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 stop(): void {\n this.stopped = true;\n this.backoff = RETRY_BASE;\n if (this.ws) {\n this.ws.close();\n this.ws = undefined;\n }\n if (this.sse) {\n this.sse.close();\n this.sse = undefined;\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 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 ws.addEventListener(\"open\", () => {\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 ws.addEventListener(\"message\", (event) => this.handleIncoming(event.data));\n ws.addEventListener(\"close\", () => {\n if (this.stopped) return;\n this.retryFallback(\"ws\");\n });\n ws.addEventListener(\"error\", () => {\n this.handlers.onError(new Error(\"SpilkiWidget: websocket error\"));\n if (ws.readyState !== WebSocket.OPEN) {\n reject(new Error(\"WebSocket failed\"));\n }\n });\n } catch (error) {\n reject(error as Error);\n }\n });\n }\n\n private startSse(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (typeof EventSource === \"undefined\") {\n reject(new Error(\"SSE not supported\"));\n return;\n }\n const sse = new EventSource(url);\n this.sse = sse;\n let opened = false;\n sse.addEventListener(\"open\", () => {\n opened = true;\n if (this.stopped) {\n sse.close();\n return;\n }\n this.currentKind = \"sse\";\n this.handlers.onOpen(\"sse\");\n this.backoff = RETRY_BASE;\n resolve();\n });\n sse.addEventListener(\"message\", (event) => this.handleIncoming(event.data));\n sse.addEventListener(\"error\", () => {\n if (!opened) {\n reject(new Error(\"SSE failed\"));\n return;\n }\n this.handlers.onError(new Error(\"SpilkiWidget: SSE error\"));\n if (this.stopped) return;\n this.retryFallback(\"sse\");\n });\n });\n }\n\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 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 private retryFallback(failed: TransportKind): void {\n if (this.stopped) return;\n this.stop();\n this.backoff = Math.min(this.backoff * 1.5, RETRY_MAX);\n setTimeout(() => {\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 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 const message: Message = {\n id: `${Date.now()}`,\n author: \"bot\",\n text: raw,\n ts: Date.now()\n };\n this.handlers.onMessage(message);\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 }\n return;\n }\n this.handlers.onMessage(data as Message);\n }\n}\n","import {clampMessages, createId} from \"./utils\";\nimport type {Message} from \"../types\";\n\ntype Listener = () => void;\n\nexport interface WidgetStateSnapshot {\n isOpen: boolean;\n isTyping: boolean;\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 readonly persist: boolean;\n private state: WidgetStateSnapshot = {\n isOpen: false,\n isTyping: false,\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.persist = options.persist;\n this.state.messages = this.loadMessages();\n }\n\n get snapshot(): WidgetStateSnapshot {\n return {...this.state, messages: [...this.state.messages]};\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 setTyping(value: boolean): void {\n if (this.state.isTyping !== value) {\n this.state.isTyping = value;\n this.emit();\n }\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 {\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 this.state.messages = clampMessages([...this.state.messages, full], HISTORY_LIMIT);\n this.persistMessages();\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 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\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\nexport function isExpired(token: string): boolean {\n const payload = parseJwt(token);\n if (!payload?.exp) return false;\n const now = Math.floor(Date.now() / 1000);\n return payload.exp < now;\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, TransportKind, WidgetAccessTokenResponse, WidgetHooks} from \"./types\";\n\nexport interface SpilkiWidgetInstance {\n open(): void;\n\n close(): 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: {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\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\n let accessToken = state.accessToken ?? undefined;\n\n const ensureAccessToken = async () => {\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 return;\n }\n\n if (isExpired(accessToken)) {\n accessToken = await refreshAccessToken(options.apiBase, accessToken);\n state.persistAccessToken(accessToken);\n transport.setAccessToken(accessToken);\n }\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 state.open();\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 panel.appendMessage(message);\n ensureAccessToken()\n .then(() => transport.send(text))\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 stored = state.addMessage(message);\n panel.appendMessage(stored);\n hooks.onMessage(stored);\n },\n onTyping(active) {\n state.setTyping(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 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 {\n panel.updateMessages(initialMessages);\n }\n\n const unsubscribe = state.subscribe(() => {\n const snap = state.snapshot;\n bubble.setOpen(snap.isOpen);\n panel.setTyping(snap.isTyping);\n panel.setOffline(!snap.isConnected);\n panel.updateTheme(getTheme(options.theme));\n if (snap.isOpen) {\n panel.show();\n } else {\n panel.hide();\n }\n });\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 )\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 state.open();\n hooks.onOpen();\n },\n close() {\n state.close();\n hooks.onClose();\n },\n destroy() {\n unsubscribe();\n transport.stop();\n bubble.destroy();\n panel.destroy();\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 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\nif (typeof window !== \"undefined\") {\n const globalAny = window as any;\n globalAny.SpilkiWidget = globalAny.SpilkiWidget || {};\n globalAny.SpilkiWidget.init = (options: InitOptions) => initSpilkiWidget(options);\n}\n\nautoInit();\n\nexport type {InitOptions, Message} from \"./types\";\n"],"names":["TEMPLATE","createBubble","options","host","shadow","button","open","Panel","styles","event","keyboard","theme","messages","message","item","bubble","active","text","items","el","first","last","DEFAULT_API_BASE","DEFAULT_I18N","DEFAULT_OPTIONS","mergeOptions","mergedI18n","_a","_b","_c","_d","_e","_f","_g","createId","prefix","prefersDark","getTheme","clampMessages","limit","warnAllowedOrigins","hint","origin","RETRY_BASE","RETRY_MAX","TransportManager","handlers","token","connectUrl","currentSession","body","res","data","payload","url","connect","attempts","attempt","error","resolve","reject","ws","sse","opened","poll","failed","raw","HISTORY_LIMIT","WidgetState","org","listener","value","full","sessionId","parsed","decodePart","part","normalized","padded","c","buffer","parseJwt","parts","isExpired","now","DEFAULT_HOOKS","installAndGetAccessToken","apiBase","installationToken","organisationId","refreshAccessToken","oldToken","initSpilkiWidget","rawOptions","hooks","state","accessToken","ensureAccessToken","transport","panel","kind","instance","stored","initialMessages","welcome","unsubscribe","snap","response","autoInit","script","dataset","globalAny"],"mappings":"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"}
|