Skip to main content

fluent_app/
assets.rs

1use std::{borrow::Cow, sync::Arc};
2
3use gpui::{AssetSource, Result, SharedString};
4
5const ADD: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M7 2h2v5h5v2H9v5H7V9H2V7h5z"/></svg>"#;
6const ARROW_FORWARD: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M9.3 3.3 14 8l-4.7 4.7-1.4-1.4L10.2 9H2V7h8.2L7.9 4.7z"/></svg>"#;
7const CHECKMARK: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="m6.2 11.4-3.3-3.3 1.4-1.4 1.9 1.9 5.5-5.5 1.4 1.4z"/></svg>"#;
8const CHEVRON_DOWN: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M4.2 6 8 9.8 11.8 6l1.2 1.2-5 5-5-5z"/></svg>"#;
9const CHEVRON_UP: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M4.2 10 8 6.2l3.8 3.8 1.2-1.2-5-5-5 5z"/></svg>"#;
10const CLEAR: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M4.3 3 8 6.7 11.7 3 13 4.3 9.3 8l3.7 3.7-1.3 1.3L8 9.3 4.3 13 3 11.7 6.7 8 3 4.3z"/></svg>"#;
11const COPY: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M5 2h7v9H5zm-2 3h1v7h6v1H3z"/></svg>"#;
12const CUT: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M4 5a2 2 0 1 1 1.4-.6L8 7l2.6-2.6A2 2 0 1 1 12 5a2 2 0 0 1-.7-.1L9.4 8l1.9 3.1A2 2 0 1 1 10 12l-2-2-2 2a2 2 0 1 1-1.3-.9L6.6 8 4.7 4.9A2 2 0 0 1 4 5z"/></svg>"#;
13const DISMISS: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="m4.3 3 3.7 3.7L11.7 3 13 4.3 9.3 8l3.7 3.7-1.3 1.3L8 9.3 4.3 13 3 11.7 6.7 8 3 4.3z"/></svg>"#;
14const FOLDER: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M2 4h5l1 1h6v7.5A1.5 1.5 0 0 1 12.5 14h-9A1.5 1.5 0 0 1 2 12.5z"/></svg>"#;
15const GRID: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M2 2h5v5H2zm7 0h5v5H9zM2 9h5v5H2zm7 0h5v5H9z"/></svg>"#;
16const LIGHT: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M8 4a4 4 0 1 1 0 8A4 4 0 0 1 8 4zm0-3h1v2H7V1zm0 12h1v2H7v-2zM1 7h2v2H1zm12 0h2v2h-2zM3 2.3 4.4 3.7 3.7 4.4 2.3 3zm9.3 9.3 1.4 1.4-.7.7-1.4-1.4zM13.7 3l-1.4 1.4-.7-.7L13 2.3zM4.4 12.3 3 13.7l-.7-.7 1.4-1.4z"/></svg>"#;
17const LIST_TREE: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 2h2v2H3zm4 .5h7v1H7zM3 7h2v2H3zm4 .5h7v1H7zM3 12h2v2H3zm4 .5h7v1H7z"/></svg>"#;
18const MINIMIZE: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 8h10v1.5H3z"/></svg>"#;
19const MAXIMIZE: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 3h10v10H3zm1.5 1.5v7h7v-7z"/></svg>"#;
20const PANEL_OPEN: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M2 3h12v10H2zm2 1.5v7h2.5v-7zm4 0v7h4.5v-7z"/></svg>"#;
21const PASTE: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M6 1h4l.5 1H13v12H3V2h2.5zm0 2v1h4V3zM5 6v6h6V6z"/></svg>"#;
22const PLUG: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M6 1h1v4h2V1h1v4h1v3a4 4 0 0 1-3 3.9V15H7v-3.1A4 4 0 0 1 4 8V5h2z"/></svg>"#;
23const SEARCH: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M7 2a5 5 0 0 1 3.9 8.1l3 3-1.4 1.4-3-3A5 5 0 1 1 7 2zm0 2a3 3 0 1 0 0 6 3 3 0 0 0 0-6z"/></svg>"#;
24const SETTINGS: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M7 1h2l.4 2 1.7.7 1.7-1.1 1.4 1.4-1.1 1.7.7 1.7 2 .4v2l-2 .4-.7 1.7 1.1 1.7-1.4 1.4-1.7-1.1-1.7.7-.4 2H7l-.4-2-1.7-.7-1.7 1.1-1.4-1.4 1.1-1.7-.7-1.7-2-.4V8l2-.4.7-1.7-1.1-1.7 1.4-1.4 1.7 1.1 1.7-.7zM8 6a2 2 0 1 0 0 4 2 2 0 0 0 0-4z"/></svg>"#;
25const SPINNER: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M8 1a7 7 0 0 1 7 7h-2a5 5 0 0 0-5-5z"/></svg>"#;
26const DOCUMENT: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 2h7l3 3v9H3V2zm7 0v3h3"/></svg>"#;
27const DOWNLOAD: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M8 11 4 7h2.5V2h3v5H12zm-6 2h12v1.5H2z"/></svg>"#;
28const UPLOAD: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M8 2l4 4H9.5v5h-3V6H4zm-6 11h12v1.5H2z"/></svg>"#;
29const GENERIC: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 3h10v10H3z"/></svg>"#;
30
31/// Built-in FluentGUI assets plus an optional application asset fallback.
32#[derive(Default)]
33pub struct FluentAssets {
34    fallback: Option<Arc<dyn AssetSource>>,
35}
36
37impl FluentAssets {
38    pub fn new(fallback: Option<Arc<dyn AssetSource>>) -> Self {
39        Self { fallback }
40    }
41
42    fn builtin(path: &str) -> Option<&'static [u8]> {
43        Some(match path {
44            "icons/add.svg" | "icons/folder_add.svg" => ADD,
45            "icons/arrow_forward.svg" => ARROW_FORWARD,
46            "icons/checkmark.svg" => CHECKMARK,
47            "icons/chevron_down.svg" => CHEVRON_DOWN,
48            "icons/chevron_up.svg" => CHEVRON_UP,
49            "icons/clear.svg" | "icons/eraser.svg" => CLEAR,
50            "icons/copy.svg" => COPY,
51            "icons/cut.svg" => CUT,
52            "icons/dismiss.svg" | "icons/delete.svg" | "icons/plug_disconnected.svg" => DISMISS,
53            "icons/document.svg" | "icons/file.svg" => DOCUMENT,
54            "icons/download.svg" | "icons/download_arrow.svg" => DOWNLOAD,
55            "icons/upload.svg" | "icons/upload_arrow.svg" => UPLOAD,
56            "icons/folder_open.svg" | "icons/folder_sync.svg" => FOLDER,
57            "icons/grid.svg" => GRID,
58            "icons/light_theme.svg" => LIGHT,
59            "icons/dark_theme.svg" => GENERIC,
60            "icons/list_tree.svg" => LIST_TREE,
61            "icons/minimize.svg" => MINIMIZE,
62            "icons/maximize.svg" | "icons/full_screen.svg" | "icons/fit_width.svg" => MAXIMIZE,
63            "icons/panel_open.svg" => PANEL_OPEN,
64            "icons/paste.svg" | "icons/clipboard_paste.svg" | "icons/clipboard_sync.svg" => PASTE,
65            "icons/plug.svg" | "icons/connect.svg" | "icons/network.svg" => PLUG,
66            "icons/search.svg" | "icons/find_replace.svg" => SEARCH,
67            "icons/settings.svg" => SETTINGS,
68            "icons/spinner.svg" => SPINNER,
69            path if path.starts_with("icons/") && path.ends_with(".svg") => GENERIC,
70            _ => return None,
71        })
72    }
73}
74
75impl AssetSource for FluentAssets {
76    fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
77        if let Some(bytes) = Self::builtin(path) {
78            return Ok(Some(Cow::Borrowed(bytes)));
79        }
80        if let Some(fallback) = &self.fallback {
81            return fallback.load(path);
82        }
83        Ok(None)
84    }
85
86    fn list(&self, path: &str) -> Result<Vec<SharedString>> {
87        if let Some(fallback) = &self.fallback {
88            return fallback.list(path);
89        }
90        let names = if path == "icons" {
91            vec![
92                "add.svg",
93                "arrow_forward.svg",
94                "checkmark.svg",
95                "chevron_down.svg",
96                "chevron_up.svg",
97                "clear.svg",
98                "copy.svg",
99                "dismiss.svg",
100                "grid.svg",
101                "light_theme.svg",
102                "panel_open.svg",
103                "plug.svg",
104                "search.svg",
105                "settings.svg",
106                "spinner.svg",
107            ]
108        } else {
109            vec![]
110        };
111        Ok(names.into_iter().map(SharedString::from).collect())
112    }
113}