Skip to content

Commit 03663fd

Browse files
committed
feat(agent): foldable Vue terminal with Ctrl+O toggle
Replaces the xterm.js scrollback with a Vue-native terminal that can fold tool-call output. Tool calls collapse to a one-line summary by default — "$ <cmd> N lines, exit M" with green/red exit-code colour — and Ctrl+O (or ⌘+O) toggles fold/unfold across every block at once. Click the block header to toggle a single tool call. Why this trade-off: - xterm.js is a character grid; folding requires a parallel logical model and a full re-render on every toggle. Vue components track semantic blocks natively, so fold state is just reactive data. - Side benefits: per-block text selection/copy, native theming via Tailwind tokens, no ANSI cursor math, no @xterm/* deps in the agent bundle, easier to extend (per-tool icons, inline images, links). Implementation notes: - store.AgentMessage gains an optional 'tool' meta field carrying script/stdout/stderr/exitCode so the renderer doesn't have to parse the synthesized text summary back into structure. - Global keydown listener with capture:true preempts the browser's default Open-File dialog before Ctrl+O reaches it. - Drops src/agent/ui/XtermPanel.vue, useXtermReadline.ts, and shell/useCompletion.ts. The xterm package is still used elsewhere in the codebase (logs/CommandTerminal), so no dep removal.
1 parent 359395a commit 03663fd

8 files changed

Lines changed: 756 additions & 1176 deletions

File tree

src/agent/composables/useAgentSession.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,16 @@ export function useAgentSession() {
187187
if (ac.signal.aborted) return
188188
toolCalls.push(inv)
189189
const summary = `$ ${inv.script}\n${inv.stdout}${inv.stderr ? `\n[stderr] ${inv.stderr}` : ''}`
190-
store.addMessage({ role: 'system', text: summary })
190+
store.addMessage({
191+
role: 'system',
192+
text: summary,
193+
tool: {
194+
script: inv.script,
195+
stdout: inv.stdout,
196+
stderr: inv.stderr,
197+
exitCode: inv.exitCode
198+
}
199+
})
191200
}
192201
)
193202
// Fallback: model ran tools but didn't speak — surface a minimal

src/agent/shell/useCompletion.ts

Lines changed: 0 additions & 185 deletions
This file was deleted.

src/agent/stores/agentStore.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,25 @@ export interface IngestedAsset {
1616
previewUrl?: string
1717
}
1818

19+
interface ToolMessageMeta {
20+
script: string
21+
stdout: string
22+
stderr?: string
23+
exitCode: number
24+
}
25+
1926
interface AgentMessage {
2027
id: string
2128
role: AgentMessageRole
2229
text: string
2330
assets?: IngestedAsset[]
2431
createdAt: number
32+
/**
33+
* Present on system messages that record a tool invocation. Lets the
34+
* renderer fold/unfold individual tool calls by structure instead of
35+
* re-parsing the synthesized text summary.
36+
*/
37+
tool?: ToolMessageMeta
2538
}
2639

2740
interface FabPosition {

src/agent/ui/AgentRoot.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<Teleport to="body">
33
<AgentFab />
4-
<XtermPanel />
4+
<FoldablePanel />
55
</Teleport>
66
</template>
77

@@ -14,7 +14,7 @@ import { useCommandStore } from '@/stores/commandStore'
1414
1515
import { useAgentStore } from '../stores/agentStore'
1616
import AgentFab from './AgentFab.vue'
17-
import XtermPanel from './XtermPanel.vue'
17+
import FoldablePanel from './FoldablePanel.vue'
1818
1919
onMounted(() => {
2020
const commandStore = useCommandStore()

0 commit comments

Comments
 (0)