feat(web): reach a tab or a pane by name, and stamp history in the reader's zone
The session view had one flat row of the OTHER panes in the workspace, tagged `Tab` and `Space`, each chip named `label || pane_id`. On the measured host 28 of the 30 panes carry no operator label, so that row read `w6:pH w6:pQ w6:pR` -- three chips whose names differ by one character, no mark for the pane you were standing in, and no way to reach a tab by name at all. It is now herdr's own two levels below the space, ported from Collie's TabStrip + PaneStrip: the tabs of this space, then the panes of this tab. - A pane is named by what it can still say for itself (paneChipName, after Collie's paneDisplayName): the operator's label, then an agent's activity title, then its harness; a terminal's cwd basename, then `shell`. Never `project` -- the relay sets that to basename(cwd), which every pane in one worktree shares. The pane id's suffix rides along as a muted tag, because a name is routinely shared by every chip in the row (three shells in `herdr`, three claudes in one tab) and the suffix is the only part that separates them. - The open pane and its tab are filled and carry aria-current. Blue is selection everywhere else on this page, and the old row had no `you are here`. - Tapping a tab lands on tabLandingPane -- the neediest agent in it by bucketOf, else its first terminal. A tab with no pane the client can name is not a chip; the tab you are in is inert rather than closing your open panel. - Each row is hidden when it holds no choice: tabs unless the space has two reachable ones (6 of 10 agent panes measured are in a single-tab space), panes unless the tab holds a second (10 of 10 do). 66px together, 7.8% of 390x844. - Neither row is labelled in words: a `Tabs` / `Panes` label cost 40px of the row, and 4 of the 5 rows that scrolled on the real host overflowed by less than that. The name is on the strip's aria-label; the distinction is shape (a tab chip is squarer) and the id tag a tab has not got. A chip's height is fixed at 22px because a CJK activity title is taller than latin at the same size and grew the row 2px; the name is capped with the rest in the tooltip. And three fixes to the history panel: - A turn is stamped in the READER's zone. Claude writes every transcript timestamp in UTC -- all 4,450 rows sampled here end in `Z` -- and this was ts.slice(11, 16), so a turn made at 17:08 in UTC+8 read 09:08. The date rides along when the turn is not from today, because paging back is the point of the panel and a bare 18:51 cannot say which day it is. - The header is one row. It was a title bar over a filter bar, 80px of a 390x844 screen spent before a single turn rendered; the Find chip now opens the input in place of the title, 35px in both states, and closing it drops the needle. - The list carries overscroll-behavior: contain, like .term-content already did. A drag at the top chained to the document and handed Chrome its pull-to-refresh, which reloads the app -- losing the session, the panel, and however far back you had paged. tests/test_web_spaces.py rewrites the strip suite around the two rows (49 tests); tests/test_web_history.py adds WebHistoryPanelTests, which pins the page to Asia/Shanghai and reopens it as America/Los_Angeles because "the reader's zone" is the claim and the runner's clock would make it a test of the runner. It also drops that file's second playwright instance for the module-shared browser its siblings use -- two in one process is the contention that times out goto.
N
Nick007 committed
41926e427912d07c73af736eba5499c8a4e54e98
Parent: 23c1de2