1pub use vtcode_commons::ui_protocol::{
12 InlineHeaderContext, InlineHeaderHighlight, InlineHeaderStatusBadge, InlineHeaderStatusTone,
13 InlineLinkRange, InlineLinkTarget, InlineListItem, InlineListSearchConfig, InlineListSelection,
14 InlineMessageKind, InlineSegment, InlineTextStyle, InlineTheme, LayoutModeOverride,
15 PlanContent, PlanPhase, PlanStep, ReasoningDisplayMode, RewindAction, SecurePromptConfig,
16 SessionSurface, SlashCommandItem, UiMode, WizardModalMode, WizardStep, convert_style,
17 theme_from_color_fields,
18};
19
20pub use vtcode_commons::ui_protocol::KeyboardProtocolSettings;
21
22#[cfg(feature = "tui")]
25pub use vtcode_ui::tui::app::*;
26
27#[cfg(not(feature = "tui"))]
30mod headless {
31 use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
32
33 use super::{
34 InlineListItem, InlineListSearchConfig, InlineListSelection, InlineMessageKind,
35 InlineSegment, SecurePromptConfig,
36 };
37
38 use crate::ui::theme::ThemeStyles;
39
40 #[derive(Clone, Debug, PartialEq, Eq)]
42 pub enum InlineEvent {
43 Submit(String),
44 QueueSubmit(String),
45 Steer(String),
46 ProcessLatestQueued,
47 EditQueue,
48 Cancel,
49 Exit,
50 Interrupt,
51 Pause,
52 Resume,
53 BackgroundOperation,
54 ScrollLineUp,
55 ScrollLineDown,
56 ScrollPageUp,
57 ScrollPageDown,
58 OpenFileInEditor(String),
59 OpenUrl(String),
60 LaunchEditor,
61 ForceCancelPtySession,
62 RequestInlinePromptSuggestion(String),
63 HistoryPrevious,
64 HistoryNext,
65 }
66
67 #[derive(Clone, Debug)]
69 pub enum InlineCommand {
70 AppendLine {
71 kind: InlineMessageKind,
72 segments: Vec<InlineSegment>,
73 },
74 AppendPastedMessage {
75 kind: InlineMessageKind,
76 text: String,
77 line_count: usize,
78 },
79 Inline {
80 kind: InlineMessageKind,
81 segment: InlineSegment,
82 },
83 ReplaceLast {
84 count: usize,
85 kind: InlineMessageKind,
86 lines: Vec<Vec<InlineSegment>>,
87 },
88 ForceRedraw,
89 Shutdown,
90 ClearScreen,
91 CloseModal,
92 SetReasoningStage(Option<String>),
93 }
94
95 #[derive(Clone, Debug)]
97 pub struct InlineHandle {
98 sender: Option<UnboundedSender<InlineCommand>>,
99 }
100
101 impl InlineHandle {
102 pub fn new_for_tests(sender: UnboundedSender<InlineCommand>) -> Self {
103 Self {
104 sender: Some(sender),
105 }
106 }
107
108 fn send_command(&self, command: InlineCommand) {
109 if let Some(sender) = &self.sender {
110 let _ = sender.send(command);
111 }
112 }
113
114 pub fn append_line(&self, kind: InlineMessageKind, segments: Vec<InlineSegment>) {
115 self.send_command(InlineCommand::AppendLine { kind, segments });
116 }
117 pub fn append_pasted_message(
118 &self,
119 kind: InlineMessageKind,
120 text: String,
121 line_count: usize,
122 ) {
123 self.send_command(InlineCommand::AppendPastedMessage {
124 kind,
125 text,
126 line_count,
127 });
128 }
129 pub fn inline(&self, kind: InlineMessageKind, segment: InlineSegment) {
130 self.send_command(InlineCommand::Inline { kind, segment });
131 }
132 pub fn replace_last(
133 &self,
134 count: usize,
135 kind: InlineMessageKind,
136 lines: Vec<Vec<InlineSegment>>,
137 ) {
138 self.send_command(InlineCommand::ReplaceLast { count, kind, lines });
139 }
140 pub fn force_redraw(&self) {
141 self.send_command(InlineCommand::ForceRedraw);
142 }
143 pub fn shutdown(&self) {
144 self.send_command(InlineCommand::Shutdown);
145 }
146 pub fn clear_screen(&self) {
147 self.send_command(InlineCommand::ClearScreen);
148 }
149 pub fn show_modal(
150 &self,
151 _title: String,
152 _lines: Vec<String>,
153 _secure_prompt: Option<SecurePromptConfig>,
154 ) {
155 }
156 pub fn show_list_modal(
157 &self,
158 _title: String,
159 _lines: Vec<String>,
160 _items: Vec<InlineListItem>,
161 _selected: Option<InlineListSelection>,
162 _search: Option<InlineListSearchConfig>,
163 ) {
164 }
165 pub fn close_modal(&self) {
166 self.send_command(InlineCommand::CloseModal);
167 }
168 pub fn set_reasoning_stage(&self, stage: Option<String>) {
169 self.send_command(InlineCommand::SetReasoningStage(stage));
170 }
171 }
172
173 pub struct InlineSession {
175 pub handle: InlineHandle,
176 pub events: UnboundedReceiver<InlineEvent>,
177 }
178
179 impl InlineSession {
180 pub async fn next_event(&mut self) -> Option<InlineEvent> {
181 self.events.recv().await
182 }
183
184 pub fn clone_inline_handle(&self) -> InlineHandle {
185 self.handle.clone()
186 }
187 }
188
189 #[derive(Debug, Clone, Default)]
191 pub struct SessionAppearanceConfig {
192 pub theme: String,
193 pub ui_mode: super::UiMode,
194 pub show_sidebar: bool,
195 pub min_content_width: u16,
196 pub min_navigation_width: u16,
197 pub navigation_width_percent: u8,
198 pub transcript_bottom_padding: u16,
199 pub dim_completed_todos: bool,
200 pub message_block_spacing: u8,
201 pub layout_mode: super::LayoutModeOverride,
202 pub reasoning_display_mode: super::ReasoningDisplayMode,
203 pub reasoning_visible_default: bool,
204 pub vim_mode: bool,
205 pub screen_reader_mode: bool,
206 pub reduce_motion_mode: bool,
207 pub reduce_motion_keep_progress_animation: bool,
208 pub customization: (),
209 }
210
211 pub fn theme_from_styles(styles: &ThemeStyles) -> super::InlineTheme {
213 super::theme_from_color_fields(
214 styles.foreground,
215 styles.background,
216 styles.primary,
217 styles.secondary,
218 styles.tool,
219 styles.tool_detail,
220 styles.pty_output,
221 )
222 }
223}
224
225#[cfg(not(feature = "tui"))]
226pub use headless::*;