tui(run): use keymap instead of raw key events (#30077)

Co-authored-by: Sebastian Herrlinger <hasta84@gmail.com>
This commit is contained in:
Simon Klee
2026-05-31 10:58:12 +02:00
committed by GitHub
parent 151bc7ac86
commit 628148c828
19 changed files with 760 additions and 891 deletions
@@ -9,7 +9,9 @@
// Also wires SIGINT so Ctrl-c clears a live prompt draft first, then falls
// back to the usual two-press exit sequence through RunFooter.requestExit().
import { createCliRenderer, type CliRenderer, type ScrollbackWriter } from "@opentui/core"
import { createDefaultOpenTuiKeymap } from "@opentui/keymap/opentui"
import { Session as SessionApi } from "@/session/session"
import { registerOpencodeKeymap } from "@/cli/cmd/tui/keymap"
import * as Locale from "@/util/locale"
import { withRunSpan } from "./otel"
import { resolveInteractiveStdin } from "./runtime.stdin"
@@ -17,15 +19,14 @@ import { entrySplash, exitSplash, splashMeta } from "./splash"
import { resolveRunTheme } from "./theme"
import type {
FooterApi,
FooterKeybinds,
PermissionReply,
QuestionReject,
QuestionReply,
RunAgent,
RunDiffStyle,
RunInput,
RunPrompt,
RunResource,
RunTuiConfig,
} from "./types"
import { formatModelLabel } from "./variant.shared"
@@ -61,8 +62,7 @@ export type LifecycleInput = {
agent: string | undefined
model: RunInput["model"]
variant: string | undefined
keybinds: FooterKeybinds
diffStyle: RunDiffStyle
tuiConfig: RunTuiConfig
onPermissionReply: (input: PermissionReply) => void | Promise<void>
onQuestionReply: (input: QuestionReply) => void | Promise<void>
onQuestionReject: (input: QuestionReject) => void | Promise<void>
@@ -169,6 +169,7 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lif
},
async () => {
const source = resolveInteractiveStdin()
let unregisterKeymap: (() => void) | undefined
try {
const renderer = await createCliRenderer({
@@ -188,6 +189,8 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lif
})
const theme = await resolveRunTheme(renderer)
renderer.setBackgroundColor(theme.background)
const keymap = createDefaultOpenTuiKeymap(renderer)
unregisterKeymap = registerOpencodeKeymap(keymap, renderer, input.tuiConfig)
const state: SplashState = {
entry: false,
exit: false,
@@ -230,8 +233,9 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lif
history: input.history,
theme,
wrote,
keybinds: input.keybinds,
diffStyle: input.diffStyle,
keymap,
tuiConfig: input.tuiConfig,
diffStyle: input.tuiConfig.diff_style ?? "auto",
onPermissionReply: input.onPermissionReply,
onQuestionReply: input.onQuestionReply,
onQuestionReject: input.onQuestionReject,
@@ -293,6 +297,7 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lif
footer.close()
await footer.idle().catch(() => {})
footer.destroy()
unregisterKeymap?.()
shutdown(renderer)
source.cleanup?.()
}
@@ -305,6 +310,7 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lif
close,
}
} catch (error) {
unregisterKeymap?.()
source.cleanup?.()
throw error
}