Просмотр исходного кода

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

Joseph Campuzano 1 месяц назад
Родитель
Сommit
091e88c1e1

+ 22 - 1
packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx

@@ -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}>

+ 21 - 0
packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx

@@ -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)