import QtQuick import qs.Common import qs.Services import qs.Widgets import qs.Modules.Plugins import Quickshell.Io PluginComponent { id: root property bool isConnected: false property string tailscaleIP: "" property string currentExitNode: "" Timer { interval: 5000 running: true repeat: true onTriggered: statusCheck.running = true } Component.onCompleted: statusCheck.running = true Process { id: toggleProcess onExited: (code, status) => { statusCheck.running = true } } Process { id: statusCheck command: ["tailscale", "status", "--json"] stdout: StdioCollector { onStreamFinished: { try { const data = JSON.parse(this.text) root.isConnected = data.BackendState === "Running" root.tailscaleIP = (data.Self?.TailscaleIPs?.[0]) || "" root.currentExitNode = data.CurrentExitNode?.HostName || "" } catch (e) { root.isConnected = false root.tailscaleIP = "" root.currentExitNode = "" ToastService.showError("tailscalectl", "Failed to parse Tailscale status") } } } onExited: (code, status) => { if (code !== 0) { root.isConnected = false root.tailscaleIP = "" root.currentExitNode = "" } } } function toggleTailscale() { if (root.isConnected) { toggleProcess.command = ["tailscale", "down"] } else { toggleProcess.command = ["tailscale", "up"] } toggleProcess.running = true } horizontalBarPill: Component { MouseArea { implicitWidth: contentRow.implicitWidth implicitHeight: contentRow.implicitHeight cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.RightButton onClicked: toggleTailscale() Row { id: contentRow spacing: Theme.spacingS DankIcon { name: root.isConnected ? "vpn_key" : "vpn_key_off" size: Theme.iconSize color: root.isConnected ? Theme.primary : Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } } } } verticalBarPill: Component { MouseArea { implicitWidth: contentColumn.implicitWidth implicitHeight: contentColumn.implicitHeight cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.RightButton onClicked: toggleTailscale() Column { id: contentColumn spacing: Theme.spacingXS DankIcon { name: root.isConnected ? "vpn_key" : "vpn_key_off" size: Theme.iconSize color: root.isConnected ? Theme.primary : Theme.surfaceText anchors.horizontalCenter: parent.horizontalCenter } } } } }