Skip to content

feat(player-bar): redesign right cluster (Spotify-style) + overflow defaults#18

Merged
InstaZDLL merged 4 commits into
mainfrom
feat/player-bar-overflow-default
May 15, 2026
Merged

feat(player-bar): redesign right cluster (Spotify-style) + overflow defaults#18
InstaZDLL merged 4 commits into
mainfrom
feat/player-bar-overflow-default

Conversation

@InstaZDLL
Copy link
Copy Markdown
Owner

@InstaZDLL InstaZDLL commented May 15, 2026

Summary

Reshapes the right side of the player bar to follow the Spotify layout and the overflow-first doctrine from CLAUDE.md.

  • Overflow by default: A-B loop + Sleep timer now live in the "⋯" menu by default. Settings toggles re-purpose from "show in bar" to "pin in bar" (default OFF) — both features stay discoverable instead of being hidden until opt-in.
  • Spotify-style right cluster: Mini-player and Fullscreen are promoted to primary icon buttons sitting right after Volume, replacing their entries in the "⋯" menu (they're frequent enough to deserve a permanent slot).
  • Playback speed moves into the overflow menu (slider + 5 presets). The "⋯" trigger surfaces a compact 1.25× emerald badge when speed ≠ 1× so the live indicator stays visible without opening the menu. Sleep-timer countdown still wins the badge slot when armed.
  • Auto-hide "⋯" trigger when nothing inside would render (Spotify mode + both pinnable entries pinned).
  • SpeedControl.tsx removed (functionality folded into MoreActionsMenu).

Test plan

  • Default fresh profile: "⋯" trigger visible with Speed / A-B / Sleep panel inside; Mini-player + Fullscreen icons sit after Volume.
  • Pin Sleep timer in Settings → moon icon appears in bar, entry disappears from "⋯".
  • Pin both Sleep + A-B (non-Spotify mode) → "⋯" still visible with Speed only.
  • Spotify mode + both pinned → "⋯" trigger hidden entirely.
  • Change playback speed to 1.25× → emerald 1.25× badge appears on "⋯".
  • Arm sleep timer (15 min) → countdown badge replaces the speed badge.
  • Cancel sleep timer → speed badge returns if speed ≠ 1×.
  • Existing user with ui.show_sleep_timer = true reads as "pinned" → no behaviour regression.
  • All 17 locales: open Settings → Lecture and Settings → Réglages, confirm reworded titles render correctly.
  • bun run typecheck && bun run lint pass.

Summary by CodeRabbit

  • New Features
    • Playback speed controls moved into the player-bar overflow menu with a compact emerald speed badge when ≠1×; badge shows sleep-timer countdown when armed.
    • A–B loop and Sleep timer can be pinned to the player bar from Settings; pinned items are removed from overflow to avoid duplication.
  • Documentation
    • Updated player-bar, playback, and settings docs to reflect new layout, pinning flow, and UI copy (localized across languages).

Review Change Stack

InstaZDLL added 3 commits May 15, 2026 19:47
…ault

Both features were previously hidden behind opt-in visibility toggles
that, when ON, surfaced them as primary buttons. This violated the
overflow-first doctrine in CLAUDE.md and left typical users unable to
discover the features. Now both live in the "..." menu by default and
the Settings toggle re-purposes to "pin in bar" for frequent users.
…volume

Promotes the two view toggles to primary slots — they're frequent
enough that hiding them behind "..." cost a click on every use. The
overflow menu now only hosts sleep timer + A-B loop and auto-hides
its trigger when both are pinned.
Speed is used too rarely to deserve a permanent slot — most users
never touch 1×. The "..." trigger now shows a compact emerald badge
when speed differs from 1× so the live indicator stays visible
without opening the menu. Sleep-timer countdown still wins the
badge slot when armed.
@github-actions github-actions Bot added scope: frontend React/Vite frontend (src/) scope: i18n Translations (src/i18n/) scope: docs Docs, README, assets type: feat New feature size: xl > 500 lines labels May 15, 2026
@InstaZDLL InstaZDLL self-assigned this May 15, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: acf77b3f-bfe3-47dd-9fe3-87215bde94c8

📥 Commits

Reviewing files that changed from the base of the PR and between c322e54 and 6458185.

📒 Files selected for processing (18)
  • src/components/player/MoreActionsMenu.tsx
  • 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/kr.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

📝 Walkthrough

Walkthrough

This PR reorganizes player bar control placement by moving playback speed from a dedicated inline component into the overflow menu, refactoring that menu to host speed, A-B loop, and sleep timer controls with conditional visibility based on per-feature pin toggles, and updating all documentation and 16 locales to reflect the new "pin to bar" semantics.

Changes

Player Bar Overflow Refactoring

Layer / File(s) Summary
Documentation of UI Architecture Changes
CLAUDE.md, docs/features/playback.md, docs/features/ui.md
Repository architecture docs and feature guides are updated to describe the new three-tier player bar layout (Primary/Overflow/Pinnable), badge-driven overflow trigger behavior with sleep-timer countdown priority, and the shift from visibility toggles to pin-to-bar toggles for A-B loop and sleep timer.
MoreActionsMenu Component Refactoring
src/components/player/MoreActionsMenu.tsx
MoreActionsMenu is rewritten to serve as the player-bar overflow menu with conditional sections for playback speed (range slider + presets shown when showSpeed=true), A-B loop (shown when not pinned), and sleep timer (shown when not pinned, including cancel/preset/end-of-track/custom-duration forms). Props change from fullscreen/mini-player handlers to pin state flags and a sleepTimer callback object. Overflow trigger displays a badge showing armed sleep-timer countdown (with priority) or non-default playback speed.
PlayerBar Integration and State Transitions
src/components/player/PlayerBar.tsx
PlayerBar switches from showSleepTimer/showAbLoop visibility flags to pinSleepTimer/pinAbLoop pin state, rendering pinned controls (AbLoopButton, SleepTimerMenu) directly in the primary bar area. The compact SpeedControl component is removed. MoreActionsMenu is conditionally shown and wired with pin state and sleep-timer callbacks. Mini-player and fullscreen actions are moved from overflow to dedicated bar buttons. Spotify mode still hides speed controls entirely.
Internationalization Updates
src/i18n/locales/{ar,de,en,es,fr,hi,id,it,ja,kr,nl,pt-BR,pt,ru,tr,zh-CN,zh-TW}.json
All 16 locale files are consistently updated: playerBar.abLoop label is added for the A-B loop control, and settings.showSleepTimer and settings.showAbLoop titles/subtitles are revised to use "pin to the bar" terminology, replacing prior "show" wording and clarifying that controls remain accessible via the overflow menu when unpinned.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A bar with three tiers, so neat and precise,
Speed floats to the overflow—oh what a splice!
Pin A-B and timers, or let them drift free,
Sixteen tongues sing: "Come dance with the three!"
— The WaveFlow Rabbit

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% 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 The title accurately describes the main change: redesigning the right cluster of the player bar with a Spotify-style layout and implementing overflow-first defaults for controls.
Description check ✅ Passed The description covers all required template sections: summary with clear context and rationale, detailed test plan with concrete steps, and checklist items verified. The PR objectives align well with the description content.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/player-bar-overflow-default

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 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/MoreActionsMenu.tsx`:
- Around line 112-113: The button and panel currently use menu semantics
(aria-haspopup="menu" and role="menu"), which is incorrect for a panel with
sliders and form fields; change these to dialog/popover semantics by setting
aria-haspopup="dialog" (or removing aria-haspopup) and replace role="menu" with
role="dialog" (and add aria-modal={true} if the panel is modal) for the
corresponding elements in MoreActionsMenu.tsx (update both the trigger element
using aria-haspopup/aria-expanded and the panel element that currently has
role="menu"); ensure focus management matches dialog semantics (focus trap or
return focus on close) after making the role change.
- Around line 275-283: The formatRemaining function currently uses Math.ceil
when converting totalSec to minutes/hours which overstates remaining time (e.g.,
1h1m becomes 2h); change the conversions for hours and minutes to use Math.floor
instead of Math.ceil while keeping totalSec = Math.max(0, Math.ceil(ms/1000))
for second-level rounding. Update the logic in formatRemaining (the branches
that compute h and m) to compute h = Math.floor(totalSec / 3600) and m =
Math.floor(totalSec / 60) so the badge rounds down once it reaches minutes or
hours.
- Around line 249-257: The custom sleep input currently only uses a placeholder
so it has no stable accessible name; update the input in MoreActionsMenu (the
form using handleCustomSleep, state customMinutes and setter setCustomMinutes)
to include a programmatic label by either adding a <label> with htmlFor matching
an id on the input (e.g., id="custom-sleep-minutes") or by adding an
aria-label/aria-labelledby attribute with a descriptive string (e.g., "Custom
sleep minutes"), and ensure the input keeps min/max/value/onChange behavior and
the form submission via handleCustomSleep continues to work.
- Around line 115-120: The trigger is being tinted green even when speed
controls are hidden because isOffSpeed is true whenever playbackSpeed !== 1;
update the condition so the emerald tint only applies when speed UI is visible:
either change how isOffSpeed is computed (e.g., include showSpeed &&
playbackSpeed !== 1) or add showSpeed to the className ternary check so the
emerald/hover styles only apply when showSpeed is true; adjust references to
isOffSpeed, playbackSpeed, and showSpeed in MoreActionsMenu (around the
className/conditional that uses isOpen, sleepArmed, showSleepInMenu, isOffSpeed)
accordingly.

In `@src/i18n/locales/de.json`:
- Around line 1128-1133: The German subtitle strings for the entries containing
"title": "Sleep-Timer in der Leiste anheften" and "title": "A-B-Schleife in der
Leiste anheften" use an escaped ASCII double-quote (\") producing „…"; update
both "subtitle" values to use proper German matching quotes „…“ (replace the
trailing \" with the Unicode closing quote so each subtitle reads e.g. 'Wenn
aus, bleibt der Timer über das Menü „…“ erreichbar'). Ensure you change the
"subtitle" fields for the Sleep-Timer and A-B-Schleife entries (the keys shown
as the surrounding objects) only.

In `@src/i18n/locales/nl.json`:
- Around line 1129-1133: The Dutch subtitles for the keys "subtitle" under the
related entries (the one ending with menu „…\" and the "showAbLoop" subtitle)
use an escaped straight double-quote (\"), causing mismatched quotes; update
both subtitle strings to use a matching closing quote style (for example change
the trailing `\"` to `“` or the matching German-style closing `“` so the menu
reads `„…“`) so both lines use the same paired quote characters (refer to the
"subtitle" entries for the loop and showAbLoop objects).

