diff --git a/Cargo.lock b/Cargo.lock index 9c4e55c..bdd5d5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5930,6 +5930,7 @@ dependencies = [ "serde_json", "smallvec", "smol", + "theme", ] [[package]] diff --git a/assets/themes/catppuccin-frappe.json b/assets/themes/catppuccin-frappe.json new file mode 100644 index 0000000..6804db9 --- /dev/null +++ b/assets/themes/catppuccin-frappe.json @@ -0,0 +1,140 @@ +{ + "id": "catppuccin-frappe", + "name": "Catppuccin Frappé", + "author": "Catppuccin", + "url": "https://github.com/catppuccin/catppuccin", + "light": { + "background": "#303446", + "surface_background": "#292c3c", + "elevated_surface_background": "#232634", + "panel_background": "#303446", + "overlay": "#c6d0f51a", + "title_bar": "#292c3c", + "title_bar_inactive": "#232634", + "window_border": "#737994", + "border": "#626880", + "border_variant": "#51576d", + "border_focused": "#8caaee", + "border_selected": "#8caaee", + "border_transparent": "#00000000", + "border_disabled": "#414559", + "ring": "#8caaee", + "text": "#c6d0f5", + "text_muted": "#b5bfe2", + "text_placeholder": "#a5adce", + "text_accent": "#8caaee", + "icon": "#b5bfe2", + "icon_muted": "#a5adce", + "icon_accent": "#8caaee", + "element_foreground": "#232634", + "element_background": "#8caaee", + "element_hover": "#8caaeee6", + "element_active": "#7e99d6", + "element_selected": "#7088bf", + "element_disabled": "#8caaee4d", + "secondary_foreground": "#7088bf", + "secondary_background": "#292c3c", + "secondary_hover": "#8caaee1a", + "secondary_active": "#232634", + "secondary_selected": "#232634", + "secondary_disabled": "#8caaee4d", + "danger_foreground": "#232634", + "danger_background": "#e78284", + "danger_hover": "#e782841a", + "danger_active": "#d07576", + "danger_selected": "#b96869", + "danger_disabled": "#e782844d", + "warning_foreground": "#232634", + "warning_background": "#e5c890", + "warning_hover": "#e5c8901a", + "warning_active": "#ceb482", + "warning_selected": "#b7a074", + "warning_disabled": "#e5c8904d", + "ghost_element_background": "#00000000", + "ghost_element_background_alt": "#414559", + "ghost_element_hover": "#c6d0f51a", + "ghost_element_active": "#51576d", + "ghost_element_selected": "#51576d", + "ghost_element_disabled": "#c6d0f50d", + "tab_inactive_background": "#292c3c", + "tab_inactive_foreground": "#b5bfe2", + "tab_active_background": "#303446", + "tab_active_foreground": "#c6d0f5", + "tab_hover_foreground": "#8caaee", + "scrollbar_thumb_background": "#c6d0f533", + "scrollbar_thumb_hover_background": "#c6d0f54d", + "scrollbar_thumb_border": "#00000000", + "scrollbar_track_background": "#00000000", + "scrollbar_track_border": "#51576d", + "drop_target_background": "#8caaee1a", + "cursor": "#f2d5cf", + "selection": "#949cbb40" + }, + "dark": { + "background": "#303446", + "surface_background": "#292c3c", + "elevated_surface_background": "#232634", + "panel_background": "#303446", + "overlay": "#c6d0f51a", + "title_bar": "#292c3c", + "title_bar_inactive": "#232634", + "window_border": "#737994", + "border": "#626880", + "border_variant": "#51576d", + "border_focused": "#8caaee", + "border_selected": "#8caaee", + "border_transparent": "#00000000", + "border_disabled": "#414559", + "ring": "#8caaee", + "text": "#c6d0f5", + "text_muted": "#b5bfe2", + "text_placeholder": "#a5adce", + "text_accent": "#8caaee", + "icon": "#b5bfe2", + "icon_muted": "#a5adce", + "icon_accent": "#8caaee", + "element_foreground": "#232634", + "element_background": "#8caaee", + "element_hover": "#8caaeee6", + "element_active": "#7e99d6", + "element_selected": "#7088bf", + "element_disabled": "#8caaee4d", + "secondary_foreground": "#7088bf", + "secondary_background": "#292c3c", + "secondary_hover": "#8caaee1a", + "secondary_active": "#232634", + "secondary_selected": "#232634", + "secondary_disabled": "#8caaee4d", + "danger_foreground": "#232634", + "danger_background": "#e78284", + "danger_hover": "#e782841a", + "danger_active": "#d07576", + "danger_selected": "#b96869", + "danger_disabled": "#e782844d", + "warning_foreground": "#232634", + "warning_background": "#e5c890", + "warning_hover": "#e5c8901a", + "warning_active": "#ceb482", + "warning_selected": "#b7a074", + "warning_disabled": "#e5c8904d", + "ghost_element_background": "#00000000", + "ghost_element_background_alt": "#414559", + "ghost_element_hover": "#c6d0f51a", + "ghost_element_active": "#51576d", + "ghost_element_selected": "#51576d", + "ghost_element_disabled": "#c6d0f50d", + "tab_inactive_background": "#292c3c", + "tab_inactive_foreground": "#b5bfe2", + "tab_active_background": "#303446", + "tab_active_foreground": "#c6d0f5", + "tab_hover_foreground": "#8caaee", + "scrollbar_thumb_background": "#c6d0f533", + "scrollbar_thumb_hover_background": "#c6d0f54d", + "scrollbar_thumb_border": "#00000000", + "scrollbar_track_background": "#00000000", + "scrollbar_track_border": "#51576d", + "drop_target_background": "#8caaee1a", + "cursor": "#f2d5cf", + "selection": "#949cbb40" + } +} diff --git a/assets/themes/catppuccin-latte.json b/assets/themes/catppuccin-latte.json new file mode 100644 index 0000000..45af458 --- /dev/null +++ b/assets/themes/catppuccin-latte.json @@ -0,0 +1,140 @@ +{ + "id": "catppuccin-latte", + "name": "Catppuccin Latte", + "author": "Catppuccin", + "url": "https://github.com/catppuccin/catppuccin", + "light": { + "background": "#eff1f5", + "surface_background": "#e6e9ef", + "elevated_surface_background": "#dce0e8", + "panel_background": "#eff1f5", + "overlay": "#4c4f691a", + "title_bar": "#e6e9ef", + "title_bar_inactive": "#dce0e8", + "window_border": "#9ca0b0", + "border": "#acb0be", + "border_variant": "#bcc0cc", + "border_focused": "#1e66f5", + "border_selected": "#1e66f5", + "border_transparent": "#00000000", + "border_disabled": "#ccd0da", + "ring": "#1e66f5", + "text": "#4c4f69", + "text_muted": "#5c5f77", + "text_placeholder": "#6c6f85", + "text_accent": "#1e66f5", + "icon": "#5c5f77", + "icon_muted": "#6c6f85", + "icon_accent": "#1e66f5", + "element_foreground": "#eff1f5", + "element_background": "#1e66f5", + "element_hover": "#1e66f5e6", + "element_active": "#1c5ce0", + "element_selected": "#1a52cc", + "element_disabled": "#1e66f54d", + "secondary_foreground": "#1a52cc", + "secondary_background": "#e6e9ef", + "secondary_hover": "#1e66f51a", + "secondary_active": "#dce0e8", + "secondary_selected": "#dce0e8", + "secondary_disabled": "#1e66f54d", + "danger_foreground": "#eff1f5", + "danger_background": "#d20f39", + "danger_hover": "#d20f391a", + "danger_active": "#bd0d33", + "danger_selected": "#a80b2d", + "danger_disabled": "#d20f394d", + "warning_foreground": "#4c4f69", + "warning_background": "#df8e1d", + "warning_hover": "#df8e1d1a", + "warning_active": "#c9801a", + "warning_selected": "#b47217", + "warning_disabled": "#df8e1d4d", + "ghost_element_background": "#00000000", + "ghost_element_background_alt": "#ccd0da", + "ghost_element_hover": "#4c4f691a", + "ghost_element_active": "#bcc0cc", + "ghost_element_selected": "#bcc0cc", + "ghost_element_disabled": "#4c4f690d", + "tab_inactive_background": "#e6e9ef", + "tab_inactive_foreground": "#5c5f77", + "tab_active_background": "#eff1f5", + "tab_active_foreground": "#4c4f69", + "tab_hover_foreground": "#1e66f5", + "scrollbar_thumb_background": "#4c4f6933", + "scrollbar_thumb_hover_background": "#4c4f694d", + "scrollbar_thumb_border": "#00000000", + "scrollbar_track_background": "#00000000", + "scrollbar_track_border": "#bcc0cc", + "drop_target_background": "#1e66f51a", + "cursor": "#dc8a78", + "selection": "#7c7f9340" + }, + "dark": { + "background": "#eff1f5", + "surface_background": "#e6e9ef", + "elevated_surface_background": "#dce0e8", + "panel_background": "#eff1f5", + "overlay": "#4c4f691a", + "title_bar": "#e6e9ef", + "title_bar_inactive": "#dce0e8", + "window_border": "#9ca0b0", + "border": "#acb0be", + "border_variant": "#bcc0cc", + "border_focused": "#1e66f5", + "border_selected": "#1e66f5", + "border_transparent": "#00000000", + "border_disabled": "#ccd0da", + "ring": "#1e66f5", + "text": "#4c4f69", + "text_muted": "#5c5f77", + "text_placeholder": "#6c6f85", + "text_accent": "#1e66f5", + "icon": "#5c5f77", + "icon_muted": "#6c6f85", + "icon_accent": "#1e66f5", + "element_foreground": "#eff1f5", + "element_background": "#1e66f5", + "element_hover": "#1e66f5e6", + "element_active": "#1c5ce0", + "element_selected": "#1a52cc", + "element_disabled": "#1e66f54d", + "secondary_foreground": "#1a52cc", + "secondary_background": "#e6e9ef", + "secondary_hover": "#1e66f51a", + "secondary_active": "#dce0e8", + "secondary_selected": "#dce0e8", + "secondary_disabled": "#1e66f54d", + "danger_foreground": "#eff1f5", + "danger_background": "#d20f39", + "danger_hover": "#d20f391a", + "danger_active": "#bd0d33", + "danger_selected": "#a80b2d", + "danger_disabled": "#d20f394d", + "warning_foreground": "#4c4f69", + "warning_background": "#df8e1d", + "warning_hover": "#df8e1d1a", + "warning_active": "#c9801a", + "warning_selected": "#b47217", + "warning_disabled": "#df8e1d4d", + "ghost_element_background": "#00000000", + "ghost_element_background_alt": "#ccd0da", + "ghost_element_hover": "#4c4f691a", + "ghost_element_active": "#bcc0cc", + "ghost_element_selected": "#bcc0cc", + "ghost_element_disabled": "#4c4f690d", + "tab_inactive_background": "#e6e9ef", + "tab_inactive_foreground": "#5c5f77", + "tab_active_background": "#eff1f5", + "tab_active_foreground": "#4c4f69", + "tab_hover_foreground": "#1e66f5", + "scrollbar_thumb_background": "#4c4f6933", + "scrollbar_thumb_hover_background": "#4c4f694d", + "scrollbar_thumb_border": "#00000000", + "scrollbar_track_background": "#00000000", + "scrollbar_track_border": "#bcc0cc", + "drop_target_background": "#1e66f51a", + "cursor": "#dc8a78", + "selection": "#7c7f9340" + } +} diff --git a/assets/themes/catppuccin-macchiato.json b/assets/themes/catppuccin-macchiato.json new file mode 100644 index 0000000..7d23d59 --- /dev/null +++ b/assets/themes/catppuccin-macchiato.json @@ -0,0 +1,140 @@ +{ + "id": "catppuccin-macchiato", + "name": "Catppuccin Macchiato", + "author": "Catppuccin", + "url": "https://github.com/catppuccin/catppuccin", + "light": { + "background": "#24273a", + "surface_background": "#1e2030", + "elevated_surface_background": "#181926", + "panel_background": "#24273a", + "overlay": "#cad3f51a", + "title_bar": "#1e2030", + "title_bar_inactive": "#181926", + "window_border": "#6e738d", + "border": "#5b6078", + "border_variant": "#494d64", + "border_focused": "#8aadf4", + "border_selected": "#8aadf4", + "border_transparent": "#00000000", + "border_disabled": "#363a4f", + "ring": "#8aadf4", + "text": "#cad3f5", + "text_muted": "#b8c0e0", + "text_placeholder": "#a5adcb", + "text_accent": "#8aadf4", + "icon": "#b8c0e0", + "icon_muted": "#a5adcb", + "icon_accent": "#8aadf4", + "element_foreground": "#181926", + "element_background": "#8aadf4", + "element_hover": "#8aadf4e6", + "element_active": "#7c9cdc", + "element_selected": "#6e8bc5", + "element_disabled": "#8aadf44d", + "secondary_foreground": "#6e8bc5", + "secondary_background": "#1e2030", + "secondary_hover": "#8aadf41a", + "secondary_active": "#181926", + "secondary_selected": "#181926", + "secondary_disabled": "#8aadf44d", + "danger_foreground": "#181926", + "danger_background": "#ed8796", + "danger_hover": "#ed87961a", + "danger_active": "#d57a87", + "danger_selected": "#be6d78", + "danger_disabled": "#ed87964d", + "warning_foreground": "#181926", + "warning_background": "#eed49f", + "warning_hover": "#eed49f1a", + "warning_active": "#d6bf8f", + "warning_selected": "#beaa7f", + "warning_disabled": "#eed49f4d", + "ghost_element_background": "#00000000", + "ghost_element_background_alt": "#363a4f", + "ghost_element_hover": "#cad3f51a", + "ghost_element_active": "#494d64", + "ghost_element_selected": "#494d64", + "ghost_element_disabled": "#cad3f50d", + "tab_inactive_background": "#1e2030", + "tab_inactive_foreground": "#b8c0e0", + "tab_active_background": "#24273a", + "tab_active_foreground": "#cad3f5", + "tab_hover_foreground": "#8aadf4", + "scrollbar_thumb_background": "#cad3f533", + "scrollbar_thumb_hover_background": "#cad3f54d", + "scrollbar_thumb_border": "#00000000", + "scrollbar_track_background": "#00000000", + "scrollbar_track_border": "#494d64", + "drop_target_background": "#8aadf41a", + "cursor": "#f4dbd6", + "selection": "#939ab740" + }, + "dark": { + "background": "#24273a", + "surface_background": "#1e2030", + "elevated_surface_background": "#181926", + "panel_background": "#24273a", + "overlay": "#cad3f51a", + "title_bar": "#1e2030", + "title_bar_inactive": "#181926", + "window_border": "#6e738d", + "border": "#5b6078", + "border_variant": "#494d64", + "border_focused": "#8aadf4", + "border_selected": "#8aadf4", + "border_transparent": "#00000000", + "border_disabled": "#363a4f", + "ring": "#8aadf4", + "text": "#cad3f5", + "text_muted": "#b8c0e0", + "text_placeholder": "#a5adcb", + "text_accent": "#8aadf4", + "icon": "#b8c0e0", + "icon_muted": "#a5adcb", + "icon_accent": "#8aadf4", + "element_foreground": "#181926", + "element_background": "#8aadf4", + "element_hover": "#8aadf4e6", + "element_active": "#7c9cdc", + "element_selected": "#6e8bc5", + "element_disabled": "#8aadf44d", + "secondary_foreground": "#6e8bc5", + "secondary_background": "#1e2030", + "secondary_hover": "#8aadf41a", + "secondary_active": "#181926", + "secondary_selected": "#181926", + "secondary_disabled": "#8aadf44d", + "danger_foreground": "#181926", + "danger_background": "#ed8796", + "danger_hover": "#ed87961a", + "danger_active": "#d57a87", + "danger_selected": "#be6d78", + "danger_disabled": "#ed87964d", + "warning_foreground": "#181926", + "warning_background": "#eed49f", + "warning_hover": "#eed49f1a", + "warning_active": "#d6bf8f", + "warning_selected": "#beaa7f", + "warning_disabled": "#eed49f4d", + "ghost_element_background": "#00000000", + "ghost_element_background_alt": "#363a4f", + "ghost_element_hover": "#cad3f51a", + "ghost_element_active": "#494d64", + "ghost_element_selected": "#494d64", + "ghost_element_disabled": "#cad3f50d", + "tab_inactive_background": "#1e2030", + "tab_inactive_foreground": "#b8c0e0", + "tab_active_background": "#24273a", + "tab_active_foreground": "#cad3f5", + "tab_hover_foreground": "#8aadf4", + "scrollbar_thumb_background": "#cad3f533", + "scrollbar_thumb_hover_background": "#cad3f54d", + "scrollbar_thumb_border": "#00000000", + "scrollbar_track_background": "#00000000", + "scrollbar_track_border": "#494d64", + "drop_target_background": "#8aadf41a", + "cursor": "#f4dbd6", + "selection": "#939ab740" + } +} diff --git a/assets/themes/catppuccin-mocha.json b/assets/themes/catppuccin-mocha.json new file mode 100644 index 0000000..5de69f7 --- /dev/null +++ b/assets/themes/catppuccin-mocha.json @@ -0,0 +1,140 @@ +{ + "id": "catppuccin-mocha", + "name": "Catppuccin Mocha", + "author": "Catppuccin", + "url": "https://github.com/catppuccin/catppuccin", + "light": { + "background": "#1e1e2e", + "surface_background": "#181825", + "elevated_surface_background": "#11111b", + "panel_background": "#1e1e2e", + "overlay": "#cdd6f41a", + "title_bar": "#181825", + "title_bar_inactive": "#11111b", + "window_border": "#6c7086", + "border": "#585b70", + "border_variant": "#45475a", + "border_focused": "#89b4fa", + "border_selected": "#89b4fa", + "border_transparent": "#00000000", + "border_disabled": "#313244", + "ring": "#89b4fa", + "text": "#cdd6f4", + "text_muted": "#bac2de", + "text_placeholder": "#a6adc8", + "text_accent": "#89b4fa", + "icon": "#bac2de", + "icon_muted": "#a6adc8", + "icon_accent": "#89b4fa", + "element_foreground": "#11111b", + "element_background": "#89b4fa", + "element_hover": "#89b4fae6", + "element_active": "#7ba2e1", + "element_selected": "#6d90c9", + "element_disabled": "#89b4fa4d", + "secondary_foreground": "#6d90c9", + "secondary_background": "#181825", + "secondary_hover": "#89b4fa1a", + "secondary_active": "#11111b", + "secondary_selected": "#11111b", + "secondary_disabled": "#89b4fa4d", + "danger_foreground": "#11111b", + "danger_background": "#f38ba8", + "danger_hover": "#f38ba81a", + "danger_active": "#db7d98", + "danger_selected": "#c46f88", + "danger_disabled": "#f38ba84d", + "warning_foreground": "#11111b", + "warning_background": "#f9e2af", + "warning_hover": "#f9e2af1a", + "warning_active": "#e0cb9e", + "warning_selected": "#c8b48d", + "warning_disabled": "#f9e2af4d", + "ghost_element_background": "#00000000", + "ghost_element_background_alt": "#313244", + "ghost_element_hover": "#cdd6f41a", + "ghost_element_active": "#45475a", + "ghost_element_selected": "#45475a", + "ghost_element_disabled": "#cdd6f40d", + "tab_inactive_background": "#181825", + "tab_inactive_foreground": "#bac2de", + "tab_active_background": "#1e1e2e", + "tab_active_foreground": "#cdd6f4", + "tab_hover_foreground": "#89b4fa", + "scrollbar_thumb_background": "#cdd6f433", + "scrollbar_thumb_hover_background": "#cdd6f44d", + "scrollbar_thumb_border": "#00000000", + "scrollbar_track_background": "#00000000", + "scrollbar_track_border": "#45475a", + "drop_target_background": "#89b4fa1a", + "cursor": "#f5e0dc", + "selection": "#9399b240" + }, + "dark": { + "background": "#1e1e2e", + "surface_background": "#181825", + "elevated_surface_background": "#11111b", + "panel_background": "#1e1e2e", + "overlay": "#cdd6f41a", + "title_bar": "#181825", + "title_bar_inactive": "#11111b", + "window_border": "#6c7086", + "border": "#585b70", + "border_variant": "#45475a", + "border_focused": "#89b4fa", + "border_selected": "#89b4fa", + "border_transparent": "#00000000", + "border_disabled": "#313244", + "ring": "#89b4fa", + "text": "#cdd6f4", + "text_muted": "#bac2de", + "text_placeholder": "#a6adc8", + "text_accent": "#89b4fa", + "icon": "#bac2de", + "icon_muted": "#a6adc8", + "icon_accent": "#89b4fa", + "element_foreground": "#11111b", + "element_background": "#89b4fa", + "element_hover": "#89b4fae6", + "element_active": "#7ba2e1", + "element_selected": "#6d90c9", + "element_disabled": "#89b4fa4d", + "secondary_foreground": "#6d90c9", + "secondary_background": "#181825", + "secondary_hover": "#89b4fa1a", + "secondary_active": "#11111b", + "secondary_selected": "#11111b", + "secondary_disabled": "#89b4fa4d", + "danger_foreground": "#11111b", + "danger_background": "#f38ba8", + "danger_hover": "#f38ba81a", + "danger_active": "#db7d98", + "danger_selected": "#c46f88", + "danger_disabled": "#f38ba84d", + "warning_foreground": "#11111b", + "warning_background": "#f9e2af", + "warning_hover": "#f9e2af1a", + "warning_active": "#e0cb9e", + "warning_selected": "#c8b48d", + "warning_disabled": "#f9e2af4d", + "ghost_element_background": "#00000000", + "ghost_element_background_alt": "#313244", + "ghost_element_hover": "#cdd6f41a", + "ghost_element_active": "#45475a", + "ghost_element_selected": "#45475a", + "ghost_element_disabled": "#cdd6f40d", + "tab_inactive_background": "#181825", + "tab_inactive_foreground": "#bac2de", + "tab_active_background": "#1e1e2e", + "tab_active_foreground": "#cdd6f4", + "tab_hover_foreground": "#89b4fa", + "scrollbar_thumb_background": "#cdd6f433", + "scrollbar_thumb_hover_background": "#cdd6f44d", + "scrollbar_thumb_border": "#00000000", + "scrollbar_track_background": "#00000000", + "scrollbar_track_border": "#45475a", + "drop_target_background": "#89b4fa1a", + "cursor": "#f5e0dc", + "selection": "#9399b240" + } +} diff --git a/assets/themes/flexoki.json b/assets/themes/flexoki.json new file mode 100644 index 0000000..9fff1c3 --- /dev/null +++ b/assets/themes/flexoki.json @@ -0,0 +1,140 @@ +{ + "id": "flexoki", + "name": "Flexoki", + "author": "Stephan Ango", + "url": "https://stephango.com/flexoki", + "light": { + "background": "#FFFCF0", + "surface_background": "#F2F0E5", + "elevated_surface_background": "#E6E4D9", + "panel_background": "#FFFCF0", + "overlay": "#100F0F1a", + "title_bar": "#F2F0E5", + "title_bar_inactive": "#E6E4D9", + "window_border": "#B7B5AC", + "border": "#CECDC3", + "border_variant": "#DAD8CE", + "border_focused": "#205EA6", + "border_selected": "#205EA6", + "border_transparent": "#00000000", + "border_disabled": "#E6E4D9", + "ring": "#205EA6", + "text": "#100F0F", + "text_muted": "#6F6E69", + "text_placeholder": "#9F9D96", + "text_accent": "#205EA6", + "icon": "#6F6E69", + "icon_muted": "#9F9D96", + "icon_accent": "#205EA6", + "element_foreground": "#FFFCF0", + "element_background": "#205EA6", + "element_hover": "#205EA6e6", + "element_active": "#1A4F8C", + "element_selected": "#163B66", + "element_disabled": "#205EA64d", + "secondary_foreground": "#163B66", + "secondary_background": "#F2F0E5", + "secondary_hover": "#205EA61a", + "secondary_active": "#E6E4D9", + "secondary_selected": "#E6E4D9", + "secondary_disabled": "#205EA64d", + "danger_foreground": "#FFFCF0", + "danger_background": "#D14D41", + "danger_hover": "#D14D411a", + "danger_active": "#C03E35", + "danger_selected": "#AF3029", + "danger_disabled": "#D14D414d", + "warning_foreground": "#100F0F", + "warning_background": "#D0A215", + "warning_hover": "#D0A2151a", + "warning_active": "#BE9207", + "warning_selected": "#AD8301", + "warning_disabled": "#D0A2154d", + "ghost_element_background": "#00000000", + "ghost_element_background_alt": "#E6E4D9", + "ghost_element_hover": "#100F0F1a", + "ghost_element_active": "#DAD8CE", + "ghost_element_selected": "#DAD8CE", + "ghost_element_disabled": "#100F0F0d", + "tab_inactive_background": "#F2F0E5", + "tab_inactive_foreground": "#6F6E69", + "tab_active_background": "#FFFCF0", + "tab_active_foreground": "#100F0F", + "tab_hover_foreground": "#205EA6", + "scrollbar_thumb_background": "#100F0F33", + "scrollbar_thumb_hover_background": "#100F0F4d", + "scrollbar_thumb_border": "#00000000", + "scrollbar_track_background": "#00000000", + "scrollbar_track_border": "#DAD8CE", + "drop_target_background": "#205EA61a", + "cursor": "#205EA6", + "selection": "#205EA640" + }, + "dark": { + "background": "#100F0F", + "surface_background": "#1C1B1A", + "elevated_surface_background": "#282726", + "panel_background": "#100F0F", + "overlay": "#FFFCF01a", + "title_bar": "#1C1B1A", + "title_bar_inactive": "#282726", + "window_border": "#575653", + "border": "#403E3C", + "border_variant": "#343331", + "border_focused": "#4385BE", + "border_selected": "#4385BE", + "border_transparent": "#00000000", + "border_disabled": "#282726", + "ring": "#4385BE", + "text": "#FFFCF0", + "text_muted": "#878580", + "text_placeholder": "#6F6E69", + "text_accent": "#4385BE", + "icon": "#878580", + "icon_muted": "#6F6E69", + "icon_accent": "#4385BE", + "element_foreground": "#100F0F", + "element_background": "#4385BE", + "element_hover": "#4385BEe6", + "element_active": "#3171B2", + "element_selected": "#205EA6", + "element_disabled": "#4385BE4d", + "secondary_foreground": "#205EA6", + "secondary_background": "#1C1B1A", + "secondary_hover": "#4385BE1a", + "secondary_active": "#282726", + "secondary_selected": "#282726", + "secondary_disabled": "#4385BE4d", + "danger_foreground": "#100F0F", + "danger_background": "#E8705F", + "danger_hover": "#E8705F1a", + "danger_active": "#D14D41", + "danger_selected": "#C03E35", + "danger_disabled": "#E8705F4d", + "warning_foreground": "#100F0F", + "warning_background": "#DFB431", + "warning_hover": "#DFB4311a", + "warning_active": "#D0A215", + "warning_selected": "#BE9207", + "warning_disabled": "#DFB4314d", + "ghost_element_background": "#00000000", + "ghost_element_background_alt": "#282726", + "ghost_element_hover": "#FFFCF01a", + "ghost_element_active": "#343331", + "ghost_element_selected": "#343331", + "ghost_element_disabled": "#FFFCF00d", + "tab_inactive_background": "#1C1B1A", + "tab_inactive_foreground": "#878580", + "tab_active_background": "#100F0F", + "tab_active_foreground": "#FFFCF0", + "tab_hover_foreground": "#4385BE", + "scrollbar_thumb_background": "#FFFCF033", + "scrollbar_thumb_hover_background": "#FFFCF04d", + "scrollbar_thumb_border": "#00000000", + "scrollbar_track_background": "#00000000", + "scrollbar_track_border": "#343331", + "drop_target_background": "#4385BE1a", + "cursor": "#4385BE", + "selection": "#4385BE40" + } +} diff --git a/crates/coop/src/dialogs/settings.rs b/crates/coop/src/dialogs/settings.rs index fd2df6b..75c007f 100644 --- a/crates/coop/src/dialogs/settings.rs +++ b/crates/coop/src/dialogs/settings.rs @@ -4,7 +4,7 @@ use gpui::{ Styled, Window, }; use settings::{AppSettings, AuthMode}; -use theme::ActiveTheme; +use theme::{ActiveTheme, ThemeMode}; use ui::button::{Button, ButtonVariants}; use ui::group_box::{GroupBox, GroupBoxVariants}; use ui::input::{InputState, TextInput}; @@ -53,11 +53,15 @@ impl Render for Preferences { "When opening a request, a popup will appear to help you identify the sender."; const AVATAR: &str = "Hide all avatar pictures to improve performance and protect your privacy."; - const AUTH: &str = "Authentication before send events for some relays."; + const MODE: &str = + "Choose whether to use the selected light or dark theme, or to follow the OS."; + const AUTH: &str = "Choose the authentication behavior for relays."; + const RESET: &str = "Reset the theme to the default one."; let screening = AppSettings::get_screening(cx); let hide_avatar = AppSettings::get_hide_avatar(cx); let auth_mode = AppSettings::get_auth_mode(cx); + let theme_mode = AppSettings::get_theme_mode(cx); v_flex() .gap_4() @@ -131,6 +135,78 @@ impl Render for Preferences { ), ), ) + .child( + GroupBox::new() + .id("appearance") + .title("Appearance") + .fill() + .child( + h_flex() + .gap_3() + .justify_between() + .child( + v_flex() + .child(div().text_sm().child(SharedString::from("Mode"))) + .child( + div() + .text_xs() + .text_color(cx.theme().text_muted) + .child(SharedString::from(MODE)), + ), + ) + .child( + Button::new("theme-mode") + .label(theme_mode.name()) + .ghost_alt() + .small() + .dropdown_menu(|this, _window, _cx| { + this.min_w(px(256.)) + .item(PopupMenuItem::new("Light").on_click( + |_ev, _window, cx| { + AppSettings::update_theme_mode( + ThemeMode::Light, + cx, + ); + }, + )) + .item(PopupMenuItem::new("Dark").on_click( + |_ev, _window, cx| { + AppSettings::update_theme_mode( + ThemeMode::Dark, + cx, + ); + }, + )) + }), + ), + ) + .child( + h_flex() + .gap_3() + .justify_between() + .child( + v_flex() + .child(div().text_sm().child(SharedString::from("Reset theme"))) + .child( + div() + .text_xs() + .text_color(cx.theme().text_muted) + .child(SharedString::from(RESET)), + ), + ) + .child( + Button::new("reset") + .label("Reset") + .ghost_alt() + .small() + .on_click(move |_ev, window, cx| { + AppSettings::global(cx).update(cx, |this, cx| { + this.reset_theme(window, cx); + }) + }), + ), + ), + ) .child( GroupBox::new() .id("media") diff --git a/crates/coop/src/main.rs b/crates/coop/src/main.rs index 0540bd4..902f80f 100644 --- a/crates/coop/src/main.rs +++ b/crates/coop/src/main.rs @@ -88,7 +88,7 @@ fn main() { device::init(window, cx); // Initialize settings - settings::init(cx); + settings::init(window, cx); // Initialize relay auth registry relay_auth::init(window, cx); diff --git a/crates/coop/src/workspace.rs b/crates/coop/src/workspace.rs index b95de07..f6d6d5a 100644 --- a/crates/coop/src/workspace.rs +++ b/crates/coop/src/workspace.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use ::settings::AppSettings; use chat::{ChatEvent, ChatRegistry, InboxState}; use gpui::prelude::FluentBuilder; use gpui::{ @@ -10,7 +11,7 @@ use person::PersonRegistry; use serde::Deserialize; use smallvec::{smallvec, SmallVec}; use state::{NostrRegistry, RelayState}; -use theme::{ActiveTheme, Theme, SIDEBAR_WIDTH}; +use theme::{ActiveTheme, Theme, ThemeRegistry, SIDEBAR_WIDTH}; use title_bar::TitleBar; use ui::avatar::Avatar; use ui::button::{Button, ButtonVariants}; @@ -271,10 +272,91 @@ impl Workspace { this.ensure_messaging_relays(cx); }); } - Command::ToggleTheme => {} + Command::ToggleTheme => { + self.theme_selector(window, cx); + } } } + fn theme_selector(&mut self, window: &mut Window, cx: &mut Context) { + window.open_modal(cx, move |this, _window, cx| { + let registry = ThemeRegistry::global(cx); + let themes = registry.read(cx).themes(); + + this.width(px(520.)) + .show_close(true) + .title("Select theme") + .pb_4() + .child(v_flex().gap_2().w_full().children({ + let mut items = vec![]; + + for (ix, (path, theme)) in themes.iter().enumerate() { + items.push( + h_flex() + .group("") + .px_2() + .h_8() + .w_full() + .justify_between() + .rounded(cx.theme().radius) + .hover(|this| this.bg(cx.theme().elevated_surface_background)) + .child( + h_flex() + .gap_1p5() + .flex_1() + .text_sm() + .child(theme.name.clone()) + .child( + div() + .text_xs() + .italic() + .text_color(cx.theme().text_muted) + .child(theme.author.clone()), + ), + ) + .child( + h_flex() + .gap_1() + .invisible() + .group_hover("", |this| this.visible()) + .child( + Button::new(format!("url-{ix}")) + .icon(IconName::Link) + .ghost() + .small() + .on_click({ + let theme = theme.clone(); + move |_ev, _window, cx| { + cx.open_url(&theme.url); + } + }), + ) + .child( + Button::new(format!("set-{ix}")) + .icon(IconName::Check) + .primary() + .small() + .on_click({ + let path = path.clone(); + move |_ev, window, cx| { + let settings = AppSettings::global(cx); + let path = path.clone(); + + settings.update(cx, |this, cx| { + this.set_theme(path, window, cx); + }) + } + }), + ), + ), + ); + } + + items + })) + }); + } + fn titlebar_left(&mut self, _window: &mut Window, cx: &Context) -> impl IntoElement { let nostr = NostrRegistry::global(cx); let signer = nostr.read(cx).signer(); diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index e330b85..3fcb518 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true publish.workspace = true [dependencies] +theme = { path = "../theme" } common = { path = "../common" } nostr-sdk.workspace = true diff --git a/crates/settings/src/lib.rs b/crates/settings/src/lib.rs index 862abd6..4b52f52 100644 --- a/crates/settings/src/lib.rs +++ b/crates/settings/src/lib.rs @@ -1,15 +1,17 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Display; +use std::rc::Rc; use anyhow::{anyhow, Error}; use common::config_dir; -use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task}; +use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task, Window}; use nostr_sdk::prelude::*; use serde::{Deserialize, Serialize}; use smallvec::{smallvec, SmallVec}; +use theme::{Theme, ThemeFamily, ThemeMode}; -pub fn init(cx: &mut App) { - AppSettings::set_global(cx.new(AppSettings::new), cx) +pub fn init(window: &mut Window, cx: &mut App) { + AppSettings::set_global(cx.new(|cx| AppSettings::new(window, cx)), cx) } macro_rules! setting_accessors { @@ -34,6 +36,8 @@ macro_rules! setting_accessors { } setting_accessors! { + pub theme: Option, + pub theme_mode: ThemeMode, pub hide_avatar: bool, pub screening: bool, pub auth_mode: AuthMode, @@ -53,7 +57,7 @@ pub enum AuthMode { impl Display for AuthMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - AuthMode::Auto => write!(f, "Auto authentication"), + AuthMode::Auto => write!(f, "Auto"), AuthMode::Manual => write!(f, "Ask every time"), } } @@ -114,6 +118,12 @@ impl RoomConfig { /// Settings #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Settings { + /// Theme + pub theme: Option, + + /// Theme mode + pub theme_mode: ThemeMode, + /// Hide user avatars pub hide_avatar: bool, @@ -136,6 +146,8 @@ pub struct Settings { impl Default for Settings { fn default() -> Self { Self { + theme: None, + theme_mode: ThemeMode::default(), hide_avatar: false, screening: true, auth_mode: AuthMode::default(), @@ -176,7 +188,7 @@ impl AppSettings { cx.set_global(GlobalAppSettings(state)); } - fn new(cx: &mut Context) -> Self { + fn new(window: &mut Window, cx: &mut Context) -> Self { let mut subscriptions = smallvec![]; subscriptions.push( @@ -186,12 +198,9 @@ impl AppSettings { }), ); - cx.defer(|cx| { - let settings = AppSettings::global(cx); - - settings.update(cx, |this, cx| { - this.load(cx); - }); + // Run at the end of current cycle + cx.defer_in(window, |this, window, cx| { + this.load(window, cx); }); Self { @@ -207,7 +216,7 @@ impl AppSettings { } /// Load settings - fn load(&mut self, cx: &mut Context) { + fn load(&mut self, window: &mut Window, cx: &mut Context) { let task: Task> = cx.background_spawn(async move { let path = config_dir().join(".settings"); @@ -218,12 +227,13 @@ impl AppSettings { } }); - cx.spawn(async move |this, cx| { + cx.spawn_in(window, async move |this, cx| { let settings = task.await.unwrap_or(Settings::default()); // Update settings - this.update(cx, |this, cx| { + this.update_in(cx, |this, window, cx| { this.set_settings(settings, cx); + this.apply_theme(window, cx); }) .ok(); }) @@ -247,6 +257,36 @@ impl AppSettings { task.detach(); } + /// Set theme + pub fn set_theme(&mut self, theme: T, window: &mut Window, cx: &mut Context) + where + T: Into, + { + // Update settings + self.values.theme = Some(theme.into()); + cx.notify(); + + // Apply the new theme + self.apply_theme(window, cx); + } + + /// Apply theme + pub fn apply_theme(&mut self, window: &mut Window, cx: &mut Context) { + if let Some(name) = self.values.theme.as_ref() { + if let Ok(new_theme) = ThemeFamily::from_assets(name) { + Theme::apply_theme(Rc::new(new_theme), Some(window), cx); + } + } else { + Theme::apply_theme(Rc::new(ThemeFamily::default()), Some(window), cx); + } + } + + /// Reset theme + pub fn reset_theme(&mut self, window: &mut Window, cx: &mut Context) { + self.values.theme = None; + self.apply_theme(window, cx); + } + /// Check if the given relay is already authenticated pub fn trusted_relay(&self, url: &RelayUrl, _cx: &App) -> bool { self.values.trusted_relays.iter().any(|relay| { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index b22a865..4f396cc 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::ThemeColors; -#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd, Eq, Hash)] +#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd, Eq, Hash, Deserialize, Serialize)] pub enum ThemeMode { #[default] Light, @@ -18,11 +18,11 @@ impl ThemeMode { matches!(self, Self::Dark) } - /// Return lower_case theme name: `light`, `dark`. + /// Return theme name: `light`, `dark`. pub fn name(&self) -> &'static str { match self { - ThemeMode::Light => "light", - ThemeMode::Dark => "dark", + ThemeMode::Light => "Light", + ThemeMode::Dark => "Dark", } } } @@ -153,14 +153,14 @@ impl ThemeFamily { /// /// # fn main() -> anyhow::Result<()> { /// // Assuming the file exists at `assets/themes/my-theme.json` - /// let theme = ThemeFamily::from_assets("my-theme")?; + /// let theme = ThemeFamily::from_assets("themes/my-theme.json")?; /// /// println!("Loaded theme: {}", theme.name); /// # Ok(()) /// # } /// ``` - pub fn from_assets(name: &str) -> anyhow::Result { - let path = format!("assets/themes/{}.json", name); + pub fn from_assets(target: &str) -> anyhow::Result { + let path = format!("assets/{target}"); Self::from_file(path) } }