This repository has been archived on 2026-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
ollie-kde/plasmoid/contents/ui/main.qml

307 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.kde.kirigami.delegates as KirigamiDelegates
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 : ""
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
onVisibleChanged: {
if (visible && promptInput.enabled) {
promptInput.forceActiveFocus()
}
}
// --- 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.createSessionWithDialog()
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: KirigamiDelegates.RoundedItemDelegate {
id: sessionDelegate
width: sessionList.width
highlighted: modelData.id === dbus.activeSessionId
property bool renaming: false
contentItem: RowLayout {
spacing: 6
Kirigami.Icon {
implicitWidth: Kirigami.Units.iconSizes.small
implicitHeight: Kirigami.Units.iconSizes.small
source: {
let state = modelData.state
if (state === "idle") return "media-playback-paused"
if (state.startsWith("calling")) return "system-run"
return "media-playback-start"
}
color: modelData.state !== "idle"
? Kirigami.Theme.positiveTextColor
: Kirigami.Theme.textColor
}
PlasmaComponents.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
}
}
}
PlasmaComponents.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
}
}
}
}