feat(tiles): keep the arranged layout per browser across toggles and reloads

The Tiles toggle used to fill a remembered grid to the count picked in the
menu (refilling a hole the user made, or trimming tiles), removing the last
tile or "Open group as tiles" forgot it, and Ctrl/Cmd+click repacked it.
The owner asked for the opposite: "when I turn tiles off and on, always
keep what the last setting was".

Now the layout the user arranged (which session sits in which cell, holes
included, the tile count, the divider sizes, the focused tile and a zoom
they chose) is written to localStorage on every change and comes back
exactly from the toggle, Ctrl+Shift+G and a reload, however the grid was
closed (toggle, a non-tiled tab, leaveTiles or a #session= link, Home, the
width gate, the last tile, a group, closing or killing sessions). Nothing
of it reaches the server.

- The stored value stays v: 1 and gains `count`: how many tiles the user's
  own last change left. A session that goes away by itself (deleted,
  popped out, socket refused) does not lower it, so the next activation
  fills that cell from the ranking; a hole the user made stays a hole.
- sanitizeTileGridState reports `freed` cells (sessions gone since) and
  derives `count` for values written before it; restoreTileGridCells (pure)
  puts a stored grid back, freed cells first, never trimmed to the window
  (a too-small window shows the focused tile until it fits).
- Only when none of the stored sessions survive does activation rank from
  scratch. A count picked in the menu still re-forms the grid to it.
- Nothing is persisted while a stored grid is being put back, and a
  #session= link only flips `open`, keeping gone ids for the fill.
- The hover card no longer promises "a click opens N" when a stored grid
  will open instead.

This supersedes decision 10's answer that the count wins over a
remembered grid's size; the affected tests say so.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-10-10 05:58:17 +02:00
1 parent 950450b137
commit 16980d644d
11 files changed
+948 -161

No files matched your search

+3 -2
View File
@@ -89,8 +89,9 @@ describe('opening the grid over an open split', () => {
expect(paneB.destroy).toHaveBeenCalledTimes(1);
expect(app._splitPane).toBeNull();
// Filled from tab order (s-other first), not seeded with the split's two.
expect(app._tileGrid.ids).toEqual(['s-c', 's-other']);
// Exactly as stored (owner request: the last arrangement comes back), not
// seeded with the split's two, nor filled to the count.
expect(app._tileGrid.ids).toEqual(['s-c']);
expect(app.activeSessionId).toBe('s-c');
// Pane A (s-a) is not a tile: the split's closing resize gave it its full width back.
expect(app.sendResize.mock.calls).toEqual([['s-a', { force: true }]]);