authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-04-05 11:22:24-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-04-05 11:22:24-07:00
logcf2c628bc005f3c88911a6fcb957f56e6d1ce45f
treec5189d8bb42ce42acf441b6a6ba5cc9ed3d944b9
parente0874b651d5c5fa5c578be932aada627b95c3f52
signature Signed by PGP key B5690EEEBB952194

Use active worktree path for workspace saves (#1762)


2 files changed, 52 insertions(+), 2 deletions(-)

apps/web/src/components/ChatView.browser.tsx+49
......@@ -2818,6 +2818,55 @@ describe("ChatView timeline estimator parity (full app)", () => {
28182818 }
28192819 });
28202820
2821 it("uses the active worktree path when saving a proposed plan to the workspace", async () => {
2822 const snapshot = createSnapshotWithLongProposedPlan();
2823 const threads = snapshot.threads.slice();
2824 const targetThreadIndex = threads.findIndex((thread) => thread.id === THREAD_ID);
2825 const targetThread = targetThreadIndex >= 0 ? threads[targetThreadIndex] : undefined;
2826 if (targetThread) {
2827 threads[targetThreadIndex] = {
2828 ...targetThread,
2829 worktreePath: "/repo/worktrees/plan-thread",
2830 };
2831 }
2832
2833 const mounted = await mountChatView({
2834 viewport: DEFAULT_VIEWPORT,
2835 snapshot: {
2836 ...snapshot,
2837 threads,
2838 },
2839 });
2840
2841 try {
2842 const planActionsButton = await waitForElement(
2843 () => document.querySelector<HTMLButtonElement>('button[aria-label="Plan actions"]'),
2844 "Unable to find proposed plan actions button.",
2845 );
2846 planActionsButton.click();
2847
2848 const saveToWorkspaceItem = await waitForElement(
2849 () =>
2850 (Array.from(document.querySelectorAll('[data-slot="menu-item"]')).find(
2851 (item) => item.textContent?.trim() === "Save to workspace",
2852 ) ?? null) as HTMLElement | null,
2853 'Unable to find "Save to workspace" menu item.',
2854 );
2855 saveToWorkspaceItem.click();
2856
2857 await vi.waitFor(
2858 () => {
2859 expect(document.body.textContent).toContain(
2860 "Enter a path relative to /repo/worktrees/plan-thread.",
2861 );
2862 },
2863 { timeout: 8_000, interval: 16 },
2864 );
2865 } finally {
2866 await mounted.cleanup();
2867 }
2868 });
2869
28212870 it("keeps pending-question footer actions inside the composer after a real resize", async () => {
28222871 const mounted = await mountChatView({
28232872 viewport: WIDE_FOOTER_VIEWPORT,
apps/web/src/components/ChatView.tsx+3-2
......@@ -1526,6 +1526,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
15261526 );
15271527 const activeProjectCwd = activeProject?.cwd ?? null;
15281528 const activeThreadWorktreePath = activeThread?.worktreePath ?? null;
1529 const activeWorkspaceRoot = activeThreadWorktreePath ?? activeProjectCwd ?? undefined;
15291530 const activeTerminalLaunchContext =
15301531 terminalLaunchContext?.threadId === activeThreadId
15311532 ? terminalLaunchContext
......@@ -4004,7 +4005,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
40044005 markdownCwd={gitCwd ?? undefined}
40054006 resolvedTheme={resolvedTheme}
40064007 timestampFormat={timestampFormat}
4007 workspaceRoot={activeProject?.cwd ?? undefined}
4008 workspaceRoot={activeWorkspaceRoot}
40084009 />
40094010 </div>
40104011
......@@ -4438,7 +4439,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
44384439 activePlan={activePlan}
44394440 activeProposedPlan={sidebarProposedPlan}
44404441 markdownCwd={gitCwd ?? undefined}
4441 workspaceRoot={activeProject?.cwd ?? undefined}
4442 workspaceRoot={activeWorkspaceRoot}
44424443 timestampFormat={timestampFormat}
44434444 onClose={() => {
44444445 setPlanSidebarOpen(false);