Commit Graph
102 Commits
Author SHA1 Message Date
vicrodh 0b7cd6c5e3 feat(qbz-app): settings bundle engine with importer-side classification
Export/import CLI on qbzd; requirements 4 and 5 complete with zero desktop builds.

cc
2026-07-14 19:44:01 -06:00
vicrodh e700084f0b feat(qbzd): playback routes and transport CLI verbs with full advance ritual
cc
2026-07-14 16:35:21 -06:00
vicrodh 8534a02f0d test(qbz-app): pin duration==0 end-detection edge in playback driver
cc
2026-07-14 14:22:02 -06:00
vicrodh 297bbad326 feat(qbz-app): headless playback driver with pure plan_tick and thin IO shell
cc
2026-07-14 14:09:22 -06:00
vicrodh d911f43856 feat(qbzd): qbzd.toml with unknown-key warning, flock instance lock, daemon_prefs store
cc
2026-07-14 13:10:08 -06:00
vicrodh abb529e22b feat(discover): Most Played Albums — play-count store, hook, section id
Data foundation for the new Most Played Albums rail. New qbz-app store album_play_history (SQLite, mirrors play_history): one album_play_event row per track-start + an album_meta side table; top_albums(n)/all_albums() rank by COUNT(*) GROUP BY album_id, tie-broken by most-recent play. Hooked in playback::record_recent next to the recently/artist stores, same album identity. Registers the mostPlayedAlbums section id in discover_prefs (Home + For You, default off). The Qobuz API exposes no most-played endpoint (verified vs the inferred OpenAPI), so the ranking is local. 15 unit tests green (store 5 + prefs 10).

cc
2026-07-13 23:30:28 -06:00
Afonso Jorge Ramos c9b9aea118 fix(scrobble): skip tracks shorter than 30 seconds 2026-07-13 16:12:06 +02:00
Afonso Jorge Ramos 0ff3fc7f40 fix(scrobble): host delay gate in qbz-app for unit testing
Extract scrobble_delay_secs so cargo test -p qbz-app covers unknown
duration without compiling the Slint binary.
2026-07-13 00:08:18 +02:00
vicrodh 13fce17bd1 refactor(qbz-app): drop Tauri-only boot/graphics/settings modules
Remove boot_watchdog, graphics_detection, and settings::{download,legal,
library,image_cache} (only src-tauri consumed them). Split
graphics_autoconfig: keep Environment/detect_environment/detect_gpu_name
(used by the Slint diagnostics panel), drop the WebKit Recommendation half.

cc
2026-07-10 01:02:38 -06:00
vicrodh 338d20df9b fix(home): render all 13 configurator sections; own recent-albums history
Two Home bugs, one dropped port. #566: four configurator sections
(favoriteAlbums 'Library Albums', qobuzMixes, releaseWatch, topArtists)
were offered and persisted but had no HomeView render arm and no data
in load_home — enabling them produced an invisible row. All four were
genuine Tauri-Home features (DiscoveryView.svelte arms :632/:653/:660/
:666, loaded unconditionally), so this is the full port, not a trim:
own HomeState fields (separate lifecycle from ForYouState), shared
foryou fetch pipelines (favorite_album_cards / fetch_release_watch /
top_artist_cards extracted pub(crate), For You behavior untouched),
Home-scoped artwork targets, and HomeView arms mirroring ForYouView.
load_home joins the three fetches concurrently with the index — zero
added latency. A HOME_RENDERABLE guard next to descriptors_for is now
the single source of truth: an enabled id without a render arm can
never emit an invisible descriptor again.

#567: 'Recently Played Albums' starved because albums derived from the
24-track history (long albums collapse to ~4 distinct). recently.rs
now keeps a separate album history (dedupe by id at record time, own
24 cap) with additive migration from the legacy bare-array format;
tracks keep their cap; load()/load_albums() signatures unchanged.

