From 7acad444ed1abf1ae4a792a666cb2e6558ed73cc Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Fri, 7 Aug 2026 22:35:09 -0700 Subject: [PATCH] Re-pin entry to deferred tab results that rank higher Track whether selection auto-follows the top-ranked result or was manually positioned. Re-pin entry to tabs when they rank higher, but preserve manual selection. --- .../TabBarCreateEntry.tab-results.test.tsx | 25 ++++++++++++++++++- .../components/tab-bar/TabBarCreateEntry.tsx | 11 ++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/tab-bar/TabBarCreateEntry.tab-results.test.tsx b/src/renderer/src/components/tab-bar/TabBarCreateEntry.tab-results.test.tsx index 6ab3f625f2d..b793566182d 100644 --- a/src/renderer/src/components/tab-bar/TabBarCreateEntry.tab-results.test.tsx +++ b/src/renderer/src/components/tab-bar/TabBarCreateEntry.tab-results.test.tsx @@ -233,7 +233,28 @@ describe('TabBarCreateEntry tab results', () => { expect(rows[3]).toContain('Open file') }) - it('keeps Enter on the row the user saw when a tab row arrives a render later', () => { + it('re-pins Enter to a deferred tab row that ranks above the auto-selected file', () => { + entryOptionsMock.options = [newFileOption] + tabSearchMock.resultsByQuery['add tab'] = [terminalResult()] + tabSearchMock.hold = '' + const onOpenEntry = vi.fn().mockResolvedValue(undefined) + const onDidOpenEntry = vi.fn() + renderEntry({ onDidOpenEntry, onOpenEntry }) + + setQuery('add tab') + expect(rowTexts()).toHaveLength(1) + + tabSearchMock.hold = null + renderEntry({ onDidOpenEntry, onOpenEntry }) + expect(rowTexts()[0]).toContain('Switch to tab') + submitForm() + + expect(activationMocks.workspace).toHaveBeenCalledTimes(1) + expect(onDidOpenEntry).toHaveBeenCalledTimes(1) + expect(onOpenEntry).not.toHaveBeenCalled() + }) + + it('keeps a manually chosen row when a tab row arrives a render later', () => { entryOptionsMock.options = [newFileOption] tabSearchMock.resultsByQuery['add tab'] = [terminalResult()] tabSearchMock.hold = '' @@ -242,6 +263,8 @@ describe('TabBarCreateEntry tab results', () => { setQuery('add tab') expect(rowTexts()).toHaveLength(1) + // Arrow on the only row marks selection as user-owned, not auto-default. + pressKey(queryInput(), 'ArrowDown') tabSearchMock.hold = null renderEntry({ onOpenEntry }) diff --git a/src/renderer/src/components/tab-bar/TabBarCreateEntry.tsx b/src/renderer/src/components/tab-bar/TabBarCreateEntry.tsx index 750924bf4fb..bab26355034 100644 --- a/src/renderer/src/components/tab-bar/TabBarCreateEntry.tsx +++ b/src/renderer/src/components/tab-bar/TabBarCreateEntry.tsx @@ -84,6 +84,9 @@ export default function TabBarCreateEntry({ const [switchError, setSwitchError] = useState(null) const [selectedOptionId, setSelectedOptionId] = useState(null) const [selectedOptionQuery, setSelectedOptionQuery] = useState(query) + // True while selection still tracks ranking (no arrow/click yet). Deferred tab + // rows can prepend a better match; re-pin only while this stays true. + const [selectionIsAutoDefault, setSelectionIsAutoDefault] = useState(true) const [lastMenuOpen, setLastMenuOpen] = useState(menuOpen) const inputRef = useRef(null) const imeEnter = useImeEnterGestureOwnership() @@ -171,6 +174,7 @@ export default function TabBarCreateEntry({ setError(null) setSwitchError(null) setSelectedOptionId(null) + setSelectionIsAutoDefault(true) } } @@ -198,10 +202,16 @@ export default function TabBarCreateEntry({ if (selectedOptionQuery !== query) { setSelectedOptionQuery(query) setSelectedOptionId(topOptionId) + setSelectionIsAutoDefault(true) } else if (selectedOptionId === null && topOptionId !== null) { // Why pin the top row by id: the tab search defers the query, so tab rows // arrive a render later and would otherwise slide under an index-kept highlight. setSelectedOptionId(topOptionId) + setSelectionIsAutoDefault(true) + } else if (selectionIsAutoDefault && topOptionId !== null && selectedOptionId !== topOptionId) { + // Files paint before deferred tab search; follow the new top while the user + // has not moved selection themselves. + setSelectedOptionId(topOptionId) } const selectedOptionIndex = selectedOptionId ? activeOptions.findIndex((option) => getActiveOptionId(option) === selectedOptionId) @@ -294,6 +304,7 @@ export default function TabBarCreateEntry({ const delta = event.key === 'ArrowDown' ? 1 : -1 const nextIndex = (activeSelectedIndex + delta + activeOptions.length) % activeOptions.length + setSelectionIsAutoDefault(false) setSelectedOptionId(getActiveOptionId(activeOptions[nextIndex])) return }