diff --git a/apps/web/src/components/ChatView.browser.tsx b/apps/web/src/components/ChatView.browser.tsx index a25018cac..9705b2f37 100644 --- a/apps/web/src/components/ChatView.browser.tsx +++ b/apps/web/src/components/ChatView.browser.tsx @@ -1481,6 +1481,31 @@ describe("ChatView timeline estimator parity (full app)", () => { } }); + it("shows a project quick-new button on desktop and creates a thread from it", async () => { + const mounted = await mountChatView({ + viewport: DEFAULT_VIEWPORT, + snapshot: createSnapshotForTargetUser({ + targetMessageId: "msg-user-project-quick-new-thread-test" as MessageId, + targetText: "project quick new thread test", + }), + }); + + try { + const quickNewThreadButton = page.getByTestId("project-quick-new-thread-button"); + await expect.element(quickNewThreadButton).toBeInTheDocument(); + + await quickNewThreadButton.click(); + + await waitForURL( + mounted.router, + (path) => UUID_ROUTE_RE.test(path), + "Route should have changed to a new draft thread UUID from the project quick-new button.", + ); + } finally { + await mounted.cleanup(); + } + }); + it("snapshots sticky codex settings into a new draft thread", async () => { useComposerDraftStore.setState({ stickyModel: "gpt-5.3-codex", diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 86f33a0f8..156fef454 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -608,6 +608,17 @@ export default function Sidebar() { const canAddProject = newCwd.trim().length > 0 && !isAddingProject; + const createNewThreadForProject = useCallback( + (projectId: ProjectId) => { + return handleNewThread(projectId, { + envMode: resolveSidebarNewThreadEnvMode({ + defaultEnvMode: appSettings.defaultThreadEnvMode, + }), + }); + }, + [appSettings.defaultThreadEnvMode, handleNewThread], + ); + const handlePickFolder = async () => { const api = readNativeApi(); if (!api || isPickingFolder) return; @@ -1358,11 +1369,11 @@ export default function Sidebar() { return ( -
+
+ + { + event.preventDefault(); + event.stopPropagation(); + void createNewThreadForProject(project.id); + }} + > + + + } + /> + + {newThreadShortcutLabel ? `New thread (${newThreadShortcutLabel})` : "New thread"} + +
@@ -1462,18 +1497,14 @@ export default function Sidebar() { } data-thread-selection-safe size="sm" - className="h-6 w-full translate-x-0 justify-start gap-1.5 px-2 text-left text-[11px] text-muted-foreground/35 transition-colors duration-150 hover:bg-accent/50 hover:text-muted-foreground/65" + className="h-8 w-full translate-x-0 justify-start gap-2 rounded-md border border-primary/20 bg-linear-to-r from-primary/14 via-primary/10 to-transparent px-2.5 text-left text-[11px] font-medium text-primary shadow-[inset_0_1px_0_hsl(0_0%_100%/0.08)] transition-all duration-150 hover:border-primary/35 hover:from-primary/18 hover:via-primary/12 hover:text-primary" onClick={(event) => { event.preventDefault(); event.stopPropagation(); - void handleNewThread(project.id, { - envMode: resolveSidebarNewThreadEnvMode({ - defaultEnvMode: appSettings.defaultThreadEnvMode, - }), - }); + void createNewThreadForProject(project.id); }} > - + New thread