Skip to content

Multi-window: per-client tmux attach for native window-size negotiation (dotted-filler) #613

Description

@h0x91b

Hi — this is Claude (the AI assistant working on the multi-window branch). Filing this as a future improvement so we don't lose the context. Deferred on purpose: it's a large, high-risk refactor of the core PTY module.

Goal

When the same task is open in two app windows of different sizes, we want real tmux multi-client behavior: the session sizes to the focused / most-recently-active client (window-size latest), and any larger window renders tmux's dotted-filler for the unused area (content top-left, dots filling the rest). Focusing the small window should make the big one show the dotted filler — tmux does this natively once each client is its own attach.

Current architecture (the blocker)

src/bun/pty-server.ts keeps one PtySession per task with a single PTY (session.proc = one tmux attach) and a Set of WebSocket clients. PTY output is broadcast byte-for-byte to every client (flushPendingData); a resize message resizes the one shared PTY. Consequences:

  • All windows render identical bytes — they physically cannot differ (one fitted, one dotted).
  • tmux only ever knows one client size, so it never draws per-client dotted-filler.
  • Remote/browser clients proxy to the same PTY (remote-access-server.ts), so they share it too.

Current mitigation (this branch): the shared PTY is clamped to the smallest connected client (smallest-wins; larger windows letterbox with blank cells — NOT real tmux dots). See smallestClientSize / applyClientSizes in pty-server.ts and decision 060.

Desired fix — per-connection tmux attach

Give each WebSocket connection its own tmux attach-session -t <session> (its own PTY), all attaching to the same underlying tmux session. tmux then negotiates sizes natively (window-size latest) and draws the dotted-filler for larger clients automatically. "Resize on focus" becomes tmux's default behavior.

Why it's deferred (work + risk)

Significant refactor of the most battle-tested module:

  • Remove broadcast — each attach has its own output stream.
  • Dedup side-effects — bell / OSC52 clipboard / idle detection currently hook the single proc; with N attaches they'd fire N times. Need a single "primary" client or session-level detection.
  • Death vs detach — today a proc exit means "task died"; with per-client attach, one attach exiting just means a window closed. Need tmux has-session to distinguish.
  • Many documented edge-case fixes live here (decisions 036, 056, …); regression risk is real.

Acceptance

  • Two windows, same task, different sizes: focusing the small one shrinks the session; the large one shows tmux dotted-filler (matches the screenshot from the originating discussion).
  • No duplicated bells / clipboard copies / idle events.
  • Closing one window does not kill the tmux session for the other.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ComplexenhancementNew feature or requestterminalTerminal / ghostty-web related

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions