From d08d9c94491c38d4c3a43bce95ded30c667292a1 Mon Sep 17 00:00:00 2001 From: wgqqqqq Date: Mon, 30 Mar 2026 20:42:07 +0800 Subject: [PATCH 1/2] fix(web-ui): avoid recursive reload after lazy file tree load --- .../tools/file-system/hooks/useFileSystem.ts | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/src/web-ui/src/tools/file-system/hooks/useFileSystem.ts b/src/web-ui/src/tools/file-system/hooks/useFileSystem.ts index 9571deed..514e4235 100644 --- a/src/web-ui/src/tools/file-system/hooks/useFileSystem.ts +++ b/src/web-ui/src/tools/file-system/hooks/useFileSystem.ts @@ -13,6 +13,38 @@ const EMPTY_FILE_TREE: FileSystemNode[] = []; /** Polling keeps remote workspaces and lazy-loaded trees in sync when OS/file watch is unreliable. */ const FILE_TREE_POLL_INTERVAL_MS = 1000; +function areStringArraysEqual(left: string[] = [], right: string[] = []): boolean { + if (left.length !== right.length) { + return false; + } + + return left.every((value, index) => value === right[index]); +} + +function cloneOptions(options: FileSystemOptions): FileSystemOptions { + return { + ...options, + excludePatterns: [...(options.excludePatterns ?? [])], + }; +} + +function didReloadRelevantOptionsChange( + previous: FileSystemOptions | null, + current: FileSystemOptions +): boolean { + if (!previous) { + return false; + } + + return ( + previous.showHiddenFiles !== current.showHiddenFiles || + previous.sortBy !== current.sortBy || + previous.sortOrder !== current.sortOrder || + previous.maxDepth !== current.maxDepth || + !areStringArraysEqual(previous.excludePatterns, current.excludePatterns) + ); +} + function findNodeByPath(nodes: FileSystemNode[], targetPath: string): FileSystemNode | undefined { for (const node of nodes) { if (node.path === targetPath) return node; @@ -89,6 +121,8 @@ export function useFileSystem(options: UseFileSystemOptions = {}): UseFileSystem const isLoadingRef = useRef(false); const optionsRef = useRef(state.options); const expandedFoldersRef = useRef(state.expandedFolders); + const lastReloadOptionsRef = useRef(cloneOptions(state.options)); + const lastReloadRootPathRef = useRef(rootPath); useEffect(() => { optionsRef.current = state.options; @@ -509,10 +543,34 @@ export function useFileSystem(options: UseFileSystemOptions = {}): UseFileSystem }, [autoLoad, rootPath, enableLazyLoad, loadFileTree, loadFileTreeLazy]); useEffect(() => { - if (rootPath && state.fileTree.length > 0) { - void loadFileTree(); + const rootChanged = lastReloadRootPathRef.current !== rootPath; + const optionsChanged = didReloadRelevantOptionsChange(lastReloadOptionsRef.current, state.options); + + lastReloadRootPathRef.current = rootPath; + lastReloadOptionsRef.current = cloneOptions(state.options); + + if (!rootPath || rootChanged || !optionsChanged || state.fileTree.length === 0) { + return; } - }, [state.fileTree.length, state.options.showHiddenFiles, state.options.excludePatterns, rootPath, loadFileTree]); + + if (enableLazyLoad) { + void loadFileTreeLazy(rootPath); + return; + } + + void loadFileTree(rootPath); + }, [ + enableLazyLoad, + loadFileTree, + loadFileTreeLazy, + rootPath, + state.fileTree.length, + state.options.excludePatterns, + state.options.maxDepth, + state.options.showHiddenFiles, + state.options.sortBy, + state.options.sortOrder, + ]); useEffect(() => { if (!rootPath) { From 7eda2d5fa2b18122588c08b350442f0b5e4aa91a Mon Sep 17 00:00:00 2001 From: wgqqqqq Date: Mon, 30 Mar 2026 20:53:20 +0800 Subject: [PATCH 2/2] fix(web-ui): satisfy file tree hook deps lint --- src/web-ui/src/tools/file-system/hooks/useFileSystem.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/web-ui/src/tools/file-system/hooks/useFileSystem.ts b/src/web-ui/src/tools/file-system/hooks/useFileSystem.ts index 514e4235..692b7b91 100644 --- a/src/web-ui/src/tools/file-system/hooks/useFileSystem.ts +++ b/src/web-ui/src/tools/file-system/hooks/useFileSystem.ts @@ -565,11 +565,7 @@ export function useFileSystem(options: UseFileSystemOptions = {}): UseFileSystem loadFileTreeLazy, rootPath, state.fileTree.length, - state.options.excludePatterns, - state.options.maxDepth, - state.options.showHiddenFiles, - state.options.sortBy, - state.options.sortOrder, + state.options, ]); useEffect(() => {