From 0345d948ac3f33c59a87b3eb63db93e2697c296f Mon Sep 17 00:00:00 2001 From: DiheChen Date: Sat, 7 Nov 2026 07:57:21 +0800 Subject: [PATCH] feat: show the player bar volume as a read-only readout --- web/src/features/playback/playback.css | 4 +-- web/src/features/playback/player-bar.tsx | 29 ++++++++++++------- web/src/features/settings/settings-page.tsx | 3 +- .../settings}/volume-editor.test.ts | 0 .../ui => features/settings}/volume-editor.ts | 0 .../ui => features/settings}/volume-input.tsx | 5 ++-- web/src/shared/ui/index.ts | 1 - 7 files changed, 24 insertions(+), 18 deletions(-) rename web/src/{shared/ui => features/settings}/volume-editor.test.ts (100%) rename web/src/{shared/ui => features/settings}/volume-editor.ts (100%) rename web/src/{shared/ui => features/settings}/volume-input.tsx (91%) diff --git a/web/src/features/playback/playback.css b/web/src/features/playback/playback.css index 63f5dc8..db055c8 100644 --- a/web/src/features/playback/playback.css +++ b/web/src/features/playback/playback.css @@ -15,9 +15,7 @@ .player-tools { display: flex; align-items: center; justify-content: flex-end; gap: 4px; } .player-tools .volume-control { display: flex; align-items: center; gap: 6px; margin-right: 16px; } .player-tools .volume-slider { width: 90px; accent-color: var(--color-foreground); } -.player-tools .volume-input { box-sizing: border-box; width: 4.5ch; min-width: 0; padding: 4px 3px; text-align: center; font-variant-numeric: tabular-nums; -moz-appearance: textfield; } -.player-tools .volume-input::-webkit-inner-spin-button, .player-tools .volume-input::-webkit-outer-spin-button { margin: 0; -webkit-appearance: none; } -.player-tools .volume-unit { color: var(--color-foreground-subtle); font-size: var(--text-ui-xs); } +.player-tools .volume-readout { min-width: 5ch; color: var(--color-foreground-subtle); font-size: var(--text-ui-sm); font-variant-numeric: tabular-nums; } .player-panel-toggle { color: var(--color-foreground-subtle); } .player-panel-toggle:hover:not(:disabled), .player-panel-toggle:focus-visible:not(:disabled), .player-panel-toggle[aria-pressed='true'] { color: var(--color-brand); background: transparent; } .player-panel-toggle:active:not(:disabled) { color: var(--color-brand); background: transparent; transform: none; } diff --git a/web/src/features/playback/player-bar.tsx b/web/src/features/playback/player-bar.tsx index a460ce5..a090bd6 100644 --- a/web/src/features/playback/player-bar.tsx +++ b/web/src/features/playback/player-bar.tsx @@ -4,7 +4,7 @@ import { roomStore } from '@/core/room' import { sourceName } from '@/features/queue' import { formatDuration } from '@/shared/lib/time' import { useStore } from '@/shared/lib/use-store' -import { Artwork, AsyncIconButton, IconButton, VolumeInput } from '@/shared/ui' +import { Artwork, AsyncIconButton, IconButton } from '@/shared/ui' import { playerStore } from './player-store' import { volumeStore } from './volume-store' import './playback.css' @@ -64,6 +64,7 @@ export function PlayerBar({ onSkip, onLyrics, onQueue, onChat, queueOpen, chatOp const connected = useStore(roomStore, s => s.status === 'open') const volume = useStore(volumeStore, s => s.volume) const muted = useStore(volumeStore, s => s.muted) + const audibleVolume = muted ? 0 : volume const meta = nowPlaying?.entry.meta const artist = meta?.artist || '未知艺术家' const source = nowPlaying ? sourceName(nowPlaying.entry.track) : undefined @@ -90,15 +91,23 @@ export function PlayerBar({ onSkip, onLyrics, onQueue, onChat, queueOpen, chatOp
volumeStore.setMuted(!muted)} /> - { - volumeStore.setVolume(next) - if (muted && next > 0) - volumeStore.setMuted(false) - }} - /> +
+ { + const next = Number(event.target.value) + volumeStore.setVolume(next) + if (muted && next > 0) + volumeStore.setMuted(false) + }} + /> + +
diff --git a/web/src/features/settings/settings-page.tsx b/web/src/features/settings/settings-page.tsx index 23b17c9..ac84d20 100644 --- a/web/src/features/settings/settings-page.tsx +++ b/web/src/features/settings/settings-page.tsx @@ -6,10 +6,11 @@ import { errorMessage } from '@/core/errors' import { historyStore, searchLimits } from '@/features/history' import { reminderStore, requestNotificationPermission, systemNotificationsSupported, volumeStore } from '@/features/playback' import { useStore } from '@/shared/lib/use-store' -import { notificationStore, Select, VolumeInput } from '@/shared/ui' +import { notificationStore, Select } from '@/shared/ui' import { appearanceStore, fontSizes, themes } from './appearance-store' import { performanceLevels, performanceStore } from './performance-store' import { profileStore } from './profile-store' +import { VolumeInput } from './volume-input' import './settings.css' interface SettingsDraft { diff --git a/web/src/shared/ui/volume-editor.test.ts b/web/src/features/settings/volume-editor.test.ts similarity index 100% rename from web/src/shared/ui/volume-editor.test.ts rename to web/src/features/settings/volume-editor.test.ts diff --git a/web/src/shared/ui/volume-editor.ts b/web/src/features/settings/volume-editor.ts similarity index 100% rename from web/src/shared/ui/volume-editor.ts rename to web/src/features/settings/volume-editor.ts diff --git a/web/src/shared/ui/volume-input.tsx b/web/src/features/settings/volume-input.tsx similarity index 91% rename from web/src/shared/ui/volume-input.tsx rename to web/src/features/settings/volume-input.tsx index 760b24f..ce35b47 100644 --- a/web/src/shared/ui/volume-input.tsx +++ b/web/src/features/settings/volume-input.tsx @@ -4,13 +4,12 @@ import { beginVolumeEdit, changeVolumeDraft, commitVolumeDraft, initialVolumeEdi interface VolumeInputProps { volume: number onCommit: (volume: number) => void - sliderValue?: number id?: string className?: string disabled?: boolean } -export function VolumeInput({ volume, onCommit, sliderValue, id, className = '', disabled = false }: VolumeInputProps) { +export function VolumeInput({ volume, onCommit, id, className = '', disabled = false }: VolumeInputProps) { const [editor, setEditor] = useState(() => initialVolumeEditor(volume)) const syncedEditor = syncVolumeDraft(editor, volume) const commit = () => { @@ -27,7 +26,7 @@ export function VolumeInput({ volume, onCommit, sliderValue, id, className = '', type="range" min="0" max="100" - value={sliderValue ?? volume} + value={volume} aria-label="音量" disabled={disabled} onChange={(event) => { diff --git a/web/src/shared/ui/index.ts b/web/src/shared/ui/index.ts index c5f046b..e5b275c 100644 --- a/web/src/shared/ui/index.ts +++ b/web/src/shared/ui/index.ts @@ -16,4 +16,3 @@ export { Skeleton } from './skeleton' export { SongArtwork } from './song-artwork' export { SongList } from './song-list' export type { SongAction, SongItem } from './song-list' -export { VolumeInput } from './volume-input'