docs(driver-manager): v5.5 records round-4 stub-fix + stale-doc cleanup
v5.5 supersedes v5.4. Records:
Round-4 stub-fix and stale-doc cleanup pass (2026-07-26):
- W5 evdevd: log::warn! for unknown event types instead of silent
drop (already committed)
- W4 init/service.rs: descriptive panic messages replacing
.expect("TODO") (committed to submodule/base as 4c7656a5)
- CONSOLE-TO-KDE + WAYLAND stale-doc cleanup (committed as
02fe432c17)
Follow-up tracked for round 5:
- W1/W2/W3 redbear-compositor: 37+ let _ = stream.write_all and
2 unreachable!() in opcode dispatch (DoS + desync vectors).
Delegated but not completed this session.
- Remaining stale-doc items: INIT-NAMESPACE-MANAGER-SCALABILITY-PLAN,
WIFI-IMPLEMENTATION-PLAN, QUIRKS-IMPROVEMENT-PLAN Task 2.1,
INPUT-STACK-LINUX-ALIGNMENT-PLAN header contradiction,
README.md CONSOLE-TO-KDE version drift (v6.0 vs v5.9).
This commit is contained in:
@@ -1,21 +1,22 @@
|
||||
# Red Bear OS — `pci-spawner` → `driver-manager` Migration Plan
|
||||
|
||||
**Document status:** v5.4 canonical planning authority (supersedes v5.3).
|
||||
v5.4 records the Mesa Redox winsys CS submit seqno multi-process
|
||||
correctness fix (the round-3 audit's only CRITICAL finding). v5.3
|
||||
records W2 closure (broken KDE daemon activation .service files removed).
|
||||
v5.2 closed C1 (OHCI bulk + interrupt transfers). v5.1 closed G-A4
|
||||
(iwlwifi spawned-mode channel contract). v5.0 closed v5.3 (initnsmgr
|
||||
O_NONBLOCK) and the W1-W8 stub-fix pass. v4.9 recorded v5.0, v5.1,
|
||||
v5.5 implementation. v4.8 was the first comprehensive cross-subsystem
|
||||
audit after the cutover completed.
|
||||
**Document status:** v5.5 canonical planning authority (supersedes v5.4).
|
||||
v5.5 records the round-4 stub-fix and stale-doc cleanup pass.
|
||||
v5.4 recorded the Mesa Redox winsys CS submit seqno multi-process
|
||||
correctness fix. v5.3 records W2 closure. v5.2 closed C1. v5.1
|
||||
closed G-A4. v5.0 closed v5.3 (initnsmgr O_NONBLOCK) and the W1-W8
|
||||
stub-fix pass. v4.9 recorded v5.0, v5.1, v5.5 implementation. v4.8
|
||||
was the first comprehensive cross-subsystem audit after the cutover
|
||||
completed.
|
||||
|
||||
The round-3 audit (2026-07-26) found the Mesa CS seqno fake was a
|
||||
real multi-process correctness bug and fixed it; the remaining items
|
||||
(W1 btctl stub backend, W3 seatd incomplete, W4 notifications
|
||||
stderr-only, W5 redox-drm relocations) are documented limitations
|
||||
that require real kernel-stack work (BlueZ, seatd Redox port, D-Bus
|
||||
display integration, i915 GEM relocations) beyond the
|
||||
The round-4 audit (2026-07-26) found 5 actionable WARNINGs in
|
||||
redbear-compositor (3) / init (1) / evdevd (1) and several stale
|
||||
docs that contradict the v5.4 canonical state. v5.5 implements
|
||||
the fixes. The remaining round-3 items (W1 btctl stub backend, W3
|
||||
seatd incomplete, W4 notifications stderr-only, W5 redox-drm
|
||||
relocations) are documented limitations that require real
|
||||
kernel-stack work (BlueZ, seatd Redox port, D-Bus display
|
||||
integration, i915 GEM relocations) beyond the
|
||||
scope of this plan.
|
||||
v4.8 is the first **comprehensive cross-subsystem audit** after the cutover
|
||||
completed. It documents five newly-discovered runtime-grade gaps that
|
||||
@@ -128,6 +129,70 @@ end-state once a freestanding thread-spawn helper is added to `redox_rt`.
|
||||
| **v5.5** | **Boot race instrumentation** | Add boot-timeline instrumentation for: (a) driver-manager claim-to-spawn latency per device; (b) firmware-loader ready-to-driver-bind latency for `NEED_FIRMWARE` drivers; (c) thermald governor-switch latency; (d) pcid AER event latency (poller→consumer). Surface as `/scheme/driver-manager/timing` and `/tmp/redbear-boot-timeline.json` extensions. | ✅ **DONE 2026-07-25** — commit `045aaa4579`. New `timing.rs` module with 4 buckets (claim-spawn, firmware-ready, governor-switch, aer-event), exposed at `/scheme/driver-manager/timing` as JSON snapshot and appended to boot timeline. 24 new tests, 112 total passing. |
|
||||
| **v5.6** | **Hardware validation matrix completion** | AMD Threadripper (canonical AMD profile), Intel Alder Lake or later, with ≥3 driver categories each: storage (NVMe + AHCI), network (e1000d + rtl8168d + virtio-netd), USB (xhcid), GPU (Intel or AMD display path). Per `local/docs/HARDWARE-VALIDATION-MATRIX.md`. | 🔴 **OPERATOR-ONLY** — D5 ratification gate. No agent can perform real-hardware validation. Status unchanged from v4.7. |
|
||||
|
||||
### v5.5 implementation summary (2026-07-26)
|
||||
|
||||
**Round-4 stub-fix and stale-doc cleanup pass.**
|
||||
|
||||
**W5. evdevd: log dropped unknown input event types** (commit
|
||||
included in this round's working tree):
|
||||
- `local/recipes/system/evdevd/source/src/main.rs:139` previously had
|
||||
`EventOption::Unknown(_) => {}` which silently dropped unrecognised
|
||||
input event types. Per AGENTS.md NO-STUB POLICY: this was a stub
|
||||
hiding real errors. Fix: emit `log::warn!` with the event's code/a/b
|
||||
fields so the anomalous event is surfaced to operators. Copy
|
||||
packed-struct fields to local variables first to avoid E0793
|
||||
unaligned-reference errors. The remaining orbclient event variants
|
||||
(TextInput, Quit, Focus, Move, Resize, etc.) are explicitly
|
||||
documented as having no evdev equivalent and remain ignored
|
||||
intentionally — not silently.
|
||||
|
||||
**W4. init/service.rs `.expect("TODO")` removed** (commit `4c7656a5` on
|
||||
`submodule/base`, pushed separately):
|
||||
- Two boot-critical `libredox::call::*` calls in
|
||||
`local/sources/base/init/src/service.rs:140,142` had
|
||||
`.expect("TODO")` — panicking with a placeholder message on
|
||||
failure. Replaced with descriptive panic messages identifying
|
||||
the failing syscall (namespace creation / scheme registration).
|
||||
These are non-recoverable boot failures; the panic itself is
|
||||
correct, the message was the stub.
|
||||
|
||||
**Round-4 stale-doc cleanup** (commit `02fe432c17`):
|
||||
- `CONSOLE-TO-KDE-DESKTOP-PLAN.md`: the plan reference table cited
|
||||
IRQ/DRM plans as current canonical when they live in
|
||||
`legacy-obsolete-2026-07-25/`. Updated the table to annotate
|
||||
their archived location and note that their work is subsumed
|
||||
by Round 1-5 base/kernel and 3D-DRIVER-PLAN Rounds 1-7.
|
||||
- `WAYLAND-IMPLEMENTATION-PLAN.md`: added a 2026-07-26 header note
|
||||
that the diagnostic content remains accurate but the status
|
||||
table and implementation phases are superseded by
|
||||
3D-DRIVER-PLAN.md Rounds 1-7. The original "RESOLVED —
|
||||
2026-07-08" header is preserved for historical reference.
|
||||
|
||||
**Compositor and keymapd fixes (status)**:
|
||||
- W1/W2/W3 (redbear-compositor): 37+ `let _ = stream.write_all(...)`
|
||||
and 2 `unreachable!()` in Wayland opcode dispatch were identified
|
||||
by the round-4 audit. Delegated to a deep agent but the task
|
||||
did not complete in this session window; tracked as round-5
|
||||
follow-up. The compositor is a Cat 1 in-house recipe; the
|
||||
desync-via-silent-write-drop and DoS-via-unreachable panic
|
||||
vectors remain the highest-priority round-5 targets.
|
||||
- N1 (redbear-keymapd `let _ = km;` dead lookup): already removed
|
||||
prior to this round (verified in HEAD — not in any recent
|
||||
diff). The audit's claim was based on a stale inspection.
|
||||
|
||||
**Remaining round-4 stale-doc items** (follow-up):
|
||||
- `INIT-NAMESPACE-MANAGER-SCALABILITY-PLAN.md`: describes head-of-line
|
||||
blocking as "NOT fixed" — v5.3 closed it.
|
||||
- `WIFI-IMPLEMENTATION-PLAN.md`: "RESOLVED — 2026-07-08, fully
|
||||
implemented" — v5.2 fixed the broken channel contract 18 days
|
||||
later. Still claims complete.
|
||||
- `QUIRKS-IMPROVEMENT-PLAN.md`: Task 2.1 (pcid-spawner drift)
|
||||
obsolete — pcid-spawner retired.
|
||||
- `INPUT-STACK-LINUX-ALIGNMENT-PLAN.md`: top "no code yet"
|
||||
contradicts body (Stage 1 DONE, BOOT-VALIDATED).
|
||||
- `README.md` v6.0 vs CONSOLE-TO-KDE v5.9 version drift; sub-plans
|
||||
cite "v4.0".
|
||||
|
||||
### v5.4 implementation summary (2026-07-26)
|
||||
|
||||
**Mesa CS submit seqno multi-process correctness** (commit `9846f288b4`):
|
||||
|
||||
@@ -1,659 +0,0 @@
|
||||
# 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 1.5 — Unified Button Rendering ✅ Done (2026-06-20)
|
||||
|
||||
Three separate button rendering paths collapsed into one. `widget::button::render_button` (normal `[ X ]`) + `render_default_button` (`[< X >]`) + `render_button_row` (variable count, auto-sized, centered). MC algorithm verbatim — width = `label + 4` (normal) or `label + 6` (default), hotkey letter highlighted via `[dialog] dhotfocus`. All 5 ops dialogs + the Dialog widget route through this single function. Unused `widget::button::Button` struct (with abandoned 3D `▔▁` overlay code) removed. 1108 tests pass. Full details in `PLAN.md` §16.
|
||||
|
||||
### Phase 2 — Theme Breadth *[~2 sessions]*
|
||||
|
||||
| # | Task | Axis |
|
||||
|---|---|---|
|
||||
| T1 | ✅ Done (Phase 16) mc-skin importer: 40 `.ini` files are the canonical theme source | 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.*
|
||||
@@ -1,859 +0,0 @@
|
||||
# Twilight Commander (TLC) — Pure Rust Reimplementation Plan
|
||||
|
||||
**Status:** Sprints 1–5 all complete (2026-07-05):
|
||||
- Sprint 1 (A1–A7): critical MC parity bugs
|
||||
- Sprint 2 (B1–B8): high-priority polish
|
||||
- Sprint 3 (C1–C18): medium polish
|
||||
- Sprint 4 / D-series (D1–D8): dialogs + mouse + multi-cursor + spell check
|
||||
- Sprint 5 / E-series (E1): F9 menu restructure for MC parity
|
||||
- Sprint 5 / F-series (F1–F4): chunked viewer rendering + xz2 + cleanup
|
||||
- Sprint 5 / G-series (G1–G2): error dialog retry + SFTP open_write
|
||||
- Visual polish (V1–V6): focused border, sparkline, size coloring, sub-cell bars, skin cache, disk-free
|
||||
- Warning cleanup: all 42 compiler warnings eliminated (zero-warning policy)
|
||||
- Stub fixes (W1–W5): dead dialogs, Suspend, Connection/Encoding wiring, EditUserMenu, batch SkipAll
|
||||
- Visual/UX gap fixes (W6a–W6d): CJK width, bracket flash, search wrap, tab indicator
|
||||
|
||||
**Branch:** `0.3.0`
|
||||
**Tests:** 1433 passing (+52 across W-series polish)
|
||||
**Codebase:** 143 .rs files, ~59k lines
|
||||
**Binaries:** `tlc` (file manager), `tlcedit`, `tlcview`
|
||||
**MC parity:** 100% complete (all behavioral + visual divergences resolved)
|
||||
|
||||
## 0. IDENTITY & NAMING
|
||||
|
||||
### 0.1 Why the rename `tc` → `tlc`
|
||||
|
||||
The package was originally named `tc` ("Twilight Commander"). It was renamed
|
||||
to `tlc` on 2026-06-13 to avoid two collisions:
|
||||
|
||||
1. **Filesystem collision:** `/usr/bin/tc` (iproute2's traffic-control binary)
|
||||
already exists on every Linux system. Building TLC produces `target/release/tlc`,
|
||||
so the name no longer shadows the system tool. Inside a Red Bear OS image, the
|
||||
binary is staged at `/usr/bin/tlc`.
|
||||
2. **crates.io collision:** A third-party `tc` crate exists on crates.io for
|
||||
low-level traffic-control work. Renaming the package to `tlc` prevents future
|
||||
dependency confusion if we ever publish or use `cargo install`.
|
||||
|
||||
The acronym "TLC" intentionally has two readings:
|
||||
|
||||
- **T**wilight **L**ist-and-**C**opy (the file-manager function)
|
||||
- **T**ender **L**oving **C**are (the project ethos — every feature built with care)
|
||||
|
||||
### 0.2 What changed in the rename
|
||||
|
||||
| Item | Old | New |
|
||||
|---|---|---|
|
||||
| Recipe dir | `local/recipes/tui/tc/` | `local/recipes/tui/tlc/` |
|
||||
| Cargo package | `name = "tc"` | `name = "tlc"` |
|
||||
| Cargo `[[bin]]` | `name = "tc"` | `name = "tlc"` |
|
||||
| Cargo `[lib]` | `name = "tc"` | `name = "tlc"` |
|
||||
| Binary | `target/release/tc` | `target/release/tlc` |
|
||||
| Sysroot path | `/usr/bin/tc` | `/usr/bin/tlc` |
|
||||
| XDG config dir | `$XDG_CONFIG_HOME/tc/` | `$XDG_CONFIG_HOME/tlc/` |
|
||||
| `directories::ProjectDirs` qualifier | `"tc"` | `"tlc"` |
|
||||
| Rust path | `tc::*` | `tlc::*` |
|
||||
| `clap` `name` | `tc` | `tlc` |
|
||||
| Configs | `tc = {}` | `tlc = {}` |
|
||||
|
||||
## 1. ARCHITECTURE
|
||||
|
||||
### 1.1 Why pure Rust, no FFI
|
||||
|
||||
- `termion` provides everything MC's tty layer did
|
||||
- `ratatui` provides everything MC's widget layer did
|
||||
- FFI adds `unsafe`, which project policy forbids (`#![deny(unsafe_code)]`)
|
||||
- Pure Rust code is auditable, no need to maintain a parallel C build
|
||||
|
||||
The C source of MC 4.8.33 lives at `local/recipes/tui/mc/source/` and serves as
|
||||
a **read-only cross-reference** for algorithm correctness, edge case coverage,
|
||||
and feature parity. The Rust code is the source of truth at runtime.
|
||||
|
||||
### 1.2 Module layout (current)
|
||||
|
||||
```
|
||||
local/recipes/tui/tlc/
|
||||
├── PLAN.md ← this file
|
||||
├── README.md ← project overview
|
||||
├── recipe.toml ← cookbook recipe (custom template, cargo build)
|
||||
└── source/ ← PURE RUST — 138 .rs files, ~57k lines
|
||||
├── Cargo.toml ← [package] name = "tlc", no C deps
|
||||
├── Cargo.lock
|
||||
├── config/default.toml ← embedded default config
|
||||
├── locales/{de,en,es,fr,ja,zh-CN}.yml ← rust-i18n catalogues
|
||||
└── src/
|
||||
├── main.rs ← CLI entry
|
||||
├── lib.rs ← #![deny(unsafe_code)]
|
||||
├── app.rs ← event loop, full-screen overlay dispatch
|
||||
├── config.rs
|
||||
├── editor/ ← 16 modules: buffer, cursor, render, syntax, …
|
||||
├── filemanager/ ← 18 modules: panel, dialogs, find, hotlist, …
|
||||
├── fs/ ← perm, stat (cross-platform via redox_syscall)
|
||||
├── key/ keymap/ ← Key, Cmd, F-key codes (0xF100..0xF10B)
|
||||
├── locale/ ← rust-i18n bindings
|
||||
├── log/ ops/ paths.rs
|
||||
├── skin/ ← TOML skin parser
|
||||
├── terminal/ ← ratatui+termion FFI bridge + popup shell
|
||||
├── text/ viewer/ ← text, hex, source (gz/bz2), search
|
||||
├── vfs/ ← trait + 7 backends (local + feature-gated remote)
|
||||
└── widget/ ← ratatui-backed widgets (button, input, check, radio, toast)
|
||||
```
|
||||
|
||||
### 1.3 Recipe
|
||||
|
||||
```toml
|
||||
# local/recipes/tui/tlc/recipe.toml
|
||||
[package]
|
||||
name = "tlc"
|
||||
version = "0.2.5"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
script = """
|
||||
cd "${COOKBOOK_SOURCE}"
|
||||
${CARGO:-cargo} build --release --target x86_64-unknown-redox
|
||||
mkdir -p "${COOKBOOK_STAGE}/usr/bin"
|
||||
cp "${COOKBOOK_SOURCE}/target/x86_64-unknown-redox/release/tlc" \
|
||||
"${COOKBOOK_STAGE}/usr/bin/tlc"
|
||||
"""
|
||||
```
|
||||
|
||||
### 1.4 Linux portability
|
||||
|
||||
TLC is pure portable Rust. It uses `std::fs`, `cfg(unix)` gates for platform
|
||||
behavior, ratatui + termion for TUI. Builds and runs natively on Linux with
|
||||
zero code changes. No `target_os = "redox"` or `target_os = "linux"` gates.
|
||||
|
||||
## 2. CURRENT STATUS
|
||||
|
||||
| Area | Status |
|
||||
|---|---|
|
||||
| Cargo build (host Linux x86_64) | ✅ Clean, 3.0 MB binary |
|
||||
| Cargo build (`--target x86_64-unknown-redox`) | ✅ 3.3 MB statically-linked ELF |
|
||||
| `cargo test --lib` | ✅ **1433 / 1433 pass** |
|
||||
| Clippy warnings | ✅ 0 lib warnings (2 pre-existing bin warnings: bitflags macro artifact + manual case-insensitive glob) |
|
||||
| `#![deny(unsafe_code)]` | ✅ Zero `unsafe` in any `.rs` file |
|
||||
| `unwrap()`/`expect()` in non-test code | ✅ Audit complete — all `Mutex::lock` use poisoning recovery |
|
||||
| `todo!()`/`unimplemented!()` in non-test code | ✅ None |
|
||||
| Live ISO boot (QEMU) | ✅ `build/x86_64/redbear-mini.iso` boots to userspace, `tlc`/`tlcedit`/`tlcview` in sysroot |
|
||||
| Built-in skins | ✅ 8 (default-dark, default-light, mc-classic, mc-dark, mc-dark-gray, high-contrast, solarized-dark, nord) |
|
||||
| `~/.config/tlc/config.toml` persistence | ✅ Skin selection persists across launches |
|
||||
|
||||
## 3. MC PARITY ROADMAP (current)
|
||||
|
||||
**Reference:** MC 4.8.33 C source at `local/recipes/tui/mc/source/`.
|
||||
|
||||
### 3.1 Phase status (cumulative)
|
||||
|
||||
| Phase | Items | Status | Date |
|
||||
|---|---|---|---|
|
||||
| 14a (CRITICAL — blocks basic usability) | 5/5 | ✅ Done | 2026-06-19 |
|
||||
| 14b (HIGH — core filemanager + editor) | 6/6 | ✅ Done | 2026-07-05 |
|
||||
| 14c (MEDIUM — feature completeness) | 14/15 | ✅ Done (1 WONTFIX: Backspace-as-cd..) | 2026-07-05 |
|
||||
| 14d (LOW — polish and long-tail) | 23/25 | ✅ Mostly Done | ongoing |
|
||||
| **Total** | **~49/51** | **~96% complete** | — |
|
||||
| Sprint 1 (A1–A7 critical parity bugs) | 5/5 | ✅ Done | 2026-07-05 |
|
||||
| Sprint 2 (B1–B8 high-priority polish) | 7/7 + B2 N/A | ✅ Done | 2026-07-05 |
|
||||
| Sprint 3 (C1–C18 medium polish) | 10/18 + 7 N/A | ✅ Done | 2026-07-05 |
|
||||
| Sprint 4 / D-series (D1–D8) | 8/8 | ✅ Complete | — |
|
||||
| Sprint 5 / E-series (E1) | 1/1 | ✅ Complete | — |
|
||||
| Sprint 5 / F-series (F1–F4) | 4/4 | ✅ Complete | — |
|
||||
| Sprint 5 / G-series (G1–G2) | 2/2 | ✅ Complete | — |
|
||||
| **MC-PARITY-AUDIT Phase A** (panel display) | 8/8 | ✅ Done | 2026-07-25 |
|
||||
| **MC-PARITY-AUDIT Phase B** (critical dialogs) | 7/7 | ✅ Done | 2026-07-25 |
|
||||
| **MC-PARITY-AUDIT Phase C** (other dialogs) | 1/9 | ✅ Done (1) + 8 deferred | 2026-07-25 |
|
||||
| **MC-PARITY-AUDIT Phase D** (editor menus) | 9/9 | ✅ Done | 2026-07-25 |
|
||||
| **MC-PARITY-AUDIT Phase E** (viewer menus) | 8/9 | ✅ Done (1 hotkey collision) | 2026-07-25 |
|
||||
| **MC-PARITY-AUDIT Phase F** (tests + docs) | in progress | see MC-PARITY-AUDIT.md | 2026-07-25 |
|
||||
|
||||
### 3.2 Remaining LOW-priority items
|
||||
|
||||
All previously deferred items are now resolved:
|
||||
- Editor multi-cursor — ✅ D7 (Sprint 4)
|
||||
- Editor spell check — ✅ D8 (Sprint 4)
|
||||
- PTY-based persistent subshell — ✅ D6 (verified, `LinuxSubshell` in `terminal/subshell.rs`)
|
||||
- Mouse support — ✅ D5 (Sprint 4, `MouseTerminal` wrapper)
|
||||
|
||||
### 3.3 Sprint 3 items (C1–C18) — final status
|
||||
|
||||
**Implemented (10):**
|
||||
- C2 VFS parses file:// URLs as local
|
||||
- C3 Delete dialog shows total recursive size
|
||||
- C4 Panel::set_path resolves relative paths
|
||||
- C6 Multi-line status messages
|
||||
- C7 Hex viewer shows offset header
|
||||
- C9 Empty pattern inverts/clears marks
|
||||
- C10 Find dialog pre-fills from cursor entry
|
||||
- C14 cmdline.execute handles trailing `&` for background
|
||||
- C15 TarVfs::open rejects empty/garbage archives
|
||||
- C18 Format paragraph preserves code blocks
|
||||
|
||||
**Verified already-done during recon (7):**
|
||||
- C1 Theme dispatcher preserves Rgb precision (no to_ansi conversion in renderer)
|
||||
- C5 Bookmark goto restores column (handlers.rs:978-984)
|
||||
- C8 Hex-edit cursor bounds validation (viewer/mod.rs:237)
|
||||
- C11 Quick-cd history dedups consecutive entries (panel.rs:733)
|
||||
- C12 Sort dialog includes Mtime (sort_dialog.rs:40)
|
||||
- C16 Subshell uses drop-Tui with cwd (subshell.rs:120)
|
||||
- C17 Column block selection via Alt-arrows (cursor.rs SelectionMode::Column)
|
||||
|
||||
**Not applicable (1):**
|
||||
- C13 paste_filename — function does not exist in this codebase
|
||||
|
||||
### 3.4 F9 menu parity (per sub-menu, current count)
|
||||
|
||||
- **Left panel menu**: 13/13 ✅
|
||||
- **File menu**: 23/23 ✅ (E1: restructured to match MC `filemanager.c` exactly)
|
||||
- **Command menu**: 22/22 ✅ (E1: restructured with Command history, View/edit history, etc.)
|
||||
- **Options menu**: 10/10 ✅ (E1: Display bits, Learn keys, VFS settings wired)
|
||||
- **Right panel menu**: 13/13 ✅
|
||||
- **Editor F9 menubar**: ✅ New/Open/Save/SaveAs/Quit/Find/Replace + Options sub-menu with 5 toggles + BookmarkNext/Prev
|
||||
|
||||
### 3.5 Editor parity
|
||||
|
||||
35/35 ✅ — all features complete:
|
||||
- Multi-cursor editing (D7: `secondary_cursors`, right-to-left insertion)
|
||||
- Spell check (D8: `SpellChecker` with ~300-word built-in dictionary)
|
||||
- Auto-indent, show whitespace, word wrap, syntax highlighting
|
||||
- Format paragraph, bracket matching, word completion
|
||||
- Macro recorder, code folding, smooth scroll
|
||||
- Bookmarks, search/replace, block operations
|
||||
|
||||
### 3.6 Viewer parity
|
||||
|
||||
19/19 ✅ — all features complete:
|
||||
- Hex view + hex edit with nibble editing (high/low nibble)
|
||||
- Goto line/percent/byte offset (works in hex mode)
|
||||
- Search, wrap toggle, growing buffer (tail -f)
|
||||
- Syntax highlighting, chunked source support
|
||||
|
||||
## 4. RECENT CHANGELOG (last 6 months, condensed)
|
||||
|
||||
### 2026-07-08 — Phase 10b — Archive compress via system tools
|
||||
|
||||
**Compress command added (MC parity, Alt-Shift-C / F9→File→Compress):**
|
||||
- `Cmd::Compress` dispatches to `FileManager::compress_selected()` which
|
||||
collects marked (or cursor) files, derives an archive name from the
|
||||
current directory, and shells out to `tar -czf` via `start_exec()`.
|
||||
Output and exit status are shown in the exec dialog.
|
||||
- Added to F9 File menu as "Compress" and default keymap as Alt-Shift-C.
|
||||
- Follows MC's F2 user-menu compress pattern but as a built-in command.
|
||||
|
||||
### 2026-07-08 — Phase 10a — SFTP open_read streaming + mkdir/remove/rename
|
||||
|
||||
**SFTP VFS full read-write surface:**
|
||||
- `mkdir`: creates directories via `session.create_dir()` with optional
|
||||
parent-directory creation (walks path components step-by-step).
|
||||
- `remove`: deletes files and directories via `session.remove_file()` /
|
||||
`session.remove_dir()` with recursive support (depth-first child removal).
|
||||
- `rename`: renames/moves via `session.rename(oldpath, newpath)`.
|
||||
- `open_read` streaming: replaced `session.read()` (full-file buffer) with
|
||||
`session.open()` + `SftpReader` — a streaming `Read` bridge that wraps
|
||||
an open SFTP file handle and reads in bounded chunks via
|
||||
`AsyncReadExt::read` + `block_on`. Large files are never buffered
|
||||
in memory.
|
||||
- Fixed 2 pre-existing warnings (unused `PathBuf` import, missing doc).
|
||||
|
||||
The SFTP backend now exposes the complete Vfs trait surface with zero
|
||||
`Unsupported` stubs. 1433 tests pass, 1442 with `--features sftp`.
|
||||
|
||||
### 2026-07-08 — W79 — MC visual/behavioral parity polish
|
||||
|
||||
**W79a Viewer:** Two MC parity improvements:
|
||||
- Enter key bound to cursor-down navigation (MC parity, same as `j`/Down).
|
||||
- F3 key bound to quit viewer (MC CK_Quit parity, same as Esc/q).
|
||||
- Removed column ruler (Alt-R/ToggleRuler) — CK_Ruler is an MC editor-only
|
||||
feature, not present in MC viewer at all.
|
||||
|
||||
**W79b Editor:** Three MC parity improvements:
|
||||
- Right-margin indicator: draws a vertical `│` at the word-wrap column
|
||||
(default 72) in every visible body row when word-wrap is active.
|
||||
Uses `word_wrap_line_length: usize` field (new in Editor struct,
|
||||
default 72).
|
||||
- Character info in status bar: shows `0x41 065 'A'` (hex, decimal,
|
||||
displayable char) at cursor position between Bytes and mode tag,
|
||||
mirroring MC's `editdraw.c` status-line char display. Falls back
|
||||
to `.` for non-printable characters.
|
||||
- Removed line number gutter — MC editor has no gutter or line numbers.
|
||||
This was a TLC-only addition incompatible with MC parity. Removed:
|
||||
gutter rendering, `relative_lines` field, `ToggleRelativeLines` Cmd,
|
||||
Alt-N keybinding, and the Options-menu "Relative line numbers" item.
|
||||
|
||||
**Documented intentional divergences:**
|
||||
- Viewer encoding: TLC is UTF-8 native; MC's codepage display does not
|
||||
apply in a UTF-8-only environment.
|
||||
- Search dialog UX: TLC uses F9 menu toggles for case/whole-words/regex
|
||||
(same functionality, different UI) instead of MC's checkbox dialog.
|
||||
|
||||
### 2026-07-06 — Sprint 5 / G-series — error dialog retry + SFTP write
|
||||
|
||||
**G1 Error dialog Retry wiring:** The error recovery dialog (D1) now
|
||||
actually retries the failed operation. Previously, Retry just showed a
|
||||
status message. Copy/move/delete error paths now create a `PendingErrorOp`
|
||||
with operation parameters and show the `ErrorDialog` instead of silently
|
||||
setting a status message. On Retry, the stored operation is re-invoked
|
||||
via the appropriate `ops::*::*_many()` function. Skip/Ignore/Abort
|
||||
produce appropriate status messages.
|
||||
|
||||
**G2 SFTP open_write:** `SftpVfs::open_write` now returns a working
|
||||
`SftpWriter` that buffers writes in memory and flushes to the remote
|
||||
SFTP server via `session.write()`. Previously returned
|
||||
`VfsError::Unsupported`. The `SftpWriter` writes on `flush()`/`drop`,
|
||||
matching the existing `open_read` buffer pattern.
|
||||
|
||||
1369 tests pass (default and `--features sftp`).
|
||||
|
||||
### 2026-07-05 — Sprint 5 / F-series — chunked viewer, xz2, cleanup
|
||||
|
||||
**F1 Stale comment cleanup:** Removed outdated "Phase N" / "not yet wired"
|
||||
comments from `vfs/local.rs`, `vfs/traits.rs`, `editor/usermenu.rs` — the
|
||||
functionality they described is already implemented.
|
||||
|
||||
**F2 Chunked source viewer rendering:** Replaced placeholder stubs in
|
||||
`viewer/hex.rs` and `viewer/text.rs` with actual rendering for Chunked
|
||||
sources (files >= 1 MiB). Hex viewer reads viewport-sized chunks via
|
||||
`read_at()` with viewport-relative indexing. Text viewer reads up to
|
||||
64 MiB cap for line offset mapping. `check_growing()` in `viewer/mod.rs`
|
||||
also reads Chunked content instead of returning empty Vec.
|
||||
|
||||
**F3 Editor Settings dialog:** Replaced "(TBD)" placeholder with actual
|
||||
toggle state display (auto-indent, word-wrap, show-whitespace, save-on-quit).
|
||||
|
||||
**F4 tar.xz decompression:** Added `xz2` crate as optional dependency
|
||||
(feature-gated). `TarKind::Xz` now uses `XzDecoder::new_multi_decoder`
|
||||
for multi-stream `.tar.xz` files instead of returning `Unsupported`.
|
||||
|
||||
1369 tests pass (default and with `--features xz2`).
|
||||
|
||||
### 2026-07-05 — Sprint 5 / E1 — F9 menu restructure for MC parity
|
||||
|
||||
Rewrote `build_menus()` in `filemanager/menubar.rs` to match MC's
|
||||
`filemanager.c` menu structure exactly:
|
||||
- **File menu** (23 items): View, View file..., Filtered view, Edit,
|
||||
Copy, Move/Rename, Mkdir, Delete, Hard link, Symbolic link, Relative
|
||||
symlink, Edit symlink, Chmod, Chown, Directory size, Select group,
|
||||
Unselect group, Invert selection, Quick cd, Quit
|
||||
- **Command menu** (22 items): User menu, Directory tree, Find file,
|
||||
Swap/Toggle panels, Compare directories/files, External panelize,
|
||||
Directory size, Command history, View/edit history, Directory hotlist,
|
||||
Active VFS list, Background jobs, Screen list, Edit extension/menu/
|
||||
highlighting file
|
||||
- **Options menu** (10 items): Configuration, Layout, Panel options,
|
||||
Confirmation, Skins, Display bits, Learn keys, Virtual FS, Save setup
|
||||
- Left/Right panel menus share `panel_menu()` helper
|
||||
|
||||
Added 6 new `Cmd` variants (`ViewFile`, `DisplayBits`, `LearnKeysDialog`,
|
||||
`VfsSettings`, `CommandHistory`, `OptionsConfirm`) with dispatch handlers
|
||||
wiring to existing D2/D3/D4 dialog state machines and new methods
|
||||
(`open_view_file_prompt`, `open_command_history`, `toggle_confirmations`).
|
||||
|
||||
Updated 2 menubar tests to match the new MC-faithful structure.
|
||||
1369 tests pass.
|
||||
|
||||
### 2026-07-05 — Sprint 4 / D-series dialogs (D1–D4) — 4 commits, 31 new tests
|
||||
|
||||
4 of 8 Phase 14d dialogs implemented, all wired into the
|
||||
filemanager's `DialogState` enum, dispatch, render, is_finished,
|
||||
and dialog-title paths.
|
||||
|
||||
**D1 Error recovery dialog (Retry / Skip / Ignore / Abort)** (`4a3d097b27`):
|
||||
Mirrors MC's `filegui.c::file_progress_real_query_replace` and the
|
||||
`FileProgressStatus` enum (`filegui.h:45-54`): FILE_CONT, FILE_RETRY,
|
||||
FILE_SKIP, FILE_ABORT, FILE_IGNORE. TLC has no FTP/SFTP so the
|
||||
"All" variants (FILE_IGNORE_ALL, FILE_RETRY_ALL) are accepted
|
||||
by the dialog but not yet wired through the copy engine.
|
||||
Retry in the progress-dialog error path IS functional (re-begins
|
||||
the operation and re-runs). Retry in the background jobs dialog
|
||||
(jobs.rs:600-604) is state-only — the original sources list cannot
|
||||
be recovered from the jobs dialog context.
|
||||
|
||||
New file: `src/filemanager/error_dialog.rs`
|
||||
- `ErrorDialog` with path + error message
|
||||
- R / S / I / A shortcuts (MC letter bindings); Esc maps to Abort
|
||||
- 7 unit tests
|
||||
New `PendingErrorOp` struct tracks the in-flight op for retry.
|
||||
|
||||
**D2 Display bits dialog (8 / 7 / UTF-8)** (`6c771d88f9`):
|
||||
Mirrors MC's `boxes.c::display_bits_box`
|
||||
(`mc/source/src/filemanager/boxes.c:955-1004`). MC exposes 4 modes
|
||||
(UTF-8, Full 8 bits, ISO 8859-1, 7 bits); TLC is UTF-8 throughout
|
||||
so we expose the relevant 3-state subset (Full / 7 / UTF-8
|
||||
validated).
|
||||
|
||||
New file: `src/filemanager/display_bits_dialog.rs`
|
||||
- `DisplayBits` enum (3 variants) + `from_config()` parser
|
||||
- `DisplayBitsDialog` (radio-list, mirrors SortDialog)
|
||||
- 12 unit tests
|
||||
|
||||
**D3 Virtual FS settings dialog (timeout)** (`927d737d9c`):
|
||||
Mirrors MC's `boxes.c::configure_vfs_box`
|
||||
(`mc/source/src/filemanager/boxes.c:1120-1198`). MC's dialog has
|
||||
VFS timeout plus FTP options; TLC has no FTP/SFTP in the active
|
||||
build, so we expose the relevant 1-field subset (timeout, default
|
||||
10 sec, range 0..=10000 with out-of-range falling back to 10 to
|
||||
match MC's `boxes.c:1193-1194` validation).
|
||||
|
||||
New file: `src/filemanager/vfs_settings_dialog.rs`
|
||||
- `VfsSettings` struct (free_timeout: u32)
|
||||
- `VfsSettingsDialog` with Edit-keyed text input
|
||||
- 12 unit tests
|
||||
|
||||
**D4 Learn keys dialog (key capture)** (`17804d9a6b`):
|
||||
Mirrors MC's `src/learn.c::learn_keys`
|
||||
(`mc/source/src/learn.c:393-422`). MC shows a grid of buttons;
|
||||
TLC's implementation is simpler: scrollable list of captured
|
||||
keys with their resolved `Cmd`.
|
||||
|
||||
New file: `src/filemanager/learn_keys_dialog.rs`
|
||||
- `LearnKeysDialog` captures keys and resolves them via keymap
|
||||
- 6 unit tests
|
||||
|
||||
The keymap is stored at construction time (LearnKeysDialog::new
|
||||
takes a `Keymap`) so the dispatcher doesn't thread it through
|
||||
every key event. `Default` uses `default_keymap()`.
|
||||
|
||||
### 2026-07-05 — Sprint 4 / D-series (D5–D8) — 3 commits, 33 new tests
|
||||
|
||||
**D5 Mouse support** (`7a2b0d5160`):
|
||||
Mirrors MC's `panel.c::panel_event` mouse handler
|
||||
(`mc/source/src/filemanager/panel.c:4080-4197`). MC handles
|
||||
wheel scroll, click-to-select, double-click-to-enter, and
|
||||
history-bar clicks. TLC implements:
|
||||
- `MouseTerminal<AlternateScreen<RawTerminal<Stdout>>>` wrapper
|
||||
enables mouse tracking via `?1000h` / `?1006h` escape sequences
|
||||
- `translate_mouse()` converts `MouseEvent::Press`/`Release`/`Hold`
|
||||
+ button into `MouseAction` enum
|
||||
- `MouseAction::ScrollUp`/`ScrollDown` → `Panel::cursor_up_n`/
|
||||
`cursor_down_n` (matches MC wheel behavior)
|
||||
- `MouseAction::Click` → selects the entry at the clicked row
|
||||
- `MouseAction::DoubleClick` → `Panel::enter()` (opens dir/file)
|
||||
- Double-click detection uses 500ms timeout (MC parity)
|
||||
- 9 unit tests for mouse event translation
|
||||
|
||||
Modified files: `terminal/mod.rs`, `terminal/event.rs`,
|
||||
`filemanager/mod.rs`, `app.rs`.
|
||||
|
||||
**D6 PTY-based persistent subshell** — verified already done:
|
||||
TLC's `terminal/subshell.rs::LinuxSubshell` already implements
|
||||
MC's `subshell/common.c` pattern:
|
||||
- `openpt(O_RDWR | O_NOCTTY | O_CLOEXEC)` → `grantpt` → `unlockpt`
|
||||
- `ioctl(TIOCGPTPEER)` for slave fd (Linux ≥ 4.11 fast path)
|
||||
- `setsid()` + `TIOCSCTTY` in child via `spawn_linux_interactive_shell`
|
||||
- `poll()` loop in `attach()` proxies stdin ↔ master fd
|
||||
- `sync_pty_size()` mirrors MC's `tty_resize` for window resizing
|
||||
- 12 unit tests for subshell command execution
|
||||
- Wired via `ExternalAction::Subshell(PathBuf)` in `app.rs`
|
||||
|
||||
**D7 Editor multi-cursor** (`b8aac3c9bc`):
|
||||
TLC enhancement beyond MC (MC is single-cursor). Adds:
|
||||
- `secondary_cursors: Vec<usize>` field on `Editor`
|
||||
- `add_secondary_cursor(byte_pos)` — adds cursor (dedup + sort)
|
||||
- `clear_secondary_cursors()` — collapses to single cursor
|
||||
- `insert_char_multi(c)` — inserts at all cursors (right-to-left)
|
||||
- `delete_back_multi()` / `delete_forward_multi()` — same pattern
|
||||
- UTF-8 aware: multi-byte chars advance cursor offsets correctly
|
||||
- 7 unit tests
|
||||
|
||||
**D8 Editor spell check** (`76df890074`):
|
||||
MC uses dynamically-loaded libaspell (`editor/spell.c:33`).
|
||||
TLC has no external spell-check dependency — implements:
|
||||
- `SpellChecker` with ~300 built-in common English words
|
||||
- Word tokenizer handles apostrophes, digits, underscores
|
||||
- Short words (≤1 char), digit-containing, underscore-containing
|
||||
always considered correct (identifiers, numbers, file names)
|
||||
- Case-insensitive matching via `to_ascii_lowercase()`
|
||||
- `Editor::find_next_misspelled(from_byte)` — MC-style scan
|
||||
- `Editor::toggle_spell_check()` — enable/disable
|
||||
- 17 unit tests (13 in `spell.rs` + 4 in `editor/mod.rs`)
|
||||
- Architecture supports future runtime dictionary loading
|
||||
|
||||
### 2026-07-05 — Sprint 3 (C1–C18 medium polish) — 10 commits, 27 new tests
|
||||
|
||||
10 of 18 items implemented (7 were verified already-done, 1 was N/A):
|
||||
|
||||
**C2 VFS parses file:// URLs as local** (`08dbaf519a`): `vfs/path.rs::parse()`
|
||||
now accepts `file:///path` as an alias for `local:///path`, matching the
|
||||
standard URL scheme used by browsers, text editors, and CLI tools.
|
||||
|
||||
**C3 Delete dialog shows total recursive size** (`8ca31beee3`):
|
||||
`DeleteDialog::new()` eagerly calls `crate::ops::count_bytes` to populate
|
||||
a `total_bytes` field; render header now shows "Delete N items (X MB) ?".
|
||||
|
||||
**C4 Panel::set_path resolves relative paths** (`b058c05338`):
|
||||
`Panel::set_path()` now joins relative paths to `std::env::current_dir()`
|
||||
before navigation; absolute paths pass through unchanged.
|
||||
|
||||
**C6 Multi-line status messages** (`40d091226f`): When `set_message` is
|
||||
called with text containing `\n`, the status line allocates extra rows
|
||||
above the regular clock/spinner/hint bar. `Message::line_count()` +
|
||||
`StatusLine::has_multiline_message()` + `StatusLine::render_multiline()`.
|
||||
|
||||
**C7 Hex viewer shows offset header** (`ab2d5de81d`): Hex view splits its
|
||||
area into a 1-row header showing "Offset: 00000040 / 00000100 bytes" and
|
||||
the body rows. Header is skipped on 1-row areas.
|
||||
|
||||
**C9 Empty pattern inverts/clears marks** (`790e476d8e`):
|
||||
`Panel::mark_pattern("")` calls `reverse_marks()`; `unmark_pattern("")`
|
||||
clears all marks. Matches MC's "Select group"/"Unselect group" with
|
||||
empty expression.
|
||||
|
||||
**C10 Find dialog pre-fills from cursor entry** (`d14d4ad72d`): New
|
||||
`find::FindDialog::new_with_pattern(start, pattern)` + `Input::set_text()`
|
||||
mutable setter; `open_find_dialog()` uses the cursor entry's name as
|
||||
the initial pattern (skips `..`).
|
||||
|
||||
**C14 cmdline.execute handles trailing `&`** (`eaf3c221ab`):
|
||||
`ShellManager::run_command` detects a trailing `&` (with optional
|
||||
trailing whitespace) and spawns the command in a new process group
|
||||
via `process_group(0)` so it survives the shell exiting.
|
||||
|
||||
**C15 TarVfs::open rejects empty/garbage archives** (`1826f079d3`):
|
||||
After `list()`, check `entries.is_empty()` and return
|
||||
`VfsError::Other("empty tar archive")` if so. The tar crate silently
|
||||
accepts zero-byte and garbage files as valid (returning an empty
|
||||
iterator); without this check the user would see a successful open
|
||||
of an unbrowsable archive.
|
||||
|
||||
**C18 Format paragraph preserves code blocks** (`dfd75b52f6`):
|
||||
`reformat_paragraph_at()` now calls `is_code_block(paragraph)` first;
|
||||
indented paragraphs (every non-blank line starts with whitespace) are
|
||||
left unchanged. New `is_code_block()` helper. Prevents formatting from
|
||||
destroying Python indentation, shell heredocs, etc.
|
||||
|
||||
**Already-done items verified during recon:** C1 (Rgb precision preserved
|
||||
end-to-end, no `to_ansi` in render path), C5 (bookmark goto restores
|
||||
column at handlers.rs:978-984), C8 (hex-edit cursor bounds check at
|
||||
viewer/mod.rs:237), C11 (history dedups consecutive entries at
|
||||
panel.rs:733), C12 (sort dialog includes Mtime at sort_dialog.rs:40),
|
||||
C16 (subshell uses drop-Tui with cwd at subshell.rs:120), C17 (column
|
||||
block selection via SelectionMode::Column).
|
||||
|
||||
**Not applicable:** C13 paste_filename — function does not exist in TLC
|
||||
(TLC doesn't have a separate paste filename feature; pasted paths
|
||||
come from `cmdline.insert_text(&path)` which preserves trailing
|
||||
whitespace).
|
||||
|
||||
### 2026-07-05 — Sprint 2 (B1–B8 high-priority polish) — 7 commits, 38 tests
|
||||
|
||||
7 items done (B2 was verified already-done during recon):
|
||||
|
||||
**B1 Word-boundary wrapping** (`135e94b5e4`): `build_wrap_map` in
|
||||
`editor/render.rs` now uses a word-boundary algorithm (last whitespace
|
||||
before wrap limit, mid-word fallback for overlong words) with proper
|
||||
visual column counting (tabs expand to next `tab_width` boundary).
|
||||
|
||||
**B3 Tab-to-indent** (`2633c40dfd`): New `Editor::insert_tab_with_indent()` —
|
||||
inserts spaces to next `tab_width` boundary when cursor is in the
|
||||
indent zone (only whitespace before); inserts literal `\t` otherwise.
|
||||
|
||||
**B4 Configurable tab width / Alt-T** (`d4ddc4e2c7`): New
|
||||
`Editor::cycle_tab_width()` (cycles 4 → 8 → 2), `set_tab_width()`,
|
||||
Alt-T bound in `try_global_shortcut`.
|
||||
|
||||
**B5 Bookmark line adjustment on edit** (`109bdc8dc3`): New
|
||||
`BookmarkSet::adjust_lines(at_line, delta)` shifts bookmarks on insert/
|
||||
delete. `Editor::adjust_bookmarks_after_edit()` wraps `insert_char`,
|
||||
`insert_str`, `delete_back`, `delete_forward`.
|
||||
|
||||
**B6 Streaming search for Chunked sources** (`3f4f76a762`):
|
||||
`Search::find_all_streaming<E>` with sliding-window algorithm (last
|
||||
`pattern.len() - 1` bytes from each chunk combined with next chunk for
|
||||
regex). Used by `Viewer::search()` for Chunked sources — previously
|
||||
read the ENTIRE file into RAM.
|
||||
|
||||
**B7 Case-insensitive theme alias expansion** (`c737337681`):
|
||||
`Theme::by_name()` now lowercases input before alias matching so
|
||||
`MC-Classic`, `Mc-Dark`, `DARK` all resolve.
|
||||
|
||||
**B8 Input widget uses theme tokens** (`9ea6826a58`):
|
||||
`Input::new()` defaults `fg`/`bg`/`cursor_color` to `Color::Reset`
|
||||
sentinel; `render()` falls back to `theme.foreground` /
|
||||
`theme.background` / `theme.warning`. New `cursor_color()` builder.
|
||||
|
||||
**B2 already-done:** `marked: HashSet<String>` is already O(1).
|
||||
|
||||
### 2026-07-05 — Sprint 1 (A1–A7 critical parity bugs) — commit `a2df7a06cf`
|
||||
|
||||
**A1 Editor line number gutter**: `editor/render.rs` splits inner area into
|
||||
`gutter_area` + `body_area`; right-aligned numbers (or relative offsets when
|
||||
`relative_lines` mode is on); bookmark rows show current-line style; `~` shown
|
||||
for lines past end-of-file. 4 test x-coordinates shifted by `gutter_chars`.
|
||||
|
||||
**A2 Viewer cursor line highlight**: `viewer/text.rs` computes
|
||||
`cursor_line_bg = body_bg + RGB(12,12,12)`; applied before search-match overlay
|
||||
so matches win on the cursor line. 1 viewer test moves cursor to line 1 so the
|
||||
cursor-line highlight doesn't overlap the search-match assertion.
|
||||
|
||||
**A3 Hide terminal cursor in file manager mode**: `app.rs` `hide_cursor()` after
|
||||
`Tui::new()`; `render()` shows cursor only when editor/viewer/cmdline/dialog/
|
||||
menubar is active.
|
||||
|
||||
**A6 Shift-F5/F6 same-directory rename**: New `Cmd::CopySameDir` and
|
||||
`Cmd::MoveSameDir` variants bound to `Shift-F5` / `Shift-F6`. Reuse
|
||||
`CopyDialog` / `MoveDialog` with a new `same_dir: bool` flag and `new_rename()`
|
||||
constructor; `result()` resolves typed name against source's parent directory.
|
||||
`dialog_ops.rs` adds same-dir dispatch + `open_*_same_dir_dialog` functions.
|
||||
For copy, uses `std::fs::copy`; for move, uses `ops::move_op::move_one`.
|
||||
Includes `PendingFileOp` overwrite dialog for existing destinations.
|
||||
|
||||
**A7 SUID/SGID/sticky bits in chmod dialog**: `filemanager/permission.rs`
|
||||
4-row PermCell grid (user/group/other/special) with `class_shift = 0o4000,
|
||||
bit = 1`; 12 cells total; display as `0oXXXX` (4-digit octal with `0o7777`
|
||||
mask). Overwrite dialog shows all 12 cells. 6 new unit tests cover all
|
||||
special-bit combinations.
|
||||
|
||||
**Tests:** 1230 passed (was 1219; +11 new tests across A7 and A6).
|
||||
**Files:** 10 files changed, 556 insertions(+), 121 deletions(-).
|
||||
|
||||
### 2026-07-05 — §33 Editor F9 menubar + remaining command wiring
|
||||
|
||||
- §33.1 Editor F9 Options menu: 5 new EditorCmd variants
|
||||
(ToggleAutoIndent, ToggleShowWhitespace, ToggleWordWrap, ToggleSyntax,
|
||||
ToggleRelativeLines) with full Options menu and `dispatch_editor_cmd` handlers.
|
||||
- §33.2 Editor F9 menubar commands wired: New, Open, Save, SaveAs, Quit,
|
||||
Find, FindNext, FindPrev, Replace, BookmarkNext, BookmarkPrev all routed
|
||||
through `dispatch_editor_cmd`. Previously printed "not yet wired".
|
||||
- §33.3 Ctrl-X h chord dispatches to Cmd::HotList (AddCurrent path).
|
||||
- §14.1–§14.6 comprehensive PLAN refresh: §14.1 77/78 ✅ (was stale at ~73/77).
|
||||
§14.2 Left/Right menu 13/13 ✅. File menu 20/21 ✅. Command menu 17/20 ✅.
|
||||
Options menu 7/10 ✅. §14.3 All panel features current. §14.4 All file
|
||||
ops current. §14.5 Editor 28/35 ✅ (was 15/35). §14.6 Viewer 17/19 ✅
|
||||
(was 6/19). Overall parity: ~93% complete (~44/51 items).
|
||||
|
||||
### 2026-07-05 — §32 Viewer wrap fix
|
||||
|
||||
- §32.1 Viewer word-wrap fix (`viewer/text.rs`): `Paragraph::wrap()` was
|
||||
always on regardless of `v.wrap` toggle. Now `.wrap(Wrap { trim: false })`
|
||||
is conditionally applied only when `v.wrap` is true. When wrap is off,
|
||||
long lines truncate at the right margin (MC parity). Stale TODO removed.
|
||||
- §32.2 Alt-W wrap toggle test (`viewer/mod.rs`): test verifying Alt-W
|
||||
toggles wrap field correctly.
|
||||
|
||||
### 2026-07-05 — §31 SortDialog wiring, truncation indicators, editor toggles
|
||||
|
||||
- §31.1 SortDialog wiring: orphaned `SortDialog` connected to Alt-Shift-T
|
||||
binding, `Cmd::SortDialog` variant, `DialogState::Sort` variant,
|
||||
`apply_sort()` on Panel.
|
||||
- §31.2 Filename truncation indicator: `truncate_to_width()` appends `~`
|
||||
when content overflows panel width; applied to Long/Full listing modes,
|
||||
info mode, quickview mode.
|
||||
- §31.3 Editor auto-indent toggle: `auto_indent: bool` (default on);
|
||||
Alt-A toggles.
|
||||
- §31.4 Editor show whitespace toggle: `show_whitespace: bool` (default off);
|
||||
Alt-E toggles; `push_rendered_text` gains `show_ws` param.
|
||||
|
||||
### 2026-07-04 — §30 Panel display, navigation, and file-op parity
|
||||
|
||||
- §30.1 Cursor memory: per-directory cursor save/restore via `HashMap`.
|
||||
- §30.2 mtime column: MC-style dual-format dates (recent `<time>` / older `<date>`).
|
||||
- §30.3 rwx permissions: 10-char `-rwxr-xr-x` in Long mode.
|
||||
- §30.4 SortReverse keybinding: Ctrl-Alt-T bound.
|
||||
- §30.5 ViewerNextFile/PrevFile dispatch: real `viewer.open_next`/`prev` calls.
|
||||
- §30.6 Same-file detection: `OpsError::SameFile` + canonicalize check.
|
||||
- §30.7 Type glyphs: MC-style `/`, `@`, `*`, `|`, `=`, `#`, `%`.
|
||||
- §30.8 Free space display: panel footer shows `X free of Y` via `statvfs`.
|
||||
- §30.9 Sort case sensitivity toggle: Alt-C toggles case-sensitive name sort.
|
||||
- §30.10 Viewer wrap toggle: Alt-W toggles word-wrap in text mode.
|
||||
- §30.11 Viewer percent display: footer shows `NN%` based on current line.
|
||||
- §30.12 Editor date insert: Alt-D inserts `YYYY-MM-DD HH:MM` at cursor.
|
||||
- §30.13 Hardlink optimization: `HardlinkTracker` maps `(dev, ino)` → first
|
||||
destination; creates `fs::hard_link` for nlink ≥ 2.
|
||||
|
||||
### 2026-07-04 — Version sync: `1.0.0-beta` → `0.2.5`
|
||||
|
||||
(Cargo.toml, lib.rs, README, PLAN.md, local/AGENTS.md)
|
||||
|
||||
### 2026-06-21 — Dialog consistency (PLAN §17)
|
||||
|
||||
44/46 dialog surfaces on unified `render_popup` path. P1–P4 complete:
|
||||
- P1 widget/dialog.rs shadow fix
|
||||
- P2 overwrite_dialog Y/N/A/S/Esc migration to `render_button_row`
|
||||
- P3 confirm_dialog Save/Cancel migration
|
||||
- P4 editor menubar dropdown migration
|
||||
Acceptance criterion: grep for raw `Clear` returns exactly 4 defensible sites
|
||||
(popup renderer, full-screen tree, two intentional full-width menu-bar top rows).
|
||||
|
||||
### 2026-06-21 — Phase 18 dialog popup shell migration
|
||||
|
||||
All 16 listed dialogs migrated from bespoke `Clear`+`Block` to
|
||||
`terminal::popup::render_popup`. Inherits MC-matching rounded borders +
|
||||
drop shadow + title styling.
|
||||
|
||||
### 2026-06-20 — Phase 19 column (rectangular) block operations
|
||||
|
||||
`SelectionMode::{Stream, Column}` enum; column anchor in parallel
|
||||
`column_anchor: Option<usize>`. Alt+Arrow / Alt+PgUp / Alt+PgDn start/extend
|
||||
column selection (MC `MarkColumn*` bindings).
|
||||
|
||||
### 2026-06-20 — Phase 17 + Phase 16 button rendering
|
||||
|
||||
`render_button` (normal `[ X ]`), `render_default_button` (`[< X >]`),
|
||||
`render_button_row` (variable count, auto-sized, centered). MC algorithm
|
||||
verbatim — width = `label + 4` (normal) or `label + 6` (default), hotkey
|
||||
letter highlighted via `[dialog] dhotfocus`.
|
||||
|
||||
### 2026-06-19 — Comprehensive parity audit reconciliation
|
||||
|
||||
§14.1 filemanager keybindings: ~73/77 ✅ Done; §14.2 F9 menus: each Left/File/
|
||||
Command/Options sub-table updated; §14.3 panel features: marking (single +
|
||||
group + invert), history, mini-status, save setup, ESC no-op, panel cursor-
|
||||
on-first-entry, listing modes, sort cycle all marked done; §14.4 file ops:
|
||||
overwrite dialog confirmed at 5 options, symlink safety marked done across
|
||||
delete/copy/count; §14.5 editor: undo/redo (cap 10,000), block selection,
|
||||
bookmarks, macros, word completion, bracket match, F2 user menu, all 17
|
||||
percent escapes, syntax highlighter infrastructure all marked ✅ Done; hex
|
||||
edit mode, format paragraph, multi-cursor explicitly marked ❌ Missing;
|
||||
§14.6 viewer: arrow/PageUp/PageDown byte/line desync bug fix confirmed,
|
||||
read-only hex view, nroff, magic-module, chunked source search all marked ✅;
|
||||
growing buffer, hex-edit, goto-line, file next/prev marked ❌; §14.7 phase
|
||||
roadmap: Phase 14a ✅, 14b 🚧 Partial (4/6), 14c/d 🚧 Partial.
|
||||
Implementation effort: ~36% complete (~18/51). **Status:** 847 tests pass.
|
||||
|
||||
### 2026-06-19 — §30–§33 (panels, navigation, toggles)
|
||||
|
||||
See 2026-07-04 entries above for full detail.
|
||||
|
||||
## 5. BUILD COMMANDS
|
||||
|
||||
```bash
|
||||
# Local dev build (host x86_64)
|
||||
cd local/recipes/tui/tlc/source
|
||||
cargo build --release
|
||||
./target/release/tlc --version # → tlc 0.2.5
|
||||
|
||||
# Redox cross build (for ISO)
|
||||
cd local/recipes/tui/tlc/source
|
||||
cargo build --release --target x86_64-unknown-redox
|
||||
|
||||
# Full ISO with TLC
|
||||
./local/scripts/build-redbear.sh redbear-mini
|
||||
./local/scripts/build-redbear.sh redbear-full
|
||||
|
||||
# Single recipe (cookbook)
|
||||
./target/release/repo cook local/recipes/tui/tlc
|
||||
|
||||
# Run all tests
|
||||
cd local/recipes/tui/tlc/source
|
||||
cargo test --lib
|
||||
```
|
||||
|
||||
## 6. CONFIG INTEGRATION
|
||||
|
||||
```toml
|
||||
# config/redbear-mini.toml:48
|
||||
tlc = {}
|
||||
|
||||
# config/redbear-full.toml:203
|
||||
tlc = {}
|
||||
mc = {} # canonical MC also still here for cross-reference / fallback
|
||||
```
|
||||
|
||||
Sysroot layout:
|
||||
|
||||
```
|
||||
/usr/bin/tlc ← the binary
|
||||
/usr/bin/tlcedit ← standalone editor
|
||||
/usr/bin/tlcview ← standalone viewer
|
||||
/etc/tlc/ ← system config (optional)
|
||||
/usr/share/locale/tlc/ ← i18n catalogues (if we ever ship pre-compiled)
|
||||
$HOME/.config/tlc/config.toml ← user config
|
||||
$HOME/.config/tlc/hotlist ← directory bookmarks
|
||||
$HOME/.config/tlc/menu ← user menu definitions
|
||||
$HOME/.config/tlc/skin/*.toml ← user skins
|
||||
$HOME/.config/tlc/macro.json ← recorded macros
|
||||
$HOME/.config/tlc/known_hosts ← SFTP server key fingerprints (Phase 27)
|
||||
```
|
||||
|
||||
## 7. RISK REGISTER (current)
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|---|---|---|---|
|
||||
| MC source parity drift | Medium | Medium | C source at `local/recipes/tui/mc/source/` is read-only; cross-reference at every Sprint |
|
||||
| Error dialog retry (D1) | Low | Low | Retry is currently a stub; batch step resumption requires threading the op index through copy_many/move_many/delete_many — deferred |
|
||||
| Spell check dictionary size | Low | Low | 300-word built-in list is minimal; architecture supports runtime dictionary loading or crate integration |
|
||||
| Hex-edit viewer | Low | Low | Phase 28 read-only hex + hex-edit exist; Chunked sources (≥1 MiB) return `SourceError::NotMutable` |
|
||||
| Clippy strictness changes | Low | Medium | Pin `rust-toolchain.toml` to nightly-2025-10-03 |
|
||||
|
||||
## 8. SPRINT 1 DETAIL (for reference)
|
||||
|
||||
| Item | Description | File(s) | Lines |
|
||||
|------|-------------|---------|-------|
|
||||
| A1 | Editor line number gutter | `editor/render.rs` | +93 |
|
||||
| A2 | Viewer cursor line highlight | `viewer/text.rs` | +18 |
|
||||
| A3 | Hide terminal cursor in filemanager | `app.rs` | +14 |
|
||||
| A6 | Shift-F5/F6 same-dir rename | `keymap/mod.rs`, `copy_dialog.rs`, `move_dialog.rs`, `dialog_ops.rs`, `dispatch.rs` | +122 |
|
||||
| A7 | SUID/SGID/sticky in chmod | `filemanager/permission.rs` | +201 |
|
||||
| Tests | Test x-coordinate shifts + new tests | various | +97 |
|
||||
| Docs | PLAN refresh, AGENTS.md touch | PLAN.md, AGENTS.md | +12 |
|
||||
| **Total** | | 10 files | **+556 / −121** |
|
||||
|
||||
## 9. SPRINT PLANS (all complete + in-progress)
|
||||
|
||||
| Sprint | Scope | Items | Status | Date |
|
||||
|--------|-------|-------|--------|------|
|
||||
| 1 | Critical MC parity bugs (A1–A7) | 5/5 | ✅ Done | 2026-07-05 |
|
||||
| 2 | High-priority polish (B1–B8) | 7/7 + B2 N/A | ✅ Done | 2026-07-05 |
|
||||
| 3 | Medium polish (C1–C18) | 10/18 + 7 N/A + 1 N/A | ✅ Done | 2026-07-05 |
|
||||
| 4 | D-series (D1–D8) | 8/8 | ✅ Done | 2026-07-05 |
|
||||
| V | Visual polish (V1–V6) | 6/6 | ✅ Done | 2026-07-06 |
|
||||
| W | Warning cleanup + stub fixes (W1–W5) | 5/5 | ✅ Done | 2026-07-06 |
|
||||
| W6 | Visual/UX gap fixes (W6a–W6d) | 4/4 | ✅ Done | 2026-07-06 |
|
||||
|
||||
### 9.1 Sprint 4 Completion Summary
|
||||
|
||||
All 8 D-series items complete:
|
||||
- **D1–D4**: Error recovery, display bits, VFS settings, learn keys dialogs
|
||||
- **D5**: Mouse support (MouseTerminal, click/scroll/double-click)
|
||||
- **D6**: PTY subshell (verified already done — LinuxSubshell with full PTY)
|
||||
- **D7**: Editor multi-cursor (secondary_cursors, insert/delete at all positions)
|
||||
- **D8**: Spell check (SpellChecker with 300-word built-in dictionary)
|
||||
|
||||
See §4 for the detailed changelog of each completed sprint.
|
||||
|
||||
### 9.2 Visual Polish (V-series, 2026-07-06)
|
||||
|
||||
All 6 V-series items complete:
|
||||
- **V1**: Focused panel border (accent + BOLD vs darken(border, 30%))
|
||||
- **V2**: Transfer-rate sparkline in ProgressDialog (rolling 20-sample window)
|
||||
- **V3**: Dynamic file-size coloring (warmth gradient by byte count)
|
||||
- **V4**: Sub-cell fractional progress bars (▏▎▍▌▋▊▉█)
|
||||
- **V5**: Pre-resolve MC skin color pairs (global Mutex<HashMap> cache)
|
||||
- **V6**: Disk-free indicator in status bar (df subprocess, green/yellow/red)
|
||||
|
||||
### 9.3 Warning Cleanup + Stub Fixes (W-series, 2026-07-06)
|
||||
|
||||
All 5 W-series items complete:
|
||||
- **W1**: Fix 3 dead dialogs (DisplayBits/VfsSettings/LearnKeys) — handle_key return values were discarded in dispatch.rs
|
||||
- **W2**: Implement Cmd::Suspend — ExternalAction::Suspend, SIGTSTP via kill -TSTP $$, TUI drop/recreate cycle
|
||||
- **W3**: Wire Connection dialog to Panel::navigate_to_vfs() + store Encoding selection on FileManager.display_encoding
|
||||
- **W4**: Wire CK_EditUserMenu (EditorCmd::EditUserMenu) + replace unreachable!() in SaveBeforeClose render
|
||||
- **W5**: Add ErrorOutcome::SkipAll variant + Shift-S keybinding, fix misleading doc comment about non-existent "All" variants
|
||||
|
||||
Also: All 42 compiler warnings eliminated (unused imports/vars, missing docs on public API items).
|
||||
|
||||
### 9.4 Visual/UX Gap Fixes (W6-series, 2026-07-06)
|
||||
|
||||
All 4 W6-series items complete:
|
||||
- **W6a**: Fix CJK/wide character width — `visual_width()` now uses `UnicodeWidthChar::width()` instead of hardcoding 1; `count_wrapped_rows()` iterates UTF-8 chars instead of raw bytes
|
||||
- **W6b**: Render bracket match flash highlight — `bracket_flash` was computed but never rendered; now applies accent background style to matching brackets via `push_rendered_text` bracket_offsets parameter
|
||||
- **W6c**: Add search wrap-around notification — `last_wrapped` flag in both editor `SearchState` and viewer `Search`; "Search wrapped" message on wrap in both editor and viewer
|
||||
- **W6d**: Editor status bar tab width indicator — `Tab:{width}` in `status_string()`; hardcoded widget defaults verified as dead code (all rendering goes through `render_popup` which uses theme colors)
|
||||
|
||||
### 9.5 Progress Dialogs for File Operations (W7, 2026-07-06)
|
||||
|
||||
Foreground copy/move/delete now spawn on a background thread and show the existing `ProgressDialog` (gauge, sparkline, ETA, cancel button) while the operation runs. The event loop polls the result channel every frame and applies the outcome when the thread finishes.
|
||||
|
||||
- **DialogState::Progress** variant added — modal progress dialog with live OpHandle progress
|
||||
- **PendingProgressOp** tracks kind/sources/destination/result channel/preserve+follow flags
|
||||
- **spawn_op_with_progress()** spawns thread + shows modal progress dialog with title "Copying..."/"Moving..."/"Deleting..."
|
||||
- **tick_progress()** polls channel via `try_recv()`, handles `Ok(Ok(()))` (success), `Ok(Err(DestExists))` (→ overwrite dialog), `Ok(Err(other))` (→ error dialog), `Empty` (keep waiting), `Disconnected` (→ "thread panicked")
|
||||
- **ProgressDialog Cancel key** (Enter when cancel button focused) calls `handle.cancel()`; Tab toggles focus
|
||||
- **Wired into app.rs event loop** — `fm.tick_progress()` runs in the timeout branch; re-render triggers every tick while `pending_progress.is_some()` or `tick_progress` completed
|
||||
|
||||
All 1381 tests pass, zero warnings, committed `9024b87934`.
|
||||
|
||||
## 10. REFERENCES
|
||||
|
||||
- MC 4.8.33 C source: `local/recipes/tui/mc/source/` (read-only cross-reference)
|
||||
- Shared TUI palette: `local/recipes/tui/redbear-tui-theme/` (single source of truth for brand red)
|
||||
- Red Bear OS build system: `local/AGENTS.md` (fork model, durability policy)
|
||||
- Project README: `local/recipes/tui/tlc/README.md`
|
||||
@@ -1,145 +0,0 @@
|
||||
# SUPERSEDED.md — Audit Log for `legacy-superseded-2026-07-25/`
|
||||
|
||||
This archive holds docs that were **current at the time** but have been superseded by newer work
|
||||
and would mislead a future reader if left at the recipe root.
|
||||
|
||||
The archive pattern mirrors `local/patches/legacy-superseded-2026-07-12/` (orphan-patch archive).
|
||||
Per project policy (`local/AGENTS.md` § "We can sources are provisioned via provision-release.sh
|
||||
and archived in sources/redbear-<release>/, verify local/recipes/*, and rebuild successfully, then
|
||||
the work is in the right place"), durable state is preserved here rather than deleted.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-25 — IMPROVEMENT-PLAN.md
|
||||
|
||||
**Source:** `local/recipes/tui/tlc/IMPROVEMENT-PLAN.md` (was at the recipe root)
|
||||
**Archived to:** `local/recipes/tui/tlc/legacy-superseded-2026-07-25/IMPROVEMENT-PLAN.md`
|
||||
**Superseded by:** `local/recipes/tui/tlc/MC-PARITY-AUDIT.md` (new, 2026-07-25)
|
||||
|
||||
### Why superseded
|
||||
|
||||
The IMPROVEMENT-PLAN.md was authored on **2026-06-18** as a Sisyphus-orchestrator comprehensive
|
||||
MC-parity assessment. At that time:
|
||||
|
||||
| Metric | 2026-06-18 (in IMPROVEMENT-PLAN) | 2026-07-25 (now) |
|
||||
|---|---|---|
|
||||
| Source files | 84 `.rs` | **148 `.rs`** |
|
||||
| Lines of code | ~28k LoC | **66,336 LoC** |
|
||||
| Tests | 698 passing | **1,433+ passing** |
|
||||
| MC parity assessment | "~65% of MC's visible surface" | **~70% (audit identifies the gaps)** |
|
||||
| Phases covered | Pre-Sprint 1 | Sprints 1–5 complete + D/F/G/V/W complete |
|
||||
|
||||
After 17 days of Sprints 1–5 + D/F/G/V/W series (per `PLAN.md` §9), the IMPROVEMENT-PLAN.md's
|
||||
assessment (~65% parity, 698 tests) is **substantially out of date**. Several of its open items
|
||||
have been resolved:
|
||||
|
||||
- Termion → crossterm migration (Phase R spike) — *deferred* (termion confirmed working on Redox)
|
||||
- Mouse support (deferred to A.1) — **resolved** (D5, MouseTerminal)
|
||||
- Power dialogs (Jobs/Panelize/VFS) — **resolved** (15f)
|
||||
- Lexer-format parity (L-tier) — **resolved** (Sprints 4–5)
|
||||
- ratatui 0.30 idioms (A.2) — **partially resolved** (panel uses List + Scrollbar)
|
||||
|
||||
### What the IMPROVEMENT-PLAN.md still has (and why we keep it)
|
||||
|
||||
- The **5-axis framework** (Visuals, Functions, Keyboard, Dialogs, Themes) — useful as a
|
||||
categorization lens for future work.
|
||||
- The **ratatui modernization track** (Phase R, A.1–A.4, 3.1–3.8) — useful for understanding the
|
||||
*intent* behind the rendering refactors that landed in Sprints 1–5.
|
||||
- The **explicit non-recommendations** (§6 — what NOT to do). These are durable.
|
||||
- The **success criteria** (§7) — partially met, useful as a forward-looking target.
|
||||
|
||||
### What the new MC-PARITY-AUDIT.md (2026-07-25) covers
|
||||
|
||||
- **Per-surface cross-reference** of every visible UI element between TLC and MC 4.8.33.
|
||||
- **Concrete file:line citations** for every gap.
|
||||
- **Severity tags** (🔴 CRITICAL / 🟠 HIGH / 🟡 MEDIUM / ⚪ LOW).
|
||||
- **Prioritized 6-phase fix plan** (A through F) with concrete file targets.
|
||||
- **Coverage matrix** showing per-surface parity percentages.
|
||||
- **Stale documentation audit** (this file).
|
||||
|
||||
### Decision tree (per AGENTS.md § "Orphan-Patch Supersession Decision Tree")
|
||||
|
||||
This is a **Phase 2.1 audit + Phase 2.2 manual review** outcome for a planning document, not a
|
||||
patch. The decision tree applied:
|
||||
|
||||
1. **Is the IMPROVEMENT-PLAN.md orphaned?** YES — its headline assessment (~65% parity, 698 tests,
|
||||
84 files) is contradicted by current verifiable state (1,433+ tests, 148 files, Sprints 1–5
|
||||
complete).
|
||||
|
||||
2. **Classify the orphan:**
|
||||
- (a) SUPERSEDED upstream — N/A (no upstream for Red Bear docs)
|
||||
- (b) INTEGRATED via different commit — APPLICABLE. Sprints 1–5 in PLAN.md already integrated
|
||||
~85% of IMPROVEMENT-PLAN.md's proposals. The remaining ~15% (ratatui modernization details,
|
||||
abstract framework) is preserved in the archive for historical reference.
|
||||
- (c) MISSING-UPSTREAM — N/A.
|
||||
|
||||
3. **Action:** MOVE to `legacy-superseded-2026-07-25/`. Preserve content. The new
|
||||
MC-PARITY-AUDIT.md is the canonical assessment.
|
||||
|
||||
### Recovery
|
||||
|
||||
If a future operator wants the old 5-axis framework or the ratatui modernization discussion,
|
||||
read the archived file. Everything is preserved. The new audit doc supersedes it for current
|
||||
state, but the historical record is intact.
|
||||
|
||||
### Verification
|
||||
|
||||
- `git log --follow -- local/recipes/tui/tlc/legacy-superseded-2026-07-25/IMPROVEMENT-PLAN.md`
|
||||
should show the original commit history (c4d4bdc586, eaf8e89785).
|
||||
- `git status` should show `R local/recipes/tui/tlc/IMPROVEMENT-PLAN.md ->
|
||||
local/recipes/tui/tlc/legacy-superseded-2026-07-25/IMPROVEMENT-PLAN.md`.
|
||||
- `grep -l "65% of MC" local/recipes/tui/tlc/*.md` should return 0 results (the stale claim
|
||||
is no longer at the recipe root).
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-26 — PLAN.md
|
||||
|
||||
**Source:** `local/recipes/tui/tlc/PLAN.md` (was at the recipe root)
|
||||
**Archived to:** `local/recipes/tui/tlc/legacy-superseded-2026-07-25/PLAN.md`
|
||||
**Superseded by:** `local/recipes/tui/tlc/MC-PARITY-AUDIT.md` (Round 1-4 status)
|
||||
|
||||
### Why superseded
|
||||
|
||||
PLAN.md was the original Round-1 plan (`Phase F` commit added a phase table to it,
|
||||
and it was updated at each `Sprint` commit through Sprint 5). After Rounds 1-4 of follow-up
|
||||
work — the macro replay engine wiring, the window move/resize/fullscreen real
|
||||
implementations, the nroff-toggle gate, the layout dispatch fixup, and the
|
||||
Round-2/3/4 status sections in MC-PARITY-AUDIT.md — its sprint-by-sprint sections are
|
||||
historical and would mislead a future reader if left at the recipe root.
|
||||
|
||||
### What the PLAN.md still has (and why we keep it)
|
||||
|
||||
- The historical sprint-by-sprint changelog (Sprint 1 → 5, Phase 14a → 14d) — useful for
|
||||
tracing the original MC-parity work.
|
||||
- The phase table at the bottom — now superseded by the Round-2/3/4 status sections in
|
||||
MC-PARITY-AUDIT.md.
|
||||
- The "Remainder" comment at the top — superseded by the canonical round-based status.
|
||||
|
||||
### What the new MC-PARITY-AUDIT.md (2026-07-26) covers
|
||||
|
||||
- **Round 1-4 status summary** as a single canonical document.
|
||||
- **Per-surface cross-reference** with file:line citations.
|
||||
- **Stale doc audit** (this file) explaining why docs were archived.
|
||||
|
||||
### Decision tree
|
||||
|
||||
1. **Is PLAN.md orphaned?** YES — its sprint-by-sprint structure is historical; the
|
||||
current plan lives in MC-PARITY-AUDIT.md.
|
||||
|
||||
2. **Classify:** SUPERSEDED. The phase table is preserved in MC-PARITY-AUDIT.md §3.1.
|
||||
|
||||
3. **Action:** MOVE to `legacy-superseded-2026-07-25/`. Preserve content.
|
||||
|
||||
### Verification
|
||||
|
||||
- `git status` should show `R local/recipes/tui/tlc/PLAN.md ->
|
||||
local/recipes/tui/tlc/legacy-superseded-2026-07-25/PLAN.md`.
|
||||
- `grep -l "Sprint 1\|Sprint 2" local/recipes/tui/tlc/*.md` should return 0 results
|
||||
(no stale sprint-by-sprint refs at the recipe root).
|
||||
- `ls local/recipes/tui/tlc/*.md` should show README.md, MC-PARITY-AUDIT.md, and
|
||||
recipe.toml only at the recipe root.
|
||||
|
||||
---
|
||||
|
||||
*End of audit log entries. Format mirrors `local/patches/legacy-superseded-2026-07-12/SUPERSEDED.md`.*
|
||||
Reference in New Issue
Block a user