Cross-compile groundwork: make the tree build for Windows (3.1)

`cargo check --target x86_64-pc-windows-gnu` is now green (only the
upstream egui f32:From<f64> warnings). The Linux-only desktop deps are
target-gated so the rest of the stack — which already cross-compiles —
can build off Linux:

- Cargo.toml: ksni and rfd's xdg-portal backend move to
  [target.'cfg(target_os = "linux")'.dependencies]; non-Linux gets rfd
  with its native Win32/AppKit backend (default features).
- tray.rs: cfg-split. The ksni/SNI implementation stays Linux-only; other
  OSes get a no-op `start() -> None`, i.e. windowed-only (identical to the
  Linux no-SNI-host path). TrayEvent/TrayHandle stay cross-platform.
- plex.rs: add a Windows `symlink_file` arm to make_symlink (also fixes an
  unused-variable warning on non-unix).

The rest was already portable (disk_space/statvfs, mpv IPC UnixStream,
chmod/PermissionsExt guards all cfg(unix)). macOS is Unix so those apply
there unchanged. Still not a *shipped* binary: needs a real per-OS tray
backend and a linking CI matrix. ROADMAP 3.1 + CLAUDE.md updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Luna 2026-06-10 04:22:18 -07:00
parent ec8cf6f934
commit d797f2a698
6 changed files with 80 additions and 18 deletions

View file

@ -21,7 +21,7 @@
//! the tray entirely optional — if `TrayController::start` fails the app
//! continues normally.
use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::mpsc::Receiver;
/// Menu events emitted by the tray. The main app drains these in
/// [`eframe::App::update`] and translates them into viewport commands or
@ -49,8 +49,9 @@ pub struct TrayHandle {
/// by ksni when the SNI host needs a property; menu activations call the
/// boxed closures we store in `MenuItem::activate`, which forward into
/// the channel back to the main thread.
#[cfg(target_os = "linux")]
struct TrayModel {
tx: Sender<TrayEvent>,
tx: std::sync::mpsc::Sender<TrayEvent>,
/// PNG bytes for the icon, decoded once at construction. ksni asks
/// for raw ARGB so we keep both forms.
icon_argb: Vec<u8>,
@ -58,6 +59,7 @@ struct TrayModel {
icon_h: i32,
}
#[cfg(target_os = "linux")]
impl ksni::Tray for TrayModel {
fn id(&self) -> String {
"yt-offline".into()
@ -127,6 +129,7 @@ impl ksni::Tray for TrayModel {
/// Returns `None` if the runtime / channel can't be set up. A `Some(_)`
/// return does *not* guarantee the icon is actually visible — that depends
/// on whether a StatusNotifier host is registered on the user's desktop.
#[cfg(target_os = "linux")]
pub fn start(icon_png_bytes: &[u8]) -> Option<TrayHandle> {
// Decode the PNG eagerly so we fail fast if the embedded asset is bad.
let img = image::load_from_memory(icon_png_bytes).ok()?;
@ -141,7 +144,7 @@ pub fn start(icon_png_bytes: &[u8]) -> Option<TrayHandle> {
argb.push(px[2]);
}
let (tx, rx) = mpsc::channel();
let (tx, rx) = std::sync::mpsc::channel();
let model = TrayModel {
tx,
icon_argb: argb,
@ -175,3 +178,13 @@ pub fn start(icon_png_bytes: &[u8]) -> Option<TrayHandle> {
Some(TrayHandle { events: rx })
}
/// Non-Linux stub: there's no StatusNotifierItem tray backend on Windows or
/// macOS yet (ksni is Linux/SNI-only), so the tray is simply absent and the
/// app runs windowed-only. A real implementation would need a per-OS backend
/// (e.g. `tray-icon`) behind this same signature. Returning `None` makes
/// `App` behave exactly as it does on Linux when no SNI host is registered.
#[cfg(not(target_os = "linux"))]
pub fn start(_icon_png_bytes: &[u8]) -> Option<TrayHandle> {
None
}