From 8dd7562d3558e69deb9c900e5f1de16ab2cd32e3 Mon Sep 17 00:00:00 2001 From: jtmorris Date: Fri, 15 May 2026 19:19:01 -0700 Subject: [PATCH] feat(tailscalectl): add periodic status polling and reactive state Add Timer (5s interval) that runs tailscale status --json, parsing BackendState, Self.TailscaleIPs[0], and CurrentExitNode.HostName into isConnected, tailscaleIP, and currentExitNode properties. Parse errors surface via ToastService.showError. Initial check runs on load. Closes #4 --- tailscalectl/TailscaleWidget.qml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tailscalectl/TailscaleWidget.qml b/tailscalectl/TailscaleWidget.qml index 0c9e844..6b3b95c 100644 --- a/tailscalectl/TailscaleWidget.qml +++ b/tailscalectl/TailscaleWidget.qml @@ -9,6 +9,17 @@ 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 @@ -28,8 +39,13 @@ PluginComponent { 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") } } } @@ -37,6 +53,8 @@ PluginComponent { onExited: (code, status) => { if (code !== 0) { root.isConnected = false + root.tailscaleIP = "" + root.currentExitNode = "" } } }