1mod resolution;
2mod theme_registry;
3
4use gpui::{
5 color::Color,
6 elements::{ContainerStyle, ImageStyle, LabelStyle},
7 fonts::{HighlightStyle, TextStyle},
8 Border,
9};
10use serde::Deserialize;
11use std::{collections::HashMap, sync::Arc};
12
13pub use theme_registry::*;
14
15pub const DEFAULT_THEME_NAME: &'static str = "black";
16
17#[derive(Deserialize, Default)]
18pub struct Theme {
19 #[serde(default)]
20 pub name: String,
21 pub workspace: Workspace,
22 pub chat_panel: ChatPanel,
23 pub people_panel: PeoplePanel,
24 pub project_panel: ProjectPanel,
25 pub selector: Selector,
26 pub editor: EditorStyle,
27}
28
29#[derive(Deserialize, Default)]
30pub struct Workspace {
31 pub background: Color,
32 pub titlebar: Titlebar,
33 pub tab: Tab,
34 pub active_tab: Tab,
35 pub pane_divider: Border,
36 pub left_sidebar: Sidebar,
37 pub right_sidebar: Sidebar,
38 pub status_bar: StatusBar,
39}
40
41#[derive(Clone, Deserialize, Default)]
42pub struct Titlebar {
43 #[serde(flatten)]
44 pub container: ContainerStyle,
45 pub title: TextStyle,
46 pub avatar_width: f32,
47 pub offline_icon: OfflineIcon,
48 pub icon_color: Color,
49 pub avatar: ImageStyle,
50 pub sign_in_prompt: ContainedText,
51 pub hovered_sign_in_prompt: ContainedText,
52 pub outdated_warning: ContainedText,
53}
54
55#[derive(Clone, Deserialize, Default)]
56pub struct OfflineIcon {
57 #[serde(flatten)]
58 pub container: ContainerStyle,
59 pub width: f32,
60}
61
62#[derive(Clone, Deserialize, Default)]
63pub struct Tab {
64 pub height: f32,
65 #[serde(flatten)]
66 pub container: ContainerStyle,
67 #[serde(flatten)]
68 pub label: LabelStyle,
69 pub spacing: f32,
70 pub icon_width: f32,
71 pub icon_close: Color,
72 pub icon_close_active: Color,
73 pub icon_dirty: Color,
74 pub icon_conflict: Color,
75}
76
77#[derive(Deserialize, Default)]
78pub struct Sidebar {
79 #[serde(flatten)]
80 pub container: ContainerStyle,
81 pub width: f32,
82 pub item: SidebarItem,
83 pub active_item: SidebarItem,
84 pub resize_handle: ContainerStyle,
85}
86
87#[derive(Deserialize, Default)]
88pub struct SidebarItem {
89 pub icon_color: Color,
90 pub icon_size: f32,
91 pub height: f32,
92}
93
94#[derive(Deserialize, Default)]
95pub struct StatusBar {
96 #[serde(flatten)]
97 pub container: ContainerStyle,
98 pub height: f32,
99 pub cursor_position: TextStyle,
100 pub diagnostic_icon_size: f32,
101 pub diagnostic_icon_spacing: f32,
102 pub diagnostic_icon_color: Color,
103 pub diagnostic_message: TextStyle,
104}
105
106#[derive(Deserialize, Default)]
107pub struct ChatPanel {
108 #[serde(flatten)]
109 pub container: ContainerStyle,
110 pub message: ChatMessage,
111 pub pending_message: ChatMessage,
112 pub channel_select: ChannelSelect,
113 pub input_editor: InputEditorStyle,
114 pub sign_in_prompt: TextStyle,
115 pub hovered_sign_in_prompt: TextStyle,
116}
117
118#[derive(Debug, Deserialize, Default)]
119pub struct ProjectPanel {
120 #[serde(flatten)]
121 pub container: ContainerStyle,
122 pub entry: ProjectPanelEntry,
123 pub hovered_entry: ProjectPanelEntry,
124 pub selected_entry: ProjectPanelEntry,
125 pub hovered_selected_entry: ProjectPanelEntry,
126}
127
128#[derive(Debug, Deserialize, Default)]
129pub struct ProjectPanelEntry {
130 pub height: f32,
131 #[serde(flatten)]
132 pub container: ContainerStyle,
133 pub text: TextStyle,
134 pub icon_color: Color,
135 pub icon_size: f32,
136 pub icon_spacing: f32,
137}
138
139#[derive(Deserialize, Default)]
140pub struct PeoplePanel {
141 #[serde(flatten)]
142 pub container: ContainerStyle,
143 pub host_row_height: f32,
144 pub host_avatar: ImageStyle,
145 pub host_username: ContainedText,
146 pub tree_branch_width: f32,
147 pub tree_branch_color: Color,
148 pub shared_worktree: WorktreeRow,
149 pub hovered_shared_worktree: WorktreeRow,
150 pub unshared_worktree: WorktreeRow,
151 pub hovered_unshared_worktree: WorktreeRow,
152}
153
154#[derive(Deserialize, Default)]
155pub struct WorktreeRow {
156 #[serde(flatten)]
157 pub container: ContainerStyle,
158 pub height: f32,
159 pub name: ContainedText,
160 pub guest_avatar: ImageStyle,
161 pub guest_avatar_spacing: f32,
162}
163
164#[derive(Deserialize, Default)]
165pub struct ChatMessage {
166 #[serde(flatten)]
167 pub container: ContainerStyle,
168 pub body: TextStyle,
169 pub sender: ContainedText,
170 pub timestamp: ContainedText,
171}
172
173#[derive(Deserialize, Default)]
174pub struct ChannelSelect {
175 #[serde(flatten)]
176 pub container: ContainerStyle,
177 pub header: ChannelName,
178 pub item: ChannelName,
179 pub active_item: ChannelName,
180 pub hovered_item: ChannelName,
181 pub hovered_active_item: ChannelName,
182 pub menu: ContainerStyle,
183}
184
185#[derive(Deserialize, Default)]
186pub struct ChannelName {
187 #[serde(flatten)]
188 pub container: ContainerStyle,
189 pub hash: ContainedText,
190 pub name: TextStyle,
191}
192
193#[derive(Deserialize, Default)]
194pub struct Selector {
195 #[serde(flatten)]
196 pub container: ContainerStyle,
197 pub empty: ContainedLabel,
198 pub input_editor: InputEditorStyle,
199 pub item: ContainedLabel,
200 pub active_item: ContainedLabel,
201}
202
203#[derive(Clone, Debug, Deserialize, Default)]
204pub struct ContainedText {
205 #[serde(flatten)]
206 pub container: ContainerStyle,
207 #[serde(flatten)]
208 pub text: TextStyle,
209}
210
211#[derive(Deserialize, Default)]
212pub struct ContainedLabel {
213 #[serde(flatten)]
214 pub container: ContainerStyle,
215 #[serde(flatten)]
216 pub label: LabelStyle,
217}
218
219#[derive(Clone, Deserialize, Default)]
220pub struct EditorStyle {
221 pub text: TextStyle,
222 #[serde(default)]
223 pub placeholder_text: Option<TextStyle>,
224 pub background: Color,
225 pub selection: SelectionStyle,
226 pub gutter_background: Color,
227 pub active_line_background: Color,
228 pub highlighted_line_background: Color,
229 pub line_number: Color,
230 pub line_number_active: Color,
231 pub guest_selections: Vec<SelectionStyle>,
232 pub syntax: Arc<SyntaxTheme>,
233 pub error_diagnostic: DiagnosticStyle,
234 pub invalid_error_diagnostic: DiagnosticStyle,
235 pub warning_diagnostic: DiagnosticStyle,
236 pub invalid_warning_diagnostic: DiagnosticStyle,
237 pub information_diagnostic: DiagnosticStyle,
238 pub invalid_information_diagnostic: DiagnosticStyle,
239 pub hint_diagnostic: DiagnosticStyle,
240 pub invalid_hint_diagnostic: DiagnosticStyle,
241}
242
243#[derive(Copy, Clone, Deserialize, Default)]
244pub struct DiagnosticStyle {
245 pub text: Color,
246 #[serde(flatten)]
247 pub block: BlockStyle,
248}
249
250#[derive(Clone, Copy, Default, Deserialize)]
251pub struct SelectionStyle {
252 pub cursor: Color,
253 pub selection: Color,
254}
255
256#[derive(Clone, Deserialize, Default)]
257pub struct InputEditorStyle {
258 #[serde(flatten)]
259 pub container: ContainerStyle,
260 pub text: TextStyle,
261 #[serde(default)]
262 pub placeholder_text: Option<TextStyle>,
263 pub selection: SelectionStyle,
264}
265
266#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
267pub struct BlockStyle {
268 pub background: Option<Color>,
269 pub border: Option<Color>,
270 pub gutter_background: Option<Color>,
271 pub gutter_border: Option<Color>,
272}
273
274impl EditorStyle {
275 pub fn placeholder_text(&self) -> &TextStyle {
276 self.placeholder_text.as_ref().unwrap_or(&self.text)
277 }
278}
279
280impl InputEditorStyle {
281 pub fn as_editor(&self) -> EditorStyle {
282 EditorStyle {
283 text: self.text.clone(),
284 placeholder_text: self.placeholder_text.clone(),
285 background: self
286 .container
287 .background_color
288 .unwrap_or(Color::transparent_black()),
289 selection: self.selection,
290 gutter_background: Default::default(),
291 active_line_background: Default::default(),
292 highlighted_line_background: Default::default(),
293 line_number: Default::default(),
294 line_number_active: Default::default(),
295 guest_selections: Default::default(),
296 syntax: Default::default(),
297 error_diagnostic: Default::default(),
298 invalid_error_diagnostic: Default::default(),
299 warning_diagnostic: Default::default(),
300 invalid_warning_diagnostic: Default::default(),
301 information_diagnostic: Default::default(),
302 invalid_information_diagnostic: Default::default(),
303 hint_diagnostic: Default::default(),
304 invalid_hint_diagnostic: Default::default(),
305 }
306 }
307}
308
309#[derive(Default)]
310pub struct SyntaxTheme {
311 pub highlights: Vec<(String, HighlightStyle)>,
312}
313
314impl SyntaxTheme {
315 pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
316 Self { highlights }
317 }
318}
319
320impl<'de> Deserialize<'de> for SyntaxTheme {
321 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
322 where
323 D: serde::Deserializer<'de>,
324 {
325 let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
326
327 let mut result = Self::new(Vec::new());
328 for (key, style) in syntax_data {
329 match result
330 .highlights
331 .binary_search_by(|(needle, _)| needle.cmp(&key))
332 {
333 Ok(i) | Err(i) => {
334 result.highlights.insert(i, (key, style));
335 }
336 }
337 }
338
339 Ok(result)
340 }
341}