fission_command_ui/
lib.rs1mod actions;
2mod app;
3mod commands;
4mod components;
5mod density;
6mod publish;
7mod release_config;
8mod routes;
9mod screens;
10mod state;
11mod theme;
12
13use anyhow::Result;
14use std::path::PathBuf;
15
16#[cfg(test)]
17use theme::UiThemeMode;
18
19pub use app::CliUiApp;
20pub use publish::{
21 default_publish_options, run_publish_tui, run_publish_window, PublishApp, PublishUiOptions,
22 PublishUiState,
23};
24pub use release_config::{
25 run_release_config_tui, ReleaseConfigEditorApp, ReleaseConfigEditorOptions,
26 ReleaseConfigEditorState,
27};
28pub use state::UiState;
29
30#[derive(Clone, Debug)]
31pub struct UiOptions {
32 pub project_dir: PathBuf,
33 pub screenshot: Option<PathBuf>,
34 pub exit_after_render: bool,
35 pub width: Option<u16>,
36 pub height: Option<u16>,
37}
38
39pub fn run_ui(options: UiOptions) -> Result<()> {
40 let mut publish_options = publish::default_publish_options(options.project_dir);
41 publish_options.screenshot = options.screenshot;
42 publish_options.exit_after_render = options.exit_after_render;
43 publish_options.width = options.width;
44 publish_options.height = options.height;
45 run_publish_tui(publish_options)
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use crate::routes::UiRoute;
52 use crate::state::all_targets;
53 use fission_command_core::Target;
54 use std::path::PathBuf;
55
56 #[test]
57 fn cli_ui_renders_every_route_in_terminal_shell() {
58 for route in UiRoute::ALL {
59 let mut state = UiState {
60 project_dir: PathBuf::from("."),
61 project_name: "test-app".to_string(),
62 app_id: "com.example.test".to_string(),
63 project_status: "Project loaded".to_string(),
64 targets: all_targets().to_vec(),
65 selected_target: Some(Target::Web),
66 route,
67 host: "127.0.0.1".to_string(),
68 port: "8123".to_string(),
69 theme_mode: UiThemeMode::Dark,
70 ..Default::default()
71 };
72 state.devices = vec![crate::state::UiDevice {
73 id: "chrome".to_string(),
74 name: "Chrome/Chromium".to_string(),
75 target: Target::Web,
76 kind: "browser".to_string(),
77 status: "available".to_string(),
78 detail: String::new(),
79 available: true,
80 }];
81
82 let mut app = fission::terminal::TerminalApp::with_state(CliUiApp, state)
83 .with_sync_env(|state, env| {
84 env.theme = match state.theme_mode {
85 UiThemeMode::Dark => fission::theme::Theme::dark(),
86 UiThemeMode::Light => fission::theme::Theme::default(),
87 };
88 });
89 app.render_frame(120, 40).expect("route should render");
90 }
91 }
92
93 #[test]
94 fn cli_ui_renders_confirmation_dialog() {
95 let mut state = UiState {
96 project_dir: PathBuf::from("."),
97 project_name: "test-app".to_string(),
98 app_id: "com.example.test".to_string(),
99 project_status: "Project loaded".to_string(),
100 targets: all_targets().to_vec(),
101 selected_target: Some(Target::Web),
102 host: "127.0.0.1".to_string(),
103 port: "8123".to_string(),
104 theme_mode: UiThemeMode::Dark,
105 ..Default::default()
106 };
107 state.request_command_confirmation(crate::commands::UiCommand::RunSelected);
108
109 let mut app = fission::terminal::TerminalApp::with_state(CliUiApp, state).with_sync_env(
110 |state, env| {
111 env.theme = match state.theme_mode {
112 UiThemeMode::Dark => fission::theme::Theme::dark(),
113 UiThemeMode::Light => fission::theme::Theme::default(),
114 };
115 },
116 );
117 let frame = app.render_frame(120, 40).expect("dialog should render");
118 assert!(frame
119 .as_plain_text()
120 .contains("Confirm: run the selected target"));
121 assert!(frame.as_plain_text().contains("Cancel"));
122 }
123}