fix: rename closeTab/setActiveTab to closeEditorTab/setActiveEditorTab

Resolve naming collision between editorSlice and tabSlice.
Both slices defined closeTab and setActiveTab, and since editorSlice
was spread last in the store composition, it silently overwrote the
tabSlice methods, breaking tab management.
This commit is contained in:
iliya 2026-02-28 23:53:10 +02:00
parent 30c78b01e2
commit 8e6fb13e5f
6 changed files with 48 additions and 48 deletions

View file

@ -30,7 +30,7 @@ export const EditorTabBar = ({
const tabs = useStore((s) => s.editorOpenTabs);
const activeTabId = useStore((s) => s.editorActiveTabId);
const modifiedFiles = useStore((s) => s.editorModifiedFiles);
const setActiveTab = useStore((s) => s.setActiveTab);
const setActiveEditorTab = useStore((s) => s.setActiveEditorTab);
if (tabs.length === 0) return null;
@ -45,7 +45,7 @@ export const EditorTabBar = ({
tab={tab}
isActive={tab.id === activeTabId}
isModified={!!modifiedFiles[tab.filePath]}
onActivate={() => setActiveTab(tab.id)}
onActivate={() => setActiveEditorTab(tab.id)}
onClose={() => onRequestCloseTab(tab.id)}
/>
))}

View file

@ -66,7 +66,7 @@ export const ProjectEditorOverlay = ({
const openEditor = useStore((s) => s.openEditor);
const closeEditor = useStore((s) => s.closeEditor);
const openFile = useStore((s) => s.openFile);
const closeTab = useStore((s) => s.closeTab);
const closeEditorTab = useStore((s) => s.closeEditorTab);
const saveFile = useStore((s) => s.saveFile);
const activeTabId = useStore((s) => s.editorActiveTabId);
const openTabs = useStore((s) => s.editorOpenTabs);
@ -284,10 +284,10 @@ export const ProjectEditorOverlay = ({
if (modifiedFiles[tabId]) {
setConfirmCloseTabId(tabId);
} else {
closeTab(tabId);
closeEditorTab(tabId);
}
},
[modifiedFiles, closeTab]
[modifiedFiles, closeEditorTab]
);
// Listen for editor-close-tab custom events from keyboard shortcut hook
@ -303,15 +303,15 @@ export const ProjectEditorOverlay = ({
const handleSaveAndCloseTab = useCallback(async () => {
if (!confirmCloseTabId) return;
await saveFile(confirmCloseTabId);
closeTab(confirmCloseTabId);
closeEditorTab(confirmCloseTabId);
setConfirmCloseTabId(null);
}, [confirmCloseTabId, saveFile, closeTab]);
}, [confirmCloseTabId, saveFile, closeEditorTab]);
const handleDiscardAndCloseTab = useCallback(() => {
if (!confirmCloseTabId) return;
closeTab(confirmCloseTabId);
closeEditorTab(confirmCloseTabId);
setConfirmCloseTabId(null);
}, [confirmCloseTabId, closeTab]);
}, [confirmCloseTabId, closeEditorTab]);
const handleCancelCloseTab = useCallback(() => {
setConfirmCloseTabId(null);
@ -579,7 +579,7 @@ export const ProjectEditorOverlay = ({
</span>
{externalChanges[activeTabId] === 'delete' ? (
<button
onClick={() => closeTab(activeTabId)}
onClick={() => closeEditorTab(activeTabId)}
className="ml-auto rounded px-2 py-0.5 text-text-muted transition-colors hover:bg-surface-raised hover:text-text"
>
Close tab

View file

@ -28,7 +28,7 @@ interface UseEditorKeyboardShortcutsOptions {
export interface EditorKeyHandlerDeps {
activeTabId: string | null;
openTabs: EditorFileTab[];
setActiveTab: (id: string) => void;
setActiveEditorTab: (id: string) => void;
saveFile: (tabId: string) => Promise<void>;
saveAllFiles: () => Promise<void>;
hasUnsavedChanges: () => boolean;
@ -127,9 +127,9 @@ export function createEditorKeyHandler(deps: EditorKeyHandlerDeps): (e: Keyboard
e.stopPropagation();
const idx = deps.openTabs.findIndex((t) => t.id === deps.activeTabId);
if (idx !== -1 && idx < deps.openTabs.length - 1) {
deps.setActiveTab(deps.openTabs[idx + 1].id);
deps.setActiveEditorTab(deps.openTabs[idx + 1].id);
} else if (deps.openTabs.length > 0) {
deps.setActiveTab(deps.openTabs[0].id); // wrap
deps.setActiveEditorTab(deps.openTabs[0].id); // wrap
}
return;
}
@ -140,9 +140,9 @@ export function createEditorKeyHandler(deps: EditorKeyHandlerDeps): (e: Keyboard
e.stopPropagation();
const idx = deps.openTabs.findIndex((t) => t.id === deps.activeTabId);
if (idx > 0) {
deps.setActiveTab(deps.openTabs[idx - 1].id);
deps.setActiveEditorTab(deps.openTabs[idx - 1].id);
} else if (deps.openTabs.length > 0) {
deps.setActiveTab(deps.openTabs[deps.openTabs.length - 1].id); // wrap
deps.setActiveEditorTab(deps.openTabs[deps.openTabs.length - 1].id); // wrap
}
return;
}
@ -154,10 +154,10 @@ export function createEditorKeyHandler(deps: EditorKeyHandlerDeps): (e: Keyboard
const idx = deps.openTabs.findIndex((t) => t.id === deps.activeTabId);
if (e.shiftKey) {
const prev = idx > 0 ? idx - 1 : deps.openTabs.length - 1;
if (deps.openTabs[prev]) deps.setActiveTab(deps.openTabs[prev].id);
if (deps.openTabs[prev]) deps.setActiveEditorTab(deps.openTabs[prev].id);
} else {
const next = idx < deps.openTabs.length - 1 ? idx + 1 : 0;
if (deps.openTabs[next]) deps.setActiveTab(deps.openTabs[next].id);
if (deps.openTabs[next]) deps.setActiveEditorTab(deps.openTabs[next].id);
}
}
@ -177,7 +177,7 @@ export function useEditorKeyboardShortcuts({
}: UseEditorKeyboardShortcutsOptions): void {
const openTabs = useStore((s) => s.editorOpenTabs);
const activeTabId = useStore((s) => s.editorActiveTabId);
const setActiveTab = useStore((s) => s.setActiveTab);
const setActiveEditorTab = useStore((s) => s.setActiveEditorTab);
const saveFile = useStore((s) => s.saveFile);
const saveAllFiles = useStore((s) => s.saveAllFiles);
const hasUnsavedChanges = useStore((s) => s.hasUnsavedChanges);
@ -187,7 +187,7 @@ export function useEditorKeyboardShortcuts({
const handler = createEditorKeyHandler({
activeTabId,
openTabs,
setActiveTab,
setActiveEditorTab,
saveFile,
saveAllFiles,
hasUnsavedChanges,
@ -201,7 +201,7 @@ export function useEditorKeyboardShortcuts({
[
activeTabId,
openTabs,
setActiveTab,
setActiveEditorTab,
saveFile,
saveAllFiles,
hasUnsavedChanges,

View file

@ -77,8 +77,8 @@ export interface EditorSlice {
editorActiveTabId: string | null;
openFile: (filePath: string) => void;
closeTab: (tabId: string) => void;
setActiveTab: (tabId: string) => void;
closeEditorTab: (tabId: string) => void;
setActiveEditorTab: (tabId: string) => void;
// ═══════════════════════════════════════════════════════
// Group 3: Content + Save
@ -343,7 +343,7 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
});
},
closeTab: (tabId: string) => {
closeEditorTab: (tabId: string) => {
const { editorOpenTabs, editorActiveTabId, editorModifiedFiles, editorSaveError } = get();
const filtered = editorOpenTabs.filter((t) => t.id !== tabId);
@ -383,7 +383,7 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
});
},
setActiveTab: (tabId: string) => {
setActiveEditorTab: (tabId: string) => {
set({ editorActiveTabId: tabId });
},
@ -581,7 +581,7 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
(t) => t.filePath === filePath || t.filePath.startsWith(filePath + '/')
);
for (const tab of tabsToClose) {
get().closeTab(tab.id);
get().closeEditorTab(tab.id);
}
// Refresh parent directory

View file

@ -44,7 +44,7 @@ function createMockDeps(overrides: Partial<EditorKeyHandlerDeps> = {}): EditorKe
language: 'typescript',
},
] as EditorFileTab[],
setActiveTab: vi.fn(),
setActiveEditorTab: vi.fn(),
saveFile: vi.fn().mockResolvedValue(undefined),
saveAllFiles: vi.fn().mockResolvedValue(undefined),
hasUnsavedChanges: vi.fn().mockReturnValue(false),
@ -207,27 +207,27 @@ describe('createEditorKeyHandler', () => {
it('moves to next tab with Cmd+Shift+]', () => {
const handler = createEditorKeyHandler(deps);
handler(createKeyEvent(']', { shiftKey: true }));
expect(deps.setActiveTab).toHaveBeenCalledWith('/project/file2.ts');
expect(deps.setActiveEditorTab).toHaveBeenCalledWith('/project/file2.ts');
});
it('wraps to first tab when on last', () => {
deps = createMockDeps({ activeTabId: '/project/file3.ts' });
const handler = createEditorKeyHandler(deps);
handler(createKeyEvent(']', { shiftKey: true }));
expect(deps.setActiveTab).toHaveBeenCalledWith('/project/file1.ts');
expect(deps.setActiveEditorTab).toHaveBeenCalledWith('/project/file1.ts');
});
it('moves to previous tab with Cmd+Shift+[', () => {
deps = createMockDeps({ activeTabId: '/project/file2.ts' });
const handler = createEditorKeyHandler(deps);
handler(createKeyEvent('[', { shiftKey: true }));
expect(deps.setActiveTab).toHaveBeenCalledWith('/project/file1.ts');
expect(deps.setActiveEditorTab).toHaveBeenCalledWith('/project/file1.ts');
});
it('wraps to last tab when on first with Cmd+Shift+[', () => {
const handler = createEditorKeyHandler(deps);
handler(createKeyEvent('[', { shiftKey: true }));
expect(deps.setActiveTab).toHaveBeenCalledWith('/project/file3.ts');
expect(deps.setActiveEditorTab).toHaveBeenCalledWith('/project/file3.ts');
});
});
@ -235,27 +235,27 @@ describe('createEditorKeyHandler', () => {
it('moves to next tab', () => {
const handler = createEditorKeyHandler(deps);
handler(createKeyEvent('Tab', { metaKey: false, ctrlKey: true }));
expect(deps.setActiveTab).toHaveBeenCalledWith('/project/file2.ts');
expect(deps.setActiveEditorTab).toHaveBeenCalledWith('/project/file2.ts');
});
it('moves to previous tab with Shift', () => {
deps = createMockDeps({ activeTabId: '/project/file2.ts' });
const handler = createEditorKeyHandler(deps);
handler(createKeyEvent('Tab', { metaKey: false, ctrlKey: true, shiftKey: true }));
expect(deps.setActiveTab).toHaveBeenCalledWith('/project/file1.ts');
expect(deps.setActiveEditorTab).toHaveBeenCalledWith('/project/file1.ts');
});
it('wraps forward on last tab', () => {
deps = createMockDeps({ activeTabId: '/project/file3.ts' });
const handler = createEditorKeyHandler(deps);
handler(createKeyEvent('Tab', { metaKey: false, ctrlKey: true }));
expect(deps.setActiveTab).toHaveBeenCalledWith('/project/file1.ts');
expect(deps.setActiveEditorTab).toHaveBeenCalledWith('/project/file1.ts');
});
it('wraps backward on first tab', () => {
const handler = createEditorKeyHandler(deps);
handler(createKeyEvent('Tab', { metaKey: false, ctrlKey: true, shiftKey: true }));
expect(deps.setActiveTab).toHaveBeenCalledWith('/project/file3.ts');
expect(deps.setActiveEditorTab).toHaveBeenCalledWith('/project/file3.ts');
});
});
@ -264,7 +264,7 @@ describe('createEditorKeyHandler', () => {
deps = createMockDeps({ openTabs: [], activeTabId: null });
const handler = createEditorKeyHandler(deps);
handler(createKeyEvent(']', { shiftKey: true }));
expect(deps.setActiveTab).not.toHaveBeenCalled();
expect(deps.setActiveEditorTab).not.toHaveBeenCalled();
});
it('stopPropagation is called on handled shortcuts', () => {

View file

@ -308,14 +308,14 @@ describe('editorSlice', () => {
});
});
describe('closeTab', () => {
describe('closeEditorTab', () => {
it('removes tab and activates adjacent', () => {
store.getState().openFile('/project/a.ts');
store.getState().openFile('/project/b.ts');
store.getState().openFile('/project/c.ts');
// Active is c.ts, close it
store.getState().closeTab('/project/c.ts');
store.getState().closeEditorTab('/project/c.ts');
const state = store.getState();
expect(state.editorOpenTabs).toHaveLength(2);
@ -325,16 +325,16 @@ describe('editorSlice', () => {
it('activates first remaining tab when first is closed', () => {
store.getState().openFile('/project/a.ts');
store.getState().openFile('/project/b.ts');
store.getState().setActiveTab('/project/a.ts');
store.getState().setActiveEditorTab('/project/a.ts');
store.getState().closeTab('/project/a.ts');
store.getState().closeEditorTab('/project/a.ts');
expect(store.getState().editorActiveTabId).toBe('/project/b.ts');
});
it('sets null when last tab is closed', () => {
store.getState().openFile('/project/a.ts');
store.getState().closeTab('/project/a.ts');
store.getState().closeEditorTab('/project/a.ts');
expect(store.getState().editorActiveTabId).toBeNull();
expect(store.getState().editorOpenTabs).toHaveLength(0);
@ -347,7 +347,7 @@ describe('editorSlice', () => {
editorSaveError: { '/project/a.ts': 'Save failed' },
});
store.getState().closeTab('/project/a.ts');
store.getState().closeEditorTab('/project/a.ts');
expect(store.getState().editorModifiedFiles).toEqual({});
expect(store.getState().editorSaveError).toEqual({});
@ -358,20 +358,20 @@ describe('editorSlice', () => {
store.getState().openFile('/project/b.ts');
// b.ts is active
store.getState().closeTab('/project/a.ts');
store.getState().closeEditorTab('/project/a.ts');
expect(store.getState().editorActiveTabId).toBe('/project/b.ts');
expect(store.getState().editorOpenTabs).toHaveLength(1);
});
});
describe('setActiveTab', () => {
describe('setActiveEditorTab', () => {
it('changes the active tab', () => {
store.getState().openFile('/project/a.ts');
store.getState().openFile('/project/b.ts');
// b.ts is active
store.getState().setActiveTab('/project/a.ts');
store.getState().setActiveEditorTab('/project/a.ts');
expect(store.getState().editorActiveTabId).toBe('/project/a.ts');
});
@ -582,7 +582,7 @@ describe('editorSlice', () => {
});
});
describe('closeTab clears disambiguation when names become unique', () => {
describe('closeEditorTab clears disambiguation when names become unique', () => {
it('removes label after closing duplicate', () => {
store.getState().openFile('/project/src/main/index.ts');
store.getState().openFile('/project/src/renderer/index.ts');
@ -591,7 +591,7 @@ describe('editorSlice', () => {
expect(store.getState().editorOpenTabs[0].disambiguatedLabel).toBe('(main)');
// Close one
store.getState().closeTab('/project/src/main/index.ts');
store.getState().closeEditorTab('/project/src/main/index.ts');
// Remaining should lose its label
const tabs = store.getState().editorOpenTabs;
@ -600,10 +600,10 @@ describe('editorSlice', () => {
});
});
describe('closeTab calls editorBridge.deleteState', () => {
describe('closeEditorTab calls editorBridge.deleteState', () => {
it('clears cached state for the closed tab', () => {
store.getState().openFile('/project/a.ts');
store.getState().closeTab('/project/a.ts');
store.getState().closeEditorTab('/project/a.ts');
expect(mockBridge.deleteState).toHaveBeenCalledWith('/project/a.ts');
});