authorgravatar for cursoragent@cursor.comCursor Agent <cursoragent@cursor.com> 2026-02-18 08:20:50+00:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-18 00:59:06-08:00
log0e73dc15275f24b7ba94a7c200939907690f70b2
treed67c879a2ce203d563614febc43c940538459a37
parent1a94fcbd294ab69709a3895b30941537d415764a

Fix terminal stability bugs: remove autoFocus from effect deps, stabilize memo, remove dead export

- Remove autoFocus from the main terminal setup effect dependency array to prevent full terminal teardown/recreation when switching tabs in split view. Focus is already handled by a dedicated effect. - Change threadTerminalRuntimeEnv memo dependency from activeProject to activeProject?.cwd so unrelated project state changes (e.g. saving scripts) don't trigger terminal re-initialization. - Unexport injectEnvIntoShellCommand which is never imported by production code (env injection is handled server-side), and remove its tests. Applied via @cursor push command

4 files changed, 3 insertions(+), 23 deletions(-)

apps/web/src/components/ChatView.tsx+1-1
......@@ -552,7 +552,7 @@ export default function ChatView() {
552552 },
553553 worktreePath: activeThread?.worktreePath ?? null,
554554 });
555 }, [activeProject, activeThread?.worktreePath]);
555 }, [activeProject?.cwd, activeThread?.worktreePath]);
556556 // Default true while loading to avoid toolbar flicker.
557557 const isGitRepo = branchesQuery.data?.isRepo ?? true;
558558 const splitTerminalShortcutLabel = useMemo(
apps/web/src/components/ThreadTerminalDrawer.tsx+1-1
......@@ -372,7 +372,7 @@ function TerminalViewport({
372372 fitAddonRef.current = null;
373373 terminal.dispose();
374374 };
375 }, [api, autoFocus, cwd, runtimeEnv, terminalId, threadId]);
375 }, [api, cwd, runtimeEnv, terminalId, threadId]);
376376
377377 useEffect(() => {
378378 if (!autoFocus) return;
apps/web/src/projectScripts.test.ts-20
......@@ -2,7 +2,6 @@ import { describe, expect, it } from "vitest";
22
33import {
44 commandForProjectScript,
5 injectEnvIntoShellCommand,
65 nextProjectScriptId,
76 primaryProjectScript,
87 projectScriptRuntimeEnv,
......@@ -24,25 +23,6 @@ describe("projectScripts helpers", () => {
2423 expect(nextProjectScriptId("!!!", [])).toBe("script");
2524 });
2625
27 it("injects environment variables for posix shells", () => {
28 const command = injectEnvIntoShellCommand("bun install", { T3CODE_PROJECT_ROOT: "/tmp/project" }, "MacIntel");
29 expect(command).toBe("env T3CODE_PROJECT_ROOT='/tmp/project' bun install");
30 });
31
32 it("injects environment variables for windows shells", () => {
33 const command = injectEnvIntoShellCommand("bun install", { T3CODE_PROJECT_ROOT: "C:\\\\repo path" }, "Win32");
34 expect(command).toBe('set "T3CODE_PROJECT_ROOT=C:\\\\repo path" && bun install');
35 });
36
37 it("escapes windows env values that trigger interpolation", () => {
38 const command = injectEnvIntoShellCommand(
39 "bun install",
40 { T3CODE_PROJECT_ROOT: '%USERPROFILE%^"repo"' },
41 "Win32",
42 );
43 expect(command).toBe('set "T3CODE_PROJECT_ROOT=^%USERPROFILE^%^^""repo""" && bun install');
44 });
45
4626 it("resolves primary and setup scripts", () => {
4727 const scripts = [
4828 {
apps/web/src/projectScripts.ts+1-1
......@@ -60,7 +60,7 @@ function shellEscapeWindowsSetValue(value: string): string {
6060 return value.replace(/\^/g, "^^").replace(/%/g, "^%").replace(/"/g, '""');
6161}
6262
63export function injectEnvIntoShellCommand(
63function injectEnvIntoShellCommand(
6464 command: string,
6565 env: Record<string, string>,
6666 platform = typeof navigator === "undefined" ? "" : navigator.platform,