diff --git a/src/web/public/tile-grid.js b/src/web/public/tile-grid.js index fca591c2..35ab7224 100644 --- a/src/web/public/tile-grid.js +++ b/src/web/public/tile-grid.js @@ -3012,8 +3012,16 @@ Object.assign(CodemanApp.prototype, { this._lastTileDrag = null; const grid = this._tileGrid; if (!grid?.has(sessionId)) return false; - if (grid.ids.length > 1) return this.removeTile(sessionId, { focus: false }); - this.closeTileGrid({ keepStored: false, reselect: false }); + // One stored grid per browser, and the window that docked the tile has + // just written it: this window losing the tile is no change of the + // user's to remember over that one (nor to forget it). + this._tilePersistHold = true; + try { + if (grid.ids.length > 1) return this.removeTile(sessionId, { focus: false }); + this.closeTileGrid({ keepStored: true, reselect: false }); + } finally { + this._tilePersistHold = false; + } this.activeSessionId = null; try { localStorage.removeItem('codeman-active-session'); diff --git a/test/tile-grid-detach.test.ts b/test/tile-grid-detach.test.ts index fdc7d637..d8b6205c 100644 --- a/test/tile-grid-detach.test.ts +++ b/test/tile-grid-detach.test.ts @@ -42,6 +42,7 @@ import { FakeTile, documentAddEventListener, documentRemoveEventListener, + localStore, makeGridApp, pointHits, resetGridHarness, @@ -281,6 +282,40 @@ describe('another window docking the tile this one dragged', () => { expect(app.terminal.clear).toHaveBeenCalled(); }); + // One stored grid per browser: the window that docked the tile wrote it a + // moment before this one hears the claim. Losing the tile here is not a + // layout of this window's to store over that one, nor a reason to forget it. + const docked = JSON.stringify({ + v: 1, + open: true, + ids: ['s-x', 's-b'], + count: 2, + focused: 's-b', + zoomed: null, + colFr: [1, 1], + rowFr: [1], + }); + + it("the stored grid stays the docking window's when this one keeps other tiles", () => { + const app = openGrid(FOUR); + startDrag('s-b'); + endDrag('s-b', { clientX: -50, clientY: 300 }, 'move'); + localStore.set('codeman:tile-grid', docked); + app._onWindowMessage({ type: 'tile-adopted', id: 's-b', by: 'c-there' }); + expect(app._tileGrid.ids).toEqual(['s-a', 's-c', 's-d']); + expect(localStore.get('codeman:tile-grid')).toBe(docked); + }); + + it("the stored grid stays the docking window's when this one's last tile leaves", () => { + const app = openGrid(['s-b']); + startDrag('s-b'); + endDrag('s-b', { clientX: -50, clientY: 300 }, 'move'); + localStore.set('codeman:tile-grid', docked); + app._onWindowMessage({ type: 'tile-adopted', id: 's-b', by: 'c-there' }); + expect(app._tileGrid.open).toBe(false); + expect(localStore.get('codeman:tile-grid')).toBe(docked); + }); + it('a claim for a tile this window did not drag, or one it dragged long ago, is ignored', () => { const app = openGrid(FOUR); app._onWindowMessage({ type: 'tile-adopted', id: 's-b', by: 'c-there' });