refactor(tui): add shared DialogHeader (#55501)

This commit is contained in:
James Long
2026-10-15 11:39:21 -04:00
committed by GitHub
Unverified
parent 4f31eb987d
commit af6ec83979
21 changed files with 131 additions and 229 deletions
+2 -10
View File
@@ -1,8 +1,7 @@
import { TextAttributes } from "@opentui/core"
import { createMemo, createSignal, For } from "solid-js"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog } from "../ui/dialog"
import { DialogHeader, useDialog } from "../ui/dialog"
import { useRoute } from "../context/route"
import { useLocal } from "../context/local"
import { useClipboard } from "../context/clipboard"
@@ -54,14 +53,7 @@ export function DialogDebug() {
return (
<box paddingLeft={2} paddingRight={2} gap={1} paddingBottom={1}>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.text.base} attributes={TextAttributes.BOLD}>
Debug
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader title="Debug" />
{/* No click-to-copy here: releasing a mouse selection must trigger the
global copy-on-select so users can copy a single value, e.g. the session id. */}
<box>
@@ -9,7 +9,7 @@ import { useRoute } from "../context/route"
import { getScrollAcceleration } from "../util/scroll"
import { useTheme } from "../context/theme"
import { emptyPrompt } from "../prompt/history"
import { dialogWidth, useDialog } from "../ui/dialog"
import { DialogHeader, dialogWidth, useDialog } from "../ui/dialog"
import { FilePath } from "../ui/file-path"
import { useToast } from "../ui/toast"
import { errorDetails } from "../util/error-details"
@@ -73,21 +73,22 @@ export function DialogErrorDetails(props: {
return (
<box paddingLeft={2} paddingRight={2} paddingBottom={1} gap={1}>
<box>
<box flexDirection="row" gap={2}>
<text
attributes={TextAttributes.BOLD}
fg={theme.text.base}
flexGrow={1}
minWidth={0}
wrapMode="none"
truncate
>
{props.title}
</text>
<text fg={theme.text.muted} flexShrink={0} onMouseUp={props.onBack}>
esc
</text>
</box>
<DialogHeader
gap={2}
onClose={props.onBack}
titleView={
<text
attributes={TextAttributes.BOLD}
fg={theme.text.base}
flexGrow={1}
minWidth={0}
wrapMode="none"
truncate
>
{props.title}
</text>
}
/>
<Show when={props.source}>
{(source) => (
<FilePath
@@ -5,7 +5,7 @@ import { useConfig } from "../config"
import { Keymap } from "../context/keymap"
import type { TuiHighlight } from "../context/runtime"
import { useTheme } from "../context/theme"
import { useDialog } from "../ui/dialog"
import { DialogHeader, useDialog } from "../ui/dialog"
import { getScrollAcceleration } from "../util/scroll"
export function DialogHighlights(props: { version: string; items: ReadonlyArray<TuiHighlight> }) {
@@ -34,14 +34,7 @@ export function DialogHighlights(props: { version: string; items: ReadonlyArray<
return (
<box gap={1} paddingBottom={1}>
<box flexDirection="row" justifyContent="space-between" paddingLeft={2} paddingRight={2}>
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
What's new in {props.version}
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader paddingX={2} title={`What's new in ${props.version}`} />
{/* The band owns the vertical padding so it stays visible while the highlights scroll. */}
<box backgroundColor={overlay.background.raised.high} paddingTop={1} paddingBottom={1}>
<scrollbox
@@ -1,9 +1,8 @@
import { TextAttributes } from "@opentui/core"
import { useTerminalDimensions } from "@opentui/solid"
import { createMemo, createSignal } from "solid-js"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog } from "../ui/dialog"
import { DialogHeader, useDialog } from "../ui/dialog"
type ImagePreviewItem = Readonly<{
uri: string
@@ -38,14 +37,7 @@ export function DialogImagePreview(props: { images: readonly ImagePreviewItem[];
return (
<box id="prompt-image-viewer" paddingLeft={2} paddingRight={2} paddingBottom={1} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
Image {index() + 1} of {props.images.length}
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader title={`Image ${index() + 1} of ${props.images.length}`} />
<image
id="prompt-image-viewer-image"
source={current().uri}
@@ -1,4 +1,4 @@
import { ScrollBoxRenderable, TextareaRenderable, TextAttributes } from "@opentui/core"
import { ScrollBoxRenderable, TextareaRenderable } from "@opentui/core"
import { useTerminalDimensions } from "@opentui/solid"
import type {
ConnectionInfo,
@@ -21,7 +21,7 @@ import { useClient } from "../context/client"
import { Keymap } from "../context/keymap"
import { useLocation } from "../context/location"
import { useTheme } from "../context/theme"
import { useDialog } from "../ui/dialog"
import { DialogHeader, useDialog } from "../ui/dialog"
import { DialogPrompt } from "../ui/dialog-prompt"
import { DialogSelect } from "../ui/dialog-select"
import { Link } from "../ui/link"
@@ -510,14 +510,7 @@ function CommandView(props: { title: string; output: string; message: string })
onMount(() => dialog.setSize("large"))
return (
<box gap={1} paddingBottom={1}>
<box flexDirection="row" justifyContent="space-between" paddingLeft={2} paddingRight={2}>
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
{props.title}
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc close
</text>
</box>
<DialogHeader paddingX={2} title={props.title} closeLabel="esc close" />
<box
backgroundColor={overlayTheme.background.raised.high}
paddingLeft={2}
@@ -801,18 +794,10 @@ function OAuthView(props: {
copy?: boolean
open?: boolean
}) {
const dialog = useDialog()
const theme = useTheme().surface("dialog")
return (
<box paddingLeft={2} paddingRight={2} gap={1} paddingBottom={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
{props.title}
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader title={props.title} />
<Show when={props.url}>
{(url) => (
<box gap={1}>
@@ -986,14 +971,7 @@ function StringChoiceField(props: {
}))
return (
<box paddingLeft={4} paddingRight={4} paddingBottom={1} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.text.base} attributes={TextAttributes.BOLD}>
{formLabel(props.field)}
</text>
<text fg={theme.text.muted} onMouseUp={() => (store.editing ? back() : dialog.clear())}>
esc
</text>
</box>
<DialogHeader title={formLabel(props.field)} onClose={() => (store.editing ? back() : dialog.clear())} />
<Show when={props.field.description}>{(description) => <text fg={theme.text.muted}>{description()}</text>}</Show>
<scrollbox
gap={1}
+2 -9
View File
@@ -4,7 +4,7 @@ import { createMemo, createResource, createSignal, For, Show } from "solid-js"
import { renderUnicodeCompact } from "uqr"
import { useClient } from "../context/client"
import { useTheme } from "../context/theme"
import { useDialog } from "../ui/dialog"
import { DialogHeader, useDialog } from "../ui/dialog"
import { Link } from "../ui/link"
import { errorMessage } from "../util/error"
@@ -88,14 +88,7 @@ export function DialogPair() {
return (
<box paddingLeft={2} paddingRight={2} paddingBottom={1} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.text.base} attributes={TextAttributes.BOLD}>
Pair
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader title="Pair" />
<Show
when={loadError()}
fallback={
@@ -6,7 +6,7 @@ import stripAnsi from "strip-ansi"
import { useClient } from "../context/client"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog } from "../ui/dialog"
import { DialogHeader, useDialog } from "../ui/dialog"
const PAGE_BYTES = 64 * 1024
@@ -101,15 +101,16 @@ export function DialogShellOutput(props: { shell: ShellInfo; location: LocationR
return (
<box paddingLeft={2} paddingRight={2} paddingBottom={1} gap={1}>
<box flexDirection="row" gap={2}>
<text fg={theme.text.base} attributes={TextAttributes.BOLD} flexGrow={1}>
Shell output
</text>
<DialogHeader
gap={2}
titleView={
<text fg={theme.text.base} attributes={TextAttributes.BOLD} flexGrow={1}>
Shell output
</text>
}
>
<text fg={theme.text.muted}>{status()}</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
</DialogHeader>
<text fg={theme.text.muted} maxHeight={3} wrapMode="word">
{props.shell.command}
</text>
+2 -11
View File
@@ -1,13 +1,11 @@
import { TextAttributes } from "@opentui/core"
import { useTheme } from "../context/theme"
import { useDialog } from "../ui/dialog"
import { DialogHeader } from "../ui/dialog"
import { useData } from "../context/data"
import { For, Match, Switch, Show, createMemo } from "solid-js"
export function DialogStatus() {
const data = useData()
const theme = useTheme().surface("dialog")
const dialog = useDialog()
const mcp = createMemo(() => data.location.mcp.server.list() ?? [])
const color = (status: string) => {
@@ -18,14 +16,7 @@ export function DialogStatus() {
}
return (
<box paddingLeft={2} paddingRight={2} gap={1} paddingBottom={1}>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.text.base} attributes={TextAttributes.BOLD}>
Status
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader title="Status" />
<Show when={mcp().length > 0} fallback={<text fg={theme.text.base}>No MCP servers</text>}>
<box>
<text fg={theme.text.base}>
+7 -11
View File
@@ -1,9 +1,8 @@
import { TextAttributes } from "@opentui/core"
import { createEffect, createMemo, createResource, createSignal, For, Match, onCleanup, Show, Switch } from "solid-js"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import type { UpdateState } from "../context/update-notification"
import { useDialog } from "../ui/dialog"
import { DialogHeader, useDialog } from "../ui/dialog"
import { errorMessage } from "../util/error"
import { Spinner } from "./spinner"
@@ -87,18 +86,15 @@ export function DialogUpdate(props: {
return (
<box paddingLeft={2} paddingRight={2} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
{state().type === "installing"
<DialogHeader
title={
state().type === "installing"
? "Updating OpenCode"
: state().type === "available" || state().type === "failed"
? "Update available"
: "Update"}
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
: "Update"
}
/>
<box paddingBottom={1}>
<Show when={state()} keyed>
{(current) => (
@@ -1,4 +1,3 @@
import { TextAttributes } from "@opentui/core"
import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
import type { VcsFileStatus } from "@opencode/client"
import { createMemo, For } from "solid-js"
@@ -6,7 +5,7 @@ import { createStore } from "solid-js/store"
import { FilePath } from "../ui/file-path"
import { useTheme } from "../context/theme"
import { useConfig } from "../config"
import { useDialog, type DialogContext } from "../ui/dialog"
import { DialogHeader, useDialog, type DialogContext } from "../ui/dialog"
import { getScrollAcceleration } from "../util/scroll"
const options = ["no", "yes"] as const
@@ -71,14 +70,7 @@ export function DialogWorkspaceFileChanges(props: {
return (
<box gap={1}>
<box flexDirection="row" justifyContent="space-between" paddingLeft={2} paddingRight={2}>
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
{props.title ?? "File Changes Found"}
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader paddingX={2} title={props.title ?? "File Changes Found"} />
<box paddingLeft={2} paddingRight={2}>
<text fg={theme.text.muted} wrapMode="word">
{props.message ?? "Do you want to move these changes with the session?"}
@@ -1,9 +1,9 @@
import { InputRenderable, TextAttributes } from "@opentui/core"
import { InputRenderable } from "@opentui/core"
import { Slug } from "@opencode/core/util/slug"
import { createSignal, onMount } from "solid-js"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog, type DialogContext } from "../ui/dialog"
import { DialogHeader, useDialog, type DialogContext } from "../ui/dialog"
import { useConfig } from "../config"
export function DialogWorktreeName(props: { onConfirm: (name: string) => void }) {
@@ -48,14 +48,7 @@ export function DialogWorktreeName(props: { onConfirm: (name: string) => void })
return (
<box paddingLeft={2} paddingRight={2} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
Name worktree
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader title="Name worktree" />
<input
cursorStyle={config.cursor}
ref={(value: InputRenderable) => {
+11 -10
View File
@@ -8,7 +8,7 @@ import { useClipboard } from "../../context/clipboard"
import { Keymap } from "../../context/keymap"
import { useTheme, useThemes } from "../../context/theme"
import { usePlugin } from "../../plugin/context"
import { useDialog } from "../../ui/dialog"
import { DialogHeader } from "../../ui/dialog"
import { useToast } from "../../ui/toast"
import { getScrollAcceleration } from "../../util/scroll"
@@ -90,7 +90,6 @@ export function Answer(props: {
answer: string
markdown: ReturnType<typeof usePlugin>["markdown"]
}) {
const dialog = useDialog()
const toast = useToast()
const clipboard = useClipboard()
const theme = useTheme().surface("dialog")
@@ -126,17 +125,19 @@ export function Answer(props: {
return (
<box gap={1} paddingBottom={1}>
<box flexDirection="row" gap={2} paddingLeft={2} paddingRight={2}>
<text attributes={TextAttributes.BOLD} fg={theme.text.base} flexShrink={0}>
/btw
</text>
<DialogHeader
gap={2}
paddingX={2}
titleView={
<text attributes={TextAttributes.BOLD} fg={theme.text.base} flexShrink={0}>
/btw
</text>
}
>
<text fg={theme.text.muted} wrapMode="none" flexGrow={1} truncate>
{props.question.replace(/\s+/g, " ")}
</text>
<text fg={theme.text.muted} flexShrink={0} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
</DialogHeader>
<scrollbox
ref={(element: ScrollBoxRenderable) => (scroll = element)}
maxHeight={maxHeight()}
@@ -8,7 +8,7 @@ import { useConfig } from "../../config"
import { useClipboard } from "../../context/clipboard"
import { Keymap } from "../../context/keymap"
import { useTheme, useThemes } from "../../context/theme"
import { dialogWidth, useDialog } from "../../ui/dialog"
import { DialogHeader, dialogWidth, useDialog } from "../../ui/dialog"
import { useToast } from "../../ui/toast"
import { Locale } from "../../util/locale"
import { getScrollAcceleration } from "../../util/scroll"
@@ -93,15 +93,17 @@ export function DialogExecute(props: { part: SessionMessageAssistantTool }) {
return (
<box gap={1} paddingBottom={1}>
<box flexDirection="row" gap={2} paddingLeft={2} paddingRight={2}>
<text fg={theme.text.base} attributes={TextAttributes.BOLD} flexGrow={1}>
execute
</text>
<DialogHeader
gap={2}
paddingX={2}
titleView={
<text fg={theme.text.base} attributes={TextAttributes.BOLD} flexGrow={1}>
execute
</text>
}
>
<text fg={failed() ? theme.text.feedback.error.base : theme.text.muted}>{status()}</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
</DialogHeader>
<scrollbox
id="execute-detail-scroll"
ref={(value: ScrollBoxRenderable) => (scroll = value)}
+2 -10
View File
@@ -1,7 +1,6 @@
import { TextAttributes } from "@opentui/core"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog } from "./dialog"
import { DialogHeader, useDialog } from "./dialog"
export type DialogAlertProps = {
title: string
@@ -29,14 +28,7 @@ export function DialogAlert(props: DialogAlertProps) {
}))
return (
<box paddingLeft={2} paddingRight={2} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
{props.title}
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader title={props.title} />
<box paddingBottom={1}>
<text fg={theme.text.muted}>{props.message}</text>
</box>
+2 -10
View File
@@ -1,7 +1,6 @@
import { TextAttributes } from "@opentui/core"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog } from "./dialog"
import { DialogHeader, useDialog } from "./dialog"
import { createStore } from "solid-js/store"
import { For } from "solid-js"
import { Locale } from "../util/locale"
@@ -57,14 +56,7 @@ export function DialogConfirm(props: DialogConfirmProps) {
}))
return (
<box paddingLeft={2} paddingRight={2} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
{props.title}
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader title={props.title} />
<box paddingBottom={1}>
<text fg={theme.text.muted}>{props.message}</text>
</box>
+2 -11
View File
@@ -1,7 +1,6 @@
import { TextAttributes } from "@opentui/core"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog, type DialogContext } from "./dialog"
import { DialogHeader, type DialogContext } from "./dialog"
import { createStore } from "solid-js/store"
import { For, Show } from "solid-js"
@@ -22,7 +21,6 @@ export type DialogExportOptionsProps = {
type Active = ExportFormat | "thinking" | "tools" | "sanitize" | "copy" | "export"
export function DialogExportOptions(props: DialogExportOptionsProps) {
const dialog = useDialog()
const theme = useTheme().surface("dialog")
const overlayTheme = useTheme()
const [store, setStore] = createStore({
@@ -84,14 +82,7 @@ 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.base}>
Export session
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc
</text>
</box>
<DialogHeader title="Export session" />
<box flexDirection="row" gap={1}>
<text fg={theme.text.base}>Export as:</text>
<box flexDirection="row" gap={1}>
+2 -10
View File
@@ -1,7 +1,6 @@
import { TextAttributes } from "@opentui/core"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog, type DialogContext } from "./dialog"
import { DialogHeader, useDialog, type DialogContext } from "./dialog"
export function DialogExportResult(props: { path: string; onClose?: () => void }) {
const dialog = useDialog()
@@ -26,14 +25,7 @@ export function DialogExportResult(props: { path: string; onClose?: () => void }
return (
<box paddingLeft={2} paddingRight={2} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
Session exported
</text>
<text fg={theme.text.muted} onMouseUp={close}>
esc
</text>
</box>
<DialogHeader title="Session exported" onClose={close} />
<box>
<text fg={theme.text.base}>{props.path}</text>
</box>
+2 -10
View File
@@ -1,7 +1,6 @@
import { TextAttributes } from "@opentui/core"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog } from "./dialog"
import { DialogHeader, useDialog } from "./dialog"
export function DialogHelp() {
const dialog = useDialog()
@@ -18,14 +17,7 @@ export function DialogHelp() {
return (
<box paddingLeft={2} paddingRight={2} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
Help
</text>
<text fg={theme.text.muted} onMouseUp={() => dialog.clear()}>
esc/enter
</text>
</box>
<DialogHeader title="Help" closeLabel="esc/enter" />
<box paddingBottom={1}>
<text fg={theme.text.muted}>
Press {shortcuts.get("command.palette.show")} to see all available actions and commands in any context.
+8 -15
View File
@@ -1,7 +1,7 @@
import { TextareaRenderable, TextAttributes } from "@opentui/core"
import { TextareaRenderable } from "@opentui/core"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog, type DialogSize } from "./dialog"
import { DialogHeader, useDialog, type DialogSize } from "./dialog"
import { Show, createEffect, createSignal, onMount, type JSX } from "solid-js"
import { Spinner } from "../component/spinner"
import { useConfig } from "../config"
@@ -97,19 +97,12 @@ 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.base}>
{props.title}
</text>
<text
fg={theme.text.muted}
onMouseUp={() => {
if (!props.busy) (props.onCancel ?? dialog.clear)()
}}
>
esc
</text>
</box>
<DialogHeader
title={props.title}
onClose={() => {
if (!props.busy) (props.onCancel ?? dialog.clear)()
}}
/>
<box gap={1}>
{props.description?.()}
<textarea
+2 -11
View File
@@ -7,7 +7,7 @@ import { createStore } from "solid-js/store"
import { useRenderer, useTerminalDimensions } from "@opentui/solid"
import * as fuzzysort from "fuzzysort"
import { isDeepEqual } from "remeda"
import { useDialog, type DialogContext } from "./dialog"
import { DialogHeader, useDialog, type DialogContext } from "./dialog"
import { Locale } from "../util/locale"
import { getScrollAcceleration } from "../util/scroll"
import { useConfig } from "../config"
@@ -672,16 +672,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
return (
<box gap={1} paddingBottom={1} flexGrow={1}>
<box paddingLeft={4} paddingRight={4}>
<box flexDirection="row" justifyContent="space-between">
{props.titleView ?? (
<text fg={theme.text.base} attributes={TextAttributes.BOLD}>
{props.title}
</text>
)}
<text fg={theme.text.muted} onMouseUp={() => (props.onCancel ?? dialog.clear)()}>
esc
</text>
</box>
<DialogHeader title={props.title} titleView={props.titleView} onClose={props.onCancel} />
<Show when={props.renderFilter !== false}>
<box paddingTop={1}>
<input
+35 -1
View File
@@ -2,7 +2,7 @@ import { useRenderer, useTerminalDimensions } from "@opentui/solid"
import { batch, createContext, createEffect, onCleanup, Show, useContext, type JSX, type ParentProps } from "solid-js"
import { Keymap } from "../context/keymap"
import { ThemeContextProvider, useTheme } from "../context/theme"
import { InputRenderable, MouseButton, Renderable, RGBA } from "@opentui/core"
import { InputRenderable, MouseButton, Renderable, RGBA, TextAttributes } from "@opentui/core"
import { createStore } from "solid-js/store"
import { useToast } from "./toast"
import { useClipboard } from "../context/clipboard"
@@ -79,6 +79,40 @@ export function Dialog(
)
}
export function DialogHeader(
props: ParentProps<{
title?: JSX.Element
titleView?: JSX.Element
closeLabel?: string
onClose?: () => void
gap?: number
paddingX?: number
newInset?: number
}>,
) {
const dialog = useDialog()
const theme = useTheme().surface("dialog")
return (
<box
flexDirection="row"
justifyContent="space-between"
gap={props.gap}
paddingLeft={props.newInset ?? props.paddingX}
paddingRight={props.newInset ?? props.paddingX}
>
{props.titleView ?? (
<text attributes={TextAttributes.BOLD} fg={theme.text.base}>
{props.title}
</text>
)}
{props.children}
<text fg={theme.text.muted} flexShrink={0} onMouseUp={() => (props.onClose ?? dialog.clear)()}>
{props.closeLabel ?? "esc"}
</text>
</box>
)
}
function init() {
const [store, setStore] = createStore({
stack: [] as {