refactor: remove tab bar — single note open at a time
Replace the multi-tab model with single-note navigation. One note is open at a time; clicking any note (sidebar, wikilink, relationship) replaces the current note in the editor. Back/Forward history still works via Cmd+[/]. Removed: TabBar component, useClosedTabHistory, tab reorder/close/ reopen logic, Cmd+W/Cmd+Shift+T shortcuts, Close Tab and Reopen Closed Tab menu items, tabLayout utility, and all related tests. Simplified: useTabManagement (single-note), useAppNavigation (no tab lookup), useKeyboardNavigation (note nav only), useNoteCreation (no close-tab cleanup), useDeleteActions (deselect instead of close tab), Editor (no TabBar render), Rust menu (removed 2 menu items). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,13 +34,7 @@ const makeEntry = (overrides: Partial<VaultEntry> = {}): VaultEntry => ({
|
||||
...overrides,
|
||||
})
|
||||
|
||||
interface Tab {
|
||||
entry: VaultEntry
|
||||
content: string
|
||||
}
|
||||
|
||||
describe('useKeyboardNavigation', () => {
|
||||
const onSwitchTab = vi.fn()
|
||||
const onReplaceActiveTab = vi.fn()
|
||||
const onSelectNote = vi.fn()
|
||||
|
||||
@@ -50,12 +44,6 @@ describe('useKeyboardNavigation', () => {
|
||||
makeEntry({ path: '/vault/c.md', title: 'C', modifiedAt: 1700000001 }),
|
||||
]
|
||||
|
||||
const tabs: Tab[] = [
|
||||
{ entry: entries[0], content: '# A' },
|
||||
{ entry: entries[1], content: '# B' },
|
||||
{ entry: entries[2], content: '# C' },
|
||||
]
|
||||
|
||||
const selection: SidebarSelection = { kind: 'filter', filter: 'all' }
|
||||
let addedListeners: { type: string; handler: EventListenerOrEventListenerObject }[] = []
|
||||
|
||||
@@ -81,70 +69,19 @@ describe('useKeyboardNavigation', () => {
|
||||
it('registers keydown listener on mount', () => {
|
||||
renderHook(() =>
|
||||
useKeyboardNavigation({
|
||||
tabs, activeTabPath: '/vault/a.md', entries, selection,
|
||||
onSwitchTab, onReplaceActiveTab, onSelectNote,
|
||||
activeTabPath: '/vault/a.md', entries, selection,
|
||||
onReplaceActiveTab, onSelectNote,
|
||||
})
|
||||
)
|
||||
|
||||
expect(addedListeners.some(l => l.type === 'keydown')).toBe(true)
|
||||
})
|
||||
|
||||
it('switches to next tab on Cmd+Shift+ArrowRight (browser mode)', () => {
|
||||
renderHook(() =>
|
||||
useKeyboardNavigation({
|
||||
tabs, activeTabPath: '/vault/a.md', entries, selection,
|
||||
onSwitchTab, onReplaceActiveTab, onSelectNote,
|
||||
})
|
||||
)
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(new KeyboardEvent('keydown', {
|
||||
key: 'ArrowRight', metaKey: true, shiftKey: true, bubbles: true,
|
||||
}))
|
||||
})
|
||||
|
||||
expect(onSwitchTab).toHaveBeenCalledWith('/vault/b.md')
|
||||
})
|
||||
|
||||
it('switches to previous tab on Cmd+Shift+ArrowLeft (browser mode)', () => {
|
||||
renderHook(() =>
|
||||
useKeyboardNavigation({
|
||||
tabs, activeTabPath: '/vault/b.md', entries, selection,
|
||||
onSwitchTab, onReplaceActiveTab, onSelectNote,
|
||||
})
|
||||
)
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(new KeyboardEvent('keydown', {
|
||||
key: 'ArrowLeft', metaKey: true, shiftKey: true, bubbles: true,
|
||||
}))
|
||||
})
|
||||
|
||||
expect(onSwitchTab).toHaveBeenCalledWith('/vault/a.md')
|
||||
})
|
||||
|
||||
it('wraps around when navigating past last tab', () => {
|
||||
renderHook(() =>
|
||||
useKeyboardNavigation({
|
||||
tabs, activeTabPath: '/vault/c.md', entries, selection,
|
||||
onSwitchTab, onReplaceActiveTab, onSelectNote,
|
||||
})
|
||||
)
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(new KeyboardEvent('keydown', {
|
||||
key: 'ArrowRight', metaKey: true, shiftKey: true, bubbles: true,
|
||||
}))
|
||||
})
|
||||
|
||||
expect(onSwitchTab).toHaveBeenCalledWith('/vault/a.md')
|
||||
})
|
||||
|
||||
it('navigates to next note on Cmd+Alt+ArrowDown', () => {
|
||||
renderHook(() =>
|
||||
useKeyboardNavigation({
|
||||
tabs, activeTabPath: '/vault/a.md', entries, selection,
|
||||
onSwitchTab, onReplaceActiveTab, onSelectNote,
|
||||
activeTabPath: '/vault/a.md', entries, selection,
|
||||
onReplaceActiveTab, onSelectNote,
|
||||
})
|
||||
)
|
||||
|
||||
@@ -160,8 +97,8 @@ describe('useKeyboardNavigation', () => {
|
||||
it('navigates to previous note on Cmd+Alt+ArrowUp', () => {
|
||||
renderHook(() =>
|
||||
useKeyboardNavigation({
|
||||
tabs, activeTabPath: '/vault/b.md', entries, selection,
|
||||
onSwitchTab, onReplaceActiveTab, onSelectNote,
|
||||
activeTabPath: '/vault/b.md', entries, selection,
|
||||
onReplaceActiveTab, onSelectNote,
|
||||
})
|
||||
)
|
||||
|
||||
@@ -177,8 +114,8 @@ describe('useKeyboardNavigation', () => {
|
||||
it('selects first note when no active tab', () => {
|
||||
renderHook(() =>
|
||||
useKeyboardNavigation({
|
||||
tabs: [], activeTabPath: null, entries, selection,
|
||||
onSwitchTab, onReplaceActiveTab, onSelectNote,
|
||||
activeTabPath: null, entries, selection,
|
||||
onReplaceActiveTab, onSelectNote,
|
||||
})
|
||||
)
|
||||
|
||||
@@ -194,8 +131,8 @@ describe('useKeyboardNavigation', () => {
|
||||
it('does nothing without modifier keys', () => {
|
||||
renderHook(() =>
|
||||
useKeyboardNavigation({
|
||||
tabs, activeTabPath: '/vault/a.md', entries, selection,
|
||||
onSwitchTab, onReplaceActiveTab, onSelectNote,
|
||||
activeTabPath: '/vault/a.md', entries, selection,
|
||||
onReplaceActiveTab, onSelectNote,
|
||||
})
|
||||
)
|
||||
|
||||
@@ -205,25 +142,7 @@ describe('useKeyboardNavigation', () => {
|
||||
}))
|
||||
})
|
||||
|
||||
expect(onSwitchTab).not.toHaveBeenCalled()
|
||||
expect(onReplaceActiveTab).not.toHaveBeenCalled()
|
||||
expect(onSelectNote).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does nothing with empty tabs for tab navigation', () => {
|
||||
renderHook(() =>
|
||||
useKeyboardNavigation({
|
||||
tabs: [], activeTabPath: null, entries, selection,
|
||||
onSwitchTab, onReplaceActiveTab, onSelectNote,
|
||||
})
|
||||
)
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(new KeyboardEvent('keydown', {
|
||||
key: 'ArrowRight', metaKey: true, shiftKey: true, bubbles: true,
|
||||
}))
|
||||
})
|
||||
|
||||
expect(onSwitchTab).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user