From 41ea4694db7636ba184d238fd2a00deb770f9c0b Mon Sep 17 00:00:00 2001 From: Sebastian Herrlinger Date: Thu, 29 Jan 2026 00:17:34 -0500 Subject: [PATCH] more timeout race guards --- .../opencode/src/cli/cmd/tui/component/prompt/index.tsx | 7 +++++++ packages/opencode/src/cli/cmd/tui/context/keybind.tsx | 5 ++--- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 3 ++- .../opencode/src/cli/cmd/tui/ui/dialog-export-options.tsx | 1 + packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx | 1 + 5 files changed, 13 insertions(+), 4 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 e19c8b709..caa130322 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -93,8 +93,11 @@ export function Prompt(props: PromptProps) { let promptPartTypeId = 0 sdk.event.on(TuiEvent.PromptAppend.type, (evt) => { + if (!input || input.isDestroyed) return input.insertText(evt.properties.text) setTimeout(() => { + // setTimeout is a workaround and needs to be addressed properly + if (!input || input.isDestroyed) return input.getLayoutNode().markDirty() input.gotoBufferEnd() renderer.requestRender() @@ -924,6 +927,8 @@ export function Prompt(props: PromptProps) { // Force layout update and render for the pasted content setTimeout(() => { + // setTimeout is a workaround and needs to be addressed properly + if (!input || input.isDestroyed) return input.getLayoutNode().markDirty() renderer.requestRender() }, 0) @@ -935,6 +940,8 @@ export function Prompt(props: PromptProps) { } props.ref?.(ref) setTimeout(() => { + // setTimeout is a workaround and needs to be addressed properly + if (!input || input.isDestroyed) return input.cursorColor = theme.text }, 0) }} diff --git a/packages/opencode/src/cli/cmd/tui/context/keybind.tsx b/packages/opencode/src/cli/cmd/tui/context/keybind.tsx index 4c82e594c..0dbbbc6f9 100644 --- a/packages/opencode/src/cli/cmd/tui/context/keybind.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/keybind.tsx @@ -34,9 +34,8 @@ export const { use: useKeybind, provider: KeybindProvider } = createSimpleContex timeout = setTimeout(() => { if (!store.leader) return leader(false) - if (focus) { - focus.focus() - } + if (!focus || focus.isDestroyed) return + focus.focus() }, 2000) return } diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 66dac85ec..d36a7d209 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -275,7 +275,8 @@ export function Session() { function toBottom() { setTimeout(() => { - if (scroll) scroll.scrollTo(scroll.scrollHeight) + if (!scroll || scroll.isDestroyed) return + scroll.scrollTo(scroll.scrollHeight) }, 50) } 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 90699e1f0..867ed6810 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 @@ -68,6 +68,7 @@ export function DialogExportOptions(props: DialogExportOptionsProps) { onMount(() => { dialog.setSize("medium") setTimeout(() => { + if (!textarea || textarea.isDestroyed) return textarea.focus() }, 1) textarea.gotoLineEnd() 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 1b9acb589..b29652412 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx @@ -27,6 +27,7 @@ export function DialogPrompt(props: DialogPromptProps) { onMount(() => { dialog.setSize("medium") setTimeout(() => { + if (!textarea || textarea.isDestroyed) return textarea.focus() }, 1) textarea.gotoLineEnd()