fix(tiles): popping out a tiled session keeps the grid's count

A session popped out while the grid was open (its pop-out button, or
another dashboard tab announcing the pop-out over the window channel) left
the grid through _markDetached, which removed its tile as if the user had
closed it: the stored count dropped to five and its cell stayed a hole the
ranking never filled, so after the window re-docked Tiles off and on showed
five tiles and an empty cell. Popping out with the grid closed, or a reload
reconcile, already kept the count and refilled the cell.

_markDetached now removes the tile as `gone` (it left by itself), the same
as a deleted session or a refused socket, so the count stays and the next
activation fills the cell from the ranking.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-10-10 06:30:08 +02:00
parent 3a40dae1dd
commit 7b95856cc5
3 changed files with 29 additions and 3 deletions
+23
View File
@@ -475,6 +475,29 @@ describe('a session that went away by itself while the grid was open', () => {
expect(app._tileGrid.ids).not.toContain('s-e');
});
it('popped out while open (the pop-out button, or another tab announcing it): the count stays, the cell refills after re-dock', () => {
const app = six();
// What the real pop-out paths touch: detachSession and the window channel
// both mark through _markDetached; _redock clears it again.
app._detachOrphanStrikes = new Map();
app._detachWatchTimers = new Map();
app._redockGrace = new Map();
app.detachedWindows = new Map();
app._elemCache = {};
app._onWindowMessage({ type: 'detached', id: 's-e' });
expect(app.detachedSessions.has('s-e')).toBe(true);
expect(app._tileGrid.cells).toEqual(['s-a', 's-b', 's-c', 's-d', null, 's-f']);
// Not the user's hand: the count stays six, the same as a pop-out with the grid closed.
expect(stored()).toMatchObject({ ids: ['s-a', 's-b', 's-c', 's-d', null, 's-f'], count: 6 });
app._redock('s-e');
expect(app.detachedSessions.has('s-e')).toBe(false);
app.toggleTileGrid();
app.toggleTileGrid();
// The freed cell is filled from the ranking (all quiet here: tab order, s-other first).
expect(app._tileGrid.cells).toEqual(['s-a', 's-b', 's-c', 's-d', 's-other', 's-f']);
expect(stored().count).toBe(6);
});
it('removed by hand instead: the hole stays a hole', () => {
const app = six(['s-g']);
app.removeTile('s-d');