Merge pull request #10 from avivbintangaringga/main
feat: add nix-monitor plugin
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
# Nix Monitor
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Nix Monitor** checks Nixpkgs update by comparing local nix hash and remote Nixpkgs's hash
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Shows local and remote nixpkgs hash
|
||||||
|
- Shows NixOS and optionally Home Manager generations
|
||||||
|
- Shows Nix Store size and closure size
|
||||||
|
- Customizable clean and update command
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- Git
|
||||||
|
- NixOS commands (`nix`, `nixos-rebuild`, `nixos-version`)
|
||||||
|
- Linux tools (`du`, `cat`, `awk`, `grep`, `tail`, `wc`, `kill`, `pkill`)
|
||||||
|
- Optionally `home-manager`
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- You need to add the update command in the setting first
|
||||||
|
- By default, clean command executes `nix-collect-garbage -d`, you can change it too!
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
function tr(key)
|
||||||
|
return noctalia.tr(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfg(key)
|
||||||
|
return noctalia.getConfig(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getState(key)
|
||||||
|
return noctalia.state.get(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function displayWidget(glyph: string, text: string, color: string?)
|
||||||
|
if color == nil then
|
||||||
|
color = "on_surface"
|
||||||
|
end
|
||||||
|
|
||||||
|
if cfg("colorize_text") then
|
||||||
|
barWidget.setColor(color)
|
||||||
|
end
|
||||||
|
|
||||||
|
if cfg("colorize_glyph") then
|
||||||
|
barWidget.setGlyphColor(color)
|
||||||
|
end
|
||||||
|
|
||||||
|
if cfg("show_glyph") then
|
||||||
|
barWidget.setGlyph(glyph)
|
||||||
|
end
|
||||||
|
|
||||||
|
if cfg("show_text") then
|
||||||
|
barWidget.setText(text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Hooks
|
||||||
|
function update()
|
||||||
|
if getState("isRemoteCheckRunning") then
|
||||||
|
displayWidget(cfg("checking_glyph"), tr("bar.checking"), cfg("checking_color"))
|
||||||
|
else
|
||||||
|
if getState("isUpdateAvailable") then
|
||||||
|
displayWidget(cfg("update_available_glyph"), tr("bar.update_available"), cfg("update_available_color"))
|
||||||
|
else
|
||||||
|
if getState("localHash") == "n/a" or getState("remoteHash") == "n/a" then
|
||||||
|
displayWidget(cfg("unknown_glyph"), tr("bar.unknown"), cfg("unknown_color"))
|
||||||
|
else
|
||||||
|
displayWidget(cfg("up_to_date_glyph"), tr("bar.up_to_date"), cfg("up_to_date_color"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function onClick()
|
||||||
|
noctalia.togglePanel("avivbintangaringga/nix-monitor:panel")
|
||||||
|
end
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
noctalia.setUpdateInterval(5000)
|
||||||
|
|
||||||
|
-- Helper functions
|
||||||
|
function tr(key)
|
||||||
|
return noctalia.tr(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfg(key)
|
||||||
|
return noctalia.getConfig(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getState(key)
|
||||||
|
return noctalia.state.get(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function setState(key, val)
|
||||||
|
noctalia.state.set(key, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
function watchState(key, cb)
|
||||||
|
noctalia.state.watch(key, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
function killWithPidFile(file)
|
||||||
|
if noctalia.fileExists(file) then
|
||||||
|
local pid = noctalia.string.trim(noctalia.readFile(file))
|
||||||
|
|
||||||
|
if type(pid) == "string" and pid ~= "" then
|
||||||
|
noctalia.runAsync(`pkill -9 -P {pid} && kill -9 {pid}`)
|
||||||
|
end
|
||||||
|
noctalia.removeFile(file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Vars
|
||||||
|
local branch = cfg("branch")
|
||||||
|
local updateCheckInterval = cfg("update_check_interval")
|
||||||
|
local updateCheckDurationThreshold = cfg("update_check_duration_threshold")
|
||||||
|
local systemStatsCheckInterval = cfg("system_stats_check_interval")
|
||||||
|
local systemStatsCheckDurationThreshold = cfg("system_stats_check_duration_threshold")
|
||||||
|
local stateDir = "/tmp/nix-monitor"
|
||||||
|
local remoteHashFile = stateDir .. "/remoteHash"
|
||||||
|
local storeSizeFile = stateDir .. "/storeSize"
|
||||||
|
local closureSizeFile = stateDir .. "/closureSize"
|
||||||
|
local remotePidFile = stateDir .. "/remoteHash.pid"
|
||||||
|
local storePidFile = stateDir .. "/storeSize.pid"
|
||||||
|
local closurePidFile = stateDir .. "/closureSize.pid"
|
||||||
|
local showUpdateAvailableNotification = true
|
||||||
|
|
||||||
|
local localHashCmd = "nixos-version --hash 2> /dev/null | cut -c -7"
|
||||||
|
local remoteHashCmd = "git ls-remote https://github.com/NixOS/nixpkgs " .. branch .. " 2>/dev/null | cut -c 1-7"
|
||||||
|
local nixosGenerationsCmd = "nixos-rebuild list-generations | tail -n +2 | wc -l"
|
||||||
|
local nixosCurrentGenCmd = "nixos-rebuild list-generations | grep True | awk '{print $1}'"
|
||||||
|
local hmGenerationsCmd = "home-manager generations | wc -l"
|
||||||
|
local hmCurrentGenCmd = "home-manager generations | grep '(current)' | awk '{print $5}'"
|
||||||
|
local storeSizeCmd = "du -s --block-size=1GiB /nix/store | awk '\{print $1 \" GiB\"\}'"
|
||||||
|
local closureSizeCmd = "nix path-info --closure-size --human-readable /run/current-system | awk '\{print $2, $3\}'"
|
||||||
|
|
||||||
|
-- State inits
|
||||||
|
setState("lastLocalCheckTime", os.time())
|
||||||
|
setState("lastRemoteCheckRunTime", os.time())
|
||||||
|
setState("lastRemoteCheckTime", os.time())
|
||||||
|
setState("lastRemoteCheckTimeStr", "n/a")
|
||||||
|
setState("localHash", "n/a")
|
||||||
|
setState("remoteHash", "n/a")
|
||||||
|
setState("isRemoteCheckRunning", false)
|
||||||
|
setState("isUpdateAvailable", false)
|
||||||
|
setState("lastGenerationsCheckTime", 0)
|
||||||
|
setState("isSystemStatsCheckRunning", false)
|
||||||
|
setState("lastSystemStatsCheckTime", os.time())
|
||||||
|
setState("lastSystemStatsCheckRunTime", os.time())
|
||||||
|
setState("hasHomeManager", noctalia.commandExists("home-manager"))
|
||||||
|
setState("nixosGenerations", "•••")
|
||||||
|
setState("nixosCurrentGen", "•••")
|
||||||
|
setState("hmGenerations", "•••")
|
||||||
|
setState("hmCurrentGen", "•••")
|
||||||
|
setState("storeSize", "•••")
|
||||||
|
setState("closureSize", "•••")
|
||||||
|
|
||||||
|
noctalia.mkdirAll(stateDir)
|
||||||
|
noctalia.writeFile(remoteHashFile, "")
|
||||||
|
|
||||||
|
-- Local check
|
||||||
|
function checkLocalHash()
|
||||||
|
setState("lastLocalCheckTime", os.time())
|
||||||
|
noctalia.runAsync(localHashCmd, function(res)
|
||||||
|
if res.exitCode == 0 then
|
||||||
|
setState("localHash", noctalia.string.trim(res.stdout))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remote check
|
||||||
|
--
|
||||||
|
-- Currently it is writing to a file because when I use the runAsync with
|
||||||
|
-- the callback, it is timed out
|
||||||
|
function checkRemoteHash(delay: number?)
|
||||||
|
local delay = delay or 0
|
||||||
|
|
||||||
|
if getState("isRemoteCheckRunning") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
setState("isRemoteCheckRunning", true)
|
||||||
|
setState("lastRemoteCheckRunTime", os.time())
|
||||||
|
|
||||||
|
if cfg("show_update_check_notification") then
|
||||||
|
noctalia.notify(tr("notification.checking_update"))
|
||||||
|
end
|
||||||
|
|
||||||
|
noctalia.writeFile(remoteHashFile, "")
|
||||||
|
noctalia.runAsync(`sleep {delay} && {remoteHashCmd} > {remoteHashFile} & echo $! > {remotePidFile}`)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if the remoteHash check command is finished by reading the file
|
||||||
|
function checkRemoteStatus()
|
||||||
|
local remoteHash = noctalia.string.trim(noctalia.readFile(remoteHashFile))
|
||||||
|
|
||||||
|
if type(remoteHash) == "string" and remoteHash ~= "" then
|
||||||
|
setState("isRemoteCheckRunning", false)
|
||||||
|
setState("remoteHash", remoteHash)
|
||||||
|
onRemoteHashCheckCompleted()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function onRemoteHashCheckCompleted()
|
||||||
|
if cfg("show_update_check_notification") then
|
||||||
|
noctalia.notify(tr("notification.update_check_completed"))
|
||||||
|
end
|
||||||
|
|
||||||
|
setState("lastRemoteCheckTime", os.time())
|
||||||
|
|
||||||
|
local date = noctalia.formatTime("%Y-%m-%d %H:%M:%S")
|
||||||
|
setState("lastRemoteCheckTimeStr", date)
|
||||||
|
|
||||||
|
noctalia.removeFile(remotePidFile)
|
||||||
|
end
|
||||||
|
|
||||||
|
function cancelRemoteCheck()
|
||||||
|
setState("isRemoteCheckRunning", false)
|
||||||
|
killWithPidFile(remotePidFile)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Generation check
|
||||||
|
function checkGenerations()
|
||||||
|
setState("lastGenerationsCheckTime", os.time())
|
||||||
|
|
||||||
|
noctalia.runAsync(nixosGenerationsCmd, function(result)
|
||||||
|
setState("nixosGenerations", result.stdout)
|
||||||
|
end)
|
||||||
|
|
||||||
|
noctalia.runAsync(nixosCurrentGenCmd, function(result)
|
||||||
|
setState("nixosCurrentGen", result.stdout)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if getState("hasHomeManager") then
|
||||||
|
noctalia.runAsync(hmGenerationsCmd, function(result)
|
||||||
|
setState("hmGenerations", result.stdout)
|
||||||
|
end)
|
||||||
|
|
||||||
|
noctalia.runAsync(hmCurrentGenCmd, function(result)
|
||||||
|
setState("hmCurrentGen", result.stdout)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- System stats check
|
||||||
|
function checkSystemStats()
|
||||||
|
if getState("isSystemStatsCheckRunning") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
setState("isSystemStatsCheckRunning", true)
|
||||||
|
setState("lastSystemStatsCheckRunTime", os.time())
|
||||||
|
|
||||||
|
-- Sleep 0 to trigger noctalia calling /bin/sh
|
||||||
|
noctalia.runAsync(`sleep 0 && {storeSizeCmd} > {storeSizeFile} & echo $! > {storePidFile}`)
|
||||||
|
noctalia.runAsync(`sleep 0 && {closureSizeCmd} > {closureSizeFile} & echo $! > {closurePidFile}`)
|
||||||
|
end
|
||||||
|
|
||||||
|
function checkSystemStatsFile()
|
||||||
|
local storeSize = noctalia.string.trim(noctalia.readFile(storeSizeFile))
|
||||||
|
local closureSize = noctalia.string.trim(noctalia.readFile(closureSizeFile))
|
||||||
|
|
||||||
|
if typeof(storeSize) == "string" and storeSize ~= "" then
|
||||||
|
setState("storeSize", storeSize)
|
||||||
|
end
|
||||||
|
|
||||||
|
if typeof(closureSize) == "string" and closureSize ~= "" then
|
||||||
|
setState("closureSize", closureSize)
|
||||||
|
end
|
||||||
|
|
||||||
|
local success = typeof(storeSize) == "string" and storeSize ~= "" and
|
||||||
|
typeof(closureSize) == "string" and closureSize ~= ""
|
||||||
|
|
||||||
|
if success then
|
||||||
|
setState("isSystemStatsCheckRunning", false)
|
||||||
|
setState("lastSystemStatsCheckTime", os.time())
|
||||||
|
noctalia.removeFile(storePidFile)
|
||||||
|
noctalia.removeFile(closurePidFile)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cancelSystemCheck()
|
||||||
|
setState("isSystemStatsCheckRunning", false)
|
||||||
|
killWithPidFile(storePidFile)
|
||||||
|
killWithPidFile(closurePidFile)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Init
|
||||||
|
checkLocalHash()
|
||||||
|
checkRemoteHash(10)
|
||||||
|
checkSystemStats()
|
||||||
|
|
||||||
|
-- Command watcher
|
||||||
|
watchState("command", function(command)
|
||||||
|
if command == "check" then
|
||||||
|
checkLocalHash()
|
||||||
|
cancelRemoteCheck()
|
||||||
|
checkRemoteHash()
|
||||||
|
checkGenerations()
|
||||||
|
checkSystemStats()
|
||||||
|
end
|
||||||
|
|
||||||
|
if command == "cancel" then
|
||||||
|
cancelRemoteCheck()
|
||||||
|
cancelSystemCheck()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Hooks
|
||||||
|
--
|
||||||
|
|
||||||
|
function update()
|
||||||
|
-- Check local hash every 10 seconds
|
||||||
|
if (os.time() - getState("lastLocalCheckTime") >= 10) then
|
||||||
|
checkLocalHash()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remote check
|
||||||
|
if (os.time() - getState("lastRemoteCheckTime")) >= 60 * updateCheckInterval then
|
||||||
|
checkRemoteHash()
|
||||||
|
end
|
||||||
|
|
||||||
|
if getState("isRemoteCheckRunning") then
|
||||||
|
checkRemoteStatus()
|
||||||
|
|
||||||
|
-- Remote check timeout
|
||||||
|
if (os.time() - getState("lastRemoteCheckRunTime")) >= 60 * updateCheckDurationThreshold then
|
||||||
|
cancelRemoteCheck()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if getState("localHash") ~= getState("remoteHash") and
|
||||||
|
getState("localHash") ~= "n/a" and getState("remoteHash") ~= "n/a"
|
||||||
|
then
|
||||||
|
setState("isUpdateAvailable", true)
|
||||||
|
|
||||||
|
if cfg("show_update_available_notification") and showUpdateAvailableNotification then
|
||||||
|
noctalia.notify(tr("notification.update_is_available"))
|
||||||
|
showUpdateAvailableNotification = false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
setState("isUpdateAvailable", false)
|
||||||
|
showUpdateAvailableNotification = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Generations check
|
||||||
|
if (os.time() - getState("lastGenerationsCheckTime")) >= 20 then
|
||||||
|
checkGenerations()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- System stats check
|
||||||
|
if (os.time() - getState("lastSystemStatsCheckTime")) >= 60 * systemStatsCheckInterval then
|
||||||
|
checkSystemStats()
|
||||||
|
end
|
||||||
|
|
||||||
|
if getState("isSystemStatsCheckRunning") then
|
||||||
|
checkSystemStatsFile()
|
||||||
|
|
||||||
|
-- System stats check timeout
|
||||||
|
if (os.time() - getState("lastSystemStatsCheckRunTime")) >= 60 * systemStatsCheckDurationThreshold then
|
||||||
|
cancelSystemCheck()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function onExit(signal)
|
||||||
|
cancelRemoteCheck()
|
||||||
|
cancelSystemCheck()
|
||||||
|
end
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
-- Helper functions
|
||||||
|
|
||||||
|
function tr(key)
|
||||||
|
return noctalia.tr(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfg(key)
|
||||||
|
return noctalia.getConfig(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function getState(key)
|
||||||
|
return noctalia.state.get(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function setState(key, val)
|
||||||
|
noctalia.state.set(key, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
function watchState(key, cb)
|
||||||
|
noctalia.state.watch(key, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function labelBox(subtext, text, glyph)
|
||||||
|
return ui.column({
|
||||||
|
fill = "surface/0.7",
|
||||||
|
flexGrow = 1,
|
||||||
|
height = 100,
|
||||||
|
radius = 8,
|
||||||
|
align = "center",
|
||||||
|
padding = 12,
|
||||||
|
justify = "space_between"
|
||||||
|
}, {
|
||||||
|
ui.glyph({
|
||||||
|
name = glyph,
|
||||||
|
size = 30
|
||||||
|
}),
|
||||||
|
ui.label({
|
||||||
|
text = text,
|
||||||
|
fontWeight = "bold"
|
||||||
|
}),
|
||||||
|
ui.label({
|
||||||
|
text = subtext,
|
||||||
|
fontSize = 12,
|
||||||
|
fontWeight = "medium"
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function statLabel(text, glyph: string?)
|
||||||
|
local items = {}
|
||||||
|
if glyph ~= nil then
|
||||||
|
table.insert(items, ui.glyph({
|
||||||
|
name = glyph,
|
||||||
|
size = 20
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(items, ui.label({
|
||||||
|
text = text,
|
||||||
|
fontSize = 12,
|
||||||
|
fontWeight = "semibold"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return ui.row({
|
||||||
|
gap = 8,
|
||||||
|
align = "center"
|
||||||
|
}, items)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function render()
|
||||||
|
local firstBtn
|
||||||
|
|
||||||
|
if getState("isRemoteCheckRunning") then
|
||||||
|
firstBtn = ui.button({
|
||||||
|
text = tr("panel.cancel"),
|
||||||
|
glyph = "cancel",
|
||||||
|
flexGrow = 1,
|
||||||
|
variant = "destructive",
|
||||||
|
onClick = "onCancelClicked",
|
||||||
|
})
|
||||||
|
else
|
||||||
|
if getState("isUpdateAvailable") then
|
||||||
|
firstBtn = ui.button({
|
||||||
|
text = tr("panel.update"),
|
||||||
|
glyph = "progress-down",
|
||||||
|
flexGrow = 1,
|
||||||
|
variant = "primary",
|
||||||
|
onClick = "onUpdateClicked",
|
||||||
|
})
|
||||||
|
else
|
||||||
|
firstBtn = ui.button({
|
||||||
|
text = tr("panel.check"),
|
||||||
|
glyph = "search",
|
||||||
|
flexGrow = 1,
|
||||||
|
variant = "outline",
|
||||||
|
onClick = "onCheckClicked",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local lastCheckTimeStr = getState("lastRemoteCheckTimeStr")
|
||||||
|
local generationItems = {
|
||||||
|
statLabel(`SYS: {getState("nixosGenerations")}`, "device-desktop"),
|
||||||
|
statLabel(`{tr("panel.current")}: {getState("nixosCurrentGen")}`, "device-desktop-check")
|
||||||
|
}
|
||||||
|
|
||||||
|
if getState("hasHomeManager") then
|
||||||
|
table.insert(generationItems,
|
||||||
|
statLabel(`HM: {getState("hmGenerations")}`, "home")
|
||||||
|
)
|
||||||
|
|
||||||
|
table.insert(generationItems,
|
||||||
|
statLabel(`{tr("panel.current")}: {getState("hmCurrentGen")}`, "home-check")
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
panel.render(
|
||||||
|
ui.column({
|
||||||
|
gap = 8,
|
||||||
|
}, {
|
||||||
|
ui.row({
|
||||||
|
justify = "space_between",
|
||||||
|
align = "center"
|
||||||
|
}, {
|
||||||
|
ui.label({
|
||||||
|
text = tr("panel.title"),
|
||||||
|
fontSize = 20,
|
||||||
|
}),
|
||||||
|
|
||||||
|
ui.label({
|
||||||
|
text = cfg("branch"),
|
||||||
|
fontSize = 14,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
ui.separator({
|
||||||
|
thickness = 2,
|
||||||
|
orientation = "horizontal",
|
||||||
|
spacing = 4
|
||||||
|
}),
|
||||||
|
ui.row({
|
||||||
|
gap = 8,
|
||||||
|
align = "center",
|
||||||
|
justify = "space_between"
|
||||||
|
}, {
|
||||||
|
labelBox(tr("panel.local"), getState("localHash"), "devices-pc"),
|
||||||
|
labelBox(tr("panel.remote"), getState("remoteHash"), "world"),
|
||||||
|
}),
|
||||||
|
ui.separator({
|
||||||
|
thickness = 2,
|
||||||
|
orientation = "horizontal",
|
||||||
|
spacing = 4
|
||||||
|
}),
|
||||||
|
ui.row({
|
||||||
|
fill = "surface/0.7",
|
||||||
|
align = "center",
|
||||||
|
justify = "space_between",
|
||||||
|
flexGrow = 1,
|
||||||
|
paddingV = 4,
|
||||||
|
paddingH = 8,
|
||||||
|
radius = 8,
|
||||||
|
}, generationItems),
|
||||||
|
ui.row({
|
||||||
|
fill = "surface/0.7",
|
||||||
|
align = "center",
|
||||||
|
justify = "center",
|
||||||
|
flexGrow = 1,
|
||||||
|
paddingV = 4,
|
||||||
|
paddingH = 8,
|
||||||
|
radius = 8,
|
||||||
|
}, {
|
||||||
|
statLabel(`{tr("panel.store_size")}: {getState("storeSize")}`, "database"),
|
||||||
|
ui.spacer({width = 16}),
|
||||||
|
statLabel(`{tr("panel.closure_size")}: {getState("closureSize")}`, "server"),
|
||||||
|
}),
|
||||||
|
ui.separator({
|
||||||
|
thickness = 2,
|
||||||
|
orientation = "horizontal",
|
||||||
|
spacing = 4
|
||||||
|
}),
|
||||||
|
ui.label({
|
||||||
|
text = `{tr("panel.last_checked")}: {lastCheckTimeStr}`
|
||||||
|
}),
|
||||||
|
ui.row({
|
||||||
|
gap = 8,
|
||||||
|
align = "center",
|
||||||
|
justify = "space_between"
|
||||||
|
}, {
|
||||||
|
firstBtn,
|
||||||
|
ui.button({
|
||||||
|
text = tr("panel.clean"),
|
||||||
|
glyph = "trash",
|
||||||
|
flexGrow = 1,
|
||||||
|
variant = "destructive",
|
||||||
|
onClick = "onCleanClicked",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- State watcher
|
||||||
|
watchState("isRemoteCheckRunning", render)
|
||||||
|
watchState("lastRemoteCheckTimeStr", render)
|
||||||
|
watchState("localHash", render)
|
||||||
|
watchState("remoteHash", render)
|
||||||
|
|
||||||
|
-- Hooks
|
||||||
|
function onOpen(_ctx)
|
||||||
|
render()
|
||||||
|
end
|
||||||
|
|
||||||
|
function update()
|
||||||
|
render()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Click handler
|
||||||
|
function onCheckClicked()
|
||||||
|
setState("command", "check")
|
||||||
|
end
|
||||||
|
|
||||||
|
function onCancelClicked()
|
||||||
|
setState("command", "cancel")
|
||||||
|
end
|
||||||
|
|
||||||
|
function onUpdateClicked()
|
||||||
|
command = cfg("update_command")
|
||||||
|
if command == "" then
|
||||||
|
noctalia.notifyError(tr("notification.update_command_is_empty"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if cfg("close_on_enter") then
|
||||||
|
command = `{command} && echo && read -p "{tr("terminal.press_enter_to_exit")}"`
|
||||||
|
end
|
||||||
|
|
||||||
|
panel.close()
|
||||||
|
noctalia.runInTerminal(command)
|
||||||
|
end
|
||||||
|
|
||||||
|
function onCleanClicked()
|
||||||
|
command = cfg("clean_command")
|
||||||
|
if command == "" then
|
||||||
|
noctalia.notifyError(tr("notification.clean_command_is_empty"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if cfg("close_on_enter") then
|
||||||
|
command = `{command} && echo && read -p "{tr("terminal.press_enter_to_exit")}"`
|
||||||
|
end
|
||||||
|
|
||||||
|
panel.close()
|
||||||
|
noctalia.runInTerminal(command)
|
||||||
|
end
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
id = "avivbintangaringga/nix-monitor"
|
||||||
|
name = "Nix Monitor"
|
||||||
|
version = "1.0.0"
|
||||||
|
min_noctalia = "5.0.0"
|
||||||
|
icon = "package"
|
||||||
|
author = "avivbintangaringga"
|
||||||
|
description = "Nixpkgs update monitor"
|
||||||
|
tags = [ "nixos", "utility" ]
|
||||||
|
dependencies = [ "nix", "nixos-version", "nixos-rebuild", "git", "du", "cat", "awk", "grep", "tail", "wc", "kill", "pkill" ]
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[[widget]]
|
||||||
|
id = "nix-monitor"
|
||||||
|
entry = "nix_monitor.luau"
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "show_text"
|
||||||
|
type = "bool"
|
||||||
|
label_key = "setting.widget.show_text.label"
|
||||||
|
default = true
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "colorize_text"
|
||||||
|
type = "bool"
|
||||||
|
label_key = "setting.widget.colorize_text.label"
|
||||||
|
default = false
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "show_glyph"
|
||||||
|
type = "bool"
|
||||||
|
label_key = "setting.widget.show_glyph.label"
|
||||||
|
default = true
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "colorize_glyph"
|
||||||
|
type = "bool"
|
||||||
|
label_key = "setting.widget.colorize_glyph.label"
|
||||||
|
default = true
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "up_to_date_glyph"
|
||||||
|
type = "glyph"
|
||||||
|
label_key = "setting.widget.up_to_date_glyph.label"
|
||||||
|
default = "rosette-discount-check"
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "up_to_date_color"
|
||||||
|
type = "color"
|
||||||
|
label_key = "setting.widget.up_to_date_color.label"
|
||||||
|
default = "#57ff57"
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "checking_glyph"
|
||||||
|
type = "glyph"
|
||||||
|
label_key = "setting.widget.checking_glyph.label"
|
||||||
|
default = "loader-3"
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "checking_color"
|
||||||
|
type = "color"
|
||||||
|
label_key = "setting.widget.checking_color.label"
|
||||||
|
default = "#ffeb57"
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "update_available_glyph"
|
||||||
|
type = "glyph"
|
||||||
|
label_key = "setting.widget.update_available_glyph.label"
|
||||||
|
default = "cloud-download"
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "update_available_color"
|
||||||
|
type = "color"
|
||||||
|
label_key = "setting.widget.update_available_color.label"
|
||||||
|
default = "#ff5757"
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "unknown_glyph"
|
||||||
|
type = "glyph"
|
||||||
|
label_key = "setting.widget.unknown_glyph.label"
|
||||||
|
default = "cloud-question"
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "unknown_color"
|
||||||
|
type = "color"
|
||||||
|
label_key = "setting.widget.unknown_color.label"
|
||||||
|
default = "on_surface"
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "update_check_interval"
|
||||||
|
type = "int"
|
||||||
|
label_key = "setting.update_check_interval.label"
|
||||||
|
description_key = "setting.update_check_interval.description"
|
||||||
|
default = 60
|
||||||
|
min = 10
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "update_check_duration_threshold"
|
||||||
|
type = "int"
|
||||||
|
label_key = "setting.update_check_duration_threshold.label"
|
||||||
|
description_key = "setting.update_check_duration_threshold.description"
|
||||||
|
default = 5
|
||||||
|
min = 1
|
||||||
|
max = 30
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "system_stats_check_interval"
|
||||||
|
type = "int"
|
||||||
|
label_key = "setting.system_stats_check_interval.label"
|
||||||
|
description_key = "setting.system_stats_check_interval.description"
|
||||||
|
default = 10
|
||||||
|
min = 5
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "system_stats_check_duration_threshold"
|
||||||
|
type = "int"
|
||||||
|
label_key = "setting.system_stats_check_duration_threshold.label"
|
||||||
|
description_key = "setting.system_stats_check_duration_threshold.description"
|
||||||
|
default = 5
|
||||||
|
min = 1
|
||||||
|
max = 30
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "show_update_check_notification"
|
||||||
|
type = "bool"
|
||||||
|
label_key = "setting.show_update_check_notification.label"
|
||||||
|
description_key = "setting.show_update_check_notification.description"
|
||||||
|
default = false
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "show_update_available_notification"
|
||||||
|
type = "bool"
|
||||||
|
label_key = "setting.show_update_available_notification.label"
|
||||||
|
description_key = "setting.show_update_available_notification.description"
|
||||||
|
default = true
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "branch"
|
||||||
|
type = "select"
|
||||||
|
label_key = "setting.branch.label"
|
||||||
|
description_key = "setting.branch.description"
|
||||||
|
options = [
|
||||||
|
{ value = "master", label_key = "setting.option.branch.master" },
|
||||||
|
{ value = "nixos-unstable", label_key = "setting.option.branch.nixos_unstable" },
|
||||||
|
{ value = "nixos-unstable-small", label_key = "setting.option.branch.nixos_unstable_small" },
|
||||||
|
{ value = "nixos-25.11", label_key = "setting.option.branch.nixos_25_11" },
|
||||||
|
{ value = "nixos-25.05", label_key = "setting.option.branch.nixos_25_05" },
|
||||||
|
{ value = "nixos-24.11", label_key = "setting.option.branch.nixos_24_11" },
|
||||||
|
{ value = "nixos-24.05", label_key = "setting.option.branch.nixos_24_05" },
|
||||||
|
{ value = "nixos-23.11", label_key = "setting.option.branch.nixos_23_11" },
|
||||||
|
{ value = "nixos-23.05", label_key = "setting.option.branch.nixos_23_05" },
|
||||||
|
{ value = "nixos-25.11-small", label_key = "setting.option.branch.nixos_25_11_small" },
|
||||||
|
{ value = "nixos-25.05-small", label_key = "setting.option.branch.nixos_25_05_small" },
|
||||||
|
{ value = "nixos-24.11-small", label_key = "setting.option.branch.nixos_24_11_small" },
|
||||||
|
{ value = "nixos-24.05-small", label_key = "setting.option.branch.nixos_24_05_small" },
|
||||||
|
{ value = "nixos-23.11-small", label_key = "setting.option.branch.nixos_23_11_small" },
|
||||||
|
{ value = "nixos-23.05-small", label_key = "setting.option.branch.nixos_23_05_small" },
|
||||||
|
]
|
||||||
|
default = "nixos-unstable"
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "update_command"
|
||||||
|
type = "string"
|
||||||
|
label_key = "setting.update_command.label"
|
||||||
|
description_key = "setting.update_command.description"
|
||||||
|
default = ""
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "clean_command"
|
||||||
|
type = "string"
|
||||||
|
label_key = "setting.clean_command.label"
|
||||||
|
description_key = "setting.clean_command.description"
|
||||||
|
default = "nix-collect-garbage -d"
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "close_on_enter"
|
||||||
|
type = "bool"
|
||||||
|
label_key = "setting.close_on_enter.label"
|
||||||
|
description_key = "setting.close_on_enter.description"
|
||||||
|
default = true
|
||||||
|
|
||||||
|
[[panel]]
|
||||||
|
id = "panel"
|
||||||
|
entry = "panel.luau"
|
||||||
|
placement = "attached"
|
||||||
|
position = "auto"
|
||||||
|
open_near_click = true
|
||||||
|
width = 420
|
||||||
|
height = 370
|
||||||
|
|
||||||
|
[[service]]
|
||||||
|
id = "service"
|
||||||
|
entry = "nix_monitor_service.luau"
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"bar.checking": "Checking...",
|
||||||
|
"bar.update_available": "Update available!",
|
||||||
|
"bar.up_to_date": "Up to date",
|
||||||
|
"bar.unknown": "Unknown",
|
||||||
|
"notification.checking_update": "Checking update...",
|
||||||
|
"notification.update_is_available": "Update is available!",
|
||||||
|
"notification.update_check_completed": "Update check completed!",
|
||||||
|
"notification.update_command_is_empty": "Update command is empty!",
|
||||||
|
"notification.clean_command_is_empty": "Clean command is empty!",
|
||||||
|
"panel.title": "Nix Monitor",
|
||||||
|
"panel.cancel": "Cancel",
|
||||||
|
"panel.update": "Update",
|
||||||
|
"panel.check": "Check",
|
||||||
|
"panel.clean": "Clean",
|
||||||
|
"panel.local": "Local",
|
||||||
|
"panel.remote": "Remote",
|
||||||
|
"panel.last_checked": "Last checked",
|
||||||
|
"panel.current": "Current",
|
||||||
|
"panel.store_size": "Store size",
|
||||||
|
"panel.closure_size": "Closure size",
|
||||||
|
"terminal.press_enter_to_exit": "Press enter to exit...",
|
||||||
|
"setting.widget.show_text.label": "Show text",
|
||||||
|
"setting.widget.colorize_text.label": "Colorize text",
|
||||||
|
"setting.widget.show_glyph.label": "Show glyph",
|
||||||
|
"setting.widget.colorize_glyph.label": "Colorize glyph",
|
||||||
|
"setting.widget.up_to_date_glyph.label": "Up to date glyph",
|
||||||
|
"setting.widget.up_to_date_color.label": "Up to date color",
|
||||||
|
"setting.widget.checking_glyph.label": "Checking glyph",
|
||||||
|
"setting.widget.checking_color.label": "Checking color",
|
||||||
|
"setting.widget.update_available_glyph.label": "Update available glyph",
|
||||||
|
"setting.widget.update_available_color.label": "Update available color",
|
||||||
|
"setting.widget.unknown_glyph.label": "Unknown status glyph",
|
||||||
|
"setting.widget.unknown_color.label": "Unknown status color",
|
||||||
|
"setting.update_check_interval.label": "Update check interval",
|
||||||
|
"setting.update_check_interval.description": "How often to check for updates (in minutes)",
|
||||||
|
"setting.update_check_duration_threshold.label": "Update check duration threshold",
|
||||||
|
"setting.update_check_duration_threshold.description": "Cancel update check if it took too long (in minutes)",
|
||||||
|
"setting.system_stats_check_interval.label": "System stats check interval",
|
||||||
|
"setting.system_stats_check_interval.description": "How often to check for system stats (in minutes)",
|
||||||
|
"setting.system_stats_check_duration_threshold.label": "System stats check duration threshold",
|
||||||
|
"setting.system_stats_check_duration_threshold.description": "Cancel system stats check if it took too long (in minutes)",
|
||||||
|
"setting.show_update_check_notification.label": "Show update check notification",
|
||||||
|
"setting.show_update_check_notification.description": "Whether to show a notification whenever update check is in progress and completed",
|
||||||
|
"setting.show_update_available_notification.label": "Show update available notification",
|
||||||
|
"setting.show_update_available_notification.description": "Whether to show a notification whenever an update is available",
|
||||||
|
"setting.branch.label": "Branch",
|
||||||
|
"setting.branch.description": "Which nixpkgs branch to use",
|
||||||
|
"setting.update_command.label": "Update command",
|
||||||
|
"setting.update_command.description": "Command to run when clicking Update button",
|
||||||
|
"setting.clean_command.label": "Clean command",
|
||||||
|
"setting.clean_command.description": "Command to run when clicking Clean button",
|
||||||
|
"setting.close_on_enter.label": "Close on press Enter",
|
||||||
|
"setting.close_on_enter.description": "Add 'Press enter to exit' on the terminal window",
|
||||||
|
"setting.option.branch.master": "Master",
|
||||||
|
"setting.option.branch.nixos_unstable": "NixOS Unstable",
|
||||||
|
"setting.option.branch.nixos_unstable_small": "NixOS Unstable Small",
|
||||||
|
"setting.option.branch.nixos_25_11": "NixOS 25.11",
|
||||||
|
"setting.option.branch.nixos_25_05": "NixOS 25.05",
|
||||||
|
"setting.option.branch.nixos_24_11": "NixOS 24.11",
|
||||||
|
"setting.option.branch.nixos_24_05": "NixOS 24.05",
|
||||||
|
"setting.option.branch.nixos_23_11": "NixOS 23.11",
|
||||||
|
"setting.option.branch.nixos_23_05": "NixOS 23.05",
|
||||||
|
"setting.option.branch.nixos_25_11_small": "NixOS 25.11 Small",
|
||||||
|
"setting.option.branch.nixos_25_05_small": "NixOS 25.05 Small",
|
||||||
|
"setting.option.branch.nixos_24_11_small": "NixOS 24.11 Small",
|
||||||
|
"setting.option.branch.nixos_24_05_small": "NixOS 24.05 Small",
|
||||||
|
"setting.option.branch.nixos_23_11_small": "NixOS 23.11 Small",
|
||||||
|
"setting.option.branch.nixos_23_05_small": "NixOS 23.05 Small"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user