A file tree + code preview Web Component built for custom AI agent interfaces, fully benchmarked against the right-side file preview panel in ChatGPT Codex.
Designed around the agent workflow: present a browsable project structure on one side and rich file previews on the other, so your agent can show, explain, and let users inspect code, configs, images, and more — directly inside the conversation.
- Codex-style side panel — draggable split view with the file tree on the right and the preview on the left, just like ChatGPT Codex.
- Agent-native interactions — copy selection, add to agent, Google search, plus an extensible context menu for custom agent actions.
- No framework lock-in — standard Web Component with Shadow DOM; drops into React, Vue, Svelte, Angular, or vanilla HTML.
- Rich file preview — syntax highlighting via Shiki, line numbers, breadcrumbs, image preview, and file metadata.
- Rendered markdown — Markdown files are rendered to a styled preview (headings, tables, blockquotes, code blocks highlighted with Shiki) with a one-click Preview / Source toggle.
- Production-grade tree — virtual scrolling, search/filter, keyboard navigation, 50+ file-type icons, and Git status indicators.
- Theme aware — light, dark, or system mode via
--trees-*CSS variables.
npm install @kayingai/kaying-filetree
# or
pnpm add @kayingai/kaying-filetree
# or
yarn add @kayingai/kaying-filetree<script type="module">
import '@kayingai/kaying-filetree';
</script>
<agent-file-explorer
id="explorer"
theme="dark"
shiki-theme="github-dark"
style="width: 100%; height: 100vh;"
></agent-file-explorer>
<script type="module">
const explorer = document.getElementById('explorer');
explorer.tree = {
id: 'root',
name: 'project',
path: '/project',
isDirectory: true,
children: [
{
id: 'src/index.ts',
name: 'index.ts',
path: '/project/src/index.ts',
isDirectory: false,
},
],
};
explorer.addEventListener('agent-file-open', (e) => {
const { path, node } = e.detail;
// load file content from your backend / filesystem
const content = `// content of ${node.name}`;
explorer.setFileContent(node.name, content, null, path, content.length);
});
</script>import '@kayingai/kaying-filetree';
import { useRef, useEffect } from 'react';
function FileExplorer({ tree }) {
const ref = useRef(null);
useEffect(() => {
if (!ref.current) return;
ref.current.tree = tree;
ref.current.addEventListener('agent-file-open', (e) => {
const { node, path } = e.detail;
// load content
ref.current.setFileContent(node.name, '// content', null, path, 10);
});
}, [tree]);
return (
<agent-file-explorer
ref={ref}
theme="dark"
shiki-theme="github-dark"
style={{ width: '100%', height: '100vh' }}
/>
);
}For an agent-facing panel, the project tree can remain the source of truth for the current location while the preview header only shows the selected filename:
explorer.showBreadcrumb = false;
explorer.autoOpenFirstFile = true;
explorer.tree = projectTree;
explorer.addEventListener('agent-file-open', async (event) => {
const { node, path } = event.detail;
const content = await loadFileFromAgentBackend(path);
explorer.setFileContent(node.name, content, null, path, content.length);
});
// Or run initialization after the tree has finished rendering.
explorer.addEventListener('agent-file-tree-ready', () => {
console.log('file tree is ready');
});Set autoOpenFirstFile = true when a project is first detected: the component
waits for the file tree to finish rendering, expands directories, and emits
agent-file-open for the first file. Consumers do not need to coordinate render
frames or access Shadow DOM. openFirstFile() remains available for manual use.
To open a specific file when the component mounts (e.g. resume a previous
session, or deep-link to a path), set initialFilePath together with tree:
explorer.tree = projectTree;
explorer.initialFilePath = '/project/README.md'; // auto-selects README.mdThe component expands every parent directory, scrolls the file into view, and
emits agent-file-open for it — no manual coordination required. Use
explorer.openFile(path) to jump to another file at any time.
| Property | Type | Default | Description |
|---|---|---|---|
tree |
FileNode | null |
null |
Root node of the file tree. |
theme |
'light' | 'dark' | 'system' |
'dark' |
Color theme. |
shikiTheme |
string |
'github-dark' |
Shiki highlighting theme. |
splitRatio |
number |
0.5 |
Initial ratio of the left (preview) panel. |
virtualized |
boolean |
true |
Enable virtual scrolling. |
showGitStatus |
boolean |
true |
Show Git status dots. |
showBreadcrumb |
boolean |
true |
Show the preview path breadcrumb. |
extraContextMenuActions |
ContextMenuAction[] |
[] |
Custom actions for the preview context menu. |
autoOpenFirstFile |
boolean |
false |
Expand the tree and select the first file after the tree is ready. |
initialFilePath |
string | null |
null |
Path (or node ID) to preselect when the tree is ready. Auto-expands parent directories and fires agent-file-open. |
| Callback | Type | Description |
|---|---|---|
onTreeReady |
(detail: { tree: FileNode }) => void |
Called after the file tree finishes rendering. |
| Method | Description |
|---|---|
setFileContent(filename, content, imageUrl?, path?, size?) |
Display a file in the preview panel. |
expandAll() |
Expand all directories. |
collapseAll() |
Collapse all directories. |
openFirstFile() |
Expand the tree and request the first file to open. |
openFile(path) |
Select the file at path (or node ID): expands its ancestors and fires agent-file-open. |
| Event | Detail | Description |
|---|---|---|
agent-file-open |
{ node: FileNode, path: string } |
Fired when a file is selected. |
agent-file-tree-ready |
{ tree: FileNode } |
Fired after the file tree finishes rendering. |
agent-add-to-context |
{ text: string, filename: string } |
Fired from the context menu "Add to agent". |
Can be used standalone for just the file tree.
Can be used standalone for just the code preview.
| Property | Type | Description |
|---|---|---|
filename |
string |
File name for language detection. |
content |
string |
File text content. |
imageUrl |
string | null |
URL for image preview. |
filePath |
string |
Full path for breadcrumb. |
showBreadcrumb |
boolean |
Whether to show the path breadcrumb. |
fileSize |
number | null |
File size in bytes. |
extraContextMenuActions |
ContextMenuAction[] |
Custom context-menu actions. |
By default Markdown files render in the preview panel. Use the 预览/源码 toggle in the header to switch between the rendered view and the raw syntax-highlighted source.
The component uses --trees-* CSS custom properties. Override them on the host element to customize:
agent-file-explorer {
--trees-bg: #0d1117;
--trees-fg: #c9d1d9;
--trees-accent: #58a6ff;
}# install dependencies
npm install
# start dev server
npm run dev
# type check
npm run typecheck
# build
npm run buildContributions are welcome! Please open an issue or submit a pull request. Make sure npm run typecheck and npm run build pass before submitting.
MIT © Kaying Studio
