// ollie-gui-9p: minimal Qt GUI using 9P for all communication. // Uses ollie-9p subprocess for 9P transport. #include #include #include #include #include #include #include #include #include #include #include #include "ollie9pclient.h" #include "sessionmodel.h" #include "chatblockmodel.h" #include "thememanager.h" #include "clipboardhelper.h" #include "plumber.h" int main(int argc, char *argv[]) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif QApplication app(argc, argv); app.setApplicationName("ollie-gui"); // Locate the installed octopus SVG (user-local takes priority over system). // Falls back to the Qt resource if not found on disk. QIcon appIcon; QString iconPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("icons/hicolor/scalable/apps/ollie.svg")); if (!iconPath.isEmpty()) appIcon = QIcon(iconPath); else appIcon = QIcon(QStringLiteral(":/ollie.svg")); app.setWindowIcon(appIcon); if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) QQuickStyle::setStyle("Basic"); QQmlApplicationEngine engine; auto *ollie = new Ollie9pClient(&app); auto *sessionModel = new SessionModel(ollie, &app); auto *chatModel = new ChatBlockModel(&app); auto *theme = new ThemeManager(&app); auto *clipboard = new ClipboardHelper(&app); auto *plumber = new Plumber(&app); QObject::connect(ollie, &Ollie9pClient::chatReceived, chatModel, &ChatBlockModel::appendData); // Handle ollie:// URLs from plumber — fetch block and display in chat QObject::connect(plumber, &Plumber::ollieMessage, [ollie, chatModel](const QString &url) { // Parse ollie://session/agent#blockid QUrl parsed(url); if (parsed.scheme() != QStringLiteral("ollie")) return; QString path = parsed.path(); if (path.startsWith('/')) path = path.mid(1); QStringList parts = path.split('/'); if (parts.size() < 2) return; QString sessionId = parts.value(0); QString agentId = parts.value(1); QString blockId = parsed.fragment(); if (blockId.isEmpty()) return; QString block = ollie->fetchBlock(sessionId, agentId, blockId); if (!block.isEmpty()) chatModel->appendData(block); }); engine.rootContext()->setContextProperty("ollie", ollie); engine.rootContext()->setContextProperty("sessionModel", sessionModel); engine.rootContext()->setContextProperty("chatModel", chatModel); engine.rootContext()->setContextProperty("theme", theme); engine.rootContext()->setContextProperty("clipboard", clipboard); engine.rootContext()->setContextProperty("plumber", plumber); const QUrl url(QStringLiteral("qrc:/main.qml")); engine.load(url); if (engine.rootObjects().isEmpty()) return -1; auto *window = qobject_cast(engine.rootObjects().constFirst()); auto *trayIcon = new QSystemTrayIcon(appIcon, &app); auto *trayMenu = new QMenu; auto *showAction = trayMenu->addAction("Show Ollie"); trayMenu->addSeparator(); auto *quitAction = trayMenu->addAction("Quit"); trayIcon->setContextMenu(trayMenu); trayIcon->setToolTip("Ollie"); engine.rootContext()->setContextProperty("trayIcon", trayIcon); // Start plumber reader for ollie port plumber->startReader(); QObject::connect(showAction, &QAction::triggered, [window, trayIcon]() { window->show(); window->raise(); window->requestActivate(); trayIcon->show(); }); QObject::connect(quitAction, &QAction::triggered, [window, &app]() { window->setProperty("quitting", true); app.quit(); }); QObject::connect(trayIcon, &QSystemTrayIcon::activated, [showAction](QSystemTrayIcon::ActivationReason reason) { if (reason == QSystemTrayIcon::Trigger) showAction->trigger(); }); QObject::connect(theme, &ThemeManager::closeToTrayChanged, [theme, trayIcon]() { trayIcon->setVisible(theme->closeToTray()); }); trayIcon->setVisible(theme->closeToTray()); return app.exec(); }