Skip to content

Understanding the File System

The virtual file system in @nhtio/vue-editors is designed to provide a robust, reactive, and extensible foundation for browser-based code editing, playgrounds, and REPLs. It consists of three main abstractions:

  • NVEFileSystem: The reactive, in-memory file system manager.
  • NVEDirent: The file or directory entry abstraction.
  • NVEFileTree: The reactive file tree structure for hierarchical and flat views.

The NVEFileSystem Abstraction

NVEFileSystem is a reactive, in-memory file system that manages a collection of files and directories (dirents). It is designed for use in Vue applications and is safe for SSR and hot-reload scenarios.

Key features:

  • Shallow reactivity (suitable for Vue 3's reactivity system)
  • Event-driven (creation, deletion, move, commit, error, etc.)
  • Supports file/directory creation, reading, moving, copying, unlinking, and globbing
  • Robust disposal and serialization
  • Provides a reactive file tree for UI rendering

Basic usage:

ts
import { NVEFileSystem } from '@nhtio/vue-editors/fs'
const fs = new NVEFileSystem()
const file = fs.createDirent('App.vue', '/src', '<template>Hello</template>')

See the API documentation for a full list of methods and events.

The NVEDirent Abstraction

NVEDirent represents a single file or directory in the file system. Each dirent is reactive and exposes properties such as name, parentPath, content (for files), and dirty state.

  • Files have a content property (string), and can be edited directly.
  • Directories do not have content, but can be created and managed in the same way.

Example:

ts
const file = fs.createDirent('main.ts', '/src', 'console.log("hi")')
file.content = 'console.log("hello world")' // marks file as dirty

The Reactive File Tree (NVEFileTree)

The file system provides a reactive file tree structure for rendering and traversing files and directories in both flat and hierarchical forms. This is especially useful for building file explorers, tree views, and navigation components in your UI.

Accessing the File Tree

You can access the file tree via the asTree getter on your NVEFileSystem instance:

ts
const fs = new NVEFileSystem()
const tree = fs.asTree // instance of NVEFileTree

Flat and Hierarchical Views

  • tree.flat: Returns a flat map of all file and directory entries (including virtual directories). The keys are full paths, and the values are dirent or virtual dirent instances.
  • tree.tree: Returns an array of root nodes (NVEFileTreeNode), each representing a file or directory. Each node exposes its children, selection state, and more. Each node also exposes a depth property indicating its depth in the tree (0 for root, 1 for children, etc).

Example: Rendering a File Tree

ts
const tree = fs.asTree
const rootNodes = tree.tree
rootNodes.forEach(node => {
  console.log(node.dirent.name, node.depth) // node.depth is 0 for root, 1 for children, etc.
  if (node.children) {
    node.children.forEach(child => console.log('  ', child.dirent.name, child.depth))
  }
})

File Tree Reactivity

The file tree is fully reactive:

  • Adding, removing, or moving files/dirs in the file system will automatically update the tree.
  • Node properties like selected and folded are reactive and can be used for UI state.

Virtual Directories

The tree includes virtual directories to ensure a complete hierarchy, even if some parent folders do not exist as real dirents. This is useful for rendering a consistent tree structure.

Use Cases

  • File explorers and sidebar navigation
  • Breadcrumbs and path pickers
  • Drag-and-drop reordering
  • UI state management (selection, folding/collapsing)

See the NVEFileTree API Reference and NVEFileTreeNode API Reference for more details.


Reactivity

Both NVEFileSystem and NVEDirent are shallowly reactive:

  • The file system instance itself is reactive (using Vue's reactive()), so you can use it in Vue components and templates.
  • Each dirent is also reactive, so changes to properties like content or dirty will update the UI automatically.
  • The file tree (asTree) is also reactive and updates in response to file system changes.

Note: Reactivity is shallow—nested objects inside dirents are not deeply reactive, but the main properties are.


Dirty State Tracking

A core feature is the ability to track unsaved changes ("dirty" state):

  • Each NVEDirent has a dirty property that is true if the file or directory has unsaved changes.
  • The file system exposes a reactive dirty getter, which is true if any dirent is dirty.

Example:

ts
if (fs.dirty) {
  // Show a save prompt or enable a "Save" button
}

This makes it easy to build UIs that reflect the save state of the editor.

The Commit Workflow

The commit workflow allows you to persist or synchronize changes in the file system:

  • Per-dirent commit: You can provide an onDirentCommit handler to the file system, which will be called for each dirty dirent when commit() is called.
  • File system commit: You can provide an onCommit handler, which will be called with all dirents for batch or atomic commits.
  • If both are provided, the file system-level handler takes precedence.
  • Commit errors are surfaced via events (commitError, direntCommitError).

Example:

ts
const fs = new NVEFileSystem({
  onCommit: async (dirents) => {
    // Save all files to a server
    await saveToServer(dirents)
    return true
  }
})
await fs.commit()

See NVEFileSystem.commit() and NVEDirent.commit() for more details.

Events and Error Handling

The file system emits events for all major actions (creation, deletion, move, commit, errors, etc.), allowing you to build reactive UIs and handle errors gracefully.

  • Listen for events using .on(event, handler) or .once(event, handler).
  • Remove listeners with .off(event, handler).

See the API documentation for a full list of events.

Serialization and Deserialization

You can serialize the entire file system to a compact string for storage or sharing, and restore it later:

ts
const serialized = fs.toSerialized()
const restored = NVEFileSystem.fromSerialized(serialized)

Integration Tips

  • Use the dirty state to prompt users to save changes before closing or navigating away.
  • Use events to update UI elements (e.g., file explorer, tabs) in response to file system changes.
  • Integrate with the Monaco Editor by passing file URIs and content from the file system.
  • Use the file tree (asTree) for building file explorers and navigation components.
  • For advanced usage, see the API docs and Quickstart.

Further Reading