299 lines
10 KiB
QML
299 lines
10 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
import QtQuick.Dialogs
|
|
import org.kde.plasma.plasmoid
|
|
import org.kde.plasma.components as PlasmaComponents
|
|
import org.kde.plasma.extras as PlasmaExtras
|
|
import org.kde.kirigami as Kirigami
|
|
import org.ollie.private as Ollie
|
|
|
|
PlasmoidItem {
|
|
id: root
|
|
|
|
// Convenience alias
|
|
readonly property var dbus: Ollie.OllieDBusClient
|
|
|
|
FolderDialog {
|
|
id: folderDialog
|
|
title: "Choose working directory"
|
|
currentFolder: dbus.activeCwd ? "file://" + dbus.activeCwd : StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
|
onAccepted: {
|
|
let path = selectedFolder.toString().replace("file://", "")
|
|
dbus.setConfig(dbus.activeSessionId, "cwd", path)
|
|
}
|
|
}
|
|
|
|
// On the desktop (Planar), the widget has plenty of room so Plasma will
|
|
// automatically use fullRepresentation. In a panel, the available space
|
|
// is smaller than switchWidth/switchHeight so compactRepresentation is used.
|
|
switchWidth: Kirigami.Units.gridUnit * 10
|
|
switchHeight: Kirigami.Units.gridUnit * 10
|
|
|
|
toolTipMainText: "Ollie"
|
|
toolTipSubText: {
|
|
let n = dbus.sessions.length
|
|
if (n === 0) return "No active sessions"
|
|
return n + " session" + (n > 1 ? "s" : "")
|
|
}
|
|
|
|
compactRepresentation: Item {
|
|
implicitWidth: compactRow.implicitWidth + 8
|
|
implicitHeight: parent.height
|
|
|
|
RowLayout {
|
|
id: compactRow
|
|
anchors.centerIn: parent
|
|
spacing: 4
|
|
|
|
Rectangle {
|
|
id: statusDot
|
|
width: 8
|
|
height: 8
|
|
radius: 4
|
|
color: {
|
|
let n = dbus.sessions.length
|
|
if (n === 0) return Kirigami.Theme.disabledTextColor
|
|
let state = dbus.activeState
|
|
if (state === "idle") return Kirigami.Theme.positiveTextColor
|
|
if (state.startsWith("calling")) return Kirigami.Theme.neutralTextColor
|
|
return Kirigami.Theme.activeTextColor
|
|
}
|
|
}
|
|
|
|
PlasmaComponents.Label {
|
|
text: dbus.activeCwd || "Ollie"
|
|
font.pixelSize: parent.height > 0 ? parent.height * 0.5 : Kirigami.Theme.defaultFont.pixelSize
|
|
elide: Text.ElideMiddle
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: root.expanded = !root.expanded
|
|
}
|
|
}
|
|
|
|
fullRepresentation: ColumnLayout {
|
|
Layout.preferredWidth: 420
|
|
Layout.preferredHeight: 520
|
|
spacing: 0
|
|
|
|
// --- Header ---
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
Layout.margins: 8
|
|
|
|
Rectangle {
|
|
width: 10
|
|
height: 10
|
|
radius: 5
|
|
color: {
|
|
let n = dbus.sessions.length
|
|
if (n === 0) return Kirigami.Theme.disabledTextColor
|
|
let state = dbus.activeState
|
|
if (state === "idle") return Kirigami.Theme.positiveTextColor
|
|
if (state.startsWith("calling")) return Kirigami.Theme.neutralTextColor
|
|
return Kirigami.Theme.activeTextColor
|
|
}
|
|
}
|
|
|
|
PlasmaExtras.Heading {
|
|
level: 4
|
|
text: dbus.activeCwd || "Ollie"
|
|
Layout.fillWidth: true
|
|
elide: Text.ElideMiddle
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onDoubleClicked: {
|
|
if (dbus.activeSessionId !== "") {
|
|
folderDialog.open()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PlasmaComponents.ToolButton {
|
|
icon.name: "list-add"
|
|
onClicked: dbus.createSession("")
|
|
PlasmaComponents.ToolTip { text: "New session" }
|
|
}
|
|
}
|
|
|
|
// --- Session list ---
|
|
ListView {
|
|
id: sessionList
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: Math.min(contentHeight + 4, 130)
|
|
Layout.maximumHeight: 130
|
|
Layout.leftMargin: 8
|
|
Layout.rightMargin: 8
|
|
clip: true
|
|
model: dbus.sessions
|
|
|
|
delegate: ItemDelegate {
|
|
id: sessionDelegate
|
|
width: sessionList.width
|
|
highlighted: modelData.id === dbus.activeSessionId
|
|
|
|
property bool renaming: false
|
|
|
|
contentItem: RowLayout {
|
|
spacing: 6
|
|
|
|
Label {
|
|
text: {
|
|
let state = modelData.state
|
|
if (state === "idle") return "○"
|
|
if (state.startsWith("calling")) return "⚡"
|
|
return "●"
|
|
}
|
|
font.bold: true
|
|
color: modelData.state !== "idle"
|
|
? Kirigami.Theme.positiveTextColor
|
|
: Kirigami.Theme.textColor
|
|
}
|
|
|
|
Label {
|
|
id: sessionLabel
|
|
text: modelData.id
|
|
Layout.fillWidth: true
|
|
elide: Text.ElideRight
|
|
font.family: "monospace"
|
|
font.pixelSize: Kirigami.Theme.defaultFont.pixelSize
|
|
visible: !sessionDelegate.renaming
|
|
}
|
|
|
|
TextField {
|
|
id: inlineRenameField
|
|
Layout.fillWidth: true
|
|
font.family: "monospace"
|
|
font.pixelSize: Kirigami.Theme.defaultFont.pixelSize
|
|
visible: sessionDelegate.renaming
|
|
onAccepted: {
|
|
let newName = text.trim()
|
|
if (newName !== "" && newName !== modelData.id) {
|
|
dbus.renameSession(modelData.id, newName)
|
|
}
|
|
sessionDelegate.renaming = false
|
|
}
|
|
Keys.onEscapePressed: sessionDelegate.renaming = false
|
|
onVisibleChanged: {
|
|
if (visible) {
|
|
text = modelData.id
|
|
selectAll()
|
|
forceActiveFocus()
|
|
}
|
|
}
|
|
onActiveFocusChanged: {
|
|
if (!activeFocus && sessionDelegate.renaming) {
|
|
sessionDelegate.renaming = false
|
|
}
|
|
}
|
|
}
|
|
|
|
Label {
|
|
text: modelData.agent
|
|
opacity: 0.7
|
|
font.pixelSize: Kirigami.Theme.smallFont.pixelSize
|
|
visible: !sessionDelegate.renaming
|
|
}
|
|
|
|
PlasmaComponents.ToolButton {
|
|
icon.name: "process-stop"
|
|
implicitWidth: 22; implicitHeight: 22
|
|
onClicked: dbus.killSession(modelData.id)
|
|
visible: !sessionDelegate.renaming
|
|
}
|
|
}
|
|
|
|
onClicked: dbus.activeSessionId = modelData.id
|
|
onDoubleClicked: sessionDelegate.renaming = true
|
|
}
|
|
}
|
|
|
|
Kirigami.Separator {
|
|
Layout.fillWidth: true
|
|
visible: dbus.sessions.length > 0
|
|
}
|
|
|
|
// --- Chat view ---
|
|
ScrollView {
|
|
id: chatScroll
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
Layout.margins: 4
|
|
|
|
TextArea {
|
|
id: chatArea
|
|
readOnly: true
|
|
wrapMode: TextEdit.Wrap
|
|
text: dbus.chatText || (dbus.activeSessionId
|
|
? "(waiting for output...)"
|
|
: "Select or create a session")
|
|
font.family: "monospace"
|
|
font.pixelSize: Kirigami.Theme.defaultFont.pixelSize
|
|
color: dbus.chatText
|
|
? Kirigami.Theme.textColor
|
|
: Kirigami.Theme.disabledTextColor
|
|
background: null
|
|
|
|
onTextChanged: {
|
|
cursorPosition = text.length
|
|
}
|
|
}
|
|
}
|
|
|
|
Kirigami.Separator {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
// --- Prompt input ---
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
Layout.margins: 6
|
|
spacing: 4
|
|
|
|
TextField {
|
|
id: promptInput
|
|
Layout.fillWidth: true
|
|
placeholderText: dbus.activeSessionId
|
|
? "Message..."
|
|
: "No active session"
|
|
enabled: dbus.activeSessionId !== ""
|
|
onAccepted: {
|
|
if (text.trim() !== "" && dbus.activeSessionId !== "") {
|
|
dbus.submit(
|
|
dbus.activeSessionId, text.trim())
|
|
text = ""
|
|
}
|
|
}
|
|
}
|
|
|
|
PlasmaComponents.ToolButton {
|
|
icon.name: "media-playback-stop"
|
|
enabled: dbus.activeState !== "idle"
|
|
&& dbus.activeSessionId !== ""
|
|
onClicked: dbus.interrupt(
|
|
dbus.activeSessionId)
|
|
PlasmaComponents.ToolTip { text: "Interrupt" }
|
|
}
|
|
}
|
|
|
|
// --- Status bar ---
|
|
Label {
|
|
Layout.fillWidth: true
|
|
Layout.leftMargin: 8
|
|
Layout.bottomMargin: 4
|
|
font.pixelSize: Kirigami.Theme.smallFont.pixelSize
|
|
opacity: 0.6
|
|
text: {
|
|
let id = dbus.activeSessionId
|
|
if (!id) return "ollie-kde"
|
|
return dbus.activeState + " │ " + id
|
|
}
|
|
}
|
|
}
|
|
}
|