Zero new msgids (all three titles reuse For You's). qbz-app tests
10/10; slintcheck green.

cc
2026-07-08 11:46:57 -06:00
vicrodh b6e56e3b47 feat(session): guest local-only profile — offline mode without a prior login
The 'Start offline' link was gated on has-previous-session and the
shell backstop refused to open without a recorded user, so a user who
never logged in (e.g. a purchases-only Qobuz account, #553) could not
use the app at all. The link is now always visible and the offline
shell falls back to the GUEST profile — user 0, the Tauri-era fallback
this port used to refuse — building a real per-user store set (local
library, mixtapes, prefs) under users/0. AppRuntime::activate_offline
already resolved user 0 and never persists it as the last-user marker,
so the guest never masquerades as a real session.

First real login takes possession: AppRuntime::activate renames the
users/0 data+cache dirs to the account's id when that account has no
profile on this machine yet (existing profiles always win — the guest
dirs stay parked, no merge). Best-effort, login never breaks on a
failed rename.

cc
2026-07-06 21:47:00 -06:00
vicrodh 3fbb166b3e feat(blacklist): add String-keyed album blacklist store in qbz-app
Parallel album-id axis alongside the u64 artist blacklist. New
album_blacklist TEXT-PK table in the same artist_blacklist.db (additive,
idempotent, no migration), an in-memory HashSet<String> mirror, and
is_album_blacklisted/add_album/remove_album/get_all_albums/album_count/
clear_all_albums. Shares the single enabled flag with the artist axis.
12 unit tests green (incl. shared-flag + axis-independence).

cc
2026-06-29 23:03:47 -06:00
vicrodh 99da4c381b feat(slint): pre-launch dev panel, queue controls, appearance persistence, reco cache settings
Sleep timer (queue footer): tokio monotonic deadline + 1Hz countdown that
fires core().pause() at expiry; presets + custom minutes via a popover.

Stop after this song: 4 QbzCore passthroughs + playback consume-before-advance
seam and the load-bearing gapless-prefetch gate, plus the queue marker/menu so
the Queue persists when playback halts.

qbz-log crate: TeeLogger wrapping env_logger (stderr + 5000-line ring +
~/.local/share/qbz/logs/qbz.log) with write-time secret redaction (regex +
live-secret registry) and a diagnostics bundle builder.

Dev panel logs: in-app viewer that shows content the moment it opens, advanced
controls behind a discreet [+], "Copy all" as the sole footer button, a copy
button on the upload URL, and a redacted public paste upload.

Diagnostics: qbz-app::diagnostics (extracted, src-tauri untouched) feeding a
7-section panel with the live active output device + sample rate/format (pactl)
and a complete redacted report for the upload.

Report an Issue: wires the header menu item to a 1:1 Tauri modal that opens the
GitHub bug-report template.

Appearance persistence: 6 toggles that were live-only now persist via ui_prefs
+ handler arm + startup seed (window-title-show, show-volume-steppers,
sidebar-playlist-collage, local-library-track-artwork, in-app-toasts,
theme-filter).

Recommendations cache (per-tab only): TTL select 24/36/48/72h + Refresh now in
ExternalRecoView; no change to the other Discover tabs.

Bump workspace version 0.1.0 -> 1.2.15 so app_version is correct in the
diagnostics report and the QConnect About string. i18n es/de/fr/pt for all new
strings.

cc
2026-06-29 13:19:06 -06:00
vicrodh 332fa430e5 feat(reco): Recommendations in all Discover flyouts + opt-out setting
Surface the new Recommendations tab everywhere Discover sub-sections are listed,
and make it opt-out.

- Flyouts: the sidebar section-nav flyout and both header nav flyouts (full text
  + compact icon) now list "Recommendations" alongside Home / Editor's Picks /
  For You. The Rust route handler already maps "discover-recommendations"
  (strip_prefix + select_tab + ensure_loaded), so no routing change was needed.
- Opt-out: a show_recommendations flag (default on) persisted in the per-user
  discover-prefs store (qbz_app), seeded into SettingsState on session start.
  Settings > Integrations gains a "Show Recommendations in Discover" toggle whose
  description nudges connecting Last.fm + ListenBrainz (MusicBrainz is used
  automatically to match releases). Off => the tab pill and every flyout entry
  disappear.
- Gating uses two mounted instances per flyout (one with the entry, one without)
  because Slint can't type-infer [NavMenuEntry] from a ternary of array literals;
  the HomeView pill uses a plain if.

cargo check -p qbz green.

cc
2026-06-27 11:57:46 -06:00
vicrodh 25ab1408e5 feat(reco): augment discovery known-artists with reco signal
Add RecoStore::get_known_artist_ids (artists played >threshold OR favorited;
unit-tested) and union it into the network-discovery 'skip artists I already
know' filter. play_history stays the source of the (id, name) known set:
reco_events has no artist names and the schema is frozen to match Tauri, so
full consolidation (plan D2) would regress the name-based MB candidate filter.
This augments the id set with reco's richer plays+favorites signal instead.

cc
2026-06-26 20:03:19 -06:00
vicrodh bd887d755c feat(reco): add forgotten-favorites + genre-backfill engine queries
Two additive RecoStore queries with unit tests (all 8 reco_store tests green):
- get_forgotten_favorite_album_ids(limit, recency_days): favorite albums not
  played within the window. Ported from Tauri db.rs:391-426 but adapted to the
  integer created_at schema -- Tauri's datetime('now',..) string compare never
  matched the integer column, so the recency filter is a numeric cutoff.
- update_genre_for_album(album_id, genre_id): backfills genre_id onto an
  album's still-NULL events. Plays log no genre, so the frontend writes it back
  on album resolution; this is what makes get_top_genres non-empty (Tauri
  defined the backfill fns but never called them, so its top-genres was empty).

cc
2026-06-26 19:51:31 -06:00
vicrodh 22fb4162f7 feat(slint): search cortinilla — main-header + immersive
Intelligent Search (phases 3-4): a frontend-agnostic SearchService in qbz-app (Capa A result cache + Capa B interaction ranking) behind a live header dropdown with skeleton loading, stale-while-revalidate, 220ms debounce, keyboard nav + scroll-into-view, per-section caps, a URL-match artwork guard, and an opt-out Settings toggle. Typing previews in the dropdown; Enter goes to Search > All; local-library results show in the dropdown only.

Immersive search: an in-immersive glass cortinilla (Albums/Artists/Playlists) that switches playback without navigating — a Settings action select (disabled/replace-queue/play-next/add-to-queue), the HUD stays up while searching and never hides while the cursor rests on the chrome, twinned top buttons, and a clear button with always-centered field text.

cc
2026-06-20 17:38:02 -06:00
vicrodh ef93c86437 feat(app): headless artist-blacklist service extracted from src-tauri
cc
2026-06-16 22:19:36 -06:00
vicrodh 314ed8e6ba feat(reco): shared headless reco-store module (qbz-app)
Cleanroom port of the Tauri reco-store into qbz-app::settings::reco_store
(frontend-agnostic, no tauri::State, ADR-006) so the Slint frontend can seed
DailyQ/WeeklyQ, the WeeklyQ 7-day window, and top-genres. Opens the SAME
per-user reco/events.db that Tauri uses (cross-frontend history share); ports
the reco_events schema + indexes, log_play/favorite_event, get_recent and
get_favorite track ids, a windowed get_recent_track_ids_since, get_top_genres,
get_home_seeds, and the train() scorer verbatim. 6 headless tests green.
Tauri's own reco_store is left as-is (rewiring it is a deferred cleanup).

cc
2026-06-15 16:00:41 -06:00
vicrodh 2a37f2ad91 feat(discover): add per-tab section-prefs core store (configurator model)
Frontend-agnostic port of Tauri discovery-v2/sectionPrefs.ts (ADR-006):
the ordered per-tab {id,enabled} model for the Discover configurator
(Home / Editor's Picks / For You). Pure model logic (default_prefs,
migrate with the legacy-array / object / garbage branches, reconcile_list
with drop-unknown + first-wins dedup + append-missing, toggle, move_section
with boundary clamp, reset_tab, is_enabled, enabled_count, enabled_ordered)
plus a per-user SQLite store (dedicated discover_prefs.db, single JSON-blob
column in the Tauri-compatible shape). 10 headless tests green.

Lives in qbz-app::settings::discover_prefs so any frontend (Slint, Tauri)
drives the configurator from one source of truth.

cc
2026-06-15 12:45:14 -06:00
vicrodh 3adb517236 feat(slint): ImmersiveView base - reusable atmosphere + compact player + audio-transport lift
Foundation for the ImmersiveView Slint port. Authored by Codex and accepted by
the owner after live visual iteration; committed here so the panel work builds
on top of it. The Album Reactive foreground was intentionally left out (a prior
attempt was rejected) so it can be rebuilt cleanly on this base.

Audio-transport lift (slice 1):
- qbz-audio: VizFrame/VizSink trait + spawn_visualizer_thread; FFT producer
  lifted off tauri::AppHandle. Protected audio path untouched (seam =
  RingBuffer.snapshot).
- src-tauri: fft_processor gutted to a thin TauriVizSink emitting byte-identical
  viz:* events.
- qbz-app: AppRuntime::with_visualizer + visualizer_tap accessor.
- qbz-slint: SlintVizSink + install() publishing VisualizerState
  bars/spectral/energy/waveform/transient at 30fps, zero IPC.

Immersive base (slice 2a):
- immersive.rs: host-side atmosphere generation from decoded artwork
  (8x8 sample -> upscale -> blur -> saturate/contrast/normalize -> vignette),
  mirroring Tauri texture-loader.ts::generateAtmosphere; plus glow_color.
- ImmersiveAtmosphere.slint: reusable animated atmosphere (4 counter-drifting
  copies via animation-tick); usable static for Album/Artist headers later.
- ImmersiveView.slint: base overlay (atmosphere + auto-hide Tauri-shaped compact
  player only). Foreground panels are rebuilt on this base.
- AppShell mounts the overlay; ViewMode menu opens Immersive in PlayerBar/Small.

cc
2026-06-13 04:11:53 -06:00
vicrodh 952846b6a0 feat(slint): Last.fm and ListenBrainz scrobblers wired to player and settings
Re-implements the parked scrobblers-wip stash intent on the current tree
(the stash no longer applied; it remains untouched in the stash list) with
three corrections: the Last.fm offline queue uses the SHARED per-user
offline_settings.db scrobble_queue (byte-identical SQL to Tauri - queues
flush from either frontend) instead of a private table; the ListenBrainz
cache opens Tauri's per-user path so LB creds and listen_queue are truly
shared (a Tauri LB sign-in pre-seeds Slint); and the queue flush is driven
by the offline engine's online edge (plus initial flush), not login-only.

New frontend-agnostic qbz_app::settings::scrobblers store (master enable,
Last.fm session/username, LB token/username; tests) and the shared
scrobble-queue API on OfflineModeStore. Player wiring hooks the de-duped
track-change edge inside the QConnect remote gate: now-playing fires on
change (skipped offline), the scrobble arms at min(50%, 240s) with a
generation counter cancelling stale timers, all sources scrobble (Tauri
parity incl. the #360 version-enriched title). Offline or call failure
queues; Last.fm flush honors the 50-batch and 14-day cutoff.

Settings gains an Integrations section (Plex-section idiom): Last.fm
browser-auth connect/finish/disconnect, ListenBrainz token field +
validate, per-service enable toggles. Known gap documented: Last.fm creds
cannot be shared with Tauri (webview localStorage); LB creds and both
queues are shared.

cc
2026-06-10 02:52:17 -06:00
vicrodh c207f232c5 fix(offline): logout reopens the Qobuz gate; auth endpoints exempt from it
Logging out of an offline session left offline_session set and the gate
closed, refusing the very OAuth exchange that could end the offline state
(user-reproduced: sign-in failed with OfflineMode after logout). teardown()
now ends the session-scoped offline state (offline_session and the cached
induced flag reset; the persisted preference reloads on the next
activation) and recomputes, reopening the gate when connectivity allows.
Two regression tests added.

The three login methods (login, login_with_oauth_code, login_with_token)
are exempt from the offline gate: user-initiated sign-in is explicit
intent to reach Qobuz; the gate governs services. Verified none of them
reach gated sub-calls; bundle cold-start stays gated on purpose. New unit
test pins the exemption. The recovery sign-in falls back to the full
browser OAuth flow when the saved token is gone or rejected, and
offline_session is lifted only on success paths.

cc
2026-06-10 01:32:14 -06:00
vicrodh 727c3fb4ba feat(slint): disk-first cached favorites, read-only hearts offline
Hoists Tauri's FavoritesCacheStore into qbz-app::settings::favorites_cache
(same per-user favorites_cache.db filename and schema, file shared across
frontends; 4 store tests). Slint's fav_cache now binds the store per user,
seeds the in-memory id set from disk at session activation (online,
restore, recovery and offline entry - fixing the start-offline cold-hearts
gap Tauri never closed), writes through on every favorite mutation, and
full-replaces the disk mirror on network refreshes. Queue hearts read the
shared cache instead of re-fetching from the network. All track-favorite
mutation paths are gated with a toast while offline (read-only hearts).

Part of offline-mode slice 3; main.rs wiring rides the slice-3 wiring
commit.

cc
2026-06-09 21:00:33 -06:00
vicrodh edae9d2463 feat(app): shared offline-mode engine with connectivity actor and grace gate
New qbz_app::offline_mode (frontend-agnostic, ADR-006): three-state mode
machine (Online / RealOffline session-scoped / InducedOffline persisted),
Tauri-compatible offline_settings.db store with the issue #279
stream_first_track snapshot, and a layered connectivity actor (default-route
signal on Linux, audio liveness window, IP-literal + vendor-diverse strict
probes, asymmetric hysteresis with confirmation burst, captive-portal and
suspend/resume handling).

qbz-qobuz gains the process-wide offline_gate and ApiError::OfflineMode (the
single Qobuz choke point; HTTP funnel wiring lands next).
SubscriptionStateStore gains offline_playback_allowed: binary 30-day-grace
playback gate for the offline cache, never a preview-degradation path.

cc
2026-06-09 19:48:13 -06:00
vicrodh f7619a92fc feat(slint): Plex creds store + Settings auth/PIN UX (port slices 0-2)
Fresh Slint-only Plex integration foundation (Tauri untouched; the Tauri
code is slated for deletion at port end, so creds are not shared):

- slice 0: fix the false Plex docs in local_library.rs
- slice 1: add the qbz-plex dependency to qbz-slint
- slice 2a: qbz-app/settings/plex.rs per-user store (10 fields, idempotent
  4->11 column migration keeping creds, 8 tests) - frontend-agnostic,
  no tauri::State (ADR-006)
- slice 2b: Settings > Local Library Plex section, 1:1 with the complete
  Tauri feature - master toggle + collapse, LAN-only address gate, PIN auth
  (2500ms poll, self-stopping on section-leave/expiry/disconnect),
  manual-token fallback, ping + machine-id capture, multi-section pick
  (default-all), disconnect vs clear-cache; once-per-session background
  refresh (no cache_clear churn on section re-entry)

cc
2026-06-07 21:44:33 -06:00
vicrodh 5b3ba963b0 feat(settings): tray store gains mac_hide_dock + close-to-tray opt-out
Add a mac_hide_dock column (with migration) to the shared tray settings
store for the upcoming macOS close-to-tray Dock-icon toggle, and flip the
close_to_tray default to true (opt-out: closing sends the window to the
tray and keeps the app running).

cc
2026-06-06 01:44:30 -06:00
vicrodh 1072195740 merge: bring v1.2.15 Tauri fixes (shared HTTP client / fd exhaustion, qobuz bundle-token cache + bounded fetch) into slint-mvp
cc
2026-06-05 15:11:48 -06:00
vicrodh 0cefe1ee80 fix(qobuz): cache bundle tokens and bound the fetch to stop startup hangs
Startup blocked on Qobuz's ~7 MB web bundle download with no cache or
timeout, so a slow CDN left the app on a blank screen for 25-30s before
the login screen appeared. Cache the extracted tokens on disk (keyed by
bundle version), apply them instantly on warm starts and refresh in the
background, and bound the cold fetch with a per-request timeout + retry.
The legacy api client shares the same cache. Emits BundleFetchStarted so
the UI can show a connecting state on the first run.

cc
2026-06-05 01:09:54 -06:00
vicrodh f907d9d984 refactor(qconnect): move device_uuid persistence to qbz-app (slice 1)
Relocate the persistent QConnect device-identity resolution out of the
Tauri adapter into the frontend-agnostic qbz-app crate
(qbz-app/src/qconnect_identity.rs) so both the Tauri and a future Slint
frontend resolve the SAME uuid for the same install.

Moved: resolve_qconnect_device_uuid, device_uuid_from_db,
load/persist_device_uuid, qconnect_settings_db_path.

Byte-identical persistence contract preserved: DB path
(<data_dir>/qbz/qconnect_settings.db), the 'device_uuid' key, and the
QBZ_QCONNECT_DEVICE_UUID env override are unchanged, so existing installs
keep their uuid. The Tauri transport.rs::resolve_qconnect_device_uuid is
now a one-line delegate; the device-NAME persistence shares the same DB
file via the qbz-app path helper.

qbz-app gains a uuid dependency (it already had rusqlite + dirs, so no
SQLite dep was added to qconnect-app per R6). The 2 device_uuid tests
move to qbz-app.

cc
2026-06-01 09:03:13 -06:00
vicrodh 7e6e548598 feat(slint): minimal playback wiring + Queue, plus detail/Settings fixes
Playback (minimal, album-focused):
- playback.rs controller: play an album (set queue + start audio),
  play a single track, enqueue, play a queue index, transport
  (play/pause, next, previous, seek, volume)
- the audible step uses Player::play_track (fetch -> download -> play);
  the protected audio backend is untouched
- a 450ms poll loop pushes position/progress/times/playing onto
  NowPlayingState and auto-advances at end of track
- NowPlayingState / QueueState globals; NowPlayingBar bound to real
  state (song card, seek bar, transport, volume); QueueSidebar lists
  the queue and rows play on click
- a played track is recorded into the recently-played store

Detail views:
- album description and artist biography stay with the metadata,
  filling the width to the right of the artwork (not below the header)
- header nav tabs gained a dropdown caret and 2px side padding

Settings > Playback defaults are now opt-out: show context icon,
restore session, resume position and stream-uncached default on;
streaming-only stays opt-in; initial buffer defaults to 2s.

Known gaps: queue/now-playing artwork not yet threaded (Image is not
Send); autoplay-mode preference not consulted by auto-advance; buffer
progress line stays at 0.

cc
2026-05-18 22:16:13 -06:00
vicrodh 7ef6300a22 refactor(qbz-app): add minimal session activation to AppRuntime
Task 2 of the Slint POC readiness audit. AppRuntime now performs
framework-agnostic per-user session activation: activate, activate_at,
activate_offline, deactivate, plus is_session_active, active_user_id,
and a with_session_store accessor.

This is the minimal version per the 2026-05-18 audit correction. It
opens only the session store and does the portable scaffolding (user
paths, directories, last_user_id marker, runtime state machine). It
does not touch Tauri's session_lifecycle, does not initialize the
src-tauri-side per-user stores, and does not run the flat-to-user
migration. The shipping app keeps identical behavior; this is additive.

activate_at takes explicit directories and is the testable core;
activate resolves real per-user paths through UserDataPaths.

cc
2026-05-18 09:26:22 -06:00
vicrodh a213b37807 refactor(qbz-app): add AppRuntime shell facade skeleton
Adds a framework-agnostic composition root for non-Tauri UI shells
(Slint, TUI, headless) as Task 1 of the Slint POC readiness audit
(session-20-slint-poc-readiness-audit).

AppRuntime<A> owns an Arc<QbzCore<A>> plus the runtime state machine,
built with the same recipe as the Tauri CoreBridge but with no Tauri
dependency. Scope is composition and accessors only; per-user store
ownership and session activation are deferred to Task 2.

qbz-app now depends on qbz-core, qbz-player, qbz-audio, and qbz-models.
The Tauri app does not consume this module; it is purely additive.

cc
2026-05-18 07:28:19 -06:00
vicrodh f79c550889 refactor(qbz-app): add playback context
cx
2026-05-17 19:39:14 -06:00
vicrodh 4ba96fe59a refactor(qbz-app): add session store foundation
cx
2026-05-17 19:16:13 -06:00
vicrodh 29b766ae24 fix(graphics): recommend CPU for NVIDIA-only, allow DMA-BUF on hybrid
NVIDIA-only systems no longer get a hardware-acceleration recommendation that visibly stutters under WebKitGTK. The three NVIDIA-only branches (Wayland+GNOME, Wayland, X11) collapse into one CPU-rendering profile, since XWayland does not rescue the stutter either.

NVIDIA + iGPU hybrid now allows DMA-BUF: WebKit composites on the stable iGPU stack, so the instability that affects NVIDIA-only systems does not apply. The recommendation also tells the user to pick the iGPU explicitly in the rendering GPU selector instead of Auto.

cc
2026-05-16 12:22:41 -06:00
vicrodh 1a5dbe07bb refactor(qbz-app): extract boot watchdog decision
cx
2026-05-16 09:04:34 -06:00
vicrodh 3c963cdaf3 refactor(qbz-app): extract graphics autoconfig
cx
2026-05-16 08:50:06 -06:00
vicrodh 2fc9270c89 refactor(qbz-app): add graphics detection
cx
2026-05-16 00:44:16 -06:00
vicrodh a497d61b75 refactor(qbz-app): add user data paths
cx
2026-05-16 00:20:21 -06:00
vicrodh be500956ff refactor(qbz-app): add developer settings store
cx
2026-05-15 23:25:06 -06:00
vicrodh bcdbc69913 refactor(qbz-app): add graphics settings store
cx
2026-05-15 18:52:15 -06:00
vicrodh f8f8bb88d6 refactor(qbz-app): add image cache settings store
cx
2026-05-15 18:18:36 -06:00
vicrodh 26012e98c9 refactor(qbz-app): add download settings store
cx
2026-05-15 17:48:14 -06:00
vicrodh 2e43831800 refactor(qbz-app): add remote control settings store
cx
2026-05-15 14:17:35 -06:00
vicrodh 04d5b45134 docs(qbz-app): keep app comments frontend agnostic
cx
2026-05-15 00:12:22 -06:00
vicrodh d1082317f5 refactor(qbz-app): add ui preference stores
cx
2026-05-15 00:04:36 -06:00
vicrodh 61d5715a22 refactor(qbz-app): add preference stores
cx
2026-05-14 19:48:54 -06:00
vicrodh 800447d33c refactor(qbz-app): add settings stores
cx
2026-05-14 17:08:45 -06:00
vicrodh dc74db6401 test(qbz-app): cover runtime state transitions 2026-05-14 11:54:16 -06:00