feat: enhance keyboard shortcut management and improve app responsiveness

- Updated the development script to include a build step before launching the app.
- Refactored the registration of quick ask and autocomplete functionalities to be asynchronous, ensuring proper initialization.
- Introduced IPC channels for getting and setting keyboard shortcuts, allowing users to customize their experience.
- Enhanced the platform module to support better interaction with the Electron API for clipboard operations.
- Improved the user interface for managing keyboard shortcuts in the settings dialog, providing a more intuitive experience.
This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-04-07 00:43:40 -07:00
parent e920923fa4
commit 49441233e7
30 changed files with 923 additions and 191 deletions

View file

@ -41,6 +41,7 @@ import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import type { DocumentTypeEnum } from "@/contracts/types/document.types";
import { useDebouncedValue } from "@/hooks/use-debounced-value";
import { useMediaQuery } from "@/hooks/use-media-query";
import { useElectronAPI } from "@/hooks/use-platform";
import { documentsApiService } from "@/lib/apis/documents-api.service";
import { foldersApiService } from "@/lib/apis/folders-api.service";
import { authenticatedFetch } from "@/lib/auth-utils";
@ -84,6 +85,7 @@ export function DocumentsSidebar({
const tSidebar = useTranslations("sidebar");
const params = useParams();
const isMobile = !useMediaQuery("(min-width: 640px)");
const electronAPI = useElectronAPI();
const searchSpaceId = Number(params.search_space_id);
const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom);
const setRightPanelCollapsed = useSetAtom(rightPanelCollapsedAtom);
@ -97,11 +99,11 @@ export function DocumentsSidebar({
const [watchedFolderIds, setWatchedFolderIds] = useState<Set<number>>(new Set());
useEffect(() => {
const api = typeof window !== "undefined" ? window.electronAPI : null;
if (!api?.getWatchedFolders) return;
if (!electronAPI?.getWatchedFolders) return;
const api = electronAPI;
async function loadWatchedIds() {
const folders = await api!.getWatchedFolders();
const folders = await api.getWatchedFolders();
if (folders.length === 0) {
try {
@ -109,7 +111,7 @@ export function DocumentsSidebar({
for (const bf of backendFolders) {
const meta = bf.metadata as Record<string, unknown> | null;
if (!meta?.watched || !meta.folder_path) continue;
await api!.addWatchedFolder({
await api.addWatchedFolder({
path: meta.folder_path as string,
name: bf.name,
rootFolderId: bf.id,
@ -119,7 +121,7 @@ export function DocumentsSidebar({
active: true,
});
}
const recovered = await api!.getWatchedFolders();
const recovered = await api.getWatchedFolders();
const ids = new Set(
recovered.filter((f) => f.rootFolderId != null).map((f) => f.rootFolderId as number)
);
@ -137,7 +139,7 @@ export function DocumentsSidebar({
}
loadWatchedIds();
}, [searchSpaceId]);
}, [searchSpaceId, electronAPI]);
const { mutateAsync: deleteDocumentMutation } = useAtomValue(deleteDocumentMutationAtom);
const [sidebarDocs, setSidebarDocs] = useAtom(sidebarSelectedDocumentsAtom);
@ -276,10 +278,9 @@ export function DocumentsSidebar({
const handleRescanFolder = useCallback(
async (folder: FolderDisplay) => {
const api = window.electronAPI;
if (!api) return;
if (!electronAPI) return;
const watchedFolders = await api.getWatchedFolders();
const watchedFolders = await electronAPI.getWatchedFolders();
const matched = watchedFolders.find((wf) => wf.rootFolderId === folder.id);
if (!matched) {
toast.error("This folder is not being watched");
@ -298,28 +299,27 @@ export function DocumentsSidebar({
toast.error((err as Error)?.message || "Failed to re-scan folder");
}
},
[searchSpaceId]
[searchSpaceId, electronAPI]
);
const handleStopWatching = useCallback(async (folder: FolderDisplay) => {
const api = window.electronAPI;
if (!api) return;
if (!electronAPI) return;
const watchedFolders = await api.getWatchedFolders();
const watchedFolders = await electronAPI.getWatchedFolders();
const matched = watchedFolders.find((wf) => wf.rootFolderId === folder.id);
if (!matched) {
toast.error("This folder is not being watched");
return;
}
await api.removeWatchedFolder(matched.path);
await electronAPI.removeWatchedFolder(matched.path);
try {
await foldersApiService.stopWatching(folder.id);
} catch (err) {
console.error("[DocumentsSidebar] Failed to clear watched metadata:", err);
}
toast.success(`Stopped watching: ${matched.name}`);
}, []);
}, [electronAPI]);
const handleRenameFolder = useCallback(async (folder: FolderDisplay, newName: string) => {
try {
@ -333,12 +333,11 @@ export function DocumentsSidebar({
const handleDeleteFolder = useCallback(async (folder: FolderDisplay) => {
if (!confirm(`Delete folder "${folder.name}" and all its contents?`)) return;
try {
const api = window.electronAPI;
if (api) {
const watchedFolders = await api.getWatchedFolders();
if (electronAPI) {
const watchedFolders = await electronAPI.getWatchedFolders();
const matched = watchedFolders.find((wf) => wf.rootFolderId === folder.id);
if (matched) {
await api.removeWatchedFolder(matched.path);
await electronAPI.removeWatchedFolder(matched.path);
}
}
await foldersApiService.deleteFolder(folder.id);
@ -346,7 +345,7 @@ export function DocumentsSidebar({
} catch (e: unknown) {
toast.error((e as Error)?.message || "Failed to delete folder");
}
}, []);
}, [electronAPI]);
const handleMoveFolder = useCallback(
(folder: FolderDisplay) => {