shape permission-prompt input rendering per tool

Bash, Write, and Edit/MultiEdit each get a tool-appropriate body in
the permission card instead of the indented-JSON dump: Bash renders
the command as a shell code block with a background/timeout footer,
Write shows path + content syntax-highlighted from the extension, and
Edit shows path + before/after blocks. Unknown tools keep the JSON
fallback.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jeroen Schweitzer
2026-05-28 09:52:20 +02:00
co-authored by Claude
parent 25c11c1edb
commit 762d66e819
3 changed files with 51 additions and 0 deletions
+5
View File
@@ -18,6 +18,11 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
### Changed
- Permission prompt cards render the tool input in the shape that fits the
tool — Bash shows the command as a shell code block (with a footer for
`run_in_background` / `timeout`), Write shows the path plus the content
highlighted from its extension, Edit shows the path plus before/after
blocks. Unknown tools fall back to the indented-JSON dump.
- Claude meta sidebar is now tabbed — Activity / Team / Config (T-182).
Activity shows usage stats plus the primary session's live runtime; Team
holds the roster and auto-fronts when a team spawns; Config shows the
Binary file not shown.
+46
View File
@@ -101,6 +101,52 @@ void main() {
expect(find.byType(ClideCodeBlock), findsOneWidget);
});
testWidgets('permission card: Bash renders the command as a shell code block, not JSON', (tester) async {
const prompt = ToolPrompt(
promptId: 'req-b',
toolName: 'Bash',
displayName: 'Bash',
description: 'Read IDE lock files',
input: {'command': 'cat ~/.claude/ide/97632.lock', 'description': 'Read IDE lock files'},
);
await tester.pumpWidget(harness(f, ToolPromptCard(prompt: prompt, onResolve: (_, __) {})));
await tester.pump();
final block = tester.widget<ClideCodeBlock>(find.byType(ClideCodeBlock));
expect(block.language, 'bash');
expect(block.source, 'cat ~/.claude/ide/97632.lock');
// The raw `description` key shouldn't bleed into the body — it's already
// shown above as the prompt's description line.
expect(find.textContaining('"description"'), findsNothing);
});
testWidgets('permission card: Bash shows background/timeout footer when set', (tester) async {
const prompt = ToolPrompt(
promptId: 'req-b2',
toolName: 'Bash',
displayName: 'Bash',
description: 'long task',
input: {'command': 'sleep 30', 'run_in_background': true, 'timeout': 60000},
);
await tester.pumpWidget(harness(f, ToolPromptCard(prompt: prompt, onResolve: (_, __) {})));
await tester.pump();
expect(find.text('background · timeout 60000ms'), findsOneWidget);
});
testWidgets('permission card: unknown tool falls back to JSON', (tester) async {
const prompt = ToolPrompt(
promptId: 'req-x',
toolName: 'NovelTool',
displayName: 'NovelTool',
input: {'foo': 'bar'},
);
await tester.pumpWidget(harness(f, ToolPromptCard(prompt: prompt, onResolve: (_, __) {})));
await tester.pump();
final block = tester.widget<ClideCodeBlock>(find.byType(ClideCodeBlock));
expect(block.language, 'json');
expect(block.source, contains('"foo"'));
});
testWidgets('permission card: Deny returns DenyTool with a message', (tester) async {
ToolDecision? decision;
await tester.pumpWidget(harness(