@kodaris/krubble-app-components 1.0.113 → 1.0.115
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 +142 -22
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +1320 -459
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +1332 -472
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +859 -428
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +1318 -458
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +833 -402
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -5547,6 +5547,28 @@ const CONFIRMATION_DECISIONS = [
|
|
|
5547
5547
|
label: 'No',
|
|
5548
5548
|
},
|
|
5549
5549
|
];
|
|
5550
|
+
/**
|
|
5551
|
+
* The modes as shown in the composer's mode selector, in menu order. `label` is
|
|
5552
|
+
* the short trigger text (Gemini-style inline label); `description` is the menu
|
|
5553
|
+
* sublabel. Written for a non-technical user — no "guarded tool" jargon.
|
|
5554
|
+
*/
|
|
5555
|
+
const CHATBOT_MODES = [
|
|
5556
|
+
{
|
|
5557
|
+
mode: 'NORMAL',
|
|
5558
|
+
label: 'Ask',
|
|
5559
|
+
description: 'Asks before making changes',
|
|
5560
|
+
},
|
|
5561
|
+
{
|
|
5562
|
+
mode: 'EDIT',
|
|
5563
|
+
label: 'Auto',
|
|
5564
|
+
description: 'Makes changes automatically',
|
|
5565
|
+
},
|
|
5566
|
+
{
|
|
5567
|
+
mode: 'PLAN',
|
|
5568
|
+
label: 'Plan',
|
|
5569
|
+
description: 'Suggests a plan, makes no changes',
|
|
5570
|
+
},
|
|
5571
|
+
];
|
|
5550
5572
|
/** Image extensions the chatbot renders as thumbnails (used when MIME is absent). */
|
|
5551
5573
|
const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
|
5552
5574
|
/**
|
|
@@ -5621,6 +5643,25 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
5621
5643
|
constructor() {
|
|
5622
5644
|
super(...arguments);
|
|
5623
5645
|
// --- Private properties ---
|
|
5646
|
+
/**
|
|
5647
|
+
* Internal toggle for how the action-confirmation prompt (Rail 3) is presented:
|
|
5648
|
+
* - `false`: the compact card that floats over the messages, pinned above the
|
|
5649
|
+
* composer — plain-language ask + Yes/Always/No only, no tool arguments.
|
|
5650
|
+
* - `true`: the prompt renders inline in the message stream (Claude-CLI style),
|
|
5651
|
+
* grows to any height (no inner scroll — the transcript scrolls), and offers a
|
|
5652
|
+
* "Show details" disclosure of the raw tool input for technical users.
|
|
5653
|
+
* Flip here to switch approaches; the floating path is unchanged when false.
|
|
5654
|
+
*/
|
|
5655
|
+
this._inlineConfirm = true;
|
|
5656
|
+
/**
|
|
5657
|
+
* Internal toggle for how the assistant's reasoning ("thinking") is presented:
|
|
5658
|
+
* - `false`: the current collapsible grey "Thinking" disclosure (kept so we can
|
|
5659
|
+
* revert).
|
|
5660
|
+
* - `true`: reasoning renders as a plain agentic message — same text style as a
|
|
5661
|
+
* normal assistant reply, no box, no collapse.
|
|
5662
|
+
* Flip here to switch approaches.
|
|
5663
|
+
*/
|
|
5664
|
+
this._reasoningAsMessage = true;
|
|
5624
5665
|
this._reader = null;
|
|
5625
5666
|
this._decoder = new TextDecoder();
|
|
5626
5667
|
this._buffer = '';
|
|
@@ -5635,10 +5676,10 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
5635
5676
|
this._dragStartTop = 0;
|
|
5636
5677
|
this._resizeStartW = 0;
|
|
5637
5678
|
this._resizeStartH = 0;
|
|
5638
|
-
/** Cursor X at
|
|
5639
|
-
this.
|
|
5640
|
-
/**
|
|
5641
|
-
this.
|
|
5679
|
+
/** Cursor X at drawer resize start. */
|
|
5680
|
+
this._drawerResizeStartX = 0;
|
|
5681
|
+
/** Drawer width at resize start. */
|
|
5682
|
+
this._drawerResizeStartW = 0;
|
|
5642
5683
|
/** Counter for depth-aware dragenter/dragleave tracking. */
|
|
5643
5684
|
this._dragDepth = 0;
|
|
5644
5685
|
/** Monotonic id source for pending attachments. */
|
|
@@ -5804,12 +5845,21 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
5804
5845
|
this._iterationLog = [];
|
|
5805
5846
|
/** Latest cache-checkpoint info keyed by iteration, for the verbose table. */
|
|
5806
5847
|
this._cacheCheckpoints = {};
|
|
5807
|
-
/**
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5848
|
+
/**
|
|
5849
|
+
* Whether the side drawer is open (slid in). The drawer stays mounted and
|
|
5850
|
+
* animates via a CSS transform, so this drives the slide and the content push.
|
|
5851
|
+
*/
|
|
5852
|
+
this._drawerOpen = false;
|
|
5853
|
+
/**
|
|
5854
|
+
* Which view the drawer shows — settings or debug. One drawer holds both; the
|
|
5855
|
+
* header buttons switch this and the content/width swap inside. Never null, so
|
|
5856
|
+
* the content stays visible while the drawer slides shut.
|
|
5857
|
+
*/
|
|
5858
|
+
this._drawerView = 'settings';
|
|
5859
|
+
/** Current width of the drawer in px (user-resizable, persisted). */
|
|
5860
|
+
this._drawerWidth = 430;
|
|
5861
|
+
/** True while the user is dragging the drawer's resize handle. */
|
|
5862
|
+
this._isDrawerResizing = false;
|
|
5813
5863
|
/** Files staged in the composer, before the message is sent. */
|
|
5814
5864
|
this._pendingConversationFiles = [];
|
|
5815
5865
|
/** True while a file is being dragged over the panel (shows the drop overlay). */
|
|
@@ -5859,6 +5909,31 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
5859
5909
|
this._skillBrowseQuery = '';
|
|
5860
5910
|
/** True while the mic is live and speech is being transcribed into the composer. */
|
|
5861
5911
|
this._isDictating = false;
|
|
5912
|
+
/**
|
|
5913
|
+
* Conversation posture for guarded tools (the composer's mode selector). Sent on
|
|
5914
|
+
* each turn's `mode` field and kept in sync with the server's `CONVERSATION_STATE`
|
|
5915
|
+
* event. Defaults to NORMAL (ask before each guarded tool).
|
|
5916
|
+
*/
|
|
5917
|
+
this._mode = 'NORMAL';
|
|
5918
|
+
/** Whether the composer's mode menu (Ask / Auto / Plan) is open. */
|
|
5919
|
+
this._modeMenuOpened = false;
|
|
5920
|
+
/**
|
|
5921
|
+
* Whether to show the tools the assistant ran, as activity lines in the
|
|
5922
|
+
* transcript. On by default; a user can hide them from the settings panel.
|
|
5923
|
+
* User-controlled in the settings panel; persisted in localStorage.
|
|
5924
|
+
*/
|
|
5925
|
+
this._showTools = true;
|
|
5926
|
+
/**
|
|
5927
|
+
* Whether to also show each tool's inputs and results under its line. Only
|
|
5928
|
+
* has an effect when `_showTools` is on. Off by default.
|
|
5929
|
+
*/
|
|
5930
|
+
this._showToolInputs = false;
|
|
5931
|
+
/**
|
|
5932
|
+
* Whether to show the raw tool input inside the confirmation card (the JSON the
|
|
5933
|
+
* model proposed for a guarded action). The plain-language ask + Yes/No always
|
|
5934
|
+
* show; this only reveals the technical detail. Off by default.
|
|
5935
|
+
*/
|
|
5936
|
+
this._showConfirmationInput = false;
|
|
5862
5937
|
/**
|
|
5863
5938
|
* Close the `/` skill menu when the click lands outside it.
|
|
5864
5939
|
*
|
|
@@ -5869,6 +5944,14 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
5869
5944
|
* inside click would look like an outside one.
|
|
5870
5945
|
*/
|
|
5871
5946
|
this._handleDocumentClick = (e) => {
|
|
5947
|
+
// Close the mode menu on any click outside the mode selector (the trigger
|
|
5948
|
+
// owns its own toggle, so exclude the whole `.mode` block).
|
|
5949
|
+
if (this._modeMenuOpened) {
|
|
5950
|
+
const modePath = e.composedPath();
|
|
5951
|
+
if (!modePath.some((el) => el instanceof HTMLElement && el.classList.contains('mode'))) {
|
|
5952
|
+
this._modeMenuOpened = false;
|
|
5953
|
+
}
|
|
5954
|
+
}
|
|
5872
5955
|
if (!this._skillMenuOpened) {
|
|
5873
5956
|
return;
|
|
5874
5957
|
}
|
|
@@ -5973,23 +6056,23 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
5973
6056
|
}
|
|
5974
6057
|
}
|
|
5975
6058
|
};
|
|
5976
|
-
this.
|
|
5977
|
-
if (!this.
|
|
6059
|
+
this._handleDrawerResizeMouseMove = (e) => {
|
|
6060
|
+
if (!this._isDrawerResizing) {
|
|
5978
6061
|
return;
|
|
5979
6062
|
}
|
|
5980
6063
|
// Handle is on the drawer's left edge; dragging left (negative dx) widens it.
|
|
5981
|
-
const dx = e.clientX - this.
|
|
6064
|
+
const dx = e.clientX - this._drawerResizeStartX;
|
|
5982
6065
|
const minW = 300;
|
|
5983
6066
|
// Never let the chat column shrink below 300px — cap the drawer at the
|
|
5984
6067
|
// container width minus that reserved chat width.
|
|
5985
6068
|
const minChatWidth = 300;
|
|
5986
6069
|
const maxW = Math.max(minW, this.offsetWidth - minChatWidth);
|
|
5987
|
-
this.
|
|
6070
|
+
this._drawerWidth = Math.max(minW, Math.min(this._drawerResizeStartW - dx, maxW));
|
|
5988
6071
|
};
|
|
5989
|
-
this.
|
|
5990
|
-
this.
|
|
5991
|
-
document.removeEventListener('mousemove', this.
|
|
5992
|
-
document.removeEventListener('mouseup', this.
|
|
6072
|
+
this._handleDrawerResizeMouseUp = () => {
|
|
6073
|
+
this._isDrawerResizing = false;
|
|
6074
|
+
document.removeEventListener('mousemove', this._handleDrawerResizeMouseMove);
|
|
6075
|
+
document.removeEventListener('mouseup', this._handleDrawerResizeMouseUp);
|
|
5993
6076
|
this._saveState();
|
|
5994
6077
|
};
|
|
5995
6078
|
}
|
|
@@ -6010,6 +6093,36 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
6010
6093
|
return KRHttp.fetch({ url: `/api/system/conversation/${this.conversationId}/messages` })
|
|
6011
6094
|
.then((r) => r.json())
|
|
6012
6095
|
.then((res) => Promise.all((res?.data || []).map((msg) => {
|
|
6096
|
+
// Persisted tool_use / tool_result rows — the message IS the row: keep
|
|
6097
|
+
// the type and parse content into `data`, the same {type, data} shape
|
|
6098
|
+
// the live stream pushes. The render reads data.* and pairs a result to
|
|
6099
|
+
// its use by data.toolUseId. No transform, so live and reload match.
|
|
6100
|
+
if (msg.type === 'tool_use' || msg.type === 'tool_result') {
|
|
6101
|
+
let data = {};
|
|
6102
|
+
try {
|
|
6103
|
+
data = JSON.parse(msg.content || '{}');
|
|
6104
|
+
}
|
|
6105
|
+
catch { /* malformed — render a bare line from what we have */ }
|
|
6106
|
+
return Promise.resolve({
|
|
6107
|
+
type: msg.type,
|
|
6108
|
+
content: '',
|
|
6109
|
+
data,
|
|
6110
|
+
});
|
|
6111
|
+
}
|
|
6112
|
+
// A persisted approve/deny record — content is {outcome, tools:[...]}.
|
|
6113
|
+
// Keep the type, parse content into `data`; the render reads msg.data.*.
|
|
6114
|
+
if (msg.type === 'action_decision') {
|
|
6115
|
+
let data = {};
|
|
6116
|
+
try {
|
|
6117
|
+
data = JSON.parse(msg.content || '{}');
|
|
6118
|
+
}
|
|
6119
|
+
catch { /* malformed — render a bare line from what we have */ }
|
|
6120
|
+
return Promise.resolve({
|
|
6121
|
+
type: 'action_decision',
|
|
6122
|
+
content: '',
|
|
6123
|
+
data,
|
|
6124
|
+
});
|
|
6125
|
+
}
|
|
6013
6126
|
// Resolve this message's `conversationFileIDs` (a JSON array string of
|
|
6014
6127
|
// ids) into full file objects — each fetch response IS the
|
|
6015
6128
|
// KRChatbotConversationFile (id + niceName + mimeType), and niceName
|
|
@@ -6264,8 +6377,13 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
6264
6377
|
const state = {
|
|
6265
6378
|
opened: String(this.opened),
|
|
6266
6379
|
message: this._inputValue,
|
|
6267
|
-
|
|
6268
|
-
|
|
6380
|
+
mode: this._mode,
|
|
6381
|
+
showTools: String(this._showTools),
|
|
6382
|
+
showToolInputs: String(this._showToolInputs),
|
|
6383
|
+
showConfirmationInput: String(this._showConfirmationInput),
|
|
6384
|
+
drawerOpen: String(this._drawerOpen),
|
|
6385
|
+
drawerView: this._drawerView,
|
|
6386
|
+
drawerWidth: String(this._drawerWidth),
|
|
6269
6387
|
};
|
|
6270
6388
|
if (this._panelEl) {
|
|
6271
6389
|
const s = this._panelEl.style;
|
|
@@ -6305,10 +6423,19 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
6305
6423
|
if (typeof state.message === 'string' && state.message.trim() !== '/') {
|
|
6306
6424
|
this._inputValue = state.message;
|
|
6307
6425
|
}
|
|
6308
|
-
|
|
6309
|
-
|
|
6426
|
+
if (state.mode === 'NORMAL' || state.mode === 'EDIT' || state.mode === 'PLAN') {
|
|
6427
|
+
this._mode = state.mode;
|
|
6428
|
+
}
|
|
6429
|
+
this._showTools = state.showTools === 'true';
|
|
6430
|
+
this._showToolInputs = state.showToolInputs === 'true';
|
|
6431
|
+
this._showConfirmationInput = state.showConfirmationInput === 'true';
|
|
6432
|
+
this._drawerOpen = state.drawerOpen === 'true';
|
|
6433
|
+
if (state.drawerView === 'settings' || state.drawerView === 'debug') {
|
|
6434
|
+
this._drawerView = state.drawerView;
|
|
6435
|
+
}
|
|
6436
|
+
const w = Number(state.drawerWidth);
|
|
6310
6437
|
if (w >= 300) {
|
|
6311
|
-
this.
|
|
6438
|
+
this._drawerWidth = w;
|
|
6312
6439
|
}
|
|
6313
6440
|
// The floating widget's open/layout state doesn't apply to inline mode.
|
|
6314
6441
|
if (this.variant !== 'inline' && state.opened === 'true') {
|
|
@@ -6608,6 +6735,29 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
6608
6735
|
this._skillMenuOpened = true;
|
|
6609
6736
|
this._searchInvocableSkills('');
|
|
6610
6737
|
}
|
|
6738
|
+
_handleModeToggle() {
|
|
6739
|
+
this._modeMenuOpened = !this._modeMenuOpened;
|
|
6740
|
+
}
|
|
6741
|
+
_handleModeSelect(mode) {
|
|
6742
|
+
this._mode = mode;
|
|
6743
|
+
this._modeMenuOpened = false;
|
|
6744
|
+
// Persist the choice so it survives a reload, alongside the other composer
|
|
6745
|
+
// state. The server is still the source of truth on the next turn's
|
|
6746
|
+
// CONVERSATION_STATE, but this keeps the selector from flashing the default.
|
|
6747
|
+
this._saveState();
|
|
6748
|
+
}
|
|
6749
|
+
_handleShowToolsChange(e) {
|
|
6750
|
+
this._showTools = e.target.checked;
|
|
6751
|
+
this._saveState();
|
|
6752
|
+
}
|
|
6753
|
+
_handleShowToolInputsChange(e) {
|
|
6754
|
+
this._showToolInputs = e.target.checked;
|
|
6755
|
+
this._saveState();
|
|
6756
|
+
}
|
|
6757
|
+
_handleShowConfirmationInputChange(e) {
|
|
6758
|
+
this._showConfirmationInput = e.target.checked;
|
|
6759
|
+
this._saveState();
|
|
6760
|
+
}
|
|
6611
6761
|
/**
|
|
6612
6762
|
* The mic button toggles dictation. Speech is transcribed straight into the
|
|
6613
6763
|
* composer as editable text — the user still reviews and presses send, so a
|
|
@@ -7034,22 +7184,25 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7034
7184
|
content: '',
|
|
7035
7185
|
}];
|
|
7036
7186
|
this._isStreaming = true;
|
|
7037
|
-
// Scroll user's message to the top (Gemini-style).
|
|
7038
|
-
//
|
|
7039
|
-
//
|
|
7187
|
+
// Scroll user's message to the top (Gemini-style). The runway (min-height =
|
|
7188
|
+
// viewport) goes on the agent-turn WRAPPER, not an individual message — the
|
|
7189
|
+
// whole turn (reasoning, tool lines, text, decision) streams inside that one
|
|
7190
|
+
// element, so nothing strands below it. Enough scroll room below the user
|
|
7191
|
+
// message to push it to the top.
|
|
7040
7192
|
this.updateComplete.then(() => {
|
|
7041
7193
|
if (!this._messagesEl) {
|
|
7042
7194
|
return;
|
|
7043
7195
|
}
|
|
7044
|
-
// Clear
|
|
7045
|
-
this._messagesEl.querySelectorAll('.
|
|
7196
|
+
// Clear runway from previous turns.
|
|
7197
|
+
this._messagesEl.querySelectorAll('.agent-turn').forEach((el) => {
|
|
7046
7198
|
el.style.minHeight = '';
|
|
7047
7199
|
});
|
|
7048
|
-
const
|
|
7049
|
-
const userMsgEl =
|
|
7050
|
-
const
|
|
7051
|
-
|
|
7052
|
-
|
|
7200
|
+
const userEls = this._messagesEl.querySelectorAll('.message--user');
|
|
7201
|
+
const userMsgEl = userEls[userEls.length - 1];
|
|
7202
|
+
const turnEls = this._messagesEl.querySelectorAll('.agent-turn');
|
|
7203
|
+
const turnEl = turnEls[turnEls.length - 1];
|
|
7204
|
+
if (turnEl) {
|
|
7205
|
+
turnEl.style.minHeight = this._messagesEl.clientHeight + 'px';
|
|
7053
7206
|
}
|
|
7054
7207
|
if (userMsgEl) {
|
|
7055
7208
|
this._messagesEl.scrollTop = userMsgEl.offsetTop - this._messagesEl.offsetTop - 24;
|
|
@@ -7131,6 +7284,9 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7131
7284
|
const body = {
|
|
7132
7285
|
userText: params.message,
|
|
7133
7286
|
fileIds: files.map((f) => f.conversationFileID),
|
|
7287
|
+
// Posture for guarded tools this turn (Ask / Auto / Plan). The server
|
|
7288
|
+
// validates and persists it, and only re-sets it when it changes.
|
|
7289
|
+
mode: this._mode,
|
|
7134
7290
|
};
|
|
7135
7291
|
// Ground the model in where the user is and what they've highlighted.
|
|
7136
7292
|
// Only sent when present so an empty selection / failed capture doesn't
|
|
@@ -7229,30 +7385,36 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7229
7385
|
}
|
|
7230
7386
|
this._confirmationInFlight = true;
|
|
7231
7387
|
this._error = '';
|
|
7232
|
-
// The
|
|
7233
|
-
//
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7388
|
+
// The decision line is authored by the server (single source of truth): it
|
|
7389
|
+
// persists an `action_decision` row AND streams an `ACTION_DECISION` event
|
|
7390
|
+
// back. We keep the prompt in place — the clicked button shows a loading
|
|
7391
|
+
// spinner — until that event arrives, which is our ack that the server
|
|
7392
|
+
// accepted the decision. The event handler then drops the prompt, appends the
|
|
7393
|
+
// decision line, and the follow-up turn streams into a new assistant bubble.
|
|
7394
|
+
const btn = this._messagesEl?.querySelector(`.confirmation__btn[data-decision="${decision}"]`);
|
|
7395
|
+
btn?.showLoading();
|
|
7240
7396
|
this._isStreaming = true;
|
|
7241
7397
|
this._userHasScrolledUp = false;
|
|
7242
7398
|
this.send({
|
|
7243
7399
|
message: '',
|
|
7244
|
-
messages: this.messages
|
|
7400
|
+
messages: this.messages,
|
|
7245
7401
|
conversationFiles: [],
|
|
7246
7402
|
pageContext: '',
|
|
7247
7403
|
selectionContext: '',
|
|
7248
7404
|
confirmation: decision,
|
|
7249
7405
|
}).then((response) => {
|
|
7250
|
-
|
|
7406
|
+
// Leave _confirmationInFlight true — the buttons stay loading until the
|
|
7407
|
+
// streamed ACTION_DECISION event resolves the prompt. Only clear it on an
|
|
7408
|
+
// error path, where no such event will arrive.
|
|
7251
7409
|
if (!response.ok) {
|
|
7410
|
+
this._confirmationInFlight = false;
|
|
7411
|
+
btn?.reset();
|
|
7252
7412
|
this._handleStreamError('Request failed: ' + response.status);
|
|
7253
7413
|
return;
|
|
7254
7414
|
}
|
|
7255
7415
|
if (!response.body) {
|
|
7416
|
+
this._confirmationInFlight = false;
|
|
7417
|
+
btn?.reset();
|
|
7256
7418
|
this._handleStreamError('No response body');
|
|
7257
7419
|
return;
|
|
7258
7420
|
}
|
|
@@ -7262,11 +7424,13 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7262
7424
|
this._readStreamChunk();
|
|
7263
7425
|
}).catch((err) => {
|
|
7264
7426
|
this._confirmationInFlight = false;
|
|
7427
|
+
btn?.reset();
|
|
7265
7428
|
if (err.name !== 'AbortError') {
|
|
7266
7429
|
this._handleStreamError(err.message);
|
|
7267
7430
|
}
|
|
7268
7431
|
});
|
|
7269
7432
|
}
|
|
7433
|
+
/** Clear the in-flight confirmation flags (error paths, or once resolved). */
|
|
7270
7434
|
/**
|
|
7271
7435
|
* Capture ambient "where is the user / what are they looking at" context at
|
|
7272
7436
|
* send time, so the AI is grounded without the host passing anything in.
|
|
@@ -7340,18 +7504,36 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7340
7504
|
_handleContextWarningDismiss() {
|
|
7341
7505
|
this._contextWarning = '';
|
|
7342
7506
|
}
|
|
7343
|
-
|
|
7344
|
-
|
|
7507
|
+
/**
|
|
7508
|
+
* A header button either opens the drawer to its view, switches the view if the
|
|
7509
|
+
* drawer is already open on the other one, or closes the drawer if it's already
|
|
7510
|
+
* showing this view. `_drawerView` is left intact on close so the content stays
|
|
7511
|
+
* visible while the drawer slides shut.
|
|
7512
|
+
*/
|
|
7513
|
+
_toggleDrawer(view) {
|
|
7514
|
+
if (this._drawerOpen && this._drawerView === view) {
|
|
7515
|
+
this._drawerOpen = false;
|
|
7516
|
+
}
|
|
7517
|
+
else {
|
|
7518
|
+
this._drawerOpen = true;
|
|
7519
|
+
this._drawerView = view;
|
|
7520
|
+
}
|
|
7521
|
+
this._saveState();
|
|
7522
|
+
}
|
|
7523
|
+
/** Close the drawer. `_drawerView` is left intact so its content stays visible
|
|
7524
|
+
* while the drawer slides shut. */
|
|
7525
|
+
_handleCloseDrawer() {
|
|
7526
|
+
this._drawerOpen = false;
|
|
7345
7527
|
this._saveState();
|
|
7346
7528
|
}
|
|
7347
|
-
|
|
7348
|
-
this.
|
|
7349
|
-
this.
|
|
7350
|
-
this.
|
|
7529
|
+
_handleDrawerResizeMouseDown(e) {
|
|
7530
|
+
this._isDrawerResizing = true;
|
|
7531
|
+
this._drawerResizeStartX = e.clientX;
|
|
7532
|
+
this._drawerResizeStartW = this._drawerWidth;
|
|
7351
7533
|
e.preventDefault();
|
|
7352
7534
|
e.stopPropagation();
|
|
7353
|
-
document.addEventListener('mousemove', this.
|
|
7354
|
-
document.addEventListener('mouseup', this.
|
|
7535
|
+
document.addEventListener('mousemove', this._handleDrawerResizeMouseMove);
|
|
7536
|
+
document.addEventListener('mouseup', this._handleDrawerResizeMouseUp);
|
|
7355
7537
|
}
|
|
7356
7538
|
_readStreamChunk() {
|
|
7357
7539
|
if (!this._reader) {
|
|
@@ -7397,28 +7579,54 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7397
7579
|
}));
|
|
7398
7580
|
if (data.type === 'REASONING_PART') {
|
|
7399
7581
|
if (data.message) {
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
this.requestUpdate();
|
|
7582
|
+
// Text after a tool line belongs in a NEW assistant bubble — the
|
|
7583
|
+
// last message may be a tool line, which text can't append to.
|
|
7584
|
+
// _ensureAssistantBubble opens a fresh bubble when needed.
|
|
7585
|
+
const target = this._ensureAssistantBubble();
|
|
7586
|
+
if (!target.reasoning) {
|
|
7587
|
+
target.reasoning = '';
|
|
7407
7588
|
}
|
|
7589
|
+
target.reasoning += String(data.message);
|
|
7590
|
+
this.requestUpdate();
|
|
7408
7591
|
}
|
|
7409
7592
|
}
|
|
7410
7593
|
else if (data.type === 'RESPONSE_PART') {
|
|
7411
7594
|
if (data.message) {
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
this.messages[lastIndex].content += String(data.message);
|
|
7415
|
-
this.requestUpdate();
|
|
7416
|
-
}
|
|
7595
|
+
this._ensureAssistantBubble().content += String(data.message);
|
|
7596
|
+
this.requestUpdate();
|
|
7417
7597
|
}
|
|
7418
7598
|
}
|
|
7599
|
+
else if (data.type === 'tool_use' || data.type === 'tool_result') {
|
|
7600
|
+
// Push the event straight into the transcript — the message IS the event
|
|
7601
|
+
// ({type, data}). The render reads msg.data.* and pairs a tool_result to
|
|
7602
|
+
// its tool_use by data.toolUseId. Same shape the DB persists and reload
|
|
7603
|
+
// replays, so live and reload share one representation — no transform.
|
|
7604
|
+
this.messages.push(data);
|
|
7605
|
+
this.requestUpdate();
|
|
7606
|
+
}
|
|
7607
|
+
else if (data.type === 'action_decision') {
|
|
7608
|
+
// The server accepted the confirmation decision and authored the record
|
|
7609
|
+
// (single source of truth). Its arrival is the ack: drop the prompt,
|
|
7610
|
+
// stop the buttons' loading, and push the event straight in — the render
|
|
7611
|
+
// reads msg.data.{outcome,tools}. Same type + shape the DB persists, so
|
|
7612
|
+
// live and reload share one representation.
|
|
7613
|
+
this._pendingActions = [];
|
|
7614
|
+
this._confirmationHighlighted = -1;
|
|
7615
|
+
this._confirmationInFlight = false;
|
|
7616
|
+
this.messages.push(data);
|
|
7617
|
+
this.requestUpdate();
|
|
7618
|
+
}
|
|
7419
7619
|
else if (data.type === 'ERROR') {
|
|
7420
7620
|
this._handleStreamError(String(data.message || 'Unknown error'));
|
|
7421
7621
|
}
|
|
7622
|
+
else if (data.type === 'CONVERSATION_STATE') {
|
|
7623
|
+
// Turn-start state sync: the server is the source of truth for the
|
|
7624
|
+
// conversation's mode (it may have decayed EDIT → NORMAL after idle).
|
|
7625
|
+
// Adopt it so the composer's selector reflects the real posture.
|
|
7626
|
+
if (data.data?.mode === 'NORMAL' || data.data?.mode === 'EDIT' || data.data?.mode === 'PLAN') {
|
|
7627
|
+
this._mode = data.data.mode;
|
|
7628
|
+
}
|
|
7629
|
+
}
|
|
7422
7630
|
else if (data.type === 'CONVERSATION_TOKEN_WARNING') {
|
|
7423
7631
|
// User-facing: friendly "conversation is getting long" guidance.
|
|
7424
7632
|
// Text only, never sent to the model — render verbatim as a banner.
|
|
@@ -7429,16 +7637,14 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7429
7637
|
// (it was created with a truncated first-message placeholder that we
|
|
7430
7638
|
// deliberately never show). Adopt it as the header label and pass it
|
|
7431
7639
|
// up so consumers can update their own chrome, e.g. a breadcrumb.
|
|
7432
|
-
if (data.data) {
|
|
7433
|
-
const title = String(data.data.title
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
}));
|
|
7441
|
-
}
|
|
7640
|
+
if (data.data?.title) {
|
|
7641
|
+
const title = String(data.data.title);
|
|
7642
|
+
this.label = title;
|
|
7643
|
+
this.dispatchEvent(new CustomEvent('conversation-title', {
|
|
7644
|
+
detail: { title },
|
|
7645
|
+
bubbles: true,
|
|
7646
|
+
composed: true,
|
|
7647
|
+
}));
|
|
7442
7648
|
}
|
|
7443
7649
|
}
|
|
7444
7650
|
else if (data.type === 'CONVERSATION_LIMIT_REACHED') {
|
|
@@ -7510,6 +7716,24 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7510
7716
|
}
|
|
7511
7717
|
});
|
|
7512
7718
|
}
|
|
7719
|
+
/**
|
|
7720
|
+
* The assistant bubble streamed text should append to. Returns the last message
|
|
7721
|
+
* when it's already an assistant bubble; otherwise (the last message is a tool
|
|
7722
|
+
* line, or there's nothing yet) opens a fresh assistant bubble so post-tool text
|
|
7723
|
+
* doesn't get dropped or glued to a tool line.
|
|
7724
|
+
*/
|
|
7725
|
+
_ensureAssistantBubble() {
|
|
7726
|
+
const last = this.messages[this.messages.length - 1];
|
|
7727
|
+
if (last && last.type === 'assistant') {
|
|
7728
|
+
return last;
|
|
7729
|
+
}
|
|
7730
|
+
const bubble = {
|
|
7731
|
+
type: 'assistant',
|
|
7732
|
+
content: '',
|
|
7733
|
+
};
|
|
7734
|
+
this.messages = [...this.messages, bubble];
|
|
7735
|
+
return bubble;
|
|
7736
|
+
}
|
|
7513
7737
|
/**
|
|
7514
7738
|
* End the current turn: release the turn lock and the reader. Called on the
|
|
7515
7739
|
* primary unlock signal (`finished:true`) and the fallback (stream close).
|
|
@@ -7534,6 +7758,17 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7534
7758
|
&& !this.messages[lastIndex].reasoning) {
|
|
7535
7759
|
this.messages = this.messages.slice(0, -1);
|
|
7536
7760
|
}
|
|
7761
|
+
// Scroll the confirmation into view so the user actually sees the ask — an
|
|
7762
|
+
// agentic turn can stream many tool lines past the fold, leaving the prompt
|
|
7763
|
+
// below it. `block: 'end'` brings its bottom up to just above the composer.
|
|
7764
|
+
if (this._inlineConfirm) {
|
|
7765
|
+
this.updateComplete.then(() => {
|
|
7766
|
+
this._messagesEl?.querySelector('.confirmation')?.scrollIntoView({
|
|
7767
|
+
behavior: 'smooth',
|
|
7768
|
+
block: 'end',
|
|
7769
|
+
});
|
|
7770
|
+
});
|
|
7771
|
+
}
|
|
7537
7772
|
}
|
|
7538
7773
|
}
|
|
7539
7774
|
_handleStreamError(message) {
|
|
@@ -7553,33 +7788,107 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7553
7788
|
}
|
|
7554
7789
|
// --- Render ---
|
|
7555
7790
|
/**
|
|
7556
|
-
* The
|
|
7791
|
+
* The composer's mode selector — a Gemini-style inline label + caret sitting
|
|
7792
|
+
* just before the mic, that opens a small menu (Ask / Auto / Plan). Inline text
|
|
7793
|
+
* rather than a pill so it doesn't grow the single-row composer's height. The
|
|
7794
|
+
* menu opens upward above the trigger.
|
|
7795
|
+
*/
|
|
7796
|
+
_renderModeSelector() {
|
|
7797
|
+
const active = CHATBOT_MODES.find((m) => m.mode === this._mode) || CHATBOT_MODES[0];
|
|
7798
|
+
return b `
|
|
7799
|
+
<div class="mode">
|
|
7800
|
+
${this._modeMenuOpened ? b `
|
|
7801
|
+
<div class="mode-menu">
|
|
7802
|
+
${CHATBOT_MODES.map((m) => b `
|
|
7803
|
+
<button
|
|
7804
|
+
class=${e({
|
|
7805
|
+
'mode-menu__item': true,
|
|
7806
|
+
'mode-menu__item--active': m.mode === this._mode,
|
|
7807
|
+
})}
|
|
7808
|
+
@click=${() => this._handleModeSelect(m.mode)}
|
|
7809
|
+
>
|
|
7810
|
+
<span class="mode-menu__label">${m.label}</span>
|
|
7811
|
+
<span class="mode-menu__desc">${m.description}</span>
|
|
7812
|
+
</button>
|
|
7813
|
+
`)}
|
|
7814
|
+
</div>
|
|
7815
|
+
` : A}
|
|
7816
|
+
<button
|
|
7817
|
+
class="mode-trigger"
|
|
7818
|
+
aria-haspopup="true"
|
|
7819
|
+
aria-expanded=${this._modeMenuOpened ? 'true' : 'false'}
|
|
7820
|
+
@click=${this._handleModeToggle}
|
|
7821
|
+
>
|
|
7822
|
+
<span class="mode-trigger__label">${active.label}</span>
|
|
7823
|
+
<svg
|
|
7824
|
+
class="mode-trigger__caret"
|
|
7825
|
+
viewBox="0 0 24 24"
|
|
7826
|
+
fill="none"
|
|
7827
|
+
stroke="currentColor"
|
|
7828
|
+
stroke-width="2"
|
|
7829
|
+
stroke-linecap="round"
|
|
7830
|
+
stroke-linejoin="round"
|
|
7831
|
+
aria-hidden="true"
|
|
7832
|
+
>
|
|
7833
|
+
<path d="M6 9l6 6 6-6" />
|
|
7834
|
+
</svg>
|
|
7835
|
+
</button>
|
|
7836
|
+
</div>
|
|
7837
|
+
`;
|
|
7838
|
+
}
|
|
7839
|
+
/**
|
|
7840
|
+
* The approve/deny card shown when guarded tools are paused (Rail 3). Floats
|
|
7557
7841
|
* OVER the messages, anchored just above the composer — not part of the message
|
|
7558
7842
|
* scroll — so it stays in view while the transcript scrolls behind it.
|
|
7559
7843
|
*
|
|
7560
|
-
* Written for a non-technical user
|
|
7561
|
-
*
|
|
7562
|
-
*
|
|
7563
|
-
*
|
|
7564
|
-
*
|
|
7844
|
+
* Written for a non-technical user. A single action reads as one plain-language
|
|
7845
|
+
* ask (`Run "{tool}"?` + the model's `confirmationMessage`). When the model
|
|
7846
|
+
* batches several guarded tools, they're listed together under one header and
|
|
7847
|
+
* the user accepts or rejects them all at once — the backend decision is
|
|
7848
|
+
* all-or-nothing. Either way: NO tool code, NO raw arguments/JSON — those are
|
|
7849
|
+
* noise (or alarming) to a normal user.
|
|
7565
7850
|
*/
|
|
7566
7851
|
_renderConfirmation() {
|
|
7567
|
-
//
|
|
7568
|
-
// (and only) pending action.
|
|
7569
|
-
const action = this._pendingActions[0];
|
|
7570
|
-
// Title names the tool being run ("Run {tool}?").
|
|
7852
|
+
// Title: name the single tool, else a generic header for a batch.
|
|
7571
7853
|
let title = 'Run this action?';
|
|
7572
|
-
if (
|
|
7573
|
-
title =
|
|
7854
|
+
if (this._pendingActions.length > 1) {
|
|
7855
|
+
title = 'These actions need your approval';
|
|
7856
|
+
}
|
|
7857
|
+
else if (this._pendingActions[0]) {
|
|
7858
|
+
title = `Run "${this._pendingActions[0].displayName || this._pendingActions[0].toolName}"?`;
|
|
7574
7859
|
}
|
|
7575
7860
|
return b `
|
|
7576
7861
|
<div
|
|
7577
|
-
class
|
|
7862
|
+
class=${e({
|
|
7863
|
+
'confirmation': true,
|
|
7864
|
+
'confirmation--inline': this._inlineConfirm,
|
|
7865
|
+
})}
|
|
7578
7866
|
role="alertdialog"
|
|
7579
7867
|
aria-label="Confirm before continuing"
|
|
7580
7868
|
>
|
|
7581
7869
|
<div class="confirmation__title">${title}</div>
|
|
7582
|
-
|
|
7870
|
+
${this._pendingActions.length > 1
|
|
7871
|
+
? b `
|
|
7872
|
+
<ol class="confirmation__list">
|
|
7873
|
+
${this._pendingActions.map((action) => b `
|
|
7874
|
+
<li class="confirmation__list-item">
|
|
7875
|
+
<span class="confirmation__list-name">${action.displayName || action.toolName}</span>
|
|
7876
|
+
${action.confirmationMessage
|
|
7877
|
+
? b `<span class="confirmation__list-desc">${action.confirmationMessage}</span>`
|
|
7878
|
+
: A}
|
|
7879
|
+
${this._showConfirmationInput && this._inlineConfirm && action.input && Object.keys(action.input).length
|
|
7880
|
+
? b `<pre class="confirmation__details-input">${JSON.stringify(action.input, null, 2)}</pre>`
|
|
7881
|
+
: A}
|
|
7882
|
+
</li>
|
|
7883
|
+
`)}
|
|
7884
|
+
</ol>
|
|
7885
|
+
`
|
|
7886
|
+
: b `
|
|
7887
|
+
<p class="confirmation__message">${this._pendingActions[0]?.confirmationMessage || 'The assistant wants to take an action. Is that OK?'}</p>
|
|
7888
|
+
${this._showConfirmationInput && this._inlineConfirm && this._pendingActions[0]?.input && Object.keys(this._pendingActions[0].input).length
|
|
7889
|
+
? b `<pre class="confirmation__details-input">${JSON.stringify(this._pendingActions[0].input, null, 2)}</pre>`
|
|
7890
|
+
: A}
|
|
7891
|
+
`}
|
|
7583
7892
|
<div class="confirmation__actions">
|
|
7584
7893
|
${CONFIRMATION_DECISIONS.map((option, index) => b `
|
|
7585
7894
|
<kr-button
|
|
@@ -7590,7 +7899,7 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7590
7899
|
variant=${option.decision === 'approve' ? 'flat' : 'outline'}
|
|
7591
7900
|
color=${option.decision === 'reject' ? 'secondary' : 'primary'}
|
|
7592
7901
|
size="small"
|
|
7593
|
-
|
|
7902
|
+
data-decision=${option.decision}
|
|
7594
7903
|
@click=${() => this._handleConfirmationDecision(option.decision)}
|
|
7595
7904
|
>${option.label}</kr-button>
|
|
7596
7905
|
`)}
|
|
@@ -7812,26 +8121,37 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7812
8121
|
* three scopes from the latest TOKEN_USAGE event — context gauge, this turn,
|
|
7813
8122
|
* lifetime — plus a verbose per-iteration table. Never customer-facing.
|
|
7814
8123
|
*/
|
|
7815
|
-
|
|
8124
|
+
/**
|
|
8125
|
+
* The side drawer — one sliding panel that holds both the Settings and Debug
|
|
8126
|
+
* views. The header buttons switch `_drawerView`; the title and body swap
|
|
8127
|
+
* inside. The panel keeps one consistent (resizable) width across both views —
|
|
8128
|
+
* switching views only changes the content, never the panel size.
|
|
8129
|
+
*/
|
|
8130
|
+
_renderDrawer() {
|
|
8131
|
+
// The debug view is gated on the server-side flag (inline variant + debug
|
|
8132
|
+
// telemetry enabled); if it's not enabled, the drawer only ever shows settings.
|
|
8133
|
+
const isDebug = this._drawerView === 'debug'
|
|
8134
|
+
&& this.variant === 'inline'
|
|
8135
|
+
&& this._aiFrameworkConfig.debugTelemetryEnabled === true;
|
|
7816
8136
|
return b `
|
|
7817
8137
|
<div
|
|
7818
8138
|
class=${e({
|
|
7819
|
-
'
|
|
7820
|
-
'
|
|
7821
|
-
'
|
|
8139
|
+
'drawer': true,
|
|
8140
|
+
'drawer--open': this._drawerOpen,
|
|
8141
|
+
'drawer--resizing': this._isDrawerResizing,
|
|
7822
8142
|
})}
|
|
7823
|
-
style=${o({ width: `${this.
|
|
8143
|
+
style=${o({ width: `${this._drawerWidth}px` })}
|
|
7824
8144
|
>
|
|
7825
8145
|
<div
|
|
7826
|
-
class="
|
|
7827
|
-
@mousedown=${this.
|
|
8146
|
+
class="drawer__resize"
|
|
8147
|
+
@mousedown=${this._handleDrawerResizeMouseDown}
|
|
7828
8148
|
></div>
|
|
7829
|
-
<div class="
|
|
7830
|
-
<span class="
|
|
8149
|
+
<div class="drawer__header">
|
|
8150
|
+
<span class="drawer__title">${isDebug ? 'Debug' : 'Settings'}</span>
|
|
7831
8151
|
<button
|
|
7832
8152
|
class="header-action"
|
|
7833
|
-
@click=${this.
|
|
7834
|
-
title="Close
|
|
8153
|
+
@click=${this._handleCloseDrawer}
|
|
8154
|
+
title="Close"
|
|
7835
8155
|
>
|
|
7836
8156
|
<svg
|
|
7837
8157
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -7849,105 +8169,163 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7849
8169
|
</svg>
|
|
7850
8170
|
</button>
|
|
7851
8171
|
</div>
|
|
7852
|
-
<div class="
|
|
7853
|
-
${this.
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
8172
|
+
<div class="drawer__body">
|
|
8173
|
+
${isDebug ? this._renderDebugContent() : this._renderSettingsContent()}
|
|
8174
|
+
</div>
|
|
8175
|
+
</div>
|
|
8176
|
+
`;
|
|
8177
|
+
}
|
|
8178
|
+
/** The Settings view's body — user preferences, grouped in sections. */
|
|
8179
|
+
_renderSettingsContent() {
|
|
8180
|
+
return b `
|
|
8181
|
+
<div class="settings-section">
|
|
8182
|
+
<div class="settings-section__title">Tools</div>
|
|
8183
|
+
<div class="settings-row">
|
|
8184
|
+
<kr-checkbox
|
|
8185
|
+
label="Show tools"
|
|
8186
|
+
.checked=${this._showTools}
|
|
8187
|
+
@change=${this._handleShowToolsChange}
|
|
8188
|
+
></kr-checkbox>
|
|
8189
|
+
</div>
|
|
8190
|
+
<div class="settings-row">
|
|
8191
|
+
<kr-checkbox
|
|
8192
|
+
label="Show tool inputs"
|
|
8193
|
+
.checked=${this._showToolInputs}
|
|
8194
|
+
@change=${this._handleShowToolInputsChange}
|
|
8195
|
+
></kr-checkbox>
|
|
8196
|
+
</div>
|
|
8197
|
+
<div class="settings-row">
|
|
8198
|
+
<kr-checkbox
|
|
8199
|
+
label="Show tool confirmation inputs"
|
|
8200
|
+
.checked=${this._showConfirmationInput}
|
|
8201
|
+
@change=${this._handleShowConfirmationInputChange}
|
|
8202
|
+
></kr-checkbox>
|
|
8203
|
+
</div>
|
|
8204
|
+
</div>
|
|
8205
|
+
`;
|
|
8206
|
+
}
|
|
8207
|
+
/** The Debug view's body — telemetry scopes and the per-iteration table. */
|
|
8208
|
+
_renderDebugContent() {
|
|
8209
|
+
return b `
|
|
8210
|
+
${this._telemetry ? b `
|
|
8211
|
+
<div class="debug-section">
|
|
8212
|
+
<div class="debug-section__label">Context</div>
|
|
8213
|
+
<div class="debug-gauge">
|
|
8214
|
+
<div class="debug-gauge__bar">
|
|
8215
|
+
<div
|
|
8216
|
+
class="debug-gauge__fill"
|
|
8217
|
+
style=${o({ width: `${Math.min(100, Math.max(0, this._telemetry.contextUsedPercent || 0))}%` })}
|
|
8218
|
+
></div>
|
|
7868
8219
|
</div>
|
|
8220
|
+
<span class="debug-gauge__text">
|
|
8221
|
+
${this._fmtTokens(this._telemetry.currentContextTokens)} / ${this._fmtTokens(this._telemetry.contextWindowTokens)}
|
|
8222
|
+
· ${this._telemetry.contextUsedPercent ?? 0}%
|
|
8223
|
+
</span>
|
|
8224
|
+
</div>
|
|
8225
|
+
</div>
|
|
7869
8226
|
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
8227
|
+
<div class="debug-section">
|
|
8228
|
+
<div class="debug-section__label">This turn</div>
|
|
8229
|
+
<div class="debug-metrics">
|
|
8230
|
+
<span class="debug-metrics__key">Input</span>
|
|
8231
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.turnInputTokens)}</span>
|
|
8232
|
+
<span class="debug-metrics__key">Output</span>
|
|
8233
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.turnOutputTokens)}</span>
|
|
8234
|
+
<span class="debug-metrics__key">Cache read</span>
|
|
8235
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.turnCacheReadTokens)}</span>
|
|
8236
|
+
<span class="debug-metrics__key">Hit rate</span>
|
|
8237
|
+
<span class="debug-metrics__val">${this._telemetry.turnHitRate ?? '—'}%</span>
|
|
8238
|
+
<span class="debug-metrics__key">Cost</span>
|
|
8239
|
+
<span class="debug-metrics__val debug-cost">${this._fmtCost(this._telemetry.turnCostUsd)}</span>
|
|
8240
|
+
</div>
|
|
8241
|
+
</div>
|
|
7885
8242
|
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
8243
|
+
<div class="debug-section">
|
|
8244
|
+
<div class="debug-section__label">Lifetime</div>
|
|
8245
|
+
<div class="debug-metrics">
|
|
8246
|
+
<span class="debug-metrics__key">Input</span>
|
|
8247
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.conversationInputTokens)}</span>
|
|
8248
|
+
<span class="debug-metrics__key">Output</span>
|
|
8249
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.conversationOutputTokens)}</span>
|
|
8250
|
+
<span class="debug-metrics__key">Cache read</span>
|
|
8251
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.conversationCacheReadTokens)}</span>
|
|
8252
|
+
<span class="debug-metrics__key">Cache write</span>
|
|
8253
|
+
<span class="debug-metrics__val">${this._fmtTokens(this._telemetry.conversationCacheWriteTokens)}</span>
|
|
8254
|
+
<span class="debug-metrics__key">Cost so far</span>
|
|
8255
|
+
<span class="debug-metrics__val debug-cost">${this._fmtCost(this._telemetry.conversationCostUsd)}</span>
|
|
8256
|
+
</div>
|
|
8257
|
+
</div>
|
|
7901
8258
|
` : b `
|
|
7902
8259
|
<div class="debug-empty">No telemetry yet — send a message to populate.</div>
|
|
7903
8260
|
`}
|
|
7904
8261
|
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
8262
|
+
${this._iterationLog.length ? b `
|
|
8263
|
+
<div class="debug-section">
|
|
8264
|
+
<div class="debug-section__label">Iterations (this turn)</div>
|
|
8265
|
+
<div class="debug-iterations-scroll">
|
|
8266
|
+
<table class="debug-iterations">
|
|
8267
|
+
<thead>
|
|
8268
|
+
<tr>
|
|
8269
|
+
<th>#</th>
|
|
8270
|
+
<th>In</th>
|
|
8271
|
+
<th>Out</th>
|
|
8272
|
+
<th>Cache R</th>
|
|
8273
|
+
<th>Cache W</th>
|
|
8274
|
+
<th>Hit</th>
|
|
8275
|
+
<th>Cost</th>
|
|
8276
|
+
<th>Ckpt</th>
|
|
8277
|
+
</tr>
|
|
8278
|
+
</thead>
|
|
8279
|
+
<tbody>
|
|
8280
|
+
${this._iterationLog.map((it) => {
|
|
7924
8281
|
const ckpt = this._cacheCheckpoints[it.iteration];
|
|
7925
8282
|
return b `
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
8283
|
+
<tr>
|
|
8284
|
+
<td>${it.iteration}</td>
|
|
8285
|
+
<td>${this._fmtTokens(it.iterationInput)}</td>
|
|
8286
|
+
<td>${this._fmtTokens(it.iterationOutput)}</td>
|
|
8287
|
+
<td>${this._fmtTokens(it.iterationCacheRead)}</td>
|
|
8288
|
+
<td>${this._fmtTokens(it.iterationCacheWrite)}</td>
|
|
8289
|
+
<td>${it.iterationHitRate ?? '—'}%</td>
|
|
8290
|
+
<td class="debug-cost">${this._fmtCost(it.iterationCostUsd)}</td>
|
|
8291
|
+
<td>${ckpt ? ckpt.mode : '—'}</td>
|
|
8292
|
+
</tr>
|
|
7936
8293
|
`;
|
|
7937
8294
|
})}
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
</div>
|
|
7942
|
-
` : A}
|
|
8295
|
+
</tbody>
|
|
8296
|
+
</table>
|
|
8297
|
+
</div>
|
|
7943
8298
|
</div>
|
|
7944
|
-
|
|
8299
|
+
` : A}
|
|
7945
8300
|
`;
|
|
7946
8301
|
}
|
|
7947
8302
|
render() {
|
|
7948
8303
|
// Debug affordances (drawer toggle, drawer, /test-* options) require the
|
|
7949
8304
|
// inline variant AND the server-side debug flag from aiFrameworkConfig.
|
|
7950
8305
|
const isDebugEnabled = this.variant === 'inline' && this._aiFrameworkConfig.debugTelemetryEnabled === true;
|
|
8306
|
+
// Push the content over by the open drawer's width so the drawer sits beside
|
|
8307
|
+
// the content rather than overlaying it. One consistent (resizable) width for
|
|
8308
|
+
// both views. Only pushes while the drawer is open.
|
|
8309
|
+
const contentPadding = {};
|
|
8310
|
+
if (this._drawerOpen) {
|
|
8311
|
+
contentPadding.paddingRight = `${this._drawerWidth}px`;
|
|
8312
|
+
}
|
|
8313
|
+
// Group the transcript into runs so an agent turn (its reasoning/tool/text/
|
|
8314
|
+
// decision messages, which stream in as separate entries) renders inside ONE
|
|
8315
|
+
// wrapper. The scroll-to-top runway goes on that wrapper, so everything the
|
|
8316
|
+
// turn appends fills the SAME tall element instead of stacking as siblings
|
|
8317
|
+
// below it (which is what stranded tool lines below the fold). A user message
|
|
8318
|
+
// is its own group and is unaffected.
|
|
8319
|
+
const messageGroups = [];
|
|
8320
|
+
this.messages.forEach((msg) => {
|
|
8321
|
+
const lastGroup = messageGroups[messageGroups.length - 1];
|
|
8322
|
+
if (msg.type === 'user' || !lastGroup || lastGroup[0].type === 'user') {
|
|
8323
|
+
messageGroups.push([msg]);
|
|
8324
|
+
}
|
|
8325
|
+
else {
|
|
8326
|
+
lastGroup.push(msg);
|
|
8327
|
+
}
|
|
8328
|
+
});
|
|
7951
8329
|
return b `
|
|
7952
8330
|
${(this.hideToggle || this.variant === 'inline') ? A : b `
|
|
7953
8331
|
<button
|
|
@@ -7976,9 +8354,9 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7976
8354
|
class=${e({
|
|
7977
8355
|
'panel': true,
|
|
7978
8356
|
'panel--opened': this.opened,
|
|
7979
|
-
'panel--resizing-
|
|
8357
|
+
'panel--resizing-drawer': this._isDrawerResizing,
|
|
7980
8358
|
})}
|
|
7981
|
-
style=${o(
|
|
8359
|
+
style=${o(contentPadding)}
|
|
7982
8360
|
@dragenter=${this._handleDragEnter}
|
|
7983
8361
|
@dragover=${this._handleDragOver}
|
|
7984
8362
|
@dragleave=${this._handleDragLeave}
|
|
@@ -8051,13 +8429,35 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
8051
8429
|
</div>
|
|
8052
8430
|
</div>
|
|
8053
8431
|
<div class="header-actions">
|
|
8432
|
+
<button
|
|
8433
|
+
class="header-action"
|
|
8434
|
+
@click=${() => this._toggleDrawer('settings')}
|
|
8435
|
+
title="Settings"
|
|
8436
|
+
>
|
|
8437
|
+
<svg
|
|
8438
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8439
|
+
fill="none"
|
|
8440
|
+
viewBox="0 0 24 24"
|
|
8441
|
+
stroke-width="1.5"
|
|
8442
|
+
stroke="currentColor"
|
|
8443
|
+
class="header-action-icon"
|
|
8444
|
+
>
|
|
8445
|
+
<path
|
|
8446
|
+
stroke-linecap="round"
|
|
8447
|
+
stroke-linejoin="round"
|
|
8448
|
+
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
|
|
8449
|
+
/>
|
|
8450
|
+
<path
|
|
8451
|
+
stroke-linecap="round"
|
|
8452
|
+
stroke-linejoin="round"
|
|
8453
|
+
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
|
8454
|
+
/>
|
|
8455
|
+
</svg>
|
|
8456
|
+
</button>
|
|
8054
8457
|
${isDebugEnabled ? b `
|
|
8055
8458
|
<button
|
|
8056
|
-
class
|
|
8057
|
-
|
|
8058
|
-
'header-action--active': this._debugPanelOpen,
|
|
8059
|
-
})}
|
|
8060
|
-
@click=${this._handleToggleDebugPanel}
|
|
8459
|
+
class="header-action"
|
|
8460
|
+
@click=${() => this._toggleDrawer('debug')}
|
|
8061
8461
|
title="Debug telemetry"
|
|
8062
8462
|
>
|
|
8063
8463
|
<svg
|
|
@@ -8108,161 +8508,322 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
8108
8508
|
<span class="empty-text">What can I help you with?</span>
|
|
8109
8509
|
</div>
|
|
8110
8510
|
` : A}
|
|
8111
|
-
${
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8511
|
+
${messageGroups.map((group) => {
|
|
8512
|
+
// A user group is a single message rendered bare. An agent group wraps
|
|
8513
|
+
// its items in one `.agent-turn` so the scroll-to-top runway sits on
|
|
8514
|
+
// that wrapper and the whole turn fills it (no stranded siblings).
|
|
8515
|
+
const items = group.map((msg) => {
|
|
8516
|
+
const index = this.messages.indexOf(msg);
|
|
8517
|
+
// Skip an empty assistant placeholder (no content, no reasoning) —
|
|
8518
|
+
// _send and _ensureAssistantBubble append these to stream into,
|
|
8519
|
+
// but between tool calls with no text they stay empty. An invisible
|
|
8520
|
+
// .message div still occupies a flex-gap slot, doubling the spacing
|
|
8521
|
+
// between tool lines. Keep it only while it's the actively-streaming
|
|
8522
|
+
// last message (it shows the typing dots).
|
|
8523
|
+
if (msg.type === 'assistant'
|
|
8524
|
+
&& !msg.content
|
|
8525
|
+
&& !msg.reasoning
|
|
8526
|
+
&& !(this._isStreaming && msg === this.messages[this.messages.length - 1])) {
|
|
8527
|
+
return A;
|
|
8528
|
+
}
|
|
8529
|
+
// Tool visibility is user-controlled ("Show tools" in the settings
|
|
8530
|
+
// panel). When off, drop the whole tool row so it doesn't leave an
|
|
8531
|
+
// empty flex-gap slot in the transcript.
|
|
8532
|
+
if ((msg.type === 'tool_use' || msg.type === 'tool_result') && !this._showTools) {
|
|
8533
|
+
return A;
|
|
8534
|
+
}
|
|
8535
|
+
// A tool_result renders nothing on its own — its tool_use line pulls
|
|
8536
|
+
// it in (matched by data.toolUseId) and renders it under the ⎿ cliff.
|
|
8537
|
+
if (msg.type === 'tool_result') {
|
|
8538
|
+
return A;
|
|
8539
|
+
}
|
|
8540
|
+
// A tool_use line (Claude-CLI style): header "› ToolName(input)" and,
|
|
8541
|
+
// when "Show tool inputs" is on, the result under a ⎿ cliff pulled
|
|
8542
|
+
// from the next tool_result with the same toolUseId. Precompute the
|
|
8543
|
+
// result and the two display strings (they need statements — JSON
|
|
8544
|
+
// reformat, truncation — that can't live in a template expression).
|
|
8545
|
+
let result;
|
|
8546
|
+
let inputSummary = '';
|
|
8547
|
+
let resultPreview = '';
|
|
8548
|
+
let resultRemainder = 0;
|
|
8549
|
+
if (msg.type === 'tool_use') {
|
|
8550
|
+
const next = this.messages[index + 1];
|
|
8551
|
+
if (next && next.type === 'tool_result' && next.data?.toolUseId === msg.data?.toolUseId) {
|
|
8552
|
+
result = next.data;
|
|
8553
|
+
}
|
|
8554
|
+
if (this._showToolInputs && msg.data?.toolInput) {
|
|
8555
|
+
inputSummary = String(msg.data.toolInput).replace(/\s+/g, ' ').trim();
|
|
8556
|
+
if (inputSummary.length > 80) {
|
|
8557
|
+
inputSummary = `${inputSummary.slice(0, 79)}…`;
|
|
8558
|
+
}
|
|
8559
|
+
}
|
|
8560
|
+
if (this._showToolInputs && result?.toolResultString) {
|
|
8561
|
+
let formatted;
|
|
8562
|
+
try {
|
|
8563
|
+
formatted = JSON.stringify(JSON.parse(result.toolResultString), null, 2);
|
|
8564
|
+
}
|
|
8565
|
+
catch {
|
|
8566
|
+
formatted = String(result.toolResultString);
|
|
8567
|
+
}
|
|
8568
|
+
const lines = formatted.split('\n');
|
|
8569
|
+
resultPreview = lines.slice(0, 3).join('\n');
|
|
8570
|
+
resultRemainder = Math.max(0, lines.length - 3);
|
|
8571
|
+
}
|
|
8572
|
+
}
|
|
8573
|
+
// A resolved-decision line (Rail 3) — reads "Approved Create AI Tool"
|
|
8574
|
+
// / "Denied …" / "Always allowed …". Precompute the verb and the
|
|
8575
|
+
// friendly tool names (an if/else and a map — not template exprs).
|
|
8576
|
+
let decisionVerb = 'Approved';
|
|
8577
|
+
let decisionTools = [];
|
|
8578
|
+
if (msg.type === 'action_decision') {
|
|
8579
|
+
if (msg.data?.outcome === 'denied') {
|
|
8580
|
+
decisionVerb = 'Denied';
|
|
8581
|
+
}
|
|
8582
|
+
else if (msg.data?.outcome === 'alwaysAllowed') {
|
|
8583
|
+
decisionVerb = 'Always allowed';
|
|
8584
|
+
}
|
|
8585
|
+
decisionTools = (msg.data?.tools || []).map((t) => t.displayName || t.toolName);
|
|
8586
|
+
}
|
|
8587
|
+
// A tool line and a decision line ARE assistant-side messages —
|
|
8588
|
+
// render them inside the same .message wrapper as everything else so
|
|
8589
|
+
// they share its spacing/structure, not as separate element types.
|
|
8139
8590
|
return b `
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
|
|
8591
|
+
<div class=${e({
|
|
8592
|
+
'message': true,
|
|
8593
|
+
'message--user': msg.type === 'user',
|
|
8594
|
+
'message--assistant': msg.type !== 'user',
|
|
8595
|
+
})}>
|
|
8596
|
+
${msg.type === 'tool_use' ? b `
|
|
8597
|
+
<div class="tool-activity-block">
|
|
8598
|
+
<div class=${e({
|
|
8599
|
+
'tool-activity': true,
|
|
8600
|
+
'tool-activity--error': result?.toolResultStatus === 'error',
|
|
8601
|
+
})}>
|
|
8602
|
+
<span class="tool-activity__chevron">›</span>
|
|
8603
|
+
<span class="tool-activity__label">${msg.data?.displayName || msg.data?.toolName || 'Tool'}</span>
|
|
8604
|
+
${inputSummary ? b `<span class="tool-activity__input">(${inputSummary})</span>` : A}
|
|
8605
|
+
</div>
|
|
8606
|
+
${this._showToolInputs && result?.toolResultString ? b `
|
|
8607
|
+
<div class=${e({
|
|
8608
|
+
'tool-activity__result': true,
|
|
8609
|
+
'tool-activity__result--error': result.toolResultStatus === 'error',
|
|
8610
|
+
})}>
|
|
8611
|
+
<span class="tool-activity__connector">⎿</span>
|
|
8612
|
+
<pre class="tool-activity__result-body">${resultPreview}${resultRemainder > 0
|
|
8613
|
+
? b `\n<span class="tool-activity__more">… +${resultRemainder} lines</span>`
|
|
8614
|
+
: A}</pre>
|
|
8615
|
+
</div>
|
|
8616
|
+
` : A}
|
|
8617
|
+
</div>
|
|
8618
|
+
` : A}
|
|
8619
|
+
${msg.type === 'action_decision' ? b `
|
|
8620
|
+
<div class=${e({
|
|
8621
|
+
'decision': true,
|
|
8622
|
+
'decision--denied': msg.data?.outcome === 'denied',
|
|
8623
|
+
})}>
|
|
8624
|
+
<span class="decision__icon">
|
|
8625
|
+
<svg
|
|
8626
|
+
viewBox="0 0 24 24"
|
|
8627
|
+
fill="none"
|
|
8628
|
+
stroke="currentColor"
|
|
8629
|
+
stroke-width="2"
|
|
8630
|
+
stroke-linecap="round"
|
|
8631
|
+
stroke-linejoin="round"
|
|
8632
|
+
>
|
|
8633
|
+
${msg.data?.outcome === 'denied'
|
|
8634
|
+
? b `<path d="M18 6 6 18M6 6l12 12" />`
|
|
8635
|
+
: b `<path d="M20 6 9 17l-5-5" />`}
|
|
8636
|
+
</svg>
|
|
8637
|
+
</span>
|
|
8638
|
+
<span class="decision__label">${decisionVerb}${decisionTools.length ? ' ' + decisionTools.join(', ') : A}</span>
|
|
8639
|
+
</div>
|
|
8640
|
+
` : A}
|
|
8641
|
+
${msg.reasoning ? (this._reasoningAsMessage ? b `
|
|
8642
|
+
<div class="message-content">${msg.reasoning}</div>
|
|
8643
|
+
` : b `
|
|
8644
|
+
<details class="reasoning">
|
|
8645
|
+
<summary>Thinking</summary>
|
|
8646
|
+
<div class="reasoning-content">${msg.reasoning}</div>
|
|
8647
|
+
</details>
|
|
8648
|
+
`) : A}
|
|
8649
|
+
${msg.conversationFiles && msg.conversationFiles.length ? b `
|
|
8650
|
+
<div class="attachments">
|
|
8651
|
+
${msg.conversationFiles.map((file) => {
|
|
8652
|
+
const fileName = file.niceName || file.name || 'File';
|
|
8653
|
+
// A stored file is an image by its MIME when it has one, else
|
|
8654
|
+
// by its extension.
|
|
8655
|
+
let isImage = IMAGE_EXTENSIONS.includes(this._getFileExtension(fileName));
|
|
8656
|
+
if (file.mimeType) {
|
|
8657
|
+
isImage = file.mimeType.startsWith('image/');
|
|
8658
|
+
}
|
|
8659
|
+
// An image shows its thumbnail; everything else shows a
|
|
8660
|
+
// file-type tile. Both open the previewer on click — it renders
|
|
8661
|
+
// images, video, PDFs, and text inline, and shows a download
|
|
8662
|
+
// tile for the rest (Office formats), so a click always does
|
|
8663
|
+
// something useful.
|
|
8664
|
+
if (isImage) {
|
|
8665
|
+
return b `
|
|
8666
|
+
<button
|
|
8667
|
+
class="attachment"
|
|
8668
|
+
type="button"
|
|
8669
|
+
title=${file.niceName || 'View image'}
|
|
8670
|
+
@click=${() => this._handleFileOpen(file)}
|
|
8671
|
+
>
|
|
8672
|
+
<img
|
|
8673
|
+
src=${file.previewUrl || `${KRHttp.getBaseUrl()}/api/system/conversationFile/${file.conversationFileID}/view`}
|
|
8674
|
+
alt=${file.niceName || ''}
|
|
8675
|
+
/>
|
|
8676
|
+
</button>
|
|
8677
|
+
`;
|
|
8678
|
+
}
|
|
8679
|
+
return b `
|
|
8680
|
+
<button
|
|
8681
|
+
class="attachment attachment--doc attachment--openable"
|
|
8682
|
+
type="button"
|
|
8683
|
+
title=${fileName}
|
|
8684
|
+
@click=${() => this._handleFileOpen(file)}
|
|
8685
|
+
>
|
|
8686
|
+
<span class="attachment__type">${this._getFileExtension(fileName) || 'File'}</span>
|
|
8687
|
+
<span class="attachment__name">${fileName}</span>
|
|
8688
|
+
</button>
|
|
8689
|
+
`;
|
|
8690
|
+
})}
|
|
8691
|
+
</div>
|
|
8692
|
+
` : A}
|
|
8693
|
+
${msg.content ? b `
|
|
8694
|
+
<div class="message-content">
|
|
8695
|
+
${msg.type === 'user'
|
|
8696
|
+
? this._renderUserContent(msg.content)
|
|
8697
|
+
: o$1(marked.parse(msg.content, { breaks: true }))}
|
|
8698
|
+
</div>
|
|
8699
|
+
` : A}
|
|
8700
|
+
${msg.type === 'assistant' && this._isStreaming && msg === this.messages[this.messages.length - 1] ? b `
|
|
8701
|
+
<div class="typing">
|
|
8702
|
+
<span></span>
|
|
8703
|
+
<span></span>
|
|
8704
|
+
<span></span>
|
|
8705
|
+
</div>
|
|
8706
|
+
` : A}
|
|
8707
|
+
${msg.content ? b `
|
|
8708
|
+
<button
|
|
8709
|
+
class="message-copy"
|
|
8710
|
+
title=${this._copiedIndex === index ? 'Copied' : 'Copy'}
|
|
8711
|
+
@click=${() => this._handleCopyMessage(msg.content, index)}
|
|
8712
|
+
>
|
|
8713
|
+
${this._copiedIndex === index ? b `
|
|
8714
|
+
<svg
|
|
8715
|
+
viewBox="0 0 24 24"
|
|
8716
|
+
fill="none"
|
|
8717
|
+
stroke="currentColor"
|
|
8718
|
+
stroke-width="1.5"
|
|
8719
|
+
stroke-linecap="round"
|
|
8720
|
+
stroke-linejoin="round"
|
|
8721
|
+
>
|
|
8722
|
+
<path d="M20 6 9 17l-5-5" />
|
|
8723
|
+
</svg>
|
|
8724
|
+
` : b `
|
|
8725
|
+
<svg
|
|
8726
|
+
viewBox="0 0 24 24"
|
|
8727
|
+
fill="none"
|
|
8728
|
+
stroke="currentColor"
|
|
8729
|
+
stroke-width="1.5"
|
|
8730
|
+
stroke-linecap="round"
|
|
8731
|
+
stroke-linejoin="round"
|
|
8145
8732
|
>
|
|
8146
|
-
<
|
|
8147
|
-
|
|
8148
|
-
|
|
8733
|
+
<rect
|
|
8734
|
+
x="9"
|
|
8735
|
+
y="9"
|
|
8736
|
+
width="13"
|
|
8737
|
+
height="13"
|
|
8738
|
+
rx="2"
|
|
8149
8739
|
/>
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8740
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
8741
|
+
</svg>
|
|
8742
|
+
`}
|
|
8743
|
+
</button>
|
|
8744
|
+
` : A}
|
|
8745
|
+
</div>
|
|
8746
|
+
`;
|
|
8747
|
+
});
|
|
8748
|
+
if (group[0].type === 'user') {
|
|
8749
|
+
return items;
|
|
8750
|
+
}
|
|
8751
|
+
// The inline confirmation is this turn's final step, so it renders
|
|
8752
|
+
// INSIDE the agent-turn wrapper (not as a sibling) — it sits right
|
|
8753
|
+
// after the last message and the runway falls below it naturally.
|
|
8754
|
+
// Only on the last group, which is the current (paused) turn.
|
|
8755
|
+
const isLastGroup = group === messageGroups[messageGroups.length - 1];
|
|
8756
|
+
const showConfirmation = isLastGroup && this._pendingActions.length && this._inlineConfirm;
|
|
8757
|
+
// Keep the "thinking" indicator visible while a tool is running or
|
|
8758
|
+
// the model is between steps with no text yet. The trailing empty
|
|
8759
|
+
// assistant bubble shows the dots itself; but once a tool line is
|
|
8760
|
+
// the last message, that bubble is gone — so render the dots here
|
|
8761
|
+
// at the end of the live turn instead. Not while paused for a
|
|
8762
|
+
// confirmation (the ask replaces the indicator).
|
|
8763
|
+
const lastMsg = this.messages[this.messages.length - 1];
|
|
8764
|
+
const showTyping = isLastGroup
|
|
8765
|
+
&& this._isStreaming
|
|
8766
|
+
&& !this._pendingActions.length
|
|
8767
|
+
&& lastMsg?.type !== 'assistant';
|
|
8153
8768
|
return b `
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
<span
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8769
|
+
<div class="agent-turn">
|
|
8770
|
+
${items}
|
|
8771
|
+
${showTyping ? b `
|
|
8772
|
+
<div class="typing">
|
|
8773
|
+
<span></span>
|
|
8774
|
+
<span></span>
|
|
8775
|
+
<span></span>
|
|
8776
|
+
</div>
|
|
8777
|
+
` : A}
|
|
8778
|
+
${showConfirmation ? this._renderConfirmation() : A}
|
|
8779
|
+
</div>
|
|
8780
|
+
`;
|
|
8164
8781
|
})}
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
|
|
8171
|
-
|
|
8172
|
-
</
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
class="message-copy"
|
|
8184
|
-
title=${this._copiedIndex === index ? 'Copied' : 'Copy'}
|
|
8185
|
-
@click=${() => this._handleCopyMessage(msg.content, index)}
|
|
8186
|
-
>
|
|
8187
|
-
${this._copiedIndex === index ? b `
|
|
8188
|
-
<svg
|
|
8189
|
-
viewBox="0 0 24 24"
|
|
8190
|
-
fill="none"
|
|
8191
|
-
stroke="currentColor"
|
|
8192
|
-
stroke-width="1.5"
|
|
8193
|
-
stroke-linecap="round"
|
|
8194
|
-
stroke-linejoin="round"
|
|
8195
|
-
>
|
|
8196
|
-
<path d="M20 6 9 17l-5-5" />
|
|
8197
|
-
</svg>
|
|
8198
|
-
` : b `
|
|
8199
|
-
<svg
|
|
8200
|
-
viewBox="0 0 24 24"
|
|
8201
|
-
fill="none"
|
|
8202
|
-
stroke="currentColor"
|
|
8203
|
-
stroke-width="1.5"
|
|
8782
|
+
</div>
|
|
8783
|
+
</div>
|
|
8784
|
+
|
|
8785
|
+
${this._pendingActions.length && !this._inlineConfirm ? this._renderConfirmation() : A}
|
|
8786
|
+
|
|
8787
|
+
${this._error ? b `
|
|
8788
|
+
<div class="error">
|
|
8789
|
+
<span>${this._error}</span>
|
|
8790
|
+
<button class="error-dismiss" @click=${this._handleErrorDismiss}>
|
|
8791
|
+
<svg
|
|
8792
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8793
|
+
fill="none"
|
|
8794
|
+
viewBox="0 0 24 24"
|
|
8795
|
+
stroke-width="2"
|
|
8796
|
+
stroke="currentColor"
|
|
8797
|
+
class="error-dismiss-icon"
|
|
8798
|
+
>
|
|
8799
|
+
<path
|
|
8204
8800
|
stroke-linecap="round"
|
|
8205
8801
|
stroke-linejoin="round"
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
y="9"
|
|
8210
|
-
width="13"
|
|
8211
|
-
height="13"
|
|
8212
|
-
rx="2"
|
|
8213
|
-
/>
|
|
8214
|
-
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
8215
|
-
</svg>
|
|
8216
|
-
`}
|
|
8802
|
+
d="M6 18 18 6M6 6l12 12"
|
|
8803
|
+
/>
|
|
8804
|
+
</svg>
|
|
8217
8805
|
</button>
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8806
|
+
</div>
|
|
8807
|
+
` : this._contextLimitReached ? b `
|
|
8808
|
+
<div class="context-limit" role="alert">
|
|
8809
|
+
${this._contextLimitMessage
|
|
8810
|
+
|| 'This conversation has reached its limit. Please start a new one to continue.'}
|
|
8221
8811
|
</div>
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8812
|
+
` : this._contextWarning ? b `
|
|
8813
|
+
<div class="context-warning" role="status">
|
|
8814
|
+
<span>${this._contextWarning}</span>
|
|
8815
|
+
<button
|
|
8816
|
+
class="context-warning-dismiss"
|
|
8817
|
+
title="Dismiss"
|
|
8818
|
+
@click=${this._handleContextWarningDismiss}
|
|
8819
|
+
>
|
|
8230
8820
|
<svg
|
|
8231
8821
|
xmlns="http://www.w3.org/2000/svg"
|
|
8232
8822
|
fill="none"
|
|
8233
8823
|
viewBox="0 0 24 24"
|
|
8234
8824
|
stroke-width="2"
|
|
8235
8825
|
stroke="currentColor"
|
|
8236
|
-
class="
|
|
8237
|
-
>
|
|
8238
|
-
<path
|
|
8239
|
-
stroke-linecap="round"
|
|
8240
|
-
stroke-linejoin="round"
|
|
8241
|
-
d="M6 18 18 6M6 6l12 12"
|
|
8242
|
-
/>
|
|
8243
|
-
</svg>
|
|
8244
|
-
</button>
|
|
8245
|
-
</div>
|
|
8246
|
-
` : this._contextLimitReached ? b `
|
|
8247
|
-
<div class="context-limit" role="alert">
|
|
8248
|
-
${this._contextLimitMessage
|
|
8249
|
-
|| 'This conversation has reached its limit. Please start a new one to continue.'}
|
|
8250
|
-
</div>
|
|
8251
|
-
` : this._contextWarning ? b `
|
|
8252
|
-
<div class="context-warning" role="status">
|
|
8253
|
-
<span>${this._contextWarning}</span>
|
|
8254
|
-
<button
|
|
8255
|
-
class="context-warning-dismiss"
|
|
8256
|
-
title="Dismiss"
|
|
8257
|
-
@click=${this._handleContextWarningDismiss}
|
|
8258
|
-
>
|
|
8259
|
-
<svg
|
|
8260
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
8261
|
-
fill="none"
|
|
8262
|
-
viewBox="0 0 24 24"
|
|
8263
|
-
stroke-width="2"
|
|
8264
|
-
stroke="currentColor"
|
|
8265
|
-
class="context-warning-dismiss-icon"
|
|
8826
|
+
class="context-warning-dismiss-icon"
|
|
8266
8827
|
>
|
|
8267
8828
|
<path
|
|
8268
8829
|
stroke-linecap="round"
|
|
@@ -8274,68 +8835,68 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
8274
8835
|
</div>
|
|
8275
8836
|
` : A}
|
|
8276
8837
|
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8838
|
+
<div class="footer">
|
|
8839
|
+
${this._pendingConversationFiles.length ? b `
|
|
8840
|
+
<div class="composer-attachments">
|
|
8841
|
+
${c(this._pendingConversationFiles, (p) => p.localId, (p) => b `
|
|
8842
|
+
<div
|
|
8843
|
+
class=${e({
|
|
8283
8844
|
chip: true,
|
|
8284
8845
|
'chip--doc': !p.previewUrl,
|
|
8285
8846
|
})}
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8847
|
+
title=${p.name}
|
|
8848
|
+
>
|
|
8849
|
+
<div
|
|
8850
|
+
class=${e({
|
|
8290
8851
|
'chip-body': true,
|
|
8291
8852
|
'chip-body--image': Boolean(p.previewUrl),
|
|
8292
8853
|
})}
|
|
8293
|
-
|
|
8294
|
-
|
|
8854
|
+
>
|
|
8855
|
+
${p.previewUrl
|
|
8295
8856
|
? b `<img src=${p.previewUrl} alt=${p.name} />`
|
|
8296
8857
|
: b `
|
|
8297
|
-
|
|
8298
|
-
|
|
8858
|
+
<span class="chip-type">${this._getFileExtension(p.name) || 'File'}</span>
|
|
8859
|
+
<span class="chip-name">${p.name}</span>
|
|
8299
8860
|
`}
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8861
|
+
</div>
|
|
8862
|
+
<button
|
|
8863
|
+
class="chip-remove"
|
|
8864
|
+
title="Remove"
|
|
8865
|
+
@click=${() => this._handlePendingRemove(p.localId)}
|
|
8866
|
+
>
|
|
8867
|
+
<svg
|
|
8868
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8869
|
+
fill="none"
|
|
8870
|
+
viewBox="0 0 24 24"
|
|
8871
|
+
stroke-width="1.75"
|
|
8872
|
+
stroke="currentColor"
|
|
8873
|
+
>
|
|
8874
|
+
<path
|
|
8875
|
+
stroke-linecap="round"
|
|
8876
|
+
stroke-linejoin="round"
|
|
8877
|
+
d="M6 18 18 6M6 6l12 12"
|
|
8878
|
+
/>
|
|
8879
|
+
</svg>
|
|
8880
|
+
</button>
|
|
8881
|
+
</div>
|
|
8321
8882
|
`)}
|
|
8322
|
-
|
|
8883
|
+
</div>
|
|
8323
8884
|
` : A}
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8885
|
+
<input
|
|
8886
|
+
class="file-input"
|
|
8887
|
+
type="file"
|
|
8888
|
+
accept=${ACCEPTED_FILE_EXTENSIONS.map((ext) => '.' + ext).join(',')}
|
|
8889
|
+
multiple
|
|
8890
|
+
hidden
|
|
8891
|
+
@change=${this._handleFileInputChange}
|
|
8892
|
+
/>
|
|
8893
|
+
${this._skillMenuOpened ? this._renderSkillMenu() : A}
|
|
8894
|
+
<div class="input-wrapper">
|
|
8895
|
+
<button
|
|
8335
8896
|
class="input-plus"
|
|
8336
8897
|
@click=${this._handlePlusClick}
|
|
8337
|
-
|
|
8338
|
-
|
|
8898
|
+
>+</button>
|
|
8899
|
+
<textarea
|
|
8339
8900
|
class="input"
|
|
8340
8901
|
.value=${this._inputValue}
|
|
8341
8902
|
placeholder=${this.placeholder}
|
|
@@ -8343,31 +8904,32 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
8343
8904
|
@input=${this._handleInputChange}
|
|
8344
8905
|
@keydown=${this._handleKeyDown}
|
|
8345
8906
|
@paste=${this._handlePaste}
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
${
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
<svg
|
|
8356
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
8357
|
-
fill="currentColor"
|
|
8358
|
-
viewBox="0 0 24 24"
|
|
8359
|
-
class="mic-stop-icon"
|
|
8360
|
-
aria-hidden="true"
|
|
8907
|
+
></textarea>
|
|
8908
|
+
${this._renderModeSelector()}
|
|
8909
|
+
${window.SpeechRecognition || window.webkitSpeechRecognition ? b `
|
|
8910
|
+
${this._isDictating ? b `
|
|
8911
|
+
<button
|
|
8912
|
+
class="mic-stop"
|
|
8913
|
+
title="Stop dictation"
|
|
8914
|
+
aria-label="Stop dictation"
|
|
8915
|
+
@click=${this._handleMicClick}
|
|
8361
8916
|
>
|
|
8362
|
-
<
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8917
|
+
<svg
|
|
8918
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8919
|
+
fill="currentColor"
|
|
8920
|
+
viewBox="0 0 24 24"
|
|
8921
|
+
class="mic-stop-icon"
|
|
8922
|
+
aria-hidden="true"
|
|
8923
|
+
>
|
|
8924
|
+
<rect
|
|
8925
|
+
x="6"
|
|
8926
|
+
y="6"
|
|
8927
|
+
width="12"
|
|
8928
|
+
height="12"
|
|
8929
|
+
rx="2"
|
|
8930
|
+
/>
|
|
8931
|
+
</svg>
|
|
8932
|
+
</button>
|
|
8371
8933
|
` : b `
|
|
8372
8934
|
<button
|
|
8373
8935
|
class="mic"
|
|
@@ -8393,35 +8955,32 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
8393
8955
|
</button>
|
|
8394
8956
|
`}
|
|
8395
8957
|
` : A}
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
>
|
|
8402
|
-
<svg
|
|
8403
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
8404
|
-
fill="currentColor"
|
|
8405
|
-
viewBox="0 0 24 24"
|
|
8406
|
-
class="stop-icon"
|
|
8958
|
+
${this._isStreaming ? b `
|
|
8959
|
+
<button
|
|
8960
|
+
class="stop"
|
|
8961
|
+
@click=${this._handleStop}
|
|
8962
|
+
title="Stop generating"
|
|
8407
8963
|
>
|
|
8408
|
-
<
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8964
|
+
<svg
|
|
8965
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8966
|
+
fill="currentColor"
|
|
8967
|
+
viewBox="0 0 24 24"
|
|
8968
|
+
class="stop-icon"
|
|
8969
|
+
>
|
|
8970
|
+
<rect
|
|
8971
|
+
x="6"
|
|
8972
|
+
y="6"
|
|
8973
|
+
width="12"
|
|
8974
|
+
height="12"
|
|
8975
|
+
rx="2"
|
|
8976
|
+
/>
|
|
8977
|
+
</svg>
|
|
8978
|
+
</button>
|
|
8417
8979
|
` : b `
|
|
8418
8980
|
<button
|
|
8419
8981
|
class=${e({
|
|
8420
8982
|
'send': true,
|
|
8421
|
-
|
|
8422
|
-
// normal message would be rejected until it's resolved.
|
|
8423
|
-
'send--disabled': (!this._inputValue.trim() && !this._pendingConversationFiles.length)
|
|
8424
|
-
|| this._pendingActions.length > 0,
|
|
8983
|
+
'send--disabled': (!this._inputValue.trim() && !this._pendingConversationFiles.length) || this._pendingActions.length > 0,
|
|
8425
8984
|
})}
|
|
8426
8985
|
@click=${this._handleSubmit}
|
|
8427
8986
|
title=${this._pendingActions.length ? 'Approve or deny the pending action first' : 'Send message'}
|
|
@@ -8440,12 +8999,12 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
8440
8999
|
</svg>
|
|
8441
9000
|
</button>
|
|
8442
9001
|
`}
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
9002
|
+
</div>
|
|
9003
|
+
</div>
|
|
9004
|
+
${this._skillBrowseOpened ? this._renderSkillBrowse() : A}
|
|
9005
|
+
${this._renderDrawer()}
|
|
9006
|
+
</div>
|
|
9007
|
+
`;
|
|
8449
9008
|
}
|
|
8450
9009
|
};
|
|
8451
9010
|
KRChatbot.styles = i$6 `
|
|
@@ -8558,6 +9117,14 @@ KRChatbot.styles = i$6 `
|
|
|
8558
9117
|
box-shadow: none;
|
|
8559
9118
|
border: none;
|
|
8560
9119
|
animation: none;
|
|
9120
|
+
/* Reserve space for the drawer by animating padding-right in sync with the
|
|
9121
|
+
drawer's slide, so the chat column shrinks rather than being overlaid. */
|
|
9122
|
+
transition: padding-right 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
9123
|
+
}
|
|
9124
|
+
|
|
9125
|
+
/* Track the cursor 1:1 while dragging the drawer's resize handle. */
|
|
9126
|
+
:host([variant='inline']) .panel--resizing-drawer {
|
|
9127
|
+
transition: none;
|
|
8561
9128
|
}
|
|
8562
9129
|
|
|
8563
9130
|
@keyframes chatbot-panel-in {
|
|
@@ -8625,16 +9192,31 @@ KRChatbot.styles = i$6 `
|
|
|
8625
9192
|
color: #000000;
|
|
8626
9193
|
}
|
|
8627
9194
|
|
|
8628
|
-
.header-action--active {
|
|
8629
|
-
background: rgba(0, 0, 0, 0.08);
|
|
8630
|
-
color: #000000;
|
|
8631
|
-
}
|
|
8632
|
-
|
|
8633
9195
|
.header-action-icon {
|
|
8634
9196
|
width: 20px;
|
|
8635
9197
|
height: 20px;
|
|
8636
9198
|
}
|
|
8637
9199
|
|
|
9200
|
+
.settings-section {
|
|
9201
|
+
padding: 14px 20px;
|
|
9202
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
|
9203
|
+
}
|
|
9204
|
+
|
|
9205
|
+
.settings-section:last-child {
|
|
9206
|
+
border-bottom: none;
|
|
9207
|
+
}
|
|
9208
|
+
|
|
9209
|
+
.settings-section__title {
|
|
9210
|
+
font-size: 14px;
|
|
9211
|
+
font-weight: 600;
|
|
9212
|
+
color: #000000;
|
|
9213
|
+
margin-bottom: 12px;
|
|
9214
|
+
}
|
|
9215
|
+
|
|
9216
|
+
.settings-row {
|
|
9217
|
+
padding: 6px 0;
|
|
9218
|
+
}
|
|
9219
|
+
|
|
8638
9220
|
/* Messages */
|
|
8639
9221
|
/* Full-width scroll container — keeps the scrollbar at the panel edge. */
|
|
8640
9222
|
.messages {
|
|
@@ -8715,6 +9297,138 @@ KRChatbot.styles = i$6 `
|
|
|
8715
9297
|
gap: 24px;
|
|
8716
9298
|
}
|
|
8717
9299
|
|
|
9300
|
+
/* An agent turn wraps all of its messages (reasoning, tool lines, text,
|
|
9301
|
+
decision) so the scroll-to-top runway can sit on the whole turn and content
|
|
9302
|
+
streams inside it instead of stacking as siblings below the runway. A flex
|
|
9303
|
+
column with the same 36px gap as .messages-inner, so item spacing is
|
|
9304
|
+
unchanged whether grouped or not. Full width so its children align exactly
|
|
9305
|
+
as before (assistant left, tool/decision left). */
|
|
9306
|
+
.agent-turn {
|
|
9307
|
+
display: flex;
|
|
9308
|
+
flex-direction: column;
|
|
9309
|
+
gap: 36px;
|
|
9310
|
+
width: 100%;
|
|
9311
|
+
}
|
|
9312
|
+
|
|
9313
|
+
/* Tool-activity line — "the assistant ran X" row in the transcript. Same text
|
|
9314
|
+
style as any other message (black, 14px); a leading › chevron marks it as a
|
|
9315
|
+
step (in the same text style, not an icon). */
|
|
9316
|
+
.tool-activity {
|
|
9317
|
+
align-self: flex-start;
|
|
9318
|
+
display: flex;
|
|
9319
|
+
align-items: baseline;
|
|
9320
|
+
gap: 6px;
|
|
9321
|
+
font-size: 14px;
|
|
9322
|
+
color: #000000;
|
|
9323
|
+
/* Keep the chevron + name + input on a single line; input truncates if long. */
|
|
9324
|
+
white-space: nowrap;
|
|
9325
|
+
}
|
|
9326
|
+
|
|
9327
|
+
.tool-activity--error {
|
|
9328
|
+
color: var(--kr-chatbot-error-text);
|
|
9329
|
+
}
|
|
9330
|
+
|
|
9331
|
+
/* Leading chevron — plain text in the same style as the name, so it reads as
|
|
9332
|
+
one label rather than an icon. */
|
|
9333
|
+
.tool-activity__chevron {
|
|
9334
|
+
flex-shrink: 0;
|
|
9335
|
+
}
|
|
9336
|
+
|
|
9337
|
+
/* The tool name never shrinks — it's the input that truncates if the header
|
|
9338
|
+
runs out of room. */
|
|
9339
|
+
.tool-activity__label {
|
|
9340
|
+
flex-shrink: 0;
|
|
9341
|
+
}
|
|
9342
|
+
|
|
9343
|
+
/* The tool input rendered inline in the header, Claude-CLI style:
|
|
9344
|
+
"Read(chatbot.ts)". Same color/font as the tool name — it reads as one
|
|
9345
|
+
label. Truncates with an ellipsis so the header stays on one line. */
|
|
9346
|
+
.tool-activity__input {
|
|
9347
|
+
overflow: hidden;
|
|
9348
|
+
text-overflow: ellipsis;
|
|
9349
|
+
min-width: 0;
|
|
9350
|
+
}
|
|
9351
|
+
|
|
9352
|
+
/* Header + result grouped as one unit so the parent .message--assistant 24px
|
|
9353
|
+
gap doesn't push them apart. Rows sit tight together inside. */
|
|
9354
|
+
.tool-activity-block {
|
|
9355
|
+
display: flex;
|
|
9356
|
+
flex-direction: column;
|
|
9357
|
+
gap: 4px;
|
|
9358
|
+
}
|
|
9359
|
+
|
|
9360
|
+
/* The result block indented under the activity line with a ⎿ tree connector,
|
|
9361
|
+
like Claude CLI's tool output preview. Uses the normal message text style
|
|
9362
|
+
(14px, black) — only the ⎿ connector is muted. */
|
|
9363
|
+
.tool-activity__result {
|
|
9364
|
+
align-self: flex-start;
|
|
9365
|
+
display: flex;
|
|
9366
|
+
gap: 2px;
|
|
9367
|
+
/* Align the ⎿ connector's visible stroke under the first character of the
|
|
9368
|
+
tool name. The name is preceded by the "› " chevron (chevron width + 6px
|
|
9369
|
+
gap ≈ 11px), and the ⎿ glyph carries ~5.6px more internal left bearing
|
|
9370
|
+
than the name's first letter — so net-shift the block right to land the
|
|
9371
|
+
stroke under the name. */
|
|
9372
|
+
margin-left: 6px;
|
|
9373
|
+
font-size: 14px;
|
|
9374
|
+
color: #000000;
|
|
9375
|
+
}
|
|
9376
|
+
|
|
9377
|
+
.tool-activity__result--error {
|
|
9378
|
+
color: var(--kr-chatbot-error-text);
|
|
9379
|
+
}
|
|
9380
|
+
|
|
9381
|
+
.tool-activity__connector {
|
|
9382
|
+
flex-shrink: 0;
|
|
9383
|
+
color: rgba(0, 0, 0, 0.55);
|
|
9384
|
+
user-select: none;
|
|
9385
|
+
/* The ⎿ arm sits near the baseline, ~6px below the result's first-character
|
|
9386
|
+
vertical center. Nudge the glyph up so the arm tip lines up with that center. */
|
|
9387
|
+
transform: translateY(-6px);
|
|
9388
|
+
}
|
|
9389
|
+
|
|
9390
|
+
.tool-activity__result-body {
|
|
9391
|
+
/* Small horizontal gap between the ⎿ arm and the result text so they don't
|
|
9392
|
+
collide (the ⎿ glyph's own arm already extends most of the way). */
|
|
9393
|
+
margin: 0 0 0 6px;
|
|
9394
|
+
white-space: pre-wrap;
|
|
9395
|
+
word-break: break-word;
|
|
9396
|
+
font: inherit;
|
|
9397
|
+
}
|
|
9398
|
+
|
|
9399
|
+
.tool-activity__more {
|
|
9400
|
+
color: rgba(0, 0, 0, 0.4);
|
|
9401
|
+
}
|
|
9402
|
+
|
|
9403
|
+
/* Decision line — the persistent record of an approve/deny (Rail 3). Uses the
|
|
9404
|
+
normal message text style (14px, black); a check/deny icon marks it. */
|
|
9405
|
+
.decision {
|
|
9406
|
+
align-self: flex-start;
|
|
9407
|
+
display: flex;
|
|
9408
|
+
align-items: center;
|
|
9409
|
+
gap: 8px;
|
|
9410
|
+
font-size: 14px;
|
|
9411
|
+
color: #000000;
|
|
9412
|
+
}
|
|
9413
|
+
|
|
9414
|
+
.decision--denied {
|
|
9415
|
+
color: var(--kr-chatbot-error-text);
|
|
9416
|
+
}
|
|
9417
|
+
|
|
9418
|
+
.decision__icon {
|
|
9419
|
+
display: inline-flex;
|
|
9420
|
+
align-items: center;
|
|
9421
|
+
justify-content: center;
|
|
9422
|
+
width: 16px;
|
|
9423
|
+
height: 16px;
|
|
9424
|
+
flex-shrink: 0;
|
|
9425
|
+
}
|
|
9426
|
+
|
|
9427
|
+
.decision__icon svg {
|
|
9428
|
+
width: 15px;
|
|
9429
|
+
height: 15px;
|
|
9430
|
+
}
|
|
9431
|
+
|
|
8718
9432
|
/* anywhere, NOT break-word: both break a long unbreakable run (a pasted URL) for
|
|
8719
9433
|
layout, but only anywhere also shrinks the element's intrinsic min-content width.
|
|
8720
9434
|
With break-word the URL still reports its full unbroken width as the minimum,
|
|
@@ -8996,11 +9710,49 @@ KRChatbot.styles = i$6 `
|
|
|
8996
9710
|
overflow-y: auto;
|
|
8997
9711
|
}
|
|
8998
9712
|
|
|
9713
|
+
/* Inline variant (Claude-CLI style): sits IN the message stream as just
|
|
9714
|
+
another assistant message — no card chrome (padding/background/border/
|
|
9715
|
+
radius/shadow), no float offsets, no height cap. It flows and scrolls with
|
|
9716
|
+
the transcript. */
|
|
9717
|
+
.confirmation--inline {
|
|
9718
|
+
position: static;
|
|
9719
|
+
left: auto;
|
|
9720
|
+
right: auto;
|
|
9721
|
+
bottom: auto;
|
|
9722
|
+
z-index: auto;
|
|
9723
|
+
max-width: none;
|
|
9724
|
+
margin: 0;
|
|
9725
|
+
max-height: none;
|
|
9726
|
+
overflow: visible;
|
|
9727
|
+
padding: 0;
|
|
9728
|
+
border: none;
|
|
9729
|
+
border-radius: 0;
|
|
9730
|
+
background: none;
|
|
9731
|
+
box-shadow: none;
|
|
9732
|
+
/* scrollIntoView({ block: 'end' }) aligns the card's bottom to the viewport
|
|
9733
|
+
bottom; this margin makes it stop short so the Yes/No buttons aren't flush
|
|
9734
|
+
against (or clipped by) the fold. */
|
|
9735
|
+
scroll-margin-bottom: 24px;
|
|
9736
|
+
}
|
|
9737
|
+
|
|
9738
|
+
/* The raw arguments, pretty-printed. Wraps rather than scrolling — the whole
|
|
9739
|
+
card grows with the transcript (the point of the inline variant). */
|
|
9740
|
+
.confirmation__details-input {
|
|
9741
|
+
margin: 8px 0 0;
|
|
9742
|
+
border-radius: 6px;
|
|
9743
|
+
color: var(--kr-chatbot-text);
|
|
9744
|
+
font-family: 'SF Mono', SFMono-Regular, ui-monospace, Menlo, Consolas, monospace;
|
|
9745
|
+
font-size: 13px;
|
|
9746
|
+
line-height: 1.5;
|
|
9747
|
+
white-space: pre-wrap;
|
|
9748
|
+
overflow-wrap: anywhere;
|
|
9749
|
+
}
|
|
9750
|
+
|
|
8999
9751
|
/* "Run {tool}?" heading — names the tool being run. */
|
|
9000
9752
|
.confirmation__title {
|
|
9001
9753
|
font-size: 16px;
|
|
9002
9754
|
font-weight: 600;
|
|
9003
|
-
margin-bottom:
|
|
9755
|
+
margin-bottom: 12px;
|
|
9004
9756
|
}
|
|
9005
9757
|
|
|
9006
9758
|
.confirmation__message {
|
|
@@ -9009,8 +9761,26 @@ KRChatbot.styles = i$6 `
|
|
|
9009
9761
|
line-height: 1.5;
|
|
9010
9762
|
}
|
|
9011
9763
|
|
|
9012
|
-
|
|
9013
|
-
|
|
9764
|
+
/* Batch list: one bullet per pending tool — its name, then the model's
|
|
9765
|
+
plain-language intent under it. */
|
|
9766
|
+
.confirmation__list {
|
|
9767
|
+
margin: 0;
|
|
9768
|
+
padding-left: 20px;
|
|
9769
|
+
font-size: 14px;
|
|
9770
|
+
line-height: 1.5;
|
|
9771
|
+
}
|
|
9772
|
+
|
|
9773
|
+
.confirmation__list-item:not(:last-child) {
|
|
9774
|
+
margin-bottom: 8px;
|
|
9775
|
+
}
|
|
9776
|
+
|
|
9777
|
+
.confirmation__list-name {
|
|
9778
|
+
display: block;
|
|
9779
|
+
font-weight: 600;
|
|
9780
|
+
}
|
|
9781
|
+
|
|
9782
|
+
.confirmation__list-desc {
|
|
9783
|
+
display: block;
|
|
9014
9784
|
}
|
|
9015
9785
|
|
|
9016
9786
|
/* Buttons in a row; wrap on a narrow panel rather than overflowing the card. */
|
|
@@ -9018,45 +9788,34 @@ KRChatbot.styles = i$6 `
|
|
|
9018
9788
|
display: flex;
|
|
9019
9789
|
flex-wrap: wrap;
|
|
9020
9790
|
align-items: center;
|
|
9021
|
-
gap:
|
|
9022
|
-
margin-top:
|
|
9791
|
+
gap: 10px;
|
|
9792
|
+
margin-top: 20px;
|
|
9023
9793
|
}
|
|
9024
9794
|
|
|
9025
|
-
/* Keyboard-highlighted decision — a focus ring around the button
|
|
9795
|
+
/* Keyboard-highlighted decision — a focus ring around the button, drawn
|
|
9026
9796
|
outside its border so it reads over both the flat (Yes) and outline
|
|
9027
|
-
variants without recoloring the button
|
|
9797
|
+
variants without recoloring or reshaping the button. Don't set
|
|
9798
|
+
border-radius here: that's the button's own (its pill shape); outline
|
|
9799
|
+
follows it and doesn't affect the button's size. */
|
|
9028
9800
|
.confirmation__btn--highlighted {
|
|
9029
|
-
border-radius: 8px;
|
|
9030
9801
|
outline: 2px solid var(--kr-chatbot-primary);
|
|
9031
9802
|
outline-offset: 2px;
|
|
9032
9803
|
}
|
|
9033
9804
|
|
|
9034
9805
|
/*
|
|
9035
|
-
*
|
|
9036
|
-
*
|
|
9037
|
-
* overlay.
|
|
9038
|
-
*
|
|
9039
|
-
*
|
|
9806
|
+
* Side drawer, docked to the right of the inline chat, holding the Settings
|
|
9807
|
+
* and Debug views. The chat column shrinks (padding-right) to make room; the
|
|
9808
|
+
* drawer does not overlay. It stays mounted and slides in/out via a transform,
|
|
9809
|
+
* so opening and closing animate; switching views just swaps the content
|
|
9810
|
+
* inside (no re-slide, no width change). One consistent resizable width across
|
|
9811
|
+
* both views; the width is set inline from _drawerWidth.
|
|
9040
9812
|
*/
|
|
9041
|
-
|
|
9042
|
-
* The chat column reserves space for the drawer by animating padding-right,
|
|
9043
|
-
* so opening the drawer shrinks the chat (header/messages/composer) rather
|
|
9044
|
-
* than overlaying it. Only applies in inline mode.
|
|
9045
|
-
*/
|
|
9046
|
-
:host([variant='inline']) .panel {
|
|
9047
|
-
transition: padding-right 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
9048
|
-
}
|
|
9049
|
-
|
|
9050
|
-
/* Track the cursor 1:1 while dragging the drawer's resize handle. */
|
|
9051
|
-
:host([variant='inline']) .panel--resizing-debug {
|
|
9052
|
-
transition: none;
|
|
9053
|
-
}
|
|
9054
|
-
|
|
9055
|
-
.debug-drawer {
|
|
9813
|
+
.drawer {
|
|
9056
9814
|
position: absolute;
|
|
9057
9815
|
top: 0;
|
|
9058
9816
|
right: 0;
|
|
9059
9817
|
bottom: 0;
|
|
9818
|
+
max-width: 100%;
|
|
9060
9819
|
background: var(--kr-chatbot-bg);
|
|
9061
9820
|
display: flex;
|
|
9062
9821
|
flex-direction: column;
|
|
@@ -9068,17 +9827,17 @@ KRChatbot.styles = i$6 `
|
|
|
9068
9827
|
|
|
9069
9828
|
/* While dragging the resize handle, suppress the slide transition so the
|
|
9070
9829
|
drawer tracks the cursor 1:1. */
|
|
9071
|
-
.
|
|
9830
|
+
.drawer--resizing {
|
|
9072
9831
|
transition: none;
|
|
9073
9832
|
}
|
|
9074
9833
|
|
|
9075
|
-
.
|
|
9834
|
+
.drawer--open {
|
|
9076
9835
|
transform: translateX(0);
|
|
9077
9836
|
box-shadow: -4px 0 20px rgba(10, 26, 46, 0.07);
|
|
9078
9837
|
}
|
|
9079
9838
|
|
|
9080
9839
|
/* Left-edge grab handle for resizing the drawer. */
|
|
9081
|
-
.
|
|
9840
|
+
.drawer__resize {
|
|
9082
9841
|
position: absolute;
|
|
9083
9842
|
top: 0;
|
|
9084
9843
|
left: 0;
|
|
@@ -9088,7 +9847,7 @@ KRChatbot.styles = i$6 `
|
|
|
9088
9847
|
z-index: 10;
|
|
9089
9848
|
}
|
|
9090
9849
|
|
|
9091
|
-
.
|
|
9850
|
+
.drawer__header {
|
|
9092
9851
|
display: flex;
|
|
9093
9852
|
align-items: center;
|
|
9094
9853
|
justify-content: space-between;
|
|
@@ -9098,13 +9857,13 @@ KRChatbot.styles = i$6 `
|
|
|
9098
9857
|
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
9099
9858
|
}
|
|
9100
9859
|
|
|
9101
|
-
.
|
|
9860
|
+
.drawer__title {
|
|
9102
9861
|
font-size: 14px;
|
|
9103
9862
|
font-weight: 600;
|
|
9104
9863
|
color: #000000;
|
|
9105
9864
|
}
|
|
9106
9865
|
|
|
9107
|
-
.
|
|
9866
|
+
.drawer__body {
|
|
9108
9867
|
flex: 1;
|
|
9109
9868
|
overflow-y: auto;
|
|
9110
9869
|
overflow-x: hidden;
|
|
@@ -9313,6 +10072,89 @@ KRChatbot.styles = i$6 `
|
|
|
9313
10072
|
margin-right: 4px;
|
|
9314
10073
|
}
|
|
9315
10074
|
|
|
10075
|
+
/* Mode selector — Gemini-style inline label + caret before the mic. Anchors
|
|
10076
|
+
its upward menu; sized so it never grows the single-row composer height. */
|
|
10077
|
+
.mode {
|
|
10078
|
+
position: relative;
|
|
10079
|
+
flex-shrink: 0;
|
|
10080
|
+
display: flex;
|
|
10081
|
+
align-items: center;
|
|
10082
|
+
}
|
|
10083
|
+
|
|
10084
|
+
.mode-trigger {
|
|
10085
|
+
display: inline-flex;
|
|
10086
|
+
align-items: center;
|
|
10087
|
+
gap: 2px;
|
|
10088
|
+
height: 34px;
|
|
10089
|
+
padding: 0 8px;
|
|
10090
|
+
border: none;
|
|
10091
|
+
background: transparent;
|
|
10092
|
+
color: var(--kr-chatbot-text);
|
|
10093
|
+
border-radius: 17px;
|
|
10094
|
+
cursor: pointer;
|
|
10095
|
+
font: inherit;
|
|
10096
|
+
font-size: 14px;
|
|
10097
|
+
white-space: nowrap;
|
|
10098
|
+
transition: background 0.15s;
|
|
10099
|
+
}
|
|
10100
|
+
|
|
10101
|
+
.mode-trigger:hover {
|
|
10102
|
+
background: #f8f9fc;
|
|
10103
|
+
}
|
|
10104
|
+
|
|
10105
|
+
.mode-trigger__caret {
|
|
10106
|
+
width: 16px;
|
|
10107
|
+
height: 16px;
|
|
10108
|
+
flex-shrink: 0;
|
|
10109
|
+
}
|
|
10110
|
+
|
|
10111
|
+
/* Menu opens upward, anchored to the trigger's right edge. */
|
|
10112
|
+
.mode-menu {
|
|
10113
|
+
position: absolute;
|
|
10114
|
+
bottom: calc(100% + 8px);
|
|
10115
|
+
right: 0;
|
|
10116
|
+
min-width: 220px;
|
|
10117
|
+
background: white;
|
|
10118
|
+
border: 1px solid #b0b0b0;
|
|
10119
|
+
border-radius: 8px;
|
|
10120
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
10121
|
+
padding: 6px 0;
|
|
10122
|
+
z-index: 10000;
|
|
10123
|
+
display: flex;
|
|
10124
|
+
flex-direction: column;
|
|
10125
|
+
}
|
|
10126
|
+
|
|
10127
|
+
.mode-menu__item {
|
|
10128
|
+
display: flex;
|
|
10129
|
+
flex-direction: column;
|
|
10130
|
+
gap: 1px;
|
|
10131
|
+
width: 100%;
|
|
10132
|
+
padding: 8px 14px;
|
|
10133
|
+
background: none;
|
|
10134
|
+
border: none;
|
|
10135
|
+
text-align: left;
|
|
10136
|
+
cursor: pointer;
|
|
10137
|
+
font: inherit;
|
|
10138
|
+
color: var(--kr-chatbot-text);
|
|
10139
|
+
}
|
|
10140
|
+
|
|
10141
|
+
.mode-menu__item:hover {
|
|
10142
|
+
background: #f3f4f6;
|
|
10143
|
+
}
|
|
10144
|
+
|
|
10145
|
+
.mode-menu__item--active {
|
|
10146
|
+
background: #eef1f6;
|
|
10147
|
+
}
|
|
10148
|
+
|
|
10149
|
+
.mode-menu__label {
|
|
10150
|
+
font-size: 14px;
|
|
10151
|
+
font-weight: 500;
|
|
10152
|
+
}
|
|
10153
|
+
|
|
10154
|
+
.mode-menu__desc {
|
|
10155
|
+
font-size: 12px;
|
|
10156
|
+
}
|
|
10157
|
+
|
|
9316
10158
|
.mic {
|
|
9317
10159
|
width: 34px;
|
|
9318
10160
|
height: 34px;
|
|
@@ -9991,13 +10833,16 @@ __decorate([
|
|
|
9991
10833
|
], KRChatbot.prototype, "_cacheCheckpoints", void 0);
|
|
9992
10834
|
__decorate([
|
|
9993
10835
|
r()
|
|
9994
|
-
], KRChatbot.prototype, "
|
|
10836
|
+
], KRChatbot.prototype, "_drawerOpen", void 0);
|
|
10837
|
+
__decorate([
|
|
10838
|
+
r()
|
|
10839
|
+
], KRChatbot.prototype, "_drawerView", void 0);
|
|
9995
10840
|
__decorate([
|
|
9996
10841
|
r()
|
|
9997
|
-
], KRChatbot.prototype, "
|
|
10842
|
+
], KRChatbot.prototype, "_drawerWidth", void 0);
|
|
9998
10843
|
__decorate([
|
|
9999
10844
|
r()
|
|
10000
|
-
], KRChatbot.prototype, "
|
|
10845
|
+
], KRChatbot.prototype, "_isDrawerResizing", void 0);
|
|
10001
10846
|
__decorate([
|
|
10002
10847
|
r()
|
|
10003
10848
|
], KRChatbot.prototype, "_pendingConversationFiles", void 0);
|
|
@@ -10040,6 +10885,21 @@ __decorate([
|
|
|
10040
10885
|
__decorate([
|
|
10041
10886
|
r()
|
|
10042
10887
|
], KRChatbot.prototype, "_isDictating", void 0);
|
|
10888
|
+
__decorate([
|
|
10889
|
+
r()
|
|
10890
|
+
], KRChatbot.prototype, "_mode", void 0);
|
|
10891
|
+
__decorate([
|
|
10892
|
+
r()
|
|
10893
|
+
], KRChatbot.prototype, "_modeMenuOpened", void 0);
|
|
10894
|
+
__decorate([
|
|
10895
|
+
r()
|
|
10896
|
+
], KRChatbot.prototype, "_showTools", void 0);
|
|
10897
|
+
__decorate([
|
|
10898
|
+
r()
|
|
10899
|
+
], KRChatbot.prototype, "_showToolInputs", void 0);
|
|
10900
|
+
__decorate([
|
|
10901
|
+
r()
|
|
10902
|
+
], KRChatbot.prototype, "_showConfirmationInput", void 0);
|
|
10043
10903
|
__decorate([
|
|
10044
10904
|
e$3('.panel')
|
|
10045
10905
|
], KRChatbot.prototype, "_panelEl", void 0);
|