1use anyhow::Result;
2use std::sync::Arc;
3use tokio::sync::mpsc;
4
5use crate::config::types::UiSurfacePreference;
6
7pub mod alternate_screen;
8pub mod log;
9pub mod panic_hook;
10pub mod runner;
11pub mod session;
12pub mod style;
13pub mod theme_parser;
14pub mod types;
15pub mod widgets;
16
17pub use style::{convert_style, theme_from_styles};
18pub use theme_parser::ThemeConfigParser;
19pub use types::{
20 ContentPart, DiffHunk, DiffPreviewState, EditingMode, InlineCommand, InlineEvent,
21 InlineEventCallback, InlineHandle, InlineHeaderContext, InlineHeaderHighlight, InlineListItem,
22 InlineListSearchConfig, InlineListSelection, InlineMessageKind, InlineSegment, InlineSession,
23 InlineTextStyle, InlineTheme, PlanConfirmationResult, PlanContent, PlanPhase, PlanStep,
24 SecurePromptConfig, SlashCommandItem, TrustMode, WizardModalMode, WizardStep,
25};
26
27use runner::{TuiOptions, run_tui};
28
29pub fn spawn_session(
30 theme: InlineTheme,
31 placeholder: Option<String>,
32 surface_preference: UiSurfacePreference,
33 inline_rows: u16,
34 event_callback: Option<InlineEventCallback>,
35 active_pty_sessions: Option<Arc<std::sync::atomic::AtomicUsize>>,
36 workspace_root: Option<std::path::PathBuf>,
37) -> Result<InlineSession> {
38 spawn_session_with_prompts(
39 theme,
40 placeholder,
41 surface_preference,
42 inline_rows,
43 event_callback,
44 active_pty_sessions,
45 crate::config::KeyboardProtocolConfig::default(),
46 workspace_root,
47 Vec::new(),
48 )
49}
50
51#[allow(clippy::too_many_arguments)]
53pub fn spawn_session_with_prompts(
54 theme: InlineTheme,
55 placeholder: Option<String>,
56 surface_preference: UiSurfacePreference,
57 inline_rows: u16,
58 event_callback: Option<InlineEventCallback>,
59 active_pty_sessions: Option<Arc<std::sync::atomic::AtomicUsize>>,
60 keyboard_protocol: crate::config::KeyboardProtocolConfig,
61 workspace_root: Option<std::path::PathBuf>,
62 slash_commands: Vec<SlashCommandItem>,
63) -> Result<InlineSession> {
64 spawn_session_with_prompts_and_options(
65 theme,
66 placeholder,
67 surface_preference,
68 inline_rows,
69 event_callback,
70 active_pty_sessions,
71 keyboard_protocol,
72 workspace_root,
73 slash_commands,
74 None,
75 "Agent TUI".to_string(),
76 None,
77 )
78}
79
80#[allow(clippy::too_many_arguments)]
82pub fn spawn_session_with_prompts_and_options(
83 theme: InlineTheme,
84 placeholder: Option<String>,
85 surface_preference: UiSurfacePreference,
86 inline_rows: u16,
87 event_callback: Option<InlineEventCallback>,
88 active_pty_sessions: Option<Arc<std::sync::atomic::AtomicUsize>>,
89 keyboard_protocol: crate::config::KeyboardProtocolConfig,
90 workspace_root: Option<std::path::PathBuf>,
91 slash_commands: Vec<SlashCommandItem>,
92 appearance: Option<crate::core_tui::session::config::AppearanceConfig>,
93 app_name: String,
94 non_interactive_hint: Option<String>,
95) -> Result<InlineSession> {
96 use crossterm::tty::IsTty;
97
98 if !std::io::stdin().is_tty() {
100 return Err(anyhow::anyhow!(
101 "cannot run interactive TUI: stdin is not a terminal (must be run in an interactive terminal)"
102 ));
103 }
104
105 let (command_tx, command_rx) = mpsc::unbounded_channel();
106 let (event_tx, event_rx) = mpsc::unbounded_channel();
107
108 tokio::spawn(async move {
109 if let Err(error) = run_tui(
110 command_rx,
111 event_tx,
112 TuiOptions {
113 theme,
114 placeholder,
115 surface_preference,
116 inline_rows,
117 show_logs: crate::ui::tui::panic_hook::is_debug_mode(),
118 log_theme: None,
119 event_callback,
120 active_pty_sessions,
121 keyboard_protocol,
122 workspace_root,
123 slash_commands,
124 appearance,
125 app_name: app_name.clone(),
126 },
127 )
128 .await
129 {
130 let error_msg = error.to_string();
131 if error_msg.contains("stdin is not a terminal") {
132 eprintln!("Error: Interactive TUI requires a proper terminal.");
133 if let Some(hint) = non_interactive_hint.as_deref() {
134 eprintln!("{}", hint);
135 } else {
136 eprintln!("Use a non-interactive mode in your host app for piped input.");
137 }
138 } else {
139 eprintln!("Error: TUI startup failed: {:#}", error);
140 }
141 tracing::error!(%error, "inline session terminated unexpectedly");
142 }
143 });
144
145 Ok(InlineSession {
146 handle: InlineHandle { sender: command_tx },
147 events: event_rx,
148 })
149}