Merge pull request #10 from avivbintangaringga/main

feat: add nix-monitor plugin
This commit is contained in:
Lysec
2026-07-15 18:50:24 +02:00
committed by GitHub
7 changed files with 883 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
# Nix Monitor
![Nix Monitor thumbnail](thumbnail.webp)
**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!
+55
View File
@@ -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
+292
View File
@@ -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
+253
View File
@@ -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
+192
View File
@@ -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

+70
View File
@@ -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"
}