Rework the CONNECTIONS section
- Label each connection with its tailnet and account rather than the nickname alone. `tailscale switch --list --json` carries both, and on a multi-tailnet setup the nickname is the least useful field — "andre" says nothing about which tailnet you would be switching to. Renders as "headscale.pertento.ai (andre)", falling back through tailnet, then account, then profile id when a field is missing. - Mark the active connection with colour instead of a filled background: brand yellow on its label, currentFill cleared. The theme sets selected-border-width to 0, so clearing the fill leaves nothing drawn. Hover still highlights through `fill`, so the row keeps its affordance. Exit-node and Mullvad-region rows are untouched and still fill. - Sort the active connection to the top. Done in parseAccounts rather than at render time: the keyboard cursor indexes into tailscale.accounts, so sorting only in the view would leave j/k acting on a different row than the highlighted one. Array.sort is stable, so the remaining profiles keep the CLI's order. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -57,11 +57,25 @@ function osIcon(os) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Stock returned the nickname alone, which on a multi-tailnet setup is the
|
||||
// least useful field — "andre" and "pastilhas" say nothing about which tailnet
|
||||
// you would be switching to. `tailscale switch --list --json` carries both, so
|
||||
// show the tailnet with the account in parentheses:
|
||||
//
|
||||
// headscale.pertento.ai (andre)
|
||||
// offscale.pastilhas.dev (pastilhas)
|
||||
//
|
||||
// Falls back through the same chain as before when a field is absent, so a
|
||||
// profile with no tailnet still renders something meaningful.
|
||||
function accountLabel(account) {
|
||||
if (!account) return "Unknown account"
|
||||
if (account.nickname) return String(account.nickname)
|
||||
if (account.tailnet) return String(account.tailnet)
|
||||
if (account.account) return String(account.account)
|
||||
|
||||
var tailnet = String(account.tailnet || "").trim()
|
||||
var who = String(account.account || account.nickname || "").trim()
|
||||
|
||||
if (tailnet !== "" && who !== "") return tailnet + " (" + who + ")"
|
||||
if (tailnet !== "") return tailnet
|
||||
if (who !== "") return who
|
||||
return String(account.id || "Unknown account")
|
||||
}
|
||||
|
||||
@@ -292,6 +306,19 @@ function parseAccounts(raw) {
|
||||
if (account.selected === true) selected = account
|
||||
}
|
||||
}
|
||||
|
||||
// Active connection first, everything else in the order the CLI listed it.
|
||||
// Sorted here rather than in the view so the Repeater and the keyboard
|
||||
// cursor (accountIndex) index into the same ordering — sorting only at
|
||||
// render time would leave j/k selecting a different row than the one
|
||||
// highlighted. Array.sort is stable, so the unselected profiles keep their
|
||||
// relative order.
|
||||
next.sort(function(left, right) {
|
||||
var l = left.selected === true ? 0 : 1
|
||||
var r = right.selected === true ? 0 : 1
|
||||
return l - r
|
||||
})
|
||||
|
||||
return {
|
||||
accounts: next,
|
||||
selectedAccountId: selected ? String(selected.id || "") : "",
|
||||
|
||||
Reference in New Issue
Block a user