Skip to content

feat(terminal): focus on tab select and show a start-up indicator - #44

Merged
clintberry merged 2 commits into
mainfrom
feat/terminal-focus-and-loading
Jul 28, 2026
Merged

feat(terminal): focus on tab select and show a start-up indicator#44
clintberry merged 2 commits into
mainfrom
feat/terminal-focus-and-loading

Conversation

@clintberry

Copy link
Copy Markdown
Contributor

Summary

Two papercuts on the Terminal tab:

  • Selecting the tab left the cursor unfocused, so the first keystroke went nowhere and you had to click into the terminal first.
  • First attach sat on a black rectangle for several seconds with no explanation.

Focus on tab select

CenterPanel renders <TerminalView /> conditionally on the active tab, so selecting the tab mounts the component. Calling term.focus() after open() means that mount — i.e. the tab selection — lands the cursor in the terminal.

Start-up indicator

A spinner overlays the terminal container while it has nothing to show. Two decisions worth calling out.

The ready signal is first output, not the replay boundary. pty.Start returns as soon as devpod ssh spawns, but establishing the connection into the container is the slow part. The 0x03 replay-complete frame therefore lands almost immediately on a still-blank screen — keying the indicator off it would hide it while there is genuinely nothing to see. It clears on the first payload-bearing 0x00/0x02 frame instead.

The indicator is delayed 250ms. Re-attaching to a running PTY paints from the replay buffer within milliseconds. An undelayed spinner would flicker on every tab switch, which reads as a glitch rather than as feedback.

The indicator overlays the terminal container rather than replacing it — xterm has already attached to that node, so it has to stay mounted while connecting.

Test plan

84 tests pass (5 new); tsc -b clean, eslint 0 errors.

New coverage in TerminalView.test.tsx:

  • The terminal is focused on mount.
  • The indicator appears once the delay elapses with no output.
  • It never appears when output arrives inside the delay window.
  • It clears on first output.
  • It stays up when only the replay boundary has arrived — the regression that would follow from treating 0x03 as ready.

All three behaviours were mutation-tested: removing the focus call, treating 0x03 as ready, and dropping the delay each fail a specific test. The delay assertion needed strengthening to earn that (second commit) — the original version delivered output before any timer processing, so it passed whether the delay was 250ms or 0.

Post-Deploy Monitoring & Validation

No additional operational monitoring required — frontend-only, no backend, data, or auth impact.

Validate manually after deploy:

  • Select the Terminal tab and type immediately; keystrokes land without clicking first.
  • On a cold session, the spinner appears and is replaced by the shell prompt.
  • Switching away and back to a warm terminal shows no spinner flicker.
  • Failure signal: a spinner that never clears (see residuals), or a terminal that renders but won't take input. Rollback is a straight revert.

Known residuals / follow-ups

  • The indicator has no upper bound. If devpod ssh dies during startup the PTY closes, but nothing signals that over the socket, so the spinner would persist indefinitely. The same failure previously showed as a permanently black screen, so this is not a regression — but a spinner implies progress in a way a black screen does not. Mitigations range from swapping the message after ~20s to having the server signal PTY exit; deliberately out of scope here.
  • Not verified visually against a live DevPod workspace.
  • Focusing on mount means selecting the Terminal tab takes focus from wherever it was. That is the intent, but it is a behaviour change if anything else on the page expected to keep focus across a tab switch.

Compound Engineering
Claude Code

Two papercuts on the Terminal tab.

Selecting the tab left the cursor unfocused, so the first keystroke went
nowhere and users had to click into the terminal first. CenterPanel
remounts TerminalView on tab switch, so focusing after open() means
selecting the tab now lands the cursor.

First attach also sat on a black rectangle for several seconds with no
explanation: pty.Start returns as soon as `devpod ssh` spawns, but the
connection into the container takes far longer. Show a spinner until the
first payload-bearing frame arrives.

The readiness signal is first output, not the replay boundary — 0x03
lands almost immediately on a blank screen, so keying off it would drop
the indicator while there is still nothing to see. The indicator is
delayed 250ms so attaching to an existing PTY, which paints from the
replay buffer in milliseconds, doesn't flicker.
The no-flash test delivered output before any timer processing, so it
passed whether the delay was 250ms or 0. Advance a realistic attach
latency first and assert the indicator is absent at that point, which
fails if the delay is removed.
@clintberry
clintberry merged commit 3cc5a9e into main Jul 28, 2026
2 checks passed
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