tui: overlay screens via F-keys (F1=context, F2=agents)
Replaced debug_visible bool with an Overlay enum. F1 shows the context/debug screen (Ctrl+D still works as alias), F2 shows the agents screen (placeholder for now — will show surface, observe, reflect, journal status). Esc closes any overlay. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
c5efc6e650
commit
baf208281d
2 changed files with 69 additions and 24 deletions
|
|
@ -9,6 +9,8 @@
|
|||
// Uses ratatui + crossterm. The App struct holds all TUI state and
|
||||
// handles rendering. Input is processed from crossterm key events.
|
||||
|
||||
const SCREEN_LEGEND: &str = " F1=main F2=agents F10=context ";
|
||||
|
||||
use crossterm::{
|
||||
event::{EnableMouseCapture, DisableMouseCapture, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind, MouseButton},
|
||||
terminal::{self, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
|
|
@ -328,8 +330,8 @@ pub struct App {
|
|||
pub hotkey_actions: Vec<HotkeyAction>,
|
||||
/// Pane areas from last draw (for mouse click → pane selection).
|
||||
pane_areas: [Rect; 3], // [autonomous, conversation, tools]
|
||||
/// Debug screen visible (Ctrl+D toggle).
|
||||
debug_visible: bool,
|
||||
/// Active overlay screen (F1=context, F2=agents, Ctrl+D=context).
|
||||
overlay: Option<Overlay>,
|
||||
/// Debug screen scroll offset.
|
||||
debug_scroll: u16,
|
||||
/// Index of selected context section in debug view (for expand/collapse).
|
||||
|
|
@ -342,6 +344,15 @@ pub struct App {
|
|||
shared_context: SharedContextState,
|
||||
}
|
||||
|
||||
/// Overlay screens toggled by F-keys.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
enum Overlay {
|
||||
/// F1 / Ctrl+D — context window, model info, budget
|
||||
Context,
|
||||
/// F2 — subconscious agent status
|
||||
Agents,
|
||||
}
|
||||
|
||||
/// Actions triggered by hotkeys, consumed by the main loop.
|
||||
#[derive(Debug)]
|
||||
pub enum HotkeyAction {
|
||||
|
|
@ -385,7 +396,7 @@ impl App {
|
|||
submitted: Vec::new(),
|
||||
hotkey_actions: Vec::new(),
|
||||
pane_areas: [Rect::default(); 3],
|
||||
debug_visible: false,
|
||||
overlay: None,
|
||||
debug_scroll: 0,
|
||||
debug_selected: None,
|
||||
debug_expanded: std::collections::HashSet::new(),
|
||||
|
|
@ -510,8 +521,8 @@ impl App {
|
|||
return;
|
||||
}
|
||||
KeyCode::Char('d') => {
|
||||
self.debug_visible = !self.debug_visible;
|
||||
self.debug_scroll = 0;
|
||||
// Legacy alias for F10
|
||||
self.set_overlay(Overlay::Context);
|
||||
return;
|
||||
}
|
||||
KeyCode::Char('p') => {
|
||||
|
|
@ -522,13 +533,15 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
// Debug screen captures scroll keys and Esc
|
||||
if self.debug_visible {
|
||||
// Overlay screen captures scroll keys and Esc
|
||||
if self.overlay.is_some() {
|
||||
match key.code {
|
||||
KeyCode::Esc => {
|
||||
self.debug_visible = false;
|
||||
KeyCode::F(1) => {
|
||||
self.overlay = None;
|
||||
return;
|
||||
}
|
||||
KeyCode::F(10) => { self.set_overlay(Overlay::Context); return; }
|
||||
KeyCode::F(2) => { self.set_overlay(Overlay::Agents); return; }
|
||||
KeyCode::Up => {
|
||||
let cs = self.read_context_state();
|
||||
let n = self.debug_item_count(&cs);
|
||||
|
|
@ -574,6 +587,8 @@ impl App {
|
|||
}
|
||||
|
||||
match key.code {
|
||||
KeyCode::F(10) => { self.set_overlay(Overlay::Context); return; }
|
||||
KeyCode::F(2) => { self.set_overlay(Overlay::Agents); return; }
|
||||
KeyCode::Esc => {
|
||||
self.hotkey_actions.push(HotkeyAction::Interrupt);
|
||||
}
|
||||
|
|
@ -695,9 +710,10 @@ impl App {
|
|||
pub fn draw(&mut self, frame: &mut Frame) {
|
||||
let size = frame.area();
|
||||
|
||||
if self.debug_visible {
|
||||
self.draw_debug(frame, size);
|
||||
return;
|
||||
match self.overlay {
|
||||
Some(Overlay::Context) => { self.draw_debug(frame, size); return; }
|
||||
Some(Overlay::Agents) => { self.draw_agents(frame, size); return; }
|
||||
None => {}
|
||||
}
|
||||
|
||||
// Main layout: content area + active tools overlay + status bar
|
||||
|
|
@ -744,11 +760,12 @@ impl App {
|
|||
|
||||
// Draw autonomous pane
|
||||
let auto_active = self.active_pane == ActivePane::Autonomous;
|
||||
draw_pane(frame, auto_area, "autonomous", &mut self.autonomous, auto_active);
|
||||
draw_pane(frame, auto_area, "autonomous", &mut self.autonomous, auto_active,
|
||||
Some(SCREEN_LEGEND));
|
||||
|
||||
// Draw tools pane
|
||||
let tools_active = self.active_pane == ActivePane::Tools;
|
||||
draw_pane(frame, right_col, "tools", &mut self.tools, tools_active);
|
||||
draw_pane(frame, right_col, "tools", &mut self.tools, tools_active, None);
|
||||
|
||||
// Draw conversation pane (with input line)
|
||||
let conv_active = self.active_pane == ActivePane::Conversation;
|
||||
|
|
@ -849,7 +866,7 @@ impl App {
|
|||
String::new()
|
||||
};
|
||||
let right_legend = format!(
|
||||
"{}{} ^P:pause ^D:debug ^R:reason ^K:kill | {} ",
|
||||
"{}{} ^P:pause ^R:reason ^K:kill | {} ",
|
||||
reason_indicator,
|
||||
proc_indicator,
|
||||
self.status.model,
|
||||
|
|
@ -961,15 +978,35 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn draw_debug(&self, frame: &mut Frame, size: Rect) {
|
||||
fn set_overlay(&mut self, screen: Overlay) {
|
||||
self.overlay = Some(screen);
|
||||
self.debug_scroll = 0;
|
||||
}
|
||||
|
||||
fn draw_agents(&self, frame: &mut Frame, size: Rect) {
|
||||
let mut lines: Vec<Line> = Vec::new();
|
||||
let section = Style::default().fg(Color::Yellow);
|
||||
|
||||
lines.push(Line::styled(
|
||||
" Debug (Ctrl+D or Esc to close, arrows/PgUp/PgDn to scroll)",
|
||||
Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
|
||||
));
|
||||
lines.push(Line::raw(""));
|
||||
lines.push(Line::styled("── Subconscious Agents ──", section));
|
||||
lines.push(Line::raw(""));
|
||||
lines.push(Line::raw(" (not yet wired — will show surface, observe, reflect, journal status)"));
|
||||
|
||||
let block = Block::default()
|
||||
.title_top(Line::from(SCREEN_LEGEND).left_aligned())
|
||||
.title_top(Line::from(" agents ").right_aligned())
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::Cyan));
|
||||
|
||||
let para = Paragraph::new(lines)
|
||||
.block(block)
|
||||
.scroll((self.debug_scroll, 0));
|
||||
frame.render_widget(para, size);
|
||||
}
|
||||
|
||||
fn draw_debug(&self, frame: &mut Frame, size: Rect) {
|
||||
let mut lines: Vec<Line> = Vec::new();
|
||||
let section = Style::default().fg(Color::Yellow);
|
||||
|
||||
// Model
|
||||
lines.push(Line::styled("── Model ──", section));
|
||||
|
|
@ -1024,7 +1061,8 @@ impl App {
|
|||
lines.push(Line::raw(format!(" Active tools: {}", self.active_tools.len())));
|
||||
|
||||
let block = Block::default()
|
||||
.title(" Debug ")
|
||||
.title_top(Line::from(SCREEN_LEGEND).left_aligned())
|
||||
.title_top(Line::from(" context ").right_aligned())
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::Cyan));
|
||||
|
||||
|
|
@ -1142,6 +1180,7 @@ fn draw_pane(
|
|||
title: &str,
|
||||
pane: &mut PaneState,
|
||||
is_active: bool,
|
||||
left_title: Option<&str>,
|
||||
) {
|
||||
let inner_height = area.height.saturating_sub(2);
|
||||
|
||||
|
|
@ -1151,10 +1190,16 @@ fn draw_pane(
|
|||
Style::default().fg(Color::DarkGray)
|
||||
};
|
||||
|
||||
let block = Block::default()
|
||||
.title(format!(" {} ", title))
|
||||
let mut block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_style);
|
||||
if let Some(left) = left_title {
|
||||
block = block
|
||||
.title_top(Line::from(left).left_aligned())
|
||||
.title_top(Line::from(format!(" {} ", title)).right_aligned());
|
||||
} else {
|
||||
block = block.title(format!(" {} ", title));
|
||||
}
|
||||
|
||||
let lines = pane.all_lines();
|
||||
let paragraph = Paragraph::new(lines)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue