diff --git a/src/renderer/src/store/slices/editor.test.ts b/src/renderer/src/store/slices/editor.test.ts index 8819a9339a7..09305121d73 100644 --- a/src/renderer/src/store/slices/editor.test.ts +++ b/src/renderer/src/store/slices/editor.test.ts @@ -1772,6 +1772,49 @@ describe('createEditorSlice recently closed editor tabs', () => { // made close-all cubic — 4x the tabs cost ~64x the scans and froze the renderer for seconds. expect(countEntityIdReads(80)).toBeLessThanOrEqual(countEntityIdReads(20) * 8) }) + + it('reactivates the live tab when a stale reopen id targets an already-open file', () => { + const store = createEditorTabsStore() + store.setState({ + worktreesByRepo: { + 'repo-1': [ + { id: 'wt-1', repoId: 'repo-1', path: '/repo' }, + { id: 'wt-2', repoId: 'repo-1', path: '/repo-2' } + ] + } + } as unknown as Partial) + const sharedPath = '/home/me/.zshrc' + const openShared = (worktreeId: string): string => + store.getState().openFile({ + filePath: sharedPath, + relativePath: '.zshrc', + worktreeId, + language: 'shell', + mode: 'edit' + }) + + // Bare path id: nothing else owns this path yet. + const staleWt1Id = openShared('wt-1') + expect(staleWt1Id).toBe(sharedPath) + store.getState().closeFile(staleWt1Id) + + // wt-2 claims the bare id, so wt-1's reopen gets a namespaced id instead. + const wt2Id = openShared('wt-2') + expect(wt2Id).toBe(sharedPath) + const liveWt1Id = openShared('wt-1') + expect(liveWt1Id).toBe(ownedEditorFileId(sharedPath, 'wt-1', null)) + store.getState().closeFile(wt2Id) + + expect(store.getState().reopenClosedEditorTab('wt-1')).toBe(true) + + expect(store.getState().openFiles.map((file) => file.id)).toEqual([liveWt1Id]) + expect(store.getState().activeFileId).toBe(liveWt1Id) + expect(store.getState().activeFileIdByWorktree['wt-1']).toBe(liveWt1Id) + expect( + (store.getState().unifiedTabsByWorktree['wt-1'] ?? []).map((tab) => tab.entityId) + ).toEqual([liveWt1Id]) + expect(store.getState().tabBarOrderByWorktree['wt-1']).toEqual([liveWt1Id]) + }) }) describe('createEditorSlice markdown view state', () => { diff --git a/src/renderer/src/store/slices/editor.ts b/src/renderer/src/store/slices/editor.ts index a0dee8ce790..d2d6ed8b2c1 100644 --- a/src/renderer/src/store/slices/editor.ts +++ b/src/renderer/src/store/slices/editor.ts @@ -1678,8 +1678,10 @@ export const createEditorSlice: StateCreator = (s matchesEditorMode(f, reusableOpenFileModes) && isSameEditorOwner(f, worktreeId, runtimeEnvironmentId) ) - const id = - options?.reopenId && !s.openFiles.some((candidate) => candidate.id === options.reopenId) + // Why: a snapshot's reopenId can be a stale shape — the same path is bare in whichever worktree opened it first and namespaced elsewhere — so honoring it while this owner's tab is already open would strand activeFileId and the unified tab on an id no OpenFile has. + const id = existing + ? existing.id + : options?.reopenId && !s.openFiles.some((candidate) => candidate.id === options.reopenId) ? options.reopenId : resolveEditorFileIdForOwner( s,