mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-14 03:56:55 +03:00
refactor(tui): add shared DialogHeader (#55501)
This commit is contained in:
committed by
GitHub
Unverified
parent
4f31eb987d
commit
af6ec83979
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user