diff --git a/src/web/public/tile-grid.js b/src/web/public/tile-grid.js index 35ab7224..e78193f0 100644 --- a/src/web/public/tile-grid.js +++ b/src/web/public/tile-grid.js @@ -3004,7 +3004,9 @@ Object.assign(CodemanApp.prototype, { */ _onTileAdoptedElsewhere(sessionId) { const recent = this._lastTileDrag; - const dragging = this._tileTearOff?.id === sessionId; + // A header drag counts even without a tear-off (Detach Tiles off here): the + // other window can take the tile before this drag ends. + const dragging = this._tileTearOff?.id === sessionId || this._draggedTileId === sessionId; const pending = this._tileTearOffPending?.id === sessionId; const justEnded = recent?.id === sessionId && Date.now() - recent.at < TILE_ADOPT_WINDOW_MS; if (!dragging && !pending && !justEnded) return false; diff --git a/test/tile-grid-detach.test.ts b/test/tile-grid-detach.test.ts index d8b6205c..d3b983c9 100644 --- a/test/tile-grid-detach.test.ts +++ b/test/tile-grid-detach.test.ts @@ -316,6 +316,16 @@ describe('another window docking the tile this one dragged', () => { expect(localStore.get('codeman:tile-grid')).toBe(docked); }); + it('a claim that lands mid-drag lets the tile go, with no tear-off here (Detach Tiles off)', () => { + // The tile's id rides every header drag, so a window with Detach Tiles on can + // dock it while this one has the setting off; its claim can beat the dragend. + const app = openGrid(FOUR, { detach: false }); + startDrag('s-b'); + expect(app._tileTearOff ?? null).toBeNull(); + app._onWindowMessage({ type: 'tile-adopted', id: 's-b', by: 'c-there' }); + expect(app._tileGrid.ids).toEqual(['s-a', 's-c', 's-d']); + }); + 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' });