diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..3550a30f --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index 2e5417a8..87ba7f73 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,6 @@ vendor/ repo/ gradle-deps/ vendor-uniffi/ -webApp/src/wasm/ \ No newline at end of file +webApp/src/wasm/ + +/.direnv diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..96f37ecf --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1780243769, + "narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "331800de5053fcebacf6813adb5db9c9dca22a0c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..fea1e530 --- /dev/null +++ b/flake.nix @@ -0,0 +1,61 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + inputs: + inputs.flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import inputs.nixpkgs { + inherit system; + config.android_sdk.accept_license = true; + config.allowUnfree = true; + }; + + androidRelease = + let + importYAML = + file: + builtins.fromJSON ( + builtins.readFile ( + pkgs.runCommand "converted-yaml.json" { } ''${pkgs.yj}/bin/yj < "${file}" > "$out"'' + ) + ); + in + importYAML ./.github/workflows/release-android.yml; + + # this must be a full version, since it's used to find aapt2 + buildToolsVersion = androidRelease.env.ANDROID_BUILD_TOOLS; + + androidComposition = pkgs.androidenv.composeAndroidPackages { + platformVersions = [ androidRelease.env.ANDROID_API ]; + buildToolsVersions = [ buildToolsVersion ]; + includeNDK = true; + }; + + androidSdk = androidComposition.androidsdk; + in + { + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + # not really required here + androidSdk + + openjdk21 + + cargo + rustc + ]; + + # source: https://wiki.nixos.org/wiki/Android#gradlew + # override the aapt2 that gradle uses with the nix-shipped version + GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${androidSdk}/libexec/android-sdk/build-tools/${buildToolsVersion}/aapt2"; + + ANDROID_HOME = "${androidSdk}/libexec/android-sdk"; + }; + } + ); +} diff --git a/shared/src/commonMain/kotlin/org/mlm/mages/App.kt b/shared/src/commonMain/kotlin/org/mlm/mages/App.kt index adf69c0c..d4545af1 100644 --- a/shared/src/commonMain/kotlin/org/mlm/mages/App.kt +++ b/shared/src/commonMain/kotlin/org/mlm/mages/App.kt @@ -140,6 +140,7 @@ private fun AppContent( ThemeMode.System -> isSystemInDarkTheme() ThemeMode.Dark -> true ThemeMode.Light -> false + ThemeMode.Black -> true } val widgetTheme = if (isDark) "dark" else "light" val elementCallUrl = @@ -149,6 +150,7 @@ private fun AppContent( ProvideAppLocale(settings.appLanguageTagOrNull()) { MainTheme( darkTheme = isDark, + blackTheme = settings.themeMode == ThemeMode.Black, dynamicColors = settings.dynamicColors ) { val languageTag = LocalAppLocale.current diff --git a/shared/src/commonMain/kotlin/org/mlm/mages/settings/SettingsDefinition.kt b/shared/src/commonMain/kotlin/org/mlm/mages/settings/SettingsDefinition.kt index 86f097ed..bb3f6f1b 100644 --- a/shared/src/commonMain/kotlin/org/mlm/mages/settings/SettingsDefinition.kt +++ b/shared/src/commonMain/kotlin/org/mlm/mages/settings/SettingsDefinition.kt @@ -57,7 +57,7 @@ data class AppSettings( @Setting( title = "Theme", - description = "System / Light / Dark", + description = "System / Light / Dark / Black", category = Appearance::class, type = Dropdown::class, ) diff --git a/shared/src/commonMain/kotlin/org/mlm/mages/ui/theme/Theme.kt b/shared/src/commonMain/kotlin/org/mlm/mages/ui/theme/Theme.kt index 8d215e42..e43cee22 100644 --- a/shared/src/commonMain/kotlin/org/mlm/mages/ui/theme/Theme.kt +++ b/shared/src/commonMain/kotlin/org/mlm/mages/ui/theme/Theme.kt @@ -48,6 +48,34 @@ private val DarkColorScheme = darkColorScheme( onTertiaryContainer = Color(0xFFFFD8E4) ) +private val BlackColorScheme = darkColorScheme( + primary = AppColors.Purple80, + secondary = AppColors.PurpleGrey80, + tertiary = AppColors.Pink80, + + surface = Color(0xFF1A1C1E), + onSurface = Color(0xFFE2E2E6), + surfaceVariant = Color(0xFF44474E), + onSurfaceVariant = Color(0xFFC4C6D0), + + surfaceBright = Color(0xFF38393E), + surfaceDim = Color(0xFF121318), + surfaceContainerLowest = Color(0xFF0C0E13), + surfaceContainerLow = Color(0xFF1A1C22), + surfaceContainer = Color(0xFF1E2025), + surfaceContainerHigh = Color(0xFF282A2F), + surfaceContainerHighest = Color(0xFF33353A), + + background = Color(0xFF000000), + onBackground = Color(0xFFE2E2E6), + primaryContainer = Color(0xFF4F378B), + onPrimaryContainer = Color(0xFFEADDFF), + secondaryContainer = Color(0xFF36424C), + onSecondaryContainer = Color(0xFFDCE7F1), + tertiaryContainer = Color(0xFF633B48), + onTertiaryContainer = Color(0xFFFFD8E4) +) + private val LightColorScheme = lightColorScheme( primary = AppColors.Purple40, secondary = AppColors.PurpleGrey40, @@ -93,11 +121,12 @@ val AppShapes = Shapes( @Composable fun MainTheme( darkTheme: Boolean = isSystemInDarkTheme(), + blackTheme: Boolean = false, dynamicColors: Boolean = true, content: @Composable () -> Unit ) { val colorScheme = getDynamicColorScheme(darkTheme, dynamicColors) - ?: if (darkTheme) DarkColorScheme else LightColorScheme + ?: if (blackTheme) BlackColorScheme else if (darkTheme) DarkColorScheme else LightColorScheme MaterialExpressiveTheme( colorScheme = colorScheme,