From 2caa016fe1f3d8533e38e8d92fda73ce5c93082d Mon Sep 17 00:00:00 2001 From: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Date: Sat, 27 Jun 2026 17:13:09 +0800 Subject: [PATCH] fix(app): batch new session tab navigation (#34196) --- packages/app/src/components/titlebar.tsx | 4 +++- packages/app/src/context/tabs.tsx | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index 3e06787cea..b826001387 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -99,6 +99,8 @@ export function Titlebar(props: { update?: TitlebarUpdate }) { const path = () => `${location.pathname}${location.search}${location.hash}` const creating = createMemo(() => { + const route = layout.route() + if (route.type === "draft" || route.type === "dir-new-sesssion") return true if (!params.dir) return false if (params.id) return false const parts = location.pathname.replace(/\/+$/, "").split("/") @@ -466,7 +468,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) { }} onReorder={(keys) => tabsStoreActions.reorder(keys)} /> - + , prompt?: string) { const draftID = uuid() - setStore( - produce((tabs) => { - tabs.push({ type: "draft", draftID, ...draft }) - }), - ) - navigate(prompt ? `${draftHref(draftID)}&prompt=${encodeURIComponent(prompt)}` : draftHref(draftID)) + void startTransition(() => { + setStore( + produce((tabs) => { + tabs.push({ type: "draft", draftID, ...draft }) + }), + ) + navigate(prompt ? `${draftHref(draftID)}&prompt=${encodeURIComponent(prompt)}` : draftHref(draftID)) + }) }, updateDraft(draftID: string, draft: Partial>) { setStore( @@ -177,13 +179,11 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({ ) }, promoteDraft(draftID: string, session: Omit) { - // We're viewing this draft when /new-session?draftId=… points at it. Promoting - // replaces the draft tab with a session tab, so the draft route would stop resolving - // and fall back home. Navigate to the new session first so we leave /new-session - // before the draft is removed from the store. + // Keep the replacement and navigation atomic so /new-session never renders + // after its backing draft tab has been removed from the store. const active = location.pathname === "/new-session" && location.query.draftId === draftID const next = { type: "session" as const, ...session } - startTransition(() => { + void startTransition(() => { setStore( produce((tabs) => { const index = tabs.findIndex((tab) => tab.type === "draft" && tab.draftID === draftID)