From 15c18e697cde4d8c69edfbeaf2accccd9edf374a Mon Sep 17 00:00:00 2001 From: kuddev Date: Wed, 16 Sep 2026 19:11:20 +0800 Subject: [PATCH] fix(ci): keep native icons within the existing Windows boundary --- nebula_app/src/app_icon.rs | 13 ++++++------- nebula_app/src/app_icon/{ => windows}/taskbar.rs | 12 ++++++------ .../src/app_icon/{ => windows}/taskbar/shortcuts.rs | 0 nebula_app/src/brand.rs | 1 - nebula_app/src/gpui_shell/mod.rs | 2 +- scripts/tests/test_pebrel_branding.py | 6 +++++- 6 files changed, 18 insertions(+), 16 deletions(-) rename nebula_app/src/app_icon/{ => windows}/taskbar.rs (94%) rename nebula_app/src/app_icon/{ => windows}/taskbar/shortcuts.rs (100%) diff --git a/nebula_app/src/app_icon.rs b/nebula_app/src/app_icon.rs index 4b4ab70..5d676d6 100644 --- a/nebula_app/src/app_icon.rs +++ b/nebula_app/src/app_icon.rs @@ -5,11 +5,6 @@ use std::sync::{Arc, Mutex, OnceLock}; use image::{ImageEncoder as _, RgbaImage}; use nebula_settings::AppIconName; -#[cfg(windows)] -mod taskbar; -#[cfg(windows)] -pub(crate) use taskbar::refresh_pinned; - pub const FRAME_SIZES: [u32; 14] = [16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 256]; const ATLAS_WIDTH: u32 = 512; const COVERAGE_PNG: &[u8] = include_bytes!("../../extra/logo/nebula-coverage.png"); @@ -124,6 +119,9 @@ pub fn preview(variant: AppIconName, requested: u32) -> Option> #[cfg(windows)] pub mod windows { + pub(super) mod taskbar; + pub(crate) use taskbar::refresh_pinned; + use std::cell::RefCell; use std::collections::HashMap; @@ -148,7 +146,7 @@ pub mod windows { } pub fn set_window(hwnd: HWND, variant: AppIconName) { - super::taskbar::set_window(hwnd, variant); + taskbar::set_window(hwnd, variant); let dpi = unsafe { GetDpiForWindow(hwnd) }.max(96); ICONS.with(|cache| { let mut cache = cache.borrow_mut(); @@ -291,7 +289,8 @@ mod tests { for iteration in 0..10 { for (variant_index, variant) in AppIconName::ALL.into_iter().enumerate() { windows::set_window(hwnd, variant); - let resource = taskbar::icon_location(hwnd).expect("taskbar shell property"); + let resource = + windows::taskbar::icon_location(hwnd).expect("taskbar shell property"); assert!(resource.contains(variant.settings_value())); assert!(resource.ends_with(".ico,0")); let handles = [ICON_SMALL, ICON_BIG] diff --git a/nebula_app/src/app_icon/taskbar.rs b/nebula_app/src/app_icon/windows/taskbar.rs similarity index 94% rename from nebula_app/src/app_icon/taskbar.rs rename to nebula_app/src/app_icon/windows/taskbar.rs index cda8909..b130c3a 100644 --- a/nebula_app/src/app_icon/taskbar.rs +++ b/nebula_app/src/app_icon/windows/taskbar.rs @@ -24,10 +24,10 @@ const RELAUNCH_NAME: PROPERTYKEY = PROPERTYKEY { fmtid: APP_USER_MODEL, pid: 4 } const APP_ID: PROPERTYKEY = PROPERTYKEY { fmtid: APP_USER_MODEL, pid: 5 }; fn ico(variant: AppIconName) -> std::io::Result> { - let frames = super::FRAME_SIZES + let frames = crate::app_icon::FRAME_SIZES .into_iter() .map(|size| { - super::png(variant, size) + crate::app_icon::png(variant, size) .map(|png| (size, png)) .ok_or_else(|| std::io::Error::other("Could not render application icon")) }) @@ -130,18 +130,18 @@ mod tests { assert_eq!(first, materialize(directory.path(), AppIconName::Titanium).unwrap()); assert_ne!(first, materialize(directory.path(), AppIconName::GraphiteViolet).unwrap()); let bytes = std::fs::read(first).unwrap(); - for size in super::super::FRAME_SIZES { - let png = super::super::tests::ico_png(&bytes, size); + for size in crate::app_icon::FRAME_SIZES { + let png = crate::app_icon::tests::ico_png(&bytes, size); assert_eq!( image::load_from_memory(png).unwrap().to_rgba8(), - super::super::rgba(AppIconName::Titanium, size).unwrap() + crate::app_icon::rgba(AppIconName::Titanium, size).unwrap() ); } } } #[cfg(test)] -pub(super) fn icon_location( +pub(in crate::app_icon) fn icon_location( hwnd: windows_sys::Win32::Foundation::HWND, ) -> windows::core::Result { let store: IPropertyStore = unsafe { SHGetPropertyStoreForWindow(HWND(hwnd))? }; diff --git a/nebula_app/src/app_icon/taskbar/shortcuts.rs b/nebula_app/src/app_icon/windows/taskbar/shortcuts.rs similarity index 100% rename from nebula_app/src/app_icon/taskbar/shortcuts.rs rename to nebula_app/src/app_icon/windows/taskbar/shortcuts.rs diff --git a/nebula_app/src/brand.rs b/nebula_app/src/brand.rs index 928c836..d99ec32 100644 --- a/nebula_app/src/brand.rs +++ b/nebula_app/src/brand.rs @@ -1,5 +1,4 @@ pub const NAME: &str = "Pebrel"; -#[cfg(windows)] pub const WINDOWS_APP_ID: &str = "com.pebrel.terminal"; pub const DESCRIPTION: &str = "Pebrel — a GPU-accelerated terminal emulator"; diff --git a/nebula_app/src/gpui_shell/mod.rs b/nebula_app/src/gpui_shell/mod.rs index 5350188..37aba9c 100644 --- a/nebula_app/src/gpui_shell/mod.rs +++ b/nebula_app/src/gpui_shell/mod.rs @@ -361,7 +361,7 @@ pub(crate) fn apply_app_icon(variant: nebula_settings::AppIconName, cx: &mut App } crate::tray::refresh_app_icon(); #[cfg(windows)] - crate::app_icon::refresh_pinned(variant); + crate::app_icon::windows::refresh_pinned(variant); #[cfg(windows)] cx.defer(|cx| { for handle in cx.windows() { diff --git a/scripts/tests/test_pebrel_branding.py b/scripts/tests/test_pebrel_branding.py index d238847..ce5c27e 100644 --- a/scripts/tests/test_pebrel_branding.py +++ b/scripts/tests/test_pebrel_branding.py @@ -89,7 +89,11 @@ class PebrelBrandingTests(unittest.TestCase): def test_notification_identity_uses_pebrel(self): source = self.source("nebula_app/src/platform/notifications.rs") - self.assertIn('AUMID: &str = "com.pebrel.terminal";', source) + brand = self.source("nebula_app/src/brand.rs") + taskbar = self.source("nebula_app/src/app_icon/windows/taskbar.rs") + self.assertIn('WINDOWS_APP_ID: &str = "com.pebrel.terminal";', brand) + self.assertIn('AUMID: &str = crate::brand::WINDOWS_APP_ID;', source) + self.assertIn('set_string(&store, &APP_ID, crate::brand::WINDOWS_APP_ID)', taskbar) self.assertIn('set_reg_sz(&subkey, "DisplayName", crate::brand::NAME)', source) def test_config_directory_uses_pebrel_and_migrates_legacy_data(self):