Skip to content

s00d/vitepress-plugin-folder-tree

Repository files navigation

npm version npm downloads License Donate

vitepress-plugin-folder-tree

Interactive file/folder tree diagrams for VitePress. Write YAML or paste ASCII — get a beautiful, collapsible Vue component.


Features

Category What you get
Input YAML, ASCII (├── └──), external URL (YAML/JSON/JS), filesystem scan (from:)
Interaction Expand/collapse folders, keyboard nav, search & filter, context menu (right-click)
Toolbar Expand all, Collapse all, Copy as text, Download (.txt/.yaml/.json), Shell script (mkdir/touch)
Visual Guide lines, hover branch highlight, smooth animations, dark mode
Metadata description badge, note, highlight, icon (emoji/Iconify), href links
Git status added / modified / deleted indicators with color-coded badges
Tabs tab field on root nodes → Chrome-style switchable tabs
Deep linking URL hash → auto-expand, scroll, pulse animation
Preview preview field → tooltip on hover (600 ms delay)
Icons Per-extension SVG, custom emoji, Iconify classes, global iconMap
A11y ARIA attributes, prefers-reduced-motion, keyboard navigation

Quick Start

Install

# pnpm
pnpm add vitepress-plugin-folder-tree

# npm
npm install vitepress-plugin-folder-tree

# yarn
yarn add vitepress-plugin-folder-tree

Configure

1. VitePress config.vitepress/config.mts:

import { defineConfig } from 'vitepress'
import { withFolderTree } from 'vitepress-plugin-folder-tree'

export default withFolderTree(
  defineConfig({ title: 'My Docs' })
)

2. Import styles.vitepress/theme/index.ts:

import DefaultTheme from 'vitepress/theme'
import 'vitepress-plugin-folder-tree/style.css'

export default {
  extends: DefaultTheme,
}

CSS is shipped as a separate file for better performance, caching, and CSP compatibility.

Use

Write a fenced code block with language tree (or folder-tree / file-tree):

```tree
- name: src
  children:
    - name: components
      children:
        - Button.vue
        - Modal.vue
    - App.vue
    - main.ts
- package.json
```

Input Formats

YAML (structured)

Each root item is a string (file) or object (file/folder with metadata):

```tree
- name: src
  description: "Source code"
  children:
    - name: index.ts
      highlight: true
    - utils.ts
- README.md
```

ASCII (paste from terminal)

Paste tree command output — auto-detected by ├── / └── characters:

```tree
├── src
│   ├── index.ts
│   └── utils.ts
├── package.json
└── README.md
```

External URL

Load tree from a separate file (YAML, JSON, or JS module). All URL resolution happens at build time — no client-side fetching:

```tree
url: /trees/my-project.yaml
```

JS modules run in Node.js at build time, so you can use fs.readFileSync, fetch() for remote APIs, or any Node API:

