phase 4: plasma widget (plasmoid)
- QML plasmoid with session list, live chat view, inline prompt input
- C++ QML plugin (OllieDBusClient) for D-Bus interaction
- Real-time updates via D-Bus signals (ChatUpdated, StateChanged)
- State indicators: ○ idle, ● thinking, ⚡ calling tool
- Auto-scroll chat, interrupt button, new session button
- Makefile: plasmoid-install/uninstall targets for local dev
This commit is contained in:
parent
238efabcb5
commit
f4b5ce3ea0
|
|
@ -8,8 +8,12 @@ set(CMAKE_AUTOMOC ON)
|
|||
find_package(ECM REQUIRED NO_MODULE)
|
||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core DBus Widgets)
|
||||
include(KDEInstallDirs)
|
||||
include(KDECMakeSettings)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core DBus Widgets Quick)
|
||||
find_package(KF6StatusNotifierItem REQUIRED)
|
||||
find_package(Plasma REQUIRED)
|
||||
|
||||
# libollie.so built by Go (expected in build/ after `make lib`)
|
||||
set(OLLIE_LIB "${CMAKE_SOURCE_DIR}/build/libollie.so")
|
||||
|
|
@ -17,7 +21,6 @@ set(OLLIE_HEADER "${CMAKE_SOURCE_DIR}/build/libollie.h")
|
|||
|
||||
# --- Daemon ---
|
||||
|
||||
# Generate D-Bus adaptor from XML
|
||||
qt6_add_dbus_adaptor(DBUS_ADAPTOR_SRCS
|
||||
daemon/org.ollie.SessionManager.xml
|
||||
daemon/session_manager.h SessionManager)
|
||||
|
|
@ -54,6 +57,31 @@ target_link_libraries(ollie-tray
|
|||
KF6::StatusNotifierItem
|
||||
)
|
||||
|
||||
# --- Plasmoid QML Plugin ---
|
||||
|
||||
qt6_add_qml_module(ollie-plasmoid-plugin
|
||||
URI org.ollie.private
|
||||
VERSION 1.0
|
||||
SOURCES
|
||||
plasmoid/plugin/olliedbusclient.h
|
||||
plasmoid/plugin/olliedbusclient.cpp
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/org/ollie/private
|
||||
)
|
||||
target_link_libraries(ollie-plasmoid-plugin PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::DBus
|
||||
Qt6::Quick
|
||||
)
|
||||
target_include_directories(ollie-plasmoid-plugin PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/plasmoid/plugin
|
||||
)
|
||||
|
||||
# --- Plasmoid Package ---
|
||||
|
||||
plasma_install_package(plasmoid org.ollie.plasmoid)
|
||||
|
||||
# --- Install ---
|
||||
|
||||
install(TARGETS ollie-kde ollie-tray DESTINATION bin)
|
||||
install(TARGETS ollie-kde ollie-tray DESTINATION ${KDE_INSTALL_BINDIR})
|
||||
install(TARGETS ollie-plasmoid-plugin
|
||||
DESTINATION ${KDE_INSTALL_QMLDIR}/org/ollie/private)
|
||||
|
|
|
|||
22
Makefile
22
Makefile
|
|
@ -31,6 +31,28 @@ clean:
|
|||
rm -rf $(BUILD_DIR)
|
||||
rm -f $(LIB_DIR)/libollie.so $(LIB_DIR)/libollie.h
|
||||
|
||||
# Install plasmoid to user's local plasma directory
|
||||
plasmoid-install: daemon
|
||||
kpackagetool6 -t Plasma/Applet -u plasmoid/ 2>/dev/null || \
|
||||
kpackagetool6 -t Plasma/Applet -i plasmoid/
|
||||
sudo mkdir -p /usr/lib64/qt6/qml/org/ollie/private
|
||||
sudo cp $(BUILD_DIR)/org/ollie/private/ollie-plasmoid-pluginplugin.so \
|
||||
/usr/lib64/qt6/qml/org/ollie/private/libollie-plasmoid-pluginplugin.so
|
||||
sudo cp $(BUILD_DIR)/org/ollie/private/ollie-plasmoid-plugin.qmltypes \
|
||||
/usr/lib64/qt6/qml/org/ollie/private/
|
||||
sudo cp $(BUILD_DIR)/libollie-plasmoid-plugin.so /usr/lib64/
|
||||
printf 'module org.ollie.private\nplugin ollie-plasmoid-pluginplugin\ntypeinfo ollie-plasmoid-plugin.qmltypes\ndepends QtQuick\n' | \
|
||||
sudo tee /usr/lib64/qt6/qml/org/ollie/private/qmldir > /dev/null
|
||||
sudo ldconfig
|
||||
@echo "Plasmoid installed. Restart plasmashell or add widget."
|
||||
|
||||
plasmoid-uninstall:
|
||||
kpackagetool6 -t Plasma/Applet -r org.ollie.plasmoid 2>/dev/null || true
|
||||
sudo rm -rf /usr/lib64/qt6/qml/org/ollie
|
||||
sudo rm -f /usr/lib64/libollie-plasmoid-plugin.so
|
||||
sudo ldconfig
|
||||
@echo "Plasmoid uninstalled."
|
||||
|
||||
install: daemon
|
||||
install -Dm755 $(BUILD_DIR)/ollie-kde $(DESTDIR)/usr/bin/ollie-kde
|
||||
install -Dm644 $(LIB_DIR)/libollie.so $(DESTDIR)/usr/lib/libollie.so
|
||||
|
|
|
|||
38
lib/capi.go
38
lib/capi.go
|
|
@ -161,17 +161,41 @@ func ollie_create_session(cwdC *C.char, backendC *C.char, modelC *C.char, agentC
|
|||
}
|
||||
|
||||
// Subscribe to events for the chat log + callback dispatch
|
||||
streamingRole := "" // tracks current streaming role ("assistant" or "reasoning")
|
||||
core.Bus().Subscribe("event", func(ev agent.Event) {
|
||||
var text string
|
||||
switch ev.Role {
|
||||
case "assistant":
|
||||
text = ev.Content
|
||||
case "info":
|
||||
text = "[info] " + ev.Content
|
||||
case "tool":
|
||||
text = "[tool:" + ev.Name + "] " + ev.Content
|
||||
case "assistant", "reasoning":
|
||||
if streamingRole != ev.Role {
|
||||
if streamingRole != "" {
|
||||
text += "\n\n"
|
||||
}
|
||||
text += "[" + ev.Role + "]\n"
|
||||
streamingRole = ev.Role
|
||||
}
|
||||
text += ev.Content
|
||||
default:
|
||||
text = "[" + ev.Role + "] " + ev.Content
|
||||
if ev.Role == "usage" {
|
||||
return
|
||||
}
|
||||
if streamingRole != "" {
|
||||
text += "\n\n"
|
||||
streamingRole = ""
|
||||
}
|
||||
switch ev.Role {
|
||||
case "user":
|
||||
text += "[user]\n" + ev.Content + "\n\n"
|
||||
case "call":
|
||||
text += "[call:" + ev.Name + "]\n" + ev.Content + "\n\n"
|
||||
case "tool":
|
||||
text += "[tool:" + ev.Name + "]\n" + ev.Content + "\n\n"
|
||||
case "error":
|
||||
text += "[error]\n" + ev.Content + "\n\n"
|
||||
case "info":
|
||||
text += "[info] " + ev.Content + "\n\n"
|
||||
default:
|
||||
text += "[" + ev.Role + "]\n" + ev.Content + "\n\n"
|
||||
}
|
||||
}
|
||||
if text == "" {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -0,0 +1,205 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
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
|
||||
|
||||
preferredRepresentation: compactRepresentation
|
||||
|
||||
toolTipMainText: "Ollie"
|
||||
toolTipSubText: {
|
||||
let n = dbus.sessions.length
|
||||
if (n === 0) return "No active sessions"
|
||||
return n + " session" + (n > 1 ? "s" : "")
|
||||
}
|
||||
|
||||
compactRepresentation: Item {
|
||||
PlasmaComponents.Label {
|
||||
anchors.centerIn: parent
|
||||
text: {
|
||||
let state = dbus.activeState
|
||||
let n = dbus.sessions.length
|
||||
if (n === 0) return "○"
|
||||
if (state === "idle") return "◉"
|
||||
if (state.startsWith("calling")) return "⚡"
|
||||
return "●"
|
||||
}
|
||||
font.pixelSize: parent.height * 0.6
|
||||
}
|
||||
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
|
||||
|
||||
PlasmaExtras.Heading {
|
||||
level: 4
|
||||
text: "Sessions"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
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 {
|
||||
width: sessionList.width
|
||||
highlighted: modelData.id === dbus.activeSessionId
|
||||
|
||||
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 {
|
||||
text: modelData.id.substring(0, 14)
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.family: "monospace"
|
||||
font.pixelSize: 11
|
||||
}
|
||||
|
||||
Label {
|
||||
text: modelData.agent
|
||||
opacity: 0.7
|
||||
font.pixelSize: 11
|
||||
}
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
icon.name: "process-stop"
|
||||
implicitWidth: 22; implicitHeight: 22
|
||||
onClicked: dbus.killSession(modelData.id)
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: dbus.activeSessionId = modelData.id
|
||||
}
|
||||
}
|
||||
|
||||
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: 12
|
||||
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: 10
|
||||
opacity: 0.6
|
||||
text: {
|
||||
let id = dbus.activeSessionId
|
||||
if (!id) return "ollie-kde"
|
||||
return dbus.activeState + " │ " + id.substring(0, 14)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"KPackageStructure": "Plasma/Applet",
|
||||
"KPlugin": {
|
||||
"Id": "org.ollie.plasmoid",
|
||||
"Name": "Ollie",
|
||||
"Description": "AI agent session manager",
|
||||
"Icon": "system-run",
|
||||
"Category": "Utilities",
|
||||
"Authors": [
|
||||
{
|
||||
"Name": "ollie-kde"
|
||||
}
|
||||
]
|
||||
},
|
||||
"X-Plasma-API-Minimum-Version": "6.0"
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
#include "olliedbusclient.h"
|
||||
|
||||
#include <QDBusReply>
|
||||
#include <QDBusConnection>
|
||||
#include <QDir>
|
||||
|
||||
static const char *SERVICE = "org.ollie.SessionManager";
|
||||
static const char *OBJ_PATH = "/org/ollie/SessionManager";
|
||||
static const char *IFACE = "org.ollie.SessionManager";
|
||||
|
||||
OllieDBusClient::OllieDBusClient(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_iface = new QDBusInterface(SERVICE, OBJ_PATH, IFACE,
|
||||
QDBusConnection::sessionBus(), this);
|
||||
|
||||
// Connect D-Bus signals
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
bus.connect(SERVICE, OBJ_PATH, IFACE, "SessionCreated",
|
||||
this, SLOT(onSessionCreated(QString)));
|
||||
bus.connect(SERVICE, OBJ_PATH, IFACE, "SessionKilled",
|
||||
this, SLOT(onSessionKilled(QString)));
|
||||
bus.connect(SERVICE, OBJ_PATH, IFACE, "StateChanged",
|
||||
this, SLOT(onStateChanged(QString,QString)));
|
||||
bus.connect(SERVICE, OBJ_PATH, IFACE, "ChatUpdated",
|
||||
this, SLOT(onChatUpdated(QString,qlonglong,QString)));
|
||||
|
||||
refreshSessions();
|
||||
}
|
||||
|
||||
void OllieDBusClient::setActiveSessionId(const QString &id)
|
||||
{
|
||||
if (m_activeSessionId == id) return;
|
||||
m_activeSessionId = id;
|
||||
m_chatText.clear();
|
||||
m_chatOffset = 0;
|
||||
emit activeSessionIdChanged();
|
||||
emit chatTextChanged();
|
||||
fetchChat();
|
||||
}
|
||||
|
||||
void OllieDBusClient::refreshSessions()
|
||||
{
|
||||
QDBusReply<QStringList> reply = m_iface->call("ListSessions");
|
||||
if (!reply.isValid()) return;
|
||||
|
||||
m_sessions.clear();
|
||||
for (const QString &entry : reply.value()) {
|
||||
QStringList parts = entry.split('\t');
|
||||
if (parts.size() >= 4) {
|
||||
QVariantMap session;
|
||||
session["id"] = parts[0];
|
||||
session["state"] = parts[1];
|
||||
session["model"] = parts[2];
|
||||
session["agent"] = parts[3];
|
||||
m_sessions.append(session);
|
||||
}
|
||||
}
|
||||
emit sessionsChanged();
|
||||
|
||||
// Update active state
|
||||
for (const QVariant &v : m_sessions) {
|
||||
QVariantMap s = v.toMap();
|
||||
if (s["id"].toString() == m_activeSessionId) {
|
||||
QString newState = s["state"].toString();
|
||||
if (newState != m_activeState) {
|
||||
m_activeState = newState;
|
||||
emit activeStateChanged();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Active session no longer exists
|
||||
if (!m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) {
|
||||
setActiveSessionId(m_sessions.first().toMap()["id"].toString());
|
||||
} else if (m_sessions.isEmpty() && !m_activeSessionId.isEmpty()) {
|
||||
m_activeSessionId.clear();
|
||||
m_activeState = "idle";
|
||||
m_chatText.clear();
|
||||
m_chatOffset = 0;
|
||||
emit activeSessionIdChanged();
|
||||
emit activeStateChanged();
|
||||
emit chatTextChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void OllieDBusClient::fetchChat()
|
||||
{
|
||||
if (m_activeSessionId.isEmpty()) return;
|
||||
|
||||
QDBusMessage msg = QDBusMessage::createMethodCall(SERVICE, OBJ_PATH, IFACE, "GetChat");
|
||||
msg << m_activeSessionId << m_chatOffset;
|
||||
QDBusMessage reply = QDBusConnection::sessionBus().call(msg);
|
||||
|
||||
if (reply.type() != QDBusMessage::ReplyMessage) return;
|
||||
QList<QVariant> args = reply.arguments();
|
||||
if (args.size() < 2) return;
|
||||
|
||||
QString text = args[0].toString();
|
||||
qlonglong newOffset = args[1].toLongLong();
|
||||
|
||||
if (!text.isEmpty()) {
|
||||
if (m_chatOffset == 0) {
|
||||
m_chatText = text;
|
||||
} else {
|
||||
m_chatText += text;
|
||||
}
|
||||
m_chatOffset = newOffset;
|
||||
emit chatTextChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString OllieDBusClient::createSession(const QString &cwd)
|
||||
{
|
||||
QString dir = cwd.isEmpty() ? QDir::homePath() : cwd;
|
||||
QDBusReply<QString> reply = m_iface->call("CreateSession", dir, QString(), QString(), "default");
|
||||
if (reply.isValid()) {
|
||||
QString id = reply.value();
|
||||
refreshSessions();
|
||||
setActiveSessionId(id);
|
||||
return id;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool OllieDBusClient::killSession(const QString &sessionId)
|
||||
{
|
||||
QDBusReply<bool> reply = m_iface->call("KillSession", sessionId);
|
||||
if (reply.isValid() && reply.value()) {
|
||||
if (sessionId == m_activeSessionId) {
|
||||
m_activeSessionId.clear();
|
||||
m_chatText.clear();
|
||||
m_chatOffset = 0;
|
||||
m_activeState = "idle";
|
||||
emit activeSessionIdChanged();
|
||||
emit chatTextChanged();
|
||||
emit activeStateChanged();
|
||||
}
|
||||
refreshSessions();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OllieDBusClient::submit(const QString &sessionId, const QString &prompt)
|
||||
{
|
||||
QDBusReply<bool> reply = m_iface->call("Submit", sessionId, prompt);
|
||||
return reply.isValid() && reply.value();
|
||||
}
|
||||
|
||||
bool OllieDBusClient::interrupt(const QString &sessionId)
|
||||
{
|
||||
QDBusReply<bool> reply = m_iface->call("Interrupt", sessionId);
|
||||
return reply.isValid() && reply.value();
|
||||
}
|
||||
|
||||
// --- D-Bus signal handlers ---
|
||||
|
||||
void OllieDBusClient::onSessionCreated(const QString &session_id)
|
||||
{
|
||||
Q_UNUSED(session_id);
|
||||
refreshSessions();
|
||||
}
|
||||
|
||||
void OllieDBusClient::onSessionKilled(const QString &session_id)
|
||||
{
|
||||
Q_UNUSED(session_id);
|
||||
refreshSessions();
|
||||
}
|
||||
|
||||
void OllieDBusClient::onStateChanged(const QString &session_id, const QString &new_state)
|
||||
{
|
||||
if (session_id == m_activeSessionId) {
|
||||
m_activeState = new_state;
|
||||
emit activeStateChanged();
|
||||
}
|
||||
refreshSessions();
|
||||
}
|
||||
|
||||
void OllieDBusClient::onChatUpdated(const QString &session_id, qlonglong offset, const QString &new_text)
|
||||
{
|
||||
if (session_id != m_activeSessionId) return;
|
||||
|
||||
// Append directly from the signal — no need to poll
|
||||
if (offset == m_chatOffset) {
|
||||
m_chatText += new_text;
|
||||
m_chatOffset += new_text.size();
|
||||
emit chatTextChanged();
|
||||
} else {
|
||||
// Offset mismatch — do a full fetch
|
||||
fetchChat();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef OLLIE_DBUS_CLIENT_H
|
||||
#define OLLIE_DBUS_CLIENT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusConnection>
|
||||
#include <QStringList>
|
||||
#include <QVariantList>
|
||||
#include <QTimer>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class OllieDBusClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
Q_PROPERTY(QVariantList sessions READ sessions NOTIFY sessionsChanged)
|
||||
Q_PROPERTY(QString activeSessionId READ activeSessionId WRITE setActiveSessionId NOTIFY activeSessionIdChanged)
|
||||
Q_PROPERTY(QString chatText READ chatText NOTIFY chatTextChanged)
|
||||
Q_PROPERTY(QString activeState READ activeState NOTIFY activeStateChanged)
|
||||
|
||||
public:
|
||||
explicit OllieDBusClient(QObject *parent = nullptr);
|
||||
|
||||
QVariantList sessions() const { return m_sessions; }
|
||||
QString activeSessionId() const { return m_activeSessionId; }
|
||||
void setActiveSessionId(const QString &id);
|
||||
QString chatText() const { return m_chatText; }
|
||||
QString activeState() const { return m_activeState; }
|
||||
|
||||
Q_INVOKABLE void refreshSessions();
|
||||
Q_INVOKABLE void fetchChat();
|
||||
Q_INVOKABLE QString createSession(const QString &cwd);
|
||||
Q_INVOKABLE bool killSession(const QString &sessionId);
|
||||
Q_INVOKABLE bool submit(const QString &sessionId, const QString &prompt);
|
||||
Q_INVOKABLE bool interrupt(const QString &sessionId);
|
||||
|
||||
signals:
|
||||
void sessionsChanged();
|
||||
void activeSessionIdChanged();
|
||||
void chatTextChanged();
|
||||
void activeStateChanged();
|
||||
|
||||
private slots:
|
||||
void onSessionCreated(const QString &session_id);
|
||||
void onSessionKilled(const QString &session_id);
|
||||
void onStateChanged(const QString &session_id, const QString &new_state);
|
||||
void onChatUpdated(const QString &session_id, qlonglong offset, const QString &new_text);
|
||||
|
||||
private:
|
||||
QDBusInterface *m_iface;
|
||||
QVariantList m_sessions;
|
||||
QString m_activeSessionId;
|
||||
QString m_chatText;
|
||||
qlonglong m_chatOffset = 0;
|
||||
QString m_activeState = "idle";
|
||||
};
|
||||
|
||||
#endif // OLLIE_DBUS_CLIENT_H
|
||||
Reference in New Issue