diff --git a/packages/tui/src/app.tsx b/packages/tui/src/app.tsx index 73a44854d17..3cbe50a49d1 100644 --- a/packages/tui/src/app.tsx +++ b/packages/tui/src/app.tsx @@ -66,7 +66,7 @@ import { DialogHelp } from "./ui/dialog-help" import { DialogAgent } from "./component/dialog-agent" import { DialogSessionList } from "./component/dialog-session-list" import { ThemeErrorToast } from "./component/theme-error-toast" -import { ThemeProvider, useTheme } from "./context/theme" +import { ThemeProvider, useTheme, useThemes } from "./context/theme" import { Home } from "./routes/home" import { Session } from "./routes/session" import { PromptHistoryProvider } from "./component/prompt/history" @@ -423,8 +423,8 @@ function App(props: { pair?: DialogPairCredentials }) { const event = useEvent() const client = useClient() const toast = useToast() - const themeState = useTheme() - const { themeV2, mode, supports, setMode, locked, lock, unlock } = themeState + const theme = useTheme() + const { mode, supports, setMode, locked, lock, unlock } = useThemes() const data = useData() const location = useLocation() const exit = useExit() @@ -1090,7 +1090,7 @@ function App(props: { pair?: DialogPairCredentials }) { width={dimensions().width} height={dimensions().height} flexDirection="column" - backgroundColor={themeV2.background.default} + backgroundColor={theme.background.default} onMouseDown={(evt) => { if (copyOnSelectEnabled()) return if (evt.button !== MouseButton.RIGHT) return diff --git a/packages/tui/src/component/bg-pulse.tsx b/packages/tui/src/component/bg-pulse.tsx index 064cc314f93..b4635a27906 100644 --- a/packages/tui/src/component/bg-pulse.tsx +++ b/packages/tui/src/component/bg-pulse.tsx @@ -7,7 +7,7 @@ import { } from "@opentui/core" import { extend, useRenderer } from "@opentui/solid" import { onCleanup, onMount } from "solid-js" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { tint } from "../theme/color" import { GoUpsellArtPainter } from "./bg-pulse-render" @@ -70,7 +70,9 @@ declare module "@opentui/solid" { extend({ go_upsell_art: GoUpsellArtRenderable }) export function BgPulse() { - const { themeV2, mode } = useTheme().contextual("elevated") + const themes = useThemes() + const theme = themes.contextual("elevated") + const mode = themes.mode const renderer = useRenderer() let targetFps = renderer.targetFps let maxFps = renderer.maxFps @@ -91,9 +93,9 @@ export function BgPulse() { ) diff --git a/packages/tui/src/component/devtools-bar.tsx b/packages/tui/src/component/devtools-bar.tsx index 8fa55144e1b..abd9525a773 100644 --- a/packages/tui/src/component/devtools-bar.tsx +++ b/packages/tui/src/component/devtools-bar.tsx @@ -11,7 +11,7 @@ import { useData } from "../context/data" import { useLocation } from "../context/location" import { useRoute } from "../context/route" import { Keymap } from "../context/keymap" -import { useTheme } from "../context/theme" +import { useTheme, useThemes } from "../context/theme" import { DevTools } from "../devtools" import { usePlugin } from "../plugin/context" import { errorMessage } from "../util/error" @@ -31,12 +31,12 @@ export function DevToolsBar() { const location = useLocation() const route = useRoute() const plugins = usePlugin() - const theme = useTheme() + const themes = useThemes() const keymap = Keymap.use() const renderer = useRenderer() const dimensions = useTerminalDimensions() - const { themeV2, mode, supports, setMode } = theme - const elevatedTheme = theme.contextual("elevated").themeV2 + const { current: theme, mode, supports, setMode } = themes + const elevatedTheme = themes.contextual("elevated") const [panel, setPanel] = createSignal() const [dumping, setDumping] = createSignal(false) const [dumpPath, setDumpPath] = createSignal() @@ -196,8 +196,8 @@ export function DevToolsBar() { })), }, theme: { - name: theme.selected, - mode: theme.mode(), + name: themes.selected, + mode: themes.mode(), }, }, null, @@ -213,7 +213,7 @@ export function DevToolsBar() { } return ( - + {serverIndicator().icon} @@ -243,10 +243,10 @@ export function DevToolsBar() { {" "} @@ -279,10 +279,10 @@ export function DevToolsBar() { {statusIcon(runtime())} @@ -290,10 +290,10 @@ export function DevToolsBar() { {" "} @@ -320,11 +320,11 @@ export function DevToolsBar() { toggle("theme")}> - Theme + Theme Theme - + {(entry) => } @@ -336,7 +336,7 @@ export function DevToolsBar() { toggle("tools")}> - Tools + Tools Tools @@ -406,14 +406,14 @@ export function DevToolsBar() { - + ) } function BarItem(props: ParentProps<{ active: boolean; onClick: () => void }>) { - const { themeV2 } = useTheme() + const theme = useTheme() const renderer = useRenderer() return ( void }>) { flexDirection="row" paddingLeft={1} paddingRight={1} - backgroundColor={props.active ? themeV2.background.action.primary.focused : undefined} + backgroundColor={props.active ? theme.background.action.primary.focused : undefined} onMouseUp={() => { if (renderer.getSelection()?.getSelectedText()) return props.onClick() @@ -435,7 +435,7 @@ function BarItem(props: ParentProps<{ active: boolean; onClick: () => void }>) { } function PanelBox(props: ParentProps) { - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const renderer = useRenderer() return ( { if (renderer.getSelection()?.getSelectedText()) return @@ -461,32 +461,32 @@ function PanelBox(props: ParentProps) { } function PanelTitle(props: ParentProps) { - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") return ( - + {props.children} ) } function Row(props: { label: string; value: string }) { - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") return ( - {props.label} + {props.label} - {props.value} + {props.value} ) } function Action(props: ParentProps<{ onClick: () => void; disabled?: boolean; hoverBackground?: boolean }>) { - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const [hovered, setHovered] = createSignal(false) return ( setHovered(true)} onMouseOut={() => setHovered(false)} @@ -495,7 +495,7 @@ function Action(props: ParentProps<{ onClick: () => void; disabled?: boolean; ho if (!props.disabled) props.onClick() }} > - {props.children} + {props.children} ) } @@ -506,7 +506,7 @@ function cpuPercent(microseconds: number, milliseconds: number) { } function ProcessStat(props: { label: string; values: readonly number[]; unit: string; decimals?: number }) { - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const value = () => { const value = props.values.at(-1) if (value === undefined) return "--" @@ -515,13 +515,13 @@ function ProcessStat(props: { label: string; values: readonly number[]; unit: st return ( - {props.label} + {props.label} - {brailleGraph(props.values)} + {brailleGraph(props.values)} - {value()} + {value()} ) diff --git a/packages/tui/src/component/dialog-config.tsx b/packages/tui/src/component/dialog-config.tsx index 49d5f93650e..e62ce18d248 100644 --- a/packages/tui/src/component/dialog-config.tsx +++ b/packages/tui/src/component/dialog-config.tsx @@ -1,6 +1,6 @@ import { createMemo, createSignal } from "solid-js" import { useConfig } from "../config" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { DialogSelect } from "../ui/dialog-select" import { useToast } from "../ui/toast" @@ -267,7 +267,7 @@ export function settingID(setting: Setting) { export function DialogConfig(props: { current?: string }) { const config = useConfig() const toast = useToast() - const themeState = useTheme() + const themes = useThemes() const current = Math.max( 0, settings.findIndex((setting) => settingID(setting) === props.current), @@ -280,12 +280,12 @@ export function DialogConfig(props: { current?: string }) { if (!result || typeof result !== "object") return undefined return (result as Record)[key] }, config.data) - if (setting.path.join(".") === "theme.name") return current ?? themeState.selected + if (setting.path.join(".") === "theme.name") return current ?? themes.selected return current ?? setting.default } const values = (setting: Setting) => setting.path.join(".") === "theme.name" - ? Object.keys(themeState.all()).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })) + ? Object.keys(themes.all()).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })) : setting.values const display = (setting: Setting) => { const current = value(setting) diff --git a/packages/tui/src/component/dialog-debug.tsx b/packages/tui/src/component/dialog-debug.tsx index ed13a8ffe05..fcf2673187f 100644 --- a/packages/tui/src/component/dialog-debug.tsx +++ b/packages/tui/src/component/dialog-debug.tsx @@ -11,7 +11,7 @@ import { describeOS, describeTerminal } from "../util/system" import { useTuiApp } from "../context/runtime" export function DialogDebug() { - const { themeV2 } = useTheme() + const theme = useTheme() const dialog = useDialog() const route = useRoute() const local = useLocal() @@ -55,10 +55,10 @@ export function DialogDebug() { return ( - + Debug - dialog.clear()}> + dialog.clear()}> esc @@ -68,10 +68,10 @@ export function DialogDebug() { {(entry) => ( - + {entry.label.padEnd(10)} - + {entry.value} @@ -79,12 +79,12 @@ export function DialogDebug() { - Share this when reporting an issue. + Share this when reporting an issue. - + {copied() ? "✓ copied" : "copy"}{" "} - enter + enter diff --git a/packages/tui/src/component/dialog-integration.tsx b/packages/tui/src/component/dialog-integration.tsx index 8a168b457d3..5a32abe2861 100644 --- a/packages/tui/src/component/dialog-integration.tsx +++ b/packages/tui/src/component/dialog-integration.tsx @@ -11,7 +11,7 @@ import { useClipboard } from "../context/clipboard" import { useData } from "../context/data" import { useClient } from "../context/client" import { Keymap } from "../context/keymap" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { useDialog } from "../ui/dialog" import { DialogPrompt } from "../ui/dialog-prompt" import { DialogSelect } from "../ui/dialog-select" @@ -64,7 +64,7 @@ export function DialogIntegration( ) { const data = useData() const dialog = useDialog() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const options = createMemo(() => { const providers = data.location.websearch.list() ?? [] const providersByID = new Map(providers.map((provider) => [provider.id, provider])) @@ -87,7 +87,7 @@ export function DialogIntegration( disabled: methods.length === 0 && credentials.length === 0, gutter: integration.connections.length > 0 - ? () => + ? () => : undefined, onSelect: () => { if (credentials.length) return manageConnections(integration, methods, dialog, props.onConnected) @@ -103,12 +103,12 @@ export function DialogIntegration( options={options()} emptyView={ - No integrations available + No integrations available } noMatchView={ - No integrations found + No integrations found } /> @@ -303,16 +303,16 @@ function CommandPending(props: { function CommandView(props: { title: string; output: string; message: string }) { const dialog = useDialog() - const { themeV2 } = useTheme().contextual("elevated") - const { themeV2: overlayTheme } = useTheme().contextual("overlay") + const theme = useThemes().contextual("elevated") + const overlayTheme = useThemes().contextual("overlay") onMount(() => dialog.setSize("large")) return ( - + {props.title} - dialog.clear()}> + dialog.clear()}> esc close @@ -326,7 +326,7 @@ function CommandView(props: { title: string; output: string; message: string }) {props.output.trim()} - {props.message} + {props.message} ) @@ -341,7 +341,7 @@ function KeyMethod(props: { const dialog = useDialog() const client = useClient() const toast = useToast() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const [error, setError] = createSignal() return ( @@ -360,7 +360,7 @@ function KeyMethod(props: { .catch((cause) => setError(message(cause))) }} description={() => ( - {(value) => {value()}} + {(value) => {value()}} )} /> ) @@ -516,7 +516,7 @@ function OAuthCode(props: { const dialog = useDialog() const client = useClient() const toast = useToast() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const [error, setError] = createSignal() let settled = false @@ -550,9 +550,9 @@ function OAuthCode(props: { }} description={() => ( - {props.attempt.instructions} - - {(value) => {value()}} + {props.attempt.instructions} + + {(value) => {value()}} )} /> @@ -561,31 +561,31 @@ function OAuthCode(props: { function OAuthView(props: { title: string; url?: string; instructions?: string; message: string; copy?: boolean }) { const dialog = useDialog() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") return ( - + {props.title} - dialog.clear()}> + dialog.clear()}> esc {(url) => ( - + - {(instructions) => {instructions()}} + {(instructions) => {instructions()}} )} - {props.message} + {props.message} - - c copy + + c copy diff --git a/packages/tui/src/component/dialog-mcp.tsx b/packages/tui/src/component/dialog-mcp.tsx index e1ecd011f49..31fafd7ef88 100644 --- a/packages/tui/src/component/dialog-mcp.tsx +++ b/packages/tui/src/component/dialog-mcp.tsx @@ -5,7 +5,7 @@ import { Keymap } from "../context/keymap" import { pipe, sortBy } from "remeda" import { DialogSelect } from "../ui/dialog-select" import { useDialog } from "../ui/dialog" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { TextAttributes, type ScrollBoxRenderable } from "@opentui/core" import type { McpServer } from "@opencode-ai/client" import { useClipboard } from "../context/clipboard" @@ -20,12 +20,12 @@ function statusError(status: McpServer["status"]) { } function Status(props: { enabled: boolean; loading: boolean }) { - const { themeV2 } = useTheme().contextual("elevated") - if (props.loading) return ⋯ Loading + const theme = useThemes().contextual("elevated") + if (props.loading) return ⋯ Loading if (props.enabled) { - return ✓ Enabled + return ✓ Enabled } - return ○ Disabled + return ○ Disabled } export function DialogMcp() { @@ -33,7 +33,7 @@ export function DialogMcp() { const dialog = useDialog() const client = useClient() const toast = useToast() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const [focused, setFocused] = createSignal() const [detail, setDetail] = createSignal() const [loading, setLoading] = createSignal(null) @@ -110,7 +110,7 @@ export function DialogMcp() { ]} footer={ - enter to view error + enter to view error } /> @@ -134,8 +134,8 @@ function DialogMcpError(props: { server: McpServer; onBack: () => void }) { const dialog = useDialog() const clipboard = useClipboard() const toast = useToast() - const { themeV2 } = useTheme().contextual("elevated") - const { themeV2: overlayTheme } = useTheme().contextual("overlay") + const theme = useThemes().contextual("elevated") + const overlayTheme = useThemes().contextual("overlay") const dimensions = useTerminalDimensions() const config = useConfig().data const [copied, setCopied] = createSignal(false) @@ -171,14 +171,14 @@ function DialogMcpError(props: { server: McpServer; onBack: () => void }) { return ( - + MCP server: {props.server.name} - + esc back - ✗ Failed + ✗ Failed void }) { - ↑↓ scroll - + ↑↓ scroll + {copied() ? "✓ copied" : "c copy details"} diff --git a/packages/tui/src/component/dialog-move-session.tsx b/packages/tui/src/component/dialog-move-session.tsx index 6f2f3e6ba60..3a1171acf8e 100644 --- a/packages/tui/src/component/dialog-move-session.tsx +++ b/packages/tui/src/component/dialog-move-session.tsx @@ -6,7 +6,7 @@ import { DialogSelect, type DialogSelectOption } from "../ui/dialog-select" import { useDialog } from "../ui/dialog" import { useClient } from "../context/client" import { Keymap } from "../context/keymap" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { useData } from "../context/data" import { abbreviateHome } from "../runtime" import { useTuiPaths } from "../context/runtime" @@ -38,7 +38,7 @@ export function DialogMoveSession(props: DialogMoveSessionProps) { const dialog = useDialog() const client = useClient() const dimensions = useTerminalDimensions() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const sessionData = useData() const route = useRoute() const toast = useToast() @@ -172,18 +172,18 @@ export function DialogMoveSession(props: DialogMoveSessionProps) { return { title, titleView: isRemoving ? ( - Deleting {item.location} + Deleting {item.location} ) : deleting ? ( - + Press {shortcuts.get("dialog.move_session.delete")} again to confirm ) : suffix ? ( <> {visible.slice(0, split)} - {visible.slice(split)} + {visible.slice(split)} ) : undefined, - bg: deleting ? themeV2.background.action.destructive.default : undefined, + bg: deleting ? theme.background.action.destructive.default : undefined, value: { type: "directory", directory: item.location, @@ -316,7 +316,7 @@ export function DialogMoveSession(props: DialogMoveSessionProps) { title="Move session" titleView={ - + Move session @@ -329,25 +329,25 @@ export function DialogMoveSession(props: DialogMoveSessionProps) { emptyView={ showError() ? ( - + Could not load project directories - {errorMessage(loadError())} - Close and reopen Move session to try again. + {errorMessage(loadError())} + Close and reopen Move session to try again. ) : directories.loading || loadedProject.loading ? ( - Loading project directories… + Loading project directories… ) : ( - No project directories available + No project directories available ) } noMatchView={ - No project directories found + No project directories found } locked={showError() || directories.loading || loadedProject.loading || Boolean(removing())} diff --git a/packages/tui/src/component/dialog-pair.tsx b/packages/tui/src/component/dialog-pair.tsx index 0a5cb0d75d5..05565dd0df3 100644 --- a/packages/tui/src/component/dialog-pair.tsx +++ b/packages/tui/src/component/dialog-pair.tsx @@ -3,7 +3,7 @@ import { useTerminalDimensions } from "@opentui/solid" import { createMemo, createResource, createSignal, For, Show } from "solid-js" import { renderUnicodeCompact } from "uqr" import { useClient } from "../context/client" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { useDialog } from "../ui/dialog" import { errorMessage } from "../util/error" @@ -16,7 +16,7 @@ export function DialogPair(props: { credentials?: DialogPairCredentials }) { const client = useClient() const dialog = useDialog() const dimensions = useTerminalDimensions() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const [loadError, setLoadError] = createSignal() const [showPassword, setShowPassword] = createSignal(false) const [passwordHover, setPasswordHover] = createSignal(false) @@ -47,17 +47,17 @@ export function DialogPair(props: { credentials?: DialogPairCredentials }) { - URLs - {(url) => {url}} + URLs + {(url) => {url}} - Username - {value.username} + Username + {value.username} - Password + Password setPasswordHover(true)} onMouseOut={() => setPasswordHover(false)} @@ -67,7 +67,7 @@ export function DialogPair(props: { credentials?: DialogPairCredentials }) { ["localhost", "127.0.0.1", "[::1]"].includes(new URL(url).hostname))}> - + Run `opencode service set hostname 0.0.0.0` to access the service remotely. @@ -78,7 +78,7 @@ export function DialogPair(props: { credentials?: DialogPairCredentials }) { flexShrink={0} alignItems={horizontal() ? "flex-end" : "center"} > - {renderUnicodeCompact(JSON.stringify(value), { border: 1 })} + {renderUnicodeCompact(JSON.stringify(value), { border: 1 })} ) @@ -87,17 +87,17 @@ export function DialogPair(props: { credentials?: DialogPairCredentials }) { return ( - + Pair - dialog.clear()}> + dialog.clear()}> esc Loading server information…}> + Loading server information…}> = 36} fallback={ @@ -116,11 +116,11 @@ export function DialogPair(props: { credentials?: DialogPairCredentials }) { > {(error) => ( - + Could not load server information - {errorMessage(error())} - Close and reopen Pair to try again. + {errorMessage(error())} + Close and reopen Pair to try again. )} diff --git a/packages/tui/src/component/dialog-project-copy-name.tsx b/packages/tui/src/component/dialog-project-copy-name.tsx index 63a15bcace0..d172b224563 100644 --- a/packages/tui/src/component/dialog-project-copy-name.tsx +++ b/packages/tui/src/component/dialog-project-copy-name.tsx @@ -2,12 +2,12 @@ import { InputRenderable, TextAttributes } from "@opentui/core" import { Slug } from "@opencode-ai/core/util/slug" import { createSignal, onMount } from "solid-js" import { Keymap } from "../context/keymap" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { useDialog, type DialogContext } from "../ui/dialog" export function DialogProjectCopyName(props: { onConfirm: (name: string) => void }) { const dialog = useDialog() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const shortcuts = Keymap.useShortcuts() const [inputTarget, setInputTarget] = createSignal() let input: InputRenderable @@ -47,10 +47,10 @@ export function DialogProjectCopyName(props: { onConfirm: (name: string) => void return ( - + Name project copy - dialog.clear()}> + dialog.clear()}> esc @@ -61,17 +61,17 @@ export function DialogProjectCopyName(props: { onConfirm: (name: string) => void }} onSubmit={confirm} placeholder="Project copy name" - placeholderColor={themeV2.text.subdued} - textColor={themeV2.text.formfield.default} - focusedTextColor={themeV2.text.formfield.default} - cursorColor={themeV2.text.formfield.default} + placeholderColor={theme.text.subdued} + textColor={theme.text.formfield.default} + focusedTextColor={theme.text.formfield.default} + cursorColor={theme.text.formfield.default} /> - - enter submit + + enter submit - - {shortcuts.get("dialog.project_copy.generate")} generate one + + {shortcuts.get("dialog.project_copy.generate")} generate one diff --git a/packages/tui/src/component/dialog-retry-action.tsx b/packages/tui/src/component/dialog-retry-action.tsx index 24c4b7cdcf8..b98ba9580c0 100644 --- a/packages/tui/src/component/dialog-retry-action.tsx +++ b/packages/tui/src/component/dialog-retry-action.tsx @@ -2,7 +2,7 @@ import { RGBA, TextAttributes } from "@opentui/core" import open from "open" import { createSignal } from "solid-js" import { Keymap } from "../context/keymap" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { useDialog, type DialogContext } from "../ui/dialog" import { Link } from "../ui/link" import { BgPulse } from "./bg-pulse" @@ -38,9 +38,9 @@ function panelOverlay(color: RGBA) { export function DialogRetryAction(props: DialogRetryActionProps) { const dialog = useDialog() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const showGoTreatment = () => props.link === GO_URL - const textBg = () => (showGoTreatment() ? panelOverlay(themeV2.background.default) : undefined) + const textBg = () => (showGoTreatment() ? panelOverlay(theme.background.default) : undefined) const [selected, setSelected] = createSignal<"dismiss" | "action">("action") Keymap.createLayer(() => ({ @@ -85,26 +85,26 @@ export function DialogRetryAction(props: DialogRetryActionProps) { ) : null} - + {props.title} - dialog.clear()}> + dialog.clear()}> esc - + {props.message} {props.link ? ( showGoTreatment() ? ( - + ) : ( - + ) ) : ( @@ -115,13 +115,13 @@ export function DialogRetryAction(props: DialogRetryActionProps) { paddingLeft={2} paddingRight={2} backgroundColor={ - selected() === "dismiss" ? themeV2.background.action.primary.focused : RGBA.fromInts(0, 0, 0, 0) + selected() === "dismiss" ? theme.background.action.primary.focused : RGBA.fromInts(0, 0, 0, 0) } onMouseOver={() => setSelected("dismiss")} onMouseUp={() => dismiss(props, dialog)} > @@ -132,13 +132,13 @@ export function DialogRetryAction(props: DialogRetryActionProps) { paddingLeft={2} paddingRight={2} backgroundColor={ - selected() === "action" ? themeV2.background.action.primary.focused : RGBA.fromInts(0, 0, 0, 0) + selected() === "action" ? theme.background.action.primary.focused : RGBA.fromInts(0, 0, 0, 0) } onMouseOver={() => setSelected("action")} onMouseUp={() => runAction(props, dialog)} > diff --git a/packages/tui/src/component/dialog-session-delete-failed.tsx b/packages/tui/src/component/dialog-session-delete-failed.tsx index 1f671218044..d11b113a7f9 100644 --- a/packages/tui/src/component/dialog-session-delete-failed.tsx +++ b/packages/tui/src/component/dialog-session-delete-failed.tsx @@ -1,6 +1,6 @@ import { TextAttributes } from "@opentui/core" import { Keymap } from "../context/keymap" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { useDialog } from "../ui/dialog" import { createStore } from "solid-js/store" import { For } from "solid-js" @@ -13,7 +13,7 @@ export function DialogSessionDeleteFailed(props: { onDone?: () => void }) { const dialog = useDialog() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const [store, setStore] = createStore({ active: "delete" as "delete" | "restore", }) @@ -64,17 +64,17 @@ export function DialogSessionDeleteFailed(props: { return ( - + Failed to Delete Session - dialog.clear()}> + dialog.clear()}> esc - + {`The session "${props.session}" could not be deleted because the workspace "${props.workspace}" is not available.`} - + Choose how you want to recover this broken workspace session. @@ -86,7 +86,7 @@ export function DialogSessionDeleteFailed(props: { paddingRight={1} paddingTop={1} paddingBottom={1} - backgroundColor={item.id === store.active ? themeV2.background.action.primary.focused : undefined} + backgroundColor={item.id === store.active ? theme.background.action.primary.focused : undefined} onMouseUp={() => { setStore("active", item.id) void confirm() @@ -94,12 +94,12 @@ export function DialogSessionDeleteFailed(props: { > {item.title} {item.description} diff --git a/packages/tui/src/component/dialog-session-list.tsx b/packages/tui/src/component/dialog-session-list.tsx index b1067fa8598..349d216822a 100644 --- a/packages/tui/src/component/dialog-session-list.tsx +++ b/packages/tui/src/component/dialog-session-list.tsx @@ -7,7 +7,7 @@ import { useRoute } from "../context/route" import { useData } from "../context/data" import { Keymap } from "../context/keymap" import { Locale } from "../util/locale" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { useClient } from "../context/client" import { useLocal } from "../context/local" import { createDebouncedSignal } from "../util/signal" @@ -20,7 +20,9 @@ export function DialogSessionList() { const dialog = useDialog() const route = useRoute() const data = useData() - const { themeV2, mode } = useTheme().contextual("elevated") + const themes = useThemes() + const theme = themes.contextual("elevated") + const mode = themes.mode const client = useClient() const local = useLocal() const toast = useToast() @@ -109,13 +111,13 @@ export function DialogSessionList() { value: session.id, category, footer, - bg: deleting ? themeV2.background.action.destructive.focused : undefined, - fg: deleting ? themeV2.text.action.destructive.focused : undefined, + bg: deleting ? theme.background.action.destructive.focused : undefined, + fg: deleting ? theme.text.action.destructive.focused : undefined, gutter: data.session.family(session.id).some((id) => data.session.status(id) === "running") ? () => : slot === undefined ? undefined - : () => {slot}, + : () => {slot}, } } @@ -143,12 +145,12 @@ export function DialogSessionList() { }} emptyView={ - No sessions available + No sessions available } noMatchView={ - + {searchState().message} diff --git a/packages/tui/src/component/dialog-skill.tsx b/packages/tui/src/component/dialog-skill.tsx index d853e55fffd..3d2e31260ce 100644 --- a/packages/tui/src/component/dialog-skill.tsx +++ b/packages/tui/src/component/dialog-skill.tsx @@ -15,7 +15,7 @@ export type DialogSkillProps = { export function DialogSkill(props: DialogSkillProps) { const dialog = useDialog() const data = useData() - const { themeV2 } = useTheme() + const theme = useTheme() dialog.setSize("large") const [loadError, setLoadError] = createSignal() @@ -63,29 +63,29 @@ export function DialogSkill(props: DialogSkillProps) { - No skills available + No skills available } > - + Could not load skills - {errorMessage(loadError())} - Close and reopen Skills to try again. + {errorMessage(loadError())} + Close and reopen Skills to try again. - Loading skills… + Loading skills… } noMatchView={ - No skills found + No skills found } /> diff --git a/packages/tui/src/component/dialog-stash.tsx b/packages/tui/src/component/dialog-stash.tsx index 80aa75250cb..90c2dc8e534 100644 --- a/packages/tui/src/component/dialog-stash.tsx +++ b/packages/tui/src/component/dialog-stash.tsx @@ -3,7 +3,7 @@ import { DialogSelect } from "../ui/dialog-select" import { createMemo, createSignal } from "solid-js" import { Locale } from "../util/locale" import { Keymap } from "../context/keymap" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { usePromptStash, type StashEntry } from "./prompt/stash" function getRelativeTime(timestamp: number): string { @@ -29,7 +29,7 @@ function getStashPreview(input: string, maxLength: number = 50): string { export function DialogStash(props: { onSelect: (entry: StashEntry) => void }) { const dialog = useDialog() const stash = usePromptStash() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const shortcuts = Keymap.useShortcuts() const [toDelete, setToDelete] = createSignal() @@ -45,8 +45,8 @@ export function DialogStash(props: { onSelect: (entry: StashEntry) => void }) { title: isDeleting ? `Press ${shortcuts.get("stash.delete")} again to confirm` : getStashPreview(entry.prompt.text), - bg: isDeleting ? themeV2.background.action.destructive.focused : undefined, - fg: isDeleting ? themeV2.text.action.destructive.focused : undefined, + bg: isDeleting ? theme.background.action.destructive.focused : undefined, + fg: isDeleting ? theme.text.action.destructive.focused : undefined, value: index, description: getRelativeTime(entry.timestamp), footer: lineCount > 1 ? `~${lineCount} lines` : undefined, diff --git a/packages/tui/src/component/dialog-status.tsx b/packages/tui/src/component/dialog-status.tsx index 2e983465beb..2e9675c807a 100644 --- a/packages/tui/src/component/dialog-status.tsx +++ b/packages/tui/src/component/dialog-status.tsx @@ -1,5 +1,5 @@ import { TextAttributes } from "@opentui/core" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { useDialog } from "../ui/dialog" import { useData } from "../context/data" import { For, Match, Switch, Show, createMemo } from "solid-js" @@ -8,30 +8,30 @@ export type DialogStatusProps = {} export function DialogStatus() { const data = useData() - const { themeV2 } = useTheme().contextual("elevated") + const theme = useThemes().contextual("elevated") const dialog = useDialog() const mcp = createMemo(() => data.location.mcp.server.list() ?? []) const color = (status: string) => { - if (status === "connected") return themeV2.text.feedback.success.default - if (status === "failed") return themeV2.text.feedback.error.default - if (status === "needs_auth") return themeV2.text.feedback.warning.default - if (status === "needs_client_registration") return themeV2.text.feedback.error.default - return themeV2.text.subdued + if (status === "connected") return theme.text.feedback.success.default + if (status === "failed") return theme.text.feedback.error.default + if (status === "needs_auth") return theme.text.feedback.warning.default + if (status === "needs_client_registration") return theme.text.feedback.error.default + return theme.text.subdued } return ( - + Status - dialog.clear()}> + dialog.clear()}> esc - 0} fallback={No MCP servers}> + 0} fallback={No MCP servers}> - + {mcp().length} MCP server{mcp().length === 1 ? "" : "s"} @@ -40,9 +40,9 @@ export function DialogStatus() { - + {item.name}{" "} - + Connected {(val) => val().error} diff --git a/packages/tui/src/component/dialog-theme-list.tsx b/packages/tui/src/component/dialog-theme-list.tsx index 6cf3539ad94..f1a8cda376a 100644 --- a/packages/tui/src/component/dialog-theme-list.tsx +++ b/packages/tui/src/component/dialog-theme-list.tsx @@ -1,11 +1,11 @@ import { DialogSelect, type DialogSelectRef } from "../ui/dialog-select" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { useDialog } from "../ui/dialog" import { onCleanup } from "solid-js" export function DialogThemeList() { - const theme = useTheme() - const options = Object.keys(theme.all()) + const themes = useThemes() + const options = Object.keys(themes.all()) .sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })) .map((value) => ({ title: value, @@ -14,10 +14,10 @@ export function DialogThemeList() { const dialog = useDialog() let confirmed = false let ref: DialogSelectRef - const initial = theme.selected + const initial = themes.selected onCleanup(() => { - if (!confirmed) theme.set(initial) + if (!confirmed) themes.set(initial) }) return ( @@ -26,10 +26,10 @@ export function DialogThemeList() { options={options} current={initial} onMove={(opt) => { - theme.set(opt.value) + themes.set(opt.value) }} onSelect={(opt) => { - theme.set(opt.value) + themes.set(opt.value) confirmed = true dialog.clear() }} @@ -38,12 +38,12 @@ export function DialogThemeList() { }} onFilter={(query) => { if (query.length === 0) { - theme.set(initial) + themes.set(initial) return } const first = ref.filtered[0] - if (first) theme.set(first.value) + if (first) themes.set(first.value) }} /> ) diff --git a/packages/tui/src/component/dialog-workspace-file-changes.tsx b/packages/tui/src/component/dialog-workspace-file-changes.tsx index 4d79dd26c6c..d349923e05e 100644 --- a/packages/tui/src/component/dialog-workspace-file-changes.tsx +++ b/packages/tui/src/component/dialog-workspace-file-changes.tsx @@ -4,7 +4,7 @@ import type { VcsFileStatus } from "@opencode-ai/client" import { createMemo, For } from "solid-js" import { createStore } from "solid-js/store" import { FilePath } from "../ui/file-path" -import { useTheme } from "../context/theme" +import { useThemes } from "../context/theme" import { useConfig } from "../config" import { useDialog, type DialogContext } from "../ui/dialog" import { getScrollAcceleration } from "../util/scroll" @@ -31,8 +31,8 @@ export function DialogWorkspaceFileChanges(props: { message?: string }) { const dialog = useDialog() - const { themeV2 } = useTheme().contextual("elevated") - const { themeV2: overlayTheme } = useTheme().contextual("overlay") + const theme = useThemes().contextual("elevated") + const overlayTheme = useThemes().contextual("overlay") const config = useConfig().data const dimensions = useTerminalDimensions() const scrollAcceleration = createMemo(() => getScrollAcceleration(config)) @@ -72,15 +72,15 @@ export function DialogWorkspaceFileChanges(props: { return ( - + {props.title ?? "File Changes Found"} - dialog.clear()}> + dialog.clear()}> esc - + {props.message ?? "Do you want to move these changes with the session?"} @@ -118,16 +118,14 @@ export function DialogWorkspaceFileChanges(props: { { setStore("active", item) props.onSelect(item) dialog.clear() }} > - - {item} - + {item} )} diff --git a/packages/tui/src/component/logo.tsx b/packages/tui/src/component/logo.tsx index 21465820594..adf6eb7d6db 100644 --- a/packages/tui/src/component/logo.tsx +++ b/packages/tui/src/component/logo.tsx @@ -5,10 +5,10 @@ import { tint } from "../theme/color" import { logo } from "../logo" export function Logo() { - const { themeV2 } = useTheme() + const theme = useTheme() const renderLine = (line: string, fg: RGBA, bold: boolean): JSX.Element[] => { - const shadow = tint(themeV2.background.default, fg, 0.25) + const shadow = tint(theme.background.default, fg, 0.25) const attrs = bold ? TextAttributes.BOLD : undefined return Array.from(line).map((char) => { if (char === "_") { @@ -52,8 +52,8 @@ export function Logo() { {(line, index) => ( - {renderLine(line, themeV2.text.subdued, false)} - {renderLine(logo.right[index()], themeV2.text.default, true)} + {renderLine(line, theme.text.subdued, false)} + {renderLine(logo.right[index()], theme.text.default, true)} )} diff --git a/packages/tui/src/component/plugin-route-missing.tsx b/packages/tui/src/component/plugin-route-missing.tsx index 5b36dcbdaf6..d9c2ff79477 100644 --- a/packages/tui/src/component/plugin-route-missing.tsx +++ b/packages/tui/src/component/plugin-route-missing.tsx @@ -1,20 +1,20 @@ import { useTheme } from "../context/theme" export function PluginRouteMissing(props: { id: string; name: string; onHome: () => void }) { - const { themeV2 } = useTheme() + const theme = useTheme() return ( - + Unknown plugin route: {props.id}/{props.name} - go home + go home ) diff --git a/packages/tui/src/component/prompt/autocomplete.tsx b/packages/tui/src/component/prompt/autocomplete.tsx index 921d096819c..10c1f037dda 100644 --- a/packages/tui/src/component/prompt/autocomplete.tsx +++ b/packages/tui/src/component/prompt/autocomplete.tsx @@ -12,7 +12,7 @@ import { getScrollAcceleration } from "../../util/scroll" import { useTuiPaths } from "../../context/runtime" import { useConfig } from "../../config" import { useLocation } from "../../context/location" -import { useTheme } from "../../context/theme" +import { useThemes } from "../../context/theme" import { SplitBorder } from "../../ui/border" import { useTerminalDimensions } from "@opentui/solid" import { Locale } from "../../util/locale" @@ -57,7 +57,7 @@ export function Autocomplete(props: { const data = useData() const keymap = Keymap.use() const keymapCommands = Keymap.useCommands() - const { themeV2 } = useTheme().contextual("overlay") + const theme = useThemes().contextual("overlay") const dimensions = useTerminalDimensions() const frecency = useFrecency() const config = useConfig().data @@ -698,11 +698,11 @@ export function Autocomplete(props: { width={position().width} zIndex={100} {...SplitBorder} - borderColor={themeV2.border.default} + borderColor={theme.border.default} > (scroll = r)} - backgroundColor={themeV2.background.default} + backgroundColor={theme.background.default} height={height()} scrollbarOptions={{ visible: false }} scrollAcceleration={scrollAcceleration()} @@ -711,9 +711,7 @@ export function Autocomplete(props: { each={options()} fallback={ - - {emptyMessage()} - + {emptyMessage()} } > @@ -721,7 +719,7 @@ export function Autocomplete(props: { { setStore("input", "mouse") @@ -737,14 +735,14 @@ export function Autocomplete(props: { onMouseUp={() => select()} > {option().display} {" " + option().description?.trimStart()} diff --git a/packages/tui/src/component/prompt/index.tsx b/packages/tui/src/component/prompt/index.tsx index ff4b516a889..44e98f92d5d 100644 --- a/packages/tui/src/component/prompt/index.tsx +++ b/packages/tui/src/component/prompt/index.tsx @@ -12,7 +12,7 @@ import { registerOpencodeSpinner } from "../register-spinner" import path from "path" import { fileURLToPath } from "url" import { useLocal } from "../../context/local" -import { useTheme } from "../../context/theme" +import { useTheme, useThemes } from "../../context/theme" import { tint } from "../../theme/color" import { EmptyBorder, SplitBorder } from "../../ui/border" import { useTuiPaths, useTuiTerminalEnvironment } from "../../context/runtime" @@ -189,7 +189,8 @@ export function Prompt(props: PromptProps) { const renderer = useRenderer() const exit = useExit() const dimensions = useTerminalDimensions() - const { themeV2, syntax } = useTheme() + const theme = useTheme() + const { currentSyntax: syntax } = useThemes() const animationsEnabled = createMemo(() => config.animations ?? true) const list = createMemo(() => props.placeholders?.normal ?? []) const shell = createMemo(() => props.placeholders?.shell ?? []) @@ -296,8 +297,8 @@ export function Prompt(props: PromptProps) { createEffect(() => { if (!input || input.isDestroyed) return - if (props.disabled) input.cursorColor = themeV2.background.surface.offset - if (!props.disabled) input.cursorColor = themeV2.text.default + if (props.disabled) input.cursorColor = theme.background.surface.offset + if (!props.disabled) input.cursorColor = theme.text.default }) const usage = createMemo(() => { @@ -1299,10 +1300,10 @@ export function Prompt(props: PromptProps) { } const highlight = createMemo(() => { - if (leader()) return themeV2.border.default - if (store.mode === "shell") return themeV2.text.action.primary.selected + if (leader()) return theme.border.default + if (store.mode === "shell") return theme.text.action.primary.selected const agent = local.agent.current() - if (!agent) return themeV2.border.default + if (!agent) return theme.border.default return local.agent.color(agent.id) }) const agentLabel = createMemo(() => { @@ -1324,7 +1325,7 @@ export function Prompt(props: PromptProps) { () => !!local.agent.current() && store.mode === "normal" && showVariant(), animationsEnabled, ) - const borderHighlight = createMemo(() => tint(themeV2.border.default, highlight(), agentMetaAlpha())) + const borderHighlight = createMemo(() => tint(theme.border.default, highlight(), agentMetaAlpha())) const placeholderText = createMemo(() => { if (props.showPlaceholder === false) return undefined @@ -1344,7 +1345,7 @@ export function Prompt(props: PromptProps) { const spinnerDef = createMemo(() => { const agent = status() === "running" ? local.agent.current() : local.agent.current() - const color = agent ? local.agent.color(agent.id) : themeV2.border.default + const color = agent ? local.agent.color(agent.id) : theme.border.default return { frames: createFrames({ color, @@ -1364,7 +1365,7 @@ export function Prompt(props: PromptProps) { }) const maxHeight = createMemo(() => Math.max(6, Math.floor(dimensions().height / 3))) - const promptBg = createMemo(() => themeV2.raise(themeV2.background.surface.offset)) + const promptBg = createMemo(() => theme.raise(theme.background.surface.offset)) return ( <> @@ -1390,9 +1391,9 @@ export function Prompt(props: PromptProps) {