mini player + dark glass: eight shapes run by the hidden main as the one brain, dissolving covers and accents, drag from anywhere, and the space between the panes turned to real acrylic - with the webview flags that keep rest frames honest

This commit is contained in:
KeelrLeek committed 2026-08-26 07:56:00 -05:00
1 parent 9b84b30aef
commit dc7ea16b22
33 files changed
+2432 -5

No files matched your search

+4 -1
View File
@@ -3,7 +3,8 @@
"identifier": "default",
"description": "enables the default permissions",
"windows": [
"main"
"main",
"mini"
],
"permissions": [
"core:default",
@@ -16,6 +17,8 @@
"core:window:allow-is-maximized",
"core:window:allow-show",
"core:window:allow-set-focus",
"core:window:allow-set-size",
"core:window:allow-set-always-on-top",
"dialog:allow-open"
]
}
+112 -1
View File
@@ -1,4 +1,4 @@
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use tauri::{Emitter, Manager, State};
use soulseek_client::{Client, Config, DownloadState, DownloadUpdate, Event};
@@ -78,6 +78,8 @@ pub struct Settings {
pub volume_normalization: bool,
/// Word-by-word lyric fill — experimental, default off.
pub word_timing: bool,
/// Dark-glass acrylic backdrop, default off.
pub glass_mode: bool,
}
#[tauri::command]
@@ -117,6 +119,7 @@ pub async fn get_settings(
download_source_prefs: cfg.download_source_prefs,
volume_normalization: config::volume_normalization(&cfg),
word_timing: config::word_timing(&cfg),
glass_mode: config::glass_mode(&cfg),
username: cfg.username,
})
}
@@ -145,6 +148,7 @@ pub async fn set_settings(
download_source_prefs: Option<config::SourcePrefs>,
volume_normalization: Option<bool>,
word_timing: Option<bool>,
glass_mode: Option<bool>,
) -> Result<(), String> {
let mut cfg = config::load(&app);
let library_folder_moved = download_dir
@@ -216,6 +220,9 @@ pub async fn set_settings(
if word_timing.is_some() {
cfg.word_timing = word_timing;
}
if glass_mode.is_some() {
cfg.glass_mode = glass_mode;
}
config::save(&app, &cfg);
// A new library folder is a new library. Re-base immediately rather than
@@ -4857,6 +4864,108 @@ pub fn player_control(
Ok(())
}
/// Enter mini mode: show the pre-built mini window (declared in
/// tauri.conf.json, created hidden at startup — runtime window creation
/// from a command half-initialises on Windows) and hide the main one. The
/// main window is HIDDEN, never closed: its frontend stays alive as the one
/// brain the mini remote-controls (see src/lib/miniBridge.svelte.js).
#[tauri::command]
pub async fn mini_open(app: tauri::AppHandle, width: f64, height: f64) -> Result<(), String> {
let mini = app
.get_webview_window("mini")
.ok_or_else(|| "mini window missing from config".to_string())?;
let _ = mini.set_size(tauri::LogicalSize::new(width, height));
mini.show().map_err(|e| e.to_string())?;
let _ = mini.set_focus();
if let Some(main) = app.get_webview_window("main") {
let _ = main.hide();
}
Ok(())
}
/// Leave mini mode: bring the full app back and tuck the mini away warm
/// (hidden, not closed — reopening is instant and its mirror stays hot).
#[tauri::command]
pub async fn mini_close(app: tauri::AppHandle) -> Result<(), String> {
if let Some(main) = app.get_webview_window("main") {
let _ = main.show();
let _ = main.set_focus();
}
if let Some(mini) = app.get_webview_window("mini") {
let _ = mini.hide();
}
Ok(())
}
/// The OS half of dark-glass mode: acrylic blur behind the calling window
/// (the window itself is transparent; the CSS half — :root.glass — thins the
/// window canvas so the blur shows through).
///
/// NOT tauri's Effect::Acrylic: on Win11 22H2+ that picks the DWMSBT system
/// backdrop, which Windows SUSPENDS whenever the maximized webview gets
/// promoted to a hardware overlay — every hover animation or compositor
/// scroll blinked the glass dark for a beat (Kyle, 2026-08-26). The legacy
/// accent-policy acrylic below is composed with the window itself and holds
/// through interaction.
#[tauri::command]
pub fn window_glass(window: tauri::WebviewWindow, on: bool) -> Result<(), String> {
// Clear any system backdrop a previous toggle/build may have applied.
let _ = window.set_effects(None::<tauri::utils::config::WindowEffectsConfig>);
let hwnd = window.hwnd().map_err(|e| e.to_string())?.0 as *mut std::ffi::c_void;
#[repr(C)]
struct AccentPolicy {
state: u32,
flags: u32,
gradient: u32, // 0xAABBGGRR
animation: u32,
}
#[repr(C)]
struct AttribData {
attrib: u32,
data: *mut std::ffi::c_void,
size: usize,
}
#[allow(non_snake_case)]
#[link(name = "kernel32")]
extern "system" {
fn GetModuleHandleA(name: *const u8) -> *mut std::ffi::c_void;
fn GetProcAddress(
module: *mut std::ffi::c_void,
name: *const u8,
) -> *mut std::ffi::c_void;
}
type Swca = unsafe extern "system" fn(*mut std::ffi::c_void, *mut AttribData) -> i32;
unsafe {
let user32 = GetModuleHandleA(b"user32.dll\0".as_ptr());
let f = if user32.is_null() {
std::ptr::null_mut()
} else {
GetProcAddress(user32, b"SetWindowCompositionAttribute\0".as_ptr())
};
if f.is_null() {
return Err("SetWindowCompositionAttribute unavailable".into());
}
let f: Swca = std::mem::transmute(f);
let mut policy = AccentPolicy {
state: if on { 4 } else { 0 }, // ACCENT_ENABLE_ACRYLICBLURBEHIND / ACCENT_DISABLED
flags: 0,
// A whisper of black in the OS layer (acrylic treats alpha 0 as
// "don't draw"); the real darkening is the CSS canvas tint.
gradient: if on { 0x2E00_0000 } else { 0 },
animation: 0,
};
let mut data = AttribData {
attrib: 0x13, // WCA_ACCENT_POLICY
data: &mut policy as *mut _ as _,
size: std::mem::size_of::<AccentPolicy>(),
};
f(hwnd, &mut data);
}
Ok(())
}
#[cfg(test)]
mod hero_processing_tests {
use super::*;
@@ -4937,3 +5046,5 @@ mod hero_processing_tests {
assert_eq!(trimmed.dimensions(), (256, 120));
}
}
+8
View File
@@ -140,6 +140,10 @@ pub struct AppConfig {
#[serde(default)]
pub volume_normalization: Option<bool>,
pub word_timing: Option<bool>,
/// Dark-glass mode: acrylic blur behind a transparent window, with the
/// base surfaces thinned in CSS so the desktop glows through. None → false.
#[serde(default)]
pub glass_mode: Option<bool>,
}
/// Default longest edge for embedded covers when the user hasn't chosen.
@@ -185,6 +189,10 @@ pub fn volume_normalization(cfg: &AppConfig) -> bool {
/// default: generated word spans still drift on dense or echo-heavy mixes,
/// and the solid line-level sync must be what users live with until the
/// feature earns its keep (Kyle, 2026-08-19).
pub fn glass_mode(cfg: &AppConfig) -> bool {
cfg.glass_mode.unwrap_or(false)
}
pub fn word_timing(cfg: &AppConfig) -> bool {
cfg.word_timing.unwrap_or(false)
}
+36
View File
@@ -27,6 +27,39 @@ mod ytdlp;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
// Two WebView2 presentation fixes, both delivered by env var because
// tauri.conf's additionalBrowserArgs never reaches the browser process
// (verified via its command line). Merge, don't clobber — dev launches
// carry the CDP port here. Both found 2026-08-26 via the dark-glass work:
//
// 1. NO delegated compositing: with it, DWM composes the webview's layers
// at rest and blends semi-transparent darks in the wrong colorspace —
// the app rested washed-out LIGHT and looked correct only while an
// animation forced Chromium to compose in-process.
// 2. Force the sRGB color profile: on an HDR display the MAXIMIZED
// window's rest path takes a different SDR-white tone mapping than
// the animated path — covers blew out near-white at rest and snapped
// correct on hover. One profile, one mapping, both paths agree.
#[cfg(windows)]
{
const KEY: &str = "WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS";
let mut args = std::env::var(KEY).unwrap_or_default();
let mut ensure = |flag: &str, key: &str| {
if !args.contains(key) {
if !args.is_empty() {
args.push(' ');
}
args.push_str(flag);
}
};
ensure(
"--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection,DelegatedCompositing",
"DelegatedCompositing",
);
ensure("--force-color-profile=srgb", "force-color-profile");
std::env::set_var(KEY, args);
}
tauri::Builder::default()
.manage(state::AppState::default())
.setup(|app| {
@@ -108,6 +141,9 @@ pub fn run() {
commands::unfollow_artist,
commands::home_artist_news,
commands::home_warm,
commands::mini_open,
commands::mini_close,
commands::window_glass,
playlists::list_playlists,
playlists::create_playlist,
playlists::rename_playlist,
+16 -1
View File
@@ -19,8 +19,23 @@
"height": 820,
"minWidth": 960,
"minHeight": 600,
"backgroundColor": "#121212",
"transparent": true,
"decorations": false
},
{
"label": "mini",
"title": "SoulStream Mini",
"url": "mini.html",
"width": 484,
"height": 96,
"visible": false,
"decorations": false,
"resizable": false,
"maximizable": false,
"minimizable": false,
"alwaysOnTop": true,
"shadow": true,
"backgroundColor": "#181818"
}
],
"security": {