Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ Built-in plugins and their command prefixes are:
- Shell commands (`sh echo hi`)
- System actions (`sys shutdown`)
- Process list (`ps`), providing "Switch to" and "Kill" actions
- System information (`info`, `info cpu`, `info mem`, `info disk`)
- Timers and alarms (`timer 5m tea`, `alarm 07:30`). Use `timer list` to view
remaining time. Pending alarms are saved to `alarms.json` and resume after
restarting the launcher. A plugin setting controls pop-up dialogs when a
Expand Down Expand Up @@ -213,6 +214,8 @@ The bookmarks plugin uses the `bm` prefix. Use `bm add <url>` to save a link,
`bm rm` to list and remove bookmarks (optionally filtering with a pattern) or
`bm list` to show all bookmarks. Searching with `bm <term>` matches both URLs
and aliases.
The system information plugin uses the `info` prefix. Type `info` to show CPU,
memory and disk usage or `info cpu` for a single metric.
### Security Considerations
The shell plugin runs commands using the system shell without sanitising input. Only enable it if you trust the commands you type. Errors while spawning the process are logged.
Type `sh` in the launcher to open the shell command editor for managing predefined commands.
Expand Down
2 changes: 2 additions & 0 deletions src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::plugins::history::HistoryPlugin;
use crate::plugins::folders::FoldersPlugin;
use crate::plugins::system::SystemPlugin;
use crate::plugins::processes::ProcessesPlugin;
use crate::plugins::sysinfo::SysInfoPlugin;
use crate::plugins::help::HelpPlugin;
use crate::plugins::youtube::YoutubePlugin;
use crate::plugins::reddit::RedditPlugin;
Expand Down Expand Up @@ -68,6 +69,7 @@ impl PluginManager {
self.register(Box::new(FoldersPlugin::default()));
self.register(Box::new(SystemPlugin));
self.register(Box::new(ProcessesPlugin));
self.register(Box::new(SysInfoPlugin));
self.register(Box::new(ShellPlugin));
self.register(Box::new(HistoryPlugin));
self.register(Box::new(NotesPlugin::default()));
Expand Down
1 change: 1 addition & 0 deletions src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod youtube;
pub mod reddit;
pub mod wikipedia;
pub mod processes;
pub mod sysinfo;
pub mod weather;
pub mod notes;
pub mod todo;
Expand Down
88 changes: 88 additions & 0 deletions src/plugins/sysinfo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use crate::actions::Action;
use crate::plugin::Plugin;
use sysinfo::{Disks, System};

/// Display basic system usage statistics using the `info` prefix.
pub struct SysInfoPlugin;

impl SysInfoPlugin {
fn cpu_action(system: &System) -> Action {
let usage = system.global_cpu_usage();
Action {
label: format!("CPU usage {:.0}%", usage),
desc: "SysInfo".into(),
action: "sysinfo:cpu".into(),
args: None,
}
}

fn mem_action(system: &System) -> Action {
let total = system.total_memory();
let used = system.used_memory();
let percent = if total > 0 {
used as f64 / total as f64 * 100.0
} else { 0.0 };
Action {
label: format!("Memory usage {:.0}%", percent),
desc: "SysInfo".into(),
action: "sysinfo:mem".into(),
args: None,
}
}

fn disk_action() -> Action {
let mut disks = Disks::new_with_refreshed_list();
let mut total = 0u64;
let mut avail = 0u64;
for d in disks.list() {
total += d.total_space();
avail += d.available_space();
}
let used = total.saturating_sub(avail);
let percent = if total > 0 {
used as f64 / total as f64 * 100.0
} else { 0.0 };
Action {
label: format!("Disk usage {:.0}%", percent),
desc: "SysInfo".into(),
action: "sysinfo:disk".into(),
args: None,
}
}
}

impl Plugin for SysInfoPlugin {
fn search(&self, query: &str) -> Vec<Action> {
if !query.starts_with("info") {
return Vec::new();
}
let trimmed = query.trim().to_lowercase();
let mut system = System::new();
system.refresh_cpu_usage();
system.refresh_memory();
match trimmed.as_str() {
"info" => vec![
Self::cpu_action(&system),
Self::mem_action(&system),
Self::disk_action(),
],
"info cpu" => vec![Self::cpu_action(&system)],
"info mem" => vec![Self::mem_action(&system)],
"info disk" => vec![Self::disk_action()],
_ => Vec::new(),
}
}

fn name(&self) -> &str {
"sysinfo"
}

fn description(&self) -> &str {
"Show CPU, memory and disk usage (prefix: `info`)"
}

fn capabilities(&self) -> &[&str] {
&["search"]
}
}

30 changes: 30 additions & 0 deletions tests/sysinfo_plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use multi_launcher::plugin::Plugin;
use multi_launcher::plugins::sysinfo::SysInfoPlugin;

#[test]
fn search_info_returns_actions() {
let plugin = SysInfoPlugin;
let results = plugin.search("info");
assert!(!results.is_empty());
}

#[test]
fn search_cpu_returns_action() {
let plugin = SysInfoPlugin;
let results = plugin.search("info cpu");
assert_eq!(results.len(), 1);
}

#[test]
fn search_mem_returns_action() {
let plugin = SysInfoPlugin;
let results = plugin.search("info mem");
assert_eq!(results.len(), 1);
}

#[test]
fn search_disk_returns_action() {
let plugin = SysInfoPlugin;
let results = plugin.search("info disk");
assert_eq!(results.len(), 1);
}