Commit Graph
100 Commits
Author SHA1 Message Date
Afonso Jorge Ramos 84b3c9f970 fix(theme): update registry count for Catppuccin variants 2026-07-16 01:20:53 +02:00
Afonso Jorge Ramos 4e37b1370b fix(settings): make the theme-list filter swap its icon and narrow the list
The All/Dark/Light filter next to the theme dropdown did nothing: every
branch of the icon ternary rendered disc.svg, and the Rust `theme-filter`
handler was cosmetic (persisted the value but never filtered the list).

Add distinct sun-moon/moon/sun icons and a luminance-based filter-aware
theme API (filtered_dropdown_themes/labels + index helpers), then wire
startup seeding, the filter cycle, and theme selection through it. `All`
is a proven passthrough of the previous behaviour; Dark/Light narrow the
dropdown by the registry's `is_light` flag.
2026-07-16 01:20:44 +02:00
Afonso Jorge Ramos 96672a459d fix(log): stop logging Last.fm auth token prefixes
Request-token material must never appear in support log bundles.
2026-07-14 13:33:04 +02:00
Afonso Jorge Ramos ec69bce17e feat(ui): open album from grid card title click 2026-07-13 16:45:42 +02:00
Afonso Jorge Ramos b4450965df fix(player): surface stream feeder failures to BufferWriter
CMAF and remote download tasks now error the streaming buffer on failure
so readers do not hang waiting for complete/error forever.
2026-07-13 16:16:14 +02:00
Afonso Jorge Ramos 4ce075beaa docs: tombstone Remote Control OpenAPI for 2.0 Slint
The LAN REST/SSE server is not in the shipping binary; fix license/repo
metadata and mark the document historical while retaining path schemas.
2026-07-13 16:13:05 +02:00
Afonso Jorge Ramos c9b9aea118 fix(scrobble): skip tracks shorter than 30 seconds 2026-07-13 16:12:06 +02:00
Afonso Jorge Ramos 68317f5a34 fix(dsd): treat demux I/O errors as sticky failures, not clean EOF
Only Ok(0) ends the stream cleanly; mid-file read errors set io_error.
2026-07-13 16:11:02 +02:00
Afonso Jorge Ramos 21c4817fcf fix(player): bump play generation on stop, DSD, and streaming
Non-play_track entry points must supersede in-flight play_track so a
slow fetch cannot clobber stop or a later source.
2026-07-13 13:50:06 +02:00
Afonso Jorge Ramos 61df66c529 fix(offline): purge only allowlisted cache layout directories
Stop recursive-deleting arbitrary siblings of the offline root so a
mis-set cache path cannot wipe unrelated folders on a shared mount.
2026-07-13 13:49:55 +02:00
Afonso Jorge Ramos 542ee8050f fix(player): clear streaming loaded state on streaming setup failures
Buffer timeout, engine creation failure, and incremental source failure
no longer leave has_loaded_audio true with no engine; each records a
user-visible stream error.
2026-07-13 13:49:34 +02:00
Afonso Jorge Ramos e153673acf fix(cmaf): fail init parse when raw_data length exceeds payload
Do not advance the cursor by an unclamped raw_len after a truncated slice.
2026-07-13 13:48:40 +02:00
Afonso Jorge Ramos 39dccd8b01 fix(credentials): tighten permissions of existing secret files on load 2026-07-13 13:48:30 +02:00
Afonso Jorge Ramos c1bb5b3dc2 fix(audio): fail closed when ALSA exclusive rate is not exact
Read back the hardware rate after hw_params for PCM, DoP, and native DSD
and refuse silent nearest-rate mismatches.
2026-07-13 13:47:43 +02:00
Afonso Jorge Ramos e1c34005e0 fix(credentials): write secret files with mode 0o600 on Unix
Salt, machine-id fallback, encrypted credentials, and OAuth token files
must not inherit a permissive umask.
2026-07-13 13:47:18 +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
Afonso Jorge Ramos 41daffdf71 fix(scrobble): do not scrobble when track duration is unknown
duration_secs == 0 used to arm an immediate scrobble; skip delayed
scrobble instead while still allowing now-playing when online.
2026-07-13 00:01:11 +02:00
Afonso Jorge Ramos 7e824bd65a fix(offline): place download size validator at module scope 2026-07-12 23:57:18 +02:00
Afonso Jorge Ramos f507b5c214 fix(offline): reject truncated downloads when Content-Length is known
Fail closed on short bodies and empty payloads so partial files never
become complete cache entries.
2026-07-12 23:56:59 +02:00
Afonso Jorge Ramos 19b1c2bead fix(cmaf): fail closed on invalid session-key seed hex
Reject odd length and non-hex digits instead of zero-filling bad nibbles.
2026-07-12 23:33:14 +02:00
Afonso Jorge Ramos a0bf1328d6 fix(cast): use CSPRNG media path tokens and redact URIs in logs
Replace RandomState-based tokens with getrandom, avoid logging full
media base URLs, and redact /audio/<token>/… in DLNA info logs.
2026-07-11 23:01:05 +02:00
Afonso Jorge Ramos 3dc54a751d fix(player): stop DoP writer on ALSA write failure
Match the PCM ALSA path: on write_dop_i32 Err, stop the writer thread
instead of logging and advancing position (which desyncs DoP markers).
2026-07-11 23:01:05 +02:00
Afonso Jorge Ramos 101490043d fix(player): cancel superseded play_track completions
Bump a play generation on each play intent so a slow CMAF/legacy fetch
cannot apply AudioCommand::Play after the user has already started another
track. play_data still bumps generation for standalone offline/cache calls.
2026-07-11 23:01:04 +02:00
Afonso Jorge Ramos 12a278fc28 fix(player): clear playing state when seek rebuild fails
After current_engine.take(), seek error paths used to return with no
engine while is_playing stayed true. Abort now sets stream_error and
stops the playing flag so the UI cannot show silent "playing".
2026-07-11 23:01:04 +02:00
Afonso Jorge Ramos 66e6efeffd fix(log): redact secrets on stderr and stop Last.fm body dumps
TeeLogger no longer forwards the original Record to env_logger (which
bypassed redaction). Register restored OAuth tokens for literal scrubbing,
and never debug-log the auth.getSession response body.
2026-07-11 23:01:04 +02:00
Afonso Jorge Ramos acc033915e fix(queue): keep current_index track on clear(true)
truncate(1) always preserved tracks[0], so Clear queue mid-album left the
wrong now-playing row. Keep the track at current_index and remap history
by id so removed rows do not leave stale indices.
2026-07-11 23:01:04 +02:00
Afonso Jorge Ramos 54da577d56 ci(test-crates): align workflow with release CI conventions
Match naming, permissions, concurrency, mold linker, and cache save-if
policy (write only from pre-release) used by the Slint release jobs.
2026-07-10 01:02:37 +02:00
Afonso Jorge Ramos 7a600a60f4 ci: add workspace cargo test job (exclude Slint UI graph)
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.
2026-07-10 00:52:05 +02:00
Afonso Jorge Ramos 8f1f287191 fix(settings): hide title bar section on macOS 2026-07-08 17:24:38 +02:00
Afonso Jorge Ramos 032021de44 feat(lyrics): drag region on sidebar header and selectable lyrics panel 2026-05-13 20:42:27 +01:00
Afonso Jorge Ramos e54c5de08b feat(views): drag regions on top-bars across remaining views 2026-05-13 20:40:43 +01:00
Afonso Jorge Ramos 47f3a9f335 feat(views): drag regions on home, library, and favorites top-bars 2026-05-13 20:40:39 +01:00
Afonso Jorge Ramos 897f6ac862 feat(detail-views): drag regions and selectable metadata blocks 2026-05-13 20:40:34 +01:00
Afonso Jorge Ramos eb57c70ef9 feat(modals): mark info modals and legal notice selectable 2026-05-13 20:40:29 +01:00
Afonso Jorge Ramos 418bf371e8 refactor(styles): drop now-redundant local user-select: none 2026-05-13 20:40:23 +01:00
Afonso Jorge Ramos a34e418530 feat(sidebar): drag window from aside top padding band on macos 2026-05-13 20:39:58 +01:00
Afonso Jorge Ramos 69d7ee0212 refactor(layout): remove macos overlay drag and main-content padding 2026-05-13 20:39:57 +01:00
Afonso Jorge Ramos 5610861ed8 refactor(titlebar): drag on spacer divs not full bar 2026-05-13 20:39:56 +01:00
Afonso Jorge Ramos c1c151ee25 refactor(css): user-select none on body with .selectable opt-in 2026-05-13 20:39:50 +01:00
Afonso Jorge Ramos 5865c16624 docs(player): explain set_stream_error(false) message swallow
Clearing the error flag also drops any pending stream_error_message.
A fast record_stream_error -> set_stream_error(false) -> polling-tick
sequence will silently swallow the toast — that is the intent (a
recovered error is not user-actionable), but it surprises future
readers, so document it inline.
2026-05-13 18:07:54 +01:00
Afonso Jorge Ramos 9231ea2bd4 fix(mixtapes): replace click-swallowing backdrop with document click-outside 2026-05-13 17:54:48 +01:00
Afonso Jorge Ramos b3b0fce535 fix(purchases): replace click-swallowing backdrop with document click-outside 2026-05-13 17:54:48 +01:00
Afonso Jorge Ramos 3382a36ede fix(mixtape-collection): replace click-swallowing backdrop with document click-outside 2026-05-13 17:54:48 +01:00
Afonso Jorge Ramos 29598747fb fix(collections): replace click-swallowing backdrop with document click-outside 2026-05-13 17:54:48 +01:00
Afonso Jorge Ramos ff11dea869 fix(label): drop tracks context menu backdrop in favor of click-outside listener 2026-05-13 17:54:48 +01:00
Afonso Jorge Ramos ddf0ed4175 fix(artist-detail): consolidate section sort menus and drop tracks context backdrop 2026-05-13 17:54:48 +01:00
Afonso Jorge Ramos 126dff1297 fix(library): consolidate toolbar dropdowns into single openMenu state 2026-05-13 17:54:47 +01:00
Afonso Jorge Ramos 7fccf9bd01 fix(favorites): consolidate toolbar and section dropdowns into single openMenu state 2026-05-13 17:54:47 +01:00
Afonso Jorge Ramos fc56fb4b2f feat(genre-filter): make isOpen bindable for parent dropdown coordination 2026-05-13 17:54:08 +01:00
Afonso Jorge Ramos 84dcd7ebbe perf(+page): guard lyrics state writes against subscriber churn
subscribeLyrics fires per rAF tick (~60Hz with extrapolation). The
previous version wrote all six $state slots unconditionally, which
re-triggered the lyricsSidebarLines $derived (a .map allocating N
objects per call) every tick even though lyricsLines itself only
changes per-track. Adding reference/value guards short-circuits the
fan-out for unchanged fields — the per-tick path now only writes
lyricsActiveProgress (and sometimes lyricsActiveIndex).
2026-05-13 14:31:19 +01:00
Afonso Jorge Ramos 640dead393 fix(lyricsStore): claim rAF handle pre-tick and notify on every change
Two related fixes to the active-line rAF loop:

