wip
This commit is contained in:
Generated
-2
@@ -7440,11 +7440,9 @@ dependencies = [
|
|||||||
"gpui-base",
|
"gpui-base",
|
||||||
"gpui-pre",
|
"gpui-pre",
|
||||||
"log",
|
"log",
|
||||||
"schemars",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.9",
|
"sha2 0.10.9",
|
||||||
"tempfile",
|
|
||||||
"thiserror 2.0.21",
|
"thiserror 2.0.21",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "aurora",
|
|
||||||
"name": "Aurora",
|
|
||||||
"author": "Coop",
|
|
||||||
"url": "https://coopchat.xyz",
|
|
||||||
"light": {
|
|
||||||
"background": "#fdfcfeff",
|
|
||||||
"surface_background": "#f8f8ffff",
|
|
||||||
"elevated_surface_background": "#f0f1feff",
|
|
||||||
"panel_background": "#fdfcfeff",
|
|
||||||
"overlay": "#211f4300",
|
|
||||||
"title_bar": "#f0f1feff",
|
|
||||||
"title_bar_inactive": "#fdfcfeff",
|
|
||||||
"window_border": "#dadcffff",
|
|
||||||
"border": "#dadcffff",
|
|
||||||
"border_variant": "#cbcdffff",
|
|
||||||
"border_focused": "#5b5bd6ff",
|
|
||||||
"border_selected": "#5b5bd6ff",
|
|
||||||
"border_transparent": "#00000000",
|
|
||||||
"border_disabled": "#e6e7ffff",
|
|
||||||
"ring": "#5151cdff",
|
|
||||||
"text": "#1f2d5cff",
|
|
||||||
"text_muted": "#5753c6ff",
|
|
||||||
"text_placeholder": "#9b9ef0ff",
|
|
||||||
"text_accent": "#5b5bd6ff",
|
|
||||||
"text_danger": "#e54d2eff",
|
|
||||||
"text_warning": "#f76b15ff",
|
|
||||||
"icon": "#5753c6ff",
|
|
||||||
"icon_muted": "#9b9ef0ff",
|
|
||||||
"icon_accent": "#5151cdff",
|
|
||||||
"element_foreground": "#ffffffff",
|
|
||||||
"element_background": "#5b5bd6ff",
|
|
||||||
"element_hover": "#5151cdff",
|
|
||||||
"element_active": "#6e56cfff",
|
|
||||||
"element_selected": "#654dc4ff",
|
|
||||||
"element_disabled": "#5b5bd64d",
|
|
||||||
"secondary_foreground": "#1f2d5cff",
|
|
||||||
"secondary_background": "#f0f1feff",
|
|
||||||
"secondary_hover": "#e6e7ffff",
|
|
||||||
"secondary_active": "#dadcffff",
|
|
||||||
"secondary_selected": "#dadcffff",
|
|
||||||
"secondary_disabled": "#5b5bd64d",
|
|
||||||
"danger_foreground": "#ffffffff",
|
|
||||||
"danger_background": "#feebe7ff",
|
|
||||||
"danger_hover": "#ffcdc2ff",
|
|
||||||
"danger_active": "#fdbdafff",
|
|
||||||
"danger_selected": "#fdbdafff",
|
|
||||||
"danger_disabled": "#e54d2e4d",
|
|
||||||
"warning_foreground": "#ffffffff",
|
|
||||||
"warning_background": "#fff7edff",
|
|
||||||
"warning_hover": "#ffd19aff",
|
|
||||||
"warning_active": "#ffc182ff",
|
|
||||||
"warning_selected": "#ffc182ff",
|
|
||||||
"warning_disabled": "#f76b154d",
|
|
||||||
"ghost_element_background": "#00000000",
|
|
||||||
"ghost_element_background_alt": "#f0f1feff",
|
|
||||||
"ghost_element_hover": "#211f430d",
|
|
||||||
"ghost_element_active": "#211f431a",
|
|
||||||
"ghost_element_selected": "#211f431a",
|
|
||||||
"ghost_element_disabled": "#211f4305",
|
|
||||||
"tab_background": "#f0f1feff",
|
|
||||||
"tab_foreground": "#5753c6ff",
|
|
||||||
"tab_hover_background": "#211f430d",
|
|
||||||
"tab_active_background": "#fdfcfeff",
|
|
||||||
"tab_active_foreground": "#1f2d5cff",
|
|
||||||
"scrollbar_thumb_background": "#211f431a",
|
|
||||||
"scrollbar_thumb_hover_background": "#211f4326",
|
|
||||||
"scrollbar_thumb_border": "#00000000",
|
|
||||||
"scrollbar_track_background": "#00000000",
|
|
||||||
"scrollbar_track_border": "#00000000",
|
|
||||||
"drop_target_background": "#5b5bd61a",
|
|
||||||
"cursor": "#5b5bd6ff",
|
|
||||||
"selection": "#5b5bd640"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"background": "#14121fff",
|
|
||||||
"surface_background": "#1b1525ff",
|
|
||||||
"elevated_surface_background": "#291f43ff",
|
|
||||||
"panel_background": "#14121fff",
|
|
||||||
"overlay": "#baa7ff1a",
|
|
||||||
"title_bar": "#291f43ff",
|
|
||||||
"title_bar_inactive": "#14121fff",
|
|
||||||
"window_border": "#473876ff",
|
|
||||||
"border": "#473876ff",
|
|
||||||
"border_variant": "#3c2e69ff",
|
|
||||||
"border_focused": "#7d66d9ff",
|
|
||||||
"border_selected": "#7d66d9ff",
|
|
||||||
"border_transparent": "#00000000",
|
|
||||||
"border_disabled": "#33255bff",
|
|
||||||
"ring": "#6e56cfff",
|
|
||||||
"text": "#e2ddfeff",
|
|
||||||
"text_muted": "#baa7ffff",
|
|
||||||
"text_placeholder": "#6958adff",
|
|
||||||
"text_accent": "#baa7ffff",
|
|
||||||
"text_danger": "#ff977dff",
|
|
||||||
"text_warning": "#ffa057ff",
|
|
||||||
"icon": "#baa7ffff",
|
|
||||||
"icon_muted": "#6958adff",
|
|
||||||
"icon_accent": "#6e56cfff",
|
|
||||||
"element_foreground": "#14121fff",
|
|
||||||
"element_background": "#7d66d9ff",
|
|
||||||
"element_hover": "#baa7ffff",
|
|
||||||
"element_active": "#6e56cfff",
|
|
||||||
"element_selected": "#654dc4ff",
|
|
||||||
"element_disabled": "#7d66d94d",
|
|
||||||
"secondary_foreground": "#e2ddfeff",
|
|
||||||
"secondary_background": "#291f43ff",
|
|
||||||
"secondary_hover": "#33255bff",
|
|
||||||
"secondary_active": "#3c2e69ff",
|
|
||||||
"secondary_selected": "#3c2e69ff",
|
|
||||||
"secondary_disabled": "#7d66d94d",
|
|
||||||
"danger_foreground": "#181111ff",
|
|
||||||
"danger_background": "#391714ff",
|
|
||||||
"danger_hover": "#5e1c16ff",
|
|
||||||
"danger_active": "#6e2920ff",
|
|
||||||
"danger_selected": "#6e2920ff",
|
|
||||||
"danger_disabled": "#ff977d4d",
|
|
||||||
"warning_foreground": "#17120eff",
|
|
||||||
"warning_background": "#331e0bff",
|
|
||||||
"warning_hover": "#562800ff",
|
|
||||||
"warning_active": "#66350cff",
|
|
||||||
"warning_selected": "#66350cff",
|
|
||||||
"warning_disabled": "#ffa0574d",
|
|
||||||
"ghost_element_background": "#00000000",
|
|
||||||
"ghost_element_background_alt": "#291f43ff",
|
|
||||||
"ghost_element_hover": "#baa7ff0d",
|
|
||||||
"ghost_element_active": "#baa7ff1a",
|
|
||||||
"ghost_element_selected": "#baa7ff1a",
|
|
||||||
"ghost_element_disabled": "#baa7ff05",
|
|
||||||
"tab_background": "#291f43ff",
|
|
||||||
"tab_foreground": "#baa7ffff",
|
|
||||||
"tab_hover_background": "#baa7ff0d",
|
|
||||||
"tab_active_background": "#14121fff",
|
|
||||||
"tab_active_foreground": "#e2ddfeff",
|
|
||||||
"scrollbar_thumb_background": "#baa7ff1a",
|
|
||||||
"scrollbar_thumb_hover_background": "#baa7ff26",
|
|
||||||
"scrollbar_thumb_border": "#00000000",
|
|
||||||
"scrollbar_track_background": "#00000000",
|
|
||||||
"scrollbar_track_border": "#00000000",
|
|
||||||
"drop_target_background": "#baa7ff1a",
|
|
||||||
"cursor": "#baa7ffff",
|
|
||||||
"selection": "#baa7ff40"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "catppuccin-frappe",
|
|
||||||
"name": "Catppuccin Frappé",
|
|
||||||
"author": "Catppuccin Org (ported by Coop)",
|
|
||||||
"url": "https://catppuccin.com",
|
|
||||||
"light": {
|
|
||||||
"background": "#303446",
|
|
||||||
"surface_background": "#292c3c",
|
|
||||||
"elevated_surface_background": "#232634",
|
|
||||||
"panel_background": "#303446",
|
|
||||||
"overlay": "#c6d0f51a",
|
|
||||||
"title_bar": "#232634",
|
|
||||||
"title_bar_inactive": "#303446",
|
|
||||||
"window_border": "#51576d",
|
|
||||||
"border": "#51576d",
|
|
||||||
"border_variant": "#414559",
|
|
||||||
"border_focused": "#8caaee",
|
|
||||||
"border_selected": "#8caaee",
|
|
||||||
"border_transparent": "#c6d0f500",
|
|
||||||
"border_disabled": "#292c3c",
|
|
||||||
"ring": "#babbf1",
|
|
||||||
"text": "#c6d0f5",
|
|
||||||
"text_muted": "#a5adce",
|
|
||||||
"text_placeholder": "#838ba7",
|
|
||||||
"text_accent": "#8caaee",
|
|
||||||
"text_danger": "#e78284",
|
|
||||||
"text_warning": "#ef9f76",
|
|
||||||
"icon": "#a5adce",
|
|
||||||
"icon_muted": "#838ba7",
|
|
||||||
"icon_accent": "#babbf1",
|
|
||||||
"element_foreground": "#303446",
|
|
||||||
"element_background": "#8caaee",
|
|
||||||
"element_hover": "#babbf1",
|
|
||||||
"element_active": "#99d1db",
|
|
||||||
"element_selected": "#85c1dc",
|
|
||||||
"element_disabled": "#8caaee4d",
|
|
||||||
"secondary_foreground": "#c6d0f5",
|
|
||||||
"secondary_background": "#414559",
|
|
||||||
"secondary_hover": "#51576d",
|
|
||||||
"secondary_active": "#626880",
|
|
||||||
"secondary_selected": "#626880",
|
|
||||||
"secondary_disabled": "#8caaee4d",
|
|
||||||
"danger_foreground": "#303446",
|
|
||||||
"danger_background": "#e78284",
|
|
||||||
"danger_hover": "#ea999c",
|
|
||||||
"danger_active": "#ef9f76",
|
|
||||||
"danger_selected": "#e5c890",
|
|
||||||
"danger_disabled": "#e782844d",
|
|
||||||
"warning_foreground": "#303446",
|
|
||||||
"warning_background": "#ef9f76",
|
|
||||||
"warning_hover": "#e5c890",
|
|
||||||
"warning_active": "#a6d189",
|
|
||||||
"warning_selected": "#81c8be",
|
|
||||||
"warning_disabled": "#ef9f764d",
|
|
||||||
"ghost_element_background": "#c6d0f500",
|
|
||||||
"ghost_element_background_alt": "#292c3c",
|
|
||||||
"ghost_element_hover": "#c6d0f50d",
|
|
||||||
"ghost_element_active": "#c6d0f51a",
|
|
||||||
"ghost_element_selected": "#c6d0f51a",
|
|
||||||
"ghost_element_disabled": "#c6d0f505",
|
|
||||||
"tab_background": "#232634",
|
|
||||||
"tab_foreground": "#a5adce",
|
|
||||||
"tab_hover_background": "#c6d0f50d",
|
|
||||||
"tab_active_background": "#303446",
|
|
||||||
"tab_active_foreground": "#c6d0f5",
|
|
||||||
"scrollbar_thumb_background": "#c6d0f51a",
|
|
||||||
"scrollbar_thumb_hover_background": "#c6d0f526",
|
|
||||||
"scrollbar_thumb_border": "#c6d0f500",
|
|
||||||
"scrollbar_track_background": "#c6d0f500",
|
|
||||||
"scrollbar_track_border": "#c6d0f500",
|
|
||||||
"drop_target_background": "#8caaee1a",
|
|
||||||
"cursor": "#8caaee",
|
|
||||||
"selection": "#8caaee40"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"background": "#303446",
|
|
||||||
"surface_background": "#292c3c",
|
|
||||||
"elevated_surface_background": "#232634",
|
|
||||||
"panel_background": "#303446",
|
|
||||||
"overlay": "#c6d0f51a",
|
|
||||||
"title_bar": "#232634",
|
|
||||||
"title_bar_inactive": "#303446",
|
|
||||||
"window_border": "#51576d",
|
|
||||||
"border": "#51576d",
|
|
||||||
"border_variant": "#414559",
|
|
||||||
"border_focused": "#8caaee",
|
|
||||||
"border_selected": "#8caaee",
|
|
||||||
"border_transparent": "#c6d0f500",
|
|
||||||
"border_disabled": "#292c3c",
|
|
||||||
"ring": "#babbf1",
|
|
||||||
"text": "#c6d0f5",
|
|
||||||
"text_muted": "#a5adce",
|
|
||||||
"text_placeholder": "#838ba7",
|
|
||||||
"text_accent": "#8caaee",
|
|
||||||
"text_danger": "#e78284",
|
|
||||||
"text_warning": "#ef9f76",
|
|
||||||
"icon": "#a5adce",
|
|
||||||
"icon_muted": "#838ba7",
|
|
||||||
"icon_accent": "#babbf1",
|
|
||||||
"element_foreground": "#303446",
|
|
||||||
"element_background": "#8caaee",
|
|
||||||
"element_hover": "#babbf1",
|
|
||||||
"element_active": "#99d1db",
|
|
||||||
"element_selected": "#85c1dc",
|
|
||||||
"element_disabled": "#8caaee4d",
|
|
||||||
"secondary_foreground": "#c6d0f5",
|
|
||||||
"secondary_background": "#414559",
|
|
||||||
"secondary_hover": "#51576d",
|
|
||||||
"secondary_active": "#626880",
|
|
||||||
"secondary_selected": "#626880",
|
|
||||||
"secondary_disabled": "#8caaee4d",
|
|
||||||
"danger_foreground": "#303446",
|
|
||||||
"danger_background": "#e78284",
|
|
||||||
"danger_hover": "#ea999c",
|
|
||||||
"danger_active": "#ef9f76",
|
|
||||||
"danger_selected": "#e5c890",
|
|
||||||
"danger_disabled": "#e782844d",
|
|
||||||
"warning_foreground": "#303446",
|
|
||||||
"warning_background": "#ef9f76",
|
|
||||||
"warning_hover": "#e5c890",
|
|
||||||
"warning_active": "#a6d189",
|
|
||||||
"warning_selected": "#81c8be",
|
|
||||||
"warning_disabled": "#ef9f764d",
|
|
||||||
"ghost_element_background": "#c6d0f500",
|
|
||||||
"ghost_element_background_alt": "#292c3c",
|
|
||||||
"ghost_element_hover": "#c6d0f50d",
|
|
||||||
"ghost_element_active": "#c6d0f51a",
|
|
||||||
"ghost_element_selected": "#c6d0f51a",
|
|
||||||
"ghost_element_disabled": "#c6d0f505",
|
|
||||||
"tab_background": "#232634",
|
|
||||||
"tab_foreground": "#a5adce",
|
|
||||||
"tab_hover_background": "#c6d0f50d",
|
|
||||||
"tab_active_background": "#303446",
|
|
||||||
"tab_active_foreground": "#c6d0f5",
|
|
||||||
"scrollbar_thumb_background": "#c6d0f51a",
|
|
||||||
"scrollbar_thumb_hover_background": "#c6d0f526",
|
|
||||||
"scrollbar_thumb_border": "#c6d0f500",
|
|
||||||
"scrollbar_track_background": "#c6d0f500",
|
|
||||||
"scrollbar_track_border": "#c6d0f500",
|
|
||||||
"drop_target_background": "#8caaee1a",
|
|
||||||
"cursor": "#8caaee",
|
|
||||||
"selection": "#8caaee40"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "catppuccin-latte",
|
|
||||||
"name": "Catppuccin Latte",
|
|
||||||
"author": "Catppuccin Org (ported by Coop)",
|
|
||||||
"url": "https://catppuccin.com",
|
|
||||||
"light": {
|
|
||||||
"background": "#eff1f5",
|
|
||||||
"surface_background": "#e6e9ef",
|
|
||||||
"elevated_surface_background": "#dce0e8",
|
|
||||||
"panel_background": "#eff1f5",
|
|
||||||
"overlay": "#4c4f691a",
|
|
||||||
"title_bar": "#dce0e8",
|
|
||||||
"title_bar_inactive": "#eff1f5",
|
|
||||||
"window_border": "#bcc0cc",
|
|
||||||
"border": "#bcc0cc",
|
|
||||||
"border_variant": "#ccd0da",
|
|
||||||
"border_focused": "#1e66f5",
|
|
||||||
"border_selected": "#1e66f5",
|
|
||||||
"border_transparent": "#4c4f6900",
|
|
||||||
"border_disabled": "#e6e9ef",
|
|
||||||
"ring": "#7287fd",
|
|
||||||
"text": "#4c4f69",
|
|
||||||
"text_muted": "#6c6f85",
|
|
||||||
"text_placeholder": "#8c8fa1",
|
|
||||||
"text_accent": "#1e66f5",
|
|
||||||
"text_danger": "#d20f39",
|
|
||||||
"text_warning": "#fe640b",
|
|
||||||
"icon": "#6c6f85",
|
|
||||||
"icon_muted": "#8c8fa1",
|
|
||||||
"icon_accent": "#7287fd",
|
|
||||||
"element_foreground": "#eff1f5",
|
|
||||||
"element_background": "#1e66f5",
|
|
||||||
"element_hover": "#7287fd",
|
|
||||||
"element_active": "#04a5e5",
|
|
||||||
"element_selected": "#209fb5",
|
|
||||||
"element_disabled": "#1e66f54d",
|
|
||||||
"secondary_foreground": "#4c4f69",
|
|
||||||
"secondary_background": "#ccd0da",
|
|
||||||
"secondary_hover": "#bcc0cc",
|
|
||||||
"secondary_active": "#acb0be",
|
|
||||||
"secondary_selected": "#acb0be",
|
|
||||||
"secondary_disabled": "#1e66f54d",
|
|
||||||
"danger_foreground": "#eff1f5",
|
|
||||||
"danger_background": "#d20f39",
|
|
||||||
"danger_hover": "#e64553",
|
|
||||||
"danger_active": "#fe640b",
|
|
||||||
"danger_selected": "#df8e1d",
|
|
||||||
"danger_disabled": "#d20f394d",
|
|
||||||
"warning_foreground": "#eff1f5",
|
|
||||||
"warning_background": "#fe640b",
|
|
||||||
"warning_hover": "#df8e1d",
|
|
||||||
"warning_active": "#40a02b",
|
|
||||||
"warning_selected": "#179299",
|
|
||||||
"warning_disabled": "#fe640b4d",
|
|
||||||
"ghost_element_background": "#4c4f6900",
|
|
||||||
"ghost_element_background_alt": "#e6e9ef",
|
|
||||||
"ghost_element_hover": "#4c4f690d",
|
|
||||||
"ghost_element_active": "#4c4f691a",
|
|
||||||
"ghost_element_selected": "#4c4f691a",
|
|
||||||
"ghost_element_disabled": "#4c4f6905",
|
|
||||||
"tab_background": "#e6e9ef",
|
|
||||||
"tab_foreground": "#6c6f85",
|
|
||||||
"tab_hover_background": "#4c4f690d",
|
|
||||||
"tab_active_background": "#eff1f5",
|
|
||||||
"tab_active_foreground": "#4c4f69",
|
|
||||||
"scrollbar_thumb_background": "#4c4f691a",
|
|
||||||
"scrollbar_thumb_hover_background": "#4c4f6926",
|
|
||||||
"scrollbar_thumb_border": "#4c4f6900",
|
|
||||||
"scrollbar_track_background": "#4c4f6900",
|
|
||||||
"scrollbar_track_border": "#4c4f6900",
|
|
||||||
"drop_target_background": "#1e66f51a",
|
|
||||||
"cursor": "#1e66f5",
|
|
||||||
"selection": "#1e66f540"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"background": "#eff1f5",
|
|
||||||
"surface_background": "#e6e9ef",
|
|
||||||
"elevated_surface_background": "#dce0e8",
|
|
||||||
"panel_background": "#eff1f5",
|
|
||||||
"overlay": "#4c4f691a",
|
|
||||||
"title_bar": "#dce0e8",
|
|
||||||
"title_bar_inactive": "#eff1f5",
|
|
||||||
"window_border": "#bcc0cc",
|
|
||||||
"border": "#bcc0cc",
|
|
||||||
"border_variant": "#ccd0da",
|
|
||||||
"border_focused": "#1e66f5",
|
|
||||||
"border_selected": "#1e66f5",
|
|
||||||
"border_transparent": "#4c4f6900",
|
|
||||||
"border_disabled": "#e6e9ef",
|
|
||||||
"ring": "#7287fd",
|
|
||||||
"text": "#4c4f69",
|
|
||||||
"text_muted": "#6c6f85",
|
|
||||||
"text_placeholder": "#8c8fa1",
|
|
||||||
"text_accent": "#1e66f5",
|
|
||||||
"text_danger": "#d20f39",
|
|
||||||
"text_warning": "#fe640b",
|
|
||||||
"icon": "#6c6f85",
|
|
||||||
"icon_muted": "#8c8fa1",
|
|
||||||
"icon_accent": "#7287fd",
|
|
||||||
"element_foreground": "#eff1f5",
|
|
||||||
"element_background": "#1e66f5",
|
|
||||||
"element_hover": "#7287fd",
|
|
||||||
"element_active": "#04a5e5",
|
|
||||||
"element_selected": "#209fb5",
|
|
||||||
"element_disabled": "#1e66f54d",
|
|
||||||
"secondary_foreground": "#4c4f69",
|
|
||||||
"secondary_background": "#ccd0da",
|
|
||||||
"secondary_hover": "#bcc0cc",
|
|
||||||
"secondary_active": "#acb0be",
|
|
||||||
"secondary_selected": "#acb0be",
|
|
||||||
"secondary_disabled": "#1e66f54d",
|
|
||||||
"danger_foreground": "#eff1f5",
|
|
||||||
"danger_background": "#d20f39",
|
|
||||||
"danger_hover": "#e64553",
|
|
||||||
"danger_active": "#fe640b",
|
|
||||||
"danger_selected": "#df8e1d",
|
|
||||||
"danger_disabled": "#d20f394d",
|
|
||||||
"warning_foreground": "#eff1f5",
|
|
||||||
"warning_background": "#fe640b",
|
|
||||||
"warning_hover": "#df8e1d",
|
|
||||||
"warning_active": "#40a02b",
|
|
||||||
"warning_selected": "#179299",
|
|
||||||
"warning_disabled": "#fe640b4d",
|
|
||||||
"ghost_element_background": "#4c4f6900",
|
|
||||||
"ghost_element_background_alt": "#e6e9ef",
|
|
||||||
"ghost_element_hover": "#4c4f690d",
|
|
||||||
"ghost_element_active": "#4c4f691a",
|
|
||||||
"ghost_element_selected": "#4c4f691a",
|
|
||||||
"ghost_element_disabled": "#4c4f6905",
|
|
||||||
"tab_background": "#e6e9ef",
|
|
||||||
"tab_foreground": "#6c6f85",
|
|
||||||
"tab_hover_background": "#4c4f690d",
|
|
||||||
"tab_active_background": "#eff1f5",
|
|
||||||
"tab_active_foreground": "#4c4f69",
|
|
||||||
"scrollbar_thumb_background": "#4c4f691a",
|
|
||||||
"scrollbar_thumb_hover_background": "#4c4f6926",
|
|
||||||
"scrollbar_thumb_border": "#4c4f6900",
|
|
||||||
"scrollbar_track_background": "#4c4f6900",
|
|
||||||
"scrollbar_track_border": "#4c4f6900",
|
|
||||||
"drop_target_background": "#1e66f51a",
|
|
||||||
"cursor": "#1e66f5",
|
|
||||||
"selection": "#1e66f540"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "catppuccin-macchiato",
|
|
||||||
"name": "Catppuccin Macchiato",
|
|
||||||
"author": "Catppuccin Org (ported by Coop)",
|
|
||||||
"url": "https://catppuccin.com",
|
|
||||||
"light": {
|
|
||||||
"background": "#24273a",
|
|
||||||
"surface_background": "#1e2030",
|
|
||||||
"elevated_surface_background": "#181926",
|
|
||||||
"panel_background": "#24273a",
|
|
||||||
"overlay": "#cad3f51a",
|
|
||||||
"title_bar": "#181926",
|
|
||||||
"title_bar_inactive": "#24273a",
|
|
||||||
"window_border": "#494d64",
|
|
||||||
"border": "#494d64",
|
|
||||||
"border_variant": "#363a4f",
|
|
||||||
"border_focused": "#8aadf4",
|
|
||||||
"border_selected": "#8aadf4",
|
|
||||||
"border_transparent": "#cad3f500",
|
|
||||||
"border_disabled": "#1e2030",
|
|
||||||
"ring": "#b7bdf8",
|
|
||||||
"text": "#cad3f5",
|
|
||||||
"text_muted": "#a5adcb",
|
|
||||||
"text_placeholder": "#8087a2",
|
|
||||||
"text_accent": "#8aadf4",
|
|
||||||
"text_danger": "#ed8796",
|
|
||||||
"text_warning": "#f5a97f",
|
|
||||||
"icon": "#a5adcb",
|
|
||||||
"icon_muted": "#8087a2",
|
|
||||||
"icon_accent": "#b7bdf8",
|
|
||||||
"element_foreground": "#24273a",
|
|
||||||
"element_background": "#8aadf4",
|
|
||||||
"element_hover": "#b7bdf8",
|
|
||||||
"element_active": "#91d7e3",
|
|
||||||
"element_selected": "#7dc4e4",
|
|
||||||
"element_disabled": "#8aadf44d",
|
|
||||||
"secondary_foreground": "#cad3f5",
|
|
||||||
"secondary_background": "#363a4f",
|
|
||||||
"secondary_hover": "#494d64",
|
|
||||||
"secondary_active": "#5b6078",
|
|
||||||
"secondary_selected": "#5b6078",
|
|
||||||
"secondary_disabled": "#8aadf44d",
|
|
||||||
"danger_foreground": "#24273a",
|
|
||||||
"danger_background": "#ed8796",
|
|
||||||
"danger_hover": "#ee99a0",
|
|
||||||
"danger_active": "#f5a97f",
|
|
||||||
"danger_selected": "#eed49f",
|
|
||||||
"danger_disabled": "#ed87964d",
|
|
||||||
"warning_foreground": "#24273a",
|
|
||||||
"warning_background": "#f5a97f",
|
|
||||||
"warning_hover": "#eed49f",
|
|
||||||
"warning_active": "#a6da95",
|
|
||||||
"warning_selected": "#8bd5ca",
|
|
||||||
"warning_disabled": "#f5a97f4d",
|
|
||||||
"ghost_element_background": "#cad3f500",
|
|
||||||
"ghost_element_background_alt": "#1e2030",
|
|
||||||
"ghost_element_hover": "#cad3f50d",
|
|
||||||
"ghost_element_active": "#cad3f51a",
|
|
||||||
"ghost_element_selected": "#cad3f51a",
|
|
||||||
"ghost_element_disabled": "#cad3f505",
|
|
||||||
"tab_background": "#181926",
|
|
||||||
"tab_foreground": "#a5adcb",
|
|
||||||
"tab_hover_background": "#cad3f50d",
|
|
||||||
"tab_active_background": "#24273a",
|
|
||||||
"tab_active_foreground": "#cad3f5",
|
|
||||||
"scrollbar_thumb_background": "#cad3f51a",
|
|
||||||
"scrollbar_thumb_hover_background": "#cad3f526",
|
|
||||||
"scrollbar_thumb_border": "#cad3f500",
|
|
||||||
"scrollbar_track_background": "#cad3f500",
|
|
||||||
"scrollbar_track_border": "#cad3f500",
|
|
||||||
"drop_target_background": "#8aadf41a",
|
|
||||||
"cursor": "#8aadf4",
|
|
||||||
"selection": "#8aadf440"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"background": "#24273a",
|
|
||||||
"surface_background": "#1e2030",
|
|
||||||
"elevated_surface_background": "#181926",
|
|
||||||
"panel_background": "#24273a",
|
|
||||||
"overlay": "#cad3f51a",
|
|
||||||
"title_bar": "#181926",
|
|
||||||
"title_bar_inactive": "#24273a",
|
|
||||||
"window_border": "#494d64",
|
|
||||||
"border": "#494d64",
|
|
||||||
"border_variant": "#363a4f",
|
|
||||||
"border_focused": "#8aadf4",
|
|
||||||
"border_selected": "#8aadf4",
|
|
||||||
"border_transparent": "#cad3f500",
|
|
||||||
"border_disabled": "#1e2030",
|
|
||||||
"ring": "#b7bdf8",
|
|
||||||
"text": "#cad3f5",
|
|
||||||
"text_muted": "#a5adcb",
|
|
||||||
"text_placeholder": "#8087a2",
|
|
||||||
"text_accent": "#8aadf4",
|
|
||||||
"text_danger": "#ed8796",
|
|
||||||
"text_warning": "#f5a97f",
|
|
||||||
"icon": "#a5adcb",
|
|
||||||
"icon_muted": "#8087a2",
|
|
||||||
"icon_accent": "#b7bdf8",
|
|
||||||
"element_foreground": "#24273a",
|
|
||||||
"element_background": "#8aadf4",
|
|
||||||
"element_hover": "#b7bdf8",
|
|
||||||
"element_active": "#91d7e3",
|
|
||||||
"element_selected": "#7dc4e4",
|
|
||||||
"element_disabled": "#8aadf44d",
|
|
||||||
"secondary_foreground": "#cad3f5",
|
|
||||||
"secondary_background": "#363a4f",
|
|
||||||
"secondary_hover": "#494d64",
|
|
||||||
"secondary_active": "#5b6078",
|
|
||||||
"secondary_selected": "#5b6078",
|
|
||||||
"secondary_disabled": "#8aadf44d",
|
|
||||||
"danger_foreground": "#24273a",
|
|
||||||
"danger_background": "#ed8796",
|
|
||||||
"danger_hover": "#ee99a0",
|
|
||||||
"danger_active": "#f5a97f",
|
|
||||||
"danger_selected": "#eed49f",
|
|
||||||
"danger_disabled": "#ed87964d",
|
|
||||||
"warning_foreground": "#24273a",
|
|
||||||
"warning_background": "#f5a97f",
|
|
||||||
"warning_hover": "#eed49f",
|
|
||||||
"warning_active": "#a6da95",
|
|
||||||
"warning_selected": "#8bd5ca",
|
|
||||||
"warning_disabled": "#f5a97f4d",
|
|
||||||
"ghost_element_background": "#cad3f500",
|
|
||||||
"ghost_element_background_alt": "#1e2030",
|
|
||||||
"ghost_element_hover": "#cad3f50d",
|
|
||||||
"ghost_element_active": "#cad3f51a",
|
|
||||||
"ghost_element_selected": "#cad3f51a",
|
|
||||||
"ghost_element_disabled": "#cad3f505",
|
|
||||||
"tab_background": "#181926",
|
|
||||||
"tab_foreground": "#a5adcb",
|
|
||||||
"tab_hover_background": "#cad3f50d",
|
|
||||||
"tab_active_background": "#24273a",
|
|
||||||
"tab_active_foreground": "#cad3f5",
|
|
||||||
"scrollbar_thumb_background": "#cad3f51a",
|
|
||||||
"scrollbar_thumb_hover_background": "#cad3f526",
|
|
||||||
"scrollbar_thumb_border": "#cad3f500",
|
|
||||||
"scrollbar_track_background": "#cad3f500",
|
|
||||||
"scrollbar_track_border": "#cad3f500",
|
|
||||||
"drop_target_background": "#8aadf41a",
|
|
||||||
"cursor": "#8aadf4",
|
|
||||||
"selection": "#8aadf440"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "catppuccin-mocha",
|
|
||||||
"name": "Catppuccin Mocha",
|
|
||||||
"author": "Catppuccin Org (ported by Coop)",
|
|
||||||
"url": "https://catppuccin.com",
|
|
||||||
"light": {
|
|
||||||
"background": "#1e1e2e",
|
|
||||||
"surface_background": "#181825",
|
|
||||||
"elevated_surface_background": "#11111b",
|
|
||||||
"panel_background": "#1e1e2e",
|
|
||||||
"overlay": "#cdd6f41a",
|
|
||||||
"title_bar": "#11111b",
|
|
||||||
"title_bar_inactive": "#1e1e2e",
|
|
||||||
"window_border": "#45475a",
|
|
||||||
"border": "#45475a",
|
|
||||||
"border_variant": "#313244",
|
|
||||||
"border_focused": "#89b4fa",
|
|
||||||
"border_selected": "#89b4fa",
|
|
||||||
"border_transparent": "#cdd6f400",
|
|
||||||
"border_disabled": "#181825",
|
|
||||||
"ring": "#b4befe",
|
|
||||||
"text": "#cdd6f4",
|
|
||||||
"text_muted": "#a6adc8",
|
|
||||||
"text_placeholder": "#7f849c",
|
|
||||||
"text_accent": "#89b4fa",
|
|
||||||
"text_danger": "#f38ba8",
|
|
||||||
"text_warning": "#fab387",
|
|
||||||
"icon": "#a6adc8",
|
|
||||||
"icon_muted": "#7f849c",
|
|
||||||
"icon_accent": "#b4befe",
|
|
||||||
"element_foreground": "#1e1e2e",
|
|
||||||
"element_background": "#89b4fa",
|
|
||||||
"element_hover": "#b4befe",
|
|
||||||
"element_active": "#89dceb",
|
|
||||||
"element_selected": "#74c7ec",
|
|
||||||
"element_disabled": "#89b4fa4d",
|
|
||||||
"secondary_foreground": "#cdd6f4",
|
|
||||||
"secondary_background": "#313244",
|
|
||||||
"secondary_hover": "#45475a",
|
|
||||||
"secondary_active": "#585b70",
|
|
||||||
"secondary_selected": "#585b70",
|
|
||||||
"secondary_disabled": "#89b4fa4d",
|
|
||||||
"danger_foreground": "#1e1e2e",
|
|
||||||
"danger_background": "#f38ba8",
|
|
||||||
"danger_hover": "#eba0ac",
|
|
||||||
"danger_active": "#fab387",
|
|
||||||
"danger_selected": "#f9e2af",
|
|
||||||
"danger_disabled": "#f38ba84d",
|
|
||||||
"warning_foreground": "#1e1e2e",
|
|
||||||
"warning_background": "#fab387",
|
|
||||||
"warning_hover": "#f9e2af",
|
|
||||||
"warning_active": "#a6e3a1",
|
|
||||||
"warning_selected": "#94e2d5",
|
|
||||||
"warning_disabled": "#fab3874d",
|
|
||||||
"ghost_element_background": "#cdd6f400",
|
|
||||||
"ghost_element_background_alt": "#181825",
|
|
||||||
"ghost_element_hover": "#cdd6f40d",
|
|
||||||
"ghost_element_active": "#cdd6f41a",
|
|
||||||
"ghost_element_selected": "#cdd6f41a",
|
|
||||||
"ghost_element_disabled": "#cdd6f405",
|
|
||||||
"tab_background": "#11111b",
|
|
||||||
"tab_foreground": "#a6adc8",
|
|
||||||
"tab_hover_background": "#cdd6f40d",
|
|
||||||
"tab_active_background": "#1e1e2e",
|
|
||||||
"tab_active_foreground": "#cdd6f4",
|
|
||||||
"scrollbar_thumb_background": "#cdd6f41a",
|
|
||||||
"scrollbar_thumb_hover_background": "#cdd6f426",
|
|
||||||
"scrollbar_thumb_border": "#cdd6f400",
|
|
||||||
"scrollbar_track_background": "#cdd6f400",
|
|
||||||
"scrollbar_track_border": "#cdd6f400",
|
|
||||||
"drop_target_background": "#89b4fa1a",
|
|
||||||
"cursor": "#89b4fa",
|
|
||||||
"selection": "#89b4fa40"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"background": "#1e1e2e",
|
|
||||||
"surface_background": "#181825",
|
|
||||||
"elevated_surface_background": "#11111b",
|
|
||||||
"panel_background": "#1e1e2e",
|
|
||||||
"overlay": "#cdd6f41a",
|
|
||||||
"title_bar": "#11111b",
|
|
||||||
"title_bar_inactive": "#1e1e2e",
|
|
||||||
"window_border": "#45475a",
|
|
||||||
"border": "#45475a",
|
|
||||||
"border_variant": "#313244",
|
|
||||||
"border_focused": "#89b4fa",
|
|
||||||
"border_selected": "#89b4fa",
|
|
||||||
"border_transparent": "#cdd6f400",
|
|
||||||
"border_disabled": "#181825",
|
|
||||||
"ring": "#b4befe",
|
|
||||||
"text": "#cdd6f4",
|
|
||||||
"text_muted": "#a6adc8",
|
|
||||||
"text_placeholder": "#7f849c",
|
|
||||||
"text_accent": "#89b4fa",
|
|
||||||
"text_danger": "#f38ba8",
|
|
||||||
"text_warning": "#fab387",
|
|
||||||
"icon": "#a6adc8",
|
|
||||||
"icon_muted": "#7f849c",
|
|
||||||
"icon_accent": "#b4befe",
|
|
||||||
"element_foreground": "#1e1e2e",
|
|
||||||
"element_background": "#89b4fa",
|
|
||||||
"element_hover": "#b4befe",
|
|
||||||
"element_active": "#89dceb",
|
|
||||||
"element_selected": "#74c7ec",
|
|
||||||
"element_disabled": "#89b4fa4d",
|
|
||||||
"secondary_foreground": "#cdd6f4",
|
|
||||||
"secondary_background": "#313244",
|
|
||||||
"secondary_hover": "#45475a",
|
|
||||||
"secondary_active": "#585b70",
|
|
||||||
"secondary_selected": "#585b70",
|
|
||||||
"secondary_disabled": "#89b4fa4d",
|
|
||||||
"danger_foreground": "#1e1e2e",
|
|
||||||
"danger_background": "#f38ba8",
|
|
||||||
"danger_hover": "#eba0ac",
|
|
||||||
"danger_active": "#fab387",
|
|
||||||
"danger_selected": "#f9e2af",
|
|
||||||
"danger_disabled": "#f38ba84d",
|
|
||||||
"warning_foreground": "#1e1e2e",
|
|
||||||
"warning_background": "#fab387",
|
|
||||||
"warning_hover": "#f9e2af",
|
|
||||||
"warning_active": "#a6e3a1",
|
|
||||||
"warning_selected": "#94e2d5",
|
|
||||||
"warning_disabled": "#fab3874d",
|
|
||||||
"ghost_element_background": "#cdd6f400",
|
|
||||||
"ghost_element_background_alt": "#181825",
|
|
||||||
"ghost_element_hover": "#cdd6f40d",
|
|
||||||
"ghost_element_active": "#cdd6f41a",
|
|
||||||
"ghost_element_selected": "#cdd6f41a",
|
|
||||||
"ghost_element_disabled": "#cdd6f405",
|
|
||||||
"tab_background": "#11111b",
|
|
||||||
"tab_foreground": "#a6adc8",
|
|
||||||
"tab_hover_background": "#cdd6f40d",
|
|
||||||
"tab_active_background": "#1e1e2e",
|
|
||||||
"tab_active_foreground": "#cdd6f4",
|
|
||||||
"scrollbar_thumb_background": "#cdd6f41a",
|
|
||||||
"scrollbar_thumb_hover_background": "#cdd6f426",
|
|
||||||
"scrollbar_thumb_border": "#cdd6f400",
|
|
||||||
"scrollbar_track_background": "#cdd6f400",
|
|
||||||
"scrollbar_track_border": "#cdd6f400",
|
|
||||||
"drop_target_background": "#89b4fa1a",
|
|
||||||
"cursor": "#89b4fa",
|
|
||||||
"selection": "#89b4fa40"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "flexoki",
|
|
||||||
"name": "Flexoki",
|
|
||||||
"author": "Steph Ango (ported by Coop)",
|
|
||||||
"url": "https://stephango.com/flexoki",
|
|
||||||
"light": {
|
|
||||||
"background": "#FFFCF0",
|
|
||||||
"surface_background": "#F2F0E5",
|
|
||||||
"elevated_surface_background": "#E6E4D9",
|
|
||||||
"panel_background": "#FFFCF0",
|
|
||||||
"overlay": "#100F0F1A",
|
|
||||||
"title_bar": "#E6E4D9",
|
|
||||||
"title_bar_inactive": "#FFFCF0",
|
|
||||||
"window_border": "#CECDC3",
|
|
||||||
"border": "#CECDC3",
|
|
||||||
"border_variant": "#DAD8CE",
|
|
||||||
"border_focused": "#24837B",
|
|
||||||
"border_selected": "#24837B",
|
|
||||||
"border_transparent": "#100F0F00",
|
|
||||||
"border_disabled": "#E6E4D9",
|
|
||||||
"ring": "#3AA99F",
|
|
||||||
"text": "#100F0F",
|
|
||||||
"text_muted": "#6F6E69",
|
|
||||||
"text_placeholder": "#B7B5AC",
|
|
||||||
"text_accent": "#24837B",
|
|
||||||
"text_danger": "#AF3029",
|
|
||||||
"text_warning": "#BC5215",
|
|
||||||
"icon": "#6F6E69",
|
|
||||||
"icon_muted": "#B7B5AC",
|
|
||||||
"icon_accent": "#3AA99F",
|
|
||||||
"element_foreground": "#FFFCF0",
|
|
||||||
"element_background": "#24837B",
|
|
||||||
"element_hover": "#3AA99F",
|
|
||||||
"element_active": "#1C1B1A",
|
|
||||||
"element_selected": "#100F0F",
|
|
||||||
"element_disabled": "#24837B4D",
|
|
||||||
"secondary_foreground": "#100F0F",
|
|
||||||
"secondary_background": "#E6E4D9",
|
|
||||||
"secondary_hover": "#DAD8CE",
|
|
||||||
"secondary_active": "#CECDC3",
|
|
||||||
"secondary_selected": "#CECDC3",
|
|
||||||
"secondary_disabled": "#24837B4D",
|
|
||||||
"danger_foreground": "#FFFCF0",
|
|
||||||
"danger_background": "#AF3029",
|
|
||||||
"danger_hover": "#D14D41",
|
|
||||||
"danger_active": "#1C1B1A",
|
|
||||||
"danger_selected": "#100F0F",
|
|
||||||
"danger_disabled": "#AF30294D",
|
|
||||||
"warning_foreground": "#FFFCF0",
|
|
||||||
"warning_background": "#BC5215",
|
|
||||||
"warning_hover": "#DA702C",
|
|
||||||
"warning_active": "#1C1B1A",
|
|
||||||
"warning_selected": "#100F0F",
|
|
||||||
"warning_disabled": "#BC52154D",
|
|
||||||
"ghost_element_background": "#100F0F00",
|
|
||||||
"ghost_element_background_alt": "#F2F0E5",
|
|
||||||
"ghost_element_hover": "#100F0F0D",
|
|
||||||
"ghost_element_active": "#100F0F1A",
|
|
||||||
"ghost_element_selected": "#100F0F1A",
|
|
||||||
"ghost_element_disabled": "#100F0F05",
|
|
||||||
"tab_background": "#E6E4D9",
|
|
||||||
"tab_foreground": "#6F6E69",
|
|
||||||
"tab_hover_background": "#100F0F0D",
|
|
||||||
"tab_active_background": "#FFFCF0",
|
|
||||||
"tab_active_foreground": "#100F0F",
|
|
||||||
"scrollbar_thumb_background": "#100F0F1A",
|
|
||||||
"scrollbar_thumb_hover_background": "#100F0F26",
|
|
||||||
"scrollbar_thumb_border": "#100F0F00",
|
|
||||||
"scrollbar_track_background": "#100F0F00",
|
|
||||||
"scrollbar_track_border": "#100F0F00",
|
|
||||||
"drop_target_background": "#24837B1A",
|
|
||||||
"cursor": "#24837B",
|
|
||||||
"selection": "#24837B40"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"background": "#100F0F",
|
|
||||||
"surface_background": "#1C1B1A",
|
|
||||||
"elevated_surface_background": "#282726",
|
|
||||||
"panel_background": "#100F0F",
|
|
||||||
"overlay": "#FFFCF01A",
|
|
||||||
"title_bar": "#282726",
|
|
||||||
"title_bar_inactive": "#100F0F",
|
|
||||||
"window_border": "#403E3C",
|
|
||||||
"border": "#403E3C",
|
|
||||||
"border_variant": "#343331",
|
|
||||||
"border_focused": "#3AA99F",
|
|
||||||
"border_selected": "#3AA99F",
|
|
||||||
"border_transparent": "#100F0F00",
|
|
||||||
"border_disabled": "#282726",
|
|
||||||
"ring": "#24837B",
|
|
||||||
"text": "#CECDC3",
|
|
||||||
"text_muted": "#878580",
|
|
||||||
"text_placeholder": "#575653",
|
|
||||||
"text_accent": "#3AA99F",
|
|
||||||
"text_danger": "#D14D41",
|
|
||||||
"text_warning": "#DA702C",
|
|
||||||
"icon": "#878580",
|
|
||||||
"icon_muted": "#575653",
|
|
||||||
"icon_accent": "#24837B",
|
|
||||||
"element_foreground": "#100F0F",
|
|
||||||
"element_background": "#3AA99F",
|
|
||||||
"element_hover": "#24837B",
|
|
||||||
"element_active": "#CECDC3",
|
|
||||||
"element_selected": "#F2F0E5",
|
|
||||||
"element_disabled": "#3AA99F4D",
|
|
||||||
"secondary_foreground": "#CECDC3",
|
|
||||||
"secondary_background": "#1C1B1A",
|
|
||||||
"secondary_hover": "#282726",
|
|
||||||
"secondary_active": "#343331",
|
|
||||||
"secondary_selected": "#343331",
|
|
||||||
"secondary_disabled": "#3AA99F4D",
|
|
||||||
"danger_foreground": "#100F0F",
|
|
||||||
"danger_background": "#D14D41",
|
|
||||||
"danger_hover": "#AF3029",
|
|
||||||
"danger_active": "#CECDC3",
|
|
||||||
"danger_selected": "#F2F0E5",
|
|
||||||
"danger_disabled": "#D14D414D",
|
|
||||||
"warning_foreground": "#100F0F",
|
|
||||||
"warning_background": "#DA702C",
|
|
||||||
"warning_hover": "#BC5215",
|
|
||||||
"warning_active": "#CECDC3",
|
|
||||||
"warning_selected": "#F2F0E5",
|
|
||||||
"warning_disabled": "#DA702C4D",
|
|
||||||
"ghost_element_background": "#100F0F00",
|
|
||||||
"ghost_element_background_alt": "#1C1B1A",
|
|
||||||
"ghost_element_hover": "#FFFCF00D",
|
|
||||||
"ghost_element_active": "#FFFCF01A",
|
|
||||||
"ghost_element_selected": "#FFFCF01A",
|
|
||||||
"ghost_element_disabled": "#FFFCF005",
|
|
||||||
"tab_background": "#282726",
|
|
||||||
"tab_foreground": "#878580",
|
|
||||||
"tab_hover_background": "#FFFCF00D",
|
|
||||||
"tab_active_background": "#100F0F",
|
|
||||||
"tab_active_foreground": "#CECDC3",
|
|
||||||
"scrollbar_thumb_background": "#FFFCF01A",
|
|
||||||
"scrollbar_thumb_hover_background": "#FFFCF026",
|
|
||||||
"scrollbar_thumb_border": "#100F0F00",
|
|
||||||
"scrollbar_track_background": "#100F0F00",
|
|
||||||
"scrollbar_track_border": "#100F0F00",
|
|
||||||
"drop_target_background": "#3AA99F1A",
|
|
||||||
"cursor": "#3AA99F",
|
|
||||||
"selection": "#3AA99F40"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "forest",
|
|
||||||
"name": "Forest",
|
|
||||||
"author": "Coop",
|
|
||||||
"url": "https://coopchat.xyz",
|
|
||||||
"light": {
|
|
||||||
"background": "#fbfefcff",
|
|
||||||
"surface_background": "#f4fbf6ff",
|
|
||||||
"elevated_surface_background": "#e9f6e9ff",
|
|
||||||
"panel_background": "#fbfefcff",
|
|
||||||
"overlay": "#193b2d1a",
|
|
||||||
"title_bar": "#e9f6e9ff",
|
|
||||||
"title_bar_inactive": "#fbfefcff",
|
|
||||||
"window_border": "#c4e8d1ff",
|
|
||||||
"border": "#c4e8d1ff",
|
|
||||||
"border_variant": "#b2ddb5ff",
|
|
||||||
"border_focused": "#30a46cff",
|
|
||||||
"border_selected": "#30a46cff",
|
|
||||||
"border_transparent": "#00000000",
|
|
||||||
"border_disabled": "#e0f3e6ff",
|
|
||||||
"ring": "#2b9a66ff",
|
|
||||||
"text": "#193b2dff",
|
|
||||||
"text_muted": "#2f7c57ff",
|
|
||||||
"text_placeholder": "#8eceaaff",
|
|
||||||
"text_accent": "#30a46cff",
|
|
||||||
"text_danger": "#e54d2eff",
|
|
||||||
"text_warning": "#f76b15ff",
|
|
||||||
"icon": "#2f7c57ff",
|
|
||||||
"icon_muted": "#8eceaaff",
|
|
||||||
"icon_accent": "#2b9a66ff",
|
|
||||||
"element_foreground": "#ffffffff",
|
|
||||||
"element_background": "#30a46cff",
|
|
||||||
"element_hover": "#2b9a66ff",
|
|
||||||
"element_active": "#2a7e3bff",
|
|
||||||
"element_selected": "#218358ff",
|
|
||||||
"element_disabled": "#30a46c4d",
|
|
||||||
"secondary_foreground": "#193b2dff",
|
|
||||||
"secondary_background": "#e9f6e9ff",
|
|
||||||
"secondary_hover": "#daf1dbff",
|
|
||||||
"secondary_active": "#c4e8d1ff",
|
|
||||||
"secondary_selected": "#c4e8d1ff",
|
|
||||||
"secondary_disabled": "#30a46c4d",
|
|
||||||
"danger_foreground": "#ffffffff",
|
|
||||||
"danger_background": "#feebe7ff",
|
|
||||||
"danger_hover": "#ffcdc2ff",
|
|
||||||
"danger_active": "#fdbdafff",
|
|
||||||
"danger_selected": "#fdbdafff",
|
|
||||||
"danger_disabled": "#e54d2e4d",
|
|
||||||
"warning_foreground": "#ffffffff",
|
|
||||||
"warning_background": "#fff7edff",
|
|
||||||
"warning_hover": "#ffd19aff",
|
|
||||||
"warning_active": "#ffc182ff",
|
|
||||||
"warning_selected": "#ffc182ff",
|
|
||||||
"warning_disabled": "#f76b154d",
|
|
||||||
"ghost_element_background": "#00000000",
|
|
||||||
"ghost_element_background_alt": "#e9f6e9ff",
|
|
||||||
"ghost_element_hover": "#193b2d0d",
|
|
||||||
"ghost_element_active": "#193b2d1a",
|
|
||||||
"ghost_element_selected": "#193b2d1a",
|
|
||||||
"ghost_element_disabled": "#193b2d05",
|
|
||||||
"tab_background": "#e9f6e9ff",
|
|
||||||
"tab_foreground": "#2f7c57ff",
|
|
||||||
"tab_hover_background": "#193b2d0d",
|
|
||||||
"tab_active_background": "#fbfefcff",
|
|
||||||
"tab_active_foreground": "#193b2dff",
|
|
||||||
"scrollbar_thumb_background": "#193b2d1a",
|
|
||||||
"scrollbar_thumb_hover_background": "#193b2d26",
|
|
||||||
"scrollbar_thumb_border": "#00000000",
|
|
||||||
"scrollbar_track_background": "#00000000",
|
|
||||||
"scrollbar_track_border": "#00000000",
|
|
||||||
"drop_target_background": "#30a46c1a",
|
|
||||||
"cursor": "#30a46cff",
|
|
||||||
"selection": "#30a46c40"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"background": "#0e1512ff",
|
|
||||||
"surface_background": "#121b17ff",
|
|
||||||
"elevated_surface_background": "#132d21ff",
|
|
||||||
"panel_background": "#0e1512ff",
|
|
||||||
"overlay": "#b1f1cb1a",
|
|
||||||
"title_bar": "#132d21ff",
|
|
||||||
"title_bar_inactive": "#0e1512ff",
|
|
||||||
"window_border": "#20573eff",
|
|
||||||
"border": "#20573eff",
|
|
||||||
"border_variant": "#174933ff",
|
|
||||||
"border_focused": "#33b074ff",
|
|
||||||
"border_selected": "#33b074ff",
|
|
||||||
"border_transparent": "#00000000",
|
|
||||||
"border_disabled": "#113b29ff",
|
|
||||||
"ring": "#33b074ff",
|
|
||||||
"text": "#b1f1cbff",
|
|
||||||
"text_muted": "#71d083ff",
|
|
||||||
"text_placeholder": "#2f7c57ff",
|
|
||||||
"text_accent": "#3dd68cff",
|
|
||||||
"text_danger": "#ff977dff",
|
|
||||||
"text_warning": "#ffa057ff",
|
|
||||||
"icon": "#71d083ff",
|
|
||||||
"icon_muted": "#2f7c57ff",
|
|
||||||
"icon_accent": "#33b074ff",
|
|
||||||
"element_foreground": "#0e1512ff",
|
|
||||||
"element_background": "#3dd68cff",
|
|
||||||
"element_hover": "#71d083ff",
|
|
||||||
"element_active": "#33b074ff",
|
|
||||||
"element_selected": "#30a46cff",
|
|
||||||
"element_disabled": "#3dd68c4d",
|
|
||||||
"secondary_foreground": "#b1f1cbff",
|
|
||||||
"secondary_background": "#132d21ff",
|
|
||||||
"secondary_hover": "#113b29ff",
|
|
||||||
"secondary_active": "#174933ff",
|
|
||||||
"secondary_selected": "#174933ff",
|
|
||||||
"secondary_disabled": "#3dd68c4d",
|
|
||||||
"danger_foreground": "#181111ff",
|
|
||||||
"danger_background": "#391714ff",
|
|
||||||
"danger_hover": "#5e1c16ff",
|
|
||||||
"danger_active": "#6e2920ff",
|
|
||||||
"danger_selected": "#6e2920ff",
|
|
||||||
"danger_disabled": "#ff977d4d",
|
|
||||||
"warning_foreground": "#17120eff",
|
|
||||||
"warning_background": "#331e0bff",
|
|
||||||
"warning_hover": "#562800ff",
|
|
||||||
"warning_active": "#66350cff",
|
|
||||||
"warning_selected": "#66350cff",
|
|
||||||
"warning_disabled": "#ffa0574d",
|
|
||||||
"ghost_element_background": "#00000000",
|
|
||||||
"ghost_element_background_alt": "#132d21ff",
|
|
||||||
"ghost_element_hover": "#b1f1cb0d",
|
|
||||||
"ghost_element_active": "#b1f1cb1a",
|
|
||||||
"ghost_element_selected": "#b1f1cb1a",
|
|
||||||
"ghost_element_disabled": "#b1f1cb05",
|
|
||||||
"tab_background": "#132d21ff",
|
|
||||||
"tab_foreground": "#71d083ff",
|
|
||||||
"tab_hover_background": "#b1f1cb0d",
|
|
||||||
"tab_active_background": "#0e1512ff",
|
|
||||||
"tab_active_foreground": "#b1f1cbff",
|
|
||||||
"scrollbar_thumb_background": "#b1f1cb1a",
|
|
||||||
"scrollbar_thumb_hover_background": "#b1f1cb26",
|
|
||||||
"scrollbar_thumb_border": "#00000000",
|
|
||||||
"scrollbar_track_background": "#00000000",
|
|
||||||
"scrollbar_track_border": "#00000000",
|
|
||||||
"drop_target_background": "#3dd68c1a",
|
|
||||||
"cursor": "#3dd68cff",
|
|
||||||
"selection": "#3dd68c40"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "ocean",
|
|
||||||
"name": "Ocean",
|
|
||||||
"author": "Coop",
|
|
||||||
"url": "https://coopchat.xyz",
|
|
||||||
"light": {
|
|
||||||
"background": "#fafefeff",
|
|
||||||
"surface_background": "#f2fbfaff",
|
|
||||||
"elevated_surface_background": "#e6f7f7ff",
|
|
||||||
"panel_background": "#fafefeff",
|
|
||||||
"overlay": "#00333f1a",
|
|
||||||
"title_bar": "#e6f7f7ff",
|
|
||||||
"title_bar_inactive": "#fafefeff",
|
|
||||||
"window_border": "#cce5e9ff",
|
|
||||||
"border": "#cce5e9ff",
|
|
||||||
"border_variant": "#b8dde3ff",
|
|
||||||
"border_focused": "#00a2c7ff",
|
|
||||||
"border_selected": "#00a2c7ff",
|
|
||||||
"border_transparent": "#00000000",
|
|
||||||
"border_disabled": "#e0f0f2ff",
|
|
||||||
"ring": "#0797b9ff",
|
|
||||||
"text": "#0d3c48ff",
|
|
||||||
"text_muted": "#107d98ff",
|
|
||||||
"text_placeholder": "#60b3d7ff",
|
|
||||||
"text_accent": "#00a2c7ff",
|
|
||||||
"text_danger": "#e54d2eff",
|
|
||||||
"text_warning": "#f76b15ff",
|
|
||||||
"icon": "#107d98ff",
|
|
||||||
"icon_muted": "#60b3d7ff",
|
|
||||||
"icon_accent": "#0797b9ff",
|
|
||||||
"element_foreground": "#ffffffff",
|
|
||||||
"element_background": "#00a2c7ff",
|
|
||||||
"element_hover": "#0797b9ff",
|
|
||||||
"element_active": "#12667eff",
|
|
||||||
"element_selected": "#0d4a5cff",
|
|
||||||
"element_disabled": "#00a2c74d",
|
|
||||||
"secondary_foreground": "#0d4a5cff",
|
|
||||||
"secondary_background": "#ddf9f2ff",
|
|
||||||
"secondary_hover": "#c8f4e9ff",
|
|
||||||
"secondary_active": "#b3ecdeff",
|
|
||||||
"secondary_selected": "#b3ecdeff",
|
|
||||||
"secondary_disabled": "#00a2c74d",
|
|
||||||
"danger_foreground": "#ffffffff",
|
|
||||||
"danger_background": "#feebe7ff",
|
|
||||||
"danger_hover": "#ffcdc2ff",
|
|
||||||
"danger_active": "#fdbdafff",
|
|
||||||
"danger_selected": "#fdbdafff",
|
|
||||||
"danger_disabled": "#e54d2e4d",
|
|
||||||
"warning_foreground": "#ffffffff",
|
|
||||||
"warning_background": "#fff7edff",
|
|
||||||
"warning_hover": "#ffd19aff",
|
|
||||||
"warning_active": "#ffc182ff",
|
|
||||||
"warning_selected": "#ffc182ff",
|
|
||||||
"warning_disabled": "#f76b154d",
|
|
||||||
"ghost_element_background": "#00000000",
|
|
||||||
"ghost_element_background_alt": "#e6f7f7ff",
|
|
||||||
"ghost_element_hover": "#00333f0d",
|
|
||||||
"ghost_element_active": "#00333f1a",
|
|
||||||
"ghost_element_selected": "#00333f1a",
|
|
||||||
"ghost_element_disabled": "#00333f05",
|
|
||||||
"tab_background": "#e6f7f7ff",
|
|
||||||
"tab_foreground": "#107d98ff",
|
|
||||||
"tab_hover_background": "#00333f0d",
|
|
||||||
"tab_active_background": "#fafefeff",
|
|
||||||
"tab_active_foreground": "#0d3c48ff",
|
|
||||||
"scrollbar_thumb_background": "#00333f1a",
|
|
||||||
"scrollbar_thumb_hover_background": "#00333f26",
|
|
||||||
"scrollbar_thumb_border": "#00000000",
|
|
||||||
"scrollbar_track_background": "#00000000",
|
|
||||||
"scrollbar_track_border": "#00000000",
|
|
||||||
"drop_target_background": "#00a2c71a",
|
|
||||||
"cursor": "#00a2c7ff",
|
|
||||||
"selection": "#00a2c740"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"background": "#0b161aff",
|
|
||||||
"surface_background": "#101b20ff",
|
|
||||||
"elevated_surface_background": "#082c36ff",
|
|
||||||
"panel_background": "#0b161aff",
|
|
||||||
"overlay": "#c2f3ff1a",
|
|
||||||
"title_bar": "#082c36ff",
|
|
||||||
"title_bar_inactive": "#0b161aff",
|
|
||||||
"window_border": "#1b537bff",
|
|
||||||
"border": "#1b537bff",
|
|
||||||
"border_variant": "#154467ff",
|
|
||||||
"border_focused": "#00a2c7ff",
|
|
||||||
"border_selected": "#00a2c7ff",
|
|
||||||
"border_transparent": "#00000000",
|
|
||||||
"border_disabled": "#112840ff",
|
|
||||||
"ring": "#23afd0ff",
|
|
||||||
"text": "#b6ecf7ff",
|
|
||||||
"text_muted": "#4ccce6ff",
|
|
||||||
"text_placeholder": "#197caeff",
|
|
||||||
"text_accent": "#7ce2feff",
|
|
||||||
"text_danger": "#ff977dff",
|
|
||||||
"text_warning": "#ffa057ff",
|
|
||||||
"icon": "#4ccce6ff",
|
|
||||||
"icon_muted": "#197caeff",
|
|
||||||
"icon_accent": "#23afd0ff",
|
|
||||||
"element_foreground": "#0b161aff",
|
|
||||||
"element_background": "#7ce2feff",
|
|
||||||
"element_hover": "#a8eeffff",
|
|
||||||
"element_active": "#23afd0ff",
|
|
||||||
"element_selected": "#00a2c7ff",
|
|
||||||
"element_disabled": "#7ce2fe4d",
|
|
||||||
"secondary_foreground": "#adf0ddff",
|
|
||||||
"secondary_background": "#0d2d2aff",
|
|
||||||
"secondary_hover": "#023b37ff",
|
|
||||||
"secondary_active": "#084843ff",
|
|
||||||
"secondary_selected": "#084843ff",
|
|
||||||
"secondary_disabled": "#7ce2fe4d",
|
|
||||||
"danger_foreground": "#181111ff",
|
|
||||||
"danger_background": "#391714ff",
|
|
||||||
"danger_hover": "#5e1c16ff",
|
|
||||||
"danger_active": "#6e2920ff",
|
|
||||||
"danger_selected": "#6e2920ff",
|
|
||||||
"danger_disabled": "#ff977d4d",
|
|
||||||
"warning_foreground": "#17120eff",
|
|
||||||
"warning_background": "#331e0bff",
|
|
||||||
"warning_hover": "#562800ff",
|
|
||||||
"warning_active": "#66350cff",
|
|
||||||
"warning_selected": "#66350cff",
|
|
||||||
"warning_disabled": "#ffa0574d",
|
|
||||||
"ghost_element_background": "#00000000",
|
|
||||||
"ghost_element_background_alt": "#082c36ff",
|
|
||||||
"ghost_element_hover": "#c2f3ff0d",
|
|
||||||
"ghost_element_active": "#c2f3ff1a",
|
|
||||||
"ghost_element_selected": "#c2f3ff1a",
|
|
||||||
"ghost_element_disabled": "#c2f3ff05",
|
|
||||||
"tab_background": "#082c36ff",
|
|
||||||
"tab_foreground": "#4ccce6ff",
|
|
||||||
"tab_hover_background": "#c2f3ff0d",
|
|
||||||
"tab_active_background": "#0b161aff",
|
|
||||||
"tab_active_foreground": "#b6ecf7ff",
|
|
||||||
"scrollbar_thumb_background": "#c2f3ff1a",
|
|
||||||
"scrollbar_thumb_hover_background": "#c2f3ff26",
|
|
||||||
"scrollbar_thumb_border": "#00000000",
|
|
||||||
"scrollbar_track_background": "#00000000",
|
|
||||||
"scrollbar_track_border": "#00000000",
|
|
||||||
"drop_target_background": "#7ce2fe1a",
|
|
||||||
"cursor": "#7ce2feff",
|
|
||||||
"selection": "#7ce2fe40"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ use rust_embed::RustEmbed;
|
|||||||
#[include = "fonts/**/*"]
|
#[include = "fonts/**/*"]
|
||||||
#[include = "brand/**/*"]
|
#[include = "brand/**/*"]
|
||||||
#[include = "icons/**/*"]
|
#[include = "icons/**/*"]
|
||||||
#[include = "themes/**/*"]
|
|
||||||
#[exclude = "*.DS_Store"]
|
#[exclude = "*.DS_Store"]
|
||||||
pub struct Assets;
|
pub struct Assets;
|
||||||
|
|
||||||
|
|||||||
+64
-32
@@ -1,12 +1,10 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use anyhow::{Error, anyhow};
|
use anyhow::{Error, anyhow};
|
||||||
use common::config_dir;
|
use common::config_dir;
|
||||||
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task, Window};
|
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task, Window};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use theme::{Theme, ThemeFamily, ThemeMode};
|
use theme::{AccentSelection, Appearance, SurfacePreference, Theme, ThemeSelection};
|
||||||
|
|
||||||
pub fn init(cx: &mut App) {
|
pub fn init(cx: &mut App) {
|
||||||
AppSettings::set_global(cx.new(AppSettings::new), cx)
|
AppSettings::set_global(cx.new(AppSettings::new), cx)
|
||||||
@@ -53,7 +51,10 @@ macro_rules! setting_getters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setting_accessors! {
|
setting_accessors! {
|
||||||
pub theme_mode: ThemeMode,
|
pub appearance: Appearance,
|
||||||
|
pub accent_selection: AccentSelection,
|
||||||
|
pub surface_preference: SurfacePreference,
|
||||||
|
pub theme_selection: ThemeSelection,
|
||||||
pub hide_avatar: bool,
|
pub hide_avatar: bool,
|
||||||
pub screening: bool,
|
pub screening: bool,
|
||||||
pub nip4e: bool,
|
pub nip4e: bool,
|
||||||
@@ -119,11 +120,26 @@ impl RoomConfig {
|
|||||||
/// Settings
|
/// Settings
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
/// Theme
|
/// The per-appearance theme variant selection
|
||||||
pub theme: Option<String>,
|
#[serde(default)]
|
||||||
|
pub theme_selection: ThemeSelection,
|
||||||
|
|
||||||
/// Theme mode
|
/// The active appearance; persisted under the legacy `theme_mode` key so
|
||||||
pub theme_mode: ThemeMode,
|
/// old settings files ("Light"/"Dark") keep loading
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
rename = "theme_mode",
|
||||||
|
deserialize_with = "deserialize_appearance"
|
||||||
|
)]
|
||||||
|
pub appearance: Appearance,
|
||||||
|
|
||||||
|
/// The accent color override applied on top of the selected theme
|
||||||
|
#[serde(default)]
|
||||||
|
pub accent_selection: AccentSelection,
|
||||||
|
|
||||||
|
/// The preferred surface treatment
|
||||||
|
#[serde(default)]
|
||||||
|
pub surface_preference: SurfacePreference,
|
||||||
|
|
||||||
/// Hide user avatars
|
/// Hide user avatars
|
||||||
pub hide_avatar: bool,
|
pub hide_avatar: bool,
|
||||||
@@ -152,8 +168,10 @@ pub struct Settings {
|
|||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
theme: None,
|
theme_selection: ThemeSelection::default(),
|
||||||
theme_mode: ThemeMode::default(),
|
appearance: Appearance::default(),
|
||||||
|
accent_selection: AccentSelection::default(),
|
||||||
|
surface_preference: SurfacePreference::default(),
|
||||||
hide_avatar: false,
|
hide_avatar: false,
|
||||||
screening: true,
|
screening: true,
|
||||||
nip4e: false,
|
nip4e: false,
|
||||||
@@ -165,6 +183,21 @@ impl Default for Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Accept the legacy "Light"/"Dark" capitalization alongside "light"/"dark".
|
||||||
|
fn deserialize_appearance<'de, D>(deserializer: D) -> Result<Appearance, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = String::deserialize(deserializer)?;
|
||||||
|
match value.to_lowercase().as_str() {
|
||||||
|
"light" => Ok(Appearance::Light),
|
||||||
|
"dark" => Ok(Appearance::Dark),
|
||||||
|
_ => Err(serde::de::Error::custom(format!(
|
||||||
|
"unknown appearance: {value}"
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct GlobalAppSettings(Entity<AppSettings>);
|
struct GlobalAppSettings(Entity<AppSettings>);
|
||||||
|
|
||||||
impl Global for GlobalAppSettings {}
|
impl Global for GlobalAppSettings {}
|
||||||
@@ -264,44 +297,43 @@ impl AppSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set theme
|
/// Set the theme variant for the current appearance
|
||||||
pub fn set_theme<T>(&mut self, theme: T, window: &mut Window, cx: &mut Context<Self>)
|
pub fn set_theme_variant(&mut self, id: String, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
where
|
|
||||||
T: Into<String>,
|
|
||||||
{
|
|
||||||
// Update settings
|
|
||||||
self.inner.update(cx, |this, cx| {
|
self.inner.update(cx, |this, cx| {
|
||||||
this.theme = Some(theme.into());
|
let mut selection = this.theme_selection.clone();
|
||||||
|
selection.set_variant(this.appearance, id);
|
||||||
|
this.theme_selection = selection;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply the new theme
|
|
||||||
self.apply_theme(Some(window), cx);
|
self.apply_theme(Some(window), cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset theme
|
/// Reset the theme selection to the defaults
|
||||||
pub fn reset_theme(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
pub fn reset_theme(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.inner.update(cx, |this, cx| {
|
self.inner.update(cx, |this, cx| {
|
||||||
this.theme = None;
|
this.theme_selection = ThemeSelection::default();
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
self.apply_theme(Some(window), cx);
|
self.apply_theme(Some(window), cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply theme
|
/// Apply theme
|
||||||
pub fn apply_theme(&mut self, mut window: Option<&mut Window>, cx: &mut Context<Self>) {
|
pub fn apply_theme(&mut self, window: Option<&mut Window>, cx: &mut Context<Self>) {
|
||||||
if let Some(name) = self.inner.read(cx).theme.as_ref() {
|
let (selection, accent, surface, appearance) = {
|
||||||
let mode = self.inner.read(cx).theme_mode;
|
let settings = self.inner.read(cx);
|
||||||
|
(
|
||||||
|
settings.theme_selection.clone(),
|
||||||
|
settings.accent_selection,
|
||||||
|
settings.surface_preference,
|
||||||
|
settings.appearance,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
if let Ok(new_theme) = ThemeFamily::from_assets(name) {
|
Theme::set_selection(selection, cx);
|
||||||
Theme::apply_theme(Rc::new(new_theme), window.as_deref_mut(), cx);
|
Theme::set_accent_selection(accent, cx);
|
||||||
Theme::change(mode, window, cx);
|
Theme::set_surface_preference(surface, cx);
|
||||||
} else {
|
Theme::change(appearance, window, cx);
|
||||||
log::info!("Failed to load theme: {name}");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Theme::apply_theme(Rc::new(ThemeFamily::default()), window, cx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if decoupling encryption key is enabled
|
/// Check if decoupling encryption key is enabled
|
||||||
|
|||||||
@@ -13,7 +13,3 @@ serde.workspace = true
|
|||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
schemars.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
tempfile = "3.10"
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// Ported from zeronsh/zeron crates/theme/src/builtins.rs
|
|
||||||
// at revision 916cb1ccb1342c2061963a5f6ecf5c977ba46f2c (MIT).
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|||||||
@@ -1,583 +0,0 @@
|
|||||||
use gpui::{Hsla, Rgba, hsla};
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::scale::{ColorScale, ColorScaleSet};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize, Serialize, JsonSchema)]
|
|
||||||
pub struct ThemeColors {
|
|
||||||
// Surface colors
|
|
||||||
pub background: Hsla,
|
|
||||||
pub surface_background: Hsla,
|
|
||||||
pub elevated_surface_background: Hsla,
|
|
||||||
pub panel_background: Hsla,
|
|
||||||
pub overlay: Hsla,
|
|
||||||
pub title_bar: Hsla,
|
|
||||||
pub title_bar_inactive: Hsla,
|
|
||||||
pub window_border: Hsla,
|
|
||||||
|
|
||||||
// Border colors
|
|
||||||
pub border: Hsla,
|
|
||||||
pub border_variant: Hsla,
|
|
||||||
pub border_focused: Hsla,
|
|
||||||
pub border_selected: Hsla,
|
|
||||||
pub border_transparent: Hsla,
|
|
||||||
pub border_disabled: Hsla,
|
|
||||||
pub ring: Hsla,
|
|
||||||
|
|
||||||
// Text colors
|
|
||||||
pub text: Hsla,
|
|
||||||
pub text_muted: Hsla,
|
|
||||||
pub text_placeholder: Hsla,
|
|
||||||
pub text_accent: Hsla,
|
|
||||||
pub text_danger: Hsla,
|
|
||||||
pub text_warning: Hsla,
|
|
||||||
|
|
||||||
// Icon colors
|
|
||||||
pub icon: Hsla,
|
|
||||||
pub icon_muted: Hsla,
|
|
||||||
pub icon_accent: Hsla,
|
|
||||||
|
|
||||||
// Element colors
|
|
||||||
pub element_foreground: Hsla,
|
|
||||||
pub element_background: Hsla,
|
|
||||||
pub element_hover: Hsla,
|
|
||||||
pub element_active: Hsla,
|
|
||||||
pub element_selected: Hsla,
|
|
||||||
pub element_disabled: Hsla,
|
|
||||||
|
|
||||||
// Secondary element colors
|
|
||||||
pub secondary_foreground: Hsla,
|
|
||||||
pub secondary_background: Hsla,
|
|
||||||
pub secondary_hover: Hsla,
|
|
||||||
pub secondary_active: Hsla,
|
|
||||||
pub secondary_selected: Hsla,
|
|
||||||
pub secondary_disabled: Hsla,
|
|
||||||
|
|
||||||
// Danger element colors
|
|
||||||
pub danger_foreground: Hsla,
|
|
||||||
pub danger_background: Hsla,
|
|
||||||
pub danger_hover: Hsla,
|
|
||||||
pub danger_active: Hsla,
|
|
||||||
pub danger_selected: Hsla,
|
|
||||||
pub danger_disabled: Hsla,
|
|
||||||
|
|
||||||
// Warning element colors
|
|
||||||
pub warning_foreground: Hsla,
|
|
||||||
pub warning_background: Hsla,
|
|
||||||
pub warning_hover: Hsla,
|
|
||||||
pub warning_active: Hsla,
|
|
||||||
pub warning_selected: Hsla,
|
|
||||||
pub warning_disabled: Hsla,
|
|
||||||
|
|
||||||
// Ghost element colors
|
|
||||||
pub ghost_element_background: Hsla,
|
|
||||||
pub ghost_element_background_alt: Hsla,
|
|
||||||
pub ghost_element_hover: Hsla,
|
|
||||||
pub ghost_element_active: Hsla,
|
|
||||||
pub ghost_element_selected: Hsla,
|
|
||||||
pub ghost_element_disabled: Hsla,
|
|
||||||
|
|
||||||
// Tab colors
|
|
||||||
pub tab_background: Hsla,
|
|
||||||
pub tab_foreground: Hsla,
|
|
||||||
pub tab_hover_background: Hsla,
|
|
||||||
pub tab_active_background: Hsla,
|
|
||||||
pub tab_active_foreground: Hsla,
|
|
||||||
|
|
||||||
// Scrollbar colors
|
|
||||||
pub scrollbar_thumb_background: Hsla,
|
|
||||||
pub scrollbar_thumb_hover_background: Hsla,
|
|
||||||
pub scrollbar_thumb_border: Hsla,
|
|
||||||
pub scrollbar_track_background: Hsla,
|
|
||||||
pub scrollbar_track_border: Hsla,
|
|
||||||
|
|
||||||
// Interactive colors
|
|
||||||
pub drop_target_background: Hsla,
|
|
||||||
pub cursor: Hsla,
|
|
||||||
pub selection: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The default colors for the theme.
|
|
||||||
///
|
|
||||||
/// Themes that do not specify all colors are refined off of these defaults.
|
|
||||||
impl ThemeColors {
|
|
||||||
/// Returns the default colors for light themes.
|
|
||||||
///
|
|
||||||
/// Themes that do not specify all colors are refined off of these defaults.
|
|
||||||
pub(crate) fn light() -> Self {
|
|
||||||
Self {
|
|
||||||
background: neutral().light().step_1(),
|
|
||||||
surface_background: neutral().light().step_2(),
|
|
||||||
elevated_surface_background: neutral().light().step_3(),
|
|
||||||
panel_background: neutral().light().step_1(),
|
|
||||||
overlay: neutral().light_alpha().step_3(),
|
|
||||||
title_bar: neutral().light().step_2(),
|
|
||||||
title_bar_inactive: neutral().light().step_1(),
|
|
||||||
window_border: hsl(240.0, 5.9, 78.0),
|
|
||||||
|
|
||||||
border: neutral().light().step_6(),
|
|
||||||
border_variant: neutral().light().step_4(),
|
|
||||||
border_focused: brand().light().step_7(),
|
|
||||||
border_selected: brand().light().step_7(),
|
|
||||||
border_transparent: gpui::transparent_black(),
|
|
||||||
border_disabled: neutral().light().step_3(),
|
|
||||||
ring: brand().light().step_8(),
|
|
||||||
|
|
||||||
text: neutral().light().step_12(),
|
|
||||||
text_muted: neutral().light().step_11(),
|
|
||||||
text_placeholder: neutral().light().step_10(),
|
|
||||||
text_accent: brand().light().step_9(),
|
|
||||||
text_danger: danger().light().step_9(),
|
|
||||||
text_warning: warning().light().step_9(),
|
|
||||||
|
|
||||||
icon: neutral().light().step_11(),
|
|
||||||
icon_muted: neutral().light().step_10(),
|
|
||||||
icon_accent: brand().light().step_11(),
|
|
||||||
|
|
||||||
element_foreground: brand().light().step_12(),
|
|
||||||
element_background: brand().light().step_9(),
|
|
||||||
element_hover: brand().light_alpha().step_10(),
|
|
||||||
element_active: brand().light().step_10(),
|
|
||||||
element_selected: brand().light().step_11(),
|
|
||||||
element_disabled: brand().light_alpha().step_3(),
|
|
||||||
|
|
||||||
secondary_foreground: brand().light().step_11(),
|
|
||||||
secondary_background: brand().light().step_3(),
|
|
||||||
secondary_hover: brand().light_alpha().step_4(),
|
|
||||||
secondary_active: brand().light().step_5(),
|
|
||||||
secondary_selected: brand().light().step_5(),
|
|
||||||
secondary_disabled: brand().light_alpha().step_3(),
|
|
||||||
|
|
||||||
danger_foreground: danger().light().step_12(),
|
|
||||||
danger_background: danger().light().step_3(),
|
|
||||||
danger_hover: danger().light_alpha().step_4(),
|
|
||||||
danger_active: danger().light().step_5(),
|
|
||||||
danger_selected: danger().light().step_5(),
|
|
||||||
danger_disabled: danger().light_alpha().step_3(),
|
|
||||||
|
|
||||||
warning_foreground: warning().light().step_12(),
|
|
||||||
warning_background: warning().light().step_3(),
|
|
||||||
warning_hover: warning().light_alpha().step_4(),
|
|
||||||
warning_active: warning().light().step_5(),
|
|
||||||
warning_selected: warning().light().step_5(),
|
|
||||||
warning_disabled: warning().light_alpha().step_3(),
|
|
||||||
|
|
||||||
ghost_element_background: gpui::transparent_black(),
|
|
||||||
ghost_element_background_alt: neutral().light().step_3(),
|
|
||||||
ghost_element_hover: neutral().light_alpha().step_4(),
|
|
||||||
ghost_element_active: neutral().light().step_5(),
|
|
||||||
ghost_element_selected: neutral().light().step_5(),
|
|
||||||
ghost_element_disabled: neutral().light_alpha().step_2(),
|
|
||||||
|
|
||||||
tab_background: neutral().light().step_3(),
|
|
||||||
tab_foreground: neutral().light().step_11(),
|
|
||||||
tab_hover_background: neutral().light_alpha().step_4(),
|
|
||||||
tab_active_background: neutral().light().step_1(),
|
|
||||||
tab_active_foreground: neutral().light().step_12(),
|
|
||||||
|
|
||||||
scrollbar_thumb_background: neutral().light_alpha().step_3(),
|
|
||||||
scrollbar_thumb_hover_background: neutral().light_alpha().step_4(),
|
|
||||||
scrollbar_thumb_border: gpui::transparent_black(),
|
|
||||||
scrollbar_track_background: gpui::transparent_black(),
|
|
||||||
scrollbar_track_border: gpui::transparent_black(),
|
|
||||||
|
|
||||||
drop_target_background: brand().light_alpha().step_2(),
|
|
||||||
cursor: hsl(200., 100., 50.),
|
|
||||||
selection: hsl(200., 100., 50.).alpha(0.25),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the default colors for dark themes.
|
|
||||||
///
|
|
||||||
/// Themes that do not specify all colors are refined off of these defaults.
|
|
||||||
pub(crate) fn dark() -> Self {
|
|
||||||
Self {
|
|
||||||
background: neutral().dark().step_1(),
|
|
||||||
surface_background: neutral().dark().step_2(),
|
|
||||||
elevated_surface_background: neutral().dark().step_3(),
|
|
||||||
panel_background: neutral().dark().step_1(),
|
|
||||||
overlay: neutral().dark_alpha().step_3(),
|
|
||||||
title_bar: neutral().dark().step_2(),
|
|
||||||
title_bar_inactive: neutral().dark().step_1(),
|
|
||||||
window_border: hsl(240.0, 3.7, 28.0),
|
|
||||||
|
|
||||||
border: neutral().dark().step_6(),
|
|
||||||
border_variant: neutral().dark().step_4(),
|
|
||||||
border_focused: brand().dark().step_7(),
|
|
||||||
border_selected: brand().dark().step_7(),
|
|
||||||
border_transparent: gpui::transparent_black(),
|
|
||||||
border_disabled: neutral().dark().step_3(),
|
|
||||||
ring: brand().dark().step_8(),
|
|
||||||
|
|
||||||
text: neutral().dark().step_12(),
|
|
||||||
text_muted: neutral().dark().step_11(),
|
|
||||||
text_placeholder: neutral().dark().step_10(),
|
|
||||||
text_accent: brand().dark().step_9(),
|
|
||||||
text_danger: danger().dark().step_9(),
|
|
||||||
text_warning: warning().dark().step_9(),
|
|
||||||
|
|
||||||
icon: neutral().dark().step_11(),
|
|
||||||
icon_muted: neutral().dark().step_10(),
|
|
||||||
icon_accent: brand().dark().step_11(),
|
|
||||||
|
|
||||||
element_foreground: brand().dark().step_1(),
|
|
||||||
element_background: brand().dark().step_9(),
|
|
||||||
element_hover: brand().dark_alpha().step_10(),
|
|
||||||
element_active: brand().dark().step_10(),
|
|
||||||
element_selected: brand().dark().step_11(),
|
|
||||||
element_disabled: brand().dark_alpha().step_3(),
|
|
||||||
|
|
||||||
secondary_foreground: brand().dark().step_12(),
|
|
||||||
secondary_background: brand().dark().step_3(),
|
|
||||||
secondary_hover: brand().dark_alpha().step_4(),
|
|
||||||
secondary_active: brand().dark().step_5(),
|
|
||||||
secondary_selected: brand().dark().step_5(),
|
|
||||||
secondary_disabled: brand().dark_alpha().step_3(),
|
|
||||||
|
|
||||||
danger_foreground: danger().dark().step_12(),
|
|
||||||
danger_background: danger().dark().step_3(),
|
|
||||||
danger_hover: danger().dark_alpha().step_4(),
|
|
||||||
danger_active: danger().dark().step_5(),
|
|
||||||
danger_selected: danger().dark().step_5(),
|
|
||||||
danger_disabled: danger().dark_alpha().step_3(),
|
|
||||||
|
|
||||||
warning_foreground: warning().dark().step_12(),
|
|
||||||
warning_background: warning().dark().step_3(),
|
|
||||||
warning_hover: warning().dark_alpha().step_4(),
|
|
||||||
warning_active: warning().dark().step_5(),
|
|
||||||
warning_selected: warning().dark().step_5(),
|
|
||||||
warning_disabled: warning().dark_alpha().step_3(),
|
|
||||||
|
|
||||||
ghost_element_background: gpui::transparent_black(),
|
|
||||||
ghost_element_background_alt: neutral().dark().step_3(),
|
|
||||||
ghost_element_hover: neutral().dark_alpha().step_4(),
|
|
||||||
ghost_element_active: neutral().dark().step_5(),
|
|
||||||
ghost_element_selected: neutral().dark().step_5(),
|
|
||||||
ghost_element_disabled: neutral().dark_alpha().step_2(),
|
|
||||||
|
|
||||||
tab_background: neutral().dark().step_3(),
|
|
||||||
tab_foreground: neutral().dark().step_11(),
|
|
||||||
tab_hover_background: neutral().dark_alpha().step_4(),
|
|
||||||
tab_active_background: neutral().dark().step_1(),
|
|
||||||
tab_active_foreground: neutral().dark().step_12(),
|
|
||||||
|
|
||||||
scrollbar_thumb_background: neutral().dark_alpha().step_3(),
|
|
||||||
scrollbar_thumb_hover_background: neutral().dark_alpha().step_4(),
|
|
||||||
scrollbar_thumb_border: gpui::transparent_black(),
|
|
||||||
scrollbar_track_background: gpui::transparent_black(),
|
|
||||||
scrollbar_track_border: gpui::transparent_black(),
|
|
||||||
|
|
||||||
drop_target_background: brand().dark_alpha().step_2(),
|
|
||||||
cursor: hsl(200., 100., 50.),
|
|
||||||
selection: hsl(200., 100., 50.).alpha(0.25),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn neutral() -> ColorScaleSet {
|
|
||||||
gray()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn brand() -> ColorScaleSet {
|
|
||||||
yellow()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn danger() -> ColorScaleSet {
|
|
||||||
tomato()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn warning() -> ColorScaleSet {
|
|
||||||
orange()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a [gpui::Hsla] color.
|
|
||||||
///
|
|
||||||
/// - h: 0..360.0
|
|
||||||
/// - s: 0.0..100.0
|
|
||||||
/// - l: 0.0..100.0
|
|
||||||
pub(crate) fn hsl(h: f32, s: f32, l: f32) -> Hsla {
|
|
||||||
hsla(h / 360., s / 100.0, l / 100.0, 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
type StaticColorScale = [&'static str; 12];
|
|
||||||
|
|
||||||
struct StaticColorScaleSet {
|
|
||||||
light: StaticColorScale,
|
|
||||||
light_alpha: StaticColorScale,
|
|
||||||
dark: StaticColorScale,
|
|
||||||
dark_alpha: StaticColorScale,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<StaticColorScaleSet> for ColorScaleSet {
|
|
||||||
type Error = anyhow::Error;
|
|
||||||
|
|
||||||
fn try_from(value: StaticColorScaleSet) -> Result<Self, Self::Error> {
|
|
||||||
fn to_color_scale(scale: StaticColorScale) -> Result<ColorScale, anyhow::Error> {
|
|
||||||
scale
|
|
||||||
.into_iter()
|
|
||||||
.map(|color| Rgba::try_from(color).map(Hsla::from))
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map(ColorScale::from_iter)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self::new(
|
|
||||||
to_color_scale(value.light)?,
|
|
||||||
to_color_scale(value.light_alpha)?,
|
|
||||||
to_color_scale(value.dark)?,
|
|
||||||
to_color_scale(value.dark_alpha)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn gray() -> ColorScaleSet {
|
|
||||||
StaticColorScaleSet {
|
|
||||||
light: [
|
|
||||||
"#fcfcfcff",
|
|
||||||
"#f9f9f9ff",
|
|
||||||
"#f0f0f0ff",
|
|
||||||
"#e8e8e8ff",
|
|
||||||
"#e0e0e0ff",
|
|
||||||
"#d9d9d9ff",
|
|
||||||
"#cececeff",
|
|
||||||
"#bbbbbbff",
|
|
||||||
"#8d8d8dff",
|
|
||||||
"#838383ff",
|
|
||||||
"#646464ff",
|
|
||||||
"#202020ff",
|
|
||||||
],
|
|
||||||
light_alpha: [
|
|
||||||
"#00000003",
|
|
||||||
"#00000006",
|
|
||||||
"#0000000f",
|
|
||||||
"#00000017",
|
|
||||||
"#0000001f",
|
|
||||||
"#00000026",
|
|
||||||
"#00000031",
|
|
||||||
"#00000044",
|
|
||||||
"#00000072",
|
|
||||||
"#0000007c",
|
|
||||||
"#0000009b",
|
|
||||||
"#000000df",
|
|
||||||
],
|
|
||||||
dark: [
|
|
||||||
"#111111ff",
|
|
||||||
"#191919ff",
|
|
||||||
"#222222ff",
|
|
||||||
"#2a2a2aff",
|
|
||||||
"#313131ff",
|
|
||||||
"#3a3a3aff",
|
|
||||||
"#484848ff",
|
|
||||||
"#606060ff",
|
|
||||||
"#6e6e6eff",
|
|
||||||
"#7b7b7bff",
|
|
||||||
"#b4b4b4ff",
|
|
||||||
"#eeeeeeff",
|
|
||||||
],
|
|
||||||
dark_alpha: [
|
|
||||||
"#00000000",
|
|
||||||
"#ffffff09",
|
|
||||||
"#ffffff12",
|
|
||||||
"#ffffff1b",
|
|
||||||
"#ffffff22",
|
|
||||||
"#ffffff2c",
|
|
||||||
"#ffffff3b",
|
|
||||||
"#ffffff55",
|
|
||||||
"#ffffff64",
|
|
||||||
"#ffffff72",
|
|
||||||
"#ffffffaf",
|
|
||||||
"#ffffffed",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn yellow() -> ColorScaleSet {
|
|
||||||
StaticColorScaleSet {
|
|
||||||
light: [
|
|
||||||
"#fdfdf9ff",
|
|
||||||
"#fefce9ff",
|
|
||||||
"#fffab8ff",
|
|
||||||
"#fff394ff",
|
|
||||||
"#ffe770ff",
|
|
||||||
"#f3d768ff",
|
|
||||||
"#e4c767ff",
|
|
||||||
"#d5ae39ff",
|
|
||||||
"#ffe629ff",
|
|
||||||
"#ffdc00ff",
|
|
||||||
"#9e6c00ff",
|
|
||||||
"#473b1fff",
|
|
||||||
],
|
|
||||||
light_alpha: [
|
|
||||||
"#aaaa0006",
|
|
||||||
"#f4dd0016",
|
|
||||||
"#ffee0047",
|
|
||||||
"#ffe3016b",
|
|
||||||
"#ffd5008f",
|
|
||||||
"#ebbc0097",
|
|
||||||
"#d2a10098",
|
|
||||||
"#c99700c6",
|
|
||||||
"#ffe100d6",
|
|
||||||
"#ffdc00ff",
|
|
||||||
"#9e6c00ff",
|
|
||||||
"#2e2000e0",
|
|
||||||
],
|
|
||||||
dark: [
|
|
||||||
"#14120bff",
|
|
||||||
"#1b180fff",
|
|
||||||
"#2d2305ff",
|
|
||||||
"#362b00ff",
|
|
||||||
"#433500ff",
|
|
||||||
"#524202ff",
|
|
||||||
"#665417ff",
|
|
||||||
"#836a21ff",
|
|
||||||
"#ffe629ff",
|
|
||||||
"#ffff57ff",
|
|
||||||
"#f5e147ff",
|
|
||||||
"#f6eeb4ff",
|
|
||||||
],
|
|
||||||
dark_alpha: [
|
|
||||||
"#d1510004",
|
|
||||||
"#f9b4000b",
|
|
||||||
"#ffaa001e",
|
|
||||||
"#fdb70028",
|
|
||||||
"#febb0036",
|
|
||||||
"#fec40046",
|
|
||||||
"#fdcb225c",
|
|
||||||
"#fdca327b",
|
|
||||||
"#ffe629ff",
|
|
||||||
"#ffff57ff",
|
|
||||||
"#fee949f5",
|
|
||||||
"#fef6baf6",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn orange() -> ColorScaleSet {
|
|
||||||
StaticColorScaleSet {
|
|
||||||
light: [
|
|
||||||
"#fefcfbff",
|
|
||||||
"#fff7edff",
|
|
||||||
"#ffefd6ff",
|
|
||||||
"#ffdfb5ff",
|
|
||||||
"#ffd19aff",
|
|
||||||
"#ffc182ff",
|
|
||||||
"#f5ae73ff",
|
|
||||||
"#ec9455ff",
|
|
||||||
"#f76b15ff",
|
|
||||||
"#ef5f00ff",
|
|
||||||
"#cc4e00ff",
|
|
||||||
"#582d1dff",
|
|
||||||
],
|
|
||||||
light_alpha: [
|
|
||||||
"#c0400004",
|
|
||||||
"#ff8e0012",
|
|
||||||
"#ff9c0029",
|
|
||||||
"#ff91014a",
|
|
||||||
"#ff8b0065",
|
|
||||||
"#ff81007d",
|
|
||||||
"#ed6c008c",
|
|
||||||
"#e35f00aa",
|
|
||||||
"#f65e00ea",
|
|
||||||
"#ef5f00ff",
|
|
||||||
"#cc4e00ff",
|
|
||||||
"#431200e2",
|
|
||||||
],
|
|
||||||
dark: [
|
|
||||||
"#17120eff",
|
|
||||||
"#1e160fff",
|
|
||||||
"#331e0bff",
|
|
||||||
"#462100ff",
|
|
||||||
"#562800ff",
|
|
||||||
"#66350cff",
|
|
||||||
"#7e451dff",
|
|
||||||
"#a35829ff",
|
|
||||||
"#f76b15ff",
|
|
||||||
"#ff801fff",
|
|
||||||
"#ffa057ff",
|
|
||||||
"#ffe0c2ff",
|
|
||||||
],
|
|
||||||
dark_alpha: [
|
|
||||||
"#ec360007",
|
|
||||||
"#fe6d000e",
|
|
||||||
"#fb6a0025",
|
|
||||||
"#ff590039",
|
|
||||||
"#ff61004a",
|
|
||||||
"#fd75045c",
|
|
||||||
"#ff832c75",
|
|
||||||
"#fe84389d",
|
|
||||||
"#fe6d15f7",
|
|
||||||
"#ff801fff",
|
|
||||||
"#ffa057ff",
|
|
||||||
"#ffe0c2ff",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn tomato() -> ColorScaleSet {
|
|
||||||
StaticColorScaleSet {
|
|
||||||
light: [
|
|
||||||
"#fffcfcff",
|
|
||||||
"#fff8f7ff",
|
|
||||||
"#feebe7ff",
|
|
||||||
"#ffdcd3ff",
|
|
||||||
"#ffcdc2ff",
|
|
||||||
"#fdbdafff",
|
|
||||||
"#f5a898ff",
|
|
||||||
"#ec8e7bff",
|
|
||||||
"#e54d2eff",
|
|
||||||
"#dd4425ff",
|
|
||||||
"#d13415ff",
|
|
||||||
"#5c271fff",
|
|
||||||
],
|
|
||||||
light_alpha: [
|
|
||||||
"#ff000003",
|
|
||||||
"#ff200008",
|
|
||||||
"#f52b0018",
|
|
||||||
"#ff35002c",
|
|
||||||
"#ff2e003d",
|
|
||||||
"#f92d0050",
|
|
||||||
"#e7280067",
|
|
||||||
"#db250084",
|
|
||||||
"#df2600d1",
|
|
||||||
"#d72400da",
|
|
||||||
"#cd2200ea",
|
|
||||||
"#460900e0",
|
|
||||||
],
|
|
||||||
dark: [
|
|
||||||
"#181111ff",
|
|
||||||
"#1f1513ff",
|
|
||||||
"#391714ff",
|
|
||||||
"#4e1511ff",
|
|
||||||
"#5e1c16ff",
|
|
||||||
"#6e2920ff",
|
|
||||||
"#853a2dff",
|
|
||||||
"#ac4d39ff",
|
|
||||||
"#e54d2eff",
|
|
||||||
"#ec6142ff",
|
|
||||||
"#ff977dff",
|
|
||||||
"#fbd3cbff",
|
|
||||||
],
|
|
||||||
dark_alpha: [
|
|
||||||
"#f1121208",
|
|
||||||
"#ff55330f",
|
|
||||||
"#ff35232b",
|
|
||||||
"#fd201142",
|
|
||||||
"#fe332153",
|
|
||||||
"#ff4f3864",
|
|
||||||
"#fd644a7d",
|
|
||||||
"#fe6d4ea7",
|
|
||||||
"#fe5431e4",
|
|
||||||
"#ff6847eb",
|
|
||||||
"#ff977dff",
|
|
||||||
"#ffd6cefb",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
+22
-192
@@ -1,29 +1,21 @@
|
|||||||
use std::ops::{Deref, DerefMut};
|
use gpui::{App, Pixels, px};
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use gpui::{App, Global, Pixels, SharedString, Window, px};
|
pub mod builtins;
|
||||||
|
|
||||||
pub mod model;
|
|
||||||
|
|
||||||
mod colors;
|
|
||||||
mod geometry;
|
mod geometry;
|
||||||
|
pub mod model;
|
||||||
mod notification;
|
mod notification;
|
||||||
mod platform_kind;
|
mod platform_kind;
|
||||||
mod registry;
|
mod resolved;
|
||||||
mod scale;
|
mod runtime;
|
||||||
mod scrollbar_mode;
|
mod scrollbar_mode;
|
||||||
mod theme;
|
|
||||||
|
|
||||||
mod builtins;
|
|
||||||
|
|
||||||
pub use colors::*;
|
|
||||||
pub use geometry::*;
|
pub use geometry::*;
|
||||||
|
pub use model::*;
|
||||||
pub use notification::*;
|
pub use notification::*;
|
||||||
pub use platform_kind::PlatformKind;
|
pub use platform_kind::PlatformKind;
|
||||||
pub use registry::*;
|
pub use resolved::{ResolvedAccentRoles, ResolvedColors};
|
||||||
pub use scale::*;
|
pub use runtime::Theme;
|
||||||
pub use scrollbar_mode::*;
|
pub use scrollbar_mode::*;
|
||||||
pub use theme::*;
|
|
||||||
|
|
||||||
/// Defines window border radius for platforms that use client side decorations.
|
/// Defines window border radius for platforms that use client side decorations.
|
||||||
pub const CLIENT_SIDE_DECORATION_ROUNDING: Pixels = px(10.0);
|
pub const CLIENT_SIDE_DECORATION_ROUNDING: Pixels = px(10.0);
|
||||||
@@ -44,8 +36,6 @@ pub const TABBAR_HEIGHT: Pixels = px(36.0);
|
|||||||
pub const SIDEBAR_WIDTH: Pixels = px(240.);
|
pub const SIDEBAR_WIDTH: Pixels = px(240.);
|
||||||
|
|
||||||
pub fn init(cx: &mut App) {
|
pub fn init(cx: &mut App) {
|
||||||
registry::init(cx);
|
|
||||||
|
|
||||||
Theme::sync_system_appearance(None, cx);
|
Theme::sync_system_appearance(None, cx);
|
||||||
Theme::sync_scrollbar_appearance(cx);
|
Theme::sync_scrollbar_appearance(cx);
|
||||||
}
|
}
|
||||||
@@ -68,7 +58,7 @@ pub fn sync_base(cx: &mut App) {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let appearance = if theme.mode.is_dark() {
|
let appearance = if theme.appearance.is_dark() {
|
||||||
gpui_base::ThemeAppearance::Dark
|
gpui_base::ThemeAppearance::Dark
|
||||||
} else {
|
} else {
|
||||||
gpui_base::ThemeAppearance::Light
|
gpui_base::ThemeAppearance::Light
|
||||||
@@ -79,6 +69,7 @@ pub fn sync_base(cx: &mut App) {
|
|||||||
ScrollbarMode::Always => gpui_base::ScrollbarMode::Always,
|
ScrollbarMode::Always => gpui_base::ScrollbarMode::Always,
|
||||||
};
|
};
|
||||||
let colors = theme.colors;
|
let colors = theme.colors;
|
||||||
|
let accent = theme.accent;
|
||||||
let font_family = theme.font_family.clone();
|
let font_family = theme.font_family.clone();
|
||||||
|
|
||||||
let base = gpui_base::Theme::global_mut(cx);
|
let base = gpui_base::Theme::global_mut(cx);
|
||||||
@@ -89,22 +80,22 @@ pub fn sync_base(cx: &mut App) {
|
|||||||
let tokens = &mut base.tokens.colors;
|
let tokens = &mut base.tokens.colors;
|
||||||
tokens.background = colors.background;
|
tokens.background = colors.background;
|
||||||
tokens.foreground = colors.text;
|
tokens.foreground = colors.text;
|
||||||
tokens.surface = colors.surface_background;
|
tokens.surface = colors.shell;
|
||||||
tokens.surface_foreground = colors.text;
|
tokens.surface_foreground = colors.text;
|
||||||
tokens.primary = colors.element_background;
|
tokens.primary = accent.primary;
|
||||||
tokens.primary_foreground = colors.element_foreground;
|
tokens.primary_foreground = accent.on;
|
||||||
tokens.secondary = colors.secondary_background;
|
tokens.secondary = colors.raised;
|
||||||
tokens.secondary_foreground = colors.secondary_foreground;
|
tokens.secondary_foreground = colors.text;
|
||||||
tokens.muted = colors.ghost_element_background_alt;
|
tokens.muted = colors.hover;
|
||||||
tokens.muted_foreground = colors.text_muted;
|
tokens.muted_foreground = colors.text_muted;
|
||||||
tokens.accent = colors.ghost_element_hover;
|
tokens.accent = accent.wash;
|
||||||
tokens.accent_foreground = colors.text;
|
tokens.accent_foreground = colors.text;
|
||||||
tokens.destructive = colors.danger_background;
|
tokens.destructive = colors.danger;
|
||||||
tokens.destructive_foreground = colors.danger_foreground;
|
tokens.destructive_foreground = accent.on_danger;
|
||||||
tokens.border = colors.border;
|
tokens.border = colors.border;
|
||||||
tokens.input = colors.border;
|
tokens.input = colors.input;
|
||||||
tokens.ring = colors.ring;
|
tokens.ring = accent.primary;
|
||||||
tokens.selection = colors.selection;
|
tokens.selection = accent.selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ActiveTheme {
|
pub trait ActiveTheme {
|
||||||
@@ -117,164 +108,3 @@ impl ActiveTheme for App {
|
|||||||
Theme::global(self)
|
Theme::global(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Theme {
|
|
||||||
/// Theme colors
|
|
||||||
pub colors: ThemeColors,
|
|
||||||
|
|
||||||
/// Theme family
|
|
||||||
pub theme: Rc<ThemeFamily>,
|
|
||||||
|
|
||||||
/// The appearance of the theme (light or dark).
|
|
||||||
pub mode: ThemeMode,
|
|
||||||
|
|
||||||
/// The font family for the application.
|
|
||||||
pub font_family: SharedString,
|
|
||||||
|
|
||||||
/// The root font size for the application, default is 15px.
|
|
||||||
pub font_size: Pixels,
|
|
||||||
|
|
||||||
/// Radius for the general elements.
|
|
||||||
pub radius: Pixels,
|
|
||||||
|
|
||||||
/// Radius for the large elements, e.g.: dialog, notification.
|
|
||||||
pub radius_lg: Pixels,
|
|
||||||
|
|
||||||
/// Enable shadow for the general elements. default is true
|
|
||||||
pub shadow: bool,
|
|
||||||
|
|
||||||
/// Show the scrollbar mode, default: scrolling
|
|
||||||
pub scrollbar_mode: ScrollbarMode,
|
|
||||||
|
|
||||||
/// Notification settings
|
|
||||||
pub notification: NotificationSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Theme {
|
|
||||||
type Target = ThemeColors;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.colors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Theme {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.colors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Global for Theme {}
|
|
||||||
|
|
||||||
impl Theme {
|
|
||||||
/// Returns the global theme reference
|
|
||||||
pub fn global(cx: &App) -> &Theme {
|
|
||||||
cx.global::<Theme>()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the global theme mutable reference
|
|
||||||
pub(crate) fn global_mut(cx: &mut App) -> &mut Theme {
|
|
||||||
cx.global_mut::<Theme>()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the theme is dark.
|
|
||||||
pub fn is_dark(&self) -> bool {
|
|
||||||
self.mode.is_dark()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sync the theme with the system appearance
|
|
||||||
pub fn sync_system_appearance(window: Option<&mut Window>, cx: &mut App) {
|
|
||||||
let appearance = window
|
|
||||||
.as_ref()
|
|
||||||
.map(|window| window.appearance())
|
|
||||||
.unwrap_or_else(|| cx.window_appearance());
|
|
||||||
|
|
||||||
Self::change(appearance, window, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sync the Scrollbar showing behavior with the system
|
|
||||||
pub fn sync_scrollbar_appearance(cx: &mut App) {
|
|
||||||
Theme::global_mut(cx).scrollbar_mode = if cx.should_auto_hide_scrollbars() {
|
|
||||||
ScrollbarMode::Scrolling
|
|
||||||
} else {
|
|
||||||
ScrollbarMode::Hover
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply a new theme to the application.
|
|
||||||
pub fn apply_theme(new_theme: Rc<ThemeFamily>, window: Option<&mut Window>, cx: &mut App) {
|
|
||||||
let theme = cx.global_mut::<Theme>();
|
|
||||||
let mode = theme.mode;
|
|
||||||
// Update the theme
|
|
||||||
theme.theme = new_theme;
|
|
||||||
// Emit a theme change event
|
|
||||||
Self::change(mode, window, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change the app's appearance
|
|
||||||
pub fn change<M>(mode: M, window: Option<&mut Window>, cx: &mut App)
|
|
||||||
where
|
|
||||||
M: Into<ThemeMode>,
|
|
||||||
{
|
|
||||||
if !cx.has_global::<Theme>() {
|
|
||||||
let default_theme = ThemeFamily::default();
|
|
||||||
let theme = Theme::from(default_theme);
|
|
||||||
|
|
||||||
cx.set_global(theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mode = mode.into();
|
|
||||||
let theme = cx.global_mut::<Theme>();
|
|
||||||
|
|
||||||
// Set the theme mode
|
|
||||||
theme.mode = mode;
|
|
||||||
|
|
||||||
// Set the theme colors
|
|
||||||
if mode.is_dark() {
|
|
||||||
theme.colors = *theme.theme.dark();
|
|
||||||
} else {
|
|
||||||
theme.colors = *theme.theme.light();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh the window if available
|
|
||||||
if let Some(window) = window {
|
|
||||||
window.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep the base-layer projection in step with the coop palette
|
|
||||||
sync_base(cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ThemeFamily> for Theme {
|
|
||||||
fn from(family: ThemeFamily) -> Self {
|
|
||||||
let platform = PlatformKind::platform();
|
|
||||||
let mode = ThemeMode::default();
|
|
||||||
|
|
||||||
// Define the font family based on the platform.
|
|
||||||
let font_family = match platform {
|
|
||||||
PlatformKind::Linux => "Inter",
|
|
||||||
_ => ".SystemUIFont",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define the theme colors based on the appearance
|
|
||||||
let colors = match mode {
|
|
||||||
ThemeMode::Light => family.light(),
|
|
||||||
ThemeMode::Dark => family.dark(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Theme {
|
|
||||||
font_size: px(15.),
|
|
||||||
font_family: font_family.into(),
|
|
||||||
radius: px(6.),
|
|
||||||
radius_lg: px(10.),
|
|
||||||
shadow: true,
|
|
||||||
scrollbar_mode: ScrollbarMode::default(),
|
|
||||||
notification: NotificationSettings::default(),
|
|
||||||
mode,
|
|
||||||
colors: *colors,
|
|
||||||
theme: Rc::new(family),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
// Ported from zeronsh/zeron crates/theme/src/lib.rs
|
|
||||||
// at revision 916cb1ccb1342c2061963a5f6ecf5c977ba46f2c (MIT).
|
|
||||||
// The custom theme library and VS Code import modules are out of scope for now
|
|
||||||
// and will be ported in a later phase.
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, HashSet};
|
||||||
@@ -19,10 +15,6 @@ fn custom_families() -> &'static RwLock<Vec<ThemeFamily>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Replace the process-wide custom portion of the runtime registry.
|
/// Replace the process-wide custom portion of the runtime registry.
|
||||||
///
|
|
||||||
/// The UI calls this after loading or mutating the durable theme library. The
|
|
||||||
/// returned registry remains source-neutral: renderers still see only resolved
|
|
||||||
/// families and variants.
|
|
||||||
pub fn replace_custom_families(families: Vec<ThemeFamily>) {
|
pub fn replace_custom_families(families: Vec<ThemeFamily>) {
|
||||||
*custom_families()
|
*custom_families()
|
||||||
.write()
|
.write()
|
||||||
@@ -53,8 +45,6 @@ pub enum SurfaceTreatment {
|
|||||||
Frosted,
|
Frosted,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The device-local policy applied to a theme variant's recommended surface
|
|
||||||
/// treatment. This is deliberately independent from theme and accent choices.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum SurfacePreference {
|
pub enum SurfacePreference {
|
||||||
@@ -474,8 +464,7 @@ pub struct ThemeVariant {
|
|||||||
pub family_id: String,
|
pub family_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub appearance: Appearance,
|
pub appearance: Appearance,
|
||||||
/// The author's recommended treatment. Runtime applies the user's
|
/// The author's recommended treatment.
|
||||||
/// [`SurfacePreference`] before components consume the variant.
|
|
||||||
#[serde(alias = "surfaceTreatment")]
|
#[serde(alias = "surfaceTreatment")]
|
||||||
pub recommended_surface_treatment: SurfaceTreatment,
|
pub recommended_surface_treatment: SurfaceTreatment,
|
||||||
pub colors: ThemeColors,
|
pub colors: ThemeColors,
|
||||||
@@ -617,9 +606,7 @@ impl ThemeRegistry {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
for (index, color) in variant.terminal.ansi.iter().enumerate() {
|
for (index, color) in variant.terminal.ansi.iter().enumerate() {
|
||||||
// Slots 0 and 8 are structural black/dim colors. Every
|
// Slots 0 and 8 are structural black/dim colors.
|
||||||
// chromatic normal and bright slot is expected to remain
|
|
||||||
// distinguishable as terminal text.
|
|
||||||
if index % 8 == 0 {
|
if index % 8 == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -660,8 +647,6 @@ pub enum ValidationSeverity {
|
|||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether a validation issue makes a theme structurally unsafe to install or
|
|
||||||
/// describes a visual quality problem that the compiler/runtime can harden.
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum ValidationCategory {
|
pub enum ValidationCategory {
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use anyhow::{Context as AnyhowContext, Error};
|
|
||||||
use gpui::{App, AppContext, AssetSource, Context, Entity, Global, SharedString};
|
|
||||||
|
|
||||||
use crate::ThemeFamily;
|
|
||||||
|
|
||||||
pub fn init(cx: &mut App) {
|
|
||||||
ThemeRegistry::set_global(cx.new(ThemeRegistry::new), cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GlobalThemeRegistry(Entity<ThemeRegistry>);
|
|
||||||
|
|
||||||
impl Global for GlobalThemeRegistry {}
|
|
||||||
|
|
||||||
pub struct ThemeRegistry {
|
|
||||||
/// Map of theme names to theme families
|
|
||||||
themes: HashMap<SharedString, Rc<ThemeFamily>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ThemeRegistry {
|
|
||||||
/// Retrieve the global theme registry state
|
|
||||||
pub fn global(cx: &App) -> Entity<Self> {
|
|
||||||
cx.global::<GlobalThemeRegistry>().0.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the global theme registry instance
|
|
||||||
pub(crate) fn set_global(state: Entity<Self>, cx: &mut App) {
|
|
||||||
cx.set_global(GlobalThemeRegistry(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new theme registry instance
|
|
||||||
pub(crate) fn new(cx: &mut Context<Self>) -> Self {
|
|
||||||
let mut themes = HashMap::new();
|
|
||||||
let asset = cx.asset_source();
|
|
||||||
|
|
||||||
if let Ok(paths) = asset.list("themes") {
|
|
||||||
for path in paths.into_iter() {
|
|
||||||
match Self::load(&path, asset) {
|
|
||||||
Ok(theme) => {
|
|
||||||
themes.insert(path, Rc::new(theme));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("Failed to load theme: {path}. Error: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self { themes }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load a theme from the asset source.
|
|
||||||
fn load(path: &str, asset: &Arc<dyn AssetSource>) -> Result<ThemeFamily, Error> {
|
|
||||||
// Load the theme file from the assets
|
|
||||||
let content = asset.load(path)?.context("Theme not found")?;
|
|
||||||
|
|
||||||
// Parse the JSON content into a Theme Family struct
|
|
||||||
let theme: ThemeFamily = serde_json::from_slice(&content)?;
|
|
||||||
|
|
||||||
Ok(theme)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the map of themes.
|
|
||||||
pub fn themes(&self) -> &HashMap<SharedString, Rc<ThemeFamily>> {
|
|
||||||
&self.themes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
#![allow(dead_code)] // transitional: scaffold fields die in Phase 5, unconsumed roles live on
|
||||||
|
|
||||||
|
use gpui::Hsla;
|
||||||
|
|
||||||
|
use crate::model::{AccentRoles, Color, ThemeColors, ThemeVariant};
|
||||||
|
|
||||||
|
fn hsla(color: Color) -> Hsla {
|
||||||
|
let packed = ((color.r as u32) << 24)
|
||||||
|
| ((color.g as u32) << 16)
|
||||||
|
| ((color.b as u32) << 8)
|
||||||
|
| color.a as u32;
|
||||||
|
Hsla::from(gpui::rgba(packed))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ResolvedAccentRoles {
|
||||||
|
pub primary: Hsla,
|
||||||
|
pub strong: Hsla,
|
||||||
|
pub wash: Hsla,
|
||||||
|
pub on: Hsla,
|
||||||
|
pub selection: Hsla,
|
||||||
|
pub caret: Hsla,
|
||||||
|
pub activity: Hsla,
|
||||||
|
pub glyph: [Hsla; 3],
|
||||||
|
pub hover: Hsla,
|
||||||
|
pub active: Hsla,
|
||||||
|
pub selected: Hsla,
|
||||||
|
/// Accent text guaranteed readable on `wash`.
|
||||||
|
pub text_on_wash: Hsla,
|
||||||
|
/// Text guaranteed readable on the flat `danger` color.
|
||||||
|
pub on_danger: Hsla,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResolvedAccentRoles {
|
||||||
|
pub fn resolve(accent: &AccentRoles, colors: &ThemeColors) -> Self {
|
||||||
|
let background = colors.background;
|
||||||
|
let wash_background = accent.wash.blend_over(background);
|
||||||
|
Self {
|
||||||
|
primary: hsla(accent.primary),
|
||||||
|
strong: hsla(accent.strong),
|
||||||
|
wash: hsla(accent.wash),
|
||||||
|
on: hsla(accent.on),
|
||||||
|
selection: hsla(accent.selection),
|
||||||
|
caret: hsla(accent.caret),
|
||||||
|
activity: hsla(accent.activity),
|
||||||
|
glyph: [
|
||||||
|
hsla(accent.glyph[0]),
|
||||||
|
hsla(accent.glyph[1]),
|
||||||
|
hsla(accent.glyph[2]),
|
||||||
|
],
|
||||||
|
hover: hsla(accent.primary.mix(background, 0.12)),
|
||||||
|
active: hsla(accent.primary.mix(background, 0.2)),
|
||||||
|
selected: hsla(accent.primary.mix(background, 0.28)),
|
||||||
|
text_on_wash: hsla(accent.primary.ensure_contrast(wash_background, 4.5)),
|
||||||
|
on_danger: hsla(colors.danger.best_on_color()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `Hsla` snapshot components render from, rebuilt whenever the active
|
||||||
|
/// variant, accent, or appearance changes.
|
||||||
|
///
|
||||||
|
/// The fields after `diff_hunk` are a transitional scaffold (D6): they keep the
|
||||||
|
/// removed role names alive — computed from zeron roles — so the ~51 downstream
|
||||||
|
/// files keep compiling until the Phase 3 rewrite retires them, one by one.
|
||||||
|
/// Everything below `drop_target_background` is deleted in Phase 5.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ResolvedColors {
|
||||||
|
pub background: Hsla,
|
||||||
|
pub shell: Hsla,
|
||||||
|
pub raised: Hsla,
|
||||||
|
pub card: Hsla,
|
||||||
|
pub dialog: Hsla,
|
||||||
|
pub overlay: Hsla,
|
||||||
|
pub hover: Hsla,
|
||||||
|
pub active: Hsla,
|
||||||
|
pub border: Hsla,
|
||||||
|
pub border_strong: Hsla,
|
||||||
|
pub text: Hsla,
|
||||||
|
pub text_muted: Hsla,
|
||||||
|
pub text_faint: Hsla,
|
||||||
|
pub solid: Hsla,
|
||||||
|
pub on_solid: Hsla,
|
||||||
|
pub danger: Hsla,
|
||||||
|
pub danger_muted: Hsla,
|
||||||
|
pub warning: Hsla,
|
||||||
|
pub warning_muted: Hsla,
|
||||||
|
pub success: Hsla,
|
||||||
|
pub success_muted: Hsla,
|
||||||
|
pub input: Hsla,
|
||||||
|
pub cursor: Hsla,
|
||||||
|
pub diff_add: Hsla,
|
||||||
|
pub diff_delete: Hsla,
|
||||||
|
pub diff_hunk: Hsla,
|
||||||
|
// Transitional scaffold — delete in Phase 5.
|
||||||
|
pub surface_background: Hsla,
|
||||||
|
pub elevated_surface_background: Hsla,
|
||||||
|
pub panel_background: Hsla,
|
||||||
|
pub border_variant: Hsla,
|
||||||
|
pub border_focused: Hsla,
|
||||||
|
pub border_selected: Hsla,
|
||||||
|
pub border_transparent: Hsla,
|
||||||
|
pub ring: Hsla,
|
||||||
|
pub selection: Hsla,
|
||||||
|
pub text_placeholder: Hsla,
|
||||||
|
pub text_accent: Hsla,
|
||||||
|
pub text_danger: Hsla,
|
||||||
|
pub text_warning: Hsla,
|
||||||
|
pub icon: Hsla,
|
||||||
|
pub icon_muted: Hsla,
|
||||||
|
pub icon_accent: Hsla,
|
||||||
|
pub element_background: Hsla,
|
||||||
|
pub element_foreground: Hsla,
|
||||||
|
pub element_hover: Hsla,
|
||||||
|
pub element_active: Hsla,
|
||||||
|
pub element_selected: Hsla,
|
||||||
|
pub element_disabled: Hsla,
|
||||||
|
pub secondary_background: Hsla,
|
||||||
|
pub secondary_foreground: Hsla,
|
||||||
|
pub secondary_hover: Hsla,
|
||||||
|
pub secondary_active: Hsla,
|
||||||
|
pub secondary_selected: Hsla,
|
||||||
|
pub secondary_disabled: Hsla,
|
||||||
|
pub danger_foreground: Hsla,
|
||||||
|
pub danger_background: Hsla,
|
||||||
|
pub danger_hover: Hsla,
|
||||||
|
pub danger_active: Hsla,
|
||||||
|
pub danger_selected: Hsla,
|
||||||
|
pub danger_disabled: Hsla,
|
||||||
|
pub warning_foreground: Hsla,
|
||||||
|
pub warning_background: Hsla,
|
||||||
|
pub warning_hover: Hsla,
|
||||||
|
pub warning_active: Hsla,
|
||||||
|
pub warning_selected: Hsla,
|
||||||
|
pub warning_disabled: Hsla,
|
||||||
|
pub ghost_element_background: Hsla,
|
||||||
|
pub ghost_element_background_alt: Hsla,
|
||||||
|
pub ghost_element_hover: Hsla,
|
||||||
|
pub ghost_element_active: Hsla,
|
||||||
|
pub ghost_element_selected: Hsla,
|
||||||
|
pub ghost_element_disabled: Hsla,
|
||||||
|
pub tab_background: Hsla,
|
||||||
|
pub tab_foreground: Hsla,
|
||||||
|
pub tab_hover_background: Hsla,
|
||||||
|
pub tab_active_background: Hsla,
|
||||||
|
pub tab_active_foreground: Hsla,
|
||||||
|
pub scrollbar_thumb_background: Hsla,
|
||||||
|
pub scrollbar_thumb_hover_background: Hsla,
|
||||||
|
pub scrollbar_thumb_border: Hsla,
|
||||||
|
pub scrollbar_track_background: Hsla,
|
||||||
|
pub scrollbar_track_border: Hsla,
|
||||||
|
pub drop_target_background: Hsla,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResolvedColors {
|
||||||
|
pub fn resolve(variant: &ThemeVariant, accent: &AccentRoles) -> Self {
|
||||||
|
let colors = variant.colors.clone();
|
||||||
|
let ThemeColors {
|
||||||
|
background,
|
||||||
|
shell,
|
||||||
|
raised,
|
||||||
|
card,
|
||||||
|
dialog,
|
||||||
|
overlay,
|
||||||
|
hover,
|
||||||
|
active,
|
||||||
|
border,
|
||||||
|
border_strong,
|
||||||
|
text,
|
||||||
|
text_muted,
|
||||||
|
text_faint,
|
||||||
|
solid,
|
||||||
|
on_solid,
|
||||||
|
danger,
|
||||||
|
danger_muted,
|
||||||
|
warning,
|
||||||
|
warning_muted,
|
||||||
|
success,
|
||||||
|
success_muted,
|
||||||
|
input,
|
||||||
|
cursor,
|
||||||
|
diff_add,
|
||||||
|
diff_delete,
|
||||||
|
diff_hunk,
|
||||||
|
} = colors;
|
||||||
|
|
||||||
|
let transparent = Color::rgba(0, 0, 0, 0);
|
||||||
|
let danger_tint = danger_muted;
|
||||||
|
let warning_tint = warning_muted;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
background: hsla(background),
|
||||||
|
shell: hsla(shell),
|
||||||
|
raised: hsla(raised),
|
||||||
|
card: hsla(card),
|
||||||
|
dialog: hsla(dialog),
|
||||||
|
overlay: hsla(overlay),
|
||||||
|
hover: hsla(hover),
|
||||||
|
active: hsla(active),
|
||||||
|
border: hsla(border),
|
||||||
|
border_strong: hsla(border_strong),
|
||||||
|
text: hsla(text),
|
||||||
|
text_muted: hsla(text_muted),
|
||||||
|
text_faint: hsla(text_faint),
|
||||||
|
solid: hsla(solid),
|
||||||
|
on_solid: hsla(on_solid),
|
||||||
|
danger: hsla(danger),
|
||||||
|
danger_muted: hsla(danger_muted),
|
||||||
|
warning: hsla(warning),
|
||||||
|
warning_muted: hsla(warning_muted),
|
||||||
|
success: hsla(success),
|
||||||
|
success_muted: hsla(success_muted),
|
||||||
|
input: hsla(input),
|
||||||
|
cursor: hsla(cursor),
|
||||||
|
diff_add: hsla(diff_add),
|
||||||
|
diff_delete: hsla(diff_delete),
|
||||||
|
diff_hunk: hsla(diff_hunk),
|
||||||
|
surface_background: hsla(shell),
|
||||||
|
elevated_surface_background: hsla(raised),
|
||||||
|
panel_background: hsla(shell),
|
||||||
|
border_variant: hsla(border),
|
||||||
|
border_focused: hsla(accent.primary),
|
||||||
|
border_selected: hsla(accent.primary),
|
||||||
|
border_transparent: hsla(transparent),
|
||||||
|
ring: hsla(accent.primary),
|
||||||
|
selection: hsla(accent.selection),
|
||||||
|
text_placeholder: hsla(text_faint),
|
||||||
|
text_accent: hsla(accent.primary),
|
||||||
|
text_danger: hsla(danger),
|
||||||
|
text_warning: hsla(warning),
|
||||||
|
icon: hsla(text_muted),
|
||||||
|
icon_muted: hsla(text_faint),
|
||||||
|
icon_accent: hsla(accent.primary),
|
||||||
|
element_background: hsla(accent.primary),
|
||||||
|
element_foreground: hsla(accent.on),
|
||||||
|
element_hover: hsla(accent.primary.mix(background, 0.12)),
|
||||||
|
element_active: hsla(accent.primary.mix(background, 0.2)),
|
||||||
|
element_selected: hsla(accent.primary.mix(background, 0.28)),
|
||||||
|
element_disabled: hsla(accent.primary.with_alpha(0.3)),
|
||||||
|
secondary_background: hsla(accent.wash),
|
||||||
|
secondary_foreground: hsla(
|
||||||
|
accent
|
||||||
|
.primary
|
||||||
|
.ensure_contrast(accent.wash.blend_over(background), 4.5),
|
||||||
|
),
|
||||||
|
secondary_hover: hsla(accent.selection),
|
||||||
|
secondary_active: hsla(accent.selection),
|
||||||
|
secondary_selected: hsla(accent.selection),
|
||||||
|
secondary_disabled: hsla(accent.wash.with_alpha(0.5)),
|
||||||
|
danger_foreground: hsla(danger.ensure_contrast(danger_tint, 4.5)),
|
||||||
|
danger_background: hsla(danger_tint),
|
||||||
|
danger_hover: hsla(danger.with_alpha(0.15)),
|
||||||
|
danger_active: hsla(danger.with_alpha(0.25)),
|
||||||
|
danger_selected: hsla(danger.with_alpha(0.25)),
|
||||||
|
danger_disabled: hsla(danger.with_alpha(0.12)),
|
||||||
|
warning_foreground: hsla(warning.ensure_contrast(warning_tint, 4.5)),
|
||||||
|
warning_background: hsla(warning_tint),
|
||||||
|
warning_hover: hsla(warning.with_alpha(0.15)),
|
||||||
|
warning_active: hsla(warning.with_alpha(0.25)),
|
||||||
|
warning_selected: hsla(warning.with_alpha(0.25)),
|
||||||
|
warning_disabled: hsla(warning.with_alpha(0.12)),
|
||||||
|
ghost_element_background: hsla(transparent),
|
||||||
|
ghost_element_background_alt: hsla(raised),
|
||||||
|
ghost_element_hover: hsla(hover),
|
||||||
|
ghost_element_active: hsla(active),
|
||||||
|
ghost_element_selected: hsla(active),
|
||||||
|
ghost_element_disabled: hsla(border.with_alpha(0.4)),
|
||||||
|
tab_background: hsla(shell),
|
||||||
|
tab_foreground: hsla(text_muted),
|
||||||
|
tab_hover_background: hsla(hover),
|
||||||
|
tab_active_background: hsla(background),
|
||||||
|
tab_active_foreground: hsla(text),
|
||||||
|
scrollbar_thumb_background: hsla(border.with_alpha(0.7)),
|
||||||
|
scrollbar_thumb_hover_background: hsla(border_strong),
|
||||||
|
scrollbar_thumb_border: hsla(transparent),
|
||||||
|
scrollbar_track_background: hsla(transparent),
|
||||||
|
scrollbar_track_border: hsla(border),
|
||||||
|
drop_target_background: hsla(accent.selection),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use gpui::{App, Global, Pixels, SharedString, Window, px};
|
||||||
|
|
||||||
|
use crate::model::{
|
||||||
|
AccentSelection, Appearance, SurfacePreference, SurfaceTreatment, ThemeRegistry,
|
||||||
|
ThemeSelection, ThemeVariant,
|
||||||
|
};
|
||||||
|
use crate::resolved::{ResolvedAccentRoles, ResolvedColors};
|
||||||
|
use crate::{NotificationSettings, PlatformKind, ScrollbarMode, sync_base};
|
||||||
|
|
||||||
|
impl From<gpui::WindowAppearance> for Appearance {
|
||||||
|
fn from(appearance: gpui::WindowAppearance) -> Self {
|
||||||
|
match appearance {
|
||||||
|
gpui::WindowAppearance::Dark | gpui::WindowAppearance::VibrantDark => Self::Dark,
|
||||||
|
gpui::WindowAppearance::Light | gpui::WindowAppearance::VibrantLight => Self::Light,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Theme {
|
||||||
|
/// The active theme variant.
|
||||||
|
pub variant: Arc<ThemeVariant>,
|
||||||
|
|
||||||
|
/// The active appearance (light or dark).
|
||||||
|
pub appearance: Appearance,
|
||||||
|
|
||||||
|
/// The per-appearance variant selection.
|
||||||
|
pub theme_selection: ThemeSelection,
|
||||||
|
|
||||||
|
/// The accent override applied on top of the variant.
|
||||||
|
pub accent_selection: AccentSelection,
|
||||||
|
|
||||||
|
/// The user's surface treatment preference.
|
||||||
|
pub surface_preference: SurfacePreference,
|
||||||
|
|
||||||
|
/// The surface treatment in effect for this session.
|
||||||
|
pub surface: SurfaceTreatment,
|
||||||
|
|
||||||
|
/// The resolved accent roles.
|
||||||
|
pub accent: ResolvedAccentRoles,
|
||||||
|
|
||||||
|
/// The resolved color snapshot.
|
||||||
|
pub colors: ResolvedColors,
|
||||||
|
|
||||||
|
/// The font family for the application.
|
||||||
|
pub font_family: SharedString,
|
||||||
|
|
||||||
|
/// The root font size for the application, default is 15px.
|
||||||
|
pub font_size: Pixels,
|
||||||
|
|
||||||
|
/// Radius for the general elements.
|
||||||
|
pub radius: Pixels,
|
||||||
|
|
||||||
|
/// Radius for the large elements, e.g.: dialog, notification.
|
||||||
|
pub radius_lg: Pixels,
|
||||||
|
|
||||||
|
/// Enable shadow for the general elements. default is true
|
||||||
|
pub shadow: bool,
|
||||||
|
|
||||||
|
/// Show the scrollbar mode, default: scrolling
|
||||||
|
pub scrollbar_mode: ScrollbarMode,
|
||||||
|
|
||||||
|
/// Notification settings
|
||||||
|
pub notification: NotificationSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Theme {
|
||||||
|
type Target = ResolvedColors;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.colors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Theme {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.colors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Global for Theme {}
|
||||||
|
|
||||||
|
impl Theme {
|
||||||
|
/// Returns the global theme reference
|
||||||
|
pub fn global(cx: &App) -> &Theme {
|
||||||
|
cx.global::<Theme>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the global theme mutable reference
|
||||||
|
pub(crate) fn global_mut(cx: &mut App) -> &mut Theme {
|
||||||
|
cx.global_mut::<Theme>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the theme is dark.
|
||||||
|
pub fn is_dark(&self) -> bool {
|
||||||
|
self.appearance.is_dark()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
let platform = PlatformKind::platform();
|
||||||
|
let appearance = Appearance::default();
|
||||||
|
|
||||||
|
// Define the font family based on the platform.
|
||||||
|
let font_family = match platform {
|
||||||
|
PlatformKind::Linux => "Inter",
|
||||||
|
_ => ".SystemUIFont",
|
||||||
|
};
|
||||||
|
|
||||||
|
let selection = ThemeSelection::default();
|
||||||
|
let registry = ThemeRegistry::active();
|
||||||
|
let variant = registry.resolve(&selection, appearance).clone();
|
||||||
|
let accent = variant.accent_for(AccentSelection::default());
|
||||||
|
let surface = SurfacePreference::default().resolve(variant.recommended_surface_treatment);
|
||||||
|
let resolved_accent = ResolvedAccentRoles::resolve(&accent, &variant.colors);
|
||||||
|
let colors = ResolvedColors::resolve(&variant, &accent);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
variant: Arc::new(variant),
|
||||||
|
appearance,
|
||||||
|
theme_selection: selection,
|
||||||
|
accent_selection: AccentSelection::default(),
|
||||||
|
surface_preference: SurfacePreference::default(),
|
||||||
|
surface,
|
||||||
|
accent: resolved_accent,
|
||||||
|
colors,
|
||||||
|
font_family: font_family.into(),
|
||||||
|
font_size: px(15.),
|
||||||
|
radius: px(6.),
|
||||||
|
radius_lg: px(10.),
|
||||||
|
shadow: true,
|
||||||
|
scrollbar_mode: ScrollbarMode::default(),
|
||||||
|
notification: NotificationSettings::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sync the theme with the system appearance
|
||||||
|
pub fn sync_system_appearance(window: Option<&mut Window>, cx: &mut App) {
|
||||||
|
let appearance = window
|
||||||
|
.as_ref()
|
||||||
|
.map(|window| window.appearance())
|
||||||
|
.unwrap_or_else(|| cx.window_appearance());
|
||||||
|
|
||||||
|
Self::change(appearance, window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sync the Scrollbar showing behavior with the system
|
||||||
|
pub fn sync_scrollbar_appearance(cx: &mut App) {
|
||||||
|
Theme::global_mut(cx).scrollbar_mode = if cx.should_auto_hide_scrollbars() {
|
||||||
|
ScrollbarMode::Scrolling
|
||||||
|
} else {
|
||||||
|
ScrollbarMode::Hover
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply a theme variant by id for the current appearance.
|
||||||
|
pub fn apply_variant(id: &str, window: Option<&mut Window>, cx: &mut App) {
|
||||||
|
let appearance = Theme::global(cx).appearance;
|
||||||
|
Theme::global_mut(cx)
|
||||||
|
.theme_selection
|
||||||
|
.set_variant(appearance, id);
|
||||||
|
Self::change(appearance, window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace the per-appearance variant selection.
|
||||||
|
pub fn set_selection(selection: ThemeSelection, cx: &mut App) {
|
||||||
|
Theme::global_mut(cx).theme_selection = selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace the accent override and re-resolve the theme.
|
||||||
|
pub fn set_accent_selection(selection: AccentSelection, cx: &mut App) {
|
||||||
|
Theme::global_mut(cx).accent_selection = selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace the surface treatment preference.
|
||||||
|
pub fn set_surface_preference(preference: SurfacePreference, cx: &mut App) {
|
||||||
|
Theme::global_mut(cx).surface_preference = preference;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change the app's appearance, resolving the selected variant (with its
|
||||||
|
/// accent override) into a fresh color snapshot.
|
||||||
|
pub fn change<M>(appearance: M, window: Option<&mut Window>, cx: &mut App)
|
||||||
|
where
|
||||||
|
M: Into<Appearance>,
|
||||||
|
{
|
||||||
|
if !cx.has_global::<Theme>() {
|
||||||
|
cx.set_global(Theme::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let appearance = appearance.into();
|
||||||
|
let (selection, accent_selection, surface_preference) = {
|
||||||
|
let theme = cx.global_mut::<Theme>();
|
||||||
|
theme.appearance = appearance;
|
||||||
|
(
|
||||||
|
theme.theme_selection.clone(),
|
||||||
|
theme.accent_selection,
|
||||||
|
theme.surface_preference,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let registry = ThemeRegistry::active();
|
||||||
|
let variant = registry.resolve(&selection, appearance).clone();
|
||||||
|
let accent = variant.accent_for(accent_selection);
|
||||||
|
let surface = surface_preference.resolve(variant.recommended_surface_treatment);
|
||||||
|
let colors = ResolvedColors::resolve(&variant, &accent);
|
||||||
|
|
||||||
|
{
|
||||||
|
let theme = cx.global_mut::<Theme>();
|
||||||
|
theme.variant = Arc::new(variant.clone());
|
||||||
|
theme.accent = ResolvedAccentRoles::resolve(&accent, &variant.colors);
|
||||||
|
theme.surface = surface;
|
||||||
|
theme.colors = colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the window if available
|
||||||
|
if let Some(window) = window {
|
||||||
|
window.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the base-layer projection in step with the coop palette
|
||||||
|
sync_base(cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
use gpui::Hsla;
|
|
||||||
|
|
||||||
/// A collection of colors that are used to style the UI.
|
|
||||||
///
|
|
||||||
/// Each step has a semantic meaning, and is used to style different parts of the UI.
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
|
||||||
pub struct ColorScaleStep(usize);
|
|
||||||
|
|
||||||
impl ColorScaleStep {
|
|
||||||
pub const EIGHT: Self = Self(8);
|
|
||||||
pub const ELEVEN: Self = Self(11);
|
|
||||||
pub const FIVE: Self = Self(5);
|
|
||||||
pub const FOUR: Self = Self(4);
|
|
||||||
pub const NINE: Self = Self(9);
|
|
||||||
pub const ONE: Self = Self(1);
|
|
||||||
pub const SEVEN: Self = Self(7);
|
|
||||||
pub const SIX: Self = Self(6);
|
|
||||||
pub const TEN: Self = Self(10);
|
|
||||||
pub const THREE: Self = Self(3);
|
|
||||||
pub const TWELVE: Self = Self(12);
|
|
||||||
pub const TWO: Self = Self(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A scale of colors for a given [`ColorScaleSet`].
|
|
||||||
///
|
|
||||||
/// Each [`ColorScale`] contains exactly 12 colors. Refer to
|
|
||||||
/// [`ColorScaleStep`] for a reference of what each step is used for.
|
|
||||||
pub struct ColorScale(Vec<Hsla>);
|
|
||||||
|
|
||||||
impl FromIterator<Hsla> for ColorScale {
|
|
||||||
fn from_iter<T: IntoIterator<Item = Hsla>>(iter: T) -> Self {
|
|
||||||
Self(Vec::from_iter(iter))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ColorScale {
|
|
||||||
/// Returns the specified step in the [`ColorScale`].
|
|
||||||
#[inline]
|
|
||||||
pub fn step(&self, step: ColorScaleStep) -> Hsla {
|
|
||||||
// Steps are one-based, so we need convert to the zero-based vec index.
|
|
||||||
self.0[step.0 - 1]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 1` - Used for main application backgrounds.
|
|
||||||
///
|
|
||||||
/// This step provides a neutral base for any overlaying components, ideal for applications' main backdrop or empty spaces such as canvas areas.
|
|
||||||
///
|
|
||||||
#[inline]
|
|
||||||
pub fn step_1(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::ONE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 2` - Used for both main application backgrounds and subtle component backgrounds.
|
|
||||||
///
|
|
||||||
/// Like `Step 1`, this step allows variations in background styles, from striped tables, sidebar backgrounds, to card backgrounds.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_2(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::TWO)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 3` - Used for UI component backgrounds in their normal states.
|
|
||||||
///
|
|
||||||
/// This step maintains accessibility by guaranteeing a contrast ratio of 4.5:1 with steps 11 and 12 for text. It could also suit hover states for transparent components.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_3(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::THREE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 4` - Used for UI component backgrounds in their hover states.
|
|
||||||
///
|
|
||||||
/// Also suited for pressed or selected states of components with a transparent background.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_4(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::FOUR)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 5` - Used for UI component backgrounds in their pressed or selected states.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_5(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::FIVE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 6` - Used for subtle borders on non-interactive components.
|
|
||||||
///
|
|
||||||
/// Its usage spans from sidebars' borders, headers' dividers, cards' outlines, to alerts' edges and separators.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_6(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::SIX)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 7` - Used for subtle borders on interactive components.
|
|
||||||
///
|
|
||||||
/// This step subtly delineates the boundary of elements users interact with.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_7(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::SEVEN)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 8` - Used for stronger borders on interactive components and focus rings.
|
|
||||||
///
|
|
||||||
/// It strengthens the visibility and accessibility of active elements and their focus states.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_8(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::EIGHT)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 9` - Used for solid backgrounds.
|
|
||||||
///
|
|
||||||
/// `Step 9` is the most saturated step, having the least mix of white or black.
|
|
||||||
///
|
|
||||||
/// Due to its high chroma, `Step 9` is versatile and particularly useful for semantic colors such as
|
|
||||||
/// error, warning, and success indicators.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_9(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::NINE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 10` - Used for hovered or active solid backgrounds, particularly when `Step 9` is their normal state.
|
|
||||||
///
|
|
||||||
/// May also be used for extremely low contrast text. This should be used sparingly, as it may be difficult to read.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_10(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::TEN)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 11` - Used for text and icons requiring low contrast or less emphasis.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_11(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::ELEVEN)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Step 12` - Used for text and icons requiring high contrast or prominence.
|
|
||||||
#[inline]
|
|
||||||
pub fn step_12(&self) -> Hsla {
|
|
||||||
self.step(ColorScaleStep::TWELVE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provides groups of [`ColorScale`]s for light and dark themes, as well as transparent versions of each scale.
|
|
||||||
pub struct ColorScaleSet {
|
|
||||||
light: ColorScale,
|
|
||||||
dark: ColorScale,
|
|
||||||
light_alpha: ColorScale,
|
|
||||||
dark_alpha: ColorScale,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ColorScaleSet {
|
|
||||||
pub(crate) fn new(
|
|
||||||
light: ColorScale,
|
|
||||||
light_alpha: ColorScale,
|
|
||||||
dark: ColorScale,
|
|
||||||
dark_alpha: ColorScale,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
light,
|
|
||||||
light_alpha,
|
|
||||||
dark,
|
|
||||||
dark_alpha,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn light(&self) -> &ColorScale {
|
|
||||||
&self.light
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn light_alpha(&self) -> &ColorScale {
|
|
||||||
&self.light_alpha
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn dark(&self) -> &ColorScale {
|
|
||||||
&self.dark
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn dark_alpha(&self) -> &ColorScale {
|
|
||||||
&self.dark_alpha
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
||||||
pub enum ScrollbarMode {
|
pub enum ScrollbarMode {
|
||||||
#[default]
|
#[default]
|
||||||
Scrolling,
|
Scrolling,
|
||||||
|
|||||||
@@ -1,359 +0,0 @@
|
|||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use gpui::{SharedString, WindowAppearance};
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::ThemeColors;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd, Eq, Hash, Deserialize, Serialize)]
|
|
||||||
pub enum ThemeMode {
|
|
||||||
#[default]
|
|
||||||
Light,
|
|
||||||
Dark,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ThemeMode {
|
|
||||||
pub fn is_dark(&self) -> bool {
|
|
||||||
matches!(self, Self::Dark)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return theme name: `light`, `dark`.
|
|
||||||
pub fn name(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
ThemeMode::Light => "Light",
|
|
||||||
ThemeMode::Dark => "Dark",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<WindowAppearance> for ThemeMode {
|
|
||||||
fn from(appearance: WindowAppearance) -> Self {
|
|
||||||
match appearance {
|
|
||||||
WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::Dark,
|
|
||||||
WindowAppearance::Light | WindowAppearance::VibrantLight => Self::Light,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Theme family
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)]
|
|
||||||
pub struct ThemeFamily {
|
|
||||||
/// The unique identifier for the theme.
|
|
||||||
pub id: String,
|
|
||||||
|
|
||||||
/// The name of the theme.
|
|
||||||
pub name: SharedString,
|
|
||||||
|
|
||||||
/// The author of the theme.
|
|
||||||
pub author: SharedString,
|
|
||||||
|
|
||||||
/// The URL of the theme.
|
|
||||||
pub url: String,
|
|
||||||
|
|
||||||
/// The light colors for the theme.
|
|
||||||
pub light: ThemeColors,
|
|
||||||
|
|
||||||
/// The dark colors for the theme.
|
|
||||||
pub dark: ThemeColors,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ThemeFamily {
|
|
||||||
fn default() -> Self {
|
|
||||||
ThemeFamily {
|
|
||||||
id: "coop".into(),
|
|
||||||
name: "Coop Default Theme".into(),
|
|
||||||
author: "Coop".into(),
|
|
||||||
url: "https://github.com/reyakov/coop".into(),
|
|
||||||
light: ThemeColors::light(),
|
|
||||||
dark: ThemeColors::dark(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ThemeFamily {
|
|
||||||
/// Returns the light colors for the theme.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn light(&self) -> &ThemeColors {
|
|
||||||
&self.light
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the dark colors for the theme.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn dark(&self) -> &ThemeColors {
|
|
||||||
&self.dark
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load a theme family from a JSON file.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `path` - Path to the JSON file containing the theme family. This can be
|
|
||||||
/// an absolute path or a path relative to the current working directory.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// Returns `Ok(ThemeFamily)` if the file was successfully loaded and parsed,
|
|
||||||
/// or `Err(anyhow::Error)` if there was an error reading or parsing the file.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// This function will return an error if:
|
|
||||||
/// - The file cannot be read (permission issues, file doesn't exist, etc.)
|
|
||||||
/// - The file contains invalid JSON
|
|
||||||
/// - The JSON structure doesn't match the `ThemeFamily` schema
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use theme::ThemeFamily;
|
|
||||||
///
|
|
||||||
/// # fn main() -> anyhow::Result<()> {
|
|
||||||
/// // Load from a relative path
|
|
||||||
/// let theme = ThemeFamily::from_file("assets/themes/my-theme.json")?;
|
|
||||||
///
|
|
||||||
/// // Load from an absolute path
|
|
||||||
/// let theme = ThemeFamily::from_file("/path/to/themes/my-theme.json")?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub(crate) fn from_file<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
|
|
||||||
let json_data = std::fs::read(path)?;
|
|
||||||
let theme_family = serde_json::from_slice(&json_data)?;
|
|
||||||
|
|
||||||
Ok(theme_family)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load a theme family from a JSON file in the assets/themes directory.
|
|
||||||
///
|
|
||||||
/// This function looks for the file at `assets/themes/{name}.json` relative
|
|
||||||
/// to the current working directory. This is useful for loading themes
|
|
||||||
/// from the standard theme directory in the project structure.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `name` - Name of the theme file (without the .json extension)
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// Returns `Ok(ThemeFamily)` if the file was successfully loaded and parsed,
|
|
||||||
/// or `Err(anyhow::Error)` if there was an error reading or parsing the file.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// This function will return an error if:
|
|
||||||
/// - The file cannot be read (permission issues, file doesn't exist, etc.)
|
|
||||||
/// - The file contains invalid JSON
|
|
||||||
/// - The JSON structure doesn't match the `ThemeFamily` schema
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use theme::ThemeFamily;
|
|
||||||
///
|
|
||||||
/// # fn main() -> anyhow::Result<()> {
|
|
||||||
/// // Assuming the file exists at `assets/themes/my-theme.json`
|
|
||||||
/// let theme = ThemeFamily::from_assets("themes/my-theme.json")?;
|
|
||||||
///
|
|
||||||
/// println!("Loaded theme: {}", theme.name);
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub fn from_assets(target: &str) -> anyhow::Result<Self> {
|
|
||||||
let path = format!("assets/{target}");
|
|
||||||
Self::from_file(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
use tempfile::tempdir;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_file() {
|
|
||||||
// Create a temporary directory for our test
|
|
||||||
let dir = tempdir().unwrap();
|
|
||||||
let file_path = dir.path().join("test-theme.json");
|
|
||||||
|
|
||||||
// Create a minimal valid theme JSON with hex colors
|
|
||||||
// Using simple hex colors that Hsla can parse
|
|
||||||
// Note: We need to escape the # characters in the raw string
|
|
||||||
let json_data = r##"{
|
|
||||||
"id": "test-theme",
|
|
||||||
"name": "Test Theme",
|
|
||||||
"author": "Coop",
|
|
||||||
"url": "https://github.com/reyakov/coop",
|
|
||||||
"light": {
|
|
||||||
"background": "#ffffff",
|
|
||||||
"surface_background": "#fafafa",
|
|
||||||
"elevated_surface_background": "#f5f5f5",
|
|
||||||
"panel_background": "#ffffff",
|
|
||||||
"overlay": "#0000001a",
|
|
||||||
"title_bar": "#00000000",
|
|
||||||
"title_bar_inactive": "#ffffff",
|
|
||||||
"window_border": "#c7c7cf",
|
|
||||||
"border": "#dbdbdb",
|
|
||||||
"border_variant": "#d1d1d1",
|
|
||||||
"border_focused": "#3366cc",
|
|
||||||
"border_selected": "#3366cc",
|
|
||||||
"border_transparent": "#00000000",
|
|
||||||
"border_disabled": "#e6e6e6",
|
|
||||||
"ring": "#4d79d6",
|
|
||||||
"text": "#1a1a1a",
|
|
||||||
"text_muted": "#4d4d4d",
|
|
||||||
"text_placeholder": "#808080",
|
|
||||||
"text_accent": "#3366cc",
|
|
||||||
"icon": "#4d4d4d",
|
|
||||||
"icon_muted": "#808080",
|
|
||||||
"icon_accent": "#3366cc",
|
|
||||||
"element_foreground": "#ffffff",
|
|
||||||
"element_background": "#3366cc",
|
|
||||||
"element_hover": "#3366cce6",
|
|
||||||
"element_active": "#2e5cb8",
|
|
||||||
"element_selected": "#2952a3",
|
|
||||||
"element_disabled": "#3366cc4d",
|
|
||||||
"secondary_foreground": "#2952a3",
|
|
||||||
"secondary_background": "#e6ecf5",
|
|
||||||
"secondary_hover": "#3366cc1a",
|
|
||||||
"secondary_active": "#d9e2f0",
|
|
||||||
"secondary_selected": "#d9e2f0",
|
|
||||||
"secondary_disabled": "#3366cc4d",
|
|
||||||
"danger_foreground": "#ffffff",
|
|
||||||
"danger_background": "#f5e6e6",
|
|
||||||
"danger_hover": "#cc33331a",
|
|
||||||
"danger_active": "#f0d9d9",
|
|
||||||
"danger_selected": "#f0d9d9",
|
|
||||||
"danger_disabled": "#cc33334d",
|
|
||||||
"warning_foreground": "#1a1a1a",
|
|
||||||
"warning_background": "#f5f0e6",
|
|
||||||
"warning_hover": "#cc99331a",
|
|
||||||
"warning_active": "#f0ead9",
|
|
||||||
"warning_selected": "#f0ead9",
|
|
||||||
"warning_disabled": "#cc99334d",
|
|
||||||
"ghost_element_background": "#00000000",
|
|
||||||
"ghost_element_background_alt": "#e6e6e6",
|
|
||||||
"ghost_element_hover": "#0000001a",
|
|
||||||
"ghost_element_active": "#d9d9d9",
|
|
||||||
"ghost_element_selected": "#d9d9d9",
|
|
||||||
"ghost_element_disabled": "#0000000d",
|
|
||||||
"tab_inactive_background": "#e6e6e6",
|
|
||||||
"tab_hover_background": "#e0e0e0",
|
|
||||||
"tab_active_background": "#d9d9d9",
|
|
||||||
"scrollbar_thumb_background": "#00000033",
|
|
||||||
"scrollbar_thumb_hover_background": "#0000004d",
|
|
||||||
"scrollbar_thumb_border": "#00000000",
|
|
||||||
"scrollbar_track_background": "#00000000",
|
|
||||||
"scrollbar_track_border": "#d9d9d9",
|
|
||||||
"drop_target_background": "#3366cc1a",
|
|
||||||
"cursor": "#3399ff",
|
|
||||||
"selection": "#3399ff40"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"background": "#1a1a1a",
|
|
||||||
"surface_background": "#1f1f1f",
|
|
||||||
"elevated_surface_background": "#242424",
|
|
||||||
"panel_background": "#262626",
|
|
||||||
"overlay": "#ffffff1a",
|
|
||||||
"title_bar": "#00000000",
|
|
||||||
"title_bar_inactive": "#1a1a1a",
|
|
||||||
"window_border": "#404046",
|
|
||||||
"border": "#404040",
|
|
||||||
"border_variant": "#383838",
|
|
||||||
"border_focused": "#4d79d6",
|
|
||||||
"border_selected": "#4d79d6",
|
|
||||||
"border_transparent": "#00000000",
|
|
||||||
"border_disabled": "#2e2e2e",
|
|
||||||
"ring": "#668cdf",
|
|
||||||
"text": "#f2f2f2",
|
|
||||||
"text_muted": "#b3b3b3",
|
|
||||||
"text_placeholder": "#808080",
|
|
||||||
"text_accent": "#668cdf",
|
|
||||||
"icon": "#b3b3b3",
|
|
||||||
"icon_muted": "#808080",
|
|
||||||
"icon_accent": "#668cdf",
|
|
||||||
"element_foreground": "#ffffff",
|
|
||||||
"element_background": "#4d79d6",
|
|
||||||
"element_hover": "#4d79d6e6",
|
|
||||||
"element_active": "#456dc1",
|
|
||||||
"element_selected": "#3e62ac",
|
|
||||||
"element_disabled": "#4d79d64d",
|
|
||||||
"secondary_foreground": "#3e62ac",
|
|
||||||
"secondary_background": "#2a3652",
|
|
||||||
"secondary_hover": "#4d79d61a",
|
|
||||||
"secondary_active": "#303d5c",
|
|
||||||
"secondary_selected": "#303d5c",
|
|
||||||
"secondary_disabled": "#4d79d64d",
|
|
||||||
"danger_foreground": "#ffffff",
|
|
||||||
"danger_background": "#522a2a",
|
|
||||||
"danger_hover": "#d64d4d1a",
|
|
||||||
"danger_active": "#5c3030",
|
|
||||||
"danger_selected": "#5c3030",
|
|
||||||
"danger_disabled": "#d64d4d4d",
|
|
||||||
"warning_foreground": "#f2f2f2",
|
|
||||||
"warning_background": "#52482a",
|
|
||||||
"warning_hover": "#d6b34d1a",
|
|
||||||
"warning_active": "#5c5430",
|
|
||||||
"warning_selected": "#5c5430",
|
|
||||||
"warning_disabled": "#d6b34d4d",
|
|
||||||
"ghost_element_background": "#00000000",
|
|
||||||
"ghost_element_background_alt": "#2e2e2e",
|
|
||||||
"ghost_element_hover": "#ffffff1a",
|
|
||||||
"ghost_element_active": "#383838",
|
|
||||||
"ghost_element_selected": "#383838",
|
|
||||||
"ghost_element_disabled": "#ffffff0d",
|
|
||||||
"tab_inactive_background": "#2e2e2e",
|
|
||||||
"tab_hover_background": "#333333",
|
|
||||||
"tab_active_background": "#383838",
|
|
||||||
"scrollbar_thumb_background": "#ffffff33",
|
|
||||||
"scrollbar_thumb_hover_background": "#ffffff4d",
|
|
||||||
"scrollbar_thumb_border": "#00000000",
|
|
||||||
"scrollbar_track_background": "#00000000",
|
|
||||||
"scrollbar_track_border": "#383838",
|
|
||||||
"drop_target_background": "#4d79d61a",
|
|
||||||
"cursor": "#4db3ff",
|
|
||||||
"selection": "#4db3ff40"
|
|
||||||
}
|
|
||||||
}"##;
|
|
||||||
|
|
||||||
// Write the JSON to the file
|
|
||||||
fs::write(&file_path, json_data).unwrap();
|
|
||||||
|
|
||||||
// Test loading the theme from file
|
|
||||||
let theme = ThemeFamily::from_file(&file_path).unwrap();
|
|
||||||
|
|
||||||
// Verify the loaded theme
|
|
||||||
assert_eq!(theme.id, "test-theme");
|
|
||||||
assert_eq!(theme.name, "Test Theme");
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
dir.close().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_file_nonexistent() {
|
|
||||||
// Test that loading a non-existent file returns an error
|
|
||||||
let result = ThemeFamily::from_file("non-existent-file.json");
|
|
||||||
assert!(result.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_file_invalid_json() {
|
|
||||||
// Create a temporary directory for our test
|
|
||||||
let dir = tempdir().unwrap();
|
|
||||||
let file_path = dir.path().join("invalid-theme.json");
|
|
||||||
|
|
||||||
// Write invalid JSON
|
|
||||||
fs::write(&file_path, "invalid json").unwrap();
|
|
||||||
|
|
||||||
// Test that loading invalid JSON returns an error
|
|
||||||
let result = ThemeFamily::from_file(&file_path);
|
|
||||||
assert!(result.is_err());
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
dir.close().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ use gpui::{
|
|||||||
Window, div, px,
|
Window, div, px,
|
||||||
};
|
};
|
||||||
use settings::AppSettings;
|
use settings::AppSettings;
|
||||||
use theme::{ActiveTheme, Theme, ThemeMode};
|
use theme::{ActiveTheme, Appearance, Theme};
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants};
|
||||||
use ui::group_box::{GroupBox, GroupBoxVariants};
|
use ui::group_box::{GroupBox, GroupBoxVariants};
|
||||||
use ui::input::{Input, InputState};
|
use ui::input::{Input, InputState};
|
||||||
@@ -47,10 +47,10 @@ impl Preferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the theme mode (light or dark)
|
/// Set the appearance (light or dark)
|
||||||
fn set_theme_mode(mode: ThemeMode, window: &mut Window, cx: &mut App) {
|
fn set_appearance(appearance: Appearance, window: &mut Window, cx: &mut App) {
|
||||||
AppSettings::update_theme_mode(mode, cx);
|
AppSettings::update_appearance(appearance, cx);
|
||||||
Theme::change(mode, Some(window), cx);
|
Theme::change(appearance, Some(window), cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ impl Render for Preferences {
|
|||||||
let screening = AppSettings::get_screening(cx);
|
let screening = AppSettings::get_screening(cx);
|
||||||
let hide_avatar = AppSettings::get_hide_avatar(cx);
|
let hide_avatar = AppSettings::get_hide_avatar(cx);
|
||||||
let nip4e = AppSettings::get_nip4e(cx);
|
let nip4e = AppSettings::get_nip4e(cx);
|
||||||
let theme_mode = AppSettings::get_theme_mode(cx);
|
let appearance = AppSettings::get_appearance(cx);
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_4()
|
.gap_4()
|
||||||
@@ -114,18 +114,22 @@ impl Render for Preferences {
|
|||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("theme-mode")
|
Button::new("theme-mode")
|
||||||
.label(theme_mode.name())
|
.label(if appearance.is_dark() {
|
||||||
|
"Dark"
|
||||||
|
} else {
|
||||||
|
"Light"
|
||||||
|
})
|
||||||
.ghost_alt()
|
.ghost_alt()
|
||||||
.small()
|
.small()
|
||||||
.dropdown_menu(|this, _window, _cx| {
|
.dropdown_menu(|this, _window, _cx| {
|
||||||
this.item(PopupMenuItem::new("Light").on_click(
|
this.item(PopupMenuItem::new("Light").on_click(
|
||||||
|_, window, cx| {
|
|_, window, cx| {
|
||||||
Self::set_theme_mode(ThemeMode::Light, window, cx);
|
Self::set_appearance(Appearance::Light, window, cx);
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.item(
|
.item(
|
||||||
PopupMenuItem::new("Dark").on_click(|_, window, cx| {
|
PopupMenuItem::new("Dark").on_click(|_, window, cx| {
|
||||||
Self::set_theme_mode(ThemeMode::Dark, window, cx);
|
Self::set_appearance(Appearance::Dark, window, cx);
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|||||||
+20
-12
@@ -15,7 +15,7 @@ use nostr_sdk::prelude::*;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use state::{NostrRegistry, StateEvent};
|
use state::{NostrRegistry, StateEvent};
|
||||||
use theme::{ActiveTheme, SIDEBAR_WIDTH, Theme, ThemeRegistry};
|
use theme::{ActiveTheme, SIDEBAR_WIDTH, Theme, ThemeRegistry, ThemeVariant};
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants};
|
||||||
use ui::dock::{self, DockArea, DockLayout, DockPlacement, Panel, PanelHandle};
|
use ui::dock::{self, DockArea, DockLayout, DockPlacement, Panel, PanelHandle};
|
||||||
use ui::notification::{Notification, NotificationKind};
|
use ui::notification::{Notification, NotificationKind};
|
||||||
@@ -464,17 +464,20 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn theme_selector(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn theme_selector(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
window.open_dialog(cx, move |this, _window, cx| {
|
let appearance = cx.theme().appearance;
|
||||||
let registry = ThemeRegistry::global(cx);
|
let variants: Vec<ThemeVariant> = ThemeRegistry::active()
|
||||||
let themes = registry.read(cx).themes();
|
.variants_for(appearance)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
window.open_dialog(cx, move |this, _window, cx| {
|
||||||
this.width(px(520.))
|
this.width(px(520.))
|
||||||
.show_close(true)
|
.show_close(true)
|
||||||
.title("Select theme")
|
.title("Select theme")
|
||||||
.child(v_flex().gap_2().w_full().children({
|
.child(v_flex().gap_2().w_full().children({
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
|
|
||||||
for (ix, (path, theme)) in themes.iter().enumerate() {
|
for (ix, variant) in variants.iter().enumerate() {
|
||||||
items.push(
|
items.push(
|
||||||
h_flex()
|
h_flex()
|
||||||
.id(ix)
|
.id(ix)
|
||||||
@@ -491,13 +494,15 @@ impl Workspace {
|
|||||||
.gap_1p5()
|
.gap_1p5()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.child(theme.name.clone())
|
.child(SharedString::from(variant.name.clone()))
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.text_xs()
|
.text_xs()
|
||||||
.italic()
|
.italic()
|
||||||
.text_color(cx.theme().text_muted)
|
.text_color(cx.theme().text_muted)
|
||||||
.child(theme.author.clone()),
|
.child(SharedString::from(
|
||||||
|
variant.family_id.clone(),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
@@ -511,9 +516,9 @@ impl Workspace {
|
|||||||
.ghost()
|
.ghost()
|
||||||
.small()
|
.small()
|
||||||
.on_click({
|
.on_click({
|
||||||
let theme = theme.clone();
|
let url = variant.source.url.clone();
|
||||||
move |_ev, _window, cx| {
|
move |_ev, _window, cx| {
|
||||||
cx.open_url(&theme.url);
|
cx.open_url(&url);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -523,13 +528,16 @@ impl Workspace {
|
|||||||
.primary()
|
.primary()
|
||||||
.small()
|
.small()
|
||||||
.on_click({
|
.on_click({
|
||||||
let path = path.clone();
|
let id = variant.id.clone();
|
||||||
move |_ev, window, cx| {
|
move |_ev, window, cx| {
|
||||||
let settings = AppSettings::global(cx);
|
let settings = AppSettings::global(cx);
|
||||||
let path = path.clone();
|
|
||||||
|
|
||||||
settings.update(cx, |this, cx| {
|
settings.update(cx, |this, cx| {
|
||||||
this.set_theme(path, window, cx);
|
this.set_theme_variant(
|
||||||
|
id.clone(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user