Skip to content

feat(ui): merged immersive view with tabbed control panel + native fullscreen (#328)#333

Merged
InstaZDLL merged 6 commits into
mainfrom
feat/immersive-merged-fullscreen
Jun 28, 2026
Merged

feat(ui): merged immersive view with tabbed control panel + native fullscreen (#328)#333
InstaZDLL merged 6 commits into
mainfrom
feat/immersive-merged-fullscreen

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented Jun 28, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #328. Reworks the immersive view to address the reporter's two points (not actually fullscreen; now-playing and lyrics are mutually-exclusive overlays with dead side space) — but ships them as opt-in, per-profile options so the out-of-the-box experience is unchanged.

Both toggles live in Settings → Appearance (ImmersiveViewCard), persist in profile_setting, and default OFF:

Toggle Off (default) On
Lyrics side column (immersive.merged_lyrics) Classic immersive — now-playing fullscreen with a Mic2 button flipping to a lyrics-only fullscreen (pre-#328 behaviour, intact) Merged 2-column view: now-playing left + a tabbed control panel (Lyrics / Queue) right, PanelRight toggle to hide/show
Native fullscreen (immersive.use_native_fullscreen) In-window overlay (as before) Real OS fullscreen (setFullscreen(true)), restoring the prior maximized state on close

Narrow windows (< 900 px) force the classic interface even when merged is on.

Architecture

  • useTrackLyrics — extracts the full lyrics lifecycle (three-tier fetch + parse + active-line/word tracking + import/refetch/clear, with every mid-flight staleness guard) from LyricsPanel into a shared hook. Both LyricsPanel and the immersive view consume it, so there's no second fetch path and the old createPortal-from-panel coupling is gone.
  • Components: ImmersiveView (orchestrator + reversible native-fullscreen effect + narrow fallback) → ImmersiveNowPlaying, ImmersiveSidePanel (tabs) → ImmersiveLyricsColumn + ImmersiveQueueTab, ImmersiveShareButton.
  • ImmersiveQueueTab reuses player_get_queue / player_jump_to_index (white-on-dark styled); the docked QueuePanel keeps its drag-reorder.
  • PlayerContext: fullscreenOverlay enum → immersiveOpen bool + immersiveInitialTab; old openFullscreenNowPlaying / openFullscreenLyrics / close* kept as aliases.
  • Native fullscreen needs core:window:allow-set-fullscreen / allow-is-fullscreen / allow-is-maximized / allow-maximize — added to capabilities/default.json (compiled into the binary, so it needs a Rust rebuild).
  • Removes FullscreenNowPlaying + FullscreenLyrics.

Notes

  • i18n propagated to all 17 locales; docs (CLAUDE.md, docs/features/ui.md, integrations.md) synced.
  • Onboarding "done" step gets a discreet tip about the immersive view.

Validation

bun run typecheck ✅ · bun run lint ✅ · bun run build

Summary by CodeRabbit

  • Nouvelles fonctionnalités
    • Nouvelle vue immersive plein écran fusionnant “Now Playing” et les paroles en deux colonnes (responsive) avec onglets Paroles / File.
    • Option plein écran natif (ouverture/retour automatique).
    • Carte de partage Now Playing dédiée à la vue immersive (enregistrer ou copier).
  • Améliorations
    • Paroles : cycle de vie unifié via une logique partagée (import / rechargement / effacement / seek), avec synchronisation et mise en évidence de la ligne active.
    • File immersive mise à jour en temps réel avec rafraîchissements mieux contrôlés.
  • Documentation
    • Mise à jour de l’UI immersive, du rendu “word-level lyrics” et des textes d’onboarding.

…llscreen (#328)

Merge the now-playing and lyrics fullscreens into one optional immersive
view with a tabbed control panel (Lyrics / Queue) on the right, and add
opt-in native OS fullscreen. Both are per-profile profile_setting toggles
defaulting OFF, so the out-of-the-box experience is unchanged.

- useTrackLyrics: shared lyrics fetch/parse/active-line hook consumed by
  both LyricsPanel and the immersive view (no double-fetch, all race
  guards preserved); removes the createPortal coupling from LyricsPanel
- ImmersiveView orchestrator + ImmersiveNowPlaying / ImmersiveSidePanel /
  ImmersiveLyricsColumn / ImmersiveQueueTab / ImmersiveShareButton
- classic interface (merged OFF or narrow window) keeps the pre-#328
  now-playing <-> lyrics Mic2 toggle intact
- PlayerContext: fullscreenOverlay enum -> immersiveOpen bool, old
  open/close names kept as aliases
- native fullscreen capabilities added to capabilities/default.json
- ImmersiveViewCard (Settings -> Appearance) + onboarding done-step tip
- i18n 17 locales, docs synced

Removes FullscreenNowPlaying + FullscreenLyrics.
@InstaZDLL InstaZDLL added scope: frontend React/Vite frontend (src/) scope: backend Rust/Tauri backend (src-tauri/) scope: i18n Translations (src/i18n/) scope: docs Docs, README, assets type: feat New feature size: xl > 500 lines labels Jun 28, 2026
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

La vue plein écran Now Playing/Lyrics devient une vue immersive unifiée à deux colonnes. L’état player, les préférences, le hook de paroles, le rendu immersif, l’intégration dans le player et les textes associés sont mis à jour autour de ce nouveau flux.

Changes

Vue immersive fusionnée

Layer / File(s) Summary
État player et préférences
src/contexts/PlayerContext.tsx, src/hooks/usePlayer.ts, src/hooks/useImmersivePrefs.ts, src-tauri/crates/app/capabilities/default.json
Le contexte player expose immersiveOpen et immersiveInitialTab, avec openImmersive/closeImmersive et alias de compatibilité. Les préférences mergedLyrics et useNativeFullscreen sont relues par profil et les permissions fenêtre fullscreen/maximize sont ajoutées.
Hook paroles et panneau latéral
src/hooks/useTrackLyrics.ts, src/components/layout/LyricsPanel.tsx, src/components/player/ImmersiveLyricsColumn.tsx, src/components/player/ImmersiveSidePanel.tsx, src/components/player/ImmersiveQueueTab.tsx
Le hook centralise le chargement, le parsing et les actions sur les paroles; le panneau de paroles s’y branche, et la colonne/panneau immersifs affichent les paroles synchronisées, statiques ou la file via des tabs.
Vue immersive et colonne média
src/components/player/ImmersiveView.tsx, src/components/player/ImmersiveNowPlaying.tsx, src/components/player/ImmersiveShareButton.tsx
La vue immersive choisit entre mode double colonne et mode classique, gère le fullscreen natif réversible, affiche la colonne Now Playing et le partage de carte.
Câblage player, réglages et onboarding
src/components/player/PlayerBar.tsx, src/components/views/SettingsView.tsx, src/components/views/settings/ImmersiveViewCard.tsx, src/components/common/OnboardingModal.tsx
Le player rend la vue immersive, les réglages ajoutent la carte immersive avec persistance, et l’onboarding affiche un nouveau conseil de présentation.
Traductions et documentation
src/i18n/locales/*.json, docs/features/ui.md, docs/features/integrations.md, CLAUDE.md
Les locales, la documentation et les notes de support ajoutent les libellés, les descriptions de vue immersive et les références aux nouveaux composants.

Estimation de l'effort de revue

🎯 4 (Complex) | ⏱️ ~60 minutes

PRs potentiellement liées

Poème

Deux overlays se taisent, la vue unique s’ouvre,
La colonne des paroles et le lecteur se recouvrent.
Plein écran natif, retour sans heurt au départ,
Un seul chemin, deux panneaux, et la musique en phare.
🎵

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Le titre résume bien la fusion de la vue immersive avec panneau à onglets et fullscreen natif, et reste clair.
Description check ✅ Passed La description couvre le résumé, l’architecture, les notes et la validation, avec le lien #328; elle est suffisamment complète.
Linked Issues check ✅ Passed La PR livre la vue immersive fusionnée, le fallback étroit, le fullscreen natif réversible et la logique paroles partagée.
Out of Scope Changes check ✅ Passed Les mises à jour docs, i18n, permissions et onboarding restent liées à l’immersive view; pas d’écart évident.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/immersive-merged-fullscreen

Comment @coderabbitai help to get the list of available commands.

@InstaZDLL InstaZDLL self-assigned this Jun 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/features/ui.md`:
- Line 165: The `immersive` description in the UI docs overstates the behavior
of `ui.cover_action`; update the wording to match `ImmersiveView` more
accurately. Clarify that the merged immersive view is only used when
`merged_lyrics` is enabled and the window is wide enough, and that it falls back
to the classic view otherwise. Keep the change in the `immersive` entry so the
docs reflect the actual behavior without promising a guaranteed fullscreen
merged view.

In `@src/components/player/ImmersiveQueueTab.tsx`:
- Around line 31-38: The fetch error handling in ImmersiveQueueTab should not
clear the last known queue snapshot when playerGetQueue() fails. Update the
promise chain in the ImmersiveQueueTab component so the catch path only logs the
refresh failure and leaves setSnapshot unchanged, while still guarding updates
with fetchSeqRef.current and seq to avoid stale results overwriting newer data.
- Around line 41-53: The listener setup in ImmersiveQueueTab’s useEffect can
leak if the component unmounts before listen("player:queue-changed", doFetch)
resolves, because unlisten is still null when cleanup runs. Update the effect to
track whether it has been disposed and, after listen resolves, immediately call
the returned unlisten if the component already unmounted; keep the existing
cleanup path in useEffect so the subscription is always removed.

In `@src/components/player/ImmersiveShareButton.tsx`:
- Around line 102-118: The action buttons in ImmersiveShareButton are missing an
explicit button type, so they can act as submit buttons inside a form; update
each menu action button in the component to use type="button" while keeping the
existing onClick handlers for handleSave and handleCopy.

In `@src/components/player/ImmersiveView.tsx`:
- Around line 109-147: The fullscreen enter/exit effect in ImmersiveView can
race on a fast open→close, leaving the window fullscreen after unmount because
the cleanup skips restoration while win.setFullscreen(true) is still in flight.
In the useEffect around enteredFullscreenRef and priorMaximizedRef, track an
explicit “enter in progress” state or cancellation flag that is checked again
after the await, and if the component has already been cleaned up, immediately
undo the fullscreen change and restore the prior maximized state before setting
enteredFullscreenRef.current.

In `@src/components/player/PlayerBar.tsx`:
- Around line 409-412: The `ImmersiveView` rendering in `PlayerBar` is
incorrectly guarded by `currentTrack`, which lets the modal unmount without
running `closeImmersive` when the track disappears. Remove the `currentTrack`
condition from the `immersiveOpen` check so `ImmersiveView` stays mounted while
open, and rely on `ImmersiveView` itself to handle missing track state; keep
`closeImmersive` as the only way to clear `immersiveOpen`.

In `@src/contexts/PlayerContext.tsx`:
- Around line 200-203: Reset the immersive UI state in PlayerContext whenever
the active profile/track is cleared, because immersiveOpen can stay true after
currentTrack becomes null and reopen ImmersiveView on the next track. Update the
PlayerContext state handling around immersiveOpen, setImmersiveOpen, and
currentTrack so the immersive view is explicitly closed when the track/profile
reset happens, and keep immersiveInitialTab in sync if needed.

In `@src/hooks/useImmersivePrefs.ts`:
- Around line 74-104: Le hook useImmersivePrefs peut réappliquer des préférences
périmées car refresh ne se relance qu’au mount/événement custom et les réponses
async peuvent arriver hors ordre. Faites dépendre le useEffect d’un signal de
changement de profil (ou équivalent) et ajoutez une garde de
séquence/cancellation dans refresh pour ignorer toute lecture obsolète avant
setPrefs, afin que ImmersiveViewCard reflète toujours le bon profil.

In `@src/hooks/useTrackLyrics.ts`:
- Around line 294-296: The applyPayload callback in useTrackLyrics only updates
the payload and leaves any previous error state visible, which can mask newly
applied external lyrics. Update applyPayload so that when a non-null
LyricsPayload is applied it also clears the existing error state, matching the
cleanup already done in importLyrics; use the applyPayload and importLyrics
callbacks in useTrackLyrics as the main reference points.
- Around line 109-115: The `useTrackLyrics` effect branch for `trackId == null`
currently resets payload and error but leaves `isFetching` unchanged, so the
spinner can stay stuck after a canceled request. Update the `trackId == null`
handling in `useTrackLyrics` to explicitly clear `isFetching` along with
`setPayload(null)` and `setError(null)`, keeping `prevIsRadioRef` in sync before
returning.
- Around line 277-285: Le callback clear() dans useTrackLyrics peut appliquer
setPayload(null) après un changement de piste, ce qui risque d’effacer l’état de
la nouvelle piste. Ajoute le même garde que dans importLyrics et refetch en
comparant requestedTrackId à trackIdRef.current juste après await
clearLyrics(trackId) et avant setPayload(null). Si la piste a changé
entre-temps, quitte sans modifier l’état; conserve aussi la gestion d’erreur
existante.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ef0d070b-be6b-4bda-b53c-884e43190a10

📥 Commits

Reviewing files that changed from the base of the PR and between 66fc8ae and 836e744.

📒 Files selected for processing (38)
  • CLAUDE.md
  • docs/features/integrations.md
  • docs/features/ui.md
  • src-tauri/crates/app/capabilities/default.json
  • src/components/common/OnboardingModal.tsx
  • src/components/layout/LyricsPanel.tsx
  • src/components/player/FullscreenLyrics.tsx
  • src/components/player/FullscreenNowPlaying.tsx
  • src/components/player/ImmersiveLyricsColumn.tsx
  • src/components/player/ImmersiveNowPlaying.tsx
  • src/components/player/ImmersiveQueueTab.tsx
  • src/components/player/ImmersiveShareButton.tsx
  • src/components/player/ImmersiveSidePanel.tsx
  • src/components/player/ImmersiveView.tsx
  • src/components/player/PlayerBar.tsx
  • src/components/views/SettingsView.tsx
  • src/components/views/settings/ImmersiveViewCard.tsx
  • src/contexts/PlayerContext.tsx
  • src/hooks/useImmersivePrefs.ts
  • src/hooks/usePlayer.ts
  • src/hooks/useTrackLyrics.ts
  • src/i18n/locales/ar.json
  • src/i18n/locales/de.json
  • src/i18n/locales/en.json
  • src/i18n/locales/es.json
  • src/i18n/locales/fr.json
  • src/i18n/locales/hi.json
  • src/i18n/locales/id.json
  • src/i18n/locales/it.json
  • src/i18n/locales/ja.json
  • src/i18n/locales/ko.json
  • src/i18n/locales/nl.json
  • src/i18n/locales/pt-BR.json
  • src/i18n/locales/pt.json
  • src/i18n/locales/ru.json
  • src/i18n/locales/tr.json
  • src/i18n/locales/zh-CN.json
  • src/i18n/locales/zh-TW.json
💤 Files with no reviewable changes (2)
  • src/components/player/FullscreenLyrics.tsx
  • src/components/player/FullscreenNowPlaying.tsx

Comment thread docs/features/ui.md Outdated
Comment thread src/components/player/ImmersiveQueueTab.tsx
Comment thread src/components/player/ImmersiveQueueTab.tsx
Comment thread src/components/player/ImmersiveShareButton.tsx
Comment thread src/components/player/ImmersiveView.tsx
Comment thread src/contexts/PlayerContext.tsx
Comment thread src/hooks/useImmersivePrefs.ts Outdated
Comment thread src/hooks/useTrackLyrics.ts
Comment thread src/hooks/useTrackLyrics.ts
Comment thread src/hooks/useTrackLyrics.ts
- useTrackLyrics: clear isFetching on track->null, guard clear() against
  track switch, clear error in applyPayload (match import/refetch)
- useImmersivePrefs: re-read on profile switch + sequence guard vs
  out-of-order async reads
- ImmersiveView: undo fullscreen if cleanup raced setFullscreen(true)
- ImmersiveQueueTab: keep last queue on transient fetch error, fix
  listener leak when unmounted before listen() resolves
- ImmersiveShareButton: type="button" on menu actions
- PlayerBar/PlayerContext: drop currentTrack mount guard, close immersive
  on profile reset so it can't reopen on the next track
- docs: cover_action `immersive` wording matches actual opt-in behaviour
@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai thanks — all findings were valid, fixed in 37767a7:

  • useTrackLyrics: applyPayload now clears error when a non-null payload is applied (mirrors importLyrics); the trackId == null branch clears isFetching too; clear() got the same requestedTrackId !== trackIdRef.current staleness guard as importLyrics/refetch.
  • useImmersivePrefs: the effect now depends on the active profile id (re-reads per-profile prefs on switch) and a monotonic seqRef drops out-of-order async reads before setPrefs.
  • ImmersiveView: after setFullscreen(true) resolves, if the cleanup already ran (cancelled) we undo the fullscreen + restore the prior maximized state instead of leaving the window stuck fullscreen.
  • ImmersiveQueueTab: the fetch catch only logs now (keeps the last queue snapshot instead of blanking it); the listen setup tracks a disposed flag and tears the subscription down immediately if it resolves after unmount.
  • ImmersiveShareButton: type="button" on both menu actions.
  • PlayerBar + PlayerContext: dropped the currentTrack mount guard so the view unmounts only via closeImmersive, and the profile-reset effect now sets immersiveOpen = false so it can't linger through the currentTrack null window and reopen on the next track.
  • docs: the ui.cover_action immersive row no longer promises a guaranteed merged/fullscreen view — it states the merged columns + native fullscreen are opt-in, classic by default.

typecheck / lint / build all green.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@InstaZDLL Looks good — thanks for addressing these thoroughly and for calling out the validation pass. The fixes you listed cover the real regression risks I flagged.

🐇✅

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
src/hooks/useImmersivePrefs.ts (1)

75-115: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Réinitialise loaded pendant le changement de profil.

prefs conserve les valeurs du profil précédent jusqu’à la fin de la nouvelle lecture. Pendant cette fenêtre, ImmersiveView peut appliquer l’ancien mergedLyrics / useNativeFullscreen au nouveau profil.

Correctif proposé
   useEffect(() => {
     let cancelled = false;
+    // eslint-disable-next-line react-hooks/set-state-in-effect
+    setPrefs({ ...DEFAULTS, loaded: false });
     const refresh = async () => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useImmersivePrefs.ts` around lines 75 - 115, `useImmersivePrefs`
keeps the previous profile’s values during a profile switch because `loaded`
stays true until `refresh` finishes. Update the `useEffect` in
`useImmersivePrefs` so that when `activeProfileId` changes it immediately resets
`prefs` back to `DEFAULTS` (with `loaded` false) before starting the async read,
then let `refresh` replace it with the new profile’s values once
`getProfileSetting` resolves.
src/hooks/useTrackLyrics.ts (1)

280-289: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Efface aussi l’erreur après un clear() réussi.

Après setPayload(null), une erreur précédente reste dans l’état et peut continuer à masquer l’état vide attendu.

Correctif proposé
-      await clearLyrics(trackId);
+      await clearLyrics(requestedTrackId);
       if (requestedTrackId !== trackIdRef.current) return;
       setPayload(null);
+      setError(null);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useTrackLyrics.ts` around lines 280 - 289, The clear path in
useTrackLyrics’s clear callback leaves a previous error in state even after a
successful clear, so the empty state can still be masked. Update the clear()
flow after clearLyrics(trackId) succeeds and the staleness guard passes to also
reset the error state alongside setPayload(null), using the existing state
setter in useTrackLyrics so a successful clear fully reflects the empty/no-error
state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/hooks/useImmersivePrefs.ts`:
- Around line 75-115: `useImmersivePrefs` keeps the previous profile’s values
during a profile switch because `loaded` stays true until `refresh` finishes.
Update the `useEffect` in `useImmersivePrefs` so that when `activeProfileId`
changes it immediately resets `prefs` back to `DEFAULTS` (with `loaded` false)
before starting the async read, then let `refresh` replace it with the new
profile’s values once `getProfileSetting` resolves.

In `@src/hooks/useTrackLyrics.ts`:
- Around line 280-289: The clear path in useTrackLyrics’s clear callback leaves
a previous error in state even after a successful clear, so the empty state can
still be masked. Update the clear() flow after clearLyrics(trackId) succeeds and
the staleness guard passes to also reset the error state alongside
setPayload(null), using the existing state setter in useTrackLyrics so a
successful clear fully reflects the empty/no-error state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ab8175cd-ecfc-4717-9ada-cde7f47b1400

📥 Commits

Reviewing files that changed from the base of the PR and between 836e744 and 37767a7.

📒 Files selected for processing (8)
  • docs/features/ui.md
  • src/components/player/ImmersiveQueueTab.tsx
  • src/components/player/ImmersiveShareButton.tsx
  • src/components/player/ImmersiveView.tsx
  • src/components/player/PlayerBar.tsx
  • src/contexts/PlayerContext.tsx
  • src/hooks/useImmersivePrefs.ts
  • src/hooks/useTrackLyrics.ts

@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai both valid, fixed in 3d804be:

  • useImmersivePrefs: the effect now resets prefs to { ...DEFAULTS, loaded: false } up front, so a profile switch never surfaces the previous profile's values during the async re-read. It's a no-op on first mount (already DEFAULTS) and isn't run on the custom IMMERSIVE_PREFS_EVENT (that calls refresh directly), so toggling a setting doesn't flash the card to defaults.
  • useTrackLyrics clear(): after a successful clearLyrics + the staleness guard, it now also setError(null) so a prior fetch error can't mask the empty state. Also switched the call to clearLyrics(requestedTrackId) for consistency with the captured id.

typecheck / lint green.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@InstaZDLL: Looks good — these fixes address the remaining user-facing edge cases I was worried about.

In particular:

  • resetting prefs before the async profile reload avoids leaking the previous profile’s settings into the UI
  • clearing error in useTrackLyrics.clear() prevents a stale fetch failure from hiding the true empty state

Thanks for tightening this up, and for confirming typecheck/lint are green.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/hooks/useImmersivePrefs.ts`:
- Around line 83-90: The `useImmersivePrefs` refresh flow resets state to
`loaded:false`, but the error path never publishes a დასრულებული state, so
callers can stay blocked after a Tauri read failure. Update the `refresh` logic
in `useImmersivePrefs` so the `catch` path still sets a final prefs state based
on `DEFAULTS` for the current refresh, with `loaded` true, and keep the existing
`setPrefs({ ...DEFAULTS, loaded: false })` only as the temporary pre-read reset.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 94811dec-a95e-40ea-987d-c318d1b49a18

📥 Commits

Reviewing files that changed from the base of the PR and between 37767a7 and 3d804be.

📒 Files selected for processing (2)
  • src/hooks/useImmersivePrefs.ts
  • src/hooks/useTrackLyrics.ts

Comment thread src/hooks/useImmersivePrefs.ts
@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai valid, fixed in 98e3b8c — the refresh catch now publishes setPrefs({ ...DEFAULTS, loaded: true }) (guarded by cancelled/seq) so a failed Tauri read still resolves loaded instead of leaving it stuck at the pre-read false reset. typecheck + lint green.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@InstaZDLL: Nice catch — that fixes the real failure mode.

Publishing { ...DEFAULTS, loaded: true } on the read-error path is the right fallback here: the UI no longer gets stuck in a perpetual loading state, and the cancelled/seq guards keep the async behavior safe. Thanks for tightening it up and confirming typecheck/lint are green.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/player/ImmersiveLyricsColumn.tsx`:
- Around line 84-90: The cover button in ImmersiveLyricsColumn uses an
accessibility label that describes the wrong action. Update the button’s
aria-label and title to a dedicated “return to classic lyrics/view” label
instead of playerBar.openFullscreen, since ImmersiveView passes
onCoverClick={toggleClassicLyrics} and the click does not open fullscreen. Keep
the change localized to the cover-click button and use the existing i18n pattern
so screen readers and the tooltip announce the actual behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 972357fa-e278-4d62-acc1-90e15e569678

📥 Commits

Reviewing files that changed from the base of the PR and between 98e3b8c and cdc953c.

📒 Files selected for processing (2)
  • src/components/player/ImmersiveLyricsColumn.tsx
  • src/components/player/ImmersiveView.tsx

Comment thread src/components/player/ImmersiveLyricsColumn.tsx
@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai valid, fixed in the latest push — the cover-click button now uses t("playerBar.nowPlaying") ("Vue en cours de lecture" / "Now Playing view") instead of playerBar.openFullscreen, since onCoverClick={toggleClassicLyrics} flips back to the now-playing view rather than opening fullscreen. Reused the existing key (present in all 17 locales) so no new i18n. typecheck + lint green.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@InstaZDLL Good fix — that label now matches the actual action, which avoids misleading screen-reader users and anyone relying on the tooltip/accessible name. Reusing the existing key across all locales is the right move too.

Thanks for tightening it up and confirming typecheck + lint are green.

@InstaZDLL InstaZDLL merged commit 8534d45 into main Jun 28, 2026
14 checks passed
@InstaZDLL InstaZDLL deleted the feat/immersive-merged-fullscreen branch June 28, 2026 22:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: backend Rust/Tauri backend (src-tauri/) scope: docs Docs, README, assets scope: frontend React/Vite frontend (src/) scope: i18n Translations (src/i18n/) size: xl > 500 lines type: feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

immersive view: merge lyrics into now-playing as a side column + use native window fullscreen

1 participant