@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.d.ts +36 -27
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +429 -321
- package/dist/chatbot.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/krubble-app.bundled.js +3200 -521
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +395 -298
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +3198 -519
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +395 -298
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/dist/router.d.ts +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +8 -2
- package/dist/router.js.map +1 -1
- package/dist/scaffold/nav-item-edit.d.ts.map +1 -1
- package/dist/scaffold/nav-item-edit.js.map +1 -1
- package/dist/scaffold.d.ts +12 -12
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +196 -92
- package/dist/scaffold.js.map +1 -1
- package/dist/screen-detail.js +2 -2
- package/dist/screen-nav.d.ts +4 -4
- package/dist/screen-nav.d.ts.map +1 -1
- package/dist/screen-nav.js +14 -10
- package/dist/screen-nav.js.map +1 -1
- package/dist/subbar.d.ts.map +1 -1
- package/dist/subbar.js +43 -10
- package/dist/subbar.js.map +1 -1
- package/package.json +4 -3
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
|
|
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,
|
|
16
|
-
*
|
|
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
|
-
* - `
|
|
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
|
|
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 = '
|
|
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(
|
|
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
|
-
|
|
130
|
-
|
|
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
|
-
|
|
140
|
-
|
|
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('
|
|
155
|
+
this._panelEl.classList.remove('panel--dragging');
|
|
157
156
|
}
|
|
158
157
|
if (this._isResizing) {
|
|
159
158
|
this._isResizing = false;
|
|
160
|
-
this._panelEl.classList.remove('
|
|
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('
|
|
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('
|
|
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.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
this.
|
|
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, '&')
|
|
523
|
-
.replace(/</g, '<')
|
|
524
|
-
.replace(/>/g, '>')
|
|
525
|
-
.replace(/"/g, '"')
|
|
526
|
-
.replace(/'/g, ''');
|
|
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
|
-
'
|
|
557
|
-
'
|
|
602
|
+
'toggle': true,
|
|
603
|
+
'toggle--hidden': this.opened,
|
|
558
604
|
})}
|
|
559
605
|
@click=${this._handleToggle}
|
|
560
606
|
title="Open chat"
|
|
561
607
|
>
|
|
562
|
-
<svg
|
|
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
|
-
'
|
|
570
|
-
'
|
|
622
|
+
'panel': true,
|
|
623
|
+
'panel--opened': this.opened,
|
|
571
624
|
})}>
|
|
572
|
-
<div
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
<div
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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="
|
|
670
|
+
<span class="header-subtitle">${this.subtitle}</span>
|
|
591
671
|
` : nothing}
|
|
592
672
|
</div>
|
|
593
673
|
</div>
|
|
594
|
-
<div class="
|
|
674
|
+
<div class="header-actions">
|
|
595
675
|
${this.messages.length > 0 ? html `
|
|
596
|
-
<button
|
|
597
|
-
|
|
598
|
-
|
|
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
|
|
603
|
-
|
|
604
|
-
|
|
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="
|
|
720
|
+
<div class="messages" @scroll=${this._handleMessagesScroll}>
|
|
611
721
|
${this.messages.length === 0 ? html `
|
|
612
|
-
<div class="
|
|
613
|
-
<
|
|
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
|
-
'
|
|
622
|
-
'
|
|
623
|
-
'
|
|
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="
|
|
733
|
+
<details class="reasoning">
|
|
627
734
|
<summary>Thinking</summary>
|
|
628
|
-
<div class="
|
|
735
|
+
<div class="reasoning-content">${msg.reasoning}</div>
|
|
629
736
|
</details>
|
|
630
737
|
` : nothing}
|
|
631
|
-
|
|
632
|
-
|
|
738
|
+
${msg.content ? html `
|
|
739
|
+
<div class="message-content">
|
|
740
|
+
${msg.role === 'user'
|
|
633
741
|
? msg.content
|
|
634
|
-
: unsafeHTML(
|
|
635
|
-
|
|
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="
|
|
757
|
+
<div class="error">
|
|
651
758
|
<span>${this._error}</span>
|
|
652
|
-
<button class="
|
|
653
|
-
<svg
|
|
654
|
-
|
|
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="
|
|
661
|
-
<div class="
|
|
662
|
-
<button class="
|
|
778
|
+
<div class="footer">
|
|
779
|
+
<div class="input-wrapper">
|
|
780
|
+
<button class="input-plus" title="Attach">+</button>
|
|
663
781
|
<textarea
|
|
664
|
-
class="
|
|
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
|
|
673
|
-
|
|
674
|
-
|
|
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
|
-
'
|
|
681
|
-
'
|
|
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
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
895
|
+
.toggle--hidden {
|
|
755
896
|
transform: scale(0);
|
|
756
897
|
opacity: 0;
|
|
757
898
|
pointer-events: none;
|
|
758
899
|
}
|
|
759
900
|
|
|
760
|
-
.
|
|
901
|
+
.toggle-icon {
|
|
761
902
|
width: 26px;
|
|
762
903
|
height: 26px;
|
|
763
904
|
}
|
|
764
905
|
|
|
765
906
|
/* Panel */
|
|
766
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
801
|
-
padding:
|
|
802
|
-
|
|
803
|
-
|
|
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
|
-
.
|
|
953
|
+
.header-left {
|
|
811
954
|
display: flex;
|
|
812
955
|
align-items: center;
|
|
813
956
|
gap: 10px;
|
|
814
957
|
}
|
|
815
958
|
|
|
816
|
-
|
|
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
|
-
.
|
|
837
|
-
font-size:
|
|
964
|
+
.header-title {
|
|
965
|
+
font-size: 16px;
|
|
838
966
|
font-weight: 600;
|
|
839
|
-
letter-spacing: -0.2px;
|
|
840
967
|
}
|
|
841
968
|
|
|
842
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
985
|
+
.header-actions {
|
|
859
986
|
display: flex;
|
|
860
987
|
align-items: center;
|
|
861
988
|
gap: 4px;
|
|
862
989
|
}
|
|
863
990
|
|
|
864
|
-
.
|
|
865
|
-
width:
|
|
866
|
-
height:
|
|
991
|
+
.header-action {
|
|
992
|
+
width: 32px;
|
|
993
|
+
height: 32px;
|
|
867
994
|
background: transparent;
|
|
868
995
|
border: none;
|
|
869
|
-
color:
|
|
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
|
-
.
|
|
879
|
-
background: rgba(
|
|
880
|
-
color:
|
|
1005
|
+
.header-action:hover {
|
|
1006
|
+
background: rgba(0, 0, 0, 0.05);
|
|
1007
|
+
color: #000000;
|
|
881
1008
|
}
|
|
882
1009
|
|
|
883
|
-
.
|
|
884
|
-
width:
|
|
885
|
-
height:
|
|
1010
|
+
.header-action-icon {
|
|
1011
|
+
width: 20px;
|
|
1012
|
+
height: 20px;
|
|
886
1013
|
}
|
|
887
1014
|
|
|
888
1015
|
/* Messages */
|
|
889
|
-
.
|
|
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
|
-
.
|
|
1027
|
+
.messages::-webkit-scrollbar {
|
|
901
1028
|
width: 4px;
|
|
902
1029
|
}
|
|
903
1030
|
|
|
904
|
-
.
|
|
1031
|
+
.messages::-webkit-scrollbar-track {
|
|
905
1032
|
background: transparent;
|
|
906
1033
|
}
|
|
907
1034
|
|
|
908
|
-
.
|
|
1035
|
+
.messages::-webkit-scrollbar-thumb {
|
|
909
1036
|
background: rgba(0, 0, 0, 0.08);
|
|
910
1037
|
border-radius: 2px;
|
|
911
1038
|
}
|
|
912
1039
|
|
|
913
|
-
.
|
|
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
|
-
|
|
924
|
-
|
|
925
|
-
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
1079
|
+
.message--assistant {
|
|
958
1080
|
align-self: flex-start;
|
|
1081
|
+
gap: 24px;
|
|
959
1082
|
}
|
|
960
1083
|
|
|
961
|
-
.
|
|
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
|
-
.
|
|
1090
|
+
.message--user .message-content {
|
|
968
1091
|
display: inline-block;
|
|
969
1092
|
background: var(--kr-chatbot-user-bg);
|
|
970
|
-
color:
|
|
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
|
-
.
|
|
1100
|
+
.message--assistant .message-content {
|
|
978
1101
|
background: transparent;
|
|
979
|
-
color:
|
|
1102
|
+
color: #000000;
|
|
980
1103
|
padding: 0;
|
|
981
1104
|
font-size: 14px;
|
|
982
1105
|
}
|
|
983
1106
|
|
|
984
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
1129
|
+
.message--assistant .message-content strong {
|
|
1007
1130
|
font-weight: 600;
|
|
1008
1131
|
}
|
|
1009
1132
|
|
|
1010
|
-
.
|
|
1133
|
+
.message--assistant .message-content p {
|
|
1011
1134
|
margin: 0 0 8px 0;
|
|
1012
1135
|
}
|
|
1013
1136
|
|
|
1014
|
-
.
|
|
1137
|
+
.message--assistant .message-content p:last-child {
|
|
1015
1138
|
margin-bottom: 0;
|
|
1016
1139
|
}
|
|
1017
1140
|
|
|
1018
1141
|
/* Reasoning */
|
|
1019
|
-
.
|
|
1020
|
-
margin-bottom: 8px;
|
|
1142
|
+
.reasoning {
|
|
1021
1143
|
border-radius: 6px;
|
|
1022
|
-
background:
|
|
1144
|
+
background: rgb(244 244 244 / 80%);
|
|
1023
1145
|
font-size: 12px;
|
|
1024
1146
|
}
|
|
1025
1147
|
|
|
1026
|
-
.
|
|
1148
|
+
.reasoning summary {
|
|
1027
1149
|
cursor: pointer;
|
|
1028
|
-
padding: 6px 10px;
|
|
1029
|
-
color:
|
|
1030
|
-
font-style: italic;
|
|
1150
|
+
padding: 6px 10px 6px 10px;
|
|
1151
|
+
color: black;
|
|
1031
1152
|
user-select: none;
|
|
1032
1153
|
}
|
|
1033
1154
|
|
|
1034
|
-
.
|
|
1155
|
+
.reasoning-content {
|
|
1035
1156
|
padding: 6px 10px 10px;
|
|
1036
|
-
color:
|
|
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
|
-
|
|
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
|
-
.
|
|
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
|
-
.
|
|
1177
|
+
.typing span:nth-child(2) {
|
|
1065
1178
|
animation-delay: 0.2s;
|
|
1066
1179
|
}
|
|
1067
1180
|
|
|
1068
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
1220
|
+
.error-dismiss-icon {
|
|
1108
1221
|
width: 14px;
|
|
1109
1222
|
height: 14px;
|
|
1110
1223
|
}
|
|
1111
1224
|
|
|
1112
1225
|
/* Footer / Input */
|
|
1113
|
-
.
|
|
1114
|
-
padding:
|
|
1226
|
+
.footer {
|
|
1227
|
+
padding: 16px 16px 16px;
|
|
1115
1228
|
flex-shrink: 0;
|
|
1116
1229
|
background: white;
|
|
1117
1230
|
}
|
|
1118
1231
|
|
|
1119
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
1265
|
+
.input-plus:hover {
|
|
1153
1266
|
background: #f8f9fc;
|
|
1154
1267
|
}
|
|
1155
1268
|
|
|
1156
|
-
.
|
|
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:
|
|
1281
|
+
color: black;
|
|
1169
1282
|
}
|
|
1170
1283
|
|
|
1171
|
-
.
|
|
1284
|
+
.input::placeholder {
|
|
1172
1285
|
color: #b0b0b0;
|
|
1173
1286
|
}
|
|
1174
1287
|
|
|
1175
|
-
.
|
|
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
|
-
.
|
|
1303
|
+
.send:hover {
|
|
1191
1304
|
background: var(--kr-chatbot-primary-hover);
|
|
1192
1305
|
}
|
|
1193
1306
|
|
|
1194
|
-
.
|
|
1307
|
+
.send--disabled {
|
|
1195
1308
|
opacity: 0.4;
|
|
1196
1309
|
cursor: default;
|
|
1197
1310
|
}
|
|
1198
1311
|
|
|
1199
|
-
.
|
|
1312
|
+
.send--disabled:hover {
|
|
1200
1313
|
background: var(--kr-chatbot-primary);
|
|
1201
1314
|
}
|
|
1202
1315
|
|
|
1203
|
-
.
|
|
1316
|
+
.send-icon {
|
|
1204
1317
|
width: 16px;
|
|
1205
1318
|
height: 16px;
|
|
1206
1319
|
}
|
|
1207
1320
|
|
|
1208
|
-
.
|
|
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
|
-
.
|
|
1336
|
+
.stop:hover {
|
|
1224
1337
|
background: #b91c1c;
|
|
1225
1338
|
}
|
|
1226
1339
|
|
|
1227
|
-
.
|
|
1340
|
+
.stop-icon {
|
|
1228
1341
|
width: 14px;
|
|
1229
1342
|
height: 14px;
|
|
1230
1343
|
}
|
|
1231
1344
|
|
|
1232
1345
|
/* Header drag cursor */
|
|
1233
|
-
.
|
|
1346
|
+
.header {
|
|
1234
1347
|
cursor: grab;
|
|
1235
1348
|
user-select: none;
|
|
1236
1349
|
}
|
|
1237
1350
|
|
|
1238
|
-
.
|
|
1351
|
+
.header:active {
|
|
1239
1352
|
cursor: grabbing;
|
|
1240
1353
|
}
|
|
1241
1354
|
|
|
1242
1355
|
/* Drag / Resize states */
|
|
1243
|
-
.
|
|
1244
|
-
.
|
|
1356
|
+
.panel--dragging,
|
|
1357
|
+
.panel--resizing {
|
|
1245
1358
|
transition: none;
|
|
1246
1359
|
user-select: none;
|
|
1247
1360
|
}
|
|
1248
1361
|
|
|
1249
|
-
.
|
|
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
|
-
.
|
|
1367
|
+
.resize {
|
|
1255
1368
|
position: absolute;
|
|
1256
1369
|
z-index: 10;
|
|
1257
1370
|
}
|
|
1258
1371
|
|
|
1259
|
-
.
|
|
1260
|
-
.
|
|
1261
|
-
.
|
|
1262
|
-
.
|
|
1263
|
-
.
|
|
1264
|
-
.
|
|
1265
|
-
.
|
|
1266
|
-
.
|
|
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({
|
|
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('.
|
|
1422
|
+
query('.panel')
|
|
1315
1423
|
], KRChatbot.prototype, "_panelEl", void 0);
|
|
1316
1424
|
__decorate([
|
|
1317
|
-
query('.
|
|
1425
|
+
query('.messages')
|
|
1318
1426
|
], KRChatbot.prototype, "_messagesEl", void 0);
|
|
1319
1427
|
__decorate([
|
|
1320
|
-
query('.
|
|
1428
|
+
query('.input')
|
|
1321
1429
|
], KRChatbot.prototype, "_inputEl", void 0);
|
|
1322
1430
|
KRChatbot = __decorate([
|
|
1323
1431
|
customElement('kr-chatbot')
|