Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export type PromptProps = {
sessionID?: string
visible?: boolean
disabled?: boolean
maxHeight?: number
onSubmit?: () => void
ref?: (ref: PromptRef) => void
hint?: JSX.Element
Expand Down Expand Up @@ -821,7 +822,7 @@ export function Prompt(props: PromptProps) {
textColor={keybind.leader ? theme.textMuted : theme.text}
focusedTextColor={keybind.leader ? theme.textMuted : theme.text}
minHeight={1}
maxHeight={6}
maxHeight={props.maxHeight ?? 6}
onContentChange={() => {
const value = input.plainText
setStore("prompt", "input", value)
Expand Down
64 changes: 61 additions & 3 deletions packages/opencode/src/cli/cmd/tui/routes/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Prompt, type PromptRef } from "@tui/component/prompt"
import { createMemo, Match, onMount, Show, Switch } from "solid-js"
import { useTheme } from "@tui/context/theme"
import { useKeybind } from "@tui/context/keybind"
import { useTerminalDimensions } from "@opentui/solid"
import { Logo } from "../component/logo"
import { Tips } from "../component/tips"
import { Locale } from "@/util/locale"
Expand All @@ -22,6 +23,7 @@ export function Home() {
const sync = useSync()
const kv = useKV()
const { theme } = useTheme()
const terminal = useTerminalDimensions()
const route = useRouteData("home")
const promptRef = usePromptRef()
const command = useCommandDialog()
Expand Down Expand Up @@ -91,29 +93,85 @@ export function Home() {

const keybind = useKeybind()

const initialPrompt = createMemo(() => sync.data.config.tui?.initial_prompt)
const size = createMemo(() => initialPrompt()?.size ?? "compact")
const width = createMemo(() => {
const value = initialPrompt()?.width_percent
if (value) return Math.max(40, Math.min(100, value))
if (size() === "medium") return 80
if (size() === "large") return 90
return 75
})
const maxWidth = createMemo(() => {
if (size() === "compact" && !initialPrompt()?.width_percent) return 75
const available = Math.max(50, terminal().width - 8)
return Math.max(50, Math.min(available, Math.floor((terminal().width * width()) / 100)))
})
const height = createMemo(() => {
const value = initialPrompt()?.height_percent
if (value) return Math.max(10, Math.min(60, value))
if (size() === "medium") return 25
if (size() === "large") return 35
return 0
})
const maxHeight = createMemo(() => {
if (size() === "compact" && !initialPrompt()?.height_percent) return 6
const available = Math.max(6, terminal().height - 18)
return Math.max(6, Math.min(available, Math.floor((terminal().height * height()) / 100)))
})
const top = createMemo(() => {
if (size() === "medium") return 2
if (size() === "large") return 1
return 4
})
const tipsHeight = createMemo(() => {
if (size() === "medium") return 3
if (size() === "large") return 2
return 4
})
const tipsPadding = createMemo(() => {
if (size() === "compact") return 3
if (size() === "medium") return 2
return 1
})

return (
<>
<box flexGrow={1} alignItems="center" paddingLeft={2} paddingRight={2}>
<box flexGrow={1} minHeight={0} />
<box height={4} minHeight={0} flexShrink={1} />
<box height={top()} minHeight={0} flexShrink={1} />
<box flexShrink={0}>
<Logo />
</box>
<box height={1} minHeight={0} flexShrink={1} />
<box width="100%" maxWidth={75} zIndex={1000} paddingTop={1} flexShrink={0}>
<box width="100%" maxWidth={maxWidth()} zIndex={1000} paddingTop={1} flexShrink={0}>
<Prompt
ref={(r) => {
prompt = r
promptRef.set(r)
}}
hint={Hint}
maxHeight={maxHeight()}
/>
</box>
<box height={4} minHeight={0} width="100%" maxWidth={75} alignItems="center" paddingTop={3} flexShrink={1}>
<box
height={tipsHeight()}
minHeight={0}
width="100%"
maxWidth={maxWidth()}
alignItems="center"
paddingTop={tipsPadding()}
flexShrink={1}
>
<Show when={showTips()}>
<Tips />
</Show>
</box>
<Show when={size() !== "compact"}>
<box width="100%" maxWidth={maxWidth()} alignItems="center" paddingTop={1}>
<text fg={theme.textMuted}>home prompt size: {size()}</text>
</box>
</Show>
<box flexGrow={1} minHeight={0} />
<Toast />
</box>
Expand Down
23 changes: 23 additions & 0 deletions packages/opencode/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,29 @@ export namespace Config {
.enum(["auto", "stacked"])
.optional()
.describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"),
initial_prompt: z
.object({
size: z
.enum(["compact", "medium", "large"])
.optional()
.describe("Preset size for the opening home-screen prompt composer"),
width_percent: z
.number()
.int()
.min(40)
.max(100)
.optional()
.describe("Override opening home-screen prompt width as terminal percentage (40-100)"),
height_percent: z
.number()
.int()
.min(10)
.max(60)
.optional()
.describe("Override opening home-screen prompt max input height as terminal percentage (10-60)"),
})
.optional()
.describe("Opening home-screen prompt sizing preferences"),
})

export const Server = z
Expand Down
72 changes: 72 additions & 0 deletions packages/opencode/test/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,78 @@ test("handles command configuration", async () => {
})
})

test("handles tui initial prompt configuration", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
tui: {
initial_prompt: {
size: "large",
width_percent: 92,
height_percent: 38,
},
},
})
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const config = await Config.get()
expect(config.tui?.initial_prompt).toEqual({
size: "large",
width_percent: 92,
height_percent: 38,
})
},
})
})

