Skip to main content

vtcode_core/ui/
tui_compat.rs

1//! Compatibility layer between vtcode-core config types and TUI types.
2//!
3//! These conversions bridge the core configuration layer with the UI surface
4//! types shared via `vtcode-commons::ui_protocol`.
5
6use crate::config::KeyboardProtocolConfig;
7use crate::config::loader::VTCodeConfig;
8use crate::config::types::ReasoningEffortLevel;
9use crate::ui::slash::SlashCommandInfo;
10use crate::ui::theme::ThemeStyles;
11use crate::ui::tui::{
12    InlineTheme, KeyboardProtocolSettings, LayoutModeOverride, ReasoningDisplayMode,
13    SessionSurface, SlashCommandItem, UiMode,
14};
15#[cfg(feature = "tui")]
16use vtcode_tui::ui::theme::ThemeStyles as TuiThemeStyles;
17
18#[cfg(feature = "tui")]
19use crate::ui::tui::{FullscreenInteractionSettings, SessionAppearanceConfig};
20
21#[cfg(not(feature = "tui"))]
22use crate::ui::tui::SessionAppearanceConfig;
23
24/// Convert a config `ReasoningEffortLevel` to the string form used by
25/// `InlineListSelection::Reasoning`.
26pub fn reasoning_to_selection_string(level: ReasoningEffortLevel) -> String {
27    level.as_str().to_owned()
28}
29
30/// Convert a reasoning selection string back to a `ReasoningEffortLevel`.
31pub fn reasoning_from_selection_string(s: &str) -> ReasoningEffortLevel {
32    ReasoningEffortLevel::parse(s).unwrap_or_default()
33}
34
35pub fn to_tui_surface(preference: crate::config::types::UiSurfacePreference) -> SessionSurface {
36    match preference {
37        crate::config::types::UiSurfacePreference::Auto => SessionSurface::Auto,
38        crate::config::types::UiSurfacePreference::Alternate => SessionSurface::Alternate,
39        crate::config::types::UiSurfacePreference::Inline => SessionSurface::Inline,
40    }
41}
42
43pub fn to_tui_keyboard_protocol(keyboard: KeyboardProtocolConfig) -> KeyboardProtocolSettings {
44    KeyboardProtocolSettings {
45        enabled: keyboard.enabled,
46        mode: keyboard.mode,
47        disambiguate_escape_codes: keyboard.disambiguate_escape_codes,
48        report_event_types: keyboard.report_event_types,
49        report_alternate_keys: keyboard.report_alternate_keys,
50        report_all_keys: keyboard.report_all_keys,
51    }
52}
53
54#[cfg(feature = "tui")]
55pub fn to_tui_fullscreen(config: &VTCodeConfig) -> FullscreenInteractionSettings {
56    FullscreenInteractionSettings {
57        mouse_capture: config.ui.fullscreen.mouse_capture,
58        copy_on_select: config.ui.fullscreen.copy_on_select,
59        scroll_speed: config.ui.fullscreen.scroll_speed,
60    }
61}
62
63/// Build an [`InlineTheme`] from core [`ThemeStyles`].
64pub fn inline_theme_from_core_styles(styles: &ThemeStyles) -> InlineTheme {
65    crate::ui::tui::theme_from_color_fields(
66        styles.foreground,
67        styles.background,
68        styles.primary,
69        styles.secondary,
70        styles.tool,
71        styles.tool_detail,
72        styles.pty_output,
73    )
74}
75
76#[cfg(feature = "tui")]
77pub fn tui_theme_styles_from_core(styles: &ThemeStyles) -> TuiThemeStyles {
78    TuiThemeStyles {
79        info: styles.info,
80        error: styles.error,
81        output: styles.output,
82        response: styles.response,
83        reasoning: styles.reasoning,
84        tool: styles.tool,
85        tool_detail: styles.tool_detail,
86        tool_output: styles.tool_output,
87        pty_output: styles.pty_output,
88        status: styles.status,
89        mcp: styles.mcp,
90        user: styles.user,
91        primary: styles.primary,
92        secondary: styles.secondary,
93        foreground: styles.foreground,
94        background: styles.background,
95    }
96}
97
98#[cfg(not(feature = "tui"))]
99pub fn tui_theme_styles_from_core(styles: &ThemeStyles) -> ThemeStyles {
100    styles.clone()
101}
102
103pub fn to_tui_slash_commands(commands: &[SlashCommandInfo]) -> Vec<SlashCommandItem> {
104    commands
105        .iter()
106        .map(|cmd| SlashCommandItem::new(cmd.name, cmd.description))
107        .collect()
108}
109
110pub fn to_tui_appearance(config: &VTCodeConfig) -> SessionAppearanceConfig {
111    let reduce_motion_mode =
112        config.ui.reduce_motion_mode || matches!(config.tui.animations, Some(false));
113    SessionAppearanceConfig {
114        theme: config.agent.theme.clone(),
115        ui_mode: match config.ui.display_mode {
116            crate::config::UiDisplayMode::Full => UiMode::Full,
117            crate::config::UiDisplayMode::Minimal => UiMode::Minimal,
118            crate::config::UiDisplayMode::Focused => UiMode::Focused,
119        },
120        show_sidebar: config.ui.show_sidebar,
121        min_content_width: 40,
122        min_navigation_width: 20,
123        navigation_width_percent: 25,
124        transcript_bottom_padding: 0,
125        dim_completed_todos: config.ui.dim_completed_todos,
126        message_block_spacing: if config.ui.message_block_spacing {
127            1
128        } else {
129            0
130        },
131        layout_mode: match config.ui.layout_mode {
132            crate::config::LayoutModeOverride::Auto => LayoutModeOverride::Auto,
133            crate::config::LayoutModeOverride::Compact => LayoutModeOverride::Compact,
134            crate::config::LayoutModeOverride::Standard => LayoutModeOverride::Standard,
135            crate::config::LayoutModeOverride::Wide => LayoutModeOverride::Wide,
136        },
137        reasoning_display_mode: match config.ui.reasoning_display_mode {
138            crate::config::ReasoningDisplayMode::Always => ReasoningDisplayMode::Always,
139            crate::config::ReasoningDisplayMode::Toggle => ReasoningDisplayMode::Toggle,
140            crate::config::ReasoningDisplayMode::Hidden => ReasoningDisplayMode::Hidden,
141        },
142        reasoning_visible_default: config.ui.reasoning_visible_default,
143        vim_mode: config.ui.vim_mode,
144        readline_mode: false,
145        screen_reader_mode: config.ui.screen_reader_mode,
146        reduce_motion_mode,
147        reduce_motion_keep_progress_animation: config.ui.reduce_motion_keep_progress_animation,
148        hide_header: config.ui.hide_header,
149        customization: Default::default(),
150    }
151}