From aca3aeb592d2873620a29e5d38a2cdc107382407 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Tue, 27 Oct 2026 18:53:47 +0600 Subject: [PATCH] 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 --- composeApp/build.gradle.kts | 1 + .../spotube/core/ui/theming/AppTheme.kt | 3 +-- .../core/ui/theming/SystemDarkTheme.kt | 24 ++++++++++++++++++ .../core/ui/theming/SystemDarkTheme.jvm.kt | 25 +++++++++++++++++++ .../core/ui/theming/SystemDarkTheme.mobile.kt | 24 ++++++++++++++++++ gradle/libs.versions.toml | 1 + 6 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.kt create mode 100644 composeApp/src/jvmMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.jvm.kt create mode 100644 composeApp/src/mobileMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.mobile.kt diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index a72f675f..99794847 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -366,6 +366,7 @@ kotlin { implementation(libs.nucleus.decorated.window.tao) implementation(libs.compose.native.tray) implementation(libs.nucleus.media.control) + implementation(libs.nucleus.darkmode.detector) } } } diff --git a/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/AppTheme.kt b/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/AppTheme.kt index 6720efb9..b283451e 100644 --- a/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/AppTheme.kt +++ b/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/AppTheme.kt @@ -17,7 +17,6 @@ package org.spotube.spotube.core.ui.theming -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable @@ -205,7 +204,7 @@ fun SpotubeTheme( val isDarkTheme = when (settings.theme) { Theme.LIGHT -> false Theme.DARK -> true - Theme.SYSTEM -> isSystemInDarkTheme() + Theme.SYSTEM -> isSystemDarkTheme() } val palette: MillenniumPalette = settings.palette diff --git a/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.kt b/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.kt new file mode 100644 index 00000000..3efa4474 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.kt @@ -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 . + */ + +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 diff --git a/composeApp/src/jvmMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.jvm.kt b/composeApp/src/jvmMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.jvm.kt new file mode 100644 index 00000000..eae0a660 --- /dev/null +++ b/composeApp/src/jvmMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.jvm.kt @@ -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 . + */ + +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() diff --git a/composeApp/src/mobileMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.mobile.kt b/composeApp/src/mobileMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.mobile.kt new file mode 100644 index 00000000..047248e3 --- /dev/null +++ b/composeApp/src/mobileMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.mobile.kt @@ -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 . + */ + +package org.spotube.spotube.core.ui.theming + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable + +@Composable +actual fun isSystemDarkTheme(): Boolean = isSystemInDarkTheme() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c167d59c..ccc34f1f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -134,6 +134,7 @@ newpipeextractor = { module = "com.github.teamnewpipe:NewPipeExtractor", version 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-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-nucleus-application = { module = "dev.nucleusframework:nucleus.nucleus-application", version.ref = "nucleusNucleusApplication" } semver = { module = "net.swiftzer.semver:semver", version.ref = "semver" }