Skip to content

Sidebar explorer

Replace Lite’s built-in explorer when you want to organize files by project, tags, or your own rules. Start with a small file list: show the current directory’s files and open them in the main area.

This Lite-only capability requires plugin API 3.1.0. Prepare a test Space with a few files. The example deliberately lists files without implementing an expandable directory tree.

Create plugin.json and src/tree.ts. Use this manifest:

{
"apiVersion": 1,
"id": "example.file-list",
"name": "File List",
"version": "0.1.0",
"requires": {
"pluginApi": "3.1.0"
},
"workspace": {
"files": {
"read": true
}
},
"views": [
{
"id": "tree",
"title": "File List",
"kind": "page",
"entry": "./src/tree.ts"
}
],
"placements": [
{
"location": "sidebar/explorer",
"view": "tree"
}
]
}

sidebar/explorer makes the plugin an available sidebar browser. workspace.files.read allows file listings across the Space. It is still a page View, but unlike a normal navigation page it receives explorer state.

Write this in src/tree.ts:

import type { Mount } from "@eidos.space/plugin-sdk"
const mount: Mount = async (ctx, root) => {
const { explorer, fs, ui } = ctx.capabilities
if (!explorer || !fs || !ui.openFile) throw new Error("Explorer unavailable")
const openFile = ui.openFile
const list = document.createElement("div")
const status = document.createElement("p")
status.setAttribute("role", "status")
root.append(list, status)
let revision = 0
const refresh = async () => {
const current = ++revision
try {
const state = explorer.read()
const files = await fs.list(state.rootDirectory ?? "", {
recursive: false,
includeDirectories: false,
})
if (ctx.signal.aborted || current !== revision) return
const buttons = files.map((file) => {
const button = document.createElement("button")
button.textContent = file.name
button.style.display = "block"
button.setAttribute(
"aria-current",
String(file.path === state.activePath)
)
button.onclick = () => {
void openFile(file.path).catch((error) => {
status.textContent = String(error)
})
}
return button
})
list.replaceChildren(...buttons)
status.textContent = files.length ? "" : "No files in this folder."
} catch (error) {
if (!ctx.signal.aborted && current === revision)
status.textContent = String(error)
}
}
ctx.subscriptions.add(explorer.watch(() => void refresh()))
ctx.subscriptions.add(await fs.watch("", () => void refresh()))
await refresh()
}
export default mount

explorer.read() provides the current directory and open file. When rootDirectory is null, the empty path means the Space root. fs.list reads one directory level; ui.openFile opens a selected file in the main area.

The two subscriptions serve different purposes: explorer.watch responds to browsing state, and fs.watch responds to filesystem changes. The former has no initial callback, so the example explicitly calls refresh().

The revision counter discards late results after rapid navigation. Failed refreshes preserve the current list and display an error.

  1. Load plugin.json in Plugin Manager and enable it for the Space.
  2. Select File List under Plugins → Choose file explorer.
  3. Check that the sidebar lists files. Click one to open its configured View.
  4. Add or remove a test file in the Space and check that the list refreshes.
  5. Switch back to the built-in explorer in Plugin Manager.

Lite saves the selection per Space. If the plugin fails, Lite restores the built-in heading and tree with a retry action.

Set includeDirectories to true to include directories, even empty ones. On expansion, call fs.list(path, { recursive: false, includeDirectories: true }) for that directory and keep expansion state in the plugin. Directory clicks should expand or navigate; only file clicks should call ui.openFile.

Read explorer.read().sort for the selected sort order; modifiedAtMs supports time sorting. This example does not implement sorting, directory navigation, or selection styling. Add those for a complete browser.

The plugin owns the entire explorer area, including its heading, search, sort controls, and file toolbar. Lite retains window navigation and application controls. Global Space text search temporarily overlays the sidebar without unmounting it. Explorer state does not grant file access; the host checks permissions separately.

See Explorer and Filesystem for interface details.