From f4280a6a02876371af7c4363baebf17f8a1bb583 Mon Sep 17 00:00:00 2001 From: arumata Date: Thu, 3 Sep 2026 10:52:13 +0300 Subject: [PATCH] fix(details): clear a stale commit selection after refresh When the selected commit is invalidated by a rebase/amend/merge and the graph refreshes, the commit disappears from commitStore but the selection was left pointing at it, so the bottom panel stayed open and empty with no way to dismiss it. Prune stale selections (single and multi) right after a log/full refresh so the panel closes cleanly (issue #74). --- webview-ui/src/App.svelte | 20 ++++++++++++++++++++ webview-ui/src/__tests__/App.test.ts | 13 +++++++++++++ 2 files changed, 33 insertions(+) diff --git a/webview-ui/src/App.svelte b/webview-ui/src/App.svelte index c649320..ed635fa 100644 --- a/webview-ui/src/App.svelte +++ b/webview-ui/src/App.svelte @@ -82,6 +82,24 @@ import AmendModal from './components/modals/AmendModal.svelte'; document.documentElement.style.setProperty('--badge-bar-width', `${uiStore.badgeBarWidth}px`); }); + // After the graph is replaced (refresh, rebase, amend, filter change), the + // previously selected commit may no longer exist in the new list. Clear such + // stale selections so the bottom panel closes instead of staying open, empty, + // and without a way to dismiss it (issue #74). + function pruneInvalidSelection() { + if (uiStore.multiSelectArmed) { + const valid = uiStore.selectedCommitHashes.filter((h) => commitStore.getCommit(h)); + if (valid.length !== uiStore.selectedCommitHashes.length) { + if (valid.length === 0) uiStore.exitMultiSelect(); + else uiStore.selectedCommitHashes = valid; + } + return; + } + if (uiStore.selectedCommitHash && !commitStore.getCommit(uiStore.selectedCommitHash)) { + uiStore.selectCommit(null); + } + } + onMount(() => { uiStore.bottomPanelHeight = Math.round(window.innerHeight * BOTTOM_PANEL_DEFAULT_RATIO); @@ -92,6 +110,7 @@ import AmendModal from './components/modals/AmendModal.svelte'; if (msg.payload.remoteFilter !== undefined) remoteFilter = msg.payload.remoteFilter; if (msg.payload.branches !== undefined) branchFilter = msg.payload.branches; commitStore.setData(msg.payload); + pruneInvalidSelection(); break; case 'branchData': branchStore.setData(msg.payload); @@ -101,6 +120,7 @@ import AmendModal from './components/modals/AmendModal.svelte'; branchFilter = msg.payload.logData.branches ?? []; branchStore.setData(msg.payload.branchData); commitStore.setData(msg.payload.logData); + pruneInvalidSelection(); break; case 'setLocale': i18n.setLocale(msg.payload.locale); diff --git a/webview-ui/src/__tests__/App.test.ts b/webview-ui/src/__tests__/App.test.ts index 69b013b..57e5ff3 100644 --- a/webview-ui/src/__tests__/App.test.ts +++ b/webview-ui/src/__tests__/App.test.ts @@ -71,6 +71,19 @@ describe('App — message handling', () => { }); }); + it('clears a selection whose commit disappears from a refreshed log (issue #74)', async () => { + render(App); + uiStore.selectCommit('deadbeef'); + await waitFor(() => expect(uiStore.selectedCommitHash).toBe('deadbeef')); + + // A refresh that no longer contains the selected commit (e.g. after a + // rebase/amend) must clear the selection rather than leaving the bottom + // panel open and empty. + postMsg('logData', { commits: [], graph: [], hasMore: false, currentLimit: 100 }); + + await waitFor(() => expect(uiStore.selectedCommitHash).toBeNull()); + }); + it('branchData updates branchStore', async () => { render(App); postMsg('branchData', {