Overview
ZapDev’s file explorer provides a familiar IDE-like experience for browsing your AI-generated codebase. View hierarchical file structures, inspect source code with syntax highlighting, and copy files to your clipboard with a single click.
The file explorer automatically filters out system files (like node_modules/, .next/, etc.) to show only your AI-generated application code.
Interface Components
The file explorer consists of two resizable panels:
┌──────────────────┬───────────────────────────────────────┐
│ │ │
│ File Tree │ Code Viewer │
│ │ │
│ 📁 app/ │ ┌─────────────────────────────────┐ │
│ 📄 page.tsx │ │ app / page.tsx [Copy]│ │
│ 📄 layout.tsx │ ├─────────────────────────────────┤ │
│ 📁 components/ │ │ │ │
│ 📁 ui/ │ │ 'use client'; │ │
│ 📄 hero.tsx │ │ │ │
│ 📁 lib/ │ │ export default function Page() │ │
│ 📄 utils.ts │ │ return ( │ │
│ │ │ <div>...</div> │ │
│ [30% width] │ │ ); │ │
│ │ │ } │ │
└──────────────────┴──────────────────────────────────────┘
File Tree Panel
Hierarchical Structure
Files are organized in a nested tree view:
// From src/components/file-explorer.tsx
import { TreeView } from "./tree-view" ;
import { convertFilesToTreeItems } from "@/lib/utils" ;
const treeData = useMemo (() => {
return convertFilesToTreeItems ( files );
}, [ files ]);
< TreeView
data = { treeData }
value = { selectedFile }
onSelect = { handleFileSelect }
/>
Example Tree Structure :
📁 app/
├── 📄 page.tsx
├── 📄 layout.tsx
└── 📄 globals.css
📁 components/
├── 📁 ui/
│ ├── 📄 button.tsx
│ ├── 📄 card.tsx
│ ├── 📄 dialog.tsx
│ └── 📄 input.tsx
├── 📄 hero.tsx
├── 📄 navbar.tsx
└── 📄 footer.tsx
📁 lib/
├── 📄 utils.ts
└── 📄 cn.ts
📄 package.json
📄 tsconfig.json
📄 tailwind.config.ts
File Type Icons
Different file types get appropriate icons:
File Type Icon Extensions Folder 📁 Directories TypeScript 📘 .ts, .tsxJavaScript 📙 .js, .jsxStyles 🎨 .css, .scss, .sassConfig ⚙️ .json, .yaml, .tomlMarkdown 📝 .md, .mdxGeneric 📄 All others
Tree Interactions
Select File
Expand/Collapse Folders
Keyboard Navigation
Click any file to view its contents: const handleFileSelect = useCallback (( filePath : string ) => {
if ( files [ filePath ]) {
setSelectedFile ( filePath );
}
}, [ files ]);
Visual feedback : Selected file is highlighted
Instant loading : Code appears immediately
Breadcrumb updates : Path updates in header
Click folder icons to expand/collapse: const [ expandedFolders , setExpandedFolders ] = useState < Set < string >>( new Set ());
const toggleFolder = ( folderPath : string ) => {
setExpandedFolders ( prev => {
const next = new Set ( prev );
if ( next . has ( folderPath )) {
next . delete ( folderPath );
} else {
next . add ( folderPath );
}
return next ;
});
};
Persistent state : Expansion state preserved during session
Keyboard support : Arrow keys navigate, Enter toggles
Auto-expand : Parent folders of selected file auto-expand
Navigate files with keyboard: Key Action ↑/↓Navigate up/down →Expand folder ←Collapse folder EnterSelect file SpaceToggle folder
const handleKeyDown = ( e : KeyboardEvent ) => {
switch ( e . key ) {
case 'ArrowUp' : selectPreviousFile (); break ;
case 'ArrowDown' : selectNextFile (); break ;
case 'ArrowRight' : expandFolder (); break ;
case 'ArrowLeft' : collapseFolder (); break ;
case 'Enter' : selectCurrentFile (); break ;
}
};
File Filtering
System and build files are automatically hidden:
// From src/lib/filter-ai-files.ts
const EXCLUDED_PATTERNS = [
'node_modules/' , // Dependencies
'.next/' , // Next.js build
'dist/' , // Build output
'build/' , // Build output
'.git/' , // Version control
'.angular/' , // Angular cache
'package-lock.json' , // Lock files
'bun.lockb' , // Lock files
'.turbo/' , // Turbopack cache
'.cache/' // General cache
];
export function filterAIGeneratedFiles ( files : Record < string , string >) {
return Object . entries ( files )
. filter (([ path ]) =>
! EXCLUDED_PATTERNS . some ( pattern => path . includes ( pattern ))
)
. reduce (( acc , [ path , content ]) => ({
... acc ,
[path]: content
}), {});
}
What you see : Only AI-generated application code
What’s hidden : System files, dependencies, build artifacts
Code Viewer Panel
Syntax Highlighting
Professional code highlighting powered by Prism.js:
// From src/components/code-view/index.tsx
import Prism from "prismjs" ;
import "prismjs/components/prism-javascript" ;
import "prismjs/components/prism-jsx" ;
import "prismjs/components/prism-tsx" ;
import "prismjs/components/prism-typescript" ;
import "./code-theme.css" ;
export const CodeView = ({ code , lang } : { code : string ; lang : string }) => {
useEffect (() => {
Prism . highlightAll ();
}, [ code ]);
return (
< pre className = "p-2 bg-transparent border-none rounded-none m-0 text-xs" >
< code className = { `language- ${ lang } ` } >
{ code }
</ code >
</ pre >
);
};
Supported Languages
TypeScript/TSX
JavaScript/JSX
CSS/SCSS
JSON
'use client' ;
import { Button } from '@/components/ui/button' ;
import { Card } from '@/components/ui/card' ;
export default function Page () {
return (
< Card className = "p-6" >
< Button > Click Me </ Button >
</ Card >
);
}
Language Detection
Automatic language detection from file extensions:
function getLanguageFromExtension ( filename : string ) : string {
const extension = filename . split ( "." ). pop ()?. toLowerCase ();
const languageMap : Record < string , string > = {
'ts' : 'typescript' ,
'tsx' : 'tsx' ,
'js' : 'javascript' ,
'jsx' : 'jsx' ,
'json' : 'json' ,
'css' : 'css' ,
'scss' : 'scss' ,
'html' : 'html' ,
'md' : 'markdown' ,
'yaml' : 'yaml' ,
'yml' : 'yaml'
};
return languageMap [ extension || '' ] || 'text' ;
}
Code Theme
Dark theme optimized for readability:
/* From src/components/code-view/code-theme.css */
.token.comment { color : #6a9955 ; }
.token.string { color : #ce9178 ; }
.token.keyword { color : #569cd6 ; }
.token.function { color : #dcdcaa ; }
.token.class-name { color : #4ec9b0 ; }
.token.operator { color : #d4d4d4 ; }
.token.punctuation { color : #d4d4d4 ; }
Color Scheme :
Keywords (import, export, const): Blue (#569cd6)
Strings : Orange (#ce9178)
Functions : Yellow (#dcdcaa)
Classes/Types : Teal (#4ec9b0)
Comments : Green (#6a9955)
Breadcrumb Navigation
Clear path indication for deeply nested files:
// From src/components/file-explorer.tsx
const FileBreadcrumb = ({ filePath } : { filePath : string }) => {
const pathSegments = filePath . split ( "/" );
const maxSegments = 4 ;
const renderBreadcrumbItems = () => {
if ( pathSegments . length <= maxSegments ) {
// Show all segments
return pathSegments . map (( segment , index ) => {
const isLast = index === pathSegments . length - 1 ;
return (
< Fragment key = { index } >
< BreadcrumbItem >
{ isLast ? (
< BreadcrumbPage className = "font-medium" >
{ segment }
</ BreadcrumbPage >
) : (
< span className = "text-muted-foreground" > { segment } </ span >
) }
</ BreadcrumbItem >
{ ! isLast && < BreadcrumbSeparator /> }
</ Fragment >
);
});
} else {
// Truncate middle segments
const firstSegment = pathSegments [ 0 ];
const lastSegment = pathSegments [ pathSegments . length - 1 ];
return (
<>
< BreadcrumbItem >
< span className = "text-muted-foreground" > { firstSegment } </ span >
</ BreadcrumbItem >
< BreadcrumbSeparator />
< BreadcrumbItem >
< BreadcrumbEllipsis />
</ BreadcrumbItem >
< BreadcrumbSeparator />
< BreadcrumbItem >
< BreadcrumbPage className = "font-medium" >
{ lastSegment }
</ BreadcrumbPage >
</ BreadcrumbItem >
</>
);
}
};
return (
< Breadcrumb >
< BreadcrumbList > { renderBreadcrumbItems () } </ BreadcrumbList >
</ Breadcrumb >
);
};
Examples :
Short path:
app / page.tsx
Medium path:
components / ui / button.tsx
Long path:
app / ... / button.tsx
Copy to Clipboard
One-click file copying:
const [ copied , setCopied ] = useState ( false );
const handleCopy = useCallback (() => {
if ( selectedFile ) {
navigator . clipboard . writeText ( files [ selectedFile ]);
setCopied ( true );
setTimeout (() => setCopied ( false ), 2000 );
}
}, [ selectedFile , files ]);
< Hint text = "Copy to clipboard" side = "bottom" >
< Button
variant = "outline"
size = "icon"
onClick = { handleCopy }
>
{ copied ? (
< CopyCheckIcon className = "h-4 w-4 text-green-500" />
) : (
< CopyIcon className = "h-4 w-4" />
) }
</ Button >
</ Hint >
User Feedback :
✅ Icon changes to checkmark on success
✅ Green color for 2 seconds
✅ Tooltip shows “Copy to clipboard”
✅ Returns to copy icon after timeout
Resizable Panels
Adjust panel sizes to your preference:
import {
ResizablePanelGroup ,
ResizablePanel ,
ResizableHandle
} from "@/components/ui/resizable" ;
< ResizablePanelGroup direction = "horizontal" >
{ /* File Tree */ }
< ResizablePanel
defaultSize = { 30 }
minSize = { 20 }
maxSize = { 50 }
className = "bg-sidebar"
>
< TreeView />
</ ResizablePanel >
{ /* Resize Handle */ }
< ResizableHandle className = "hover:bg-primary transition-colors" />
{ /* Code Viewer */ }
< ResizablePanel
defaultSize = { 70 }
minSize = { 50 }
>
< CodeView />
</ ResizablePanel >
</ ResizablePanelGroup >
Features :
Drag handle to resize
Minimum/maximum size constraints
Smooth transitions on hover
Layout persists during session
Virtualized Tree (Future)
For projects with 100+ files:
import { FixedSizeTree } from 'react-window' ;
const VirtualizedTree = ({ files , height }) => (
< FixedSizeTree
treeWalker = { treeWalker }
itemSize = { 30 }
height = { height }
width = "100%"
>
{ Node }
</ FixedSizeTree >
);
Memoized Computations
Expensive operations are cached:
// Tree data conversion
const treeData = useMemo (() => {
return convertFilesToTreeItems ( files );
}, [ files ]);
// Language detection
const language = useMemo (() => {
return getLanguageFromExtension ( selectedFile || '' );
}, [ selectedFile ]);
// Filtered files
const displayFiles = useMemo (() => {
return filterAIGeneratedFiles ( files );
}, [ files ]);
Lazy Syntax Highlighting
Code is highlighted only when visible:
const CodeView = ({ code , lang }) => {
const [ isHighlighted , setIsHighlighted ] = useState ( false );
useEffect (() => {
if ( ! isHighlighted ) {
Prism . highlightAll ();
setIsHighlighted ( true );
}
}, [ code , isHighlighted ]);
// Render...
};
Real-Time Updates
File explorer updates as AI generates code:
const [ streamingFiles , setStreamingFiles ] = useState < Record < string , string >>({});
// Stream events from AI
const handleStreamEvent = ( event : StreamEvent ) => {
if ( event . type === 'file-created' ) {
setStreamingFiles ( prev => ({
... prev ,
[event.data.path]: event . data . content
}));
} else if ( event . type === 'file-updated' ) {
setStreamingFiles ( prev => ({
... prev ,
[event.data.path]: event . data . content
}));
}
};
// Merge streaming files with final files
const allFiles = useMemo (() => {
return { ... streamingFiles , ... fragmentFiles };
}, [ streamingFiles , fragmentFiles ]);
Update Flow :
AI generates file → Stream event fires
File added to streamingFiles state
Tree view re-renders with new file
Auto-select first file if none selected
Code viewer shows new content
Accessibility
Keyboard Navigation
< div
role = "tree"
aria-label = "File explorer"
onKeyDown = { handleKeyDown }
tabIndex = { 0 }
>
< TreeView />
</ div >
Screen Reader Support
< button
aria-label = { `Select file ${ filename } ` }
aria-selected = { isSelected }
role = "treeitem"
>
{ filename }
</ button >
Focus Management
const fileRef = useRef < HTMLButtonElement >( null );
useEffect (() => {
if ( isSelected ) {
fileRef . current ?. focus ();
}
}, [ isSelected ]);
Next Steps
Live Preview See how the preview tab works alongside file explorer
AI Code Generation Learn how AI generates the code you’re viewing
Real-Time Sandboxes Understand where files are stored and executed
Multi-Framework Support Explore different file structures across frameworks