Unify screen dispatch — put InteractScreen in the screens array

active_screen is now the F-key number (1-based), dispatch is just
screens[active_screen - 1].tick() everywhere. Eliminates the
special-cased interact variable and duplicated if/else branching.

Also adds diff_mind_state() for dirty-flag tracking and gates the
bottom-of-loop render on dirty, avoiding redundant redraws.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-06 19:00:53 -04:00
parent 6e9ad04bfc
commit 0d20d66196

View file

@ -58,11 +58,10 @@ pub struct ContextInfo {
} }
/// Build the screen legend from screen labels. /// Build the screen legend from screen labels.
pub(crate) fn screen_legend_from(interact: &dyn ScreenView, screens: &[Box<dyn ScreenView>]) -> String { pub(crate) fn screen_legend_from(screens: &[Box<dyn ScreenView>]) -> String {
let mut parts = vec![format!("F1={}", interact.label())]; let parts: Vec<String> = screens.iter().enumerate()
for (i, s) in screens.iter().enumerate() { .map(|(i, s)| format!("F{}={}", i + 1, s.label()))
parts.push(format!("F{}={}", i + 2, s.label())); .collect();
}
format!(" {} ", parts.join(" ")) format!(" {} ", parts.join(" "))
} }
@ -357,18 +356,18 @@ pub async fn run(
} }
let notify_rx = crate::thalamus::channels::subscribe_all(); let notify_rx = crate::thalamus::channels::subscribe_all();
let mut interact = crate::user::chat::InteractScreen::new( // F1=chat, F2=conscious, F3=subconscious, F4=unconscious, F5=thalamus
mind.agent.clone(), mind.shared.clone(), mind_tx.clone(),
);
// Overlay screens: F2=conscious, F3=subconscious, F4=unconscious, F5=thalamus
let mut screens: Vec<Box<dyn tui::ScreenView>> = vec![ let mut screens: Vec<Box<dyn tui::ScreenView>> = vec![
Box::new(crate::user::chat::InteractScreen::new(
mind.agent.clone(), mind.shared.clone(), mind_tx.clone(),
)),
Box::new(crate::user::context::ConsciousScreen::new()), Box::new(crate::user::context::ConsciousScreen::new()),
Box::new(crate::user::subconscious::SubconsciousScreen::new()), Box::new(crate::user::subconscious::SubconsciousScreen::new()),
Box::new(crate::user::unconscious::UnconsciousScreen::new()), Box::new(crate::user::unconscious::UnconsciousScreen::new()),
Box::new(crate::user::thalamus::ThalamusScreen::new()), Box::new(crate::user::thalamus::ThalamusScreen::new()),
]; ];
let mut active_screen: usize = 0; // 0 = interact, 1-4 = overlay let mut active_screen: usize = 1; // F-key number
tui::set_screen_legend(tui::screen_legend_from(&interact, &screens)); tui::set_screen_legend(tui::screen_legend_from(&*screens));
let mut terminal = tui::init_terminal()?; let mut terminal = tui::init_terminal()?;
@ -397,7 +396,7 @@ pub async fn run(
{ {
let mut frame = terminal.get_frame(); let mut frame = terminal.get_frame();
let area = frame.area(); let area = frame.area();
interact.tick(&mut frame, area, &[], &mut app); screens[active_screen - 1].tick(&mut frame, area, &[], &mut app);
drop(frame); drop(frame);
terminal.flush()?; terminal.flush()?;
terminal.swap_buffers(); terminal.swap_buffers();
@ -465,11 +464,7 @@ pub async fn run(
if global_pos > 0 { if global_pos > 0 {
let mut frame = terminal.get_frame(); let mut frame = terminal.get_frame();
let area = frame.area(); let area = frame.area();
if active_screen == 0 { screens[active_screen - 1].tick(&mut frame, area, &pending[..global_pos], &mut app);
interact.tick(&mut frame, area, &pending[..global_pos], &mut app);
} else {
screens[active_screen - 1].tick(&mut frame, area, &pending[..global_pos], &mut app);
}
drop(frame); drop(frame);
terminal.flush()?; terminal.flush()?;
terminal.swap_buffers(); terminal.swap_buffers();
@ -486,11 +481,10 @@ pub async fn run(
match event { match event {
Event::Key(key) => { Event::Key(key) => {
if let KeyCode::F(n) = key.code { if let KeyCode::F(n) = key.code {
active_screen = match n { let idx = n as usize;
1 => 0, if idx >= 1 && idx <= screens.len() {
n @ 2..=5 if (n as usize - 2) < screens.len() => n as usize - 1, active_screen = idx;
_ => active_screen, }
};
} else if key.modifiers.contains(KeyModifiers::CONTROL) { } else if key.modifiers.contains(KeyModifiers::CONTROL) {
match key.code { match key.code {
KeyCode::Char('c') => { app.should_quit = true; } KeyCode::Char('c') => { app.should_quit = true; }
@ -510,11 +504,7 @@ pub async fn run(
if dirty { if dirty {
let mut frame = terminal.get_frame(); let mut frame = terminal.get_frame();
let area = frame.area(); let area = frame.area();
if active_screen == 0 { screens[active_screen - 1].tick(&mut frame, area, &[], &mut app);
interact.tick(&mut frame, area, &[], &mut app);
} else {
screens[active_screen - 1].tick(&mut frame, area, &[], &mut app);
}
drop(frame); drop(frame);
terminal.flush()?; terminal.flush()?;
terminal.swap_buffers(); terminal.swap_buffers();