@kodaris/krubble-app-components 1.0.67 → 1.0.69

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/chatbot.js CHANGED
@@ -8,19 +8,19 @@ import { LitElement, html, css, nothing } from 'lit';
8
8
  import { customElement, property, state, query } from 'lit/decorators.js';
9
9
  import { classMap } from 'lit/directives/class-map.js';
10
10
  import { unsafeHTML } from 'lit/directives/unsafe-html.js';
11
+ import { marked } from 'marked';
11
12
  /**
12
- * AI chatbot component with SSE streaming and native browser action execution.
13
+ * AI chatbot component with SSE streaming.
13
14
  *
14
15
  * Accepts a `send` callback that returns a streaming Response (SSE format).
15
- * Parses the stream for text tokens, status updates, errors, and frontend actions.
16
- * When an ACTION event arrives, dispatches a cancelable `action` custom event.
17
- * If not prevented, executes the native browser action (reload, navigate, etc.).
16
+ * Parses the stream for text tokens, status updates, and errors.
17
+ * Dispatches a `stream-event` custom event for every SSE message so consumers can react.
18
18
  *
19
19
  * ## SSE Stream Format
20
20
  * The component expects `data:` lines with JSON payloads containing a `type` field:
21
21
  * - `STATUS` — `{ type: "STATUS", message: "Thinking..." }`
22
22
  * - `RESPONSE_PART` — `{ type: "RESPONSE_PART", message: "token text" }`
23
- * - `ACTION` — `{ type: "ACTION", action: "RELOAD" }` (see KRChatbotAction)
23
+ * - `TOOL_RESULT` — `{ type: "TOOL_RESULT", message: "tool_name", data: {...} }`
24
24
  * - `ERROR` — `{ type: "ERROR", message: "Something went wrong" }`
25
25
  * - `HEARTBEAT` — `{ type: "HEARTBEAT" }` (ignored)
26
26
  *
@@ -44,7 +44,7 @@ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
44
44
  * @slot - Optional slot content displayed above the messages area
45
45
  *
46
46
  * @fires message-submit - When the user sends a message. Cancelable. Detail: `{ message: string }`
47
- * @fires action - When the AI requests a frontend action. Cancelable. Detail: `{ action: KRChatbotAction }`
47
+ * @fires stream-event - Dispatched for every SSE message received. Detail contains the parsed event data.
48
48
  */
