diff --git a/.config/fish/config.fish b/.config/fish/config.fish index bd5fb85..9a6e418 100644 --- a/.config/fish/config.fish +++ b/.config/fish/config.fish @@ -29,3 +29,6 @@ end # bun set --export BUN_INSTALL "$HOME/.bun" set --export PATH $BUN_INSTALL/bin $PATH + +# opencode +fish_add_path /home/alex/.opencode/bin diff --git a/.config/hypr/hyprland.lua b/.config/hypr/hyprland.lua index 1ad1273..98ef9d1 100644 --- a/.config/hypr/hyprland.lua +++ b/.config/hypr/hyprland.lua @@ -54,12 +54,13 @@ hl.workspace_rule({ -- Application and session bindings. local noctalia_ipc = "noctalia msg " +local pi_status_ui_toggle = 'ui="${PI_STATUS_UI_BINARY:-$HOME/projects/pi-status-bridge/ui/src-tauri/target/release/pi-status-ui}"; if test -x "$ui"; then setsid -f env TMPDIR=/tmp "$ui" --toggle >/tmp/pi-status-ui.log 2>&1; fi' -- Tap and release Super on its own to open Noctalia's control-center home. hl.bind(main_mod .. " + SUPER_L", hl.dsp.exec_cmd(noctalia_ipc .. "panel-toggle control-center"), { release = true }) hl.bind(main_mod .. " + RETURN", hl.dsp.exec_cmd(terminal)) hl.bind(main_mod .. " + CTRL + RETURN", hl.dsp.exec_cmd(terminal .. " -e zellij")) hl.bind(main_mod .. " + SHIFT + RETURN", hl.dsp.exec_cmd("rofi -show combi")) -hl.bind(main_mod .. " + SPACE", hl.dsp.exec_cmd(noctalia_ipc .. "panel-toggle launcher")) +hl.bind(main_mod .. " + SPACE", hl.dsp.exec_cmd(pi_status_ui_toggle)) hl.bind(main_mod .. " + S", hl.dsp.exec_cmd(noctalia_ipc .. "panel-toggle control-center")) hl.bind(main_mod .. " + SHIFT + F", hl.dsp.exec_cmd(noctalia_ipc .. "bar-toggle main")) hl.bind(main_mod .. " + SHIFT + R", hl.dsp.exec_cmd("hyprctl reload")) @@ -67,7 +68,7 @@ hl.bind(main_mod .. " + CTRL + L", hl.dsp.exec_cmd("command -v hyprlock >/dev/nu hl.bind(main_mod .. " + CTRL + SHIFT + ALT + S", hl.dsp.exec_cmd("systemctl suspend")) hl.bind(main_mod .. " + CTRL + SHIFT + ALT + H", hl.dsp.exec_cmd("systemctl hibernate")) hl.bind(main_mod .. " + SHIFT + C", hl.dsp.window.close()) -hl.bind(main_mod .. " + CTRL + SHIFT + F", hl.dsp.exec_cmd("env LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 flameshot gui")) +hl.bind(main_mod .. " + CTRL + SHIFT + F", hl.dsp.exec_cmd("/home/alex/.local/bin/screenshot-select")) -- Audio, media, brightness, and calculator keys. hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { locked = true }) @@ -130,6 +131,7 @@ local function float_rule(name, match, border_size) end hl.window_rule({ name = "thunderbird-workspace", match = { class = "^Thunderbird$" }, workspace = "9" }) +float_rule("pi-status-ui", { class = "^pi-status-ui$" }, 1) float_rule("alsamixer", { title = "^alsamixer$" }, 1) float_rule("qalculate", { title = "^Qalculate!$" }) float_rule("psensor", { class = "^Psensor$" }) diff --git a/.config/noctalia/10-base.toml b/.config/noctalia/10-base.toml index 2daed59..0577563 100644 --- a/.config/noctalia/10-base.toml +++ b/.config/noctalia/10-base.toml @@ -22,6 +22,9 @@ type = "alex/system-status:cpu" [widget.ram] type = "alex/system-status:memory" +[widget.network-rate] +type = "alex/system-status:network-rate" + [widget.temp] type = "sysmon" stat = "cpu_temp" diff --git a/.config/noctalia/90-machine.toml##class.arch_laptop b/.config/noctalia/90-machine.toml##class.arch_laptop index 41e0c61..4cfbf6e 100644 --- a/.config/noctalia/90-machine.toml##class.arch_laptop +++ b/.config/noctalia/90-machine.toml##class.arch_laptop @@ -16,7 +16,8 @@ auto_hide = false smart_auto_hide = false reserve_space = true start = ["tray", "group:g4", "privacy"] -thickness = 28 +# Accommodate the battery-rate widget's wattage + ETA lines without clipping. +thickness = 39 widget_spacing = 10 [[bar.main.capsule_group]] @@ -31,7 +32,7 @@ padding = 6.0 enabled = true fill = "surface_variant" id = "g2" -members = ["network_rx", "network_tx", "network", "bluetooth"] +members = ["network-rate", "network", "bluetooth"] opacity = 1.0 padding = 6.0 diff --git a/.local/share/noctalia/plugins/system-status/cpu.luau b/.local/share/noctalia/plugins/system-status/cpu.luau index 0e5a89e..b9768ca 100644 --- a/.local/share/noctalia/plugins/system-status/cpu.luau +++ b/.local/share/noctalia/plugins/system-status/cpu.luau @@ -11,6 +11,26 @@ local command = [[awk ' local previous = {} local request_in_flight = false +local function colorForUsage(percent) + if percent >= 85 then + return "#ff453a" -- red + end + if percent >= 70 then + return "#ff8800" -- orange + end + if percent >= 50 then + return "#ffd60a" -- yellow + end + return "#ffffff" -- white +end + +local function renderStatus(text, color) + barWidget.render(ui.row({ gap = 4, align = "center" }, { + ui.glyph({ name = "cpu", size = 14, color = color }), + ui.label({ text = text, color = color }), + })) +end + function update() noctalia.setUpdateInterval(2000) @@ -23,7 +43,8 @@ function update() request_in_flight = false if result.exitCode ~= 0 then - barWidget.setText("CPU —") + renderStatus("CPU —", "#fff1e6") + barWidget.setTooltip("Unable to read CPU utilization") return end @@ -47,12 +68,19 @@ function update() previous[name] = { total = total, idle = idle } end - barWidget.setGlyph("cpu") if sampled > 0 then - barWidget.setText(string.format("%.0f%%", utilization)) - barWidget.setTooltip("Aggregate CPU utilization (100% per logical CPU)") + -- Keep the aggregate display, but base its heat color on average + -- utilization per logical CPU so multicore systems do not stay red. + local per_cpu_utilization = utilization / sampled + renderStatus(string.format("%.0f%%", utilization), colorForUsage(per_cpu_utilization)) + barWidget.setTooltip( + string.format( + "Aggregate CPU utilization (100%% per logical CPU) · %.0f%% average per CPU", + per_cpu_utilization + ) + ) else - barWidget.setText("—") + renderStatus("—", "#fff1e6") barWidget.setTooltip("Sampling CPU utilization…") end end) diff --git a/.local/share/noctalia/plugins/system-status/memory.luau b/.local/share/noctalia/plugins/system-status/memory.luau index f1bcc88..e5dc05f 100644 --- a/.local/share/noctalia/plugins/system-status/memory.luau +++ b/.local/share/noctalia/plugins/system-status/memory.luau @@ -8,16 +8,37 @@ $1 == "SwapFree:" { swap_free = $2 } END { if (!total || !available) exit 1 used = total - available + memory_used = used * 100 / total if (swap_total > 0) { swap_used = (swap_total - swap_free) * 100 / swap_total - printf "RAM %.1f/%.1f GiB S %.0f%%\n", used / 1048576, total / 1048576, swap_used + printf "%.1f/%.1f GiB S %.0f%%\t%.0f\t%.0f\n", used / 1048576, total / 1048576, swap_used, memory_used, swap_used } else { - printf "RAM %.1f/%.1f GiB S —\n", used / 1048576, total / 1048576 + printf "%.1f/%.1f GiB S —\t%.0f\t0\n", used / 1048576, total / 1048576, memory_used } }' /proc/meminfo]] local request_in_flight = false +local function colorForUsage(percent) + if percent >= 85 then + return "#ff453a" -- red + end + if percent >= 70 then + return "#ff8800" -- orange + end + if percent >= 50 then + return "#ffd60a" -- yellow + end + return "#ffffff" -- white +end + +local function renderStatus(text, color) + barWidget.render(ui.row({ gap = 4, align = "center" }, { + ui.glyph({ name = "memory", size = 14, color = color }), + ui.label({ text = text, color = color }), + })) +end + function update() noctalia.setUpdateInterval(5000) @@ -30,14 +51,32 @@ function update() request_in_flight = false if result.exitCode ~= 0 then - barWidget.setText("RAM —") + renderStatus("RAM —", "#fff1e6") barWidget.setTooltip("Unable to read /proc/meminfo") return end - local text = string.gsub(result.stdout, "%s+$", "") - barWidget.setGlyph("memory") - barWidget.setText(text) - barWidget.setTooltip("RAM used / total · S = swap used") + local text, memory_percent, swap_percent = string.match( + result.stdout, + "^([^\t]+)\t([%d.]+)\t([%d.]+)" + ) + memory_percent = tonumber(memory_percent) + swap_percent = tonumber(swap_percent) + + if text == nil or memory_percent == nil or swap_percent == nil then + renderStatus("RAM —", "#fff1e6") + barWidget.setTooltip("Unable to parse memory utilization") + return + end + + local highest_usage = math.max(memory_percent, swap_percent) + renderStatus(text, colorForUsage(highest_usage)) + barWidget.setTooltip( + string.format( + "RAM used / total · %.0f%% RAM · %.0f%% swap", + memory_percent, + swap_percent + ) + ) end) end diff --git a/.local/share/noctalia/plugins/system-status/plugin.toml b/.local/share/noctalia/plugins/system-status/plugin.toml index c99af93..f0096fd 100644 --- a/.local/share/noctalia/plugins/system-status/plugin.toml +++ b/.local/share/noctalia/plugins/system-status/plugin.toml @@ -3,7 +3,7 @@ name = "System Status" version = "1.0.0" plugin_api = 3 author = "alex" -description = "Low-overhead memory and swap indicator for the bar." +description = "Low-overhead system and network status indicators for the bar." [[widget]] id = "cpu" @@ -17,6 +17,10 @@ entry = "memory.luau" id = "battery-rate" entry = "battery-rate.luau" +[[widget]] +id = "network-rate" +entry = "network-rate.luau" + [[widget]] id = "gpu-mode" entry = "gpu-mode.luau" diff --git a/.pi/agent/settings.json b/.pi/agent/settings.json index 2c82baf..c8fdc8a 100644 --- a/.pi/agent/settings.json +++ b/.pi/agent/settings.json @@ -21,8 +21,7 @@ "npm:gentle-engram", "npm:pi-lens", "npm:@juicesharp/rpiv-todo", - "npm:@majorgilles/pi-grill-me", - "git:git.commumedia.org/alex/pi-map" + "npm:@majorgilles/pi-grill-me" ], "powerline": "full", "hideThinkingBlock": false,