gui: convert ProcManager to separate Window, sync to active session
- Convert from Dialog to Window (not modal, separate window) - Title shows current session name - Syncs to activeSessionId/activeAgentId changes in main window - Header shows which session is being displayed - Double-click row to view output - Sort processes by PID descending (newest first) - Clear selection when session changes
This commit is contained in:
parent
491f5ff2ae
commit
4774cb9fe9
|
|
@ -145,7 +145,7 @@ qt_add_resources(ollie-gui "gui"
|
||||||
gui/NewSessionDialog.qml
|
gui/NewSessionDialog.qml
|
||||||
gui/NewAgentDialog.qml
|
gui/NewAgentDialog.qml
|
||||||
gui/GoalDialog.qml
|
gui/GoalDialog.qml
|
||||||
gui/ProcManagerDialog.qml
|
gui/ProcManagerWindow.qml
|
||||||
gui/ChatBlockDelegate.qml
|
gui/ChatBlockDelegate.qml
|
||||||
gui/SessionTree.qml
|
gui/SessionTree.qml
|
||||||
gui/ChatPane.qml
|
gui/ChatPane.qml
|
||||||
|
|
|
||||||
|
|
@ -361,7 +361,7 @@ Item {
|
||||||
text: "⚙️ Procs"
|
text: "⚙️ Procs"
|
||||||
font.pixelSize: theme.fontSize > 0 ? theme.fontSize * 0.9 : 11
|
font.pixelSize: theme.fontSize > 0 ? theme.fontSize * 0.9 : 11
|
||||||
implicitHeight: 28
|
implicitHeight: 28
|
||||||
onClicked: procManagerDialog.open()
|
onClicked: procManagerWindow.show()
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: "Background processes"
|
ToolTip.text: "Background processes"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,85 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Window
|
||||||
|
|
||||||
Dialog {
|
Window {
|
||||||
id: procDialog
|
id: procWindow
|
||||||
title: "Background Processes"
|
title: "Background Processes" + (currentSessionName ? " — " + currentSessionName : "")
|
||||||
width: 600
|
width: 650
|
||||||
height: 400
|
height: 450
|
||||||
modal: false
|
minimumWidth: 400
|
||||||
standardButtons: Dialog.Close
|
minimumHeight: 300
|
||||||
|
|
||||||
property var procs: []
|
property var procs: []
|
||||||
property string outputText: ""
|
property string outputText: ""
|
||||||
property int selectedPid: -1
|
property int selectedPid: -1
|
||||||
|
|
||||||
|
// Track the session we're displaying (synced from main window)
|
||||||
|
property string currentSessionId: ollie.activeSessionId
|
||||||
|
property string currentAgentId: ollie.activeAgentId
|
||||||
|
property string currentSessionName: ""
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
procs = ollie.listProcs(ollie.activeSessionId, ollie.activeAgentId)
|
if (!currentSessionId || !currentAgentId) {
|
||||||
|
procs = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var rawProcs = ollie.listProcs(currentSessionId, currentAgentId)
|
||||||
|
// Sort by PID descending (newest first)
|
||||||
|
rawProcs.sort(function(a, b) { return b.pid - a.pid })
|
||||||
|
procs = rawProcs
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSessionName() {
|
||||||
|
if (currentSessionId) {
|
||||||
|
var cfg = ollie.readSessionConfig(currentSessionId)
|
||||||
|
currentSessionName = cfg.name || currentSessionId
|
||||||
|
} else {
|
||||||
|
currentSessionName = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: refreshTimer
|
id: refreshTimer
|
||||||
interval: 2000
|
interval: 2000
|
||||||
running: procDialog.visible
|
running: procWindow.visible
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: refresh()
|
onTriggered: refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible) refresh()
|
if (visible) {
|
||||||
|
currentSessionId = ollie.activeSessionId
|
||||||
|
currentAgentId = ollie.activeAgentId
|
||||||
|
updateSessionName()
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync to active session/agent changes
|
||||||
Connections {
|
Connections {
|
||||||
target: ollie
|
target: ollie
|
||||||
|
function onActiveSessionIdChanged() {
|
||||||
|
if (procWindow.visible) {
|
||||||
|
currentSessionId = ollie.activeSessionId
|
||||||
|
currentAgentId = ollie.activeAgentId
|
||||||
|
updateSessionName()
|
||||||
|
selectedPid = -1
|
||||||
|
outputText = ""
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onActiveAgentIdChanged() {
|
||||||
|
if (procWindow.visible) {
|
||||||
|
currentAgentId = ollie.activeAgentId
|
||||||
|
selectedPid = -1
|
||||||
|
outputText = ""
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
function onProcStarted(sessionId, agentId, payload) {
|
function onProcStarted(sessionId, agentId, payload) {
|
||||||
if (sessionId === ollie.activeSessionId && agentId === ollie.activeAgentId) {
|
if (procWindow.visible && sessionId === currentSessionId && agentId === currentAgentId) {
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -41,9 +87,25 @@ Dialog {
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 8
|
anchors.margins: 12
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
|
// Header showing current session/agent
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Session:"
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: currentSessionName || "(none)"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Process list
|
// Process list
|
||||||
ListView {
|
ListView {
|
||||||
id: procList
|
id: procList
|
||||||
|
|
@ -52,14 +114,20 @@ Dialog {
|
||||||
model: procs
|
model: procs
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded }
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
width: procList.width
|
width: procList.width
|
||||||
height: 48
|
height: 44
|
||||||
color: selectedPid === modelData.pid ? palette.highlight : (index % 2 ? palette.alternateBase : palette.base)
|
color: selectedPid === modelData.pid ? palette.highlight : (index % 2 ? palette.alternateBase : palette.base)
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: selectedPid = modelData.pid
|
onClicked: selectedPid = modelData.pid
|
||||||
|
onDoubleClicked: {
|
||||||
|
selectedPid = modelData.pid
|
||||||
|
outputText = ollie.procOutput(currentSessionId, currentAgentId, selectedPid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|
@ -69,17 +137,17 @@ Dialog {
|
||||||
|
|
||||||
// Status indicator
|
// Status indicator
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 12
|
width: 10
|
||||||
height: 12
|
height: 10
|
||||||
radius: 6
|
radius: 5
|
||||||
color: modelData.status === "running" ? "green" : (modelData.exitCode === 0 ? "gray" : "red")
|
color: modelData.status === "running" ? "#4CAF50" : (modelData.exitCode === 0 ? "#9E9E9E" : "#F44336")
|
||||||
}
|
}
|
||||||
|
|
||||||
// PID
|
// PID
|
||||||
Label {
|
Label {
|
||||||
text: modelData.pid
|
text: modelData.pid
|
||||||
font.family: "monospace"
|
font.family: "monospace"
|
||||||
Layout.preferredWidth: 60
|
Layout.preferredWidth: 50
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tool
|
// Tool
|
||||||
|
|
@ -102,17 +170,17 @@ Dialog {
|
||||||
Label {
|
Label {
|
||||||
text: formatRuntime(modelData.runtime)
|
text: formatRuntime(modelData.runtime)
|
||||||
font.family: "monospace"
|
font.family: "monospace"
|
||||||
Layout.preferredWidth: 60
|
Layout.preferredWidth: 55
|
||||||
horizontalAlignment: Text.AlignRight
|
horizontalAlignment: Text.AlignRight
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit code (if exited)
|
// Exit code (if exited)
|
||||||
Label {
|
Label {
|
||||||
visible: modelData.status !== "running"
|
visible: modelData.status !== "running"
|
||||||
text: "exit " + modelData.exitCode
|
text: modelData.exitCode === 0 ? "✓" : "exit " + modelData.exitCode
|
||||||
font.family: "monospace"
|
font.family: "monospace"
|
||||||
color: modelData.exitCode === 0 ? "gray" : "red"
|
color: modelData.exitCode === 0 ? "#4CAF50" : "#F44336"
|
||||||
Layout.preferredWidth: 50
|
Layout.preferredWidth: 45
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -120,15 +188,15 @@ Dialog {
|
||||||
Label {
|
Label {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: procs.length === 0
|
visible: procs.length === 0
|
||||||
text: "No background processes"
|
text: currentSessionId ? "No background processes" : "No session selected"
|
||||||
opacity: 0.5
|
opacity: 0.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output pane (collapsible)
|
// Output pane (expandable)
|
||||||
GroupBox {
|
GroupBox {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: outputText ? 150 : 0
|
Layout.preferredHeight: outputText.length > 0 ? 150 : 0
|
||||||
visible: outputText.length > 0
|
visible: outputText.length > 0
|
||||||
title: "Output (PID " + selectedPid + ")"
|
title: "Output (PID " + selectedPid + ")"
|
||||||
|
|
||||||
|
|
@ -140,6 +208,7 @@ Dialog {
|
||||||
font.family: "monospace"
|
font.family: "monospace"
|
||||||
font.pointSize: 9
|
font.pointSize: 9
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
|
selectByMouse: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +229,7 @@ Dialog {
|
||||||
icon.name: "document-preview"
|
icon.name: "document-preview"
|
||||||
enabled: selectedPid > 0
|
enabled: selectedPid > 0
|
||||||
onClicked: {
|
onClicked: {
|
||||||
outputText = ollie.procOutput(ollie.activeSessionId, ollie.activeAgentId, selectedPid)
|
outputText = ollie.procOutput(currentSessionId, currentAgentId, selectedPid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,7 +240,7 @@ Dialog {
|
||||||
icon.name: "process-stop"
|
icon.name: "process-stop"
|
||||||
enabled: selectedPid > 0 && isRunning(selectedPid)
|
enabled: selectedPid > 0 && isRunning(selectedPid)
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ollie.procTerm(ollie.activeSessionId, ollie.activeAgentId, selectedPid)
|
ollie.procTerm(currentSessionId, currentAgentId, selectedPid)
|
||||||
Qt.callLater(refresh)
|
Qt.callLater(refresh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +250,7 @@ Dialog {
|
||||||
icon.name: "edit-delete"
|
icon.name: "edit-delete"
|
||||||
enabled: selectedPid > 0 && isRunning(selectedPid)
|
enabled: selectedPid > 0 && isRunning(selectedPid)
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ollie.procKill(ollie.activeSessionId, ollie.activeAgentId, selectedPid)
|
ollie.procKill(currentSessionId, currentAgentId, selectedPid)
|
||||||
Qt.callLater(refresh)
|
Qt.callLater(refresh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,9 +189,10 @@ ApplicationWindow {
|
||||||
id: goalDialog
|
id: goalDialog
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Process Manager Dialog ---
|
// --- Process Manager Window ---
|
||||||
ProcManagerDialog {
|
ProcManagerWindow {
|
||||||
id: procManagerDialog
|
id: procManagerWindow
|
||||||
|
visible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
SplitView {
|
SplitView {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<file>NewSessionDialog.qml</file>
|
<file>NewSessionDialog.qml</file>
|
||||||
<file>NewAgentDialog.qml</file>
|
<file>NewAgentDialog.qml</file>
|
||||||
<file>GoalDialog.qml</file>
|
<file>GoalDialog.qml</file>
|
||||||
<file>ProcManagerDialog.qml</file>
|
<file>ProcManagerWindow.qml</file>
|
||||||
<file>ChatBlockDelegate.qml</file>
|
<file>ChatBlockDelegate.qml</file>
|
||||||
<file>SessionTree.qml</file>
|
<file>SessionTree.qml</file>
|
||||||
<file>ChatPane.qml</file>
|
<file>ChatPane.qml</file>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue