generated from nhcarrigan/template
feat: collapsible tool lines in terminal
Long tool messages (e.g. Bash commands) are now stored in full and displayed collapsed by default, with a toggle button to expand/collapse. Removes backend truncation of Bash commands at 50 chars.
This commit is contained in:
@@ -1689,12 +1689,7 @@ fn format_tool_description(name: &str, input: &serde_json::Value) -> String {
|
|||||||
}
|
}
|
||||||
"Bash" => {
|
"Bash" => {
|
||||||
if let Some(cmd) = input.get("command").and_then(|v| v.as_str()) {
|
if let Some(cmd) = input.get("command").and_then(|v| v.as_str()) {
|
||||||
let truncated = if cmd.len() > 50 {
|
format!("Running: {}", cmd)
|
||||||
format!("{}...", &cmd[..50])
|
|
||||||
} else {
|
|
||||||
cmd.to_string()
|
|
||||||
};
|
|
||||||
format!("Running: {}", truncated)
|
|
||||||
} else {
|
} else {
|
||||||
"Running command...".to_string()
|
"Running command...".to_string()
|
||||||
}
|
}
|
||||||
@@ -1855,9 +1850,7 @@ mod tests {
|
|||||||
let long_cmd = "a".repeat(100);
|
let long_cmd = "a".repeat(100);
|
||||||
let input = serde_json::json!({"command": long_cmd});
|
let input = serde_json::json!({"command": long_cmd});
|
||||||
let desc = format_tool_description("Bash", &input);
|
let desc = format_tool_description("Bash", &input);
|
||||||
assert!(desc.starts_with("Running: "));
|
assert_eq!(desc, format!("Running: {}", long_cmd));
|
||||||
assert!(desc.ends_with("..."));
|
|
||||||
assert!(desc.len() < 70);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { claudeStore, type TerminalLine } from "$lib/stores/claude";
|
import { claudeStore, type TerminalLine } from "$lib/stores/claude";
|
||||||
import { afterUpdate, tick, onMount, onDestroy } from "svelte";
|
import { afterUpdate, tick, onMount, onDestroy } from "svelte";
|
||||||
|
|
||||||
import ConversationTabs from "./ConversationTabs.svelte";
|
import ConversationTabs from "./ConversationTabs.svelte";
|
||||||
import Markdown from "./Markdown.svelte";
|
import Markdown from "./Markdown.svelte";
|
||||||
import HighlightedText from "./HighlightedText.svelte";
|
import HighlightedText from "./HighlightedText.svelte";
|
||||||
@@ -187,6 +188,22 @@
|
|||||||
copiedMessageId = null;
|
copiedMessageId = null;
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collapsible tool lines
|
||||||
|
const TOOL_COLLAPSE_THRESHOLD = 60;
|
||||||
|
let expandedToolLines: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
function isToolContentLong(content: string): boolean {
|
||||||
|
return content.length > TOOL_COLLAPSE_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncateToolContent(content: string): string {
|
||||||
|
return content.slice(0, TOOL_COLLAPSE_THRESHOLD) + "…";
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleToolLine(id: string) {
|
||||||
|
expandedToolLines = { ...expandedToolLines, [id]: !expandedToolLines[id] };
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -289,6 +306,22 @@
|
|||||||
<span class="copy-text">{copiedMessageId === line.id ? "Copied!" : "Copy"}</span>
|
<span class="copy-text">{copiedMessageId === line.id ? "Copied!" : "Copy"}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{:else if line.type === "tool" && isToolContentLong(maskPaths(line.content, hidePaths))}
|
||||||
|
<span class="tool-collapsible">
|
||||||
|
<HighlightedText
|
||||||
|
content={expandedToolLines[line.id]
|
||||||
|
? maskPaths(line.content, hidePaths)
|
||||||
|
: truncateToolContent(maskPaths(line.content, hidePaths))}
|
||||||
|
searchQuery={currentSearchQuery}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="tool-toggle-btn"
|
||||||
|
onclick={() => toggleToolLine(line.id)}
|
||||||
|
title={expandedToolLines[line.id] ? "Collapse" : "Expand to see full content"}
|
||||||
|
>
|
||||||
|
{expandedToolLines[line.id] ? "▲" : "▼"}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
<HighlightedText
|
<HighlightedText
|
||||||
content={maskPaths(line.content, hidePaths)}
|
content={maskPaths(line.content, hidePaths)}
|
||||||
@@ -408,4 +441,28 @@
|
|||||||
.terminal-line {
|
.terminal-line {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tool-collapsible {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-toggle-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-tertiary, #6b7280);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.7em;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1;
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-toggle-btn:hover {
|
||||||
|
opacity: 1;
|
||||||
|
color: var(--terminal-tool, #c084fc);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user