@kodaris/krubble-app-components 1.0.115 → 1.0.117
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 +71 -4
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +500 -19
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +498 -19
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +278 -32
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +498 -19
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +277 -31
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/krubble-app.umd.js
CHANGED
|
@@ -5854,11 +5854,26 @@
|
|
|
5854
5854
|
*/
|
|
5855
5855
|
this._drawerOpen = false;
|
|
5856
5856
|
/**
|
|
5857
|
-
* Which view the drawer shows — settings or
|
|
5858
|
-
*
|
|
5859
|
-
*
|
|
5857
|
+
* Which view the drawer shows — settings, debug, or a single tool call's full
|
|
5858
|
+
* detail. One drawer holds all three; the header buttons switch settings/debug
|
|
5859
|
+
* and clicking a tool line switches to `toolDetail`. The content/width swap
|
|
5860
|
+
* inside. Never null, so the content stays visible while the drawer slides shut.
|
|
5860
5861
|
*/
|
|
5861
5862
|
this._drawerView = 'settings';
|
|
5863
|
+
/**
|
|
5864
|
+
* The tool call whose full input/output the `toolDetail` drawer view shows —
|
|
5865
|
+
* the clicked `tool_use` message and its paired `tool_result` message, stored
|
|
5866
|
+
* whole. The render reads `.data.*` off them (and formats for display) so we
|
|
5867
|
+
* don't duplicate the message shape here. Transient — set on click, never
|
|
5868
|
+
* persisted (its bytes can be large and the view is a peek). Null when none.
|
|
5869
|
+
*/
|
|
5870
|
+
this._toolDetail = null;
|
|
5871
|
+
/** Which tool-detail block ('input' | 'output') just flashed "Copied" (null = none). */
|
|
5872
|
+
this._toolDetailCopied = null;
|
|
5873
|
+
/** Whether the tool-detail Input section is expanded. Both open on open. */
|
|
5874
|
+
this._toolDetailInputOpen = true;
|
|
5875
|
+
/** Whether the tool-detail Output section is expanded. */
|
|
5876
|
+
this._toolDetailOutputOpen = true;
|
|
5862
5877
|
/** Current width of the drawer in px (user-resizable, persisted). */
|
|
5863
5878
|
this._drawerWidth = 430;
|
|
5864
5879
|
/** True while the user is dragging the drawer's resize handle. */
|
|
@@ -6377,6 +6392,12 @@
|
|
|
6377
6392
|
}
|
|
6378
6393
|
_saveState() {
|
|
6379
6394
|
try {
|
|
6395
|
+
// `toolDetail` is a transient peek, not a saved view — persist only the two
|
|
6396
|
+
// real preference views so a reload never reopens a stale tool.
|
|
6397
|
+
let persistedDrawerView = this._drawerView;
|
|
6398
|
+
if (persistedDrawerView === 'toolDetail') {
|
|
6399
|
+
persistedDrawerView = 'settings';
|
|
6400
|
+
}
|
|
6380
6401
|
const state = {
|
|
6381
6402
|
opened: String(this.opened),
|
|
6382
6403
|
message: this._inputValue,
|
|
@@ -6385,7 +6406,7 @@
|
|
|
6385
6406
|
showToolInputs: String(this._showToolInputs),
|
|
6386
6407
|
showConfirmationInput: String(this._showConfirmationInput),
|
|
6387
6408
|
drawerOpen: String(this._drawerOpen),
|
|
6388
|
-
drawerView:
|
|
6409
|
+
drawerView: persistedDrawerView,
|
|
6389
6410
|
drawerWidth: String(this._drawerWidth),
|
|
6390
6411
|
};
|
|
6391
6412
|
if (this._panelEl) {
|
|
@@ -7433,7 +7454,6 @@
|
|
|
7433
7454
|
}
|
|
7434
7455
|
});
|
|
7435
7456
|
}
|
|
7436
|
-
/** Clear the in-flight confirmation flags (error paths, or once resolved). */
|
|
7437
7457
|
/**
|
|
7438
7458
|
* Capture ambient "where is the user / what are they looking at" context at
|
|
7439
7459
|
* send time, so the AI is grounded without the host passing anything in.
|
|
@@ -7529,6 +7549,162 @@
|
|
|
7529
7549
|
this._drawerOpen = false;
|
|
7530
7550
|
this._saveState();
|
|
7531
7551
|
}
|
|
7552
|
+
/**
|
|
7553
|
+
* Open the drawer to a single tool call's full input/output. Called when the
|
|
7554
|
+
* user clicks a tool-activity line in the transcript. Independent of the
|
|
7555
|
+
* `_showToolInputs` setting — this is the on-demand "show me everything for
|
|
7556
|
+
* THIS call" peek, so it works even when inline inputs are hidden. `result` is
|
|
7557
|
+
* the paired `tool_result` message data (may be absent if the tool hasn't
|
|
7558
|
+
* returned yet).
|
|
7559
|
+
*/
|
|
7560
|
+
_openToolDetail(use) {
|
|
7561
|
+
// Pair the tool_use to the tool_result that immediately follows it (matched by
|
|
7562
|
+
// toolUseId) — the same pairing the transcript render uses for the ⎿ preview.
|
|
7563
|
+
const next = this.messages[this.messages.indexOf(use) + 1];
|
|
7564
|
+
this._toolDetail = {
|
|
7565
|
+
use,
|
|
7566
|
+
result: null,
|
|
7567
|
+
};
|
|
7568
|
+
if (next?.type === 'tool_result' && next.data?.toolUseId === use.data?.toolUseId) {
|
|
7569
|
+
this._toolDetail.result = next;
|
|
7570
|
+
}
|
|
7571
|
+
this._toolDetailCopied = null;
|
|
7572
|
+
this._toolDetailInputOpen = true;
|
|
7573
|
+
this._toolDetailOutputOpen = true;
|
|
7574
|
+
this._drawerView = 'toolDetail';
|
|
7575
|
+
this._drawerOpen = true;
|
|
7576
|
+
this._saveState();
|
|
7577
|
+
// Reset the panel scroll so a newly-opened tool starts at the top, rather
|
|
7578
|
+
// than inheriting the previous tool's scroll position. Wait for the render.
|
|
7579
|
+
this.updateComplete.then(() => {
|
|
7580
|
+
if (this._drawerBodyEl) {
|
|
7581
|
+
this._drawerBodyEl.scrollTop = 0;
|
|
7582
|
+
}
|
|
7583
|
+
});
|
|
7584
|
+
}
|
|
7585
|
+
/** Toggle the tool-detail Input section open/closed. */
|
|
7586
|
+
_handleToggleToolDetailInput() {
|
|
7587
|
+
this._toolDetailInputOpen = !this._toolDetailInputOpen;
|
|
7588
|
+
}
|
|
7589
|
+
/** Toggle the tool-detail Output section open/closed. */
|
|
7590
|
+
_handleToggleToolDetailOutput() {
|
|
7591
|
+
this._toolDetailOutputOpen = !this._toolDetailOutputOpen;
|
|
7592
|
+
}
|
|
7593
|
+
/**
|
|
7594
|
+
* Pretty-print a tool input/result for the detail view. The values arrive as
|
|
7595
|
+
* strings that usually hold JSON; reindent when they do, otherwise show as-is.
|
|
7596
|
+
* Never throws — falls back to the original text.
|
|
7597
|
+
*
|
|
7598
|
+
* A large/overflowing result is stashed server-side and returned as a preview
|
|
7599
|
+
* wrapped in `<<<uuid_BEGIN>>> … <<<uuid_END>>>` markers, followed by a paging
|
|
7600
|
+
* note. Two things make `JSON.parse` unusable here: the marker wrapper isn't
|
|
7601
|
+
* JSON, and the payload between the markers is a truncated SLICE of a bigger
|
|
7602
|
+
* blob (cut off mid-string), so it never parses on its own. So we reindent the
|
|
7603
|
+
* payload STRUCTURALLY (see `_reindentJson`) rather than parse it, and keep the
|
|
7604
|
+
* markers + note exactly where they were so it still reads as a paged response.
|
|
7605
|
+
*/
|
|
7606
|
+
_formatToolJson(value) {
|
|
7607
|
+
if (value === undefined || value === null) {
|
|
7608
|
+
return '';
|
|
7609
|
+
}
|
|
7610
|
+
const raw = String(value);
|
|
7611
|
+
const marker = raw.match(/(<<<[0-9a-f-]+_BEGIN>>>\n?)([\s\S]*?)(\n?<<<[0-9a-f-]+_END>>>)/);
|
|
7612
|
+
if (marker && marker.index != null) {
|
|
7613
|
+
// Splice by index rather than String.replace — the reindented JSON can
|
|
7614
|
+
// contain `$` sequences (e.g. "$dtoRef") that replace() treats as special.
|
|
7615
|
+
const start = marker.index + marker[1].length;
|
|
7616
|
+
const end = start + marker[2].length;
|
|
7617
|
+
return raw.slice(0, start) + this._reindentJson(marker[2]) + raw.slice(end);
|
|
7618
|
+
}
|
|
7619
|
+
return this._reindentJson(raw);
|
|
7620
|
+
}
|
|
7621
|
+
/**
|
|
7622
|
+
* Reindent JSON-ish text for display. Tries a strict parse+stringify first (the
|
|
7623
|
+
* clean path for complete, valid JSON). If that fails — which it does for the
|
|
7624
|
+
* truncated slices returned by the stashed-response pager — it falls back to a
|
|
7625
|
+
* structural walk that inserts newlines/indentation around `{}[],:` while
|
|
7626
|
+
* tracking string state (so braces and commas INSIDE string values, and escaped
|
|
7627
|
+
* quotes, are left alone). This formats even incomplete JSON. Non-JSON text
|
|
7628
|
+
* (no leading `{`/`[`) is returned untouched.
|
|
7629
|
+
*/
|
|
7630
|
+
_reindentJson(text) {
|
|
7631
|
+
const trimmed = text.trim();
|
|
7632
|
+
if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
|
|
7633
|
+
return text;
|
|
7634
|
+
}
|
|
7635
|
+
try {
|
|
7636
|
+
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
7637
|
+
}
|
|
7638
|
+
catch {
|
|
7639
|
+
// Fall through to the tolerant structural reindent below.
|
|
7640
|
+
}
|
|
7641
|
+
let out = '';
|
|
7642
|
+
let depth = 0;
|
|
7643
|
+
let inString = false;
|
|
7644
|
+
let escaped = false;
|
|
7645
|
+
const pad = (level) => '\n' + ' '.repeat(Math.max(0, level));
|
|
7646
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
7647
|
+
const ch = trimmed[i];
|
|
7648
|
+
if (inString) {
|
|
7649
|
+
out += ch;
|
|
7650
|
+
if (escaped) {
|
|
7651
|
+
escaped = false;
|
|
7652
|
+
}
|
|
7653
|
+
else if (ch === '\\') {
|
|
7654
|
+
escaped = true;
|
|
7655
|
+
}
|
|
7656
|
+
else if (ch === '"') {
|
|
7657
|
+
inString = false;
|
|
7658
|
+
}
|
|
7659
|
+
continue;
|
|
7660
|
+
}
|
|
7661
|
+
if (ch === '"') {
|
|
7662
|
+
inString = true;
|
|
7663
|
+
out += ch;
|
|
7664
|
+
}
|
|
7665
|
+
else if (ch === '{' || ch === '[') {
|
|
7666
|
+
depth++;
|
|
7667
|
+
out += ch + pad(depth);
|
|
7668
|
+
}
|
|
7669
|
+
else if (ch === '}' || ch === ']') {
|
|
7670
|
+
depth--;
|
|
7671
|
+
out += pad(depth) + ch;
|
|
7672
|
+
}
|
|
7673
|
+
else if (ch === ',') {
|
|
7674
|
+
out += ch + pad(depth);
|
|
7675
|
+
}
|
|
7676
|
+
else if (ch === ':') {
|
|
7677
|
+
out += ': ';
|
|
7678
|
+
}
|
|
7679
|
+
else if (ch === ' ' || ch === '\n' || ch === '\t' || ch === '\r') ;
|
|
7680
|
+
else {
|
|
7681
|
+
out += ch;
|
|
7682
|
+
}
|
|
7683
|
+
}
|
|
7684
|
+
return out;
|
|
7685
|
+
}
|
|
7686
|
+
/** Copy the tool-detail Input to the clipboard. Stops propagation so the click
|
|
7687
|
+
* doesn't also toggle the (parent) section header. */
|
|
7688
|
+
_handleCopyToolDetailInput(e) {
|
|
7689
|
+
e.stopPropagation();
|
|
7690
|
+
this._copyToolDetail('input', this._formatToolJson(this._toolDetail?.use.data?.toolInput));
|
|
7691
|
+
}
|
|
7692
|
+
/** Copy the tool-detail Output to the clipboard (see input handler). */
|
|
7693
|
+
_handleCopyToolDetailOutput(e) {
|
|
7694
|
+
e.stopPropagation();
|
|
7695
|
+
this._copyToolDetail('output', this._formatToolJson(this._toolDetail?.result?.data?.toolResultString));
|
|
7696
|
+
}
|
|
7697
|
+
/** Write one tool-detail block to the clipboard and flash "Copied" on it. */
|
|
7698
|
+
_copyToolDetail(block, content) {
|
|
7699
|
+
navigator.clipboard.writeText(content).then(() => {
|
|
7700
|
+
this._toolDetailCopied = block;
|
|
7701
|
+
setTimeout(() => {
|
|
7702
|
+
if (this._toolDetailCopied === block) {
|
|
7703
|
+
this._toolDetailCopied = null;
|
|
7704
|
+
}
|
|
7705
|
+
}, 2000);
|
|
7706
|
+
}).catch(() => { });
|
|
7707
|
+
}
|
|
7532
7708
|
_handleDrawerResizeMouseDown(e) {
|
|
7533
7709
|
this._isDrawerResizing = true;
|
|
7534
7710
|
this._drawerResizeStartX = e.clientX;
|
|
@@ -7641,10 +7817,9 @@
|
|
|
7641
7817
|
// deliberately never show). Adopt it as the header label and pass it
|
|
7642
7818
|
// up so consumers can update their own chrome, e.g. a breadcrumb.
|
|
7643
7819
|
if (data.data?.title) {
|
|
7644
|
-
|
|
7645
|
-
this.label = title;
|
|
7820
|
+
this.label = data.data.title;
|
|
7646
7821
|
this.dispatchEvent(new CustomEvent('conversation-title', {
|
|
7647
|
-
detail: { title },
|
|
7822
|
+
detail: { title: data.data.title },
|
|
7648
7823
|
bubbles: true,
|
|
7649
7824
|
composed: true,
|
|
7650
7825
|
}));
|
|
@@ -8136,6 +8311,16 @@
|
|
|
8136
8311
|
const isDebug = this._drawerView === 'debug'
|
|
8137
8312
|
&& this.variant === 'inline'
|
|
8138
8313
|
&& this._aiFrameworkConfig.debugTelemetryEnabled === true;
|
|
8314
|
+
let title = 'Settings';
|
|
8315
|
+
let body = this._renderSettingsContent();
|
|
8316
|
+
if (isDebug) {
|
|
8317
|
+
title = 'Debug';
|
|
8318
|
+
body = this._renderDebugContent();
|
|
8319
|
+
}
|
|
8320
|
+
else if (this._drawerView === 'toolDetail' && this._toolDetail) {
|
|
8321
|
+
title = this._toolDetail.use.data?.displayName || this._toolDetail.use.data?.toolName || 'Tool';
|
|
8322
|
+
body = this._renderToolDetailContent();
|
|
8323
|
+
}
|
|
8139
8324
|
return b `
|
|
8140
8325
|
<div
|
|
8141
8326
|
class=${e({
|
|
@@ -8150,7 +8335,7 @@
|
|
|
8150
8335
|
@mousedown=${this._handleDrawerResizeMouseDown}
|
|
8151
8336
|
></div>
|
|
8152
8337
|
<div class="drawer__header">
|
|
8153
|
-
<span class="drawer__title">${
|
|
8338
|
+
<span class="drawer__title">${title}</span>
|
|
8154
8339
|
<button
|
|
8155
8340
|
class="header-action"
|
|
8156
8341
|
@click=${this._handleCloseDrawer}
|
|
@@ -8173,7 +8358,7 @@
|
|
|
8173
8358
|
</button>
|
|
8174
8359
|
</div>
|
|
8175
8360
|
<div class="drawer__body">
|
|
8176
|
-
${
|
|
8361
|
+
${body}
|
|
8177
8362
|
</div>
|
|
8178
8363
|
</div>
|
|
8179
8364
|
`;
|
|
@@ -8205,6 +8390,129 @@
|
|
|
8205
8390
|
></kr-checkbox>
|
|
8206
8391
|
</div>
|
|
8207
8392
|
</div>
|
|
8393
|
+
`;
|
|
8394
|
+
}
|
|
8395
|
+
/**
|
|
8396
|
+
* The Tool detail view's body — the full input and output for one tool call,
|
|
8397
|
+
* pretty-printed in scrollable code blocks with a per-block copy button. This
|
|
8398
|
+
* is the "occasionally see everything" path: the transcript stays terse, and
|
|
8399
|
+
* clicking a tool line drops its complete input/output here, uncapped (the
|
|
8400
|
+
* blocks scroll rather than truncating).
|
|
8401
|
+
*/
|
|
8402
|
+
_renderToolDetailContent() {
|
|
8403
|
+
return b `
|
|
8404
|
+
<div class="tool-detail">
|
|
8405
|
+
<div class="tool-detail__section">
|
|
8406
|
+
<button
|
|
8407
|
+
type="button"
|
|
8408
|
+
class="tool-detail__section-head"
|
|
8409
|
+
aria-expanded=${this._toolDetailInputOpen}
|
|
8410
|
+
@click=${this._handleToggleToolDetailInput}
|
|
8411
|
+
>
|
|
8412
|
+
<span class=${e({
|
|
8413
|
+
'tool-detail__caret': true,
|
|
8414
|
+
'tool-detail__caret--open': this._toolDetailInputOpen,
|
|
8415
|
+
})}>›</span>
|
|
8416
|
+
<span class="tool-detail__section-title">Input</span>
|
|
8417
|
+
<span
|
|
8418
|
+
class="tool-detail__copy"
|
|
8419
|
+
role="button"
|
|
8420
|
+
@click=${this._handleCopyToolDetailInput}
|
|
8421
|
+
>
|
|
8422
|
+
${this._toolDetailCopied === 'input'
|
|
8423
|
+
? b `
|
|
8424
|
+
<svg
|
|
8425
|
+
viewBox="0 0 24 24"
|
|
8426
|
+
fill="none"
|
|
8427
|
+
stroke="currentColor"
|
|
8428
|
+
stroke-width="1.5"
|
|
8429
|
+
stroke-linecap="round"
|
|
8430
|
+
stroke-linejoin="round"
|
|
8431
|
+
>
|
|
8432
|
+
<path d="M20 6 9 17l-5-5" />
|
|
8433
|
+
</svg>
|
|
8434
|
+
`
|
|
8435
|
+
: b `
|
|
8436
|
+
<svg
|
|
8437
|
+
viewBox="0 0 24 24"
|
|
8438
|
+
fill="none"
|
|
8439
|
+
stroke="currentColor"
|
|
8440
|
+
stroke-width="1.5"
|
|
8441
|
+
stroke-linecap="round"
|
|
8442
|
+
stroke-linejoin="round"
|
|
8443
|
+
>
|
|
8444
|
+
<rect
|
|
8445
|
+
x="9"
|
|
8446
|
+
y="9"
|
|
8447
|
+
width="13"
|
|
8448
|
+
height="13"
|
|
8449
|
+
rx="2"
|
|
8450
|
+
/>
|
|
8451
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
8452
|
+
</svg>
|
|
8453
|
+
`}
|
|
8454
|
+
</span>
|
|
8455
|
+
</button>
|
|
8456
|
+
${this._toolDetailInputOpen
|
|
8457
|
+
? b `<pre class="tool-detail__code">${this._formatToolJson(this._toolDetail.use.data?.toolInput) || '(no input)'}</pre>`
|
|
8458
|
+
: A}
|
|
8459
|
+
</div>
|
|
8460
|
+
<div class="tool-detail__section">
|
|
8461
|
+
<button
|
|
8462
|
+
type="button"
|
|
8463
|
+
class="tool-detail__section-head"
|
|
8464
|
+
aria-expanded=${this._toolDetailOutputOpen}
|
|
8465
|
+
@click=${this._handleToggleToolDetailOutput}
|
|
8466
|
+
>
|
|
8467
|
+
<span class=${e({
|
|
8468
|
+
'tool-detail__caret': true,
|
|
8469
|
+
'tool-detail__caret--open': this._toolDetailOutputOpen,
|
|
8470
|
+
})}>›</span>
|
|
8471
|
+
<span class="tool-detail__section-title">Output</span>
|
|
8472
|
+
<span
|
|
8473
|
+
class="tool-detail__copy"
|
|
8474
|
+
role="button"
|
|
8475
|
+
@click=${this._handleCopyToolDetailOutput}
|
|
8476
|
+
>
|
|
8477
|
+
${this._toolDetailCopied === 'output'
|
|
8478
|
+
? b `
|
|
8479
|
+
<svg
|
|
8480
|
+
viewBox="0 0 24 24"
|
|
8481
|
+
fill="none"
|
|
8482
|
+
stroke="currentColor"
|
|
8483
|
+
stroke-width="1.5"
|
|
8484
|
+
stroke-linecap="round"
|
|
8485
|
+
stroke-linejoin="round"
|
|
8486
|
+
>
|
|
8487
|
+
<path d="M20 6 9 17l-5-5" />
|
|
8488
|
+
</svg>
|
|
8489
|
+
`
|
|
8490
|
+
: b `
|
|
8491
|
+
<svg
|
|
8492
|
+
viewBox="0 0 24 24"
|
|
8493
|
+
fill="none"
|
|
8494
|
+
stroke="currentColor"
|
|
8495
|
+
stroke-width="1.5"
|
|
8496
|
+
stroke-linecap="round"
|
|
8497
|
+
stroke-linejoin="round"
|
|
8498
|
+
>
|
|
8499
|
+
<rect
|
|
8500
|
+
x="9"
|
|
8501
|
+
y="9"
|
|
8502
|
+
width="13"
|
|
8503
|
+
height="13"
|
|
8504
|
+
rx="2"
|
|
8505
|
+
/>
|
|
8506
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
8507
|
+
</svg>
|
|
8508
|
+
`}
|
|
8509
|
+
</span>
|
|
8510
|
+
</button>
|
|
8511
|
+
${this._toolDetailOutputOpen
|
|
8512
|
+
? b `<pre class="tool-detail__code">${this._formatToolJson(this._toolDetail.result?.data?.toolResultString) || '(no output yet)'}</pre>`
|
|
8513
|
+
: A}
|
|
8514
|
+
</div>
|
|
8515
|
+
</div>
|
|
8208
8516
|
`;
|
|
8209
8517
|
}
|
|
8210
8518
|
/** The Debug view's body — telemetry scopes and the per-iteration table. */
|
|
@@ -8550,9 +8858,9 @@
|
|
|
8550
8858
|
let resultPreview = '';
|
|
8551
8859
|
let resultRemainder = 0;
|
|
8552
8860
|
if (msg.type === 'tool_use') {
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
result =
|
|
8861
|
+
if (this.messages[index + 1]?.type === 'tool_result'
|
|
8862
|
+
&& this.messages[index + 1].data?.toolUseId === msg.data?.toolUseId) {
|
|
8863
|
+
result = this.messages[index + 1].data;
|
|
8556
8864
|
}
|
|
8557
8865
|
if (this._showToolInputs && msg.data?.toolInput) {
|
|
8558
8866
|
inputSummary = String(msg.data.toolInput).replace(/\s+/g, ' ').trim();
|
|
@@ -8568,8 +8876,17 @@
|
|
|
8568
8876
|
catch {
|
|
8569
8877
|
formatted = String(result.toolResultString);
|
|
8570
8878
|
}
|
|
8879
|
+
// Cap the inline preview by BOTH lines and characters. Line-only
|
|
8880
|
+
// truncation blows up on results whose first line is huge (e.g. a
|
|
8881
|
+
// minified-JSON blob or a stashed-response marker with a 30k-char
|
|
8882
|
+
// opening line) — 3 lines then still dumps tens of thousands of
|
|
8883
|
+
// chars into the transcript. Char-cap first (content-blind, like
|
|
8884
|
+
// the backend chokepoint), then keep at most 3 lines of that.
|
|
8571
8885
|
const lines = formatted.split('\n');
|
|
8572
8886
|
resultPreview = lines.slice(0, 3).join('\n');
|
|
8887
|
+
if (resultPreview.length > 200) {
|
|
8888
|
+
resultPreview = `${resultPreview.slice(0, 200)}…`;
|
|
8889
|
+
}
|
|
8573
8890
|
resultRemainder = Math.max(0, lines.length - 3);
|
|
8574
8891
|
}
|
|
8575
8892
|
}
|
|
@@ -8598,24 +8915,32 @@
|
|
|
8598
8915
|
})}>
|
|
8599
8916
|
${msg.type === 'tool_use' ? b `
|
|
8600
8917
|
<div class="tool-activity-block">
|
|
8601
|
-
<
|
|
8918
|
+
<button
|
|
8919
|
+
type="button"
|
|
8920
|
+
class=${e({
|
|
8602
8921
|
'tool-activity': true,
|
|
8603
8922
|
'tool-activity--error': result?.toolResultStatus === 'error',
|
|
8604
|
-
})}
|
|
8923
|
+
})}
|
|
8924
|
+
@click=${() => this._openToolDetail(msg)}
|
|
8925
|
+
>
|
|
8605
8926
|
<span class="tool-activity__chevron">›</span>
|
|
8606
8927
|
<span class="tool-activity__label">${msg.data?.displayName || msg.data?.toolName || 'Tool'}</span>
|
|
8607
8928
|
${inputSummary ? b `<span class="tool-activity__input">(${inputSummary})</span>` : A}
|
|
8608
|
-
</
|
|
8929
|
+
</button>
|
|
8609
8930
|
${this._showToolInputs && result?.toolResultString ? b `
|
|
8610
|
-
<
|
|
8931
|
+
<button
|
|
8932
|
+
type="button"
|
|
8933
|
+
class=${e({
|
|
8611
8934
|
'tool-activity__result': true,
|
|
8612
8935
|
'tool-activity__result--error': result.toolResultStatus === 'error',
|
|
8613
|
-
})}
|
|
8936
|
+
})}
|
|
8937
|
+
@click=${() => this._openToolDetail(msg)}
|
|
8938
|
+
>
|
|
8614
8939
|
<span class="tool-activity__connector">⎿</span>
|
|
8615
8940
|
<pre class="tool-activity__result-body">${resultPreview}${resultRemainder > 0
|
|
8616
8941
|
? b `\n<span class="tool-activity__more">… +${resultRemainder} lines</span>`
|
|
8617
8942
|
: A}</pre>
|
|
8618
|
-
</
|
|
8943
|
+
</button>
|
|
8619
8944
|
` : A}
|
|
8620
8945
|
</div>
|
|
8621
8946
|
` : A}
|
|
@@ -9220,6 +9545,102 @@
|
|
|
9220
9545
|
padding: 6px 0;
|
|
9221
9546
|
}
|
|
9222
9547
|
|
|
9548
|
+
/* Tool detail view — the full input/output for one tool call. */
|
|
9549
|
+
.tool-detail {
|
|
9550
|
+
display: flex;
|
|
9551
|
+
flex-direction: column;
|
|
9552
|
+
gap: 24px;
|
|
9553
|
+
padding: 16px 20px;
|
|
9554
|
+
}
|
|
9555
|
+
|
|
9556
|
+
.tool-detail__section {
|
|
9557
|
+
display: flex;
|
|
9558
|
+
flex-direction: column;
|
|
9559
|
+
gap: 8px;
|
|
9560
|
+
min-width: 0;
|
|
9561
|
+
}
|
|
9562
|
+
|
|
9563
|
+
/* Clickable section header — toggles the code block below. Button chrome is
|
|
9564
|
+
stripped so it reads as a heading row. */
|
|
9565
|
+
.tool-detail__section-head {
|
|
9566
|
+
display: flex;
|
|
9567
|
+
align-items: center;
|
|
9568
|
+
gap: 8px;
|
|
9569
|
+
appearance: none;
|
|
9570
|
+
background: none;
|
|
9571
|
+
border: none;
|
|
9572
|
+
width: 100%;
|
|
9573
|
+
font-family: inherit;
|
|
9574
|
+
text-align: left;
|
|
9575
|
+
cursor: pointer;
|
|
9576
|
+
border-radius: 4px;
|
|
9577
|
+
}
|
|
9578
|
+
|
|
9579
|
+
.tool-detail__section-head:focus-visible {
|
|
9580
|
+
outline: 2px solid var(--kr-chatbot-primary);
|
|
9581
|
+
outline-offset: 2px;
|
|
9582
|
+
}
|
|
9583
|
+
|
|
9584
|
+
/* Disclosure caret — the same "›" used on tool lines; rotates down when open. */
|
|
9585
|
+
.tool-detail__caret {
|
|
9586
|
+
flex-shrink: 0;
|
|
9587
|
+
font-size: 16px;
|
|
9588
|
+
color: rgba(0, 0, 0, 0.55);
|
|
9589
|
+
transition: transform 0.15s ease;
|
|
9590
|
+
}
|
|
9591
|
+
|
|
9592
|
+
.tool-detail__caret--open {
|
|
9593
|
+
transform: rotate(90deg);
|
|
9594
|
+
}
|
|
9595
|
+
|
|
9596
|
+
.tool-detail__section-title {
|
|
9597
|
+
font-size: 12px;
|
|
9598
|
+
font-weight: 700;
|
|
9599
|
+
text-transform: uppercase;
|
|
9600
|
+
letter-spacing: 0.06em;
|
|
9601
|
+
color: #000000;
|
|
9602
|
+
}
|
|
9603
|
+
|
|
9604
|
+
/* Copy icon-button — same treatment as the per-message copy button. */
|
|
9605
|
+
.tool-detail__copy {
|
|
9606
|
+
margin-left: auto;
|
|
9607
|
+
display: inline-flex;
|
|
9608
|
+
align-items: center;
|
|
9609
|
+
justify-content: center;
|
|
9610
|
+
width: 26px;
|
|
9611
|
+
height: 26px;
|
|
9612
|
+
padding: 0;
|
|
9613
|
+
background: none;
|
|
9614
|
+
border: none;
|
|
9615
|
+
border-radius: 6px;
|
|
9616
|
+
color: #000000;
|
|
9617
|
+
cursor: pointer;
|
|
9618
|
+
transition: background-color 0.15s, color 0.15s;
|
|
9619
|
+
}
|
|
9620
|
+
|
|
9621
|
+
.tool-detail__copy:hover {
|
|
9622
|
+
background: rgba(0, 0, 0, 0.06);
|
|
9623
|
+
}
|
|
9624
|
+
|
|
9625
|
+
.tool-detail__copy svg {
|
|
9626
|
+
width: 14px;
|
|
9627
|
+
height: 14px;
|
|
9628
|
+
}
|
|
9629
|
+
|
|
9630
|
+
/* The code block — pretty-printed JSON, shown in full (no inner scroll). The
|
|
9631
|
+
drawer body is the only scroll container, so long inputs/outputs extend the
|
|
9632
|
+
page and you scroll the panel, not a nested box. */
|
|
9633
|
+
.tool-detail__code {
|
|
9634
|
+
margin: 0;
|
|
9635
|
+
padding: 0 18px;
|
|
9636
|
+
font-family: 'SF Mono', 'Fira Code', Monaco, monospace;
|
|
9637
|
+
font-size: 12px;
|
|
9638
|
+
line-height: 1.5;
|
|
9639
|
+
color: #000000;
|
|
9640
|
+
white-space: pre-wrap;
|
|
9641
|
+
word-break: break-word;
|
|
9642
|
+
}
|
|
9643
|
+
|
|
9223
9644
|
/* Messages */
|
|
9224
9645
|
/* Full-width scroll container — keeps the scrollbar at the panel edge. */
|
|
9225
9646
|
.messages {
|
|
@@ -9316,6 +9737,9 @@
|
|
|
9316
9737
|
/* Tool-activity line — "the assistant ran X" row in the transcript. Same text
|
|
9317
9738
|
style as any other message (black, 14px); a leading › chevron marks it as a
|
|
9318
9739
|
step (in the same text style, not an icon). */
|
|
9740
|
+
/* The tool-activity line and its result preview are <button>s (they open the
|
|
9741
|
+
tool-detail drawer on click) — strip the native button chrome so they read
|
|
9742
|
+
as plain transcript text, then re-add the layout. */
|
|
9319
9743
|
.tool-activity {
|
|
9320
9744
|
align-self: flex-start;
|
|
9321
9745
|
display: flex;
|
|
@@ -9325,6 +9749,22 @@
|
|
|
9325
9749
|
color: #000000;
|
|
9326
9750
|
/* Keep the chevron + name + input on a single line; input truncates if long. */
|
|
9327
9751
|
white-space: nowrap;
|
|
9752
|
+
/* Button reset */
|
|
9753
|
+
appearance: none;
|
|
9754
|
+
background: none;
|
|
9755
|
+
border: none;
|
|
9756
|
+
margin: 0;
|
|
9757
|
+
padding: 2px 4px;
|
|
9758
|
+
font-family: inherit;
|
|
9759
|
+
text-align: left;
|
|
9760
|
+
cursor: pointer;
|
|
9761
|
+
border-radius: 4px;
|
|
9762
|
+
max-width: 100%;
|
|
9763
|
+
}
|
|
9764
|
+
|
|
9765
|
+
.tool-activity:focus-visible {
|
|
9766
|
+
outline: 2px solid var(--kr-chatbot-primary);
|
|
9767
|
+
outline-offset: 1px;
|
|
9328
9768
|
}
|
|
9329
9769
|
|
|
9330
9770
|
.tool-activity--error {
|
|
@@ -9360,6 +9800,16 @@
|
|
|
9360
9800
|
gap: 4px;
|
|
9361
9801
|
}
|
|
9362
9802
|
|
|
9803
|
+
/* One hover for the whole tool block: hovering anywhere over the header or its
|
|
9804
|
+
result underlines the tool name, input, and result together as a single
|
|
9805
|
+
link, rather than lighting up header and result separately. */
|
|
9806
|
+
.tool-activity-block:hover .tool-activity__label,
|
|
9807
|
+
.tool-activity-block:hover .tool-activity__input,
|
|
9808
|
+
.tool-activity-block:hover .tool-activity__result-body {
|
|
9809
|
+
text-decoration: underline;
|
|
9810
|
+
text-decoration-color: rgba(0, 0, 0, 0.25);
|
|
9811
|
+
}
|
|
9812
|
+
|
|
9363
9813
|
/* The result block indented under the activity line with a ⎿ tree connector,
|
|
9364
9814
|
like Claude CLI's tool output preview. Uses the normal message text style
|
|
9365
9815
|
(14px, black) — only the ⎿ connector is muted. */
|
|
@@ -9375,6 +9825,20 @@
|
|
|
9375
9825
|
margin-left: 6px;
|
|
9376
9826
|
font-size: 14px;
|
|
9377
9827
|
color: #000000;
|
|
9828
|
+
/* Button reset — this preview row is also a click target for the detail view. */
|
|
9829
|
+
appearance: none;
|
|
9830
|
+
background: none;
|
|
9831
|
+
border: none;
|
|
9832
|
+
padding: 0;
|
|
9833
|
+
font-family: inherit;
|
|
9834
|
+
text-align: left;
|
|
9835
|
+
cursor: pointer;
|
|
9836
|
+
}
|
|
9837
|
+
|
|
9838
|
+
.tool-activity__result:focus-visible {
|
|
9839
|
+
outline: 2px solid var(--kr-chatbot-primary);
|
|
9840
|
+
outline-offset: 1px;
|
|
9841
|
+
border-radius: 4px;
|
|
9378
9842
|
}
|
|
9379
9843
|
|
|
9380
9844
|
.tool-activity__result--error {
|
|
@@ -10840,6 +11304,18 @@
|
|
|
10840
11304
|
__decorate([
|
|
10841
11305
|
r()
|
|
10842
11306
|
], exports.Chatbot.prototype, "_drawerView", void 0);
|
|
11307
|
+
__decorate([
|
|
11308
|
+
r()
|
|
11309
|
+
], exports.Chatbot.prototype, "_toolDetail", void 0);
|
|
11310
|
+
__decorate([
|
|
11311
|
+
r()
|
|
11312
|
+
], exports.Chatbot.prototype, "_toolDetailCopied", void 0);
|
|
11313
|
+
__decorate([
|
|
11314
|
+
r()
|
|
11315
|
+
], exports.Chatbot.prototype, "_toolDetailInputOpen", void 0);
|
|
11316
|
+
__decorate([
|
|
11317
|
+
r()
|
|
11318
|
+
], exports.Chatbot.prototype, "_toolDetailOutputOpen", void 0);
|
|
10843
11319
|
__decorate([
|
|
10844
11320
|
r()
|
|
10845
11321
|
], exports.Chatbot.prototype, "_drawerWidth", void 0);
|
|
@@ -10921,6 +11397,9 @@
|
|
|
10921
11397
|
__decorate([
|
|
10922
11398
|
e$3('.skill-menu')
|
|
10923
11399
|
], exports.Chatbot.prototype, "_skillMenuEl", void 0);
|
|
11400
|
+
__decorate([
|
|
11401
|
+
e$3('.drawer__body')
|
|
11402
|
+
], exports.Chatbot.prototype, "_drawerBodyEl", void 0);
|
|
10924
11403
|
exports.Chatbot = __decorate([
|
|
10925
11404
|
t$2('kr-chatbot')
|
|
10926
11405
|
], exports.Chatbot);
|