refactor(tui): migrate workflow views to V2 themes (#37526)

This commit is contained in:
James Long 2026-07-17 15:18:58 -04:00 committed by GitHub
commit c7a7900ff2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 243 additions and 138 deletions

View file

@ -17,7 +17,8 @@ type Active = ExportFormat | "debug" | "thinking" | "copy" | "export"
export function DialogExportOptions(props: DialogExportOptionsProps) {
const dialog = useDialog()
const { theme } = useTheme()
const { themeV2 } = useTheme().contextual("elevated")
const { themeV2: overlayTheme } = useTheme().contextual("overlay")
const [store, setStore] = createStore({
format: "markdown" as ExportFormat,
debug: false,
@ -75,25 +76,33 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
return (
<box paddingLeft={2} paddingRight={2} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD} fg={theme.text}>
<text attributes={TextAttributes.BOLD} fg={themeV2.text()}>
Export session
</text>
<text fg={theme.textMuted} onMouseUp={() => dialog.clear()}>
<text fg={themeV2.text.subdued()} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<box flexDirection="row" gap={1}>
<text fg={theme.text}>Export as:</text>
<text fg={themeV2.text()}>Export as:</text>
<box flexDirection="row" gap={1}>
<For each={["markdown", "json"] as const}>
{(format) => (
<box
paddingLeft={1}
paddingRight={1}
backgroundColor={store.format === format ? theme.backgroundElement : undefined}
backgroundColor={themeV2.background.formfield({
focused: store.active === format,
selected: store.format === format,
})}
onMouseUp={() => selectFormat(format)}
>
<text fg={store.format === format ? theme.text : theme.textMuted}>
<text
fg={themeV2.text.formfield({
focused: store.active === format,
selected: store.format === format,
})}
>
{store.format === format ? "◉" : "○"} {format === "markdown" ? "Markdown" : "JSON"}
</text>
</box>
@ -105,43 +114,60 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
<box
flexDirection="row"
gap={1}
backgroundColor={store.active === "thinking" ? theme.backgroundElement : undefined}
backgroundColor={themeV2.background.formfield({
focused: store.active === "thinking",
selected: store.thinking,
})}
onMouseUp={() => {
setStore("active", "thinking")
setStore("thinking", !store.thinking)
}}
>
<text fg={store.active === "thinking" ? theme.primary : theme.textMuted}>
<text fg={themeV2.text.formfield({ focused: store.active === "thinking", selected: store.thinking })}>
{store.thinking ? "[x]" : "[ ]"}
</text>
<text fg={store.active === "thinking" ? theme.primary : theme.text}>Include thinking</text>
<text fg={themeV2.text.formfield({ focused: store.active === "thinking", selected: store.thinking })}>
Include thinking
</text>
</box>
</Show>
<Show when={store.format === "json"}>
<box
flexDirection="row"
gap={1}
backgroundColor={store.active === "debug" ? theme.backgroundElement : undefined}
backgroundColor={themeV2.background.formfield({
focused: store.active === "debug",
selected: store.debug,
})}
onMouseUp={() => {
setStore("active", "debug")
setStore("debug", !store.debug)
}}
>
<text fg={store.active === "debug" ? theme.primary : theme.textMuted}>{store.debug ? "[x]" : "[ ]"}</text>
<text fg={store.active === "debug" ? theme.primary : theme.text}>Events (debug)</text>
<text fg={themeV2.text.formfield({ focused: store.active === "debug", selected: store.debug })}>
{store.debug ? "[x]" : "[ ]"}
</text>
<text fg={themeV2.text.formfield({ focused: store.active === "debug", selected: store.debug })}>
Events (debug)
</text>
</box>
</Show>
<box flexDirection="row" justifyContent="flex-end" gap={1} paddingBottom={1}>
<box
paddingLeft={4}
paddingRight={4}
backgroundColor={theme.backgroundElement}
backgroundColor={overlayTheme.background()}
onMouseUp={() => confirm("copy")}
>
<text fg={theme.text}>Copy</text>
<text fg={overlayTheme.text()}>Copy</text>
</box>
<box paddingLeft={4} paddingRight={4} backgroundColor={theme.primary} onMouseUp={() => confirm("export")}>
<text fg={theme.selectedListItemText}>Export</text>
<box
paddingLeft={4}
paddingRight={4}
backgroundColor={themeV2.background.action({ focused: store.active === "export" })}
onMouseUp={() => confirm("export")}
>
<text fg={themeV2.text.action({ focused: store.active === "export" })}>Export</text>
</box>
</box>
</box>

View file

@ -18,7 +18,7 @@ export type DialogPromptProps = {
export function DialogPrompt(props: DialogPromptProps) {
const dialog = useDialog()
const { theme } = useTheme()
const { themeV2 } = useTheme().contextual("elevated")
const shortcuts = Keymap.useShortcuts()
const [textareaTarget, setTextareaTarget] = createSignal<TextareaRenderable>()
let textarea: TextareaRenderable
@ -74,10 +74,10 @@ export function DialogPrompt(props: DialogPromptProps) {
return (
<box paddingLeft={2} paddingRight={2} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD} fg={theme.text}>
<text attributes={TextAttributes.BOLD} fg={themeV2.text()}>
{props.title}
</text>
<text fg={theme.textMuted} onMouseUp={() => dialog.clear()}>
<text fg={themeV2.text.subdued()} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
@ -91,20 +91,20 @@ export function DialogPrompt(props: DialogPromptProps) {
}}
initialValue={props.value}
placeholder={props.placeholder ?? "Enter text"}
placeholderColor={theme.textMuted}
textColor={props.busy ? theme.textMuted : theme.text}
focusedTextColor={props.busy ? theme.textMuted : theme.text}
cursorColor={props.busy ? theme.backgroundElement : theme.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()}
/>
<Show when={props.busy}>
<Spinner color={theme.textMuted}>{props.busyText ?? "Working..."}</Spinner>
<Spinner color={themeV2.text.subdued()}>{props.busyText ?? "Working..."}</Spinner>
</Show>
</box>
<box paddingBottom={1} gap={1} flexDirection="row">
<Show when={!props.busy} fallback={<text fg={theme.textMuted}>processing...</text>}>
<Show when={!props.busy} fallback={<text fg={themeV2.text.subdued()}>processing...</text>}>
<Show when={shortcuts.get("dialog.prompt.submit")}>
<text fg={theme.text}>
{shortcuts.get("dialog.prompt.submit")} <span style={{ fg: theme.textMuted }}>submit</span>
<text fg={themeV2.text()}>
{shortcuts.get("dialog.prompt.submit")} <span style={{ fg: themeV2.text.subdued() }}>submit</span>
</text>
</Show>
</Show>

View file

@ -16,7 +16,7 @@ export function Dialog(
}>,
) {
const dimensions = useTerminalDimensions()
const { theme } = useTheme()
const { themeV2 } = useTheme().contextual("elevated")
const renderer = useRenderer()
let dismiss = false
@ -59,7 +59,7 @@ export function Dialog(
}}
width={width()}
maxWidth={dimensions().width - 2}
backgroundColor={theme.backgroundPanel}
backgroundColor={themeV2.background()}
paddingTop={1}
>
{props.children}

View file

@ -14,7 +14,7 @@ type ToastInput = Omit<ToastOptions, "duration"> & { duration?: number }
export function Toast() {
const toast = useToast()
const { theme, themeV2 } = useTheme()
const { themeV2 } = useTheme().contextual("overlay")
const dimensions = useTerminalDimensions()
return (
@ -31,8 +31,8 @@ export function Toast() {
paddingRight={2}
paddingTop={1}
paddingBottom={1}
backgroundColor={theme.backgroundPanel}
borderColor={theme[current().variant]}
backgroundColor={themeV2.background()}
borderColor={themeV2.text.feedback[current().variant]()}
border={["left", "right"]}
customBorderChars={SplitBorder.customBorderChars}
>