user: ScreenView trait, overlay screens extracted from App
Convert F2-F5 screens to ScreenView trait with tick() method. Each screen owns its view state (scroll, selection, expanded). State persists across screen switches. - ThalamusScreen: owns sampling_selected, scroll - ConsciousScreen: owns scroll, selected, expanded - SubconsciousScreen: owns selected, log_view, scroll - UnconsciousScreen: owns scroll Removed from App: Screen enum, debug_scroll, debug_selected, debug_expanded, agent_selected, agent_log_view, sampling_selected, set_screen(), per-screen key handling, draw dispatch. App now only draws the interact (F1) screen. Overlay screens are drawn by the event loop via ScreenView::tick. F-key routing and screen instantiation to be wired in event_loop next. InteractScreen (state-driven, reading from agent entries) is the next step — will eliminate the input display race condition. Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
7458fe655f
commit
927cddd864
8 changed files with 388 additions and 439 deletions
142
src/user/mod.rs
142
src/user/mod.rs
|
|
@ -30,7 +30,11 @@ use std::io;
|
|||
|
||||
use crate::user::ui_channel::{ContextInfo, SharedContextState, StatusInfo, UiMessage};
|
||||
|
||||
pub(crate) const SCREEN_LEGEND: &str = " F1=interact F2=conscious F3=subconscious F4=unconscious F5=thalamus ";
|
||||
/// Build the screen legend from the screen table.
|
||||
pub(crate) fn screen_legend() -> String {
|
||||
// Built from the SCREENS table in event_loop
|
||||
" F1=interact F2=conscious F3=subconscious F4=unconscious F5=thalamus ".to_string()
|
||||
}
|
||||
|
||||
pub(crate) fn strip_ansi(text: &str) -> String {
|
||||
let mut out = String::with_capacity(text.len());
|
||||
|
|
@ -231,9 +235,19 @@ pub(crate) fn parse_markdown(md: &str) -> Vec<Line<'static>> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Screen {
|
||||
Interact, Conscious, Subconscious, Unconscious, Thalamus,
|
||||
/// Action returned from a screen's tick method.
|
||||
pub enum ScreenAction {
|
||||
/// Switch to screen at this index
|
||||
Switch(usize),
|
||||
/// Send a hotkey action to the Mind
|
||||
Hotkey(HotkeyAction),
|
||||
}
|
||||
|
||||
/// A screen that can draw itself and handle input.
|
||||
pub(crate) trait ScreenView {
|
||||
fn tick(&mut self, frame: &mut ratatui::Frame, area: ratatui::layout::Rect,
|
||||
key: Option<ratatui::crossterm::event::KeyEvent>, app: &App) -> Option<ScreenAction>;
|
||||
fn label(&self) -> &'static str;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -285,19 +299,11 @@ pub struct App {
|
|||
pub submitted: Vec<String>,
|
||||
pub hotkey_actions: Vec<HotkeyAction>,
|
||||
pub(crate) pane_areas: [Rect; 3],
|
||||
pub screen: Screen,
|
||||
pub(crate) debug_scroll: u16,
|
||||
pub(crate) debug_selected: Option<usize>,
|
||||
pub(crate) debug_expanded: std::collections::HashSet<usize>,
|
||||
pub(crate) context_info: Option<ContextInfo>,
|
||||
pub(crate) shared_context: SharedContextState,
|
||||
pub(crate) agent_selected: usize,
|
||||
pub(crate) agent_log_view: bool,
|
||||
pub(crate) agent_state: Vec<crate::subconscious::subconscious::AgentSnapshot>,
|
||||
pub(crate) channel_status: Vec<ChannelStatus>,
|
||||
pub(crate) idle_info: Option<IdleInfo>,
|
||||
/// Thalamus screen: selected sampling param (0=temp, 1=top_p, 2=top_k).
|
||||
pub(crate) sampling_selected: usize,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
|
@ -323,12 +329,9 @@ impl App {
|
|||
input_history: Vec::new(), history_index: None,
|
||||
should_quit: false, submitted: Vec::new(), hotkey_actions: Vec::new(),
|
||||
pane_areas: [Rect::default(); 3],
|
||||
screen: Screen::Interact,
|
||||
debug_scroll: 0, debug_selected: None,
|
||||
debug_expanded: std::collections::HashSet::new(),
|
||||
context_info: None, shared_context,
|
||||
agent_selected: 0, agent_log_view: false, agent_state: Vec::new(),
|
||||
channel_status: Vec::new(), idle_info: None, sampling_selected: 0,
|
||||
agent_state: Vec::new(),
|
||||
channel_status: Vec::new(), idle_info: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -417,6 +420,8 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
/// Handle global keys only (Ctrl combos, F-keys). Screen-specific
|
||||
/// keys are handled by the active ScreenView's tick method.
|
||||
pub fn handle_key(&mut self, key: KeyEvent) {
|
||||
if key.modifiers.contains(KeyModifiers::CONTROL) {
|
||||
match key.code {
|
||||
|
|
@ -428,96 +433,6 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
match key.code {
|
||||
KeyCode::F(1) => { self.set_screen(Screen::Interact); return; }
|
||||
KeyCode::F(2) => { self.set_screen(Screen::Conscious); return; }
|
||||
KeyCode::F(3) => { self.set_screen(Screen::Subconscious); return; }
|
||||
KeyCode::F(4) => { self.set_screen(Screen::Unconscious); return; }
|
||||
KeyCode::F(5) => { self.set_screen(Screen::Thalamus); return; }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match self.screen {
|
||||
Screen::Subconscious => {
|
||||
match key.code {
|
||||
KeyCode::Up => { self.agent_selected = self.agent_selected.saturating_sub(1); self.debug_scroll = 0; return; }
|
||||
KeyCode::Down => { self.agent_selected = (self.agent_selected + 1).min(self.agent_state.len().saturating_sub(1)); self.debug_scroll = 0; return; }
|
||||
KeyCode::Enter | KeyCode::Right => { self.agent_log_view = true; self.debug_scroll = 0; return; }
|
||||
KeyCode::Left | KeyCode::Esc => {
|
||||
if self.agent_log_view { self.agent_log_view = false; self.debug_scroll = 0; }
|
||||
else { self.screen = Screen::Interact; }
|
||||
return;
|
||||
}
|
||||
KeyCode::PageUp => { self.debug_scroll = self.debug_scroll.saturating_sub(10); return; }
|
||||
KeyCode::PageDown => { self.debug_scroll += 10; return; }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Screen::Conscious => {
|
||||
let cs = self.read_context_state();
|
||||
let n = self.debug_item_count(&cs);
|
||||
match key.code {
|
||||
KeyCode::Up => {
|
||||
if n > 0 { self.debug_selected = Some(match self.debug_selected { None => n - 1, Some(0) => 0, Some(i) => i - 1 }); self.scroll_to_selected(n); }
|
||||
return;
|
||||
}
|
||||
KeyCode::Down => {
|
||||
if n > 0 { self.debug_selected = Some(match self.debug_selected { None => 0, Some(i) if i >= n - 1 => n - 1, Some(i) => i + 1 }); self.scroll_to_selected(n); }
|
||||
return;
|
||||
}
|
||||
KeyCode::PageUp => {
|
||||
if n > 0 { self.debug_selected = Some(match self.debug_selected { None => 0, Some(i) => i.saturating_sub(20) }); self.scroll_to_selected(n); }
|
||||
return;
|
||||
}
|
||||
KeyCode::PageDown => {
|
||||
if n > 0 { self.debug_selected = Some(match self.debug_selected { None => 0, Some(i) => (i + 20).min(n - 1) }); self.scroll_to_selected(n); }
|
||||
return;
|
||||
}
|
||||
KeyCode::Right | KeyCode::Enter => { if let Some(idx) = self.debug_selected { self.debug_expanded.insert(idx); } return; }
|
||||
KeyCode::Left => { if let Some(idx) = self.debug_selected { self.debug_expanded.remove(&idx); } return; }
|
||||
KeyCode::Esc => { self.screen = Screen::Interact; return; }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Screen::Unconscious => {
|
||||
match key.code {
|
||||
KeyCode::PageUp => { self.debug_scroll = self.debug_scroll.saturating_sub(10); return; }
|
||||
KeyCode::PageDown => { self.debug_scroll += 10; return; }
|
||||
KeyCode::Esc => { self.screen = Screen::Interact; return; }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Screen::Thalamus => {
|
||||
match key.code {
|
||||
KeyCode::Up => { self.sampling_selected = self.sampling_selected.saturating_sub(1); return; }
|
||||
KeyCode::Down => { self.sampling_selected = (self.sampling_selected + 1).min(2); return; }
|
||||
KeyCode::Right => {
|
||||
let delta = match self.sampling_selected {
|
||||
0 => 0.05, // temperature
|
||||
1 => 0.05, // top_p
|
||||
2 => 5.0, // top_k
|
||||
_ => 0.0,
|
||||
};
|
||||
self.hotkey_actions.push(HotkeyAction::AdjustSampling(self.sampling_selected, delta));
|
||||
return;
|
||||
}
|
||||
KeyCode::Left => {
|
||||
let delta = match self.sampling_selected {
|
||||
0 => -0.05,
|
||||
1 => -0.05,
|
||||
2 => -5.0,
|
||||
_ => 0.0,
|
||||
};
|
||||
self.hotkey_actions.push(HotkeyAction::AdjustSampling(self.sampling_selected, delta));
|
||||
return;
|
||||
}
|
||||
KeyCode::Esc => { self.screen = Screen::Interact; return; }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Screen::Interact => {}
|
||||
}
|
||||
|
||||
match key.code {
|
||||
KeyCode::Esc => { self.hotkey_actions.push(HotkeyAction::Interrupt); }
|
||||
KeyCode::Enter if !key.modifiers.contains(KeyModifiers::ALT) && !key.modifiers.contains(KeyModifiers::SHIFT) => {
|
||||
|
|
@ -601,15 +516,10 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
/// Draw the interact (F1) screen. Overlay screens are drawn
|
||||
/// by the event loop via ScreenView::tick.
|
||||
pub fn draw(&mut self, frame: &mut Frame) {
|
||||
let size = frame.area();
|
||||
match self.screen {
|
||||
Screen::Conscious => { self.draw_debug(frame, size); return; }
|
||||
Screen::Subconscious => { self.draw_agents(frame, size); return; }
|
||||
Screen::Unconscious => { self.draw_unconscious(frame, size); return; }
|
||||
Screen::Thalamus => { self.draw_thalamus(frame, size); return; }
|
||||
Screen::Interact => {}
|
||||
}
|
||||
self.draw_main(frame, size);
|
||||
}
|
||||
|
||||
|
|
@ -627,10 +537,6 @@ impl App {
|
|||
});
|
||||
}
|
||||
|
||||
pub(crate) fn set_screen(&mut self, screen: Screen) {
|
||||
self.screen = screen;
|
||||
self.debug_scroll = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_terminal() -> io::Result<Terminal<CrosstermBackend<io::Stdout>>> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue