tui: fix approval dialog for large commands (#3087)
#### Summary - Emit a “Proposed Command” history cell when an ExecApprovalRequest arrives (parity with proposed patches). - Simplify the approval dialog: show only the reason/instructions; move the command preview into history. - Make approval/abort decision history concise: - Single line snippet; if multiline, show first line + " ...". - Truncate to 80 graphemes with ellipsis for very long commands. #### Details - History - Add `new_proposed_command` to render a header and indented command preview. - Use shared `prefix_lines` helper for first/subsequent line prefixes. - Approval UI - `UserApprovalWidget` no longer renders the command in the modal; shows optional `reason` text only. - Decision history renders an inline, dimmed snippet per rules above. - Tests (snapshot-based) - Proposed/decision flow for short command. - Proposed multi-line + aborted decision snippet with “ ...”. - Very long one-line command -> truncated snippet with “…”. - Updated existing exec approval snapshots and test reasons. <img width="1053" height="704" alt="Screenshot 2025-09-03 at 11 57 35 AM" src="https://github.com/user-attachments/assets/9ed4c316-9daf-4ac1-80ff-7ae1f481dd10" /> after approving: <img width="1053" height="704" alt="Screenshot 2025-09-03 at 11 58 18 AM" src="https://github.com/user-attachments/assets/a44e243f-eb9d-42ea-87f4-171b3fb481e7" /> rejection: <img width="1053" height="207" alt="Screenshot 2025-09-03 at 11 58 45 AM" src="https://github.com/user-attachments/assets/a022664b-ae0e-4b70-a388-509208707934" /> big command: https://github.com/user-attachments/assets/2dd976e5-799f-4af7-9682-a046e66cc494
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::diff_render::create_diff_summary;
|
||||
use crate::exec_command::relativize_to_home;
|
||||
use crate::exec_command::strip_bash_lc_and_escape;
|
||||
use crate::markdown::append_markdown;
|
||||
use crate::render::line_utils::prefix_lines;
|
||||
use crate::slash_command::SlashCommand;
|
||||
use crate::text_formatting::format_and_truncate_tool_result;
|
||||
use base64::Engine;
|
||||
@@ -1140,33 +1141,9 @@ impl HistoryCell for PlanUpdateCell {
|
||||
.into_iter()
|
||||
.map(|s| s.to_string().set_style(step_style).into())
|
||||
.collect();
|
||||
prefix_lines(step_text, &box_str.into(), &" ".into())
|
||||
prefix_lines(step_text, box_str.into(), " ".into())
|
||||
};
|
||||
|
||||
fn prefix_lines(
|
||||
lines: Vec<Line<'static>>,
|
||||
initial_prefix: &Span<'static>,
|
||||
subsequent_prefix: &Span<'static>,
|
||||
) -> Vec<Line<'static>> {
|
||||
lines
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, l)| {
|
||||
Line::from(
|
||||
[
|
||||
vec![if i == 0 {
|
||||
initial_prefix.clone()
|
||||
} else {
|
||||
subsequent_prefix.clone()
|
||||
}],
|
||||
l.spans,
|
||||
]
|
||||
.concat(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
let mut lines: Vec<Line<'static>> = vec![];
|
||||
lines.push(vec!["• ".into(), "Updated Plan".bold()].into());
|
||||
|
||||
@@ -1187,7 +1164,7 @@ impl HistoryCell for PlanUpdateCell {
|
||||
indented_lines.extend(render_step(status, step));
|
||||
}
|
||||
}
|
||||
lines.extend(prefix_lines(indented_lines, &" └ ".into(), &" ".into()));
|
||||
lines.extend(prefix_lines(indented_lines, " └ ".into(), " ".into()));
|
||||
|
||||
lines
|
||||
}
|
||||
@@ -1231,6 +1208,26 @@ pub(crate) fn new_patch_apply_failure(stderr: String) -> PlainHistoryCell {
|
||||
PlainHistoryCell { lines }
|
||||
}
|
||||
|
||||
/// Create a new history cell for a proposed command approval.
|
||||
/// Renders a header and the command preview similar to how proposed patches
|
||||
/// show a header and summary.
|
||||
pub(crate) fn new_proposed_command(command: &[String]) -> PlainHistoryCell {
|
||||
let cmd = strip_bash_lc_and_escape(command);
|
||||
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
lines.push(Line::from(vec!["• ".into(), "Proposed Command".bold()]));
|
||||
|
||||
let cmd_lines: Vec<Line<'static>> = cmd
|
||||
.lines()
|
||||
.map(|part| Line::from(part.to_string()))
|
||||
.collect();
|
||||
let initial_prefix: Span<'static> = " └ ".dim();
|
||||
let subsequent_prefix: Span<'static> = " ".into();
|
||||
lines.extend(prefix_lines(cmd_lines, initial_prefix, subsequent_prefix));
|
||||
|
||||
PlainHistoryCell { lines }
|
||||
}
|
||||
|
||||
pub(crate) fn new_reasoning_block(
|
||||
full_reasoning_buffer: String,
|
||||
config: &Config,
|
||||
|
||||
Reference in New Issue
Block a user