feat: add multi-session desktop workspace
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user