Skip to content

fix(ui): immersive view skin neutrality + long-title marquee#334

Merged
InstaZDLL merged 7 commits into
mainfrom
fix/skin-immersive-and-liquid-polish
Jun 29, 2026
Merged

fix(ui): immersive view skin neutrality + long-title marquee#334
InstaZDLL merged 7 commits into
mainfrom
fix/skin-immersive-and-liquid-polish

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented Jun 28, 2026

Copy link
Copy Markdown
Owner

Summary

Polish pass on the immersive view + skins, reported visually during testing.

Immersive view was wrecked by the skins

The immersive view (role="dialog" + a shadow-2xl cover) was captured by every skin's modal / surface / text-colour chrome:

  • Liquid (light) + Editorial repainted it as a light glass slab with dark, invisible text and washed-out (grey-on-grey) controls.
  • Lounge / Pulse flattened the cover backdrop.

Fix: the root is tagged data-immersive + a nested dark context (so the shared PlaybackControls / VolumeControl / ProgressBar render their dark-theme variants over the always-dark backdrop), and each skin's dialog / surface / text-colour rules carry :not([data-immersive]):not([data-immersive] *). The view now renders identically across all skins.

Long titles scroll instead of truncating

New MarqueeText — measures overflow via ResizeObserver and, only when the text actually overflows, glides it end-to-end and back (ping-pong with a pause at each end, prefers-reduced-motion respected). Wired into the now-playing title + the bottom PlayerBar title.

  • Per-profile Settings → Appearance toggle (ui.scroll_long_titles, default ON) via useScrollLongTitles + ScrollTitlesCard; off = ellipsis truncation.
  • Also fixes descenders (g / y / p) being clipped in the immersive title.

Notes

  • i18n propagated to all 17 locales; docs/features/ui.md updated.
  • Validated visually across Liquid (light + dark), Editorial, Lounge, Pulse.

Validation

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

Summary by CodeRabbit

  • Nouvelles fonctionnalités
    • Les titres longs peuvent défiler en mode “marquee” dans la vue immersive et la barre du lecteur.
    • Nouveau réglage par profil pour activer/désactiver ce défilement (activé par défaut).
  • Améliorations
    • Le défilement ne démarre que si le titre déborde réellement, et il respecte “réduire les animations”.
    • La vue immersive est mieux isolée des styles de thème (modales/surfaces/couleurs) pour un rendu plus cohérent.
  • Documentation
    • Mise à jour des notes sur la vue immersive et le défilement des titres.
  • Traductions
    • Nouvelles chaînes ajoutées pour l’option dans les langues disponibles.

…oss all skins

The immersive view has role="dialog" + a shadow-2xl cover, so skins captured
it with their modal / surface / text-colour chrome — Liquid (light) and
Editorial repainted it as a light glass slab with dark, invisible text and
washed-out controls; Lounge / Pulse flattened the cover backdrop.

- mark the immersive root with data-immersive + a `dark` context (so the
  shared PlaybackControls / VolumeControl / ProgressBar render their
  dark-theme variants over the always-dark backdrop)
- exclude [data-immersive] (and its subtree) from each skin's dialog
  background, surface neutralization, and text-colour overrides so the
  view renders with its own styling, identical across skins
Long track titles now glide end-to-end and back instead of being cut by
an ellipsis, in both the PlayerBar and the immersive now-playing title.

- MarqueeText: measures overflow via ResizeObserver, animates only when
  the text actually overflows (ping-pong with a pause at each end),
  static + truncated otherwise; respects prefers-reduced-motion
- per-profile Settings -> Appearance toggle (ui.scroll_long_titles,
  default ON) via useScrollLongTitles + ScrollTitlesCard; off = truncate
- also fixes descenders (g / y / p) being clipped in the immersive title
  (leading-tight + pb-1 under the overflow:hidden clip)
- i18n 17 locales
@InstaZDLL InstaZDLL added scope: frontend React/Vite frontend (src/) scope: i18n Translations (src/i18n/) scope: docs Docs, README, assets type: fix Bug fix size: l 200-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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e4e4998c-4805-4831-9fae-470a2d4444ac

📥 Commits

Reviewing files that changed from the base of the PR and between 104cd0f and cf89452.

📒 Files selected for processing (1)
  • src/hooks/useScrollLongTitles.ts

📝 Walkthrough

Walkthrough

Ajout du défilement conditionnel des titres longs avec préférence persistée par profil, intégration dans la barre du lecteur et la vue immersive, marquage de la vue immersive, exclusions CSS des skins, et nouvelles chaînes de traduction avec documentation.

