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, FocusChangeCallback, InlineCommand,
21 InlineEvent, InlineEventCallback, InlineHandle, InlineHeaderContext, InlineHeaderHighlight,
22 InlineListItem, InlineListSearchConfig, InlineListSelection, InlineMessageKind, InlineSegment,
23 InlineSession, InlineTextStyle, InlineTheme, PlanConfirmationResult, PlanContent, PlanPhase,
24 PlanStep, 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 None,
71 active_pty_sessions,
72 keyboard_protocol,
73 workspace_root,
74 slash_commands,
75 None,
76 "Agent TUI".to_string(),
77 None,
78 )
79}
80
81#[allow(clippy::too_many_arguments)]
83pub fn spawn_session_with_prompts_and_options(
84 theme: InlineTheme,
85 placeholder: Option<String>,
86 surface_preference: UiSurfacePreference,
87 inline_rows: u16,
88 event_callback: Option<InlineEventCallback>,
89 focus_callback: Option<FocusChangeCallback>,
90 active_pty_sessions: Option<Arc<std::sync::atomic::AtomicUsize>>,
91 keyboard_protocol: crate::config::KeyboardProtocolConfig,
92 workspace_root: Option<std::path::PathBuf>,
93 slash_commands: Vec<SlashCommandItem>,
94 appearance: Option<session::config::AppearanceConfig>,
95 app_name: String,
96 non_interactive_hint: Option<String>,
97) -> Result<InlineSession> {
98 use crossterm::tty::IsTty;
99
100 if !std::io::stdin().is_tty() {
102 return Err(anyhow::anyhow!(
103 "cannot run interactive TUI: stdin is not a terminal (must be run in an interactive terminal)"
104 ));
105 }
106
107 let (command_tx, command_rx) = mpsc::unbounded_channel();
108 let (event_tx, event_rx) = mpsc::unbounded_channel();
109
110 tokio::spawn(async move {
111 if let Err(error) = run_tui(
112 command_rx,
113 event_tx,
114 TuiOptions {
115 theme,
116 placeholder,
117 surface_preference,
118 inline_rows,
119 show_logs: log::is_tui_log_capture_enabled(),
120 log_theme: None,
121 event_callback,
122 focus_callback,
123 active_pty_sessions,
124 keyboard_protocol,
125 workspace_root,
126 slash_commands,
127 appearance,
128 app_name: app_name.clone(),
129 },
130 )
131 .await
132 {
133 let error_msg = error.to_string();
134 if error_msg.contains("stdin is not a terminal") {
135 eprintln!("Error: Interactive TUI requires a proper terminal.");
136 if let Some(hint) = non_interactive_hint.as_deref() {
137 eprintln!("{}", hint);
138 } else {
139 eprintln!("Use a non-interactive mode in your host app for piped input.");
140 }
141 } else {
142 eprintln!("Error: TUI startup failed: {:#}", error);
143 }
144 tracing::error!(%error, "inline session terminated unexpectedly");
145 }
146 });
147
148 Ok(InlineSession {
149 handle: InlineHandle { sender: command_tx },
150 events: event_rx,
151 })
152}