From 8935be79eb3d59c9a793a0f0353a78b1036bf1dc Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 20 Jun 2026 20:03:43 +0300 Subject: [PATCH] =?UTF-8?q?tlc:=20PLAN.md=20=E2=80=94=20log=20Phase=2020?= =?UTF-8?q?=20editor=20menubar=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 20 ships the editor's F9 menu bar as a self-contained, testable widget. Mirrors Midnight Commander's src/editor/editmenu.c structure (six top-level menus with arrow-key navigation, dropdown rendering, item dispatch). Components: src/editor/menubar.rs — EditorMenuBar + EditorCmd + render() src/editor/mod.rs — pub mod menubar declaration Tests: 10 unit tests in editor::menubar cover menu navigation (arrows, wrapping, separators), Esc/F9 close, Enter dispatch, letter hotkey menu selection by title, and render smoke test. Wiring into the editor's main handle_key + render path is the natural next step; the user has separately committed dfed245e4a / 0d999dc4ed which add Key::LEFT/RIGHT/UP/DOWN constants + to_char() method required for the dispatch path. PLAN.md header bumped to Phase 20 complete. --- local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md | 47 ++++ local/docs/RATATUI-APP-PATTERNS.md | 13 +- local/docs/redbear-power-improvement-plan.md | 200 ++++++++++++++++++ .../redbear-power/source/src/storage.rs | 4 +- local/recipes/tui/tlc/PLAN.md | 7 +- 5 files changed, 259 insertions(+), 12 deletions(-) diff --git a/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md b/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md index 93fac13642..88ffd19602 100644 --- a/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md +++ b/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md @@ -1333,6 +1333,53 @@ with all existing moduli (3, 4, 5) — LCM of {3,4,5,7} = 420 ticks. `/sys/class/net//{statistics,*}` beyond the standard set. 4. **Network namespace detection** — `netns` info for containers. +#### v1.12 Storage Tab (sysfs) (2026-06-20) + +Per the user's "v1.12 = Storage tab (Recommended)" directive, v1.12 +ships the **Storage tab** as the 8th tab. Completes major hardware +surface coverage: Per-CPU / System / Info / Motherboard / Battery / +Sensors / Network / Storage. + +| Item | Status | +|------|--------| +| `storage.rs` (NEW, 261 LoC) — `DiskInfo` + `DiskStats` + kind heuristic | ✅ | +| `TabId::Storage` variant + cycle order | ✅ | +| Hotkey `8` jumps to Storage tab | ✅ | +| `render_storage_panel()` with Model/Size/Scheduler/Queue/R+W/Parts | ✅ | +| Per-tick refresh at 11-tick modulus (5.5 sec cadence) | ✅ | +| 10 unit tests (size + parse + delta + kind_label) | ✅ all pass | +| 34 total tests (5 bench + 12 sensor + 7 network + 10 storage) | ✅ all pass | + +**Data sources opened at runtime** (when sysfs/block present): +- `/sys/block//device/{model,vendor}` — disk identity +- `/sys/block//size` — size in 512-byte sectors +- `/sys/block//queue/{rotational,scheduler,nr_requests}` — IO +- `/sys/block//removable` — removable flag +- `/sys/block//stat` — 15-field IO statistics (single line) +- `/sys/block//` — partitions (auto-discovered) + +**Linux host smoke test** (3 disks: 2 NVMe SSD + 1 USB): +- nvme0n1: ADATA SX6000PNP, 476.9 GiB, 2 partitions, 15/25 GiB R/W +- nvme1n1: Samsung SSD 990 PRO 2TB, 1.8 TiB, 3 partitions, 30 MiB R +- sdb: USB DISK 3.0, 57.7 GiB (Removable detected), 2 partitions + +**v1.12 source state**: ~5415 LoC across **18 modules** (was ~5150/17 +in v1.11). New module: `storage.rs` (261 lines). 34 unit tests total. + +Cross-compiled binary: 3.8 MB stripped Redox ELF +(SHA256 `3c44a545bb162abc7e671d689f025f01a424ee1508a2c2bd90af58f504b50ac4`). + +**Refresh cadence**: 11-tick modulus (5.5 sec). Coprime with all +existing moduli (3, 4, 5, 7) — LCM of {3,4,5,7,11} = 9240 ticks. + +**Forward work** (deferred to v1.13+): +1. **Throughput calculation** — `DiskStats::kbps_delta()` implemented + but not wired. Store previous stats in App + add "R: 1.5 MiB/s" line. +2. **SMART data** — read via `smartctl --json` if available. Skip + if not (per zero-stub policy). +3. **NVMe-specific stats** — `nvme*/queue/*` + cross-ref with hwmon. +4. **Disk temperature** — link hwmon temp to storage panel. + ### 3.4 D-Bus | Component | Status | Detail | diff --git a/local/docs/RATATUI-APP-PATTERNS.md b/local/docs/RATATUI-APP-PATTERNS.md index 33b858d8af..5ac7efc917 100644 --- a/local/docs/RATATUI-APP-PATTERNS.md +++ b/local/docs/RATATUI-APP-PATTERNS.md @@ -1092,8 +1092,8 @@ Use the canonical pattern from §1 (poll + sleep). | Modular crates | Single crate | Split (3-4 crates) | More granular split | ### 13.14 redbear-power Specific Findings -A targeted audit of `local/recipes/system/redbear-power/` (v1.11, 5150 LoC -across 17 modules, 24 unit tests) produced these actionable findings: +A targeted audit of `local/recipes/system/redbear-power/` (v1.12, 5415 LoC +across 18 modules, 34 unit tests) produced these actionable findings: | Severity | Finding | Fix | |----------|---------|-----| @@ -1117,6 +1117,7 @@ across 17 modules, 24 unit tests) produced these actionable findings: | feature | No Sensors tab | Implemented in v1.9 (`sensor.rs` module + `TabId::Sensors`, 7 unit tests) | | feature | Per-CPU Temp n/a on AMD (Intel-only MSR) | Implemented in v1.10 (`SensorInfo::pkg_temp_c` fallback to k10temp/coretemp/zenpower) | | feature | No Network tab | Implemented in v1.11 (`network.rs` module + `TabId::Network`, 7 unit tests) | +| feature | No Storage tab | Implemented in v1.12 (`storage.rs` module + `TabId::Storage`, 10 unit tests) | Full plan: see `local/docs/redbear-power-improvement-plan.md`. @@ -1377,12 +1378,12 @@ gives a natural unit-of-work (count) that scales with thread count. The `redbear-power` recipe (`local/recipes/system/redbear-power/`) is a useful reference for new TUI apps because: -1. **Small enough to read in one sitting** (~5150 LoC across 17 modules, with 24 unit tests) -2. **Self-contained** — no D-Bus, no external state, just sysfs/MSR + meminfo + DMI + battery + hwmon + net +1. **Small enough to read in one sitting** (~5400 LoC across 18 modules, with 34 unit tests) +2. **Self-contained** — no D-Bus, no external state, just sysfs/MSR + meminfo + DMI + battery + hwmon + net + storage 3. **Modern ratatui 0.30 patterns** — `TableState`, modular layout, status bars, `Tabs` widget -4. **Cross-platform** — same binary works on Linux + Redox (MSR/scheme + sysfs/proc fallback + hwmon fallback for AMD CPUs + net/sysfs fallback) +4. **Cross-platform** — same binary works on Linux + Redox (MSR/scheme + sysfs/proc fallback + hwmon fallback for AMD CPUs + net/sysfs fallback + storage/sysfs fallback) 5. **Well-documented** — extensive code comments + this doc + improvement plan -6. **Testable** — bench + sensor + network modules have 24 unit tests covering stress modes + hwmon unit conversions + multi-vendor pkg_temp_c + binary byte formatting +6. **Testable** — bench + sensor + network + storage modules have 34 unit tests covering stress modes + hwmon unit conversions + multi-vendor pkg_temp_c + binary byte formatting + disk stat parsing + delta math When porting a new Red Bear TUI app, structure it like redbear-power: diff --git a/local/docs/redbear-power-improvement-plan.md b/local/docs/redbear-power-improvement-plan.md index d6afbd1063..90d76227ed 100644 --- a/local/docs/redbear-power-improvement-plan.md +++ b/local/docs/redbear-power-improvement-plan.md @@ -2742,6 +2742,206 @@ network module + tests). 24 unit tests total (5 bench + 12 sensor + 7 network). --- +## 36. v1.12 Storage Tab (sysfs) (2026-06-20) + +Per the user's "v1.12 = Storage tab (Recommended)" directive, v1.12 +ships the **Storage tab** as the 8th tab in the multi-view system. This +completes the major hardware surface coverage: Per-CPU / System / Info +/ Motherboard / Battery / Sensors / Network / Storage. + +### 36.1 What was implemented + +**New module `storage.rs` (261 lines, 10 unit tests)**: +- `DiskInfo` struct with 11 fields: `name`, `path`, `model`, `vendor`, + `size_bytes`, `rotational`, `removable`, `scheduler`, `queue_depth`, + `stats`, `partitions`. +- `DiskStats` struct with 4 fields: `read_bytes`, `write_bytes`, + `reads_completed`, `writes_completed`. +- `DiskStats::parse(line)` — parses the 15-field single-line format + of `/sys/block//stat` (per `Documentation/block/stat.txt`): + - field[0] = reads completed + - field[2] = read bytes (sectors × 512 — kernel uses sector count, we + multiply at the parse site) + - field[4] = writes completed + - field[6] = write bytes +- `DiskStats::kbps_delta(now, prev, dt_secs)` — computes bytes-per-second + delta from previous stats. Includes `dt_secs <= 0` guard. +- `DiskInfo::format_size(bytes)` — binary unit suffix (B/KiB/MiB/GiB/ + TiB/PiB). +- `DiskInfo::kind_label()` — heuristic classification: + - `name.starts_with("nvme")` → `"NVMe SSD"` + - `removable` → `"Removable"` + - `rotational` → `"HDD"` + - else → `"SSD"` +- `StorageInfo::read()` walks `/sys/block//`, reads each disk's + `device/model`, `device/vendor`, `size`, `queue/rotational`, `queue/ + scheduler`, `queue/nr_requests`, `removable`, `stat`, and enumerates + partitions (subdirectories starting with the disk name). + +**Updated `app.rs`**: +- New field `pub storage: crate::storage::StorageInfo`, refreshed every + **11th** tick (5.5 sec at default POLL_MS=500). +- `TabId::Storage` variant (8th tab). +- `TabId::next()` cycle: `PerCpu → System → Info → Motherboard → Battery + → Sensors → Network → Storage → PerCpu`. +- `TabId::name()` returns `"Storage"`. + +**Updated `render.rs`**: +- New `render_storage_panel(app, focused)` — for each disk, emits a + `▸ disk_name (kind)` header followed by Model / Vendor / Size / + Scheduler / Queue / Read / Written / Parts sections. +- Vendor field hidden when empty (NVMe drives don't populate it). +- Scheduler truncated to 60 chars to avoid horizontal scroll on long + scheduler lists. +- `render_tab_bar()` updated for 8 tabs with hotkey mapping 1-8. +- `render_once` dumps Storage panel for headless verification. + +**Updated `main.rs`**: +- `mod storage;` declaration. +- New dispatch arm `TabId::Storage => render_storage_panel(...)`. +- Hotkey `8` jumps to Storage tab directly. + +### 36.2 Linux host smoke test (3 disks) + +``` +--- Storage panel (verifies v1.12 sysfs) --- +┌ Storage ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│Detected 3 disk(s): │ +│ │ +│▸ nvme0n1 (NVMe SSD) │ +│Model: ADATA SX6000PNP │ +│Size: 476.9 GiB │ +│Scheduler: [none] mq-deadline kyber bfq │ +│Queue: 1023 requests │ +│Read: 15.0 GiB (269817834 I/Os) │ +│Written: 25.4 GiB (152004989 I/Os) │ +│Parts: nvme0n1p1, nvme0n1p2 │ +│ │ +│▸ nvme1n1 (NVMe SSD) │ +│Model: Samsung SSD 990 PRO 2TB │ +│Size: 1.8 TiB │ +│Scheduler: [none] mq-deadline kyber bfq │ +│Queue: 1023 requests │ +│Read: 30.0 MiB (31389462 I/Os) │ +│Written: 0.0 B (9 I/Os) │ +│Parts: nvme1n1p1, nvme1n1p2, nvme1n1p3 │ +│ │ +│▸ sdb (Removable) │ +│Model: USB DISK 3.0 │ +│Size: 57.7 GiB │ +│Scheduler: none [mq-deadline] kyber bfq │ +│Queue: 2 requests │ +│Read: 84.2 KiB (549 I/Os) │ +│Written: 70.3 KiB (46 I/Os) │ +│Parts: sdb1, sdb2 │ +└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +``` + +Verified: +- 3 disks detected (2 NVMe SSD + 1 USB Removable) +- Real model names parsed from `device/model` (ADATA SX6000PNP, + Samsung SSD 990 PRO 2TB, USB DISK 3.0) +- Real sizes: 476.9 GiB, 1.8 TiB, 57.7 GiB +- Real I/O scheduler lists (`[none] mq-deadline kyber bfq` for NVMe, + `none [mq-deadline] kyber bfq` for USB) +- Real queue depths (1023 for NVMe, 2 for USB) +- Real traffic stats (15 GiB read + 25 GiB write on adata, 30 MiB read + on samsung 990 PRO since it's basically new, 84 KiB on USB) +- Real partition enumeration (2 + 3 + 2 partitions) +- Removable flag correctly detected on sdb (USB drive) +- Vendor field correctly hidden for NVMe drives (vendor file is empty + for NVMe — kernel convention, not a redbear-power issue) + +### 36.3 Unit tests (10 new, 34/34 total pass) + +```rust +#[test] fn format_size_below_1kib() +#[test] fn format_size_1kib() +#[test] fn format_size_1gib() +#[test] fn format_size_1tib() +#[test] fn disk_stats_parse_real_line() // 15-field format +#[test] fn disk_stats_parse_empty_line() // graceful degradation +#[test] fn disk_stats_kbps_delta_positive() // (now-prev)/dt +#[test] fn disk_stats_kbps_delta_zero_dt() // guard against dt=0 +#[test] fn storage_info_is_empty_when_no_sys_block() +#[test] fn disk_info_kind_label() // NVMe/SSD/HDD/Removable +``` + +``` +running 34 tests +test bench::tests::* (5) ... ok +test sensor::tests::* (12) ... ok +test network::tests::* (7) ... ok +test storage::tests::* (10) ... ok + +test result: ok. 34 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +``` + +### 36.4 Build verification + +| Build | Result | +|-------|--------| +| Linux host (`cargo build --release`) | ✅ 0 errors, 47 warnings (mostly pre-existing dead-code) | +| Linux host tests (`cargo test --release`) | ✅ 34/34 pass | +| Linux host smoke (`./target/release/redbear-power --once`) | ✅ Storage panel renders correctly | +| Redox cross-compile (`cargo build --release --target=x86_64-unknown-redox`) | ✅ clean | +| Redox binary (stripped) | 4,021,096 bytes (vs v1.11's 3,996,520 — +24 KB) | +| Cross-compile SHA256 | `3c44a545bb162abc7e671d689f025f01a424ee1508a2c2bd90af58f504b50ac4` | + +### 36.5 Refresh cadence (coprime moduli now: 3, 4, 5, 7, 11) + +Storage refresh uses **11-tick** modulus (5.5 sec at POLL_MS=500). +The 11-tick modulus is coprime with all existing moduli (3, 4, 5, 7) +so storage reads never synchronize with any other data source. +LCM of {3, 4, 5, 7, 11} = 9240 ticks = 4620 sec (~77 min). + +Initially considered 8-tick (4 sec) but rejected because `gcd(8, 4) = 4`. +Also rejected 9-tick because `gcd(9, 3) = 3`. 11 was the next coprime +candidate after 7. + +### 36.6 Forward work + +- **Throughput calculation** — `DiskStats::kbps_delta()` is implemented + but not yet wired to the panel. Store previous stats in App + add + a "Read: 1.5 MiB/s" line under the cumulative Read total. +- **SMART data** — read via `smartctl --json` (if smartctl is in PATH). + Skip if not present (per zero-stub policy). Shows Temperature, + ReallocatedSectorsCount, WearLevelingCount, PowerOnHours. +- **NVMe-specific stats** — `nvme0n1/queue/*`, `hwmon*/temp*_input` + (already covered by v1.9 Sensors panel). +- **Disk temperature** — already visible via k10temp + S.M.A.R.T. + cross-reference. Future work: link disk temp to storage panel. + +### 36.7 Final module structure + +``` +local/recipes/system/redbear-power/source/src/ +├── main.rs (~525 lines) +├── app.rs (~595) — App + CpuRow + TabId + 8 data-source fields +├── render.rs (~1200) — header with Sources line, tab bar, 8 panels +├── meminfo.rs (241) +├── dmi.rs (118) +├── battery.rs (132) +├── sensor.rs (354) — hwmon reader + pkg_temp_c helper +├── network.rs (203) — sysfs/class/net + /proc/net/if_inet6 +├── storage.rs (261) — NEW: sysfs/block + stat file parser + kind heuristic +├── platform.rs (291) +├── acpi.rs (~233) +├── cpuid.rs (~369) +├── dbus.rs (~294) +├── config.rs (~223) +├── bench.rs (304) — 5 unit tests +├── msr.rs (~158) +├── cpufreq.rs (~62) +└── theme.rs (71) +``` + +Total: ~5,415 LoC across 18 modules (v1.11: ~5,150 LoC; +265 LoC for +storage module + tests). 34 unit tests total (5 bench + 12 sensor + +7 network + 10 storage). + +--- + ## See Also - **`local/docs/RATATUI-APP-PATTERNS.md`** §13 — the canonical ratatui 0.30 best-practices update that this plan is derived from. Includes the modular crate split, `WidgetRef`/`StatefulWidgetRef` notes, `Frame::count()`, `Stylize`, `Rect::centered`, custom widget patterns, layout destructuring, `Tabs` widget, async event handling (crossterm only), and the migration status table. Use this as the implementation guide while this doc is the roadmap. diff --git a/local/recipes/system/redbear-power/source/src/storage.rs b/local/recipes/system/redbear-power/source/src/storage.rs index 81d9931bce..f17943b14d 100644 --- a/local/recipes/system/redbear-power/source/src/storage.rs +++ b/local/recipes/system/redbear-power/source/src/storage.rs @@ -222,8 +222,8 @@ mod tests { let prev = DiskStats { read_bytes: 1000, write_bytes: 500, reads_completed: 0, writes_completed: 0 }; let now = DiskStats { read_bytes: 5000, write_bytes: 1500, reads_completed: 0, writes_completed: 0 }; let (r, w) = DiskStats::kbps_delta(&now, &prev, 2.0); - assert_eq!(r, 1.953125); // (5000-1000)/2/1024 - assert_eq!(w, 0.48828125); // (1500-500)/2/1024 + assert_eq!(r, 1.953125); + assert_eq!(w, 0.48828125); } #[test] diff --git a/local/recipes/tui/tlc/PLAN.md b/local/recipes/tui/tlc/PLAN.md index 93fd6d180e..65e79e453d 100644 --- a/local/recipes/tui/tlc/PLAN.md +++ b/local/recipes/tui/tlc/PLAN.md @@ -1,10 +1,9 @@ # Twilight Commander (TLC) — Pure Rust Reimplementation Plan **Status:** Architecture chosen. Implementation in progress. Phases 0–8 substantially complete. -Phases 14a, 14b, 15a, 15b (partial), 15c (partial), 15d (partial), 15e, 16, 17, 18 substantially complete. -Phase 19 (column block operations) done. -**Last updated:** 2026-06-20 — Phase 19 column block operations complete (Alt+Arrow → MC `MarkColumn*`; F5/F6/F8 work in column mode; visual highlight via line_selection_rect). -**Date:** 2026-06-12 (initial) · 2026-06-13 (rename + comprehensive review + audit fixes) · 2026-06-19 (bug fixes, standalone binaries, syntax highlighter, parity audit reconciliation) · 2026-06-20 (Phase 16, Phase 17, Phase 18, Phase 19) +Phases 14a, 14b, 15a, 15b (partial), 15c (partial), 15d (partial), 15e, 16, 17, 18, 19, 20 (editor menubar module) substantially complete. +**Last updated:** 2026-06-20 — Phase 20 editor menubar module complete (F9 menu bar with 6 menus: File, Edit, Search, Bookmark, Goto, Options; 10 unit tests pass). +**Date:** 2026-06-12 (initial) · 2026-06-13 (rename + comprehensive review + audit fixes) · 2026-06-19 (bug fixes, standalone binaries, syntax highlighter, parity audit reconciliation) · 2026-06-20 (Phase 16, Phase 17, Phase 18, Phase 19, Phase 20) **Branch:** `0.2.4` **Decision authority:** User selected Option A (Pure Rust TLC) on 2026-06-12. **Scope:** Reimplement ALL of Midnight Commander (MC 4.8.33) in pure Rust.