Skip to content

feat(theme): add light/dark/system appearance preference (#96)#111

Closed
thedavidweng wants to merge 17 commits into
mainfrom
feat/light-mode-96
Closed

feat(theme): add light/dark/system appearance preference (#96)#111
thedavidweng wants to merge 17 commits into
mainfrom
feat/light-mode-96

Conversation

@thedavidweng

@thedavidweng thedavidweng commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add ThemePreference (system|light|dark) persisted in AppConfig with a new set_theme_preference IPC command and core:window:allow-set-theme capability
  • Frontend theme-runtime hook resolves the preference against the OS prefers-color-scheme signal, applies data-theme + color-scheme before first paint via useLayoutEffect, and syncs the Tauri native window theme; the startup ready gate waits for the first native setTheme settlement (750ms timeout guard) so the hidden window is never shown with the wrong theme
  • Semantic CSS tokens replace hardcoded text-white/bg-white/rgba(255,255,255,…) across production components; the audience/fullscreen stage stays explicitly dark via a data-presentation-mode="audience" marker regardless of the primary preference
  • Settings gains an Appearance radio with en/zh-CN locale strings; the settings store action is optimistic with generation-based rollback
  • New settings IPC contract doc at docs/references/contracts/settings.md

Test plan

  • pnpm build (tsc + vite) passes
  • pnpm test — 1334 tests pass
  • pnpm lint — 0 warnings, 0 errors
  • cargo test — 389 Rust tests pass
  • cargo fmt via pre-commit hook
  • Patch coverage 84.6% (target 80%)
  • Manual platform matrix: macOS (WKWebView), Windows (WebView2), Linux (WebKitGTK) — verify light/dark/system switching, native window theme sync, and audience stage isolation

Open in Devin Review

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 77.78% (🎯 65%) 3571 / 4591
🔵 Statements 77.46% (🎯 65%) 3801 / 4907
🔵 Functions 68.93% (🎯 60%) 983 / 1426
🔵 Branches 71.41% (🎯 60%) 2099 / 2939
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/App.tsx 93.33% 100% 66.66% 92.85% 38
src/components/ErrorBoundary.tsx 50% 25% 60% 50% 29-32, 26-31, 36-50
src/components/Layout/DesktopTitlebar.tsx 31.81% 50% 20% 31.25% 144-147, 151-175, 180-203, 208-213, 272-363, 380-381
src/components/Layout/GlobalProgressBar.tsx 61.42% 26.92% 75% 58.33% 75-91, 110-114, 119-137, 142-159, 206
src/components/Layout/Sidebar.tsx 60.81% 57.97% 54.83% 56.14% 49, 53-56, 77-82, 94, 117-189, 205-317
src/components/Layout/SidebarRail.tsx 14.28% 60% 20% 14.28% 13, 28-56
src/components/Layout/ToastContainer.tsx 81.25% 100% 71.42% 76.92% 40-51
src/components/Layout/Toolbar.tsx 75% 70.58% 33.33% 75% 129-142
src/components/Library/ContextMenu.tsx 18.27% 20% 8% 18.88% 38-40, 45-54, 60-76, 81, 85-110, 115, 125, 132-255
src/components/Library/EmptyLibrary.tsx 100% 100% 100% 100%
src/components/Library/SearchBox.tsx 87.5% 100% 75% 83.33% 16
src/components/Library/SongListItem.tsx 42.35% 51.76% 41.37% 38.15% 72-78, 82-83, 87-132, 137-156, 162-356
src/components/Lyrics/LyricLine.tsx 97.29% 80.23% 100% 98.5% 97, 119
src/components/Lyrics/LyricsEditDialog.tsx 56.66% 50% 50% 59.25% 57-71, 79-102
src/components/Lyrics/LyricsFontSizeControl.tsx 75% 80% 57.14% 66.66% 30-55
src/components/Lyrics/LyricsOffsetControl.tsx 81.81% 100% 66.66% 71.42% 22-41
src/components/Overlay/Tooltip.tsx 97.64% 92.3% 100% 97.64% 132, 169
src/components/Playback/PlaybackStage.tsx 64% 80.55% 60% 57.89% 34-45
src/components/Player/MonitorPicker.tsx 85.45% 35% 78.94% 85.18% 80-81, 92-98, 102-103, 120-121
src/components/Player/NowPlayingInfo.tsx 52.77% 53.12% 62.5% 51.61% 22-27, 76-94
src/components/Player/PlayControls.tsx 95.45% 81.81% 100% 100% 26
src/components/Player/PlaybackBar.tsx 80% 74.35% 80% 78.04% 40, 45, 65-67, 72-76
src/components/Player/QueueButton.tsx 100% 66.66% 100% 100%
src/components/Player/QueuePanel.tsx 62% 40.5% 51.28% 56.32% 248-262, 292-293, 300-303, 309-312, 316, 336, 341-342, 347-366, 378-419, 427-429, 433, 438-445, 533-542
src/components/Player/RotationControls.tsx 31.48% 33.33% 33.33% 31.25% 17-20, 28-52, 115-121, 128-142, 178-195
src/components/Player/SeekBar.tsx 89.79% 85% 85.71% 90.47% 31-37, 52, 72
src/components/Player/SingerPickerDialog.tsx 42.85% 60% 33.33% 46.15% 23-29, 54, 91
src/components/Player/VolumeSliders.tsx 51.3% 52.22% 37.5% 49.53% 41, 44, 80-86, 95-103, 117-132, 139-143, 148-157, 162-166, 171-175, 180-184, 202-241, 302-363
src/components/Player/queue-dnd.ts 100% 100% 100% 100%
src/components/Settings/ConfirmationDialog.tsx 46.66% 50% 20% 50% 26, 30-36, 92
src/components/Settings/InputDialog.tsx 40.9% 33.33% 14.28% 42.85% 27-28, 32-40, 68-82, 97
src/components/Settings/LibrarySetup.tsx 69.93% 62.36% 53.48% 70.89% 170, 194, 196, 203, 212-229, 257-263, 272, 280, 284-290, 425, 524, 580-843
src/components/Settings/RemoteLibraryWizard.tsx 78.84% 78.21% 65.38% 79.41% 83-88, 109-114, 174, 181, 189-194, 202-213, 282-303, 342-414, 505
src/components/Settings/SettingsDangerZoneSection.tsx 71.42% 60.86% 44.44% 71.42% 67, 80-85, 106-135
src/components/Settings/SettingsExecutionProviderSection.tsx 81.81% 100% 66.66% 81.81% 62, 78
src/components/Settings/SettingsGeneralSection.tsx 70% 100% 57.14% 70% 50-99
src/components/Settings/SettingsLibrarySection.tsx 35.55% 47.5% 16.66% 35.55% 52-63, 91-97, 101-107, 131-337
src/components/Settings/SettingsModelVariantSection.tsx 79.16% 76% 50% 79.16% 50, 90, 101-109, 125-133
src/components/Settings/SettingsOverlay.context.ts 100% 100% 12.5% 100%
src/components/Settings/SettingsOverlay.state.ts 80% 68.42% 83.33% 79.45% 175-176, 181-194, 199-205, 216, 281
src/components/Settings/SettingsOverlay.tsx 100% 100% 100% 100%
src/components/Settings/SettingsSectionCard.tsx 100% 100% 100% 100%
src/components/Settings/SettingsStemModeSection.tsx 62.5% 100% 40% 62.5% 43, 57-64
src/components/Settings/settings-overlay.library-actions.ts 95% 82.22% 100% 94.89% 43, 86-96, 157, 162-164, 173-175
src/locales/en.json 100% 100% 100% 100%
src/locales/zh-CN.json 100% 100% 100% 100%
src/runtime/app-runtime.ts 100% 100% 100% 100%
src/runtime/shell-document.ts 100% 100% 100% 100%
src/runtime/theme-runtime.ts 100% 100% 100% 100%
src/stores/settings-store.ts 100% 90.9% 100% 100%
Generated in workflow #938 for commit a7dfc4e by the Vitest Coverage Report Action

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.33508% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.1%. Comparing base (0eeb951) to head (a7dfc4e).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/components/Layout/DesktopTitlebar.tsx 40.0% 3 Missing ⚠️
src/components/Layout/Toolbar.tsx 60.0% 2 Missing ⚠️
src/components/Layout/Sidebar.tsx 66.6% 1 Missing ⚠️
src/components/Library/SongListItem.tsx 87.5% 1 Missing ⚠️
Additional details and impacted files
@@                      Coverage Diff                       @@
##           feat/scrollbar-visibility-97    #111     +/-   ##
==============================================================
+ Coverage                          77.3%   78.1%   +0.8%     
==============================================================
  Files                               126     127      +1     
  Lines                              6266    6386    +120     
  Branches                           2022    2042     +20     
==============================================================
+ Hits                               4846    4992    +146     
+ Misses                             1376    1352     -24     
+ Partials                             44      42      -2     
Flag Coverage Δ
frontend 78.1% <96.3%> (+0.8%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
frontend 78.1% <96.3%> (+0.8%) ⬆️
rust ∅ <ø> (∅)
Files with missing lines Coverage Δ
src/App.tsx 93.3% <100.0%> (+1.0%) ⬆️
src/components/ErrorBoundary.tsx 53.3% <ø> (ø)
src/components/Layout/GlobalProgressBar.tsx 55.1% <ø> (ø)
src/components/Layout/SidebarRail.tsx 31.0% <ø> (ø)
src/components/Layout/ToastContainer.tsx 81.8% <ø> (ø)
src/components/Library/ContextMenu.tsx 20.5% <ø> (ø)
src/components/Library/EmptyLibrary.tsx 100.0% <ø> (ø)
src/components/Library/SearchBox.tsx 75.0% <ø> (ø)
src/components/Lyrics/LyricLine.tsx 84.8% <100.0%> (+0.3%) ⬆️
src/components/Lyrics/LyricsEditDialog.tsx 60.0% <ø> (ø)
... and 39 more
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

devin-ai-integration[bot]

This comment was marked as resolved.

@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a persisted light, dark, and system appearance preference. The main changes are:

  • New Rust ThemePreference config field, settings snapshot field, and set_theme_preference IPC command.
  • Frontend settings store and Appearance radio support for changing the preference.
  • Theme runtime that resolves system preference, applies document theme tokens, and syncs the native Tauri window theme before showing the app.
  • Semantic CSS token updates for light theme support while keeping audience presentation mode dark.
  • Updated IPC contract docs, changelog, localization strings, and tests.

Confidence Score: 5/5

Safe to merge based on the reviewed changed paths.

The theme preference is wired through config, IPC, frontend types, store hydration, settings UI, startup gating, CSS tokens, and focused tests. No verified correctness or security issue was found, and the audience color-scheme issue is addressed.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a proof for the posted P1 finding and linked it to the corresponding review comment.
  • A Playwright end-to-end test for theme UI validation was executed against the Chromium project to exercise the appearance settings flow, with results collected for review.
  • The test run failed due to locator.check: Clicking the checkbox did not change its state for input[name=\"theme-preference\"][value=\"light\"].
  • The tauri-mock fixture provides an initial dark theme and lacks a set_theme_preference handler, and the settings store rolls back the optimistic theme when the API rejects.
  • Artifacts were captured to inspect the failure, including a real-app-state image and a failure screenshot plus a link to the failure log.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
src/runtime/theme-runtime.ts Adds theme resolution, document token application, native window theme sync, and startup readiness gating; audience color-scheme preservation is handled.
src/runtime/app-runtime.ts Extends startup readiness to wait for settings hydration and theme native-sync settlement before showing the window.
src/stores/settings-store.ts Adds themePreference to settings snapshots and implements optimistic setThemePreference with generation-based rollback.
src/components/Settings/SettingsGeneralSection.tsx Adds Appearance radio controls bound to overlay state and theme preference actions.
src/components/Settings/SettingsOverlay.state.ts Threads themePreference through initial overlay state, initialization, and settings hydration.
src-tauri/src/config.rs Adds ThemePreference enum, AppConfig persistence field, effective defaulting, and serialization tests.
src-tauri/src/commands/settings.rs Adds theme_preference to settings snapshots and a validating set_theme_preference command.
src/types/ipc.ts Adds ThemePreference and ResolvedTheme types and includes theme_preference in AppSettings.
src/lib/tauri/settings.ts Adds the frontend invoke wrapper for set_theme_preference.
src/styles/globals.css Introduces light theme semantic tokens and keeps audience presentation scoped to dark tokens and color-scheme.
src/App.tsx Integrates theme runtime into the app startup hook order and ready gate.
docs/references/contracts/settings.md Documents the new theme preference IPC contract and AppSettings field.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User
participant SettingsUI as Settings Appearance Radio
participant Store as settings-store
participant IPC as set_theme_preference IPC
participant Config as AppConfig
participant ThemeRuntime as theme-runtime
participant Window as Tauri Window

User->>SettingsUI: choose system/light/dark
SettingsUI->>Store: setThemePreference(preference)
Store->>Store: optimistic update + generation
Store->>IPC: invoke set_theme_preference
IPC->>Config: parse and persist ThemePreference
Config-->>IPC: AppSettings snapshot
IPC-->>Store: updated settings
Store->>ThemeRuntime: themePreference changes
ThemeRuntime->>ThemeRuntime: resolve system via prefers-color-scheme
ThemeRuntime->>ThemeRuntime: apply data-theme + color-scheme
ThemeRuntime->>Window: setTheme(light/dark/null)
Window-->>ThemeRuntime: settled or timeout guard
ThemeRuntime-->>Store: startupThemeReady permits windowReady gate
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant User
participant SettingsUI as Settings Appearance Radio
participant Store as settings-store
participant IPC as set_theme_preference IPC
participant Config as AppConfig
participant ThemeRuntime as theme-runtime
participant Window as Tauri Window

User->>SettingsUI: choose system/light/dark
SettingsUI->>Store: setThemePreference(preference)
Store->>Store: optimistic update + generation
Store->>IPC: invoke set_theme_preference
IPC->>Config: parse and persist ThemePreference
Config-->>IPC: AppSettings snapshot
IPC-->>Store: updated settings
Store->>ThemeRuntime: themePreference changes
ThemeRuntime->>ThemeRuntime: resolve system via prefers-color-scheme
ThemeRuntime->>ThemeRuntime: apply data-theme + color-scheme
ThemeRuntime->>Window: setTheme(light/dark/null)
Window-->>ThemeRuntime: settled or timeout guard
ThemeRuntime-->>Store: startupThemeReady permits windowReady gate
Loading

Comments Outside Diff (1)

  1. General comment

    P1 Mocked Tauri IPC lacks set_theme_preference, causing Settings theme switching to roll back in Chromium UI tests

    • Bug
      • In the real app running under the repository's Playwright/Tauri mock, the Settings Appearance radio group renders with Dark selected, but selecting Light fails: the radio click does not persist and Playwright reports that checking input[name="theme-preference"][value="light"] did not change its state. This blocks the PR's sandbox validation path for light/dark/system switching and mocked native theme sync.
    • Cause
      • tests/e2e/fixtures/tauri-mock.ts was updated to return theme_preference: "dark" from get_settings, but it does not implement the new set_theme_preference IPC command. src/stores/settings-store.ts performs an optimistic themePreference update, awaits api.setThemePreference(preference), and rolls back to the previous preference on rejection. Because the mock rejects unknown commands, the UI immediately reverts to Dark.
    • Fix
      • Add a set_theme_preference handler to the Tauri E2E mock that returns an AppSettings-shaped object including the requested theme_preference and the existing settings fields. Ideally keep mock settings state mutable so subsequent get_settings and settings setter responses remain consistent.

    T-Rex Ran code and verified through T-Rex

Reviews (2): Last reviewed commit: "fix: roll back only themePreference on f..." | Re-trigger Greptile

greptile-apps[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Owner Author

Maintainer acceptance — merge slot 5, strictly after #105

BLOCKED — changes required.

Blocking first-paint race

useThemeRuntime initializes systemPrefersDark to true and only reads matchMedia(...).matches in a passive useEffect. For a persisted system preference on a light OS, the hydrated useLayoutEffect can apply dark tokens before that passive effect corrects the state; the native setTheme(null) readiness path can also settle during that window. This violates #96's requirement that the resolved theme be applied before the hidden window is shown and can produce a dark flash/wrong initial window.

Initialize the system preference synchronously from the media query (with the existing unavailable fallback), or otherwise resolve it before the layout/ready gates. Add a test for first hydrated paint with system + light OS preference, not only a later media-change test.

Then rebase onto #105, re-run CI, and attach the required packaged macOS/Windows/Linux manual matrix. This is a major visual change and must not be merged before that evidence.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Owner Author

Re-review: code blockers are addressed; required dependency and physical-platform acceptance remain

The current head fixes the initial system/light preference initialization and avoids restoring an entire stale settings snapshot on failure. This PR is still not ready to merge because its predecessor and the required native-WebView validation are incomplete.

Complete these steps:

  1. Wait for fix(ui): unify native-style scrollbar visibility across platforms (#97) #105 to complete its three-platform scrollbar acceptance and merge. Rebase this branch onto the resulting main; do not duplicate scrollbar/theme fixture changes already present upstream.
  2. Build and launch packaged application artifacts on macOS/WKWebView, Windows/WebView2, and Linux/WebKitGTK. On each platform verify:
    • first launch with OS light + system is light before the window becomes visible;
    • first launch with OS dark + system is dark;
    • switching Light/Dark/System updates web content and native window chrome together;
    • an OS scheme change updates System mode without reload;
    • audience/fullscreen presentation remains deliberately dark and does not leak the primary preference.
  3. Attach screenshots or short recordings plus a matrix containing platform, OS scheme, stored preference, resolved web theme, native-window result, and audience result.
  4. If a matrix failure is found, add the smallest targeted regression test and fix; do not use a longer startup timeout as a substitute for correct synchronization.

Run the existing unit/browser tests after rebasing and reply with the package build IDs, manual matrix, and commit SHA. #112 must not be merged before this PR is accepted.

@thedavidweng
thedavidweng changed the base branch from main to feat/scrollbar-visibility-97 July 15, 2026 09:15
@thedavidweng
thedavidweng force-pushed the feat/scrollbar-visibility-97 branch from c89f8c2 to 8fd568c Compare July 16, 2026 03:44
@thedavidweng

Copy link
Copy Markdown
Owner Author

@greptile review

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Linux VM theme acceptance (Cursor Cloud)

Commit: 6a789ff25e0ddfeb0023fa6270029e6c1f4a1e48
Platform: Linux Cursor Cloud VM — Vite + Tauri IPC mock (Chromium + Playwright WebKit)
Note: Packaged WebKitGTK / native window chrome sync (plugin:window|set_theme) is not available in this harness; web data-theme + CSS tokens were validated. Dependency on #105 merge/rebase remains.

Matrix (web content)

Stored preference OS scheme Resolved data-theme Result
dark (default) unset dark dark shell ✅
light light light shell ✅
dark dark dark shell ✅
system light light follows OS without reload ✅
system dark dark follows OS without reload ✅
(fullscreen audience) data-presentation-mode=audience stays dark presentation ✅

Screenshots

Light shell
light shell

Dark shell
dark shell

System + OS light
system os light

System + OS dark
system os dark

Audience / fullscreen
audience fullscreen

Recording

chromium-theme-tour.webm

WebKit frames also under frames/webkit-theme-*.png.

Gaps still requiring human / native runners

Format base-test.ts and scrollbar.spec.ts to pass oxfmt format-check
in pre-push hook. These were pre-existing uncommitted changes on the
branch that failed format-check during push.
Avoid depending on the i18n "Preferences" heading so settings scroll
overflow checks stay stable across locales and settings sections.
Two scrollbar spec tests registered a second page.addInitScript that
called window.__OPENKARA_E2E__.setMockSongs(), depending on the
fixture's init script having already defined that object. Playwright
does not guarantee evaluation order for multiple init scripts, so if
the override ran first it would throw before the app loaded and make
the overflow tests flaky.

Move both overrides after page.goto: call tauriMock.setMockSongs()
(which runs the same helper through page.evaluate) and trigger a
library reload via the openkara.library BroadcastChannel — the same
cross-webview sync path the app already subscribes to. This keeps the
mock data setup in the same execution context that defines
window.__OPENKARA_E2E__ and eliminates the ordering hazard.
@cursor
cursor Bot force-pushed the feat/scrollbar-visibility-97 branch from 6d0e6d1 to 4a1ec11 Compare July 17, 2026 02:13
…ollbar contract

The non-greedy regex only captured up to the first inner rule's closing
brace, leaving the remaining ~30 lines of the @supports not block
unchecked. Reuse blockBodyForSelectorAt for correct full-block extraction.
Add a ThemePreference (system|light|dark) persisted in AppConfig with a
new set_theme_preference IPC command. The frontend theme-runtime hook
resolves the preference against the OS prefers-color-scheme signal,
applies data-theme + color-scheme before first paint, and syncs the
Tauri native window theme. The startup ready gate now waits for the
first native setTheme settlement (with a 750ms timeout guard) so the
hidden window is never shown with the wrong theme.

Semantic CSS tokens replace hardcoded colors across production
components; the audience/fullscreen stage stays explicitly dark via a
data-presentation-mode marker regardless of the primary preference.
Settings gains an Appearance radio with en/zh-CN locale strings.
The scrollbar platform contract test asserts color-scheme contains
'dark', but the light-mode feature added a theme_preference field to
AppSettings that the e2e mock did not return. Without it the settings
store hydrated themePreference to undefined, so applyResolvedTheme
never set color-scheme to dark and the assertion failed. Add
theme_preference: 'dark' to the mock get_settings payload.
… audience dark mode, and contract doc

- Fix dead conditional in LyricsFontSizeControl and LyricsOffsetControl:
  non-default values now show accent color instead of identical text color
- Fix macOS light-mode scrollbar: use descendant combinator instead of
  compound selector so :root[data-theme="light"] matches the mac platform
  element correctly
- Preserve audience dark color-scheme in applyResolvedTheme when
  data-presentation-mode="audience" marker is present
- Correct settings contract doc: effective_theme_preference() returns raw
  preference, does not resolve System against OS appearance
- Resolve CHANGELOG.md merge conflict with main
Fix QueueButton queue fixture typing, extract queue item state class
resolution for drop-indicator token coverage, and assert volume mute
hover tokens on both collapsed and expanded paths.
The font-size and offset controls used text-[var(--color-accent)] for the
non-default branch, which was visually distinct from the default
text-[var(--color-text-dim)] branch. However, the tests were checking for
text-[var(--color-text)] which appears in every button element, making them
pass vacuously. Switch the highlight token to text-[var(--color-control-primary)]
and update the tests to assert on the actual non-default class.
devin-ai-integration[bot]

This comment was marked as resolved.

…nguage switch failure

When loadStartupSettings hydrates saved settings successfully but the
subsequent language switch fails, the catch handler was overwriting
every already-loaded preference with DEFAULT_APP_SETTINGS. Guard the
default-settings patch with a hydrated check so only the genuinely
un-hydrated case (missing/corrupt config) applies defaults, preserving
user preferences when the language switch fails.

Copy link
Copy Markdown
Owner Author

Re-review — no LGTM yet: retarget/rebase and native acceptance required

Reviewed head f47eb75582f0942868afb64210597145fcef0f5f.

#105 has merged, but this PR still targets the feature branch feat/scrollbar-visibility-97 rather than current main. Rebase/retarget it to current main, preserve the merged scrollbar contract, and rerun CI/Packaging.

The code-level first-paint and rollback issues previously identified are addressed. Final sign-off still requires the issue's packaged native-WebView matrix:

  • light/dark/system startup without a wrong-theme flash;
  • live OS scheme change while set to system;
  • native window/control theme synchronization;
  • audience/presentation mode remains dark;
  • macOS, Windows, and Linux packaged builds.

Request re-review with the new head and attached platform evidence.

Align light-mode surfaces and semantic foregrounds with the approved visual direction. Keep the native traffic-light controls available by routing the green control through AppKit zoom instead of titlebar-less native full screen.

Copy link
Copy Markdown
Owner Author

已补充本轮 #111 修复,CI run 938 全绿。

本轮变更

  • 浅色模式统一改用语义前景、悬停、选中和强调色 token;设置、对话框、列表、工具栏、歌词和播放控件不再在浅色表面使用白字或低可见度控件。
  • 无封面图时,标准歌词舞台不再渲染半透明深色 backdrop,避免出现灰黑空白播放区。
  • macOS 主窗口绿灯改为 AppKit performZoom::占满可用屏幕并可再次还原,同时保留原生交通灯与顶部工具栏。没有重绘、移动或伪造原生控件。

自动验证

  • 本地:格式、lint、生产构建、1,361 项前端单测;受影响的 SongListItem 单测 13/13。
  • GitHub Actions:Frontend quality/build/test、Playwright UI smoke、Cargo deny、依赖检查、macOS/Windows/Linux Rust、macOS/Windows/Linux Tauri build 全部通过。

仍需人工 UI 验收,暂不建议仅凭 CI 合并

  1. 在真实 macOS(WKWebView)中检查 Light / Dark / System 三种偏好,重点确认图示的设置页、侧栏选中态、按钮、输入框、滑块和歌词均有可读对比度。
  2. 无封面歌曲/未选歌时,主舞台应保持明亮且无灰色遮罩;有封面时歌词仍应清晰。
  3. 点击左上绿灯应进入可用屏幕的 zoom 状态,交通灯与工具栏持续可见;再次点击应还原,拖动、调整窗口大小和多显示器切换不得出现空隙或错位。
  4. View → Enter Full Screen 仍是 macOS 原生全屏;该模式按系统行为隐藏标题栏,和绿灯的“保留交通灯的 zoom”是有意区分的。

@thedavidweng
thedavidweng changed the base branch from feat/scrollbar-visibility-97 to main July 19, 2026 02:18
thedavidweng added a commit that referenced this pull request Jul 19, 2026
Integrate #111 theme preference, #112 unified playback buttons, and #118
volume rails onto main (with EQ/sort coexistence fixes).
thedavidweng added a commit that referenced this pull request Jul 19, 2026
Integrates the #111#112#118 stack onto current main:

- Light/dark/system appearance with monochrome controls and selective accent
- Unified right-side playback button chrome
- Wider volume sliders with density-based px token table
- Stem mixer popup via body portal (no clipping in settings)
- Restored E2E sort fixtures after stack-merge conflict resolution

Supersedes #111, #112, #118.
@thedavidweng

Copy link
Copy Markdown
Owner Author

Superseded by #126, which lands the full #111#112#118 stack (light mode, playback chrome, volume sliders) onto current main after conflict resolution. Merged: #126

@thedavidweng
thedavidweng deleted the feat/light-mode-96 branch July 19, 2026 03:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant