Tool Use.

This commit is contained in:
Drew 2026-02-24 18:31:26 -08:00
parent 6b85ff3cb8
commit 0c1c928498
20 changed files with 1822 additions and 129 deletions

View file

@ -105,11 +105,37 @@ pub(super) fn render(frame: &mut Frame, state: &AppState) {
let output = Paragraph::new(lines)
.wrap(Wrap { trim: false })
.scroll((state.scroll, 0));
frame.render_widget(output, chunks[0]);
let output_area = chunks[0];
frame.render_widget(output, output_area);
// --- Tool approval overlay ---
if let Some(ref approval) = state.pending_approval {
let overlay_w = (output_area.width / 2).max(60).min(output_area.width);
let overlay_h: u16 = 5;
let overlay_x = output_area.x + (output_area.width.saturating_sub(overlay_w)) / 2;
let overlay_y = output_area.y + output_area.height.saturating_sub(overlay_h) / 2;
let overlay_area = Rect {
x: overlay_x,
y: overlay_y,
width: overlay_w,
height: overlay_h.min(output_area.height),
};
frame.render_widget(Clear, overlay_area);
let text = format!(
"{}: {}\n\ny = approve, n = deny",
approval.tool_name, approval.input_summary
);
let overlay = Paragraph::new(text).block(
Block::bordered()
.border_style(Style::default().fg(Color::Yellow))
.title("Tool Approval"),
);
frame.render_widget(overlay, overlay_area);
}
// --- Command overlay (floating box centered on output pane) ---
if state.mode == Mode::Command {
let overlay_area = command_overlay_rect(chunks[0]);
let overlay_area = command_overlay_rect(output_area);
// Clear the area behind the overlay so it appears floating.
frame.render_widget(Clear, overlay_area);
let overlay = Paragraph::new(format!(":{}", state.command_buffer)).block(
@ -146,7 +172,7 @@ pub(super) fn render(frame: &mut Frame, state: &AppState) {
}
Mode::Command => {
// Cursor in the floating overlay
let overlay = command_overlay_rect(chunks[0]);
let overlay = command_overlay_rect(output_area);
// border(1) + ":" (1) + buf len
let cursor_x = overlay.x + 1 + 1 + state.command_buffer.len() as u16;
let cursor_y = overlay.y + 1; // inside the border
@ -386,4 +412,31 @@ mod tests {
"expected error in status bar"
);
}
#[test]
fn render_approval_overlay_visible() {
let backend = TestBackend::new(80, 24);
let mut terminal = Terminal::new(backend).unwrap();
let mut state = AppState::new();
state.pending_approval = Some(super::super::events::PendingApproval {
tool_use_id: "t1".to_string(),
tool_name: "write_file".to_string(),
input_summary: "path: foo.txt".to_string(),
});
terminal.draw(|frame| render(frame, &state)).unwrap();
let buf = terminal.backend().buffer().clone();
let all_text: String = buf
.content()
.iter()
.map(|c| c.symbol().to_string())
.collect();
assert!(
all_text.contains("Tool Approval"),
"expected 'Tool Approval' overlay"
);
assert!(
all_text.contains("write_file"),
"expected tool name in overlay"
);
}
}