feat: add multi-session desktop workspace

This commit is contained in:
2026-08-13 21:34:04 +02:00
parent 12b9b83d51
commit 475f95ba70
56 changed files with 8998 additions and 3805 deletions
+23 -7
View File
@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Widgets
@@ -16,13 +17,31 @@ Item {
readonly property string screenName: screen?.name ?? ""
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
readonly property string bridgeState: pluginApi?.pluginSettings?.bridgeState ?? "idle"
readonly property string projectLabel: pluginApi?.pluginSettings?.projectLabel ?? "Home"
readonly property string projectLabel: pluginApi?.pluginSettings?.projectLabel ?? "0 sessions"
readonly property int attentionCount: pluginApi?.pluginSettings?.attentionCount ?? 0
readonly property string glyph: bridgeState === "streaming" ? "◒" : bridgeState === "failed" ? "!" : attentionCount > 0 ? "●" : "●"
readonly property color stateColor: bridgeState === "failed" ? Color.mError : attentionCount > 0 ? Color.mPrimary : Color.mOnSurface
readonly property int workingCount: pluginApi?.pluginSettings?.workingCount ?? 0
readonly property int recoveringCount: pluginApi?.pluginSettings?.recoveringCount ?? 0
readonly property int errorCount: pluginApi?.pluginSettings?.errorCount ?? 0
readonly property string glyph: errorCount > 0 ? "!" : bridgeState === "streaming" ? "◒" : recoveringCount > 0 ? "↻" : attentionCount > 0 ? "●" : "●"
readonly property color stateColor: errorCount > 0 ? Color.mError : attentionCount > 0 ? Color.mPrimary : Color.mOnSurface
implicitWidth: content.implicitWidth + Style.marginM * 2
implicitHeight: capsuleHeight
// Keep launch paths configurable for deployments while retaining the local build fallback.
Process {
id: bridgeStartProcess
command: ["systemctl", "--user", "start", "pi-status-bridge.service"]
onExited: function(exitCode) {
if (exitCode === 0)
uiLaunchProcess.running = true
}
}
Process {
id: uiLaunchProcess
command: ["bash", "-c", "ui=\"${PI_STATUS_UI_BINARY:-$HOME/projects/pi-status-bridge/ui/src-tauri/target/release/pi-status-ui}\"; test -x \"$ui\" && exec setsid -f env TMPDIR=/tmp \"$ui\" --toggle >/tmp/pi-status-ui.log 2>&1"]
}
Rectangle {
id: visualCapsule
anchors.centerIn: parent
@@ -46,10 +65,7 @@ Item {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (pluginApi)
pluginApi.togglePanel(root.screen, visualCapsule)
}
onClicked: bridgeStartProcess.running = true
}
}
}
+7 -3
View File
@@ -5,21 +5,25 @@ Item {
id: root
property var pluginApi: null
function setBridgeState(state, projectLabel, attentionCount, detail) {
function setBridgeState(state, projectLabel, attentionCount, detail, openCount, workingCount, recoveringCount, errorCount) {
if (!pluginApi)
return
pluginApi.pluginSettings.bridgeState = state
pluginApi.pluginSettings.projectLabel = projectLabel
pluginApi.pluginSettings.attentionCount = attentionCount
pluginApi.pluginSettings.detail = detail
pluginApi.pluginSettings.openCount = openCount ?? 0
pluginApi.pluginSettings.workingCount = workingCount ?? 0
pluginApi.pluginSettings.recoveringCount = recoveringCount ?? 0
pluginApi.pluginSettings.errorCount = errorCount ?? 0
pluginApi.saveSettings()
}
IpcHandler {
target: "plugin:pi-status-bridge"
function setBridgeState(state, projectLabel, attentionCount, detail) {
root.setBridgeState(state, projectLabel, attentionCount, detail)
function setBridgeState(state, projectLabel, attentionCount, detail, openCount, workingCount, recoveringCount, errorCount) {
root.setBridgeState(state, projectLabel, attentionCount, detail, openCount, workingCount, recoveringCount, errorCount)
}
}
}
@@ -1,36 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
Item {
id: root
property var pluginApi: null
readonly property var geometryPlaceholder: panelContainer
readonly property bool allowAttach: true
property real contentPreferredWidth: 680 * Style.uiScaleRatio
property real contentPreferredHeight: 420 * Style.uiScaleRatio
readonly property string bridgeState: pluginApi?.pluginSettings?.bridgeState ?? "idle"
readonly property string projectLabel: pluginApi?.pluginSettings?.projectLabel ?? "Home"
readonly property string detail: pluginApi?.pluginSettings?.detail ?? "Bridge not connected"
anchors.fill: parent
Rectangle {
id: panelContainer
anchors.fill: parent
color: "transparent"
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginM
Text { text: "Pi Status Bridge"; color: Color.mOnSurface; font.bold: true; font.pixelSize: 20 }
Text { text: "Project: " + root.projectLabel; color: Color.mOnSurfaceVariant }
Text { text: "State: " + root.bridgeState; color: Color.mOnSurfaceVariant }
Rectangle { Layout.fillWidth: true; height: 1; color: Color.mOutlineVariant }
Text { text: root.detail; color: Color.mOnSurface; wrapMode: Text.Wrap; Layout.fillWidth: true }
Text { text: "Select a project explicitly before sending worktree-scoped actions."; color: Color.mOnSurfaceVariant; wrapMode: Text.Wrap; Layout.fillWidth: true }
Item { Layout.fillHeight: true }
}
}
}
+7 -5
View File
@@ -1,14 +1,16 @@
# Pi Status Bridge Noctalia plugin
This Noctalia v4 plugin is deliberately **presentation-only**. It renders
state supplied through its `plugin:pi-status-bridge` IPC handler and opens its
panel with `pluginApi.togglePanel`.
This Noctalia v4 plugin is an aggregate-status **launcher**. It renders the
workspace counts supplied through its `plugin:pi-status-bridge` IPC handler.
Clicking its bar widget starts `pi-status-bridge.service` and toggles the
standalone Pi Status UI. Set `PI_STATUS_UI_BINARY` to override the UI binary;
the packaged local build path is the fallback.
Use `pi-status-bridge-client` to query the owner-only Unix bridge socket. A
local integration script should translate responses and events into the plugin
IPC handler's `setBridgeState(state, projectLabel, attentionCount, detail)`
call. The plugin never starts Pi, opens TCP, chooses a worktree silently, or
approves extension requests.
call. The plugin never opens a Noctalia panel, sends prompts, switches
sessions, chooses a worktree silently, or approves extension requests.
Install/register it through Noctalia's normal plugin workflow, then add its
bar widget in Noctalia settings.
@@ -1,18 +1,17 @@
{
"id": "pi-status-bridge",
"name": "Pi Status Bridge",
"version": "0.1.0",
"version": "0.2.0",
"minNoctaliaVersion": "4.0.0",
"description": "Presentation-only status bar and panel for a local Pi Status Bridge.",
"description": "Aggregate Pi workspace status and launcher for the standalone Pi Status UI.",
"entryPoints": {
"main": "Main.qml",
"barWidget": "BarWidget.qml",
"panel": "Panel.qml"
"barWidget": "BarWidget.qml"
},
"metadata": {
"defaultSettings": {
"bridgeState": "idle",
"projectLabel": "Home",
"projectLabel": "0 sessions",
"attentionCount": 0,
"detail": "Bridge not connected"
}