fix(ui): harden shell highlighting rendering

This commit is contained in:
marius-kilocode
2026-05-14 09:45:35 +02:00
parent 911e7ce733
commit dea6169340
5 changed files with 33 additions and 16 deletions
@@ -251,7 +251,7 @@ html[data-theme="kilo-vscode"] [data-component="bash-output"] {
[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: 13px;
font-size: var(--font-size-small);
line-height: var(--line-height-large);
white-space: pre-wrap;
overflow-wrap: anywhere;
@@ -2053,13 +2053,14 @@ function BashCopyButton(props: { value: () => string; label: string }) {
)
}
function BashHighlightedOutput(props: { cmd: string; output: string; outputPath?: string }) {
function BashHighlightedOutput(props: { cmd: string; output: string; outputPath?: string; active?: boolean }) {
const data = useData()
const i18n = useI18n()
let cmdRef: HTMLDivElement | undefined
let outRef: HTMLDivElement | undefined
createEffect(() => {
if (!props.active) return
const cmd = props.cmd
if (!cmdRef || !cmd) return
cmdRef.innerHTML = `<pre data-slot="bash-pre"><code data-lang="shellscript">${escapeHtml(cmd)}</code></pre>`
@@ -2067,6 +2068,7 @@ function BashHighlightedOutput(props: { cmd: string; output: string; outputPath?
})
createEffect(() => {
if (!props.active) return
const out = props.output
if (!outRef || !out) return
outRef.innerHTML = `<pre data-slot="bash-pre"><code data-lang="log">${escapeHtml(out)}</code></pre>`
@@ -2128,6 +2130,7 @@ ToolRegistry.register({
const pending = () => busy(props.status)
const reveal = useToolReveal(pending, () => props.reveal !== false)
const subtitle = () => props.input.description ?? props.metadata.description
const [open, setOpen] = createSignal(props.defaultOpen ?? true)
// also apply processCarriageReturns for Windows CLI tools
const cmd = createMemo(() => {
@@ -2148,6 +2151,7 @@ ToolRegistry.register({
icon="console"
animated
defaultOpen={props.defaultOpen ?? true}
onOpenChange={setOpen}
allowPendingToggle
trigger={
<div data-slot="basic-tool-tool-info-structured">
@@ -2160,7 +2164,7 @@ ToolRegistry.register({
</div>
}
>
<BashHighlightedOutput cmd={cmd()} output={out()} outputPath={props.metadata.outputPath} />
<BashHighlightedOutput cmd={cmd()} output={out()} outputPath={props.metadata.outputPath} active={open()} />
</BasicTool>
)
},
@@ -59,10 +59,11 @@ function ShellRollingCommand(props: { text: string; animate?: boolean }) {
)
}
function ShellOutputHighlight(props: { code: string }) {
function ShellOutputHighlight(props: { code: string; active?: boolean }) {
let ref: HTMLDivElement | undefined
createEffect(() => {
if (!props.active) return
const code = props.code
if (!ref || !code) return
ref.innerHTML = `<pre data-slot="shell-expanded-pre"><code data-lang="log">${escapeHtml(code)}</code></pre>`
@@ -171,7 +172,7 @@ function ShellExpanded(props: { cmd: string; out: string; open: boolean }) {
onScroll={updateMask}
style={{ "max-height": `${cap()}px` }}
>
<ShellOutputHighlight code={props.out} />
<ShellOutputHighlight code={props.out} active={props.open} />
</div>
</>
</Show>
+21 -11
View File
@@ -607,6 +607,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
await routeSuggestionWebviewMessage(this.questionCtx, message)
if (await ModelState.handleMessage(message.type, message, this.client, (msg) => this.postMessage(msg))) return
if (await routeAutocompleteMessage(message, (msg) => this.postMessage(msg))) return
if (this.handleEditorOpenMessage(message)) return
if (
await handleSidebarWorktreeMessage(message, {
post: (msg) => this.postMessage(msg),
@@ -771,16 +772,6 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
break
case "saveImage":
return saveImage(this.getWorkspaceDirectory(this.currentSession?.id), message)
case "openFile":
if (message.filePath) {
this.handleOpenFile(message.filePath, message.line, message.column)
}
break
case "openContent":
if (message.content) {
this.handleOpenContent(message.content, message.language)
}
break
case "requestProviders":
this.fetchAndSendProviders().catch((e) => console.error("[Kilo New] fetchAndSendProviders failed:", e))
break
@@ -2902,8 +2893,27 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
.then(open, (err) => console.error("[Kilo New] KiloProvider: Failed to preview image:", err))
}
private handleEditorOpenMessage(message: {
type?: string
filePath?: string
line?: number
column?: number
content?: string
language?: string
}): boolean {
if (message.type === "openFile") {
if (message.filePath) this.handleOpenFile(message.filePath, message.line, message.column)
return true
}
if (message.type === "openContent") {
if (message.content) this.handleOpenContent(message.content, message.language)
return true
}
return false
}
/**
* Handle openContent request — open arbitrary text in an untitled VS Code editor tab.
* Handle openContent request - open arbitrary text in an untitled VS Code editor tab.
*/
private handleOpenContent(content: string, language?: string): void {
vscode.workspace.openTextDocument({ content, language: language || "log" }).then(
@@ -35,6 +35,7 @@ export interface BasicToolProps {
animated?: boolean
allowPendingToggle?: boolean // kilocode_change
onSubtitleClick?: () => void
onOpenChange?: (open: boolean) => void // kilocode_change
onTriggerClick?: JSX.EventHandlerUnion<HTMLElement, MouseEvent>
triggerHref?: string
clickable?: boolean
@@ -124,6 +125,7 @@ export function BasicTool(props: BasicToolProps) {
if (props.hideDetails) return // kilocode_change
if (props.locked && !value) return
setState("open", value)
props.onOpenChange?.(value) // kilocode_change
}
const trigger = () => (