fix(tui): responsive layout for narrow screens (#9703)
This commit is contained in:
committed by
GitHub
parent
34d473c0f5
commit
b93f33eaa4
@@ -8,6 +8,7 @@ import type { AssistantMessage, Session } from "@opencode-ai/sdk/v2"
|
|||||||
import { useCommandDialog } from "@tui/component/dialog-command"
|
import { useCommandDialog } from "@tui/component/dialog-command"
|
||||||
import { useKeybind } from "../../context/keybind"
|
import { useKeybind } from "../../context/keybind"
|
||||||
import { Installation } from "@/installation"
|
import { Installation } from "@/installation"
|
||||||
|
import { useTerminalDimensions } from "@opentui/solid"
|
||||||
|
|
||||||
const Title = (props: { session: Accessor<Session> }) => {
|
const Title = (props: { session: Accessor<Session> }) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
@@ -63,6 +64,8 @@ export function Header() {
|
|||||||
const keybind = useKeybind()
|
const keybind = useKeybind()
|
||||||
const command = useCommandDialog()
|
const command = useCommandDialog()
|
||||||
const [hover, setHover] = createSignal<"parent" | "prev" | "next" | null>(null)
|
const [hover, setHover] = createSignal<"parent" | "prev" | "next" | null>(null)
|
||||||
|
const dimensions = useTerminalDimensions()
|
||||||
|
const narrow = createMemo(() => dimensions().width < 80)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<box flexShrink={0}>
|
<box flexShrink={0}>
|
||||||
@@ -79,49 +82,52 @@ export function Header() {
|
|||||||
>
|
>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={session()?.parentID}>
|
<Match when={session()?.parentID}>
|
||||||
<box flexDirection="row" gap={2}>
|
<box flexDirection="column" gap={1}>
|
||||||
<text fg={theme.text}>
|
<box flexDirection={narrow() ? "column" : "row"} justifyContent="space-between" gap={narrow() ? 1 : 0}>
|
||||||
<b>Subagent session</b>
|
|
||||||
</text>
|
|
||||||
<box
|
|
||||||
onMouseOver={() => setHover("parent")}
|
|
||||||
onMouseOut={() => setHover(null)}
|
|
||||||
onMouseUp={() => command.trigger("session.parent")}
|
|
||||||
backgroundColor={hover() === "parent" ? theme.backgroundElement : theme.backgroundPanel}
|
|
||||||
>
|
|
||||||
<text fg={theme.text}>
|
<text fg={theme.text}>
|
||||||
Parent <span style={{ fg: theme.textMuted }}>{keybind.print("session_parent")}</span>
|
<b>Subagent session</b>
|
||||||
</text>
|
</text>
|
||||||
|
<box flexDirection="row" gap={1} flexShrink={0}>
|
||||||
|
<ContextInfo context={context} cost={cost} />
|
||||||
|
<text fg={theme.textMuted}>v{Installation.VERSION}</text>
|
||||||
|
</box>
|
||||||
</box>
|
</box>
|
||||||
<box
|
<box flexDirection="row" gap={2}>
|
||||||
onMouseOver={() => setHover("prev")}
|
<box
|
||||||
onMouseOut={() => setHover(null)}
|
onMouseOver={() => setHover("parent")}
|
||||||
onMouseUp={() => command.trigger("session.child.previous")}
|
onMouseOut={() => setHover(null)}
|
||||||
backgroundColor={hover() === "prev" ? theme.backgroundElement : theme.backgroundPanel}
|
onMouseUp={() => command.trigger("session.parent")}
|
||||||
>
|
backgroundColor={hover() === "parent" ? theme.backgroundElement : theme.backgroundPanel}
|
||||||
<text fg={theme.text}>
|
>
|
||||||
Prev <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle_reverse")}</span>
|
<text fg={theme.text}>
|
||||||
</text>
|
Parent <span style={{ fg: theme.textMuted }}>{keybind.print("session_parent")}</span>
|
||||||
</box>
|
</text>
|
||||||
<box
|
</box>
|
||||||
onMouseOver={() => setHover("next")}
|
<box
|
||||||
onMouseOut={() => setHover(null)}
|
onMouseOver={() => setHover("prev")}
|
||||||
onMouseUp={() => command.trigger("session.child.next")}
|
onMouseOut={() => setHover(null)}
|
||||||
backgroundColor={hover() === "next" ? theme.backgroundElement : theme.backgroundPanel}
|
onMouseUp={() => command.trigger("session.child.previous")}
|
||||||
>
|
backgroundColor={hover() === "prev" ? theme.backgroundElement : theme.backgroundPanel}
|
||||||
<text fg={theme.text}>
|
>
|
||||||
Next <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle")}</span>
|
<text fg={theme.text}>
|
||||||
</text>
|
Prev <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle_reverse")}</span>
|
||||||
</box>
|
</text>
|
||||||
<box flexGrow={1} flexShrink={1} />
|
</box>
|
||||||
<box flexDirection="row" gap={1} flexShrink={0}>
|
<box
|
||||||
<ContextInfo context={context} cost={cost} />
|
onMouseOver={() => setHover("next")}
|
||||||
<text fg={theme.textMuted}>v{Installation.VERSION}</text>
|
onMouseOut={() => setHover(null)}
|
||||||
|
onMouseUp={() => command.trigger("session.child.next")}
|
||||||
|
backgroundColor={hover() === "next" ? theme.backgroundElement : theme.backgroundPanel}
|
||||||
|
>
|
||||||
|
<text fg={theme.text}>
|
||||||
|
Next <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle")}</span>
|
||||||
|
</text>
|
||||||
|
</box>
|
||||||
</box>
|
</box>
|
||||||
</box>
|
</box>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<box flexDirection="row" justifyContent="space-between" gap={1}>
|
<box flexDirection={narrow() ? "column" : "row"} justifyContent="space-between" gap={1}>
|
||||||
<Title session={session} />
|
<Title session={session} />
|
||||||
<box flexDirection="row" gap={1} flexShrink={0}>
|
<box flexDirection="row" gap={1} flexShrink={0}>
|
||||||
<ContextInfo context={context} cost={cost} />
|
<ContextInfo context={context} cost={cost} />
|
||||||
|
|||||||
@@ -302,6 +302,8 @@ function RejectPrompt(props: { onConfirm: (message: string) => void; onCancel: (
|
|||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const keybind = useKeybind()
|
const keybind = useKeybind()
|
||||||
const textareaKeybindings = useTextareaKeybindings()
|
const textareaKeybindings = useTextareaKeybindings()
|
||||||
|
const dimensions = useTerminalDimensions()
|
||||||
|
const narrow = createMemo(() => dimensions().width < 80)
|
||||||
|
|
||||||
useKeyboard((evt) => {
|
useKeyboard((evt) => {
|
||||||
if (evt.name === "escape" || keybind.match("app_exit", evt)) {
|
if (evt.name === "escape" || keybind.match("app_exit", evt)) {
|
||||||
@@ -332,14 +334,16 @@ function RejectPrompt(props: { onConfirm: (message: string) => void; onCancel: (
|
|||||||
</box>
|
</box>
|
||||||
</box>
|
</box>
|
||||||
<box
|
<box
|
||||||
flexDirection="row"
|
flexDirection={narrow() ? "column" : "row"}
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
paddingTop={1}
|
paddingTop={1}
|
||||||
paddingLeft={2}
|
paddingLeft={2}
|
||||||
paddingRight={3}
|
paddingRight={3}
|
||||||
paddingBottom={1}
|
paddingBottom={1}
|
||||||
backgroundColor={theme.backgroundElement}
|
backgroundColor={theme.backgroundElement}
|
||||||
justifyContent="space-between"
|
justifyContent={narrow() ? "flex-start" : "space-between"}
|
||||||
|
alignItems={narrow() ? "flex-start" : "center"}
|
||||||
|
gap={1}
|
||||||
>
|
>
|
||||||
<textarea
|
<textarea
|
||||||
ref={(val: TextareaRenderable) => (input = val)}
|
ref={(val: TextareaRenderable) => (input = val)}
|
||||||
@@ -349,7 +353,7 @@ function RejectPrompt(props: { onConfirm: (message: string) => void; onCancel: (
|
|||||||
cursorColor={theme.primary}
|
cursorColor={theme.primary}
|
||||||
keyBindings={textareaKeybindings()}
|
keyBindings={textareaKeybindings()}
|
||||||
/>
|
/>
|
||||||
<box flexDirection="row" gap={2} flexShrink={0} marginLeft={1}>
|
<box flexDirection="row" gap={2} flexShrink={0}>
|
||||||
<text fg={theme.text}>
|
<text fg={theme.text}>
|
||||||
enter <span style={{ fg: theme.textMuted }}>confirm</span>
|
enter <span style={{ fg: theme.textMuted }}>confirm</span>
|
||||||
</text>
|
</text>
|
||||||
@@ -379,6 +383,7 @@ function Prompt<const T extends Record<string, string>>(props: {
|
|||||||
expanded: false,
|
expanded: false,
|
||||||
})
|
})
|
||||||
const diffKey = Keybind.parse("ctrl+f")[0]
|
const diffKey = Keybind.parse("ctrl+f")[0]
|
||||||
|
const narrow = createMemo(() => dimensions().width < 80)
|
||||||
|
|
||||||
useKeyboard((evt) => {
|
useKeyboard((evt) => {
|
||||||
if (evt.name === "left" || evt.name == "h") {
|
if (evt.name === "left" || evt.name == "h") {
|
||||||
@@ -440,7 +445,7 @@ function Prompt<const T extends Record<string, string>>(props: {
|
|||||||
{props.body}
|
{props.body}
|
||||||
</box>
|
</box>
|
||||||
<box
|
<box
|
||||||
flexDirection="row"
|
flexDirection={narrow() ? "column" : "row"}
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
gap={1}
|
gap={1}
|
||||||
paddingTop={1}
|
paddingTop={1}
|
||||||
@@ -448,9 +453,10 @@ function Prompt<const T extends Record<string, string>>(props: {
|
|||||||
paddingRight={3}
|
paddingRight={3}
|
||||||
paddingBottom={1}
|
paddingBottom={1}
|
||||||
backgroundColor={theme.backgroundElement}
|
backgroundColor={theme.backgroundElement}
|
||||||
justifyContent="space-between"
|
justifyContent={narrow() ? "flex-start" : "space-between"}
|
||||||
|
alignItems={narrow() ? "flex-start" : "center"}
|
||||||
>
|
>
|
||||||
<box flexDirection="row" gap={1}>
|
<box flexDirection="row" gap={1} flexShrink={0}>
|
||||||
<For each={keys}>
|
<For each={keys}>
|
||||||
{(option) => (
|
{(option) => (
|
||||||
<box
|
<box
|
||||||
@@ -470,7 +476,7 @@ function Prompt<const T extends Record<string, string>>(props: {
|
|||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</box>
|
</box>
|
||||||
<box flexDirection="row" gap={2}>
|
<box flexDirection="row" gap={2} flexShrink={0}>
|
||||||
<Show when={props.fullscreen}>
|
<Show when={props.fullscreen}>
|
||||||
<text fg={theme.text}>
|
<text fg={theme.text}>
|
||||||
{"ctrl+f"} <span style={{ fg: theme.textMuted }}>{hint()}</span>
|
{"ctrl+f"} <span style={{ fg: theme.textMuted }}>{hint()}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user