1package soy.iko.opencode.ui.theme
3import android.app.Activity
4import android.content.Context
5import android.content.ContextWrapper
7import androidx.compose.foundation.isSystemInDarkTheme
8import androidx.compose.material3.MaterialTheme
9import androidx.compose.material3.darkColorScheme
10import androidx.compose.material3.dynamicDarkColorScheme
11import androidx.compose.material3.dynamicLightColorScheme
12import androidx.compose.material3.lightColorScheme
13import androidx.compose.runtime.Composable
14import androidx.compose.runtime.SideEffect
15import androidx.compose.ui.graphics.Color
16import androidx.compose.ui.platform.LocalContext
17import androidx.compose.ui.platform.LocalView
18import androidx.core.view.WindowCompat
21 * Tokyo Night — a dark, muted blue/purple palette. Every Material 3 color role is set
22 * explicitly (not just primary/secondary/tertiary) so components that read `error`,
23 * `primaryContainer`, `surfaceVariant`, `outline`, etc. match the brand instead of
24 * falling back to stock M3 defaults that clash with it.
26 * Source palette (Tokyo Night):
27 * bg #1A1B26 fg #C0CAF5 blue #7AA2F7 cyan #7DCFFF
28 * green #9ECE6A magenta #BB9AF7 red #F7768E yellow #E0AF68
29 * comment #565F89 gutter #414868
31private val DarkColors = darkColorScheme(
32 primary = Color(0xFF7AA2F7),
33 onPrimary = Color(0xFF112036),
34 primaryContainer = Color(0xFF2A3A5C),
35 onPrimaryContainer = Color(0xFFD6E2FF),
36 secondary = Color(0xFF9ECE6A),
37 onSecondary = Color(0xFF13260A),
38 secondaryContainer = Color(0xFF2B3B1F),
39 onSecondaryContainer = Color(0xFFD2F0A0),
40 tertiary = Color(0xFFBB9AF7),
41 onTertiary = Color(0xFF221036),
42 tertiaryContainer = Color(0xFF3A2A55),
43 onTertiaryContainer = Color(0xFFE6D6FF),
44 error = Color(0xFFF7768E),
45 onError = Color(0xFF370610),
46 errorContainer = Color(0xFF5C1A24),
47 onErrorContainer = Color(0xFFFFD9DE),
48 background = Color(0xFF1A1B26),
49 onBackground = Color(0xFFC0CAF5),
50 surface = Color(0xFF1F2335),
51 onSurface = Color(0xFFC0CAF5),
52 surfaceVariant = Color(0xFF2D3247),
53 onSurfaceVariant = Color(0xFF9AA5CE),
54 surfaceTint = Color(0xFF7AA2F7),
55 inverseSurface = Color(0xFFC0CAF5),
56 inverseOnSurface = Color(0xFF1A1B26),
57 outline = Color(0xFF565F89),
58 outlineVariant = Color(0xFF414868),
59 scrim = Color(0xFF000000),
63 * Tokyo Night Light — the day-mode counterpart. Pairs the same hue family with a soft
64 * off-white background so the brand identity survives a light setting instead of
65 * snapping to stock M3 purple-grey.
67private val LightColors = lightColorScheme(
68 primary = Color(0xFF34548A),
69 onPrimary = Color(0xFFFFFFFF),
70 primaryContainer = Color(0xFFD6E2FF),
71 onPrimaryContainer = Color(0xFF001A41),
72 secondary = Color(0xFF485E30),
73 onSecondary = Color(0xFFFFFFFF),
74 secondaryContainer = Color(0xFFC8F0A0),
75 onSecondaryContainer = Color(0xFF0E2000),
76 tertiary = Color(0xFF5A4A78),
77 onTertiary = Color(0xFFFFFFFF),
78 tertiaryContainer = Color(0xFFE6D6FF),
79 onTertiaryContainer = Color(0xFF180038),
80 error = Color(0xFF9B1C2E),
81 onError = Color(0xFFFFFFFF),
82 errorContainer = Color(0xFFFFD9DE),
83 onErrorContainer = Color(0xFF410008),
84 background = Color(0xFFF6F7FB),
85 onBackground = Color(0xFF1A1B26),
86 surface = Color(0xFFF1F3FA),
87 onSurface = Color(0xFF1A1B26),
88 surfaceVariant = Color(0xFFE0E4F0),
89 onSurfaceVariant = Color(0xFF444B6E),
90 surfaceTint = Color(0xFF34548A),
91 inverseSurface = Color(0xFF2D3247),
92 inverseOnSurface = Color(0xFFEEF0FB),
93 outline = Color(0xFF747DAA),
94 outlineVariant = Color(0xFFC3C8DA),
95 scrim = Color(0xFF000000),
99 * AMOLED — the Tokyo Night dark palette with pure-black backgrounds (#000000) so OLED
100 * displays power off pixels in large black areas, extending battery life. The accent
101 * colours stay the same so the brand identity is preserved; only the backgrounds and
102 * surfaces drop to true black.
104 * Every Material 3 surface role is overridden — not just `background`/`surface` — because
105 * stock M3 components (Card, ModalBottomSheet, AlertDialog, MenuItem) read the
106 * `surfaceContainer*` / `surfaceBright` / `surfaceDim` tiers internally. Leaving those at
107 * the Tokyo-Night-dark values would leak non-black dialogs and sheets, undercutting the
108 * OLED power-off promise. The container tiers step up from pure black so elevated surfaces
109 * (cards over the list, dialogs) still read as a distinct layer without powering pixels on.
111private val AmoledColors = DarkColors.copy(
112 background = Color(0xFF000000),
113 surface = Color(0xFF000000),
114 surfaceVariant = Color(0xFF1A1F2E),
115 surfaceDim = Color(0xFF000000),
116 surfaceBright = Color(0xFF1A1F2E),
117 surfaceContainerLowest = Color(0xFF000000),
118 surfaceContainerLow = Color(0xFF000000),
119 surfaceContainer = Color(0xFF0A0B10),
120 surfaceContainerHigh = Color(0xFF12141C),
121 surfaceContainerHighest = Color(0xFF1A1F2E),
122 inverseOnSurface = Color(0xFF000000),
127 darkTheme: Boolean = isSystemInDarkTheme(),
128 dynamicColor: Boolean = false,
129 amoled: Boolean = false,
130 content: @Composable () -> Unit,
132 val context = LocalContext.current
133 val colorScheme = when {
134 // AMOLED takes precedence over dynamic color: a user who explicitly chose pure-black
135 // surfaces wants OLED-black even with Material You on, rather than silently getting the
136 // wallpaper-derived palette (which left the AMOLED choice appearing to do nothing).
137 darkTheme && amoled -> AmoledColors
138 dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ->
139 if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
140 darkTheme -> DarkColors
143 val view = LocalView.current
144 if (!view.isInEditMode) {
146 val window = view.context.findActivity()?.window ?: return@SideEffect
147 val controller = WindowCompat.getInsetsController(window, view)
148 controller.isAppearanceLightStatusBars = !darkTheme
149 // Keep the navigation-bar icon/affordance contrast in sync with the status bar so
150 // 3-button-nav and gesture-nav devices don't end up with invisible controls on one
151 // theme. The system only guesses when this isn't set explicitly.
152 controller.isAppearanceLightNavigationBars = !darkTheme
156 colorScheme = colorScheme,
157 typography = OpencodeTypography,
158 shapes = OpencodeShapes,
164 * Preview swatches for the Tokyo Night palettes, exposed so the Settings theme picker
165 * can show color dots without reaching into the (otherwise private) full color schemes.
166 * Each list is [primary, secondary, tertiary, background].
168val DarkPaletteSwatches: List<Color> = listOf(
170 DarkColors.secondary,
172 DarkColors.background,
174val LightPaletteSwatches: List<Color> = listOf(
176 LightColors.secondary,
177 LightColors.tertiary,
178 LightColors.background,
180// AMOLED shares the Dark accent colors but uses a pure-black background, so its preview
181// must differ from Dark's (#1A1B26) dot — otherwise the two theme rows are visually
182// indistinguishable in the picker and the user can't tell them apart before selecting.
183val AmoledPaletteSwatches: List<Color> = listOf(
184 AmoledColors.primary,
185 AmoledColors.secondary,
186 AmoledColors.tertiary,
187 AmoledColors.background,
190private tailrec fun Context.findActivity(): Activity? = when (this) {
192 is ContextWrapper -> baseContext.findActivity()
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list