- PR1: gamescope detection at boot — X11 backend, scale 1, kiosk
fullscreen; tray + miniplayer suppressed (click-offset fix).
- PR2: full keyboard navigation for the kiosk (3 zones, focus ring,
scroll-into-view; typing never navigates).
- PR3: native gamepad via gilrs — D-pad/stick/A/B to synthetic Slint
keys with hysteresis, repeat and debounce; B suppresses Escape while
a text field is focused (closes the Steam OSK).
- PR4: Steam OSK via the steam://open/keyboard deeplink on text focus.
- PR5: scripts/qbz-add-to-steam (stdlib-only, 19/19 unit tests) with
artwork assets + pure-gamepad controller layout.
Spec: qbz-nix-docs/steam-deck/STEAM_DECK_GAME_MODE_PLAN.md
km
Docker/cross build of the slint-free daemon for the Pi streamer
(~90 s incremental, dist/qbzd-aarch64-linux); native path included for
aarch64 hosts.
km
Learned per-tier ETA from the last successful build, a live elapsed/ETA/percent ticker while codegen is silent, and start/finish banners. Safe under zsh/oh-my-zsh (runs under bash, newline-terminated, TTY-gated colors). NO_TICKER=1 silences it.
cc
Keeps only the most-recently-modified incremental-compilation dir per crate under crates/target and prunes the rest (slint-run.sh's per-RAM-tier RUSTFLAGS spawn a new hash dir each time; Cargo never GCs them). Release builds do not use incremental, so unaffected.
cc
Run unit/doc tests for the shipping crates workspace on PRs without
compiling qbz-ui. Fix three green-blockers: local playlist schema for
tests, Track.work fixture, and a stale ListenBrainz doctest.
The file was an untracked local script predating this session; a
directory-level git add pulled it into the #549 commit unintentionally.
Back to untracked, file kept on disk.
cc
Users on CPUs without AES-NI (Nehalem i7-860, pre-2010 Intel Macs) or
without SSSE3 (AMD Phenom II) crashed at launch with SIGILL. The
global `-C target-feature=+aes,+ssse3` was kept after the #353/#343
x86-64-v3 incident on the belief that the aes crate's cpufeatures
runtime check would protect CPUs without AES-NI — but a compile-time
target feature makes cpufeatures compile its check to a constant, so
the AES-NI intrinsics ran unconditionally (credentials AES-GCM decrypt
at startup), and +ssse3 let LLVM autovectorize anywhere, breaking
Phenom II. Measured on the qbz-cmaf hot path (Ctr128BE<Aes128>
apply_keystream, 256 MB, release): 2145 MB/s compiled-in vs 2162 MB/s
runtime-dispatched, identical output — the flags bought nothing on
capable CPUs. Removed from .cargo/config.toml, all dev/build scripts
and the release/build workflows; old CPUs now get the software AES
fallback instead of a crash.
Fixes#549
cc
Root-caused with an ftrace signal trap: earlyoom (RAM-only trigger,
10% of 31 GB) SIGTERM'd the qbz_ui rustc while the cgroup sat under
its cap — the 3.5 GB margin ignored that the desktop grows during an
hour-long build, and global MemAvailable cratered to 3.1 GB. Anchor
memory.high at avail minus 9 GB so the build throttles into swap
long before the global floor approaches the trigger.
cc
--memory-swap=26g OOM-killed the ~30-33 GB qbz_ui rustc. Bump to 24g RAM /
48g total — above the demand, still well under the host's 30G+46G so a
runaway is caught without freezing the box.
cc
qbz-ui's Slint build imports the lyrics fonts from the repo root
(../../../../static/fonts/*.ttf), which is outside the crates/ workspace
cross mounts — so the cross build couldn't find them. Mount static/ at its
host path via CROSS_CONTAINER_OPTS.
cc
Native (on an ARM runner: apt deps + cargo build -p qbz) and cross (from
x86-64 via cross + Cross.toml arm64 sysroot) paths, auto-selected by host
arch. Documents the ~30 GB qbz_ui RAM wall (8 GB Macs / 4 GB Pis cannot build
natively). Cross path is best-effort/untested pending a first run.
cc
Slint cannot split the .slint compilation across crates, but a Slint LIBRARY
crate exposes its generated Rust types to dependent crates (verified with a
minimal proof). So:
- NEW crate `qbz-ui` (lib): owns the whole .slint tree + build.rs + bundled
translations; `slint::include_modules!()` + the generated types (AppWindow,
MiniPlayerWindow, all globals/structs) are re-exported. Translation domain is
now `qbz-ui` (the .po files renamed accordingly).
- `qbz-slint` (bin) -> renamed to `qbz` (package + `[[bin]] name = "qbz"`),
reclaiming the binary name from the retired Tauri app. It depends on `qbz-ui`
and does `pub use qbz_ui::*;` so existing `crate::AppWindow` / `crate::*State`
references resolve unchanged.
This splits the single ~20 GB rustc into two crates (UI/codegen vs the 75k LOC
of logic) — the foundation for lower build peak, incremental caching, and the
upcoming per-feature crate extraction.
Also fixed for the move: qbz-i18n bundled-.po include paths, the font/WGSL
`include_bytes!`/`include_str!` paths (-> ../../qbz-ui/ui/), workspace members,
dev scripts (`-p qbz`), and the CI workflows. `cargo check -p qbz` is green.
cc
The fixed threads=16/opt=3 release build SIGTERM'd at codegen whenever the
desktop held ~8-11 GB (30 GB box, no hibernation, hard-freezes on OOM). Now
scales rustc frontend threads + codegen-units + opt-level to MemAvailable
(FAST >=26G / SAFE 14-26G / MIN <14G), runs under cargo-capped with the cap
matched to free RAM, and adds FAST/THREADS/CODEGEN_UNITS/OPT/CAPPED/NORUN env
overrides. Verified: full release build completed at SAFE tier (threads=2
cgu=256 opt=3, capped ~19.7G) in 55m without freezing.
cc
Builds in release then execs crates/target/release/qbz-slint directly instead of
via 'cargo run', so process monitors show it as 'qbz-slint' (a cargo-run child
inherits CARGO_* env and shows as 'cargo'), cleanly separate from the build
processes. Same release flags as slint-dev.sh.
cc
(cherry picked from commit 41269aabbe)
slint-dev.sh / slint-dev-fast.sh assume a Linux toolchain (mold linker,
x86 AES-NI features). On macOS clang rejects -fuse-ld=mold and the build
fails at the first link. This adds slint-dev-mac.sh: same nightly parallel
frontend and release-by-default intent, with --fast for a quick debug
build, the AES-NI/SSSE3 features scoped to x86_64 only, and the default
Apple linker (ld-prime). Verified on Apple Silicon: builds, runs, plays audio.
Add scripts/slint-dev-fast.sh: a DEBUG (non-release) build for quick visual
iteration — opt-level 0 so the giant Slint-generated module skips the heavy
LLVM passes, plus the mold linker, no debuginfo, and the nightly parallel
frontend. It builds into target/debug, separate from the release cache.
slint-dev.sh (release, kept for perf measurement) gains the mold linker and
bumps the frontend threads 8 -> 16 — both only cut compile time, the optimised
binary is unchanged.
cc
slint-dev.sh runs qbz-slint via 'cargo +nightly run --release -Z threads', splitting the serial compile of the Slint-generated module across cores while producing an identical optimized release binary (no cranelift). RUSTFLAGS re-lists the AES-NI features from .cargo/config.toml since RUSTFLAGS overrides rather than merges it.
cc
Sister script to dev-cpu-mode.sh. Where the CPU script forces pure
software rendering across every layer that can sneak the GPU back in,
this one pins WebKit / GTK to the integrated GPU (Intel/AMD) so we can
test the "modest HW accel" tier the current detector ignores.
Specifically pins __EGL_VENDOR_LIBRARY_FILENAMES to the Mesa EGL JSON
so the NVIDIA EGL ICD doesn't win (it has the lower 10_* priority
prefix and WebKitGTK prefers EGL). Leaves WEBKIT_DISABLE_COMPOSITING_
MODE, WEBKIT_DISABLE_DMABUF_RENDERER, GSK_RENDERER, and LIBGL_ALWAYS_
SOFTWARE unset — the inverse of dev-cpu-mode — so real iGPU paint
paths exercise.
Header documents verification commands (nvidia-smi must not list
QBZ; intel_gpu_top / nvtop should show activity on Intel only).
Purpose is to inform a future refinement of graphicsState's detector:
binary hardware_accel_enabled doesn't differentiate "iGPU is fine for
balanced visuals" from "no GPU at all, force lite". Manual harness
first; detector smarts second once we know what actually works on
real iGPU paint.
cc
Sister script of dev-ultra-restore.sh. Runs `npm run tauri dev` with
the full degraded-rendering env applied so the SW path can be
exercised even when the persisted graphics_settings DB has HW accel
enabled. Useful for testing the modal mount cost, splash spinner
smoothness, and Discovery V2 paint cost under WebKitGTK without GPU.
Sets five env vars:
QBZ_HARDWARE_ACCEL=0 (QBZ's own nuclear opt-out)
WEBKIT_DISABLE_COMPOSITING_MODE=1
WEBKIT_DISABLE_DMABUF_RENDERER=1
GSK_RENDERER=cairo (GTK4 SW renderer)
LIBGL_ALWAYS_SOFTWARE=1 (Mesa llvmpipe)
Flags:
--no-kill skip stale-process cleanup
--print print the env vars and exit (for sourcing)
cc
Samples /proc/PID/stat directly (no pidstat dependency) for the qbz binary
and any WebKit child processes. Outputs mean/p50/p95/max per process plus
appends a CSV row to /tmp/qbz-cpu-measurements.csv for cross-run comparison.
Usage:
./scripts/measure-cpu-window.sh <label> [duration_seconds]
Built during the 2026-05-11 perf debug session to replace seat-of-the-pants
"does it feel faster?" with comparable numbers across changes.
cc
The script was created when commands_v2 was a single file. After the
refactor to a directory, the path was stale and the script errored on
missing file. Also switch from rg to grep so the check works in
environments without ripgrep installed.
cc
Completes the legacy root list by adding config::favorites_cache,
which is registered in lib.rs (e.g., is_track_favorite) but was
missing from the audit's known-roots list. No frontend currently
invokes it, so this is preventive — guards against future silent
misclassification as 'unknown'.
Found in code review of commit 3ea96fb5.
cc
The frontend invoke audit was capturing the wrong segment of legacy
handler registrations. The pattern 'commands::([A-Za-z0-9_]+)' grabbed
the submodule name (e.g. 'search', 'playback') instead of the function
leaf, so commands like 'commands::search::get_artist_albums' fell into
the unknown bucket even though they were registered.
Switch to a per-root regex of the form
'<root_path>(::<segment>)*::(<leaf>),' which captures the leaf
identifier regardless of how many submodule levels sit between the root
and the function. Also add four legacy module roots that were missing:
- updates (has_flatpak_welcome_been_shown, has_snap_welcome_been_shown)
- config::legal_settings (get_qobuz_tos_accepted)
- desktop_theme (detect_desktop_theme)
- flatpak (get_flatpak_help_text)
After this change the unknown bucket is empty and the 16 callsites that
were previously misclassified as 'unknown' now correctly show up as
'legacy', which is the accurate status pending future V2 migration.
cc
The previous regex only matched commands registered under commands_v2::,
missing 50+ legitimate v2_qconnect_* commands registered under
qconnect_service::. This produced 64 false-positive "missing_v2" callsites
that obscured real audit signal.
Generalize the registration regex to match any module prefix ending in
::v2_* or ::runtime_*, so future modules that follow the v2_ naming
convention are picked up automatically without script changes. The inner
alternation still excludes legacy commands, which never carry the v2_ or
runtime_ prefix on the function name.
After the fix, missing_v2_callsites drops from 64 to 6 (the remaining 6
are real frontend typos, not audit-script bugs); legacy_callsites stays
at 0; unknown_callsites stays at 16.
cc
When last_view pointed to a view with incomplete context (e.g.
album without selectedAlbum data), the Svelte CSS extraction
pipeline would corrupt and produce PostCSS "Unknown word" errors
that survived cache cleans and npm ci.
- Add isSessionRestoreSafeView() whitelist for context-free views
- Add getSessionFallbackView() for graceful degradation
- Centralize persist logic in getPersistedSessionViewState()
- Only persist views that have valid context data
- Add purchase-album to restore path
- Add dev:reset-last-view script to reset session.db
- Fix AlbumCreditsModal.svelte event handler typing
Add dev-ghost-doctor.sh (t-shadow check + runtime clean + restart) and
dev-ultra-restore.sh (deep clean with --full/--no-start/--check flags).
Improve check-no-t-shadow.sh patterns for broader t-variable detection.
Clean-dev-runtime now runs guardrail check automatically. Remove --force
from vite dev. Add npm script shortcuts for all new tools.
- Save user_auth_token after successful OAuth login
- On bootstrap, restore session via saved token if no email/password creds
- If token is expired, clear it so user sees login screen normally
- Clear OAuth token on v2_clear_saved_credentials (logout)
- Add login_with_token() to api client (reuses OAuth /user/login step)
Also rename t->translateStore in NowPlayingBar.svelte (ADR-001 fix from Codex)
and improve clean-dev-runtime.sh to kill all stale processes and ports
- Create dev-with-env.sh script to load .env before build
- Add npm run dev:tauri command that uses the script
- Document why .env must be loaded before compilation
- Explain compile-time vs runtime environment variables
The issue: option_env!() reads vars at compile time, but .env
loads at runtime. Solution: source .env before cargo build.
cp
- Playback state polling: 1000ms when paused, 250ms when playing
- Audio thread timeout: 500ms when idle, 100ms when playing
- Lyrics interval: self-check to auto-stop when conditions not met
- Add cleanup return to lyrics in +page.svelte
- Add build-and-run.sh and monitor-resources.sh scripts
- Add OPTIMIZATION_PLAN.md documentation