49
49
  let KRChatbot = class KRChatbot extends LitElement {
50
50
  constructor() {
@@ -56,6 +56,7 @@ let KRChatbot = class KRChatbot extends LitElement {
56
56
  this._userHasScrolledUp = false;
57
57
  this._isDragging = false;
58
58
  this._isResizing = false;
59
+ this._overlay = null;
59
60
  this._resizeDir = '';
60
61
  this._dragStartX = 0;
61
62
  this._dragStartY = 0;
@@ -74,6 +75,11 @@ let KRChatbot = class KRChatbot extends LitElement {
74
75
  * Must return a Promise — messages are only cleared if it resolves.
75
76
  */
76
77
  this.clear = null;
78
+ /**
79
+ * Callback function called on init to load existing conversation messages.
80
+ * Must return a Promise that resolves with an array of KRChatbotMessage.
81
+ */
82
+ this.load = null;
77
83
  /**
78
84
  * Conversation messages. Can be set externally for initial messages.
79
85
  * Updated internally as the conversation progresses.
@@ -82,7 +88,7 @@ let KRChatbot = class KRChatbot extends LitElement {
82
88
  /**
83
89
  * Header title text.
84
90
  */
85
- this.title = 'AI Assistant';
91
+ this.title = 'Kat';
86
92
  /**
87
93
  * Header subtitle text (shown below title with a green status dot).
88
94
  */
@@ -105,9 +111,8 @@ let KRChatbot = class KRChatbot extends LitElement {
105
111
  let newLeft = this._dragStartLeft + (e.clientX - this._dragStartX);
106
112
  let newTop = this._dragStartTop + (e.clientY - this._dragStartY);
107
113
  const w = this._panelEl.offsetWidth;
108
- const h = this._panelEl.offsetHeight;
109
114
  newLeft = Math.max(-w + 48, Math.min(newLeft, window.innerWidth - 48));
110
- newTop = Math.max(-h + 48, Math.min(newTop, window.innerHeight - 48));
115
+ newTop = Math.max(0, Math.min(newTop, window.innerHeight - 48));
111
116
  this._panelEl.style.left = newLeft + 'px';
112
117
  this._panelEl.style.top = newTop + 'px';
113
118
  }
@@ -126,21 +131,15 @@ let KRChatbot = class KRChatbot extends LitElement {
126
131
  newW = Math.max(minW, Math.min(this._resizeStartW + dx, maxW));
127
132
  }
128
133
  if (this._resizeDir.includes('w')) {
129
- const proposedW = this._resizeStartW - dx;
130
- if (proposedW >= minW && proposedW <= maxW) {
131
- newW = proposedW;
132
- newLeft = this._dragStartLeft + dx;
133
- }
134
+ newW = Math.max(minW, Math.min(this._resizeStartW - dx, maxW));
135
+ newLeft = this._dragStartLeft + (this._resizeStartW - newW);
134
136
  }
135
137
  if (this._resizeDir.includes('s')) {
136
138
  newH = Math.max(minH, Math.min(this._resizeStartH + dy, maxH));
137
139
  }
138
140
  if (this._resizeDir.includes('n')) {
139
- const proposedH = this._resizeStartH - dy;
140
- if (proposedH >= minH && proposedH <= maxH) {
141
- newH = proposedH;
142
- newTop = this._dragStartTop + dy;
143
- }
141
+ newH = Math.max(minH, Math.min(this._resizeStartH - dy, maxH));
142
+ newTop = this._dragStartTop + (this._resizeStartH - newH);
144
143
  }
145
144
  newLeft = Math.max(0, newLeft);
146
145
  newTop = Math.max(0, newTop);
@@ -153,17 +152,30 @@ let KRChatbot = class KRChatbot extends LitElement {
153
152
  this._handleMouseUp = () => {
154
153
  if (this._isDragging) {
155
154
  this._isDragging = false;
156
- this._panelEl.classList.remove('chatbot__panel--dragging');
155
+ this._panelEl.classList.remove('panel--dragging');
157
156
  }
158
157
  if (this._isResizing) {
159
158
  this._isResizing = false;
160
- this._panelEl.classList.remove('chatbot__panel--resizing');
159
+ this._panelEl.classList.remove('panel--resizing');
161
160
  }
161
+ this._hideOverlay();
162
162
  document.removeEventListener('mousemove', this._handleMouseMove);
163
163
  document.removeEventListener('mouseup', this._handleMouseUp);
164
+ this._saveState();
164
165
  };
165
166
  }
166
167
  // --- Lifecycle ---
168
+ connectedCallback() {
169
+ super.connectedCallback();
170
+ if (this.load) {
171
+ this.load().then((messages) => {
172
+ if (messages && messages.length) {
173
+ this.messages = messages;
174
+ }
175
+ }).catch(() => { });
176
+ }
177
+ this._restoreState();
178
+ }
167
179
  disconnectedCallback() {
168
180
  super.disconnectedCallback();
169
181
  if (this._reader) {
@@ -175,7 +187,7 @@ let KRChatbot = class KRChatbot extends LitElement {
175
187
  }
176
188
  updated(changed) {
177
189
  if (changed.has('messages') || changed.has('_statusText')) {
178
- if (!this._userHasScrolledUp && this._messagesEl) {
190
+ if (!this._userHasScrolledUp && !this._isStreaming && this._messagesEl) {
179
191
  requestAnimationFrame(() => {
180
192
  if (this._messagesEl) {
181
193
  this._messagesEl.scrollTop = this._messagesEl.scrollHeight;
@@ -199,6 +211,38 @@ let KRChatbot = class KRChatbot extends LitElement {
199
211
  this._handleStop();
200
212
  }
201
213
  // --- Private methods ---
214
+ /**
215
+ * Shows a full-viewport transparent overlay during drag/resize operations.
216
+ *
217
+ * This overlay solves a critical rendering issue when the chatbot is used inside
218
+ * a page that contains iframes (e.g., the AI editor shell where the page content
219
+ * is rendered in an iframe and the chatbot floats on top).
220
+ *
221
+ * Without the overlay, modifying the iframe's `pointer-events` style during drag
222
+ * (to prevent it from swallowing mousemove events) causes Chrome to recalculate
223
+ * compositing layers. This layer recalculation blocks the browser from repainting
224
+ * the chatbot panel's style changes (width/height/position) even though they are
225
+ * correctly applied to the DOM. The result is that the panel appears frozen while
226
+ * the user drags, despite the inline styles updating on every mousemove.
227
+ *
228
+ * The overlay approach avoids this entirely: instead of touching the iframe's styles,
229
+ * we place a transparent div (z-index 9999) above the iframe but below the chatbot
230
+ * (z-index 10000). This div intercepts all mouse events that would otherwise hit the
231
+ * iframe, without triggering any compositing layer changes on the iframe itself.
232
+ */
233
+ _showOverlay() {
234
+ if (!this._overlay) {
235
+ this._overlay = document.createElement('div');
236
+ this._overlay.style.cssText = 'position:fixed; inset:0; z-index:9999; display:none;';
237
+ document.body.appendChild(this._overlay);
238
+ }
239
+ this._overlay.style.display = 'block';
240
+ }
241
+ _hideOverlay() {
242
+ if (this._overlay) {
243
+ this._overlay.style.display = 'none';
244
+ }
245
+ }
202
246
  _handleHeaderMouseDown(e) {
203
247
  // Ignore clicks on buttons inside the header
204
248
  if (e.target.closest('button')) {
@@ -208,7 +252,8 @@ let KRChatbot = class KRChatbot extends LitElement {
208
252
  return;
209
253
  }
210
254
  this._isDragging = true;
211
- this._panelEl.classList.add('chatbot__panel--dragging');
255
+ this._panelEl.classList.add('panel--dragging');
256
+ this._showOverlay();
212
257
  const rect = this._panelEl.getBoundingClientRect();
213
258
  this._panelEl.style.left = rect.left + 'px';
214
259
  this._panelEl.style.top = rect.top + 'px';
@@ -229,7 +274,8 @@ let KRChatbot = class KRChatbot extends LitElement {
229
274
  }
230
275
  this._isResizing = true;
231
276
  this._resizeDir = e.currentTarget.dataset.dir || '';
232
- this._panelEl.classList.add('chatbot__panel--resizing');
277
+ this._panelEl.classList.add('panel--resizing');
278
+ this._showOverlay();
233
279
  const rect = this._panelEl.getBoundingClientRect();
234
280
  this._panelEl.style.left = rect.left + 'px';
235
281
  this._panelEl.style.top = rect.top + 'px';
@@ -249,16 +295,75 @@ let KRChatbot = class KRChatbot extends LitElement {
249
295
  }
250
296
  _handleToggle() {
251
297
  this.opened = !this.opened;
252
- // Reset position when opening so it returns to default bottom-right
253
298
  if (this.opened && this._panelEl) {
254
- this._panelEl.style.left = '';
255
- this._panelEl.style.top = '';
256
- this._panelEl.style.right = '';
257
- this._panelEl.style.bottom = '';
258
- this._panelEl.style.width = '';
259
- this._panelEl.style.height = '';
260
- this._panelEl.style.position = '';
299
+ this._applyStoredLayout();
300
+ }
301
+ this._saveState();
302
+ }
303
+ _saveState() {
304
+ try {
305
+ const state = { opened: String(this.opened) };
306
+ if (this._panelEl) {
307
+ const s = this._panelEl.style;
308
+ if (s.left) {
309
+ state.left = s.left;
310
+ }
311
+ if (s.top) {
312
+ state.top = s.top;
313
+ }
314
+ if (s.width) {
315
+ state.width = s.width;
316
+ }
317
+ if (s.height) {
318
+ state.height = s.height;
319
+ }
320
+ if (s.position) {
321
+ state.position = s.position;
322
+ }
323
+ }
324
+ localStorage.setItem('kr-chatbot-state', JSON.stringify(state));
325
+ }
326
+ catch { /* localStorage not available */ }
327
+ }
328
+ _restoreState() {
329
+ try {
330
+ const raw = localStorage.getItem('kr-chatbot-state');
331
+ if (!raw) {
332
+ return;
333
+ }
334
+ const state = JSON.parse(raw);
335
+ if (state.opened === 'true') {
336
+ this.opened = true;
337
+ // Apply layout after first render
338
+ this.updateComplete.then(() => this._applyStoredLayout());
339
+ }
340
+ }
341
+ catch { /* localStorage not available */ }
342
+ }
343
+ _applyStoredLayout() {
344
+ try {
345
+ const raw = localStorage.getItem('kr-chatbot-state');
346
+ if (!raw || !this._panelEl) {
347
+ return;
348
+ }
349
+ const state = JSON.parse(raw);
350
+ if (state.position) {
351
+ this._panelEl.style.position = state.position;
352
+ }
353
+ if (state.left) {
354
+ this._panelEl.style.left = state.left;
355
+ }
356
+ if (state.top) {
357
+ this._panelEl.style.top = state.top;
358
+ }
359
+ if (state.width) {
360
+ this._panelEl.style.width = state.width;
361
+ }
362
+ if (state.height) {
363
+ this._panelEl.style.height = state.height;
364
+ }
261
365
  }
366
+ catch { /* localStorage not available */ }
262
367
  }
263
368
  _handleInputChange(e) {
264
369
  this._inputValue = e.target.value;
@@ -308,6 +413,27 @@ let KRChatbot = class KRChatbot extends LitElement {
308
413
  }];
309
414
  this._isStreaming = true;
310
415
  this._statusText = '';
416
+ // Scroll user's message to the top (Gemini-style).
417
+ // Set min-height on the assistant message = container height so there's enough
418
+ // scroll room below the user message to push it to the top.
419
+ this.updateComplete.then(() => {
420
+ if (!this._messagesEl) {
421
+ return;
422
+ }
423
+ // Clear min-height from previous responses
424
+ this._messagesEl.querySelectorAll('.message--assistant').forEach((el) => {
425
+ el.style.minHeight = '';
426
+ });
427
+ const allMsgEls = this._messagesEl.querySelectorAll('.message');
428
+ const userMsgEl = allMsgEls[this.messages.length - 2];
429
+ const assistantMsgEl = allMsgEls[this.messages.length - 1];
430
+ if (assistantMsgEl) {
431
+ assistantMsgEl.style.minHeight = this._messagesEl.clientHeight + 'px';
432
+ }
433
+ if (userMsgEl) {
434
+ this._messagesEl.scrollTop = userMsgEl.offsetTop - this._messagesEl.offsetTop - 24;
435
+ }
436
+ });
311
437
  this.send({
312
438
  message: messageText,
313
439
  messages: this.messages.slice(0, -1),
@@ -435,49 +561,6 @@ let KRChatbot = class KRChatbot extends LitElement {
435
561
  }
436
562
  }
437
563
  }
438
- else if (data.type === 'ACTION') {
439
- const action = data;
440
- const actionEvent = new CustomEvent('action', {
441
- detail: {
442
- action: action,
443
- },
444
- bubbles: true,
445
- composed: true,
446
- cancelable: true,
447
- });
448
- this.dispatchEvent(actionEvent);
449
- if (!actionEvent.defaultPrevented) {
450
- if (action.action === 'RELOAD') {
451
- location.reload();
452
- }
453
- else if (action.action === 'NAVIGATE') {
454
- if (action.data && action.data.url) {
455
- window.location.href = action.data.url;
456
- }
457
- }
458
- else if (action.action === 'OPEN_TAB') {
459
- if (action.data && action.data.url) {
460
- window.open(action.data.url, '_blank');
461
- }
462
- }
463
- else if (action.action === 'UPDATE_HTML') {
464
- if (action.data && action.data.selector && action.data.html) {
465
- const el = document.querySelector(action.data.selector);
466
- if (el) {
467
- el.innerHTML = action.data.html;
468
- }
469
- }
470
- }
471
- else if (action.action === 'SCROLL_TO') {
472
- if (action.data && action.data.selector) {
473
- const el = document.querySelector(action.data.selector);
474
- if (el) {
475
- el.scrollIntoView({ behavior: 'smooth' });
476
- }
477
- }
478
- }
479
- }
480
- }
481
564
  else if (data.type === 'ERROR') {
482
565
  this._handleStreamError(String(data.message || 'Unknown error'));
483
566
  }
@@ -511,157 +594,192 @@ let KRChatbot = class KRChatbot extends LitElement {
511
594
  }
512
595
  }
513
596
  }
514
- /**
515
- * Lightweight text formatting for assistant messages.
516
- * Handles code blocks, inline code, bold, and line breaks.
517
- * Escapes HTML entities first to prevent XSS.
518
- */
519
- _formatAssistantContent(text) {
520
- // Escape HTML entities
521
- let escaped = text
522
- .replace(/&/g, '&amp;')
523
- .replace(/</g, '&lt;')
524
- .replace(/>/g, '&gt;')
525
- .replace(/"/g, '&quot;')
526
- .replace(/'/g, '&#039;');
527
- // Code blocks: ```...```
528
- escaped = escaped.replace(/```(\w*)\n?([\s\S]*?)```/g, '<pre><code>$2</code></pre>');
529
- // Inline code: `...`
530
- escaped = escaped.replace(/`([^`]+)`/g, '<code>$1</code>');
531
- // Bold: **...**
532
- escaped = escaped.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
533
- // Split on code blocks to only convert newlines outside of pre tags
534
- const parts = escaped.split(/(<pre><code>[\s\S]*?<\/code><\/pre>)/g);
535
- for (let i = 0; i < parts.length; i++) {
536
- // Skip code blocks (odd indices from the split)
537
- if (i % 2 === 0) {
538
- // Convert double newlines to paragraph breaks
539
- parts[i] = parts[i].replace(/\n\n/g, '</p><p>');
540
- // Convert single newlines to line breaks
541
- parts[i] = parts[i].replace(/\n/g, '<br>');
542
- }
543
- }
544
- escaped = parts.join('');
545
- // Wrap in paragraph if we inserted paragraph breaks
546
- if (escaped.includes('</p><p>')) {
547
- escaped = '<p>' + escaped + '</p>';
548
- }
549
- return escaped;
550
- }
551
597
  // --- Render ---
552
598
  render() {
553
599
  return html `
554
600
  <button
555
601
  class=${classMap({
556
- 'chatbot__toggle': true,
557
- 'chatbot__toggle--hidden': this.opened,
602
+ 'toggle': true,
603
+ 'toggle--hidden': this.opened,
558
604
  })}
559
605
  @click=${this._handleToggle}
560
606
  title="Open chat"
561
607
  >
562
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" class="chatbot__toggle-icon">
608
+ <svg
609
+ viewBox="0 0 24 24"
610
+ fill="none"
611
+ stroke="currentColor"
612
+ stroke-width="2"
613
+ stroke-linecap="round"
614
+ class="toggle-icon"
615
+ >
563
616
  <path d="M12 2C6.48 2 2 6.03 2 11c0 2.61 1.19 4.94 3.05 6.55L4 22l4.8-2.4C9.82 19.85 10.88 20 12 20c5.52 0 10-4.03 10-9S17.52 2 12 2z"/>
564
617
  <path d="M8 11h.01M12 11h.01M16 11h.01" stroke-width="2.5"/>
565
618
  </svg>
566
619
  </button>
567
620
 
568
621
  <div class=${classMap({
569
- 'chatbot__panel': true,
570
- 'chatbot__panel--opened': this.opened,
622
+ 'panel': true,
623
+ 'panel--opened': this.opened,
571
624
  })}>
572
- <div class="chatbot__resize chatbot__resize--n" data-dir="n" @mousedown=${this._handleResizeMouseDown}></div>
573
- <div class="chatbot__resize chatbot__resize--s" data-dir="s" @mousedown=${this._handleResizeMouseDown}></div>
574
- <div class="chatbot__resize chatbot__resize--w" data-dir="w" @mousedown=${this._handleResizeMouseDown}></div>
575
- <div class="chatbot__resize chatbot__resize--e" data-dir="e" @mousedown=${this._handleResizeMouseDown}></div>
576
- <div class="chatbot__resize chatbot__resize--nw" data-dir="nw" @mousedown=${this._handleResizeMouseDown}></div>
577
- <div class="chatbot__resize chatbot__resize--ne" data-dir="ne" @mousedown=${this._handleResizeMouseDown}></div>
578
- <div class="chatbot__resize chatbot__resize--sw" data-dir="sw" @mousedown=${this._handleResizeMouseDown}></div>
579
- <div class="chatbot__resize chatbot__resize--se" data-dir="se" @mousedown=${this._handleResizeMouseDown}></div>
580
- <div class="chatbot__header" @mousedown=${this._handleHeaderMouseDown}>
581
- <div class="chatbot__header-left">
582
- <div class="chatbot__header-drag-hint">
583
- <span></span>
584
- <span></span>
585
- <span></span>
586
- </div>
587
- <div class="chatbot__header-info">
588
- <span class="chatbot__header-title">${this.title}</span>
625
+ <div
626
+ class="resize resize--n"
627
+ data-dir="n"
628
+ @mousedown=${this._handleResizeMouseDown}
629
+ ></div>
630
+ <div
631
+ class="resize resize--s"
632
+ data-dir="s"
633
+ @mousedown=${this._handleResizeMouseDown}
634
+ ></div>
635
+ <div
636
+ class="resize resize--w"
637
+ data-dir="w"
638
+ @mousedown=${this._handleResizeMouseDown}
639
+ ></div>
640
+ <div
641
+ class="resize resize--e"
642
+ data-dir="e"
643
+ @mousedown=${this._handleResizeMouseDown}
644
+ ></div>
645
+ <div
646
+ class="resize resize--nw"
647
+ data-dir="nw"
648
+ @mousedown=${this._handleResizeMouseDown}
649
+ ></div>
650
+ <div
651
+ class="resize resize--ne"
652
+ data-dir="ne"
653
+ @mousedown=${this._handleResizeMouseDown}
654
+ ></div>
655
+ <div
656
+ class="resize resize--sw"
657
+ data-dir="sw"
658
+ @mousedown=${this._handleResizeMouseDown}
659
+ ></div>
660
+ <div
661
+ class="resize resize--se"
662
+ data-dir="se"
663
+ @mousedown=${this._handleResizeMouseDown}
664
+ ></div>
665
+ <div class="header" @mousedown=${this._handleHeaderMouseDown}>
666
+ <div class="header-left">
667
+ <div class="header-info">
668
+ <span class="header-title">${this.title}</span>
589
669
  ${this.subtitle ? html `
590
- <span class="chatbot__header-subtitle">${this.subtitle}</span>
670
+ <span class="header-subtitle">${this.subtitle}</span>
591
671
  ` : nothing}
592
672
  </div>
593
673
  </div>
594
- <div class="chatbot__header-actions">
674
+ <div class="header-actions">
595
675
  ${this.messages.length > 0 ? html `
596
- <button class="chatbot__header-action" @click=${this._handleClear} title="Clear conversation">
597
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="chatbot__header-action-icon">
598
- <path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
676
+ <button
677
+ class="header-action"
678
+ @click=${this._handleClear}
679
+ title="Clear conversation"
680
+ >
681
+ <svg
682
+ xmlns="http://www.w3.org/2000/svg"
683
+ fill="none"
684
+ viewBox="0 0 24 24"
685
+ stroke-width="1.5"
686
+ stroke="currentColor"
687
+ class="header-action-icon"
688
+ >
689
+ <path
690
+ stroke-linecap="round"
691
+ stroke-linejoin="round"
692
+ d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
693
+ />
599
694
  </svg>
600
695
  </button>
601
696
  ` : nothing}
602
- <button class="chatbot__header-action" @click=${this._handleToggle} title="Close">
603
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="chatbot__header-action-icon">
604
- <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
697
+ <button
698
+ class="header-action"
699
+ @click=${this._handleToggle}
700
+ title="Close"
701
+ >
702
+ <svg
703
+ xmlns="http://www.w3.org/2000/svg"
704
+ fill="none"
705
+ viewBox="0 0 24 24"
706
+ stroke-width="2"
707
+ stroke="currentColor"
708
+ class="header-action-icon"
709
+ >
710
+ <path
711
+ stroke-linecap="round"
712
+ stroke-linejoin="round"
713
+ d="M6 18 18 6M6 6l12 12"
714
+ />
605
715
  </svg>
606
716
  </button>
607
717
  </div>
608
718
  </div>
609
719
 
610
- <div class="chatbot__messages" @scroll=${this._handleMessagesScroll}>
720
+ <div class="messages" @scroll=${this._handleMessagesScroll}>
611
721
  ${this.messages.length === 0 ? html `
612
- <div class="chatbot__empty">
613
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" class="chatbot__empty-icon">
614
- <path stroke-linecap="round" stroke-linejoin="round" d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.076-4.076a1.526 1.526 0 0 1 1.037-.443 48.282 48.282 0 0 0 5.68-.494c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z" />
615
- </svg>
616
- <span class="chatbot__empty-text">How can I help?</span>
722
+ <div class="empty">
723
+ <span class="empty-text">What can I help you with?</span>
617
724
  </div>
618
725
  ` : nothing}
619
726
  ${this.messages.map((msg) => html `
620
727
  <div class=${classMap({
621
- 'chatbot__message': true,
622
- 'chatbot__message--user': msg.role === 'user',
623
- 'chatbot__message--assistant': msg.role === 'assistant',
728
+ 'message': true,
729
+ 'message--user': msg.role === 'user',
730
+ 'message--assistant': msg.role === 'assistant',
624
731
  })}>
625
732
  ${msg.reasoning ? html `
626
- <details class="chatbot__reasoning">
733
+ <details class="reasoning">
627
734
  <summary>Thinking</summary>
628
- <div class="chatbot__reasoning-content">${msg.reasoning}</div>
735
+ <div class="reasoning-content">${msg.reasoning}</div>
629
736
  </details>
630
737
  ` : nothing}
631
- <div class="chatbot__message-content">
632
- ${msg.role === 'user'
738
+ ${msg.content ? html `
739
+ <div class="message-content">
740
+ ${msg.role === 'user'
633
741
  ? msg.content
634
- : unsafeHTML(this._formatAssistantContent(msg.content))}
635
- </div>
742
+ : unsafeHTML(marked.parse(msg.content, { breaks: true }))}
743
+ </div>
744
+ ` : nothing}
745
+ ${msg.role === 'assistant' && this._isStreaming && msg === this.messages[this.messages.length - 1] ? html `
746
+ <div class="typing">
747
+ <span></span>
748
+ <span></span>
749
+ <span></span>
750
+ </div>
751
+ ` : nothing}
636
752
  </div>
637
753
  `)}
638
- ${this._statusText ? html `
639
- <div class="chatbot__message chatbot__message--assistant">
640
- <div class="chatbot__typing">
641
- <span></span>
642
- <span></span>
643
- <span></span>
644
- </div>
645
- </div>
646
- ` : nothing}
647
754
  </div>
648
755
 
649
756
  ${this._error ? html `
650
- <div class="chatbot__error">
757
+ <div class="error">
651
758
  <span>${this._error}</span>
652
- <button class="chatbot__error-dismiss" @click=${this._handleErrorDismiss}>
653
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="chatbot__error-dismiss-icon">
654
- <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
759
+ <button class="error-dismiss" @click=${this._handleErrorDismiss}>
760
+ <svg
761
+ xmlns="http://www.w3.org/2000/svg"
762
+ fill="none"
763
+ viewBox="0 0 24 24"
764
+ stroke-width="2"
765
+ stroke="currentColor"
766
+ class="error-dismiss-icon"
767
+ >
768
+ <path
769
+ stroke-linecap="round"
770
+ stroke-linejoin="round"
771
+ d="M6 18 18 6M6 6l12 12"
772
+ />
655
773
  </svg>
656
774
  </button>
657
775
  </div>
658
776
  ` : nothing}
659
777
 
660
- <div class="chatbot__footer">
661
- <div class="chatbot__input-wrapper">
662
- <button class="chatbot__input-plus" title="Attach">+</button>
778
+ <div class="footer">
779
+ <div class="input-wrapper">
780
+ <button class="input-plus" title="Attach">+</button>
663
781
  <textarea
664
- class="chatbot__input"
782
+ class="input"
665
783
  .value=${this._inputValue}
666
784
  placeholder=${this.placeholder}
667
785
  rows="1"
@@ -669,21 +787,45 @@ let KRChatbot = class KRChatbot extends LitElement {
669
787
  @keydown=${this._handleKeyDown}
670
788
  ></textarea>
671
789
  ${this._isStreaming ? html `
672
- <button class="chatbot__stop" @click=${this._handleStop} title="Stop generating">
673
- <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" class="chatbot__stop-icon">
674
- <rect x="6" y="6" width="12" height="12" rx="2" />
790
+ <button
791
+ class="stop"
792
+ @click=${this._handleStop}
793
+ title="Stop generating"
794
+ >
795
+ <svg
796
+ xmlns="http://www.w3.org/2000/svg"
797
+ fill="currentColor"
798
+ viewBox="0 0 24 24"
799
+ class="stop-icon"
800
+ >
801
+ <rect
802
+ x="6"
803
+ y="6"
804
+ width="12"
805
+ height="12"
806
+ rx="2"
807
+ />
675
808
  </svg>
676
809
  </button>
677
810
  ` : html `
678
811
  <button
679
812
  class=${classMap({
680
- 'chatbot__send': true,
681
- 'chatbot__send--disabled': !this._inputValue.trim(),
813
+ 'send': true,
814
+ 'send--disabled': !this._inputValue.trim(),
682
815
  })}
683
816
  @click=${this._handleSubmit}
684
817
  title="Send message"
685
818
  >
686
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="chatbot__send-icon">
819
+ <svg
820
+ width="16"
821
+ height="16"
822
+ viewBox="0 0 24 24"
823
+ fill="none"
824
+ stroke="currentColor"
825
+ stroke-width="2.5"
826
+ stroke-linecap="round"
827
+ class="send-icon"
828
+ >
687
829
  <path d="M12 19V5M5 12l7-7 7 7"/>
688
830
  </svg>
689
831
  </button>
@@ -714,7 +856,6 @@ KRChatbot.styles = css `
714
856
  --kr-chatbot-text: #1a1a2e;
715
857
  --kr-chatbot-text-secondary: #64668b;
716
858
  --kr-chatbot-user-bg: #e9e9e980;
717
- --kr-chatbot-user-text: #1a1a2e;
718
859
  --kr-chatbot-error-bg: #fef2f2;
719
860
  --kr-chatbot-error-text: #dc2626;
720
861
  --kr-chatbot-success: #00b894;
@@ -731,7 +872,7 @@ KRChatbot.styles = css `
731
872
  }
732
873
 
733
874
  /* Toggle button */
734
- .chatbot__toggle {
875
+ .toggle {
735
876
  width: var(--kr-chatbot-toggle-size);
736
877
  height: var(--kr-chatbot-toggle-size);
737
878
  border-radius: 18px;
@@ -746,24 +887,24 @@ KRChatbot.styles = css `
746
887
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
747
888
  }
748
889
 
749
- .chatbot__toggle:hover {
890
+ .toggle:hover {
750
891
  transform: scale(1.05);
751
892
  box-shadow: 0 6px 32px rgba(22, 48, 82, 0.5);
752
893
  }
753
894
 
754
- .chatbot__toggle--hidden {
895
+ .toggle--hidden {
755
896
  transform: scale(0);
756
897
  opacity: 0;
757
898
  pointer-events: none;
758
899
  }
759
900
 
760
- .chatbot__toggle-icon {
901
+ .toggle-icon {
761
902
  width: 26px;
762
903
  height: 26px;
763
904
  }
764
905
 
765
906
  /* Panel */
766
- .chatbot__panel {
907
+ .panel {
767
908
  display: none;
768
909
  flex-direction: column;
769
910
  background: var(--kr-chatbot-bg);
@@ -781,7 +922,7 @@ KRChatbot.styles = css `
781
922
  transform-origin: bottom right;
782
923
  }
783
924
 
784
- .chatbot__panel--opened {
925
+ .panel--opened {
785
926
  display: flex;
786
927
  }
787
928
 
@@ -797,49 +938,35 @@ KRChatbot.styles = css `
797
938
  }
798
939
 
799
940
  /* Header */
800
- .chatbot__header {
801
- padding: 10px 16px;
802
- background: var(--kr-chatbot-primary);
803
- color: white;
941
+ .header {
942
+ padding: 0 16px 0 24px;
943
+ height: 64px;
944
+ background: #ffffff;
945
+ color: #000000;
946
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
804
947
  display: flex;
805
948
  align-items: center;
806
949
  justify-content: space-between;
807
950
  flex-shrink: 0;
808
951
  }
809
952
 
810
- .chatbot__header-left {
953
+ .header-left {
811
954
  display: flex;
812
955
  align-items: center;
813
956
  gap: 10px;
814
957
  }
815
958
 
816
- .chatbot__header-drag-hint {
817
- display: flex;
818
- flex-direction: column;
819
- gap: 2px;
820
- opacity: 0.35;
821
- }
822
-
823
- .chatbot__header-drag-hint span {
824
- display: block;
825
- width: 14px;
826
- height: 2px;
827
- background: white;
828
- border-radius: 1px;
829
- }
830
-
831
- .chatbot__header-info {
959
+ .header-info {
832
960
  display: flex;
833
961
  flex-direction: column;
834
962
  }
835
963
 
836
- .chatbot__header-title {
837
- font-size: 13px;
964
+ .header-title {
965
+ font-size: 16px;
838
966
  font-weight: 600;
839
- letter-spacing: -0.2px;
840
967
  }
841
968
 
842
- .chatbot__header-subtitle {
969
+ .header-subtitle {
843
970
  color: rgba(255, 255, 255, 0.5);
844
971
  font-size: 11px;
845
972
  display: flex;
@@ -847,7 +974,7 @@ KRChatbot.styles = css `
847
974
  gap: 4px;
848
975
  }
849
976
 
850
- .chatbot__header-subtitle::before {
977
+ .header-subtitle::before {
851
978
  content: '';
852
979
  width: 5px;
853
980
  height: 5px;
@@ -855,18 +982,18 @@ KRChatbot.styles = css `
855
982
  border-radius: 50%;
856
983
  }
857
984
 
858
- .chatbot__header-actions {
985
+ .header-actions {
859
986
  display: flex;
860
987
  align-items: center;
861
988
  gap: 4px;
862
989
  }
863
990
 
864
- .chatbot__header-action {
865
- width: 28px;
866
- height: 28px;
991
+ .header-action {
992
+ width: 32px;
993
+ height: 32px;
867
994
  background: transparent;
868
995
  border: none;
869
- color: rgba(255, 255, 255, 0.6);
996
+ color: rgb(0 0 0 / 70%);
870
997
  border-radius: 8px;
871
998
  cursor: pointer;
872
999
  display: flex;
@@ -875,18 +1002,18 @@ KRChatbot.styles = css `
875
1002
  transition: all 0.15s;
876
1003
  }
877
1004
 
878
- .chatbot__header-action:hover {
879
- background: rgba(255, 255, 255, 0.1);
880
- color: white;
1005
+ .header-action:hover {
1006
+ background: rgba(0, 0, 0, 0.05);
1007
+ color: #000000;
881
1008
  }
882
1009
 
883
- .chatbot__header-action-icon {
884
- width: 16px;
885
- height: 16px;
1010
+ .header-action-icon {
1011
+ width: 20px;
1012
+ height: 20px;
886
1013
  }
887
1014
 
888
1015
  /* Messages */
889
- .chatbot__messages {
1016
+ .messages {
890
1017
  flex: 1;
891
1018
  overflow-y: auto;
892
1019
  padding: 32px;
@@ -897,20 +1024,20 @@ KRChatbot.styles = css `
897
1024
  background: white;
898
1025
  }
899
1026
 
900
- .chatbot__messages::-webkit-scrollbar {
1027
+ .messages::-webkit-scrollbar {
901
1028
  width: 4px;
902
1029
  }
903
1030
 
904
- .chatbot__messages::-webkit-scrollbar-track {
1031
+ .messages::-webkit-scrollbar-track {
905
1032
  background: transparent;
906
1033
  }
907
1034
 
908
- .chatbot__messages::-webkit-scrollbar-thumb {
1035
+ .messages::-webkit-scrollbar-thumb {
909
1036
  background: rgba(0, 0, 0, 0.08);
910
1037
  border-radius: 2px;
911
1038
  }
912
1039
 
913
- .chatbot__empty {
1040
+ .empty {
914
1041
  flex: 1;
915
1042
  display: flex;
916
1043
  flex-direction: column;
@@ -920,17 +1047,12 @@ KRChatbot.styles = css `
920
1047
  gap: 8px;
921
1048
  }
922
1049
 
923
- .chatbot__empty-icon {
924
- width: 40px;
925
- height: 40px;
926
- opacity: 0.4;
927
- }
928
-
929
- .chatbot__empty-text {
930
- font-size: 13px;
1050
+ .empty-text {
1051
+ font-size: 14px;
1052
+ color: rgb(0 0 0 / 80%);
931
1053
  }
932
1054
 
933
- .chatbot__message {
1055
+ .message {
934
1056
  display: flex;
935
1057
  flex-direction: column;
936
1058
  max-width: 100%;
@@ -948,40 +1070,41 @@ KRChatbot.styles = css `
948
1070
  }
949
1071
  }
950
1072
 
951
- .chatbot__message--user {
1073
+ .message--user {
952
1074
  align-self: flex-end;
953
1075
  align-items: flex-end;
954
1076
  max-width: 85%;
955
1077
  }
956
1078
 
957
- .chatbot__message--assistant {
1079
+ .message--assistant {
958
1080
  align-self: flex-start;
1081
+ gap: 24px;
959
1082
  }
960
1083
 
961
- .chatbot__message-content {
1084
+ .message-content {
962
1085
  line-height: 1.7;
963
1086
  word-wrap: break-word;
964
1087
  overflow-wrap: break-word;
965
1088
  }
966
1089
 
967
- .chatbot__message--user .chatbot__message-content {
1090
+ .message--user .message-content {
968
1091
  display: inline-block;
969
1092
  background: var(--kr-chatbot-user-bg);
970
- color: var(--kr-chatbot-user-text);
1093
+ color: #000000;
971
1094
  padding: 10px 16px;
972
1095
  border-radius: 20px;
973
1096
  border-bottom-right-radius: 6px;
974
1097
  font-size: 14px;
975
1098
  }
976
1099
 
977
- .chatbot__message--assistant .chatbot__message-content {
1100
+ .message--assistant .message-content {
978
1101
  background: transparent;
979
- color: var(--kr-chatbot-text);
1102
+ color: #000000;
980
1103
  padding: 0;
981
1104
  font-size: 14px;
982
1105
  }
983
1106
 
984
- .chatbot__message--assistant .chatbot__message-content pre {
1107
+ .message--assistant .message-content pre {
985
1108
  background: #1e1e1e;
986
1109
  color: #d4d4d4;
987
1110
  padding: 12px;
@@ -992,67 +1115,57 @@ KRChatbot.styles = css `
992
1115
  line-height: 1.4;
993
1116
  }
994
1117
 
995
- .chatbot__message--assistant .chatbot__message-content code {
1118
+ .message--assistant .message-content code {
996
1119
  font-family: 'SF Mono', 'Fira Code', Monaco, monospace;
997
1120
  font-size: 13px;
998
1121
  }
999
1122
 
1000
- .chatbot__message--assistant .chatbot__message-content :not(pre) > code {
1123
+ .message--assistant .message-content :not(pre) > code {
1001
1124
  background: rgba(0, 0, 0, 0.06);
1002
1125
  padding: 2px 5px;
1003
1126
  border-radius: 3px;
1004
1127
  }
1005
1128
 
1006
- .chatbot__message--assistant .chatbot__message-content strong {
1129
+ .message--assistant .message-content strong {
1007
1130
  font-weight: 600;
1008
1131
  }
1009
1132
 
1010
- .chatbot__message--assistant .chatbot__message-content p {
1133
+ .message--assistant .message-content p {
1011
1134
  margin: 0 0 8px 0;
1012
1135
  }
1013
1136
 
1014
- .chatbot__message--assistant .chatbot__message-content p:last-child {
1137
+ .message--assistant .message-content p:last-child {
1015
1138
  margin-bottom: 0;
1016
1139
  }
1017
1140
 
1018
1141
  /* Reasoning */
1019
- .chatbot__reasoning {
1020
- margin-bottom: 8px;
1142
+ .reasoning {
1021
1143
  border-radius: 6px;
1022
- background: rgba(0, 0, 0, 0.03);
1144
+ background: rgb(244 244 244 / 80%);
1023
1145
  font-size: 12px;
1024
1146
  }
1025
1147
 
1026
- .chatbot__reasoning summary {
1148
+ .reasoning summary {
1027
1149
  cursor: pointer;
1028
- padding: 6px 10px;
1029
- color: var(--kr-chatbot-text-secondary);
1030
- font-style: italic;
1150
+ padding: 6px 10px 6px 10px;
1151
+ color: black;
1031
1152
  user-select: none;
1032
1153
  }
1033
1154
 
1034
- .chatbot__reasoning-content {
1155
+ .reasoning-content {
1035
1156
  padding: 6px 10px 10px;
1036
- color: var(--kr-chatbot-text-secondary);
1037
- line-height: 1.5;
1157
+ color: black;
1038
1158
  white-space: pre-wrap;
1039
1159
  word-wrap: break-word;
1040
1160
  }
1041
1161
 
1042
- /* Status */
1043
- .chatbot__status {
1044
- font-size: 13px;
1045
- color: var(--kr-chatbot-text-secondary);
1046
- font-style: italic;
1047
- }
1048
-
1049
- .chatbot__typing {
1162
+ .typing {
1050
1163
  display: flex;
1051
1164
  gap: 4px;
1052
1165
  padding: 4px 0;
1053
1166
  }
1054
1167
 
1055
- .chatbot__typing span {
1168
+ .typing span {
1056
1169
  width: 6px;
1057
1170
  height: 6px;
1058
1171
  background: var(--kr-chatbot-text-secondary);
@@ -1061,11 +1174,11 @@ KRChatbot.styles = css `
1061
1174
  opacity: 0.4;
1062
1175
  }
1063
1176
 
1064
- .chatbot__typing span:nth-child(2) {
1177
+ .typing span:nth-child(2) {
1065
1178
  animation-delay: 0.2s;
1066
1179
  }
1067
1180
 
1068
- .chatbot__typing span:nth-child(3) {
1181
+ .typing span:nth-child(3) {
1069
1182
  animation-delay: 0.4s;
1070
1183
  }
1071
1184
 
@@ -1081,7 +1194,7 @@ KRChatbot.styles = css `
1081
1194
  }
1082
1195
 
1083
1196
  /* Error */
1084
- .chatbot__error {
1197
+ .error {
1085
1198
  padding: 8px 16px;
1086
1199
  background: var(--kr-chatbot-error-bg);
1087
1200
  color: var(--kr-chatbot-error-text);
@@ -1093,7 +1206,7 @@ KRChatbot.styles = css `
1093
1206
  flex-shrink: 0;
1094
1207
  }
1095
1208
 
1096
- .chatbot__error-dismiss {
1209
+ .error-dismiss {
1097
1210
  background: none;
1098
1211
  border: none;
1099
1212
  color: var(--kr-chatbot-error-text);
@@ -1104,19 +1217,19 @@ KRChatbot.styles = css `
1104
1217
  flex-shrink: 0;
1105
1218
  }
1106
1219
 
1107
- .chatbot__error-dismiss-icon {
1220
+ .error-dismiss-icon {
1108
1221
  width: 14px;
1109
1222
  height: 14px;
1110
1223
  }
1111
1224
 
1112
1225
  /* Footer / Input */
1113
- .chatbot__footer {
1114
- padding: 12px 16px 16px;
1226
+ .footer {
1227
+ padding: 16px 16px 16px;
1115
1228
  flex-shrink: 0;
1116
1229
  background: white;
1117
1230
  }
1118
1231
 
1119
- .chatbot__input-wrapper {
1232
+ .input-wrapper {
1120
1233
  display: flex;
1121
1234
  align-items: center;
1122
1235
  gap: 4px;
@@ -1127,12 +1240,12 @@ KRChatbot.styles = css `
1127
1240
  transition: border-color 0.2s, box-shadow 0.2s;
1128
1241
  }
1129
1242
 
1130
- .chatbot__input-wrapper:focus-within {
1243
+ .input-wrapper:focus-within {
1131
1244
  border-color: #b0b0b0;
1132
1245
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04);
1133
1246
  }
1134
1247
 
1135
- .chatbot__input-plus {
1248
+ .input-plus {
1136
1249
  width: 34px;
1137
1250
  height: 34px;
1138
1251
  border: none;
@@ -1149,11 +1262,11 @@ KRChatbot.styles = css `
1149
1262
  transition: background 0.15s;
1150
1263
  }
1151
1264
 
1152
- .chatbot__input-plus:hover {
1265
+ .input-plus:hover {
1153
1266
  background: #f8f9fc;
1154
1267
  }
1155
1268
 
1156
- .chatbot__input {
1269
+ .input {
1157
1270
  flex: 1;
1158
1271
  border: none;
1159
1272
  outline: none;
@@ -1165,14 +1278,14 @@ KRChatbot.styles = css `
1165
1278
  min-height: 26px;
1166
1279
  max-height: 120px;
1167
1280
  background: transparent;
1168
- color: var(--kr-chatbot-text);
1281
+ color: black;
1169
1282
  }
1170
1283
 
1171
- .chatbot__input::placeholder {
1284
+ .input::placeholder {
1172
1285
  color: #b0b0b0;
1173
1286
  }
1174
1287
 
1175
- .chatbot__send {
1288
+ .send {
1176
1289
  width: 34px;
1177
1290
  height: 34px;
1178
1291
  border-radius: 50%;
@@ -1187,25 +1300,25 @@ KRChatbot.styles = css `
1187
1300
  transition: all 0.15s;
1188
1301
  }
1189
1302
 
1190
- .chatbot__send:hover {
1303
+ .send:hover {
1191
1304
  background: var(--kr-chatbot-primary-hover);
1192
1305
  }
1193
1306
 
1194
- .chatbot__send--disabled {
1307
+ .send--disabled {
1195
1308
  opacity: 0.4;
1196
1309
  cursor: default;
1197
1310
  }
1198
1311
 
1199
- .chatbot__send--disabled:hover {
1312
+ .send--disabled:hover {
1200
1313
  background: var(--kr-chatbot-primary);
1201
1314
  }
1202
1315
 
1203
- .chatbot__send-icon {
1316
+ .send-icon {
1204
1317
  width: 16px;
1205
1318
  height: 16px;
1206
1319
  }
1207
1320
 
1208
- .chatbot__stop {
1321
+ .stop {
1209
1322
  width: 34px;
1210
1323
  height: 34px;
1211
1324
  border-radius: 50%;
@@ -1220,62 +1333,51 @@ KRChatbot.styles = css `
1220
1333
  transition: all 0.15s;
1221
1334
  }
1222
1335
 
1223
- .chatbot__stop:hover {
1336
+ .stop:hover {
1224
1337
  background: #b91c1c;
1225
1338
  }
1226
1339
 
1227
- .chatbot__stop-icon {
1340
+ .stop-icon {
1228
1341
  width: 14px;
1229
1342
  height: 14px;
1230
1343
  }
1231
1344
 
1232
1345
  /* Header drag cursor */
1233
- .chatbot__header {
1346
+ .header {
1234
1347
  cursor: grab;
1235
1348
  user-select: none;
1236
1349
  }
1237
1350
 
1238
- .chatbot__header:active {
1351
+ .header:active {
1239
1352
  cursor: grabbing;
1240
1353
  }
1241
1354
 
1242
1355
  /* Drag / Resize states */
1243
- .chatbot__panel--dragging,
1244
- .chatbot__panel--resizing {
1356
+ .panel--dragging,
1357
+ .panel--resizing {
1245
1358
  transition: none;
1246
1359
  user-select: none;
1247
1360
  }
1248
1361
 
1249
- .chatbot__panel--dragging {
1362
+ .panel--dragging {
1250
1363
  box-shadow: 0 32px 96px rgba(0, 0, 0, 0.18), 0 12px 40px rgba(0, 0, 0, 0.1);
1251
1364
  }
1252
1365
 
1253
1366
  /* Resize handles */
1254
- .chatbot__resize {
1367
+ .resize {
1255
1368
  position: absolute;
1256
1369
  z-index: 10;
1257
1370
  }
1258
1371
 
1259
- .chatbot__resize--n { top: -4px; left: 12px; right: 12px; height: 8px; cursor: n-resize; }
1260
- .chatbot__resize--s { bottom: -4px; left: 12px; right: 12px; height: 8px; cursor: s-resize; }
1261
- .chatbot__resize--w { left: -4px; top: 12px; bottom: 12px; width: 8px; cursor: w-resize; }
1262
- .chatbot__resize--e { right: -4px; top: 12px; bottom: 12px; width: 8px; cursor: e-resize; }
1263
- .chatbot__resize--nw { top: -4px; left: -4px; width: 16px; height: 16px; cursor: nw-resize; }
1264
- .chatbot__resize--ne { top: -4px; right: -4px; width: 16px; height: 16px; cursor: ne-resize; }
1265
- .chatbot__resize--sw { bottom: -4px; left: -4px; width: 16px; height: 16px; cursor: sw-resize; }
1266
- .chatbot__resize--se { bottom: -4px; right: -4px; width: 16px; height: 16px; cursor: se-resize; }
1372
+ .resize--n { top: -4px; left: 12px; right: 12px; height: 8px; cursor: n-resize; }
1373
+ .resize--s { bottom: -4px; left: 12px; right: 12px; height: 8px; cursor: s-resize; }
1374
+ .resize--w { left: -4px; top: 12px; bottom: 12px; width: 8px; cursor: w-resize; }
1375
+ .resize--e { right: -4px; top: 12px; bottom: 12px; width: 8px; cursor: e-resize; }
1376
+ .resize--nw { top: -4px; left: -4px; width: 16px; height: 16px; cursor: nw-resize; }
1377
+ .resize--ne { top: -4px; right: -4px; width: 16px; height: 16px; cursor: ne-resize; }
1378
+ .resize--sw { bottom: -4px; left: -4px; width: 16px; height: 16px; cursor: sw-resize; }
1379
+ .resize--se { bottom: -4px; right: -4px; width: 16px; height: 16px; cursor: se-resize; }
1267
1380
 
1268
- .chatbot__resize--se::after {
1269
- content: '';
1270
- position: absolute;
1271
- bottom: 7px;
1272
- right: 7px;
1273
- width: 10px;
1274
- height: 10px;
1275
- border-right: 2px solid rgba(0, 0, 0, 0.12);
1276
- border-bottom: 2px solid rgba(0, 0, 0, 0.12);
1277
- border-radius: 0 0 3px 0;
1278
- }
1279
1381
  `;
1280
1382
  __decorate([
1281
1383
  property({ attribute: false })
@@ -1283,6 +1385,9 @@ __decorate([
1283
1385
  __decorate([
1284
1386
  property({ attribute: false })
1285
1387
  ], KRChatbot.prototype, "clear", void 0);
1388
+ __decorate([
1389
+ property({ attribute: false })
1390
+ ], KRChatbot.prototype, "load", void 0);
1286
1391
  __decorate([
1287
1392
  property({ attribute: false })
1288
1393
  ], KRChatbot.prototype, "messages", void 0);
@@ -1296,7 +1401,10 @@ __decorate([
1296
1401
  property({ type: String })
1297
1402
  ], KRChatbot.prototype, "placeholder", void 0);
1298
1403
  __decorate([
1299
- property({ type: Boolean, reflect: true })
1404
+ property({
1405
+ type: Boolean,
1406
+ reflect: true
1407
+ })
1300
1408
  ], KRChatbot.prototype, "opened", void 0);
1301
1409
  __decorate([
1302
1410
  state()
@@ -1311,13 +1419,13 @@ __decorate([
1311
1419
  state()
1312
1420
  ], KRChatbot.prototype, "_error", void 0);
1313
1421
  __decorate([
1314
- query('.chatbot__panel')
1422
+ query('.panel')
1315
1423
  ], KRChatbot.prototype, "_panelEl", void 0);
1316
1424
  __decorate([
1317
- query('.chatbot__messages')
1425
+ query('.messages')
1318
1426
  ], KRChatbot.prototype, "_messagesEl", void 0);
1319
1427
  __decorate([
1320
- query('.chatbot__input')
1428
+ query('.input')
1321
1429
  ], KRChatbot.prototype, "_inputEl", void 0);
1322
1430
  KRChatbot = __decorate([
1323
1431
  customElement('kr-chatbot')