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:
@@ -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()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user