ollie/kde/krunner/ollie_runner.cpp

201 lines
6.2 KiB
C++

#include "ollie_runner.h"
#include <KPluginFactory>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QTextEdit>
#include <QPushButton>
#include <QClipboard>
#include <QApplication>
#include <QDir>
#include <QStandardPaths>
K_PLUGIN_CLASS_WITH_JSON(OllieRunner, "metadata.json")
static QString ninepBin()
{
QString bin = QStandardPaths::findExecutable("9p");
if (!bin.isEmpty()) return bin;
QString plan9 = qEnvironmentVariable("PLAN9");
if (!plan9.isEmpty() && QFileInfo::exists(plan9 + "/bin/9p"))
return plan9 + "/bin/9p";
return "9p";
}
#if QT_VERSION_MAJOR >= 6
OllieRunner::OllieRunner(QObject *parent, const KPluginMetaData &data)
: AbstractRunner(parent, data)
#else
OllieRunner::OllieRunner(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
: AbstractRunner(parent, data, args)
#endif
{
}
QString OllieRunner::serverAddr()
{
QString ns = qEnvironmentVariable("NAMESPACE");
if (ns.isEmpty()) {
QString user = qEnvironmentVariable("USER");
QString display = qEnvironmentVariable("DISPLAY");
ns = "/tmp/ns." + user + "." + display;
}
return "unix!" + ns + "/ollie";
}
QByteArray OllieRunner::run9p(const QStringList &args)
{
QProcess proc;
QStringList fullArgs = {"-a", serverAddr()};
fullArgs.append(args);
proc.start(ninepBin(), fullArgs);
if (!proc.waitForFinished(5000)) return {};
return proc.readAllStandardOutput();
}
void OllieRunner::write9p(const QString &path, const QByteArray &data)
{
QProcess proc;
proc.start(ninepBin(), {"-a", serverAddr(), "write", path});
proc.waitForStarted(3000);
proc.write(data);
proc.closeWriteChannel();
proc.waitForFinished(3000);
}
QStringList OllieRunner::listSessions()
{
QByteArray out = run9p({"read", "session/idx"});
QString raw = QString::fromUtf8(out).trimmed();
if (raw.isEmpty()) return {};
return raw.split('\n');
}
void OllieRunner::killSession(const QString &id)
{
write9p("session/" + id + "/ctl", "kill");
}
void OllieRunner::match(RunnerNS::RunnerContext &context)
{
const QString query = context.query().trimmed();
if (!query.startsWith(QLatin1String("ollie ")) && query != QLatin1String("ollie"))
return;
QString args = query.mid(6).trimmed();
if (args.isEmpty() || args == QLatin1String("list")) {
QStringList sessions = listSessions();
if (sessions.isEmpty()) {
RunnerNS::QueryMatch match(this);
match.setId("ollie-no-sessions");
match.setText("No active sessions");
match.setIconName("dialog-information");
match.setRelevance(0.5);
context.addMatch(match);
} else {
for (const QString &entry : sessions) {
QStringList parts = entry.split('\t');
if (parts.size() < 4) continue;
RunnerNS::QueryMatch match(this);
match.setId("ollie-session-" + parts[0]);
QString state = parts[1] == "idle" ? "○" : "●";
match.setText(state + " " + parts[0].left(14) + " [" + parts[3] + "]");
match.setSubtext(parts[2] + " — " + parts[1]);
match.setIconName("system-run");
match.setRelevance(0.7);
match.setData(parts[0]);
context.addMatch(match);
}
}
return;
}
if (args.startsWith(QLatin1String("kill "))) {
QString target = args.mid(5).trimmed();
RunnerNS::QueryMatch match(this);
match.setId("ollie-kill-" + target);
match.setText("Kill session: " + target);
match.setSubtext("Terminate ollie session");
match.setIconName("process-stop");
match.setRelevance(0.8);
match.setData("kill:" + target);
context.addMatch(match);
return;
}
// Default: one-shot prompt via generate file
RunnerNS::QueryMatch match(this);
match.setId("ollie-prompt");
match.setText("Ask Ollie: " + args);
match.setSubtext("One-shot generation");
match.setIconName("system-run");
match.setRelevance(0.9);
match.setData("prompt:" + args);
context.addMatch(match);
}
void OllieRunner::run(const RunnerNS::RunnerContext &context, const RunnerNS::QueryMatch &match)
{
Q_UNUSED(context);
QString data = match.data().toString();
if (data.startsWith("kill:")) {
killSession(data.mid(5));
return;
}
if (data.startsWith("prompt:")) {
QString prompt = data.mid(7);
// Use the generate rdwr file for one-shot
QProcess proc;
proc.start(ninepBin(), {"-a", serverAddr(), "rdwr", "generate"});
proc.waitForStarted(3000);
proc.write(prompt.toUtf8());
proc.closeWriteChannel();
proc.waitForFinished(30000);
QString result = QString::fromUtf8(proc.readAllStandardOutput()).trimmed();
if (result.isEmpty()) result = "(no response)";
// Show popup
auto *dialog = new QDialog(nullptr);
dialog->setWindowTitle("Ollie — " + prompt.left(40));
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->resize(520, 380);
auto *layout = new QVBoxLayout(dialog);
auto *textEdit = new QTextEdit(dialog);
textEdit->setReadOnly(true);
textEdit->setPlainText(result);
QFont monoFont("monospace");
monoFont.setPointSize(QApplication::font().pointSize());
textEdit->setFont(monoFont);
layout->addWidget(textEdit);
auto *buttonLayout = new QHBoxLayout();
buttonLayout->addStretch();
auto *copyBtn = new QPushButton("Copy", dialog);
QObject::connect(copyBtn, &QPushButton::clicked, [result]() {
QApplication::clipboard()->setText(result);
});
buttonLayout->addWidget(copyBtn);
auto *closeBtn = new QPushButton("Close", dialog);
QObject::connect(closeBtn, &QPushButton::clicked, dialog, &QDialog::close);
buttonLayout->addWidget(closeBtn);
layout->addLayout(buttonLayout);
dialog->show();
dialog->raise();
dialog->activateWindow();
return;
}
// Session selected — launch GUI
QProcess::startDetached("ollie-gui", {});
}
#include "ollie_runner.moc"