206 lines
6.0 KiB
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
|
|
}
|
|
}
|