@kodaris/krubble-app-components 1.0.61 → 1.0.63

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.
@@ -0,0 +1,1320 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { LitElement, html, css, nothing } from 'lit';
8
+ import { customElement, property, state, query } from 'lit/decorators.js';
9
+ import { classMap } from 'lit/directives/class-map.js';
10
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
11
+ /**
12
+ * AI chatbot component with SSE streaming and native browser action execution.
13
+ *
14
+ * 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.).
18
+ *
19
+ * ## SSE Stream Format
20
+ * The component expects `data:` lines with JSON payloads containing a `type` field:
21
+ * - `STATUS` — `{ type: "STATUS", message: "Thinking..." }`
22
+ * - `RESPONSE_PART` — `{ type: "RESPONSE_PART", message: "token text" }`
23
+ * - `ACTION` — `{ type: "ACTION", action: "RELOAD" }` (see KRChatbotAction)
24
+ * - `ERROR` — `{ type: "ERROR", message: "Something went wrong" }`
25
+ * - `HEARTBEAT` — `{ type: "HEARTBEAT" }` (ignored)
26
+ *
27
+ * The final event should include `finished: true` to signal stream completion.
28
+ *
29
+ * ## Usage
30
+ * ```html
31
+ * <kr-chatbot
32
+ * title="AI Assistant"
33
+ * .send=${(params) => {
34
+ * return fetch('/api/ai/chat/stream', {
35
+ * method: 'POST',
36
+ * headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' },
37
+ * credentials: 'include',
38
+ * body: JSON.stringify({ userText: params.message })
39
+ * });
40
+ * }}
41
+ * ></kr-chatbot>
42
+ * ```
43
+ *
44
+ * @slot - Optional slot content displayed above the messages area
45
+ *
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 }`
48
+ */
49
+ let KRChatbot = class KRChatbot extends LitElement {
50
+ constructor() {
51
+ super(...arguments);
52
+ // --- Private properties ---
53
+ this._reader = null;
54
+ this._decoder = new TextDecoder();
55
+ this._buffer = '';
56
+ this._userHasScrolledUp = false;
57
+ this._isDragging = false;
58
+ this._isResizing = false;
59
+ this._resizeDir = '';
60
+ this._dragStartX = 0;
61
+ this._dragStartY = 0;
62
+ this._dragStartLeft = 0;
63
+ this._dragStartTop = 0;
64
+ this._resizeStartW = 0;
65
+ this._resizeStartH = 0;
66
+ // --- @property (public API) ---
67
+ /**
68
+ * Callback function for sending messages. Receives the user's message and
69
+ * current conversation history. Must return a Response with a streaming SSE body.
70
+ */
71
+ this.send = null;
72
+ /**
73
+ * Callback function called when the user clears the conversation.
74
+ * Must return a Promise — messages are only cleared if it resolves.
75
+ */
76
+ this.clear = null;
77
+ /**
78
+ * Conversation messages. Can be set externally for initial messages.
79
+ * Updated internally as the conversation progresses.
80
+ */
81
+ this.messages = [];
82
+ /**
83
+ * Header title text.
84
+ */
85
+ this.title = 'AI Assistant';
86
+ /**
87
+ * Header subtitle text (shown below title with a green status dot).
88
+ */
89
+ this.subtitle = '';
90
+ /**
91
+ * Input placeholder text.
92
+ */
93
+ this.placeholder = 'Type a message...';
94
+ /**
95
+ * Whether the chat panel is open. Only relevant in floating mode.
96
+ */
97
+ this.opened = false;
98
+ // --- @state (internal) ---
99
+ this._inputValue = '';
100
+ this._isStreaming = false;
101
+ this._statusText = '';
102
+ this._error = '';
103
+ this._handleMouseMove = (e) => {
104
+ if (this._isDragging) {
105
+ let newLeft = this._dragStartLeft + (e.clientX - this._dragStartX);
106
+ let newTop = this._dragStartTop + (e.clientY - this._dragStartY);
107
+ const w = this._panelEl.offsetWidth;
108
+ const h = this._panelEl.offsetHeight;
109
+ newLeft = Math.max(-w + 48, Math.min(newLeft, window.innerWidth - 48));
110
+ newTop = Math.max(-h + 48, Math.min(newTop, window.innerHeight - 48));
111
+ this._panelEl.style.left = newLeft + 'px';
112
+ this._panelEl.style.top = newTop + 'px';
113
+ }
114
+ else if (this._isResizing) {
115
+ const dx = e.clientX - this._dragStartX;
116
+ const dy = e.clientY - this._dragStartY;
117
+ const minW = 340;
118
+ const minH = 380;
119
+ const maxW = window.innerWidth * 0.9;
120
+ const maxH = window.innerHeight - 72;
121
+ let newW = this._resizeStartW;
122
+ let newH = this._resizeStartH;
123
+ let newLeft = this._dragStartLeft;
124
+ let newTop = this._dragStartTop;
125
+ if (this._resizeDir.includes('e')) {
126
+ newW = Math.max(minW, Math.min(this._resizeStartW + dx, maxW));
127
+ }
128
+ 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
+ }
135
+ if (this._resizeDir.includes('s')) {
136
+ newH = Math.max(minH, Math.min(this._resizeStartH + dy, maxH));
137
+ }
138
+ 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
+ }
144
+ }
145
+ newLeft = Math.max(0, newLeft);
146
+ newTop = Math.max(0, newTop);
147
+ this._panelEl.style.width = newW + 'px';
148
+ this._panelEl.style.height = newH + 'px';
149
+ this._panelEl.style.left = newLeft + 'px';
150
+ this._panelEl.style.top = newTop + 'px';
151
+ }
152
+ };
153
+ this._handleMouseUp = () => {
154
+ if (this._isDragging) {
155
+ this._isDragging = false;
156
+ this._panelEl.classList.remove('chatbot__panel--dragging');
157
+ }
158
+ if (this._isResizing) {
159
+ this._isResizing = false;
160
+ this._panelEl.classList.remove('chatbot__panel--resizing');
161
+ }
162
+ document.removeEventListener('mousemove', this._handleMouseMove);
163
+ document.removeEventListener('mouseup', this._handleMouseUp);
164
+ };
165
+ }
166
+ // --- Lifecycle ---
167
+ disconnectedCallback() {
168
+ super.disconnectedCallback();
169
+ if (this._reader) {
170
+ this._reader.cancel();
171
+ this._reader = null;
172
+ }
173
+ document.removeEventListener('mousemove', this._handleMouseMove);
174
+ document.removeEventListener('mouseup', this._handleMouseUp);
175
+ }
176
+ updated(changed) {
177
+ if (changed.has('messages') || changed.has('_statusText')) {
178
+ if (!this._userHasScrolledUp && this._messagesEl) {
179
+ requestAnimationFrame(() => {
180
+ if (this._messagesEl) {
181
+ this._messagesEl.scrollTop = this._messagesEl.scrollHeight;
182
+ }
183
+ });
184
+ }
185
+ }
186
+ if (changed.has('opened') && this.opened && this._inputEl) {
187
+ setTimeout(() => {
188
+ if (this._inputEl) {
189
+ this._inputEl.focus();
190
+ }
191
+ }, 200);
192
+ }
193
+ }
194
+ // --- Public methods ---
195
+ /**
196
+ * Stops the current stream without clearing the conversation.
197
+ */
198
+ stop() {
199
+ this._handleStop();
200
+ }
201
+ // --- Private methods ---
202
+ _handleHeaderMouseDown(e) {
203
+ // Ignore clicks on buttons inside the header
204
+ if (e.target.closest('button')) {
205
+ return;
206
+ }
207
+ if (!this._panelEl) {
208
+ return;
209
+ }
210
+ this._isDragging = true;
211
+ this._panelEl.classList.add('chatbot__panel--dragging');
212
+ const rect = this._panelEl.getBoundingClientRect();
213
+ this._panelEl.style.left = rect.left + 'px';
214
+ this._panelEl.style.top = rect.top + 'px';
215
+ this._panelEl.style.right = 'auto';
216
+ this._panelEl.style.bottom = 'auto';
217
+ this._panelEl.style.position = 'fixed';
218
+ this._dragStartX = e.clientX;
219
+ this._dragStartY = e.clientY;
220
+ this._dragStartLeft = rect.left;
221
+ this._dragStartTop = rect.top;
222
+ e.preventDefault();
223
+ document.addEventListener('mousemove', this._handleMouseMove);
224
+ document.addEventListener('mouseup', this._handleMouseUp);
225
+ }
226
+ _handleResizeMouseDown(e) {
227
+ if (!this._panelEl) {
228
+ return;
229
+ }
230
+ this._isResizing = true;
231
+ this._resizeDir = e.currentTarget.dataset.dir || '';
232
+ this._panelEl.classList.add('chatbot__panel--resizing');
233
+ const rect = this._panelEl.getBoundingClientRect();
234
+ this._panelEl.style.left = rect.left + 'px';
235
+ this._panelEl.style.top = rect.top + 'px';
236
+ this._panelEl.style.right = 'auto';
237
+ this._panelEl.style.bottom = 'auto';
238
+ this._panelEl.style.position = 'fixed';
239
+ this._dragStartX = e.clientX;
240
+ this._dragStartY = e.clientY;
241
+ this._dragStartLeft = rect.left;
242
+ this._dragStartTop = rect.top;
243
+ this._resizeStartW = rect.width;
244
+ this._resizeStartH = rect.height;
245
+ e.preventDefault();
246
+ e.stopPropagation();
247
+ document.addEventListener('mousemove', this._handleMouseMove);
248
+ document.addEventListener('mouseup', this._handleMouseUp);
249
+ }
250
+ _handleToggle() {
251
+ this.opened = !this.opened;
252
+ // Reset position when opening so it returns to default bottom-right
253
+ 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 = '';
261
+ }
262
+ }
263
+ _handleInputChange(e) {
264
+ this._inputValue = e.target.value;
265
+ // Auto-resize textarea
266
+ const textarea = e.target;
267
+ textarea.style.height = 'auto';
268
+ textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
269
+ }
270
+ _handleKeyDown(e) {
271
+ if (e.key === 'Enter' && !e.shiftKey) {
272
+ e.preventDefault();
273
+ this._handleSubmit();
274
+ }
275
+ }
276
+ _handleSubmit() {
277
+ if (!this._inputValue.trim() || !this.send || this._isStreaming) {
278
+ return;
279
+ }
280
+ const messageText = this._inputValue.trim();
281
+ const submitEvent = new CustomEvent('message-submit', {
282
+ detail: {
283
+ message: messageText,
284
+ },
285
+ bubbles: true,
286
+ composed: true,
287
+ cancelable: true,
288
+ });
289
+ this.dispatchEvent(submitEvent);
290
+ if (submitEvent.defaultPrevented) {
291
+ return;
292
+ }
293
+ this.messages = [...this.messages, {
294
+ role: 'user',
295
+ content: messageText,
296
+ }];
297
+ this._inputValue = '';
298
+ this._error = '';
299
+ this._userHasScrolledUp = false;
300
+ // Reset textarea height
301
+ if (this._inputEl) {
302
+ this._inputEl.style.height = 'auto';
303
+ }
304
+ // Add empty assistant message to stream into
305
+ this.messages = [...this.messages, {
306
+ role: 'assistant',
307
+ content: '',
308
+ }];
309
+ this._isStreaming = true;
310
+ this._statusText = '';
311
+ this.send({
312
+ message: messageText,
313
+ messages: this.messages.slice(0, -1),
314
+ }).then((response) => {
315
+ if (!response.ok) {
316
+ this._handleStreamError('Request failed: ' + response.status);
317
+ return;
318
+ }
319
+ if (!response.body) {
320
+ this._handleStreamError('No response body');
321
+ return;
322
+ }
323
+ this._reader = response.body.getReader();
324
+ this._decoder = new TextDecoder();
325
+ this._buffer = '';
326
+ this._readStreamChunk();
327
+ }).catch((err) => {
328
+ if (err.name !== 'AbortError') {
329
+ this._handleStreamError(err.message);
330
+ }
331
+ });
332
+ }
333
+ _handleStop() {
334
+ if (this._reader) {
335
+ this._reader.cancel();
336
+ this._reader = null;
337
+ }
338
+ this._isStreaming = false;
339
+ this._statusText = '';
340
+ }
341
+ _handleClear() {
342
+ if (!this.clear) {
343
+ return;
344
+ }
345
+ this.clear().then(() => {
346
+ if (this._reader) {
347
+ this._reader.cancel();
348
+ this._reader = null;
349
+ }
350
+ this.messages = [];
351
+ this._isStreaming = false;
352
+ this._statusText = '';
353
+ this._error = '';
354
+ this._inputValue = '';
355
+ this._userHasScrolledUp = false;
356
+ });
357
+ }
358
+ _handleMessagesScroll(e) {
359
+ const container = e.target;
360
+ const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
361
+ if (distanceFromBottom > 50) {
362
+ this._userHasScrolledUp = true;
363
+ }
364
+ else {
365
+ this._userHasScrolledUp = false;
366
+ }
367
+ }
368
+ _handleErrorDismiss() {
369
+ this._error = '';
370
+ }
371
+ _readStreamChunk() {
372
+ if (!this._reader) {
373
+ return;
374
+ }
375
+ this._reader.read().then((result) => {
376
+ if (result.done) {
377
+ this._isStreaming = false;
378
+ this._statusText = '';
379
+ this._reader = null;
380
+ return;
381
+ }
382
+ this._buffer = this._buffer + this._decoder.decode(result.value, { stream: true });
383
+ const events = this._buffer.split('\n\n');
384
+ this._buffer = events.pop() || '';
385
+ for (let i = 0; i < events.length; i++) {
386
+ if (!events[i].trim()) {
387
+ continue;
388
+ }
389
+ const lines = events[i].split('\n');
390
+ for (let j = 0; j < lines.length; j++) {
391
+ if (!lines[j].startsWith('data:')) {
392
+ continue;
393
+ }
394
+ const raw = lines[j].slice(5).trim();
395
+ if (!raw) {
396
+ continue;
397
+ }
398
+ let data;
399
+ try {
400
+ data = JSON.parse(raw);
401
+ }
402
+ catch {
403
+ continue;
404
+ }
405
+ if (data.type === 'STATUS') {
406
+ if (data.message) {
407
+ this._statusText = String(data.message);
408
+ }
409
+ }
410
+ else if (data.type === 'REASONING_PART') {
411
+ if (data.message) {
412
+ const lastIndex = this.messages.length - 1;
413
+ if (lastIndex >= 0 && this.messages[lastIndex].role === 'assistant') {
414
+ if (!this.messages[lastIndex].reasoning) {
415
+ this.messages[lastIndex].reasoning = '';
416
+ }
417
+ this.messages[lastIndex].reasoning += String(data.message);
418
+ this.requestUpdate();
419
+ }
420
+ }
421
+ }
422
+ else if (data.type === 'RESPONSE_PART') {
423
+ if (data.message) {
424
+ this._statusText = '';
425
+ const lastIndex = this.messages.length - 1;
426
+ if (lastIndex >= 0 && this.messages[lastIndex].role === 'assistant') {
427
+ this.messages[lastIndex].content += String(data.message);
428
+ this.requestUpdate();
429
+ }
430
+ }
431
+ }
432
+ else if (data.type === 'ACTION') {
433
+ const action = data;
434
+ const actionEvent = new CustomEvent('action', {
435
+ detail: {
436
+ action: action,
437
+ },
438
+ bubbles: true,
439
+ composed: true,
440
+ cancelable: true,
441
+ });
442
+ this.dispatchEvent(actionEvent);
443
+ if (!actionEvent.defaultPrevented) {
444
+ if (action.action === 'RELOAD') {
445
+ location.reload();
446
+ }
447
+ else if (action.action === 'NAVIGATE') {
448
+ if (action.data && action.data.url) {
449
+ window.location.href = action.data.url;
450
+ }
451
+ }
452
+ else if (action.action === 'OPEN_TAB') {
453
+ if (action.data && action.data.url) {
454
+ window.open(action.data.url, '_blank');
455
+ }
456
+ }
457
+ else if (action.action === 'UPDATE_HTML') {
458
+ if (action.data && action.data.selector && action.data.html) {
459
+ const el = document.querySelector(action.data.selector);
460
+ if (el) {
461
+ el.innerHTML = action.data.html;
462
+ }
463
+ }
464
+ }
465
+ else if (action.action === 'SCROLL_TO') {
466
+ if (action.data && action.data.selector) {
467
+ const el = document.querySelector(action.data.selector);
468
+ if (el) {
469
+ el.scrollIntoView({ behavior: 'smooth' });
470
+ }
471
+ }
472
+ }
473
+ }
474
+ }
475
+ else if (data.type === 'ERROR') {
476
+ this._handleStreamError(String(data.message || 'Unknown error'));
477
+ }
478
+ if (data.finished) {
479
+ this._isStreaming = false;
480
+ this._statusText = '';
481
+ this._reader = null;
482
+ }
483
+ }
484
+ }
485
+ this._readStreamChunk();
486
+ }).catch((err) => {
487
+ if (err.name !== 'AbortError') {
488
+ this._handleStreamError(err.message);
489
+ }
490
+ });
491
+ }
492
+ _handleStreamError(message) {
493
+ this._isStreaming = false;
494
+ this._statusText = '';
495
+ this._error = message;
496
+ if (this._reader) {
497
+ this._reader.cancel();
498
+ this._reader = null;
499
+ }
500
+ // Remove the empty assistant message if it has no content
501
+ if (this.messages.length > 0) {
502
+ const lastMessage = this.messages[this.messages.length - 1];
503
+ if (lastMessage.role === 'assistant' && !lastMessage.content) {
504
+ this.messages = this.messages.slice(0, -1);
505
+ }
506
+ }
507
+ }
508
+ /**
509
+ * Lightweight text formatting for assistant messages.
510
+ * Handles code blocks, inline code, bold, and line breaks.
511
+ * Escapes HTML entities first to prevent XSS.
512
+ */
513
+ _formatAssistantContent(text) {
514
+ // Escape HTML entities
515
+ let escaped = text
516
+ .replace(/&/g, '&amp;')
517
+ .replace(/</g, '&lt;')
518
+ .replace(/>/g, '&gt;')
519
+ .replace(/"/g, '&quot;')
520
+ .replace(/'/g, '&#039;');
521
+ // Code blocks: ```...```
522
+ escaped = escaped.replace(/```(\w*)\n?([\s\S]*?)```/g, '<pre><code>$2</code></pre>');
523
+ // Inline code: `...`
524
+ escaped = escaped.replace(/`([^`]+)`/g, '<code>$1</code>');
525
+ // Bold: **...**
526
+ escaped = escaped.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
527
+ // Split on code blocks to only convert newlines outside of pre tags
528
+ const parts = escaped.split(/(<pre><code>[\s\S]*?<\/code><\/pre>)/g);
529
+ for (let i = 0; i < parts.length; i++) {
530
+ // Skip code blocks (odd indices from the split)
531
+ if (i % 2 === 0) {
532
+ // Convert double newlines to paragraph breaks
533
+ parts[i] = parts[i].replace(/\n\n/g, '</p><p>');
534
+ // Convert single newlines to line breaks
535
+ parts[i] = parts[i].replace(/\n/g, '<br>');
536
+ }
537
+ }
538
+ escaped = parts.join('');
539
+ // Wrap in paragraph if we inserted paragraph breaks
540
+ if (escaped.includes('</p><p>')) {
541
+ escaped = '<p>' + escaped + '</p>';
542
+ }
543
+ return escaped;
544
+ }
545
+ // --- Render ---
546
+ render() {
547
+ return html `
548
+ <button
549
+ class=${classMap({
550
+ 'chatbot__toggle': true,
551
+ 'chatbot__toggle--hidden': this.opened,
552
+ })}
553
+ @click=${this._handleToggle}
554
+ title="Open chat"
555
+ >
556
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" class="chatbot__toggle-icon">
557
+ <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"/>
558
+ <path d="M8 11h.01M12 11h.01M16 11h.01" stroke-width="2.5"/>
559
+ </svg>
560
+ </button>
561
+
562
+ <div class=${classMap({
563
+ 'chatbot__panel': true,
564
+ 'chatbot__panel--opened': this.opened,
565
+ })}>
566
+ <div class="chatbot__resize chatbot__resize--n" data-dir="n" @mousedown=${this._handleResizeMouseDown}></div>
567
+ <div class="chatbot__resize chatbot__resize--s" data-dir="s" @mousedown=${this._handleResizeMouseDown}></div>
568
+ <div class="chatbot__resize chatbot__resize--w" data-dir="w" @mousedown=${this._handleResizeMouseDown}></div>
569
+ <div class="chatbot__resize chatbot__resize--e" data-dir="e" @mousedown=${this._handleResizeMouseDown}></div>
570
+ <div class="chatbot__resize chatbot__resize--nw" data-dir="nw" @mousedown=${this._handleResizeMouseDown}></div>
571
+ <div class="chatbot__resize chatbot__resize--ne" data-dir="ne" @mousedown=${this._handleResizeMouseDown}></div>
572
+ <div class="chatbot__resize chatbot__resize--sw" data-dir="sw" @mousedown=${this._handleResizeMouseDown}></div>
573
+ <div class="chatbot__resize chatbot__resize--se" data-dir="se" @mousedown=${this._handleResizeMouseDown}></div>
574
+ <div class="chatbot__header" @mousedown=${this._handleHeaderMouseDown}>
575
+ <div class="chatbot__header-left">
576
+ <div class="chatbot__header-drag-hint">
577
+ <span></span>
578
+ <span></span>
579
+ <span></span>
580
+ </div>
581
+ <div class="chatbot__header-info">
582
+ <span class="chatbot__header-title">${this.title}</span>
583
+ ${this.subtitle ? html `
584
+ <span class="chatbot__header-subtitle">${this.subtitle}</span>
585
+ ` : nothing}
586
+ </div>
587
+ </div>
588
+ <div class="chatbot__header-actions">
589
+ ${this.messages.length > 0 ? html `
590
+ <button class="chatbot__header-action" @click=${this._handleClear} title="Clear conversation">
591
+ <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">
592
+ <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" />
593
+ </svg>
594
+ </button>
595
+ ` : nothing}
596
+ <button class="chatbot__header-action" @click=${this._handleToggle} title="Close">
597
+ <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">
598
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
599
+ </svg>
600
+ </button>
601
+ </div>
602
+ </div>
603
+
604
+ <div class="chatbot__messages" @scroll=${this._handleMessagesScroll}>
605
+ ${this.messages.length === 0 ? html `
606
+ <div class="chatbot__empty">
607
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" class="chatbot__empty-icon">
608
+ <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" />
609
+ </svg>
610
+ <span class="chatbot__empty-text">How can I help?</span>
611
+ </div>
612
+ ` : nothing}
613
+ ${this.messages.map((msg) => html `
614
+ <div class=${classMap({
615
+ 'chatbot__message': true,
616
+ 'chatbot__message--user': msg.role === 'user',
617
+ 'chatbot__message--assistant': msg.role === 'assistant',
618
+ })}>
619
+ ${msg.reasoning ? html `
620
+ <details class="chatbot__reasoning">
621
+ <summary>Thinking</summary>
622
+ <div class="chatbot__reasoning-content">${msg.reasoning}</div>
623
+ </details>
624
+ ` : nothing}
625
+ <div class="chatbot__message-content">
626
+ ${msg.role === 'user'
627
+ ? msg.content
628
+ : unsafeHTML(this._formatAssistantContent(msg.content))}
629
+ </div>
630
+ </div>
631
+ `)}
632
+ ${this._statusText ? html `
633
+ <div class="chatbot__message chatbot__message--assistant">
634
+ <div class="chatbot__typing">
635
+ <span></span>
636
+ <span></span>
637
+ <span></span>
638
+ </div>
639
+ </div>
640
+ ` : nothing}
641
+ </div>
642
+
643
+ ${this._error ? html `
644
+ <div class="chatbot__error">
645
+ <span>${this._error}</span>
646
+ <button class="chatbot__error-dismiss" @click=${this._handleErrorDismiss}>
647
+ <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">
648
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
649
+ </svg>
650
+ </button>
651
+ </div>
652
+ ` : nothing}
653
+
654
+ <div class="chatbot__footer">
655
+ <div class="chatbot__input-wrapper">
656
+ <button class="chatbot__input-plus" title="Attach">+</button>
657
+ <textarea
658
+ class="chatbot__input"
659
+ .value=${this._inputValue}
660
+ placeholder=${this.placeholder}
661
+ rows="1"
662
+ @input=${this._handleInputChange}
663
+ @keydown=${this._handleKeyDown}
664
+ ></textarea>
665
+ ${this._isStreaming ? html `
666
+ <button class="chatbot__stop" @click=${this._handleStop} title="Stop generating">
667
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" class="chatbot__stop-icon">
668
+ <rect x="6" y="6" width="12" height="12" rx="2" />
669
+ </svg>
670
+ </button>
671
+ ` : html `
672
+ <button
673
+ class=${classMap({
674
+ 'chatbot__send': true,
675
+ 'chatbot__send--disabled': !this._inputValue.trim(),
676
+ })}
677
+ @click=${this._handleSubmit}
678
+ title="Send message"
679
+ >
680
+ <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">
681
+ <path d="M12 19V5M5 12l7-7 7 7"/>
682
+ </svg>
683
+ </button>
684
+ `}
685
+ </div>
686
+ </div>
687
+ </div>
688
+ `;
689
+ }
690
+ };
691
+ KRChatbot.styles = css `
692
+ *,
693
+ *::before,
694
+ *::after {
695
+ box-sizing: border-box;
696
+ }
697
+
698
+ :host {
699
+ display: block;
700
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
701
+ font-size: 14px;
702
+ line-height: 1.6;
703
+
704
+ --kr-chatbot-primary: #163052;
705
+ --kr-chatbot-primary-hover: #1e4068;
706
+ --kr-chatbot-bg: #ffffff;
707
+ --kr-chatbot-border: #d9d9d9;
708
+ --kr-chatbot-text: #1a1a2e;
709
+ --kr-chatbot-text-secondary: #64668b;
710
+ --kr-chatbot-user-bg: #e9e9e980;
711
+ --kr-chatbot-user-text: #1a1a2e;
712
+ --kr-chatbot-error-bg: #fef2f2;
713
+ --kr-chatbot-error-text: #dc2626;
714
+ --kr-chatbot-success: #00b894;
715
+ --kr-chatbot-toggle-size: 56px;
716
+ --kr-chatbot-panel-width: 440px;
717
+ --kr-chatbot-panel-height: 620px;
718
+ }
719
+
720
+ :host {
721
+ position: fixed;
722
+ bottom: 24px;
723
+ right: 24px;
724
+ z-index: 10000;
725
+ }
726
+
727
+ /* Toggle button */
728
+ .chatbot__toggle {
729
+ width: var(--kr-chatbot-toggle-size);
730
+ height: var(--kr-chatbot-toggle-size);
731
+ border-radius: 18px;
732
+ background: var(--kr-chatbot-primary);
733
+ color: white;
734
+ border: none;
735
+ cursor: pointer;
736
+ display: flex;
737
+ align-items: center;
738
+ justify-content: center;
739
+ box-shadow: 0 4px 24px rgba(22, 48, 82, 0.4);
740
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
741
+ }
742
+
743
+ .chatbot__toggle:hover {
744
+ transform: scale(1.05);
745
+ box-shadow: 0 6px 32px rgba(22, 48, 82, 0.5);
746
+ }
747
+
748
+ .chatbot__toggle--hidden {
749
+ transform: scale(0);
750
+ opacity: 0;
751
+ pointer-events: none;
752
+ }
753
+
754
+ .chatbot__toggle-icon {
755
+ width: 26px;
756
+ height: 26px;
757
+ }
758
+
759
+ /* Panel */
760
+ .chatbot__panel {
761
+ display: none;
762
+ flex-direction: column;
763
+ background: var(--kr-chatbot-bg);
764
+ overflow: hidden;
765
+ position: absolute;
766
+ bottom: 0;
767
+ right: 0;
768
+ width: var(--kr-chatbot-panel-width);
769
+ height: var(--kr-chatbot-panel-height);
770
+ max-height: calc(100vh - 72px);
771
+ border-radius: 16px;
772
+ box-shadow: 0 8px 40px rgba(0, 0, 0, 0.08), 0 2px 12px rgba(0, 0, 0, 0.04);
773
+ border: 1px solid rgba(0, 0, 0, 0.08);
774
+ animation: chatbot-panel-in 0.35s cubic-bezier(0.4, 0, 0.2, 1);
775
+ transform-origin: bottom right;
776
+ }
777
+
778
+ .chatbot__panel--opened {
779
+ display: flex;
780
+ }
781
+
782
+ @keyframes chatbot-panel-in {
783
+ from {
784
+ opacity: 0;
785
+ transform: scale(0.92) translateY(20px);
786
+ }
787
+ to {
788
+ opacity: 1;
789
+ transform: scale(1) translateY(0);
790
+ }
791
+ }
792
+
793
+ /* Header */
794
+ .chatbot__header {
795
+ padding: 10px 16px;
796
+ background: var(--kr-chatbot-primary);
797
+ color: white;
798
+ display: flex;
799
+ align-items: center;
800
+ justify-content: space-between;
801
+ flex-shrink: 0;
802
+ }
803
+
804
+ .chatbot__header-left {
805
+ display: flex;
806
+ align-items: center;
807
+ gap: 10px;
808
+ }
809
+
810
+ .chatbot__header-drag-hint {
811
+ display: flex;
812
+ flex-direction: column;
813
+ gap: 2px;
814
+ opacity: 0.35;
815
+ }
816
+
817
+ .chatbot__header-drag-hint span {
818
+ display: block;
819
+ width: 14px;
820
+ height: 2px;
821
+ background: white;
822
+ border-radius: 1px;
823
+ }
824
+
825
+ .chatbot__header-info {
826
+ display: flex;
827
+ flex-direction: column;
828
+ }
829
+
830
+ .chatbot__header-title {
831
+ font-size: 13px;
832
+ font-weight: 600;
833
+ letter-spacing: -0.2px;
834
+ }
835
+
836
+ .chatbot__header-subtitle {
837
+ color: rgba(255, 255, 255, 0.5);
838
+ font-size: 11px;
839
+ display: flex;
840
+ align-items: center;
841
+ gap: 4px;
842
+ }
843
+
844
+ .chatbot__header-subtitle::before {
845
+ content: '';
846
+ width: 5px;
847
+ height: 5px;
848
+ background: var(--kr-chatbot-success);
849
+ border-radius: 50%;
850
+ }
851
+
852
+ .chatbot__header-actions {
853
+ display: flex;
854
+ align-items: center;
855
+ gap: 4px;
856
+ }
857
+
858
+ .chatbot__header-action {
859
+ width: 28px;
860
+ height: 28px;
861
+ background: transparent;
862
+ border: none;
863
+ color: rgba(255, 255, 255, 0.6);
864
+ border-radius: 8px;
865
+ cursor: pointer;
866
+ display: flex;
867
+ align-items: center;
868
+ justify-content: center;
869
+ transition: all 0.15s;
870
+ }
871
+
872
+ .chatbot__header-action:hover {
873
+ background: rgba(255, 255, 255, 0.1);
874
+ color: white;
875
+ }
876
+
877
+ .chatbot__header-action-icon {
878
+ width: 16px;
879
+ height: 16px;
880
+ }
881
+
882
+ /* Messages */
883
+ .chatbot__messages {
884
+ flex: 1;
885
+ overflow-y: auto;
886
+ padding: 32px;
887
+ display: flex;
888
+ flex-direction: column;
889
+ gap: 24px;
890
+ scroll-behavior: smooth;
891
+ background: white;
892
+ }
893
+
894
+ .chatbot__messages::-webkit-scrollbar {
895
+ width: 4px;
896
+ }
897
+
898
+ .chatbot__messages::-webkit-scrollbar-track {
899
+ background: transparent;
900
+ }
901
+
902
+ .chatbot__messages::-webkit-scrollbar-thumb {
903
+ background: rgba(0, 0, 0, 0.08);
904
+ border-radius: 2px;
905
+ }
906
+
907
+ .chatbot__empty {
908
+ flex: 1;
909
+ display: flex;
910
+ flex-direction: column;
911
+ align-items: center;
912
+ justify-content: center;
913
+ color: var(--kr-chatbot-text-secondary);
914
+ gap: 8px;
915
+ }
916
+
917
+ .chatbot__empty-icon {
918
+ width: 40px;
919
+ height: 40px;
920
+ opacity: 0.4;
921
+ }
922
+
923
+ .chatbot__empty-text {
924
+ font-size: 13px;
925
+ }
926
+
927
+ .chatbot__message {
928
+ display: flex;
929
+ flex-direction: column;
930
+ max-width: 100%;
931
+ animation: chatbot-msg-in 0.3s ease-out;
932
+ }
933
+
934
+ @keyframes chatbot-msg-in {
935
+ from {
936
+ opacity: 0;
937
+ transform: translateY(6px);
938
+ }
939
+ to {
940
+ opacity: 1;
941
+ transform: translateY(0);
942
+ }
943
+ }
944
+
945
+ .chatbot__message--user {
946
+ align-self: flex-end;
947
+ align-items: flex-end;
948
+ max-width: 85%;
949
+ }
950
+
951
+ .chatbot__message--assistant {
952
+ align-self: flex-start;
953
+ }
954
+
955
+ .chatbot__message-content {
956
+ line-height: 1.7;
957
+ word-wrap: break-word;
958
+ overflow-wrap: break-word;
959
+ }
960
+
961
+ .chatbot__message--user .chatbot__message-content {
962
+ display: inline-block;
963
+ background: var(--kr-chatbot-user-bg);
964
+ color: var(--kr-chatbot-user-text);
965
+ padding: 10px 16px;
966
+ border-radius: 20px;
967
+ border-bottom-right-radius: 6px;
968
+ font-size: 14px;
969
+ }
970
+
971
+ .chatbot__message--assistant .chatbot__message-content {
972
+ background: transparent;
973
+ color: var(--kr-chatbot-text);
974
+ padding: 0;
975
+ font-size: 14px;
976
+ }
977
+
978
+ .chatbot__message--assistant .chatbot__message-content pre {
979
+ background: #1e1e1e;
980
+ color: #d4d4d4;
981
+ padding: 12px;
982
+ border-radius: 6px;
983
+ overflow-x: auto;
984
+ margin: 8px 0;
985
+ font-size: 13px;
986
+ line-height: 1.4;
987
+ }
988
+
989
+ .chatbot__message--assistant .chatbot__message-content code {
990
+ font-family: 'SF Mono', 'Fira Code', Monaco, monospace;
991
+ font-size: 13px;
992
+ }
993
+
994
+ .chatbot__message--assistant .chatbot__message-content :not(pre) > code {
995
+ background: rgba(0, 0, 0, 0.06);
996
+ padding: 2px 5px;
997
+ border-radius: 3px;
998
+ }
999
+
1000
+ .chatbot__message--assistant .chatbot__message-content strong {
1001
+ font-weight: 600;
1002
+ }
1003
+
1004
+ .chatbot__message--assistant .chatbot__message-content p {
1005
+ margin: 0 0 8px 0;
1006
+ }
1007
+
1008
+ .chatbot__message--assistant .chatbot__message-content p:last-child {
1009
+ margin-bottom: 0;
1010
+ }
1011
+
1012
+ /* Reasoning */
1013
+ .chatbot__reasoning {
1014
+ margin-bottom: 8px;
1015
+ border-radius: 6px;
1016
+ background: rgba(0, 0, 0, 0.03);
1017
+ font-size: 12px;
1018
+ }
1019
+
1020
+ .chatbot__reasoning summary {
1021
+ cursor: pointer;
1022
+ padding: 6px 10px;
1023
+ color: var(--kr-chatbot-text-secondary);
1024
+ font-style: italic;
1025
+ user-select: none;
1026
+ }
1027
+
1028
+ .chatbot__reasoning-content {
1029
+ padding: 6px 10px 10px;
1030
+ color: var(--kr-chatbot-text-secondary);
1031
+ line-height: 1.5;
1032
+ white-space: pre-wrap;
1033
+ word-wrap: break-word;
1034
+ }
1035
+
1036
+ /* Status */
1037
+ .chatbot__status {
1038
+ font-size: 13px;
1039
+ color: var(--kr-chatbot-text-secondary);
1040
+ font-style: italic;
1041
+ }
1042
+
1043
+ .chatbot__typing {
1044
+ display: flex;
1045
+ gap: 4px;
1046
+ padding: 4px 0;
1047
+ }
1048
+
1049
+ .chatbot__typing span {
1050
+ width: 6px;
1051
+ height: 6px;
1052
+ background: var(--kr-chatbot-text-secondary);
1053
+ border-radius: 50%;
1054
+ animation: chatbot-typing 1.4s infinite;
1055
+ opacity: 0.4;
1056
+ }
1057
+
1058
+ .chatbot__typing span:nth-child(2) {
1059
+ animation-delay: 0.2s;
1060
+ }
1061
+
1062
+ .chatbot__typing span:nth-child(3) {
1063
+ animation-delay: 0.4s;
1064
+ }
1065
+
1066
+ @keyframes chatbot-typing {
1067
+ 0%, 60%, 100% {
1068
+ opacity: 0.4;
1069
+ transform: translateY(0);
1070
+ }
1071
+ 30% {
1072
+ opacity: 1;
1073
+ transform: translateY(-4px);
1074
+ }
1075
+ }
1076
+
1077
+ /* Error */
1078
+ .chatbot__error {
1079
+ padding: 8px 16px;
1080
+ background: var(--kr-chatbot-error-bg);
1081
+ color: var(--kr-chatbot-error-text);
1082
+ font-size: 13px;
1083
+ display: flex;
1084
+ align-items: center;
1085
+ justify-content: space-between;
1086
+ gap: 8px;
1087
+ flex-shrink: 0;
1088
+ }
1089
+
1090
+ .chatbot__error-dismiss {
1091
+ background: none;
1092
+ border: none;
1093
+ color: var(--kr-chatbot-error-text);
1094
+ cursor: pointer;
1095
+ padding: 2px;
1096
+ display: flex;
1097
+ align-items: center;
1098
+ flex-shrink: 0;
1099
+ }
1100
+
1101
+ .chatbot__error-dismiss-icon {
1102
+ width: 14px;
1103
+ height: 14px;
1104
+ }
1105
+
1106
+ /* Footer / Input */
1107
+ .chatbot__footer {
1108
+ padding: 12px 16px 16px;
1109
+ flex-shrink: 0;
1110
+ background: white;
1111
+ }
1112
+
1113
+ .chatbot__input-wrapper {
1114
+ display: flex;
1115
+ align-items: center;
1116
+ gap: 4px;
1117
+ background: white;
1118
+ border: 1px solid var(--kr-chatbot-border);
1119
+ border-radius: 26px;
1120
+ padding: 6px;
1121
+ transition: border-color 0.2s, box-shadow 0.2s;
1122
+ }
1123
+
1124
+ .chatbot__input-wrapper:focus-within {
1125
+ border-color: #b0b0b0;
1126
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04);
1127
+ }
1128
+
1129
+ .chatbot__input-plus {
1130
+ width: 34px;
1131
+ height: 34px;
1132
+ border: none;
1133
+ background: transparent;
1134
+ color: var(--kr-chatbot-text);
1135
+ border-radius: 50%;
1136
+ cursor: pointer;
1137
+ display: flex;
1138
+ align-items: center;
1139
+ justify-content: center;
1140
+ flex-shrink: 0;
1141
+ font-size: 20px;
1142
+ font-weight: 300;
1143
+ transition: background 0.15s;
1144
+ }
1145
+
1146
+ .chatbot__input-plus:hover {
1147
+ background: #f8f9fc;
1148
+ }
1149
+
1150
+ .chatbot__input {
1151
+ flex: 1;
1152
+ border: none;
1153
+ outline: none;
1154
+ resize: none;
1155
+ font-family: inherit;
1156
+ font-size: 15px;
1157
+ line-height: 1.5;
1158
+ padding: 4px 0;
1159
+ min-height: 26px;
1160
+ max-height: 120px;
1161
+ background: transparent;
1162
+ color: var(--kr-chatbot-text);
1163
+ }
1164
+
1165
+ .chatbot__input::placeholder {
1166
+ color: #b0b0b0;
1167
+ }
1168
+
1169
+ .chatbot__send {
1170
+ width: 34px;
1171
+ height: 34px;
1172
+ border-radius: 50%;
1173
+ background: var(--kr-chatbot-primary);
1174
+ color: white;
1175
+ border: none;
1176
+ cursor: pointer;
1177
+ display: flex;
1178
+ align-items: center;
1179
+ justify-content: center;
1180
+ flex-shrink: 0;
1181
+ transition: all 0.15s;
1182
+ }
1183
+
1184
+ .chatbot__send:hover {
1185
+ background: var(--kr-chatbot-primary-hover);
1186
+ }
1187
+
1188
+ .chatbot__send--disabled {
1189
+ opacity: 0.4;
1190
+ cursor: default;
1191
+ }
1192
+
1193
+ .chatbot__send--disabled:hover {
1194
+ background: var(--kr-chatbot-primary);
1195
+ }
1196
+
1197
+ .chatbot__send-icon {
1198
+ width: 16px;
1199
+ height: 16px;
1200
+ }
1201
+
1202
+ .chatbot__stop {
1203
+ width: 34px;
1204
+ height: 34px;
1205
+ border-radius: 50%;
1206
+ background: var(--kr-chatbot-error-text);
1207
+ color: white;
1208
+ border: none;
1209
+ cursor: pointer;
1210
+ display: flex;
1211
+ align-items: center;
1212
+ justify-content: center;
1213
+ flex-shrink: 0;
1214
+ transition: all 0.15s;
1215
+ }
1216
+
1217
+ .chatbot__stop:hover {
1218
+ background: #b91c1c;
1219
+ }
1220
+
1221
+ .chatbot__stop-icon {
1222
+ width: 14px;
1223
+ height: 14px;
1224
+ }
1225
+
1226
+ /* Header drag cursor */
1227
+ .chatbot__header {
1228
+ cursor: grab;
1229
+ user-select: none;
1230
+ }
1231
+
1232
+ .chatbot__header:active {
1233
+ cursor: grabbing;
1234
+ }
1235
+
1236
+ /* Drag / Resize states */
1237
+ .chatbot__panel--dragging,
1238
+ .chatbot__panel--resizing {
1239
+ transition: none;
1240
+ user-select: none;
1241
+ }
1242
+
1243
+ .chatbot__panel--dragging {
1244
+ box-shadow: 0 32px 96px rgba(0, 0, 0, 0.18), 0 12px 40px rgba(0, 0, 0, 0.1);
1245
+ }
1246
+
1247
+ /* Resize handles */
1248
+ .chatbot__resize {
1249
+ position: absolute;
1250
+ z-index: 10;
1251
+ }
1252
+
1253
+ .chatbot__resize--n { top: -4px; left: 12px; right: 12px; height: 8px; cursor: n-resize; }
1254
+ .chatbot__resize--s { bottom: -4px; left: 12px; right: 12px; height: 8px; cursor: s-resize; }
1255
+ .chatbot__resize--w { left: -4px; top: 12px; bottom: 12px; width: 8px; cursor: w-resize; }
1256
+ .chatbot__resize--e { right: -4px; top: 12px; bottom: 12px; width: 8px; cursor: e-resize; }
1257
+ .chatbot__resize--nw { top: -4px; left: -4px; width: 16px; height: 16px; cursor: nw-resize; }
1258
+ .chatbot__resize--ne { top: -4px; right: -4px; width: 16px; height: 16px; cursor: ne-resize; }
1259
+ .chatbot__resize--sw { bottom: -4px; left: -4px; width: 16px; height: 16px; cursor: sw-resize; }
1260
+ .chatbot__resize--se { bottom: -4px; right: -4px; width: 16px; height: 16px; cursor: se-resize; }
1261
+
1262
+ .chatbot__resize--se::after {
1263
+ content: '';
1264
+ position: absolute;
1265
+ bottom: 7px;
1266
+ right: 7px;
1267
+ width: 10px;
1268
+ height: 10px;
1269
+ border-right: 2px solid rgba(0, 0, 0, 0.12);
1270
+ border-bottom: 2px solid rgba(0, 0, 0, 0.12);
1271
+ border-radius: 0 0 3px 0;
1272
+ }
1273
+ `;
1274
+ __decorate([
1275
+ property({ attribute: false })
1276
+ ], KRChatbot.prototype, "send", void 0);
1277
+ __decorate([
1278
+ property({ attribute: false })
1279
+ ], KRChatbot.prototype, "clear", void 0);
1280
+ __decorate([
1281
+ property({ attribute: false })
1282
+ ], KRChatbot.prototype, "messages", void 0);
1283
+ __decorate([
1284
+ property({ type: String })
1285
+ ], KRChatbot.prototype, "title", void 0);
1286
+ __decorate([
1287
+ property({ type: String })
1288
+ ], KRChatbot.prototype, "subtitle", void 0);
1289
+ __decorate([
1290
+ property({ type: String })
1291
+ ], KRChatbot.prototype, "placeholder", void 0);
1292
+ __decorate([
1293
+ property({ type: Boolean, reflect: true })
1294
+ ], KRChatbot.prototype, "opened", void 0);
1295
+ __decorate([
1296
+ state()
1297
+ ], KRChatbot.prototype, "_inputValue", void 0);
1298
+ __decorate([
1299
+ state()
1300
+ ], KRChatbot.prototype, "_isStreaming", void 0);
1301
+ __decorate([
1302
+ state()
1303
+ ], KRChatbot.prototype, "_statusText", void 0);
1304
+ __decorate([
1305
+ state()
1306
+ ], KRChatbot.prototype, "_error", void 0);
1307
+ __decorate([
1308
+ query('.chatbot__panel')
1309
+ ], KRChatbot.prototype, "_panelEl", void 0);
1310
+ __decorate([
1311
+ query('.chatbot__messages')
1312
+ ], KRChatbot.prototype, "_messagesEl", void 0);
1313
+ __decorate([
1314
+ query('.chatbot__input')
1315
+ ], KRChatbot.prototype, "_inputEl", void 0);
1316
+ KRChatbot = __decorate([
1317
+ customElement('kr-chatbot')
1318
+ ], KRChatbot);
1319
+ export { KRChatbot };
1320
+ //# sourceMappingURL=chatbot.js.map