vtcode_tui/core_tui/session/
impl_input.rs1use super::*;
2
3impl Session {
4 pub fn cursor(&self) -> usize {
5 self.input_manager.cursor()
6 }
7
8 pub fn set_input(&mut self, text: impl Into<String>) {
9 self.input_manager.set_content(text.into());
10 self.input_compact_mode = self.input_compact_placeholder().is_some();
11 self.mark_dirty();
12 }
13
14 pub fn set_cursor(&mut self, pos: usize) {
15 self.input_manager.set_cursor(pos);
16 self.mark_dirty();
17 }
18
19 pub fn process_key(&mut self, key: KeyEvent) -> Option<InlineEvent> {
20 events::process_key(self, key)
21 }
22
23 pub fn handle_command(&mut self, command: InlineCommand) {
24 if matches!(
26 &command,
27 InlineCommand::AppendLine { kind: InlineMessageKind::Agent, segments }
28 if !segments.is_empty()
29 ) || matches!(
30 &command,
31 InlineCommand::AppendPastedMessage { kind: InlineMessageKind::Agent, text, .. }
32 if !text.is_empty()
33 ) || matches!(
34 &command,
35 InlineCommand::Inline { kind: InlineMessageKind::Agent, segment }
36 if !segment.text.is_empty()
37 ) {
38 self.is_streaming_final_answer = true;
39 }
40
41 if let InlineCommand::SetInputStatus { left, right } = &command
43 && self.is_streaming_final_answer
44 && left.is_none()
45 && right.is_none()
46 {
47 self.is_streaming_final_answer = false;
48 }
49
50 match command {
51 InlineCommand::AppendLine { kind, segments } => {
52 self.clear_thinking_spinner_if_active(kind);
53 self.push_line(kind, segments);
54 self.transcript_content_changed = true;
55 }
56 InlineCommand::AppendPastedMessage {
57 kind,
58 text,
59 line_count,
60 } => {
61 self.clear_thinking_spinner_if_active(kind);
62 self.append_pasted_message(kind, text, line_count);
63 self.transcript_content_changed = true;
64 }
65 InlineCommand::Inline { kind, segment } => {
66 self.clear_thinking_spinner_if_active(kind);
67 self.append_inline(kind, segment);
68 self.transcript_content_changed = true;
69 }
70 InlineCommand::ReplaceLast { count, kind, lines } => {
71 self.clear_thinking_spinner_if_active(kind);
72 self.replace_last(count, kind, lines);
73 self.transcript_content_changed = true;
74 }
75 InlineCommand::SetPrompt { prefix, style } => {
76 self.prompt_prefix = prefix;
77 self.prompt_style = style;
78 self.ensure_prompt_style_color();
79 }
80 InlineCommand::SetPlaceholder { hint, style } => {
81 self.placeholder = hint;
82 self.placeholder_style = style;
83 }
84 InlineCommand::SetMessageLabels { agent, user } => {
85 self.labels.agent = agent.filter(|label| !label.is_empty());
86 self.labels.user = user.filter(|label| !label.is_empty());
87 self.invalidate_scroll_metrics();
88 }
89 InlineCommand::SetHeaderContext { context } => {
90 self.header_context = context;
91 self.needs_redraw = true;
92 }
93 InlineCommand::SetInputStatus { left, right } => {
94 self.input_status_left = left;
95 self.input_status_right = right;
96 if self.thinking_spinner.is_active {
97 self.thinking_spinner.stop();
98 }
99 self.needs_redraw = true;
100 }
101 InlineCommand::SetTheme { theme } => {
102 let previous_theme = self.theme.clone();
103 self.theme = theme.clone();
104 self.styles.set_theme(theme);
105 self.retint_lines_for_theme_change(&previous_theme);
106 self.ensure_prompt_style_color();
107 self.invalidate_transcript_cache();
108 }
109 InlineCommand::SetAppearance { appearance } => {
110 self.appearance = appearance;
111 self.invalidate_transcript_cache();
112 self.invalidate_scroll_metrics();
113 }
114 InlineCommand::SetQueuedInputs { entries } => {
115 self.set_queued_inputs_entries(entries);
116 self.mark_dirty();
117 }
118 InlineCommand::SetCursorVisible(value) => {
119 self.cursor_visible = value;
120 }
121 InlineCommand::SetInputEnabled(value) => {
122 self.input_enabled = value;
123 slash::update_slash_suggestions(self);
124 }
125 InlineCommand::SetInput(content) => {
126 if Self::is_error_content(&content) {
129 crate::utils::transcript::display_error(&content);
131 } else {
132 self.input_manager.set_content(content);
133 self.input_compact_mode = self.input_compact_placeholder().is_some();
134 self.scroll_manager.set_offset(0);
135 slash::update_slash_suggestions(self);
136 }
137 }
138 InlineCommand::ClearInput => {
139 command::clear_input(self);
140 }
141 InlineCommand::ForceRedraw => {
142 self.mark_dirty();
143 }
144 InlineCommand::ShowModal {
145 title,
146 lines,
147 secure_prompt,
148 } => {
149 self.show_modal(title, lines, secure_prompt);
150 }
151 InlineCommand::ShowListModal {
152 title,
153 lines,
154 items,
155 selected,
156 search,
157 } => {
158 self.show_list_modal(title, lines, items, selected, search);
159 }
160 InlineCommand::ShowWizardModal {
161 title,
162 steps,
163 current_step,
164 search,
165 mode,
166 } => {
167 self.show_wizard_modal(title, steps, current_step, search, mode);
168 }
169 InlineCommand::CloseModal => {
170 self.close_modal();
171 }
172 InlineCommand::LoadFilePalette { files, workspace } => {
173 self.load_file_palette(files, workspace);
174 }
175 InlineCommand::ClearScreen => {
176 self.clear_screen();
177 }
178 InlineCommand::SuspendEventLoop
179 | InlineCommand::ResumeEventLoop
180 | InlineCommand::ClearInputQueue => {
181 }
183 InlineCommand::SetEditingMode(mode) => {
184 self.header_context.editing_mode = mode;
185 self.needs_redraw = true;
186 }
187 InlineCommand::SetAutonomousMode(enabled) => {
188 self.header_context.autonomous_mode = enabled;
189 self.needs_redraw = true;
190 }
191 InlineCommand::ShowPlanConfirmation { plan } => {
192 command::show_plan_confirmation_modal(self, *plan);
193 }
194 InlineCommand::ShowDiffPreview {
195 file_path,
196 before,
197 after,
198 hunks,
199 current_hunk,
200 } => {
201 command::show_diff_preview(self, file_path, before, after, hunks, current_hunk);
202 }
203 InlineCommand::SetSkipConfirmations(skip) => {
204 self.skip_confirmations = skip;
205 if skip {
206 self.close_modal();
207 }
208 }
209 InlineCommand::Shutdown => {
210 self.request_exit();
211 }
212 InlineCommand::SetReasoningStage(stage) => {
213 self.header_context.reasoning_stage = stage;
214 self.invalidate_header_cache();
215 }
216 }
217 self.needs_redraw = true;
218 }
219}