A docked Claude session panel and tab hover cards
Explore this workspace.
Technical background
A panel docked to any edge of the window lists every Claude Code session on the machine — what it is doing, what it last asked, how much of its context window is gone — and a tab's hover card says the same about the session running in that tab. Tabs are joined to sessions by launch directory, never by PID, because a session inside WSL reports Linux PIDs that can never match Tabby's conpty PIDs.
The problem
Running several Claude Code sessions across several terminals means the interesting state — which one is waiting for you, which one is nearly out of context, what one of them last asked — lives in whichever tab you are not looking at.
How it works
A panel docked to any edge of the window lists every session on the machine, and a tab's hover card says the same about the session running in that tab. It rests on two new generic extension points in tabby-core, both add-only files: SidePanelProvider contributes a panel to a dock host that owns the header, the edge picker and the resize handle, and TabHoverProvider contributes a rich hover card for a tab header, falling back to the plain title tooltip when no provider applies.
Settings
sidePanel.enabled:falseShow the docked panel.
sidePanel.side:'right'left, right, top or bottom. Moving it never re-creates the component — it is a CSS grid area.
sidePanel.size:320Width when docked left/right, height when top/bottom.
claude.stithURL:'https://stith.lvh.me'The session registry the panel reads.
claude.pollIntervalMs:2000How often the registry is asked.
claude.readTranscripts:trueDerive context-window usage from the transcript tail.
claude.hover.enabled:trueShow the tab hover card.
claude.clickAction:'focus'What clicking a session in the panel does.
Details
- Tabs are joined to sessions by directory, never by PID. A session inside WSL reports Linux PIDs that can never match Tabby's Windows conpty PIDs. Only an unambiguous 1:1 pairing is trusted — a card on the wrong tab is worse than no card.
- The join key is the launch directory, not
cwd. A session's reported cwd follows everycdthe agent makes; measured live, 2 of 13 sessions had already drifted. The launch directory is recoverable from the transcript's project folder name, whose encoding is lossy — so it is never decoded: the tab's directory is encoded the same way and the encoded forms compared, which is exact. Verified against the live registry: 11 of 13 reproduce, and the 2 that do not are exactly the drifted ones. - The launch directory can be recovered by walking a drifted cwd's ancestors until one encodes to the project key.
claude --resumeonly finds a session from its launch directory, so this is what makes Resume work at all. - Data comes from the session registry, not from the hook spool. The spool is consume-and-delete, so a second reader would steal events from the plugin that owns audio and tab decoration. Reading the registry means zero conflict and no plugin changes.
- Transcripts reach 160 MB, so one is never read whole — a 256 KB tail is enough, verified against every live session.
What this does not claim
getWorkingDirectory()alone never matches Windows tabs:tabby-localdeliberately returns null when the shell's live directory still equals the one it launched in — i.e. the common case of opening a terminal in a repo and runningclaude. The profile's cwd and the tab's initial cwd are used as fallbacks.- The panel depends on a local session registry service being reachable. With nothing answering at
claude.stithURLthe panel is empty, which is the honest result rather than an error.