@spilki/widget 0.1.2 → 0.1.4
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 +1 -1
- package/dist/bootstrap.es.js +12 -9
- 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/transport.d.ts +1 -0
- package/dist/core/transport.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/widget.es.js +12 -9
- 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/README.md
CHANGED
|
@@ -100,4 +100,4 @@ relayed via websocket by default; stop the websocket server to see SSE fallback.
|
|
|
100
100
|
## Publishing
|
|
101
101
|
|
|
102
102
|
The GitHub Actions workflow publishes on tags that match `v*`. Ensure `NPM_TOKEN` is configured in the repository
|
|
103
|
-
secrets before tagging `v0.1.
|
|
103
|
+
secrets before tagging `v0.1.4` or later.
|
package/dist/bootstrap.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const A = `
|
|
2
2
|
<style>
|
|
3
3
|
:host {
|
|
4
4
|
all: initial;
|
|
@@ -44,11 +44,11 @@ const M = `
|
|
|
44
44
|
</span>
|
|
45
45
|
</button>
|
|
46
46
|
`;
|
|
47
|
-
function
|
|
47
|
+
function M(r) {
|
|
48
48
|
const e = document.createElement("div");
|
|
49
49
|
e.setAttribute("part", "bubble-root"), e.style.setProperty("--spilki-accent", r.color), e.style.position = "fixed", e.style.bottom = "24px", e.style[r.position === "bottom-right" ? "right" : "left"] = "24px";
|
|
50
50
|
const t = e.attachShadow({ mode: "open" });
|
|
51
|
-
t.innerHTML =
|
|
51
|
+
t.innerHTML = A;
|
|
52
52
|
const s = t.querySelector("button");
|
|
53
53
|
return s.addEventListener("click", () => r.onClick()), {
|
|
54
54
|
element: e,
|
|
@@ -205,6 +205,9 @@ class U {
|
|
|
205
205
|
constructor(e, t) {
|
|
206
206
|
this.sessionId = null, this.currentKind = null, this.stopped = !1, this.backoff = d, this.options = e, this.handlers = t;
|
|
207
207
|
}
|
|
208
|
+
setAccessToken(e) {
|
|
209
|
+
this.options.accessToken = e;
|
|
210
|
+
}
|
|
208
211
|
get kind() {
|
|
209
212
|
return this.currentKind;
|
|
210
213
|
}
|
|
@@ -225,7 +228,7 @@ class U {
|
|
|
225
228
|
method: "POST",
|
|
226
229
|
headers: {
|
|
227
230
|
"Content-Type": "application/json",
|
|
228
|
-
...this.options.accessToken ? { Authorization: `Bearer ${this.options.accessToken}` } : {}
|
|
231
|
+
...this.options.accessToken ? { "X-Authorization": `Bearer ${this.options.accessToken}` } : {}
|
|
229
232
|
},
|
|
230
233
|
body: JSON.stringify(s)
|
|
231
234
|
});
|
|
@@ -250,7 +253,7 @@ class U {
|
|
|
250
253
|
method: "POST",
|
|
251
254
|
headers: {
|
|
252
255
|
"Content-Type": "application/json",
|
|
253
|
-
...this.options.accessToken ? { Authorization: `Bearer ${this.options.accessToken}` } : {}
|
|
256
|
+
...this.options.accessToken ? { "X-Authorization": `Bearer ${this.options.accessToken}` } : {}
|
|
254
257
|
},
|
|
255
258
|
body: JSON.stringify(t)
|
|
256
259
|
});
|
|
@@ -533,7 +536,7 @@ async function q(r, e, t) {
|
|
|
533
536
|
async function J(r, e) {
|
|
534
537
|
const t = `${r.replace(/\/$/, "")}/widget/refresh`, s = await fetch(t, {
|
|
535
538
|
method: "POST",
|
|
536
|
-
headers: { Authorization: `Bearer ${e}`, Origin: window.location.origin }
|
|
539
|
+
headers: { "X-Authorization": `Bearer ${e}`, Origin: window.location.origin }
|
|
537
540
|
});
|
|
538
541
|
if (!s.ok) throw new Error(`SpilkiWidget: refresh failed (${s.status})`);
|
|
539
542
|
return (await s.json()).accessToken;
|
|
@@ -550,11 +553,11 @@ function E(r) {
|
|
|
550
553
|
if (!i) {
|
|
551
554
|
if (!r.installationToken) throw new Error("SpilkiWidget: missing installationToken");
|
|
552
555
|
if (!r.org) throw new Error("SpilkiWidget: missing org");
|
|
553
|
-
i = await q(e.apiBase, r.installationToken, r.org), s.persistAccessToken(i);
|
|
556
|
+
i = await q(e.apiBase, r.installationToken, r.org), s.persistAccessToken(i), h.setAccessToken(i);
|
|
554
557
|
return;
|
|
555
558
|
}
|
|
556
|
-
z(i) && (i = await J(e.apiBase, i), s.persistAccessToken(i));
|
|
557
|
-
}, l =
|
|
559
|
+
z(i) && (i = await J(e.apiBase, i), s.persistAccessToken(i), h.setAccessToken(i));
|
|
560
|
+
}, l = M({
|
|
558
561
|
color: e.color,
|
|
559
562
|
position: (y = e.position) != null ? y : "bottom-right",
|
|
560
563
|
onClick: () => {
|
package/dist/bootstrap.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.es.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","../src/bootstrap.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 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 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 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","import { autoInit } from \"./index\";\n\nautoInit();\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","_a","_b","_c","_d","_e","_f","_g","mergedI18n","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","listener","value","full","sessionId","token","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","panel","transport","kind","instance","stored","initialMessages","welcome","unsubscribe","snap","response","autoInit","script","dataset","globalAny"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bootstrap.es.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","../src/bootstrap.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 ? {\"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): 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 ? {\"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 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: {\"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\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","import { autoInit } from \"./index\";\n\nautoInit();\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","_a","_b","_c","_d","_e","_f","_g","mergedI18n","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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"}
|
package/dist/bootstrap.umd.js
CHANGED
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
</svg>
|
|
44
44
|
</span>
|
|
45
45
|
</button>
|
|
46
|
-
`;function O(r){const e=document.createElement("div");e.setAttribute("part","bubble-root"),e.style.setProperty("--spilki-accent",r.color),e.style.position="fixed",e.style.bottom="24px",e.style[r.position==="bottom-right"?"right":"left"]="24px";const t=e.attachShadow({mode:"open"});t.innerHTML=u;const s=t.querySelector("button");return s.addEventListener("click",()=>r.onClick()),{element:e,mount(){document.body.appendChild(e)},destroy(){e.remove()},setOpen(n){s.setAttribute("aria-expanded",String(n))}}}const
|
|
47
|
-
<style>${
|
|
46
|
+
`;function O(r){const e=document.createElement("div");e.setAttribute("part","bubble-root"),e.style.setProperty("--spilki-accent",r.color),e.style.position="fixed",e.style.bottom="24px",e.style[r.position==="bottom-right"?"right":"left"]="24px";const t=e.attachShadow({mode:"open"});t.innerHTML=u;const s=t.querySelector("button");return s.addEventListener("click",()=>r.onClick()),{element:e,mount(){document.body.appendChild(e)},destroy(){e.remove()},setOpen(n){s.setAttribute("aria-expanded",String(n))}}}const A=':host{--spilki-bg-light: #ffffff;--spilki-bg-dark: #0f172a;--spilki-text-light: #0f172a;--spilki-text-dark: #f8fafc;--spilki-border-light: rgba(15, 23, 42, .1);--spilki-border-dark: rgba(148, 163, 184, .25);--spilki-shadow: 0 10px 40px rgba(15, 23, 42, .2);--spilki-radius: 16px;--spilki-font: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;color:inherit;font-family:var(--spilki-font)}.wrapper{width:100%;height:100%;display:flex;flex-direction:column;background:var(--spilki-surface);color:var(--spilki-text);border-radius:var(--spilki-radius);box-shadow:var(--spilki-shadow);border:1px solid var(--spilki-border);overflow:hidden}header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;background:var(--spilki-surface);border-bottom:1px solid var(--spilki-border)}header h1{font-size:1rem;margin:0;display:flex;align-items:center;gap:.5rem}header button.close{border:none;background:transparent;color:inherit;font-size:1.25rem;cursor:pointer}header .status-dot{width:10px;height:10px;border-radius:999px;background:var(--spilki-accent)}.messages{flex:1;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:.5rem}.message{display:flex;flex-direction:column;gap:.25rem;max-width:85%;line-height:1.4;word-wrap:break-word;overflow-wrap:anywhere;white-space:pre-wrap}.message.user{align-self:flex-end;text-align:right}.message .bubble{padding:.6rem .8rem;border-radius:1rem;background:#6366f126}.message.user .bubble{background:var(--spilki-accent);color:#fff}.message.bot .bubble{background:#94a3b826}.input-area{display:flex;align-items:flex-end;gap:.5rem;padding:.75rem 1rem;border-top:1px solid var(--spilki-border)}.input-area textarea{flex:1;resize:none;min-height:2.5rem;max-height:6rem;border-radius:.75rem;border:1px solid var(--spilki-border);padding:.6rem .75rem;font-family:inherit;font-size:.95rem;background:var(--spilki-surface);color:var(--spilki-text)}.input-area button{border:none;border-radius:999px;padding:.6rem 1.1rem;background:var(--spilki-accent);color:#fff;font-weight:600;cursor:pointer}.typing{font-size:.75rem;color:#94a3b8e6;padding:0 1rem .75rem}.offline{font-size:.8rem;padding:0 1rem;color:#f97316}:host([data-theme="dark"]){--spilki-surface: var(--spilki-bg-dark);--spilki-text: var(--spilki-text-dark);--spilki-border: var(--spilki-border-dark)}:host([data-theme="light"]){--spilki-surface: var(--spilki-bg-light);--spilki-text: var(--spilki-text-light);--spilki-border: var(--spilki-border-light)}:host([data-theme="dark"]) .message .bubble{background:#94a3b81f}:host([data-theme="dark"]) .message.bot .bubble{background:#6366f126}:host([data-theme="dark"]) .input-area textarea{background:#0f172ad9}.messages::-webkit-scrollbar,.input-area textarea::-webkit-scrollbar{width:6px}.messages::-webkit-scrollbar-track,.input-area textarea::-webkit-scrollbar-track{background:transparent}.messages::-webkit-scrollbar-thumb,.input-area textarea::-webkit-scrollbar-thumb{background:#94a3b84d;border-radius:999px}.messages::-webkit-scrollbar-thumb:hover,.input-area textarea::-webkit-scrollbar-thumb:hover{background:#94a3b880}:host([data-theme="dark"]) .messages::-webkit-scrollbar-thumb,:host([data-theme="dark"]) .input-area textarea::-webkit-scrollbar-thumb{background:#fff3}:host([data-theme="dark"]) .messages::-webkit-scrollbar-thumb:hover,:host([data-theme="dark"]) .input-area textarea::-webkit-scrollbar-thumb:hover{background:#ffffff59}.messages{scroll-behavior:smooth}';class M{constructor(e){this.options=e,this.focusable=[],this.open=!1,this.host=document.createElement("div"),this.host.setAttribute("part","panel-root"),this.host.style.position="fixed",this.host.style.bottom="96px",this.host.style[e.position==="bottom-right"?"right":"left"]="24px",this.host.style.width="360px",this.host.style.maxWidth="calc(100vw - 32px)",this.host.style.height="520px",this.host.style.display="none",this.host.style.zIndex="2147483001",this.shadow=this.host.attachShadow({mode:"open"}),this.shadow.innerHTML=`
|
|
47
|
+
<style>${A}</style>
|
|
48
48
|
<div class="wrapper" role="dialog" aria-modal="true" aria-label="${e.i18n.title}">
|
|
49
49
|
<header>
|
|
50
50
|
<h1><span class="status-dot" aria-hidden="true"></span>${e.i18n.title}</h1>
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
<button type="button">${e.i18n.sendLabel}</button>
|
|
59
59
|
</div>
|
|
60
60
|
</div>
|
|
61
|
-
`,this.host.dataset.theme=e.theme,this.host.style.setProperty("--spilki-accent",e.color),this.messagesEl=this.shadow.querySelector(".messages"),this.typingEl=this.shadow.querySelector(".typing"),this.input=this.shadow.querySelector("textarea"),this.offlineEl=this.shadow.querySelector(".offline"),this.sendButton=this.shadow.querySelector(".input-area button"),this.shadow.querySelector("header .close").addEventListener("click",()=>this.options.onClose()),this.sendButton.addEventListener("click",()=>this.send()),this.input.addEventListener("keydown",s=>{s.key==="Enter"&&!s.shiftKey?(s.preventDefault(),this.send()):s.key==="Escape"&&this.options.onClose()}),this.shadow.addEventListener("keydown",s=>{const i=s;i.key==="Escape"&&this.options.onClose(),i.key==="Tab"&&this.trapFocus(i)}),this.shadow.addEventListener("focusin",()=>this.collectFocusable()),this.collectFocusable()}mount(){document.body.appendChild(this.host)}destroy(){this.host.remove()}show(){this.open||(this.open=!0,this.host.style.display="block",this.focusInput())}hide(){this.open&&(this.open=!1,this.host.style.display="none")}focusInput(){queueMicrotask(()=>{this.input.focus()})}updateTheme(e){this.host.dataset.theme=e}updateMessages(e){this.messagesEl.innerHTML="",e.forEach(t=>{const s=document.createElement("div");s.className=`message ${t.author}`,s.setAttribute("data-author",t.author);const i=document.createElement("div");i.className="bubble",i.textContent=t.text,s.appendChild(i),this.messagesEl.appendChild(s)}),this.messagesEl.scrollTop=this.messagesEl.scrollHeight}appendMessage(e){const t=document.createElement("div");t.className=`message ${e.author}`,t.setAttribute("data-author",e.author);const s=document.createElement("div");s.className="bubble",s.textContent=e.text,t.appendChild(s),this.messagesEl.appendChild(t),this.messagesEl.scrollTop=this.messagesEl.scrollHeight}setTyping(e){this.typingEl.toggleAttribute("hidden",!e)}setOffline(e){this.offlineEl.toggleAttribute("hidden",!e)}clearInput(){this.input.value="",this.input.dispatchEvent(new Event("input"))}send(){const e=this.input.value.trim();e&&(this.options.onSend(e),this.clearInput())}collectFocusable(){const e=this.shadow.querySelectorAll('button, textarea, [href], [tabindex]:not([tabindex="-1"])');this.focusable.length=0,e.forEach(t=>{t.hasAttribute("disabled")||this.focusable.push(t)})}trapFocus(e){if(this.focusable.length===0)return;const t=this.focusable[0],s=this.focusable[this.focusable.length-1],i=this.shadow.activeElement;e.shiftKey&&i===t?(e.preventDefault(),s.focus()):!e.shiftKey&&i===s&&(e.preventDefault(),t.focus())}}const C="https://api.spilki.ai",g={welcome:"Hi! I'm your assistant.",placeholder:"Type a message…",sendLabel:"Send",typing:"Assistant is typing…",offline:"Unable to connect. Please try again later.",title:"Spilki Assistant"},p={apiBase:C,position:"bottom-right",theme:"auto",color:"#6366f1",welcome:g.welcome,persist:!0,i18n:g};function W(r){var t,s,i,n,l,a,c;const e={...g,...(t=r.i18n)!=null?t:{}};return{...p,...r,apiBase:(s=r.apiBase)!=null?s:p.apiBase,i18n:e,welcome:(i=r.welcome)!=null?i:e.welcome,position:(n=r.position)!=null?n:p.position,theme:(l=r.theme)!=null?l:p.theme,color:(a=r.color)!=null?a:p.color,persist:(c=r.persist)!=null?c:p.persist}}function $(r="msg"){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():`${r}-${Math.random().toString(16).slice(2)}`}function B(){var r,e;return(e=(r=window.matchMedia)==null?void 0:r.call(window,"(prefers-color-scheme: dark)").matches)!=null?e:!1}function b(r){return r==="light"||r==="dark"?r:B()?"dark":"light"}function f(r,e=30){return r.slice(-e)}function L(r){if(!r||r.length===0)return;const e=window.location.origin;r.includes(e)||console.warn(`SpilkiWidget: current origin ${e} not in allowedOriginsHint: ${r.join(", ")}`)}const h=1500,k=8e3;class K{constructor(e,t){this.sessionId=null,this.currentKind=null,this.stopped=!1,this.backoff=h,this.options=e,this.handlers=t}get kind(){return this.currentKind}get activeSession(){var e,t;return(t=(e=this.sessionId)!=null?e:this.options.sessionId)!=null?t:null}async connect(){var l,a;this.stopped=!1;const e=`${this.options.apiBase.replace(/\/$/,"")}/widget/session`,t=(a=(l=this.sessionId)!=null?l:this.options.sessionId)!=null?a:void 0,s={organisationId:this.options.org,sessionId:t,userAgent:typeof navigator!="undefined"?navigator.userAgent:"",referrer:typeof document!="undefined"?document.referrer:"",origin:typeof window!="undefined"?window.location.origin:""},i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.options.accessToken?{Authorization:`Bearer ${this.options.accessToken}`}:{}},body:JSON.stringify(s)});if(!i.ok)throw new Error(`SpilkiWidget: connect failed (${i.status})`);const n=await i.json();return this.sessionId=n.sessionId,this.options.sessionId=n.sessionId,this.backoff=h,await this.startTransport(n),n}async send(e){var n,l;const t={sessionId:(l=(n=this.sessionId)!=null?n:this.options.sessionId)!=null?l:"",text:e};if(!t.sessionId)throw new Error("SpilkiWidget: missing session id");if(this.currentKind==="ws"&&this.ws&&this.ws.readyState===WebSocket.OPEN){this.ws.send(JSON.stringify({type:"message",payload:t}));return}const s=`${this.options.apiBase.replace(/\/$/,"")}/widget/message`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",...this.options.accessToken?{Authorization:`Bearer ${this.options.accessToken}`}:{}},body:JSON.stringify(t)});if(!i.ok)throw new Error(`SpilkiWidget: send failed (${i.status})`)}stop(){this.stopped=!0,this.backoff=h,this.ws&&(this.ws.close(),this.ws=void 0),this.sse&&(this.sse.close(),this.sse=void 0),this.pollTimer&&(clearTimeout(this.pollTimer),this.pollTimer=void 0),this.currentKind=null}async startTransport(e){if(this.stopped)return;const t=[];e.wsUrl&&t.push(()=>this.startWs(e.wsUrl)),e.sseUrl&&t.push(()=>this.startSse(e.sseUrl)),e.pollUrl&&t.push(()=>this.startPoll(e.pollUrl));for(const s of t)try{await s();return}catch(i){this.handlers.onError(i)}throw new Error("SpilkiWidget: unable to establish transport")}startWs(e){return new Promise((t,s)=>{try{const i=new WebSocket(e);this.ws=i,i.addEventListener("open",()=>{if(this.stopped){i.close();return}this.currentKind="ws",this.handlers.onOpen("ws"),this.backoff=h,t()}),i.addEventListener("message",n=>this.handleIncoming(n.data)),i.addEventListener("close",()=>{this.stopped||this.retryFallback("ws")}),i.addEventListener("error",()=>{this.handlers.onError(new Error("SpilkiWidget: websocket error")),i.readyState!==WebSocket.OPEN&&s(new Error("WebSocket failed"))})}catch(i){s(i)}})}startSse(e){return new Promise((t,s)=>{if(typeof EventSource=="undefined"){s(new Error("SSE not supported"));return}const i=new EventSource(e);this.sse=i;let n=!1;i.addEventListener("open",()=>{if(n=!0,this.stopped){i.close();return}this.currentKind="sse",this.handlers.onOpen("sse"),this.backoff=h,t()}),i.addEventListener("message",l=>this.handleIncoming(l.data)),i.addEventListener("error",()=>{if(!n){s(new Error("SSE failed"));return}this.handlers.onError(new Error("SpilkiWidget: SSE error")),!this.stopped&&this.retryFallback("sse")})})}async startPoll(e){this.currentKind="poll",this.handlers.onOpen("poll"),this.backoff=h;const t=async()=>{if(!this.stopped)try{const s=await fetch(e);if(!s.ok)throw new Error(`Poll failed ${s.status}`);const i=await s.json();f(i).forEach(n=>this.handlers.onMessage(n)),this.backoff=h}catch(s){this.handlers.onError(s),this.backoff=Math.min(this.backoff*1.5,k)}finally{this.stopped||(this.pollTimer=window.setTimeout(t,this.backoff))}};await t()}retryFallback(e){this.stopped||(this.stop(),this.backoff=Math.min(this.backoff*1.5,k),setTimeout(()=>{this.handlers.onError(new Error(`SpilkiWidget: retrying after ${e}`)),this.connect().catch(t=>this.handlers.onError(t))},this.backoff))}handleIncoming(e){try{const t=JSON.parse(e);if(Array.isArray(t)){t.forEach(s=>this.dispatchIncoming(s));return}this.dispatchIncoming(t)}catch{const t={id:`${Date.now()}`,author:"bot",text:e,ts:Date.now()};this.handlers.onMessage(t)}}dispatchIncoming(e){var t;if("type"in e){e.type==="message"?this.handlers.onMessage(e.payload):e.type==="typing"&&this.handlers.onTyping(!!((t=e.payload)!=null&&t.active));return}this.handlers.onMessage(e)}}const m=30;class P{constructor(e,t){this.org=e,this.listeners=new Set,this.state={isOpen:!1,isTyping:!1,isConnected:!1,messages:[]},this.historyKey=`spilki-history:${e}`,this.sessionKey=`spilki-session:${e}`,this.tokenKey=`spilki-token:${e}`,this.persist=t.persist,this.state.messages=this.loadMessages()}get snapshot(){return{...this.state,messages:[...this.state.messages]}}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}open(){this.state.isOpen||(this.state.isOpen=!0,this.emit())}close(){this.state.isOpen&&(this.state.isOpen=!1,this.emit())}setTyping(e){this.state.isTyping!==e&&(this.state.isTyping=e,this.emit())}setConnected(e){this.state.isConnected!==e&&(this.state.isConnected=e,this.emit())}addMessage(e){var s,i;const t={id:(s=e.id)!=null?s:$("msg"),ts:(i=e.ts)!=null?i:Date.now(),author:e.author,text:e.text};return this.state.messages=f([...this.state.messages,t],m),this.persistMessages(),this.emit(),t}setMessages(e){this.state.messages=f(e,m),this.persistMessages(),this.emit()}clearMessages(){this.state.messages=[],this.persistMessages(),this.emit()}get sessionId(){if(!this.persist)return null;try{return localStorage.getItem(this.sessionKey)}catch(e){return console.error("SpilkiWidget: unable to get item",e),null}}persistSession(e){if(this.persist)try{localStorage.setItem(this.sessionKey,e)}catch(t){console.error("SpilkiWidget: unable to set item",t)}}clearSession(){if(this.persist)try{localStorage.removeItem(this.sessionKey)}catch(e){console.error("SpilkiWidget: unable to remove item",e)}}get accessToken(){if(!this.persist)return null;try{return localStorage.getItem(this.tokenKey)}catch(e){return console.error("SpilkiWidget: unable to get item",e),null}}persistAccessToken(e){if(this.persist)try{localStorage.setItem(this.tokenKey,e)}catch(t){console.error("SpilkiWidget: unable to set item",t)}}clearAccessToken(){if(this.persist)try{localStorage.removeItem(this.tokenKey)}catch(e){console.error("SpilkiWidget: unable to remove item",e)}}emit(){this.listeners.forEach(e=>e())}persistMessages(){if(this.persist)try{localStorage.setItem(this.historyKey,JSON.stringify(this.state.messages))}catch(e){console.error("SpilkiWidget: unable to set item",e)}}loadMessages(){if(!this.persist)return[];try{const e=localStorage.getItem(this.historyKey);if(!e)return[];const t=JSON.parse(e);return Array.isArray(t)?f(t,m):[]}catch(e){return console.error("SpilkiWidget: unable to load messages",e),[]}}}function U(r){const e=r.replace(/-/g,"+").replace(/_/g,"/"),t=e.padEnd(e.length+(4-e.length%4)%4,"=");if(typeof atob=="function")return decodeURIComponent(Array.prototype.map.call(atob(t),i=>`%${`00${i.charCodeAt(0).toString(16)}`.slice(-2)}`).join(""));const s=globalThis.Buffer;if(s)return s.from(t,"base64").toString("utf8");throw new Error("SpilkiWidget: no base64 decoder available")}function N(r){if(!r)return null;const e=r.split(".");if(e.length<2)return null;try{const t=U(e[1]);return JSON.parse(t)}catch(t){return console.error("SpilkiWidget: unable to parse JWT",t),null}}function D(r){const e=N(r);if(!(e!=null&&e.exp))return!1;const t=Math.floor(Date.now()/1e3);return e.exp<t}const F={onOpen(){},onClose(){},onMessage(){},onError(){},onTransportChange(){}};async function z(r,e,t){const s=`${r.replace(/\/$/,"")}/widget/install`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",Origin:window.location.origin},body:JSON.stringify({token:e,organisationId:t})});if(!i.ok)throw new Error(`SpilkiWidget: install failed (${i.status})`);return(await i.json()).accessToken}async function H(r,e){const t=`${r.replace(/\/$/,"")}/widget/refresh`,s=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${e}`,Origin:window.location.origin}});if(!s.ok)throw new Error(`SpilkiWidget: refresh failed (${s.status})`);return(await s.json()).accessToken}function w(r){var v,E,T,I;if(!r.org)throw new Error("SpilkiWidget: org is required");const e=W(r);L(e.allowedOriginsHint);const t={...F,...(v=e.hooks)!=null?v:{}},s=new P(e.org,{persist:e.persist});let i=(E=s.accessToken)!=null?E:void 0;const n=async()=>{if(!i){if(!r.installationToken)throw new Error("SpilkiWidget: missing installationToken");if(!r.org)throw new Error("SpilkiWidget: missing org");i=await z(e.apiBase,r.installationToken,r.org),s.persistAccessToken(i);return}D(i)&&(i=await H(e.apiBase,i),s.persistAccessToken(i))},l=O({color:e.color,position:(T=e.position)!=null?T:"bottom-right",onClick:()=>{s.snapshot.isOpen?(s.close(),t.onClose()):(s.open(),t.onOpen())}}),a=new A({color:e.color,theme:b(e.theme),position:(I=e.position)!=null?I:"bottom-right",i18n:e.i18n,onClose:()=>{s.close(),t.onClose()},onSend:o=>{const d=s.addMessage({author:"user",text:o});a.appendMessage(d),n().then(()=>c.send(o)).catch(J=>{t.onError(J),s.setConnected(!1),a.setOffline(!0)})}}),c=new K({apiBase:e.apiBase,accessToken:i,org:e.org,sessionId:s.sessionId},{onOpen(o){x.transport=o,t.onTransportChange(o),s.setConnected(!0),a.setOffline(!1)},onMessage(o){const d=s.addMessage(o);a.appendMessage(d),t.onMessage(d)},onTyping(o){s.setTyping(o)},onError(o){t.onError(o),s.setConnected(!1),s.snapshot.isOpen&&a.setOffline(!0)}});l.mount(),a.mount();const S=s.snapshot.messages;if(S.length===0&&e.welcome){const o={id:"welcome",author:"bot",text:e.welcome,ts:Date.now()};s.addMessage(o),a.appendMessage(o)}else a.updateMessages(S);const q=s.subscribe(()=>{const o=s.snapshot;l.setOpen(o.isOpen),a.setTyping(o.isTyping),a.setOffline(!o.isConnected),a.updateTheme(b(e.theme)),o.isOpen?a.show():a.hide()});n().then(()=>c.connect().then(o=>{var d;e.persist&&s.persistSession(o.sessionId),s.setConnected(!0),t.onTransportChange((d=c.kind)!=null?d:"ws")})).catch(o=>{t.onError(o),s.setConnected(!1),a.setOffline(!0)});const x={transport:null,open(){s.open(),t.onOpen()},close(){s.close(),t.onClose()},destroy(){q(),c.stop(),l.destroy(),a.destroy()}};return x}function y(){var s,i;if(typeof document=="undefined")return;const r=document.currentScript;if(!r)return;const e=r.dataset;if(e.autoinit==="false")return;const t=e.org;if(!t){console.error("SpilkiWidget: data-org and is required for auto init");return}w({org:t,installationToken:e.installationToken,apiBase:e.apiBase,position:(s=e.position)!=null?s:void 0,theme:(i=e.theme)!=null?i:void 0})}if(typeof window!="undefined"){const r=window;r.SpilkiWidget=r.SpilkiWidget||{},r.SpilkiWidget.init=e=>w(e)}y(),y()});
|
|
61
|
+
`,this.host.dataset.theme=e.theme,this.host.style.setProperty("--spilki-accent",e.color),this.messagesEl=this.shadow.querySelector(".messages"),this.typingEl=this.shadow.querySelector(".typing"),this.input=this.shadow.querySelector("textarea"),this.offlineEl=this.shadow.querySelector(".offline"),this.sendButton=this.shadow.querySelector(".input-area button"),this.shadow.querySelector("header .close").addEventListener("click",()=>this.options.onClose()),this.sendButton.addEventListener("click",()=>this.send()),this.input.addEventListener("keydown",s=>{s.key==="Enter"&&!s.shiftKey?(s.preventDefault(),this.send()):s.key==="Escape"&&this.options.onClose()}),this.shadow.addEventListener("keydown",s=>{const i=s;i.key==="Escape"&&this.options.onClose(),i.key==="Tab"&&this.trapFocus(i)}),this.shadow.addEventListener("focusin",()=>this.collectFocusable()),this.collectFocusable()}mount(){document.body.appendChild(this.host)}destroy(){this.host.remove()}show(){this.open||(this.open=!0,this.host.style.display="block",this.focusInput())}hide(){this.open&&(this.open=!1,this.host.style.display="none")}focusInput(){queueMicrotask(()=>{this.input.focus()})}updateTheme(e){this.host.dataset.theme=e}updateMessages(e){this.messagesEl.innerHTML="",e.forEach(t=>{const s=document.createElement("div");s.className=`message ${t.author}`,s.setAttribute("data-author",t.author);const i=document.createElement("div");i.className="bubble",i.textContent=t.text,s.appendChild(i),this.messagesEl.appendChild(s)}),this.messagesEl.scrollTop=this.messagesEl.scrollHeight}appendMessage(e){const t=document.createElement("div");t.className=`message ${e.author}`,t.setAttribute("data-author",e.author);const s=document.createElement("div");s.className="bubble",s.textContent=e.text,t.appendChild(s),this.messagesEl.appendChild(t),this.messagesEl.scrollTop=this.messagesEl.scrollHeight}setTyping(e){this.typingEl.toggleAttribute("hidden",!e)}setOffline(e){this.offlineEl.toggleAttribute("hidden",!e)}clearInput(){this.input.value="",this.input.dispatchEvent(new Event("input"))}send(){const e=this.input.value.trim();e&&(this.options.onSend(e),this.clearInput())}collectFocusable(){const e=this.shadow.querySelectorAll('button, textarea, [href], [tabindex]:not([tabindex="-1"])');this.focusable.length=0,e.forEach(t=>{t.hasAttribute("disabled")||this.focusable.push(t)})}trapFocus(e){if(this.focusable.length===0)return;const t=this.focusable[0],s=this.focusable[this.focusable.length-1],i=this.shadow.activeElement;e.shiftKey&&i===t?(e.preventDefault(),s.focus()):!e.shiftKey&&i===s&&(e.preventDefault(),t.focus())}}const C="https://api.spilki.ai",g={welcome:"Hi! I'm your assistant.",placeholder:"Type a message…",sendLabel:"Send",typing:"Assistant is typing…",offline:"Unable to connect. Please try again later.",title:"Spilki Assistant"},p={apiBase:C,position:"bottom-right",theme:"auto",color:"#6366f1",welcome:g.welcome,persist:!0,i18n:g};function W(r){var t,s,i,n,l,a,c;const e={...g,...(t=r.i18n)!=null?t:{}};return{...p,...r,apiBase:(s=r.apiBase)!=null?s:p.apiBase,i18n:e,welcome:(i=r.welcome)!=null?i:e.welcome,position:(n=r.position)!=null?n:p.position,theme:(l=r.theme)!=null?l:p.theme,color:(a=r.color)!=null?a:p.color,persist:(c=r.persist)!=null?c:p.persist}}function $(r="msg"){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():`${r}-${Math.random().toString(16).slice(2)}`}function B(){var r,e;return(e=(r=window.matchMedia)==null?void 0:r.call(window,"(prefers-color-scheme: dark)").matches)!=null?e:!1}function b(r){return r==="light"||r==="dark"?r:B()?"dark":"light"}function f(r,e=30){return r.slice(-e)}function L(r){if(!r||r.length===0)return;const e=window.location.origin;r.includes(e)||console.warn(`SpilkiWidget: current origin ${e} not in allowedOriginsHint: ${r.join(", ")}`)}const h=1500,k=8e3;class K{constructor(e,t){this.sessionId=null,this.currentKind=null,this.stopped=!1,this.backoff=h,this.options=e,this.handlers=t}setAccessToken(e){this.options.accessToken=e}get kind(){return this.currentKind}get activeSession(){var e,t;return(t=(e=this.sessionId)!=null?e:this.options.sessionId)!=null?t:null}async connect(){var l,a;this.stopped=!1;const e=`${this.options.apiBase.replace(/\/$/,"")}/widget/session`,t=(a=(l=this.sessionId)!=null?l:this.options.sessionId)!=null?a:void 0,s={organisationId:this.options.org,sessionId:t,userAgent:typeof navigator!="undefined"?navigator.userAgent:"",referrer:typeof document!="undefined"?document.referrer:"",origin:typeof window!="undefined"?window.location.origin:""},i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.options.accessToken?{"X-Authorization":`Bearer ${this.options.accessToken}`}:{}},body:JSON.stringify(s)});if(!i.ok)throw new Error(`SpilkiWidget: connect failed (${i.status})`);const n=await i.json();return this.sessionId=n.sessionId,this.options.sessionId=n.sessionId,this.backoff=h,await this.startTransport(n),n}async send(e){var n,l;const t={sessionId:(l=(n=this.sessionId)!=null?n:this.options.sessionId)!=null?l:"",text:e};if(!t.sessionId)throw new Error("SpilkiWidget: missing session id");if(this.currentKind==="ws"&&this.ws&&this.ws.readyState===WebSocket.OPEN){this.ws.send(JSON.stringify({type:"message",payload:t}));return}const s=`${this.options.apiBase.replace(/\/$/,"")}/widget/message`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",...this.options.accessToken?{"X-Authorization":`Bearer ${this.options.accessToken}`}:{}},body:JSON.stringify(t)});if(!i.ok)throw new Error(`SpilkiWidget: send failed (${i.status})`)}stop(){this.stopped=!0,this.backoff=h,this.ws&&(this.ws.close(),this.ws=void 0),this.sse&&(this.sse.close(),this.sse=void 0),this.pollTimer&&(clearTimeout(this.pollTimer),this.pollTimer=void 0),this.currentKind=null}async startTransport(e){if(this.stopped)return;const t=[];e.wsUrl&&t.push(()=>this.startWs(e.wsUrl)),e.sseUrl&&t.push(()=>this.startSse(e.sseUrl)),e.pollUrl&&t.push(()=>this.startPoll(e.pollUrl));for(const s of t)try{await s();return}catch(i){this.handlers.onError(i)}throw new Error("SpilkiWidget: unable to establish transport")}startWs(e){return new Promise((t,s)=>{try{const i=new WebSocket(e);this.ws=i,i.addEventListener("open",()=>{if(this.stopped){i.close();return}this.currentKind="ws",this.handlers.onOpen("ws"),this.backoff=h,t()}),i.addEventListener("message",n=>this.handleIncoming(n.data)),i.addEventListener("close",()=>{this.stopped||this.retryFallback("ws")}),i.addEventListener("error",()=>{this.handlers.onError(new Error("SpilkiWidget: websocket error")),i.readyState!==WebSocket.OPEN&&s(new Error("WebSocket failed"))})}catch(i){s(i)}})}startSse(e){return new Promise((t,s)=>{if(typeof EventSource=="undefined"){s(new Error("SSE not supported"));return}const i=new EventSource(e);this.sse=i;let n=!1;i.addEventListener("open",()=>{if(n=!0,this.stopped){i.close();return}this.currentKind="sse",this.handlers.onOpen("sse"),this.backoff=h,t()}),i.addEventListener("message",l=>this.handleIncoming(l.data)),i.addEventListener("error",()=>{if(!n){s(new Error("SSE failed"));return}this.handlers.onError(new Error("SpilkiWidget: SSE error")),!this.stopped&&this.retryFallback("sse")})})}async startPoll(e){this.currentKind="poll",this.handlers.onOpen("poll"),this.backoff=h;const t=async()=>{if(!this.stopped)try{const s=await fetch(e);if(!s.ok)throw new Error(`Poll failed ${s.status}`);const i=await s.json();f(i).forEach(n=>this.handlers.onMessage(n)),this.backoff=h}catch(s){this.handlers.onError(s),this.backoff=Math.min(this.backoff*1.5,k)}finally{this.stopped||(this.pollTimer=window.setTimeout(t,this.backoff))}};await t()}retryFallback(e){this.stopped||(this.stop(),this.backoff=Math.min(this.backoff*1.5,k),setTimeout(()=>{this.handlers.onError(new Error(`SpilkiWidget: retrying after ${e}`)),this.connect().catch(t=>this.handlers.onError(t))},this.backoff))}handleIncoming(e){try{const t=JSON.parse(e);if(Array.isArray(t)){t.forEach(s=>this.dispatchIncoming(s));return}this.dispatchIncoming(t)}catch{const t={id:`${Date.now()}`,author:"bot",text:e,ts:Date.now()};this.handlers.onMessage(t)}}dispatchIncoming(e){var t;if("type"in e){e.type==="message"?this.handlers.onMessage(e.payload):e.type==="typing"&&this.handlers.onTyping(!!((t=e.payload)!=null&&t.active));return}this.handlers.onMessage(e)}}const m=30;class P{constructor(e,t){this.org=e,this.listeners=new Set,this.state={isOpen:!1,isTyping:!1,isConnected:!1,messages:[]},this.historyKey=`spilki-history:${e}`,this.sessionKey=`spilki-session:${e}`,this.tokenKey=`spilki-token:${e}`,this.persist=t.persist,this.state.messages=this.loadMessages()}get snapshot(){return{...this.state,messages:[...this.state.messages]}}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}open(){this.state.isOpen||(this.state.isOpen=!0,this.emit())}close(){this.state.isOpen&&(this.state.isOpen=!1,this.emit())}setTyping(e){this.state.isTyping!==e&&(this.state.isTyping=e,this.emit())}setConnected(e){this.state.isConnected!==e&&(this.state.isConnected=e,this.emit())}addMessage(e){var s,i;const t={id:(s=e.id)!=null?s:$("msg"),ts:(i=e.ts)!=null?i:Date.now(),author:e.author,text:e.text};return this.state.messages=f([...this.state.messages,t],m),this.persistMessages(),this.emit(),t}setMessages(e){this.state.messages=f(e,m),this.persistMessages(),this.emit()}clearMessages(){this.state.messages=[],this.persistMessages(),this.emit()}get sessionId(){if(!this.persist)return null;try{return localStorage.getItem(this.sessionKey)}catch(e){return console.error("SpilkiWidget: unable to get item",e),null}}persistSession(e){if(this.persist)try{localStorage.setItem(this.sessionKey,e)}catch(t){console.error("SpilkiWidget: unable to set item",t)}}clearSession(){if(this.persist)try{localStorage.removeItem(this.sessionKey)}catch(e){console.error("SpilkiWidget: unable to remove item",e)}}get accessToken(){if(!this.persist)return null;try{return localStorage.getItem(this.tokenKey)}catch(e){return console.error("SpilkiWidget: unable to get item",e),null}}persistAccessToken(e){if(this.persist)try{localStorage.setItem(this.tokenKey,e)}catch(t){console.error("SpilkiWidget: unable to set item",t)}}clearAccessToken(){if(this.persist)try{localStorage.removeItem(this.tokenKey)}catch(e){console.error("SpilkiWidget: unable to remove item",e)}}emit(){this.listeners.forEach(e=>e())}persistMessages(){if(this.persist)try{localStorage.setItem(this.historyKey,JSON.stringify(this.state.messages))}catch(e){console.error("SpilkiWidget: unable to set item",e)}}loadMessages(){if(!this.persist)return[];try{const e=localStorage.getItem(this.historyKey);if(!e)return[];const t=JSON.parse(e);return Array.isArray(t)?f(t,m):[]}catch(e){return console.error("SpilkiWidget: unable to load messages",e),[]}}}function U(r){const e=r.replace(/-/g,"+").replace(/_/g,"/"),t=e.padEnd(e.length+(4-e.length%4)%4,"=");if(typeof atob=="function")return decodeURIComponent(Array.prototype.map.call(atob(t),i=>`%${`00${i.charCodeAt(0).toString(16)}`.slice(-2)}`).join(""));const s=globalThis.Buffer;if(s)return s.from(t,"base64").toString("utf8");throw new Error("SpilkiWidget: no base64 decoder available")}function N(r){if(!r)return null;const e=r.split(".");if(e.length<2)return null;try{const t=U(e[1]);return JSON.parse(t)}catch(t){return console.error("SpilkiWidget: unable to parse JWT",t),null}}function D(r){const e=N(r);if(!(e!=null&&e.exp))return!1;const t=Math.floor(Date.now()/1e3);return e.exp<t}const F={onOpen(){},onClose(){},onMessage(){},onError(){},onTransportChange(){}};async function z(r,e,t){const s=`${r.replace(/\/$/,"")}/widget/install`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",Origin:window.location.origin},body:JSON.stringify({token:e,organisationId:t})});if(!i.ok)throw new Error(`SpilkiWidget: install failed (${i.status})`);return(await i.json()).accessToken}async function H(r,e){const t=`${r.replace(/\/$/,"")}/widget/refresh`,s=await fetch(t,{method:"POST",headers:{"X-Authorization":`Bearer ${e}`,Origin:window.location.origin}});if(!s.ok)throw new Error(`SpilkiWidget: refresh failed (${s.status})`);return(await s.json()).accessToken}function w(r){var v,E,T,I;if(!r.org)throw new Error("SpilkiWidget: org is required");const e=W(r);L(e.allowedOriginsHint);const t={...F,...(v=e.hooks)!=null?v:{}},s=new P(e.org,{persist:e.persist});let i=(E=s.accessToken)!=null?E:void 0;const n=async()=>{if(!i){if(!r.installationToken)throw new Error("SpilkiWidget: missing installationToken");if(!r.org)throw new Error("SpilkiWidget: missing org");i=await z(e.apiBase,r.installationToken,r.org),s.persistAccessToken(i),c.setAccessToken(i);return}D(i)&&(i=await H(e.apiBase,i),s.persistAccessToken(i),c.setAccessToken(i))},l=O({color:e.color,position:(T=e.position)!=null?T:"bottom-right",onClick:()=>{s.snapshot.isOpen?(s.close(),t.onClose()):(s.open(),t.onOpen())}}),a=new M({color:e.color,theme:b(e.theme),position:(I=e.position)!=null?I:"bottom-right",i18n:e.i18n,onClose:()=>{s.close(),t.onClose()},onSend:o=>{const d=s.addMessage({author:"user",text:o});a.appendMessage(d),n().then(()=>c.send(o)).catch(J=>{t.onError(J),s.setConnected(!1),a.setOffline(!0)})}}),c=new K({apiBase:e.apiBase,accessToken:i,org:e.org,sessionId:s.sessionId},{onOpen(o){x.transport=o,t.onTransportChange(o),s.setConnected(!0),a.setOffline(!1)},onMessage(o){const d=s.addMessage(o);a.appendMessage(d),t.onMessage(d)},onTyping(o){s.setTyping(o)},onError(o){t.onError(o),s.setConnected(!1),s.snapshot.isOpen&&a.setOffline(!0)}});l.mount(),a.mount();const S=s.snapshot.messages;if(S.length===0&&e.welcome){const o={id:"welcome",author:"bot",text:e.welcome,ts:Date.now()};s.addMessage(o),a.appendMessage(o)}else a.updateMessages(S);const q=s.subscribe(()=>{const o=s.snapshot;l.setOpen(o.isOpen),a.setTyping(o.isTyping),a.setOffline(!o.isConnected),a.updateTheme(b(e.theme)),o.isOpen?a.show():a.hide()});n().then(()=>c.connect().then(o=>{var d;e.persist&&s.persistSession(o.sessionId),s.setConnected(!0),t.onTransportChange((d=c.kind)!=null?d:"ws")})).catch(o=>{t.onError(o),s.setConnected(!1),a.setOffline(!0)});const x={transport:null,open(){s.open(),t.onOpen()},close(){s.close(),t.onClose()},destroy(){q(),c.stop(),l.destroy(),a.destroy()}};return x}function y(){var s,i;if(typeof document=="undefined")return;const r=document.currentScript;if(!r)return;const e=r.dataset;if(e.autoinit==="false")return;const t=e.org;if(!t){console.error("SpilkiWidget: data-org and is required for auto init");return}w({org:t,installationToken:e.installationToken,apiBase:e.apiBase,position:(s=e.position)!=null?s:void 0,theme:(i=e.theme)!=null?i:void 0})}if(typeof window!="undefined"){const r=window;r.SpilkiWidget=r.SpilkiWidget||{},r.SpilkiWidget.init=e=>w(e)}y(),y()});
|
|
62
62
|
//# sourceMappingURL=bootstrap.umd.js.map
|