feat(player): persist volume across sessions, default to 100%
Store the playback volume in app settings (config.json) whenever it changes and re-apply the previous session's level at boot, instead of always starting at the old 70% fallback. - AppSettings gains volume (default 1 = 100%); both default-settings copies and the volume signal default are raised from 0.7 to 1. - doSetVolume persists via the app store (mirrors playbackSpeed). - The boot sync awaits the app store's async config load (new whenReady()) so a persisted level is applied even when settings load finishes after useAudio mounts. - tests/volume-persistence.test.ts: default, clamp, and cross-session reuse (fresh module instance simulates the next launch).
This commit is contained in:
@@ -74,7 +74,7 @@ let pollCount = 0; // Counts poll ticks for throttling progress saves
|
||||
const [isPlaying, setIsPlaying] = createSignal(false);
|
||||
const [position, setPosition] = createSignal(0);
|
||||
const [duration, setDuration] = createSignal(0);
|
||||
const [volume, setVolume] = createSignal(0.7);
|
||||
const [volume, setVolume] = createSignal(1);
|
||||
const [speed, setSpeed] = createSignal(1);
|
||||
const [backendName, setBackendName] = createSignal<BackendName>("none");
|
||||
const [error, setError] = createSignal<string | null>(null);
|
||||
@@ -440,6 +440,10 @@ async function doSetVolume(vol: number): Promise<void> {
|
||||
}
|
||||
}
|
||||
setVolume(clamped);
|
||||
|
||||
// Sync back to app store (persisted to config.json for the next launch).
|
||||
const appStore = useAppStore();
|
||||
appStore.updateSettings({ volume: clamped });
|
||||
}
|
||||
|
||||
async function doSetSpeed(spd: number): Promise<void> {
|
||||
@@ -555,7 +559,8 @@ export function useAudio(): AudioControls {
|
||||
// Initialize backend on first use
|
||||
ensureBackend();
|
||||
|
||||
// Sync initial speed from app store
|
||||
// Sync initial speed/volume from app store (reuse the previous session's
|
||||
// playback levels; defaults are 1x and 100%).
|
||||
if (refCount === 0) {
|
||||
const appStore = useAppStore();
|
||||
const storeSpeed = appStore.state().settings.playbackSpeed;
|
||||
@@ -563,6 +568,18 @@ export function useAudio(): AudioControls {
|
||||
setSpeed(storeSpeed);
|
||||
}
|
||||
|
||||
// Volume re-syncs once settings finish loading (async config read)
|
||||
// so a level persisted last session is applied at boot.
|
||||
appStore
|
||||
.whenReady()
|
||||
.then(() => {
|
||||
const storeVolume = appStore.state().settings.volume;
|
||||
if (storeVolume !== undefined && storeVolume !== volume()) {
|
||||
setVolume(storeVolume);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
// Restore the last player session once at boot (loaded, not playing).
|
||||
restoreLastSession().catch(() => {});
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ const defaultSettings: AppSettings = {
|
||||
theme: "system",
|
||||
fontSize: 14,
|
||||
playbackSpeed: 1,
|
||||
volume: 1,
|
||||
downloadPath: "",
|
||||
transparentBackground: false,
|
||||
showSelectionMarker: false,
|
||||
@@ -55,12 +56,14 @@ function createAppStore() {
|
||||
// Start with defaults; async load will update once ready
|
||||
const [state, setState] = createSignal<AppState>(defaultState);
|
||||
|
||||
// Fire-and-forget async initialisation
|
||||
// Fire-and-forget async initialisation; the promise is exposed via
|
||||
// whenReady() so boot-time consumers (audio-level restore) can await
|
||||
// the config read before reading settings.
|
||||
const init = async () => {
|
||||
const loaded = await loadAppStateFromFile();
|
||||
setState(loaded);
|
||||
};
|
||||
init();
|
||||
const appInit = init();
|
||||
|
||||
const saveState = (next: AppState) => {
|
||||
saveAppStateToFile(next);
|
||||
@@ -119,6 +122,8 @@ function createAppStore() {
|
||||
|
||||
return {
|
||||
state,
|
||||
/** Resolves once persisted settings are loaded from disk. */
|
||||
whenReady: () => appInit,
|
||||
updateSettings,
|
||||
updatePreferences,
|
||||
updateCustomTheme,
|
||||
|
||||
@@ -78,6 +78,8 @@ export type AppSettings = {
|
||||
theme: ThemeName;
|
||||
fontSize: number;
|
||||
playbackSpeed: number;
|
||||
/** Playback volume 0–1 (default: 1 = 100%). */
|
||||
volume: number;
|
||||
downloadPath: string;
|
||||
/** Render the app background transparent (let the terminal's own bg show). */
|
||||
transparentBackground: boolean;
|
||||
|
||||
@@ -32,6 +32,7 @@ const defaultSettings: AppSettings = {
|
||||
theme: "system",
|
||||
fontSize: 14,
|
||||
playbackSpeed: 1,
|
||||
volume: 1,
|
||||
downloadPath: "",
|
||||
transparentBackground: false,
|
||||
showSelectionMarker: false,
|
||||
|
||||
Reference in New Issue
Block a user