use super::views::TuiView;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PanelId {
StudioFiles,
StudioEditor,
StudioDag,
StudioDiagnostics,
RunnerMission,
RunnerDag,
RunnerNovanet,
RunnerReasoning,
ChatConversation,
ChatInput,
ChatContext,
}
impl PanelId {
pub fn panels_for_view(view: TuiView) -> &'static [PanelId] {
match view {
TuiView::Studio => &[
PanelId::StudioFiles,
PanelId::StudioEditor,
PanelId::StudioDag,
PanelId::StudioDiagnostics,
],
TuiView::Command => &[
PanelId::ChatConversation,
PanelId::ChatInput,
PanelId::ChatContext,
],
TuiView::Control => &[],
}
}
pub fn view(&self) -> TuiView {
match self {
PanelId::StudioFiles
| PanelId::StudioEditor
| PanelId::StudioDag
| PanelId::StudioDiagnostics => TuiView::Studio,
PanelId::ChatConversation | PanelId::ChatInput | PanelId::ChatContext => {
TuiView::Command
}
PanelId::RunnerMission
| PanelId::RunnerDag
| PanelId::RunnerNovanet
| PanelId::RunnerReasoning => TuiView::Command,
}
}
pub fn default_for_view(view: TuiView) -> PanelId {
match view {
TuiView::Studio => PanelId::StudioEditor,
TuiView::Command => PanelId::ChatInput,
TuiView::Control => PanelId::StudioEditor,
}
}
}
#[derive(Debug, Clone)]
pub struct FocusState {
current: PanelId,
stack: Vec<PanelId>,
}
impl FocusState {
pub fn new(initial: PanelId) -> Self {
Self {
current: initial,
stack: Vec::with_capacity(8),
}
}
pub fn current(&self) -> PanelId {
self.current
}
pub fn focus(&mut self, panel: PanelId) {
if panel != self.current {
self.stack.push(self.current);
self.current = panel;
if self.stack.len() > 16 {
self.stack.remove(0);
}
}
}
pub fn next_panel(&mut self) {
let view = self.current.view();
let panels = PanelId::panels_for_view(view);
if let Some(idx) = panels.iter().position(|&p| p == self.current) {
let next_idx = (idx + 1) % panels.len();
self.focus(panels[next_idx]);
}
}
pub fn prev_panel(&mut self) {
let view = self.current.view();
let panels = PanelId::panels_for_view(view);
if let Some(idx) = panels.iter().position(|&p| p == self.current) {
let prev_idx = if idx == 0 { panels.len() - 1 } else { idx - 1 };
self.focus(panels[prev_idx]);
}
}
pub fn back(&mut self) -> bool {
if let Some(prev) = self.stack.pop() {
self.current = prev;
true
} else {
false
}
}
pub fn is_focused(&self, panel: PanelId) -> bool {
self.current == panel
}
pub fn reset_to_view(&mut self, view: TuiView) {
let default = PanelId::default_for_view(view);
self.stack.clear();
self.current = default;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_focus_state() {
let state = FocusState::new(PanelId::StudioFiles);
assert_eq!(state.current(), PanelId::StudioFiles);
}
#[test]
fn test_focus_changes_current() {
let mut state = FocusState::new(PanelId::StudioFiles);
state.focus(PanelId::StudioDag);
assert_eq!(state.current(), PanelId::StudioDag);
}
#[test]
fn test_focus_pushes_to_stack() {
let mut state = FocusState::new(PanelId::StudioFiles);
state.focus(PanelId::StudioDag);
assert!(state.back());
assert_eq!(state.current(), PanelId::StudioFiles);
}
#[test]
fn test_next_panel_cycles() {
let mut state = FocusState::new(PanelId::StudioFiles);
state.next_panel();
assert_eq!(state.current(), PanelId::StudioEditor);
state.next_panel();
assert_eq!(state.current(), PanelId::StudioDag);
state.next_panel();
assert_eq!(state.current(), PanelId::StudioDiagnostics);
state.next_panel();
assert_eq!(state.current(), PanelId::StudioFiles); }
#[test]
fn test_prev_panel_cycles() {
let mut state = FocusState::new(PanelId::StudioFiles);
state.prev_panel();
assert_eq!(state.current(), PanelId::StudioDiagnostics); }
#[test]
fn test_reset_to_view() {
let mut state = FocusState::new(PanelId::StudioFiles);
state.focus(PanelId::StudioDag);
state.focus(PanelId::StudioEditor);
state.reset_to_view(TuiView::Command);
assert_eq!(state.current(), PanelId::ChatInput);
assert!(!state.back()); }
#[test]
fn test_panels_for_view() {
let studio_panels = PanelId::panels_for_view(TuiView::Studio);
assert_eq!(studio_panels.len(), 4);
let command_panels = PanelId::panels_for_view(TuiView::Command);
assert_eq!(command_panels.len(), 3);
let control_panels = PanelId::panels_for_view(TuiView::Control);
assert_eq!(control_panels.len(), 0);
}
#[test]
fn test_panel_view() {
assert_eq!(PanelId::StudioFiles.view(), TuiView::Studio);
assert_eq!(PanelId::StudioEditor.view(), TuiView::Studio);
assert_eq!(PanelId::StudioDag.view(), TuiView::Studio);
assert_eq!(PanelId::StudioDiagnostics.view(), TuiView::Studio);
assert_eq!(PanelId::ChatInput.view(), TuiView::Command);
assert_eq!(PanelId::RunnerDag.view(), TuiView::Command);
}
}