@aikaara/chat-sdk 0.1.4 → 0.3.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 CHANGED
@@ -1,5 +1,5 @@
1
- import { a as p, D as u, e as d, f as g, g as m, h as f, i as x, j as k, k as c, l as y } from "./headless-BhsiNVQj.mjs";
2
- class v {
1
+ import { a as k, D as y, g as u, h as v, j as w, k as T, l as C, m as L, n as b, o as A } from "./AikaaraChatClient-Cqbcd1jb.mjs";
2
+ class E {
3
3
  client;
4
4
  panel;
5
5
  bubble;
@@ -8,8 +8,8 @@ class v {
8
8
  input;
9
9
  errorBanner;
10
10
  isOpen = !1;
11
- constructor(e, t) {
12
- this.client = new p(e), this.bubble = t.querySelector("aikaara-chat-bubble"), this.panel = t.querySelector(".aikaara-panel"), this.header = t.querySelector("aikaara-chat-header"), this.messageList = t.querySelector("aikaara-message-list"), this.input = t.querySelector("aikaara-chat-input"), this.errorBanner = t.querySelector("aikaara-error-banner"), e.welcomeMessage && this.messageList.setWelcomeMessage(e.welcomeMessage), e.showTimestamps !== void 0 && this.messageList.setShowTimestamps(e.showTimestamps), this.wireEvents();
11
+ constructor(e, t, a) {
12
+ this.client = new k(e, { uploadAdapter: a?.uploadAdapter }), this.bubble = t.querySelector("aikaara-chat-bubble"), this.panel = t.querySelector(".aikaara-panel"), this.header = t.querySelector("aikaara-chat-header"), this.messageList = t.querySelector("aikaara-message-list"), this.input = t.querySelector("aikaara-chat-input"), this.errorBanner = t.querySelector("aikaara-error-banner"), e.welcomeMessage && this.messageList.setWelcomeMessage(e.welcomeMessage), e.showTimestamps !== void 0 && this.messageList.setShowTimestamps(e.showTimestamps), this.wireEvents();
13
13
  }
14
14
  async connect() {
15
15
  try {
@@ -47,6 +47,13 @@ class v {
47
47
  this.header.setStatus(e), e === "connected" ? (this.errorBanner.hide(), this.input.disabled = !1) : e === "reconnecting" ? (this.errorBanner.show("Connection lost. Reconnecting..."), this.input.disabled = !0) : e === "disconnected" && (this.input.disabled = !0);
48
48
  }), this.client.on("error", (e) => {
49
49
  this.errorBanner.show(e.message, 5e3);
50
+ }), this.client.on("message:received", (e) => {
51
+ this.messageList.upsertMessage(e);
52
+ }), this.client.on("message:updated", (e) => {
53
+ this.messageList.upsertMessage(e);
54
+ }), this.messageList.addEventListener("message-action", (e) => {
55
+ const t = e.detail;
56
+ t?.text && this.handleSend(t.text);
50
57
  });
51
58
  }
52
59
  async handleSend(e) {
@@ -56,6 +63,12 @@ class v {
56
63
  this.errorBanner.show("Failed to send message", 3e3);
57
64
  }
58
65
  }
66
+ sendUserEvent(e, t, a) {
67
+ this.client.sendUserEvent(e, t, a);
68
+ }
69
+ getClient() {
70
+ return this.client;
71
+ }
59
72
  togglePanel(e) {
60
73
  this.isOpen = e !== void 0 ? e : !this.isOpen, this.isOpen ? (this.panel.removeAttribute("hidden"), requestAnimationFrame(() => {
61
74
  this.panel.classList.remove("entering"), this.panel.classList.add("visible"), this.input.focus();
@@ -64,7 +77,7 @@ class v {
64
77
  }, 200));
65
78
  }
66
79
  }
67
- class w extends HTMLElement {
80
+ class S extends HTMLElement {
68
81
  shadow;
69
82
  controller = null;
70
83
  _config = {};
@@ -109,38 +122,45 @@ class w extends HTMLElement {
109
122
  apiKey: this.getAttribute("api-key") || this._config.apiKey,
110
123
  title: this.getAttribute("title") || this._config.title || "Chat",
111
124
  subtitle: this.getAttribute("subtitle") || this._config.subtitle,
112
- theme: this.getAttribute("theme") || this._config.theme || y,
113
- primaryColor: this.getAttribute("primary-color") || this._config.primaryColor || c,
114
- position: this.getAttribute("position") || this._config.position || k,
115
- width: Number(this.getAttribute("width")) || this._config.width || x,
116
- height: Number(this.getAttribute("height")) || this._config.height || f,
117
- fontFamily: this._config.fontFamily || m,
118
- borderRadius: this._config.borderRadius ?? g,
119
- placeholder: this.getAttribute("placeholder") || this._config.placeholder || d,
125
+ theme: this.getAttribute("theme") || this._config.theme || A,
126
+ primaryColor: this.getAttribute("primary-color") || this._config.primaryColor || b,
127
+ position: this.getAttribute("position") || this._config.position || L,
128
+ width: Number(this.getAttribute("width")) || this._config.width || C,
129
+ height: Number(this.getAttribute("height")) || this._config.height || T,
130
+ fontFamily: this._config.fontFamily || w,
131
+ borderRadius: this._config.borderRadius ?? v,
132
+ placeholder: this.getAttribute("placeholder") || this._config.placeholder || u,
120
133
  welcomeMessage: this.getAttribute("welcome-message") || this._config.welcomeMessage,
121
134
  avatarUrl: this.getAttribute("avatar-url") || this._config.avatarUrl,
122
135
  showTimestamps: this._config.showTimestamps ?? !0,
123
136
  persistConversation: this._config.persistConversation ?? !0,
124
137
  showBubble: this._config.showBubble ?? !0,
125
- offset: this._config.offset || u,
138
+ offset: this._config.offset || y,
126
139
  conversationId: this._config.conversationId,
127
140
  systemPromptId: this._config.systemPromptId,
128
141
  channel: this._config.channel,
129
142
  extUid: this._config.extUid,
143
+ transport: this._config.transport,
144
+ tiledesk: this._config.tiledesk,
145
+ tiledeskIdentity: this._config.tiledeskIdentity,
130
146
  onMessage: this._config.onMessage,
131
147
  onStatusChange: this._config.onStatusChange,
132
148
  onError: this._config.onError,
133
149
  onStreamUpdate: this._config.onStreamUpdate,
134
- onConnectionStateChange: this._config.onConnectionStateChange
150
+ onConnectionStateChange: this._config.onConnectionStateChange,
151
+ onTemplateMessage: this._config.onTemplateMessage
135
152
  };
136
153
  }
154
+ setUploadAdapter(e) {
155
+ this._config.uploadAdapter = e, this.controller?.getClient().setUploadAdapter(e);
156
+ }
137
157
  render() {
138
158
  const e = this.getConfig();
139
159
  this.shadow.innerHTML = `
140
160
  <style>
141
161
  :host {
142
162
  --aikaara-primary: ${e.primaryColor};
143
- --aikaara-primary-hover: ${this.darkenColor(e.primaryColor || c)};
163
+ --aikaara-primary-hover: ${this.darkenColor(e.primaryColor || b)};
144
164
  --aikaara-bg: #ffffff;
145
165
  --aikaara-bg-secondary: #f9fafb;
146
166
  --aikaara-text: #1f2937;
@@ -222,25 +242,33 @@ class w extends HTMLElement {
222
242
  ${e.avatarUrl ? `avatar-url="${e.avatarUrl}"` : ""}
223
243
  ></aikaara-chat-header>
224
244
  <aikaara-message-list></aikaara-message-list>
225
- <aikaara-chat-input placeholder="${e.placeholder || d}"></aikaara-chat-input>
245
+ <aikaara-chat-input placeholder="${e.placeholder || u}"></aikaara-chat-input>
226
246
  <aikaara-error-banner></aikaara-error-banner>
227
247
  </div>
228
248
  `;
229
249
  }
230
250
  async initController() {
231
251
  const e = this.getConfig();
232
- !e.baseUrl || !e.userToken || (this.controller?.disconnect(), this.controller = new v(e, this.shadow), await this.controller.connect());
252
+ !e.baseUrl || !e.userToken || (this.controller?.disconnect(), this.controller = new E(e, this.shadow, {
253
+ uploadAdapter: this._config.uploadAdapter
254
+ }), await this.controller.connect());
255
+ }
256
+ sendUserEvent(e, t, a) {
257
+ this.controller?.sendUserEvent(e, t, a);
258
+ }
259
+ getClient() {
260
+ return this.controller?.getClient() ?? null;
233
261
  }
234
262
  darkenColor(e) {
235
263
  try {
236
- const t = parseInt(e.replace("#", ""), 16), a = Math.max(0, (t >> 16) - 20), s = Math.max(0, (t >> 8 & 255) - 20), n = Math.max(0, (t & 255) - 20);
237
- return `#${(a << 16 | s << 8 | n).toString(16).padStart(6, "0")}`;
264
+ const t = parseInt(e.replace("#", ""), 16), a = Math.max(0, (t >> 16) - 20), i = Math.max(0, (t >> 8 & 255) - 20), r = Math.max(0, (t & 255) - 20);
265
+ return `#${(a << 16 | i << 8 | r).toString(16).padStart(6, "0")}`;
238
266
  } catch {
239
267
  return e;
240
268
  }
241
269
  }
242
270
  }
243
- class T extends HTMLElement {
271
+ class M extends HTMLElement {
244
272
  shadow;
245
273
  constructor() {
246
274
  super(), this.shadow = this.attachShadow({ mode: "open" });
@@ -293,7 +321,7 @@ class T extends HTMLElement {
293
321
  t && (t.innerHTML = e);
294
322
  }
295
323
  }
296
- class L extends HTMLElement {
324
+ class _ extends HTMLElement {
297
325
  shadow;
298
326
  static get observedAttributes() {
299
327
  return ["title", "subtitle", "avatar-url", "status"];
@@ -313,7 +341,7 @@ class L extends HTMLElement {
313
341
  });
314
342
  }
315
343
  render() {
316
- const e = this.getAttribute("title") || "Chat", t = this.getAttribute("subtitle") || "", a = this.getAttribute("avatar-url"), s = this.getAttribute("status") || "connected", n = s === "connected" ? "#10b981" : s === "connecting" || s === "reconnecting" ? "#f59e0b" : "#ef4444";
344
+ const e = this.getAttribute("title") || "Chat", t = this.getAttribute("subtitle") || "", a = this.getAttribute("avatar-url"), i = this.getAttribute("status") || "connected", r = i === "connected" ? "#10b981" : i === "connecting" || i === "reconnecting" ? "#f59e0b" : "#ef4444";
317
345
  this.shadow.innerHTML = `
318
346
  <style>
319
347
  .header {
@@ -398,7 +426,7 @@ class L extends HTMLElement {
398
426
  <div class="info">
399
427
  <div class="title">
400
428
  ${e}
401
- <span class="status-dot" style="background:${n}"></span>
429
+ <span class="status-dot" style="background:${r}"></span>
402
430
  </div>
403
431
  ${t ? `<div class="subtitle">${t}</div>` : ""}
404
432
  </div>
@@ -414,14 +442,14 @@ class L extends HTMLElement {
414
442
  this.setAttribute("status", e);
415
443
  }
416
444
  }
417
- function o(i) {
418
- let e = C(i);
419
- return e = e.replace(/```(\w*)\n([\s\S]*?)```/g, (t, a, s) => `<pre><code>${s.trim()}</code></pre>`), e = e.replace(/`([^`]+)`/g, "<code>$1</code>"), e = e.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), e = e.replace(/\*(.+?)\*/g, "<em>$1</em>"), e = e.replace(
445
+ function l(s) {
446
+ let e = $(s);
447
+ return e = e.replace(/```(\w*)\n([\s\S]*?)```/g, (t, a, i) => `<pre><code>${i.trim()}</code></pre>`), e = e.replace(/`([^`]+)`/g, "<code>$1</code>"), e = e.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), e = e.replace(/\*(.+?)\*/g, "<em>$1</em>"), e = e.replace(
420
448
  /\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g,
421
449
  '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
422
450
  ), e = e.replace(/\n/g, "<br>"), e;
423
451
  }
424
- function C(i) {
452
+ function $(s) {
425
453
  const e = {
426
454
  "&": "&amp;",
427
455
  "<": "&lt;",
@@ -429,9 +457,9 @@ function C(i) {
429
457
  '"': "&quot;",
430
458
  "'": "&#039;"
431
459
  };
432
- return i.replace(/[&<>"']/g, (t) => e[t]);
460
+ return s.replace(/[&<>"']/g, (t) => e[t]);
433
461
  }
434
- const A = /* @__PURE__ */ new Set([
462
+ const H = /* @__PURE__ */ new Set([
435
463
  "p",
436
464
  "br",
437
465
  "strong",
@@ -451,38 +479,38 @@ const A = /* @__PURE__ */ new Set([
451
479
  "h6",
452
480
  "span",
453
481
  "div"
454
- ]), S = {
482
+ ]), z = {
455
483
  a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
456
484
  code: /* @__PURE__ */ new Set(["class"]),
457
485
  pre: /* @__PURE__ */ new Set(["class"]),
458
486
  span: /* @__PURE__ */ new Set(["class"]),
459
487
  div: /* @__PURE__ */ new Set(["class"])
460
488
  };
461
- function l(i) {
489
+ function d(s) {
462
490
  const e = document.createElement("template");
463
- return e.innerHTML = i, h(e.content), e.innerHTML;
491
+ return e.innerHTML = s, g(e.content), e.innerHTML;
464
492
  }
465
- function h(i) {
466
- const e = Array.from(i.childNodes);
493
+ function g(s) {
494
+ const e = Array.from(s.childNodes);
467
495
  for (const t of e)
468
496
  if (t.nodeType === Node.ELEMENT_NODE) {
469
- const a = t, s = a.tagName.toLowerCase();
470
- if (!A.has(s)) {
471
- const r = document.createTextNode(a.textContent || "");
472
- i.replaceChild(r, t);
497
+ const a = t, i = a.tagName.toLowerCase();
498
+ if (!H.has(i)) {
499
+ const n = document.createTextNode(a.textContent || "");
500
+ s.replaceChild(n, t);
473
501
  continue;
474
502
  }
475
- const n = S[s] || /* @__PURE__ */ new Set(), b = Array.from(a.attributes);
476
- for (const r of b)
477
- n.has(r.name) || a.removeAttribute(r.name);
503
+ const r = z[i] || /* @__PURE__ */ new Set(), o = Array.from(a.attributes);
504
+ for (const n of o)
505
+ r.has(n.name) || a.removeAttribute(n.name);
478
506
  if (a.hasAttribute("href")) {
479
- const r = a.getAttribute("href") || "";
480
- !r.startsWith("http://") && !r.startsWith("https://") && !r.startsWith("/") && a.removeAttribute("href");
507
+ const n = a.getAttribute("href") || "";
508
+ !n.startsWith("http://") && !n.startsWith("https://") && !n.startsWith("/") && a.removeAttribute("href");
481
509
  }
482
- h(t);
510
+ g(t);
483
511
  }
484
512
  }
485
- class E extends HTMLElement {
513
+ class I extends HTMLElement {
486
514
  shadow;
487
515
  container;
488
516
  welcomeMessage = "";
@@ -512,7 +540,41 @@ class E extends HTMLElement {
512
540
  .message-list::-webkit-scrollbar-thumb { background: var(--aikaara-border, #e5e7eb); border-radius: 3px; }
513
541
  .message-wrap { display: flex; flex-direction: column; }
514
542
  .message-wrap.user { align-items: flex-end; }
515
- .message-wrap.assistant { align-items: flex-start; }
543
+ .message-wrap.assistant,
544
+ .message-wrap.agent,
545
+ .message-wrap.system { align-items: flex-start; }
546
+ .message-wrap.system .bubble {
547
+ background: transparent;
548
+ color: var(--aikaara-text-secondary, #6b7280);
549
+ font-size: 12px;
550
+ font-style: italic;
551
+ padding: 4px 0;
552
+ }
553
+ .attachments {
554
+ display: flex;
555
+ flex-direction: column;
556
+ gap: 4px;
557
+ margin-top: 6px;
558
+ }
559
+ .attachment {
560
+ display: inline-flex;
561
+ align-items: center;
562
+ gap: 6px;
563
+ padding: 6px 10px;
564
+ background: rgba(0,0,0,0.05);
565
+ border-radius: 8px;
566
+ font-size: 12px;
567
+ color: inherit;
568
+ text-decoration: none;
569
+ }
570
+ .attachment:hover { background: rgba(0,0,0,0.08); }
571
+ .status-tick {
572
+ font-size: 10px;
573
+ margin-left: 4px;
574
+ color: var(--aikaara-text-secondary, #6b7280);
575
+ }
576
+ .status-tick.read { color: var(--aikaara-primary, #6366f1); }
577
+ aikaara-template-renderer { display: block; margin-top: 6px; }
516
578
  .bubble {
517
579
  max-width: 85%;
518
580
  padding: 10px 14px;
@@ -610,7 +672,7 @@ class E extends HTMLElement {
610
672
  renderMessages(e) {
611
673
  if (this.container) {
612
674
  if (this.container.innerHTML = "", e.length === 0 && this.welcomeMessage) {
613
- this.container.innerHTML = `<div class="welcome">${l(o(this.welcomeMessage))}</div>`;
675
+ this.container.innerHTML = `<div class="welcome">${d(l(this.welcomeMessage))}</div>`;
614
676
  return;
615
677
  }
616
678
  for (const t of e)
@@ -624,7 +686,7 @@ class E extends HTMLElement {
624
686
  }
625
687
  updateStreamingContent(e) {
626
688
  const t = this.container.querySelector('[data-streaming="true"] .bubble');
627
- t && (t.innerHTML = l(o(e)), t.classList.add("streaming-cursor"), this.scrollToBottom());
689
+ t && (t.innerHTML = d(l(e)), t.classList.add("streaming-cursor"), this.scrollToBottom());
628
690
  }
629
691
  finalizeStreaming() {
630
692
  const e = this.container.querySelector('[data-streaming="true"]');
@@ -640,14 +702,52 @@ class E extends HTMLElement {
640
702
  }
641
703
  appendMessageElement(e) {
642
704
  const t = document.createElement("div");
643
- t.classList.add("message-wrap", e.role), e.status === "streaming" && t.setAttribute("data-streaming", "true");
705
+ t.classList.add("message-wrap", e.role), e.status === "streaming" && t.setAttribute("data-streaming", "true"), e.externalId && (t.dataset.externalId = e.externalId), t.dataset.messageId = e.id;
644
706
  const a = document.createElement("div");
645
- if (a.classList.add("bubble", e.role), e.role === "user" ? a.textContent = e.content : (a.innerHTML = l(o(e.content || "")), e.status === "streaming" && a.classList.add("streaming-cursor")), t.appendChild(a), this.showTimestamps && e.createdAt) {
646
- const s = document.createElement("div");
647
- s.classList.add("timestamp"), s.textContent = this.formatTime(e.createdAt), t.appendChild(s);
707
+ if (a.classList.add("bubble", e.role), e.role === "user" ? a.textContent = e.content : (a.innerHTML = d(l(e.content || "")), e.status === "streaming" && a.classList.add("streaming-cursor")), e.template?.contentType) {
708
+ const i = document.createElement("aikaara-template-renderer");
709
+ i.setAttribute("content-type", e.template.contentType), e.template.templateId && i.setAttribute("template-id", e.template.templateId), i.setPayload(e.template.payload, e.role !== "user" ? e.content : void 0), a.appendChild(i);
710
+ }
711
+ if (e.attachments?.length) {
712
+ const i = document.createElement("div");
713
+ i.classList.add("attachments");
714
+ for (const r of e.attachments) {
715
+ const o = document.createElement("a");
716
+ o.classList.add("attachment"), o.href = r.fileUrl, o.target = "_blank", o.rel = "noopener noreferrer", o.textContent = `📎 ${r.fileName}`, i.appendChild(o);
717
+ }
718
+ a.appendChild(i);
719
+ }
720
+ if (t.appendChild(a), this.showTimestamps && e.createdAt || e.role === "user" && e.status) {
721
+ const i = document.createElement("div");
722
+ if (i.classList.add("timestamp"), this.showTimestamps && e.createdAt && (i.textContent = this.formatTime(e.createdAt)), e.role === "user" && e.status) {
723
+ const r = document.createElement("span");
724
+ r.classList.add("status-tick"), e.status === "read" && r.classList.add("read"), r.textContent = {
725
+ sending: " ○",
726
+ sent: " ✓",
727
+ delivered: " ✓✓",
728
+ read: " ✓✓"
729
+ }[e.status] ?? "", i.appendChild(r);
730
+ }
731
+ t.appendChild(i);
648
732
  }
649
733
  this.container.appendChild(t);
650
734
  }
735
+ /**
736
+ * Replace an existing rendered message (matched by id or externalId) in place.
737
+ * Used when a Tiledesk self-echo reconciles with an optimistic bubble or when
738
+ * a status update flips delivered → read.
739
+ */
740
+ upsertMessage(e) {
741
+ const t = this.findRenderedMessage(e);
742
+ t && t.remove(), this.appendMessageElement(e), this.scrollToBottom();
743
+ }
744
+ findRenderedMessage(e) {
745
+ if (e.externalId) {
746
+ const t = this.container.querySelector(`[data-external-id="${CSS.escape(e.externalId)}"]`);
747
+ if (t) return t;
748
+ }
749
+ return this.container.querySelector(`[data-message-id="${CSS.escape(e.id)}"]`);
750
+ }
651
751
  scrollToBottom() {
652
752
  requestAnimationFrame(() => {
653
753
  this.container.scrollTop = this.container.scrollHeight;
@@ -661,10 +761,12 @@ class E extends HTMLElement {
661
761
  }
662
762
  }
663
763
  }
664
- class M extends HTMLElement {
764
+ class U extends HTMLElement {
665
765
  shadow;
766
+ templatePayload = null;
767
+ attachments = [];
666
768
  static get observedAttributes() {
667
- return ["role", "content", "timestamp"];
769
+ return ["role", "content", "timestamp", "content-type", "template-id", "inner-message", "message-id", "status"];
668
770
  }
669
771
  constructor() {
670
772
  super(), this.shadow = this.attachShadow({ mode: "open" });
@@ -675,13 +777,21 @@ class M extends HTMLElement {
675
777
  attributeChangedCallback() {
676
778
  this.render();
677
779
  }
780
+ setTemplatePayload(e) {
781
+ this.templatePayload = e, this.render();
782
+ }
783
+ setAttachments(e) {
784
+ this.attachments = e, this.render();
785
+ }
678
786
  render() {
679
- const e = this.getAttribute("role") || "user", t = this.getAttribute("content") || "", a = this.getAttribute("timestamp") || "", s = e === "user" ? document.createTextNode(t).textContent || "" : l(o(t));
787
+ const e = this.getAttribute("role") || "user", t = this.getAttribute("content") || "", a = this.getAttribute("timestamp") || "", i = this.getAttribute("status") || "", r = this.getAttribute("content-type") || "", o = this.getAttribute("template-id") || "", n = this.getAttribute("inner-message") || "", h = e === "user" ? document.createTextNode(t).textContent || "" : d(l(t)), p = e === "user" && i, f = i === "sending" ? "○" : i === "sent" ? "✓" : i === "delivered" || i === "read" ? "✓✓" : "";
680
788
  this.shadow.innerHTML = `
681
789
  <style>
682
790
  :host { display: flex; flex-direction: column; }
683
791
  :host([role="user"]) { align-items: flex-end; }
684
- :host([role="assistant"]) { align-items: flex-start; }
792
+ :host([role="assistant"]),
793
+ :host([role="agent"]),
794
+ :host([role="system"]) { align-items: flex-start; }
685
795
  .bubble {
686
796
  max-width: 85%;
687
797
  padding: 10px 14px;
@@ -695,24 +805,90 @@ class M extends HTMLElement {
695
805
  color: #fff;
696
806
  border-bottom-right-radius: 4px;
697
807
  }
698
- .bubble.assistant {
808
+ .bubble.assistant, .bubble.agent {
699
809
  background: var(--aikaara-bg-secondary, #f9fafb);
700
810
  color: var(--aikaara-text, #1f2937);
701
811
  border-bottom-left-radius: 4px;
702
812
  }
813
+ .bubble.system {
814
+ background: transparent;
815
+ color: var(--aikaara-text-secondary, #6b7280);
816
+ font-size: 12px;
817
+ font-style: italic;
818
+ }
703
819
  .timestamp {
704
820
  font-size: 11px;
705
821
  color: var(--aikaara-text-secondary, #6b7280);
706
822
  margin-top: 2px;
707
823
  padding: 0 4px;
708
824
  }
825
+ .status {
826
+ font-size: 10px;
827
+ color: ${i === "read" ? "var(--aikaara-primary, #6366f1)" : "var(--aikaara-text-secondary, #6b7280)"};
828
+ margin-left: 4px;
829
+ }
830
+ .attachments {
831
+ display: flex;
832
+ flex-direction: column;
833
+ gap: 6px;
834
+ margin-top: 8px;
835
+ }
836
+ .attachment {
837
+ display: inline-flex;
838
+ align-items: center;
839
+ gap: 6px;
840
+ padding: 6px 10px;
841
+ background: rgba(0,0,0,0.05);
842
+ border-radius: 8px;
843
+ font-size: 12px;
844
+ color: inherit;
845
+ text-decoration: none;
846
+ max-width: 100%;
847
+ }
848
+ .attachment:hover { background: rgba(0,0,0,0.08); }
849
+ aikaara-template-renderer { display: block; margin-top: 6px; }
709
850
  </style>
710
- <div class="bubble ${e}">${s}</div>
711
- ${a ? `<div class="timestamp">${a}</div>` : ""}
851
+ <div class="bubble ${e}">
852
+ <div class="content">${h}</div>
853
+ ${r ? `<aikaara-template-renderer
854
+ content-type="${r}"
855
+ template-id="${o}"
856
+ inner-message="${m(n)}"
857
+ ></aikaara-template-renderer>` : ""}
858
+ ${this.renderAttachments()}
859
+ </div>
860
+ ${a || p ? `
861
+ <div class="timestamp">
862
+ ${a}${p ? `<span class="status">${f}</span>` : ""}
863
+ </div>
864
+ ` : ""}
712
865
  `;
866
+ const c = this.shadow.querySelector("aikaara-template-renderer");
867
+ c && this.templatePayload !== null && c.setPayload(this.templatePayload, n), c && c.addEventListener("template-action", (x) => {
868
+ this.dispatchEvent(
869
+ new CustomEvent("message-action", {
870
+ detail: x.detail,
871
+ bubbles: !0,
872
+ composed: !0
873
+ })
874
+ );
875
+ });
876
+ }
877
+ renderAttachments() {
878
+ return this.attachments.length ? `<div class="attachments">${this.attachments.map(
879
+ (t) => `<a class="attachment" href="${m(t.fileUrl)}" target="_blank" rel="noopener noreferrer">
880
+ 📎 ${q(t.fileName)}
881
+ </a>`
882
+ ).join("")}</div>` : "";
713
883
  }
714
884
  }
715
- class _ extends HTMLElement {
885
+ function m(s) {
886
+ return String(s).replace(/[&<>"']/g, (e) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[e]);
887
+ }
888
+ function q(s) {
889
+ return String(s).replace(/[&<>]/g, (e) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" })[e]);
890
+ }
891
+ class B extends HTMLElement {
716
892
  shadow;
717
893
  textarea;
718
894
  sendBtn;
@@ -810,7 +986,7 @@ class _ extends HTMLElement {
810
986
  this.textarea.style.height = "auto", this.textarea.style.height = Math.min(this.textarea.scrollHeight, 120) + "px";
811
987
  }
812
988
  }
813
- class H extends HTMLElement {
989
+ class D extends HTMLElement {
814
990
  shadow;
815
991
  constructor() {
816
992
  super(), this.shadow = this.attachShadow({ mode: "open" });
@@ -859,7 +1035,7 @@ class H extends HTMLElement {
859
1035
  this.removeAttribute("visible");
860
1036
  }
861
1037
  }
862
- class $ extends HTMLElement {
1038
+ class F extends HTMLElement {
863
1039
  shadow;
864
1040
  bubble;
865
1041
  constructor() {
@@ -918,13 +1094,13 @@ class $ extends HTMLElement {
918
1094
  `, this.bubble = this.shadow.querySelector(".bubble");
919
1095
  }
920
1096
  updateContent(e) {
921
- this.bubble && (this.bubble.innerHTML = l(o(e)), this.bubble.classList.add("cursor"));
1097
+ this.bubble && (this.bubble.innerHTML = d(l(e)), this.bubble.classList.add("cursor"));
922
1098
  }
923
1099
  finalize() {
924
1100
  this.bubble?.classList.remove("cursor");
925
1101
  }
926
1102
  }
927
- class z extends HTMLElement {
1103
+ class P extends HTMLElement {
928
1104
  shadow;
929
1105
  container;
930
1106
  dismissTimer = null;
@@ -970,31 +1146,89 @@ class z extends HTMLElement {
970
1146
  this.container.classList.remove("visible"), this.dismissTimer && (clearTimeout(this.dismissTimer), this.dismissTimer = null);
971
1147
  }
972
1148
  }
973
- function B() {
974
- const i = [
975
- ["aikaara-chat-widget", w],
976
- ["aikaara-chat-bubble", T],
977
- ["aikaara-chat-header", L],
978
- ["aikaara-message-list", E],
979
- ["aikaara-message-bubble", M],
980
- ["aikaara-chat-input", _],
981
- ["aikaara-typing-indicator", H],
982
- ["aikaara-streaming-message", $],
983
- ["aikaara-error-banner", z]
1149
+ class O extends HTMLElement {
1150
+ shadow;
1151
+ payloadData = null;
1152
+ innerMessage = "";
1153
+ static get observedAttributes() {
1154
+ return ["content-type", "template-id", "inner-message"];
1155
+ }
1156
+ constructor() {
1157
+ super(), this.shadow = this.attachShadow({ mode: "open" });
1158
+ }
1159
+ connectedCallback() {
1160
+ this.render();
1161
+ }
1162
+ attributeChangedCallback() {
1163
+ this.render();
1164
+ }
1165
+ setPayload(e, t) {
1166
+ this.payloadData = e, t !== void 0 && (this.innerMessage = t), this.render();
1167
+ }
1168
+ render() {
1169
+ const e = this.getAttribute("content-type") ?? "", t = this.getAttribute("template-id") ?? "", a = this.getAttribute("inner-message") ?? this.innerMessage;
1170
+ this.shadow.innerHTML = `
1171
+ <style>
1172
+ :host { display: block; }
1173
+ .inner { font-size: 14px; line-height: 1.5; color: var(--aikaara-text, #1f2937); }
1174
+ .placeholder {
1175
+ font-size: 12px;
1176
+ color: var(--aikaara-text-secondary, #6b7280);
1177
+ padding: 6px 0;
1178
+ }
1179
+ </style>
1180
+ ${a ? '<div class="inner"></div>' : ""}
1181
+ <slot></slot>
1182
+ `;
1183
+ const i = this.shadow.querySelector(".inner");
1184
+ if (i && a && (i.textContent = a), !e) return;
1185
+ const r = t ? `aikaara-template-${t}` : `aikaara-template-${e}`;
1186
+ if (customElements.get(r)) {
1187
+ const o = this.querySelector(r);
1188
+ if (o) {
1189
+ o.setPayload?.(this.payloadData);
1190
+ return;
1191
+ }
1192
+ const n = document.createElement(r);
1193
+ n.setPayload?.(this.payloadData), n.addEventListener("template-action", (h) => {
1194
+ this.dispatchEvent(
1195
+ new CustomEvent("template-action", {
1196
+ detail: h.detail,
1197
+ bubbles: !0,
1198
+ composed: !0
1199
+ })
1200
+ );
1201
+ }), this.appendChild(n);
1202
+ }
1203
+ }
1204
+ }
1205
+ function N() {
1206
+ const s = [
1207
+ ["aikaara-chat-widget", S],
1208
+ ["aikaara-chat-bubble", M],
1209
+ ["aikaara-chat-header", _],
1210
+ ["aikaara-message-list", I],
1211
+ ["aikaara-message-bubble", U],
1212
+ ["aikaara-chat-input", B],
1213
+ ["aikaara-typing-indicator", D],
1214
+ ["aikaara-streaming-message", F],
1215
+ ["aikaara-error-banner", P],
1216
+ ["aikaara-template-renderer", O]
984
1217
  ];
985
- for (const [e, t] of i)
1218
+ for (const [e, t] of s)
986
1219
  customElements.get(e) || customElements.define(e, t);
987
1220
  }
988
- B();
1221
+ N();
989
1222
  export {
990
- T as AikaaraChatBubble,
991
- L as AikaaraChatHeader,
992
- _ as AikaaraChatInput,
993
- w as AikaaraChatWidget,
994
- z as AikaaraErrorBanner,
995
- M as AikaaraMessageBubble,
996
- E as AikaaraMessageList,
997
- $ as AikaaraStreamingMessage,
998
- H as AikaaraTypingIndicator,
999
- B as registerComponents
1223
+ M as AikaaraChatBubble,
1224
+ _ as AikaaraChatHeader,
1225
+ B as AikaaraChatInput,
1226
+ S as AikaaraChatWidget,
1227
+ P as AikaaraErrorBanner,
1228
+ U as AikaaraMessageBubble,
1229
+ I as AikaaraMessageList,
1230
+ F as AikaaraStreamingMessage,
1231
+ O as AikaaraTemplateRenderer,
1232
+ D as AikaaraTypingIndicator,
1233
+ N as registerComponents
1000
1234
  };