1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-05 03:52:07 -04:00

feat(tailscale): add Tailscale control center widget (#1875)

* feat(tailscale): add Tailscale control center widget

Full-stack Tailscale integration for DMS control center:

Backend (Go):
- Event-driven manager via WatchIPNBus (no polling)
- Reconnects with exponential backoff when tailscaled unavailable
- Typed conversion from ipnstate.Status to QML-friendly IPC types
- Testable via tailscaleClient interface with mock watcher
- Manager cleanup in cleanupManagers()
- 19 unit tests

Frontend (QML):
- TailscaleService with WebSocket subscription
- TailscaleWidget with peer list, filter chips, search
- Copy-to-clipboard for IPs and DNS names
- Daemon lifecycle handling (offline/stopped states)

Dependencies:
- Add tailscale.com v1.96.1 (official local API client)
- Bump Go to 1.26.1 (required by tailscale.com)

* cleanups

---------

Co-authored-by: bbedward <bbedward@gmail.com>
This commit is contained in:
Giorgio De Trane
2026-05-04 19:37:25 +02:00
committed by GitHub
parent 408beb202c
commit d223a74740
19 changed files with 2055 additions and 11 deletions

View File

@@ -10,6 +10,7 @@ QtObject {
property var vpnBuiltinInstance: null
property var cupsBuiltinInstance: null
property var tailscaleBuiltinInstance: null
property var vpnLoader: Loader {
active: false
@@ -63,6 +64,35 @@ QtObject {
}
}
property var tailscaleLoader: Loader {
active: false
sourceComponent: Component {
TailscaleWidget {}
}
onItemChanged: {
root.tailscaleBuiltinInstance = item;
}
onActiveChanged: {
if (!active) {
root.tailscaleBuiltinInstance = null;
}
}
Connections {
target: SettingsData
function onControlCenterWidgetsChanged() {
const widgets = SettingsData.controlCenterWidgets || [];
const hasTailscaleWidget = widgets.some(w => w.id === "builtin_tailscale");
if (!hasTailscaleWidget && tailscaleLoader.active) {
root.log.debug("No Tailscale widget in control center, deactivating loader");
tailscaleLoader.active = false;
}
}
}
}
readonly property var coreWidgetDefinitions: [
{
"id": "nightMode",
@@ -202,6 +232,16 @@ QtObject {
"enabled": CupsService.available,
"warning": !CupsService.available ? I18n.tr("CUPS not available") : undefined,
"isBuiltinPlugin": true
},
{
"id": "builtin_tailscale",
"text": I18n.tr("Tailscale", "Tailscale mesh VPN widget title"),
"description": I18n.tr("Tailscale Network", "Tailscale control center widget description"),
"icon": "device_hub",
"type": "builtin_plugin",
"enabled": TailscaleService.available,
"warning": !TailscaleService.available ? I18n.tr("Tailscale not available", "Warning when Tailscale service is not running") : undefined,
"isBuiltinPlugin": true
}
]