From d38250a5e4fb6c4926ad66038d9fc1454373ec83 Mon Sep 17 00:00:00 2001 From: HorizonCode Date: Sun, 1 Nov 2026 14:54:04 +0100 Subject: [PATCH] fix(theme-v2): ensure theme audio autoplays on load and handles unlock gesture --- .../components/theme/ThemeEditorWindow.svelte | 21 ++++++++++++++++++- .../components/theme/ThemeV2Background.svelte | 18 +++++++++++----- src/lib/themeMedia.ts | 4 ++-- src/lib/themes.ts | 1 + 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/lib/components/theme/ThemeEditorWindow.svelte b/src/lib/components/theme/ThemeEditorWindow.svelte index 1762cb1..e243464 100644 --- a/src/lib/components/theme/ThemeEditorWindow.svelte +++ b/src/lib/components/theme/ThemeEditorWindow.svelte @@ -76,6 +76,7 @@ themeAssetName, themeHasLaunchButton, type ThemeV2Asset, + type ThemeV2Audio, type ThemeV2Background, type ThemeV2BehaviorTarget, type ThemeV2ButtonAction, @@ -252,6 +253,7 @@ let lastGradientBackground = $state | undefined>( undefined ); + let lastAudio = $state(undefined); let assetBase = $state(''); let savedFolder = $state(undefined); let editorFolderPath = $state(undefined); @@ -1803,6 +1805,7 @@ ? normalizeThemeV2(JSON.parse(context.themeJson) as Partial) : createDefaultThemeV2(); definition = ensureDefaults(parsed); + lastAudio = parsed.audio ? { ...parsed.audio } : undefined; savedFolder = context.folderName ?? undefined; editorFolderPath = context.folderPath ?? undefined; const httpBase = await themeAssetBase(); @@ -1992,6 +1995,9 @@ color: current.type === 'color' ? current.color : (lastColorBackground?.color ?? '#181825'), opacity: lastColorBackground?.opacity ?? opacity, }; + if (!definition.audio && lastAudio) { + definition.audio = { ...lastAudio }; + } } else if (type === 'gradient') { definition.background = { type: 'gradient', @@ -2004,6 +2010,9 @@ : (lastGradientBackground?.angle ?? 135), opacity: lastGradientBackground?.opacity ?? opacity, }; + if (!definition.audio && lastAudio) { + definition.audio = { ...lastAudio }; + } } else if (type === 'video') { const candidateVideo = current.type === 'video' && current.src @@ -2024,6 +2033,9 @@ if (candidateVideo) { lastVideoBackground = { ...definition.background }; } + if (definition.audio) { + lastAudio = { ...definition.audio }; + } definition.audio = undefined; } else { const candidateImage = @@ -2043,6 +2055,9 @@ if (candidateImage) { lastImageBackground = { ...definition.background }; } + if (!definition.audio && lastAudio) { + definition.audio = { ...lastAudio }; + } } } @@ -2086,6 +2101,7 @@ const asset = await chooseAsset('audio'); if (!asset) return; definition.audio = { src: asset.name, loop: definition.audio?.loop !== false }; + lastAudio = { ...definition.audio }; } function uniqueFamily(label: string) { @@ -5622,7 +5638,10 @@ size="icon" class="size-8 text-muted-foreground" title="Remove music" - onclick={() => (definition.audio = undefined)} + onclick={() => { + definition.audio = undefined; + lastAudio = undefined; + }} > diff --git a/src/lib/components/theme/ThemeV2Background.svelte b/src/lib/components/theme/ThemeV2Background.svelte index 8aa9fae..57ef18a 100644 --- a/src/lib/components/theme/ThemeV2Background.svelte +++ b/src/lib/components/theme/ThemeV2Background.svelte @@ -1,6 +1,6 @@ @@ -225,6 +231,8 @@ use:themeMediaSource={assetUrl(definition.audio.src)} loop={definition.audio.loop !== false} autoplay + preload="auto" + playsinline > {/if} diff --git a/src/lib/themeMedia.ts b/src/lib/themeMedia.ts index 4185357..51b5ee1 100644 --- a/src/lib/themeMedia.ts +++ b/src/lib/themeMedia.ts @@ -208,7 +208,7 @@ const armAutoplayRetry = (node: HTMLMediaElement) => { if (node.isConnected) { node.muted = false; const targetVolume = get(custom_theme_volume); - if (targetVolume > 0) node.volume = targetVolume; + if (targetVolume >= 0) node.volume = targetVolume; void node.play().catch(() => {}); } }; @@ -230,7 +230,7 @@ export const playThemeMedia = (node: HTMLMediaElement) => { node.muted = false; const currentVolume = get(custom_theme_volume); - if (node.volume === 0 && currentVolume > 0) { + if (currentVolume >= 0) { node.volume = currentVolume; } diff --git a/src/lib/themes.ts b/src/lib/themes.ts index ad1ddef..64d8fb4 100644 --- a/src/lib/themes.ts +++ b/src/lib/themes.ts @@ -434,6 +434,7 @@ export const loadTheme = async (theme: Theme, themeContainer: HTMLElement, volum if (theme.definition) { setGlobalVolume(volume); disableMediaControls(); + void invoke('unlock_media_autoplay').catch(() => {}); return; } if (theme.scriptUrl.length > 0) {