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() {...@@ -552,7 +552,7 @@ export default function ChatView() {
552 },552 },
553 worktreePath: activeThread?.worktreePath ?? null,553 worktreePath: activeThread?.worktreePath ?? null,
554 });554 });
555 }, [activeProject, activeThread?.worktreePath]);555 }, [activeProject?.cwd, activeThread?.worktreePath]);
556 // Default true while loading to avoid toolbar flicker.556 // Default true while loading to avoid toolbar flicker.
557 const isGitRepo = branchesQuery.data?.isRepo ?? true;557 const isGitRepo = branchesQuery.data?.isRepo ?? true;
558 const splitTerminalShortcutLabel = useMemo(558 const splitTerminalShortcutLabel = useMemo(
apps/web/src/components/ThreadTerminalDrawer.tsx+1-1
...@@ -372,7 +372,7 @@ function TerminalViewport({...@@ -372,7 +372,7 @@ function TerminalViewport({
372 fitAddonRef.current = null;372 fitAddonRef.current = null;
373 terminal.dispose();373 terminal.dispose();
374 };374 };
375 }, [api, autoFocus, cwd, runtimeEnv, terminalId, threadId]);375 }, [api, cwd, runtimeEnv, terminalId, threadId]);
376376
377 useEffect(() => {377 useEffect(() => {
378 if (!autoFocus) return;378 if (!autoFocus) return;
apps/web/src/projectScripts.test.ts-20
...@@ -2,7 +2,6 @@ import { describe, expect, it } from "vitest";...@@ -2,7 +2,6 @@ import { describe, expect, it } from "vitest";
22
3import {3import {
4 commandForProjectScript,4 commandForProjectScript,
5 injectEnvIntoShellCommand,
6 nextProjectScriptId,5 nextProjectScriptId,
7 primaryProjectScript,6 primaryProjectScript,
8 projectScriptRuntimeEnv,7 projectScriptRuntimeEnv,
...@@ -24,25 +23,6 @@ describe("projectScripts helpers", () => {...@@ -24,25 +23,6 @@ describe("projectScripts helpers", () => {
24 expect(nextProjectScriptId("!!!", [])).toBe("script");23 expect(nextProjectScriptId("!!!", [])).toBe("script");
25 });24 });
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
46 it("resolves primary and setup scripts", () => {26 it("resolves primary and setup scripts", () => {
47 const scripts = [27 const scripts = [
48 {28 {
apps/web/src/projectScripts.ts+1-1
...@@ -60,7 +60,7 @@ function shellEscapeWindowsSetValue(value: string): string {...@@ -60,7 +60,7 @@ function shellEscapeWindowsSetValue(value: string): string {
60 return value.replace(/\^/g, "^^").replace(/%/g, "^%").replace(/"/g, '""');60 return value.replace(/\^/g, "^^").replace(/%/g, "^%").replace(/"/g, '""');
61}61}
6262
63export function injectEnvIntoShellCommand(63function injectEnvIntoShellCommand(
64 command: string,64 command: string,
65 env: Record<string, string>,65 env: Record<string, string>,
66 platform = typeof navigator === "undefined" ? "" : navigator.platform,66 platform = typeof navigator === "undefined" ? "" : navigator.platform,