Skip to content

fix(tabs): preserve label across transient remote-host disconnects#95

Merged
attson merged 1 commit into
mainfrom
fix/preserve-disconnected-tab-label
Jun 2, 2026
Merged

fix(tabs): preserve label across transient remote-host disconnects#95
attson merged 1 commit into
mainfrom
fix/preserve-disconnected-tab-label

Conversation

@attson
Copy link
Copy Markdown
Owner

@attson attson commented Jun 2, 2026

Repro

Open several remote-mirrored tabs from a single desktop host. Let the relay poll for a while; when that host's network/process briefly drops (or it ever does), the relay's session list stops including those IDs for one or more polls — and every tab that referenced them collapses to "(空)" / "(empty)". Even after the host comes back, the title doesn't.

Root cause

`sweepMissingSessions()` reacts to the relay's new session list by nulling `pane.sessionId` for every pane whose session isn't there anymore. After that, the TabBar's `shortTitle()` sees `activeSession === null` and renders `i18n("terminal.emptyTab")` — i.e. "(空)". The tab is now indistinguishable from any other empty pane.

Fix

  • `Pane` gains an optional `lastSeenInfo: SessionInfo` field.
  • Both `applyLocalSessions` and `applyRemoteSessions` snapshot the currently-known sessions (local + remote) before replacing the list, and pass that snapshot to `sweepMissingSessions`.
  • When sweep nulls a pane it stashes the snapshot's entry for that id (or the pane's existing cache if two consecutive sweeps fire) onto `lastSeenInfo`.
  • `tabSummaries` falls back to `lastSeenInfo` and surfaces a `disconnected: true` flag.
  • `TabBar` uses the fallback for the title, dims + italicises the title, and the remote dot turns grey. Tooltip prefixes "(disconnected)" via a new i18n key.

If the same `session_id` reappears in a later poll (transient blip resolved), `findSessionInfo` returns the live entry and the disconnected styling is gone automatically — no manual reattach needed. If the host returns with new session ids, the cached label still tells the user which tab is which.

Test plan

  • New vitest case: TabBar renders the cached cwd basename + "disconnected" class when `disconnected: true` is passed
  • `npm test` — 77 files / 645 tests green
  • `npx vue-tsc --noEmit` — clean
  • manual: open ≥2 remote tabs, kill the host process / disable wifi briefly. Tabs keep their titles instead of "(空)" and visibly grey out. Restart host with same session: tab returns to normal style.

When a remote uplink dropped (host network blip, host process crash,
relay-side prune), the relay's session listing stopped including the
affected session_ids. sweepMissingSessions() then nulled each
referencing pane's sessionId and the TabBar fell back to "(空)" —
the user lost every tab's identity in one poll cycle.

Cache the SessionInfo on the pane just before nulling it (Pane.
lastSeenInfo) by snapshotting localList + remoteList right before
either is replaced. tabSummaries surfaces the cached info plus a
disconnected flag; TabBar uses it for the title and adds a dimmed +
italic style so the disconnected state is visible at a glance.

If the same session_id reappears in a later poll (transient network
loss), the live entry is preferred again automatically. If the host
returns with new session_ids the user can still read which tab was
which and close them by hand.
@attson attson merged commit ca9de67 into main Jun 2, 2026
7 checks passed
@attson attson deleted the fix/preserve-disconnected-tab-label branch June 2, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant