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 <leecc0503@gmail.com>

---------

Signed-off-by: KOWX712 <leecc0503@gmail.com>
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>
This commit is contained in:
KOWX712
2025-11-26 12:36:32 +08:00
committed by ShirkNeko
parent 7c7e72f111
commit 9817724a10
3 changed files with 119 additions and 1 deletions

View File

@@ -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()
}
)
}

View File

@@ -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<String?> = 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<String, String>.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()}"
}
}
}

View File

@@ -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) {