From acfeecbb74ed31f21ff08c7a1a03f74d8445ffef Mon Sep 17 00:00:00 2001 From: leohenon <77656081+lhenon999@users.noreply.github.com> Date: Thu, 26 Mar 2026 15:55:52 +0800 Subject: [PATCH 1/3] feat: add scrollbar to prompt input and configurable max height --- .../cli/cmd/tui/component/prompt/index.tsx | 474 ++++++++++-------- packages/opencode/src/config/tui-schema.ts | 7 + 2 files changed, 277 insertions(+), 204 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 6b058eb23d00..e663d3ee09d7 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -53,6 +53,7 @@ import { clearSelection } from "../vim/vim-motions" import { vimScroll } from "../vim/vim-scroll" import { useVimIndicator } from "../vim/vim-indicator" import { emptyRows } from "./empty-selection" +import { useTuiConfig } from "../../context/tui-config" export type PromptProps = { sessionID?: string @@ -129,9 +130,48 @@ export function Prompt(props: PromptProps) { const { theme, syntax } = useTheme() const kv = useKV() const vimEnabled = useVimEnabled() + const tuiConfig = useTuiConfig() const mini = createMemo(() => kv.get("ui_minimal", false)) const list = createMemo(() => props.placeholders?.normal ?? PLACEHOLDERS) const shell = createMemo(() => props.placeholders?.shell ?? SHELL_PLACEHOLDERS) + const maxHeight = createMemo(() => tuiConfig?.prompt_max_height ?? 6) + + // Scrollbar state: array of chars to render in the 1-col gutter + const [scrollbar, setScrollbar] = createSignal(null) + function syncScrollbar() { + setTimeout(() => { + if (!input || input.isDestroyed) return + const total = input.editorView.getTotalVirtualLineCount() + const h = input.height + if (total <= h) { + if (scrollbar() !== null) { + setScrollbar(null) + renderer.requestRender() + } + return + } + // Replicate native slider half-block rendering + const range = total - h + const virtual = h * 2 + const size = Math.max(1, Math.floor(virtual * (h / total))) + const ratio = range > 0 ? input.scrollY / range : 0 + const start = Math.round(ratio * (virtual - size)) + const end = start + size + const chars: string[] = [] + for (let row = 0; row < h; row++) { + const cs = row * 2 + const ce = cs + 2 + const ts = Math.max(start, cs) + const te = Math.min(end, ce) + const cov = te - ts + if (cov >= 2) chars.push("\u2588") + else if (cov > 0) chars.push(ts - cs === 0 ? "\u2580" : "\u2584") + else chars.push(" ") + } + setScrollbar(chars) + renderer.requestRender() + }, 0) + } function promptModelWarning() { toast.show({ @@ -1081,235 +1121,261 @@ export function Prompt(props: PromptProps) { > -