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
This commit is contained in:
Levi Neely 2026-07-06 12:33:14 +02:00
parent 28ce943e45
commit 9701d8f67c
6 changed files with 156 additions and 16 deletions

View File

@ -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/

View File

@ -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))

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -183,6 +183,33 @@ bool OllieDBusClient::killSession(const QString &sessionId)
return false;
}
bool OllieDBusClient::renameSession(const QString &sessionId, const QString &newName)
{
QDBusReply<bool> 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<bool> 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<bool> reply = m_iface->call("Submit", sessionId, prompt);

View File

@ -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);