Plugins
Plugins are Lua 5.4 files. strata loads every *.lua file and every */init.lua in ~/.config/strata/plugins/ when it starts. List names under plugins.disabled to skip some.
-- ~/.config/strata/plugins/hello.lua
strata.map("g p", function(ctx)
strata.cd(os.getenv("HOME") .. "/projects")
end, "Go to projects")
strata.command("hello", function(ctx, args)
strata.notify("hello " .. args .. " from " .. ctx.cwd)
end, "Say hello")Installing plugins
strata plugin installs plugins from git repositories. A repository holds one plugin, with init.lua at its root or in the folder --path names:
strata plugin add someone/strata-foo # GitHub owner/repo; installed as "foo"
strata plugin add https://example.com/x/bar.git --ref v1.2
strata plugin add someone/plugins --path baz # one plugin from a repository with several
strata plugin list # installed plugins and their commits
strata plugin update # newest commit of each plugin's branch
strata plugin remove fooEach plugin is copied, without its git history, to ~/.config/strata/plugins/<name>/. ~/.config/strata/plugins.toml records its source and the commit installed. Keep that file with your dotfiles and run strata plugin install on another machine to get the same plugins at the same commits. --ref follows a branch, or pins a tag or commit; without it a plugin follows the repository's default branch. Restart strata to load new plugins. You need git for all of this.
A plugin runs with your permissions, like any program you install, so add only ones you trust.
Official plugins
| Plugin | What it adds |
|---|---|
git | Branch and change count in the top bar, a Git panel (g s) and :git <args> |
bookmarks | B to bookmark a directory, ' to jump, :bookmark-remove |
archive | Lists .7z and .rar archives in the preview (extracting and compressing are built in) |
zoxide | z to jump by query, Z to pick, and visited directories are recorded |
protect | Refuses to delete, move, rename or overwrite items tagged keep (option tag), and shows a lock column (option column = false hides it) |
Enable them with plugins.enabled in the config. All but protect are on by default.
Options
If a plugin returns a table with a setup function, strata calls it with the plugin's options:
local M = {}
function M.setup(opts) -- [plugins.options.hello] greeting = "hi"
strata.statusline(function() return opts.greeting end)
end
return MContext
Callbacks receive a ctx table:
| Field | Description |
|---|---|
cwd | Directory of the focused panel |
hovered | Path under the cursor, or nil |
hovered_is_dir | Whether hovered is a directory |
selected | List of marked paths |
panel | Focused panel number (1-based) |
view | files, dashboard, docker, nas or usage |
theme | Active theme |
scheme | local, sftp, docker, archive, s3, webdav, ftp or rclone |
icons | Whether Nerd Font icons are enabled |
tags | Tags of the hovered item |
filter | The panel's filter text |
listing | What a flat listing shows (find *.log, flatten…), or nil |
tab | Tab number (1-based) |
API
Registration
| Function | Description |
|---|---|
strata.map(keys, fn(ctx), desc?) | Bind a key sequence such as "g p" or "ctrl+e" |
strata.command(name, fn(ctx, args), desc?) | Add a :name command |
strata.on(event, fn(ctx, data)) | React to an event |
strata.statusline(fn(ctx) -> string?) | Add a segment to the top bar |
strata.panel{ name, title?, render = fn(ctx, w, h) -> lines } | Draw a panel next to the file panels |
strata.previewer{ ext = {...}, fn = fn(path, w, h) -> lines? } | Preview these extensions; return nil to pass |
strata.column{ name, width?, style?, fn = fn(entry) -> text?, style? } | Add a column to the file list |
strata.sort{ name, title?, fn = fn(entry) -> key } | Add a sort order to the sort menu and :sort |
Actions
| Function | Description |
|---|---|
strata.cd(path) | Change directory |
strata.action(name) | Run a built-in action |
strata.run(line) | Run a command-palette line, e.g. "theme nord" |
strata.notify(msg, level?) | Show a message: "info", "warn" or "error" |
strata.select(title, items, fn(item?)) | Show a fuzzy picker |
strata.input(prompt, default?, fn(text?)) | Ask for text |
strata.toggle_panel(name) | Show or hide a plugin panel |
strata.exec(cmd) | Run an interactive command with the terminal handed over |
strata.refresh() | Reload the listings |
strata.mark(paths, clear?) | Mark these paths in the focused panel; clear drops other marks first |
strata.show(title, paths) | Show these paths as a flat list in the focused panel |
strata.tag(paths, tag) / strata.untag(paths, tag?) | Add or remove a tag (all tags without one); paths may be one path |
Events
Handlers get the context and a data table describing what happened:
| Event | data fields |
|---|---|
startup, quit, cd, paste | none |
hover | path: the item the cursor moved to |
open | path: a file being opened |
create | path, dir (true for a directory) |
rename | from, to; once per item, also for pattern and bulk renames |
delete | paths, permanent (false when going to the trash) |
tag | paths, tag ("" when every tag was removed), added |
find | title (find …, flatten, tag red…), count, truncated |
job_done | label, state (done, failed or cancelled), error |
mount | device, path: a removable drive was mounted |
unmount | device: a removable drive was ejected |
view | view: files, dashboard, docker, nas or usage |
tab | tab: the tab switched to (1-based) |
theme | name: the theme picked |
layout_save, layout_load | name: the layout saved or opened (:layout, --layout, default_layout) |
strata.on("rename", function(ctx, e)
strata.notify(e.from .. " → " .. e.to)
end)Refusing operations
before_* events fire before an operation starts. A handler cancels it by returning false (optionally followed by a reason) or { cancel = true, reason = "..." }, and the reason is shown.
| Event | data fields |
|---|---|
before_delete | paths, permanent |
before_rename | paths, to (new paths in the same order); also for pattern and bulk renames |
before_copy, before_move | paths, dest |
before_overwrite | paths (items that would be replaced), sources, dest |
Each also carries tags: a table from path to tags for the items in paths and every tagged item inside them.
strata.on("before_delete", function(ctx, e)
for path, tags in pairs(e.tags) do
for _, t in ipairs(tags) do
if t == "keep" then return false, path .. " is tagged keep" end
end
end
end)A handler that fails or runs longer than half a second is reported and cancels nothing. Undo and redo do not ask.
Columns and sort orders
Column and sort functions get an entry: path, name, is_dir, size, modified (epoch seconds or nil), ext and tags.
A column function returns the text and optionally a colour (muted, accent, error, warning, success, info, a tag colour, a colour name or #rrggbb). Cells are worked out only for rows on screen and kept until the listing changes. A sort function returns a key: numbers before text, nil last, ties by name; folders-first and reverse still apply (not in the tree view).
strata.column({ name = "lines", width = 6, fn = function(entry)
if entry.is_dir or entry.size > 1e6 then return nil end
local n = 0
for _ in io.lines(entry.path) do n = n + 1 end
return tostring(n), "muted"
end })
strata.sort({ name = "tagged", title = "Tagged first", fn = function(entry)
return #entry.tags > 0 and 0 or 1
end })Helpers
| Function | Description |
|---|---|
strata.shell(cmd, cwd?) -> stdout, code, stderr | Run a command and capture its output |
strata.which(program) -> bool | Whether a program is on PATH |
strata.mkdir(path) -> bool | Create a directory and its parents |
strata.config_dir(), strata.data_dir() | Where to read config and keep state |
strata.version, strata.platform | strata's version and the OS |
Lua's io, os and string libraries are available too.
Callbacks run on the UI thread, so keep them quick. statusline and panel callbacks run every couple of seconds — cache anything expensive. before_* handlers, columns and sort orders are stopped when they take too long. Errors appear as notifications and never crash strata.
The official plugins are good examples to start from.