@kodaris/krubble-app-components 1.0.75 → 1.0.77
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 +134 -0
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +636 -23
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +696 -78
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +510 -110
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +696 -78
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +466 -66
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/chatbot.js
CHANGED
|
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
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
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
10
11
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
11
12
|
import { repeat } from 'lit/directives/repeat.js';
|
|
12
13
|
import { marked } from 'marked';
|
|
@@ -71,6 +72,10 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
71
72
|
this._dragStartTop = 0;
|
|
72
73
|
this._resizeStartW = 0;
|
|
73
74
|
this._resizeStartH = 0;
|
|
75
|
+
/** Cursor X at debug-drawer resize start. */
|
|
76
|
+
this._debugResizeStartX = 0;
|
|
77
|
+
/** Debug-drawer width at resize start. */
|
|
78
|
+
this._debugResizeStartW = 0;
|
|
74
79
|
/** Counter for depth-aware dragenter/dragleave tracking. */
|
|
75
80
|
this._dragDepth = 0;
|
|
76
81
|
/** Monotonic id source for pending attachments. */
|
|
@@ -143,6 +148,39 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
143
148
|
this._inputValue = '';
|
|
144
149
|
this._isStreaming = false;
|
|
145
150
|
this._error = '';
|
|
151
|
+
/**
|
|
152
|
+
* User-facing context warning text (`CONVERSATION_TOKEN_WARNING`). Shown as a
|
|
153
|
+
* dismissible banner in all variants. Carries only friendly guidance, no numbers.
|
|
154
|
+
*/
|
|
155
|
+
this._contextWarning = '';
|
|
156
|
+
/**
|
|
157
|
+
* True once the conversation hit the context-window limit
|
|
158
|
+
* (`CONVERSATION_LIMIT_REACHED`, or `contextExceeded` on load). Drives a
|
|
159
|
+
* blocking "start a new conversation" notice. User-facing, all variants.
|
|
160
|
+
*/
|
|
161
|
+
this._contextLimitReached = false;
|
|
162
|
+
/** Server-provided limit text (`CONVERSATION_LIMIT_REACHED`), if any. */
|
|
163
|
+
this._contextLimitMessage = '';
|
|
164
|
+
/**
|
|
165
|
+
* INTERNAL-ONLY latest telemetry snapshot (last-value-wins). Populated from
|
|
166
|
+
* `TOKEN_USAGE` events during streaming and hydrated from the conversation
|
|
167
|
+
* DTO on load. Only surfaced in the `inline` variant's debug panel.
|
|
168
|
+
*/
|
|
169
|
+
this._telemetry = null;
|
|
170
|
+
/**
|
|
171
|
+
* INTERNAL-ONLY per-iteration telemetry log for the debug panel's verbose
|
|
172
|
+
* table. Append-only within a turn; reset at the start of each send. Each row
|
|
173
|
+
* may be augmented with its `CACHE_CHECKPOINT` info.
|
|
174
|
+
*/
|
|
175
|
+
this._iterationLog = [];
|
|
176
|
+
/** Latest cache-checkpoint info keyed by iteration, for the verbose table. */
|
|
177
|
+
this._cacheCheckpoints = {};
|
|
178
|
+
/** Whether the internal debug panel is expanded (off by default). */
|
|
179
|
+
this._debugPanelOpen = false;
|
|
180
|
+
/** Current width of the debug drawer in px (user-resizable, persisted). */
|
|
181
|
+
this._debugDrawerWidth = 430;
|
|
182
|
+
/** True while the user is dragging the debug drawer's resize handle. */
|
|
183
|
+
this._isDebugResizing = false;
|
|
146
184
|
/** Images staged in the composer, before the message is sent. */
|
|
147
185
|
this._pendingInternalFiles = [];
|
|
148
186
|
/** True while a file is being dragged over the panel (shows the drop overlay). */
|
|
@@ -226,6 +264,25 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
226
264
|
document.removeEventListener('mouseup', this._handleMouseUp);
|
|
227
265
|
this._saveState();
|
|
228
266
|
};
|
|
267
|
+
this._handleDebugResizeMouseMove = (e) => {
|
|
268
|
+
if (!this._isDebugResizing) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
// Handle is on the drawer's left edge; dragging left (negative dx) widens it.
|
|
272
|
+
const dx = e.clientX - this._debugResizeStartX;
|
|
273
|
+
const minW = 300;
|
|
274
|
+
// Never let the chat column shrink below 300px — cap the drawer at the
|
|
275
|
+
// container width minus that reserved chat width.
|
|
276
|
+
const minChatWidth = 300;
|
|
277
|
+
const maxW = Math.max(minW, this.offsetWidth - minChatWidth);
|
|
278
|
+
this._debugDrawerWidth = Math.max(minW, Math.min(this._debugResizeStartW - dx, maxW));
|
|
279
|
+
};
|
|
280
|
+
this._handleDebugResizeMouseUp = () => {
|
|
281
|
+
this._isDebugResizing = false;
|
|
282
|
+
document.removeEventListener('mousemove', this._handleDebugResizeMouseMove);
|
|
283
|
+
document.removeEventListener('mouseup', this._handleDebugResizeMouseUp);
|
|
284
|
+
this._saveState();
|
|
285
|
+
};
|
|
229
286
|
}
|
|
230
287
|
// --- Lifecycle ---
|
|
231
288
|
connectedCallback() {
|
|
@@ -247,10 +304,10 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
247
304
|
}
|
|
248
305
|
}).catch(() => { });
|
|
249
306
|
// Inline mode fills its container and is always open — the floating widget's
|
|
250
|
-
// toggle/drag/resize state
|
|
307
|
+
// toggle/drag/resize state doesn't apply, but the composer message and debug
|
|
308
|
+
// drawer state (restored inside _restoreState) do.
|
|
251
309
|
if (this.variant === 'inline') {
|
|
252
310
|
this.opened = true;
|
|
253
|
-
return;
|
|
254
311
|
}
|
|
255
312
|
this._restoreState();
|
|
256
313
|
}
|
|
@@ -440,7 +497,12 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
440
497
|
}
|
|
441
498
|
_saveState() {
|
|
442
499
|
try {
|
|
443
|
-
const state = {
|
|
500
|
+
const state = {
|
|
501
|
+
opened: String(this.opened),
|
|
502
|
+
message: this._inputValue,
|
|
503
|
+
debugPanelOpen: String(this._debugPanelOpen),
|
|
504
|
+
debugDrawerWidth: String(this._debugDrawerWidth),
|
|
505
|
+
};
|
|
444
506
|
if (this._panelEl) {
|
|
445
507
|
const s = this._panelEl.style;
|
|
446
508
|
if (s.left) {
|
|
@@ -470,7 +532,17 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
470
532
|
return;
|
|
471
533
|
}
|
|
472
534
|
const state = JSON.parse(raw);
|
|
473
|
-
|
|
535
|
+
// Draft text and the debug drawer are persisted for all variants.
|
|
536
|
+
if (typeof state.message === 'string') {
|
|
537
|
+
this._inputValue = state.message;
|
|
538
|
+
}
|
|
539
|
+
this._debugPanelOpen = state.debugPanelOpen === 'true';
|
|
540
|
+
const w = Number(state.debugDrawerWidth);
|
|
541
|
+
if (w >= 300) {
|
|
542
|
+
this._debugDrawerWidth = w;
|
|
543
|
+
}
|
|
544
|
+
// The floating widget's open/layout state doesn't apply to inline mode.
|
|
545
|
+
if (this.variant !== 'inline' && state.opened === 'true') {
|
|
474
546
|
this.opened = true;
|
|
475
547
|
// Apply layout after first render
|
|
476
548
|
this.updateComplete.then(() => this._applyStoredLayout());
|
|
@@ -505,6 +577,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
505
577
|
}
|
|
506
578
|
_handleInputChange(e) {
|
|
507
579
|
this._inputValue = e.target.value;
|
|
580
|
+
this._saveState();
|
|
508
581
|
// Auto-resize textarea
|
|
509
582
|
const textarea = e.target;
|
|
510
583
|
textarea.style.height = 'auto';
|
|
@@ -587,15 +660,26 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
587
660
|
this._skillRequestId++;
|
|
588
661
|
const requestId = this._skillRequestId;
|
|
589
662
|
const trimmed = query.trim();
|
|
590
|
-
//
|
|
591
|
-
//
|
|
663
|
+
// An empty query matches everything; otherwise substring-match the query
|
|
664
|
+
// against name/code/description. We use case-insensitive wildcard queries on
|
|
665
|
+
// the `.raw` keyword subfields so "ord" matches anywhere in "Create Order"
|
|
666
|
+
// (a plain match/multi_match only matches whole tokens). Escape the wildcard
|
|
667
|
+
// specials so user input is treated literally, then wrap with `*...*`.
|
|
592
668
|
let must = { match_all: {} };
|
|
593
669
|
if (trimmed) {
|
|
670
|
+
const pattern = `*${trimmed.replace(/([*?\\])/g, '\\$1')}*`;
|
|
671
|
+
const value = {
|
|
672
|
+
value: pattern,
|
|
673
|
+
case_insensitive: true,
|
|
674
|
+
};
|
|
594
675
|
must = {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
676
|
+
bool: {
|
|
677
|
+
should: [
|
|
678
|
+
{ wildcard: { 'name.raw': value } },
|
|
679
|
+
{ wildcard: { 'code.raw': value } },
|
|
680
|
+
{ wildcard: { 'description.raw': value } },
|
|
681
|
+
],
|
|
682
|
+
minimum_should_match: 1,
|
|
599
683
|
},
|
|
600
684
|
};
|
|
601
685
|
}
|
|
@@ -638,6 +722,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
638
722
|
/** Insert `/code ` into the composer and focus it, ready for the message. */
|
|
639
723
|
_handleSkillSelect(skill) {
|
|
640
724
|
this._inputValue = `/${skill.code} `;
|
|
725
|
+
this._saveState();
|
|
641
726
|
this._skillMenuOpened = false;
|
|
642
727
|
this._skillBrowseOpened = false;
|
|
643
728
|
this._inputEl?.focus();
|
|
@@ -810,6 +895,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
810
895
|
};
|
|
811
896
|
this.messages = [...this.messages, userMessage];
|
|
812
897
|
this._inputValue = '';
|
|
898
|
+
this._saveState();
|
|
813
899
|
this._error = '';
|
|
814
900
|
this._userHasScrolledUp = false;
|
|
815
901
|
// Clear the composer and release its preview object URLs.
|
|
@@ -919,6 +1005,14 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
919
1005
|
this._error = '';
|
|
920
1006
|
this._inputValue = '';
|
|
921
1007
|
this._userHasScrolledUp = false;
|
|
1008
|
+
// Reset conversation-scoped context + telemetry state for the new convo.
|
|
1009
|
+
this._contextWarning = '';
|
|
1010
|
+
this._contextLimitReached = false;
|
|
1011
|
+
this._contextLimitMessage = '';
|
|
1012
|
+
this._telemetry = null;
|
|
1013
|
+
this._iterationLog = [];
|
|
1014
|
+
this._cacheCheckpoints = {};
|
|
1015
|
+
this._saveState();
|
|
922
1016
|
});
|
|
923
1017
|
}
|
|
924
1018
|
_handleMessagesScroll(e) {
|
|
@@ -934,6 +1028,22 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
934
1028
|
_handleErrorDismiss() {
|
|
935
1029
|
this._error = '';
|
|
936
1030
|
}
|
|
1031
|
+
_handleContextWarningDismiss() {
|
|
1032
|
+
this._contextWarning = '';
|
|
1033
|
+
}
|
|
1034
|
+
_handleToggleDebugPanel() {
|
|
1035
|
+
this._debugPanelOpen = !this._debugPanelOpen;
|
|
1036
|
+
this._saveState();
|
|
1037
|
+
}
|
|
1038
|
+
_handleDebugResizeMouseDown(e) {
|
|
1039
|
+
this._isDebugResizing = true;
|
|
1040
|
+
this._debugResizeStartX = e.clientX;
|
|
1041
|
+
this._debugResizeStartW = this._debugDrawerWidth;
|
|
1042
|
+
e.preventDefault();
|
|
1043
|
+
e.stopPropagation();
|
|
1044
|
+
document.addEventListener('mousemove', this._handleDebugResizeMouseMove);
|
|
1045
|
+
document.addEventListener('mouseup', this._handleDebugResizeMouseUp);
|
|
1046
|
+
}
|
|
937
1047
|
_readStreamChunk() {
|
|
938
1048
|
if (!this._reader) {
|
|
939
1049
|
return;
|
|
@@ -997,6 +1107,47 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
997
1107
|
else if (data.type === 'ERROR') {
|
|
998
1108
|
this._handleStreamError(String(data.message || 'Unknown error'));
|
|
999
1109
|
}
|
|
1110
|
+
else if (data.type === 'CONVERSATION_TOKEN_WARNING') {
|
|
1111
|
+
// User-facing: friendly "conversation is getting long" guidance.
|
|
1112
|
+
// Text only, never sent to the model — render verbatim as a banner.
|
|
1113
|
+
this._contextWarning = String(data.message || '');
|
|
1114
|
+
}
|
|
1115
|
+
else if (data.type === 'CONVERSATION_LIMIT_REACHED') {
|
|
1116
|
+
// User-facing hard stop. Carries finished:true (handled below).
|
|
1117
|
+
// Clear any soft warning so we don't double-message the user.
|
|
1118
|
+
this._contextLimitReached = true;
|
|
1119
|
+
this._contextLimitMessage = String(data.message || '');
|
|
1120
|
+
this._contextWarning = '';
|
|
1121
|
+
}
|
|
1122
|
+
else if (data.type === 'TOKEN_USAGE') {
|
|
1123
|
+
// INTERNAL-ONLY debug telemetry. Update the panel snapshot and the
|
|
1124
|
+
// per-iteration log; never render as a chat bubble.
|
|
1125
|
+
if (data.data) {
|
|
1126
|
+
const usage = data.data;
|
|
1127
|
+
this._telemetry = usage;
|
|
1128
|
+
// A new turn starts at iteration 1 — clear the prior turn's log
|
|
1129
|
+
// only now (not at send), so it stays visible until fresh data
|
|
1130
|
+
// actually arrives rather than blanking out on submit.
|
|
1131
|
+
if (usage.iteration === 1) {
|
|
1132
|
+
this._iterationLog = [usage];
|
|
1133
|
+
this._cacheCheckpoints = {};
|
|
1134
|
+
}
|
|
1135
|
+
else {
|
|
1136
|
+
this._iterationLog = [...this._iterationLog, usage];
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
else if (data.type === 'CACHE_CHECKPOINT') {
|
|
1141
|
+
// INTERNAL-ONLY debug telemetry. Records when/under which mode the
|
|
1142
|
+
// history checkpoint was placed, keyed by iteration for the table.
|
|
1143
|
+
if (data.data) {
|
|
1144
|
+
const checkpoint = data.data;
|
|
1145
|
+
this._cacheCheckpoints = {
|
|
1146
|
+
...this._cacheCheckpoints,
|
|
1147
|
+
[checkpoint.iteration]: checkpoint,
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1000
1151
|
if (data.finished) {
|
|
1001
1152
|
this._isStreaming = false;
|
|
1002
1153
|
this._reader = null;
|
|
@@ -1140,6 +1291,158 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
1140
1291
|
}
|
|
1141
1292
|
return content;
|
|
1142
1293
|
}
|
|
1294
|
+
/** Compact integer formatting for the debug panel (e.g. 88928 → "88,928"). */
|
|
1295
|
+
_fmtTokens(n) {
|
|
1296
|
+
if (n == null || isNaN(n)) {
|
|
1297
|
+
return '—';
|
|
1298
|
+
}
|
|
1299
|
+
return n.toLocaleString('en-US');
|
|
1300
|
+
}
|
|
1301
|
+
/** Approximate USD formatting for the debug panel (e.g. 0.1434 → "~$0.1434"). */
|
|
1302
|
+
_fmtCost(n) {
|
|
1303
|
+
if (n == null || isNaN(n)) {
|
|
1304
|
+
return '—';
|
|
1305
|
+
}
|
|
1306
|
+
return '~$' + n.toFixed(4);
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* INTERNAL-ONLY debug telemetry panel. Rendered only in the inline variant
|
|
1310
|
+
* (guarded by the caller) and only when the user has toggled it open. Shows
|
|
1311
|
+
* three scopes from the latest TOKEN_USAGE event — context gauge, this turn,
|
|
1312
|
+
* lifetime — plus a verbose per-iteration table. Never customer-facing.
|
|
1313
|
+
*/
|
|
1314
|
+
_renderDebugPanel() {
|
|
1315
|
+
return html `
|
|
1316
|
+
<div
|
|
1317
|
+
class=${classMap({
|
|
1318
|
+
'debug-drawer': true,
|
|
1319
|
+
'debug-drawer--open': this._debugPanelOpen,
|
|
1320
|
+
'debug-drawer--resizing': this._isDebugResizing,
|
|
1321
|
+
})}
|
|
1322
|
+
style=${styleMap({ width: `${this._debugDrawerWidth}px` })}
|
|
1323
|
+
>
|
|
1324
|
+
<div
|
|
1325
|
+
class="debug-drawer__resize"
|
|
1326
|
+
@mousedown=${this._handleDebugResizeMouseDown}
|
|
1327
|
+
></div>
|
|
1328
|
+
<div class="debug-drawer__header">
|
|
1329
|
+
<span class="debug-drawer__title">Debug</span>
|
|
1330
|
+
<button
|
|
1331
|
+
class="header-action"
|
|
1332
|
+
@click=${this._handleToggleDebugPanel}
|
|
1333
|
+
title="Close debug panel"
|
|
1334
|
+
>
|
|
1335
|
+
<svg
|
|
1336
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1337
|
+
fill="none"
|
|
1338
|
+
viewBox="0 0 24 24"
|
|
1339
|
+
stroke-width="2"
|
|
1340
|
+
stroke="currentColor"
|
|
1341
|
+
class="header-action-icon"
|
|
1342
|
+
>
|
|
1343
|
+
<path
|
|
1344
|
+
stroke-linecap="round"
|
|
1345
|
+
stroke-linejoin="round"
|
|
1346
|
+
d="M6 18 18 6M6 6l12 12"
|
|
1347
|
+
/>
|
|
1348
|
+
</svg>
|
|
1349
|
+
</button>
|
|
1350
|
+
</div>
|
|
1351
|
+
<div class="debug-drawer__body">
|
|
1352
|
+
${this._telemetry ? html `
|
|
1353
|
+
<div class="debug-section">
|
|
1354
|
+
<div class="debug-section__label">Context</div>
|
|
1355
|
+
<div class="debug-gauge">
|
|
1356
|
+
<div class="debug-gauge__bar">
|
|
1357
|
+
<div
|
|
1358
|
+
class="debug-gauge__fill"
|
|
1359
|
+
style=${styleMap({ width: `${Math.min(100, Math.max(0, this._telemetry.contextUsedPercent || 0))}%` })}
|
|
1360
|
+
></div>
|
|
1361
|
+
</div>
|
|
1362
|
+
<span class="debug-gauge__text">
|
|
1363
|
+
${this._fmtTokens(this._telemetry.currentContextTokens)} / ${this._fmtTokens(this._telemetry.contextWindowTokens)}
|
|
1364
|
+
· ${this._telemetry.contextUsedPercent ?? 0}%
|
|
1365
|
+
</span>
|
|
1366
|
+
</div>
|
|
1367
|
+
</div>
|
|
1368
|
+
|
|
1369
|
+
<div class="debug-section">
|
|
1370
|
+
<div class="debug-section__label">This turn</div>
|
|
1371
|
+
<div class="debug-metrics">
|
|
1372
|
+
<span class="debug-metrics__key">Input</span>
|
|
1373
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.turnInputTokens)}</span>
|
|
1374
|
+
<span class="debug-metrics__key">Output</span>
|
|
1375
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.turnOutputTokens)}</span>
|
|
1376
|
+
<span class="debug-metrics__key">Cache read</span>
|
|
1377
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.turnCacheReadTokens)}</span>
|
|
1378
|
+
<span class="debug-metrics__key">Hit rate</span>
|
|
1379
|
+
<span class="debug-metrics__val">${this._telemetry.turnHitRate ?? '—'}%</span>
|
|
1380
|
+
<span class="debug-metrics__key">Cost</span>
|
|
1381
|
+
<span class="debug-metrics__val debug-cost">${this._fmtCost(this._telemetry.turnCostUsd)}</span>
|
|
1382
|
+
</div>
|
|
1383
|
+
</div>
|
|
1384
|
+
|
|
1385
|
+
<div class="debug-section">
|
|
1386
|
+
<div class="debug-section__label">Lifetime</div>
|
|
1387
|
+
<div class="debug-metrics">
|
|
1388
|
+
<span class="debug-metrics__key">Input</span>
|
|
1389
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.conversationInputTokens)}</span>
|
|
1390
|
+
<span class="debug-metrics__key">Output</span>
|
|
1391
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.conversationOutputTokens)}</span>
|
|
1392
|
+
<span class="debug-metrics__key">Cache read</span>
|
|
1393
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.conversationCacheReadTokens)}</span>
|
|
1394
|
+
<span class="debug-metrics__key">Cache write</span>
|
|
1395
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.conversationCacheWriteTokens)}</span>
|
|
1396
|
+
<span class="debug-metrics__key">Cost so far</span>
|
|
1397
|
+
<span class="debug-metrics__val debug-cost">${this._fmtCost(this._telemetry.conversationCostUsd)}</span>
|
|
1398
|
+
</div>
|
|
1399
|
+
</div>
|
|
1400
|
+
` : html `
|
|
1401
|
+
<div class="debug-empty">No telemetry yet — send a message to populate.</div>
|
|
1402
|
+
`}
|
|
1403
|
+
|
|
1404
|
+
${this._iterationLog.length ? html `
|
|
1405
|
+
<div class="debug-section">
|
|
1406
|
+
<div class="debug-section__label">Iterations (this turn)</div>
|
|
1407
|
+
<div class="debug-iterations-scroll">
|
|
1408
|
+
<table class="debug-iterations">
|
|
1409
|
+
<thead>
|
|
1410
|
+
<tr>
|
|
1411
|
+
<th>#</th>
|
|
1412
|
+
<th>In</th>
|
|
1413
|
+
<th>Out</th>
|
|
1414
|
+
<th>Cache R</th>
|
|
1415
|
+
<th>Cache W</th>
|
|
1416
|
+
<th>Hit</th>
|
|
1417
|
+
<th>Cost</th>
|
|
1418
|
+
<th>Ckpt</th>
|
|
1419
|
+
</tr>
|
|
1420
|
+
</thead>
|
|
1421
|
+
<tbody>
|
|
1422
|
+
${this._iterationLog.map((it) => {
|
|
1423
|
+
const ckpt = this._cacheCheckpoints[it.iteration];
|
|
1424
|
+
return html `
|
|
1425
|
+
<tr>
|
|
1426
|
+
<td>${it.iteration}</td>
|
|
1427
|
+
<td>${this._fmtTokens(it.iterationInput)}</td>
|
|
1428
|
+
<td>${this._fmtTokens(it.iterationOutput)}</td>
|
|
1429
|
+
<td>${this._fmtTokens(it.iterationCacheRead)}</td>
|
|
1430
|
+
<td>${this._fmtTokens(it.iterationCacheWrite)}</td>
|
|
1431
|
+
<td>${it.iterationHitRate ?? '—'}%</td>
|
|
1432
|
+
<td class="debug-cost">${this._fmtCost(it.iterationCostUsd)}</td>
|
|
1433
|
+
<td>${ckpt ? ckpt.mode : '—'}</td>
|
|
1434
|
+
</tr>
|
|
1435
|
+
`;
|
|
1436
|
+
})}
|
|
1437
|
+
</tbody>
|
|
1438
|
+
</table>
|
|
1439
|
+
</div>
|
|
1440
|
+
</div>
|
|
1441
|
+
` : nothing}
|
|
1442
|
+
</div>
|
|
1443
|
+
</div>
|
|
1444
|
+
`;
|
|
1445
|
+
}
|
|
1143
1446
|
render() {
|
|
1144
1447
|
return html `
|
|
1145
1448
|
${(this.hideToggle || this.variant === 'inline') ? nothing : html `
|
|
@@ -1169,7 +1472,9 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
1169
1472
|
class=${classMap({
|
|
1170
1473
|
'panel': true,
|
|
1171
1474
|
'panel--opened': this.opened,
|
|
1475
|
+
'panel--resizing-debug': this._isDebugResizing,
|
|
1172
1476
|
})}
|
|
1477
|
+
style=${styleMap(this.variant === 'inline' && this._debugPanelOpen ? { paddingRight: `${this._debugDrawerWidth}px` } : {})}
|
|
1173
1478
|
@dragenter=${this._handleDragEnter}
|
|
1174
1479
|
@dragover=${this._handleDragOver}
|
|
1175
1480
|
@dragleave=${this._handleDragLeave}
|
|
@@ -1242,6 +1547,31 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
1242
1547
|
</div>
|
|
1243
1548
|
</div>
|
|
1244
1549
|
<div class="header-actions">
|
|
1550
|
+
${this.variant === 'inline' ? html `
|
|
1551
|
+
<button
|
|
1552
|
+
class=${classMap({
|
|
1553
|
+
'header-action': true,
|
|
1554
|
+
'header-action--active': this._debugPanelOpen,
|
|
1555
|
+
})}
|
|
1556
|
+
@click=${this._handleToggleDebugPanel}
|
|
1557
|
+
title="Debug telemetry"
|
|
1558
|
+
>
|
|
1559
|
+
<svg
|
|
1560
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1561
|
+
fill="none"
|
|
1562
|
+
viewBox="0 0 24 24"
|
|
1563
|
+
stroke-width="1.5"
|
|
1564
|
+
stroke="currentColor"
|
|
1565
|
+
class="header-action-icon"
|
|
1566
|
+
>
|
|
1567
|
+
<path
|
|
1568
|
+
stroke-linecap="round"
|
|
1569
|
+
stroke-linejoin="round"
|
|
1570
|
+
d="M3.75 3v11.25A2.25 2.25 0 0 0 6 16.5h2.25M3.75 3h-1.5m1.5 0h16.5m0 0h1.5m-1.5 0v11.25A2.25 2.25 0 0 1 18 16.5h-2.25m-7.5 0h7.5m-7.5 0-1 3m8.5-3 1 3m0 0 .5 1.5m-.5-1.5h-9.5m0 0-.5 1.5M9 11.25v1.5M12 9v3.75m3-6v6"
|
|
1571
|
+
/>
|
|
1572
|
+
</svg>
|
|
1573
|
+
</button>
|
|
1574
|
+
` : nothing}
|
|
1245
1575
|
${this.messages.length > 0 ? html `
|
|
1246
1576
|
<button
|
|
1247
1577
|
class="header-action"
|
|
@@ -1406,6 +1736,37 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
1406
1736
|
</div>
|
|
1407
1737
|
` : nothing}
|
|
1408
1738
|
|
|
1739
|
+
${this._contextLimitReached ? html `
|
|
1740
|
+
<div class="context-limit" role="alert">
|
|
1741
|
+
${this._contextLimitMessage
|
|
1742
|
+
|| 'This conversation has reached its limit. Please start a new one to continue.'}
|
|
1743
|
+
</div>
|
|
1744
|
+
` : this._contextWarning ? html `
|
|
1745
|
+
<div class="context-warning" role="status">
|
|
1746
|
+
<span>${this._contextWarning}</span>
|
|
1747
|
+
<button
|
|
1748
|
+
class="context-warning-dismiss"
|
|
1749
|
+
title="Dismiss"
|
|
1750
|
+
@click=${this._handleContextWarningDismiss}
|
|
1751
|
+
>
|
|
1752
|
+
<svg
|
|
1753
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1754
|
+
fill="none"
|
|
1755
|
+
viewBox="0 0 24 24"
|
|
1756
|
+
stroke-width="2"
|
|
1757
|
+
stroke="currentColor"
|
|
1758
|
+
class="context-warning-dismiss-icon"
|
|
1759
|
+
>
|
|
1760
|
+
<path
|
|
1761
|
+
stroke-linecap="round"
|
|
1762
|
+
stroke-linejoin="round"
|
|
1763
|
+
d="M6 18 18 6M6 6l12 12"
|
|
1764
|
+
/>
|
|
1765
|
+
</svg>
|
|
1766
|
+
</button>
|
|
1767
|
+
</div>
|
|
1768
|
+
` : nothing}
|
|
1769
|
+
|
|
1409
1770
|
<div class="footer">
|
|
1410
1771
|
${this._pendingInternalFiles.length ? html `
|
|
1411
1772
|
<div class="composer-attachments">
|
|
@@ -1505,6 +1866,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
1505
1866
|
</div>
|
|
1506
1867
|
</div>
|
|
1507
1868
|
${this._skillBrowseOpened ? this._renderSkillBrowse() : nothing}
|
|
1869
|
+
${this.variant === 'inline' ? this._renderDebugPanel() : nothing}
|
|
1508
1870
|
</div>
|
|
1509
1871
|
`;
|
|
1510
1872
|
}
|
|
@@ -1686,6 +2048,11 @@ KRChatbot.styles = css `
|
|
|
1686
2048
|
color: #000000;
|
|
1687
2049
|
}
|
|
1688
2050
|
|
|
2051
|
+
.header-action--active {
|
|
2052
|
+
background: rgba(0, 0, 0, 0.08);
|
|
2053
|
+
color: #000000;
|
|
2054
|
+
}
|
|
2055
|
+
|
|
1689
2056
|
.header-action-icon {
|
|
1690
2057
|
width: 20px;
|
|
1691
2058
|
height: 20px;
|
|
@@ -1715,19 +2082,6 @@ KRChatbot.styles = css `
|
|
|
1715
2082
|
margin-right: auto;
|
|
1716
2083
|
}
|
|
1717
2084
|
|
|
1718
|
-
.messages::-webkit-scrollbar {
|
|
1719
|
-
width: 4px;
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
|
-
.messages::-webkit-scrollbar-track {
|
|
1723
|
-
background: transparent;
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
.messages::-webkit-scrollbar-thumb {
|
|
1727
|
-
background: rgba(0, 0, 0, 0.08);
|
|
1728
|
-
border-radius: 2px;
|
|
1729
|
-
}
|
|
1730
|
-
|
|
1731
2085
|
.empty {
|
|
1732
2086
|
flex: 1;
|
|
1733
2087
|
display: flex;
|
|
@@ -1966,6 +2320,238 @@ KRChatbot.styles = css `
|
|
|
1966
2320
|
height: 14px;
|
|
1967
2321
|
}
|
|
1968
2322
|
|
|
2323
|
+
/* Context warning banner (user-facing, dismissible, non-blocking) */
|
|
2324
|
+
.context-warning {
|
|
2325
|
+
padding: 8px 16px;
|
|
2326
|
+
background: #fffbeb;
|
|
2327
|
+
color: #92400e;
|
|
2328
|
+
font-size: 13px;
|
|
2329
|
+
display: flex;
|
|
2330
|
+
align-items: center;
|
|
2331
|
+
justify-content: space-between;
|
|
2332
|
+
gap: 8px;
|
|
2333
|
+
flex-shrink: 0;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
.context-warning-dismiss {
|
|
2337
|
+
background: none;
|
|
2338
|
+
border: none;
|
|
2339
|
+
color: #92400e;
|
|
2340
|
+
cursor: pointer;
|
|
2341
|
+
padding: 2px;
|
|
2342
|
+
display: flex;
|
|
2343
|
+
align-items: center;
|
|
2344
|
+
flex-shrink: 0;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
.context-warning-dismiss-icon {
|
|
2348
|
+
width: 14px;
|
|
2349
|
+
height: 14px;
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
/* Context-limit notice (user-facing, blocking — steers to a new conversation) */
|
|
2353
|
+
.context-limit {
|
|
2354
|
+
padding: 10px 16px;
|
|
2355
|
+
background: var(--kr-chatbot-error-bg);
|
|
2356
|
+
color: var(--kr-chatbot-error-text);
|
|
2357
|
+
font-size: 13px;
|
|
2358
|
+
line-height: 1.5;
|
|
2359
|
+
flex-shrink: 0;
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
/*
|
|
2363
|
+
* INTERNAL-ONLY debug telemetry drawer. A layout drawer docked to the right
|
|
2364
|
+
* of the inline chat — the chat column shrinks to make room; it does not
|
|
2365
|
+
* overlay. Closed by default (width 0), animates open. Not customer-facing —
|
|
2366
|
+
* token counts, cache internals, and approximate cost estimates for internal
|
|
2367
|
+
* debugging. Styled to match the chatbot's light theme (Inter, design tokens).
|
|
2368
|
+
*/
|
|
2369
|
+
/*
|
|
2370
|
+
* The chat column reserves space for the drawer by animating padding-right,
|
|
2371
|
+
* so opening the drawer shrinks the chat (header/messages/composer) rather
|
|
2372
|
+
* than overlaying it. Only applies in inline mode.
|
|
2373
|
+
*/
|
|
2374
|
+
:host([variant='inline']) .panel {
|
|
2375
|
+
transition: padding-right 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
/* Track the cursor 1:1 while dragging the drawer's resize handle. */
|
|
2379
|
+
:host([variant='inline']) .panel--resizing-debug {
|
|
2380
|
+
transition: none;
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
.debug-drawer {
|
|
2384
|
+
position: absolute;
|
|
2385
|
+
top: 0;
|
|
2386
|
+
right: 0;
|
|
2387
|
+
bottom: 0;
|
|
2388
|
+
background: var(--kr-chatbot-bg);
|
|
2389
|
+
display: flex;
|
|
2390
|
+
flex-direction: column;
|
|
2391
|
+
transform: translateX(100%);
|
|
2392
|
+
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
2393
|
+
color: #000000;
|
|
2394
|
+
z-index: 5;
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
/* While dragging the resize handle, suppress the slide transition so the
|
|
2398
|
+
drawer tracks the cursor 1:1. */
|
|
2399
|
+
.debug-drawer--resizing {
|
|
2400
|
+
transition: none;
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
.debug-drawer--open {
|
|
2404
|
+
transform: translateX(0);
|
|
2405
|
+
box-shadow: -4px 0 20px rgba(10, 26, 46, 0.07);
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
/* Left-edge grab handle for resizing the drawer. */
|
|
2409
|
+
.debug-drawer__resize {
|
|
2410
|
+
position: absolute;
|
|
2411
|
+
top: 0;
|
|
2412
|
+
left: 0;
|
|
2413
|
+
bottom: 0;
|
|
2414
|
+
width: 10px;
|
|
2415
|
+
cursor: ew-resize;
|
|
2416
|
+
z-index: 10;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
.debug-drawer__header {
|
|
2420
|
+
display: flex;
|
|
2421
|
+
align-items: center;
|
|
2422
|
+
justify-content: space-between;
|
|
2423
|
+
padding: 0 12px 0 20px;
|
|
2424
|
+
height: 64px;
|
|
2425
|
+
flex-shrink: 0;
|
|
2426
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
.debug-drawer__title {
|
|
2430
|
+
font-size: 14px;
|
|
2431
|
+
font-weight: 600;
|
|
2432
|
+
color: #000000;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
.debug-drawer__body {
|
|
2436
|
+
flex: 1;
|
|
2437
|
+
overflow-y: auto;
|
|
2438
|
+
overflow-x: hidden;
|
|
2439
|
+
padding: 4px 0;
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
.debug-section {
|
|
2443
|
+
padding: 14px 20px;
|
|
2444
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
.debug-section:last-child {
|
|
2448
|
+
border-bottom: none;
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
.debug-section__label {
|
|
2452
|
+
text-transform: uppercase;
|
|
2453
|
+
letter-spacing: 0.06em;
|
|
2454
|
+
color: #000000;
|
|
2455
|
+
font-size: 11px;
|
|
2456
|
+
font-weight: 600;
|
|
2457
|
+
margin-bottom: 10px;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
/* Context-fullness gauge */
|
|
2461
|
+
.debug-gauge {
|
|
2462
|
+
display: flex;
|
|
2463
|
+
align-items: center;
|
|
2464
|
+
gap: 10px;
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
.debug-gauge__bar {
|
|
2468
|
+
flex: 1;
|
|
2469
|
+
height: 8px;
|
|
2470
|
+
border-radius: 4px;
|
|
2471
|
+
background: rgb(244 244 244 / 100%);
|
|
2472
|
+
overflow: hidden;
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
.debug-gauge__fill {
|
|
2476
|
+
height: 100%;
|
|
2477
|
+
border-radius: 4px;
|
|
2478
|
+
background: var(--kr-chatbot-primary);
|
|
2479
|
+
transition: width 0.3s ease;
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
.debug-gauge__text {
|
|
2483
|
+
color: #000000;
|
|
2484
|
+
font-size: 12px;
|
|
2485
|
+
white-space: nowrap;
|
|
2486
|
+
font-variant-numeric: tabular-nums;
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
/* Metric rows */
|
|
2490
|
+
.debug-metrics {
|
|
2491
|
+
display: grid;
|
|
2492
|
+
grid-template-columns: auto 1fr;
|
|
2493
|
+
column-gap: 12px;
|
|
2494
|
+
row-gap: 7px;
|
|
2495
|
+
font-size: 13px;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
.debug-metrics__key {
|
|
2499
|
+
color: #000000;
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
.debug-metrics__val {
|
|
2503
|
+
color: #000000;
|
|
2504
|
+
text-align: right;
|
|
2505
|
+
font-variant-numeric: tabular-nums;
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
.debug-cost {
|
|
2509
|
+
font-weight: 600;
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
/* Per-iteration table (verbose). The wrapper scrolls horizontally on its own
|
|
2513
|
+
so a wide table never forces the whole drawer to scroll sideways. */
|
|
2514
|
+
.debug-iterations-scroll {
|
|
2515
|
+
overflow-x: auto;
|
|
2516
|
+
max-width: 100%;
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
.debug-iterations {
|
|
2520
|
+
width: 100%;
|
|
2521
|
+
border-collapse: collapse;
|
|
2522
|
+
font-size: 12px;
|
|
2523
|
+
font-variant-numeric: tabular-nums;
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
.debug-iterations th,
|
|
2527
|
+
.debug-iterations td {
|
|
2528
|
+
padding: 5px 6px;
|
|
2529
|
+
text-align: right;
|
|
2530
|
+
white-space: nowrap;
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
.debug-iterations th {
|
|
2534
|
+
color: #000000;
|
|
2535
|
+
font-weight: 600;
|
|
2536
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.10);
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
.debug-iterations tbody tr:not(:last-child) td {
|
|
2540
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
.debug-iterations td:first-child,
|
|
2544
|
+
.debug-iterations th:first-child {
|
|
2545
|
+
text-align: left;
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
.debug-empty {
|
|
2549
|
+
color: #000000;
|
|
2550
|
+
padding: 20px;
|
|
2551
|
+
font-size: 13px;
|
|
2552
|
+
text-align: center;
|
|
2553
|
+
}
|
|
2554
|
+
|
|
1969
2555
|
/* Footer / Input */
|
|
1970
2556
|
.footer {
|
|
1971
2557
|
padding: 16px 16px 16px;
|
|
@@ -2473,6 +3059,33 @@ __decorate([
|
|
|
2473
3059
|
__decorate([
|
|
2474
3060
|
state()
|
|
2475
3061
|
], KRChatbot.prototype, "_error", void 0);
|
|
3062
|
+
__decorate([
|
|
3063
|
+
state()
|
|
3064
|
+
], KRChatbot.prototype, "_contextWarning", void 0);
|
|
3065
|
+
__decorate([
|
|
3066
|
+
state()
|
|
3067
|
+
], KRChatbot.prototype, "_contextLimitReached", void 0);
|
|
3068
|
+
__decorate([
|
|
3069
|
+
state()
|
|
3070
|
+
], KRChatbot.prototype, "_contextLimitMessage", void 0);
|
|
3071
|
+
__decorate([
|
|
3072
|
+
state()
|
|
3073
|
+
], KRChatbot.prototype, "_telemetry", void 0);
|
|
3074
|
+
__decorate([
|
|
3075
|
+
state()
|
|
3076
|
+
], KRChatbot.prototype, "_iterationLog", void 0);
|
|
3077
|
+
__decorate([
|
|
3078
|
+
state()
|
|
3079
|
+
], KRChatbot.prototype, "_cacheCheckpoints", void 0);
|
|
3080
|
+
__decorate([
|
|
3081
|
+
state()
|
|
3082
|
+
], KRChatbot.prototype, "_debugPanelOpen", void 0);
|
|
3083
|
+
__decorate([
|
|
3084
|
+
state()
|
|
3085
|
+
], KRChatbot.prototype, "_debugDrawerWidth", void 0);
|
|
3086
|
+
__decorate([
|
|
3087
|
+
state()
|
|
3088
|
+
], KRChatbot.prototype, "_isDebugResizing", void 0);
|
|
2476
3089
|
__decorate([
|
|
2477
3090
|
state()
|
|
2478
3091
|
], KRChatbot.prototype, "_pendingInternalFiles", void 0);
|