253 lines
8.4 KiB
QML
253 lines
8.4 KiB
QML
import QtQuick 2.15
|
|
import QtQuick.Controls 2.15
|
|
import QtQuick.Layouts 1.15
|
|
|
|
ApplicationWindow {
|
|
id: root
|
|
visible: true
|
|
width: 1000
|
|
height: 600
|
|
minimumWidth: 320
|
|
minimumHeight: 240
|
|
title: "Ollie"
|
|
font.family: theme.fontFamily || Qt.application.font.family
|
|
font.pointSize: theme.fontSize > 0 ? theme.fontSize : Qt.application.font.pointSize
|
|
|
|
// Apply theme palette to all Qt Quick Controls
|
|
palette.active.window: theme.windowColor
|
|
palette.active.windowText: theme.windowTextColor
|
|
palette.active.base: theme.baseColor
|
|
palette.active.text: theme.textColor
|
|
palette.active.button: theme.buttonColor
|
|
palette.active.buttonText: theme.buttonTextColor
|
|
palette.active.highlight: theme.highlightColor
|
|
palette.active.highlightedText: theme.highlightedTextColor
|
|
palette.active.mid: theme.midColor
|
|
palette.active.dark: theme.darkColor
|
|
palette.active.light: theme.lightColor
|
|
palette.active.shadow: theme.shadowColor
|
|
palette.active.link: theme.linkColor
|
|
palette.active.toolTipBase: theme.tooltipBaseColor
|
|
palette.active.toolTipText: theme.tooltipTextColor
|
|
palette.active.placeholderText: theme.placeholderTextColor
|
|
palette.active.alternateBase: theme.alternateBaseColor
|
|
palette.inactive.window: theme.windowColor
|
|
palette.inactive.windowText: theme.windowTextColor
|
|
palette.inactive.base: theme.baseColor
|
|
palette.inactive.text: theme.textColor
|
|
palette.inactive.button: theme.buttonColor
|
|
palette.inactive.buttonText: theme.buttonTextColor
|
|
palette.inactive.highlight: theme.highlightColor
|
|
palette.inactive.highlightedText: theme.highlightedTextColor
|
|
palette.inactive.mid: theme.midColor
|
|
palette.inactive.dark: theme.darkColor
|
|
palette.inactive.light: theme.lightColor
|
|
palette.inactive.shadow: theme.shadowColor
|
|
palette.inactive.link: theme.linkColor
|
|
palette.inactive.toolTipBase: theme.tooltipBaseColor
|
|
palette.inactive.toolTipText: theme.tooltipTextColor
|
|
palette.inactive.placeholderText: theme.placeholderTextColor
|
|
palette.inactive.alternateBase: theme.alternateBaseColor
|
|
palette.disabled.window: theme.windowColor
|
|
palette.disabled.windowText: theme.midColor
|
|
palette.disabled.base: theme.baseColor
|
|
palette.disabled.text: theme.midColor
|
|
palette.disabled.button: theme.buttonColor
|
|
palette.disabled.buttonText: theme.midColor
|
|
palette.disabled.highlight: theme.highlightColor
|
|
palette.disabled.highlightedText: theme.highlightedTextColor
|
|
palette.disabled.mid: theme.midColor
|
|
palette.disabled.dark: theme.darkColor
|
|
palette.disabled.light: theme.lightColor
|
|
palette.disabled.shadow: theme.shadowColor
|
|
|
|
Shortcut {
|
|
sequence: "Ctrl+B"
|
|
onActivated: root.sidebarVisible = !root.sidebarVisible
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: "Escape"
|
|
onActivated: {
|
|
if (sessionTree.renamingIndex >= 0) {
|
|
sessionTree.cancelRename()
|
|
} else {
|
|
// Always send stop - server-side Interrupt() is safe to call
|
|
// on an idle agent (no-op). This avoids races where the GUI's
|
|
// cached activeState is stale.
|
|
ollie.interrupt()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bypass approval/denial shortcuts (configurable in Settings)
|
|
Shortcut {
|
|
sequence: theme.approveBypassKey
|
|
enabled: theme.approveBypassKey !== "" && chatPane.hasPendingBypass
|
|
onActivated: chatPane.approveBypass()
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: theme.denyBypassKey
|
|
enabled: theme.denyBypassKey !== "" && chatPane.hasPendingBypass
|
|
onActivated: chatPane.denyBypass()
|
|
}
|
|
|
|
property bool sidebarVisible: true
|
|
property bool quitting: false
|
|
|
|
onClosing: function(close) {
|
|
if (!quitting && theme.closeToTray) {
|
|
close.accepted = false
|
|
root.hide()
|
|
trayIcon.show()
|
|
}
|
|
}
|
|
Dialog {
|
|
id: renameErrorDialog
|
|
title: "Rename Failed"
|
|
width: 320
|
|
modal: false
|
|
x: (parent ? (parent.width - width) / 2 : 0)
|
|
y: (parent ? (parent.height - height) / 2 : 0)
|
|
|
|
// Keep the non-modal dialog visually separate from the application behind it.
|
|
background: Item {
|
|
// A dark offset layer reads as a shadow instead of a bright outline.
|
|
Rectangle {
|
|
x: 4
|
|
y: 6
|
|
width: parent.width
|
|
height: parent.height
|
|
color: Qt.rgba(0, 0, 0, 0.28)
|
|
radius: 4
|
|
}
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: theme.windowColor
|
|
border.color: theme.dialogOutlineColor
|
|
border.width: 3
|
|
radius: 4
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
anchors.margins: 3
|
|
color: "transparent"
|
|
border.color: theme.lightColor
|
|
border.width: 1
|
|
radius: 3
|
|
}
|
|
}
|
|
}
|
|
|
|
header: Rectangle {
|
|
implicitHeight: 36
|
|
color: theme.buttonColor
|
|
Text { anchors.centerIn: parent; text: renameErrorDialog.title; color: theme.buttonTextColor }
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
cursorShape: Qt.SizeAllCursor
|
|
property point pressPosition
|
|
onPressed: pressPosition = Qt.point(mouse.x, mouse.y)
|
|
onPositionChanged: if (pressed) {
|
|
renameErrorDialog.x += mouse.x - pressPosition.x
|
|
renameErrorDialog.y += mouse.y - pressPosition.y
|
|
}
|
|
}
|
|
}
|
|
|
|
standardButtons: Dialog.Ok
|
|
|
|
// Dialogs never request keyboard focus.
|
|
focus: false
|
|
|
|
property string itemType: "session"
|
|
property string attemptedName: ""
|
|
|
|
contentItem: Label {
|
|
text: "Could not rename " + renameErrorDialog.itemType + " to \"" + renameErrorDialog.attemptedName + "\".\n\nThe name may already be in use."
|
|
wrapMode: Text.Wrap
|
|
}
|
|
}
|
|
|
|
// --- Settings Dialog ---
|
|
SettingsDialog {
|
|
id: settingsDialog
|
|
}
|
|
|
|
// --- New Session Dialog ---
|
|
NewSessionDialog {
|
|
id: newSessionDialog
|
|
}
|
|
|
|
// --- New Agent Dialog ---
|
|
NewAgentDialog {
|
|
id: newAgentDialog
|
|
targetSessionId: ""
|
|
}
|
|
|
|
// --- Goal Dialog ---
|
|
GoalDialog {
|
|
id: goalDialog
|
|
}
|
|
|
|
// --- Process Manager Window ---
|
|
ProcManagerWindow {
|
|
id: procManagerWindow
|
|
visible: false
|
|
}
|
|
|
|
// --- Peer Manager Window ---
|
|
PeerManagerWindow {
|
|
id: peerManagerWindow
|
|
visible: false
|
|
}
|
|
|
|
SplitView {
|
|
anchors.fill: parent
|
|
|
|
SessionTree {
|
|
id: sessionTree
|
|
SplitView.preferredWidth: root.sidebarVisible ? root.width * 0.15 : 0
|
|
SplitView.minimumWidth: 0
|
|
visiblePane: root.sidebarVisible
|
|
onNewSessionRequested: {
|
|
newSessionDialog.editMode = false
|
|
newSessionDialog.editSessionName = ""
|
|
newSessionDialog.open()
|
|
}
|
|
onSettingsRequested: settingsDialog.open()
|
|
onSessionSettingsRequested: function(sessionName) {
|
|
newSessionDialog.openForSession(sessionName)
|
|
}
|
|
onNewAgentRequested: function(sessionId) {
|
|
newAgentDialog.editMode = false
|
|
newAgentDialog.editAgentId = ""
|
|
newAgentDialog.targetSessionId = sessionId
|
|
newAgentDialog.open()
|
|
}
|
|
onAgentSettingsRequested: function(sessionId, agentId) {
|
|
newAgentDialog.openForAgent(sessionId, agentId)
|
|
}
|
|
onGoalRequested: function(sessionId, sessionName) {
|
|
goalDialog.sessionId = sessionId
|
|
goalDialog.sessionName = sessionName
|
|
goalDialog.open()
|
|
}
|
|
onRenameFailed: function(itemType, attemptedName) {
|
|
renameErrorDialog.itemType = itemType
|
|
renameErrorDialog.attemptedName = attemptedName
|
|
renameErrorDialog.open()
|
|
}
|
|
}
|
|
|
|
ChatPane {
|
|
id: chatPane
|
|
SplitView.fillWidth: true
|
|
SplitView.fillHeight: true
|
|
sidebarVisible: root.sidebarVisible
|
|
onToggleSidebar: root.sidebarVisible = !root.sidebarVisible
|
|
}
|
|
}
|
|
}
|