Skip to main content

ccf_gpui_widgets/
theme.rs

1//! Theme system for ccf-gpui-widgets
2//!
3//! Provides a `Theme` struct with sensible defaults and builder pattern for customization.
4//! Widgets can access the theme via a global context or per-widget override.
5//!
6//! # Example
7//!
8//! ```ignore
9//! use ccf_gpui_widgets::{Theme, Palette};
10//!
11//! // Use dark theme (default)
12//! let theme = Theme::dark();
13//!
14//! // Use light theme
15//! let theme = Theme::light();
16//!
17//! // Customize
18//! let theme = Theme::dark()
19//!     .with_accent(0x00ff00)
20//!     .with_border_focus(0x00ff00);
21//!
22//! // Create from minimal palette (derives all 52 colors from 7 seeds)
23//! let theme = Theme::from_palette(Palette::dark());
24//!
25//! // Customize brand colors with palette
26//! let theme = Theme::from_palette(
27//!     Palette::dark()
28//!         .with_primary(0xe94560)
29//!         .with_accent(0x0f3460)
30//! );
31//!
32//! // Fully custom palette
33//! let theme = Theme::from_palette(Palette {
34//!     bg: 0x1a1a2e,
35//!     text: 0xeaeaea,
36//!     primary: 0xe94560,
37//!     accent: 0x0f3460,
38//!     success: 0x4ecca3,
39//!     error: 0xff6b6b,
40//!     warning: 0xffc93c,
41//! });
42//!
43//! // Set globally
44//! cx.set_global(theme);
45//! ```
46
47use crate::utils::{is_dark, lighten, darken, mix};
48
49/// Minimal color palette for generating a full Theme
50///
51/// Contains 7 seed colors that are used to derive all 52 theme colors.
52/// This provides a simple way to create custom themes while maintaining
53/// visual consistency.
54///
55/// # Example
56///
57/// ```ignore
58/// use ccf_gpui_widgets::{Theme, Palette};
59///
60/// // Use preset palettes
61/// let dark_theme = Theme::from_palette(Palette::dark());
62/// let light_theme = Theme::from_palette(Palette::light());
63///
64/// // Customize brand colors
65/// let custom = Theme::from_palette(
66///     Palette::dark()
67///         .with_primary(0xe94560)
68///         .with_accent(0x0f3460)
69/// );
70/// ```
71#[derive(Clone, Copy, Debug)]
72pub struct Palette {
73    /// Base background color
74    pub bg: u32,
75    /// Primary text color
76    pub text: u32,
77    /// Primary accent color (buttons, active states)
78    pub primary: u32,
79    /// Secondary accent color (focus rings, selections)
80    pub accent: u32,
81    /// Success/positive status color
82    pub success: u32,
83    /// Error/negative status color
84    pub error: u32,
85    /// Warning status color
86    pub warning: u32,
87}
88
89impl Default for Palette {
90    fn default() -> Self {
91        Self::dark()
92    }
93}
94
95impl Palette {
96    /// Create a dark mode palette
97    pub fn dark() -> Self {
98        Self {
99            bg: 0x1e1e1e,
100            text: 0xffffff,
101            primary: 0x3b82f6,
102            accent: 0x0078d4,
103            success: 0x4CAF50,
104            error: 0xF44336,
105            warning: 0xF57C00,
106        }
107    }
108
109    /// Create a light mode palette
110    pub fn light() -> Self {
111        Self {
112            bg: 0xf5f5f5,
113            text: 0x1a1a1a,
114            primary: 0x3b82f6,
115            accent: 0x0078d4,
116            success: 0x4CAF50,
117            error: 0xF44336,
118            warning: 0xF57C00,
119        }
120    }
121
122    /// Set the background color
123    pub fn with_bg(mut self, color: u32) -> Self {
124        self.bg = color;
125        self
126    }
127
128    /// Set the text color
129    pub fn with_text(mut self, color: u32) -> Self {
130        self.text = color;
131        self
132    }
133
134    /// Set the primary accent color
135    pub fn with_primary(mut self, color: u32) -> Self {
136        self.primary = color;
137        self
138    }
139
140    /// Set the secondary accent color
141    pub fn with_accent(mut self, color: u32) -> Self {
142        self.accent = color;
143        self
144    }
145
146    /// Set the success color
147    pub fn with_success(mut self, color: u32) -> Self {
148        self.success = color;
149        self
150    }
151
152    /// Set the error color
153    pub fn with_error(mut self, color: u32) -> Self {
154        self.error = color;
155        self
156    }
157
158    /// Set the warning color
159    pub fn with_warning(mut self, color: u32) -> Self {
160        self.warning = color;
161        self
162    }
163}
164
165/// Theme configuration for widgets
166///
167/// All colors are stored as u32 hex values (0xRRGGBB format).
168/// Use with GPUI's `rgb()` macro: `rgb(theme.bg_primary)`
169#[derive(Clone, Copy, Debug)]
170pub struct Theme {
171    // Background colors
172    /// Main application background (darkest)
173    pub bg_primary: u32,
174    /// Panel/section background (slightly lighter)
175    pub bg_secondary: u32,
176    /// Input field background
177    pub bg_input: u32,
178    /// Input field background when hovered
179    pub bg_input_hover: u32,
180    /// Button/interactive element hover background
181    pub bg_hover: u32,
182    /// Section header background
183    pub bg_section_header: u32,
184    /// Section header hover background
185    pub bg_section_header_hover: u32,
186    /// White background for light-themed elements
187    pub bg_white: u32,
188    /// Light hover background
189    pub bg_light_hover: u32,
190
191    // Text colors
192    /// Primary text color
193    pub text_primary: u32,
194    /// Label text color
195    pub text_label: u32,
196    /// Section header text color
197    pub text_section_header: u32,
198    /// Value/content text color
199    pub text_value: u32,
200    /// Muted/secondary text color
201    pub text_muted: u32,
202    /// Placeholder text color
203    pub text_placeholder: u32,
204    /// Dimmed/disabled text color
205    pub text_dimmed: u32,
206    /// Icon text color
207    pub text_icon: u32,
208    /// Dark text (on light backgrounds)
209    pub text_dark: u32,
210    /// Black text
211    pub text_black: u32,
212
213    // Border colors
214    /// Standard border color
215    pub border_default: u32,
216    /// Checkbox/radio button border
217    pub border_checkbox: u32,
218    /// Input field border
219    pub border_input: u32,
220    /// Menu/dropdown border
221    pub border_menu: u32,
222    /// Focus/active border
223    pub border_focus: u32,
224    /// Focus border for colored backgrounds (e.g., primary buttons)
225    /// Should contrast with primary/accent colored elements
226    pub border_focus_on_color: u32,
227    /// Error border
228    pub border_error: u32,
229
230    // Accent colors
231    /// Primary accent color (buttons, checkboxes)
232    pub primary: u32,
233    /// Primary hover state
234    pub primary_hover: u32,
235    /// Primary active/pressed state
236    pub primary_active: u32,
237    /// Accent color (focus rings, selections)
238    pub accent: u32,
239
240    // Status colors
241    /// Success/positive color (green)
242    pub success: u32,
243    /// Error/negative color (red)
244    pub error: u32,
245    /// Warning color (orange)
246    pub warning: u32,
247    /// Validation error text
248    pub error_text: u32,
249
250    // Tooltip colors
251    /// Tooltip background
252    pub tooltip_bg: u32,
253    /// Tooltip border
254    pub tooltip_border: u32,
255    /// Tooltip text
256    pub tooltip_text: u32,
257
258    // Selection color (for text selection)
259    pub selection: u32,
260
261    // Button disabled state
262    /// Disabled button background
263    pub disabled_bg: u32,
264    /// Disabled button text
265    pub disabled_text: u32,
266
267    // Secondary button colors
268    /// Secondary button background
269    pub secondary_bg: u32,
270    /// Secondary button hover background
271    pub secondary_bg_hover: u32,
272    /// Secondary button active background
273    pub secondary_bg_active: u32,
274    /// Secondary button border
275    pub secondary_border: u32,
276
277    // Tab colors
278    /// Tab hover background
279    pub bg_tab_hover: u32,
280    /// Active tab border
281    pub border_tab_active: u32,
282
283    // Delete/remove button colors
284    /// Delete button background
285    pub delete_bg: u32,
286    /// Delete button hover background
287    pub delete_bg_hover: u32,
288
289    // Path display
290    /// Path display hover background
291    pub bg_path_hover: u32,
292}
293
294impl Default for Theme {
295    fn default() -> Self {
296        Self::dark()
297    }
298}
299
300// Implement Global trait so Theme can be stored in GPUI context
301impl gpui::Global for Theme {}
302
303impl Theme {
304    /// Create a dark theme (default)
305    pub fn dark() -> Self {
306        Self {
307            // Background colors
308            bg_primary: 0x1e1e1e,
309            bg_secondary: 0x252525,
310            bg_input: 0x2a2a2a,
311            bg_input_hover: 0x3a3a3a,
312            bg_hover: 0x4a4a4a,
313            bg_section_header: 0x363636,
314            bg_section_header_hover: 0x404040,
315            bg_white: 0xffffff,
316            bg_light_hover: 0xf0f0f0,
317
318            // Text colors
319            text_primary: 0xffffff,
320            text_label: 0xeeeeee,
321            text_section_header: 0xdddddd,
322            text_value: 0xcccccc,
323            text_muted: 0xaaaaaa,
324            text_placeholder: 0x999999,
325            text_dimmed: 0x888888,
326            text_icon: 0x666666,
327            text_dark: 0x333333,
328            text_black: 0x000000,
329
330            // Border colors
331            border_default: 0x444444,
332            border_checkbox: 0x666666,
333            border_input: 0x999999,
334            border_menu: 0xcccccc,
335            border_focus: 0x0078d4,
336            border_focus_on_color: 0xffffff, // White for contrast on colored backgrounds
337            border_error: 0x662222,
338
339            // Accent colors
340            primary: 0x3b82f6,
341            primary_hover: 0x2563eb,
342            primary_active: 0x1d4ed8,
343            accent: 0x0078d4,
344
345            // Status colors
346            success: 0x4CAF50,
347            error: 0xF44336,
348            warning: 0xF57C00,
349            error_text: 0xFF6B6B,
350
351            // Tooltip colors
352            tooltip_bg: 0x2a2a2a,
353            tooltip_border: 0x444444,
354            tooltip_text: 0xeeeeee,
355
356            // Selection color (dark blue for contrast with white text)
357            selection: 0x264F78,
358
359            // Button disabled state
360            disabled_bg: 0x6b7280,
361            disabled_text: 0x9ca3af,
362
363            // Secondary button colors
364            secondary_bg: 0x374151,
365            secondary_bg_hover: 0x4b5563,
366            secondary_bg_active: 0x1f2937,
367            secondary_border: 0x6b7280,
368
369            // Tab colors
370            bg_tab_hover: 0x323232,
371            border_tab_active: 0x007acc,
372
373            // Delete/remove button colors
374            delete_bg: 0x4a3a3a,
375            delete_bg_hover: 0x5a4a4a,
376
377            // Path display
378            bg_path_hover: 0x333333,
379        }
380    }
381
382    /// Create a light theme
383    pub fn light() -> Self {
384        Self {
385            // Background colors
386            bg_primary: 0xf5f5f5,
387            bg_secondary: 0xffffff,
388            bg_input: 0xffffff,
389            bg_input_hover: 0xf0f0f0,
390            bg_hover: 0xe0e0e0,
391            bg_section_header: 0xeeeeee,
392            bg_section_header_hover: 0xe5e5e5,
393            bg_white: 0xffffff,
394            bg_light_hover: 0xf5f5f5,
395
396            // Text colors
397            text_primary: 0x1a1a1a,
398            text_label: 0x333333,
399            text_section_header: 0x444444,
400            text_value: 0x555555,
401            text_muted: 0x777777,
402            text_placeholder: 0x999999,
403            text_dimmed: 0xaaaaaa,
404            text_icon: 0x888888,
405            text_dark: 0x333333,
406            text_black: 0x000000,
407
408            // Border colors
409            border_default: 0xcccccc,
410            border_checkbox: 0xaaaaaa,
411            border_input: 0x444444,
412            border_menu: 0xdddddd,
413            border_focus: 0x0078d4,
414            border_focus_on_color: 0xffffff, // White for contrast on colored backgrounds
415            border_error: 0xffcccc,
416
417            // Accent colors
418            primary: 0x3b82f6,
419            primary_hover: 0x2563eb,
420            primary_active: 0x1d4ed8,
421            accent: 0x0078d4,
422
423            // Status colors
424            success: 0x4CAF50,
425            error: 0xF44336,
426            warning: 0xF57C00,
427            error_text: 0xdc3545,
428
429            // Tooltip colors
430            tooltip_bg: 0xffffff,
431            tooltip_border: 0xaaaaaa,
432            tooltip_text: 0x333333,
433
434            // Selection color
435            selection: 0xADD6FF,
436
437            // Button disabled state
438            disabled_bg: 0xd1d5db,
439            disabled_text: 0x9ca3af,
440
441            // Secondary button colors
442            secondary_bg: 0xe5e7eb,
443            secondary_bg_hover: 0xd1d5db,
444            secondary_bg_active: 0xf3f4f6,
445            secondary_border: 0x9ca3af,
446
447            // Tab colors
448            bg_tab_hover: 0xe5e5e5,
449            border_tab_active: 0x0078d4,
450
451            // Delete/remove button colors
452            delete_bg: 0xfee2e2,
453            delete_bg_hover: 0xfecaca,
454
455            // Path display
456            bg_path_hover: 0xf5f5f5,
457        }
458    }
459
460    /// Create a theme from a minimal color palette
461    ///
462    /// This derives all 52 theme colors from just 7 seed colors, making it
463    /// easy to create cohesive custom themes.
464    ///
465    /// # Example
466    ///
467    /// ```ignore
468    /// use ccf_gpui_widgets::{Theme, Palette};
469    ///
470    /// // Use preset palettes
471    /// let dark_theme = Theme::from_palette(Palette::dark());
472    /// let light_theme = Theme::from_palette(Palette::light());
473    ///
474    /// // Customize brand colors
475    /// let custom = Theme::from_palette(
476    ///     Palette::dark()
477    ///         .with_primary(0xe94560)
478    ///         .with_accent(0x0f3460)
479    /// );
480    /// ```
481    pub fn from_palette(palette: Palette) -> Self {
482        let dark_mode = is_dark(palette.bg);
483
484        // Determine the opposite pole for mixing (white for dark mode, black for light mode)
485        let opposite = if dark_mode { 0xFFFFFF } else { 0x000000 };
486
487        // Background colors - derive from base bg
488        let bg_primary = palette.bg;
489        let bg_secondary = if dark_mode {
490            lighten(palette.bg, 0.03)
491        } else {
492            0xffffff // White for light mode secondary
493        };
494        let bg_input = if dark_mode {
495            lighten(palette.bg, 0.05)
496        } else {
497            0xffffff
498        };
499        let bg_input_hover = if dark_mode {
500            lighten(palette.bg, 0.12)
501        } else {
502            darken(palette.bg, 0.03)
503        };
504        let bg_hover = if dark_mode {
505            lighten(palette.bg, 0.18)
506        } else {
507            darken(palette.bg, 0.12)
508        };
509        let bg_section_header = if dark_mode {
510            lighten(palette.bg, 0.10)
511        } else {
512            darken(palette.bg, 0.04)
513        };
514        let bg_section_header_hover = if dark_mode {
515            lighten(palette.bg, 0.14)
516        } else {
517            darken(palette.bg, 0.08)
518        };
519
520        // Text colors - derive from base text, creating a scale toward opposite
521        let text_primary = palette.text;
522        let text_label = mix(palette.text, opposite, 0.07);
523        let text_section_header = mix(palette.text, opposite, 0.13);
524        let text_value = mix(palette.text, opposite, 0.20);
525        let text_muted = mix(palette.text, opposite, 0.33);
526        let text_placeholder = mix(palette.text, opposite, 0.40);
527        let text_dimmed = mix(palette.text, opposite, 0.47);
528        let text_icon = mix(palette.text, opposite, 0.60);
529
530        // Border colors - mix bg and text at various ratios
531        let border_default = mix(palette.bg, palette.text, 0.16);
532        let border_checkbox = mix(palette.bg, palette.text, 0.30);
533        let border_input = mix(palette.bg, palette.text, 0.50);
534        let border_menu = mix(palette.bg, palette.text, 0.70);
535        let border_focus = palette.accent;
536        let border_focus_on_color = 0xffffff; // Always white for contrast
537        let border_error = if dark_mode {
538            darken(palette.error, 0.60)
539        } else {
540            lighten(palette.error, 0.60)
541        };
542
543        // Primary button variants - darken for hover/active
544        let primary_hover = darken(palette.primary, 0.15);
545        let primary_active = darken(palette.primary, 0.25);
546
547        // Error text - lighter version of error for readability
548        let error_text = if dark_mode {
549            lighten(palette.error, 0.20)
550        } else {
551            darken(palette.error, 0.10)
552        };
553
554        // Tooltip colors - use secondary bg style
555        let tooltip_bg = bg_input;
556        let tooltip_border = border_default;
557        let tooltip_text = text_label;
558
559        // Selection color - semi-transparent accent
560        let selection = if dark_mode {
561            mix(palette.accent, palette.bg, 0.50)
562        } else {
563            lighten(palette.accent, 0.60)
564        };
565
566        // Disabled state - muted grays
567        let disabled_bg = mix(palette.bg, palette.text, 0.35);
568        let disabled_text = mix(palette.bg, palette.text, 0.50);
569
570        // Secondary button colors - neutral grays derived from bg
571        let secondary_bg = if dark_mode {
572            lighten(palette.bg, 0.12)
573        } else {
574            darken(palette.bg, 0.10)
575        };
576        let secondary_bg_hover = if dark_mode {
577            lighten(palette.bg, 0.20)
578        } else {
579            darken(palette.bg, 0.15)
580        };
581        let secondary_bg_active = if dark_mode {
582            darken(palette.bg, 0.05)
583        } else {
584            lighten(palette.bg, 0.02)
585        };
586        let secondary_border = disabled_bg;
587
588        // Tab colors
589        let bg_tab_hover = if dark_mode {
590            lighten(palette.bg, 0.08)
591        } else {
592            darken(palette.bg, 0.06)
593        };
594        let border_tab_active = palette.accent;
595
596        // Delete button colors - subtle error tint
597        let delete_bg = if dark_mode {
598            mix(palette.bg, palette.error, 0.15)
599        } else {
600            lighten(palette.error, 0.80)
601        };
602        let delete_bg_hover = if dark_mode {
603            mix(palette.bg, palette.error, 0.25)
604        } else {
605            lighten(palette.error, 0.70)
606        };
607
608        // Path hover - subtle highlight
609        let bg_path_hover = if dark_mode {
610            lighten(palette.bg, 0.08)
611        } else {
612            darken(palette.bg, 0.02)
613        };
614
615        Self {
616            // Background colors
617            bg_primary,
618            bg_secondary,
619            bg_input,
620            bg_input_hover,
621            bg_hover,
622            bg_section_header,
623            bg_section_header_hover,
624            bg_white: 0xffffff,
625            bg_light_hover: 0xf0f0f0,
626
627            // Text colors
628            text_primary,
629            text_label,
630            text_section_header,
631            text_value,
632            text_muted,
633            text_placeholder,
634            text_dimmed,
635            text_icon,
636            text_dark: 0x333333,
637            text_black: 0x000000,
638
639            // Border colors
640            border_default,
641            border_checkbox,
642            border_input,
643            border_menu,
644            border_focus,
645            border_focus_on_color,
646            border_error,
647
648            // Accent colors
649            primary: palette.primary,
650            primary_hover,
651            primary_active,
652            accent: palette.accent,
653
654            // Status colors
655            success: palette.success,
656            error: palette.error,
657            warning: palette.warning,
658            error_text,
659
660            // Tooltip colors
661            tooltip_bg,
662            tooltip_border,
663            tooltip_text,
664
665            // Selection color
666            selection,
667
668            // Button disabled state
669            disabled_bg,
670            disabled_text,
671
672            // Secondary button colors
673            secondary_bg,
674            secondary_bg_hover,
675            secondary_bg_active,
676            secondary_border,
677
678            // Tab colors
679            bg_tab_hover,
680            border_tab_active,
681
682            // Delete/remove button colors
683            delete_bg,
684            delete_bg_hover,
685
686            // Path display
687            bg_path_hover,
688        }
689    }
690
691    // Builder methods for customization
692
693    /// Set the accent color
694    pub fn with_accent(mut self, color: u32) -> Self {
695        self.accent = color;
696        self
697    }
698
699    /// Set the primary color
700    pub fn with_primary(mut self, color: u32) -> Self {
701        self.primary = color;
702        self
703    }
704
705    /// Set the primary hover color
706    pub fn with_primary_hover(mut self, color: u32) -> Self {
707        self.primary_hover = color;
708        self
709    }
710
711    /// Set the focus border color
712    pub fn with_border_focus(mut self, color: u32) -> Self {
713        self.border_focus = color;
714        self
715    }
716
717    /// Set the focus border color for colored backgrounds
718    pub fn with_border_focus_on_color(mut self, color: u32) -> Self {
719        self.border_focus_on_color = color;
720        self
721    }
722
723    /// Set the success color
724    pub fn with_success(mut self, color: u32) -> Self {
725        self.success = color;
726        self
727    }
728
729    /// Set the error color
730    pub fn with_error(mut self, color: u32) -> Self {
731        self.error = color;
732        self
733    }
734
735    /// Set the warning color
736    pub fn with_warning(mut self, color: u32) -> Self {
737        self.warning = color;
738        self
739    }
740
741    /// Set the primary background color
742    pub fn with_bg_primary(mut self, color: u32) -> Self {
743        self.bg_primary = color;
744        self
745    }
746
747    /// Set the input background color
748    pub fn with_bg_input(mut self, color: u32) -> Self {
749        self.bg_input = color;
750        self
751    }
752
753    /// Set the primary text color
754    pub fn with_text_primary(mut self, color: u32) -> Self {
755        self.text_primary = color;
756        self
757    }
758
759    // Additional background color builders
760
761    /// Set the secondary background color
762    pub fn with_bg_secondary(mut self, color: u32) -> Self {
763        self.bg_secondary = color;
764        self
765    }
766
767    /// Set the input hover background color
768    pub fn with_bg_input_hover(mut self, color: u32) -> Self {
769        self.bg_input_hover = color;
770        self
771    }
772
773    /// Set the hover background color
774    pub fn with_bg_hover(mut self, color: u32) -> Self {
775        self.bg_hover = color;
776        self
777    }
778
779    /// Set the section header background color
780    pub fn with_bg_section_header(mut self, color: u32) -> Self {
781        self.bg_section_header = color;
782        self
783    }
784
785    /// Set the section header hover background color
786    pub fn with_bg_section_header_hover(mut self, color: u32) -> Self {
787        self.bg_section_header_hover = color;
788        self
789    }
790
791    /// Set the white background color
792    pub fn with_bg_white(mut self, color: u32) -> Self {
793        self.bg_white = color;
794        self
795    }
796
797    /// Set the light hover background color
798    pub fn with_bg_light_hover(mut self, color: u32) -> Self {
799        self.bg_light_hover = color;
800        self
801    }
802
803    // Additional text color builders
804
805    /// Set the label text color
806    pub fn with_text_label(mut self, color: u32) -> Self {
807        self.text_label = color;
808        self
809    }
810
811    /// Set the section header text color
812    pub fn with_text_section_header(mut self, color: u32) -> Self {
813        self.text_section_header = color;
814        self
815    }
816
817    /// Set the value text color
818    pub fn with_text_value(mut self, color: u32) -> Self {
819        self.text_value = color;
820        self
821    }
822
823    /// Set the muted text color
824    pub fn with_text_muted(mut self, color: u32) -> Self {
825        self.text_muted = color;
826        self
827    }
828
829    /// Set the placeholder text color
830    pub fn with_text_placeholder(mut self, color: u32) -> Self {
831        self.text_placeholder = color;
832        self
833    }
834
835    /// Set the dimmed text color
836    pub fn with_text_dimmed(mut self, color: u32) -> Self {
837        self.text_dimmed = color;
838        self
839    }
840
841    /// Set the icon text color
842    pub fn with_text_icon(mut self, color: u32) -> Self {
843        self.text_icon = color;
844        self
845    }
846
847    /// Set the dark text color
848    pub fn with_text_dark(mut self, color: u32) -> Self {
849        self.text_dark = color;
850        self
851    }
852
853    /// Set the black text color
854    pub fn with_text_black(mut self, color: u32) -> Self {
855        self.text_black = color;
856        self
857    }
858
859    // Additional border color builders
860
861    /// Set the default border color
862    pub fn with_border_default(mut self, color: u32) -> Self {
863        self.border_default = color;
864        self
865    }
866
867    /// Set the checkbox border color
868    pub fn with_border_checkbox(mut self, color: u32) -> Self {
869        self.border_checkbox = color;
870        self
871    }
872
873    /// Set the input border color
874    pub fn with_border_input(mut self, color: u32) -> Self {
875        self.border_input = color;
876        self
877    }
878
879    /// Set the menu border color
880    pub fn with_border_menu(mut self, color: u32) -> Self {
881        self.border_menu = color;
882        self
883    }
884
885    /// Set the error border color
886    pub fn with_border_error(mut self, color: u32) -> Self {
887        self.border_error = color;
888        self
889    }
890
891    // Additional accent color builders
892
893    /// Set the primary active color
894    pub fn with_primary_active(mut self, color: u32) -> Self {
895        self.primary_active = color;
896        self
897    }
898
899    /// Set the error text color
900    pub fn with_error_text(mut self, color: u32) -> Self {
901        self.error_text = color;
902        self
903    }
904
905    // Tooltip color builders
906
907    /// Set the tooltip background color
908    pub fn with_tooltip_bg(mut self, color: u32) -> Self {
909        self.tooltip_bg = color;
910        self
911    }
912
913    /// Set the tooltip border color
914    pub fn with_tooltip_border(mut self, color: u32) -> Self {
915        self.tooltip_border = color;
916        self
917    }
918
919    /// Set the tooltip text color
920    pub fn with_tooltip_text(mut self, color: u32) -> Self {
921        self.tooltip_text = color;
922        self
923    }
924
925    // Selection color builder
926
927    /// Set the selection highlight color
928    pub fn with_selection(mut self, color: u32) -> Self {
929        self.selection = color;
930        self
931    }
932
933    // Disabled button state builders
934
935    /// Set the disabled button background color
936    pub fn with_disabled_bg(mut self, color: u32) -> Self {
937        self.disabled_bg = color;
938        self
939    }
940
941    /// Set the disabled button text color
942    pub fn with_disabled_text(mut self, color: u32) -> Self {
943        self.disabled_text = color;
944        self
945    }
946
947    // Secondary button builders
948
949    /// Set the secondary button background color
950    pub fn with_secondary_bg(mut self, color: u32) -> Self {
951        self.secondary_bg = color;
952        self
953    }
954
955    /// Set the secondary button hover background color
956    pub fn with_secondary_bg_hover(mut self, color: u32) -> Self {
957        self.secondary_bg_hover = color;
958        self
959    }
960
961    /// Set the secondary button active background color
962    pub fn with_secondary_bg_active(mut self, color: u32) -> Self {
963        self.secondary_bg_active = color;
964        self
965    }
966
967    /// Set the secondary button border color
968    pub fn with_secondary_border(mut self, color: u32) -> Self {
969        self.secondary_border = color;
970        self
971    }
972
973    // Tab builders
974
975    /// Set the tab hover background color
976    pub fn with_bg_tab_hover(mut self, color: u32) -> Self {
977        self.bg_tab_hover = color;
978        self
979    }
980
981    /// Set the active tab border color
982    pub fn with_border_tab_active(mut self, color: u32) -> Self {
983        self.border_tab_active = color;
984        self
985    }
986
987    // Delete button builders
988
989    /// Set the delete button background color
990    pub fn with_delete_bg(mut self, color: u32) -> Self {
991        self.delete_bg = color;
992        self
993    }
994
995    /// Set the delete button hover background color
996    pub fn with_delete_bg_hover(mut self, color: u32) -> Self {
997        self.delete_bg_hover = color;
998        self
999    }
1000
1001    // Path display builder
1002
1003    /// Set the path display hover background color
1004    pub fn with_bg_path_hover(mut self, color: u32) -> Self {
1005        self.bg_path_hover = color;
1006        self
1007    }
1008}
1009
1010/// Get the theme from context, falling back to dark theme if not set
1011pub fn get_theme(cx: &gpui::App) -> Theme {
1012    cx.try_global::<Theme>()
1013        .copied()
1014        .unwrap_or_else(Theme::dark)
1015}
1016
1017/// Get the theme from context or use a custom theme
1018pub fn get_theme_or(cx: &gpui::App, custom: Option<&Theme>) -> Theme {
1019    custom.copied().unwrap_or_else(|| get_theme(cx))
1020}