In `@src/i18n/locales/tr.json`:
- Around line 1129-1133: The two Turkish subtitle strings use mixed quote
characters (`„…\"`) and should use a single consistent quote style; update the
subtitle value that currently reads "Kapalıyken zamanlayıcıya „…\" menüsünden
erişilebilir" and the "showAbLoop" object's subtitle to use a consistent
ellipsis menu reference (e.g., Kapalıyken zamanlayıcıya "…" menüsünden
erişilebilir and Kapalıyken A-B döngüsüne "…" menüsünden erişilebilir),
replacing `„…\"` with `"…"`.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2336a931-3784-48fa-8d07-08c40467ea02

📥 Commits

Reviewing files that changed from the base of the PR and between 9fa701c and c322e54.

📒 Files selected for processing (23)
  • CLAUDE.md
  • docs/features/playback.md
  • docs/features/ui.md
  • src/components/player/MoreActionsMenu.tsx
  • src/components/player/PlayerBar.tsx
  • src/components/player/SpeedControl.tsx
  • 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/kr.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 (1)
  • src/components/player/SpeedControl.tsx

Comment thread src/components/player/MoreActionsMenu.tsx Outdated
Comment thread src/components/player/MoreActionsMenu.tsx
Comment thread src/components/player/MoreActionsMenu.tsx
Comment thread src/components/player/MoreActionsMenu.tsx
Comment thread src/i18n/locales/de.json Outdated
Comment thread src/i18n/locales/nl.json Outdated
Comment thread src/i18n/locales/tr.json Outdated
- Switch role="menu" → role="dialog" (panel hosts sliders + form
  inputs, not menuitems) and matching aria-haspopup