// public/trees/my-project.js
import { readFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';

export default function () {
  const __dir = dirname(fileURLToPath(import.meta.url));
  const data = JSON.parse(readFileSync(join(__dir, 'data.json'), 'utf-8'));
  return [{ name: 'project', children: data.files }];
}

Filesystem scan

Generate tree from a real directory at build time:

```tree
from: ./src
depth: 3
exclude:
  - "*.test.ts"
```

With file content preview (hover to see, click to copy):

```tree
from: ./src
preview: true
maxPreviewSize: 4096
openDepth: 2
```
Option Type Default Description
from string Directory path to scan
depth number 10 Max directory nesting depth
preview boolean false Read file contents into tooltip
maxPreviewSize number 4096 Max file size (bytes) for preview
previewExtensions string[] common text exts Which file extensions to read
openDepth number -1 (all) Folder auto-open depth (0 = all closed)
exclude string[] built-in list Glob patterns to exclude
include string[] [] (all) Glob patterns to include
showRoot boolean true Wrap in root folder node
name string dir name Custom root folder name

Node Fields

Field Type Default Description
name string required* File or folder name
type 'file' | 'folder' auto Force node type (auto: has children → folder)
children array Nested items (makes this node a folder)
description string Badge shown next to name
note string Right-aligned label
highlight boolean false Accent background on the row
icon string Custom emoji or Iconify class
open boolean defaultOpen Initial expanded state
locked boolean false Prevent toggling this folder
href string URL — clicking the name navigates here
status 'added' | 'modified' | 'deleted' Git-style diff badge (A/M/D)
preview string Tooltip text on hover (600 ms delay)
tab string Root-level tab grouping

* name is optional when tab is set — the tab label is used as the name.


Diff / Git Status

- name: config.ts
  status: modified
- name: new-feature.ts
  status: added
- name: old-module.ts
  status: deleted
Status Visual
added Green text + A badge
modified Amber text + M badge
deleted / removed Red text + D badge + strikethrough

Tabs

Use tab on root-level nodes to create switchable views:

```tree
- tab: "Frontend"
  children:
    - name: src
      children:
        - App.vue
        - main.ts
    - package.json
- tab: "Backend"
  children:
    - name: src
      children:
        - index.ts
        - server.ts
    - package.json
```

Each tab shows the children of its root node. All toolbar operations (search, copy, download, shell script) work within the active tab.


Deep Linking

Append #path/to/file to the page URL:

https://example.com/docs/guide.html#src/components/Button.vue

The tree will:

  1. Switch to the correct tab (if tabs are used)
  2. Expand all ancestor folders
  3. Scroll to the node
  4. Highlight it with a pulse animation

Preview Tooltip

Add preview to show code/text in an IDE-style tooltip on hover:

- name: index.ts
  preview: "import { createApp } from 'vue'\ncreateApp(App).mount('#app')"

Tooltip appears after 600 ms, disappears on mouse leave.


Shell Script Generation

Click the >_ button in the toolbar to copy a shell script (mkdir -p + touch) that recreates the tree structure. Paste into your terminal to scaffold a project instantly.


Custom Icons

Per-node icon

- name: .env
  icon: "\U0001F510"

Global iconMap

Map filenames or extensions to emoji/Iconify classes:

export default withFolderTree(
  defineConfig({}),
  {
    iconMap: {
      'vite.config.ts': '\u26A1',
      'ts': 'logos:typescript-icon',
      'vue': 'logos:vue',
    },
  }
)

Priority: full filename → extension → default SVG.


Auto-generate from Filesystem

Field Type Default Description
from string required Path to scan (relative to project root)
depth number 10 Max scan depth
name string dir name Override root folder display name
showRoot boolean true Wrap in root folder
exclude string[] Glob patterns to skip
include string[] Glob patterns for files (folders always traversed)

Default excludes: node_modules, .git, .DS_Store, dist, .cache, .vitepress, __pycache__, .next, .nuxt, .idea, .vscode, coverage, and more.

```tree
from: ./src
include:
  - "*.ts"
  - "*.tsx"
exclude:
  - "*.test.ts"
options:
  defaultOpen: false
```

Context Menu

Access the context menu on any node:

Method How
Right-click Standard right mouse button
Ctrl + Click Hold Ctrl (or Cmd on Mac) and left-click
Shift + F10 Focus a row with arrow keys, then press Shift + F10

Menu actions:

  • Copy Name — file/folder name (e.g. Button.vue)
  • Copy Path — full tree path (e.g. src/components/Button.vue)

Press Escape to close.


Plugin Configuration

export default withFolderTree(
  defineConfig({}),
  {
    languages: ['tree', 'file-tree', 'folder-tree'], // trigger languages
    defaultOpen: true,       // folders expanded by default
    showToolbar: true,       // show toolbar
    showBadges: true,        // auto file/folder count badges
    interactive: true,       // allow expand/collapse
    root: process.cwd(),     // base path for `from:` resolution
    iconMap: {},             // global icon overrides
  }
)
Option Type Default Description
languages string[] ['folder-tree', 'file-tree', 'tree'] Code block languages
defaultOpen boolean true Default expand state
showToolbar boolean true Toolbar visibility
showBadges boolean true Auto count badges
interactive boolean true Allow interaction
root string process.cwd() Root for from: paths
iconMap Record<string, string> {} Filename/ext → icon mapping

Per-block Options

Override global settings per code block:

```tree
options:
  defaultOpen: false
  showToolbar: false
  showBadges: false
  interactive: false
tree:
  - name: src
    children:
      - index.ts
```

Keyboard Navigation

Key Action
/ Move focus between rows
Expand focused folder
Collapse focused folder
Enter / Space Toggle folder open/close
Shift + F10 Open context menu for focused row
Escape Close context menu / tooltip
Tab Enter / exit tree area

Modifier keys

Modifier Action
Ctrl + Click Open context menu (Win/Linux)
Cmd + Click Open context menu (Mac)

Toolbar Buttons

Icon Action
Expand all folders
Collapse all folders
Copy Copy tree as ASCII text
Download as .txt / .yaml / .json
>_ Copy shell script (mkdir + touch)
Search Filter nodes by name/description/note

Programmatic API

import {
  validateTreeInput,
  parseTree,
  parseAsciiTree,
  scanDirectory,
} from 'vitepress-plugin-folder-tree'

// Validate YAML
const result = validateTreeInput(yamlString)
// { valid: boolean, errors: string[], warnings: string[] }

// Parse YAML → TreeNode[]
const { tree, options } = parseTree(yamlString)

// Parse ASCII → TreeNode[]
const nodes = parseAsciiTree(asciiString)

// Scan filesystem → TreeNode[]
const tree = scanDirectory('/path', 5, ['node_modules'], ['*.ts'])

Development

pnpm install
pnpm build        # build plugin
pnpm dev          # dev server with examples
pnpm docs:build   # build example docs

Links


License

MIT © s00d

About

Interactive, collapsible file/folder tree diagrams for VitePress

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors