@aikaara/chat-sdk 0.1.0

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/dist/ui.mjs ADDED
@@ -0,0 +1,994 @@
1
+ var u = Object.defineProperty;
2
+ var g = (s, t, e) => t in s ? u(s, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[t] = e;
3
+ var i = (s, t, e) => g(s, typeof t != "symbol" ? t + "" : t, e);
4
+ import { a as m, D as f, e as h, f as x, g as k, h as y, i as v, j as w, k as b, l as T } from "./headless-DzI-CcOZ.mjs";
5
+ class L {
6
+ constructor(t, e) {
7
+ i(this, "client");
8
+ i(this, "panel");
9
+ i(this, "bubble");
10
+ i(this, "header");
11
+ i(this, "messageList");
12
+ i(this, "input");
13
+ i(this, "errorBanner");
14
+ i(this, "isOpen", !1);
15
+ this.client = new m(t), this.bubble = e.querySelector("aikaara-chat-bubble"), this.panel = e.querySelector(".aikaara-panel"), this.header = e.querySelector("aikaara-chat-header"), this.messageList = e.querySelector("aikaara-message-list"), this.input = e.querySelector("aikaara-chat-input"), this.errorBanner = e.querySelector("aikaara-error-banner"), t.welcomeMessage && this.messageList.setWelcomeMessage(t.welcomeMessage), t.showTimestamps !== void 0 && this.messageList.setShowTimestamps(t.showTimestamps), this.wireEvents();
16
+ }
17
+ async connect() {
18
+ try {
19
+ await this.client.connect(), this.messageList.renderMessages(this.client.messages);
20
+ } catch {
21
+ this.errorBanner.show("Failed to connect. Retrying...", 5e3);
22
+ }
23
+ }
24
+ async disconnect() {
25
+ await this.client.disconnect();
26
+ }
27
+ wireEvents() {
28
+ this.bubble.addEventListener("toggle", () => {
29
+ this.togglePanel();
30
+ }), this.header.addEventListener("close", () => {
31
+ this.togglePanel(!1);
32
+ }), this.input.addEventListener("send", ((t) => {
33
+ this.handleSend(t.detail.content);
34
+ })), this.client.on("message:sent", (t) => {
35
+ this.messageList.addMessage(t);
36
+ }), this.client.on("stream:start", () => {
37
+ this.messageList.removeTypingIndicator();
38
+ const t = this.client.messages[this.client.messages.length - 1];
39
+ t && this.messageList.addMessage(t);
40
+ }), this.client.on("stream:update", ({ content: t }) => {
41
+ this.messageList.updateStreamingContent(t);
42
+ }), this.client.on("stream:end", () => {
43
+ this.messageList.finalizeStreaming();
44
+ }), this.client.on("typing:start", () => {
45
+ const t = this.client.messages, e = t[t.length - 1];
46
+ (!e || e.status !== "streaming") && this.messageList.showTypingIndicator();
47
+ }), this.client.on("typing:stop", () => {
48
+ this.messageList.removeTypingIndicator();
49
+ }), this.client.on("connection:state", (t) => {
50
+ this.header.setStatus(t), t === "connected" ? (this.errorBanner.hide(), this.input.disabled = !1) : t === "reconnecting" ? (this.errorBanner.show("Connection lost. Reconnecting..."), this.input.disabled = !0) : t === "disconnected" && (this.input.disabled = !0);
51
+ }), this.client.on("error", (t) => {
52
+ this.errorBanner.show(t.message, 5e3);
53
+ });
54
+ }
55
+ async handleSend(t) {
56
+ try {
57
+ await this.client.sendMessage(t);
58
+ } catch {
59
+ this.errorBanner.show("Failed to send message", 3e3);
60
+ }
61
+ }
62
+ togglePanel(t) {
63
+ this.isOpen = t !== void 0 ? t : !this.isOpen, this.isOpen ? (this.panel.removeAttribute("hidden"), requestAnimationFrame(() => {
64
+ this.panel.classList.remove("entering"), this.panel.classList.add("visible"), this.input.focus();
65
+ })) : (this.panel.classList.remove("visible"), this.panel.classList.add("entering"), setTimeout(() => {
66
+ this.panel.setAttribute("hidden", "");
67
+ }, 200));
68
+ }
69
+ }
70
+ class C extends HTMLElement {
71
+ constructor() {
72
+ super();
73
+ i(this, "shadow");
74
+ i(this, "controller", null);
75
+ i(this, "_config", {});
76
+ this.shadow = this.attachShadow({ mode: "open" });
77
+ }
78
+ static get observedAttributes() {
79
+ return [
80
+ "base-url",
81
+ "user-token",
82
+ "api-key",
83
+ "title",
84
+ "subtitle",
85
+ "theme",
86
+ "primary-color",
87
+ "position",
88
+ "width",
89
+ "height",
90
+ "placeholder",
91
+ "welcome-message",
92
+ "avatar-url"
93
+ ];
94
+ }
95
+ connectedCallback() {
96
+ this.render(), this.initController();
97
+ }
98
+ disconnectedCallback() {
99
+ var e;
100
+ (e = this.controller) == null || e.disconnect();
101
+ }
102
+ attributeChangedCallback(e, a, r) {
103
+ a !== r && this.controller && (this.render(), this.initController());
104
+ }
105
+ configure(e) {
106
+ this._config = { ...this._config, ...e };
107
+ }
108
+ getConfig() {
109
+ return {
110
+ baseUrl: this.getAttribute("base-url") || this._config.baseUrl || "",
111
+ userToken: this.getAttribute("user-token") || this._config.userToken || "",
112
+ apiKey: this.getAttribute("api-key") || this._config.apiKey,
113
+ title: this.getAttribute("title") || this._config.title || "Chat",
114
+ subtitle: this.getAttribute("subtitle") || this._config.subtitle,
115
+ theme: this.getAttribute("theme") || this._config.theme || T,
116
+ primaryColor: this.getAttribute("primary-color") || this._config.primaryColor || b,
117
+ position: this.getAttribute("position") || this._config.position || w,
118
+ width: Number(this.getAttribute("width")) || this._config.width || v,
119
+ height: Number(this.getAttribute("height")) || this._config.height || y,
120
+ fontFamily: this._config.fontFamily || k,
121
+ borderRadius: this._config.borderRadius ?? x,
122
+ placeholder: this.getAttribute("placeholder") || this._config.placeholder || h,
123
+ welcomeMessage: this.getAttribute("welcome-message") || this._config.welcomeMessage,
124
+ avatarUrl: this.getAttribute("avatar-url") || this._config.avatarUrl,
125
+ showTimestamps: this._config.showTimestamps ?? !0,
126
+ persistConversation: this._config.persistConversation ?? !0,
127
+ showBubble: this._config.showBubble ?? !0,
128
+ offset: this._config.offset || f,
129
+ conversationId: this._config.conversationId,
130
+ systemPromptId: this._config.systemPromptId,
131
+ channel: this._config.channel,
132
+ onMessage: this._config.onMessage,
133
+ onStatusChange: this._config.onStatusChange,
134
+ onError: this._config.onError,
135
+ onStreamUpdate: this._config.onStreamUpdate,
136
+ onConnectionStateChange: this._config.onConnectionStateChange
137
+ };
138
+ }
139
+ render() {
140
+ var a, r;
141
+ const e = this.getConfig();
142
+ this.shadow.innerHTML = `
143
+ <style>
144
+ :host {
145
+ --aikaara-primary: ${e.primaryColor};
146
+ --aikaara-primary-hover: ${this.darkenColor(e.primaryColor || b)};
147
+ --aikaara-bg: #ffffff;
148
+ --aikaara-bg-secondary: #f9fafb;
149
+ --aikaara-text: #1f2937;
150
+ --aikaara-text-secondary: #6b7280;
151
+ --aikaara-border: #e5e7eb;
152
+ --aikaara-radius: ${e.borderRadius}px;
153
+ --aikaara-font: ${e.fontFamily};
154
+ --aikaara-panel-width: ${e.width}px;
155
+ --aikaara-panel-height: ${e.height}px;
156
+ --aikaara-bubble-size: 60px;
157
+ --aikaara-offset-x: ${((a = e.offset) == null ? void 0 : a.x) ?? 20}px;
158
+ --aikaara-offset-y: ${((r = e.offset) == null ? void 0 : r.y) ?? 20}px;
159
+ font-family: var(--aikaara-font);
160
+ position: fixed;
161
+ z-index: 9999;
162
+ bottom: var(--aikaara-offset-y);
163
+ ${e.position === "bottom-left" ? "left" : "right"}: var(--aikaara-offset-x);
164
+ }
165
+
166
+ .aikaara-panel {
167
+ width: var(--aikaara-panel-width);
168
+ height: var(--aikaara-panel-height);
169
+ max-height: calc(100vh - 100px);
170
+ background: var(--aikaara-bg);
171
+ border-radius: var(--aikaara-radius);
172
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
173
+ border: 1px solid var(--aikaara-border);
174
+ display: flex;
175
+ flex-direction: column;
176
+ overflow: hidden;
177
+ position: absolute;
178
+ bottom: calc(var(--aikaara-bubble-size) + 16px);
179
+ ${e.position === "bottom-left" ? "left" : "right"}: 0;
180
+ transition: opacity 200ms ease, transform 200ms ease;
181
+ }
182
+
183
+ .aikaara-panel[hidden] {
184
+ display: none;
185
+ }
186
+
187
+ .aikaara-panel.entering {
188
+ opacity: 0;
189
+ transform: translateY(8px) scale(0.98);
190
+ }
191
+
192
+ .aikaara-panel.visible {
193
+ opacity: 1;
194
+ transform: translateY(0) scale(1);
195
+ }
196
+ </style>
197
+
198
+ <aikaara-chat-bubble></aikaara-chat-bubble>
199
+
200
+ <div class="aikaara-panel entering" hidden role="dialog" aria-label="Chat">
201
+ <aikaara-chat-header
202
+ title="${e.title || "Chat"}"
203
+ ${e.subtitle ? `subtitle="${e.subtitle}"` : ""}
204
+ ${e.avatarUrl ? `avatar-url="${e.avatarUrl}"` : ""}
205
+ ></aikaara-chat-header>
206
+ <aikaara-message-list></aikaara-message-list>
207
+ <aikaara-chat-input placeholder="${e.placeholder || h}"></aikaara-chat-input>
208
+ <aikaara-error-banner></aikaara-error-banner>
209
+ </div>
210
+ `;
211
+ }
212
+ async initController() {
213
+ var a;
214
+ const e = this.getConfig();
215
+ !e.baseUrl || !e.userToken || ((a = this.controller) == null || a.disconnect(), this.controller = new L(e, this.shadow), await this.controller.connect());
216
+ }
217
+ darkenColor(e) {
218
+ try {
219
+ const a = parseInt(e.replace("#", ""), 16), r = Math.max(0, (a >> 16) - 20), n = Math.max(0, (a >> 8 & 255) - 20), l = Math.max(0, (a & 255) - 20);
220
+ return `#${(r << 16 | n << 8 | l).toString(16).padStart(6, "0")}`;
221
+ } catch {
222
+ return e;
223
+ }
224
+ }
225
+ }
226
+ class A extends HTMLElement {
227
+ constructor() {
228
+ super();
229
+ i(this, "shadow");
230
+ this.shadow = this.attachShadow({ mode: "open" });
231
+ }
232
+ connectedCallback() {
233
+ var e;
234
+ this.render(), (e = this.shadow.querySelector(".bubble")) == null || e.addEventListener("click", () => {
235
+ this.dispatchEvent(new CustomEvent("toggle", { bubbles: !0, composed: !0 }));
236
+ });
237
+ }
238
+ render() {
239
+ this.shadow.innerHTML = `
240
+ <style>
241
+ .bubble {
242
+ width: var(--aikaara-bubble-size, 60px);
243
+ height: var(--aikaara-bubble-size, 60px);
244
+ border-radius: 50%;
245
+ background: var(--aikaara-primary, #6366f1);
246
+ color: #ffffff;
247
+ border: none;
248
+ cursor: pointer;
249
+ display: flex;
250
+ align-items: center;
251
+ justify-content: center;
252
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
253
+ transition: transform 200ms ease, box-shadow 200ms ease;
254
+ }
255
+ .bubble:hover {
256
+ transform: scale(1.05);
257
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
258
+ }
259
+ .bubble svg {
260
+ width: 28px;
261
+ height: 28px;
262
+ }
263
+ .bubble-text {
264
+ font-family: var(--aikaara-font, system-ui, sans-serif);
265
+ font-size: 12px;
266
+ font-weight: 500;
267
+ }
268
+ </style>
269
+ <button class="bubble" aria-label="Open chat">
270
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
271
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
272
+ </svg>
273
+ </button>
274
+ `;
275
+ }
276
+ setIcon(e) {
277
+ const a = this.shadow.querySelector(".bubble");
278
+ a && (a.innerHTML = e);
279
+ }
280
+ }
281
+ class S extends HTMLElement {
282
+ constructor() {
283
+ super();
284
+ i(this, "shadow");
285
+ this.shadow = this.attachShadow({ mode: "open" });
286
+ }
287
+ static get observedAttributes() {
288
+ return ["title", "subtitle", "avatar-url", "status"];
289
+ }
290
+ connectedCallback() {
291
+ var e;
292
+ this.render(), (e = this.shadow.querySelector(".close-btn")) == null || e.addEventListener("click", () => {
293
+ this.dispatchEvent(new CustomEvent("close", { bubbles: !0, composed: !0 }));
294
+ });
295
+ }
296
+ attributeChangedCallback() {
297
+ this.render();
298
+ }
299
+ render() {
300
+ const e = this.getAttribute("title") || "Chat", a = this.getAttribute("subtitle") || "", r = this.getAttribute("avatar-url"), n = this.getAttribute("status") || "connected", l = n === "connected" ? "#10b981" : n === "connecting" || n === "reconnecting" ? "#f59e0b" : "#ef4444";
301
+ this.shadow.innerHTML = `
302
+ <style>
303
+ .header {
304
+ display: flex;
305
+ align-items: center;
306
+ gap: 12px;
307
+ padding: 14px 16px;
308
+ background: var(--aikaara-primary, #6366f1);
309
+ color: #ffffff;
310
+ flex-shrink: 0;
311
+ }
312
+ .avatar {
313
+ width: 36px;
314
+ height: 36px;
315
+ border-radius: 50%;
316
+ background: rgba(255,255,255,0.2);
317
+ display: flex;
318
+ align-items: center;
319
+ justify-content: center;
320
+ flex-shrink: 0;
321
+ overflow: hidden;
322
+ }
323
+ .avatar img {
324
+ width: 100%;
325
+ height: 100%;
326
+ object-fit: cover;
327
+ }
328
+ .avatar svg {
329
+ width: 20px;
330
+ height: 20px;
331
+ }
332
+ .info {
333
+ flex: 1;
334
+ min-width: 0;
335
+ }
336
+ .title {
337
+ font-size: 15px;
338
+ font-weight: 600;
339
+ line-height: 1.2;
340
+ display: flex;
341
+ align-items: center;
342
+ gap: 6px;
343
+ }
344
+ .status-dot {
345
+ width: 8px;
346
+ height: 8px;
347
+ border-radius: 50%;
348
+ flex-shrink: 0;
349
+ }
350
+ .subtitle {
351
+ font-size: 12px;
352
+ opacity: 0.85;
353
+ white-space: nowrap;
354
+ overflow: hidden;
355
+ text-overflow: ellipsis;
356
+ }
357
+ .close-btn {
358
+ background: none;
359
+ border: none;
360
+ color: #ffffff;
361
+ cursor: pointer;
362
+ padding: 4px;
363
+ border-radius: 4px;
364
+ display: flex;
365
+ align-items: center;
366
+ justify-content: center;
367
+ opacity: 0.8;
368
+ transition: opacity 200ms;
369
+ }
370
+ .close-btn:hover {
371
+ opacity: 1;
372
+ }
373
+ .close-btn svg {
374
+ width: 20px;
375
+ height: 20px;
376
+ }
377
+ </style>
378
+ <div class="header">
379
+ <div class="avatar">
380
+ ${r ? `<img src="${r}" alt="Avatar" />` : '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>'}
381
+ </div>
382
+ <div class="info">
383
+ <div class="title">
384
+ ${e}
385
+ <span class="status-dot" style="background:${l}"></span>
386
+ </div>
387
+ ${a ? `<div class="subtitle">${a}</div>` : ""}
388
+ </div>
389
+ <button class="close-btn" aria-label="Close chat">
390
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
391
+ <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
392
+ </svg>
393
+ </button>
394
+ </div>
395
+ `;
396
+ }
397
+ setStatus(e) {
398
+ this.setAttribute("status", e);
399
+ }
400
+ }
401
+ function d(s) {
402
+ let t = E(s);
403
+ return t = t.replace(/```(\w*)\n([\s\S]*?)```/g, (e, a, r) => `<pre><code>${r.trim()}</code></pre>`), t = t.replace(/`([^`]+)`/g, "<code>$1</code>"), t = t.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), t = t.replace(/\*(.+?)\*/g, "<em>$1</em>"), t = t.replace(
404
+ /\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g,
405
+ '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
406
+ ), t = t.replace(/\n/g, "<br>"), t;
407
+ }
408
+ function E(s) {
409
+ const t = {
410
+ "&": "&amp;",
411
+ "<": "&lt;",
412
+ ">": "&gt;",
413
+ '"': "&quot;",
414
+ "'": "&#039;"
415
+ };
416
+ return s.replace(/[&<>"']/g, (e) => t[e]);
417
+ }
418
+ const M = /* @__PURE__ */ new Set([
419
+ "p",
420
+ "br",
421
+ "strong",
422
+ "em",
423
+ "code",
424
+ "pre",
425
+ "a",
426
+ "ul",
427
+ "ol",
428
+ "li",
429
+ "blockquote",
430
+ "h1",
431
+ "h2",
432
+ "h3",
433
+ "h4",
434
+ "h5",
435
+ "h6",
436
+ "span",
437
+ "div"
438
+ ]), _ = {
439
+ a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
440
+ code: /* @__PURE__ */ new Set(["class"]),
441
+ pre: /* @__PURE__ */ new Set(["class"]),
442
+ span: /* @__PURE__ */ new Set(["class"]),
443
+ div: /* @__PURE__ */ new Set(["class"])
444
+ };
445
+ function c(s) {
446
+ const t = document.createElement("template");
447
+ return t.innerHTML = s, p(t.content), t.innerHTML;
448
+ }
449
+ function p(s) {
450
+ const t = Array.from(s.childNodes);
451
+ for (const e of t)
452
+ if (e.nodeType === Node.ELEMENT_NODE) {
453
+ const a = e, r = a.tagName.toLowerCase();
454
+ if (!M.has(r)) {
455
+ const o = document.createTextNode(a.textContent || "");
456
+ s.replaceChild(o, e);
457
+ continue;
458
+ }
459
+ const n = _[r] || /* @__PURE__ */ new Set(), l = Array.from(a.attributes);
460
+ for (const o of l)
461
+ n.has(o.name) || a.removeAttribute(o.name);
462
+ if (a.hasAttribute("href")) {
463
+ const o = a.getAttribute("href") || "";
464
+ !o.startsWith("http://") && !o.startsWith("https://") && !o.startsWith("/") && a.removeAttribute("href");
465
+ }
466
+ p(e);
467
+ }
468
+ }
469
+ class H extends HTMLElement {
470
+ constructor() {
471
+ super();
472
+ i(this, "shadow");
473
+ i(this, "container");
474
+ i(this, "welcomeMessage", "");
475
+ i(this, "showTimestamps", !0);
476
+ this.shadow = this.attachShadow({ mode: "open" });
477
+ }
478
+ connectedCallback() {
479
+ this.shadow.innerHTML = `
480
+ <style>
481
+ .message-list {
482
+ flex: 1;
483
+ overflow-y: auto;
484
+ padding: 16px;
485
+ display: flex;
486
+ flex-direction: column;
487
+ gap: 8px;
488
+ scroll-behavior: smooth;
489
+ }
490
+ .message-list::-webkit-scrollbar { width: 6px; }
491
+ .message-list::-webkit-scrollbar-track { background: transparent; }
492
+ .message-list::-webkit-scrollbar-thumb { background: var(--aikaara-border, #e5e7eb); border-radius: 3px; }
493
+ .message-wrap { display: flex; flex-direction: column; }
494
+ .message-wrap.user { align-items: flex-end; }
495
+ .message-wrap.assistant { align-items: flex-start; }
496
+ .bubble {
497
+ max-width: 85%;
498
+ padding: 10px 14px;
499
+ border-radius: var(--aikaara-radius, 12px);
500
+ font-size: 14px;
501
+ line-height: 1.5;
502
+ word-wrap: break-word;
503
+ overflow-wrap: break-word;
504
+ }
505
+ .bubble.user {
506
+ background: var(--aikaara-primary, #6366f1);
507
+ color: #ffffff;
508
+ border-bottom-right-radius: 4px;
509
+ }
510
+ .bubble.assistant {
511
+ background: var(--aikaara-bg-secondary, #f9fafb);
512
+ color: var(--aikaara-text, #1f2937);
513
+ border-bottom-left-radius: 4px;
514
+ }
515
+ .bubble pre {
516
+ background: rgba(0,0,0,0.06);
517
+ padding: 8px 12px;
518
+ border-radius: 6px;
519
+ overflow-x: auto;
520
+ font-size: 13px;
521
+ margin: 8px 0;
522
+ }
523
+ .bubble code {
524
+ font-family: 'SF Mono', 'Fira Code', monospace;
525
+ font-size: 13px;
526
+ }
527
+ .bubble.assistant code:not(pre code) {
528
+ background: rgba(0,0,0,0.06);
529
+ padding: 2px 4px;
530
+ border-radius: 3px;
531
+ }
532
+ .bubble a { color: inherit; text-decoration: underline; }
533
+ .timestamp {
534
+ font-size: 11px;
535
+ color: var(--aikaara-text-secondary, #6b7280);
536
+ margin-top: 2px;
537
+ padding: 0 4px;
538
+ }
539
+ .welcome {
540
+ text-align: center;
541
+ color: var(--aikaara-text-secondary, #6b7280);
542
+ font-size: 14px;
543
+ padding: 24px 16px;
544
+ }
545
+ .streaming-cursor::after {
546
+ content: '\\25CF';
547
+ animation: blink 1s infinite;
548
+ margin-left: 2px;
549
+ font-size: 10px;
550
+ vertical-align: middle;
551
+ }
552
+ @keyframes blink {
553
+ 0%, 100% { opacity: 1; }
554
+ 50% { opacity: 0; }
555
+ }
556
+ .typing-indicator {
557
+ display: flex;
558
+ align-items: center;
559
+ gap: 4px;
560
+ padding: 10px 14px;
561
+ align-self: flex-start;
562
+ background: var(--aikaara-bg-secondary, #f9fafb);
563
+ border-radius: var(--aikaara-radius, 12px);
564
+ border-bottom-left-radius: 4px;
565
+ }
566
+ .typing-indicator .dot {
567
+ width: 6px;
568
+ height: 6px;
569
+ border-radius: 50%;
570
+ background: var(--aikaara-text-secondary, #6b7280);
571
+ animation: typing-bounce 1.4s infinite ease-in-out;
572
+ }
573
+ .typing-indicator .dot:nth-child(1) { animation-delay: 0ms; }
574
+ .typing-indicator .dot:nth-child(2) { animation-delay: 200ms; }
575
+ .typing-indicator .dot:nth-child(3) { animation-delay: 400ms; }
576
+ @keyframes typing-bounce {
577
+ 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
578
+ 30% { transform: translateY(-4px); opacity: 1; }
579
+ }
580
+ </style>
581
+ <div class="message-list"></div>
582
+ `, this.container = this.shadow.querySelector(".message-list");
583
+ }
584
+ setWelcomeMessage(e) {
585
+ this.welcomeMessage = e;
586
+ }
587
+ setShowTimestamps(e) {
588
+ this.showTimestamps = e;
589
+ }
590
+ renderMessages(e) {
591
+ if (this.container) {
592
+ if (this.container.innerHTML = "", e.length === 0 && this.welcomeMessage) {
593
+ this.container.innerHTML = `<div class="welcome">${c(d(this.welcomeMessage))}</div>`;
594
+ return;
595
+ }
596
+ for (const a of e)
597
+ this.appendMessageElement(a);
598
+ this.scrollToBottom();
599
+ }
600
+ }
601
+ addMessage(e) {
602
+ const a = this.container.querySelector(".welcome");
603
+ a && a.remove(), this.appendMessageElement(e), this.scrollToBottom();
604
+ }
605
+ updateStreamingContent(e) {
606
+ const a = this.container.querySelector('[data-streaming="true"] .bubble');
607
+ a && (a.innerHTML = c(d(e)), a.classList.add("streaming-cursor"), this.scrollToBottom());
608
+ }
609
+ finalizeStreaming() {
610
+ const e = this.container.querySelector('[data-streaming="true"]');
611
+ if (e) {
612
+ e.removeAttribute("data-streaming");
613
+ const a = e.querySelector(".bubble");
614
+ a == null || a.classList.remove("streaming-cursor");
615
+ }
616
+ }
617
+ showTypingIndicator() {
618
+ this.removeTypingIndicator();
619
+ const e = document.createElement("div");
620
+ e.classList.add("typing-indicator"), e.setAttribute("data-typing", "true"), e.innerHTML = '<span class="dot"></span><span class="dot"></span><span class="dot"></span>', this.container.appendChild(e), this.scrollToBottom();
621
+ }
622
+ removeTypingIndicator() {
623
+ var e;
624
+ (e = this.container.querySelector('[data-typing="true"]')) == null || e.remove();
625
+ }
626
+ appendMessageElement(e) {
627
+ const a = document.createElement("div");
628
+ a.classList.add("message-wrap", e.role), e.status === "streaming" && a.setAttribute("data-streaming", "true");
629
+ const r = document.createElement("div");
630
+ if (r.classList.add("bubble", e.role), e.role === "user" ? r.textContent = e.content : (r.innerHTML = c(d(e.content || "")), e.status === "streaming" && r.classList.add("streaming-cursor")), a.appendChild(r), this.showTimestamps && e.createdAt) {
631
+ const n = document.createElement("div");
632
+ n.classList.add("timestamp"), n.textContent = this.formatTime(e.createdAt), a.appendChild(n);
633
+ }
634
+ this.container.appendChild(a);
635
+ }
636
+ scrollToBottom() {
637
+ requestAnimationFrame(() => {
638
+ this.container.scrollTop = this.container.scrollHeight;
639
+ });
640
+ }
641
+ formatTime(e) {
642
+ try {
643
+ return new Date(e).toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" });
644
+ } catch {
645
+ return "";
646
+ }
647
+ }
648
+ }
649
+ class $ extends HTMLElement {
650
+ constructor() {
651
+ super();
652
+ i(this, "shadow");
653
+ this.shadow = this.attachShadow({ mode: "open" });
654
+ }
655
+ static get observedAttributes() {
656
+ return ["role", "content", "timestamp"];
657
+ }
658
+ connectedCallback() {
659
+ this.render();
660
+ }
661
+ attributeChangedCallback() {
662
+ this.render();
663
+ }
664
+ render() {
665
+ const e = this.getAttribute("role") || "user", a = this.getAttribute("content") || "", r = this.getAttribute("timestamp") || "", n = e === "user" ? document.createTextNode(a).textContent || "" : c(d(a));
666
+ this.shadow.innerHTML = `
667
+ <style>
668
+ :host { display: flex; flex-direction: column; }
669
+ :host([role="user"]) { align-items: flex-end; }
670
+ :host([role="assistant"]) { align-items: flex-start; }
671
+ .bubble {
672
+ max-width: 85%;
673
+ padding: 10px 14px;
674
+ border-radius: var(--aikaara-radius, 12px);
675
+ font-size: 14px;
676
+ line-height: 1.5;
677
+ word-wrap: break-word;
678
+ }
679
+ .bubble.user {
680
+ background: var(--aikaara-primary, #6366f1);
681
+ color: #fff;
682
+ border-bottom-right-radius: 4px;
683
+ }
684
+ .bubble.assistant {
685
+ background: var(--aikaara-bg-secondary, #f9fafb);
686
+ color: var(--aikaara-text, #1f2937);
687
+ border-bottom-left-radius: 4px;
688
+ }
689
+ .timestamp {
690
+ font-size: 11px;
691
+ color: var(--aikaara-text-secondary, #6b7280);
692
+ margin-top: 2px;
693
+ padding: 0 4px;
694
+ }
695
+ </style>
696
+ <div class="bubble ${e}">${n}</div>
697
+ ${r ? `<div class="timestamp">${r}</div>` : ""}
698
+ `;
699
+ }
700
+ }
701
+ class z extends HTMLElement {
702
+ constructor() {
703
+ super();
704
+ i(this, "shadow");
705
+ i(this, "textarea");
706
+ i(this, "sendBtn");
707
+ i(this, "_disabled", !1);
708
+ this.shadow = this.attachShadow({ mode: "open" });
709
+ }
710
+ connectedCallback() {
711
+ const e = this.getAttribute("placeholder") || "Type a message...";
712
+ this.shadow.innerHTML = `
713
+ <style>
714
+ .input-container {
715
+ display: flex;
716
+ align-items: flex-end;
717
+ gap: 8px;
718
+ padding: 12px 16px;
719
+ border-top: 1px solid var(--aikaara-border, #e5e7eb);
720
+ background: var(--aikaara-bg, #ffffff);
721
+ }
722
+ textarea {
723
+ flex: 1;
724
+ resize: none;
725
+ border: 1px solid var(--aikaara-border, #e5e7eb);
726
+ border-radius: 8px;
727
+ padding: 10px 12px;
728
+ font-family: var(--aikaara-font, system-ui, sans-serif);
729
+ font-size: 14px;
730
+ color: var(--aikaara-text, #1f2937);
731
+ background: var(--aikaara-bg, #ffffff);
732
+ outline: none;
733
+ min-height: 40px;
734
+ max-height: 120px;
735
+ line-height: 1.4;
736
+ transition: border-color 200ms ease;
737
+ }
738
+ textarea:focus {
739
+ border-color: var(--aikaara-primary, #6366f1);
740
+ }
741
+ textarea::placeholder {
742
+ color: var(--aikaara-text-secondary, #6b7280);
743
+ }
744
+ .send-btn {
745
+ width: 40px;
746
+ height: 40px;
747
+ border: none;
748
+ border-radius: 50%;
749
+ background: var(--aikaara-primary, #6366f1);
750
+ color: #ffffff;
751
+ cursor: pointer;
752
+ display: flex;
753
+ align-items: center;
754
+ justify-content: center;
755
+ flex-shrink: 0;
756
+ transition: background 200ms, opacity 200ms;
757
+ }
758
+ .send-btn:hover { background: var(--aikaara-primary-hover, #4f46e5); }
759
+ .send-btn:disabled { opacity: 0.5; cursor: not-allowed; }
760
+ .send-btn svg { width: 18px; height: 18px; }
761
+ </style>
762
+ <div class="input-container">
763
+ <textarea rows="1" placeholder="${e}"></textarea>
764
+ <button class="send-btn" aria-label="Send message" disabled>
765
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
766
+ <line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
767
+ </svg>
768
+ </button>
769
+ </div>
770
+ `, this.textarea = this.shadow.querySelector("textarea"), this.sendBtn = this.shadow.querySelector(".send-btn"), this.textarea.addEventListener("input", () => {
771
+ this.autoGrow(), this.sendBtn.disabled = this._disabled || !this.textarea.value.trim();
772
+ }), this.textarea.addEventListener("keydown", (a) => {
773
+ a.key === "Enter" && !a.shiftKey && (a.preventDefault(), this.handleSend());
774
+ }), this.sendBtn.addEventListener("click", () => this.handleSend());
775
+ }
776
+ set disabled(e) {
777
+ var a;
778
+ this._disabled = e, this.textarea && (this.textarea.disabled = e), this.sendBtn && (this.sendBtn.disabled = e || !((a = this.textarea) != null && a.value.trim()));
779
+ }
780
+ get disabled() {
781
+ return this._disabled;
782
+ }
783
+ focus() {
784
+ var e;
785
+ (e = this.textarea) == null || e.focus();
786
+ }
787
+ clear() {
788
+ this.textarea && (this.textarea.value = "", this.textarea.style.height = "auto", this.sendBtn.disabled = !0);
789
+ }
790
+ handleSend() {
791
+ const e = this.textarea.value.trim();
792
+ !e || this._disabled || (this.dispatchEvent(new CustomEvent("send", {
793
+ detail: { content: e },
794
+ bubbles: !0,
795
+ composed: !0
796
+ })), this.clear(), this.textarea.focus());
797
+ }
798
+ autoGrow() {
799
+ this.textarea.style.height = "auto", this.textarea.style.height = Math.min(this.textarea.scrollHeight, 120) + "px";
800
+ }
801
+ }
802
+ class B extends HTMLElement {
803
+ constructor() {
804
+ super();
805
+ i(this, "shadow");
806
+ this.shadow = this.attachShadow({ mode: "open" });
807
+ }
808
+ connectedCallback() {
809
+ this.shadow.innerHTML = `
810
+ <style>
811
+ :host { display: none; }
812
+ :host([visible]) { display: block; }
813
+ .typing {
814
+ display: flex;
815
+ align-items: center;
816
+ gap: 4px;
817
+ padding: 10px 14px;
818
+ background: var(--aikaara-bg-secondary, #f9fafb);
819
+ border-radius: var(--aikaara-radius, 12px);
820
+ border-bottom-left-radius: 4px;
821
+ width: fit-content;
822
+ }
823
+ .dot {
824
+ width: 6px;
825
+ height: 6px;
826
+ border-radius: 50%;
827
+ background: var(--aikaara-text-secondary, #6b7280);
828
+ animation: bounce 1.4s infinite ease-in-out;
829
+ }
830
+ .dot:nth-child(1) { animation-delay: 0ms; }
831
+ .dot:nth-child(2) { animation-delay: 200ms; }
832
+ .dot:nth-child(3) { animation-delay: 400ms; }
833
+ @keyframes bounce {
834
+ 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
835
+ 30% { transform: translateY(-4px); opacity: 1; }
836
+ }
837
+ </style>
838
+ <div class="typing">
839
+ <span class="dot"></span>
840
+ <span class="dot"></span>
841
+ <span class="dot"></span>
842
+ </div>
843
+ `;
844
+ }
845
+ show() {
846
+ this.setAttribute("visible", "");
847
+ }
848
+ hide() {
849
+ this.removeAttribute("visible");
850
+ }
851
+ }
852
+ class F extends HTMLElement {
853
+ constructor() {
854
+ super();
855
+ i(this, "shadow");
856
+ i(this, "bubble");
857
+ this.shadow = this.attachShadow({ mode: "open" });
858
+ }
859
+ connectedCallback() {
860
+ this.shadow.innerHTML = `
861
+ <style>
862
+ :host {
863
+ display: flex;
864
+ flex-direction: column;
865
+ align-items: flex-start;
866
+ }
867
+ .bubble {
868
+ max-width: 85%;
869
+ padding: 10px 14px;
870
+ border-radius: var(--aikaara-radius, 12px);
871
+ border-bottom-left-radius: 4px;
872
+ background: var(--aikaara-bg-secondary, #f9fafb);
873
+ color: var(--aikaara-text, #1f2937);
874
+ font-size: 14px;
875
+ line-height: 1.5;
876
+ word-wrap: break-word;
877
+ min-height: 20px;
878
+ }
879
+ .bubble pre {
880
+ background: rgba(0,0,0,0.06);
881
+ padding: 8px 12px;
882
+ border-radius: 6px;
883
+ overflow-x: auto;
884
+ font-size: 13px;
885
+ margin: 8px 0;
886
+ }
887
+ .bubble code {
888
+ font-family: 'SF Mono', 'Fira Code', monospace;
889
+ font-size: 13px;
890
+ }
891
+ .bubble code:not(pre code) {
892
+ background: rgba(0,0,0,0.06);
893
+ padding: 2px 4px;
894
+ border-radius: 3px;
895
+ }
896
+ .cursor::after {
897
+ content: '\\25CF';
898
+ animation: blink 1s infinite;
899
+ margin-left: 2px;
900
+ font-size: 10px;
901
+ vertical-align: middle;
902
+ }
903
+ @keyframes blink {
904
+ 0%, 100% { opacity: 1; }
905
+ 50% { opacity: 0; }
906
+ }
907
+ </style>
908
+ <div class="bubble cursor"></div>
909
+ `, this.bubble = this.shadow.querySelector(".bubble");
910
+ }
911
+ updateContent(e) {
912
+ this.bubble && (this.bubble.innerHTML = c(d(e)), this.bubble.classList.add("cursor"));
913
+ }
914
+ finalize() {
915
+ var e;
916
+ (e = this.bubble) == null || e.classList.remove("cursor");
917
+ }
918
+ }
919
+ class q extends HTMLElement {
920
+ constructor() {
921
+ super();
922
+ i(this, "shadow");
923
+ i(this, "container");
924
+ i(this, "dismissTimer", null);
925
+ this.shadow = this.attachShadow({ mode: "open" });
926
+ }
927
+ connectedCallback() {
928
+ var e;
929
+ this.shadow.innerHTML = `
930
+ <style>
931
+ .banner {
932
+ display: none;
933
+ padding: 8px 16px;
934
+ background: #fef2f2;
935
+ color: #dc2626;
936
+ font-size: 13px;
937
+ text-align: center;
938
+ border-top: 1px solid #fecaca;
939
+ }
940
+ .banner.visible {
941
+ display: block;
942
+ }
943
+ .dismiss {
944
+ background: none;
945
+ border: none;
946
+ color: #dc2626;
947
+ cursor: pointer;
948
+ font-size: 12px;
949
+ text-decoration: underline;
950
+ margin-left: 8px;
951
+ }
952
+ </style>
953
+ <div class="banner">
954
+ <span class="message"></span>
955
+ <button class="dismiss">Dismiss</button>
956
+ </div>
957
+ `, this.container = this.shadow.querySelector(".banner"), (e = this.shadow.querySelector(".dismiss")) == null || e.addEventListener("click", () => this.hide());
958
+ }
959
+ show(e, a) {
960
+ const r = this.container.querySelector(".message");
961
+ r && (r.textContent = e), this.container.classList.add("visible"), this.dismissTimer && clearTimeout(this.dismissTimer), a && (this.dismissTimer = setTimeout(() => this.hide(), a));
962
+ }
963
+ hide() {
964
+ this.container.classList.remove("visible"), this.dismissTimer && (clearTimeout(this.dismissTimer), this.dismissTimer = null);
965
+ }
966
+ }
967
+ function I() {
968
+ const s = [
969
+ ["aikaara-chat-widget", C],
970
+ ["aikaara-chat-bubble", A],
971
+ ["aikaara-chat-header", S],
972
+ ["aikaara-message-list", H],
973
+ ["aikaara-message-bubble", $],
974
+ ["aikaara-chat-input", z],
975
+ ["aikaara-typing-indicator", B],
976
+ ["aikaara-streaming-message", F],
977
+ ["aikaara-error-banner", q]
978
+ ];
979
+ for (const [t, e] of s)
980
+ customElements.get(t) || customElements.define(t, e);
981
+ }
982
+ I();
983
+ export {
984
+ A as AikaaraChatBubble,
985
+ S as AikaaraChatHeader,
986
+ z as AikaaraChatInput,
987
+ C as AikaaraChatWidget,
988
+ q as AikaaraErrorBanner,
989
+ $ as AikaaraMessageBubble,
990
+ H as AikaaraMessageList,
991
+ F as AikaaraStreamingMessage,
992
+ B as AikaaraTypingIndicator,
993
+ I as registerComponents
994
+ };