flesh out the Claude prompt UX: options, stepper, collapse

Builds on the in-composer prompt surface (D-78):

- Permission prompts (T-175): Allow / Allow-and-don't-ask-again / Deny.
  "Don't ask again" appears only when the request carries a
  permission_suggestion and echoes it back as updatedPermissions. An
  optional note rides Deny as the message, or Allow as a follow-up user
  message (the protocol has no allow-with-message).
- AskUserQuestion picker (T-176): a single question renders bare; 2-4
  questions step one at a time (nav shows "N · Header", ✓ when answered)
  then a review/confirm screen. Each question offers an "Other" free-text
  choice and a per-choice note; multi-select joins labels. A "chat
  instead" escape denies the prompt so the user can type freely. On
  submit the answer is echoed into the log, since the card is ephemeral.
- Collapsed tool cards (T-177): multi-line tool_use / tool_result start
  collapsed behind a one-line summary; one-line output renders inline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-25 09:30:50 +02:00
co-authored by Claude Opus 4.7
parent fbf5a720a8
commit de264e7141
8 changed files with 324 additions and 16 deletions
+25 -3
View File
@@ -149,26 +149,34 @@ class _ConversationTurn extends StatelessWidget {
Widget _toolUse(AssistantToolUse t) {
final pretty = const JsonEncoder.withIndent(' ').convert(t.input);
// Collapse only the bulky multi-line form; a trivial one-liner just shows.
final multiline = pretty.contains('\n');
return ConversationCard(
variant: ConversationCardVariant.bordered,
accent: tokens.globalFocus,
label: t.name,
copyText: pretty,
collapsible: true,
collapsible: multiline,
collapsedByDefault: multiline,
collapsedSummary: multiline ? _toolUseSummary(t) : null,
body: ClideCodeBlock(source: pretty, language: 'json'),
);
}
Widget _toolResult(ToolResultMessage t) {
final accent = t.isError ? tokens.statusError : tokens.globalTextMuted;
// A one-line result is all chrome to collapse — show it inline. Only fold
// away multi-line output, behind a summary of its first line.
final multiline = t.content.contains('\n');
return ConversationCard(
variant: ConversationCardVariant.bordered,
accent: accent,
borderColor: t.isError ? tokens.statusError : tokens.panelBorder,
label: t.isError ? 'error' : 'result',
copyText: t.content,
collapsible: true,
collapsedByDefault: true,
collapsible: multiline,
collapsedByDefault: multiline,
collapsedSummary: multiline ? _firstLine(t.content) : null,
body: ClideText(
t.content,
fontSize: clideFontMeta,
@@ -177,4 +185,18 @@ class _ConversationTurn extends StatelessWidget {
),
);
}
/// A compact one-liner for a collapsed tool-use card: the most telling arg.
String _toolUseSummary(AssistantToolUse t) {
final input = t.input;
final key =
input['file_path'] ?? input['command'] ?? input['path'] ?? input['pattern'] ?? input['url'] ?? (input.values.isNotEmpty ? input.values.first : null);
final s = key?.toString().replaceAll('\n', ' ') ?? '';
return s.length > 80 ? '${s.substring(0, 80)}…' : s;
}
String _firstLine(String content) {
final line = content.split('\n').first.trim();
return line.length > 80 ? '${line.substring(0, 80)}…' : line;
}
}