From 5a0006f7efabb03888573a2029f1d0a30b614422 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 2 Mar 2026 13:47:40 -0800 Subject: [PATCH] Guard all commitRename cleanup paths against concurrent renames All exit paths in commitRename now use a thread-guarded finishRename helper that only clears renamingThreadId when it still matches the original threadId. Prevents a stale onBlur from cancelling a newly started rename on a different thread. Co-Authored-By: Claude Opus 4.6 --- apps/web/src/components/Sidebar.tsx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 21c9005057c6fb424d3a45847af2d2a11154a5a8..a00bbb1f1d290342aadb0e7d4bacc3e4f08bfbb3 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -514,19 +514,27 @@ export default function Sidebar() { const commitRename = useCallback( async (threadId: ThreadId, newTitle: string, originalTitle: string) => { + const finishRename = () => { + setRenamingThreadId((current) => { + if (current !== threadId) return current; + renamingInputRef.current = null; + return null; + }); + }; + const trimmed = newTitle.trim(); if (trimmed.length === 0) { toastManager.add({ type: "warning", title: "Thread title cannot be empty" }); - cancelRename(); + finishRename(); return; } if (trimmed === originalTitle) { - cancelRename(); + finishRename(); return; } const api = readNativeApi(); if (!api) { - cancelRename(); + finishRename(); return; } try { @@ -543,13 +551,9 @@ export default function Sidebar() { description: error instanceof Error ? error.message : "An error occurred.", }); } - setRenamingThreadId((current) => { - if (current !== threadId) return current; - renamingInputRef.current = null; - return null; - }); + finishRename(); }, - [cancelRename], + [], ); const handleThreadContextMenu = useCallback( -- 2.54.0