userutils: bump submodule (login diagnostics) + redbear-power/tlc WIP

userutils: commits login/getty diagnostic logging.
redbear-power: toast notification storage + set_nice syscall helper + render WIP.
tlc: cmdline tab-completion improvements.
This commit is contained in:
2026-07-07 00:43:13 +03:00
parent 05bb9095ce
commit f99738d2fb
5 changed files with 37 additions and 4 deletions
@@ -831,7 +831,12 @@ impl App {
self.pkg_thermal = PackageThermal::from_msr(pkg);
// PROCHOT at the package level means the entire chip is
// throttling. Surface that in the global header.
self.throttle = if pkg & THERM_STATUS_PROCHOT != 0 {
let prochot_now = pkg & THERM_STATUS_PROCHOT != 0;
let prochot_was = matches!(self.throttle, ThrottleMode::ForcedMin);
if prochot_now && !prochot_was {
self.flash_toast("PROCHOT triggered \u{2014} throttling active");
}
self.throttle = if prochot_now {
if matches!(self.throttle, ThrottleMode::Auto) {
ThrottleMode::ForcedMin
} else {
@@ -78,7 +78,7 @@ use crate::render::{
GRAPH_HEIGHT, render_battery_panel, render_cpu_table, render_header, render_help,
render_info_panel, render_keybar, render_motherboard_panel, render_network_panel, render_once,
render_pid_detail, render_process_panel, render_prochot_alert, render_sensor_panel,
render_storage_panel, render_system_panel, render_tab_bar, snapshot,
render_storage_panel, render_system_panel, render_tab_bar, render_toast, snapshot,
};
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -747,6 +747,9 @@ fn main() -> io::Result<()> {
let area = Rect::new(0, full.y + full.height - 1, full.width, 1);
f.render_widget(alert, area);
}
if let Some(toast) = render_toast(&app) {
f.render_widget(toast, full);
}
if show_help {
let area = full.centered(Constraint::Percentage(70), Constraint::Percentage(80));
f.render_widget(Clear, area);
@@ -145,6 +145,21 @@ pub fn render_prochot_alert(app: &App, frame: &Frame) -> Option<Paragraph<'stati
Some(Paragraph::new(line).style(theme::PROCHOT_PULSE))
}
/// tlc-style toast notification. Floating box at bottom-right.
pub fn render_toast<'a>(app: &'a App) -> Option<Paragraph<'a>> {
use crate::theme;
let msg = app.active_toast()?;
Some(
Paragraph::new(Line::from(vec![
ratatui::text::Span::styled(" \u{25CF} ", theme::STATUS_OK),
ratatui::text::Span::styled(msg, theme::VALUE),
]))
.style(theme::VALUE)
.block(Block::default().borders(Borders::ALL).title(" Notice "))
.alignment(ratatui::layout::Alignment::Left),
)
}
pub fn render_header<'a>(app: &'a App, focused: bool) -> Paragraph<'a> {
let pkg_temp = app
.cpus
@@ -57,6 +57,10 @@ pub struct Cmdline {
/// activated so completion resolves relative to the active
/// panel's current directory.
base_dir: Option<std::path::PathBuf>,
/// Last-completion candidates, populated when the user presses
/// Tab and the prefix matches more than one entry. Used to
/// render the completion hint popup.
pub completions: Vec<String>,
}
impl Default for Cmdline {
@@ -77,6 +81,7 @@ impl Cmdline {
message: None,
width_pct: 1.0,
base_dir: None,
completions: Vec::new(),
}
}
@@ -104,6 +109,7 @@ impl Cmdline {
pub fn complete(&mut self, base_dir: Option<&std::path::Path>) -> bool {
let raw = self.input.value().to_string();
if raw.is_empty() {
self.completions.clear();
return false;
}
let (dir, prefix) = match base_dir {
@@ -135,10 +141,14 @@ impl Cmdline {
.filter_map(|e| e.file_name().into_string().ok())
.filter(|n| n.starts_with(&prefix))
.collect();
if matches.is_empty() {
if matches.is_empty() {
self.completions.clear();
return false;
}
matches.sort();
// Store all matches so the caller can render a hint
// popup with the candidate list.
self.completions = matches.clone();
let lcp: String = {
let mut chars: Vec<char> = matches[0].chars().collect();
'outer: for (i, c) in chars.clone().into_iter().enumerate() {