From 9701d8f67c6907f8b78003a2fd6de7545fd04139 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Mon, 6 Jul 2026 12:33:14 +0200 Subject: [PATCH] plasmoid: inline session rename, cwd folder dialog, fix rename signal bug - Double-click session name in list to rename inline (RET to confirm, ESC to cancel) - Double-click cwd heading to open folder picker and change working directory - Add setConfig() to QML D-Bus client - Fix capi.go: chat/state callbacks used stale sessID after rename - Makefile: kf5 target uses trap to always restore CMakeLists.txt --- Makefile | 26 ++++++------- lib/capi.go | 4 +- plasmoid/contents/ui/main-kf5.qml | 57 +++++++++++++++++++++++++++++ plasmoid/contents/ui/main.qml | 56 ++++++++++++++++++++++++++++ plasmoid/plugin/olliedbusclient.cpp | 27 ++++++++++++++ plasmoid/plugin/olliedbusclient.h | 2 + 6 files changed, 156 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 744d7a7..957941e 100644 --- a/Makefile +++ b/Makefile @@ -21,11 +21,11 @@ daemon: lib # Step 2b: Build using KF5/Qt5 (for Plasma 5.27) kf5: lib - cp CMakeLists.txt CMakeLists.txt.kf6.bak - cp CMakeLists-kf5.txt CMakeLists.txt - cmake -B $(BUILD_DIR) -S . --fresh - cmake --build $(BUILD_DIR) - mv CMakeLists.txt.kf6.bak CMakeLists.txt + @cp CMakeLists.txt CMakeLists.txt.kf6.bak && \ + trap 'mv -f CMakeLists.txt.kf6.bak CMakeLists.txt' EXIT && \ + cp CMakeLists-kf5.txt CMakeLists.txt && \ + cmake -B $(BUILD_DIR) -S . --fresh && \ + cmake --build $(BUILD_DIR) # Install the KF5 build kf5-install: kf5 @@ -46,15 +46,13 @@ kf5-install: kf5 /usr/lib/x86_64-linux-gnu/qt5/qml/org/ollie/private/ sudo install -m644 plasmoid/plugin/qmldir \ /usr/lib/x86_64-linux-gnu/qt5/qml/org/ollie/private/ - # Plasmoid package (swap metadata + QML for Plasma 5) - cp plasmoid/metadata.json plasmoid/metadata.json.kf6.bak - cp plasmoid/contents/ui/main.qml plasmoid/contents/ui/main.qml.kf6.bak - cp plasmoid/metadata-kf5.json plasmoid/metadata.json - cp plasmoid/contents/ui/main-kf5.qml plasmoid/contents/ui/main.qml - kpackagetool5 -t Plasma/Applet -u plasmoid/ 2>/dev/null || \ - kpackagetool5 -t Plasma/Applet -i plasmoid/ - mv plasmoid/metadata.json.kf6.bak plasmoid/metadata.json - mv plasmoid/contents/ui/main.qml.kf6.bak plasmoid/contents/ui/main.qml + # Plasmoid package (use temp copy to avoid clobbering source tree ownership) + cp -a plasmoid /tmp/ollie-plasmoid-kf5 + cp plasmoid/metadata-kf5.json /tmp/ollie-plasmoid-kf5/metadata.json + cp plasmoid/contents/ui/main-kf5.qml /tmp/ollie-plasmoid-kf5/contents/ui/main.qml + kpackagetool5 -t Plasma/Applet -u /tmp/ollie-plasmoid-kf5/ 2>/dev/null || \ + kpackagetool5 -t Plasma/Applet -i /tmp/ollie-plasmoid-kf5/ + $(RM) -r /tmp/ollie-plasmoid-kf5 # Dolphin service menus + helpers mkdir -p ~/.local/share/kio/servicemenus install -m755 dolphin/ollie-actions.desktop ~/.local/share/kio/servicemenus/ diff --git a/lib/capi.go b/lib/capi.go index 48cd4d6..9a55e45 100644 --- a/lib/capi.go +++ b/lib/capi.go @@ -207,7 +207,7 @@ func ollie_create_session(cwdC *C.char, backendC *C.char, modelC *C.char, agentC sess.logMu.Unlock() if chatCB != nil { - cID := C.CString(sessID) + cID := C.CString(sess.id) cText := C.CString(text) C.ollie_chat_cb_invoke(chatCB, cID, C.int64_t(offset), cText, C.int(len(text))) C.free(unsafe.Pointer(cID)) @@ -225,7 +225,7 @@ func ollie_create_session(cwdC *C.char, backendC *C.char, modelC *C.char, agentC } current = next if stateCB != nil { - cID := C.CString(sessID) + cID := C.CString(sess.id) cState := C.CString(current) C.ollie_state_cb_invoke(stateCB, cID, cState) C.free(unsafe.Pointer(cID)) diff --git a/plasmoid/contents/ui/main-kf5.qml b/plasmoid/contents/ui/main-kf5.qml index 3833c7a..44f74bb 100644 --- a/plasmoid/contents/ui/main-kf5.qml +++ b/plasmoid/contents/ui/main-kf5.qml @@ -1,6 +1,7 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 +import QtQuick.Dialogs 1.3 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PlasmaComponents @@ -13,6 +14,17 @@ Item { readonly property var dbus: Ollie.OllieDBusClient + FileDialog { + id: folderDialog + title: "Choose working directory" + selectFolder: true + folder: dbus.activeCwd ? "file://" + dbus.activeCwd : shortcuts.home + onAccepted: { + var path = fileUrl.toString().replace("file://", "") + dbus.setConfig(dbus.activeSessionId, "cwd", path) + } + } + Plasmoid.status: dbus.sessions.length > 0 ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.PassiveStatus @@ -90,6 +102,15 @@ Item { text: dbus.activeCwd || "Ollie" Layout.fillWidth: true elide: Text.ElideMiddle + + MouseArea { + anchors.fill: parent + onDoubleClicked: { + if (dbus.activeSessionId !== "") { + folderDialog.open() + } + } + } } PlasmaComponents.ToolButton { @@ -111,9 +132,12 @@ Item { model: dbus.sessions delegate: ItemDelegate { + id: sessionDelegate width: sessionList.width highlighted: modelData.id === dbus.activeSessionId + property bool renaming: false + contentItem: RowLayout { spacing: 6 @@ -131,27 +155,60 @@ Item { } Label { + id: sessionLabel text: modelData.id Layout.fillWidth: true elide: Text.ElideRight font.family: "monospace" font.pixelSize: 11 + visible: !sessionDelegate.renaming + } + + TextField { + id: inlineRenameField + Layout.fillWidth: true + font.family: "monospace" + font.pixelSize: 11 + visible: sessionDelegate.renaming + onAccepted: { + var 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: 11 + 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 } } diff --git a/plasmoid/contents/ui/main.qml b/plasmoid/contents/ui/main.qml index 7528ee2..160c2d5 100644 --- a/plasmoid/contents/ui/main.qml +++ b/plasmoid/contents/ui/main.qml @@ -1,6 +1,7 @@ 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 @@ -13,6 +14,16 @@ PlasmoidItem { // 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) + } + } + preferredRepresentation: compactRepresentation toolTipMainText: "Ollie" @@ -88,6 +99,15 @@ PlasmoidItem { text: dbus.activeCwd || "Ollie" Layout.fillWidth: true elide: Text.ElideMiddle + + MouseArea { + anchors.fill: parent + onDoubleClicked: { + if (dbus.activeSessionId !== "") { + folderDialog.open() + } + } + } } PlasmaComponents.ToolButton { @@ -109,9 +129,12 @@ PlasmoidItem { model: dbus.sessions delegate: ItemDelegate { + id: sessionDelegate width: sessionList.width highlighted: modelData.id === dbus.activeSessionId + property bool renaming: false + contentItem: RowLayout { spacing: 6 @@ -129,27 +152,60 @@ PlasmoidItem { } Label { + id: sessionLabel text: modelData.id Layout.fillWidth: true elide: Text.ElideRight font.family: "monospace" font.pixelSize: 11 + visible: !sessionDelegate.renaming + } + + TextField { + id: inlineRenameField + Layout.fillWidth: true + font.family: "monospace" + font.pixelSize: 11 + 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: 11 + 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 } } diff --git a/plasmoid/plugin/olliedbusclient.cpp b/plasmoid/plugin/olliedbusclient.cpp index 22a0b9b..bc275c6 100644 --- a/plasmoid/plugin/olliedbusclient.cpp +++ b/plasmoid/plugin/olliedbusclient.cpp @@ -183,6 +183,33 @@ bool OllieDBusClient::killSession(const QString &sessionId) return false; } +bool OllieDBusClient::renameSession(const QString &sessionId, const QString &newName) +{ + QDBusReply reply = m_iface->call("RenameSession", sessionId, newName); + if (reply.isValid() && reply.value()) { + if (sessionId == m_activeSessionId) { + m_activeSessionId = newName; + emit activeSessionIdChanged(); + } + refreshSessions(); + return true; + } + return false; +} + +bool OllieDBusClient::setConfig(const QString &sessionId, const QString &key, const QString &value) +{ + QDBusReply reply = m_iface->call("SetConfig", sessionId, key, value); + if (reply.isValid() && reply.value()) { + if (sessionId == m_activeSessionId && key == "cwd") { + m_activeCwd = value; + emit activeCwdChanged(); + } + return true; + } + return false; +} + bool OllieDBusClient::submit(const QString &sessionId, const QString &prompt) { QDBusReply reply = m_iface->call("Submit", sessionId, prompt); diff --git a/plasmoid/plugin/olliedbusclient.h b/plasmoid/plugin/olliedbusclient.h index e7ebe0e..1678f0f 100644 --- a/plasmoid/plugin/olliedbusclient.h +++ b/plasmoid/plugin/olliedbusclient.h @@ -39,6 +39,8 @@ public: Q_INVOKABLE void fetchChat(); Q_INVOKABLE QString createSession(const QString &cwd); Q_INVOKABLE bool killSession(const QString &sessionId); + Q_INVOKABLE bool renameSession(const QString &sessionId, const QString &newName); + Q_INVOKABLE bool setConfig(const QString &sessionId, const QString &key, const QString &value); Q_INVOKABLE bool submit(const QString &sessionId, const QString &prompt); Q_INVOKABLE bool interrupt(const QString &sessionId);