From 9817724a10593417625797f7bf7165af5a6442d5 Mon Sep 17 00:00:00 2001 From: KOWX712 Date: Wed, 26 Nov 2025 12:36:32 +0800 Subject: [PATCH] manager: provide monet color to webui (#2981) Provide app color scheme using css variable in suPath, follow MMRL monet color scheme standard since some module has already support this for a while. This will not break current module's WebUI, it is an opt-in feature, you'll need to import before using it. Example: ```css @import url('https://mui.kernelsu.org/internal/colors.css'); :root { --my-background-color: var(--surface, #FEF7FF); --my-text-color: var(--onSurface, #1D1B20); } ``` This is only provided when monet color is selected in settings. Co-Authored-By: Der_Googler <54764558+dergoogler@users.noreply.github.com> Co-Authored-By: Rifat Azad <33044977+rifsxd@users.noreply.github.com> Signed-off-by: KOWX712 --------- Signed-off-by: KOWX712 Co-authored-by: Der_Googler <54764558+dergoogler@users.noreply.github.com> Co-authored-by: Rifat Azad <33044977+rifsxd@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../java/com/sukisu/ultra/ui/theme/Theme.kt | 6 +- .../ultra/ui/webui/MonetColorsProvider.kt | 100 ++++++++++++++++++ .../ultra/ui/webui/SuFilePathHandler.java | 14 +++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 manager/app/src/main/java/com/sukisu/ultra/ui/webui/MonetColorsProvider.kt diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt index 6aa58cf3..0866565f 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt @@ -3,6 +3,7 @@ package com.sukisu.ultra.ui.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color +import com.sukisu.ultra.ui.webui.MonetColorsProvider.UpdateCss import top.yukonga.miuix.kmp.theme.ColorSchemeMode import top.yukonga.miuix.kmp.theme.MiuixTheme import top.yukonga.miuix.kmp.theme.ThemeController @@ -37,6 +38,9 @@ fun KernelSUTheme( } return MiuixTheme( controller = controller, - content = content + content = { + UpdateCss() + content() + } ) } diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/webui/MonetColorsProvider.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/webui/MonetColorsProvider.kt new file mode 100644 index 00000000..195ca058 --- /dev/null +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/webui/MonetColorsProvider.kt @@ -0,0 +1,100 @@ +package com.sukisu.ultra.ui.webui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.graphics.Color +import top.yukonga.miuix.kmp.theme.MiuixTheme +import java.util.concurrent.atomic.AtomicReference + +/** + * @author rifsxd + * @date 2025/6/2. + */ +object MonetColorsProvider { + + private val colorsCss: AtomicReference = AtomicReference(null) + + fun getColorsCss(): String { + return colorsCss.get() ?: "" + } + + @Composable + fun UpdateCss() { + val colorScheme = MiuixTheme.colorScheme + + LaunchedEffect(colorScheme) { + // Generate CSS only when colorScheme changes + val monetColors = mapOf( + // App Base Colors + "primary" to colorScheme.primary.toCssValue(), + "onPrimary" to colorScheme.onPrimary.toCssValue(), + "primaryContainer" to colorScheme.primaryContainer.toCssValue(), + "onPrimaryContainer" to colorScheme.onPrimaryContainer.toCssValue(), + "inversePrimary" to colorScheme.primaryVariant.toCssValue(), + "secondary" to colorScheme.secondary.toCssValue(), + "onSecondary" to colorScheme.onSecondary.toCssValue(), + "secondaryContainer" to colorScheme.secondaryContainer.toCssValue(), + "onSecondaryContainer" to colorScheme.onSecondaryContainer.toCssValue(), + "tertiary" to colorScheme.tertiaryContainerVariant.toCssValue(), + "onTertiary" to colorScheme.tertiaryContainer.toCssValue(), + "tertiaryContainer" to colorScheme.tertiaryContainer.toCssValue(), + "onTertiaryContainer" to colorScheme.onTertiaryContainer.toCssValue(), + "background" to colorScheme.background.toCssValue(), + "onBackground" to colorScheme.onBackground.toCssValue(), + "surface" to colorScheme.surface.toCssValue(), + "tonalSurface" to colorScheme.surfaceContainer.toCssValue(), + "onSurface" to colorScheme.onSurface.toCssValue(), + "surfaceVariant" to colorScheme.surfaceVariant.toCssValue(), + "onSurfaceVariant" to colorScheme.onSurfaceVariantSummary.toCssValue(), + "surfaceTint" to colorScheme.surface.toCssValue(), + "inverseSurface" to colorScheme.disabledOnSurface.toCssValue(), + "inverseOnSurface" to colorScheme.surfaceContainer.toCssValue(), + "error" to colorScheme.error.toCssValue(), + "onError" to colorScheme.onError.toCssValue(), + "errorContainer" to colorScheme.errorContainer.toCssValue(), + "onErrorContainer" to colorScheme.onErrorContainer.toCssValue(), + "outline" to colorScheme.outline.toCssValue(), + "outlineVariant" to colorScheme.dividerLine.toCssValue(), + "scrim" to colorScheme.windowDimming.toCssValue(), + "surfaceBright" to colorScheme.surface.toCssValue(), + "surfaceDim" to colorScheme.surface.toCssValue(), + "surfaceContainer" to colorScheme.surfaceContainer.toCssValue(), + "surfaceContainerHigh" to colorScheme.surfaceContainerHigh.toCssValue(), + "surfaceContainerHighest" to colorScheme.surfaceContainerHighest.toCssValue(), + "surfaceContainerLow" to colorScheme.surfaceContainer.toCssValue(), + "surfaceContainerLowest" to colorScheme.surfaceContainer.toCssValue(), + "filledTonalButtonContentColor" to colorScheme.onPrimaryContainer.toCssValue(), + "filledTonalButtonContainerColor" to colorScheme.secondaryContainer.toCssValue(), + "filledTonalButtonDisabledContentColor" to colorScheme.onSurfaceVariantSummary.toCssValue(), + "filledTonalButtonDisabledContainerColor" to colorScheme.surfaceVariant.toCssValue(), + "filledCardContentColor" to colorScheme.onPrimaryContainer.toCssValue(), + "filledCardContainerColor" to colorScheme.primaryContainer.toCssValue(), + "filledCardDisabledContentColor" to colorScheme.onSurfaceVariantSummary.toCssValue(), + "filledCardDisabledContainerColor" to colorScheme.surfaceVariant.toCssValue() + ) + + colorsCss.set(monetColors.toCssVars()) + } + } + + private fun Map.toCssVars(): String { + return buildString { + append(":root {\n") + for ((k, v) in this@toCssVars) { + append(" --$k: $v;\n") + } + append("}\n") + } + } + + private fun Color.toCssValue(): String { + fun Float.toHex(): String { + return (this * 255).toInt().coerceIn(0, 255).toString(16).padStart(2, '0') + } + return if (alpha == 1f) { + "#${red.toHex()}${green.toHex()}${blue.toHex()}" + } else { + "#${red.toHex()}${green.toHex()}${blue.toHex()}${alpha.toHex()}" + } + } +} diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/webui/SuFilePathHandler.java b/manager/app/src/main/java/com/sukisu/ultra/ui/webui/SuFilePathHandler.java index 1b57e3e0..74546fbf 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/webui/SuFilePathHandler.java +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/webui/SuFilePathHandler.java @@ -61,6 +61,7 @@ public final class SuFilePathHandler implements WebViewAssetLoader.PathHandler { private final Shell mShell; private final InsetsSupplier mInsetsSupplier; + private final Context mContext; public interface InsetsSupplier { @NonNull @@ -93,6 +94,7 @@ public final class SuFilePathHandler implements WebViewAssetLoader.PathHandler { */ public SuFilePathHandler(@NonNull Context context, @NonNull File directory, Shell rootShell, @NonNull InsetsSupplier insetsSupplier) { try { + mContext = context; mInsetsSupplier = insetsSupplier; mDirectory = new File(getCanonicalDirPath(directory)); if (!isAllowedInternalStorageDir(context)) { @@ -149,6 +151,18 @@ public final class SuFilePathHandler implements WebViewAssetLoader.PathHandler { new ByteArrayInputStream(css.getBytes(StandardCharsets.UTF_8)) ); } + if ("internal/colors.css".equals(path)) { + int colorMode = mContext.getSharedPreferences("settings", Context.MODE_PRIVATE).getInt("color_mode", 0); + String css = ""; + if (colorMode >= 3 && colorMode <= 5) { + css = MonetColorsProvider.INSTANCE.getColorsCss(); + } + return new WebResourceResponse( + "text/css", + "utf-8", + new ByteArrayInputStream(css.getBytes(StandardCharsets.UTF_8)) + ); + } try { File file = getCanonicalFileIfChild(mDirectory, path); if (file != null) {