From 1708552a6ec1b831926bace4b5db0ff97b4875e2 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Sun, 20 Sep 2026 21:14:08 -0700 Subject: [PATCH] Handle blur race when file changes during rename When switching files mid-rename, React may deliver the old input's blur event after the new file renders, causing a stale rename commit. Mark the rename as cancelled when the active file changes, and add test coverage verifying stale blur events are ignored. --- .../src/components/editor/EditorPanelHeaderPath.test.tsx | 6 +++++- .../src/components/editor/editor-header-file-rename.ts | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/editor/EditorPanelHeaderPath.test.tsx b/src/renderer/src/components/editor/EditorPanelHeaderPath.test.tsx index 2c5cbe5f288..973ae4277d0 100644 --- a/src/renderer/src/components/editor/EditorPanelHeaderPath.test.tsx +++ b/src/renderer/src/components/editor/EditorPanelHeaderPath.test.tsx @@ -196,7 +196,8 @@ describe('EditorPanelHeaderPath inline rename', () => { const rerenderPath = renderPath(baseFile()) openRenameInput() - fireEvent.change(getRenameInput('Rename file notes.md'), { target: { value: 'renamed.md' } }) + const input = getRenameInput('Rename file notes.md') + fireEvent.change(input, { target: { value: 'renamed.md' } }) rerenderPath( baseFile({ id: '/repo/other.md', @@ -207,6 +208,9 @@ describe('EditorPanelHeaderPath inline rename', () => { expect(screen.queryByLabelText('Rename file notes.md')).toBeNull() expect(screen.queryByLabelText('Rename file other.md')).toBeNull() + + // React may deliver the removed input's blur after the active file render. + fireEvent.blur(input) expect(renameFileOnDiskMock).not.toHaveBeenCalled() }) diff --git a/src/renderer/src/components/editor/editor-header-file-rename.ts b/src/renderer/src/components/editor/editor-header-file-rename.ts index 328e2ba2c05..5dc67d80358 100644 --- a/src/renderer/src/components/editor/editor-header-file-rename.ts +++ b/src/renderer/src/components/editor/editor-header-file-rename.ts @@ -30,6 +30,7 @@ export function useEditorHeaderFileRename(activeFile: OpenFile): EditorHeaderFil // Why: the header renders one unkeyed path strip for every file, so a file // switch mid-rename would otherwise commit the typed name against the new path. if (renameFilePath !== activeFile.filePath) { + renameCancelledRef.current = true setRenameFilePath(activeFile.filePath) setIsRenaming(false) }