- The previous guard reassigned updateRafHandle *after* updateActiveLine
  returned. Any listener that synchronously re-entered
  startActiveLineUpdates during notifyListeners() saw a null handle and
  scheduled a second parallel rAF chain. Now the handle is claimed
  before the work runs, so re-entry is a no-op.

- Drops the inter-tick progress threshold. With playerStore extrapolating
  audio time between backend events, every rAF tick has a freshly-changed
  progress value. Threshold-throttling would skip the edge samples where
  the snap-to-1 and indexChanged transitions need to land — re-introducing
  the "starts midway"/"ends before 100%" bugs. Downstream consumers
  equality-guard their $state writes, so fan-out only fires on real
  changes.
2026-05-13 14:31:11 +01:00
Afonso Jorge Ramos 6166584ee8 perf(playerStore): extrapolate audio time between backend position events
The Rust backend emits playback:state at 250ms cadence. Frontend
getCurrentTime() was returning that staircase value — for the karaoke
rAF loop running at 60Hz, every 15 frames got the same answer, then a
250ms jump. This caused two visible artifacts:

- Active line appears to "start midway" when detection of the new index
  lags by up to 250ms past the line's timeMs (audio is already that far
  into the line by the time we see the boundary).
- Active line gets demoted before its highlight reaches the end, because
  the last in-line notification reports progress ~0.92 (one tick before
  bound) and the next backend tick has already crossed into the next
  line — the snap-to-1 never gets a chance to fire.

