fix(theme): follow the OS dark mode on desktop

Compose's isSystemInDarkTheme never updates under the Tao window, so Theme.SYSTEM
stayed light. Use Nucleus' darkmode-detector on JVM and keep Compose's check on mobile.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Kingkor Roy Tirtho 2026-10-27 18:53:47 +06:00
parent 5b36edd259
commit aca3aeb592
6 changed files with 76 additions and 2 deletions

View File

@ -366,6 +366,7 @@ kotlin {
implementation(libs.nucleus.decorated.window.tao) implementation(libs.nucleus.decorated.window.tao)
implementation(libs.compose.native.tray) implementation(libs.compose.native.tray)
implementation(libs.nucleus.media.control) implementation(libs.nucleus.media.control)
implementation(libs.nucleus.darkmode.detector)
} }
} }
} }

View File

@ -17,7 +17,6 @@
package org.spotube.spotube.core.ui.theming package org.spotube.spotube.core.ui.theming
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -205,7 +204,7 @@ fun SpotubeTheme(
val isDarkTheme = when (settings.theme) { val isDarkTheme = when (settings.theme) {
Theme.LIGHT -> false Theme.LIGHT -> false
Theme.DARK -> true Theme.DARK -> true
Theme.SYSTEM -> isSystemInDarkTheme() Theme.SYSTEM -> isSystemDarkTheme()
} }
val palette: MillenniumPalette = settings.palette val palette: MillenniumPalette = settings.palette

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.spotube.spotube.core.ui.theming
import androidx.compose.runtime.Composable
/** Whether the OS is in dark mode, observed live; desktop can't rely on Compose's own check under Tao. */
@Composable
expect fun isSystemDarkTheme(): Boolean

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.spotube.spotube.core.ui.theming
import androidx.compose.runtime.Composable
import dev.nucleusframework.darkmodedetector.isSystemInDarkMode
// The Tao window never feeds Compose's LocalSystemTheme, so ask the OS directly
@Composable
actual fun isSystemDarkTheme(): Boolean = isSystemInDarkMode()

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.spotube.spotube.core.ui.theming
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
@Composable
actual fun isSystemDarkTheme(): Boolean = isSystemInDarkTheme()

View File

@ -134,6 +134,7 @@ newpipeextractor = { module = "com.github.teamnewpipe:NewPipeExtractor", version
nucleus-core-runtime = { module = "dev.nucleusframework:nucleus.core-runtime", version.ref = "nucleusNucleusApplication" } nucleus-core-runtime = { module = "dev.nucleusframework:nucleus.core-runtime", version.ref = "nucleusNucleusApplication" }
nucleus-decorated-window-tao = { module = "dev.nucleusframework:nucleus.decorated-window-tao", version.ref = "nucleusNucleusApplication" } nucleus-decorated-window-tao = { module = "dev.nucleusframework:nucleus.decorated-window-tao", version.ref = "nucleusNucleusApplication" }
nucleus-media-control = { module = "dev.nucleusframework:nucleus.media-control", version.ref = "nucleusNucleusApplication" } nucleus-media-control = { module = "dev.nucleusframework:nucleus.media-control", version.ref = "nucleusNucleusApplication" }
nucleus-darkmode-detector = { module = "dev.nucleusframework:nucleus.darkmode-detector", version.ref = "nucleusNucleusApplication" }
nucleus-graalvm-runtime = { module = "dev.nucleusframework:nucleus.graalvm-runtime", version.ref = "nucleusNucleusApplication" } nucleus-graalvm-runtime = { module = "dev.nucleusframework:nucleus.graalvm-runtime", version.ref = "nucleusNucleusApplication" }
nucleus-nucleus-application = { module = "dev.nucleusframework:nucleus.nucleus-application", version.ref = "nucleusNucleusApplication" } nucleus-nucleus-application = { module = "dev.nucleusframework:nucleus.nucleus-application", version.ref = "nucleusNucleusApplication" }
semver = { module = "net.swiftzer.semver:semver", version.ref = "semver" } semver = { module = "net.swiftzer.semver:semver", version.ref = "semver" }