@miphamai/cli 0.32.4 → 0.32.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/ui/input.tsx +31 -3
package/package.json
CHANGED
package/src/ui/input.tsx
CHANGED
|
@@ -79,6 +79,11 @@ export function InputBar({
|
|
|
79
79
|
}: InputBarProps) {
|
|
80
80
|
const { t } = useI18n()
|
|
81
81
|
const [value, setValue] = useState('')
|
|
82
|
+
// Ref mirror of value — used by useInput to read latest without stale closure.
|
|
83
|
+
// Also used by throttled onChange to hold the latest pending value.
|
|
84
|
+
const valueRef = useRef(value)
|
|
85
|
+
// Throttle timer for onChange — prevents React render floods during paste.
|
|
86
|
+
const onChangeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
82
87
|
const [verb, setVerb] = useState(() => t(pick(LOADING_KEYS)))
|
|
83
88
|
const [completionVerb, setCompletionVerb] = useState<string | null>(null)
|
|
84
89
|
const prevLoading = useRef(isLoading)
|
|
@@ -330,14 +335,27 @@ export function InputBar({
|
|
|
330
335
|
prevValueRef.current = value
|
|
331
336
|
}, [value, vimMode])
|
|
332
337
|
|
|
338
|
+
// Keep valueRef in sync with state (so useInput handlers read latest value)
|
|
339
|
+
useEffect(() => {
|
|
340
|
+
valueRef.current = value
|
|
341
|
+
}, [value])
|
|
342
|
+
|
|
333
343
|
const handleSubmit = (val: string) => {
|
|
334
|
-
|
|
344
|
+
// Flush any pending throttled onChange before submitting
|
|
345
|
+
if (onChangeTimerRef.current) {
|
|
346
|
+
clearTimeout(onChangeTimerRef.current)
|
|
347
|
+
onChangeTimerRef.current = null
|
|
348
|
+
}
|
|
349
|
+
// Use latest value from ref (may be ahead of state during throttle)
|
|
350
|
+
const finalValue = val || valueRef.current
|
|
351
|
+
if (!finalValue.trim() || isLoading) return
|
|
335
352
|
// Save to message history for arrow-key navigation
|
|
336
|
-
setSubmittedHistory((prev) => [...prev,
|
|
353
|
+
setSubmittedHistory((prev) => [...prev, finalValue])
|
|
337
354
|
historyIndexRef.current = -1
|
|
338
355
|
savedDraftRef.current = ''
|
|
339
|
-
onSubmit(
|
|
356
|
+
onSubmit(finalValue)
|
|
340
357
|
setValue('')
|
|
358
|
+
valueRef.current = ''
|
|
341
359
|
setPickerActive(false)
|
|
342
360
|
}
|
|
343
361
|
|
|
@@ -380,7 +398,17 @@ export function InputBar({
|
|
|
380
398
|
historyIndexRef.current = -1
|
|
381
399
|
savedDraftRef.current = ''
|
|
382
400
|
}
|
|
401
|
+
// Throttle: first keystroke renders immediately, then batch at ~30fps.
|
|
402
|
+
// During paste, the terminal may send hundreds of characters in rapid
|
|
403
|
+
// succession — without throttling, each triggers a React render+Ink write
|
|
404
|
+
// cycle that starves the event loop and freezes the UI.
|
|
405
|
+
valueRef.current = val
|
|
406
|
+
if (onChangeTimerRef.current) return // timer pending, latest value in ref
|
|
383
407
|
setValue(val)
|
|
408
|
+
onChangeTimerRef.current = setTimeout(() => {
|
|
409
|
+
onChangeTimerRef.current = null
|
|
410
|
+
setValue(valueRef.current)
|
|
411
|
+
}, 33) // ~30fps
|
|
384
412
|
}}
|
|
385
413
|
onSubmit={handleSubmit}
|
|
386
414
|
placeholder={
|