Replaces the Phase 21 TODO stub with the full MC
edit_user_menu flow:
src/editor/mod.rs — Editor::run_user_menu_command(raw_command):
1. Expand percent variables via PercentCtx::for_file
(MC: expand_format).
2. Stash active selection to clipfile (MC: edit_save_block).
Block file path exposed via %b for downstream expansion.
3. Run via 'sh -c <expanded>' so user can use pipes,
redirections, globs (matches MC user_menu_cmd shell-out).
4. On non-empty stdout: delete any selection, then
insert_str at cursor position (MC: edit_insert_file).
Buffer cursor is synced to editor cursor after
delete_selection so the insert lands at the right spot.
5. Status line: 'Menu: ok (N bytes inserted)' on success,
'Menu: exit N, stderr: ...' on failure.
src/editor/handlers.rs — replaces TODO with call to
self.run_user_menu_command(command).
Tests: 1141 passed (was 1137, +4):
- inserts stdout at cursor
- replaces active selection (cursor sync verified)
- reports non-zero exit status
- expands %f to current file path via cat %f
Release binaries build clean.
Mirrors Midnight Commander's editor user-menu flow (MC
src/editor/edit.c::edit_user_menu + CK_UserMenu).
Architecture reuses the existing filemanager usermenu infra:
filemanager::usermenu::UserMenuDialog is constructed with
condition='edit' so the same ~/.config/tlc/menu file works
in both filemanager (condition='view') and editor contexts.
Components:
src/editor/usermenu.rs (new) — EditorUserMenu wrapper that
owns the dialog + cursor snapshot + selection flag +
clipfile path. Plus reuses
PercentCtx::for_file; default_block_file() resolves
/tlc/editor.block or ~/.config/tlc/editor.block.
src/editor/menubar.rs — adds EditorCmd::UserMenu and
EditorCmd::EditUserMenu variants (cross-referenced MC
CK_UserMenu + CK_EditUserMenu).
src/editor/mod.rs — new usermenu_session: Option<EditorUserMenu>
field, init None in both constructors, Editor::open_user_menu
method, dispatch_editor_cmd wires UserMenu/EditUserMenu
variants (EditUserMenu surfaces path via status message —
full Open wiring deferred to follow-up).
src/editor/handlers.rs — F11 keybind opens user menu (F11 is
MC's editor UserMenu binding per misc/mc.default.keymap;
F2 in the editor is reserved for Save). Routing at top of
handle_key intercepts Running/Cancel/Execute outcomes;
Execute surfaces the command in the status line — full
selection-stash + stdout-capture is deferred TODO.
Tests: 1137 passed (was 1132, +5: 4 editor::usermenu module
tests covering expand, default_block_file, EditorUserMenu
construction, key routing; 1 handler test for F11 open + Esc close).
Closes the v1.13 §37.6 forward-work item. Process tab now shows
real-time CPU usage per process, computed from the delta of
total CPU ticks between successive 13th-tick refreshes.
Source code already landed (process.rs + app.rs + render.rs).
This commit captures full v1.14 docs:
- Improvement plan §38 (CPU% in Process Tab)
- CONSOLE-TO-KDE §3.3.2 v1.14
- RATATUI-APP-PATTERNS §13.14 (audit table +14) + §14 (19 modules, 47 tests)
Implementation summary:
- New cpu_pct: f64 field on ProcessInfo
- New ProcInfo::read_with_cpu_pct(prev, dt_secs, num_cpus)
- Wall-clock dt (SystemTime) — accurate even when TUI pauses
- saturating_sub on ticks prevents underflow if now < prev
- num_cpus from self.cpus.len() (Per-CPU detection result)
- 4 new unit tests (formula + zero + underflow + dt=0)
- 47/47 total tests pass
Math sanity check (verified by unit test):
utime=100→200, stime=50→80, dt=2sec, num_cpus=4
delta = 280-150 = 130 ticks / 2 sec = 65 ticks/sec
CPU% = 65 / 4 cpus * 100 = 1625.0%
Cross-compile SHA256: d46cd66b8e158e2327839ef502879951877a5500d4a40807d3dbc72ed7397231.
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.
Adds the 6th tab in the multi-view system: Sensors, reading
hardware monitoring data from /sys/class/hwmon/hwmonN/ on Linux
hosts. Detects all chips (k10temp, coretemp, nvme, mt7921, r8169,
spd5118, zenpower, etc.) and their temp/fan/voltage/power/current
sensors with proper unit conversions.
New module sensor.rs (231 lines):
- SensorKind enum: Temp (m°C) / Fan (RPM) / Voltage (mV) /
Power (µW) / Current (mA), with #[default] on Temp
- SensorReading: kind, label, raw_value, display_value
- HwmonChip: name, path, readings
- SensorInfo::read() walks /sys/class/hwmon/hwmonN/, reads
name + all *_input files (with corresponding *_label for
human-readable names like 'Tctl', 'Composite')
- 7 unit tests covering unit conversions + empty state
Updated app.rs:
- New field sensors: SensorInfo, refreshed every 3rd tick
(1.5 sec at POLL_MS=500). 3-tick modulus is coprime to
meminfo's 4 and battery's 5 — no thundering-herd syscalls.
- TabId::Sensors variant (6th tab)
- TabId::next() cycles PerCpu → System → Info → Motherboard →
Battery → Sensors → PerCpu
Updated render.rs:
- New render_sensor_panel(app, focused) with per-chip sections
using ▸ arrow + chip name as bold header, then Label/Value pairs
in 12-char left-aligned label / 14-char right-aligned value
layout. Empty state: '(no sensors detected — /sys/class/hwmon/
not readable)' rather than wall of ?.
- render_tab_bar() updated for 6 tabs with hotkey 1/2/3/4/5/6
- render_once now dumps Sensors panel for headless verification
Updated main.rs:
- mod sensor; declaration
- New dispatch arm TabId::Sensors => render_sensor_panel
- Hotkey 6 jumps to Sensors tab directly
- render_sensor_panel added to imports
Linux host smoke test (Manjaro, Ryzen 9 7900X, 7 chips, 11 sensors):
▸ mt7921_phy0 temp 58.0 °C
▸ r8169_0_e00:00 temp 51.0 °C
▸ k10temp Tccd1 82.6 °C
Tccd2 57.1 °C
Tctl 85.6 °C
▸ nvme Sensor 2 53.9 °C
Composite 50.9 °C
Sensor 1 50.9 °C
▸ spd5118 temp 50.0 °C
▸ spd5118 temp 51.5 °C
▸ nvme Composite 48.9 °C
Unit conversions verified: m°C → °C (50850 → 50.9°C), mV → V,
µW → W, mA → A. Unit tests: 12/12 pass (5 bench + 7 sensor).
Source state: 4885 LoC across 16 modules.
Redox stripped: 3.8 MB (SHA256 7a7c31bc...).
Docs: improvement plan §33, CONSOLE-TO-KDE §3.3.2 v1.9,
RATATUI-APP-PATTERNS §13.14 + §14 (16 modules, 12 tests).
Cleans up §14 (Cross-Reference: redbear-power as a Reference
Implementation) which had three duplicate headers from successive
edits. Collapses to a single canonical version and adds the
v1.8-specific bullet:
'Testable — bench module has 5 unit tests covering all stress
modes + toggles'
Net change: -25 lines, +2 lines.
Source code for v1.8 (bench.rs + main.rs + render.rs) and full
docs for §32 + v1.8 in improvement plan + CONSOLE-TO-KDE plan
landed in commit d6ac3d1377 (qtbase bundled). This commit
completes the doc-only cleanup that didn't fit in that bundle.
Phase 18 dialog popup shell migration is now complete. All 16
dialogs from the Phase 17 follow-up list have been migrated to
terminal::popup::render_popup, giving them all the MC-matching
rounded borders + drop shadow chrome.
Commits in the Phase 18 series:
e4987256f7 — Phase 18a: filter, encoding, pattern, confirm,
overwrite, layout, panel_options, chattr
2b2b5803ba — Phase 18c: permission, owner, connection, config,
compare, sort, progress
c032c9a787 — menubar dropdown (via user commit)
Tree (full-screen directory tree) intentionally skipped — uses
the entire frame area, not a centered popup, so it does not
benefit from the popup shell or drop shadow.
Remaining Clear uses in the codebase are all legitimate:
- menubar top F9 bar (not a popup)
- tree.rs (full-screen dialog)
- popup.rs (render_popup itself)
- dialog.rs (Dialog widget body clear)
Tests: 1112 passed; release binaries build clean.
Closes the v1.6 forward-work item from §30.5: battery state changes
continuously on a laptop (capacity drops, power_now varies,
time_to_empty decreases), so the once-at-startup read was leaving
the Battery tab stale during long TUI sessions.
Updated app.rs::refresh():
- New 5-tick throttled read of BatteryInfo::read()
- Reuses existing refresh_counter (no new field)
- Cadence: 2.5 sec at default POLL_MS=500 (0.04% CPU cost)
- Independent of meminfo's 4-tick modulus (coprime moduli prevent
thundering-herd syscalls — only 5% of ticks see simultaneous
meminfo + battery reads)
- find_battery_dir() re-checks on each refresh, so a laptop
plugged in mid-session populates the Battery tab on the next
5th refresh tick without any external trigger
Verification:
- Mock battery at /tmp/fake-battery/BAT0/ with capacity=67:
redbear-power --once shows Capacity: 67%
- Changed capacity to 50, re-ran --once: shows Capacity: 50%
- Strace confirms 14 sysfs opens per read() call
Cadence rationale (modulus 5 chosen):
- Every tick (500ms): 0.2% CPU — too aggressive
- Every 5th tick (2.5s): 0.04% CPU — chosen
- Once at startup: 0% CPU — too stale
- Every 4th tick would also work but 5 chosen for clean
coprime separation from meminfo's 4-tick modulus
Build: same 3.8 MB stripped Redox binary (single if branch added).
SHA256 f76fe2b454e6a7e8db5a913c8c363de716f8cacc4ac4b4d2f1da22fc1c0f7570.
Docs: improvement plan §31, CONSOLE-TO-KDE §3.3.2 v1.7,
RATATUI-APP-PATTERNS §13.19 (coprime moduli pattern) + §14.
Continue Phase 18 mechanical migration of centered dialogs to the
shared terminal::popup::render_popup() shell (MC-matching rounded
borders + drop shadow):
permission, owner, connection_dialog, config_dialog,
compare, sort_dialog, progress
All 7 share the same pattern: inline Clear + Block + title replaced
with render_popup() + centered_cols_rect()/centered_percent_rect().
Title color now derives from [dialog] dtitle via the unified shell.
compare.rs also dropped its local centered_rect helper (used only
for the popup shell; the body Block stays inline since it's a
sub-frame, not the popup shell).
Tree (full-screen directory tree dialog) intentionally skipped — it
uses the entire frame area, not a centered popup, so it does not
benefit from the popup shell or shadow.
Tests: 1112 passed (no regressions).
Same mechanical pattern as Phase 17 skin_dialog. Each dialog now
inherits the MC-matching rounded borders + drop shadow from the
shared terminal::popup::render_popup() shell:
filter_dialog, encoding_dialog, pattern_dialog, confirm_dialog,
overwrite_dialog, layout_dialog, panel_options, chattr_dialog
Changes per file:
- Remove import of Block, Borders, Clear
- Add import of centered_cols_rect, render_popup
- Replace inline geometry+Clear+Block with centered_cols_rect()
+ render_popup() call
- Title no longer needs manual styling (render_popup applies
[dialog] dtitle colors)
confirm_dialog also dropped its BorderType::Double variant (now
matches the rest of TLC's rounded look); removed its now-unused
local centered_rect helper.
Tests: 1112 passed (no regressions; same count as pre-Phase 18).
Document Phase 17 (skin_dialog popup shell migration) and enumerate
the 16 remaining dialogs that still bypass render_popup:
sort, compare, overwrite, filter, tree, encoding, connection,
layout, owner, panel_options, pattern, confirm, config,
permission, chattr, progress
Each is ~50-100 LOC of refactor work (replace inline Clear+Block
with render_popup call). Same premium chrome (rounded borders,
MC-matching drop shadow) as Phase 17.
Migrate src/filemanager/skin_dialog.rs::render() from the bespoke
Clear+Block shell (no shadow, square borders) to
terminal::popup::render_popup (rounded borders + MC-matching drop
shadow + centered layout).
This makes skin_dialog visually consistent with all 13 other TLC
dialogs that already use render_popup. The /tmp/5.png screenshot
(showing the skin selector without shadow) is now fixed — every
TLC dialog has identical premium chrome.
Deleted:
- Duplicate 'centered_rect' helper (replaced by popup::centered_percent_rect)
- Inline Block construction
- Manual Clear render
Added:
- render_drops_shadow_outside_popup test asserts the bottom-right
cell carries theme.shadow background (assertion of MC's
tty_draw_box_shadow algorithm at the dialog level).
Tests: 1112 passed (was 1111, +1).
After cross-referencing MC's button.c (lib/widget/button.c) and the
ratatui button pattern survey, two gaps remained in Phase 16:
1. MC defines NARROW_BUTTON ([X], no inner padding) — adds for
future compact toolbar/dialog use. Width formula: label + 2.
2. While width was already chars().count() (Unicode-safe CJK), no
test asserted this. Added explicit assertion: '日本語' Normal = 7,
Default = 9 cells.
Tests: 1111 passed (was 1109, +2).
Binaries rebuild clean.
Ref: MC lib/widget/button.c button_flags_t {NORMAL,DEFPUSH,NARROW,HIDDEN}.
Implements the three gaps from §26:
- Phase A: new platform.rs (291 lines) with runtime probes for
MSR, ACPI PSS, /proc/stat, cpufreq sysfs, hwmon — emits one
eprintln! diagnostic per source at startup
- Phase B: msr.rs reads /dev/cpu/{cpu}/msr on Linux via lseek+pread;
acpi.rs read_load falls back to /proc/stat; acpi.rs read_acpi_pss
reads /sys/devices/system/cpu/cpu*/cpufreq/scaling_available_frequencies;
cpufreq.rs reads/writes /sys/.../scaling_governor. Removed the
hardcoded P0..P5 fallback table (violates zero-stub policy).
- Phase C: replaced misleading "MSR: not available (QEMU?)" with a
"Sources: MSR=ok PSS=no load=ok gov=ok hwmon=ok" header line
that shows per-source availability at a glance.
Verified on AMD Ryzen 9 7900X (24 threads, AMD-pstate driver):
- /dev/cpu/0/msr detected (probes ok; reads blocked by CAP_SYS_RAWIO
for non-root users — kernel-level permission, not a code issue)
- /proc/stat readable (load populated after second refresh tick)
- /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor readable
(governor shows "powersave")
- hwmon2 (k10temp) detected (not yet wired into per-CPU temp column —
requires per-driver hwmon→tempX_input mapping, deferred to v1.4)
- PSS=no (amd-pstate driver does not expose scaling_available_frequencies)
v1.3 source: 3501 LoC across 12 modules. Cross-compiled Redox binary
3.3 MB stripped, SHA256 cbc0a6d04e9d9252314dd71a1c411d4c488417e25f8d860970f718990864431a.
What is NOT yet wired (deferred to v1.4):
- Hwmon→per-CPU temp mapping (k10temp Tdie is package-level only)
- MSR reads without root (CAP_SYS_RAWIO required)
- zenpower / zen 5+ per-driver logic
- Add x11proto to redbear-full.toml package list
- libxau recipe updated with x11proto dependency and custom build script
- Fixes libxau build failure: 'Package xproto was not found'
Adds comprehensive analysis answering the user's "no per-core info"
question. Documents:
§26.1 Why every per-CPU column is empty on Linux (root cause)
- 6-column trace (Freq, PkgW, Temp, P-state, State, Flags) + Load%
- Maps Redox scheme paths to Linux equivalents (where they exist)
- Identifies that two paths already have Linux fallbacks (detect_cpus,
read_cpu_id) and four do not
§26.2 cpu-x patterns reviewed
- 6-row comparison table: missing-MSR / daemon / per-source UI /
refresh logic / CLI flags / temperature fallback
- Concludes: redbear-power's "n/a" placeholder is already better UX
than cpu-x's empty cells; daemon broker pattern is NOT applicable;
retry-cache and per-source logging ARE worth adopting
§26.3 Recommended Phase A/B/C (v1.3)
- Phase A: new platform.rs with /dev/cpu/*/msr + /proc/stat +
/sys/devices/system/cpu/cpu*/cpufreq/ fallbacks, ~80-120 LoC
- Phase B: replace hardcoded P0..P5 fallback (acpi.rs:101-108) with
real sysfs reads; generalize read_load to /proc/stat
- Phase C: header per-source availability badge
§26.4 AMD-specific concerns (separate from Linux fallback)
- msr.rs reader is Intel-only by design (file comment)
- AMD Zen uses 0xC0010063/0xC0010064 for P-states and k10temp for
temp, not Intel MSR 0x19c
- Recommended: vendor detection branch + Linux hwmon fallback
§26.5 Conclusion
- Screenshot is NOT a bug — TUI is working as designed
- Three substantive gaps: no Linux fallbacks, no per-source
startup logging, no header availability summary
- All three addressable without violating zero-stub policy
Source: cpu-x v4.7 at /tmp/cpu-x-src/ (cloned in earlier session).