tile-grid-park-guards.test.ts carried its own copy of the fake DOM, written
before test/mocks/tile-grid-vm.ts existed, including the remove() that spliced
the wrong element when a child was no longer listed (fixed in the shared copy
only). It now uses the shared harness, which gains what the guards need: a
settable clock behind performance.now, the PerformanceObserver callbacks the
code under test registers, and localFit on the fake tile. Same 35 cases.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Shortcut registry (DEFAULT_SHORTCUTS, group Tiles, all rebindable):
- Toggle Tile Grid, Ctrl+Shift+G: opens the grid this tab last left (one step
back after a selection outside it), else an open split as two tiles, else
the active session as one tile; pressed again, back to the single view of
the focused session. xterm emits nothing for a shifted Ctrl letter; the
browser's find-previous is overridden only where the grid can open.
- Focus Tile Left/Right/Up/Down, Alt+Shift+Arrows: a human selection of the
tile in that direction.
- Remove Focused Tile, unbound: the session keeps running.
tileShortcutFor() decides whether a chord applies (the toggle wherever a grid
could open, the rest only while one is open, so outside the grid
Alt+Shift+Arrows reach the terminal untouched) and is registry-aware. The
capture handler dispatches it, and the main terminal's and every tile's xterm
key handler return false for it, for every event type and before the
Shift+Enter gate, so a chord that applies never reaches a PTY.
Coexistence with the split pane: opening the grid over an open split closes
it (no wasted resize for the pane about to park) and seeds the grid with both
of its sessions, Pane A focused. While the grid is open openSplitPicker and
openSplitPane refuse and the Split button reads as unavailable
(aria-disabled); closing the grid never reopens a split.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
selectSession gets the tile branch, right after its "already active" early
return: a tiled session is focused in its tile (_selectTiledSession: an
activeSessionId change, the shared _refreshSessionPanels and xterm.focus(),
no cleanup, replay, resize or socket of the parked main terminal). Decision 1:
a USER-initiated pick of a session that is not tiled leaves the grid for the
single view (the grid is remembered), and so does an explicit leaveTiles; an
app-driven pick (auto) never collapses it. A followed #session= link passes
leaveTiles (navigation).
App-driven paths pick a tile instead of the first sessionOrder entry:
- closeSession on the focused tile focuses the neighbouring tile (next in grid
order, else previous), captured before the await like wasActive, since the
delete broadcast may already have removed the tile; the last tile closes the
grid and falls back to the normal pick.
- A tiled session deleted elsewhere loses its tile and a neighbour takes focus
with auto (the last one lands on the welcome screen as before); a close from
this tab only drops the tile and leaves the follow-up to closeSession.
- A tiled session popped out to its own window leaves the grid.
Focus rules: pressing a tile is a human selection (pointerdown, never
preventDefault); Ctrl+Tab and Alt+[ / Alt+] cycle through the tiles; only a
human selection acknowledges an idle alert. Home leaves the grid (remembered);
killing every session closes it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>