- Round h/m countdown branches with floor instead of ceil so 1h 1m
  doesn't display as "2h"
- Add aria-label to the custom-minutes input (sleepTimer.customAriaLabel
  added to all 17 locales)
- Gate isOffSpeed behind showSpeed so the trigger doesn't tint green
  in Spotify mode when speed is hidden
- Fix mismatched German/Dutch/Turkish quote characters in the
  show* subtitles (proper „…" pair for de/nl, "…" for tr)
@InstaZDLL InstaZDLL merged commit ade9f13 into main May 15, 2026
13 checks passed
@InstaZDLL InstaZDLL deleted the feat/player-bar-overflow-default branch May 15, 2026 18:27
InstaZDLL added a commit that referenced this pull request May 15, 2026
…ckout/critical)

The previous one-workflow design checked out a release-please PR
branch inside a workflow_run context with contents: write and ran
cargo check on it. CodeQL flags that as critical (alert #18,
actions/untrusted-checkout/critical) because a privileged
workflow_run job must never execute code from a non-default ref —
the gh pr list author + branch-prefix gate is a soft filter, not
a security boundary, since CodeQL treats any non-default ref as
untrusted-by-default.

Split into the canonical two-workflow pattern:

- release-please-lockfile-build.yml (NEW, pull_request, contents:
  read): checks out the PR branch, runs cargo check, uploads the
  refreshed Cargo.lock + PR metadata as a 1-day artifact. Runs
  unprivileged so executing release-please's Cargo.toml /
  Cargo.lock through cargo can't reach secrets or push.

- release-please-bump-lockfile.yml (REWRITTEN, workflow_run,
  contents: write): no actions/checkout of the PR branch, no
  cargo invocation. Downloads the artifact, validates the
  Cargo.lock header + size envelope, re-fetches the PR via the
  API to confirm it's still open, authored by
  github-actions[bot], on the same head SHA the artifact was
  built against, then pushes the lockfile via the Git Data API
  (createBlob -> createTree -> createCommit -> updateRef).

Head-SHA check ensures we never push a Cargo.lock generated
against a stale Cargo.toml — if the bot pushed a follow-up while
the build was running, we skip and let the next build-workflow
artifact carry the fresh lock.
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: xl > 500 lines type: feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant