import QtQuick import QtQuick.Layouts import Quickshell.Io import qs.Common import qs.Widgets // Registered as plugin.json's "settings" key - the only thing shown in // DMS's global Settings -> Plugins -> Dank Chat area, by explicit user // direction (this pane started empty and gets exactly what's asked for, // nothing assumed beyond it). Per-protocol chat settings (message // filters, connection defaults, SASL, etc.) live in ChatSettings.qml // instead, reachable from inside the chat panel's own three-dot menu - // "is the background daemon actually running" is a different, always- // relevant concern from any of that. // // DMS's own PluginsTab (see PluginListItem.qml's settingsLoader) injects // `pluginService` into any loaded settings component that declares the // property, the same way it does for every other plugin's "settings" - // no manual embedding needed here, unlike ChatSettings.qml's in-panel // case (which isn't reached through this normal path at all). Item { id: root property var pluginService: null property bool chatdConnected: false function refreshStatus() { root.chatdConnected = root.pluginService ? !!root.pluginService.getGlobalVar("dankChat", "chatdConnected", false) : false; } Component.onCompleted: { root.refreshStatus(); root.checkGitBranch(); } onPluginServiceChanged: { root.refreshStatus(); root.checkGitBranch(); } // Which checkout this running plugin is actually loaded from - see // /home/ultra/.claude memory on the development-branch workflow: // "master" is the stable build users should be running day to day, // "development" is where bug fixes/new features land first. Read live // via git rather than baked in at some build step, since this plugin // has no such step at all (the QML is interpreted directly from // whatever's currently checked out) - so this always reflects the // real current state, including mid-session branch switches once the // plugin's settings page is reopened. readonly property string pluginDir: { if (root.pluginService) { const p = root.pluginService.getPluginPath("dankChat"); if (p) return p.endsWith("/") ? p : p + "/"; } // Fallback matches ChatDaemon.qml's own pluginDir - this settings // pane's QML file lives in the plugin's own directory regardless of // whether pluginService.getPluginPath() comes through, since the // component is loaded directly off disk either way. const dirUrl = Qt.resolvedUrl(".").toString(); return dirUrl.replace(/^file:\/\//, ""); } property string gitBranch: "" readonly property string buildLabel: { if (root.gitBranch === "master") return "Production"; if (root.gitBranch === "development") return "Development"; if (root.gitBranch === "") return "Unknown"; return root.gitBranch; // some other branch - show it verbatim rather than guessing } readonly property color buildLabelColor: { if (root.gitBranch === "master") return Theme.primary; if (root.gitBranch === "development") return Theme.warning; return Theme.surfaceVariantText; } function checkGitBranch() { if (root.pluginDir && !gitBranchProcess.running) gitBranchProcess.running = true; } onPluginDirChanged: root.checkGitBranch() Process { id: gitBranchProcess running: false command: ["git", "-C", root.pluginDir, "rev-parse", "--abbrev-ref", "HEAD"] stdout: StdioCollector { onStreamFinished: root.gitBranch = (text || "").trim() } } // getGlobalVar() is a plain function call, not a property read - QML's // binding dependency tracking can't auto-refresh from that, so this // has to explicitly re-poll on the one signal that means the value // might have changed instead of binding chatdConnected directly. Connections { target: root.pluginService enabled: root.pluginService !== null function onGlobalVarChanged(changedPluginId, varName) { if (changedPluginId === "dankChat" && varName === "chatdConnected") root.refreshStatus(); } } implicitHeight: content.implicitHeight height: implicitHeight ColumnLayout { id: content width: parent.width spacing: Theme.spacingM StyledText { Layout.fillWidth: true text: "Chat Daemon" font.pixelSize: Theme.fontSizeLarge font.weight: Font.Medium color: Theme.surfaceText } StyledText { Layout.fillWidth: true text: "The background process (chatd) that actually talks to IRC/XMPP servers on Dank Chat's behalf. If it's not running, no account can connect regardless of what's configured for it." wrapMode: Text.WordWrap color: Theme.surfaceVariantText } RowLayout { Layout.fillWidth: true spacing: Theme.spacingS Rectangle { width: 10 height: 10 radius: 5 color: root.chatdConnected ? Theme.primary : Theme.error } StyledText { text: root.chatdConnected ? "Running" : "Not running" color: Theme.surfaceText font.weight: Font.Medium } } Rectangle { Layout.fillWidth: true height: 1 color: Theme.outlineMedium } StyledText { Layout.fillWidth: true text: "Build" font.pixelSize: Theme.fontSizeLarge font.weight: Font.Medium color: Theme.surfaceText } RowLayout { Layout.fillWidth: true spacing: Theme.spacingS Rectangle { radius: Theme.cornerRadius color: Theme.withAlpha(root.buildLabelColor, 0.18) implicitWidth: buildLabelText.implicitWidth + Theme.spacingM * 2 implicitHeight: buildLabelText.implicitHeight + Theme.spacingXS * 2 StyledText { id: buildLabelText anchors.centerIn: parent text: root.buildLabel color: root.buildLabelColor font.weight: Font.Bold font.pixelSize: Theme.fontSizeSmall } } StyledText { text: root.gitBranch ? ("(" + root.gitBranch + " branch)") : "" visible: text !== "" color: Theme.surfaceVariantText font.pixelSize: Theme.fontSizeSmall } } DankButton { Layout.alignment: Qt.AlignLeft text: "Restart daemon" onClicked: { if (root.pluginService) root.pluginService.setGlobalVar("dankChat", "restartNonce", Date.now()); } } } }