Audio position is wall-clock-based on the backend (see the 250ms emit
loop comment in lib.rs); we now do the same math in JS: anchor
(positionSecs, performance.now()) on every event-driven update, then
return anchor + elapsed while playing. Cap extrapolation at +1s so a
stalled backend doesn't run away. Existing 33 playerStore tests pass
unchanged — they read currentTime directly, not getCurrentTime().
2026-05-13 14:31:03 +01:00
Afonso Jorge Ramos 64511c4fa2 feat(lyrics): segment active-line gradient by visual line via pretext
Pre-pretext, the karaoke gradient applied to a single span shared the same
horizontal cut across every wrapped visual line — line 2 appeared partially
sung when only line 1's worth of vocals had played. Pretext lays out the
active lyric into visual lines; each renders as its own block-level span
with its own 0->100% gradient, so progress fills sequentially.

Also adds race/perf safeguards layered on top:
- Cancellation token on tick().then(layoutActiveLine) so rapid activeIndex
  changes (seek-scrub, fast songs) don't queue stale microtasks against
  the current DOM.
- ResizeObserver coalesces via rAF and ignores sub-pixel width deltas, so
  window-drag and the active-line scale animation don't trigger Pretext
  re-measurement every callback.
- segmentBounds prefix array memoizes cumulative widths, dropping per-
  segment progress lookup from O(i) to O(1) inside the each-block.
