Replace inline shell command building in QML with calls to
parseClipboardDetection, parseStatusResult, buildToggleCommand, and
buildCopyCommand. This centralizes sanitization logic and removes
duplicated try/catch and ternary blocks from the UI layer.
- Add errorMessage() utility for user-friendly toast messages
- Show toast on tailscale up/down/set/status failures
- Detect missing tailscale binary (exit code 127)
- Guard toggleTailscale and setExitNode when binary unavailable
- Show 'Tailscale not available' in popout when binary missing
QML's JS engine does not support ES module export/import syntax.
Replace bare export with a CommonJS guard that only runs under Node.js,
so the plugin loads in QML while tests still work.
- Extract peer parsing and exit-node command logic into lib.js
- Add ↗ button on peers that are ExitNodeOption candidates
- Highlight active exit node in primary color
- Add clear (×) button next to current exit node label
- Add unit tests for parsePeers and makeExitNodeCommand
- Parse Peers from tailscale status --json into a model
- Add ListView with hostname/IP rows below status row
- Click hostname or IP to copy via wl-copy + toast confirmation
- Increase popout height to 400 to accommodate peer list
- Online peers shown in primary color, offline in surfaceVariantText
Add propagateComposedEvents and left-button handling to both
horizontal and vertical MouseArea blocks. Left-click is forwarded
to PluginComponent (mouse.accepted = false) so it opens popoutContent.
Right-click still toggles Tailscale connection.
Closes#10
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
Implement connected/disconnected icon (vpn_key/vpn_key_off) in
both horizontal and vertical bar orientations. Right-click triggers
tailscale up / tailscale down via Process. Icon color follows
DMS theme (primary when connected, surfaceText when disconnected).
Closes#3
Minimal loadable DMS widget skeleton with plugin.json manifest
and TailscaleWidget.qml component. Symlinked, enabled, and loaded
cleanly in Dank Material Shell.
References #1, #2