From 332dc2acf3c462fd8c03437047c4b15b774b349e Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Thu, 13 Feb 2025 12:50:06 +0100 Subject: [PATCH 1/7] wip: preventing too frequent double-pastes --- src/components/ChatForm/ChatForm.tsx | 1 + src/components/ChatForm/useInputValue.ts | 18 +++-- src/components/ComboBox/ComboBox.test.tsx | 23 ++++--- src/components/ComboBox/ComboBox.tsx | 84 ++++++++++++++++++++++- 4 files changed, 109 insertions(+), 17 deletions(-) diff --git a/src/components/ChatForm/ChatForm.tsx b/src/components/ChatForm/ChatForm.tsx index 7c465961..8d4e842d 100644 --- a/src/components/ChatForm/ChatForm.tsx +++ b/src/components/ChatForm/ChatForm.tsx @@ -210,6 +210,7 @@ export const ChatForm: React.FC = ({ const handleChange = useCallback( (command: string) => { + console.log(`[DEBUG]: handleChange`); setValue(command); const trimmedCommand = command.trim(); setFileInteracted(!!trimmedCommand); diff --git a/src/components/ChatForm/useInputValue.ts b/src/components/ChatForm/useInputValue.ts index ba2d670a..03b563af 100644 --- a/src/components/ChatForm/useInputValue.ts +++ b/src/components/ChatForm/useInputValue.ts @@ -6,7 +6,7 @@ import { } from "../../hooks"; import { selectPages, change, ChatPage } from "../../features/Pages/pagesSlice"; import { setInputValue, addInputValue } from "./actions"; -import { debugRefact } from "../../debugConfig"; +// import { console.log } from "../../debugConfig"; export function useInputValue( uncheckCheckboxes: () => void, @@ -34,11 +34,14 @@ export function useInputValue( (event: MessageEvent) => { if (addInputValue.match(event.data) || setInputValue.match(event.data)) { const { payload } = event.data; - debugRefact(`[DEBUG]: receiving event setInputValue/addInputValue`); + console.log( + `[DEBUG]: receiving event setInputValue/addInputValue with payload:`, + payload, + ); setUpIfNotReady(); if (payload.messages) { - debugRefact(`[DEBUG]: payload messages: `, payload.messages); + console.log(`[DEBUG]: payload messages: `, payload.messages); setIsSendImmediately(true); submit({ maybeMessages: payload.messages, @@ -49,18 +52,23 @@ export function useInputValue( if (addInputValue.match(event.data)) { const { payload } = event.data; + console.log(`[DEBUG]: addInputValue triggered with:`, payload); const { send_immediately, value } = payload; - setValue((prev) => prev + value); + setValue((prev) => { + console.log(`[DEBUG]: Previous value: "${prev}", Adding: "${value}"`); + return prev + value; + }); setIsSendImmediately(send_immediately); return; } if (setInputValue.match(event.data)) { const { payload } = event.data; + console.log(`[DEBUG]: setInputValue triggered with:`, payload); const { send_immediately, value } = payload; uncheckCheckboxes(); setValue(value ?? ""); - debugRefact(`[DEBUG]: setInputValue.payload: `, payload); + console.log(`[DEBUG]: setInputValue.payload: `, payload); setIsSendImmediately(send_immediately); return; } diff --git a/src/components/ComboBox/ComboBox.test.tsx b/src/components/ComboBox/ComboBox.test.tsx index 3f0edac5..7e191b4f 100644 --- a/src/components/ComboBox/ComboBox.test.tsx +++ b/src/components/ComboBox/ComboBox.test.tsx @@ -465,13 +465,18 @@ describe("ComboBox", () => { expect(onSubmitSpy).not.toHaveBeenCalled(); }); - // test("textarea should be empty after submit", async () => { - // const submitSpy = vi.fn(); - // const { user, ...app } = render(); - // const textarea = app.getByRole("combobox") as HTMLTextAreaElement; - // await user.type(textarea, "hello"); - // await user.keyboard("{Enter}"); - // expect(submitSpy).toHaveBeenCalled(); - // expect(textarea.textContent).toEqual(""); - // }); + test("paste path after @file command should show only one path", async () => { + const { user, ...app } = render(); + const textarea = app.getByRole("combobox"); + await user.type(textarea, "@file "); + expect(textarea.textContent).toEqual("@file "); + + // Simulate pasting a path + await user.paste("/custom/path/to/file.txt"); + + // Should show only the pasted path, not suggestions + expect(textarea.textContent).toEqual("@file /custom/path/to/file.txt"); + expect(app.queryByText("/foo")).toBeNull(); + expect(app.queryByText("/bar")).toBeNull(); + }); }); diff --git a/src/components/ComboBox/ComboBox.tsx b/src/components/ComboBox/ComboBox.tsx index c4638bef..ba38d513 100644 --- a/src/components/ComboBox/ComboBox.tsx +++ b/src/components/ComboBox/ComboBox.tsx @@ -36,6 +36,8 @@ export const ComboBox: React.FC = ({ }) => { const ref = React.useRef(null); const [moveCursorTo, setMoveCursorTo] = React.useState(null); + const [lastPasteTimestamp, setLastPasteTimestamp] = React.useState(0); + const [lastValue, setLastValue] = React.useState(""); const shiftEnterToSubmit = useAppSelector(selectSubmitOption); const { escapeKeyPressed } = useEventsBusForIDE(); @@ -89,6 +91,19 @@ export const ComboBox: React.FC = ({ const handleReplace = useCallback( (input: string) => { if (!ref.current) return; + console.log("[DEBUG] handleReplace called with:", { + input, + currentValue: ref.current.value, + replaceRange: commands.replace, + timeSinceLastPaste: Date.now() - lastPasteTimestamp, + }); + + // If this is happening right after a paste, skip it + if (Date.now() - lastPasteTimestamp < 100) { + console.log("[DEBUG] Skipping handleReplace due to recent paste"); + return; + } + const nextValue = replaceRange( ref.current.value, commands.replace, @@ -99,7 +114,13 @@ export const ComboBox: React.FC = ({ onChange(nextValue); setMoveCursorTo(commands.replace[0] + input.length); }, - [closeCombobox, commands.replace, onChange, requestCommandsCompletion], + [ + closeCombobox, + commands.replace, + onChange, + requestCommandsCompletion, + lastPasteTimestamp, + ], ); const onKeyDown = useCallback( @@ -198,9 +219,65 @@ export const ComboBox: React.FC = ({ const handleChange = useCallback( (event: React.ChangeEvent) => { + const now = Date.now(); + console.log( + "[DEBUG] handleChange called with value:", + event.target.value, + ); + console.log("[DEBUG] previous value was:", lastValue); + console.log( + "[DEBUG] time since last paste:", + now - lastPasteTimestamp, + "ms", + ); + console.log("[DEBUG] combobox state:", { + open: state.open, + activeValue: state.activeValue, + activeId: state.activeId, + replace: commands.replace, + }); + const isPaste = event.target.value.length > value.length + 1; + + if (isPaste) { + if (now - lastPasteTimestamp < 100) { + console.log("[DEBUG] Skipping duplicate paste event"); + return; + } + console.log( + "[DEBUG] paste detected, closing combobox and canceling completion", + ); + setLastPasteTimestamp(now); + closeCombobox(); + requestCommandsCompletion.cancel(); + } + + setLastValue(event.target.value); onChange(event.target.value); }, - [onChange], + [ + onChange, + closeCombobox, + state, + commands.replace, + value.length, + requestCommandsCompletion, + lastPasteTimestamp, + lastValue, + ], + ); + + // Add paste event handler + const handlePaste = useCallback( + (_event: React.ClipboardEvent) => { + console.log("[DEBUG] handlePaste event fired"); + setLastPasteTimestamp(Date.now()); + if (state.open) { + console.log("[DEBUG] paste event detected, closing combobox"); + closeCombobox(); + requestCommandsCompletion.cancel(); + } + }, + [closeCombobox, state.open, requestCommandsCompletion], ); const onItemClick = useCallback( @@ -240,7 +317,7 @@ export const ComboBox: React.FC = ({ showOnChange={false} showOnKeyDown={false} showOnMouseDown={false} - setValueOnChange={true} + setValueOnChange={false} render={render({ ref, placeholder, @@ -250,6 +327,7 @@ export const ComboBox: React.FC = ({ onKeyUp: onKeyUp, onKeyDown: onKeyDown, onSubmit: onSubmit, + onPaste: handlePaste, })} /> From 910d4bdc99c29a14e7655cc67cac38f71ce9e0ea Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Thu, 13 Feb 2025 13:37:07 +0100 Subject: [PATCH 2/7] wip: fixed tests of undo/redo & lowercase take of event.key for redo handling --- src/components/ChatForm/useInputValue.ts | 14 +++++----- src/components/ComboBox/ComboBox.test.tsx | 31 +++++++++-------------- src/components/TextArea/TextArea.tsx | 6 +++-- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/components/ChatForm/useInputValue.ts b/src/components/ChatForm/useInputValue.ts index 03b563af..e55ad46b 100644 --- a/src/components/ChatForm/useInputValue.ts +++ b/src/components/ChatForm/useInputValue.ts @@ -6,7 +6,7 @@ import { } from "../../hooks"; import { selectPages, change, ChatPage } from "../../features/Pages/pagesSlice"; import { setInputValue, addInputValue } from "./actions"; -// import { console.log } from "../../debugConfig"; +import { debugRefact } from "../../debugConfig"; export function useInputValue( uncheckCheckboxes: () => void, @@ -34,14 +34,14 @@ export function useInputValue( (event: MessageEvent) => { if (addInputValue.match(event.data) || setInputValue.match(event.data)) { const { payload } = event.data; - console.log( + debugRefact( `[DEBUG]: receiving event setInputValue/addInputValue with payload:`, payload, ); setUpIfNotReady(); if (payload.messages) { - console.log(`[DEBUG]: payload messages: `, payload.messages); + debugRefact(`[DEBUG]: payload messages: `, payload.messages); setIsSendImmediately(true); submit({ maybeMessages: payload.messages, @@ -52,10 +52,10 @@ export function useInputValue( if (addInputValue.match(event.data)) { const { payload } = event.data; - console.log(`[DEBUG]: addInputValue triggered with:`, payload); + debugRefact(`[DEBUG]: addInputValue triggered with:`, payload); const { send_immediately, value } = payload; setValue((prev) => { - console.log(`[DEBUG]: Previous value: "${prev}", Adding: "${value}"`); + debugRefact(`[DEBUG]: Previous value: "${prev}", Adding: "${value}"`); return prev + value; }); setIsSendImmediately(send_immediately); @@ -64,11 +64,11 @@ export function useInputValue( if (setInputValue.match(event.data)) { const { payload } = event.data; - console.log(`[DEBUG]: setInputValue triggered with:`, payload); + debugRefact(`[DEBUG]: setInputValue triggered with:`, payload); const { send_immediately, value } = payload; uncheckCheckboxes(); setValue(value ?? ""); - console.log(`[DEBUG]: setInputValue.payload: `, payload); + debugRefact(`[DEBUG]: setInputValue.payload: `, payload); setIsSendImmediately(send_immediately); return; } diff --git a/src/components/ComboBox/ComboBox.test.tsx b/src/components/ComboBox/ComboBox.test.tsx index 7e191b4f..fef74e63 100644 --- a/src/components/ComboBox/ComboBox.test.tsx +++ b/src/components/ComboBox/ComboBox.test.tsx @@ -377,6 +377,8 @@ describe("ComboBox", () => { await user.keyboard("{z}"); expect(textarea.textContent).toEqual("@file "); + await pause(100); // required, because of cancelling on frequent paste + await user.keyboard("{z}{/Meta}{/Shift}"); expect(textarea.textContent).toEqual("@file /foo "); }); @@ -387,23 +389,29 @@ describe("ComboBox", () => { await user.type(textarea, "@"); await user.keyboard("{Enter}"); - await pause(50); + await pause(150); await user.keyboard("{Enter}"); expect(textarea.textContent).toEqual("@file /foo "); + await user.keyboard("{Control>}{z}"); expect(textarea.textContent).toEqual("@file "); + await user.keyboard("{z}"); expect(textarea.textContent).toEqual("@"); + await user.keyboard("{z}{/Control}"); expect(textarea.textContent).toEqual(""); - await user.keyboard("{Shift>}{Control>}{z}"); + await user.keyboard("{Shift>}{Control>}{Z}"); expect(textarea.textContent).toEqual("@"); - await user.keyboard("{z}"); + + await user.keyboard("{Z}"); expect(textarea.textContent).toEqual("@file "); - await user.keyboard("{z}{/Control}{/Shift}"); + await pause(100); // required, because of cancelling on frequent paste + + await user.keyboard("{z}{/Shift}{/Control}"); expect(textarea.textContent).toEqual("@file /foo "); }); @@ -464,19 +472,4 @@ describe("ComboBox", () => { await user.type(textarea, action); expect(onSubmitSpy).not.toHaveBeenCalled(); }); - - test("paste path after @file command should show only one path", async () => { - const { user, ...app } = render(); - const textarea = app.getByRole("combobox"); - await user.type(textarea, "@file "); - expect(textarea.textContent).toEqual("@file "); - - // Simulate pasting a path - await user.paste("/custom/path/to/file.txt"); - - // Should show only the pasted path, not suggestions - expect(textarea.textContent).toEqual("@file /custom/path/to/file.txt"); - expect(app.queryByText("/foo")).toBeNull(); - expect(app.queryByText("/bar")).toBeNull(); - }); }); diff --git a/src/components/TextArea/TextArea.tsx b/src/components/TextArea/TextArea.tsx index e51dda08..939d76eb 100644 --- a/src/components/TextArea/TextArea.tsx +++ b/src/components/TextArea/TextArea.tsx @@ -28,13 +28,15 @@ export const TextArea = React.forwardRef( const handleKeyDown = useCallback( (event: React.KeyboardEvent) => { const isMod = event.metaKey || event.ctrlKey; - if (isMod && event.key === "z" && !event.shiftKey) { + const eventKey = event.key.toLowerCase(); + + if (isMod && eventKey === "z" && !event.shiftKey) { event.preventDefault(); undoRedo.undo(); setCallChange(true); } - if (isMod && event.key === "z" && event.shiftKey) { + if (isMod && eventKey === "z" && event.shiftKey) { event.preventDefault(); undoRedo.redo(); setCallChange(true); From ae5a0d587ad738b875dfc585b0724b78fff88942 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Thu, 13 Feb 2025 13:40:58 +0100 Subject: [PATCH 3/7] chore: return commented test case for textarea --- src/components/ComboBox/ComboBox.test.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/ComboBox/ComboBox.test.tsx b/src/components/ComboBox/ComboBox.test.tsx index fef74e63..2969885b 100644 --- a/src/components/ComboBox/ComboBox.test.tsx +++ b/src/components/ComboBox/ComboBox.test.tsx @@ -472,4 +472,14 @@ describe("ComboBox", () => { await user.type(textarea, action); expect(onSubmitSpy).not.toHaveBeenCalled(); }); + + // test("textarea should be empty after submit", async () => { + // const submitSpy = vi.fn(); + // const { user, ...app } = render(); + // const textarea = app.getByRole("combobox") as HTMLTextAreaElement; + // await user.type(textarea, "hello"); + // await user.keyboard("{Enter}"); + // expect(submitSpy).toHaveBeenCalled(); + // expect(textarea.textContent).toEqual(""); + // }); }); From 4969b516fa32dff647457b70c4fea7daaca52c4a Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Thu, 13 Feb 2025 13:43:45 +0100 Subject: [PATCH 4/7] wip: disable no-console rule for reviewing purposes --- src/components/ChatForm/ChatForm.tsx | 1 - src/components/ComboBox/ComboBox.tsx | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ChatForm/ChatForm.tsx b/src/components/ChatForm/ChatForm.tsx index 8d4e842d..7c465961 100644 --- a/src/components/ChatForm/ChatForm.tsx +++ b/src/components/ChatForm/ChatForm.tsx @@ -210,7 +210,6 @@ export const ChatForm: React.FC = ({ const handleChange = useCallback( (command: string) => { - console.log(`[DEBUG]: handleChange`); setValue(command); const trimmedCommand = command.trim(); setFileInteracted(!!trimmedCommand); diff --git a/src/components/ComboBox/ComboBox.tsx b/src/components/ComboBox/ComboBox.tsx index ba38d513..e33e63dd 100644 --- a/src/components/ComboBox/ComboBox.tsx +++ b/src/components/ComboBox/ComboBox.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import React, { useCallback, useEffect, useMemo } from "react"; import { useComboboxStore, Combobox } from "@ariakit/react"; import { getAnchorRect, replaceRange } from "./utils"; From ea3b55e893d1561300bedb103d38117441e2345e Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 14 Feb 2025 18:22:02 +0100 Subject: [PATCH 5/7] fix: cut off handlePaste event since it's not used --- src/components/ComboBox/ComboBox.tsx | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/components/ComboBox/ComboBox.tsx b/src/components/ComboBox/ComboBox.tsx index e33e63dd..adc8e8ed 100644 --- a/src/components/ComboBox/ComboBox.tsx +++ b/src/components/ComboBox/ComboBox.tsx @@ -267,20 +267,6 @@ export const ComboBox: React.FC = ({ ], ); - // Add paste event handler - const handlePaste = useCallback( - (_event: React.ClipboardEvent) => { - console.log("[DEBUG] handlePaste event fired"); - setLastPasteTimestamp(Date.now()); - if (state.open) { - console.log("[DEBUG] paste event detected, closing combobox"); - closeCombobox(); - requestCommandsCompletion.cancel(); - } - }, - [closeCombobox, state.open, requestCommandsCompletion], - ); - const onItemClick = useCallback( (item: string, event: React.MouseEvent) => { event.stopPropagation(); @@ -328,7 +314,6 @@ export const ComboBox: React.FC = ({ onKeyUp: onKeyUp, onKeyDown: onKeyDown, onSubmit: onSubmit, - onPaste: handlePaste, })} /> From 86f36c1fdf23cd6de088845cca701673b2b4ac9b Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Mon, 17 Feb 2025 13:47:18 +0100 Subject: [PATCH 6/7] fix: defining isPasteEvent by inputType, not by length of characters --- src/components/ComboBox/ComboBox.tsx | 52 ++++++++++++++++------------ 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/components/ComboBox/ComboBox.tsx b/src/components/ComboBox/ComboBox.tsx index adc8e8ed..97b7a310 100644 --- a/src/components/ComboBox/ComboBox.tsx +++ b/src/components/ComboBox/ComboBox.tsx @@ -221,46 +221,54 @@ export const ComboBox: React.FC = ({ const handleChange = useCallback( (event: React.ChangeEvent) => { const now = Date.now(); - console.log( - "[DEBUG] handleChange called with value:", - event.target.value, - ); + const newValue = event.target.value; + + const inputType = (event.nativeEvent as InputEvent).inputType; + const isPasteEvent = [ + "insertFromPaste", + "insertFromDrop", + "insertFromYank", + "insertReplacementText", + ].includes(inputType); + + const timeSinceLastChange = now - lastPasteTimestamp; + + console.log("[DEBUG] handleChange called with:", { + value: newValue, + inputType, + previousValue: lastValue, + timeSinceLastChange, + }); console.log("[DEBUG] previous value was:", lastValue); - console.log( - "[DEBUG] time since last paste:", - now - lastPasteTimestamp, - "ms", - ); + console.log("[DEBUG] time since last paste:", timeSinceLastChange, "ms"); console.log("[DEBUG] combobox state:", { open: state.open, activeValue: state.activeValue, activeId: state.activeId, replace: commands.replace, }); - const isPaste = event.target.value.length > value.length + 1; - if (isPaste) { - if (now - lastPasteTimestamp < 100) { - console.log("[DEBUG] Skipping duplicate paste event"); - return; - } - console.log( - "[DEBUG] paste detected, closing combobox and canceling completion", - ); + // Only apply paste throttling for large changes + if (isPasteEvent && timeSinceLastChange < 100) { + console.log("[DEBUG] Skipping duplicate paste event"); + return; + } + + if (isPasteEvent) { + console.log("[DEBUG] Paste event detected, resetting combobox state"); setLastPasteTimestamp(now); closeCombobox(); requestCommandsCompletion.cancel(); } - - setLastValue(event.target.value); - onChange(event.target.value); + setLastValue(newValue); + onChange(newValue); }, [ onChange, closeCombobox, state, commands.replace, - value.length, + // value, requestCommandsCompletion, lastPasteTimestamp, lastValue, From 8c0d353c6bf8db3e40497cde41bd905413de9a94 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Tue, 18 Feb 2025 11:31:50 +0100 Subject: [PATCH 7/7] fix: using native event timestamp instead of Date.now() & removed console.log statements --- src/components/ComboBox/ComboBox.tsx | 68 ++++------------------------ 1 file changed, 9 insertions(+), 59 deletions(-) diff --git a/src/components/ComboBox/ComboBox.tsx b/src/components/ComboBox/ComboBox.tsx index 97b7a310..c26d013c 100644 --- a/src/components/ComboBox/ComboBox.tsx +++ b/src/components/ComboBox/ComboBox.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React, { useCallback, useEffect, useMemo } from "react"; import { useComboboxStore, Combobox } from "@ariakit/react"; import { getAnchorRect, replaceRange } from "./utils"; @@ -38,7 +37,6 @@ export const ComboBox: React.FC = ({ const ref = React.useRef(null); const [moveCursorTo, setMoveCursorTo] = React.useState(null); const [lastPasteTimestamp, setLastPasteTimestamp] = React.useState(0); - const [lastValue, setLastValue] = React.useState(""); const shiftEnterToSubmit = useAppSelector(selectSubmitOption); const { escapeKeyPressed } = useEventsBusForIDE(); @@ -92,19 +90,6 @@ export const ComboBox: React.FC = ({ const handleReplace = useCallback( (input: string) => { if (!ref.current) return; - console.log("[DEBUG] handleReplace called with:", { - input, - currentValue: ref.current.value, - replaceRange: commands.replace, - timeSinceLastPaste: Date.now() - lastPasteTimestamp, - }); - - // If this is happening right after a paste, skip it - if (Date.now() - lastPasteTimestamp < 100) { - console.log("[DEBUG] Skipping handleReplace due to recent paste"); - return; - } - const nextValue = replaceRange( ref.current.value, commands.replace, @@ -115,13 +100,7 @@ export const ComboBox: React.FC = ({ onChange(nextValue); setMoveCursorTo(commands.replace[0] + input.length); }, - [ - closeCombobox, - commands.replace, - onChange, - requestCommandsCompletion, - lastPasteTimestamp, - ], + [closeCombobox, commands.replace, onChange, requestCommandsCompletion], ); const onKeyDown = useCallback( @@ -220,10 +199,11 @@ export const ComboBox: React.FC = ({ const handleChange = useCallback( (event: React.ChangeEvent) => { - const now = Date.now(); const newValue = event.target.value; + const nativeEvent = event.nativeEvent as InputEvent; + const currentEventTimestamp = nativeEvent.timeStamp; - const inputType = (event.nativeEvent as InputEvent).inputType; + const inputType = nativeEvent.inputType; const isPasteEvent = [ "insertFromPaste", "insertFromDrop", @@ -231,48 +211,18 @@ export const ComboBox: React.FC = ({ "insertReplacementText", ].includes(inputType); - const timeSinceLastChange = now - lastPasteTimestamp; - - console.log("[DEBUG] handleChange called with:", { - value: newValue, - inputType, - previousValue: lastValue, - timeSinceLastChange, - }); - console.log("[DEBUG] previous value was:", lastValue); - console.log("[DEBUG] time since last paste:", timeSinceLastChange, "ms"); - console.log("[DEBUG] combobox state:", { - open: state.open, - activeValue: state.activeValue, - activeId: state.activeId, - replace: commands.replace, - }); - - // Only apply paste throttling for large changes - if (isPasteEvent && timeSinceLastChange < 100) { - console.log("[DEBUG] Skipping duplicate paste event"); - return; - } + const timeSinceLastChange = currentEventTimestamp - lastPasteTimestamp; + + if (isPasteEvent && timeSinceLastChange < 100) return; if (isPasteEvent) { - console.log("[DEBUG] Paste event detected, resetting combobox state"); - setLastPasteTimestamp(now); + setLastPasteTimestamp(currentEventTimestamp); closeCombobox(); requestCommandsCompletion.cancel(); } - setLastValue(newValue); onChange(newValue); }, - [ - onChange, - closeCombobox, - state, - commands.replace, - // value, - requestCommandsCompletion, - lastPasteTimestamp, - lastValue, - ], + [onChange, closeCombobox, requestCommandsCompletion, lastPasteTimestamp], ); const onItemClick = useCallback(