gpui_macos/
native_assets.rs1use gpui_kit::{AssetSource, Result, SharedString};
3use std::borrow::Cow;
4
5#[derive(Clone, Copy, Debug, Default)]
12pub struct NativeAssets<A> {
13 fallback: A,
14}
15
16impl<A> NativeAssets<A> {
17 pub fn new(fallback: A) -> Self {
18 Self { fallback }
19 }
20}
21
22impl<A: AssetSource> AssetSource for NativeAssets<A> {
23 fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
24 #[cfg(target_os = "macos")]
25 if let Some(svg) = crate::native_symbols::svg_for_path(path) {
26 return Ok(Some(Cow::Owned(svg.as_ref().to_vec())));
27 }
28 self.fallback.load(path)
29 }
30
31 fn list(&self, path: &str) -> Result<Vec<SharedString>> {
32 self.fallback.list(path)
33 }
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 struct CustomAssets;
41 impl AssetSource for CustomAssets {
42 fn load(&self, _: &str) -> Result<Option<Cow<'static, [u8]>>> {
43 Ok(Some(Cow::Borrowed(b"custom asset bytes")))
44 }
45 fn list(&self, _: &str) -> Result<Vec<SharedString>> {
46 Ok(vec!["custom.svg".into()])
47 }
48 }
49
50 #[test]
51 fn custom_assets_and_listing_pass_through() {
52 let assets = NativeAssets::new(CustomAssets);
53 assert_eq!(
54 assets.load("custom.svg").unwrap().unwrap().as_ref(),
55 b"custom asset bytes"
56 );
57 assert_eq!(
58 assets.list("").unwrap(),
59 vec![SharedString::from("custom.svg")]
60 );
61 }
62
63 #[test]
64 fn fallback_artwork_is_embedded_in_debug_application_bundles() {
65 let bytes = gpui_kit::assets::AllAssets
66 .load("icons/star.svg")
67 .expect("bundled fallback icon loads")
68 .expect("bundled fallback icon exists");
69 assert!(
70 matches!(bytes, Cow::Borrowed(_)),
71 "App bundles must load embedded bytes without opening checkout files"
72 );
73 assert!(String::from_utf8_lossy(&bytes).contains("<svg"));
74 }
75
76 #[cfg(target_os = "macos")]
77 #[test]
78 fn gpui_svg_renderer_accepts_native_symbols_with_embedded_alpha() {
79 use gpui_kit::{DevicePixels, SvgRenderer, SvgSize, size};
80 use std::sync::Arc;
81 let assets = NativeAssets::new(gpui_kit::assets::AllAssets);
82 let bytes = assets.load("icons/check.svg").unwrap().unwrap();
83 assert!(String::from_utf8_lossy(&bytes).contains("data:image/png;base64,"));
84 let renderer = SvgRenderer::new(Arc::new(assets));
85 let parsed = renderer
86 .parse_svg(&bytes)
87 .expect("embedded SF Symbol PNG parses");
88 let image = renderer
89 .render_parsed(
90 &parsed,
91 SvgSize::ExactSize(size(DevicePixels(128), DevicePixels(128))),
92 )
93 .expect("embedded SF Symbol PNG rasterizes");
94 let pixels = image.as_bytes(0).unwrap();
95 assert_eq!(pixels.len(), 128 * 128 * 4);
96 assert!(pixels.as_chunks::<4>().0.iter().any(|pixel| pixel[3] > 0));
97 assert!(pixels.as_chunks::<4>().0.iter().any(|pixel| pixel[3] == 0));
98 let native = crate::native_symbols::image(
99 crate::IconName::Check,
100 16.,
101 8.,
102 gpui_kit::FontWeight::NORMAL,
103 gpui_kit::rgb(0xffffff).into(),
104 )
105 .unwrap();
106 let native_alpha = native
107 .as_bytes(0)
108 .unwrap()
109 .as_chunks::<4>()
110 .0
111 .iter()
112 .map(|pixel| pixel[3]);
113 let rendered_alpha = pixels.as_chunks::<4>().0.iter().map(|pixel| pixel[3]);
114 assert!(
115 native_alpha.eq(rendered_alpha),
116 "the Kit SVG pipeline preserves native symbol coverage exactly"
117 );
118 }
119}