@@ -0,0 +1,655 @@
# Twilight Commander (`tlc`) — Comprehensive Improvement Plan
**Author: ** Sisyphus (orchestrator) · **Date: ** 2026-06-18
**Subject: ** Full MC parity assessment + ratatui-era modernization roadmap
**Scope: ** Visuals · Functions · Keyboard shortcuts · Dialogs · Themes · Architecture
**Reference baseline: ** Midnight Commander 4.8.33 (C source at `local/recipes/tui/mc/source/` )
---
## Platform Priority (foundational)
**Red Bear OS is the PRIMARY platform for tlc. Linux is the SECONDARY
platform.**
TLC is the Red Bear OS file manager. Every decision in this plan is made
Redox-first:
1. **A change must build and run on `x86_64-unknown-redox` before it is
considered done.** The Linux host build is a development convenience and a
portability proof — it is not the shipping target. The `/usr/bin/tlc`
that lands in the `redbear-mini` and `redbear-full` ISOs is the product.
2. **No Linux-only dependency may become a hard requirement. ** If a feature
needs a crate that does not cross-compile to Redox (or that needs a C
toolchain Redox lacks), it goes behind a Cargo feature that is **off by
default** and stays off in the Redox build, OR it is not adopted.
3. **The `cfg(target_os = "redox")` / `cfg(unix)` split is the supported
portability seam.** The existing `Cargo.toml`
`[target.'cfg(target_os = "redox")']` block (`libredox` , `redox_event` ,
`redox_syscall` , `redox_termios` ) is the Redox path; everything else
builds on `std` + `cfg(unix)` for Linux/macOS dev.
4. **Acceptance for any visual/IO change includes a Redox cross-compile +
QEMU smoke** (`make all CONFIG_NAME=redbear-mini` , then run `tlc` in the
guest). A change that only works on the Linux host is incomplete.
This priority order directly shapes Phase R (the backend migration must be
Redox-safe — see A.1 caveat) and the subshell work (Phase 4 must use Redox's
pty/scheme surface, not just Linux `openpty` ).
---
## 0. Executive Summary
TLC is a **pure-Rust, `#![deny(unsafe_code)]` ** reimplementation of Midnight
Commander built on **ratatui 0.30 + termion 4 ** . As of 2026-06-18 it is
**84+ `.rs` files, ~28k LoC, 698 passing tests ** , and ships 30 filemanager
dialogs, a 497-line F9 menubar, 16 editor modules, 6 viewer modules, 8
built-in skins, and 6 i18n locales. It cross-compiles to a 3.3 MB
statically-linked Redox ELF.
**Assessment: ~65% of MC's *visible* surface is implemented; ~35% remains. **
The headline gaps are not "does feature X exist" — most do. They are:
1. **A real subshell ** (PTY-based persistent shell; today it drops/recreates
the Tui). This is MC's signature power feature.
2. **A real syntax-highlighting engine ** (MC ships 102 `.syntax` files; tlc
has a `syntax.rs` module but no bundled rules).
3. **Mouse support ** — * blocked today * because tlc uses the **termion **
backend, which does not surface mouse/resize/focus events the way
crossterm does.
4. **A handful of power-user dialogs ** (background jobs, external panelize,
full Options → Configuration, confirmation toggles).
5. **Visual polish that ratatui makes trivial but tlc hasn't adopted **
(Scrollbar widget, `Table` row-highlight, `Stylize` trait, `Clear` popup
consistency, proper 256/16-color fallback per cell).
This document supersedes the gap lists in `PLAN.md` §14–§15 by reframing the
work around **five externally-visible axes the user cares about ** , adding the
**ratatui modernization track ** the internal PLAN omits, and sequencing the
work so each phase ships a demonstrably better tlc.
---
## 1. Comprehensive Current-State Assessment
### 1.1 What exists and works (verified by direct source read, 2026-06-18)
| Layer | Modules | Evidence |
|---|---|---|
| **Keymap ** | 37 `Cmd` variants, all F1– F11 bound | `src/keymap/mod.rs` (377 lines) |
| **F9 menubar ** | 5 pull-down menus (Left/File/Command/Options/Right) | `src/filemanager/menubar.rs` (497 lines) |
| **Filemanager dialogs (30) ** | copy, move, mkdir, delete, find, info, permission, owner, link, symlink, tree, hotlist, connection_manager, usermenu, skin_dialog, help, quickcd, pattern (select/unselect), overwrite, quit, rename, layout, panel_options, config, mc_ext, percent, cmdline, exec, menubar, panel | `src/filemanager/*.rs` |
| **Editor (16 modules) ** | buffer (gap), cursor, mode, prompt, search, replace, goto, save, format, history, bookmark, completion, macro, syntax, view | `src/editor/*.rs` |
| **Viewer (6 modules) ** | text, hex, source (gz/bz2 capped 256 MiB), search, goto | `src/viewer/*.rs` |
| **Terminal layer ** | color (Theme), event, mc_skin, popup, status, **subshell ** , mod | `src/terminal/*.rs` |
| **Widgets ** | button, check, radio, input, dialog, gauge | `src/widget/*.rs` |
| **VFS ** | local, tar, zip, cpio, extfs, sftp (feature), ftp (feature) | `src/vfs/*.rs` |
| **Skin system ** | 8 built-in (default-dark/light, mc-classic/dark/dark-gray, high-contrast, solarized-dark, nord), TOML parser, runtime Alt-S, config persistence | `src/skin/` , `src/terminal/color.rs` , `src/filemanager/skin_dialog.rs` |
| **Shared palette ** | 33-slot `redbear-tui-theme` crate, WCAG 2.1 AA-verified, 256/16-color fallback | `local/recipes/tui/redbear-tui-theme/` |
| **i18n ** | 6 locales (en/de/es/fr/ja/zh-CN), 97 keys each, `t!()` wired into 17 dialog strings | `source/locales/*.yml` |
| **Ops engine ** | copy/move/delete/mkdir with symlink-safety (lstat), progress | `src/ops/*.rs` |
### 1.2 Binding coverage vs MC
MC's `mc.default.keymap` defines **380 active bindings across 15 contexts **
(~77 in `[filemanager]` + `[filemanager:xmap]` alone). TLC binds **37 Cmd
variants in the global keymap** plus in-context handlers (Ctrl-X prefix in
`app.rs` , viewer/editor local keys). Mapping the filemanager+xmap layer:
| Category | MC bindings | TLC done | Coverage |
|---|---|---|---|
| F-keys (F1– F11) | 11 | 11 | **100% ** |
| File ops (copy/move/del/mkdir/rename) | 8 | 8 | **100% ** |
| Panel navigation (tab/swap/reload/hidden/layout) | 9 | 9 | **100% ** |
| Marking (toggle/range/invert/group select/unselect) | 7 | 7 | **100% ** |
| Sort + history + hotlist | 9 | 9 | **100% ** |
| Ctrl-X extended (chmod/chown/link/symlink/compare) | 8 | 5 | 63% |
| Subshell / shell integration | 6 | 2 | 33% |
| Power dialogs (jobs, panelize, vfs list, screen list) | 8 | 0 | 0% |
| **Total filemanager-tier ** | * * ~77** | * * ~55** | * * ~71%** |
The remaining 29% is concentrated in three clusters: the full PTY subshell,
the power-dialog family (Jobs/Panelize/VFS/Screen-list), and the long-tail
editor/viewer niceties.
### 1.3 Critical correctness posture (resolved)
The 2026-06-13 audit found 4 CRITICAL defects (symlink-following in
delete/copy/count, unbounded gz decompress). **All four are fixed ** with
`lstat` + 256 MiB caps and covered by 5 symlink-safety tests. The
`#![deny(unsafe_code)]` guarantee holds — zero `unsafe` in any `.rs` file.
### 1.4 Known weaknesses (carry-over from `PLAN.md` §3.B)
| # | Severity | Item | Status |
|---|---|---|---|
| W1 | **HIGH ** | 5 production unwraps remain (4× `Mutex::lock().unwrap()` now poison-recovered, 1× `Tui::default()` legit) | Acceptable; documented |
| W2 | **HIGH ** | `vfs/zip.rs` reads whole archive into memory (no 256 MiB cap like viewer has) | **Open ** — streaming zip needed |
| W3 | MEDIUM | SFTP `AcceptAnyKey::check_server_key` always returns `Ok(true)` | **Open ** — TOFU known_hosts needed |
| W4 | MEDIUM | `filemanager/mod.rs` ~1597 LOC, `editor/mod.rs` ~1227 LOC — over-sized | **Open ** — split recommended |
| W5 | MEDIUM | VFS backends defined but filemanager only operates on local paths (no UI enters `extfs://` /`tar://` ) | **Open ** — wiring gap |
| W6 | LOW | 2 clippy warnings (1 intentional glob ASCII compare, 1 bitflags macro artifact) | Acceptable |
---
## 2. Improvement Plan — Five Axes
The user asked for parity across **visuals, functions, keyboard shortcuts,
dialogs, and themes** — "but better graphics since we use ratatui." Each axis
below lists (a) current state, (b) MC parity target, (c) ratatui-era
improvement beyond MC.
### AXIS A — VISUALS & RENDERING
#### A.1 Backend migration: termion → crossterm (PREREQUISITE for mouse/Windows)
**Why this is on the critical path: ** TLC uses `ratatui` with
`features = ["termion"]` . The termion backend does **not ** deliver mouse
events, focus events, or bracketed-paste in a portable way. MC supports
mouse column-click sorting, double-click open, and scrollbar drag. Until tlc
has a backend that surfaces those events, **all mouse features are blocked **
and the "better graphics" promise can't be fully delivered. Crossterm is the
ratatui backend that exposes `MouseEvent` , `ResizeEvent` , `FocusEvent` , and
`PasteEvent` .
* * ⚠ REDOX-FIRST CAVEAT (this is the primary platform).** Crossterm's Redox
support is **not assumed ** . Termion already works on Redox today (via the
`redox_termios` /`redox_event` deps already in `Cargo.toml` ); crossterm must
be **proven ** on `x86_64-unknown-redox` before it can replace termion. The
migration is therefore a **verify-then-adopt ** task, not a blind swap:
**Action (Redox-first sequence): **
1. **Spike first: ** `cargo build --target x86_64-unknown-redox` a throwaway
crate that pulls `crossterm` and calls `enable_raw_mode` + reads one
event. If it does **not ** cross-compile or does not link on Redox,
**stop ** — go to the fallback below.
2. **QEMU smoke: ** if the spike builds, run it inside a `redbear-mini` guest
and confirm raw mode + a key event round-trips. Mouse event optional for
the spike.
3. **Only if both pass: ** `Cargo.toml` →
`ratatui = { version = "0.30", default-features = false, features = ["crossterm"] }` ,
drop the `termion` direct dep, and rewrite `src/terminal/event.rs` to
consume `crossterm::event::Event` . Keep the `Key` /`Cmd` translation layer
— only the source enum changes. Verify `Ctrl-O` subshell still restores
the terminal cleanly under crossterm on **both ** Redox and Linux.
**Fallback if crossterm does NOT support Redox (do not abandon mouse): **
Keep termion as the Redox backend and add crossterm behind a Cargo feature
for the Linux/dev build, selected via `cfg` :
``` toml
[ dependencies ]
ratatui = { version = "0.30" , default-features = false }
termion = "4" # Redox + Unix fallback
crossterm = { version = "0.28" , optional = true } # Linux/dev mouse path
[ features ]
default = [ ]
mouse-crossterm = [ "dep:crossterm" , "ratatui/crossterm" ]
# Redox build: ratatui/termion (current behavior), no mouse.
# Linux dev with mouse: cargo build --features mouse-crossterm.
```
`src/terminal/event.rs` then has two adapter impls gated by the feature.
This keeps the **primary platform stable ** while still delivering mouse on
the secondary platform and leaving the door open to full crossterm if/when
its Redox support matures. Track upstream crossterm Redox status and
re-evaluate each release.
**Risk: ** Medium (was Low before the Redox-first constraint). The spike in
step 1 is the de-risking step — it's cheap and tells you which branch of the
plan to take. Do **not ** merge a backend swap that has not cross-compiled to
Redox.
#### A.2 Adopt ratatui 0.30 idioms tlc currently hand-rolls
| Pattern | Today | ratatui idiom | Benefit |
|---|---|---|---|
| Panel rendering | custom loop over rows | `Table` widget with `widths` , `header` , `row_highlight_style` , `highlight_symbol(">>")` | Column alignment, selection symbol, header/footer for free |
| Long lists | manual scroll offset | `List` + `Scrollbar` stateful widget | Consistent scroll UX, native scrollbar glyph |
| Dialog frames | manual `Block` borders | `Block::bordered().title(...).border_style(theme.border)` + `Clear` for popup | Idiomatic, themeable borders |
| Styling | `Style::default().fg(...).bg(...)` chains | `Stylize` trait: `.fg().bg().bold().reversed()` | Concise, composable |
| Layout | manual Rect math in places | `Layout::vertical([Length(1), Min(0), Length(1)])` destructured | Resize-safe, declarative |
| App bootstrap | manual `Tui::new()` + raw panic | `ratatui::run()` + `set_panic_hook()` | Automatic terminal restore on panic |
**Action: ** A focused refactor pass (not a rewrite). Each file migrates one
widget at a time, gated by `cargo test` . The `Table` migration of `panel.rs`
is the single highest-impact change — it makes the panel look substantially
more polished (proper column headers, highlight symbol, scrollbar) for
minimal effort.
#### A.3 Color fallback hardening
The shared palette ships `fallback_256()` and `fallback_16_name()` , but tlc
renders `Color::Rgb` directly. On a 256-color terminal (common in tmux/screen
without `Tc` ), every `Rgb` silently degrades to the terminal's nearest
match — which can be ugly.
**Action: ** In `terminal/color.rs` , add a `Theme::resolved_colors(capability)
-> Theme<Color>` adapter that pre-computes the palette for the detected
capability (`ColorCapability::TrueColor | Color256 | Color16` ). Probe once at
startup via terminfo/`COLORTERM` . This is what MC's `tty_colorize` does; tlc
should match it.
#### A.4 Visual parity items MC has and tlc lacks
| Item | MC | TLC | Effort |
|---|---|---|---|
| File-type glyphs (`/` , `~` , `@` , `*` , `=` , `\|` ) in name column | 13 glyphs | Basic | Small — `Panel::glyph_for(entry)` |
| Sort indicator arrows in column header (`▼ size` ) | Yes | No | Small |
| Filename scroll indicators `{` `}` when truncated | Yes | No | Small |
| Free-space readout in panel footer (`<Free: 12G>` ) | Yes | No | Small |
| Mini-status that adapts (SEL/MID/TOTAL, symlink target, `UP--DIR` ) | Yes | Basic | Medium |
| Ruler / position percent in viewer | Yes | No | Small |
| Hex view 16-byte offset column + ASCII gutter | Yes | Partial (`hex.rs` exists) | Medium |
---
### AXIS B — FUNCTIONS (Feature Parity)
Ranked by user-visible impact. Each item lists MC source location,
acceptance criterion, and effort.
#### B.1 PTY-based persistent subshell *(HIGH — MC's signature feature)*
- **MC:** `src/subshell/*` — PTY fork, 7 shell-type init scripts, CWD-pipe
sync, `SIGSTOP` /`SIGCONT` handshake, Ctrl-O toggle.
- **TLC today:** `src/terminal/subshell.rs` drops the entire `Tui` , spawns
`$SHELL` , waits, recreates `Tui` . Works as an escape hatch but loses the
"panels overlay a live shell" behavior MC users expect.
- **Acceptance:** Ctrl-O shows the shell * behind * translucent panels; the
cmdline can `cd` into the shell's CWD; typing in the shell while panels are
hidden works; Ctrl-O again restores panels with the shell's new CWD.
- **Approach:** `portable-pty` or `rustix::pty` crate; non-blocking poll of
the PTY master fd in the event loop; render captured shell output to a
background buffer.
- **Effort:** Large (~2000 LoC, 7 shell init scripts). Sequence **after **
the crossterm migration.
#### B.2 Syntax highlighting engine *(HIGH — editor credibility)*
- **MC:** `src/editor/syntax.c` + 102 `.syntax` files in `misc/syntaxes/` .
- **TLC today:** `src/editor/syntax.rs` exists but ships no bundled rules.
`syntect` is a default feature (oniguruma-based) but isn't wired into the
render path.
- **Acceptance:** Opening a `.rs` /`.c` /`.py` /`.sh` file shows highlighted
keywords, strings, comments using the active theme's palette.
- **Two implementation paths:**
1. **syntect path (recommended): ** wire `syntect` into `editor/view.rs` .
Ship a curated subset of `.sublime-syntax` files. Map syntect styles →
theme palette (8 token classes). Pros: mature, 100+ languages. Cons:
larger binary (+~600 KB), regex compile cost.
2. **MC-syntax path: ** port MC's own `.syntax` rule format (first-rule-
matches, context stack). Pros: byte-compatible with MC's 102 files, small.
Cons: reinvents a tokenizer.
- **Effort:** Medium-large. Path 1 is faster to value.
#### B.3 Power dialogs (Jobs / Panelize / VFS list / Screen list)
- **MC:** `src/filemanager/{dialog,panelize}.c` , `src/vfs/vfs.c` .
- **TLC today:** None of these.
- **Acceptance:** Ctrl-X j shows background copy/move jobs with cancel;
Ctrl-X ! runs a shell command and shows its stdout lines as a read-only
panel; Ctrl-X a lists active VFS mounts.
- **Effort:** Medium. Reuses the existing dialog framework. Depends on B.4
(background ops) for Jobs.
#### B.4 Background file operations
- **MC:** `file.c` forks a child for long copies; the UI stays responsive;
the Jobs dialog lists/cancels them.
- **TLC today:** Copy/move/delete are synchronous with a modal progress bar.
- **Acceptance:** A "Background" button on the progress dialog moves the op
to a worker thread; the UI returns to panels; Ctrl-X j lists running ops.
- **Effort:** Medium-large. Needs a `BackgroundJobs` registry, cancellation,
and a thread/async runtime. The `async-vfs` feature already pulls `tokio` .
#### B.5 Compare directories (Ctrl-X d)
- **MC:** Marks files that differ between the two panels (quick / size-only
/ thorough modes).
- **TLC today:** `Cmd::CompareDirs` exists and is handled, but verify depth
(quick vs thorough).
- **Effort:** Small (likely already done — verify and add thorough mode).
#### B.6 External mc.ext dispatch *(verify wiring)*
- **MC:** `mc.ext` INI maps file extensions → Open/View/Edit commands.
- **TLC today:** `src/filemanager/mc_ext.rs` and `percent.rs` (17 escapes)
exist. **Verify ** Enter/F3/F4 actually consult `mc_ext` before falling
back to the built-in editor/viewer.
- **Effort:** Small (likely a wiring audit + tests).
#### B.7 Editor long-tail
| Feature | MC key | Effort |
|---|---|---|
| Rectangular (column) block selection | Ctrl-Q | Medium |
| Format paragraph | Alt-P | Small |
| Spell check (pipe to aspell) | Alt-B | Small |
| Persistent cursor position per file | (auto) | Small |
| Match bracket | Alt-B (verify vs viewer bookmark conflict) | Small |
#### B.8 Viewer long-tail
| Feature | MC key | Effort |
|---|---|---|
| Hex **edit ** mode (mutate bytes) | F4 in hex | Medium |
| Nroff mode (backspace-bold/underline) | Alt-N | Small |
| Magic mode (preprocess via mc.ext) | Alt-M | Small |
| Growing/tail-f buffer | F-r | Small |
| Goto byte offset | Alt-g | Small |
---
### AXIS C — KEYBOARD SHORTCUTS (Keymap Completeness)
TLC's global keymap covers the F-layer and core ops. The remaining gaps are
in **context-local ** bindings (editor, viewer, dialog-internal) and the
Ctrl-X prefix family. Concrete missing bindings to add:
| Binding | MC key | Action | Effort |
|---|---|---|---|
| Ctrl-X v | relative symlink | `Cmd::SymlinkRelative` | Small |
| Ctrl-X Ctrl-S | edit symlink target | `Cmd::SymlinkEdit` | Small |
| Ctrl-X d | compare dirs | **verify bound ** (Cmd exists) | — |
| Ctrl-X ! | external panelize | `Cmd::Panelize` | Small (after B.3) |
| Ctrl-X j | background jobs | `Cmd::Jobs` | Small (after B.3) |
| Ctrl-X a | VFS list | `Cmd::VfsList` | Small (after B.3) |
| Alt-` | screen list | ` Cmd::ScreenList` | Small |
| Alt-Shift-E | viewed/edited history | ` Cmd::EditHistory` | Small |
| Ctrl-Space | directory size | ` Cmd::DirSize` | Small |
| Alt-= | equal split | ` Cmd::EqualSplit` | Small |
| Alt-Shift-←/→ | adjust split ratio | ` Cmd::SplitLess`/` More` | Small |
| Alt-a / Alt-Shift-A | insert current/other path into cmdline | ` Cmd::InsertPath` | Small |
| Ctrl-Enter / Alt-Enter | insert cursor filename into cmdline | ` Cmd::InsertFile` | Small |
| Shift-F10 | quiet quit (no confirm) | ` Cmd::QuitQuiet` | Small |
| Ctrl-Z | suspend (SIGTSTP) | ` Cmd::Suspend` | Small |
| Alt-! | filtered view (pipe) | ` Cmd::FilteredView` | Small |
**Also:** add a **Learn Keys** dialog (Options → Learn Keys) so users on
unusual terminals can rebind. This is MC's escape hatch for broken terminals
and is cheap to port.
---
### AXIS D — DIALOGS (Inventory & Gaps)
TLC already has **30 filemanager dialog modules** — the dense part of MC's
dialog surface is covered. The remaining dialog work:
| Dialog | MC location | TLC | Priority |
|---|---|---|---|
| Configuration (verbose/safe-delete/pause-after-run/shell-patterns) | boxes.c | ` config_dialog.rs` exists — **verify completeness** | Medium |
| Confirmation toggles (per-op) | boxes.c | **Missing** | Medium |
| Listing format editor (BNF format string) | boxes.c | Missing (cycle only) | Low |
| Sort order (full radio dialog) | boxes.c | cycle only (` Alt-T`) | Medium |
| Display bits (8th bit, full 8-bit) | boxes.c | Missing | Low |
| Learn keys | boxes.c | Missing | Low |
| Virtual FS settings (ftp/sftp defaults) | boxes.c | Missing | Low |
| Advanced chown (combined owner+perm) | chmod_dialog.c | Missing | Low |
| Chattr (ext2 inode flags) | chattr.c | Missing (ext2-only) | Low |
| Filtered view (pipe through cmd) | cmd.c | Missing | Medium |
| Background jobs | dialog.c | **Missing** | Medium |
| External panelize | panelize.c | **Missing** | Medium |
| Active VFS list | vfs.c | Missing | Low |
| Directory hotlist editor | hotlist.c | ` hotlist.rs` exists — verify add/remove | Low |
**Pattern note:** Every TLC dialog should be rendered through the
` widget::dialog` helper with ` Clear` (popup pattern) and theme-derived
border styles for visual consistency. Audit the existing 30 for consistency.
---
### AXIS E — THEMES (Skin System)
TLC's skin system is **genuinely ahead of MC** in architecture (shared
33-slot WCAG-verified palette, runtime switching, TOML format). Gaps are in
**breadth** and **runtime fidelity**.
#### E.1 Ship the 40 MC reference skins as importable themes
` source/mc-skins/skins/` already contains **40 MC ` .ini` skin files**
(default, dark, nicedark, sand256, gotar, xoria256, julia256, modarin256,
the modarcon16 family, the seasons family, etc.). Today only **5 of these**
are approximated as built-in ` const Theme` values (mc-classic, mc-dark,
mc-dark-gray + the redbear defaults).
**Action:** Write a one-time ` mc-skin-import` tool (or build-script) that
parses each ` .ini` and emits a ` Theme` constant. Add an "MC Skins" section to
the Alt-S dialog. This gives tlc **45+ skins** with zero runtime cost.
**MC ` .ini` → TLC ` Theme` mapping** (define once):
- ` [core]` ` _default_`, ` selected`, ` marked`, ` markselect` → background/foreground/selection_*/marked_*
- ` [dialog]`, ` [error]`, ` [menu]`, ` [popupmenu]`, ` [buttonbar]`, ` [statusbar]`, ` [help]` → corresponding Theme slots
- ` [filehighlight]` ` directory`/` executable`/` symlink`/` device`/` stalelink`/` hardlink`/` socket` → file-type slots
- ` [editor]`, ` [viewer]`, ` [diffviewer]` → editor/viewer slots
- 256-color hex (` color005f87`) and truecolor (` #005f87 `) syntax both parse.
#### E.2 User skin directory + hot-reload
- Scan ` ~/.config/tlc/skin/*.toml` (already done in ` skin_dialog.rs`).
- **Add:** ` Alt-Shift-R` to hot-reload the current skin from disk (lets
users iterate on a skin without restarting).
- **Add:** skin inheritance (` extends = "mc-dark"`) so user skins only
override the slots they change.
#### E.3 Truecolor/256/16 capability detection
See A.3. The skin must degrade gracefully. Add a ` --color-test` CLI
subcommand that prints the palette in the detected capability for diagnosis.
#### E.4 cub / redbear-info / redbear-netctl palette migration
The shared crate exists; ` cub` is wired; ` redbear-info` and
` redbear-netctl-console` still use raw ANSI-16. Migrating them makes the
whole Red Bear TUI ecosystem visually coherent (same brand red, same
selection color). Tracked in ` redbear-tui-theme/README.md`.
---
## 3. ratatui Best Practices to Adopt (Grounded in v0.30 docs)
These are the modernization items the internal ` PLAN.md` does **not** cover.
Each is justified by the ratatui 0.30 docs retrieved for this plan.
### 3.1 ` ratatui::run()` + panic hook *(safety)*
ratatui 0.30's canonical bootstrap is ` ratatui::run(|terminal| { ... })`,
which handles ` enable_raw_mode`, alternate-screen enter/exit, and terminal
restoration **including on panic**. TLC currently does this manually in
` Tui::new()`/` Drop`. Migrating to ` ratatui::run()` + ` set_panic_hook()`
removes a class of "terminal left broken after crash" bugs. (Source:
ratatui 0.30 lib.rs ` run()` example.)
### 3.2 Stateful widgets via ` render_stateful_widget`
TLC's panel keeps a manual ` selected`/` offset`. ratatui's ` List`/` Table` +
` ListState`/` TableState` manage selection/offset natively and render the
` highlight_symbol` and ` row_highlight_style`. Migrating the panel to
` Table` (which supports column widths, header row, footer for mini-status)
is the single biggest visual win for the least code. (Source: ratatui 0.30
` Table` example with ` row_highlight_style(Style::new().reversed())` and
` highlight_symbol(">>")`.)
### 3.3 ` Stylize` trait for concise styling
Replace ` Style::default().fg(c).bg(c2).add_modifier(Modifier::BOLD)` with
` .fg(c).bg(c2).bold()`. Composable, readable, and the idiomatic ratatui 0.30
style. (Source: ratatui 0.30 ` Table` Stylize example: ` .red().italic()`.)
### 3.4 ` Clear` + centered ` Rect` for every popup
The popup pattern is: ` frame.render_widget(Clear, popup_area)` then render
the dialog widget. TLC's ` terminal/popup.rs` and ` widget/dialog.rs` do this;
**audit the 30 dialogs** for consistency — any dialog rendered without ` Clear`
first leaves the underlying panel bleeding through.
### 3.5 ` Scrollbar` widget for long lists
ratatui 0.30 ships a ` Scrollbar` stateful widget. The file panel (long
directories), the find-results list, the hotlist, and the help dialog all
benefit. Today tlc renders scroll position only implicitly (cursor row).
### 3.6 ` Layout` with ` Constraint::{Fill, Length, Min}` — no manual Rect math
Any place tlc computes ` Rect { x, y, width, height }` by hand should migrate
to ` Layout::vertical([Length(1), Min(0), Length(1)])` destructured into
` [title, main, status]`. Resize-safe and declarative. (Source: ratatui 0.30
Layout example.)
### 3.7 Immutable-state rendering recommended
ratatui 0.30 docs explicitly recommend **immutable** state patterns
(` fn render(frame, area, &state)`) for most cases, reserving
` StatefulWidget` for widgets that mutate state during render. TLC's
` render(frame, &self, theme)` already follows this — keep it. Avoid
` Rc<RefCell<State>>` interior mutability unless a widget truly needs it.
### 3.8 Test the TUI with ` TestBackend`
ratatui ships ` TestBackend` + ` buffer!` / ` assert_buffer_eq`. TLC has 698
unit tests on logic but **zero render-snapshot tests**. Adding
` TestBackend`-based render tests for each widget/dialog catches visual
regressions automatically. High leverage, low cost.
---
## 4. Prioritized Roadmap
Sequenced so each phase ships a demonstrably better tlc and unblocks the
next. Effort in "sessions" (one session ≈ a focused half-day).
### Phase R — ratatui Modernization (PREREQUISITE) *[~4 sessions]*
| # | Task | Axis | Effort |
|---|---|---|---|
| R1 | **Redox-first spike:** verify crossterm cross-compiles + runs on ` x86_64-unknown-redox`; if yes → swap backend, if no → dual-backend feature flag (A.1) | A.1 | 1.5 |
| R2 | Bootstrap via ` ratatui::run()` + panic hook | 3.1 | 0.5 |
| R3 | Migrate panel to ` Table` + ` TableState` + ` highlight_symbol` | A.2/3.2 | 1.5 |
| R4 | Add ` Scrollbar` to panel/help/hotlist/find | 3.5 | 0.5 |
**Why first:** unblocks mouse **without breaking the primary platform** (R1
spike gates the approach), makes the panel look substantially better
immediately (R3), and every subsequent visual improvement lands on the
modern base. R1 is Redox-gated by design.
### Phase 1 — Visual Parity Polish *[~3 sessions]*
| # | Task | Axis |
|---|---|---|
| V1 | File-type glyphs in name column | A.4 |
| V2 | Sort indicator + filename scroll indicators | A.4 |
| V3 | Free-space + adaptive mini-status | A.4 |
| V4 | Color capability detection + 256/16 fallback (A.3) | A.3 |
| V5 | ` Stylize` trait sweep + ` Clear` audit across 30 dialogs | 3.3/3.4 |
| V6 | Render-snapshot tests with ` TestBackend` (3.8) | 3.8 |
### Phase 2 — Theme Breadth *[~2 sessions]*
| # | Task | Axis |
|---|---|---|
| T1 | mc-skin importer: convert 40 ` .ini` → ` Theme` consts | E.1 |
| T2 | "MC Skins" section in Alt-S dialog | E.1 |
| T3 | Skin ` extends =` inheritance + ` Alt-Shift-R` hot-reload | E.2 |
| T4 | ` --color-test` CLI subcommand | E.3 |
### Phase 3 — Function Gaps (Power User) *[~6 sessions]*
| # | Task | Axis |
|---|---|---|
| F1 | Background file ops + thread registry | B.4 |
| F2 | Jobs dialog (Ctrl-X j) | B.3 |
| F3 | External panelize (Ctrl-X !) | B.3 |
| F4 | VFS list (Ctrl-X a) | B.3 |
| F5 | Compare directories thorough mode | B.5 |
| F6 | mc.ext dispatch audit + tests | B.6 |
| F7 | Syntax highlighting (syntect path) | B.2 |
### Phase 4 — Subshell (Signature Feature) *[~5 sessions]*
| # | Task | Axis |
|---|---|---|
| S1 | **Redox PTY path** via ` scheme:pty`/` redox-scheme` + **Linux path** via ` rustix::pty`; abstract behind one trait so both platforms share the CWD-sync logic | B.1 |
| S2 | Shell init scripts (bash/zsh/fish) — verify ` ion`/default Redox shell works on the primary platform | B.1 |
| S3 | CWD-pipe sync + Ctrl-O overlay rendering | B.1 |
| S4 | cmdline ` cd` reflects subshell CWD | B.1 |
> **Redox note:** the primary platform's shell and PTY surface differ from
> Linux. The S1 trait must be validated in a ` redbear-mini` QEMU guest, not
> just on the Linux host. Keep the existing "drop/recreate Tui" path as the
> fallback until the PTY overlay is proven on Redox.
### Phase 5 — Long Tail (Editor/Viewer/Keymap) *[~5 sessions]*
| # | Task | Axis |
|---|---|---|
| L1 | Remaining Ctrl-X bindings + Alt-` /Alt-Shift-E/Ctrl-Space/Alt-=/split-adjust | C |
| L2 | Editor: rectangular block, format-paragraph, match-bracket | B.7 |
| L3 | Viewer: hex edit, nroff, magic, growing, goto-offset | B.8 |
| L4 | Missing dialogs: Confirmation toggles, Sort order, Learn keys | D |
| L5 | Mouse support (column-click sort, double-click open, scrollbar drag) | A.1 (post-R1) |
**Total: ~25 sessions (~6– 8 focused weeks) ** to full MC parity + ratatui-era
visual superiority.
---
## 5. Risks & Mitigations
| Risk | Impact | Mitigation |
|---|---|---|
| **crossterm does not support Redox (primary platform) ** | **High ** | R1 is a **spike, not a swap ** — verify cross-compile + QEMU run * first * ; if it fails, fall back to the dual-backend feature-flag (termion on Redox, crossterm on Linux) defined in A.1. Never merge a backend change unproven on Redox |
| crossterm migration breaks Ctrl-O restore | High | Phase R1 ships with a dedicated restore test on **both ** Redox and Linux; keep termion reachable until verified |
| PTY subshell Redox path differs from Linux | Medium | S1 abstracts PTY behind a trait with `RedoxPty` + `UnixPty` impls; validate in `redbear-mini` QEMU; keep drop-Tui fallback behind `subshell = "drop"` config flag |
| PTY subshell is a 2000-LoC rabbit hole | Medium | Sequence after Phases R– 3 so it lands on a stable base |
| syntect adds 600 KB + regex compile cost | Medium | Lazy-compile grammars on first open; strip unused `.sublime-syntax` ; verify the heavier binary still cross-compiles to Redox |
| mc-skin importer drifts from `.ini` semantics | Low | Add a round-trip test: import → export → diff against original |
| TestBackend render tests are brittle | Low | Pin ratatui version; snapshot only stable widgets (panel row, dialog frame) |
| Backend split (`filemanager/mod.rs` 1597 LOC) deferred | Low | Accept; functional correctness first, split when it blocks a feature |
---
## 6. What I Explicitly Do NOT Recommend
1. **Do not ** rewrite the editor gap buffer — it is correct, tested, and the
undo invariant is verified. Rewriting would be churn.
2. **Do not ** migrate to a Cargo workspace for its own sake — single-crate
builds are working and simple. Split only `filemanager/mod.rs` and
`editor/mod.rs` if they actively block a feature.
3. **Do not ** replace the shared `redbear-tui-theme` 33-slot palette — it is
the right abstraction and already wired to cub. Extend it, don't fork it.
4. **Do not ** port MC's `.syntax` rule format if syntect is available —
syntect is mature and MC's format is a reimplementation.
5. **Do not ** add `unsafe` for performance — the `#![deny(unsafe_code)]`
guarantee is a project invariant. ratatui + crossterm are safe.
---
## 7. Success Criteria
The plan is complete when **all ** of the following hold:
- [ ] **Every item below is verified on `x86_64-unknown-redox` (primary platform), not just the Linux host ** — cross-compile + QEMU smoke for each
- [ ] Mouse works (column-click sort, double-click open) — * Phase R + L5 * (Redox: via whichever backend A.1's spike validated; Linux: crossterm)
- [ ] Panel renders via `Table` with highlight symbol + scrollbar — * Phase R3 *
- [ ] 45+ skins available in Alt-S (8 redbear + ~40 MC imports) — * Phase 2 *
- [ ] Ctrl-O overlays a live PTY subshell — * Phase 4 * (Redox PTY path proven in QEMU)
- [ ] Editor shows syntax highlighting for ≥20 languages — * Phase 3 F7 *
- [ ] All MC filemanager/xmap bindings either bound or explicitly N/A — * Phase 5 L1 *
- [ ] Background copy/move with Jobs dialog — * Phase 3 F1/F2 *
- [ ] `cargo test --lib` ≥ 750 passing, 0 failures
- [ ] `cargo build --release --target x86_64-unknown-redox` clean, binary boots in `redbear-mini` guest
- [ ] Render-snapshot tests cover panel + each dialog — * Phase 1 V6 *
- [ ] `tlc --color-test` renders correctly in 16/256/truecolor — * Phase 2 T4 *
At that point tlc is **at parity with MC on the axes users feel daily, and
visibly better on the axes ratatui enables** (smooth scrolling, crisp
truecolor, mouse, snapshot-tested rendering) — **on Red Bear OS first, Linux
second.**
---
*End of plan. See `PLAN.md` for the internal phase-by-phase implementation
log and `README.md` for build/run instructions.*