Skip to main content

vtcode_tui/core_tui/
session.rs

1use std::{collections::VecDeque, sync::Arc, time::Instant};
2
3#[cfg(test)]
4use anstyle::Color as AnsiColorEnum;
5use anstyle::RgbColor;
6use ratatui::crossterm::event::{
7    Event as CrosstermEvent, KeyCode, KeyEvent, KeyEventKind, MouseEvent, MouseEventKind,
8};
9
10use ratatui::{
11    Frame,
12    layout::{Constraint, Layout, Rect},
13    text::{Line, Span, Text},
14    widgets::{Clear, ListState, Widget},
15};
16use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
17
18use super::{
19    style::{measure_text_width, ratatui_color_from_ansi, ratatui_style_from_inline},
20    types::{
21        InlineCommand, InlineEvent, InlineHeaderContext, InlineMessageKind, InlineTextStyle,
22        InlineTheme,
23    },
24};
25use crate::config::constants::ui;
26use crate::ui::tui::widgets::SessionWidget;
27
28pub mod file_palette;
29mod header;
30mod impl_events;
31mod impl_init;
32mod impl_input;
33mod impl_layout;
34mod impl_logs;
35mod impl_render;
36mod impl_scroll;
37mod impl_style;
38mod input;
39mod input_manager;
40mod message;
41pub mod modal;
42pub mod mouse_selection;
43mod navigation;
44mod palette_renderer;
45mod queue;
46pub mod render;
47mod scroll;
48pub mod slash;
49pub mod slash_palette;
50pub mod styling;
51mod text_utils;
52mod transcript;
53pub mod utils;
54pub mod wrapping;
55
56// New modular components (refactored from main session.rs)
57mod command;
58mod editing;
59
60pub mod config;
61mod diff_preview;
62mod events;
63pub mod history_picker;
64mod message_renderer;
65mod messages;
66mod palette;
67mod reflow;
68mod reverse_search;
69mod spinner;
70mod state;
71pub mod terminal_capabilities;
72mod terminal_title;
73#[cfg(test)]
74mod tests;
75mod tool_renderer;
76mod trust;
77
78use self::file_palette::FilePalette;
79use self::history_picker::HistoryPickerState;
80use self::input_manager::InputManager;
81use self::message::{MessageLabels, MessageLine};
82use self::modal::{ModalState, WizardModalState};
83
84use self::config::AppearanceConfig;
85pub(crate) use self::input::status_requires_shimmer;
86use self::mouse_selection::MouseSelectionState;
87use self::queue::QueueOverlay;
88use self::scroll::ScrollManager;
89use self::slash_palette::SlashPalette;
90use self::spinner::{ShimmerState, ThinkingSpinner};
91use self::styling::SessionStyles;
92use self::transcript::TranscriptReflowCache;
93#[cfg(test)]
94use super::types::InlineHeaderHighlight;
95// TaskPlan integration intentionally omitted in this UI crate.
96use crate::ui::tui::log::{LogEntry, highlight_log_entry};
97
98const USER_PREFIX: &str = "";
99const PLACEHOLDER_COLOR: RgbColor =
100    RgbColor(ui::PLACEHOLDER_R, ui::PLACEHOLDER_G, ui::PLACEHOLDER_B);
101const MAX_LOG_LINES: usize = 256;
102const MAX_LOG_DRAIN_PER_TICK: usize = 256;
103
104#[derive(Clone, Debug)]
105struct CollapsedPaste {
106    line_index: usize,
107    full_text: String,
108}
109
110pub struct Session {
111    // --- Managers (Phase 2) ---
112    /// Manages user input, cursor, and command history
113    pub(crate) input_manager: InputManager,
114    /// Manages scroll state and viewport metrics
115    pub(crate) scroll_manager: ScrollManager,
116    user_scrolled: bool,
117
118    // --- Message Management ---
119    pub(crate) lines: Vec<MessageLine>,
120    collapsed_pastes: Vec<CollapsedPaste>,
121    pub(crate) theme: InlineTheme,
122    pub(crate) styles: SessionStyles,
123    pub(crate) appearance: AppearanceConfig,
124    pub(crate) header_context: InlineHeaderContext,
125    pub(crate) header_rows: u16,
126    pub(crate) labels: MessageLabels,
127
128    // --- Prompt/Input Display ---
129    prompt_prefix: String,
130    prompt_style: InlineTextStyle,
131    placeholder: Option<String>,
132    placeholder_style: Option<InlineTextStyle>,
133    pub(crate) input_status_left: Option<String>,
134    pub(crate) input_status_right: Option<String>,
135    input_compact_mode: bool,
136
137    // --- UI State ---
138    slash_palette: SlashPalette,
139    #[allow(dead_code)]
140    navigation_state: ListState,
141    input_enabled: bool,
142    cursor_visible: bool,
143    pub(crate) needs_redraw: bool,
144    pub(crate) needs_full_clear: bool,
145    /// Track if transcript content changed (not just scroll position)
146    pub(crate) transcript_content_changed: bool,
147    should_exit: bool,
148    scroll_cursor_steady_until: Option<Instant>,
149    last_shimmer_active: bool,
150    pub(crate) view_rows: u16,
151    pub(crate) input_height: u16,
152    pub(crate) transcript_rows: u16,
153    pub(crate) transcript_width: u16,
154    pub(crate) transcript_view_top: usize,
155    transcript_area: Option<Rect>,
156    input_area: Option<Rect>,
157
158    // --- Logging ---
159    log_receiver: Option<UnboundedReceiver<LogEntry>>,
160    log_lines: VecDeque<Arc<Text<'static>>>,
161    log_cached_text: Option<Arc<Text<'static>>>,
162    log_evicted: bool,
163    pub(crate) show_logs: bool,
164
165    // --- Rendering ---
166    transcript_cache: Option<TranscriptReflowCache>,
167    /// Cache of visible lines by (scroll_offset, width) - shared via Arc for zero-copy reads
168    /// Avoids expensive clone on cache hits
169    pub(crate) visible_lines_cache: Option<(usize, u16, Arc<Vec<Line<'static>>>)>,
170    pub(crate) queued_inputs: Vec<String>,
171    queue_overlay_cache: Option<QueueOverlay>,
172    queue_overlay_version: u64,
173    pub(crate) modal: Option<ModalState>,
174    wizard_modal: Option<WizardModalState>,
175    line_revision_counter: u64,
176    /// Track the first line that needs reflow/update to avoid O(N) scans
177    first_dirty_line: Option<usize>,
178    in_tool_code_fence: bool,
179
180    // --- Palette Management ---
181    pub(crate) file_palette: Option<FilePalette>,
182    pub(crate) file_palette_active: bool,
183
184    // --- Thinking Indicator ---
185    pub(crate) thinking_spinner: ThinkingSpinner,
186    pub(crate) shimmer_state: ShimmerState,
187
188    // --- Reverse Search ---
189    pub(crate) reverse_search_state: reverse_search::ReverseSearchState,
190
191    // --- History Picker (Ctrl+R fuzzy search) ---
192    pub(crate) history_picker_state: HistoryPickerState,
193
194    // --- PTY Session Management ---
195    pub(crate) active_pty_sessions: Option<Arc<std::sync::atomic::AtomicUsize>>,
196
197    // --- Clipboard for yank/paste operations ---
198    #[allow(dead_code)]
199    pub(crate) clipboard: String,
200
201    // --- Mouse Text Selection ---
202    pub(crate) mouse_selection: MouseSelectionState,
203
204    // --- Diff Preview Modal ---
205    pub(crate) diff_preview: Option<crate::ui::tui::types::DiffPreviewState>,
206
207    pub(crate) skip_confirmations: bool,
208
209    // --- Performance Caching ---
210    pub(crate) header_lines_cache: Option<Vec<Line<'static>>>,
211    pub(crate) header_height_cache: std::collections::HashMap<u16, u16>,
212    pub(crate) queued_inputs_preview_cache: Option<Vec<String>>,
213
214    // --- Terminal Title ---
215    /// Product/app name used in terminal title branding
216    pub(crate) app_name: String,
217    /// Workspace root path for dynamic title generation
218    pub(crate) workspace_root: Option<std::path::PathBuf>,
219    /// Last set terminal title to avoid redundant updates
220    last_terminal_title: Option<String>,
221
222    // --- Streaming State ---
223    /// Track if the assistant is currently streaming a final answer.
224    /// When true, user input should be queued instead of submitted immediately
225    /// to prevent race conditions with turn completion (see GitHub #12569).
226    pub(crate) is_streaming_final_answer: bool,
227}