local request_in_flight = false local function shell_quote(value) return "'" .. string.gsub(value, "'", "'\\\"'\\\"'") .. "'" end local function ui_binary() local configured = noctalia.getenv("PI_STATUS_UI_BINARY") if configured ~= nil and configured ~= "" then return configured end return (noctalia.getenv("HOME") or "") .. "/projects/pi-status-bridge/ui/src-tauri/target/release/pi-status-ui" end local function bridge_client_binary() local configured = noctalia.getenv("PI_STATUS_BRIDGE_CLIENT") if configured ~= nil and configured ~= "" then return shell_quote(configured) end return "pi-status-bridge-client" end local function socket_path() local runtime = noctalia.getenv("XDG_RUNTIME_DIR") if runtime == nil or runtime == "" then return nil end return runtime .. "/pi-status-bridge/bridge.sock" end local bridge_start_in_flight = false local pending_ui_open = false local function open_ui() local binary = shell_quote(ui_binary()) local command = "if test -x " .. binary .. "; then setsid -f env TMPDIR=/tmp " .. binary .. " --toggle >/tmp/pi-status-ui.log 2>&1; else exit 1; fi" noctalia.runAsync(command, function(result) if result.exitCode ~= 0 then barWidget.setGlyph("alert-triangle") barWidget.setText("Pi UI unavailable") barWidget.setTooltip("Build Pi Status UI or set PI_STATUS_UI_BINARY") end end) end local function start_bridge(open_after_start) if open_after_start then pending_ui_open = true end if bridge_start_in_flight then return end bridge_start_in_flight = true barWidget.setGlyph("terminal-2") barWidget.setText("Pi starting") barWidget.setTooltip("Bridge is starting") noctalia.runAsync("systemctl --user start pi-status-bridge.service", function(result) bridge_start_in_flight = false if result.exitCode ~= 0 then pending_ui_open = false barWidget.setGlyph("alert-triangle") barWidget.setText("Pi offline") barWidget.setTooltip("Could not start pi-status-bridge.service") return end if pending_ui_open then pending_ui_open = false; open_ui() end end) end local function json_count(payload, key) local value = string.match(payload, '"' .. key .. '":(%d+)') return tonumber(value) or 0 end local function update_summary(payload) local open = json_count(payload, "openCount") local working = json_count(payload, "workingCount") local attention = json_count(payload, "attentionCount") local recovering = json_count(payload, "recoveringCount") local errors = json_count(payload, "errorCount") local glyph = errors > 0 and "alert-triangle" or attention > 0 and "circle-alert" or working > 0 and "terminal-2" or recovering > 0 and "refresh-cw" or "terminal" local text = errors > 0 and "Pi needs attention" or working > 0 and "Pi working" or recovering > 0 and "Pi recovering" or "Pi ready" local detail = open .. " open · " .. working .. " working · " .. attention .. " attention · " .. recovering .. " recovering · " .. errors .. " errors" barWidget.setGlyph(glyph) barWidget.setText(text) barWidget.setTooltip(detail .. "; click to open Pi Status UI") end function update() noctalia.setUpdateInterval(2000) local socket = socket_path() if socket == nil then start_bridge(false); return end if request_in_flight then return end request_in_flight = true local command = bridge_client_binary() .. " --socket \"" .. socket .. "\" request '{\"op\":\"get_workspace_summary\"}'" noctalia.runAsync(command, function(result) request_in_flight = false if result.exitCode ~= 0 then start_bridge(false) barWidget.setGlyph("terminal-2") barWidget.setText("Pi reconnecting") barWidget.setTooltip("Refreshing Pi workspace summary") return end update_summary(result.stdout) end) end function onClick() start_bridge(true) end