Skip to content

Commit 0a255e2

Browse files
authored
fix race on HUD after a command (#29190)
1 parent 984fdf4 commit 0a255e2

1 file changed

Lines changed: 16 additions & 6 deletions

File tree

  • shared/chat/conversation/input-area/suggestors

shared/chat/conversation/input-area/suggestors/index.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,18 @@ const useSyncInput = (p: UseSyncInputProps) => {
7878
setFilter('')
7979
}, [setActive, setFilter])
8080

81+
// activeRef always holds the latest value of active so the debounced checkTrigger
82+
// timeout reads the current state rather than a stale closure. Without this, rapid
83+
// typing after a slash command can cancel the checkTrigger that would reset active,
84+
// leaving active='commands' when the timeout finally fires over new text.
85+
const activeRef = React.useRef(active)
86+
React.useLayoutEffect(() => {
87+
activeRef.current = active
88+
}, [active])
89+
8190
const getWordAtCursor = React.useCallback(() => {
8291
if (inputRef.current) {
83-
const useSpaces = active === 'commands'
92+
const useSpaces = activeRef.current === 'commands'
8493
const input = inputRef.current
8594
const selection = input.getSelection()
8695
const text = lastTextRef.current
@@ -114,7 +123,7 @@ const useSyncInput = (p: UseSyncInputProps) => {
114123
return {position, word}
115124
}
116125
return null
117-
}, [inputRef, active, lastTextRef])
126+
}, [inputRef, activeRef, lastTextRef])
118127

119128
const triggerIDRef = React.useRef<NodeJS.Timeout>(undefined)
120129
const checkTrigger = React.useCallback(() => {
@@ -134,8 +143,8 @@ const useSyncInput = (p: UseSyncInputProps) => {
134143
setInactive()
135144
return
136145
}
137-
if (active) {
138-
const activeMarker = suggestorToMarker[active]
146+
if (activeRef.current) {
147+
const activeMarker = suggestorToMarker[activeRef.current]
139148
const matchInfo = matchesMarker(word, activeMarker)
140149
if (!matchInfo.matches) {
141150
// not active anymore
@@ -155,7 +164,7 @@ const useSyncInput = (p: UseSyncInputProps) => {
155164
}
156165
}
157166
}, 1)
158-
}, [getWordAtCursor, triggerIDRef, setActive, setFilter, setInactive, active, inputRef])
167+
}, [getWordAtCursor, triggerIDRef, setActive, setFilter, setInactive, activeRef, inputRef])
159168

160169
React.useEffect(() => {
161170
return () => {
@@ -165,6 +174,7 @@ const useSyncInput = (p: UseSyncInputProps) => {
165174

166175
const triggerTransform = React.useCallback(
167176
function (maybeValue: SelectedType | undefined, final = true) {
177+
const active = activeRef.current
168178
if (!inputRef.current || !active) {
169179
return
170180
}
@@ -208,7 +218,7 @@ const useSyncInput = (p: UseSyncInputProps) => {
208218
setLastText(transformedText.text)
209219
input.transformText(() => transformedText, final)
210220
},
211-
[active, inputRef, getWordAtCursor, selectedItemRef, setLastText, lastTextRef]
221+
[activeRef, inputRef, getWordAtCursor, selectedItemRef, setLastText, lastTextRef]
212222
)
213223

214224
return {

0 commit comments

Comments
 (0)