fix(tui): responsive layout for narrow screens (#9703)

This commit is contained in:
Vinicius da Motta
2026-01-21 01:22:38 -03:00
committed by GitHub
parent 34d473c0f5
commit b93f33eaa4
2 changed files with 55 additions and 43 deletions

View File

@@ -8,6 +8,7 @@ import type { AssistantMessage, Session } from "@opencode-ai/sdk/v2"
import { useCommandDialog } from "@tui/component/dialog-command"
import { useKeybind } from "../../context/keybind"
import { Installation } from "@/installation"
import { useTerminalDimensions } from "@opentui/solid"
const Title = (props: { session: Accessor<Session> }) => {
const { theme } = useTheme()
@@ -63,6 +64,8 @@ export function Header() {
const keybind = useKeybind()
const command = useCommandDialog()
const [hover, setHover] = createSignal<"parent" | "prev" | "next" | null>(null)
const dimensions = useTerminalDimensions()
const narrow = createMemo(() => dimensions().width < 80)
return (
<box flexShrink={0}>
@@ -79,49 +82,52 @@ export function Header() {
>
<Switch>
<Match when={session()?.parentID}>
<box flexDirection="row" gap={2}>
<text fg={theme.text}>
<b>Subagent session</b>
</text>
<box
onMouseOver={() => setHover("parent")}
onMouseOut={() => setHover(null)}
onMouseUp={() => command.trigger("session.parent")}
backgroundColor={hover() === "parent" ? theme.backgroundElement : theme.backgroundPanel}
>
<box flexDirection="column" gap={1}>
<box flexDirection={narrow() ? "column" : "row"} justifyContent="space-between" gap={narrow() ? 1 : 0}>
<text fg={theme.text}>
Parent <span style={{ fg: theme.textMuted }}>{keybind.print("session_parent")}</span>
<b>Subagent session</b>
</text>
<box flexDirection="row" gap={1} flexShrink={0}>
<ContextInfo context={context} cost={cost} />
<text fg={theme.textMuted}>v{Installation.VERSION}</text>
</box>
</box>
<box
onMouseOver={() => setHover("prev")}
onMouseOut={() => setHover(null)}
onMouseUp={() => command.trigger("session.child.previous")}
backgroundColor={hover() === "prev" ? theme.backgroundElement : theme.backgroundPanel}
>
<text fg={theme.text}>
Prev <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle_reverse")}</span>
</text>
</box>
<box
onMouseOver={() => setHover("next")}
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 flexGrow={1} flexShrink={1} />
<box flexDirection="row" gap={1} flexShrink={0}>
<ContextInfo context={context} cost={cost} />
<text fg={theme.textMuted}>v{Installation.VERSION}</text>
<box flexDirection="row" gap={2}>
<box
onMouseOver={() => setHover("parent")}
onMouseOut={() => setHover(null)}
onMouseUp={() => command.trigger("session.parent")}
backgroundColor={hover() === "parent" ? theme.backgroundElement : theme.backgroundPanel}
>
<text fg={theme.text}>
Parent <span style={{ fg: theme.textMuted }}>{keybind.print("session_parent")}</span>
</text>
</box>
<box
onMouseOver={() => setHover("prev")}
onMouseOut={() => setHover(null)}
onMouseUp={() => command.trigger("session.child.previous")}
backgroundColor={hover() === "prev" ? theme.backgroundElement : theme.backgroundPanel}
>
<text fg={theme.text}>
Prev <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle_reverse")}</span>
</text>
</box>
<box
onMouseOver={() => setHover("next")}
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>
</Match>
<Match when={true}>
<box flexDirection="row" justifyContent="space-between" gap={1}>
<box flexDirection={narrow() ? "column" : "row"} justifyContent="space-between" gap={1}>
<Title session={session} />
<box flexDirection="row" gap={1} flexShrink={0}>
<ContextInfo context={context} cost={cost} />

View File

@@ -302,6 +302,8 @@ function RejectPrompt(props: { onConfirm: (message: string) => void; onCancel: (
const { theme } = useTheme()
const keybind = useKeybind()
const textareaKeybindings = useTextareaKeybindings()
const dimensions = useTerminalDimensions()
const narrow = createMemo(() => dimensions().width < 80)
useKeyboard((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
flexDirection="row"
flexDirection={narrow() ? "column" : "row"}
flexShrink={0}
paddingTop={1}
paddingLeft={2}
paddingRight={3}
paddingBottom={1}
backgroundColor={theme.backgroundElement}
justifyContent="space-between"
justifyContent={narrow() ? "flex-start" : "space-between"}
alignItems={narrow() ? "flex-start" : "center"}
gap={1}
>
<textarea
ref={(val: TextareaRenderable) => (input = val)}
@@ -349,7 +353,7 @@ function RejectPrompt(props: { onConfirm: (message: string) => void; onCancel: (
cursorColor={theme.primary}
keyBindings={textareaKeybindings()}
/>
<box flexDirection="row" gap={2} flexShrink={0} marginLeft={1}>
<box flexDirection="row" gap={2} flexShrink={0}>
<text fg={theme.text}>
enter <span style={{ fg: theme.textMuted }}>confirm</span>
</text>
@@ -379,6 +383,7 @@ function Prompt<const T extends Record<string, string>>(props: {
expanded: false,
})
const diffKey = Keybind.parse("ctrl+f")[0]
const narrow = createMemo(() => dimensions().width < 80)
useKeyboard((evt) => {
if (evt.name === "left" || evt.name == "h") {
@@ -440,7 +445,7 @@ function Prompt<const T extends Record<string, string>>(props: {
{props.body}
</box>
<box
flexDirection="row"
flexDirection={narrow() ? "column" : "row"}
flexShrink={0}
gap={1}
paddingTop={1}
@@ -448,9 +453,10 @@ function Prompt<const T extends Record<string, string>>(props: {
paddingRight={3}
paddingBottom={1}
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}>
{(option) => (
<box
@@ -470,7 +476,7 @@ function Prompt<const T extends Record<string, string>>(props: {
)}
</For>
</box>
<box flexDirection="row" gap={2}>
<box flexDirection="row" gap={2} flexShrink={0}>
<Show when={props.fullscreen}>
<text fg={theme.text}>
{"ctrl+f"} <span style={{ fg: theme.textMuted }}>{hint()}</span>