refactor: optimized the logic of esc key handling (#437)

This commit is contained in:
ayangweb
2025-04-25 13:25:19 +08:00
committed by GitHub
parent 3826346fdf
commit 70f876fd4a
9 changed files with 73 additions and 61 deletions

View File

@@ -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();
}}
>
<Input
<PopoverInput
ref={searchInputRef}
autoFocus
value={keyword}

View File

@@ -0,0 +1,34 @@
import { POPOVER_PANEL_SELECTOR } from "@/constants";
import { Input, InputProps } from "@headlessui/react";
import { useKeyPress } from "ahooks";
import { forwardRef, useImperativeHandle, useRef } from "react";
const PopoverInput = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
const inputRef = useRef<HTMLInputElement>(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 <Input ref={inputRef} {...props} />;
});
export default PopoverInput;

View File

@@ -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<string>();
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 && (
<MCPPopover
isMCPActive={isMCPActive}

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useCallback, useRef } from "react";
import { Input, Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
import {
ChevronDownIcon,
RefreshCw,
@@ -21,6 +21,7 @@ import { useShortcutsStore } from "@/stores/shortcutsStore";
import VisibleKey from "@/components/Common/VisibleKey";
import { useChatStore } from "@/stores/chatStore";
import NoDataImage from "@/components/Common/NoDataImage";
import PopoverInput from "../Common/PopoverInput";
interface SearchPopoverProps {
isMCPActive: boolean;
@@ -248,7 +249,7 @@ export default function SearchPopover({
/>
</div>
<Input
<PopoverInput
autoFocus
ref={searchInputRef}
className="size-full px-2 rounded-lg border dark:border-white/10 bg-transparent"

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useCallback, useRef } from "react";
import { Input, Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
import {
ChevronDownIcon,
RefreshCw,
@@ -22,6 +22,7 @@ import VisibleKey from "@/components/Common/VisibleKey";
import { useChatStore } from "@/stores/chatStore";
import NoDataImage from "@/components/Common/NoDataImage";
import FontIcon from "../Common/Icons/FontIcon";
import PopoverInput from "../Common/PopoverInput";
interface SearchPopoverProps {
isSearchActive: boolean;
@@ -254,7 +255,7 @@ export default function SearchPopover({
/>
</div>
<Input
<PopoverInput
autoFocus
ref={searchInputRef}
className="size-full px-2 rounded-lg border dark:border-white/10 bg-transparent"

View File

@@ -1,7 +1,7 @@
import { useCallback, useEffect } from "react";
import platformAdapter from "@/utils/platformAdapter";
import { useSearchStore } from "@/stores/searchStore";
import { useKeyPress } from "ahooks";
import { HISTORY_PANEL_ID } from "@/constants";
const useEscape = () => {
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;

View File

@@ -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();
}, []);

View File

@@ -64,8 +64,6 @@ function WebApp({
localStorage.setItem("headers", JSON.stringify(headers || {}));
}, []);
useModifierKeyPress();
const isMobile = useIsMobile();
const [isChatMode, setIsChatMode] = useState(false);

View File

@@ -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();
},