summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/pages
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-12-28 05:12:32 -0600
committerAdam <[email protected]>2025-12-28 05:12:36 -0600
commit4a9ff9412e8daedc36319bd2ee8ca62d5aa52be7 (patch)
treead035366cdebc87d4eea75475201af3d11bd64ed /packages/app/src/pages
parentd6db6ff198c4513ca5511ae49b03d4277bc21718 (diff)
downloadopencode-4a9ff9412e8daedc36319bd2ee8ca62d5aa52be7.tar.gz
opencode-4a9ff9412e8daedc36319bd2ee8ca62d5aa52be7.zip
feat(desktop): themes
Diffstat (limited to 'packages/app/src/pages')
-rw-r--r--packages/app/src/pages/layout.tsx173
1 files changed, 123 insertions, 50 deletions
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 0b4e040b7..08b340187 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -47,6 +47,7 @@ import { useNotification } from "@/context/notification"
import { Binary } from "@opencode-ai/util/binary"
import { Header } from "@/components/header"
import { useDialog } from "@opencode-ai/ui/context/dialog"
+import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme"
import { DialogSelectProvider } from "@/components/dialog-select-provider"
import { useCommand } from "@/context/command"
import { ConstrainDragXAxis } from "@/utils/solid-dnd"
@@ -89,6 +90,41 @@ export default function Layout(props: ParentProps) {
const providers = useProviders()
const dialog = useDialog()
const command = useCommand()
+ const theme = useTheme()
+ const availableThemeEntries = createMemo(() => Object.entries(theme.themes()))
+ const colorSchemeOrder: ColorScheme[] = ["system", "light", "dark"]
+ const colorSchemeLabel: Record<ColorScheme, string> = {
+ system: "System",
+ light: "Light",
+ dark: "Dark",
+ }
+
+ function cycleTheme(direction = 1) {
+ const ids = availableThemeEntries().map(([id]) => id)
+ if (ids.length === 0) return
+ const currentIndex = ids.indexOf(theme.themeId())
+ const nextIndex = currentIndex === -1 ? 0 : (currentIndex + direction + ids.length) % ids.length
+ const nextThemeId = ids[nextIndex]
+ theme.setTheme(nextThemeId)
+ const nextTheme = theme.themes()[nextThemeId]
+ showToast({
+ title: "Theme switched",
+ description: nextTheme?.name ?? nextThemeId,
+ })
+ }
+
+ function cycleColorScheme(direction = 1) {
+ const current = theme.colorScheme()
+ const currentIndex = colorSchemeOrder.indexOf(current)
+ const nextIndex =
+ currentIndex === -1 ? 0 : (currentIndex + direction + colorSchemeOrder.length) % colorSchemeOrder.length
+ const next = colorSchemeOrder[nextIndex]
+ theme.setColorScheme(next)
+ showToast({
+ title: "Color scheme",
+ description: colorSchemeLabel[next],
+ })
+ }
onMount(async () => {
if (platform.checkUpdate && platform.update && platform.restart) {
@@ -286,57 +322,94 @@ export default function Layout(props: ParentProps) {
}
}
- command.register(() => [
- {
- id: "sidebar.toggle",
- title: "Toggle sidebar",
- category: "View",
- keybind: "mod+b",
- onSelect: () => layout.sidebar.toggle(),
- },
- ...(platform.openDirectoryPickerDialog
- ? [
- {
- id: "project.open",
- title: "Open project",
- category: "Project",
- keybind: "mod+o",
- onSelect: () => chooseProject(),
- },
- ]
- : []),
- {
- id: "provider.connect",
- title: "Connect provider",
- category: "Provider",
- onSelect: () => connectProvider(),
- },
- {
- id: "session.previous",
- title: "Previous session",
- category: "Session",
- keybind: "alt+arrowup",
- onSelect: () => navigateSessionByOffset(-1),
- },
- {
- id: "session.next",
- title: "Next session",
- category: "Session",
- keybind: "alt+arrowdown",
- onSelect: () => navigateSessionByOffset(1),
- },
- {
- id: "session.archive",
- title: "Archive session",
- category: "Session",
- keybind: "mod+shift+backspace",
- disabled: !params.dir || !params.id,
- onSelect: () => {
- const session = currentSessions().find((s) => s.id === params.id)
- if (session) archiveSession(session)
+ command.register(() => {
+ const commands = [
+ {
+ id: "sidebar.toggle",
+ title: "Toggle sidebar",
+ category: "View",
+ keybind: "mod+b",
+ onSelect: () => layout.sidebar.toggle(),
+ },
+ ...(platform.openDirectoryPickerDialog
+ ? [
+ {
+ id: "project.open",
+ title: "Open project",
+ category: "Project",
+ keybind: "mod+o",
+ onSelect: () => chooseProject(),
+ },
+ ]
+ : []),
+ {
+ id: "provider.connect",
+ title: "Connect provider",
+ category: "Provider",
+ onSelect: () => connectProvider(),
+ },
+ {
+ id: "session.previous",
+ title: "Previous session",
+ category: "Session",
+ keybind: "alt+arrowup",
+ onSelect: () => navigateSessionByOffset(-1),
},
- },
- ])
+ {
+ id: "session.next",
+ title: "Next session",
+ category: "Session",
+ keybind: "alt+arrowdown",
+ onSelect: () => navigateSessionByOffset(1),
+ },
+ {
+ id: "session.archive",
+ title: "Archive session",
+ category: "Session",
+ keybind: "mod+shift+backspace",
+ disabled: !params.dir || !params.id,
+ onSelect: () => {
+ const session = currentSessions().find((s) => s.id === params.id)
+ if (session) archiveSession(session)
+ },
+ },
+ {
+ id: "theme.cycle",
+ title: "Cycle theme",
+ category: "Theme",
+ keybind: "mod+shift+t",
+ onSelect: () => cycleTheme(1),
+ },
+ ]
+
+ for (const [id, definition] of availableThemeEntries()) {
+ commands.push({
+ id: `theme.set.${id}`,
+ title: `Use theme: ${definition.name ?? id}`,
+ category: "Theme",
+ onSelect: () => theme.setTheme(id),
+ })
+ }
+
+ commands.push({
+ id: "theme.scheme.cycle",
+ title: "Cycle color scheme",
+ category: "Theme",
+ keybind: "mod+shift+s",
+ onSelect: () => cycleColorScheme(1),
+ })
+
+ for (const scheme of colorSchemeOrder) {
+ commands.push({
+ id: `theme.scheme.${scheme}`,
+ title: `Use color scheme: ${colorSchemeLabel[scheme]}`,
+ category: "Theme",
+ onSelect: () => theme.setColorScheme(scheme),
+ })
+ }
+
+ return commands
+ })
function connectProvider() {
dialog.show(() => <DialogSelectProvider />)