ollie/kde/gui/ProcManagerDialog.qml

206 lines
6.0 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Dialog {
id: procDialog
title: "Background Processes"
width: 600
height: 400
modal: false
standardButtons: Dialog.Close
property var procs: []
property string outputText: ""
property int selectedPid: -1
function refresh() {
procs = ollie.listProcs(ollie.activeSessionId, ollie.activeAgentId)
}
Timer {
id: refreshTimer
interval: 2000
running: procDialog.visible
repeat: true
onTriggered: refresh()
}
onVisibleChanged: {
if (visible) refresh()
}
Connections {
target: ollie
function onProcStarted(sessionId, agentId, payload) {
if (sessionId === ollie.activeSessionId && agentId === ollie.activeAgentId) {
refresh()
}
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 8
spacing: 8
// Process list
ListView {
id: procList
Layout.fillWidth: true
Layout.fillHeight: true
model: procs
clip: true
delegate: Rectangle {
width: procList.width
height: 48
color: selectedPid === modelData.pid ? palette.highlight : (index % 2 ? palette.alternateBase : palette.base)
MouseArea {
anchors.fill: parent
onClicked: selectedPid = modelData.pid
}
RowLayout {
anchors.fill: parent
anchors.margins: 8
spacing: 12
// Status indicator
Rectangle {
width: 12
height: 12
radius: 6
color: modelData.status === "running" ? "green" : (modelData.exitCode === 0 ? "gray" : "red")
}
// PID
Label {
text: modelData.pid
font.family: "monospace"
Layout.preferredWidth: 60
}
// Tool
Label {
text: modelData.tool
font.weight: Font.Medium
Layout.preferredWidth: 80
elide: Text.ElideRight
}
// Command
Label {
text: modelData.cmd
Layout.fillWidth: true
elide: Text.ElideMiddle
opacity: 0.8
}
// Runtime
Label {
text: formatRuntime(modelData.runtime)
font.family: "monospace"
Layout.preferredWidth: 60
horizontalAlignment: Text.AlignRight
}
// Exit code (if exited)
Label {
visible: modelData.status !== "running"
text: "exit " + modelData.exitCode
font.family: "monospace"
color: modelData.exitCode === 0 ? "gray" : "red"
Layout.preferredWidth: 50
}
}
}
Label {
anchors.centerIn: parent
visible: procs.length === 0
text: "No background processes"
opacity: 0.5
}
}
// Output pane (collapsible)
GroupBox {
Layout.fillWidth: true
Layout.preferredHeight: outputText ? 150 : 0
visible: outputText.length > 0
title: "Output (PID " + selectedPid + ")"
ScrollView {
anchors.fill: parent
TextArea {
text: outputText
readOnly: true
font.family: "monospace"
font.pointSize: 9
wrapMode: TextEdit.Wrap
}
}
}
// Action buttons
RowLayout {
Layout.fillWidth: true
spacing: 8
Button {
text: "Refresh"
icon.name: "view-refresh"
onClicked: refresh()
}
Button {
text: "View Output"
icon.name: "document-preview"
enabled: selectedPid > 0
onClicked: {
outputText = ollie.procOutput(ollie.activeSessionId, ollie.activeAgentId, selectedPid)
}
}
Item { Layout.fillWidth: true }
Button {
text: "Terminate"
icon.name: "process-stop"
enabled: selectedPid > 0 && isRunning(selectedPid)
onClicked: {
ollie.procTerm(ollie.activeSessionId, ollie.activeAgentId, selectedPid)
Qt.callLater(refresh)
}
}
Button {
text: "Kill"
icon.name: "edit-delete"
enabled: selectedPid > 0 && isRunning(selectedPid)
onClicked: {
ollie.procKill(ollie.activeSessionId, ollie.activeAgentId, selectedPid)
Qt.callLater(refresh)
}
}
}
}
function formatRuntime(seconds) {
if (seconds < 60) return seconds.toFixed(1) + "s"
if (seconds < 3600) return Math.floor(seconds / 60) + "m " + Math.floor(seconds % 60) + "s"
return Math.floor(seconds / 3600) + "h " + Math.floor((seconds % 3600) / 60) + "m"
}
function isRunning(pid) {
for (var i = 0; i < procs.length; i++) {
if (procs[i].pid === pid) {
return procs[i].status === "running"
}
}
return false
}
}