diff --git a/packages/tui/src/app.tsx b/packages/tui/src/app.tsx
index 0ebebad798..f0ec2a59b4 100644
--- a/packages/tui/src/app.tsx
+++ b/packages/tui/src/app.tsx
@@ -1088,7 +1088,7 @@ function App(props: { pair?: DialogPairCredentials; started: number }) {
width={dimensions().width}
height={dimensions().height}
flexDirection="column"
- backgroundColor={themeV2.background()}
+ backgroundColor={themeV2.background.default}
onMouseDown={(evt) => {
if (!Flag.OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT) return
if (evt.button !== MouseButton.RIGHT) return
diff --git a/packages/tui/src/component/devtools-sidebar.tsx b/packages/tui/src/component/devtools-sidebar.tsx
index f3ef08cc73..cca1965232 100644
--- a/packages/tui/src/component/devtools-sidebar.tsx
+++ b/packages/tui/src/component/devtools-sidebar.tsx
@@ -17,26 +17,26 @@ export function DevToolsSidebar() {
paddingBottom={1}
paddingLeft={2}
paddingRight={2}
- backgroundColor={themeV2.background()}
+ backgroundColor={themeV2.background.default}
>
-
+
Theme
- Mode
+ Mode
setModeHovered(canSwitchMode())}
onMouseOut={() => setModeHovered(false)}
onMouseUp={canSwitchMode() ? () => setMode(nextMode()) : undefined}
>
- {mode()}
+ {mode()}
@@ -44,16 +44,16 @@ export function DevToolsSidebar() {
{(group) => (
-
+
{group.title}
{(entry) => (
- {entry.key}
+ {entry.key}
- {String(entry.value)}
+ {String(entry.value)}
)}
diff --git a/packages/tui/src/component/dialog-debug.tsx b/packages/tui/src/component/dialog-debug.tsx
index deab786726..99e0fd0f71 100644
--- a/packages/tui/src/component/dialog-debug.tsx
+++ b/packages/tui/src/component/dialog-debug.tsx
@@ -54,10 +54,10 @@ export function DialogDebug() {
return (
-
+
Debug
- dialog.clear()}>
+ dialog.clear()}>
esc
@@ -67,10 +67,10 @@ export function DialogDebug() {
{(entry) => (
-
+
{entry.label.padEnd(10)}
-
+
{entry.value}
@@ -78,12 +78,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 0024bf3443..a2c6a531ab 100644
--- a/packages/tui/src/component/dialog-integration.tsx
+++ b/packages/tui/src/component/dialog-integration.tsx
@@ -74,7 +74,7 @@ export function DialogIntegration(props: { onConnected?: OnIntegrationConnected
footer: connectionSummary(integration) || undefined,
category: integration.id in INTEGRATION_PRIORITY ? "Popular" : "Services",
disabled: methods.length === 0,
- gutter: connected ? () => ✓ : undefined,
+ gutter: connected ? () => ✓ : undefined,
onSelect: () =>
credentialConnections(integration).length
? manageConnections(integration, methods, dialog, props.onConnected)
@@ -89,12 +89,12 @@ export function DialogIntegration(props: { onConnected?: OnIntegrationConnected
options={options()}
emptyView={
- No integrations available
+ No integrations available
}
noMatchView={
- No integrations found
+ No integrations found
}
/>
@@ -295,24 +295,24 @@ function CommandView(props: { title: string; output: string; message: string })
return (
-
+
{props.title}
- dialog.clear()}>
+ dialog.clear()}>
esc close
- {props.output.trim()}
+ {props.output.trim()}
- {props.message}
+ {props.message}
)
@@ -346,7 +346,7 @@ function KeyMethod(props: {
.catch((cause) => setError(message(cause)))
}}
description={() => (
- {(value) => {value()}}
+ {(value) => {value()}}
)}
/>
)
@@ -536,9 +536,9 @@ function OAuthCode(props: {
}}
description={() => (
- {props.attempt.instructions}
-
- {(value) => {value()}}
+ {props.attempt.instructions}
+
+ {(value) => {value()}}
)}
/>
@@ -551,27 +551,27 @@ function OAuthView(props: { title: string; url?: string; instructions?: string;
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 606e47a48e..e1ecd011f4 100644
--- a/packages/tui/src/component/dialog-mcp.tsx
+++ b/packages/tui/src/component/dialog-mcp.tsx
@@ -21,11 +21,11 @@ function statusError(status: McpServer["status"]) {
function Status(props: { enabled: boolean; loading: boolean }) {
const { themeV2 } = useTheme().contextual("elevated")
- if (props.loading) return ⋯ Loading
+ if (props.loading) return ⋯ Loading
if (props.enabled) {
- return ✓ Enabled
+ return ✓ Enabled
}
- return ○ Disabled
+ return ○ Disabled
}
export function DialogMcp() {
@@ -110,7 +110,7 @@ export function DialogMcp() {
]}
footer={
- enter to view error
+ enter to view error
}
/>
@@ -171,16 +171,16 @@ function DialogMcpError(props: { server: McpServer; onBack: () => void }) {
return (
-
+
MCP server: {props.server.name}
-
+
esc back
- ✗ Failed
+ ✗ Failed
void }) {
scrollbarOptions={{ visible: false }}
scrollAcceleration={getScrollAcceleration(config)}
>
-
+
{error()}
- ↑↓ 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 3348b6c2c8..6f2f3e6ba6 100644
--- a/packages/tui/src/component/dialog-move-session.tsx
+++ b/packages/tui/src/component/dialog-move-session.tsx
@@ -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() : undefined,
+ bg: deleting ? themeV2.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 d26c248a6d..0a5cb0d75d 100644
--- a/packages/tui/src/component/dialog-pair.tsx
+++ b/packages/tui/src/component/dialog-pair.tsx
@@ -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 ef51e5a37a..63a15bcace 100644
--- a/packages/tui/src/component/dialog-project-copy-name.tsx
+++ b/packages/tui/src/component/dialog-project-copy-name.tsx
@@ -47,10 +47,10 @@ export function DialogProjectCopyName(props: { onConfirm: (name: string) => void
return (
-
+
Name project copy
- dialog.clear()}>
+ dialog.clear()}>
esc
@@ -61,18 +61,17 @@ export function DialogProjectCopyName(props: { onConfirm: (name: string) => void
}}
onSubmit={confirm}
placeholder="Project copy name"
- placeholderColor={themeV2.text.subdued()}
- textColor={themeV2.text.formfield()}
- focusedTextColor={themeV2.text.formfield()}
- cursorColor={themeV2.text.formfield()}
+ placeholderColor={themeV2.text.subdued}
+ textColor={themeV2.text.formfield.default}
+ focusedTextColor={themeV2.text.formfield.default}
+ cursorColor={themeV2.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 01b5226fa1..24c4b7cdcf 100644
--- a/packages/tui/src/component/dialog-retry-action.tsx
+++ b/packages/tui/src/component/dialog-retry-action.tsx
@@ -40,7 +40,7 @@ export function DialogRetryAction(props: DialogRetryActionProps) {
const dialog = useDialog()
const { themeV2 } = useTheme().contextual("elevated")
const showGoTreatment = () => props.link === GO_URL
- const textBg = () => (showGoTreatment() ? panelOverlay(themeV2.background()) : undefined)
+ const textBg = () => (showGoTreatment() ? panelOverlay(themeV2.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("focused") : RGBA.fromInts(0, 0, 0, 0)
+ selected() === "dismiss" ? themeV2.background.action.primary.focused : RGBA.fromInts(0, 0, 0, 0)
}
onMouseOver={() => setSelected("dismiss")}
onMouseUp={() => dismiss(props, dialog)}
>
@@ -131,12 +131,14 @@ export function DialogRetryAction(props: DialogRetryActionProps) {
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 b70a17d44e..1f67121804 100644
--- a/packages/tui/src/component/dialog-session-delete-failed.tsx
+++ b/packages/tui/src/component/dialog-session-delete-failed.tsx
@@ -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("focused") : undefined}
+ backgroundColor={item.id === store.active ? themeV2.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-skill.tsx b/packages/tui/src/component/dialog-skill.tsx
index 8db0009100..d853e55fff 100644
--- a/packages/tui/src/component/dialog-skill.tsx
+++ b/packages/tui/src/component/dialog-skill.tsx
@@ -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-status.tsx b/packages/tui/src/component/dialog-status.tsx
index 7a0d71897d..2e983465be 100644
--- a/packages/tui/src/component/dialog-status.tsx
+++ b/packages/tui/src/component/dialog-status.tsx
@@ -13,25 +13,25 @@ export function DialogStatus() {
const mcp = createMemo(() => data.location.mcp.server.list() ?? [])
const color = (status: string) => {
- if (status === "connected") return themeV2.text.feedback.success()
- if (status === "failed") return themeV2.text.feedback.error()
- if (status === "needs_auth") return themeV2.text.feedback.warning()
- if (status === "needs_client_registration") return themeV2.text.feedback.error()
- return themeV2.text.subdued()
+ 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
}
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-workspace-file-changes.tsx b/packages/tui/src/component/dialog-workspace-file-changes.tsx
index f2c344065c..4d79dd26c6 100644
--- a/packages/tui/src/component/dialog-workspace-file-changes.tsx
+++ b/packages/tui/src/component/dialog-workspace-file-changes.tsx
@@ -72,21 +72,21 @@ 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?"}
@@ -95,18 +95,16 @@ export function DialogWorkspaceFileChanges(props: {
- {statusLabel(item.status)}
+ {statusLabel(item.status)}
-
+
{" "}
- {item.additions ? (
- +{item.additions}
- ) : null}
+ {item.additions ? +{item.additions} : null}
{item.deletions ? (
- -{item.deletions}
+ -{item.deletions}
) : null}
@@ -120,14 +118,16 @@ 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 0acaa44882..2146582059 100644
--- a/packages/tui/src/component/logo.tsx
+++ b/packages/tui/src/component/logo.tsx
@@ -8,7 +8,7 @@ export function Logo() {
const { themeV2 } = useTheme()
const renderLine = (line: string, fg: RGBA, bold: boolean): JSX.Element[] => {
- const shadow = tint(themeV2.background(), fg, 0.25)
+ const shadow = tint(themeV2.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(), true)}
+ {renderLine(line, themeV2.text.subdued, false)}
+ {renderLine(logo.right[index()], themeV2.text.default, true)}
)}
diff --git a/packages/tui/src/component/plugin-route-missing.tsx b/packages/tui/src/component/plugin-route-missing.tsx
index 2cbe40446b..5b36dcbdaf 100644
--- a/packages/tui/src/component/plugin-route-missing.tsx
+++ b/packages/tui/src/component/plugin-route-missing.tsx
@@ -5,16 +5,16 @@ export function PluginRouteMissing(props: { id: string; name: string; onHome: ()
return (
-
+
Unknown plugin route: {props.id}/{props.name}
- go home
+ go home
)
diff --git a/packages/tui/src/component/prompt/index.tsx b/packages/tui/src/component/prompt/index.tsx
index 1aed9227da..d6b39d85d4 100644
--- a/packages/tui/src/component/prompt/index.tsx
+++ b/packages/tui/src/component/prompt/index.tsx
@@ -297,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()
+ if (props.disabled) input.cursorColor = themeV2.background.surface.offset
+ if (!props.disabled) input.cursorColor = themeV2.text.default
})
const usage = createMemo(() => {
@@ -1304,10 +1304,10 @@ export function Prompt(props: PromptProps) {
}
const highlight = createMemo(() => {
- if (leader()) return themeV2.border()
- if (store.mode === "shell") return themeV2.background.action()
+ if (leader()) return themeV2.border.default
+ if (store.mode === "shell") return themeV2.background.action.primary.default
const agent = local.agent.current()
- if (!agent) return themeV2.border()
+ if (!agent) return themeV2.border.default
return local.agent.color(agent.id)
})
@@ -1324,7 +1324,7 @@ export function Prompt(props: PromptProps) {
() => !!local.agent.current() && store.mode === "normal" && showVariant(),
animationsEnabled,
)
- const borderHighlight = createMemo(() => tint(themeV2.border(), highlight(), agentMetaAlpha()))
+ const borderHighlight = createMemo(() => tint(themeV2.border.default, highlight(), agentMetaAlpha()))
const placeholderText = createMemo(() => {
if (props.showPlaceholder === false) return undefined
@@ -1344,7 +1344,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()
+ const color = agent ? local.agent.color(agent.id) : themeV2.border.default
return {
frames: createFrames({
color,
@@ -1364,7 +1364,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(() => themeV2.raise(themeV2.background.surface.offset))
return (
<>
@@ -1390,9 +1390,9 @@ export function Prompt(props: PromptProps) {
- auto
+ auto
- ·
+ ·
{local.model.parsed().model}
- {currentProviderLabel()}
+ {currentProviderLabel()}
- ·
+ ·
@@ -1543,12 +1543,12 @@ export function Prompt(props: PromptProps) {
- [⋯]}>
+ [⋯]}>
0 ? themeV2.background.action() : themeV2.text()}
+ fg={store.interrupt > 0 ? themeV2.background.action.primary.default : themeV2.text.default}
wrapMode="none"
truncate
flexShrink={1}
@@ -1556,7 +1556,7 @@ export function Prompt(props: PromptProps) {
esc{" "}
0 ? themeV2.background.action() : themeV2.text.subdued(),
+ fg: store.interrupt > 0 ? themeV2.background.action.primary.default : themeV2.text.subdued,
}}
>
{store.interrupt > 0 ? "again to interrupt" : "interrupt"}
@@ -1567,16 +1567,16 @@ export function Prompt(props: PromptProps) {
{(progress) => (
-
+
{progress()}
- {".".repeat(move.creatingDots())}
+ {".".repeat(move.creatingDots())}
)}
-
+
(new working copy)
@@ -1584,7 +1584,7 @@ export function Prompt(props: PromptProps) {
}>
{(location) => (
-
+
{location()}
)}
@@ -1598,7 +1598,7 @@ export function Prompt(props: PromptProps) {
wrapMode="none"
truncate
flexShrink={1}
- fg={editorContextLabelState() === "pending" ? themeV2.hue.accent(500) : themeV2.text.subdued()}
+ fg={editorContextLabelState() === "pending" ? themeV2.hue.accent[500] : themeV2.text.subdued}
>
{file()}
@@ -1608,40 +1608,40 @@ export function Prompt(props: PromptProps) {
0}>
-
+
- {(shortcut) => {shortcut()} }
+ {(shortcut) => {shortcut()} }
- {(label) => {label()}}
+ {(label) => {label()}}
- ·
+ ·
- {(label) => {label()}}
+ {(label) => {label()}}
0}>
- ·
+ ·
0}>
- {statusItems().join(" · ")}
+ {statusItems().join(" · ")}
-
- {agentShortcut()} agents
+
+ {agentShortcut()} agents
-
- {paletteShortcut()} commands
+
+ {paletteShortcut()} commands
-
- esc exit shell mode
+
+ esc exit shell mode
diff --git a/packages/tui/src/component/reconnecting.tsx b/packages/tui/src/component/reconnecting.tsx
index 538daafdd7..5c3e7f3618 100644
--- a/packages/tui/src/component/reconnecting.tsx
+++ b/packages/tui/src/component/reconnecting.tsx
@@ -12,12 +12,12 @@ export function Reconnecting() {
right={0}
bottom={0}
left={0}
- backgroundColor={themeV2.background()}
+ backgroundColor={themeV2.background.default}
alignItems="center"
justifyContent="center"
>
- Waiting for background service...
+ Waiting for background service...
)
diff --git a/packages/tui/src/component/spinner.tsx b/packages/tui/src/component/spinner.tsx
index af376153f4..85469c4884 100644
--- a/packages/tui/src/component/spinner.tsx
+++ b/packages/tui/src/component/spinner.tsx
@@ -13,7 +13,7 @@ registerOpencodeSpinner()
export function Spinner(props: { children?: JSX.Element; color?: RGBA }) {
const { themeV2 } = useTheme()
const config = useConfig().data
- const color = () => props.color ?? themeV2.text.subdued()
+ const color = () => props.color ?? themeV2.text.subdued
return (
boolean }) {
return (
-
- {text()}
+
+ {text()}
diff --git a/packages/tui/src/context/local.tsx b/packages/tui/src/context/local.tsx
index dd0a4a3f86..03716cdf12 100644
--- a/packages/tui/src/context/local.tsx
+++ b/packages/tui/src/context/local.tsx
@@ -91,7 +91,10 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
})
const colors = createMemo(() => {
const step = mode() === "light" ? 800 : 200
- return dedupeWith(themeV2.categorical(step), (first, second) => first.equals(second))
+ return dedupeWith(
+ themeV2.categorical.map((scale) => scale[step]),
+ (first, second) => first.equals(second),
+ )
})
return {
list() {
diff --git a/packages/tui/src/feature-plugins/home/footer.tsx b/packages/tui/src/feature-plugins/home/footer.tsx
index 3ff7b4d433..903bc16dfe 100644
--- a/packages/tui/src/feature-plugins/home/footer.tsx
+++ b/packages/tui/src/feature-plugins/home/footer.tsx
@@ -17,7 +17,7 @@ function Directory(props: { context: Plugin.Context; maxWidth: number }) {
return (
- {(value) => }
+ {(value) => }
)
}
@@ -31,20 +31,18 @@ function Mcp(props: { context: Plugin.Context }) {
return (
-
+
- ⊙
+ ⊙
- 0 ? themeV2.text.feedback.success() : themeV2.text.subdued() }}>
- ⊙{" "}
-
+ 0 ? themeV2.text.feedback.success.default : themeV2.text.subdued }}>⊙
{count()} MCP
- /status
+ /status
)
@@ -78,7 +76,7 @@ function View(props: { context: Plugin.Context }) {
- {InstallationVersion}
+ {InstallationVersion}
)
diff --git a/packages/tui/src/feature-plugins/sidebar/context.tsx b/packages/tui/src/feature-plugins/sidebar/context.tsx
index d2d33a9eaa..aaec364aa3 100644
--- a/packages/tui/src/feature-plugins/sidebar/context.tsx
+++ b/packages/tui/src/feature-plugins/sidebar/context.tsx
@@ -20,20 +20,20 @@ function View(props: { context: Plugin.Context; sessionID: string }) {
return (
-
+
Context
- Not measured}>
+ Not measured}>
{(value) => (
<>
- {value().tokens.toLocaleString()} tokens
+ {value().tokens.toLocaleString()} tokens
- {value().percent}% used
+ {value().percent}% used
>
)}
- {money.format(cost())} spent
+ {money.format(cost())} spent
)
}
diff --git a/packages/tui/src/feature-plugins/sidebar/footer.tsx b/packages/tui/src/feature-plugins/sidebar/footer.tsx
index 6ad0f5d848..49f5c25a48 100644
--- a/packages/tui/src/feature-plugins/sidebar/footer.tsx
+++ b/packages/tui/src/feature-plugins/sidebar/footer.tsx
@@ -12,7 +12,7 @@ function View(props: { context: Plugin.Context }) {
props.context.location ? abbreviateHome(props.context.location.directory, paths.home) : undefined,
)
return (
- {(value) => }
+ {(value) => }
)
}
diff --git a/packages/tui/src/feature-plugins/sidebar/lsp.tsx b/packages/tui/src/feature-plugins/sidebar/lsp.tsx
index 98f73bd5f5..63369e3448 100644
--- a/packages/tui/src/feature-plugins/sidebar/lsp.tsx
+++ b/packages/tui/src/feature-plugins/sidebar/lsp.tsx
@@ -5,10 +5,10 @@ function View() {
const { themeV2 } = useTheme()
return (
-
+
LSP
- LSP status unavailable
+ LSP status unavailable
)
}
diff --git a/packages/tui/src/feature-plugins/sidebar/mcp.tsx b/packages/tui/src/feature-plugins/sidebar/mcp.tsx
index a91f73c8d8..17f21c2b6c 100644
--- a/packages/tui/src/feature-plugins/sidebar/mcp.tsx
+++ b/packages/tui/src/feature-plugins/sidebar/mcp.tsx
@@ -19,12 +19,12 @@ function View(props: { context: Plugin.Context; sessionID: string }) {
)
const dot = (status: string) => {
- if (status === "connected") return themeV2.text.feedback.success()
- if (status === "failed") return themeV2.text.feedback.error()
- if (status === "disabled") return themeV2.text.subdued()
- if (status === "needs_auth") return themeV2.text.feedback.warning()
- if (status === "needs_client_registration") return themeV2.text.feedback.error()
- return themeV2.text.subdued()
+ if (status === "connected") return themeV2.text.feedback.success.default
+ if (status === "failed") return themeV2.text.feedback.error.default
+ if (status === "disabled") return themeV2.text.subdued
+ 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
}
return (
@@ -32,12 +32,12 @@ function View(props: { context: Plugin.Context; sessionID: string }) {
list().length > 2 && setOpen((x) => !x)}>
2}>
- {open() ? "▼" : "▶"}
+ {open() ? "▼" : "▶"}
-
+
MCP
-
+
{" "}
({on()} active{bad() > 0 ? `, ${bad()} error${bad() > 1 ? "s" : ""}` : ""})
@@ -56,9 +56,9 @@ function View(props: { context: Plugin.Context; sessionID: string }) {
>
•
-
+
{item.name}{" "}
-
+
Connected
diff --git a/packages/tui/src/feature-plugins/system/diff-viewer-ui.tsx b/packages/tui/src/feature-plugins/system/diff-viewer-ui.tsx
index af44ad655f..ca0e05aeed 100644
--- a/packages/tui/src/feature-plugins/system/diff-viewer-ui.tsx
+++ b/packages/tui/src/feature-plugins/system/diff-viewer-ui.tsx
@@ -38,7 +38,7 @@ export function Panel(props: Omit & { bo
? {}
: {
border: panelBorderSides(group?.axis ?? "y", border),
- borderColor: themeV2.border(),
+ borderColor: themeV2.border.default,
}
return (
@@ -60,7 +60,7 @@ function panelBorderSides(axis: Axis, border: Exclude): Bor
export function Separator(props: { axis?: Axis; color?: ColorInput; start?: SeparatorEdge; end?: SeparatorEdge }) {
const group = usePanelGroup()
const { themeV2 } = useTheme()
- const color = () => props.color ?? themeV2.border()
+ const color = () => props.color ?? themeV2.border.default
const axis = () => props.axis ?? crossAxis(group?.axis ?? "y")
if (axis() === "y") {
return (
diff --git a/packages/tui/src/feature-plugins/system/scrap.tsx b/packages/tui/src/feature-plugins/system/scrap.tsx
index 6118da5aac..1660a892f8 100644
--- a/packages/tui/src/feature-plugins/system/scrap.tsx
+++ b/packages/tui/src/feature-plugins/system/scrap.tsx
@@ -43,19 +43,19 @@ function Scrap(props: { context: Plugin.Context }) {
}))
return (
-
+
- ~/code/anomalyco/opencode
+ ~/code/anomalyco/opencode
- esc home
+ esc home
)
diff --git a/packages/tui/src/routes/session/composer/index.tsx b/packages/tui/src/routes/session/composer/index.tsx
index a68e1b9f9b..4c53fe9043 100644
--- a/packages/tui/src/routes/session/composer/index.tsx
+++ b/packages/tui/src/routes/session/composer/index.tsx
@@ -113,8 +113,8 @@ export function Composer(props: ComposerProps) {
1}
fallback={
-
+
{tabList()[0]?.label ?? ""}
}
@@ -136,7 +136,7 @@ export function Composer(props: ComposerProps) {
const isActive = createMemo(() => store.active === t.id)
return (
{t.label}
@@ -146,7 +146,7 @@ export function Composer(props: ComposerProps) {
-
+
esc
@@ -156,19 +156,19 @@ export function Composer(props: ComposerProps) {
{(hint) => (
-
+
{hint.label}{" "}
- {hint.shortcut}
+ {hint.shortcut}
)}
1}>
-
+
tabs{" "}
- ←/→
+ ←/→
diff --git a/packages/tui/src/routes/session/composer/shell-tab.tsx b/packages/tui/src/routes/session/composer/shell-tab.tsx
index ed1b4ca827..b945292de9 100644
--- a/packages/tui/src/routes/session/composer/shell-tab.tsx
+++ b/packages/tui/src/routes/session/composer/shell-tab.tsx
@@ -98,7 +98,7 @@ export function ShellTab(props: { sessionID: string }) {
return (
(scroll = r)}>
- 0} fallback={ No shell commands}>
+ 0} fallback={ No shell commands}>
{(shell, index) => {
const active = createMemo(() => index() === store.selected)
@@ -107,11 +107,13 @@ export function ShellTab(props: { sessionID: string }) {
flexDirection="row"
paddingLeft={1}
paddingRight={1}
- backgroundColor={themeV2.background.action({ focused: active() })}
+ backgroundColor={
+ active() ? themeV2.background.action.primary.focused : themeV2.background.action.primary.default
+ }
onMouseOver={() => setStore("selected", index())}
>
diff --git a/packages/tui/src/routes/session/composer/subagents-tab.tsx b/packages/tui/src/routes/session/composer/subagents-tab.tsx
index 1e907120fb..4f3c976ddd 100644
--- a/packages/tui/src/routes/session/composer/subagents-tab.tsx
+++ b/packages/tui/src/routes/session/composer/subagents-tab.tsx
@@ -205,7 +205,7 @@ export function SubagentsTab(props: { sessionID: string }) {
return (
(scroll = r)}>
- 0} fallback={ No subagents}>
+ 0} fallback={ No subagents}>
{(entry, index) => {
const active = createMemo(() => index() === selected())
@@ -218,7 +218,13 @@ export function SubagentsTab(props: { sessionID: string }) {
flexDirection="row"
paddingLeft={1}
paddingRight={1}
- backgroundColor={themeV2.background.action({ focused: active(), selected: entry.current })}
+ backgroundColor={
+ active()
+ ? themeV2.background.action.primary.focused
+ : entry.current
+ ? themeV2.background.action.primary.selected
+ : themeV2.background.action.primary.default
+ }
onMouseOver={() => setStore("selected", index())}
onMouseUp={() => {
setStore("selected", index())
@@ -227,7 +233,13 @@ export function SubagentsTab(props: { sessionID: string }) {
>
@@ -235,14 +247,7 @@ export function SubagentsTab(props: { sessionID: string }) {
-
+
{status()}
diff --git a/packages/tui/src/routes/session/footer.tsx b/packages/tui/src/routes/session/footer.tsx
index c0e9f5e409..106cbda253 100644
--- a/packages/tui/src/routes/session/footer.tsx
+++ b/packages/tui/src/routes/session/footer.tsx
@@ -13,9 +13,7 @@ export function Footer() {
const mcp = createMemo(
() => (data.location.mcp.server.list() ?? []).filter((x) => x.status.status === "connected").length,
)
- const mcpError = createMemo(() =>
- (data.location.mcp.server.list() ?? []).some((x) => x.status.status === "failed"),
- )
+ const mcpError = createMemo(() => (data.location.mcp.server.list() ?? []).some((x) => x.status.status === "failed"))
const permissions = createMemo(() => {
if (route.data.type !== "session") return []
return data.session.permission.list(route.data.sessionID) ?? []
@@ -54,35 +52,35 @@ export function Footer() {
return (
- {directory()}
+ {directory()}
-
- Get started /connect
+
+ Get started /connect
0}>
-
- △ {permissions().length} Permission
+
+ △ {permissions().length} Permission
{permissions().length > 1 ? "s" : ""}
-
+
- ⊙
+ ⊙
- ⊙
+ ⊙
{mcp()} MCP
- /status
+ /status
diff --git a/packages/tui/src/routes/session/form.tsx b/packages/tui/src/routes/session/form.tsx
index 5546e5f54f..510bc7b632 100644
--- a/packages/tui/src/routes/session/form.tsx
+++ b/packages/tui/src/routes/session/form.tsx
@@ -624,27 +624,27 @@ export function FormPrompt(props: { form: FormWithLocation }) {
return (
- {props.form.title}
+ {props.form.title}
- {message()}
+ {message()}
-
+
{confirm() ? "Review" : `Field ${Math.min(store.tab, fields().length - 1) + 1} of ${fields().length}`}
0}>
-
+
· {answered()}/{fields().length} completed
@@ -661,10 +661,10 @@ export function FormPrompt(props: { form: FormWithLocation }) {
paddingRight={2}
backgroundColor={
isTab()
- ? themeV2.background.formfield("selected")
+ ? themeV2.background.formfield.selected
: tabHover() === index()
- ? themeV2.background.formfield("focused")
- : themeV2.background()
+ ? themeV2.background.formfield.focused
+ : themeV2.background.default
}
onMouseOver={() => setTabHover(index())}
onMouseOut={() => setTabHover(null)}
@@ -676,12 +676,12 @@ export function FormPrompt(props: { form: FormWithLocation }) {
{truncate(formLabel(item), 24)}
@@ -693,10 +693,10 @@ export function FormPrompt(props: { form: FormWithLocation }) {
setTabHover("confirm")}
onMouseOut={() => setTabHover(null)}
@@ -705,7 +705,7 @@ export function FormPrompt(props: { form: FormWithLocation }) {
selectTabFromMouse()
}}
>
- Confirm
+ Confirm
@@ -714,13 +714,13 @@ export function FormPrompt(props: { form: FormWithLocation }) {
{(external) => (
- {external().title}
+ {external().title}
- {external().description}
+ {external().description}
{
if (renderer.getSelection()?.getSelectedText()) return
openExternal()
@@ -729,7 +729,9 @@ export function FormPrompt(props: { form: FormWithLocation }) {
{external().url}
{store.answers[external().key] === true
? "✓ Acknowledged"
@@ -744,7 +746,7 @@ export function FormPrompt(props: { form: FormWithLocation }) {
-
+
{answerField()!.description ?? formLabel(answerField()!)}
{answerField()!.required ? " (required)" : ""}
{multi() ? " (select all that apply)" : ""}
@@ -765,12 +767,12 @@ export function FormPrompt(props: { form: FormWithLocation }) {
input() || formDisplayValue(answerField()!, store.answers[answerField()!.key], "(none)")
}
placeholder={placeholder()}
- placeholderColor={themeV2.text.subdued()}
+ placeholderColor={themeV2.text.subdued}
minHeight={1}
maxHeight={6}
- textColor={themeV2.text()}
- focusedTextColor={themeV2.text()}
- cursorColor={themeV2.text()}
+ textColor={themeV2.text.default}
+ focusedTextColor={themeV2.text.default}
+ cursorColor={themeV2.text.default}
/>
@@ -795,25 +797,39 @@ export function FormPrompt(props: { form: FormWithLocation }) {
>
- {`${i() + 1}.`}
+ {`${i() + 1}.`}
-
+
{multi() ? `[${picked() ? "✓" : " "}] ${row.label}` : row.label}
- {picked() ? " ✓" : ""}
+ {picked() ? " ✓" : ""}
- {row.description}
+ {row.description}
@@ -831,28 +847,30 @@ export function FormPrompt(props: { form: FormWithLocation }) {
>
-
+
{`${rows().length + 1}.`}
-
+
{multi() ? `[${customPicked() ? "✓" : " "}] Type your own answer` : "Type your own answer"}
- {customPicked() ? " ✓" : ""}
+ {customPicked() ? " ✓" : ""}
@@ -868,18 +886,18 @@ export function FormPrompt(props: { form: FormWithLocation }) {
}}
initialValue={input()}
placeholder="Type your own answer"
- placeholderColor={themeV2.text.subdued()}
+ placeholderColor={themeV2.text.subdued}
minHeight={1}
maxHeight={6}
- textColor={themeV2.text()}
- focusedTextColor={themeV2.text()}
- cursorColor={themeV2.text()}
+ textColor={themeV2.text.default}
+ focusedTextColor={themeV2.text.default}
+ cursorColor={themeV2.text.default}
/>
- {input()}
+ {input()}
@@ -892,7 +910,7 @@ export function FormPrompt(props: { form: FormWithLocation }) {
- Review
+ Review
- {truncate(formLabel(item), 40)}:{" "}
+ {truncate(formLabel(item), 40)}:{" "}
{acknowledged() ? "Acknowledged" : "(acknowledgement required)"}
@@ -926,15 +946,15 @@ export function FormPrompt(props: { form: FormWithLocation }) {
return (
- {truncate(formLabel(item), 40)}:{" "}
+ {truncate(formLabel(item), 40)}:{" "}
{invalid() ?? (answered() ? value() : missing() ? "(required)" : "(not answered)")}
@@ -958,41 +978,41 @@ export function FormPrompt(props: { form: FormWithLocation }) {
>
-
- {"⇆"} tab
+
+ {"⇆"} tab
-
- {"↑↓"} select
+
+ {"↑↓"} select
0}>
-
- {"↑↓"} scroll
+
+ {"↑↓"} scroll
{
if (renderer.getSelection()?.getSelectedText()) return
if (confirm()) submit()
if (externalField()) acknowledgeExternal()
}}
>
- enter {actionLabel()}
+ enter {actionLabel()}
-
- c copy
+
+ c copy
-
- esc dismiss
+
+ esc dismiss
- {store.error}
+ {store.error}
diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx
index 2b4ec05269..d2f3543c0e 100644
--- a/packages/tui/src/routes/session/index.tsx
+++ b/packages/tui/src/routes/session/index.tsx
@@ -919,8 +919,8 @@ export function Session() {
paddingLeft: 1,
visible: showScrollbar(),
trackOptions: {
- backgroundColor: themeV2.raise(themeV2.background.surface.offset()),
- foregroundColor: themeV2.border(),
+ backgroundColor: themeV2.raise(themeV2.background.surface.offset),
+ foregroundColor: themeV2.border.default,
},
}}
stickyScroll={!navigationMessage()}
@@ -1098,8 +1098,8 @@ function BackgroundToolHint(props: { messages: SessionMessageInfo[] }) {
{(value) => (
-
- Press {value()} to move running work to the background
+
+ Press {value()} to move running work to the background
)}
@@ -1209,13 +1209,13 @@ function SessionReasoningGroupView(props: {
icon={expanded() ? "-" : "+"}
color={
!props.completed
- ? themeV2.text()
+ ? themeV2.text.default
: hover() || expanded()
- ? themeV2.text.feedback.warning()
+ ? themeV2.text.feedback.warning.default
: RGBA.fromValues(
- themeV2.text.feedback.warning().r,
- themeV2.text.feedback.warning().g,
- themeV2.text.feedback.warning().b,
+ themeV2.text.feedback.warning.default.r,
+ themeV2.text.feedback.warning.default.g,
+ themeV2.text.feedback.warning.default.b,
0.6,
)
}
@@ -1258,7 +1258,7 @@ function SessionReasoningGroupView(props: {
@@ -1326,7 +1326,7 @@ function SessionGroupView(props: {
0}>
- {errorMessage(props.message.error)}
+ {errorMessage(props.message.error)}
-
+
{Locale.titlecase(props.message.agent)}
- · {model()}
+ · {model()}
- · {Locale.duration(duration())}
+ · {Locale.duration(duration())}
- · interrupted
+ · interrupted
@@ -1409,7 +1409,7 @@ function SessionSwitchMessageV2(props: { message: SessionMessageInfo }) {
}
return (
- {text()}
+ {text()}
)
}
@@ -1436,15 +1436,15 @@ function SessionNoticeMessageV2(props: { message: SessionMessageInfo }) {
const heading = () => `${state() === "completed" ? "↳" : "!"} ${actor()} ${status()}`
const suffix = () => Locale.truncateWidth(` · ${description()}`, Math.max(0, ctx.width - 3 - stringWidth(heading())))
const color = () => {
- if (state() === "error") return themeV2.text.feedback.error()
- if (state() === "cancelled") return themeV2.text.feedback.warning()
- return themeV2.text.feedback.info()
+ if (state() === "error") return themeV2.text.feedback.error.default
+ if (state() === "cancelled") return themeV2.text.feedback.warning.default
+ return themeV2.text.feedback.info.default
}
return (
+
{text()}
}
@@ -1452,7 +1452,7 @@ function SessionNoticeMessageV2(props: { message: SessionMessageInfo }) {
{heading()}
- {suffix()}
+ {suffix()}
@@ -1462,7 +1462,7 @@ function SessionNoticeMessageV2(props: { message: SessionMessageInfo }) {
function SessionSkillMessage(props: { message: Extract }) {
const { themeV2 } = useTheme()
return (
-
+
Skill {props.message.name}
)
@@ -1476,7 +1476,8 @@ function CompactionMessage(props: { message: Extract
props.message.status === "failed" ? (cancelled() ? "" : props.message.error.message) : props.message.summary
const content = createMemo(() => text().trim())
- const color = () => (status() === "failed" && !cancelled() ? themeV2.text.feedback.error() : themeV2.text.subdued())
+ const color = () =>
+ status() === "failed" && !cancelled() ? themeV2.text.feedback.error.default : themeV2.text.subdued
return (
@@ -1508,8 +1509,8 @@ function CompactionMessage(props: { message: Extract
@@ -1521,12 +1522,12 @@ function CompactionQueued() {
const { themeV2 } = useTheme()
return (
-
+
- ◇
- Compaction queued
+ ◇
+ Compaction queued
-
+
)
}
@@ -1572,15 +1573,15 @@ function RevertMessage(props: {
marginTop={1}
border={["left"]}
customBorderChars={SplitBorder.customBorderChars}
- borderColor={themeV2.background()}
+ borderColor={themeV2.background.default}
>
-
+
{props.count} message{props.count === 1 ? "" : "s"} reverted
0}>
@@ -1588,7 +1589,7 @@ function RevertMessage(props: {
{(file) => (
- {statusLabel(file.status)}
+ {statusLabel(file.status)}
0 ? stringWidth(`+${file.additions}`) + 1 : 0) -
(file.deletions > 0 ? stringWidth(`-${file.deletions}`) + 1 : 0),
)}
- fg={themeV2.text()}
+ fg={themeV2.text.default}
/>
0}>
- +{file.additions}
+ +{file.additions}
0}>
- -{file.deletions}
+ -{file.deletions}
)}
-
- {redoKey()} or /redo to restore
+
+ {redoKey()} or /redo to restore
@@ -1630,13 +1631,13 @@ function ShellMessage(props: { message: Extract
- $ {props.message.command}
+ $ {props.message.command}
- {output()}
+ {output()}
)
@@ -1661,7 +1662,7 @@ function UserMessage(props: { message: SessionMessageUser }) {
- {props.message.text}
+ {props.message.text}
{(file) => {
const label = file.mime === "application/x-directory" ? "dir" : "file"
return (
-
+
{` ${label} `}
-
+
{" "}
{file.name ?? (file.source.type === "uri" ? file.source.uri : "attachment")}{" "}
@@ -1809,11 +1810,11 @@ function AssistantMessage(props: { message: SessionMessageAssistant; last: boole
paddingTop={1}
paddingBottom={1}
paddingLeft={2}
- backgroundColor={themeV2.background()}
+ backgroundColor={themeV2.background.default}
customBorderChars={SplitBorder.customBorderChars}
- borderColor={themeV2.text.feedback.error()}
+ borderColor={themeV2.text.feedback.error.default}
>
- {errorMessage(props.message.error)}
+ {errorMessage(props.message.error)}
@@ -1821,14 +1822,12 @@ function AssistantMessage(props: { message: SessionMessageAssistant; last: boole
-
+
{Locale.titlecase(props.message.agent)}
- · {model()}
+ · {model()}
- · {Locale.duration(duration())}
+ · {Locale.duration(duration())}
@@ -1844,7 +1843,7 @@ function AssistantRetry(props: { retry: SessionMessageAssistant["retry"] }) {
{(retry) => (
-
+
Retry attempt {retry().attempt} scheduled: {retry().error.message} [{retry().error.type}]
@@ -1867,7 +1866,7 @@ function ExplorationSummary(props: { parts: SessionMessageAssistantTool[]; activ
{(part, index) => (
-
+
{index() === props.parts.length - 1 ? "└" : "├"} {label(part)}
@@ -1922,7 +1921,7 @@ function ReasoningPart(props: {
@@ -1940,7 +1939,7 @@ function ReasoningPart(props: {
@@ -1976,12 +1975,12 @@ function ReasoningHeader(props: {
const fg = () =>
props.open
? RGBA.fromValues(
- themeV2.text.feedback.warning().r,
- themeV2.text.feedback.warning().g,
- themeV2.text.feedback.warning().b,
+ themeV2.text.feedback.warning.default.r,
+ themeV2.text.feedback.warning.default.g,
+ themeV2.text.feedback.warning.default.b,
0.6,
)
- : themeV2.text.feedback.warning()
+ : themeV2.text.feedback.warning.default
return (
@@ -2027,8 +2026,8 @@ function TextPart(props: { last: boolean; part: SessionMessageAssistantText }) {
content={props.part.text.trim()}
tableOptions={{ style: "grid" }}
conceal={ctx.markdownMode() === "rendered"}
- fg={themeV2.markdown()}
- bg={themeV2.background()}
+ fg={themeV2.markdown.text}
+ bg={themeV2.background.default}
/>
@@ -2135,7 +2134,7 @@ function GenericTool(props: ToolProps) {
0}>
- Input
+ Input
@@ -2153,10 +2152,13 @@ function GenericTool(props: ToolProps) {
{(value) => (
- Output
+
+ {" "}
+ Output{" "}
+
-
+
{value()}
@@ -2208,10 +2210,10 @@ function InlineTool(props: {
const clickable = createMemo(() => Boolean(props.onClick || failed()))
const fg = createMemo(() => {
if (props.color) return props.color
- if (permission()) return themeV2.text.feedback.warning()
- if (failed()) return themeV2.text.feedback.error()
- if (hover() && props.onClick) return themeV2.text()
- return themeV2.text.subdued()
+ if (permission()) return themeV2.text.feedback.warning.default
+ if (failed()) return themeV2.text.feedback.error.default
+ if (hover() && props.onClick) return themeV2.text.default
+ return themeV2.text.subdued
})
return (
@@ -2219,7 +2221,7 @@ function InlineTool(props: {
icon={props.icon}
iconColor={props.iconColor}
color={fg()}
- errorColor={themeV2.text.feedback.error()}
+ errorColor={themeV2.text.feedback.error.default}
failed={failed()}
denied={Boolean(denied())}
error={error()}
@@ -2343,7 +2345,7 @@ function InlineToolLabel(props: { color?: RGBA; denied?: boolean; status: JSX.El
function StatusBadge(props: { children: string }) {
const { themeV2 } = useTheme()
return (
-
+
{" "}
{props.children}{" "}
@@ -2376,9 +2378,9 @@ function BlockTool(props: {
paddingBottom={1}
paddingLeft={2}
gap={1}
- backgroundColor={hover() ? themeV2.raise(themeV2.background()) : themeV2.background()}
+ backgroundColor={hover() ? themeV2.raise(themeV2.background.default) : themeV2.background.default}
customBorderChars={SplitBorder.customBorderChars}
- borderColor={themeV2.background()}
+ borderColor={themeV2.background.default}
onMouseOver={() => props.onClick && setHover(true)}
onMouseOut={() => setHover(false)}
onMouseUp={() => {
@@ -2394,10 +2396,12 @@ function BlockTool(props: {
{title()}
+
+ {title()}
+
}
>
-
+
{title().replace(/^# /, "")}
@@ -2410,26 +2414,26 @@ function BlockTool(props: {
+
{path().label}
}
>
-
+
{path().label.replace(/^# /, "")}
)}
{props.children}
- {error()}
+ {error()}
)
@@ -2444,7 +2448,7 @@ function Shell(props: ToolProps) {
const request = data.session.permission.list(ctx.sessionID)?.[0]
return request?.source?.type === "tool" && request.source.callID === props.part.id
})
- const color = createMemo(() => (permission() ? themeV2.text.feedback.warning() : themeV2.text()))
+ const color = createMemo(() => (permission() ? themeV2.text.feedback.warning.default : themeV2.text.default))
const shellID = createMemo(() => stringValue(props.metadata.shellID))
const backgroundRunning = createMemo(() => {
const id = shellID()
@@ -2506,7 +2510,7 @@ function Shell(props: ToolProps) {
isRunning() || props.part.state.status === "streaming" ? (
Writing command...
) : (
- Writing command...
+ Writing command...
)
}
>
@@ -2514,14 +2518,14 @@ function Shell(props: ToolProps) {
when={isRunning()}
fallback={
- {limited().slice(0, input().length)}
- {limited().slice(input().length)}
+ {limited().slice(0, input().length)}
+ {limited().slice(input().length)}
}
>
- {limited().slice(0, input().length)}
- {limited().slice(input().length)}
+ {limited().slice(0, input().length)}
+ {limited().slice(input().length)}
@@ -2547,10 +2551,10 @@ function Write(props: ToolProps) {
path={{ label: "# Wrote", value: pathFormatter.format(stringValue(props.input.path)) }}
part={props.part}
>
-
+
{(filepath) => (
-
+
↳ Loaded {pathFormatter.format(filepath)}
@@ -2724,7 +2728,7 @@ function Execute(props: ToolProps) {
<>
{(line, index) => (
-
+
{index() === 0 ? "↳ " : " "}
{line}
@@ -2778,16 +2782,16 @@ function Edit(props: ToolProps) {
showLineNumbers={true}
width="100%"
wrapMode={ctx.diffWrapMode()}
- fg={themeV2.text()}
- addedBg={themeV2.diff.background.added()}
- removedBg={themeV2.diff.background.removed()}
- contextBg={themeV2.diff.background.context()}
- addedSignColor={themeV2.diff.highlight.added()}
- removedSignColor={themeV2.diff.highlight.removed()}
- lineNumberFg={themeV2.diff.lineNumber.text()}
- lineNumberBg={themeV2.diff.background.context()}
- addedLineNumberBg={themeV2.diff.lineNumber.background.added()}
- removedLineNumberBg={themeV2.diff.lineNumber.background.removed()}
+ fg={themeV2.text.default}
+ addedBg={themeV2.diff.background.added}
+ removedBg={themeV2.diff.background.removed}
+ contextBg={themeV2.diff.background.context}
+ addedSignColor={themeV2.diff.highlight.added}
+ removedSignColor={themeV2.diff.highlight.removed}
+ lineNumberFg={themeV2.diff.lineNumber.text}
+ lineNumberBg={themeV2.diff.background.context}
+ addedLineNumberBg={themeV2.diff.lineNumber.background.added}
+ removedLineNumberBg={themeV2.diff.lineNumber.background.removed}
/>
@@ -2852,7 +2856,7 @@ function ApplyPatch(props: ToolProps) {
+
-{file.deletions} line{file.deletions !== 1 ? "s" : ""}
}
@@ -2866,16 +2870,16 @@ function ApplyPatch(props: ToolProps) {
showLineNumbers={true}
width="100%"
wrapMode={ctx.diffWrapMode()}
- fg={themeV2.text()}
- addedBg={themeV2.diff.background.added()}
- removedBg={themeV2.diff.background.removed()}
- contextBg={themeV2.diff.background.context()}
- addedSignColor={themeV2.diff.highlight.added()}
- removedSignColor={themeV2.diff.highlight.removed()}
- lineNumberFg={themeV2.diff.lineNumber.text()}
- lineNumberBg={themeV2.diff.background.context()}
- addedLineNumberBg={themeV2.diff.lineNumber.background.added()}
- removedLineNumberBg={themeV2.diff.lineNumber.background.removed()}
+ fg={themeV2.text.default}
+ addedBg={themeV2.diff.background.added}
+ removedBg={themeV2.diff.background.removed}
+ contextBg={themeV2.diff.background.context}
+ addedSignColor={themeV2.diff.highlight.added}
+ removedSignColor={themeV2.diff.highlight.removed}
+ lineNumberFg={themeV2.diff.lineNumber.text}
+ lineNumberBg={themeV2.diff.background.context}
+ addedLineNumberBg={themeV2.diff.lineNumber.background.added}
+ removedLineNumberBg={themeV2.diff.lineNumber.background.removed}
/>
@@ -2898,7 +2902,7 @@ function ApplyPatch(props: ToolProps) {
)}
@@ -2945,8 +2949,8 @@ function Question(props: ToolProps) {
{(q, i) => (
- {q.question}
- {format(answers()?.[i()])}
+ {q.question}
+ {format(answers()?.[i()])}
)}
@@ -2987,7 +2991,7 @@ function Diagnostics(props: { diagnostics: unknown; filePath: string }) {
{(diagnostic) => (
-
+
Error [{diagnostic.range.start.line + 1}:{diagnostic.range.start.character + 1}] {diagnostic.message}
)}
diff --git a/packages/tui/src/routes/session/permission.tsx b/packages/tui/src/routes/session/permission.tsx
index b7fe4c8b0d..403e682f6f 100644
--- a/packages/tui/src/routes/session/permission.tsx
+++ b/packages/tui/src/routes/session/permission.tsx
@@ -45,8 +45,8 @@ function EditBody(props: { file?: string; diff?: string; patch?: string }) {
scrollAcceleration={scrollAcceleration()}
verticalScrollbarOptions={{
trackOptions: {
- backgroundColor: themeV2.background(),
- foregroundColor: themeV2.scrollbar(),
+ backgroundColor: themeV2.background.default,
+ foregroundColor: themeV2.scrollbar.default,
},
}}
>
@@ -58,16 +58,16 @@ function EditBody(props: { file?: string; diff?: string; patch?: string }) {
showLineNumbers={true}
width="100%"
wrapMode="word"
- fg={themeV2.text()}
- addedBg={themeV2.diff.background.added()}
- removedBg={themeV2.diff.background.removed()}
- contextBg={themeV2.diff.background.context()}
- addedSignColor={themeV2.diff.highlight.added()}
- removedSignColor={themeV2.diff.highlight.removed()}
- lineNumberFg={themeV2.diff.lineNumber.text()}
- lineNumberBg={themeV2.diff.background.context()}
- addedLineNumberBg={themeV2.diff.lineNumber.background.added()}
- removedLineNumberBg={themeV2.diff.lineNumber.background.removed()}
+ fg={themeV2.text.default}
+ addedBg={themeV2.diff.background.added}
+ removedBg={themeV2.diff.background.removed}
+ contextBg={themeV2.diff.background.context}
+ addedSignColor={themeV2.diff.highlight.added}
+ removedSignColor={themeV2.diff.highlight.removed}
+ lineNumberFg={themeV2.diff.lineNumber.text}
+ lineNumberBg={themeV2.diff.background.context}
+ addedLineNumberBg={themeV2.diff.lineNumber.background.added}
+ removedLineNumberBg={themeV2.diff.lineNumber.background.removed}
/>
@@ -76,7 +76,7 @@ function EditBody(props: { file?: string; diff?: string; patch?: string }) {
when={props.patch}
fallback={
- No diff provided
+ No diff provided
}
>
@@ -86,8 +86,8 @@ function EditBody(props: { file?: string; diff?: string; patch?: string }) {
scrollAcceleration={scrollAcceleration()}
verticalScrollbarOptions={{
trackOptions: {
- backgroundColor: themeV2.background(),
- foregroundColor: themeV2.scrollbar(),
+ backgroundColor: themeV2.background.default,
+ foregroundColor: themeV2.scrollbar.default,
},
}}
>
@@ -97,7 +97,7 @@ function EditBody(props: { file?: string; diff?: string; patch?: string }) {
streaming={true}
syntaxStyle={syntax()}
content={patch()}
- fg={themeV2.text.subdued()}
+ fg={themeV2.text.subdued}
/>
)}
@@ -140,7 +140,7 @@ export function PermissionPrompt(props: { request: PermissionV2Request; director
body={
- {(line, index) => {line}}
+ {(line, index) => {line}}
}
@@ -192,9 +192,9 @@ export function PermissionPrompt(props: { request: PermissionV2Request; director
) : props.request.action === "external_directory" ? (
0}>
- Patterns
+ Patterns
- {(line) => {line}}
+ {(line) => {line}}
@@ -207,8 +207,8 @@ export function PermissionPrompt(props: { request: PermissionV2Request; director
props.request.action === "shell" ||
props.request.action === "subagent" ||
props.request.action === "task"
- ? themeV2.text()
- : themeV2.text.subdued()
+ ? themeV2.text.default
+ : themeV2.text.subdued
}
>
{line}
@@ -221,15 +221,15 @@ export function PermissionPrompt(props: { request: PermissionV2Request; director
const header = () => (
- {"△"}
- Permission required
+ {"△"}
+ Permission required
-
+
{current.icon}
- {current.title}
+ {current.title}
@@ -329,18 +329,18 @@ function RejectPrompt(props: {
role: "dialog",
label: `Reject permission: ${props.action}`,
}))}
- backgroundColor={themeV2.background()}
+ backgroundColor={themeV2.background.default}
border={["left"]}
- borderColor={themeV2.text.feedback.error()}
+ borderColor={themeV2.text.feedback.error.default}
customBorderChars={SplitBorder.customBorderChars}
>
- {"△"}
- Reject permission
+ {"△"}
+ Reject permission
- Tell OpenCode what to do differently
+ Tell OpenCode what to do differently
props.onConfirm(input.plainText)}
>
-
- enter confirm
+
+ enter confirm
-
- esc cancel
+
+ esc cancel
@@ -534,9 +534,9 @@ function Prompt>(props: {
label: props.semanticLabel ?? props.title,
expanded: store.expanded,
}))}
- backgroundColor={themeV2.background()}
+ backgroundColor={themeV2.background.default}
border={["left"]}
- borderColor={themeV2.background.action("focused")}
+ borderColor={themeV2.background.action.primary.focused}
customBorderChars={SplitBorder.customBorderChars}
{...(store.expanded
? { top: dimensions().height * -1 + 1, bottom: 1, left: 2, right: 2, position: "absolute" }
@@ -554,8 +554,8 @@ function Prompt>(props: {
when={props.header}
fallback={
- {"△"}
- {props.title}
+ {"△"}
+ {props.title}
}
>
@@ -573,7 +573,7 @@ function Prompt>(props: {
paddingLeft={2}
paddingRight={3}
paddingBottom={1}
- backgroundColor={themeV2.raise(themeV2.background())}
+ backgroundColor={themeV2.raise(themeV2.background.default)}
justifyContent={narrow() ? "flex-start" : "space-between"}
alignItems={narrow() ? "flex-start" : "center"}
>
@@ -602,14 +602,24 @@ function Prompt>(props: {
}))}
paddingLeft={1}
paddingRight={1}
- backgroundColor={themeV2.background.action(option === store.selected ? "focused" : "default")}
+ backgroundColor={
+ option === store.selected
+ ? themeV2.background.action.primary.focused
+ : themeV2.background.action.primary.default
+ }
onMouseOver={() => setStore("selected", option)}
onMouseUp={() => {
setStore("selected", option)
props.onSelect(option)
}}
>
-
+
{props.options[option]}
@@ -618,16 +628,15 @@ function Prompt>(props: {
-
- {shortcuts.get("permission.prompt.fullscreen")}{" "}
- {hint()}
+
+ {shortcuts.get("permission.prompt.fullscreen")} {hint()}
-
- {"⇆"} select
+
+ {"⇆"} select
-
- enter confirm
+
+ enter confirm
diff --git a/packages/tui/src/routes/session/sidebar.tsx b/packages/tui/src/routes/session/sidebar.tsx
index 7d9192c4a8..80a9b209fa 100644
--- a/packages/tui/src/routes/session/sidebar.tsx
+++ b/packages/tui/src/routes/session/sidebar.tsx
@@ -18,7 +18,7 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
return (
@@ -45,11 +45,11 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
title={session()!.title}
>
-
+
{session()!.title}
- {session()!.location.workspaceID}
+ {session()!.location.workspaceID}
diff --git a/packages/tui/src/routes/session/subagent-footer.tsx b/packages/tui/src/routes/session/subagent-footer.tsx
index 6885013c11..dbaed08e03 100644
--- a/packages/tui/src/routes/session/subagent-footer.tsx
+++ b/packages/tui/src/routes/session/subagent-footer.tsx
@@ -61,18 +61,18 @@ export function SubagentFooter() {
paddingRight={1}
{...SplitBorder}
border={["left"]}
- borderColor={themeV2.border()}
+ borderColor={themeV2.border.default}
flexShrink={0}
- backgroundColor={themeV2.background()}
+ backgroundColor={themeV2.background.default}
>
-
+
{subagentInfo()}
{(item) => (
-
+
{[item().context, item().cost].filter(Boolean).join(" · ")}
)}
@@ -83,30 +83,36 @@ export function SubagentFooter() {
onMouseOver={() => setHover("parent")}
onMouseOut={() => setHover(null)}
onMouseUp={() => keymap.dispatch("session.parent")}
- backgroundColor={hover() === "parent" ? themeV2.background.action("hovered") : themeV2.background()}
+ backgroundColor={
+ hover() === "parent" ? themeV2.background.action.primary.hovered : themeV2.background.default
+ }
>
-
- Parent {shortcuts.get("session.parent")}
+
+ Parent {shortcuts.get("session.parent")}
setHover("prev")}
onMouseOut={() => setHover(null)}
onMouseUp={() => keymap.dispatch("session.child.previous")}
- backgroundColor={hover() === "prev" ? themeV2.background.action("hovered") : themeV2.background()}
+ backgroundColor={
+ hover() === "prev" ? themeV2.background.action.primary.hovered : themeV2.background.default
+ }
>
-
- Prev {shortcuts.get("session.child.previous")}
+
+ Prev {shortcuts.get("session.child.previous")}
setHover("next")}
onMouseOut={() => setHover(null)}
onMouseUp={() => keymap.dispatch("session.child.next")}
- backgroundColor={hover() === "next" ? themeV2.background.action("hovered") : themeV2.background()}
+ backgroundColor={
+ hover() === "next" ? themeV2.background.action.primary.hovered : themeV2.background.default
+ }
>
-
- Next {shortcuts.get("session.child.next")}
+
+ Next {shortcuts.get("session.child.next")}
diff --git a/packages/tui/src/theme/v2/component.ts b/packages/tui/src/theme/v2/component.ts
index accfd7b438..797d4b8b73 100644
--- a/packages/tui/src/theme/v2/component.ts
+++ b/packages/tui/src/theme/v2/component.ts
@@ -1,140 +1,40 @@
import type { RGBA } from "@opentui/core"
import type { Accessor } from "solid-js"
-import type { ActionVariant, Mode, ResolvedActionState, ResolvedThemeView } from "./index"
-import { ActionState, HueStep } from "./schema"
-
-type StateFlags = Partial>
+import type { Mode, ResolvedThemeView } from "./index"
export function createComponentTheme(current: Accessor, mode: Accessor) {
- const textAction = actions((variant, state) => current().text.action[variant][state])
- const backgroundAction = actions((variant, state) => current().background.action[variant][state])
- const textFormfield = formfield((state) => current().text.formfield[state])
- const backgroundFormfield = formfield((state) => current().background.formfield[state])
- const hue = {
- gray: (step: HueStep) => current().hue.gray[step],
- red: (step: HueStep) => current().hue.red[step],
- orange: (step: HueStep) => current().hue.orange[step],
- yellow: (step: HueStep) => current().hue.yellow[step],
- green: (step: HueStep) => current().hue.green[step],
- cyan: (step: HueStep) => current().hue.cyan[step],
- blue: (step: HueStep) => current().hue.blue[step],
- purple: (step: HueStep) => current().hue.purple[step],
- accent: (step: HueStep) => current().hue.accent[step],
- interactive: (step: HueStep) => current().hue.interactive[step],
- neutral: (step: HueStep) => current().hue.neutral[step],
- }
- const text = Object.assign(() => current().text.default, {
- subdued: () => current().text.subdued,
- action: textAction,
- formfield: textFormfield,
- feedback: {
- error: feedbackText("error"),
- warning: feedbackText("warning"),
- success: feedbackText("success"),
- info: feedbackText("info"),
- },
- })
- const background = Object.assign(() => current().background.default, {
- surface: {
- offset: () => current().background.surface.offset,
- overlay: () => current().background.surface.overlay,
- },
- action: backgroundAction,
- formfield: backgroundFormfield,
- feedback: {
- error: () => current().background.feedback.error.default,
- warning: () => current().background.feedback.warning.default,
- success: () => current().background.feedback.success.default,
- info: () => current().background.feedback.info.default,
- },
- })
- const markdown = Object.assign(() => current().markdown.text, {
- heading: () => current().markdown.heading,
- link: () => current().markdown.link,
- linkText: () => current().markdown.linkText,
- code: () => current().markdown.code,
- blockQuote: () => current().markdown.blockQuote,
- emphasis: () => current().markdown.emphasis,
- strong: () => current().markdown.strong,
- horizontalRule: () => current().markdown.horizontalRule,
- listItem: () => current().markdown.listItem,
- listEnumeration: () => current().markdown.listEnumeration,
- image: () => current().markdown.image,
- imageText: () => current().markdown.imageText,
- codeBlock: () => current().markdown.codeBlock,
- })
-
- function feedbackText(kind: "error" | "warning" | "success" | "info") {
- return Object.assign(() => current().text.feedback[kind].default, {
- subdued: () => current().text.feedback[kind].subdued,
- })
- }
-
return {
- hue,
- categorical: (step: HueStep) => current().categorical.map((scale) => scale[step]),
+ get hue() {
+ return current().hue
+ },
+ get categorical() {
+ return current().categorical
+ },
+ get text() {
+ return current().text
+ },
+ get background() {
+ return current().background
+ },
+ get border() {
+ return current().border
+ },
+ get scrollbar() {
+ return current().scrollbar
+ },
+ get diff() {
+ return current().diff
+ },
+ get syntax() {
+ return current().syntax
+ },
+ get markdown() {
+ return current().markdown
+ },
source: (color: RGBA) => current().source(color),
increase: (color: RGBA, amount = 1) => current().increase(color, amount),
decrease: (color: RGBA, amount = 1) => current().decrease(color, amount),
raise: (color: RGBA) => (mode() === "light" ? current().increase(color) : current().decrease(color)),
- text,
- background,
- border: () => current().border.default,
- scrollbar: () => current().scrollbar.default,
- diff: {
- text: {
- added: () => current().diff.text.added,
- removed: () => current().diff.text.removed,
- context: () => current().diff.text.context,
- hunkHeader: () => current().diff.text.hunkHeader,
- },
- background: {
- added: () => current().diff.background.added,
- removed: () => current().diff.background.removed,
- context: () => current().diff.background.context,
- },
- highlight: {
- added: () => current().diff.highlight.added,
- removed: () => current().diff.highlight.removed,
- },
- lineNumber: {
- text: () => current().diff.lineNumber.text,
- background: {
- added: () => current().diff.lineNumber.background.added,
- removed: () => current().diff.lineNumber.background.removed,
- },
- },
- },
- syntax: {
- comment: () => current().syntax.comment,
- keyword: () => current().syntax.keyword,
- function: () => current().syntax.function,
- variable: () => current().syntax.variable,
- string: () => current().syntax.string,
- number: () => current().syntax.number,
- type: () => current().syntax.type,
- operator: () => current().syntax.operator,
- punctuation: () => current().syntax.punctuation,
- },
- markdown,
- }
-}
-
-function actions(get: (variant: ActionVariant, state: ResolvedActionState) => RGBA) {
- const primary = stateful((state) => get("primary", state))
- return Object.assign(primary, {
- destructive: stateful((state) => get("destructive", state)),
- })
-}
-
-function formfield(get: (state: ResolvedActionState) => RGBA) {
- return stateful(get)
-}
-
-function stateful(get: (state: ResolvedActionState) => RGBA) {
- return (states: ActionState | "default" | StateFlags = "default") => {
- if (typeof states === "string") return get(states)
- return get(ActionState.literals.find((state) => states[state]) ?? "default")
}
}
diff --git a/packages/tui/src/ui/dialog-alert.tsx b/packages/tui/src/ui/dialog-alert.tsx
index 6f79609564..87d3d645ac 100644
--- a/packages/tui/src/ui/dialog-alert.tsx
+++ b/packages/tui/src/ui/dialog-alert.tsx
@@ -30,27 +30,27 @@ export function DialogAlert(props: DialogAlertProps) {
return (
-
+
{props.title}
- dialog.clear()}>
+ dialog.clear()}>
esc
- {props.message}
+ {props.message}
{
props.onConfirm?.()
dialog.clear()
}}
>
- ok
+ ok
diff --git a/packages/tui/src/ui/dialog-confirm.tsx b/packages/tui/src/ui/dialog-confirm.tsx
index 43778d22d8..6abbc98dd6 100644
--- a/packages/tui/src/ui/dialog-confirm.tsx
+++ b/packages/tui/src/ui/dialog-confirm.tsx
@@ -57,15 +57,15 @@ export function DialogConfirm(props: DialogConfirmProps) {
return (
-
+
{props.title}
- dialog.clear()}>
+ dialog.clear()}>
esc
- {props.message}
+ {props.message}
@@ -73,14 +73,14 @@ export function DialogConfirm(props: DialogConfirmProps) {
{
if (key === "confirm") props.onConfirm?.()
if (key === "cancel") props.onCancel?.()
dialog.clear()
}}
>
-
+
{Locale.titlecase(key === "cancel" ? (props.label ?? key) : key)}
diff --git a/packages/tui/src/ui/dialog-export-options.tsx b/packages/tui/src/ui/dialog-export-options.tsx
index 4ccd87e9ce..104aa04be2 100644
--- a/packages/tui/src/ui/dialog-export-options.tsx
+++ b/packages/tui/src/ui/dialog-export-options.tsx
@@ -76,32 +76,38 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
return (
-
+
Export session
- dialog.clear()}>
+ dialog.clear()}>
esc
- Export as:
+ Export as:
{(format) => (
selectFormat(format)}
>
{store.format === format ? "◉" : "○"} {format === "markdown" ? "Markdown" : "JSON"}
@@ -114,19 +120,38 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
{
setStore("active", "thinking")
setStore("thinking", !store.thinking)
}}
>
-
+
{store.thinking ? "[x]" : "[ ]"}
-
+
Include thinking
@@ -135,19 +160,38 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
{
setStore("active", "debug")
setStore("debug", !store.debug)
}}
>
-
+
{store.debug ? "[x]" : "[ ]"}
-
+
Events (debug)
@@ -156,18 +200,26 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
confirm("copy")}
>
- Copy
+ Copy
confirm("export")}
>
- Export
+
+ Export
+
diff --git a/packages/tui/src/ui/dialog-export-result.tsx b/packages/tui/src/ui/dialog-export-result.tsx
index d6e8eae434..6013b6580f 100644
--- a/packages/tui/src/ui/dialog-export-result.tsx
+++ b/packages/tui/src/ui/dialog-export-result.tsx
@@ -27,19 +27,24 @@ export function DialogExportResult(props: { path: string; onClose?: () => void }
return (
-
+
Session exported
-
+
esc
- {props.path}
+ {props.path}
-
- Close
+
+ Close
diff --git a/packages/tui/src/ui/dialog-help.tsx b/packages/tui/src/ui/dialog-help.tsx
index 6ab1f9cad9..20b0c151e0 100644
--- a/packages/tui/src/ui/dialog-help.tsx
+++ b/packages/tui/src/ui/dialog-help.tsx
@@ -19,15 +19,15 @@ export function DialogHelp() {
return (
-
+
Help
- dialog.clear()}>
+ dialog.clear()}>
esc/enter
-
+
Press {shortcuts.get("command.palette.show")} to see all available actions and commands in any context.
@@ -35,10 +35,10 @@ export function DialogHelp() {
dialog.clear()}
>
- ok
+ ok
diff --git a/packages/tui/src/ui/dialog-prompt.tsx b/packages/tui/src/ui/dialog-prompt.tsx
index b997fc8a93..54d5e8b995 100644
--- a/packages/tui/src/ui/dialog-prompt.tsx
+++ b/packages/tui/src/ui/dialog-prompt.tsx
@@ -74,10 +74,10 @@ export function DialogPrompt(props: DialogPromptProps) {
return (
-
+
{props.title}
- dialog.clear()}>
+ dialog.clear()}>
esc
@@ -91,20 +91,20 @@ export function DialogPrompt(props: DialogPromptProps) {
}}
initialValue={props.value}
placeholder={props.placeholder ?? "Enter text"}
- placeholderColor={themeV2.text.subdued()}
- textColor={themeV2.text.formfield({ disabled: props.busy })}
- focusedTextColor={themeV2.text.formfield({ disabled: props.busy })}
- cursorColor={props.busy ? themeV2.background.formfield("disabled") : themeV2.text()}
+ placeholderColor={themeV2.text.subdued}
+ textColor={props.busy ? themeV2.text.formfield.disabled : themeV2.text.formfield.default}
+ focusedTextColor={props.busy ? themeV2.text.formfield.disabled : themeV2.text.formfield.default}
+ cursorColor={props.busy ? themeV2.background.formfield.disabled : themeV2.text.default}
/>
- {props.busyText ?? "Working..."}
+ {props.busyText ?? "Working..."}
- processing...}>
+ processing...}>
-
- {shortcuts.get("dialog.prompt.submit")} submit
+
+ {shortcuts.get("dialog.prompt.submit")} submit
diff --git a/packages/tui/src/ui/dialog.tsx b/packages/tui/src/ui/dialog.tsx
index 99062e967e..773a3f2579 100644
--- a/packages/tui/src/ui/dialog.tsx
+++ b/packages/tui/src/ui/dialog.tsx
@@ -59,7 +59,7 @@ export function Dialog(
}}
width={width()}
maxWidth={dimensions().width - 2}
- backgroundColor={themeV2.background()}
+ backgroundColor={themeV2.background.default}
paddingTop={1}
>
{props.children}
diff --git a/packages/tui/src/ui/toast.tsx b/packages/tui/src/ui/toast.tsx
index c5edb8689d..48e499cfc2 100644
--- a/packages/tui/src/ui/toast.tsx
+++ b/packages/tui/src/ui/toast.tsx
@@ -31,17 +31,17 @@ export function Toast() {
paddingRight={2}
paddingTop={1}
paddingBottom={1}
- backgroundColor={themeV2.background()}
- borderColor={themeV2.text.feedback[current().variant]()}
+ backgroundColor={themeV2.background.default}
+ borderColor={themeV2.text.feedback[current().variant].default}
border={["left", "right"]}
customBorderChars={SplitBorder.customBorderChars}
>
-
+
{current().title}
-
+
{current().message}
diff --git a/packages/tui/test/theme/v2/component.test.ts b/packages/tui/test/theme/v2/component.test.ts
index ca003bf42b..469624f8ba 100644
--- a/packages/tui/test/theme/v2/component.test.ts
+++ b/packages/tui/test/theme/v2/component.test.ts
@@ -7,7 +7,7 @@ import { resolveTheme } from "../../../src/theme/v2/resolve"
import { selectTheme } from "../../../src/theme/v2/select"
import type { ContextKey } from "../../../src/theme/v2"
-test("provides reactive property, variant, state, and context accessors", () => {
+test("provides reactive properties, states, contexts, and color operations", () => {
const [resolved, setResolved] = createSignal(resolveTheme(selectTheme(DEFAULT_THEME, "light")))
const [mode, setMode] = createSignal<"light" | "dark">("light")
const [context, setContext] = createSignal()
@@ -16,69 +16,57 @@ test("provides reactive property, variant, state, and context accessors", () =>
return key ? (resolved().contexts[key] ?? resolved()) : resolved()
}, mode)
- expect(theme.text()).toBe(resolved().text.default)
- expect(theme.hue.accent(500)).toBe(resolved().hue.accent[500])
- expect(theme.hue.interactive(500)).toBe(resolved().hue.interactive[500])
- expect(theme.hue.gray(200)).toBe(resolved().hue.gray[200])
- expect(theme.categorical(500)).toEqual(resolved().categorical.map((scale) => scale[500]))
- expect(theme.increase(theme.background.surface.offset(), 1)).toBe(resolved().hue.neutral[400])
- expect(theme.raise(theme.background.surface.offset())).toBe(resolved().hue.neutral[400])
- expect(theme.decrease(theme.hue.red(300), 2)).toBe(resolved().hue.red[100])
- expect(theme.increase(theme.hue.red(900), 3)).toBe(resolved().hue.red[900])
- expect(theme.decrease(theme.hue.red(100), 3)).toBe(resolved().hue.red[100])
- expect(theme.source(theme.background.surface.offset())).toEqual({ hue: "neutral", step: 300 })
+ expect(theme.text.default).toBe(resolved().text.default)
+ expect(theme.hue.accent[500]).toBe(resolved().hue.accent[500])
+ expect(theme.hue.interactive[500]).toBe(resolved().hue.interactive[500])
+ expect(theme.hue.gray[200]).toBe(resolved().hue.gray[200])
+ expect(theme.categorical.map((scale) => scale[500])).toEqual(resolved().categorical.map((scale) => scale[500]))
+ expect(theme.increase(theme.background.surface.offset, 1)).toBe(resolved().hue.neutral[400])
+ expect(theme.raise(theme.background.surface.offset)).toBe(resolved().hue.neutral[400])
+ expect(theme.decrease(theme.hue.red[300], 2)).toBe(resolved().hue.red[100])
+ expect(theme.increase(theme.hue.red[900], 3)).toBe(resolved().hue.red[900])
+ expect(theme.decrease(theme.hue.red[100], 3)).toBe(resolved().hue.red[100])
+ expect(theme.source(theme.background.surface.offset)).toEqual({ hue: "neutral", step: 300 })
const equivalent = RGBA.fromInts(...resolved().hue.green[500].toInts())
expect(theme.source(equivalent)).toBeUndefined()
expect(theme.increase(equivalent, 1)).toBe(equivalent)
const unmatched = RGBA.fromInts(1, 2, 3)
expect(theme.increase(unmatched, 1)).toBe(unmatched)
- expect(theme.text.subdued()).toBe(resolved().text.subdued)
- expect(theme.text.action()).toBe(resolved().text.action.primary.default)
- expect(theme.text.action("hovered")).toBe(resolved().text.action.primary.hovered)
- expect(theme.text.action("pressed")).toBe(resolved().text.action.primary.pressed)
- expect(theme.text.action("selected")).toBe(resolved().text.action.primary.selected)
- expect(theme.background.action("selected")).toBe(resolved().background.action.primary.selected)
- expect(theme.background.action("hovered")).toBe(resolved().background.action.primary.hovered)
- expect(theme.background.action({ selected: true })).toBe(resolved().background.action.primary.selected)
- expect(theme.background.action({ selected: true, hovered: true })).toBe(resolved().background.action.primary.selected)
- expect(theme.background.action({ focused: true, selected: true })).toBe(resolved().background.action.primary.focused)
- expect(theme.background.action({ pressed: true, focused: true, selected: true })).toBe(
- resolved().background.action.primary.pressed,
- )
- expect(theme.background.action({ disabled: true, pressed: true, focused: true, selected: true, hovered: true })).toBe(
- resolved().background.action.primary.disabled,
- )
- expect(theme.background.action({ disabled: false, selected: false })).toBe(
- resolved().background.action.primary.default,
- )
- expect(theme.background.action.destructive("disabled")).toBe(resolved().background.action.destructive.disabled)
- expect(theme.background.formfield("hovered")).toBe(resolved().background.formfield.hovered)
- expect(theme.background.formfield({ selected: true, hovered: true })).toBe(resolved().background.formfield.selected)
- expect(theme.background.formfield({ focused: true, selected: true, hovered: true })).toBe(
- resolved().background.formfield.focused,
- )
- expect(
- theme.background.formfield({ disabled: true, pressed: true, focused: true, selected: true, hovered: true }),
- ).toBe(resolved().background.formfield.disabled)
- expect(theme.background.surface.offset()).toBe(resolved().background.surface.offset)
- expect(theme.background.surface.overlay()).toBe(resolved().background.surface.overlay)
- expect(theme.scrollbar()).toBe(resolved().scrollbar.default)
- expect(theme.diff.text.added()).toBe(resolved().diff.text.added)
+ expect(theme.text.subdued).toBe(resolved().text.subdued)
+ expect(theme.text.action.primary.default).toBe(resolved().text.action.primary.default)
+ expect(theme.text.action.primary.hovered).toBe(resolved().text.action.primary.hovered)
+ expect(theme.text.action.primary.pressed).toBe(resolved().text.action.primary.pressed)
+ expect(theme.text.action.primary.selected).toBe(resolved().text.action.primary.selected)
+ expect(theme.background.action.primary.selected).toBe(resolved().background.action.primary.selected)
+ expect(theme.background.action.primary.hovered).toBe(resolved().background.action.primary.hovered)
+ expect(theme.background.action.primary.focused).toBe(resolved().background.action.primary.focused)
+ expect(theme.background.action.primary.pressed).toBe(resolved().background.action.primary.pressed)
+ expect(theme.background.action.primary.disabled).toBe(resolved().background.action.primary.disabled)
+ expect(theme.background.action.primary.default).toBe(resolved().background.action.primary.default)
+ expect(theme.background.action.destructive.disabled).toBe(resolved().background.action.destructive.disabled)
+ expect(theme.background.formfield.hovered).toBe(resolved().background.formfield.hovered)
+ expect(theme.background.formfield.selected).toBe(resolved().background.formfield.selected)
+ expect(theme.background.formfield.focused).toBe(resolved().background.formfield.focused)
+ expect(theme.background.formfield.disabled).toBe(resolved().background.formfield.disabled)
+ expect(theme.background.surface.offset).toBe(resolved().background.surface.offset)
+ expect(theme.background.surface.overlay).toBe(resolved().background.surface.overlay)
+ expect(theme.scrollbar.default).toBe(resolved().scrollbar.default)
+ expect(theme.diff.text.added).toBe(resolved().diff.text.added)
setContext("@context:elevated")
- expect(theme.categorical(500)).toEqual(resolved().categorical.map((scale) => scale[500]))
- expect(theme.text()).toBe(resolved().contexts["@context:elevated"]!.text.default)
- expect(theme.background.action("focused")).toBe(
+ expect(theme.categorical.map((scale) => scale[500])).toEqual(resolved().categorical.map((scale) => scale[500]))
+ expect(theme.text.default).toBe(resolved().contexts["@context:elevated"]!.text.default)
+ expect(theme.background.action.primary.focused).toBe(
resolved().contexts["@context:elevated"]!.background.action.primary.focused,
)
- expect(theme.background.action("hovered")).toBe(resolved().background.surface.overlay)
- expect(theme.background.formfield("selected")).toBe(
+ expect(theme.background.action.primary.hovered).toBe(resolved().background.surface.overlay)
+ expect(theme.background.formfield.selected).toBe(
resolved().contexts["@context:elevated"]!.background.formfield.selected,
)
setResolved(resolveTheme(selectTheme(DEFAULT_THEME, "dark")))
setMode("dark")
- expect(theme.text()).toBe(resolved().contexts["@context:elevated"]!.text.default)
- expect(theme.decrease(theme.background.surface.offset(), 1)).toBe(resolved().hue.neutral[600])
- expect(theme.raise(theme.background.surface.offset())).toBe(resolved().hue.neutral[600])
+ expect(theme.text.default).toBe(resolved().contexts["@context:elevated"]!.text.default)
+ expect(theme.decrease(theme.background.surface.offset, 1)).toBe(resolved().hue.neutral[600])
+ expect(theme.raise(theme.background.surface.offset)).toBe(resolved().hue.neutral[600])
})