mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-31 01:37:28 +08:00
Merge pull request #11146 from Kilo-Org/feat/gitkilo
Polish VS Code tool call previews
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Polish chat tool-call previews for shell, search, and background-process output.
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:129ea4c06998d8418bdb8edc8e61e094849835edb3f40cf6e1ddc34568168e61
|
||||
size 52396
|
||||
oid sha256:b9d8327c530c9d071b5eb07ebbf48441ea8d64b8a4cd7201665a8fa037ca82ed
|
||||
size 50349
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a943ba7617204c720540078a5c741511de9be1e0534d96188e18ddf6e942972b
|
||||
size 41054
|
||||
oid sha256:7b0cb661ae199182341c0034495298cb939ed5487ed8d1dcd41bb558eda8d0e3
|
||||
size 45709
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:95f83279f09c34994294cef4a33c9007d6a7b7e84f553558edf6bdac62711216
|
||||
size 37558
|
||||
oid sha256:3f104b9ceef97c0dd5f5522745fb573189ba562a60b1f73196565e1b5a4b7dc6
|
||||
size 37225
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f419ed4b9435f9a01c94dca80ef5b252cc108ccbb0e4d7ba978acf0fbf02bbe4
|
||||
size 32635
|
||||
oid sha256:4cf97da43aa741ac4227026dbb4f16a3a2e44a8394a4f0ab321a0938a5d5fb6e
|
||||
size 32313
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0ac3951c6ce87f25da24a146dd5420528392720e081688646b67f33632bb6152
|
||||
size 28951
|
||||
oid sha256:bb4ff7b2909397b635c29603e93956ef16f12ffb1c3ac77f9f4c5c3fd7a07389
|
||||
size 28876
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:381b72a63cc256f520e671460431f9dc9950d27fd727d0779d1a6b558c7593cd
|
||||
size 18195
|
||||
oid sha256:e74a00c414bf627c41e0841380dcd359cc7398111caa0545aafb7681cf76ea7e
|
||||
size 18128
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5a0460abb79716b44ec7e9eb42c8d364c2e6737fc79042ca6b715dffe33b1a74
|
||||
size 24548
|
||||
oid sha256:6e4e9484d65b9dfab86a970db0242366566c2f345f13cab6fb523cc1a625873b
|
||||
size 24479
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7cabfff215cef485ec913b9a082aaac844275e506e88ba0fab112d95ff6de329
|
||||
size 18200
|
||||
oid sha256:fc84c90016236e0c08dea8989c2124092d167d1d94136c33484ece3a54525250
|
||||
size 18881
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:18d394d726ebee312e54db5d45f55cca3a1728d2b41f9f73bb9b610096bf53e0
|
||||
size 41004
|
||||
oid sha256:209002988d276858f6e6adae0083ff84c9a1b853ef396bbe4c8121e678121065
|
||||
size 44104
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b5584b7b6d9cc3b80082e1d6fb24eda43d084fb437cf10c15fa3117e99ad9e89
|
||||
size 4300
|
||||
oid sha256:222790e6c32346cf6b6cc52a0859ee01d364b52a1ef8b9f385a6e9798adfccfb
|
||||
size 4256
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:067a5c757dfa32d27955dd634b8dea52edef2141111a5eecee0161df36c0ed8b
|
||||
size 7061
|
||||
oid sha256:acccbf1fa9a76076f324031ed4d7e9ac317059a127c238cf9eaa9e4569dd38b2
|
||||
size 7023
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c583144b11ac9608ec755e9a683dced8ae6ffae9a6f3833e1c0eec6b664f358e
|
||||
size 7720
|
||||
oid sha256:516db3d182497211aa32a32d8b33de309967be93e56a348f0397ad8eaef57eec
|
||||
size 7014
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f0af006973871a81b4c0ae2fc6c98c88151655afbd9e63d77e970c18db861355
|
||||
size 26371
|
||||
oid sha256:c3ebab71919a7bafe609933ba8909f22e68171d3546473cca7ad5e3d859787ef
|
||||
size 30655
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6c05d543611aff7bf10ef406de42844fa1aa825275e380c6ded05d03170aa9b0
|
||||
size 8057
|
||||
oid sha256:76b359a64ad4dede420eb4669f973f80f87be63b5d0a1dfe36829f0c19698381
|
||||
size 7486
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b1ebe718b7747f0b0db029396c728ae859f388f11cf206bb020fcd7027f3b0a0
|
||||
size 5046
|
||||
oid sha256:cba6f84ec6138fc59be088cdf84d2ebe57a55f8514df58eed693a84969ff7a90
|
||||
size 4794
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9b47f6adfc72399cb1535be3a69661b027eec3068880aba35821d5240e0c3492
|
||||
size 16739
|
||||
oid sha256:1adee4d2de92696f86183a38fabd14dc38be1b818100092f2014017e4de12b8c
|
||||
size 16710
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5a308ad1c07d7af681670853d5e0f40b01ee88ad4e863dd5b84c0ab511757cdf
|
||||
size 6134
|
||||
oid sha256:56c7610136e6a015393f5e19b239ff446ec78ad34c75d3dda1e6d6870964f501
|
||||
size 6340
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d4d7ac9d6322d2cf673a6353cb07cf9979ad55351769ba9d90eed386170f6c42
|
||||
size 40854
|
||||
oid sha256:146055492419facf8997d352f47ee80e9f205b60cc5345cf266683bc10518a8f
|
||||
size 37718
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1d21dc25072b1c52cacaf0a91369f45e89f43ccc181d643e6b65030b829e5b1e
|
||||
size 8591
|
||||
oid sha256:0539bd8e7296f0a57af343b48b311f35292dd3b8eef257fb713cef457ea63a74
|
||||
size 7150
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a3fb9398991f40c60fb16d73b05ba4dd85af3475928df65917fefb9af1c4aaf1
|
||||
size 7836
|
||||
oid sha256:9233fec056782bc365cfbbeca8533f63a287e8363455826821d6c4c2fb99d841
|
||||
size 7619
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a8576fc52a9122900230f545f9b0886280251b3c636a7c2416432730c2844398
|
||||
size 8727
|
||||
oid sha256:8296de6d4c8196db8566f622037cd2f1559bcc4523294fec2e1d84602edf23e8
|
||||
size 8469
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:342c39c1ff3d02cef37e076209ddd1d3e1fe0f9eb207097b408d2078c0f05c58
|
||||
size 181042
|
||||
@@ -35,18 +35,29 @@
|
||||
|
||||
[data-slot="basic-tool-tool-subtitle"] {
|
||||
font-size: var(--kilo-font-size-12);
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-title"] {
|
||||
font-size: var(--kilo-font-size-14);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
font-weight: var(--font-weight-regular);
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="message-part-title-text"] {
|
||||
font-size: var(--kilo-font-size-14);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
font-weight: var(--font-weight-regular);
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-arg"] {
|
||||
font-size: var(--kilo-font-size-12);
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="message-part-title-filename"] {
|
||||
font-size: var(--kilo-font-size-12);
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="message-part-meta-line"] {
|
||||
@@ -58,6 +69,7 @@
|
||||
min-width: 0;
|
||||
flex-shrink: 1;
|
||||
font-size: var(--kilo-font-size-12);
|
||||
color: var(--text-weak);
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
@@ -108,37 +120,93 @@ html[data-theme="kilo-vscode"] [data-component="tool-trigger"] {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
color: var(--icon-base);
|
||||
color: var(--text-weak);
|
||||
|
||||
[data-component="icon"] {
|
||||
display: flex;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Card styling for tool calls in the VS Code sidebar theme */
|
||||
html[data-theme="kilo-vscode"] [data-component="tool-part-wrapper"][data-part-type="tool"] {
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-weak-base, var(--vscode-panel-border));
|
||||
overflow: hidden;
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
overflow: visible;
|
||||
|
||||
/* Header trigger — add bottom border when the collapsible is open */
|
||||
[data-component="collapsible"].tool-collapsible {
|
||||
gap: 0px;
|
||||
gap: 0;
|
||||
border-radius: 0;
|
||||
|
||||
&:has([data-slot="collapsible-trigger"][aria-expanded="true"]) {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
[data-slot="collapsible-trigger"] {
|
||||
padding: 0 8px;
|
||||
height: 36px;
|
||||
background-color: var(--surface-inset-base, var(--vscode-sideBar-background));
|
||||
margin-inline: -6px;
|
||||
width: calc(100% + 12px);
|
||||
padding: 2px 6px;
|
||||
min-height: 24px;
|
||||
height: auto;
|
||||
background-color: transparent;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--surface-inset-base-hover, var(--vscode-list-hoverBackground));
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-trigger-content"] {
|
||||
align-self: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-info-structured"],
|
||||
[data-slot="basic-tool-tool-info-main"] {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-title"]
|
||||
+ :is(
|
||||
[data-slot="basic-tool-tool-subtitle"],
|
||||
[data-slot="basic-tool-tool-arg"],
|
||||
[data-slot="message-part-meta-line"],
|
||||
[data-slot="webfetch-meta"],
|
||||
[data-component="shell-submessage"]
|
||||
)::before {
|
||||
content: "·";
|
||||
margin-right: 6px;
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="message-part-title-text"]
|
||||
+ :is([data-slot="message-part-meta-line"], [data-slot="basic-tool-tool-subtitle"])::before {
|
||||
content: "·";
|
||||
margin-right: 6px;
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-title"],
|
||||
[data-slot="basic-tool-tool-subtitle"],
|
||||
[data-slot="basic-tool-tool-arg"],
|
||||
[data-slot="message-part-title-text"],
|
||||
[data-slot="message-part-title-filename"],
|
||||
[data-slot="message-part-directory-inline"],
|
||||
[data-slot="message-part-meta-line"] {
|
||||
line-height: var(--kilo-font-size-16);
|
||||
}
|
||||
|
||||
[data-slot="collapsible-trigger"][aria-expanded="true"] {
|
||||
border-bottom: 1px solid var(--border-weak-base, var(--vscode-panel-border));
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
[data-slot="collapsible-trigger"]:has([data-slot="collapsible-arrow"]) {
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 0.15s ease,
|
||||
color 0.15s ease;
|
||||
}
|
||||
|
||||
/* Arrow always visible (no fade-on-hover) */
|
||||
@@ -146,13 +214,32 @@ html[data-theme="kilo-vscode"] [data-component="tool-part-wrapper"][data-part-ty
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Arrow: DOWN when collapsed, UP when expanded (matches question-dock toggle) */
|
||||
/* Arrow: RIGHT when collapsed, DOWN when expanded. */
|
||||
[data-slot="collapsible-arrow-icon"] {
|
||||
transform: translateZ(0) rotate(0deg);
|
||||
transform: translateZ(0) rotate(-90deg);
|
||||
}
|
||||
|
||||
[data-slot="collapsible-trigger"][aria-expanded="true"] [data-slot="collapsible-arrow-icon"] {
|
||||
transform: translateZ(0) rotate(180deg);
|
||||
transform: translateZ(0) rotate(0deg);
|
||||
}
|
||||
|
||||
[data-slot="collapsible-trigger"]:has([data-slot="collapsible-arrow"]):hover {
|
||||
background-color: var(--vscode-list-hoverBackground, var(--surface-raised-base-hover));
|
||||
|
||||
[data-slot="basic-tool-tool-title"],
|
||||
[data-slot="basic-tool-tool-subtitle"],
|
||||
[data-slot="basic-tool-tool-arg"],
|
||||
[data-slot="message-part-title-text"],
|
||||
[data-slot="message-part-title-filename"],
|
||||
[data-slot="message-part-directory-inline"],
|
||||
[data-slot="message-part-meta-line"] {
|
||||
color: var(--vscode-list-hoverForeground, var(--vscode-foreground, var(--text-strong)));
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-icon"],
|
||||
[data-slot="collapsible-arrow-icon"] {
|
||||
color: var(--vscode-list-hoverForeground, var(--vscode-foreground, var(--icon-base)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +261,100 @@ html[data-theme="kilo-vscode"] [data-component="tool-part-wrapper"][data-part-ty
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
[data-slot="mcp-section-label"] + [data-component="tool-output"] {
|
||||
margin-inline: -6px;
|
||||
width: calc(100% + 12px);
|
||||
max-height: 240px;
|
||||
padding: 8px 12px 10px;
|
||||
border: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface-tool-output-base);
|
||||
color: var(--text-weak);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
line-height: var(--kilo-font-size-16);
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-component="markdown"] {
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
[data-component="markdown"] :is(a, code, span, [data-highlight="file"]) {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="context-tool-group-trigger"] {
|
||||
min-height: 20px;
|
||||
|
||||
[data-slot="context-tool-group-title"] {
|
||||
gap: 6px;
|
||||
font-size: var(--kilo-font-size-12);
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--kilo-font-size-16);
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="context-tool-group-summary"] {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="context-tool-group-summary"]::before {
|
||||
content: "·";
|
||||
margin-right: 6px;
|
||||
color: var(--text-weak);
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="context-tool-expanded-list"] {
|
||||
max-height: 180px;
|
||||
overflow-y: auto;
|
||||
padding: 4px 0;
|
||||
border: 1px solid var(--border-weak-base);
|
||||
background: color-mix(in srgb, var(--vscode-terminal-background, var(--vscode-panel-background)) 92%, black 8%);
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="context-tool-expanded-row"],
|
||||
[data-component="context-tool-rolling-row"] {
|
||||
display: grid;
|
||||
grid-template-columns: max-content minmax(0, 1fr);
|
||||
align-items: baseline;
|
||||
column-gap: 8px;
|
||||
min-height: 20px;
|
||||
font-size: var(--kilo-font-size-12);
|
||||
line-height: var(--kilo-font-size-16);
|
||||
}
|
||||
|
||||
[data-component="context-tool-expanded-row"] {
|
||||
padding: 2px 12px;
|
||||
}
|
||||
|
||||
[data-slot="context-tool-expanded-action"],
|
||||
[data-slot="context-tool-rolling-action"] {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="context-tool-expanded-detail"],
|
||||
[data-slot="context-tool-rolling-detail"] {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--text-weak);
|
||||
font-family: var(--font-family-mono);
|
||||
font-feature-settings: var(--font-family-mono--font-feature-settings);
|
||||
}
|
||||
|
||||
/* Expandable tool output content */
|
||||
[data-component="tool-output"] {
|
||||
padding: 8px 12px;
|
||||
@@ -186,4 +367,45 @@ html[data-theme="kilo-vscode"] [data-component="tool-part-wrapper"][data-part-ty
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="tool-output"][data-variant="preview"] {
|
||||
margin-inline: -6px;
|
||||
width: calc(100% + 12px);
|
||||
max-height: 240px;
|
||||
padding: 8px 12px 10px;
|
||||
border: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface-tool-output-base);
|
||||
color: var(--text-weak);
|
||||
font-family: var(--font-family-mono);
|
||||
font-feature-settings: var(--font-family-mono--font-feature-settings);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
line-height: var(--line-height-large);
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: normal;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-component="markdown"] {
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
line-height: inherit;
|
||||
white-space: inherit;
|
||||
overflow-wrap: inherit;
|
||||
}
|
||||
|
||||
[data-component="markdown"] :is(a, code, span, [data-highlight="file"]) {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
[data-component="markdown"] > *,
|
||||
[data-component="markdown"] p,
|
||||
[data-component="markdown"] pre {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* VS Code theme: tool-collapsible arrows point DOWN (collapsed) and UP (expanded), never right */
|
||||
/* VS Code theme: tool-collapsible arrows point RIGHT when collapsed and DOWN when expanded. */
|
||||
html[data-theme="kilo-vscode"] [data-component="collapsible"].tool-collapsible {
|
||||
[data-slot="collapsible-arrow-icon"] {
|
||||
transform: translateZ(0) rotate(0deg);
|
||||
transform: translateZ(0) rotate(-90deg);
|
||||
}
|
||||
|
||||
[data-slot="collapsible-trigger"][aria-expanded="true"] [data-slot="collapsible-arrow-icon"] {
|
||||
transform: translateZ(0) rotate(180deg);
|
||||
transform: translateZ(0) rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,30 +210,72 @@
|
||||
|
||||
html[data-theme="kilo-vscode"] [data-component="bash-output"] {
|
||||
border-radius: 0;
|
||||
background: var(--vscode-terminal-background, var(--vscode-panel-background));
|
||||
border: 0;
|
||||
background: transparent;
|
||||
overflow: visible;
|
||||
|
||||
[data-slot="bash-pre"] code {
|
||||
color: var(--vscode-terminal-foreground, var(--vscode-editor-foreground));
|
||||
[data-slot="bash-terminal"] {
|
||||
border: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface-tool-output-base);
|
||||
}
|
||||
|
||||
[data-slot="bash-divider"] {
|
||||
background: var(--vscode-panel-border, var(--border-weak-base));
|
||||
[data-slot="bash-pre"] code,
|
||||
.shiki code {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
}
|
||||
|
||||
/* Bash tool: section layout (command + output) */
|
||||
/* Bash tool: static terminal preview with separate command/output surfaces. */
|
||||
[data-component="bash-output"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-inline: -6px;
|
||||
width: calc(100% + 12px);
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
[data-component="bash-output"] [data-slot="bash-terminal"] {
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--border-weak-base);
|
||||
background: var(--surface-raised-base);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[data-component="bash-output"] [data-slot="bash-section"] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[data-component="bash-output"] [data-slot="bash-section"][data-kind="command"] {
|
||||
display: grid;
|
||||
grid-template-columns: max-content minmax(0, 1fr);
|
||||
align-items: start;
|
||||
column-gap: 8px;
|
||||
padding: 8px 12px 7px;
|
||||
}
|
||||
|
||||
[data-component="bash-output"] [data-slot="bash-section"][data-kind="output"] {
|
||||
padding: 8px 12px 10px;
|
||||
}
|
||||
|
||||
[data-component="bash-output"] [data-slot="bash-prompt"] {
|
||||
color: var(--text-weak, var(--vscode-descriptionForeground));
|
||||
font-family: var(--font-family-mono);
|
||||
font-feature-settings: var(--font-family-mono--font-feature-settings);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
line-height: var(--line-height-large);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-component="bash-output"] [data-slot="bash-section-code"] {
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 240px;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="bash-output"] [data-slot="bash-section-actions"] {
|
||||
@@ -254,25 +296,29 @@ html[data-theme="kilo-vscode"] [data-component="bash-output"] {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* Edge-to-edge divider (matches MCP tool divider) */
|
||||
[data-component="bash-output"] [data-slot="bash-divider"] {
|
||||
height: 1px;
|
||||
background: var(--border-weak-base);
|
||||
[data-component="bash-output"] [data-slot="bash-pre"] {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
[data-component="bash-output"] .shiki {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* Shiki syntax-highlighted shell output */
|
||||
[data-component="bash-output"] .shiki,
|
||||
[data-component="shell-expanded-output"] .shiki {
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
[data-component="bash-output"] [data-slot="bash-pre"] code,
|
||||
[data-component="bash-output"] .shiki code,
|
||||
[data-component="shell-expanded-output"] .shiki code {
|
||||
font-family: var(--font-family-mono);
|
||||
font-feature-settings: var(--font-family-mono--font-feature-settings);
|
||||
font-size: var(--font-size-small);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
line-height: var(--line-height-large);
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
@@ -380,6 +426,32 @@ html[data-theme="kilo-vscode"] [data-component="bash-output"] {
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme="kilo-vscode"] [data-component="todos"] {
|
||||
margin-inline: -6px;
|
||||
width: calc(100% + 12px);
|
||||
padding: 8px 12px 10px;
|
||||
border: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface-tool-output-base);
|
||||
color: var(--text-weak);
|
||||
|
||||
[data-slot="checkbox-checkbox-label"] {
|
||||
color: var(--text-weak);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
line-height: var(--kilo-font-size-16);
|
||||
}
|
||||
|
||||
[data-slot="message-part-todo-hidden"] {
|
||||
color: var(--text-weaker);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
line-height: var(--kilo-font-size-16);
|
||||
}
|
||||
|
||||
[data-slot="message-part-todo-content"][data-changed="changed"] {
|
||||
color: var(--text-base);
|
||||
}
|
||||
}
|
||||
|
||||
/* User message copy button: collapse wrapper when idle, show on hover.
|
||||
* The wrapper must be tall enough to fully contain the button (20px)
|
||||
* so that hovering the button keeps [data-component="user-message"]
|
||||
@@ -514,6 +586,100 @@ html[data-theme="kilo-vscode"] [data-component="bash-output"] {
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme="kilo-vscode"] [data-component="text-part"] {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
html[data-theme="kilo-vscode"] [data-component="reasoning-part"] {
|
||||
border-left: 0;
|
||||
border-radius: 0;
|
||||
overflow: visible;
|
||||
|
||||
[data-component="collapsible"].tool-collapsible {
|
||||
gap: 0;
|
||||
border-radius: 0;
|
||||
|
||||
&:has([data-slot="collapsible-trigger"][aria-expanded="true"]) {
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="collapsible-trigger"] {
|
||||
margin-inline: -6px;
|
||||
width: calc(100% + 12px);
|
||||
min-height: 24px;
|
||||
height: auto;
|
||||
padding: 2px 6px;
|
||||
background: transparent;
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
background-color 0.15s ease,
|
||||
color 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--vscode-list-hoverBackground, var(--surface-raised-base-hover));
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="collapsible-arrow"] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[data-slot="reasoning-header"] {
|
||||
gap: 6px;
|
||||
color: var(--text-weak);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--kilo-font-size-16);
|
||||
|
||||
[data-component="icon"] {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="reasoning-label"],
|
||||
[data-slot="reasoning-title"] {
|
||||
color: var(--text-weak);
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--kilo-font-size-16);
|
||||
}
|
||||
|
||||
[data-slot="reasoning-label"] + [data-slot="reasoning-title"]::before {
|
||||
content: "·";
|
||||
margin-right: 6px;
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="collapsible-trigger"]:hover {
|
||||
[data-slot="reasoning-label"],
|
||||
[data-slot="reasoning-title"] {
|
||||
color: var(--vscode-list-hoverForeground, var(--vscode-foreground, var(--text-strong)));
|
||||
}
|
||||
|
||||
[data-slot="reasoning-header"] [data-component="icon"],
|
||||
[data-slot="collapsible-arrow-icon"] {
|
||||
color: var(--vscode-list-hoverForeground, var(--vscode-foreground, var(--icon-base)));
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="collapsible-content"] {
|
||||
margin-inline: -6px;
|
||||
width: calc(100% + 12px);
|
||||
}
|
||||
|
||||
[data-slot="reasoning-content"] {
|
||||
padding: 8px 12px 10px;
|
||||
border: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface-tool-output-base);
|
||||
|
||||
[data-component="markdown"] {
|
||||
font-size: var(--kilo-font-size-12);
|
||||
line-height: 160%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes reasoning-pulse {
|
||||
0%,
|
||||
100% {
|
||||
|
||||
@@ -1852,7 +1852,7 @@ ToolRegistry.register({
|
||||
>
|
||||
<Show when={props.output}>
|
||||
{(output) => (
|
||||
<div data-component="tool-output" data-scrollable>
|
||||
<div data-component="tool-output" data-variant="preview" data-scrollable>
|
||||
<Markdown text={output()} />
|
||||
</div>
|
||||
)}
|
||||
@@ -1883,7 +1883,7 @@ ToolRegistry.register({
|
||||
>
|
||||
<Show when={props.output}>
|
||||
{(output) => (
|
||||
<div data-component="tool-output" data-scrollable>
|
||||
<div data-component="tool-output" data-variant="preview" data-scrollable>
|
||||
<Markdown text={output()} />
|
||||
</div>
|
||||
)}
|
||||
@@ -1917,7 +1917,7 @@ ToolRegistry.register({
|
||||
>
|
||||
<Show when={props.output}>
|
||||
{(output) => (
|
||||
<div data-component="tool-output" data-scrollable>
|
||||
<div data-component="tool-output" data-variant="preview" data-scrollable>
|
||||
<Markdown text={output()} />
|
||||
</div>
|
||||
)}
|
||||
@@ -2162,34 +2162,36 @@ function BashHighlightedOutput(props: { cmd: string; output: string; outputPath?
|
||||
return (
|
||||
<div data-component="bash-output">
|
||||
<Show when={props.cmd}>
|
||||
<div data-slot="mcp-section-label">{i18n.t("ui.messagePart.shell.command")}</div>
|
||||
<div data-slot="bash-section">
|
||||
<div data-slot="bash-section-code" ref={cmdRef} />
|
||||
<div data-slot="bash-section-actions">
|
||||
<BashCopyButton value={() => props.cmd} label={i18n.t("ui.message.copy")} />
|
||||
<div data-slot="bash-terminal" data-kind="command">
|
||||
<div data-slot="bash-section" data-kind="command">
|
||||
<span data-slot="bash-prompt" aria-hidden="true">
|
||||
$
|
||||
</span>
|
||||
<div data-slot="bash-section-code" data-scrollable ref={cmdRef} />
|
||||
<div data-slot="bash-section-actions">
|
||||
<BashCopyButton value={() => props.cmd} label={i18n.t("ui.message.copy")} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={props.cmd && props.output}>
|
||||
<div data-slot="bash-divider" />
|
||||
</Show>
|
||||
<Show when={props.output}>
|
||||
<div data-slot="mcp-section-label">{i18n.t("ui.messagePart.shell.output")}</div>
|
||||
<div data-slot="bash-section">
|
||||
<div data-slot="bash-section-code" data-scrollable ref={outRef} />
|
||||
<div data-slot="bash-section-actions">
|
||||
<Show when={data.openContent || (props.outputPath && data.openFile)}>
|
||||
<Tooltip value={i18n.t("ui.messagePart.openInEditor")} placement="bottom" gutter={4}>
|
||||
<IconButton
|
||||
icon="square-arrow-top-right"
|
||||
size="small"
|
||||
variant="ghost"
|
||||
onClick={openInEditor}
|
||||
aria-label={i18n.t("ui.messagePart.openInEditor")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Show>
|
||||
<BashCopyButton value={() => props.output} label={i18n.t("ui.message.copy")} />
|
||||
<div data-slot="bash-terminal" data-kind="output">
|
||||
<div data-slot="bash-section" data-kind="output">
|
||||
<div data-slot="bash-section-code" data-scrollable ref={outRef} />
|
||||
<div data-slot="bash-section-actions">
|
||||
<Show when={data.openContent || (props.outputPath && data.openFile)}>
|
||||
<Tooltip value={i18n.t("ui.messagePart.openInEditor")} placement="bottom" gutter={4}>
|
||||
<IconButton
|
||||
icon="square-arrow-top-right"
|
||||
size="small"
|
||||
variant="ghost"
|
||||
onClick={openInEditor}
|
||||
aria-label={i18n.t("ui.messagePart.openInEditor")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Show>
|
||||
<BashCopyButton value={() => props.output} label={i18n.t("ui.message.copy")} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
@@ -2229,11 +2231,11 @@ ToolRegistry.register({
|
||||
<BasicTool
|
||||
{...props}
|
||||
icon="console"
|
||||
animated
|
||||
hasDetails
|
||||
defaultOpen={props.defaultOpen ?? true}
|
||||
onOpenChange={setOpen}
|
||||
allowPendingToggle
|
||||
animated
|
||||
trigger={
|
||||
<div data-slot="basic-tool-tool-info-structured">
|
||||
<div data-slot="basic-tool-tool-info-main">
|
||||
|
||||
@@ -48,6 +48,7 @@ html[data-theme="kilo-vscode"] {
|
||||
--surface-weak: var(--vscode-input-background, var(--vscode-list-hoverBackground));
|
||||
--surface-weaker: var(--vscode-editor-inactiveSelectionBackground);
|
||||
--surface-strong: var(--vscode-editorWidget-background);
|
||||
--surface-tool-output-base: color-mix(in srgb, var(--vscode-sideBar-background, var(--surface-base)) 64%, black 36%);
|
||||
|
||||
--surface-brand-base: var(--vscode-button-background);
|
||||
--surface-brand-hover: var(--vscode-button-hoverBackground);
|
||||
@@ -338,3 +339,8 @@ html[data-theme="kilo-vscode"] {
|
||||
--avatar-text-cyan: var(--vscode-charts-blue);
|
||||
--avatar-text-lime: var(--vscode-charts-green);
|
||||
}
|
||||
|
||||
body.vscode-light,
|
||||
body.vscode-high-contrast-light {
|
||||
--surface-tool-output-base: color-mix(in srgb, var(--vscode-sideBar-background, var(--surface-base)) 96%, black 4%);
|
||||
}
|
||||
|
||||
@@ -102,6 +102,22 @@ describe("webview font-size architecture", () => {
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
it("uses scalable line heights in polished tool previews", () => {
|
||||
const files = [
|
||||
path.join(REPO, "packages/kilo-ui/src/components/basic-tool.css"),
|
||||
path.join(REPO, "packages/kilo-ui/src/components/message-part.css"),
|
||||
]
|
||||
const violations = files.flatMap((file) => {
|
||||
const src = stripComments(fs.readFileSync(file, "utf-8"))
|
||||
return Array.from(
|
||||
src.matchAll(/line-height\s*:\s*\d+(?:\.\d+)?px\b/g),
|
||||
(match) => `${rel(file)}:${line(src, match.index ?? 0)}`,
|
||||
)
|
||||
})
|
||||
|
||||
expect(violations).toEqual([])
|
||||
})
|
||||
|
||||
it("injects and live-broadcasts the webview font-size setting to all webview providers", () => {
|
||||
const util = fs.readFileSync(path.join(ROOT, "src/utils.ts"), "utf-8")
|
||||
expect(util, "buildWebviewHtml must seed webview font tokens before app code runs").toContain("getWebviewFontSize")
|
||||
|
||||
@@ -23,6 +23,13 @@ const BASIC_TOOL_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/components/bas
|
||||
const DATA_CONTEXT_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/context/data.tsx")
|
||||
const MESSAGE_PART_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/components/message-part.tsx")
|
||||
const KILO_MESSAGE_PART_FILE = path.join(MONOREPO_ROOT, "packages/kilo-ui/src/components/message-part.tsx")
|
||||
const KILO_MESSAGE_PART_CSS_FILE = path.join(MONOREPO_ROOT, "packages/kilo-ui/src/components/message-part.css")
|
||||
const SHELL_ROLLING_FILE = path.join(MONOREPO_ROOT, "packages/kilo-ui/src/components/shell-rolling-results.tsx")
|
||||
const ASSISTANT_MESSAGE_FILE = path.join(
|
||||
MONOREPO_ROOT,
|
||||
"packages/kilo-vscode/webview-ui/src/components/chat/AssistantMessage.tsx",
|
||||
)
|
||||
const CHAT_LAYOUT_FILE = path.join(MONOREPO_ROOT, "packages/kilo-vscode/webview-ui/src/styles/chat-layout.css")
|
||||
|
||||
function check(code: string): { ok: boolean; output: string } {
|
||||
const result = Bun.spawnSync(["bun", "--conditions=browser", "-e", code], {
|
||||
@@ -185,7 +192,7 @@ describe("Write and apply_patch patch rendering contracts (source)", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("Bash tool syntax highlighting and section labels (source)", () => {
|
||||
describe("Bash tool static terminal preview (source)", () => {
|
||||
const src = fs.readFileSync(KILO_MESSAGE_PART_FILE, "utf-8")
|
||||
const block =
|
||||
src.match(/ToolRegistry\.register\(\{\s*name:\s*"bash"[\s\S]*?(?=ToolRegistry\.register\(|$)/)?.[0] ?? ""
|
||||
@@ -194,25 +201,38 @@ describe("Bash tool syntax highlighting and section labels (source)", () => {
|
||||
expect(block).toContain("BashHighlightedOutput")
|
||||
})
|
||||
|
||||
it("BashHighlightedOutput uses shellscript grammar for commands without $ prefix", () => {
|
||||
// The command should be highlighted as shellscript, but the $ prompt must
|
||||
// NOT be inside the highlighted code (it breaks Shiki's parse context)
|
||||
expect(src).toMatch(/data-lang="shellscript">\$\{escapeHtml\(cmd\)\}/)
|
||||
expect(src).not.toMatch(/data-lang="shellscript">\$\s/)
|
||||
it("animates expanded bash details", () => {
|
||||
expect(block).toMatch(/allowPendingToggle\s+animated\s+trigger=/)
|
||||
})
|
||||
|
||||
it("BashHighlightedOutput uses log grammar for output", () => {
|
||||
expect(src).toMatch(/data-lang="log"/)
|
||||
it("BashHighlightedOutput syntax highlights the command next to the prompt", () => {
|
||||
expect(src).toContain('data-slot="bash-terminal" data-kind="command"')
|
||||
expect(src).toContain('data-slot="bash-prompt"')
|
||||
expect(src).toContain('data-slot="bash-section-code" data-scrollable ref={cmdRef}')
|
||||
expect(src).toContain('data-lang="shellscript"')
|
||||
expect(src).toContain("escapeHtml(cmd)")
|
||||
})
|
||||
|
||||
it("BashHighlightedOutput renders section labels matching MCP tool pattern", () => {
|
||||
// Must use the same data-slot as MCP tools for consistent styling
|
||||
expect(src).toMatch(/data-slot="mcp-section-label".*shell\.command/)
|
||||
expect(src).toMatch(/data-slot="mcp-section-label".*shell\.output/)
|
||||
it("BashHighlightedOutput syntax highlights log output", () => {
|
||||
expect(src).toContain('data-slot="bash-terminal" data-kind="output"')
|
||||
expect(src).toContain('data-slot="bash-section-code" data-scrollable ref={outRef}')
|
||||
expect(src).toContain('data-lang="log"')
|
||||
expect(src).toContain("escapeHtml(out)")
|
||||
})
|
||||
|
||||
it("BashHighlightedOutput has edge-to-edge divider between sections", () => {
|
||||
expect(src).toContain('data-slot="bash-divider"')
|
||||
it("BashHighlightedOutput highlights only while expanded", () => {
|
||||
expect(src).toContain("if (!props.active) return")
|
||||
expect(block).toContain("active={open()}")
|
||||
})
|
||||
|
||||
it("BashHighlightedOutput keeps command and output in separate terminal containers", () => {
|
||||
const slots = src.match(/data-slot="bash-terminal"/g) ?? []
|
||||
expect(slots).toHaveLength(2)
|
||||
})
|
||||
|
||||
it("BashHighlightedOutput does not render shell section labels or a divider", () => {
|
||||
expect(src).not.toMatch(/data-slot="mcp-section-label".*shell\./)
|
||||
expect(src).not.toContain('data-slot="bash-divider"')
|
||||
})
|
||||
|
||||
it("BashHighlightedOutput supports openContent for opening output in editor", () => {
|
||||
@@ -232,6 +252,23 @@ describe("Bash tool syntax highlighting and section labels (source)", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("Expanded tool motion and typography (source)", () => {
|
||||
it("animates completed rolling shell details", () => {
|
||||
const src = fs.readFileSync(SHELL_ROLLING_FILE, "utf-8")
|
||||
expect(src).toContain("useCollapsible({")
|
||||
expect(src).toContain("content: () => contentRef")
|
||||
expect(src).toContain("body: () => bodyRef")
|
||||
})
|
||||
|
||||
it("uses the assistant markdown line-height ratio for reasoning output", () => {
|
||||
const css = fs.readFileSync(KILO_MESSAGE_PART_CSS_FILE, "utf-8")
|
||||
const block = css.match(
|
||||
/html\[data-theme="kilo-vscode"\] \[data-component="reasoning-part"\][\s\S]*?(?=@keyframes reasoning-pulse)/,
|
||||
)?.[0]
|
||||
expect(block).toMatch(/\[data-component="markdown"\]\s*\{[^}]*line-height:\s*160%;/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("HighlightedText @mention regex fallback and click handler (source)", () => {
|
||||
const src = fs.readFileSync(KILO_MESSAGE_PART_FILE, "utf-8")
|
||||
|
||||
@@ -255,13 +292,47 @@ describe("HighlightedText @mention regex fallback and click handler (source)", (
|
||||
expect(src).toMatch(/segment\.text\.replace\(\/\^@\//)
|
||||
})
|
||||
|
||||
it("escapeHtml is imported from shared util, not duplicated", () => {
|
||||
expect(src).toMatch(/import.*escapeHtml.*from.*util\/escape-html/)
|
||||
// Must NOT contain a local function definition
|
||||
it("does not duplicate HTML escaping helpers", () => {
|
||||
expect(src).not.toMatch(/function escapeHtml/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("AssistantMessage visible row contract (source)", () => {
|
||||
const src = fs.readFileSync(ASSISTANT_MESSAGE_FILE, "utf-8")
|
||||
|
||||
it("filters suppressed tools that have no visible renderer", () => {
|
||||
expect(src).toContain('state.status === "completed" && !!ToolRegistry.render(tool)')
|
||||
})
|
||||
|
||||
it("filters pending questions until their dock request exists", () => {
|
||||
expect(src).toContain('part.state.status !== "pending" && part.state.status !== "running"')
|
||||
expect(src).toContain('matchToolRequest(part, "question", session.questions())')
|
||||
})
|
||||
|
||||
it("filters completed synthetic text and redaction-only reasoning", () => {
|
||||
expect(src).toContain('part.type === "text" && part.synthetic && props.message.time.completed')
|
||||
expect(src).toContain('.text?.replace("[REDACTED]", "").trim()')
|
||||
})
|
||||
|
||||
it("uses the plan exit card only when plan metadata is renderable", () => {
|
||||
expect(src).toContain("if (!planExitInfo(part)) return")
|
||||
})
|
||||
})
|
||||
|
||||
describe("Assistant transcript spacing contract (source)", () => {
|
||||
const css = fs.readFileSync(CHAT_LAYOUT_FILE, "utf-8")
|
||||
|
||||
it("uses a 6px gap between virtualized assistant rows", () => {
|
||||
expect(css).toMatch(/\.vscode-session-turn\[data-row="assistant"\]\s*\{\s*padding-bottom: 6px;/)
|
||||
})
|
||||
|
||||
it("removes spacing from assistant rows without visible content", () => {
|
||||
expect(css).toMatch(
|
||||
/\.vscode-session-turn\[data-row="assistant"\]:has\(> \.vscode-session-turn-assistant:empty\)\s*\{\s*padding-bottom: 0;/,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("BasicTool export contract (runtime)", () => {
|
||||
it("BasicTool and GenericTool are exported from basic-tool", () => {
|
||||
const result = check(`
|
||||
|
||||
@@ -89,14 +89,16 @@ function isRenderable(part: SDKPart): boolean {
|
||||
const tool = (part as SDKPart & { tool: string }).tool
|
||||
const state = (part as SDKPart & { state: { status: string } }).state
|
||||
if (UPSTREAM_SUPPRESSED_TOOLS.has(tool)) {
|
||||
// Show todo parts only when completed (permissions are now in the dock)
|
||||
return state.status === "completed"
|
||||
// Show completed todo parts only when kilo-ui provides a visible renderer.
|
||||
return state.status === "completed" && !!ToolRegistry.render(tool)
|
||||
}
|
||||
// Always render question tool parts — active ones get the inline QuestionDock
|
||||
return true
|
||||
}
|
||||
if (part.type === "text") return !snapshotProgress(part) && !!(part as SDKPart & { text: string }).text?.trim()
|
||||
if (part.type === "reasoning") return !!(part as SDKPart & { text: string }).text?.trim()
|
||||
if (part.type === "reasoning") {
|
||||
return !!(part as SDKPart & { text: string }).text?.replace("[REDACTED]", "").trim()
|
||||
}
|
||||
return !!PART_MAPPING[part.type]
|
||||
}
|
||||
|
||||
@@ -188,7 +190,13 @@ export const AssistantMessage: Component<AssistantMessageProps> = (props) => {
|
||||
const parts = createMemo(() => {
|
||||
const stored = props.parts ?? data.store.part?.[props.message.id]
|
||||
if (!stored) return []
|
||||
return (stored as SDKPart[]).filter((part) => isRenderable(part))
|
||||
return (stored as SDKPart[]).filter((part) => {
|
||||
if (!isRenderable(part)) return false
|
||||
if (part.type === "text" && part.synthetic && props.message.time.completed) return false
|
||||
if (part.type !== "tool" || part.tool !== "question") return true
|
||||
if (part.state.status !== "pending" && part.state.status !== "running") return true
|
||||
return !!matchToolRequest(part, "question", session.questions())
|
||||
})
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -213,11 +221,8 @@ export const AssistantMessage: Component<AssistantMessageProps> = (props) => {
|
||||
return part
|
||||
})
|
||||
const planExit = createMemo(() => {
|
||||
if (part.type !== "tool") return
|
||||
const tp = part as unknown as ToolPart
|
||||
if (tp.tool !== "plan_exit") return
|
||||
if (tp.state?.status !== "completed") return
|
||||
return tp
|
||||
if (!planExitInfo(part)) return
|
||||
return part as unknown as ToolPart
|
||||
})
|
||||
|
||||
return (
|
||||
|
||||
@@ -124,25 +124,29 @@ function BackgroundProcessTool(props: ToolProps) {
|
||||
defaultOpen={expanded(props.status, props.defaultOpen)}
|
||||
allowPendingToggle
|
||||
>
|
||||
<Show when={rows().length > 0}>
|
||||
<div data-component="background-process-fields">
|
||||
<For each={rows()}>
|
||||
{(row) => (
|
||||
<div data-slot="background-process-field">
|
||||
<span data-slot="background-process-label">{row[0]}</span>
|
||||
<span data-slot="background-process-value">{row[1]}</span>
|
||||
<Show when={rows().length > 0 || data().output}>
|
||||
<div data-component="background-process-details">
|
||||
<Show when={rows().length > 0}>
|
||||
<div data-component="background-process-fields">
|
||||
<For each={rows()}>
|
||||
{(row) => (
|
||||
<div data-slot="background-process-field">
|
||||
<span data-slot="background-process-label">{row[0]}</span>
|
||||
<span data-slot="background-process-value">{row[1]}</span>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={data().output}>
|
||||
{(value) => (
|
||||
<div data-component="tool-output" data-variant="preview" data-scrollable>
|
||||
<pre data-slot="background-process-output">{value()}</pre>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={data().output}>
|
||||
{(value) => (
|
||||
<div data-component="tool-output" data-scrollable>
|
||||
<pre data-slot="background-process-output">{value()}</pre>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { ProviderContext } from "../context/provider"
|
||||
import { flattenModels, findModel as _findModel } from "../context/provider-utils"
|
||||
import { ConfigProvider, ConfigContext } from "../context/config"
|
||||
import { DisplayProvider } from "../context/display"
|
||||
import { DataProvider } from "@kilocode/kilo-ui/context/data"
|
||||
import { DataProvider, type OpenDiffFn, type OpenFileFn } from "@kilocode/kilo-ui/context/data"
|
||||
import { DiffComponentProvider } from "@kilocode/kilo-ui/context/diff"
|
||||
import { CodeComponentProvider } from "@kilocode/kilo-ui/context/code"
|
||||
import { FileComponentProvider } from "@kilocode/kilo-ui/context/file"
|
||||
@@ -180,7 +180,6 @@ export function mockSessionValue(overrides?: {
|
||||
setCurrentSessionID: noop,
|
||||
sessions: () => [],
|
||||
status: () => status,
|
||||
submitting: () => false,
|
||||
statusInfo: () => ({ type: status }),
|
||||
closeReason: () => overrides?.closeReason,
|
||||
statusText: () => (status === "idle" ? undefined : "Thinking…"),
|
||||
@@ -188,6 +187,9 @@ export function mockSessionValue(overrides?: {
|
||||
loading: () => false,
|
||||
loadingOlderMessages: () => false,
|
||||
hasOlderMessages: () => false,
|
||||
submitting: () => false,
|
||||
draftSessionID: () => undefined,
|
||||
setDraftSessionID: noop,
|
||||
messageMutation: () => undefined,
|
||||
messages: () => [],
|
||||
visibleMessages: () => [],
|
||||
@@ -196,6 +198,8 @@ export function mockSessionValue(overrides?: {
|
||||
allParts: () => ({}),
|
||||
allStatusMap: () => ({}),
|
||||
getParts: () => [],
|
||||
getSessionToolParts: () => [],
|
||||
getSessionToolCount: () => 0,
|
||||
isErrorHidden: () => false,
|
||||
hydrateParts: noop,
|
||||
todos: () => [],
|
||||
@@ -256,6 +260,7 @@ export function mockSessionValue(overrides?: {
|
||||
deleteSession: noop,
|
||||
renameSession: noop,
|
||||
syncSession: noop,
|
||||
exportSessionTranscript: noop,
|
||||
cloudPreviewId: () => null,
|
||||
selectCloudSession: noop,
|
||||
}
|
||||
@@ -280,6 +285,8 @@ interface StoryProvidersProps {
|
||||
onConfigChange?: (config: Config) => void
|
||||
onGlobalConfigChange?: (config: Config) => void
|
||||
onProjectConfigChange?: (config: Config) => void
|
||||
onOpenDiff?: OpenDiffFn
|
||||
onOpenFile?: OpenFileFn
|
||||
kiloAuth?: boolean
|
||||
/** When true, renders children without the default 12px padding wrapper */
|
||||
noPadding?: boolean
|
||||
@@ -403,7 +410,12 @@ export const StoryProviders: ParentComponent<StoryProvidersProps> = (props) => {
|
||||
<SessionContext.Provider value={session as any}>
|
||||
<IndexingProvider>
|
||||
<KiloEmbeddingModelsProvider>
|
||||
<DataProvider data={data()} directory="/project/">
|
||||
<DataProvider
|
||||
data={data()}
|
||||
directory="/project/"
|
||||
onOpenDiff={props.onOpenDiff}
|
||||
onOpenFile={props.onOpenFile}
|
||||
>
|
||||
<DiffComponentProvider component={Diff}>
|
||||
<CodeComponentProvider component={Code}>
|
||||
<FileComponentProvider component={File}>
|
||||
|
||||
@@ -0,0 +1,467 @@
|
||||
/** @jsxImportSource solid-js */
|
||||
import type { Meta, StoryObj } from "storybook-solidjs-vite"
|
||||
import type {
|
||||
AssistantMessage as SDKAssistantMessage,
|
||||
Part as SDKPart,
|
||||
ReasoningPart,
|
||||
TextPart,
|
||||
ToolPart,
|
||||
} from "@kilocode/sdk/v2"
|
||||
import { StoryProviders, defaultMockData } from "./StoryProviders"
|
||||
import { AssistantMessage } from "../components/chat/AssistantMessage"
|
||||
import { registerVscodeToolOverrides } from "../components/chat/VscodeToolOverrides"
|
||||
import type { QuestionRequest, SuggestionRequest } from "../types/messages"
|
||||
import { writeToolOpen } from "../../../../kilo-ui/src/components/tool-open-state"
|
||||
|
||||
registerVscodeToolOverrides()
|
||||
|
||||
const SID = "tool-call-lab-session"
|
||||
const MID = "tool-call-lab-message"
|
||||
const stamp = Date.now()
|
||||
|
||||
const base: SDKAssistantMessage = {
|
||||
id: MID,
|
||||
sessionID: SID,
|
||||
role: "assistant",
|
||||
parentID: "tool-call-lab-user-message",
|
||||
time: { created: stamp - 9000, completed: stamp - 1000 },
|
||||
modelID: "anthropic/claude-sonnet-4-6",
|
||||
providerID: "kilo",
|
||||
mode: "default",
|
||||
agent: "default",
|
||||
path: { cwd: "/project", root: "/project" },
|
||||
cost: 0.0021,
|
||||
tokens: { total: 742, input: 386, output: 356, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
}
|
||||
|
||||
const hits = [
|
||||
'packages/kilo-ui/src/components/message-part.tsx:1847: <div data-component="tool-output">',
|
||||
'packages/kilo-ui/src/components/basic-tool.css:250: [data-component="tool-output"]',
|
||||
"packages/kilo-vscode/webview-ui/src/components/chat/VscodeToolOverrides.tsx:141: background process output",
|
||||
].join("\n")
|
||||
|
||||
const proc = [
|
||||
"pid: 48122",
|
||||
"status: running",
|
||||
"cwd: /project",
|
||||
"command: bun run --cwd packages/kilo-vscode storybook",
|
||||
"last_output:",
|
||||
"Storybook 9.0.18 for solid-vite started",
|
||||
"Local: http://localhost:6007/",
|
||||
].join("\n")
|
||||
|
||||
function completed(input: Record<string, unknown>, title: string, value: string): ToolPart["state"] {
|
||||
return {
|
||||
status: "completed",
|
||||
input,
|
||||
output: value,
|
||||
title,
|
||||
metadata: {},
|
||||
time: { start: stamp - 5000, end: stamp - 4400 },
|
||||
}
|
||||
}
|
||||
|
||||
function failed(input: Record<string, unknown>, value: string): ToolPart["state"] {
|
||||
return {
|
||||
status: "error",
|
||||
input,
|
||||
error: value,
|
||||
metadata: {},
|
||||
time: { start: stamp - 3600, end: stamp - 3400 },
|
||||
}
|
||||
}
|
||||
|
||||
function tool(id: string, call: string, name: string, state: ToolPart["state"]): ToolPart {
|
||||
return {
|
||||
id,
|
||||
sessionID: SID,
|
||||
messageID: MID,
|
||||
type: "tool",
|
||||
callID: call,
|
||||
tool: name,
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
function text(id: string, value: string): TextPart {
|
||||
return {
|
||||
id,
|
||||
sessionID: SID,
|
||||
messageID: MID,
|
||||
type: "text",
|
||||
text: value,
|
||||
}
|
||||
}
|
||||
|
||||
function reasoning(id: string, value: string): ReasoningPart {
|
||||
return {
|
||||
id,
|
||||
sessionID: SID,
|
||||
messageID: MID,
|
||||
type: "reasoning",
|
||||
text: value,
|
||||
time: { start: stamp - 6800, end: stamp - 6200 },
|
||||
}
|
||||
}
|
||||
|
||||
function bash(id: string, call: string, description: string, command: string, output: string): ToolPart {
|
||||
return tool(id, call, "bash", completed({ description, command }, description, output))
|
||||
}
|
||||
|
||||
const gapPatch = [
|
||||
"===================================================================",
|
||||
"--- packages/kilo-ui/src/components/message-part.css",
|
||||
"+++ packages/kilo-ui/src/components/message-part.css",
|
||||
"@@ -560,5 +560,5 @@",
|
||||
' html[data-theme="kilo-vscode"] [data-component="reasoning-part"] {',
|
||||
' [data-component="collapsible"].tool-collapsible {',
|
||||
"- gap: 4px;",
|
||||
"+ gap: 8px;",
|
||||
" }",
|
||||
" }",
|
||||
].join("\n")
|
||||
|
||||
const writePatch = [
|
||||
"===================================================================",
|
||||
"--- packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx",
|
||||
"+++ packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx",
|
||||
"@@ -1,3 +1,4 @@",
|
||||
" /** @jsxImportSource solid-js */",
|
||||
' import { For } from "solid-js"',
|
||||
' import type { Meta, StoryObj } from "storybook-solidjs-vite"',
|
||||
'+import { AssistantMessage } from "../components/chat/AssistantMessage"',
|
||||
].join("\n")
|
||||
|
||||
const blockQuestions: QuestionRequest[] = [
|
||||
{
|
||||
id: "matrix-question-request",
|
||||
sessionID: SID,
|
||||
questions: [
|
||||
{
|
||||
question: "Which visual family should this new block follow?",
|
||||
header: "Block Style",
|
||||
options: [
|
||||
{ label: "Tool row", description: "Compact header with dark expanded output" },
|
||||
{ label: "Inline card", description: "Standalone VS Code prompt-style card" },
|
||||
],
|
||||
},
|
||||
],
|
||||
tool: { messageID: MID, callID: "matrix-call-question-active" },
|
||||
},
|
||||
]
|
||||
|
||||
const blockSuggestions: SuggestionRequest[] = [
|
||||
{
|
||||
id: "matrix-suggestion-request",
|
||||
sessionID: SID,
|
||||
text: "Run a local visual review after checking this block matrix.",
|
||||
actions: [
|
||||
{ label: "Review UI", prompt: "/local-review-uncommitted" },
|
||||
{ label: "Open Storybook", prompt: "Inspect the Tool Call Lab Block Matrix story" },
|
||||
],
|
||||
tool: { messageID: MID, callID: "matrix-call-suggest-active" },
|
||||
},
|
||||
]
|
||||
|
||||
const blocks: SDKPart[] = [
|
||||
text("matrix-text-intro", "Block matrix rendered through the real VS Code AssistantMessage path."),
|
||||
reasoning(
|
||||
"matrix-reasoning-open",
|
||||
"**Reasoning output**\n\nThis should use the same trigger-to-output gap and expanded width as regular tool calls.",
|
||||
),
|
||||
bash(
|
||||
"matrix-bash",
|
||||
"matrix-call-bash",
|
||||
"Run visual check",
|
||||
"bun run --cwd packages/kilo-vscode build-storybook",
|
||||
["storybook v10.2.10", "info => Output directory: storybook-static", "success Built Storybook in 4.2s"].join("\n"),
|
||||
),
|
||||
tool(
|
||||
"matrix-grep",
|
||||
"matrix-call-grep",
|
||||
"grep",
|
||||
completed(
|
||||
{ pattern: "tool-collapsible", include: "*.css", path: "packages/kilo-ui/src/components" },
|
||||
"Find gaps",
|
||||
hits,
|
||||
),
|
||||
),
|
||||
tool(
|
||||
"matrix-background-start",
|
||||
"matrix-call-background-start",
|
||||
"background_process",
|
||||
completed(
|
||||
{
|
||||
action: "start",
|
||||
command: "bun run --cwd packages/kilo-vscode storybook",
|
||||
description: "Start Storybook",
|
||||
ready: { port: 6007, pattern: "Local:", timeout: 30000 },
|
||||
workdir: "/project",
|
||||
},
|
||||
"Start Storybook",
|
||||
proc,
|
||||
),
|
||||
),
|
||||
tool("matrix-edit", "matrix-call-edit", "edit", {
|
||||
status: "completed",
|
||||
input: {
|
||||
filePath: "packages/kilo-ui/src/components/message-part.css",
|
||||
oldString: "gap: 4px;",
|
||||
newString: "gap: 8px;",
|
||||
},
|
||||
output: "",
|
||||
title: "Edit reasoning gap",
|
||||
metadata: {
|
||||
filediff: {
|
||||
file: "packages/kilo-ui/src/components/message-part.css",
|
||||
patch: gapPatch,
|
||||
additions: 1,
|
||||
deletions: 1,
|
||||
},
|
||||
},
|
||||
time: { start: stamp - 3300, end: stamp - 3100 },
|
||||
}),
|
||||
tool("matrix-write", "matrix-call-write", "write", {
|
||||
status: "completed",
|
||||
input: {
|
||||
filePath: "packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx",
|
||||
content: "export const SearchPreviews = {}",
|
||||
},
|
||||
output: "",
|
||||
title: "Write story fixture",
|
||||
metadata: {
|
||||
filediff: {
|
||||
file: "packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx",
|
||||
patch: writePatch,
|
||||
additions: 1,
|
||||
deletions: 0,
|
||||
},
|
||||
},
|
||||
time: { start: stamp - 3000, end: stamp - 2800 },
|
||||
}),
|
||||
tool("matrix-todos", "matrix-call-todos", "todowrite", {
|
||||
status: "completed",
|
||||
input: {
|
||||
todos: [
|
||||
{ id: "todo-1", content: "Add block matrix story", status: "completed" },
|
||||
{ id: "todo-2", content: "Check spacing against reasoning output", status: "in_progress" },
|
||||
{ id: "todo-3", content: "Run visual regression", status: "pending" },
|
||||
],
|
||||
},
|
||||
output: "",
|
||||
title: "Update todos",
|
||||
metadata: {},
|
||||
time: { start: stamp - 2300, end: stamp - 2100 },
|
||||
}),
|
||||
tool("matrix-patch", "matrix-call-patch", "apply_patch", {
|
||||
status: "completed",
|
||||
input: {},
|
||||
output: "",
|
||||
title: "Patch two files",
|
||||
metadata: {
|
||||
files: [
|
||||
{
|
||||
filePath: "/project/packages/kilo-ui/src/components/message-part.css",
|
||||
relativePath: "packages/kilo-ui/src/components/message-part.css",
|
||||
type: "update",
|
||||
patch: gapPatch,
|
||||
diff: gapPatch,
|
||||
additions: 1,
|
||||
deletions: 1,
|
||||
},
|
||||
{
|
||||
filePath: "/project/packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx",
|
||||
relativePath: "packages/kilo-vscode/webview-ui/src/stories/tool-call-lab.stories.tsx",
|
||||
type: "update",
|
||||
patch: writePatch,
|
||||
diff: writePatch,
|
||||
additions: 1,
|
||||
deletions: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
time: { start: stamp - 2700, end: stamp - 2400 },
|
||||
}),
|
||||
tool("matrix-question-active", "matrix-call-question-active", "question", {
|
||||
status: "running",
|
||||
input: { questions: blockQuestions[0].questions },
|
||||
title: "Ask design question",
|
||||
metadata: {},
|
||||
time: { start: stamp - 2000 },
|
||||
}),
|
||||
tool("matrix-suggest-active", "matrix-call-suggest-active", "suggest", {
|
||||
status: "running",
|
||||
input: { text: blockSuggestions[0].text, actions: blockSuggestions[0].actions },
|
||||
title: "Suggest follow-up",
|
||||
metadata: {},
|
||||
time: { start: stamp - 1900 },
|
||||
}),
|
||||
tool("matrix-plan-exit", "matrix-call-plan-exit", "plan_exit", {
|
||||
status: "completed",
|
||||
input: { path: "docs/plans/tool-call-polish.md" },
|
||||
output: "",
|
||||
title: "Plan ready",
|
||||
metadata: { plan: "/project/docs/plans/tool-call-polish.md" },
|
||||
time: { start: stamp - 1800, end: stamp - 1700 },
|
||||
}),
|
||||
tool("matrix-mcp", "matrix-call-mcp", "linear_search_documentation", {
|
||||
status: "completed",
|
||||
input: { query: "Linear attachments", page: 1 },
|
||||
output:
|
||||
'## Linear attachments\n\nUse uploaded asset URLs to create issue attachments.\n\n```json\n{\n "status": "ready"\n}\n```',
|
||||
title: "Search docs",
|
||||
metadata: {},
|
||||
time: { start: stamp - 1600, end: stamp - 1400 },
|
||||
}),
|
||||
tool(
|
||||
"matrix-tool-hint",
|
||||
"matrix-call-tool-hint",
|
||||
"edit",
|
||||
failed({ filePath: "packages/kilo-ui/src/components/message-part.css" }, "oldString and newString are identical"),
|
||||
),
|
||||
tool(
|
||||
"matrix-tool-error",
|
||||
"matrix-call-tool-error",
|
||||
"github-pr-search",
|
||||
failed({ query: "tool call preview" }, "GitHub API error: 401 Unauthorized"),
|
||||
),
|
||||
]
|
||||
|
||||
const data = {
|
||||
...defaultMockData,
|
||||
message: { [SID]: [base] },
|
||||
part: { [MID]: blocks },
|
||||
}
|
||||
|
||||
for (const key of [
|
||||
"grep:matrix-call-grep",
|
||||
"edit:matrix-call-edit",
|
||||
"write:matrix-call-write",
|
||||
"apply_patch:matrix-call-patch",
|
||||
"todowrite:matrix-call-todos",
|
||||
"linear_search_documentation:matrix-call-mcp",
|
||||
]) {
|
||||
writeToolOpen(key, true)
|
||||
}
|
||||
|
||||
const css = `
|
||||
.tool-call-lab-search-previews {
|
||||
box-sizing: border-box;
|
||||
width: min(1180px, 100%);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.tool-call-lab-search-previews * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tool-call-lab-header {
|
||||
margin-bottom: 14px;
|
||||
color: var(--vscode-foreground, var(--text-base));
|
||||
}
|
||||
|
||||
.tool-call-lab-title {
|
||||
margin: 0 0 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tool-call-lab-subtitle {
|
||||
margin: 0;
|
||||
max-width: 760px;
|
||||
color: var(--vscode-descriptionForeground, var(--text-weak));
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.tool-call-lab-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.tool-call-lab-panel {
|
||||
min-width: 0;
|
||||
border: 1px solid var(--vscode-panel-border, var(--border-weak-base));
|
||||
background: color-mix(in srgb, var(--vscode-sideBar-background, var(--surface-base)) 96%, transparent);
|
||||
}
|
||||
|
||||
.tool-call-lab-panel-wide {
|
||||
max-width: 780px;
|
||||
}
|
||||
|
||||
.tool-call-lab-panel-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--vscode-panel-border, var(--border-weak-base));
|
||||
}
|
||||
|
||||
.tool-call-lab-panel-title {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--vscode-foreground, var(--text-base));
|
||||
}
|
||||
|
||||
.tool-call-lab-panel-note {
|
||||
font-size: 11px;
|
||||
line-height: 16px;
|
||||
color: var(--vscode-descriptionForeground, var(--text-weak));
|
||||
}
|
||||
|
||||
.tool-call-lab-stack {
|
||||
padding: 12px;
|
||||
}
|
||||
`
|
||||
|
||||
const meta: Meta = {
|
||||
title: "Labs/Tool Call Lab",
|
||||
parameters: { layout: "padded" },
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj
|
||||
|
||||
export const SearchPreviews: Story = {
|
||||
name: "Block Matrix",
|
||||
render: () => (
|
||||
<StoryProviders
|
||||
data={data}
|
||||
noPadding
|
||||
onOpenDiff={() => undefined}
|
||||
onOpenFile={() => undefined}
|
||||
questions={blockQuestions}
|
||||
sessionID={SID}
|
||||
status="busy"
|
||||
suggestions={blockSuggestions}
|
||||
>
|
||||
<style>{css}</style>
|
||||
<div class="tool-call-lab-search-previews">
|
||||
<div class="tool-call-lab-header">
|
||||
<p class="tool-call-lab-title">Assistant block matrix</p>
|
||||
<p class="tool-call-lab-subtitle">
|
||||
Real VS Code AssistantMessage rendering for the block families that can appear between assistant text and
|
||||
tool output: reasoning, shell, search previews, background processes, diffs, todos, active prompts, plan
|
||||
exits, MCP output, hints, and errors.
|
||||
</p>
|
||||
</div>
|
||||
<section class="tool-call-lab-panel tool-call-lab-panel-wide">
|
||||
<div class="tool-call-lab-panel-header">
|
||||
<span class="tool-call-lab-panel-title">Real assistant path</span>
|
||||
<span class="tool-call-lab-panel-note">
|
||||
Uses AssistantMessage instead of raw Part so VS Code-only cards and overrides are included.
|
||||
</span>
|
||||
</div>
|
||||
<div class="tool-call-lab-stack">
|
||||
<div class="vscode-session-turn-assistant">
|
||||
<AssistantMessage message={base} parts={blocks} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</StoryProviders>
|
||||
),
|
||||
}
|
||||
@@ -204,6 +204,14 @@
|
||||
padding: 0 4px 12px;
|
||||
}
|
||||
|
||||
.vscode-session-turn[data-row="assistant"] {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.vscode-session-turn[data-row="assistant"]:has(> .vscode-session-turn-assistant:empty) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.vscode-session-turn-user {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -216,7 +224,7 @@
|
||||
.vscode-session-turn-assistant {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
padding: 6px 0;
|
||||
padding: 0;
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--kilo-font-size-13);
|
||||
line-height: var(--line-height-large);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
============================================ */
|
||||
|
||||
[data-component="question-dock"] {
|
||||
margin: 6px 8px;
|
||||
margin: 0 8px;
|
||||
background-color: var(--surface-base);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
/* ============================================
|
||||
Todo tool — align checkboxes with icon in header
|
||||
============================================ */
|
||||
|
||||
[data-component="todos"] {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Task Tool (sub-agent, v1.0.25 style)
|
||||
tool-output scrollable container size override
|
||||
@@ -19,13 +11,35 @@
|
||||
Background Process Tool
|
||||
============================================ */
|
||||
|
||||
[data-component="background-process-details"] {
|
||||
overflow: hidden;
|
||||
margin-inline: -6px;
|
||||
width: calc(100% + 12px);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface-tool-output-base);
|
||||
}
|
||||
|
||||
[data-component="background-process-fields"] {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
html[data-theme="kilo-vscode"]
|
||||
[data-component="tool-part-wrapper"][data-part-type="tool"]
|
||||
[data-component="background-process-details"]
|
||||
[data-component="tool-output"][data-variant="preview"] {
|
||||
width: auto;
|
||||
margin-inline: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
[data-component="background-process-fields"] + [data-component="tool-output"][data-variant="preview"] {
|
||||
border-top: 1px solid color-mix(in srgb, var(--border-weak-base) 70%, transparent);
|
||||
}
|
||||
|
||||
[data-slot="background-process-field"] {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(72px, max-content) minmax(0, 1fr);
|
||||
@@ -35,19 +49,21 @@
|
||||
}
|
||||
|
||||
[data-slot="background-process-label"] {
|
||||
color: var(--text-dimmed);
|
||||
color: var(--text-weaker);
|
||||
}
|
||||
|
||||
[data-slot="background-process-value"] {
|
||||
color: var(--text-base);
|
||||
font-family: var(--font-mono);
|
||||
color: var(--text-weak);
|
||||
font-family: var(--font-family-mono);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
[data-slot="background-process-output"] {
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
font-family: var(--font-mono);
|
||||
font-family: var(--font-family-mono);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
line-height: var(--line-height-large);
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user