feat(dev): run dev alongside release with shared app data (#866)

* feat(dev): run dev alongside release with shared app data

Skip tauri-plugin-single-instance in debug builds so `tauri dev` can run
while an installed release instance is open. Keep the same bundle identifier
and data directory; label the dev window "Psysonic (Dev)".

* fix(dev): gate on_second_instance behind release cfg

Avoid dead_code warning in debug builds where single-instance is skipped.

* feat(dev): red sidebar brand and monochrome titlebar chrome

Tag the document in Vite dev and style the logo header with a red
background plus gray window controls so dev is obvious at a glance.

* feat(dev): skip OS hotkeys and add mobile DEV markers

Debug builds no longer register global shortcuts, MPRIS, or Windows
taskbar media controls so release keeps system input when both run.
Flip the tray icon horizontally in dev and show a fixed DEV badge on
narrow layouts.

* docs(changelog): note PR #866 parallel dev alongside release

* fix(dev): satisfy clippy needless_return in debug-only paths
This commit is contained in:
cucadmuh
2026-05-24 23:39:57 +03:00
committed by GitHub
parent de6462cbd2
commit 820f71c421
9 changed files with 264 additions and 55 deletions
+74 -2
View File
@@ -10,6 +10,59 @@ use crate::tray_runtime::{
};
use super::super::ui::{PAUSE_RENDERING_JS, RESUME_RENDERING_JS};
use tauri::image::Image;
/// Debug builds: mirror the default app icon horizontally so the tray differs from release.
fn app_tray_icon(app: &tauri::AppHandle) -> Image<'static> {
let icon = app.default_window_icon().expect("default window icon");
#[cfg(debug_assertions)]
{
flip_image_horizontal(icon)
}
#[cfg(not(debug_assertions))]
{
icon.clone().to_owned()
}
}
#[cfg(debug_assertions)]
fn flip_image_horizontal(icon: &Image<'_>) -> Image<'static> {
let width = icon.width();
let height = icon.height();
let mut rgba = icon.rgba().to_vec();
flip_rgba_horizontal(&mut rgba, width, height);
Image::new_owned(rgba, width, height)
}
#[cfg(debug_assertions)]
fn flip_rgba_horizontal(rgba: &mut [u8], width: u32, height: u32) {
let w = width as usize;
let h = height as usize;
if w == 0 || h == 0 || rgba.len() < w * h * 4 {
return;
}
for y in 0..h {
let row = y * w * 4;
for x in 0..w / 2 {
let l = row + x * 4;
let r = row + (w - 1 - x) * 4;
let left = [
rgba[l],
rgba[l + 1],
rgba[l + 2],
rgba[l + 3],
];
let right = [
rgba[r],
rgba[r + 1],
rgba[r + 2],
rgba[r + 3],
];
rgba[l..l + 4].copy_from_slice(&right);
rgba[r..r + 4].copy_from_slice(&left);
}
}
}
pub(crate) fn build_tray_icon(app: &tauri::AppHandle) -> tauri::Result<TrayIcon> {
let labels = app
@@ -92,7 +145,7 @@ pub(crate) fn build_tray_icon(app: &tauri::AppHandle) -> tauri::Result<TrayIcon>
#[cfg(target_os = "windows")]
let tray_builder = TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.icon(app_tray_icon(app))
.menu(&menu)
.tooltip(&tooltip_with_icon)
// tray-icon defaults to opening the context menu on every WM_LBUTTONUP when this is true.
@@ -102,7 +155,7 @@ pub(crate) fn build_tray_icon(app: &tauri::AppHandle) -> tauri::Result<TrayIcon>
.show_menu_on_left_click(false);
#[cfg(not(target_os = "windows"))]
let tray_builder = TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.icon(app_tray_icon(app))
.menu(&menu)
.tooltip(&cached_tooltip);
@@ -423,3 +476,22 @@ pub(crate) fn is_tiling_wm() -> bool {
pub(crate) fn is_tiling_wm_cmd() -> bool {
is_tiling_wm()
}
#[cfg(all(test, debug_assertions))]
mod tests {
use super::*;
#[test]
fn flip_rgba_horizontal_mirrors_pixels() {
// 3×1: A B C → C B A
let mut rgba = vec![
1, 0, 0, 255, // A
2, 0, 0, 255, // B
3, 0, 0, 255, // C
];
flip_rgba_horizontal(&mut rgba, 3, 1);
assert_eq!(rgba[0], 3);
assert_eq!(rgba[4], 2);
assert_eq!(rgba[8], 1);
}
}