vtcode_core/utils/ansi/
core.rs1use super::*;
4
5use crate::config::ToolDisplayMode;
6use crate::config::loader::SyntaxHighlightingConfig;
7use crate::ui::tui::{InlineHandle, InlineMessageKind};
8use crate::utils::ansi_capabilities::AnsiCapabilities;
9use anstream::{AutoStream, ColorChoice};
10use std::io;
11use vtcode_commons::color_policy::{self, ColorOutputPolicySource};
12use vtcode_commons::ui_protocol::ToolOutputId;
13
14impl AnsiRenderer {
15 pub fn stdout() -> Self {
17 let mut capabilities = AnsiCapabilities::detect();
18 let policy = color_policy::current_color_output_policy();
19
20 if !policy.enabled {
21 capabilities.no_color = true;
22 capabilities.force_color = false;
23 } else if matches!(
24 policy.source,
25 ColorOutputPolicySource::CliColorAlways | ColorOutputPolicySource::ConfigOverride
26 ) {
27 capabilities.no_color = false;
28 capabilities.force_color = true;
29 }
30
31 let color = capabilities.supports_color();
32 let choice = if !color {
33 ColorChoice::Never
34 } else if matches!(
35 policy.source,
36 ColorOutputPolicySource::CliColorAlways | ColorOutputPolicySource::ConfigOverride
37 ) {
38 ColorChoice::Always
39 } else {
40 ColorChoice::Auto
41 };
42 Self {
43 writer: AutoStream::new(io::stdout(), choice),
44 buffer: String::with_capacity(1024),
45 color,
46 sink: None,
47 last_line_was_empty: false,
48 highlight_config: SyntaxHighlightingConfig::default(),
49 capabilities,
50 reasoning_visible: true,
51 screen_reader_mode: false,
52 show_diagnostics_in_transcript: false,
53 tool_display_mode: ToolDisplayMode::Compact,
54 diff_preview_mode: vtcode_commons::ui_protocol::DiffPreviewMode::Inline,
55 compact_command_group: None,
56 next_compact_group_id: 0,
57 pending_tool_output_anchor: None,
58 session_expand_anchor: None,
59 session_body: false,
60 }
61 }
62
63 pub fn with_inline_ui(handle: InlineHandle, highlight_config: SyntaxHighlightingConfig) -> Self {
65 let mut renderer = Self::stdout();
66 renderer.highlight_config = highlight_config.clone();
67 renderer.sink = Some(InlineSink::new(handle, highlight_config));
68 renderer.last_line_was_empty = false;
69 renderer
70 }
71
72 #[cfg(feature = "tui")]
74 pub fn program_status_wait(
75 &self,
76 kind: vtcode_commons::program_status::InteractionKind,
77 ) -> Option<vtcode_ui::tui::core_tui::types::ProgramStatusWaitGuard> {
78 self.sink.as_ref().map(|sink| sink.handle.program_status_wait(kind))
79 }
80
81 pub fn set_highlight_config(&mut self, config: SyntaxHighlightingConfig) {
83 if let Some(sink) = &mut self.sink {
84 sink.set_highlight_config(config.clone());
85 }
86 self.highlight_config = config;
87 }
88
89 pub fn set_next_tool_output_anchor(&mut self, id: ToolOutputId) {
95 self.pending_tool_output_anchor = Some(id);
96 }
97
98 pub fn set_session_expand_anchor(&mut self, id: ToolOutputId) {
102 self.session_expand_anchor = Some(id);
103 }
104
105 pub fn take_session_expand_anchor(&mut self) -> Option<ToolOutputId> {
107 self.session_expand_anchor.take()
108 }
109
110 pub fn set_session_body(&mut self, session_body: bool) {
113 self.session_body = session_body;
114 }
115
116 pub fn session_body_active(&self) -> bool {
118 self.session_body
119 }
120
121 pub fn was_previous_line_empty(&self) -> bool {
123 self.last_line_was_empty
124 }
125
126 pub(super) fn message_kind(style: MessageStyle) -> InlineMessageKind {
127 style.message_kind()
128 }
129
130 pub fn supports_streaming_markdown(&self) -> bool {
131 self.sink.is_some()
132 }
133
134 pub fn prefers_untruncated_output(&self) -> bool {
139 self.sink.is_some()
140 }
141
142 pub fn supports_inline_ui(&self) -> bool {
143 self.sink.is_some()
144 }
145
146 pub fn set_reasoning_visible(&mut self, visible: bool) {
147 self.reasoning_visible = visible;
148 }
149
150 pub fn reasoning_visible(&self) -> bool {
151 self.reasoning_visible
152 }
153
154 pub fn writes_to_inline_sink(&self) -> bool {
157 self.sink.is_some()
158 }
159
160 pub fn set_screen_reader_mode(&mut self, enabled: bool) {
161 self.screen_reader_mode = enabled;
162 }
163
164 pub fn set_show_diagnostics_in_transcript(&mut self, enabled: bool) {
165 self.show_diagnostics_in_transcript = if cfg!(debug_assertions) { enabled } else { false };
166 }
167
168 pub fn set_tool_display_mode(&mut self, mode: ToolDisplayMode) {
169 self.flush_compact_command_group();
170 self.tool_display_mode = match mode {
171 ToolDisplayMode::Compact => ToolDisplayMode::Compact,
172 ToolDisplayMode::Expanded | ToolDisplayMode::Unknown => ToolDisplayMode::Expanded,
173 };
174 }
175
176 pub fn tool_display_mode(&self) -> ToolDisplayMode {
177 self.tool_display_mode
178 }
179
180 pub fn is_compact_display(&self) -> bool {
186 self.tool_display_mode == ToolDisplayMode::Compact
187 }
188
189 pub fn set_diff_preview_mode(&mut self, mode: vtcode_commons::ui_protocol::DiffPreviewMode) {
190 self.diff_preview_mode = mode;
191 }
192
193 pub fn record_diff_review(&self, anchor: vtcode_commons::ui_protocol::DiffReviewAnchor) {
195 if let Some(sink) = &self.sink {
196 sink.handle.record_diff_review(anchor);
197 }
198 }
199
200 pub fn diff_preview_mode(&self) -> vtcode_commons::ui_protocol::DiffPreviewMode {
201 self.diff_preview_mode
202 }
203
204 pub fn toggle_tool_display_mode(&mut self) -> ToolDisplayMode {
205 self.flush_compact_command_group();
206 let next = match self.tool_display_mode {
207 ToolDisplayMode::Expanded | ToolDisplayMode::Unknown => ToolDisplayMode::Compact,
208 ToolDisplayMode::Compact => ToolDisplayMode::Expanded,
209 };
210 self.tool_display_mode = next;
211 next
212 }
213}