lib.rs

  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}
 39
 40#[derive(Clone, Deserialize, Default)]
 41pub struct Titlebar {
 42    #[serde(flatten)]
 43    pub container: ContainerStyle,
 44    pub title: TextStyle,
 45    pub avatar_width: f32,
 46    pub offline_icon: OfflineIcon,
 47    pub icon_color: Color,
 48    pub avatar: ImageStyle,
 49    pub outdated_warning: ContainedText,
 50}
 51
 52#[derive(Clone, Deserialize, Default)]
 53pub struct OfflineIcon {
 54    #[serde(flatten)]
 55    pub container: ContainerStyle,
 56    pub width: f32,
 57}
 58
 59#[derive(Clone, Deserialize, Default)]
 60pub struct Tab {
 61    pub height: f32,
 62    #[serde(flatten)]
 63    pub container: ContainerStyle,
 64    #[serde(flatten)]
 65    pub label: LabelStyle,
 66    pub spacing: f32,
 67    pub icon_width: f32,
 68    pub icon_close: Color,
 69    pub icon_close_active: Color,
 70    pub icon_dirty: Color,
 71    pub icon_conflict: Color,
 72}
 73
 74#[derive(Deserialize, Default)]
 75pub struct Sidebar {
 76    #[serde(flatten)]
 77    pub container: ContainerStyle,
 78    pub width: f32,
 79    pub item: SidebarItem,
 80    pub active_item: SidebarItem,
 81    pub resize_handle: ContainerStyle,
 82}
 83
 84#[derive(Deserialize, Default)]
 85pub struct SidebarItem {
 86    pub icon_color: Color,
 87    pub icon_size: f32,
 88    pub height: f32,
 89}
 90
 91#[derive(Deserialize, Default)]
 92pub struct ChatPanel {
 93    #[serde(flatten)]
 94    pub container: ContainerStyle,
 95    pub message: ChatMessage,
 96    pub pending_message: ChatMessage,
 97    pub channel_select: ChannelSelect,
 98    pub input_editor: InputEditorStyle,
 99    pub sign_in_prompt: TextStyle,
100    pub hovered_sign_in_prompt: TextStyle,
101}
102
103#[derive(Debug, Deserialize, Default)]
104pub struct ProjectPanel {
105    #[serde(flatten)]
106    pub container: ContainerStyle,
107    pub entry: ProjectPanelEntry,
108    pub hovered_entry: ProjectPanelEntry,
109    pub selected_entry: ProjectPanelEntry,
110    pub hovered_selected_entry: ProjectPanelEntry,
111}
112
113#[derive(Debug, Deserialize, Default)]
114pub struct ProjectPanelEntry {
115    pub height: f32,
116    #[serde(flatten)]
117    pub container: ContainerStyle,
118    pub text: TextStyle,
119    pub icon_color: Color,
120    pub icon_size: f32,
121    pub icon_spacing: f32,
122}
123
124#[derive(Deserialize, Default)]
125pub struct PeoplePanel {
126    #[serde(flatten)]
127    pub container: ContainerStyle,
128    pub host_row_height: f32,
129    pub host_avatar: ImageStyle,
130    pub host_username: ContainedText,
131    pub tree_branch_width: f32,
132    pub tree_branch_color: Color,
133    pub shared_worktree: WorktreeRow,
134    pub hovered_shared_worktree: WorktreeRow,
135    pub unshared_worktree: WorktreeRow,
136    pub hovered_unshared_worktree: WorktreeRow,
137}
138
139#[derive(Deserialize, Default)]
140pub struct WorktreeRow {
141    #[serde(flatten)]
142    pub container: ContainerStyle,
143    pub height: f32,
144    pub name: ContainedText,
145    pub guest_avatar: ImageStyle,
146    pub guest_avatar_spacing: f32,
147}
148
149#[derive(Deserialize, Default)]
150pub struct ChatMessage {
151    #[serde(flatten)]
152    pub container: ContainerStyle,
153    pub body: TextStyle,
154    pub sender: ContainedText,
155    pub timestamp: ContainedText,
156}
157
158#[derive(Deserialize, Default)]
159pub struct ChannelSelect {
160    #[serde(flatten)]
161    pub container: ContainerStyle,
162    pub header: ChannelName,
163    pub item: ChannelName,
164    pub active_item: ChannelName,
165    pub hovered_item: ChannelName,
166    pub hovered_active_item: ChannelName,
167    pub menu: ContainerStyle,
168}
169
170#[derive(Deserialize, Default)]
171pub struct ChannelName {
172    #[serde(flatten)]
173    pub container: ContainerStyle,
174    pub hash: ContainedText,
175    pub name: TextStyle,
176}
177
178#[derive(Deserialize, Default)]
179pub struct Selector {
180    #[serde(flatten)]
181    pub container: ContainerStyle,
182    pub empty: ContainedLabel,
183    pub input_editor: InputEditorStyle,
184    pub item: ContainedLabel,
185    pub active_item: ContainedLabel,
186}
187
188#[derive(Clone, Debug, Deserialize, Default)]
189pub struct ContainedText {
190    #[serde(flatten)]
191    pub container: ContainerStyle,
192    #[serde(flatten)]
193    pub text: TextStyle,
194}
195
196#[derive(Deserialize, Default)]
197pub struct ContainedLabel {
198    #[serde(flatten)]
199    pub container: ContainerStyle,
200    #[serde(flatten)]
201    pub label: LabelStyle,
202}
203
204#[derive(Clone, Deserialize, Default)]
205pub struct EditorStyle {
206    pub text: TextStyle,
207    #[serde(default)]
208    pub placeholder_text: Option<TextStyle>,
209    pub background: Color,
210    pub selection: SelectionStyle,
211    pub gutter_background: Color,
212    pub active_line_background: Color,
213    pub line_number: Color,
214    pub line_number_active: Color,
215    pub guest_selections: Vec<SelectionStyle>,
216    pub syntax: Arc<SyntaxTheme>,
217    pub error_underline: Color,
218    pub warning_underline: Color,
219    #[serde(default)]
220    pub information_underline: Color,
221    #[serde(default)]
222    pub hint_underline: Color,
223}
224
225#[derive(Clone, Copy, Default, Deserialize)]
226pub struct SelectionStyle {
227    pub cursor: Color,
228    pub selection: Color,
229}
230
231#[derive(Clone, Deserialize, Default)]
232pub struct InputEditorStyle {
233    #[serde(flatten)]
234    pub container: ContainerStyle,
235    pub text: TextStyle,
236    #[serde(default)]
237    pub placeholder_text: Option<TextStyle>,
238    pub selection: SelectionStyle,
239}
240
241impl EditorStyle {
242    pub fn placeholder_text(&self) -> &TextStyle {
243        self.placeholder_text.as_ref().unwrap_or(&self.text)
244    }
245}
246
247impl InputEditorStyle {
248    pub fn as_editor(&self) -> EditorStyle {
249        EditorStyle {
250            text: self.text.clone(),
251            placeholder_text: self.placeholder_text.clone(),
252            background: self
253                .container
254                .background_color
255                .unwrap_or(Color::transparent_black()),
256            selection: self.selection,
257            gutter_background: Default::default(),
258            active_line_background: Default::default(),
259            line_number: Default::default(),
260            line_number_active: Default::default(),
261            guest_selections: Default::default(),
262            syntax: Default::default(),
263            error_underline: Default::default(),
264            warning_underline: Default::default(),
265            information_underline: Default::default(),
266            hint_underline: Default::default(),
267        }
268    }
269}
270
271#[derive(Default)]
272pub struct SyntaxTheme {
273    pub highlights: Vec<(String, HighlightStyle)>,
274}
275
276impl SyntaxTheme {
277    pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
278        Self { highlights }
279    }
280}
281
282impl<'de> Deserialize<'de> for SyntaxTheme {
283    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
284    where
285        D: serde::Deserializer<'de>,
286    {
287        let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
288
289        let mut result = Self::new(Vec::new());
290        for (key, style) in syntax_data {
291            match result
292                .highlights
293                .binary_search_by(|(needle, _)| needle.cmp(&key))
294            {
295                Ok(i) | Err(i) => {
296                    result.highlights.insert(i, (key, style));
297                }
298            }
299        }
300
301        Ok(result)
302    }
303}