mod chat;
pub mod command;
mod control;
mod home;
mod monitor;
mod settings;
mod studio;
mod view_trait;
mod wizard;
pub use chat::ChatView;
pub use settings::SettingsView;
pub use command::CommandView;
pub use control::ControlView;
pub use home::HomeView;
pub use monitor::MonitorView;
pub use studio::YamlEditorPanel;
pub use studio::StudioView;
pub use view_trait::View;
pub use wizard::WizardView;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum MissionTab {
#[default]
Progress,
TaskIO,
Output,
}
impl MissionTab {
pub fn next(&self) -> Self {
match self {
MissionTab::Progress => MissionTab::TaskIO,
MissionTab::TaskIO => MissionTab::Output,
MissionTab::Output => MissionTab::Progress,
}
}
pub fn title(&self) -> &'static str {
match self {
MissionTab::Progress => "Progress",
MissionTab::TaskIO => "IO",
MissionTab::Output => "Output",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DagTab {
#[default]
Graph,
Yaml,
}
impl DagTab {
pub fn next(&self) -> Self {
match self {
DagTab::Graph => DagTab::Yaml,
DagTab::Yaml => DagTab::Graph,
}
}
pub fn title(&self) -> &'static str {
match self {
DagTab::Graph => "Graph",
DagTab::Yaml => "YAML",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum NovanetTab {
#[default]
Summary,
FullJson,
}
impl NovanetTab {
pub fn next(&self) -> Self {
match self {
NovanetTab::Summary => NovanetTab::FullJson,
NovanetTab::FullJson => NovanetTab::Summary,
}
}
pub fn title(&self) -> &'static str {
match self {
NovanetTab::Summary => "Summary",
NovanetTab::FullJson => "Full JSON",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ReasoningTab {
#[default]
Turns,
Thinking,
Steps,
}
impl ReasoningTab {
pub fn next(&self) -> Self {
match self {
ReasoningTab::Turns => ReasoningTab::Thinking,
ReasoningTab::Thinking => ReasoningTab::Steps,
ReasoningTab::Steps => ReasoningTab::Turns,
}
}
pub fn title(&self) -> &'static str {
match self {
ReasoningTab::Turns => "Turns",
ReasoningTab::Thinking => "Thinking",
ReasoningTab::Steps => "Steps",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TuiView {
#[default]
Studio,
Command,
Control,
}
impl TuiView {
pub fn all() -> &'static [TuiView] {
&[TuiView::Studio, TuiView::Command, TuiView::Control]
}
pub fn is_auxiliary(&self) -> bool {
matches!(self, TuiView::Control)
}
pub fn all_including_auxiliary() -> &'static [TuiView] {
Self::all()
}
pub fn is_studio(&self) -> bool {
matches!(self, TuiView::Studio)
}
pub fn next(&self) -> Self {
match self {
TuiView::Studio => TuiView::Command,
TuiView::Command => TuiView::Control,
TuiView::Control => TuiView::Studio,
}
}
pub fn prev(&self) -> Self {
match self {
TuiView::Studio => TuiView::Control,
TuiView::Command => TuiView::Studio,
TuiView::Control => TuiView::Command,
}
}
pub fn number(&self) -> u8 {
match self {
TuiView::Studio => 1,
TuiView::Command => 2,
TuiView::Control => 3,
}
}
pub fn title(&self) -> &'static str {
match self {
TuiView::Studio => "NIKA STUDIO",
TuiView::Command => "NIKA COMMAND",
TuiView::Control => "NIKA CONTROL",
}
}
pub fn icon(&self) -> &'static str {
match self {
TuiView::Studio => "📝",
TuiView::Command => "▶",
TuiView::Control => "⚙",
}
}
pub fn shortcut(&self) -> char {
match self {
TuiView::Studio => 's',
TuiView::Command => 'c',
TuiView::Control => 'x',
}
}
pub fn toggle(&self) -> Self {
self.next()
}
}
pub use crate::tui::command::{McpAction, ModelProvider};
pub use crate::tui::tokens::CosmicVariant;
#[derive(Debug, Clone, PartialEq)]
pub enum ViewAction {
None,
Quit,
SwitchView(TuiView),
RunWorkflow(std::path::PathBuf),
OpenInStudio(std::path::PathBuf),
SendChatMessage(String),
Error(String),
ChatInfer(String),
ChatExec(String),
ChatFetch(String, String),
ChatInvoke(String, Option<String>, serde_json::Value),
ChatAgent(String, Option<u32>, bool, Vec<String>),
ChatModelSwitch(ModelProvider),
ChatMcp(McpAction),
ChatClear,
OpenControl,
ToggleTheme,
SetTheme(CosmicVariant),
VerifyProviders,
RefreshVerification,
ProviderSelectorConfirm { provider_id: String, model: String },
PullNativeModel(String),
DeleteNativeModel(String),
RefreshNativeModels,
ValidateWorkflow(std::path::PathBuf),
LaunchWizard,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tui_view_default() {
let view = TuiView::default();
assert_eq!(view, TuiView::Studio);
}
#[test]
fn test_tui_view_all_three_views() {
let views = TuiView::all();
assert_eq!(views.len(), 3);
assert_eq!(views[0], TuiView::Studio);
assert_eq!(views[1], TuiView::Command);
assert_eq!(views[2], TuiView::Control);
}
#[test]
fn test_tui_view_is_auxiliary() {
assert!(!TuiView::Studio.is_auxiliary());
assert!(!TuiView::Command.is_auxiliary());
assert!(TuiView::Control.is_auxiliary());
}
#[test]
fn test_tui_view_is_studio() {
assert!(TuiView::Studio.is_studio());
assert!(!TuiView::Command.is_studio());
assert!(!TuiView::Control.is_studio());
}
#[test]
fn test_tui_view_next_cycles_all_three() {
assert_eq!(TuiView::Studio.next(), TuiView::Command);
assert_eq!(TuiView::Command.next(), TuiView::Control);
assert_eq!(TuiView::Control.next(), TuiView::Studio);
}
#[test]
fn test_tui_view_prev_cycles_all_three() {
assert_eq!(TuiView::Studio.prev(), TuiView::Control);
assert_eq!(TuiView::Command.prev(), TuiView::Studio);
assert_eq!(TuiView::Control.prev(), TuiView::Command);
}
#[test]
fn test_tui_view_number_all_three() {
assert_eq!(TuiView::Studio.number(), 1);
assert_eq!(TuiView::Command.number(), 2);
assert_eq!(TuiView::Control.number(), 3);
}
#[test]
fn test_tui_view_titles_all_three() {
assert_eq!(TuiView::Studio.title(), "NIKA STUDIO");
assert_eq!(TuiView::Command.title(), "NIKA COMMAND");
assert_eq!(TuiView::Control.title(), "NIKA CONTROL");
}
#[test]
fn test_tui_view_icons_all_three() {
assert_eq!(TuiView::Studio.icon(), "📝");
assert_eq!(TuiView::Command.icon(), "▶");
assert_eq!(TuiView::Control.icon(), "⚙");
}
#[test]
fn test_tui_view_shortcuts_all_three() {
assert_eq!(TuiView::Studio.shortcut(), 's');
assert_eq!(TuiView::Command.shortcut(), 'c');
assert_eq!(TuiView::Control.shortcut(), 'x');
}
#[test]
fn test_view_action_switch_to_all_three_views() {
let actions = [
ViewAction::SwitchView(TuiView::Studio),
ViewAction::SwitchView(TuiView::Command),
ViewAction::SwitchView(TuiView::Control),
];
assert_eq!(actions.len(), 3);
}
#[test]
fn test_view_action_open_in_studio() {
let action = ViewAction::OpenInStudio(std::path::PathBuf::from("test.nika.yaml"));
match action {
ViewAction::OpenInStudio(path) => assert_eq!(path.to_str(), Some("test.nika.yaml")),
_ => panic!("Expected OpenInStudio"),
}
}
#[test]
fn test_view_action_send_chat_message() {
let action = ViewAction::SendChatMessage("Hello Nika".to_string());
match action {
ViewAction::SendChatMessage(msg) => assert_eq!(msg, "Hello Nika"),
_ => panic!("Expected SendChatMessage"),
}
}
#[test]
fn test_mission_tab_cycles() {
let tab = MissionTab::Progress;
assert_eq!(tab.next(), MissionTab::TaskIO);
assert_eq!(tab.next().next(), MissionTab::Output);
assert_eq!(tab.next().next().next(), MissionTab::Progress);
}
#[test]
fn test_dag_tab_cycles() {
let tab = DagTab::Graph;
assert_eq!(tab.next(), DagTab::Yaml);
assert_eq!(tab.next().next(), DagTab::Graph);
}
#[test]
fn test_novanet_tab_cycles() {
let tab = NovanetTab::Summary;
assert_eq!(tab.next(), NovanetTab::FullJson);
assert_eq!(tab.next().next(), NovanetTab::Summary);
}
#[test]
fn test_reasoning_tab_cycles() {
let tab = ReasoningTab::Turns;
assert_eq!(tab.next(), ReasoningTab::Thinking);
assert_eq!(tab.next().next(), ReasoningTab::Steps);
assert_eq!(tab.next().next().next(), ReasoningTab::Turns);
}
#[test]
fn test_tab_titles() {
assert_eq!(MissionTab::Progress.title(), "Progress");
assert_eq!(MissionTab::TaskIO.title(), "IO");
assert_eq!(MissionTab::Output.title(), "Output");
assert_eq!(DagTab::Graph.title(), "Graph");
assert_eq!(DagTab::Yaml.title(), "YAML");
assert_eq!(NovanetTab::Summary.title(), "Summary");
assert_eq!(NovanetTab::FullJson.title(), "Full JSON");
assert_eq!(ReasoningTab::Turns.title(), "Turns");
assert_eq!(ReasoningTab::Thinking.title(), "Thinking");
assert_eq!(ReasoningTab::Steps.title(), "Steps");
}
#[test]
fn test_tab_defaults() {
assert_eq!(MissionTab::default(), MissionTab::Progress);
assert_eq!(DagTab::default(), DagTab::Graph);
assert_eq!(NovanetTab::default(), NovanetTab::Summary);
assert_eq!(ReasoningTab::default(), ReasoningTab::Turns);
}
#[test]
fn test_view_action_set_theme_all_variants() {
let actions = [
ViewAction::SetTheme(CosmicVariant::CosmicDark),
ViewAction::SetTheme(CosmicVariant::CosmicLight),
ViewAction::SetTheme(CosmicVariant::CosmicViolet),
];
assert_eq!(actions.len(), 3);
}
#[test]
fn test_view_action_set_theme_variant_check() {
let action = ViewAction::SetTheme(CosmicVariant::CosmicLight);
match action {
ViewAction::SetTheme(variant) => {
assert_eq!(variant, CosmicVariant::CosmicLight);
}
_ => panic!("Expected SetTheme"),
}
}
}