- EMA writes equality-guard measuredBlockMs/Delta and lookaheadEnabled so
  Svelte signals don't fan out no-op updates 60 times per second.
- Lookahead clamped at 1/30 and disabled on backward dp so a coarse audio
  jump or in-line seek can't poison the visual lead.
- measuredBlockMs seed lowered (>=20ms, dur * 0.01) since rAF-rate data
  converges the EMA within 2-3 ticks; the prior 80ms floor visibly lagged
  the gradient on the first frame of every new line.
- LyricsLine type unified from store (single source of truth).
2026-05-13 14:30:54 +01:00
Afonso Jorge Ramos 57f8307934 feat(ui): toast on audio:init-failed event
Listen for the backend audio:init-failed event and show a user-facing
error toast (8s) with the backend-provided cause. Mirrors the
audio:device-missing listener pattern alongside it. Surfaces the
silence when macOS CoreAudio init fails, instead of leaving the user
guessing why playback didn't start.
2026-05-13 13:09:35 +01:00
Afonso Jorge Ramos a251849094 i18n(toast): add audioInitFailed translation key
Add localized strings for the new backend audio init failure toast
(en, de, es, fr, pt). Reason placeholder receives the backend's
already user-friendly message (e.g. "macOS audio device changed its
sample rate during stream open...").
2026-05-13 13:09:31 +01:00
Afonso Jorge Ramos 6d91ec0e02 feat(audio): emit audio:init-failed Tauri event on backend init failure
The playback-event polling loop now drains
SharedState::take_stream_error_message and emits a Tauri
audio:init-failed event with the user-readable cause. Drain semantics
guarantee the toast fires exactly once per error instead of every
tick of the polling loop.
2026-05-13 13:09:26 +01:00
Afonso Jorge Ramos 04e17cc449 feat(player): expose user-readable stream_error_message channel
Pairs the existing stream_error AtomicBool with an Option<String> that
the audio thread can populate when a stream init failure has a
meaningful cause to surface to the user (currently: the macOS
CoreAudio rate-mismatch path).

set_stream_error(false) also clears the message; record_stream_error
sets both atomically; take_stream_error_message drains so the UI
fires its notification exactly once per error.
2026-05-13 13:09:18 +01:00
Afonso Jorge Ramos a7b40bf6bb perf(player): cache CoreAudio nominal rate query for 750ms
evaluate_stream_recreate runs on every track change on the audio
thread, and each call issued two HAL syscalls
(resolve_output_device_id + get_nominal_sample_rate). Cache the result
per-thread for 750ms — long enough to absorb the bulk of repeats
within an album, short enough to notice a system rate change.

Cache invalidates on device-name change, so switching the output
device still triggers a fresh query.
2026-05-13 09:23:39 +01:00
Afonso Jorge Ramos b5db0241a9 fix(LyricsLines): make line-text block to expand background-clip paint area 2026-05-12 18:24:37 +01:00
Afonso Jorge Ramos f9a9d103cb fix(LyricsLines): let EMA accept rAF-rate block measurements 2026-05-12 18:01:52 +01:00
Afonso Jorge Ramos f4f8fcf9bf fix(LyricsLines): use activeProgress as karaoke floor, lookahead as bonus 2026-05-12 17:58:16 +01:00
Afonso Jorge Ramos 4de657e2cd feat(LyricsLines): JS-driven karaoke with measured block-interval and lookahead 2026-05-12 17:57:35 +01:00
Afonso Jorge Ramos 6d25100133 refactor(LyricsLines): single-div template with class-toggle transitions 2026-05-12 17:55:30 +01:00
Afonso Jorge Ramos 4d426a80cd fix(lyrics): snap line progress to 1 when at least 99% through 2026-05-12 17:54:01 +01:00
Afonso Jorge Ramos 959f656866 refactor(lyrics): notify listeners on every progress change 2026-05-12 17:53:49 +01:00
Afonso Jorge Ramos 13d1d6919a perf(lyrics): drive active-line updates via requestAnimationFrame 2026-05-12 17:53:35 +01:00
Afonso Jorge Ramos a5100a329a perf(page): memoize lyrics sidebar lines projection 2026-05-12 17:53:06 +01:00
Afonso Jorge Ramos 9aacf1d7eb feat(lyrics): parse LRC gap markers and use endMs for line bounds 2026-05-12 17:52:48 +01:00
Afonso Jorge Ramos fdeb544c63 fix(lyrics): correct active-line color on macOS WebKit 2026-05-12 15:10:09 +01:00
Afonso Jorge Ramos 4fd85b5b2f fix(ci): suffix macOS .app.tar.gz bundles with arch to fix auto-updater 2026-05-11 16:17:07 +02:00
Afonso Jorge Ramos 1edde8a28a fix(audio): rephrase CoreAudio rate-mismatch errors for end users
The error strings flow up through stream_error and end up in user-
visible logs. Replace the internal-shaped messages ("opened at XHz
while device nominal rate is YHz") with text that names the cause,
the remediation, and the reason the fallback path is refused.
2026-05-11 15:19:30 +02:00
Afonso Jorge Ramos 9e632386b9 fix(audio): retry CoreAudio shared-mode open on rate mismatch
CPAL caches device configs and can briefly return a stale sample rate
right after CoreAudio changes the device nominal rate. Before this
change, opening at the stale rate failed the post-open validation and
returned Err, leaving the user with no audio on the first track after
a rate change.

Drop the bad sink, re-resolve the device, re-query nominal rate and
try once more with a 50ms backoff. Two attempts in total. Turns the
racy window into transparent recovery.
2026-05-11 15:16:46 +02:00
Afonso Jorge Ramos c408ea73cb test(player): cover compute_needs_new_stream decision rule
Extract the boolean decision out of evaluate_stream_recreate into a
pure function so the truth table can be tested without faking a
MixerDeviceSink or AudioSettings mutex. Eight cases cover the matrix:
no stream, unchanged format, format change with and without each
bit-perfect backend, and the CoreAudio shared-mode rate-mismatch path.
2026-05-11 15:15:57 +02:00
Afonso Jorge Ramos ac60113cdd refactor(player): extract StreamRecreateDecision helper
The rate-mismatch detection block was duplicated between the Play and
Stream handlers, and each handler acquired thread_settings.lock() three
times in a row. Extract into one helper that reads settings once and
returns every flag the recreate decision needs.

Also drops a dead else-branch that logged "Creating initial audio
stream" — unreachable inside if stream_opt.is_some() — and the
redundant info-log that re-announced the rate-mismatch reason at
recreate time (the warn-level log inside the helper already says
"Recreating stream to avoid wrong-speed playback").
2026-05-11 15:14:54 +02:00
Afonso Jorge Ramos 3b34a734b4 refactor(player): rename current_sample_rate to current_track_sample_rate
The field tracks the decoded source format, not the output stream's
hardware rate. The names are now explicit so future readers don't
conflate the two — on macOS shared mode these can legitimately differ
since Rodio resamples internally.
2026-05-11 15:13:29 +02:00
Afonso Jorge Ramos ac627d432b fix(home): unify sticky tab header to avoid pop-in animation 2026-05-09 17:52:39 +02:00
Afonso Jorge Ramos 8ed62676c2 fix(titlebar): tune macos drag band to 32px 2026-05-09 17:45:46 +02:00
Afonso Jorge Ramos ac5fc25944 fix(titlebar): match macos drag band height to sidebar top padding 2026-05-09 17:37:04 +02:00
Afonso Jorge Ramos be67fffb28 feat(titlebar): expose hide-title-bar toggle on macOS 2026-05-09 17:36:28 +02:00
Afonso Jorge Ramos 61bd1eb940 feat(settings): expose nav and search titlebar toggles on macOS 2026-05-09 17:23:40 +02:00
Afonso Jorge Ramos 3cb54dd06b style(sidebar): drop dead macos no-titlebar padding rule 2026-05-09 17:23:37 +02:00
Afonso Jorge Ramos fd6464fdf3 refactor(macos): drop overlay drag region superseded by qbz titlebar 2026-05-09 17:23:32 +02:00
Afonso Jorge Ramos 0ee2e429c8 feat(titlebar): mount qbz titlebar on macOS with traffic-light reservation 2026-05-09 17:23:23 +02:00
Afonso Jorge Ramos 88e072a4ac fix(macos): harden CoreAudio exclusive stream handling
Address review feedback from the macOS Exclusive Mode PR.

Pin the resolved CoreAudio output device before acquiring Hog Mode so
System Default cannot drift between Hog Mode acquisition, sample-rate
switching, and stream creation.

Fall back to software volume when CoreAudio hardware volume is unavailable,
which keeps the volume slider usable on knob-only DACs.

Snapshot and restore CoreAudio hardware volume around Exclusive Mode so
leaving Exclusive Mode does not leave the output device at QBZ's last
hardware volume.

Refuse shared-mode fallback when macOS Exclusive Mode stream creation
fails, so the app does not silently play in non-exclusive mode while the UI
still reports Exclusive Mode.

Clean up Linux unused-variable warnings from the macOS-only guard plumbing.
2026-05-07 09:36:20 -05:00
Afonso Jorge Ramos e93fcaec99 fix(audio): defer host-stream drop on Stop to reuse device across track changes 2026-05-07 02:32:39 +02:00
Afonso Jorge Ramos 468a421e26 fix(audio): gate 50ms post-stop sleep behind cfg(target_os = "linux") 2026-05-05 17:11:28 +02:00
Afonso Jorge Ramos 1e3f5ab981 fix(audio): populate macOS output device picker 2026-05-04 00:31:21 +02:00
Afonso Jorge Ramos 65792b3a9c fix: sync package-lock.json with package.json 2026-04-06 01:35:19 +02:00
Afonso Jorge Ramos e4462dfefd fix(macos): ad-hoc sign app bundle to fix Gatekeeper rejection 2026-04-06 01:35:08 +02:00
Afonso Jorge Ramos 787196db99 fix: keep exclusive mode and DAC passthrough visible on all platforms 2026-04-05 01:12:54 +02:00
Afonso Jorge Ramos 86d5286a29 fix: add Windows case to AboutModal platform label 2026-04-05 01:12:53 +02:00
Afonso Jorge Ramos 2e5b9368e4 fix: add windows to Platform type 2026-04-05 01:12:53 +02:00
Afonso Jorge Ramos adfc390009 fix(notifications): use released notify-rust 4.13.0 instead of fork 2026-04-04 15:31:58 +02:00
Afonso Jorge Ramos 85bef48a0a fix: use linux-only gates instead of not-macos for platform-specific settings 2026-04-03 14:01:54 +02:00
Afonso Jorge Ramos 7a1890ad57 fix(audio): use released coreaudio-rs 0.14.1 instead of fork 2026-04-03 14:01:25 +02:00
Afonso Jorge Ramos 94bd222c52 fix: gate Linux-only settings sections on macOS 2026-04-03 13:58:15 +02:00
Afonso Jorge Ramos b71284187b refactor: migrate platform detection to Tauri OS plugin utility 2026-04-03 13:58:11 +02:00
Afonso Jorge Ramos e089417ab5 feat: create platform utility using Tauri OS plugin 2026-04-03 13:58:08 +02:00
Afonso Jorge Ramos 5d115277a5 feat: add @tauri-apps/plugin-os for platform detection 2026-04-03 13:58:04 +02:00
Afonso Jorge Ramos b1000cb9f0 fix(macos): gate Linux graphics CLI flags and crash handler with cfg 2026-04-03 03:19:52 +02:00