From a7a758f26edc04be3e3a6ec3a3cde207f8046bef Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Fri, 24 Apr 2026 05:03:23 +0530 Subject: [PATCH] feat(filesystem): add getAgentFilesystemMounts API and integrate with LocalFilesystemBrowser for improved mount management --- surfsense_desktop/src/ipc/channels.ts | 1 + surfsense_desktop/src/ipc/handlers.ts | 5 ++ .../src/modules/agent-filesystem.ts | 7 ++- surfsense_desktop/src/preload.ts | 2 + .../ui/sidebar/LocalFilesystemBrowser.tsx | 61 +++++++++++++++++-- surfsense_web/types/window.d.ts | 6 ++ 6 files changed, 77 insertions(+), 5 deletions(-) diff --git a/surfsense_desktop/src/ipc/channels.ts b/surfsense_desktop/src/ipc/channels.ts index 5cf6e9001..ccd166899 100644 --- a/surfsense_desktop/src/ipc/channels.ts +++ b/surfsense_desktop/src/ipc/channels.ts @@ -55,6 +55,7 @@ export const IPC_CHANNELS = { ANALYTICS_GET_CONTEXT: 'analytics:get-context', // Agent filesystem mode AGENT_FILESYSTEM_GET_SETTINGS: 'agent-filesystem:get-settings', + AGENT_FILESYSTEM_GET_MOUNTS: 'agent-filesystem:get-mounts', AGENT_FILESYSTEM_SET_SETTINGS: 'agent-filesystem:set-settings', AGENT_FILESYSTEM_PICK_ROOT: 'agent-filesystem:pick-root', } as const; diff --git a/surfsense_desktop/src/ipc/handlers.ts b/surfsense_desktop/src/ipc/handlers.ts index 247d171f5..54882f4ee 100644 --- a/surfsense_desktop/src/ipc/handlers.ts +++ b/surfsense_desktop/src/ipc/handlers.ts @@ -39,6 +39,7 @@ import { import { readAgentLocalFileText, writeAgentLocalFileText, + getAgentFilesystemMounts, getAgentFilesystemSettings, pickAgentFilesystemRoot, setAgentFilesystemSettings, @@ -226,6 +227,10 @@ export function registerIpcHandlers(): void { getAgentFilesystemSettings() ); + ipcMain.handle(IPC_CHANNELS.AGENT_FILESYSTEM_GET_MOUNTS, () => + getAgentFilesystemMounts() + ); + ipcMain.handle( IPC_CHANNELS.AGENT_FILESYSTEM_SET_SETTINGS, (_event, settings: { mode?: 'cloud' | 'desktop_local_folder'; localRootPaths?: string[] | null }) => diff --git a/surfsense_desktop/src/modules/agent-filesystem.ts b/surfsense_desktop/src/modules/agent-filesystem.ts index 2bf0101d6..f00c185f8 100644 --- a/surfsense_desktop/src/modules/agent-filesystem.ts +++ b/surfsense_desktop/src/modules/agent-filesystem.ts @@ -122,7 +122,7 @@ function toVirtualPath(rootPath: string, absolutePath: string): string { return `/${rel.replace(/\\/g, "/")}`; } -type LocalRootMount = { +export type LocalRootMount = { mount: string; rootPath: string; }; @@ -145,6 +145,11 @@ function buildRootMounts(rootPaths: string[]): LocalRootMount[] { return mounts; } +export async function getAgentFilesystemMounts(): Promise { + const rootPaths = await resolveCurrentRootPaths(); + return buildRootMounts(rootPaths); +} + function parseMountedVirtualPath(virtualPath: string): { mount: string; subPath: string; diff --git a/surfsense_desktop/src/preload.ts b/surfsense_desktop/src/preload.ts index f7aaf9633..9c538f691 100644 --- a/surfsense_desktop/src/preload.ts +++ b/surfsense_desktop/src/preload.ts @@ -108,6 +108,8 @@ contextBridge.exposeInMainWorld('electronAPI', { // Agent filesystem mode getAgentFilesystemSettings: () => ipcRenderer.invoke(IPC_CHANNELS.AGENT_FILESYSTEM_GET_SETTINGS), + getAgentFilesystemMounts: () => + ipcRenderer.invoke(IPC_CHANNELS.AGENT_FILESYSTEM_GET_MOUNTS), setAgentFilesystemSettings: (settings: { mode?: "cloud" | "desktop_local_folder"; localRootPaths?: string[] | null; diff --git a/surfsense_web/components/layout/ui/sidebar/LocalFilesystemBrowser.tsx b/surfsense_web/components/layout/ui/sidebar/LocalFilesystemBrowser.tsx index 7aebf4695..5b08f2e37 100644 --- a/surfsense_web/components/layout/ui/sidebar/LocalFilesystemBrowser.tsx +++ b/surfsense_web/components/layout/ui/sidebar/LocalFilesystemBrowser.tsx @@ -34,6 +34,11 @@ interface LocalFolderNode { files: LocalFolderFileEntry[]; } +type LocalRootMount = { + mount: string; + rootPath: string; +}; + const getFolderDisplayName = (rootPath: string): string => rootPath.split(/[\\/]/).at(-1) || rootPath; @@ -50,6 +55,20 @@ function getFileName(pathValue: string): string { return pathValue.split(/[\\/]/).at(-1) || pathValue; } +function toVirtualPath(relativePath: string): string { + const normalized = relativePath.replace(/\\/g, "/").replace(/^\/+/, ""); + return `/${normalized}`; +} + +function normalizeRootPathForLookup(rootPath: string, isWindows: boolean): string { + const normalized = rootPath.replace(/\\/g, "/").replace(/\/+$/, ""); + return isWindows ? normalized.toLowerCase() : normalized; +} + +function toMountedVirtualPath(mount: string, relativePath: string): string { + return `/${mount}${toVirtualPath(relativePath)}`; +} + export function LocalFilesystemBrowser({ rootPaths, searchSpaceId, @@ -59,7 +78,9 @@ export function LocalFilesystemBrowser({ const electronAPI = useElectronAPI(); const [rootStateMap, setRootStateMap] = useState>({}); const [expandedFolderKeys, setExpandedFolderKeys] = useState>(new Set()); + const [mountByRootKey, setMountByRootKey] = useState>(new Map()); const supportedExtensions = useMemo(() => Array.from(getSupportedExtensionsSet()), []); + const isWindowsPlatform = electronAPI?.versions.platform === "win32"; useEffect(() => { if (!electronAPI?.listFolderFiles) return; @@ -116,6 +137,31 @@ export function LocalFilesystemBrowser({ }; }, [electronAPI, rootPaths, searchSpaceId, supportedExtensions]); + useEffect(() => { + if (!electronAPI?.getAgentFilesystemMounts) { + setMountByRootKey(new Map()); + return; + } + let cancelled = false; + void electronAPI + .getAgentFilesystemMounts() + .then((mounts: LocalRootMount[]) => { + if (cancelled) return; + const next = new Map(); + for (const entry of mounts) { + next.set(normalizeRootPathForLookup(entry.rootPath, isWindowsPlatform), entry.mount); + } + setMountByRootKey(next); + }) + .catch(() => { + if (cancelled) return; + setMountByRootKey(new Map()); + }); + return () => { + cancelled = true; + }; + }, [electronAPI, isWindowsPlatform, rootPaths]); + const treeByRoot = useMemo(() => { const query = searchQuery?.trim().toLowerCase() ?? ""; const hasQuery = query.length > 0; @@ -160,7 +206,7 @@ export function LocalFilesystemBrowser({ }, []); const renderFolder = useCallback( - (folder: LocalFolderNode, depth: number) => { + (folder: LocalFolderNode, depth: number, mount: string) => { const isExpanded = expandedFolderKeys.has(folder.key); const childFolders = Array.from(folder.folders.values()).sort((a, b) => a.name.localeCompare(b.name) @@ -185,12 +231,12 @@ export function LocalFilesystemBrowser({ {isExpanded && ( <> - {childFolders.map((childFolder) => renderFolder(childFolder, depth + 1))} + {childFolders.map((childFolder) => renderFolder(childFolder, depth + 1, mount))} {files.map((file) => (