Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion src/web-ui/src/tools/file-system/hooks/useFileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ const log = createLogger('useFileSystem');

const EMPTY_FILE_TREE: FileSystemNode[] = [];

function findNodeByPath(nodes: FileSystemNode[], targetPath: string): FileSystemNode | undefined {
for (const node of nodes) {
if (node.path === targetPath) return node;
if (node.children) {
const found = findNodeByPath(node.children, targetPath);
if (found) return found;
}
}
return undefined;
}

export interface UseFileSystemOptions extends FileSystemOptions {
rootPath?: string;
autoLoad?: boolean;
Expand Down Expand Up @@ -296,6 +307,32 @@ export function useFileSystem(options: UseFileSystemOptions = {}): UseFileSystem
});
}, []);

const refreshDirectoryInTree = useCallback(async (dirPath: string) => {
try {
const newChildren = await fileSystemService.getDirectoryChildren(dirPath);
directoryCache.set(dirPath, newChildren);
loadedPathsRef.current.add(dirPath);

setState(prev => {
const mergedChildren = newChildren.map(newChild => {
if (!newChild.isDirectory) return newChild;
const existingChild = findNodeByPath(prev.fileTree, newChild.path);
if (existingChild?.children) {
return { ...newChild, children: existingChild.children };
}
return newChild;
});

return {
...prev,
fileTree: updateNodeChildrenInTree(prev.fileTree, dirPath, mergedChildren)
};
});
} catch (error) {
log.warn('Failed to refresh directory after file change', { dirPath, error });
}
}, [updateNodeChildrenInTree]);

const expandFolderLazy = useCallback(async (folderPath: string) => {
if (state.expandedFolders.has(folderPath)) {
setState(prev => {
Expand Down Expand Up @@ -494,16 +531,23 @@ export function useFileSystem(options: UseFileSystemOptions = {}): UseFileSystem
debounceTimer = setTimeout(() => {
if (pendingPaths.length > 0 && rootPath) {
if (enableLazyLoad) {
const affectedParents = new Set<string>();

pendingPaths.forEach(changedPath => {
directoryCache.invalidate(changedPath);
loadedPathsRef.current.delete(changedPath);
const parentPath = changedPath.replace(/[\\/][^\\/]+$/, '');
if (parentPath && parentPath !== changedPath) {
directoryCache.invalidate(parentPath);
loadedPathsRef.current.delete(parentPath);
affectedParents.add(parentPath);
}
});
pendingPaths = [];

for (const parentPath of affectedParents) {
refreshDirectoryInTree(parentPath);
}
} else {
pendingPaths = [];
loadFileTree(rootPath, true);
Expand All @@ -522,7 +566,7 @@ export function useFileSystem(options: UseFileSystemOptions = {}): UseFileSystem
clearTimeout(debounceTimer);
}
};
}, [enableAutoWatch, rootPath, enableLazyLoad, loadFileTree, loadFileTreeLazy]);
}, [enableAutoWatch, rootPath, enableLazyLoad, loadFileTree, loadFileTreeLazy, refreshDirectoryInTree]);

const effectiveFileTree =
rootPathRef.current === rootPath ? state.fileTree : EMPTY_FILE_TREE;
Expand Down