consciousness/src/user/subconscious.rs

118 lines
4.1 KiB
Rust
Raw Normal View History

// subconscious_screen.rs — F3 subconscious agent overlay
use ratatui::{
layout::Rect,
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Paragraph, Wrap},
Frame,
2026-04-06 19:33:18 -04:00
crossterm::event::KeyCode,
};
2026-04-06 19:33:18 -04:00
use super::{App, ScreenView, screen_legend};
pub(crate) struct SubconsciousScreen {
selected: usize,
scroll: u16,
}
impl SubconsciousScreen {
pub fn new() -> Self {
Self { selected: 0, scroll: 0 }
}
}
impl ScreenView for SubconsciousScreen {
fn label(&self) -> &'static str { "subconscious" }
fn tick(&mut self, frame: &mut Frame, area: Rect,
2026-04-05 23:04:10 -04:00
events: &[ratatui::crossterm::event::Event], app: &mut App) {
for event in events {
if let ratatui::crossterm::event::Event::Key(key) = event {
if key.kind != ratatui::crossterm::event::KeyEventKind::Press { continue; }
match key.code {
KeyCode::Up => {
self.selected = self.selected.saturating_sub(1);
}
KeyCode::Down => {
self.selected = (self.selected + 1)
.min(app.agent_state.len().saturating_sub(1));
}
KeyCode::PageUp => { self.scroll = self.scroll.saturating_sub(20); }
KeyCode::PageDown => { self.scroll += 20; }
_ => {}
}
}
}
let mut lines: Vec<Line> = Vec::new();
let section = Style::default().fg(Color::Yellow);
let hint = Style::default().fg(Color::DarkGray).add_modifier(Modifier::ITALIC);
lines.push(Line::raw(""));
lines.push(Line::styled("── Subconscious Agents ──", section));
lines.push(Line::styled(" (↑/↓ select)", hint));
lines.push(Line::raw(""));
if app.agent_state.is_empty() {
lines.push(Line::styled(" (no agents loaded)", hint));
}
for (i, snap) in app.agent_state.iter().enumerate() {
let selected = i == self.selected;
let prefix = if selected { "" } else { " " };
let bg = if selected {
Style::default().bg(Color::DarkGray)
} else {
Style::default()
};
let status_spans = if snap.running {
vec![
Span::styled(
format!("{}{:<30}", prefix, snap.name),
bg.fg(Color::Green),
),
Span::styled("", bg.fg(Color::Green)),
Span::styled(
format!("phase: {} turn: {}", snap.current_phase, snap.turn),
bg,
),
]
} else {
let ago = snap.last_run_secs_ago
.map(|s| {
if s < 60.0 { format!("{:.0}s ago", s) }
else if s < 3600.0 { format!("{:.0}m ago", s / 60.0) }
else { format!("{:.1}h ago", s / 3600.0) }
})
.unwrap_or_else(|| "never".to_string());
vec![
Span::styled(
format!("{}{:<30}", prefix, snap.name),
bg.fg(Color::Gray),
),
Span::styled("", bg.fg(Color::DarkGray)),
Span::styled(
format!("idle last: {} walked: {}", ago, snap.walked_count),
bg.fg(Color::DarkGray),
),
]
};
lines.push(Line::from(status_spans));
}
let block = Block::default()
.title_top(Line::from(screen_legend()).left_aligned())
.title_top(Line::from(" subconscious ").right_aligned())
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::Cyan));
let para = Paragraph::new(lines)
.block(block)
.wrap(Wrap { trim: false })
.scroll((self.scroll, 0));
frame.render_widget(para, area);
}
}