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 caa13032292f..79698901e972 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
@@ -713,6 +713,14 @@ export function Prompt(props: PromptProps) {
return !!current
})
+ const cursorStyle = createMemo(() => {
+ const tui = sync.data.config.tui
+ return {
+ style: (tui?.cursor_style ?? "block") as "block" | "line" | "underline",
+ blinking: tui?.cursor_blink ?? true,
+ }
+ })
+
const spinnerDef = createMemo(() => {
const color = local.agent.color(local.agent.current().name)
return {
@@ -948,6 +956,7 @@ export function Prompt(props: PromptProps) {
onMouseDown={(r: MouseEvent) => r.target?.focus()}
focusedBackgroundColor={theme.backgroundElement}
cursorColor={theme.text}
+ cursorStyle={cursorStyle()}
syntaxStyle={syntax()}
/>
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx
index 9e79c76bf518..43dfc4461c8c 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx
@@ -306,6 +306,15 @@ function RejectPrompt(props: { onConfirm: (message: string) => void; onCancel: (
const dimensions = useTerminalDimensions()
const narrow = createMemo(() => dimensions().width < 80)
const dialog = useDialog()
+ const sync = useSync()
+
+ const cursorStyle = createMemo(() => {
+ const tui = sync.data.config.tui
+ return {
+ style: (tui?.cursor_style ?? "block") as "block" | "line" | "underline",
+ blinking: tui?.cursor_blink ?? true,
+ }
+ })
useKeyboard((evt) => {
if (dialog.stack.length > 0) return
@@ -355,6 +364,7 @@ function RejectPrompt(props: { onConfirm: (message: string) => void; onCancel: (
textColor={theme.text}
focusedTextColor={theme.text}
cursorColor={theme.primary}
+ cursorStyle={cursorStyle()}
keyBindings={textareaKeybindings()}
/>
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx
index 88e99c6ea8b3..943084e6caed 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx
@@ -4,6 +4,7 @@ import { useKeyboard } from "@opentui/solid"
import type { TextareaRenderable } from "@opentui/core"
import { useKeybind } from "../../context/keybind"
import { selectedForeground, tint, useTheme } from "../../context/theme"
+import { useSync } from "../../context/sync"
import type { QuestionAnswer, QuestionRequest } from "@opencode-ai/sdk/v2"
import { useSDK } from "../../context/sdk"
import { SplitBorder } from "../../component/border"
@@ -15,6 +16,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
const { theme } = useTheme()
const keybind = useKeybind()
const bindings = useTextareaKeybindings()
+ const sync = useSync()
const questions = createMemo(() => props.request.questions)
const single = createMemo(() => questions().length === 1 && questions()[0]?.multiple !== true)
@@ -41,6 +43,13 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
if (!value) return false
return store.answers[store.tab]?.includes(value) ?? false
})
+ const cursorStyle = createMemo(() => {
+ const tui = sync.data.config.tui
+ return {
+ style: (tui?.cursor_style ?? "block") as "block" | "line" | "underline",
+ blinking: tui?.cursor_blink ?? true,
+ }
+ })
function submit() {
const answers = questions().map((_, i) => store.answers[i] ?? [])
@@ -379,6 +388,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
textColor={theme.text}
focusedTextColor={theme.text}
cursorColor={theme.primary}
+ cursorStyle={cursorStyle()}
keyBindings={bindings()}
/>
diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-export-options.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-export-options.tsx
index 867ed6810028..6d257b31239a 100644
--- a/packages/opencode/src/cli/cmd/tui/ui/dialog-export-options.tsx
+++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-export-options.tsx
@@ -2,8 +2,9 @@ import { TextareaRenderable, TextAttributes } from "@opentui/core"
import { useTheme } from "../context/theme"
import { useDialog, type DialogContext } from "./dialog"
import { createStore } from "solid-js/store"
-import { onMount, Show, type JSX } from "solid-js"
+import { createMemo, onMount, Show, type JSX } from "solid-js"
import { useKeyboard } from "@opentui/solid"
+import { useSync } from "../context/sync"
export type DialogExportOptionsProps = {
defaultFilename: string
@@ -24,7 +25,17 @@ export type DialogExportOptionsProps = {
export function DialogExportOptions(props: DialogExportOptionsProps) {
const dialog = useDialog()
const { theme } = useTheme()
+ const sync = useSync()
let textarea: TextareaRenderable
+
+ const cursorStyle = createMemo(() => {
+ const tui = sync.data.config.tui
+ return {
+ style: (tui?.cursor_style ?? "block") as "block" | "line" | "underline",
+ blinking: tui?.cursor_blink ?? true,
+ }
+ })
+
const [store, setStore] = createStore({
thinking: props.defaultThinking,
toolDetails: props.defaultToolDetails,
@@ -104,6 +115,7 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
textColor={theme.text}
focusedTextColor={theme.text}
cursorColor={theme.text}
+ cursorStyle={cursorStyle()}
/>
diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx
index b29652412430..9893b86935ca 100644
--- a/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx
+++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx
@@ -1,8 +1,9 @@
import { TextareaRenderable, TextAttributes } from "@opentui/core"
import { useTheme } from "../context/theme"
import { useDialog, type DialogContext } from "./dialog"
-import { onMount, type JSX } from "solid-js"
+import { createMemo, onMount, type JSX } from "solid-js"
import { useKeyboard } from "@opentui/solid"
+import { useSync } from "../context/sync"
export type DialogPromptProps = {
title: string
@@ -16,8 +17,17 @@ export type DialogPromptProps = {
export function DialogPrompt(props: DialogPromptProps) {
const dialog = useDialog()
const { theme } = useTheme()
+ const sync = useSync()
let textarea: TextareaRenderable
+ const cursorStyle = createMemo(() => {
+ const tui = sync.data.config.tui
+ return {
+ style: (tui?.cursor_style ?? "block") as "block" | "line" | "underline",
+ blinking: tui?.cursor_blink ?? true,
+ }
+ })
+
useKeyboard((evt) => {
if (evt.name === "return") {
props.onConfirm?.(textarea.plainText)
@@ -55,6 +65,7 @@ export function DialogPrompt(props: DialogPromptProps) {
textColor={theme.text}
focusedTextColor={theme.text}
cursorColor={theme.text}
+ cursorStyle={cursorStyle()}
/>
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index 7969e3079574..9ac79393ce8f 100644
--- a/packages/opencode/src/config/config.ts
+++ b/packages/opencode/src/config/config.ts
@@ -829,6 +829,11 @@ export namespace Config {
ref: "KeybindsConfig",
})
+ export const CursorStyle = z.enum(["block", "line", "underline"]).meta({
+ ref: "CursorStyleConfig",
+ })
+ export type CursorStyle = z.infer
+
export const TUI = z.object({
scroll_speed: z.number().min(0.001).optional().describe("TUI scroll speed"),
scroll_acceleration: z
@@ -841,6 +846,8 @@ export namespace Config {
.enum(["auto", "stacked"])
.optional()
.describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"),
+ cursor_style: CursorStyle.optional().describe("Cursor shape in text inputs: 'block', 'line', or 'underline'"),
+ cursor_blink: z.boolean().optional().describe("Enable cursor blinking in text inputs"),
})
export const Server = z
diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts
index cb2f586775a1..8af4b6139733 100644
--- a/packages/sdk/js/src/v2/gen/types.gen.ts
+++ b/packages/sdk/js/src/v2/gen/types.gen.ts
@@ -1315,6 +1315,11 @@ export type KeybindsConfig = {
*/
export type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR"
+/**
+ * Cursor shape in text inputs: 'block', 'line', or 'underline'
+ */
+export type CursorStyleConfig = "block" | "line" | "underline"
+
/**
* Server configuration for opencode serve and web commands
*/
@@ -1620,6 +1625,11 @@ export type Config = {
* Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column
*/
diff_style?: "auto" | "stacked"
+ cursor_style?: CursorStyleConfig
+ /**
+ * Enable cursor blinking in text inputs
+ */
+ cursor_blink?: boolean
}
server?: ServerConfig
/**