import type { FileNode } from "../types"; export const addPaths = (node: FileNode, parentPath: string = ""): FileNode => { const currentPath = parentPath ? `${parentPath}/${node.name}` : node.name; const newNode = { ...node, path: currentPath }; if (newNode.children) { newNode.children = newNode.children.map((child) => addPaths(child, currentPath), ); } return newNode; }; export const getAllFolderPaths = (node: FileNode): string[] => { let paths: string[] = []; if (node.type === "folder") { paths.push(node.path || node.name); if (node.children) { node.children.forEach((child) => { paths = [...paths, ...getAllFolderPaths(child)]; }); } } return paths; }; export const findNode = (node: FileNode, path: string): FileNode | null => { if (node.path === path) return node; if (node.children) { for (const child of node.children) { const found = findNode(child, path); if (found) return found; } } return null; }; export const deleteNode = (node: FileNode, path: string): FileNode | null => { if (node.path === path) return null; if (node.children) { const filtered = node.children.filter((child) => child.path !== path); const mapped = filtered .map((child) => deleteNode(child, path)) .filter((child): child is FileNode => child !== null); return { ...node, children: mapped }; } return node; }; export const addNode = ( node: FileNode, parentPath: string, newNode: FileNode, ): FileNode => { if (node.path === parentPath) { const newPath = addPaths(newNode, node.path); return { ...node, children: [...(node.children || []), newPath] }; } if (node.children) { return { ...node, children: node.children.map((child) => addNode(child, parentPath, newNode), ), }; } return node; }; export const renameNode = ( node: FileNode, oldPath: string, newName: string, ): FileNode | null => { if (node.path === oldPath) { const pathParts = node.path?.split("/") || []; pathParts[pathParts.length - 1] = newName; const newPath = pathParts.join("/"); const renamedNode = { ...node, name: newName, path: newPath }; if (renamedNode.children) { renamedNode.children = renamedNode.children.map((child) => { const oldChildPath = child.path || ""; const newChildPath = oldChildPath.replace(oldPath, newPath); return ( renameNode( child, oldChildPath, newChildPath.split("/").pop() || "", ) || child ); }); } return renamedNode; } if (node.children) { return { ...node, children: node.children.map( (child) => renameNode(child, oldPath, newName) || child, ), }; } return node; }; export const filterTree = (node: FileNode, query: string): FileNode | null => { if (!query) return node; const lowerQuery = query.toLowerCase(); if (node.type === "file") { if (node.name.toLowerCase().includes(lowerQuery)) return node; return null; } if (node.children) { const filteredChildren = node.children .map((child) => filterTree(child, query)) .filter((child): child is FileNode => child !== null); if (filteredChildren.length > 0) { return { ...node, children: filteredChildren }; } } if (node.name.toLowerCase().includes(lowerQuery)) return node; return null; }; export const collectPathsToExpand = ( node: FileNode, query: string, ): Set => { const paths = new Set(); if (!query) return paths; const lowerQuery = query.toLowerCase(); const search = (n: FileNode, currentPath: string) => { if (n.name.toLowerCase().includes(lowerQuery)) { const pathParts = currentPath.split("/"); for (let i = 1; i < pathParts.length; i++) { paths.add(pathParts.slice(0, i).join("/")); } } if (n.children) { n.children.forEach((child) => { const childPath = child.path || `${currentPath}/${child.name}`; search(child, childPath); }); } }; search(node, node.path || node.name); return paths; }; export const getLanguage = (path: string) => { const ext = path.split(".").pop(); const map: Record = { py: "python", js: "javascript", ts: "typescript", jsx: "javascript", tsx: "typescript", json: "json", md: "markdown", css: "css", html: "html", }; return map[ext || ""] || "plaintext"; };