import type { Plugin } from "@/plugins/core/types"; import { booleanSetting, componentSetting, defineSettings, numberSetting, } from "@/plugins/core/settingsHelpers"; import styles from "./styles.css?inline"; import BackgroundMusicSetting from "./BackgroundMusicSetting.svelte"; import localforage from "localforage"; const settings = defineSettings({ uploader: componentSetting({ title: "Background Music", description: "Upload a .wav or .mp3 audio file to play in the background", component: BackgroundMusicSetting, }), volume: numberSetting({ title: "Volume", description: "Set background music volume", default: 0.5, min: 0, max: 1, step: 0.05, }), pauseOnHidden: booleanSetting({ title: "Pause when tab hidden", description: "Pause music when switching to another tab or minimizing the browser", default: true, }), }); const store = localforage.createInstance({ name: "background-music-store", storeName: "music", }); const GESTURE_EVENTS = ["pointerdown", "keydown", "touchstart"] as const; const gestureOpts: AddEventListenerOptions = { capture: true, passive: true }; let audio: HTMLAudioElement | null = null; let objectUrl: string | null = null; let gestureCleanup: (() => void) | null = null; let resumeTimer: ReturnType | null = null; let hintEl: HTMLElement | null = null; const clamp = (v: number) => Math.max(0, Math.min(1, v)); function clearHint(): void { hintEl?.remove(); hintEl = null; } function disarmGesture(): void { gestureCleanup?.(); gestureCleanup = null; } function stopAudio(): void { audio?.pause(); audio?.remove(); audio = null; if (objectUrl) URL.revokeObjectURL(objectUrl); objectUrl = null; } /** Prepare