1mod theme_registry;
2
3use gpui::{
4 color::Color,
5 elements::{ContainerStyle, ImageStyle, LabelStyle},
6 fonts::{HighlightStyle, TextStyle},
7 Border,
8};
9use serde::Deserialize;
10use std::{collections::HashMap, sync::Arc};
11
12pub use theme_registry::*;
13
14pub const DEFAULT_THEME_NAME: &'static str = "dark";
15
16#[derive(Deserialize, Default)]
17pub struct Theme {
18 #[serde(default)]
19 pub name: String,
20 pub workspace: Workspace,
21 pub chat_panel: ChatPanel,
22 pub contacts_panel: ContactsPanel,
23 pub project_panel: ProjectPanel,
24 pub command_palette: CommandPalette,
25 pub selector: Selector,
26 pub editor: Editor,
27 pub search: Search,
28 pub project_diagnostics: ProjectDiagnostics,
29 pub breadcrumbs: ContainedText,
30}
31
32#[derive(Deserialize, Default)]
33pub struct Workspace {
34 pub background: Color,
35 pub titlebar: Titlebar,
36 pub tab: Tab,
37 pub active_tab: Tab,
38 pub pane_divider: Border,
39 pub leader_border_opacity: f32,
40 pub leader_border_width: f32,
41 pub left_sidebar: Sidebar,
42 pub right_sidebar: Sidebar,
43 pub status_bar: StatusBar,
44 pub toolbar: Toolbar,
45 pub disconnected_overlay: ContainedText,
46 pub modal: ContainerStyle,
47}
48
49#[derive(Clone, Deserialize, Default)]
50pub struct Titlebar {
51 #[serde(flatten)]
52 pub container: ContainerStyle,
53 pub height: f32,
54 pub title: TextStyle,
55 pub avatar_width: f32,
56 pub avatar_ribbon: AvatarRibbon,
57 pub offline_icon: OfflineIcon,
58 pub share_icon_color: Color,
59 pub share_icon_active_color: Color,
60 pub avatar: ImageStyle,
61 pub sign_in_prompt: ContainedText,
62 pub hovered_sign_in_prompt: ContainedText,
63 pub outdated_warning: ContainedText,
64}
65
66#[derive(Clone, Deserialize, Default)]
67pub struct AvatarRibbon {
68 #[serde(flatten)]
69 pub container: ContainerStyle,
70 pub width: f32,
71 pub height: f32,
72}
73
74#[derive(Clone, Deserialize, Default)]
75pub struct OfflineIcon {
76 #[serde(flatten)]
77 pub container: ContainerStyle,
78 pub width: f32,
79 pub color: Color,
80}
81
82#[derive(Clone, Deserialize, Default)]
83pub struct Tab {
84 pub height: f32,
85 #[serde(flatten)]
86 pub container: ContainerStyle,
87 #[serde(flatten)]
88 pub label: LabelStyle,
89 pub spacing: f32,
90 pub icon_width: f32,
91 pub icon_close: Color,
92 pub icon_close_active: Color,
93 pub icon_dirty: Color,
94 pub icon_conflict: Color,
95}
96
97#[derive(Clone, Deserialize, Default)]
98pub struct Toolbar {
99 #[serde(flatten)]
100 pub container: ContainerStyle,
101 pub height: f32,
102 pub item_spacing: f32,
103}
104
105#[derive(Clone, Deserialize, Default)]
106pub struct Search {
107 #[serde(flatten)]
108 pub container: ContainerStyle,
109 pub editor: FindEditor,
110 pub invalid_editor: ContainerStyle,
111 pub option_button_group: ContainerStyle,
112 pub option_button: ContainedText,
113 pub active_option_button: ContainedText,
114 pub hovered_option_button: ContainedText,
115 pub active_hovered_option_button: ContainedText,
116 pub match_background: Color,
117 pub match_index: ContainedText,
118 pub results_status: TextStyle,
119 pub tab_icon_width: f32,
120 pub tab_icon_spacing: f32,
121}
122
123#[derive(Clone, Deserialize, Default)]
124pub struct FindEditor {
125 #[serde(flatten)]
126 pub input: FieldEditor,
127 pub min_width: f32,
128 pub max_width: f32,
129}
130
131#[derive(Deserialize, Default)]
132pub struct Sidebar {
133 #[serde(flatten)]
134 pub container: ContainerStyle,
135 pub width: f32,
136 pub item: SidebarItem,
137 pub active_item: SidebarItem,
138 pub resize_handle: ContainerStyle,
139}
140
141#[derive(Deserialize, Default)]
142pub struct SidebarItem {
143 pub icon_color: Color,
144 pub icon_size: f32,
145 pub height: f32,
146}
147
148#[derive(Deserialize, Default)]
149pub struct StatusBar {
150 #[serde(flatten)]
151 pub container: ContainerStyle,
152 pub height: f32,
153 pub item_spacing: f32,
154 pub cursor_position: TextStyle,
155 pub diagnostic_message: TextStyle,
156 pub lsp_message: TextStyle,
157 pub auto_update_progress_message: TextStyle,
158 pub auto_update_done_message: TextStyle,
159}
160
161#[derive(Deserialize, Default)]
162pub struct ChatPanel {
163 #[serde(flatten)]
164 pub container: ContainerStyle,
165 pub message: ChatMessage,
166 pub pending_message: ChatMessage,
167 pub channel_select: ChannelSelect,
168 pub input_editor: FieldEditor,
169 pub sign_in_prompt: TextStyle,
170 pub hovered_sign_in_prompt: TextStyle,
171}
172
173#[derive(Debug, Deserialize, Default)]
174pub struct ProjectPanel {
175 #[serde(flatten)]
176 pub container: ContainerStyle,
177 pub entry: ProjectPanelEntry,
178 pub hovered_entry: ProjectPanelEntry,
179 pub selected_entry: ProjectPanelEntry,
180 pub hovered_selected_entry: ProjectPanelEntry,
181}
182
183#[derive(Debug, Deserialize, Default)]
184pub struct ProjectPanelEntry {
185 pub height: f32,
186 #[serde(flatten)]
187 pub container: ContainerStyle,
188 pub text: TextStyle,
189 pub icon_color: Color,
190 pub icon_size: f32,
191 pub icon_spacing: f32,
192}
193
194#[derive(Debug, Deserialize, Default)]
195pub struct CommandPalette {
196 pub key: ContainedLabel,
197 pub keystroke_spacing: f32,
198}
199
200#[derive(Deserialize, Default)]
201pub struct ContactsPanel {
202 #[serde(flatten)]
203 pub container: ContainerStyle,
204 pub host_row_height: f32,
205 pub host_avatar: ImageStyle,
206 pub host_username: ContainedText,
207 pub tree_branch_width: f32,
208 pub tree_branch_color: Color,
209 pub shared_project: WorktreeRow,
210 pub hovered_shared_project: WorktreeRow,
211 pub unshared_project: WorktreeRow,
212 pub hovered_unshared_project: WorktreeRow,
213}
214
215#[derive(Deserialize, Default)]
216pub struct WorktreeRow {
217 #[serde(flatten)]
218 pub container: ContainerStyle,
219 pub height: f32,
220 pub name: ContainedText,
221 pub guest_avatar: ImageStyle,
222 pub guest_avatar_spacing: f32,
223}
224
225#[derive(Deserialize, Default)]
226pub struct ChatMessage {
227 #[serde(flatten)]
228 pub container: ContainerStyle,
229 pub body: TextStyle,
230 pub sender: ContainedText,
231 pub timestamp: ContainedText,
232}
233
234#[derive(Deserialize, Default)]
235pub struct ChannelSelect {
236 #[serde(flatten)]
237 pub container: ContainerStyle,
238 pub header: ChannelName,
239 pub item: ChannelName,
240 pub active_item: ChannelName,
241 pub hovered_item: ChannelName,
242 pub hovered_active_item: ChannelName,
243 pub menu: ContainerStyle,
244}
245
246#[derive(Deserialize, Default)]
247pub struct ChannelName {
248 #[serde(flatten)]
249 pub container: ContainerStyle,
250 pub hash: ContainedText,
251 pub name: TextStyle,
252}
253
254#[derive(Deserialize, Default)]
255pub struct Selector {
256 #[serde(flatten)]
257 pub container: ContainerStyle,
258 pub empty: ContainedLabel,
259 pub input_editor: FieldEditor,
260 pub item: ContainedLabel,
261 pub active_item: ContainedLabel,
262}
263
264#[derive(Clone, Debug, Deserialize, Default)]
265pub struct ContainedText {
266 #[serde(flatten)]
267 pub container: ContainerStyle,
268 #[serde(flatten)]
269 pub text: TextStyle,
270}
271
272#[derive(Clone, Debug, Deserialize, Default)]
273pub struct ContainedLabel {
274 #[serde(flatten)]
275 pub container: ContainerStyle,
276 #[serde(flatten)]
277 pub label: LabelStyle,
278}
279
280#[derive(Clone, Deserialize, Default)]
281pub struct ProjectDiagnostics {
282 #[serde(flatten)]
283 pub container: ContainerStyle,
284 pub empty_message: TextStyle,
285 pub status_bar_item: ContainedText,
286 pub tab_icon_width: f32,
287 pub tab_icon_spacing: f32,
288 pub tab_summary_spacing: f32,
289}
290
291#[derive(Clone, Deserialize, Default)]
292pub struct Editor {
293 pub text_color: Color,
294 #[serde(default)]
295 pub background: Color,
296 pub selection: SelectionStyle,
297 pub gutter_background: Color,
298 pub gutter_padding_factor: f32,
299 pub active_line_background: Color,
300 pub highlighted_line_background: Color,
301 pub rename_fade: f32,
302 pub document_highlight_read_background: Color,
303 pub document_highlight_write_background: Color,
304 pub diff_background_deleted: Color,
305 pub diff_background_inserted: Color,
306 pub line_number: Color,
307 pub line_number_active: Color,
308 pub guest_selections: Vec<SelectionStyle>,
309 pub syntax: Arc<SyntaxTheme>,
310 pub diagnostic_path_header: DiagnosticPathHeader,
311 pub diagnostic_header: DiagnosticHeader,
312 pub error_diagnostic: DiagnosticStyle,
313 pub invalid_error_diagnostic: DiagnosticStyle,
314 pub warning_diagnostic: DiagnosticStyle,
315 pub invalid_warning_diagnostic: DiagnosticStyle,
316 pub information_diagnostic: DiagnosticStyle,
317 pub invalid_information_diagnostic: DiagnosticStyle,
318 pub hint_diagnostic: DiagnosticStyle,
319 pub invalid_hint_diagnostic: DiagnosticStyle,
320 pub autocomplete: AutocompleteStyle,
321 pub code_actions_indicator: Color,
322 pub unnecessary_code_fade: f32,
323}
324
325#[derive(Clone, Deserialize, Default)]
326pub struct DiagnosticPathHeader {
327 #[serde(flatten)]
328 pub container: ContainerStyle,
329 pub filename: ContainedText,
330 pub path: ContainedText,
331 pub text_scale_factor: f32,
332}
333
334#[derive(Clone, Deserialize, Default)]
335pub struct DiagnosticHeader {
336 #[serde(flatten)]
337 pub container: ContainerStyle,
338 pub message: ContainedLabel,
339 pub code: ContainedText,
340 pub text_scale_factor: f32,
341 pub icon_width_factor: f32,
342}
343
344#[derive(Clone, Deserialize, Default)]
345pub struct DiagnosticStyle {
346 pub message: LabelStyle,
347 #[serde(default)]
348 pub header: ContainerStyle,
349 pub text_scale_factor: f32,
350}
351
352#[derive(Clone, Deserialize, Default)]
353pub struct AutocompleteStyle {
354 #[serde(flatten)]
355 pub container: ContainerStyle,
356 pub item: ContainerStyle,
357 pub selected_item: ContainerStyle,
358 pub hovered_item: ContainerStyle,
359 pub match_highlight: HighlightStyle,
360}
361
362#[derive(Clone, Copy, Default, Deserialize)]
363pub struct SelectionStyle {
364 pub cursor: Color,
365 pub selection: Color,
366}
367
368#[derive(Clone, Deserialize, Default)]
369pub struct FieldEditor {
370 #[serde(flatten)]
371 pub container: ContainerStyle,
372 pub text: TextStyle,
373 #[serde(default)]
374 pub placeholder_text: Option<TextStyle>,
375 pub selection: SelectionStyle,
376}
377
378impl Editor {
379 pub fn replica_selection_style(&self, replica_id: u16) -> &SelectionStyle {
380 let style_ix = replica_id as usize % (self.guest_selections.len() + 1);
381 if style_ix == 0 {
382 &self.selection
383 } else {
384 &self.guest_selections[style_ix - 1]
385 }
386 }
387}
388
389#[derive(Default)]
390pub struct SyntaxTheme {
391 pub highlights: Vec<(String, HighlightStyle)>,
392}
393
394impl SyntaxTheme {
395 pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
396 Self { highlights }
397 }
398}
399
400impl<'de> Deserialize<'de> for SyntaxTheme {
401 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
402 where
403 D: serde::Deserializer<'de>,
404 {
405 let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
406
407 let mut result = Self::new(Vec::new());
408 for (key, style) in syntax_data {
409 match result
410 .highlights
411 .binary_search_by(|(needle, _)| needle.cmp(&key))
412 {
413 Ok(i) | Err(i) => {
414 result.highlights.insert(i, (key, style));
415 }
416 }
417 }
418
419 Ok(result)
420 }
421}