1mod highlight_map;
2mod theme_registry;
3
4use anyhow::Result;
5use gpui::{
6 color::Color,
7 elements::{ContainerStyle, LabelStyle},
8 fonts::{HighlightStyle, TextStyle},
9};
10use serde::Deserialize;
11use std::collections::HashMap;
12
13pub use highlight_map::*;
14pub use theme_registry::*;
15
16pub const DEFAULT_THEME_NAME: &'static str = "dark";
17
18#[derive(Deserialize)]
19pub struct Theme {
20 #[serde(default)]
21 pub name: String,
22 pub workspace: Workspace,
23 pub chat_panel: ChatPanel,
24 pub selector: Selector,
25 pub editor: EditorStyle,
26 pub syntax: SyntaxTheme,
27}
28
29pub struct SyntaxTheme {
30 highlights: Vec<(String, HighlightStyle)>,
31}
32
33#[derive(Deserialize)]
34pub struct Workspace {
35 pub background: Color,
36 pub tab: Tab,
37 pub active_tab: Tab,
38 pub sidebar: Sidebar,
39 pub sidebar_icon: SidebarIcon,
40 pub active_sidebar_icon: SidebarIcon,
41}
42
43#[derive(Deserialize)]
44pub struct Tab {
45 #[serde(flatten)]
46 pub container: ContainerStyle,
47 #[serde(flatten)]
48 pub label: LabelStyle,
49 pub icon_close: Color,
50 pub icon_dirty: Color,
51 pub icon_conflict: Color,
52}
53
54#[derive(Deserialize)]
55pub struct Sidebar {
56 pub icons: ContainerStyle,
57 pub resize_handle: ContainerStyle,
58}
59
60#[derive(Deserialize)]
61pub struct SidebarIcon {
62 pub color: Color,
63}
64
65#[derive(Deserialize)]
66pub struct ChatPanel {
67 #[serde(flatten)]
68 pub container: ContainerStyle,
69 pub message: ChatMessage,
70 pub channel_select: ChannelSelect,
71 pub input_editor: InputEditorStyle,
72}
73
74#[derive(Deserialize)]
75pub struct ChatMessage {
76 #[serde(flatten)]
77 pub container: ContainerStyle,
78 pub body: TextStyle,
79 pub sender: ContainedText,
80 pub timestamp: ContainedText,
81}
82
83#[derive(Deserialize)]
84pub struct ChannelSelect {
85 #[serde(flatten)]
86 pub container: ContainerStyle,
87 pub header: ChannelName,
88 pub item: ChannelName,
89 pub active_item: ChannelName,
90 pub hovered_item: ChannelName,
91 pub hovered_active_item: ChannelName,
92 pub menu: ContainerStyle,
93}
94
95#[derive(Deserialize)]
96pub struct ChannelName {
97 #[serde(flatten)]
98 pub container: ContainerStyle,
99 pub hash: ContainedText,
100 pub name: TextStyle,
101}
102
103#[derive(Deserialize)]
104pub struct Selector {
105 #[serde(flatten)]
106 pub container: ContainerStyle,
107 #[serde(flatten)]
108 pub label: LabelStyle,
109
110 pub input_editor: InputEditorStyle,
111 pub item: ContainedLabel,
112 pub active_item: ContainedLabel,
113}
114
115#[derive(Deserialize)]
116pub struct ContainedText {
117 #[serde(flatten)]
118 pub container: ContainerStyle,
119 #[serde(flatten)]
120 pub text: TextStyle,
121}
122
123#[derive(Deserialize)]
124pub struct ContainedLabel {
125 #[serde(flatten)]
126 pub container: ContainerStyle,
127 #[serde(flatten)]
128 pub label: LabelStyle,
129}
130
131#[derive(Clone, Deserialize)]
132pub struct EditorStyle {
133 pub text: HighlightStyle,
134 #[serde(default)]
135 pub placeholder_text: HighlightStyle,
136 pub background: Color,
137 pub selection: SelectionStyle,
138 pub gutter_background: Color,
139 pub active_line_background: Color,
140 pub line_number: Color,
141 pub line_number_active: Color,
142 pub guest_selections: Vec<SelectionStyle>,
143}
144
145#[derive(Clone, Deserialize)]
146pub struct InputEditorStyle {
147 pub text: HighlightStyle,
148 pub placeholder_text: HighlightStyle,
149 pub background: Color,
150 pub selection: SelectionStyle,
151}
152
153#[derive(Clone, Copy, Default, Deserialize)]
154pub struct SelectionStyle {
155 pub cursor: Color,
156 pub selection: Color,
157}
158
159impl SyntaxTheme {
160 pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
161 Self { highlights }
162 }
163
164 pub fn highlight_style(&self, id: HighlightId) -> Option<HighlightStyle> {
165 self.highlights
166 .get(id.0 as usize)
167 .map(|entry| entry.1.clone())
168 }
169
170 #[cfg(test)]
171 pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
172 self.highlights.get(id.0 as usize).map(|e| e.0.as_str())
173 }
174}
175
176impl Default for EditorStyle {
177 fn default() -> Self {
178 Self {
179 text: HighlightStyle {
180 color: Color::from_u32(0xff0000ff),
181 font_properties: Default::default(),
182 },
183 placeholder_text: HighlightStyle {
184 color: Color::from_u32(0x00ff00ff),
185 font_properties: Default::default(),
186 },
187 background: Default::default(),
188 gutter_background: Default::default(),
189 active_line_background: Default::default(),
190 line_number: Default::default(),
191 line_number_active: Default::default(),
192 selection: Default::default(),
193 guest_selections: Default::default(),
194 }
195 }
196}
197
198impl InputEditorStyle {
199 pub fn as_editor(&self) -> EditorStyle {
200 EditorStyle {
201 text: self.text.clone(),
202 placeholder_text: self.placeholder_text.clone(),
203 background: self.background,
204 selection: self.selection,
205 ..Default::default()
206 }
207 }
208}
209
210impl<'de> Deserialize<'de> for SyntaxTheme {
211 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
212 where
213 D: serde::Deserializer<'de>,
214 {
215 let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
216
217 let mut result = Self::new(Vec::new());
218 for (key, style) in syntax_data {
219 match result
220 .highlights
221 .binary_search_by(|(needle, _)| needle.cmp(&key))
222 {
223 Ok(i) | Err(i) => {
224 result.highlights.insert(i, (key, style));
225 }
226 }
227 }
228
229 Ok(result)
230 }
231}