Changes

Défilement des titres longs et isolation skin immersive

Layer / File(s) Summary
Marquee et préférence de titres
src/app.css, src/hooks/useScrollLongTitles.ts, src/components/common/MarqueeText.tsx
Ajoute l’animation marquee, la préférence persistée ui.scroll_long_titles, et le composant qui n’anime le titre que quand il déborde et que l’option est active.
Titres défilants dans le lecteur
src/components/player/ImmersiveNowPlaying.tsx, src/components/player/PlayerBar.tsx
Remplace l’affichage tronqué du titre par MarqueeText dans les deux zones du lecteur.
Carte de réglage et intégration
src/components/views/settings/ScrollTitlesCard.tsx, src/components/views/SettingsView.tsx
Crée la carte de réglage et l’ajoute dans la section appearance des paramètres.
Marquage immersive et isolation skin
src/components/player/ImmersiveView.tsx, src/styles/skins/editorial.css, src/styles/skins/liquid.css, src/styles/skins/lounge.css, src/styles/skins/pulse.css
Ajoute data-immersive à la vue immersive et exclut ce sous-arbre des surcharges de modale, surface et couleur des skins.
Locales et documentation
src/i18n/locales/*.json, docs/features/ui.md
Ajoute les clés scrollTitles dans les locales et documente le comportement immersive et le défilement des titres.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • InstaZDLL/WaveFlow#83 : modifie aussi src/components/views/SettingsView.tsx autour de la section appearance.
  • InstaZDLL/WaveFlow#259 : touche les mêmes règles de src/styles/skins/{editorial,liquid,lounge,pulse}.css pour exclure [data-immersive].
  • InstaZDLL/WaveFlow#333 : concerne les mêmes composants immersive/lecteur (ImmersiveView, ImmersiveNowPlaying, PlayerBar).

Poem

Les titres trop longs se mettent à danser,
Sans casser la vue ni les skins croiser.
Un geste dans les réglages, et hop, c’est parti,
Le texte file en boucle, puis revient au lit.
Dans l’immersif, la frontière tient bon ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% 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 suit le format Conventional Commits et résume bien les deux changements majeurs : skin neutrality de l’immersive view et défilement des longs titres.
Description check ✅ Passed La description couvre le pourquoi, les effets UI, les locales, la doc et la validation; il manque surtout les sections explicites How I tested, captures et checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/skin-immersive-and-liquid-polish

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: 2

🤖 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/common/MarqueeText.tsx`:
- Around line 32-79: The reduced-motion fallback in MarqueeText is broken
because animate is still true even when the CSS animation is disabled, leaving
the text in the scrolling layout without truncation or centering. Update the
MarqueeText component’s animate/branching logic so it also respects
prefers-reduced-motion, and when motion is reduced force the non-animated
truncate path instead of the inline-block whitespace-nowrap + overflow-hidden
path. Make sure the fix preserves centered alignment for the immersive title and
restores ellipsis behavior in PlayerBar by tying the render branch to the
reduced-motion state alongside enabled, shift, and the existing useEffect
measurement logic.

In `@src/hooks/useScrollLongTitles.ts`:
- Around line 52-64: The write flow in useScrollLongTitles’s setEnabled
currently allows overlapping setProfileSetting calls, which can let an older
response overwrite the latest user intent. Update setEnabled to serialize or
coalesce preference writes, and ensure only the most recent request is allowed
to call setEnabledState and dispatch SCROLL_LONG_TITLES_EVENT. Use the existing
setEnabled callback, enabled state, and rollback path to guard against stale
responses before applying any success or failure side effects.
🪄 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: 15fb2a3a-a59a-4aec-8483-524e989493b0

📥 Commits

Reviewing files that changed from the base of the PR and between 8534d45 and f6e3209.

📒 Files selected for processing (30)
  • docs/features/ui.md
  • src/app.css
  • src/components/common/MarqueeText.tsx
  • src/components/player/ImmersiveNowPlaying.tsx
  • src/components/player/ImmersiveView.tsx
  • src/components/player/PlayerBar.tsx
  • src/components/views/SettingsView.tsx
  • src/components/views/settings/ScrollTitlesCard.tsx
  • src/hooks/useScrollLongTitles.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
  • src/styles/skins/editorial.css
  • src/styles/skins/liquid.css
  • src/styles/skins/lounge.css
  • src/styles/skins/pulse.css

Comment thread src/components/common/MarqueeText.tsx
Comment thread src/hooks/useScrollLongTitles.ts Outdated
@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai both valid, fixed:

  • MarqueeText: the render branch now gates on prefers-reduced-motion (via framer-motion's useReducedMotion) alongside enabled + shift — when motion is reduced, scroll is false so it takes the static truncate path (ellipsis + inherited centering) instead of the scrolling layout whose CSS animation was disabled but whose left-aligned nowrap layout remained.
  • useScrollLongTitles: setEnabled now bumps a monotonic writeSeqRef and captures the rollback target from an enabledRef mirror; only the latest write applies its success (dispatch) / failure (rollback) side effects, so an older overlapping response can't clobber the newer intent.

typecheck + lint + build 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 bien vu — ces deux correctifs couvrent bien les problèmes signalés.

Noté aussi pour la validation : typecheck, lint et build sont verts.

(•ᴗ•)🐇


✏️ Learnings added
Learnt from: InstaZDLL
URL: https://github.com/InstaZDLL/WaveFlow/pull/334

Timestamp: 2026-06-28T23:43:34.700Z
Learning: In `src/components/common/MarqueeText.tsx`, reduced-motion must be handled in the render branch, not only in CSS. The component now uses `framer-motion`'s `useReducedMotion` so when motion is reduced it takes the static `truncate` path instead of rendering the scrolling nowrap layout with animation disabled.

Learnt from: InstaZDLL
URL: https://github.com/InstaZDLL/WaveFlow/pull/334

Timestamp: 2026-06-28T23:43:34.700Z
Learning: In `src/hooks/useScrollLongTitles.ts`, overlapping async writes from `setEnabled` must not let stale responses override newer user intent. The hook now uses a monotonic `writeSeqRef` and an `enabledRef`-based rollback target so only the latest write applies dispatch or rollback side effects.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@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 (1)
src/hooks/useScrollLongTitles.ts (1)

36-37: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Sérialisez vraiment les écritures Tauri.

writeSeqRef empêche seulement les effets locaux obsolètes. Les setProfileSetting(...) de Line 67 partent encore en parallèle : si l’utilisateur bascule true → false et que l’écriture true termine après false, le profil peut rester persisté à true sans nouvel événement, car Line 70 ignore seulement le side effect local. Chaînez/coalescez les writes, et rollbackez vers une valeur confirmée plutôt que vers enabledRef optimiste.

Correctif possible
   const enabledRef = useRef(enabled);
+  const confirmedEnabledRef = useRef(enabled);
+  const writeChainRef = useRef<Promise<void>>(Promise.resolve());
   const writeSeqRef = useRef(0);
@@
-        setEnabledState(parseEnabled(raw));
+        const parsed = parseEnabled(raw);
+        enabledRef.current = parsed;
+        confirmedEnabledRef.current = parsed;
+        setEnabledState(parsed);
@@
   const setEnabled = useCallback(async (next: boolean) => {
     const seq = ++writeSeqRef.current;
-    const previous = enabledRef.current;
     enabledRef.current = next;
     setEnabledState(next);
     try {
-      await setProfileSetting(KEY, next ? "true" : "false", "bool");
+      const write = writeChainRef.current.then(async () => {
+        await setProfileSetting(KEY, next ? "true" : "false", "bool");
+        confirmedEnabledRef.current = next;
+      });
+      writeChainRef.current = write.catch(() => undefined);
+      await write;
       // A newer toggle superseded this one mid-write — let it own the
       // outcome so an older response can't clobber the latest intent.
       if (seq !== writeSeqRef.current) return;
       window.dispatchEvent(new CustomEvent(SCROLL_LONG_TITLES_EVENT));
     } catch (err) {
       console.error("[useScrollLongTitles] write failed", err);
       if (seq !== writeSeqRef.current) return;
-      enabledRef.current = previous;
-      setEnabledState(previous);
+      const rollback = confirmedEnabledRef.current;
+      enabledRef.current = rollback;
+      setEnabledState(rollback);
     }
   }, []);

As per path instructions, src/**: « Vérifie les erreurs de state/effects, les handlers qui lancent des commandes Tauri ».

Also applies to: 48-48, 61-76

🤖 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/useScrollLongTitles.ts` around lines 36 - 37, `useScrollLongTitles`
ne sérialise pas réellement les écritures Tauri: `writeSeqRef` ne filtre que les
effets locaux obsolètes, mais les appels à `setProfileSetting(...)` peuvent
encore s’exécuter en parallèle et laisser un état persistant incorrect. Modifie
l’effet/la logique autour de `enabledRef`, `writeSeqRef` et `setProfileSetting`
pour chaîner ou coalescer les writes, puis reviens à une valeur confirmée par la
dernière écriture réussie plutôt que de restaurer directement la valeur
optimiste de `enabledRef`.

Source: Path instructions

🤖 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/useScrollLongTitles.ts`:
- Around line 36-37: `useScrollLongTitles` ne sérialise pas réellement les
écritures Tauri: `writeSeqRef` ne filtre que les effets locaux obsolètes, mais
les appels à `setProfileSetting(...)` peuvent encore s’exécuter en parallèle et
laisser un état persistant incorrect. Modifie l’effet/la logique autour de
`enabledRef`, `writeSeqRef` et `setProfileSetting` pour chaîner ou coalescer les
writes, puis reviens à une valeur confirmée par la dernière écriture réussie
plutôt que de restaurer directement la valeur optimiste de `enabledRef`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3d7760fc-f4bf-4d24-b57a-adfc95d74e6b

📥 Commits

Reviewing files that changed from the base of the PR and between f6e3209 and d56f70f.

📒 Files selected for processing (2)
  • src/components/common/MarqueeText.tsx
  • src/hooks/useScrollLongTitles.ts

@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai valid, fixed — the Tauri writes are now serialized on a writeChainRef promise chain (each setProfileSetting runs after the previous resolves, .catch keeps the chain alive on failure), so the backend applies toggles in issue order and the last write wins. On success the written value is recorded in confirmedEnabledRef; a failed write now rolls back to that confirmed value instead of the optimistic enabledRef. The read path (refresh) seeds both refs too. 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 bien vu — ce correctif répond au problème.

La sérialisation via writeChainRef, avec rollback vers confirmedEnabledRef plutôt que l’état optimiste, élimine bien le risque d’écrasement hors ordre côté Tauri. Noté aussi pour refresh qui réamorce les deux refs, et pour typecheck + lint verts.


✏️ Learnings added
Learnt from: InstaZDLL
URL: https://github.com/InstaZDLL/WaveFlow/pull/334

Timestamp: 2026-06-28T23:51:33.480Z
Learning: In `src/hooks/useScrollLongTitles.ts`, writes triggered by `setEnabled` should be serialized through a `writeChainRef` promise chain so Tauri backend updates are applied in issue order and the last write wins. Successful writes update `confirmedEnabledRef`, failed writes must roll back to that confirmed value rather than the optimistic `enabledRef`, and `refresh` should seed both refs.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@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/useScrollLongTitles.ts`:
- Around line 37-43: The write queue in useScrollLongTitles can apply an
outdated toggle after activeProfile changes, causing persistence to hit the
wrong profile. Update the toggle/write flow around confirmedEnabledRef,
writeChainRef, and setProfileSetting so each queued write captures the current
profile id at dispatch time and aborts before the Tauri call if
activeProfile?.id has changed, or otherwise pass an explicit profile id through
the persistence API to avoid writing to a newly selected profile.
🪄 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: 8ef86ce2-6220-4364-9d46-3cbcc59e0cc2

📥 Commits

Reviewing files that changed from the base of the PR and between d56f70f and 749090e.

📒 Files selected for processing (1)
  • src/hooks/useScrollLongTitles.ts

Comment thread src/hooks/useScrollLongTitles.ts
@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai valid, fixed in 104cd0fsetEnabled now captures activeProfileIdRef.current at dispatch time, and the queued chain task aborts (skips the setProfileSetting + the confirmedEnabledRef update) if the active profile changed while it waited, so a toggle issued on profile A can't persist onto profile B after a switch. The refresh effect already re-reads per-profile on activeProfile?.id, so the new profile's value is reflected correctly. 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 bien vu — ce correctif ferme bien le trou de persistance cross-profile.

Capturer activeProfileIdRef.current au moment du dispatch puis annuler la tâche chaînée si le profil a changé évite qu’un toggle parti sur le profil A s’écrive sur le profil B en attente. Le fait que refresh relise déjà sur activeProfile?.id complète bien le flux. Noté aussi pour typecheck + lint verts.


✏️ Learnings added
Learnt from: InstaZDLL
URL: https://github.com/InstaZDLL/WaveFlow/pull/334

Timestamp: 2026-06-28T23:58:34.841Z
Learning: In `src/hooks/useScrollLongTitles.ts`, queued writes from `setEnabled` must be guarded against active profile changes. Capture `activeProfileIdRef.current` when dispatching, and when the `writeChainRef` task runs, abort before calling `setProfileSetting` or updating `confirmedEnabledRef` if the active profile has changed, so a toggle issued for one profile cannot persist onto another after a profile switch.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/hooks/useScrollLongTitles.ts (1)

91-109: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Ignore aussi les effets de bord après un changement de profil en vol.

Le garde de Line 91 protège seulement avant l’appel Tauri. Si le profil change pendant setProfileSetting(...), Line 93 peut encore confirmer une valeur de l’ancien profil, et le catch peut rollback l’UI du nouveau profil vers cette valeur obsolète.

Correctif proposé
       if (activeProfileIdRef.current !== profileId) return;
       await setProfileSetting(KEY, next ? "true" : "false", "bool");
+      if (activeProfileIdRef.current !== profileId) return;
       confirmedEnabledRef.current = next;
     });
@@
       await write;
+      if (activeProfileIdRef.current !== profileId) return;
       // A newer toggle superseded this one mid-write — let it own the
       // outcome so an older response can't clobber the latest intent.
       if (seq !== writeSeqRef.current) return;
@@
     } catch (err) {
       console.error("[useScrollLongTitles] write failed", err);
+      if (activeProfileIdRef.current !== profileId) return;
       if (seq !== writeSeqRef.current) return;

As per path instructions, src/**: Vérifie les erreurs de state/effects, les handlers qui lancent des commandes Tauri....

🤖 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/useScrollLongTitles.ts` around lines 91 - 109, The write path in
useScrollLongTitles still applies side effects after a profile switch because
the guard only runs before setProfileSetting; add a second active-profile check
after the await in the write closure and again in the catch/rollback path so
stale writes from an old profile cannot confirm or revert the new profile’s
state. Update the logic around writeSeqRef, activeProfileIdRef,
confirmedEnabledRef, and setEnabledState so only the current profile can
dispatch SCROLL_LONG_TITLES_EVENT or perform rollback.

Source: Path instructions

🤖 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.

Outside diff comments:
In `@src/hooks/useScrollLongTitles.ts`:
- Around line 91-109: The write path in useScrollLongTitles still applies side
effects after a profile switch because the guard only runs before
setProfileSetting; add a second active-profile check after the await in the
write closure and again in the catch/rollback path so stale writes from an old
profile cannot confirm or revert the new profile’s state. Update the logic
around writeSeqRef, activeProfileIdRef, confirmedEnabledRef, and setEnabledState
so only the current profile can dispatch SCROLL_LONG_TITLES_EVENT or perform
rollback.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f7899f0e-4b87-4541-9f18-9fecc59bfdc5

📥 Commits

Reviewing files that changed from the base of the PR and between 749090e and 104cd0f.

📒 Files selected for processing (1)
  • src/hooks/useScrollLongTitles.ts

@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai valid, fixed in cf89452 — added a profile re-check after the setProfileSetting await (before confirming the value) and at the top of both the success and catch paths, so a profile switch mid-write can no longer confirm the old profile's value nor roll back / dispatch onto the new profile. Only the profile the toggle was issued for applies side effects. typecheck + lint green.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Tip

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

@InstaZDLL bien vu — ça ferme bien la dernière course cross-profile.

Le re-check après le await setProfileSetting(...), puis au début des branches succès/erreur, empêche désormais toute confirmation, rollback ou dispatch sur un profil devenu actif entre-temps. Le fait de limiter les effets de bord au profil d’origine est le bon correctif.

Noté aussi pour typecheck + lint verts.


✏️ Learnings added
Learnt from: InstaZDLL
URL: https://github.com/InstaZDLL/WaveFlow/pull/334

Timestamp: 2026-06-29T00:03:08.372Z
Learning: In `src/hooks/useScrollLongTitles.ts`, serializing writes is not sufficient on its own: when `setEnabled` performs an async `setProfileSetting`, it must re-check the captured active profile after the await and again before applying success or error side effects. This prevents `confirmedEnabledRef` updates, rollback, or event dispatch from leaking onto a newly active profile if the user switches profiles mid-write.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@InstaZDLL InstaZDLL merged commit be7b192 into main Jun 29, 2026
13 checks passed
@InstaZDLL InstaZDLL deleted the fix/skin-immersive-and-liquid-polish branch June 29, 2026 00:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: docs Docs, README, assets scope: frontend React/Vite frontend (src/) scope: i18n Translations (src/i18n/) size: l 200-500 lines type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant