From 70f876fd4abc47111ca1c953b9ff34ace83db854 Mon Sep 17 00:00:00 2001 From: ayangweb <75017711+ayangweb@users.noreply.github.com> Date: Fri, 25 Apr 2025 13:25:19 +0800 Subject: [PATCH] refactor: optimized the logic of esc key handling (#437) --- src/components/Assistant/AssistantList.tsx | 5 ++- src/components/Common/PopoverInput.tsx | 34 +++++++++++++++ src/components/Search/InputBox.tsx | 25 +---------- src/components/Search/MCPPopover.tsx | 5 ++- src/components/Search/SearchPopover.tsx | 5 ++- src/hooks/useEscape.ts | 51 ++++++++++------------ src/pages/main/index.tsx | 2 +- src/pages/web/index.tsx | 2 - src/utils/tauriAdapter.ts | 5 +++ 9 files changed, 73 insertions(+), 61 deletions(-) create mode 100644 src/components/Common/PopoverInput.tsx diff --git a/src/components/Assistant/AssistantList.tsx b/src/components/Assistant/AssistantList.tsx index d3e84a99..4a5f42f1 100644 --- a/src/components/Assistant/AssistantList.tsx +++ b/src/components/Assistant/AssistantList.tsx @@ -17,10 +17,11 @@ import FontIcon from "@/components/Common/Icons/FontIcon"; import { useChatStore } from "@/stores/chatStore"; import { useShortcutsStore } from "@/stores/shortcutsStore"; import { Post } from "@/api/axiosRequest"; -import { Input, Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; +import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; import { useDebounce, useKeyPress, useMount, usePagination } from "ahooks"; import clsx from "clsx"; import NoDataImage from "../Common/NoDataImage"; +import PopoverInput from "../Common/PopoverInput"; interface AssistantListProps { assistantIDs?: string[]; @@ -262,7 +263,7 @@ export function AssistantList({ assistantIDs = [] }: AssistantListProps) { searchInputRef.current?.focus(); }} > - ((props, ref) => { + const inputRef = useRef(null); + + useImperativeHandle(ref, () => inputRef.current!); + + useKeyPress( + "esc", + (event) => { + if (inputRef.current === document.activeElement) { + event.preventDefault(); + event.stopPropagation(); + + inputRef.current?.blur(); + + const parentPanel = inputRef.current?.closest(POPOVER_PANEL_SELECTOR); + if (parentPanel instanceof HTMLElement) { + parentPanel.focus(); + } + } + }, + { + target: inputRef, + } + ); + + return ; +}); + +export default PopoverInput; diff --git a/src/components/Search/InputBox.tsx b/src/components/Search/InputBox.tsx index 8076ae64..e9bad914 100644 --- a/src/components/Search/InputBox.tsx +++ b/src/components/Search/InputBox.tsx @@ -90,7 +90,6 @@ export default function ChatInput({ getDataSourcesByServer, getMCPByServer, setupWindowFocusListener, - hideCoco, hasModules = [], searchPlaceholder, chatPlaceholder, @@ -100,7 +99,6 @@ export default function ChatInput({ const currentAssistant = useConnectStore((state) => state.currentAssistant); const showTooltip = useAppStore((state) => state.showTooltip); - const isPinned = useAppStore((state) => state.isPinned); const sourceData = useSearchStore((state) => state.sourceData); const setSourceData = useSearchStore((state) => state.setSourceData); @@ -184,35 +182,14 @@ export default function ChatInput({ const pressedKeys = new Set(); - const handleEscapeKey = useCallback(() => { - if (inputValue) { - changeInput(""); - } else if (!isPinned) { - hideCoco && hideCoco(); - } - }, [inputValue, isPinned]); - useKeyPress(`${modifierKey}.${returnToInput}`, handleToggleFocus); const visibleContextMenu = useSearchStore((state) => { return state.visibleContextMenu; }); - const setVisibleContextMenu = useSearchStore((state) => { - return state.setVisibleContextMenu; - }); const handleKeyDown = useCallback( (e: KeyboardEvent) => { - // console.log("handleKeyDown", e.code, e.key); - - if (e.key === "Escape") { - if (visibleContextMenu) { - return setVisibleContextMenu(false); - } - - return handleEscapeKey(); - } - pressedKeys.add(e.key); if (e.key === metaOrCtrlKey()) { @@ -496,7 +473,7 @@ export default function ChatInput({ getDataSourcesByServer={getDataSourcesByServer} /> )} - + {currentAssistant?._source?.mcp_servers?.visible && ( - - { const visibleContextMenu = useSearchStore((state) => { @@ -11,38 +11,33 @@ const useEscape = () => { return state.setVisibleContextMenu; }); - const handleEscape = useCallback(() => { - async (event: KeyboardEvent) => { - if (event.key === "Escape") { - console.log("Escape key pressed."); + useKeyPress("esc", (event) => { + event.preventDefault(); + event.stopPropagation(); - event.preventDefault(); + if ( + document.activeElement instanceof HTMLInputElement || + document.activeElement instanceof HTMLTextAreaElement + ) { + return document.activeElement.blur(); + } - if (visibleContextMenu) { - return setVisibleContextMenu(false); - } + if (visibleContextMenu) { + return setVisibleContextMenu(false); + } - // Hide the Tauri app window when 'Esc' is pressed - await platformAdapter.invokeBackend("hide_coco"); + const historyPanel = document.getElementById(HISTORY_PANEL_ID); - console.log("App window hidden successfully."); - } - }; - }, [visibleContextMenu]); + if (historyPanel) { + const button = document.querySelector( + `[aria-controls="${HISTORY_PANEL_ID}"]` + ); - useEffect(() => { - const unlisten = platformAdapter.listenEvent("tauri://focus", () => { - // Add event listener for keydown - window.addEventListener("keydown", handleEscape); - }); + return (button as HTMLElement).click(); + } - // Cleanup event listener on component unmount - return () => { - unlisten.then((unlistenFn) => unlistenFn()); - - window.removeEventListener("keydown", handleEscape); - }; - }, []); + platformAdapter.hideWindow(); + }); }; export default useEscape; diff --git a/src/pages/main/index.tsx b/src/pages/main/index.tsx index b3603086..21549e16 100644 --- a/src/pages/main/index.tsx +++ b/src/pages/main/index.tsx @@ -8,7 +8,7 @@ import { useSyncStore } from "@/hooks/useSyncStore"; function MainApp() { const setIsTauri = useAppStore((state) => state.setIsTauri); setIsTauri(true); - + const hideCoco = useCallback(() => { return platformAdapter.hideWindow(); }, []); diff --git a/src/pages/web/index.tsx b/src/pages/web/index.tsx index a95f094d..cbc77e3b 100644 --- a/src/pages/web/index.tsx +++ b/src/pages/web/index.tsx @@ -64,8 +64,6 @@ function WebApp({ localStorage.setItem("headers", JSON.stringify(headers || {})); }, []); - useModifierKeyPress(); - const isMobile = useIsMobile(); const [isChatMode, setIsChatMode] = useState(false); diff --git a/src/utils/tauriAdapter.ts b/src/utils/tauriAdapter.ts index c8d654a3..c5aca2af 100644 --- a/src/utils/tauriAdapter.ts +++ b/src/utils/tauriAdapter.ts @@ -11,6 +11,7 @@ import { } from "./wrappers/tauriWrappers"; import type { BasePlatformAdapter } from "@/types/platform"; import type { AppTheme } from "@/types/index"; +import { useAppStore } from "@/stores/appStore"; export interface TauriPlatformAdapter extends BasePlatformAdapter { openFileDialog: ( @@ -27,6 +28,10 @@ export const createTauriAdapter = (): TauriPlatformAdapter => { }, async hideWindow() { + const isPinned = useAppStore.getState().isPinned; + + if (isPinned) return; + const window = await windowWrapper.getWebviewWindow(); return window?.hide(); },