docs: bootstrap vault, roadmap and research
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
---
|
||||
type: research
|
||||
status: analyzed
|
||||
confidence: high
|
||||
created: 2026-10-17
|
||||
question: Which terminal file managers exist in 2026, what does each do best, and which ideas should viexp adopt or avoid?
|
||||
tags: [research, file-manager, yazi, nnn, lf, vifm, broot]
|
||||
---
|
||||
|
||||
# Competitor landscape (October 2026)
|
||||
|
||||
## Feature matrix
|
||||
|
||||
| Manager | Lang | TUI | Image preview | Extensions | Config | Async | State (last release) |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **yazi** | Rust | ratatui | native kitty/iterm2/sixel/ueberzug++/chafa | Lua (mlua, vendored) + `ya pack` | TOML ×3 | tokio, fully | Very active, v26.9.1 (2026-09) |
|
||||
| **nnn** | C | ncurses | via FIFO plugins | any executable; `NNN_PIPE`/`NNN_FIFO` protocol | none (flags+env) | no (tiny by design) | Active, v5.3 (2026-08) |
|
||||
| **lf** | Go | own | sixel native; else scripts | shell cmds, `push`, remote socket API | lfrc DSL | async I/O | Active, r42 (2026-07) |
|
||||
| **joshuto** | Rust | tui-rs/termion | external scripts | config only | 5 TOMLs | partial | Semi-stalled, v0.9.9 (2025-05) |
|
||||
| **felix** | Rust | termion | chafa + bat | none | YAML | partial | Dormant since 2025-04 |
|
||||
| **hunter** | Rust | own | halfblock/sixel/kitty | mime actions | conf+keymap | heavily threaded | Dead (2020/2022) |
|
||||
| **vifm** | C | ncurses | community scripts only | vim-like `:commands` + Lua | vifmrc | no | Active, v0.14.4 (2026-05) |
|
||||
| **broot** | Rust | crossterm+termimad | kitty graphics | verbs, skins | TOML | interruptible search | Very active, v1.61 (2026-10) |
|
||||
| **mc** | C | own | none | extfs VFS helpers | menus | background ops | Active, 4.8.33 |
|
||||
|
||||
## What the winners do right
|
||||
|
||||
- **yazi** (~43k stars): ~25-crate workspace, everything on tokio; micro/macro worker pools with
|
||||
priorities (mime/previews preempt copying); **paged preloading** — only visible rows get work;
|
||||
three cancellation mechanisms (tokio abort, atomic tickets for CPU work, instruction-count
|
||||
checks for Lua); two-pass image pipeline (downscale+cache → fit to terminal); DDS pub/sub
|
||||
between instances without a daemon. v25.4 doubled startup speed via config parser rewrite.
|
||||
- **nnn**: 150 KiB binary, zero config; plugins = executables with env-var contract
|
||||
(`$1` hovered file, `$2` cwd, `$3` picker) and a tiny write-back protocol via `NNN_PIPE`;
|
||||
`NNN_FIFO` streams hovered paths so external scripts render live previews.
|
||||
- **lf**: external `previewer(file, w, h, x, y)` + `cleaner` contract with exit-code-controlled
|
||||
caching; `push` simulates keystrokes making mappings composable; server model over Unix socket.
|
||||
- **broot**: every keystroke interrupts the running search; fuzzy+regex+content combined with
|
||||
boolean operators; collapsed tree respecting .gitignore.
|
||||
- **felix**: built-in trash + undo/redo + zoxide `z` jump — small features with huge daily value.
|
||||
|
||||
## Ideas worth stealing (ranked)
|
||||
|
||||
1. yazi's paged task scheduling + prioritized workers + cancellation tickets (perceived speed)
|
||||
2. Protocol auto-detection adapter layer behind one trait (kitty/iterm2/sixel/ueberzug++/chafa)
|
||||
3. lf's previewer/cleaner argv contract — cheapest path to infinite formats
|
||||
4. nnn's FIFO hover event stream for external live previews
|
||||
5. yazi's TOML tables with `prepend_*`/`append_*` merge semantics for rules
|
||||
6. TOML for config + (later) Lua for behavior + pinned plugin packages
|
||||
7. broot's interruptible, composable search
|
||||
8. lf's `push` key-simulation primitive
|
||||
9. felix's trash + undo + `z` jump
|
||||
10. Performance as a marketable feature (both nnn and yazi publish benchmarks)
|
||||
|
||||
## Anti-patterns to avoid
|
||||
|
||||
| Trap | Victim | Lesson |
|
||||
|------|--------|--------|
|
||||
| Full ranger parity alone | joshuto | 5 config files, janky command line, 14-month release gap → audience left for yazi. Ship narrow and polished. |
|
||||
| Nightly compiler + heavy C deps | hunter | GStreamer + nightly = unbuildable for users. Stable Rust, spawned binaries for media. |
|
||||
| No native graphics path | vifm, nnn, mc | Preview zoo of mutually incompatible scripts. Build the adapter layer early. |
|
||||
| Bespoke config DSL | lf | A whole parser users must learn; standard TOML + scripting runtime beats it. |
|
||||
| Doctrinal non-features | lf (no tabs) | Minimal defaults and power features are not contradictory (yazi proves it). |
|
||||
| Monolith | mc | Thin core + clean contracts. |
|
||||
| API churn without pinning | yazi (mild) | Plugin version pinning story from day one. |
|
||||
| Silence without governance | felix, hunter | Release cadence = perceived life. |
|
||||
|
||||
## Outcome
|
||||
Shaped ADR-001 (ratatui), ADR-002 (extension phases), ADR-004 (protocol chain), and the
|
||||
roadmap order (core narrow first, previews second, plugins third, Lua last).
|
||||
|
||||
## Sources
|
||||
- yazi: github.com/sxyazi/yazi, https://yazi-rs.github.io/blog/why-is-yazi-fast/, releases v25–v26
|
||||
- nnn: github.com/jarun/nnn + plugins/README.md
|
||||
- lf: github.com/gokcehan/lf + doc.md
|
||||
- joshuto, felix, hunter, vifm, broot, mc: repos + release APIs (dates verified 2026-10-17)
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
type: research
|
||||
status: analyzed
|
||||
confidence: high
|
||||
created: 2026-10-17
|
||||
question: How should viexp's file-type modules and plugins be designed so Linux users can disable, replace and extend them easily?
|
||||
tags: [research, plugins, architecture, lua, wasm]
|
||||
---
|
||||
|
||||
# Extension architecture research
|
||||
|
||||
## Mechanism comparison
|
||||
|
||||
| Mechanism | Safety | Ease for users | Host complexity | Hot reload | Proof |
|
||||
|---|---|---|---|---|---|
|
||||
| External process contract (argv/env/stdout) | low (= shell) | **highest** — any language | very low | free | nnn plugins, lf previewer, ranger scope.sh |
|
||||
| Declarative rules (TOML / rifle / mailcap) | high (data, not code) | high — one-liners | low | free | rifle.conf, yazi `[plugin]` tables |
|
||||
| In-process Lua (mlua) | medium — sandboxes leak (2025 CVE); Luau is the hardened dialect | medium | medium-high | yes | yazi (mlua 0.12, vendored Lua 5.5), wezterm, neovim |
|
||||
| Rhai | high | low (niche language) | low-medium | yes | niche |
|
||||
| JS (deno_core/V8) | medium-high | medium | high (~30 MB) | yes | Deno |
|
||||
| WASM (wasmtime/extism) | **highest** | lowest (compile a component) | high (WIT churn) | yes | zellij |
|
||||
| Host-language plugins (Python) | lowest (monkeypatching) | high for pythonistas | medium, fragile | poor | ranger |
|
||||
|
||||
## Case-study lessons
|
||||
|
||||
- **ranger**: the script pipeline (scope.sh + rifle) carried ~90% of real customization; the
|
||||
Python layer exists because previews/commands need *state*. Three disjoint mechanisms = confusion.
|
||||
- **nnn**: cleanest "process as API": `$1`=file, `$2`=cwd, `$3`=picker; context in env
|
||||
(`NNN_FIFO` hover stream); control back via `NNN_PIPE` with tiny opcodes (`1c/etc` = cd ctx 1).
|
||||
- **lf**: minimum viable preview contract = `(path, w, h, x, y)` + `cleaner` hook + exit-code
|
||||
caching semantics. Document SIGPIPE/exit codes or users suffer.
|
||||
- **yazi**: built-ins are preset plugins routed through the same `[plugin]` TOML tables users
|
||||
edit (`prepend_*`/`append_*` merge) — one dispatch model for built-in and user code. mlua
|
||||
with vendored Lua so users install nothing. Still BETA → in-process scripting is the most
|
||||
expensive layer to stabilize.
|
||||
- **neovim**: Lua won because the host API is rich, stable and uniform — half-exposed APIs are
|
||||
worse than external scripts.
|
||||
- **zellij**: WASM gives real sandboxing but locks architecture (single-threaded synchronous
|
||||
plugins; WASIp2 migration "a significant undertaking"). Not worth it for v1.
|
||||
- **kakoune**: `disabled_hooks` regex = cheap runtime kill-switch worth copying.
|
||||
|
||||
## Cargo feature flags = compile-time layer, not the primary switch
|
||||
|
||||
Features (`pdf`, `video`, … default-on) trim binary size and C deps, but feature unification
|
||||
breaks packagers, combinations explode, and removing a default feature is a breaking change.
|
||||
**Runtime `enabled = false` in modules config must remain the real disable mechanism**; every
|
||||
`#[cfg]`-gated module should degrade to a TOML rule pointing at an external tool.
|
||||
|
||||
## Recommended phasing (adopted)
|
||||
|
||||
- **v0.1 — declarative core**: every file-type module = rule in a TOML registry mapping
|
||||
mime/glob → previewer/opener, with per-module `enabled` switch and `prepend_/append_` merge.
|
||||
Built-ins already dispatch through this table → replacing/reordering needs no recompile.
|
||||
- **v0.2 — external process protocol**: freeze the script contract —
|
||||
`argv = (path, width, height, x, y)`, env context, FIFO/socket write-back, `cleaner` hook.
|
||||
Ship bash + Python reference plugins. This is where 90% of Linux-user extensibility lives.
|
||||
- **v1.0+ — embedded Lua** (mlua, vendored): only for what scripts cannot do — stateful,
|
||||
in-process previews/fetchers — registered through the same tables. Evaluate Luau if
|
||||
sandboxing becomes a requirement. WASM explicitly rejected for v1.
|
||||
|
||||
## Outcome
|
||||
Adopted as [[decisions/ADR-002-extension-architecture|ADR-002]] and [[decisions/ADR-003-file-type-modules|ADR-003]].
|
||||
|
||||
## Sources
|
||||
- yazi docs (plugins overview, configuration), yazi Cargo.tomls (mlua 0.12 lua55 vendored)
|
||||
- nnn plugins README; lf doc.md; rifle(1); RFC 1524 (mailcap)
|
||||
- zellij plugin docs + discussion #4139; wezterm config docs; neovim lua.txt; kakoune FAQ
|
||||
- mlua/rhai/gluon repos; deno blog; Lua sandbox-bypass CVE coverage (2025)
|
||||
- Cargo book: features.md
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
type: research
|
||||
status: analyzed
|
||||
confidence: high
|
||||
created: 2026-10-17
|
||||
question: What exactly makes ranger great, what makes it slow and fragile, and what must a Rust successor keep or fix?
|
||||
tags: [research, ranger, file-manager]
|
||||
---
|
||||
|
||||
# Ranger deep dive
|
||||
|
||||
## Research question
|
||||
Ranger is the most feature-complete terminal file manager but famously slow in specific
|
||||
scenarios. We need a precise keep/fix list before writing a single line of viexp.
|
||||
|
||||
## Findings
|
||||
|
||||
### Project state (Oct 2026)
|
||||
Python + curses, Miller-columns UI. 17.4k stars, ~900 open issues, last tag v1.9.4 (Nov 2024),
|
||||
commits still trickling. Maintainers openly state they lack manpower (issue #1717).
|
||||
|
||||
### Architecture
|
||||
- Config = 4 files with **different merge semantics**: `rc.conf` merges, `rifle.conf` replaces,
|
||||
`scope.sh` replaces (~700-line bash), `commands.py` merges (Python subclassing).
|
||||
- Preview pipeline = external `scope.sh`; communicates via exit codes:
|
||||
`0` = stdout preview, `1` = none, `2` = fixed-height text, `5` = image written to cache dir.
|
||||
- Plugins = Python monkeypatching with full interpreter access ("Plugins have full access to
|
||||
your computer!"). Ecosystem: ranger-devicons, ranger-zoxide, ranger-fzf-filter, etc.
|
||||
- Image protocols: w3m, ueberzug, urxvt, kitty, sixel, iterm2, terminology.
|
||||
|
||||
### Why it is slow (root causes)
|
||||
| Cause | Evidence |
|
||||
|-------|----------|
|
||||
| Synchronous UI loop — directory listing + `stat` of every file run inline | issue #1173: 100k files = multi-second uninterruptible hang (open since 2018) |
|
||||
| No async I/O anywhere; threading refactor stalled | issue #1025 literally labeled `wants-async` |
|
||||
| `statvfs` on **every status-bar refresh** → 4–10 s j/k lag on sshfs/NFS/CryFS/s3fs | issue #1025; vifm stays fast in identical setups |
|
||||
| Python interpreter startup ~1 s cold | issue #1717 |
|
||||
| Every preview spawns scope.sh + a chain of external binaries | architecture |
|
||||
|
||||
### Pain points (user-verified)
|
||||
1. ueberzug unmaintained and X11-only; ueberzugpp fixes exist but are bolted on
|
||||
(ranger #3188, #3158, #2995; ueberzugpp #122 fractional-scaling bug on Sway/Hyprland).
|
||||
2. Network mounts unusable without manual `setlocal` hacks or `F` (freeze_files).
|
||||
3. `:flat` broken past ~30k files (#379).
|
||||
4. No built-in trash — ArchWiki documents `map DD shell gio trash` as the workaround.
|
||||
5. Config fragmentation: four files, four formats, four merge behaviors.
|
||||
|
||||
### What users love (must keep)
|
||||
Vim keys, Miller columns, `:bulkrename` via `$EDITOR` (the killer feature), rifle-style
|
||||
ordered opener menu (`r`), preview breadth (20+ types), tabs/bookmarks/marks, `:filter`,
|
||||
`--choosefile/--choosedir` + cd-on-exit, per-path options (`setlocal`).
|
||||
|
||||
## Keep / Fix table
|
||||
|
||||
| Keep | Fix |
|
||||
|------|-----|
|
||||
| Miller 3-pane layout, vim key vocabulary | Native binary startup (no interpreter) |
|
||||
| `:bulkrename` via `$EDITOR` | Async dir loading / stat / preview — UI never blocks |
|
||||
| Rifle-style opener rules + chooser menu | No `statvfs` per frame; poll with timeout+backoff |
|
||||
| Exit-code preview contract (scope.sh idea) | First-class trash (`gio trash`-compatible) + undo |
|
||||
| Preview breadth with caching | Native kitty/sixel/ueberzug++ graphics, Wayland-safe |
|
||||
| `setlocal` per-path options | One config format (TOML) with documented merge semantics |
|
||||
| `--choosefile/--choosedir`, cd-on-exit | Virtual scrolling for 100k+ entry dirs |
|
||||
| `F` freeze escape hatch | Automatic freeze on network/fuse mounts |
|
||||
|
||||
## Outcome
|
||||
Direct input to [[decisions/ADR-002-extension-architecture|ADR-002]] (extension model),
|
||||
ADR-004 (preview protocols) and the roadmap's async requirements (Sprint 8).
|
||||
|
||||
## Sources
|
||||
- https://github.com/ranger/ranger — README, wiki, examples/plugins
|
||||
- https://wiki.archlinux.org/title/Ranger
|
||||
- Issues: #1717 (startup), #1173 (100k files), #1025 (network lag), #379 (`:flat`),
|
||||
#3188 / #3158 / #2995 (ueberzug family)
|
||||
- https://github.com/seebye/ueberzug (unmaintained notice), https://github.com/jstkdng/ueberzugpp
|
||||
- Community: r/commandline "Is yazi overhyped?", r/linux FM threads, x-cmd six-FM comparison,
|
||||
https://mqdir.com/blog/file-management/nnn-vs-yazi
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
type: research
|
||||
status: analyzed
|
||||
confidence: high
|
||||
created: 2026-10-17
|
||||
question: Which Rust crates should render previews (images, PDF, DJVU, EPUB, markdown, media) in a terminal, with what licenses and fallbacks?
|
||||
tags: [research, rust, crates, preview, ratatui]
|
||||
---
|
||||
|
||||
# Rust preview stack (October 2026)
|
||||
|
||||
Versions verified against crates.io metadata on 2026-10-17.
|
||||
|
||||
## Recommended stack
|
||||
|
||||
| Concern | Crate | Why | Notes / license |
|
||||
|---|---|---|---|
|
||||
| TUI | **ratatui 0.30** + **crossterm 0.29** | De-facto standard; 0.30 split into `ratatui-core`/`-widgets` facade; yazi & felix use it | MIT; MSRV 1.88 |
|
||||
| Images in TUI | **ratatui-image 11.1** | kitty, sixel, iTerm2, halfblocks + `Picker` protocol/font detection; lives in the ratatui org | MIT |
|
||||
| Image decode | **image 0.25** | yazi's choice; jpeg/png/gif/webp/bmp/tiff (+avif feature) | MIT/Apache |
|
||||
| Resize | **fast_image_resize 6.1** (SIMD) or `image::thumbnail` (triangle filter, yazi-style) | hot loop | MIT/Apache |
|
||||
| Sixel encode | **quantette 0.6** (+ moxcms, palette) | yazi's actual sixel pipeline | MIT |
|
||||
| PDF | **pdfium-render 0.9** + pdfium binary | only mature page→image in-process option | pdfium is **Apache-2.0** (b redistribute, ~5 MB); spawn `pdftoppm` as fallback |
|
||||
| DJVU | **djvu-rs 0.43** (pure Rust, render→image) | first usable pure-Rust DjVu decoder | **6 months old** — keep `ddjvu` fallback |
|
||||
| EPUB | hand-rolled: **zip** + quick-xml/html-to-text | the `epub` crate is **GPL-3.0 — viral** if statically linked | avoid the trap |
|
||||
| Markdown | **comrak 0.56** → styled ratatui lines, or **termimad** (ready-made renderer) | termimad = fastest integration (broot uses it) | MIT |
|
||||
| Syntax highlight | **syntect 5.3** | yazi's choice; regex Sublime syntaxes, no C deps | tree-sitter = heavier, per-language grammars |
|
||||
| Media metadata | **spawn `ffprobe -print_format json`** | no linking pain; what yazi does | ffmpeg-next ties you to system FFmpeg — avoid |
|
||||
| Video thumbnail | **spawn `ffmpeg -ss … -vframes 1 -vf scale…`** (+ `attached_pic` cover-art fallback) | yazi's proven recipe | — |
|
||||
| Audio (optional in-process) | **symphonia 0.6** | pure-Rust metadata/album art | MPL-2.0 |
|
||||
| MIME | **infer 0.22** (magic bytes) + **xdg-mime 0.4** (extension DB) | fast sniffing + correct `text/*` | `new_mime_guess` unmaintained — avoid |
|
||||
| FS events | **notify 8.2** | yazi's watcher | CC0 |
|
||||
| Fuzzy | **nucleo 0.5** | Helix's matcher, library-first | MPL-2.0 |
|
||||
| Config | **serde + toml 1.1** | yazi's set | solid |
|
||||
| Errors/logs | **anyhow + thiserror 2.x + tracing** | boring and correct | — |
|
||||
| Async | **tokio 1.x** with results marshalled to a single-threaded UI actor | yazi's exact architecture; ecosystem is tokio-first | start threads-only, adopt tokio in Sprint 8 |
|
||||
|
||||
## Image protocol fallback chain (first match wins)
|
||||
|
||||
1. **kitty graphics** (unicode-placeholder mode) — kitty ≥0.28, Ghostty ← *our primary target*
|
||||
2. **iTerm2 inline** — WezTerm, iTerm2, Warp, VSCode terminal
|
||||
3. **sixel** — foot, Konsole 22.04+, Windows Terminal ≥1.22, WezTerm, rio
|
||||
4. **ueberzug++ subprocess** — X11 + Wayland (wlroots compositors: sway/hyprland/niri/wayfire);
|
||||
the only option for alacritty; not GNOME/KDE compositors
|
||||
5. **chafa / halfblocks** — works everywhere incl. SSH and tmux
|
||||
6. text: filename + size + mime
|
||||
|
||||
Terminal facts (Oct 2026): kitty has **no sixel by design**; alacritty rejected both protocols;
|
||||
tmux needs `allow-passthrough on` and never passes kitty-old. The daily terminal here is
|
||||
**kitty without a multiplexer** → tier 1 covers the primary user; tiers 3–5 cover everyone else.
|
||||
|
||||
## Thumbnail caching (yazi-proven)
|
||||
|
||||
Decode once at preview size (max ~600×900, triangle filter) → persist rendered pages/frames to
|
||||
a disk cache dir → keep decoded previews in an in-process LRU → preload neighbors in background.
|
||||
Re-encoding for a protocol is cheap; re-decoding is not.
|
||||
|
||||
## Risk notes
|
||||
|
||||
- GPL traps: `epub` crate (static link), ueberzug++ (fine — spawned), pdftoppm (fine — spawned).
|
||||
- pdfium must be shipped as a binary (~5 MB/platform) or static-linked via pdfium-render `static`.
|
||||
- djvu-rs is young; config-selectable `ddjvu` fallback required.
|
||||
- ratatui 0.30 subcrate split may cause ecosystem churn in 2026 — pin carefully.
|
||||
|
||||
## Outcome
|
||||
Feeds ADR-001, ADR-004 and Sprints 5–8 crate choices.
|
||||
|
||||
## Sources
|
||||
- https://github.com/sxyazi/yazi (adapter/Cargo.tomls, preset plugins pdf.lua/video.lua)
|
||||
- https://yazi-rs.github.io/docs/image-preview, /blog/why-is-yazi-fast/
|
||||
- crates.io version metadata (ratatui, ratatui-image, image, fast_image_resize, quantette,
|
||||
pdfium-render, djvu-rs, epub, comrak, termimad, syntect, symphonia, notify, nucleo, infer, xdg-mime)
|
||||
- https://github.com/ajrcarey/pdfium-render; pdfium LICENSE (Apache-2.0)
|
||||
- https://www.arewesixelyet.com; https://sw.kovidgoyal.net/kitty/graphics-protocol
|
||||
- https://github.com/jstkdng/ueberzugpp; https://github.com/matyushkin/djvu-rs
|
||||
Reference in New Issue
Block a user