fix(opencode): sets input mode based on whether mouse vs keyboard is in use to prevent mouse events firing (#9449)

This commit is contained in:
Joseph Campuzano
2026-01-19 14:46:17 -06:00
committed by GitHub
parent d19e76d96c
commit 091e88c1e1
2 changed files with 43 additions and 1 deletions

View File

@@ -85,6 +85,7 @@ export function Autocomplete(props: {
index: 0,
selected: 0,
visible: false as AutocompleteRef["visible"],
input: "keyboard" as "keyboard" | "mouse",
})
const [positionTick, setPositionTick] = createSignal(0)
@@ -128,6 +129,14 @@ export function Autocomplete(props: {
return props.input().getTextRange(store.index + 1, props.input().cursorOffset)
})
// When the filter changes due to how TUI works, the mousemove might still be triggered
// via a synthetic event as the layout moves underneath the cursor. This is a workaround to make sure the input mode remains keyboard so
// that the mouseover event doesn't trigger when filtering.
createEffect(() => {
filter();
setStore("input", "keyboard")
})
function insertPart(text: string, part: PromptInfo["parts"][number]) {
const input = props.input()
const currentCursorOffset = input.cursorOffset
@@ -525,11 +534,13 @@ export function Autocomplete(props: {
const isNavDown = name === "down" || (ctrlOnly && name === "n")
if (isNavUp) {
setStore("input", "keyboard")
move(-1)
e.preventDefault()
return
}
if (isNavDown) {
setStore("input", "keyboard")
move(1)
e.preventDefault()
return
@@ -612,7 +623,17 @@ export function Autocomplete(props: {
paddingRight={1}
backgroundColor={index === store.selected ? theme.primary : undefined}
flexDirection="row"
onMouseOver={() => moveTo(index)}
onMouseMove={() => {
setStore("input", "mouse")
}}
onMouseOver={() => {
if (store.input !== "mouse") return
moveTo(index)
}}
onMouseDown={() => {
setStore("input", "mouse")
moveTo(index)
}}
onMouseUp={() => select()}
>
<text fg={index === store.selected ? selectedForeground(theme) : theme.text} flexShrink={0}>

View File

@@ -52,6 +52,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
const [store, setStore] = createStore({
selected: 0,
filter: "",
input: "keyboard" as "keyboard" | "mouse",
})
createEffect(
@@ -83,6 +84,14 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
return result
})
// When the filter changes due to how TUI works, the mousemove might still be triggered
// via a synthetic event as the layout moves underneath the cursor. This is a workaround to make sure the input mode remains keyboard
// that the mouseover event doesn't trigger when filtering.
createEffect(() => {
filtered();
setStore("input", "keyboard")
})
const grouped = createMemo(() => {
const result = pipe(
filtered(),
@@ -157,12 +166,15 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
const keybind = useKeybind()
useKeyboard((evt) => {
setStore("input", "keyboard")
if (evt.name === "up" || (evt.ctrl && evt.name === "p")) move(-1)
if (evt.name === "down" || (evt.ctrl && evt.name === "n")) move(1)
if (evt.name === "pageup") move(-10)
if (evt.name === "pagedown") move(10)
if (evt.name === "home") moveTo(0)
if (evt.name === "end") moveTo(flat().length - 1)
if (evt.name === "return") {
const option = selected()
if (option) {
@@ -259,11 +271,20 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
<box
id={JSON.stringify(option.value)}
flexDirection="row"
onMouseMove={() => {
setStore("input", "mouse")
}}
onMouseUp={() => {
option.onSelect?.(dialog)
props.onSelect?.(option)
}}
onMouseOver={() => {
if (store.input !== "mouse") return
const index = flat().findIndex((x) => isDeepEqual(x.value, option.value))
if (index === -1) return
moveTo(index)
}}
onMouseDown={() => {
const index = flat().findIndex((x) => isDeepEqual(x.value, option.value))
if (index === -1) return
moveTo(index)