Skip to content

Commit aac026f

Browse files
authored
feat(tui): implement live theme preview system (#125)
- Add preview_theme field to AppState for temporary theme previews - Add set_preview_theme, get_effective_theme_colors, start_theme_preview, cancel_theme_preview, and confirm_theme_preview methods - Add ModalResult::ActionContinue for live preview without closing modal - Add ModalAction variants: PreviewTheme, RevertTheme, ConfirmTheme - Update ThemeSelectorModal to emit preview actions on navigation - Add color swatch display in theme selector rows - Handle new theme actions in event loop - Update ThemePicker to use ThemeSelectorModal instead of interactive builder - Add comprehensive tests for new preview functionality
1 parent bc0d2cc commit aac026f

File tree

7 files changed

+245
-46
lines changed

7 files changed

+245
-46
lines changed

src/cortex-tui/src/app/methods.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,43 @@ impl AppState {
631631
}
632632
}
633633

634+
// ============================================================================
635+
// APPSTATE METHODS - Theme Preview
636+
// ============================================================================
637+
638+
impl AppState {
639+
/// Start previewing a theme.
640+
///
641+
/// This updates the cached theme colors to show the preview theme,
642+
/// without changing the active theme.
643+
pub fn start_theme_preview(&mut self, theme_name: &str) {
644+
self.set_preview_theme(Some(theme_name.to_string()));
645+
}
646+
647+
/// Cancel theme preview and revert to the original (active) theme.
648+
///
649+
/// Restores the cached colors to the active theme.
650+
pub fn cancel_theme_preview(&mut self) {
651+
self.set_preview_theme(None);
652+
}
653+
654+
/// Confirm the previewed theme as the active theme.
655+
///
656+
/// Makes the preview theme the new active theme and clears the preview state.
657+
pub fn confirm_theme_preview(&mut self) {
658+
if let Some(preview) = self.preview_theme.take() {
659+
self.active_theme = preview.clone();
660+
// Colors are already set to the preview theme, just need to clear preview state
661+
self.preview_theme = None;
662+
}
663+
}
664+
665+
/// Check if a theme preview is active.
666+
pub fn has_theme_preview(&self) -> bool {
667+
self.preview_theme.is_some()
668+
}
669+
}
670+
634671
// ============================================================================
635672
// APPSTATE METHODS - Operation Mode
636673
// ============================================================================

src/cortex-tui/src/app/state.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ pub struct AppState {
162162
pub input_mode: crate::interactive::InputMode,
163163
/// Active theme name
164164
pub active_theme: String,
165+
/// Preview theme name (for live theme preview in selector)
166+
pub preview_theme: Option<String>,
165167
/// Cached theme colors for quick access
166168
pub theme_colors: ThemeColors,
167169
/// Cached markdown theme for quick access
@@ -298,6 +300,7 @@ impl AppState {
298300
diff_scroll: 0,
299301
input_mode: crate::interactive::InputMode::Normal,
300302
active_theme: "dark".to_string(),
303+
preview_theme: None,
301304
theme_colors: ThemeColors::dark(),
302305
markdown_theme: MarkdownTheme::default(),
303306
compact_mode: false,
@@ -434,10 +437,32 @@ impl AppState {
434437
/// Change the active theme
435438
pub fn set_theme(&mut self, name: &str) {
436439
self.active_theme = name.to_string();
440+
self.preview_theme = None; // Clear any preview when setting the theme
437441
self.theme_colors = ThemeColors::from_name(name);
438442
self.markdown_theme = MarkdownTheme::from_name(name);
439443
}
440444

445+
/// Set a preview theme for live preview functionality
446+
///
447+
/// Updates the cached theme_colors to the preview theme colors.
448+
pub fn set_preview_theme(&mut self, theme: Option<String>) {
449+
self.preview_theme = theme.clone();
450+
// Update cached colors based on preview or active theme
451+
let effective_theme = theme.as_deref().unwrap_or(&self.active_theme);
452+
self.theme_colors = ThemeColors::from_name(effective_theme);
453+
self.markdown_theme = MarkdownTheme::from_name(effective_theme);
454+
}
455+
456+
/// Get the effective theme colors (preview if set, otherwise active)
457+
pub fn get_effective_theme_colors(&self) -> &ThemeColors {
458+
&self.theme_colors
459+
}
460+
461+
/// Get the name of the effective theme (preview if set, otherwise active)
462+
pub fn get_effective_theme_name(&self) -> &str {
463+
self.preview_theme.as_deref().unwrap_or(&self.active_theme)
464+
}
465+
441466
/// Get AdaptiveColors from the current theme
442467
pub fn adaptive_colors(&self) -> crate::ui::AdaptiveColors {
443468
crate::ui::AdaptiveColors::from_theme_colors(&self.theme_colors)

src/cortex-tui/src/modal/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ pub enum ModalResult {
9797
Close,
9898
/// Perform an action and close
9999
Action(ModalAction),
100+
/// Perform an action but keep the modal open (for live preview)
101+
ActionContinue(ModalAction),
100102
/// Push a new modal on top of this one
101103
Push(Box<dyn Modal>),
102104
/// Replace this modal with another
@@ -172,6 +174,14 @@ pub enum ModalAction {
172174
api_key: String,
173175
},
174176

177+
// Theme Preview Actions
178+
/// Preview a theme without applying it permanently
179+
PreviewTheme(String),
180+
/// Revert to the original theme (cancel preview)
181+
RevertTheme,
182+
/// Confirm and apply the previewed theme
183+
ConfirmTheme(String),
184+
175185
// Generic/Custom
176186
Custom(String),
177187
}
@@ -253,6 +263,10 @@ impl ModalStack {
253263
self.pop();
254264
ModalResult::Action(action)
255265
}
266+
ModalResult::ActionContinue(action) => {
267+
// Return the action but keep the modal open (for live preview)
268+
ModalResult::ActionContinue(action)
269+
}
256270
other => other,
257271
}
258272
} else {

0 commit comments

Comments
 (0)