Replace Use now with a plain Reconnect, and ignore superseded tests

Three review rounds kept finding ways Use now could point at an address a
reconnect wouldn't pick: the page was predicting the outcome of the server's
race from test results that could be unfinished, from another network, or out
of date after a reorder or a Tailscale change. Adding the offered LAN address
first in the list is what makes it win; Use now only hurried that along.

The dialog's Retry button now also shows while connected, as Reconnect: it tries
the addresses again in their current order and promises nothing about which
answers first. The test results go back to plain rows.

The review also found a test started earlier could overwrite a newer one still
running, and mark it done. Each headset's tests now have a generation; only the
newest one publishes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
saphidandClaude Opus 5.5 committed 2026-10-01 22:37:12 +10:00
1 parent 0bb2f9eb03
commit b9f96866e5
4 files changed
+41 -32

No files matched your search

+6 -10
View File
@@ -4337,8 +4337,12 @@ function renderConnDlg() {
: `${s.reason || "Connecting"}…`;
// Folded while connected; opened when something's wrong. Left as the user set it otherwise.
if (s.phase !== cd.phase) { $("connMore").open = !connected; cd.phase = s.phase; }
$("connRetryBtn").hidden = !failed;
// Connected, it's Reconnect: the addresses are tried again in their current order, so an
// address just added or moved up gets its chance now rather than at the next connection.
$("connRetryBtn").hidden = !failed && !connected;
$("connRetryBtn").classList.toggle("action", failed);
$("connRetryBtn").textContent = failed ? "Retry now" : "Reconnect";
$("connRetryBtn").title = failed ? "" : "Try the addresses again, in order";
$("connSetup").hidden = connected;
const n = s.network || {}, ts = n.tailscale || {};
const facts = [["Network", n.ssid || n.name || "—"],
@@ -4404,13 +4408,8 @@ function renderConnAddrs() {
// same address (after an edit, on that row's Edit button).
const held = list.contains(document.activeElement) && document.activeElement.closest("[data-host]");
const refocus = cd.refocus ? [cd.refocus, "[data-ed]"]
: held ? [held.dataset.host, ["[data-use]", "[data-mv='-1']", "[data-mv='1']", "[data-ed]", "[data-rm]"].find(q => document.activeElement.matches(q))] : null;
: held ? [held.dataset.host, ["[data-mv='-1']", "[data-mv='1']", "[data-ed]", "[data-rm]"].find(q => document.activeElement.matches(q))] : null;
cd.refocus = null;
// A reconnect tries the addresses in the order the finished test reports for this network
// and picks the first that answers: offer Use now on that one, if it isn't the one in use.
const test = (s.tests || {})[d.id], order = test && test.done && test.network === (s.network || {}).id && test.order;
const pick = order && order.find(h => (tested.get(h) || {}).ssh === "ok");
const switchTo = pick && inUse && pick !== inUse && order.indexOf(inUse) > order.indexOf(pick) ? pick : null;
list.innerHTML = d.addresses.map((a, i) => {
if (cd.editing === a.host && cd.editDev === d.id) return `<div class="item addr"><div class="addr-edit conn-edit">
<input type="text" id="ceHost" value="${esc(a.host)}" maxlength="253" aria-label="Address">
@@ -4424,7 +4423,6 @@ function renderConnAddrs() {
return `<div class="item addr" data-host="${esc(a.host)}">
<div class="grow"><div class="t">${esc(a.host)} <span class="tag">${esc(KIND_TAG[a.kind] || a.kind)}</span>${a.label ? ` <span class="sub">${esc(a.label)}</span>` : ""}</div>
<div class="s">${result}</div></div>
${a.host === switchTo ? `<button class="small" data-use title="Reconnect through this address">Use now</button>` : ""}
<button class="small" data-mv="-1" title="Try earlier" aria-label="Move ${esc(a.host)} up"${i ? "" : " disabled"}>↑</button>
<button class="small" data-mv="1" title="Try later" aria-label="Move ${esc(a.host)} down"${i < d.addresses.length - 1 ? "" : " disabled"}>↓</button>
<button class="small" data-ed>Edit</button><button class="small danger" data-rm>Remove</button></div>`;
@@ -4441,8 +4439,6 @@ function renderConnAddrs() {
row.querySelector("[data-ed]").onclick = () => {
cd.editing = host; cd.editDev = d.id; cd.session++; renderConnAddrs(); $("ceHost").focus();
};
const use = row.querySelector("[data-use]");
if (use) use.onclick = () => devAction(`Switch to ${host}`, { action: "retry", id: d.id }, use);
row.querySelector("[data-rm]").onclick = e => {
if (host === inUse && !confirm(`${host} is how Frame Control is reaching the headset now. Remove it?`)) return;
devAction(`Remove ${host}`, { action: "address-remove", id: d.id, host }, e.currentTarget);