test("rejects invalid tui initial prompt size", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
tui: {
initial_prompt: {
size: "huge",
},
},
})
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
await expect(Config.get()).rejects.toThrow()
},
})
})

test("rejects invalid tui initial prompt percentages", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
tui: {
initial_prompt: {
size: "medium",
width_percent: 120,
height_percent: 5,
},
},
})
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
await expect(Config.get()).rejects.toThrow()
},
})
})

test("migrates autoshare to share field", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
Expand Down
17 changes: 17 additions & 0 deletions packages/sdk/js/src/v2/gen/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,23 @@ export type Config = {
* Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column
*/
diff_style?: "auto" | "stacked"
/**
* Opening home-screen prompt sizing preferences
*/
initial_prompt?: {
/**
* Preset size for the opening home-screen prompt composer
*/
size?: "compact" | "medium" | "large"
/**
* Override opening home-screen prompt width as terminal percentage (40-100)
*/
width_percent?: number
/**
* Override opening home-screen prompt max input height as terminal percentage (10-60)
*/
height_percent?: number
}
}
server?: ServerConfig
/**
Expand Down
23 changes: 23 additions & 0 deletions packages/sdk/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -9655,6 +9655,29 @@
"description": "Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column",
"type": "string",
"enum": ["auto", "stacked"]
},
"initial_prompt": {
"description": "Opening home-screen prompt sizing preferences",
"type": "object",
"properties": {
"size": {
"description": "Preset size for the opening home-screen prompt composer",
"type": "string",
"enum": ["compact", "medium", "large"]
},
"width_percent": {
"description": "Override opening home-screen prompt width as terminal percentage (40-100)",
"type": "integer",
"minimum": 40,
"maximum": 100
},
"height_percent": {
"description": "Override opening home-screen prompt max input height as terminal percentage (10-60)",
"type": "integer",
"minimum": 10,
"maximum": 60
}
}
}
}
},
Expand Down
12 changes: 11 additions & 1 deletion packages/web/src/content/docs/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,12 @@ You can configure TUI-specific settings through the `tui` option.
"scroll_acceleration": {
"enabled": true
},
"diff_style": "auto"
"diff_style": "auto",
"initial_prompt": {
"size": "medium",
"width_percent": 80,
"height_percent": 25
}
}
}
```
Expand All @@ -174,6 +179,11 @@ Available options:
- `scroll_acceleration.enabled` - Enable macOS-style scroll acceleration. **Takes precedence over `scroll_speed`.**
- `scroll_speed` - Custom scroll speed multiplier (default: `3`, minimum: `1`). Ignored if `scroll_acceleration.enabled` is `true`.
- `diff_style` - Control diff rendering. `"auto"` adapts to terminal width, `"stacked"` always shows single column.
- `initial_prompt.size` - Preset size for the opening home-screen prompt composer. Values: `"compact"` (current behavior), `"medium"`, `"large"`.
- `initial_prompt.width_percent` - Optional opening-screen width override (percentage of terminal width, `40`-`100`).
- `initial_prompt.height_percent` - Optional opening-screen max input height override (percentage of terminal height, `10`-`60`).

`initial_prompt` only affects the opening home screen prompt. Session prompt behavior is unchanged.

[Learn more about using the TUI here](/docs/tui).

Expand Down
12 changes: 12 additions & 0 deletions packages/web/src/content/docs/tui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,11 @@ You can customize TUI behavior through your OpenCode config file.
"scroll_speed": 3,
"scroll_acceleration": {
"enabled": true
},
"initial_prompt": {
"size": "medium",
"width_percent": 80,
"height_percent": 25
}
}
}
Expand All @@ -373,6 +378,13 @@ You can customize TUI behavior through your OpenCode config file.

- `scroll_acceleration` - Enable macOS-style scroll acceleration for smooth, natural scrolling. When enabled, scroll speed increases with rapid scrolling gestures and stays precise for slower movements. **This setting takes precedence over `scroll_speed` and overrides it when enabled.**
- `scroll_speed` - Controls how fast the TUI scrolls when using scroll commands (minimum: `1`). Defaults to `3`. **Note: This is ignored if `scroll_acceleration.enabled` is set to `true`.**
- `initial_prompt.size` - Preset opening-screen prompt size: `compact`, `medium`, or `large`.
- `initial_prompt.width_percent` - Optional opening-screen width override as percent of terminal width (`40`-`100`).
- `initial_prompt.height_percent` - Optional opening-screen input max-height override as percent of terminal height (`10`-`60`).

`compact` preserves the current default opening prompt behavior.

`initial_prompt` only affects the opening home screen prompt. Session prompt behavior is unchanged.

---

Expand Down
Loading
Loading