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_RIGHT: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="m6 3 5 5-5 5-1.2-1.2L8.6 8 4.8 4.2z"/></svg>"#;
10const 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>"#;
11const 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>"#;
12const 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>"#;
13const 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>"#;
14const DARK: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M9.6 1.2a6.8 6.8 0 1 0 5.2 5.2A5.2 5.2 0 1 1 9.6 1.2z"/></svg>"#;
15const 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>"#;
16const 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>"#;
17const 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>"#;
18const 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>"#;
19const 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>"#;
20const MINIMIZE: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 8h10v1.5H3z"/></svg>"#;
21const 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>"#;
22const 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>"#;
23const 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>"#;
24const 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>"#;
25const 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>"#;
26const 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>"#;
27const 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>"#;
28const 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>"#;
29const 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>"#;
30const 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>"#;
31const GENERIC: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 3h10v10H3z"/></svg>"#;
32
33const HOME: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M8 1.5 14.5 7H13v6.5a.5.5 0 0 1-.5.5H10V10H6v4H3.5a.5.5 0 0 1-.5-.5V7H1.5z"/></svg>"#;
35const MIRROR: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M4 2h8a1.5 1.5 0 0 1 1.5 1.5v9A1.5 1.5 0 0 1 12 14H4a1.5 1.5 0 0 1-1.5-1.5v-9A1.5 1.5 0 0 1 4 2zm0 1.5v9h8v-9zM6.5 12h3v.5h-3z"/></svg>"#;
36const BELL: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M8 1.5a.75.75 0 0 1 .75.75v.55A4.5 4.5 0 0 1 12.5 7v2.5l1 1.5v.5h-11v-.5l1-1.5V7a4.5 4.5 0 0 1 3.75-4.2v-.55A.75.75 0 0 1 8 1.5zM6.5 13h3a1.5 1.5 0 0 1-3 0z"/></svg>"#;
37const BELL_OFF: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="m2 2 12 12-1 1L1.5 3zm6 0a.75.75 0 0 1 .75.75v.55A4.5 4.5 0 0 1 12.5 7v2.5l.62.93L4.4 1.81A4.5 4.5 0 0 1 7.25 3.3v-.55A.75.75 0 0 1 8 2zM3.5 7c0-.4.05-.78.14-1.14l9 9H2.5v-.5l1-1.5V7zm3 6h3a1.5 1.5 0 0 1-3 0z"/></svg>"#;
38const CHAT: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 2h10a1.5 1.5 0 0 1 1.5 1.5v7A1.5 1.5 0 0 1 13 12H7l-3.5 2.5V12H3A1.5 1.5 0 0 1 1.5 10.5v-7A1.5 1.5 0 0 1 3 2z"/></svg>"#;
39const PHONE: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M5 1.5h6A1.5 1.5 0 0 1 12.5 3v10a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 13V3A1.5 1.5 0 0 1 5 1.5zm0 1.5v8h6V3zm2.25 9.25a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0z"/></svg>"#;
40const QR: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M2 2h5v5H2zm1.5 1.5v2h2v-2zM9 2h5v5H9zm1.5 1.5v2h2v-2zM2 9h5v5H2zm1.5 1.5v2h2v-2zM9 9h2v2H9zm3 0h2v2h-2zm-3 3h2v2H9zm3 0h2v2h-2z"/></svg>"#;
41const BATTERY: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M2 5h10a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm0 1v4h10V6zm12 1h1v2h-1zM3 7h6v2H3z"/></svg>"#;
42const WIFI: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M8 12.5a1 1 0 1 1 0 2 1 1 0 0 1 0-2zM3.5 9.5a6 6 0 0 1 9 0l-1 1a4.5 4.5 0 0 0-7 0zM1.5 7a9 9 0 0 1 13 0l-1 1a7.5 7.5 0 0 0-11 0z"/></svg>"#;
43const SIGNAL: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M2 12h2v2H2zm3-2h2v4H5zm3-3h2v7H8zm3-4h2v11h-2z"/></svg>"#;
44const BLUETOOTH: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M7 1.5 11 5.5 8 8l3 2.5-4 4V9.5L4 12 3 11l4-3-4-3 1-1 3 2.5V1.5zm1.5 2.4v3.2L10 5.5zm0 5.7v3.2l1.5-1.6z"/></svg>"#;
45const SUN: &[u8] = LIGHT;
46const MOON: &[u8] = DARK;
47const VOL: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M8 2.5v11l-3.5-3H2v-5h2.5zm2.8 1.6c1.3 1 2.2 2.5 2.2 3.9s-.9 2.9-2.2 3.9l-.8-1.2c1-.7 1.5-1.7 1.5-2.7s-.5-2-1.5-2.7zm-1.4 2c.7.5 1.1 1.2 1.1 1.9 0 .7-.4 1.4-1.1 1.9l-.7-1.1c.4-.3.6-.6.6-.8 0-.2-.2-.5-.6-.8z"/></svg>"#;
48const VOL_MUTE: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M8 2.5v11l-3.5-3H2v-5h2.5zm3 3 1.5 1.5L14 5.5 15 6.5 13.5 8 15 9.5 14 10.5l-1.5-1.5L11 10.5 10 9.5 11.5 8 10 6.5z"/></svg>"#;
49const PLAY: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M4 2.5v11L13 8z"/></svg>"#;
50const PAUSE: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M4 3h3v10H4zm5 0h3v10H9z"/></svg>"#;
51const PREV: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 3h2v10H3zm10 0v10L6 8z"/></svg>"#;
52const NEXT: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M11 3h2v10h-2zM3 3l7 5-7 5z"/></svg>"#;
53const SEND: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M2 2 14 8 2 14l2-6zm1.7 1.4L4.8 7H8l-3.5.8z"/></svg>"#;
54const BACK: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M10 3 5 8l5 5 1.2-1.2L7.4 8l3.8-3.8z"/></svg>"#;
55const MORE: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 8a1.25 1.25 0 1 1 2.5 0A1.25 1.25 0 0 1 3 8zm3.75 0a1.25 1.25 0 1 1 2.5 0 1.25 1.25 0 0 1-2.5 0zm3.75 0a1.25 1.25 0 1 1 2.5 0 1.25 1.25 0 0 1-2.5 0z"/></svg>"#;
56const REFRESH: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M8 2a6 6 0 0 1 5.5 3.6L15 3.5V8h-4.5l1.6-1.5A4.5 4.5 0 0 0 3.5 8H2A6 6 0 0 1 8 2zm0 12a6 6 0 0 1-5.5-3.6L1 12.5V8h4.5L3.9 9.5A4.5 4.5 0 0 0 12.5 8H14A6 6 0 0 1 8 14z"/></svg>"#;
57const IMAGE: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 2.5h10A1.5 1.5 0 0 1 14.5 4v8a1.5 1.5 0 0 1-1.5 1.5H3A1.5 1.5 0 0 1 1.5 12V4A1.5 1.5 0 0 1 3 2.5zm0 1.5v6.5l3-3 2 2 3-3 2 2V4zM5 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg>"#;
58const VIDEO: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M2 4h8a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1zm10 1.5 3-1.5v8l-3-1.5z"/></svg>"#;
59const ZIP: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3 1.5h6L13 5v9.5H3zm6 0V5h4M6 3h1v1H6zm1 1h1v1H7zm-1 1h1v1H6zm1 1h1v1H7zm-1 1h1v1H6zm0 2h2v3H6z"/></svg>"#;
60const PIN: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M10.5 1.5 14.5 5.5 13 7l-1-1-3.5 3.5 1 4-1 1-3-3-2.5 2.5L2 13.5l2.5-2.5-3-3 1-1 4 1L10 4.5l-1-1z"/></svg>"#;
61const LOGO: &[u8] = br#"<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M5 1h6a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm1 2v9h4V3zM8 13a.75.75 0 1 1 0 1.5A.75.75 0 0 1 8 13z"/></svg>"#;
62
63#[derive(Default)]
65pub struct FluentAssets {
66 fallback: Option<Arc<dyn AssetSource>>,
67}
68
69impl FluentAssets {
70 pub fn new(fallback: Option<Arc<dyn AssetSource>>) -> Self {
71 Self { fallback }
72 }
73
74 fn builtin(path: &str) -> Option<&'static [u8]> {
75 Some(match path {
76 "icons/add.svg" | "icons/folder_add.svg" => ADD,
77 "icons/arrow_forward.svg" => ARROW_FORWARD,
78 "icons/checkmark.svg" | "icons/check.svg" => CHECKMARK,
79 "icons/chevron_down.svg" => CHEVRON_DOWN,
80 "icons/chevron_right.svg" | "icons/chevron.svg" => CHEVRON_RIGHT,
81 "icons/chevron_up.svg" => CHEVRON_UP,
82 "icons/clear.svg" | "icons/eraser.svg" => CLEAR,
83 "icons/copy.svg" => COPY,
84 "icons/cut.svg" => CUT,
85 "icons/dismiss.svg"
86 | "icons/delete.svg"
87 | "icons/plug_disconnected.svg"
88 | "icons/close.svg" => DISMISS,
89 "icons/document.svg" | "icons/file.svg" | "icons/doc.svg" => DOCUMENT,
90 "icons/download.svg" | "icons/download_arrow.svg" => DOWNLOAD,
91 "icons/upload.svg" | "icons/upload_arrow.svg" => UPLOAD,
92 "icons/folder.svg" | "icons/folder_open.svg" | "icons/folder_sync.svg" => FOLDER,
93 "icons/grid.svg" => GRID,
94 "icons/light_theme.svg" | "icons/sun.svg" => SUN,
95 "icons/dark_theme.svg" | "icons/moon.svg" => MOON,
96 "icons/list_tree.svg" => LIST_TREE,
97 "icons/minimize.svg" => MINIMIZE,
98 "icons/maximize.svg" | "icons/full_screen.svg" | "icons/fit_width.svg" => MAXIMIZE,
99 "icons/panel_open.svg" => PANEL_OPEN,
100 "icons/paste.svg" | "icons/clipboard_paste.svg" | "icons/clipboard_sync.svg" => PASTE,
101 "icons/plug.svg" | "icons/connect.svg" | "icons/network.svg" => PLUG,
102 "icons/search.svg" | "icons/find_replace.svg" => SEARCH,
103 "icons/settings.svg" => SETTINGS,
104 "icons/spinner.svg" => SPINNER,
105 "icons/home.svg" => HOME,
107 "icons/mirror.svg" => MIRROR,
108 "icons/bell.svg" => BELL,
109 "icons/bell_off.svg" => BELL_OFF,
110 "icons/chat.svg" => CHAT,
111 "icons/phone.svg" => PHONE,
112 "icons/qr.svg" => QR,
113 "icons/battery.svg" => BATTERY,
114 "icons/wifi.svg" => WIFI,
115 "icons/signal.svg" => SIGNAL,
116 "icons/bluetooth.svg" => BLUETOOTH,
117 "icons/vol.svg" | "icons/volume.svg" | "icons/speaker.svg" => VOL,
118 "icons/vol_mute.svg" | "icons/volume_mute.svg" | "icons/speaker_mute.svg" => VOL_MUTE,
119 "icons/play.svg" => PLAY,
120 "icons/pause.svg" => PAUSE,
121 "icons/prev.svg" | "icons/previous.svg" => PREV,
122 "icons/next.svg" => NEXT,
123 "icons/send.svg" => SEND,
124 "icons/back.svg" | "icons/chevron_left.svg" => BACK,
125 "icons/more.svg" => MORE,
126 "icons/refresh.svg" => REFRESH,
127 "icons/image.svg" => IMAGE,
128 "icons/video.svg" => VIDEO,
129 "icons/zip.svg" => ZIP,
130 "icons/pin.svg" => PIN,
131 "icons/logo.svg" => LOGO,
132 _ => return None,
133 })
134 }
135
136 fn generic_icon(path: &str) -> Option<&'static [u8]> {
137 (path.starts_with("icons/") && path.ends_with(".svg")).then_some(GENERIC)
138 }
139}
140
141impl AssetSource for FluentAssets {
142 fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
143 if let Some(bytes) = Self::builtin(path) {
144 return Ok(Some(Cow::Borrowed(bytes)));
145 }
146 if let Some(fallback) = &self.fallback {
147 if let Some(bytes) = fallback.load(path)? {
148 return Ok(Some(bytes));
149 }
150 }
151 if let Some(bytes) = Self::generic_icon(path) {
152 return Ok(Some(Cow::Borrowed(bytes)));
153 }
154 Ok(None)
155 }
156
157 fn list(&self, path: &str) -> Result<Vec<SharedString>> {
158 if let Some(fallback) = &self.fallback {
159 return fallback.list(path);
160 }
161 let names = if path == "icons" {
162 vec![
163 "add.svg",
164 "arrow_forward.svg",
165 "checkmark.svg",
166 "chevron_down.svg",
167 "chevron_right.svg",
168 "chevron_up.svg",
169 "clear.svg",
170 "copy.svg",
171 "cut.svg",
172 "dark_theme.svg",
173 "dismiss.svg",
174 "folder_open.svg",
175 "grid.svg",
176 "light_theme.svg",
177 "list_tree.svg",
178 "maximize.svg",
179 "minimize.svg",
180 "panel_open.svg",
181 "paste.svg",
182 "plug.svg",
183 "search.svg",
184 "settings.svg",
185 "spinner.svg",
186 ]
187 } else {
188 vec![]
189 };
190 Ok(names.into_iter().map(SharedString::from).collect())
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 const CUSTOM_ICON: &[u8] = br#"<svg viewBox="0 0 1 1"><path d="M0 0h1v1H0z"/></svg>"#;
199
200 struct CustomAssets;
201
202 impl AssetSource for CustomAssets {
203 fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
204 Ok((path == "icons/custom.svg").then_some(Cow::Borrowed(CUSTOM_ICON)))
205 }
206
207 fn list(&self, _path: &str) -> Result<Vec<SharedString>> {
208 Ok(Vec::new())
209 }
210 }
211
212 #[test]
213 fn app_icon_assets_are_loaded_before_generic_icon_fallback() {
214 let assets = FluentAssets::new(Some(Arc::new(CustomAssets)));
215 let loaded = assets.load("icons/custom.svg").unwrap().unwrap();
216
217 assert_eq!(&*loaded, CUSTOM_ICON);
218 }
219
220 #[test]
221 fn unknown_icons_still_use_generic_fallback() {
222 let assets = FluentAssets::new(None);
223 let loaded = assets.load("icons/missing.svg").unwrap().unwrap();
224
225 assert_eq!(&*loaded, GENERIC);
226 }
227
228 #[test]
229 fn chevron_right_has_a_specific_builtin_icon() {
230 let assets = FluentAssets::new(None);
231 let loaded = assets.load("icons/chevron_right.svg").unwrap().unwrap();
232
233 assert_eq!(&*loaded, CHEVRON_RIGHT);
234 assert_ne!(&*loaded, GENERIC);
235 }
236}