Skip to main content

ui/
icons.rs

1//! Embedded icon assets + the gpui [`AssetSource`] that serves them.
2//!
3//! Most glyphs come from the **Solar Icons** set (Linear weight) by 480 Design,
4//! licensed under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/);
5//! attribution: "Solar Icons by 480 Design". A few (`terminal`, `plus`,
6//! `close`, `stop`, `git-branch`, `star`) are hand-drawn in the same style.
7//!
8//! Icons render via [`icon`]: `icon(icons::PAPERCLIP).size(px(16.)).text_color(…)`.
9
10use std::borrow::Cow;
11
12use gpui::{AssetSource, Result, SharedString, Styled as _, Svg, svg};
13
14macro_rules! icon_assets {
15    ($(($const_name:ident, $path:literal)),+ $(,)?) => {
16        $(pub const $const_name: &str = concat!("icons/", $path, ".svg");)+
17
18        /// Every icon in the set: `(constant name, asset path)`. The paths are
19        /// `&'static str` so they can be handed straight to [`icon`] — which is
20        /// what makes the set browsable at all.
21        pub const ALL: &[(&str, &str)] = &[
22            $((stringify!($const_name), concat!("icons/", $path, ".svg"))),+
23        ];
24
25        /// Serves the embedded icons to gpui's SVG renderer.
26        pub struct Assets;
27
28        impl AssetSource for Assets {
29            fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
30                Ok(match path {
31                    $(concat!("icons/", $path, ".svg") => Some(Cow::Borrowed(
32                        include_bytes!(concat!("../assets/icons/", $path, ".svg")).as_slice(),
33                    )),)+
34                    _ => None,
35                })
36            }
37
38            fn list(&self, path: &str) -> Result<Vec<SharedString>> {
39                let all = [$(concat!("icons/", $path, ".svg")),+];
40                Ok(all
41                    .iter()
42                    .filter(|p| p.starts_with(path))
43                    .map(|p| SharedString::from(*p))
44                    .collect())
45            }
46        }
47    };
48}
49
50icon_assets![
51    // Solar Icons (Linear), CC BY 4.0 — 480 Design.
52    (MONITOR, "monitor"),
53    (LAPTOP, "laptop"),
54    (PEN_NEW_SQUARE, "pen-new-square"),
55    (SORT_VERTICAL, "sort-vertical"),
56    (LIST, "list"),
57    (FOLDER_WITH_FILES, "folder-with-files"),
58    (FOLDER, "folder"),
59    // Hand-drawn git-branch glyph in the Solar Linear style (like the
60    // terminal/plus/return ports) — the set has no branch icon.
61    (GIT_BRANCH, "git-branch"),
62    // Compact history-ref glyphs, drawn in the same linear style.
63    (CLOUD, "cloud"),
64    (TAG, "tag"),
65    (SIDEBAR_MINIMALISTIC, "sidebar-minimalistic"),
66    // Mirrored variant (the reference window-controls.tsx `-scale-x-100`): the LEFT
67    // sidebar toggle shows the panel line on the left; gpui divs have no
68    // scale transform at the pinned rev, so the flip is baked into the asset.
69    (SIDEBAR_MINIMALISTIC_LEFT, "sidebar-minimalistic-left"),
70    (KEY_MINIMALISTIC, "key-minimalistic"),
71    (KEYBOARD, "keyboard"),
72    (ARROW_LEFT, "arrow-left"),
73    (ARROW_RIGHT, "arrow-right"),
74    (ARROW_UP, "arrow-up"),
75    // arrow-up mirrored (like the sidebar flip) — the Solar Linear set here
76    // has no plain arrow-down.
77    (ARROW_DOWN, "arrow-down"),
78    // Hand-drawn return/enter arrow in the Solar Linear style (like the
79    // terminal/plus/close ports) — the set has no return glyph.
80    (RETURN, "return"),
81    (ALT_ARROW_DOWN, "alt-arrow-down"),
82    // Hand-drawn expand/maximize arrows in the Solar Linear style (like the
83    // terminal/plus/return ports) — the set has no expand glyph.
84    (EXPAND_ARROWS, "expand-arrows"),
85    // Hand-drawn fold-all chevrons, drawn as a family with EXPAND_ARROWS
86    // (same stroke, caps, 90° joints) — Solar has no unfold-less either.
87    (FOLD_VERTICAL, "fold-vertical"),
88    (ALT_ARROW_LEFT, "alt-arrow-left"),
89    (ALT_ARROW_RIGHT, "alt-arrow-right"),
90    (SMARTPHONE, "smartphone"),
91    (ARCHIVE_UP_MINIMALISTIC, "archive-up-minimalistic"),
92    (REFRESH, "refresh"),
93    (RESTART, "restart"),
94    (ADD_CIRCLE, "add-circle"),
95    (TUNING, "tuning"),
96    (PAPERCLIP, "paperclip"),
97    (PEN, "pen"),
98    (ARCHIVE_MINIMALISTIC, "archive-minimalistic"),
99    (TRASH_BIN_MINIMALISTIC, "trash-bin-minimalistic"),
100    (SETTINGS_MINIMALISTIC, "settings-minimalistic"),
101    (LOGOUT_2, "logout-2"),
102    (MAGNIFER, "magnifer"),
103    (COMMAND, "command"),
104    (DOCUMENT, "document"),
105    (DOCUMENT_ADD, "document-add"),
106    // The verbs an agent's tool calls come in — fetch, link, read, discover.
107    // Ported from `../desktop`'s tool-name→icon map, which stays over there:
108    // the map is that app's data model, and these are just glyphs.
109    (DOWNLOAD, "download-minimalistic"),
110    (LINK, "link-minimalistic"),
111    (BOOK, "book"),
112    (COMPASS, "compass"),
113    // What an agent recalls from. Named for the shape, like every icon here —
114    // `magnifer` rather than `search` — so the app's word for the store, brain
115    // or memory or notes, stays in the app.
116    (CPU, "cpu"),
117    (GLOBAL, "global"),
118    (CHECKLIST, "checklist"),
119    (TEXT, "text"),
120    (HASHTAG, "hashtag"),
121    (CALENDAR, "calendar"),
122    (WIDGET, "widget"),
123    (WIFI_OFF, "wifi-off"),
124    (CLOSE_CIRCLE, "close-circle"),
125    // Hand-drawn info glyph in the Solar Linear style (like the terminal/
126    // plus/return ports) — the embedded set has no info-circle.
127    (INFO_CIRCLE, "info-circle"),
128    (DANGER_TRIANGLE, "danger-triangle"),
129    (CHAT_ROUND_LINE, "chat-round-line"),
130    // Hand-drawn bell in the Solar Linear style (like the terminal/plus/return
131    // ports) — the embedded set has none.
132    (BELL, "bell"),
133    // The two ends of an appearance switch. A pair, so a switch between them
134    // needs no label to say which way is which.
135    (SUN, "sun"),
136    (MOON, "moon"),
137    // Media transport. The three volume glyphs are one Solar family on purpose:
138    // a level control swaps between them as you slide, and a speaker cone that
139    // changed shape mid-slide would read as a bug. `volume-loud` used to be
140    // hand-drawn here and no longer is, for exactly that reason.
141    (VOLUME_MUTE, "volume-mute"),
142    (VOLUME_LOW, "volume-low"),
143    (VOLUME_LOUD, "volume-loud"),
144    // Linear like their neighbours; the bold pairs are the two states a
145    // transport paints solid, the same split STAR/STAR_BOLD already makes.
146    (PLAY, "play"),
147    (PLAY_BOLD, "play-bold"),
148    (PAUSE, "pause"),
149    (PAUSE_BOLD, "pause-bold"),
150    (SKIP_PREVIOUS, "skip-previous"),
151    (SKIP_NEXT, "skip-next"),
152    (SHUFFLE, "shuffle"),
153    (REPEAT, "repeat"),
154    (REPEAT_ONE, "repeat-one"),
155    (HEART, "heart"),
156    (HEART_BOLD, "heart-bold"),
157    (PLAYLIST, "playlist"),
158    (MICROPHONE, "microphone"),
159    // Hand-drawn reference glyphs (terminal-panel.tsx / composer-actions.tsx /
160    // menu-check.tsx / logo.tsx).
161    (TERMINAL, "terminal"),
162    (PLUS, "plus"),
163    // Hand-drawn dots in the Solar Linear style — the embedded set has no
164    // menu-dots, and the `···` is what opens a row's menu.
165    (MENU_DOTS, "menu-dots"),
166    (CLOSE, "close"),
167    (STOP, "stop"),
168    (CHECK, "check"),
169    (COPY, "copy"),
170    // Hand-drawn star pair in the Solar Linear style (like the terminal/
171    // plus/return ports) — outline for the favorite affordance, bold for the
172    // favorited state and the picker's favorites rail tab.
173    (STAR, "star"),
174    (STAR_BOLD, "star-bold"),
175];
176
177/// An icon element for an embedded asset path. Size and colour are set by the
178/// caller (`.size(..)`, `.text_color(..)`), matching the web app's
179/// `[&_svg]:size-4` idiom.
180pub fn icon(path: &'static str) -> Svg {
181    svg().path(path).flex_none()
182}