@@ -25,6 +25,7 @@ import {
2525import { useLoadBalancedEnvironment } from "../hooks/useLoadBalancedEnvironment" ;
2626import { useScratchProject } from "../hooks/useScratchProject" ;
2727import { useStableCallback } from "../hooks/useStableCallback" ;
28+ import { useThreadTerminalActions } from "./terminal/useThreadTerminalActions" ;
2829import { usePendingThreadRequests } from "./chat/usePendingThreadRequests" ;
2930import { isScratchProject } from "@t3tools/client-runtime/state/projects" ;
3031import { visibleThreadPullRequests } from "@t3tools/shared/threadPullRequests" ;
@@ -76,7 +77,6 @@ import {
7677 resolveEnvironmentMachineKind ,
7778 resolveProjectProviderInstanceEnabled ,
7879 RuntimeMode ,
79- TerminalOpenInput ,
8080 type WorktreeSetupSnapshot ,
8181} from "@t3tools/contracts" ;
8282import { type EnvironmentConnectionPresentation } from "@t3tools/client-runtime/connection" ;
@@ -125,11 +125,7 @@ import {
125125 resolvePromptInjectedEffort ,
126126 resolveSelectableModel ,
127127} from "@t3tools/shared/model" ;
128- import {
129- projectScriptCwd ,
130- projectScriptRuntimeEnv ,
131- resolveProjectScripts ,
132- } from "@t3tools/shared/projectScripts" ;
128+ import { projectScriptCwd , resolveProjectScripts } from "@t3tools/shared/projectScripts" ;
133129import { CHAT_LIST_ANCHOR_OFFSET } from "@t3tools/shared/chatList" ;
134130import { derivePendingBackgroundWork } from "@t3tools/shared/orchestrationV2PendingBackgroundWork" ;
135131import {
@@ -140,7 +136,7 @@ import { resolveProjectSettings } from "@t3tools/shared/projectSettings";
140136import { sourceControlRepositorySelector } from "@t3tools/shared/sourceControl" ;
141137import { truncate } from "@t3tools/shared/String" ;
142138import { resolveThreadReferenceCopyTarget } from "@t3tools/shared/threadReference" ;
143- import { nextTerminalId , resolveTerminalSessionLabel } from "@t3tools/shared/terminalLabels" ;
139+ import { resolveTerminalSessionLabel } from "@t3tools/shared/terminalLabels" ;
144140import { Debouncer } from "@tanstack/react-pacer" ;
145141import { useAtomValue } from "@effect/atom-react" ;
146142import { Atom } from "effect/reactivity" ;
@@ -400,7 +396,6 @@ import {
400396 primaryServerKeybindingsAtom ,
401397 serverEnvironment ,
402398} from "../state/server" ;
403- import { terminalEnvironment } from "../state/terminal" ;
404399import { threadEnvironment } from "../state/threads" ;
405400import { workspacePreparationRetryRunIds } from "@t3tools/client-runtime/state/turn-item-presentation" ;
406401import { resolveProviderSkillsForCwd } from "@t3tools/client-runtime/providerSkills" ;
@@ -1051,9 +1046,6 @@ export default function ChatView(props: ChatViewProps) {
10511046 const removeKeybinding = useAtomCommand ( serverEnvironment . removeKeybinding , {
10521047 reportFailure : false ,
10531048 } ) ;
1054- const openTerminal = useAtomCommand ( terminalEnvironment . open , "terminal open" ) ;
1055- const writeTerminal = useAtomCommand ( terminalEnvironment . write , "terminal write" ) ;
1056- const closeTerminalMutation = useAtomCommand ( terminalEnvironment . close , "terminal close" ) ;
10571049 const createThread = useOrchestrationCommand ( threadEnvironment . create , { reportFailure : false } ) ;
10581050 const deleteThread = useOrchestrationCommand ( threadEnvironment . delete , { reportFailure : false } ) ;
10591051 const updateThreadMetadata = useOrchestrationCommand ( threadEnvironment . updateMetadata , {
@@ -1668,6 +1660,7 @@ export default function ChatView(props: ChatViewProps) {
16681660 return labels ;
16691661 } , [ activeThreadKnownSessions ] ) ;
16701662 const activeThreadRef = useActiveThreadRef ( activeThread ) ;
1663+ const terminalActions = useThreadTerminalActions ( activeThreadRef ) ;
16711664 const activeThreadKey = activeThreadRef ? scopedThreadKey ( activeThreadRef ) : null ;
16721665 const activeEnvironmentServerBrowser = useEnvironmentSupportsServerBrowser (
16731666 activeThreadRef ?. environmentId ?? null ,
@@ -1759,18 +1752,7 @@ export default function ChatView(props: ChatViewProps) {
17591752 ) ,
17601753 [ rightPanelState . surfaces ] ,
17611754 ) ;
1762- const allocatableActiveTerminalIds = useMemo (
1763- ( ) => [ ...new Set ( [ ...activeKnownTerminalIds , ...panelTerminalIds ] ) ] ,
1764- [ activeKnownTerminalIds , panelTerminalIds ] ,
1765- ) ;
17661755 const canReuseTerminal = activeThreadKnownSessionsRaw !== null ;
1767- const allocateTerminalId = ( ) =>
1768- nextTerminalId (
1769- allocatableActiveTerminalIds ,
1770- canReuseTerminal && readEnvironmentScope ( environmentId , AuthTerminalReadScope )
1771- ? undefined
1772- : randomUUID ( ) ,
1773- ) ;
17741756 const previewPanelOpen = activeRightPanelKind === "preview" && browserAvailable ;
17751757 const rightPanelOpen = rightPanelState . isOpen ;
17761758 const { active : panelAnimationsActive , durationMs : panelAnimationDurationMs } =
@@ -4011,119 +3993,40 @@ export default function ChatView(props: ChatViewProps) {
40113993 storeEnsureTerminal ( activeThreadRef , existingTerminalId , { open : true , active : true } ) ;
40123994 return ;
40133995 }
4014- if ( ! activeThreadId || ! activeProject ) {
4015- return ;
4016- }
4017- const cwdForOpen = gitCwd ?? activeProject . workspaceRoot ;
4018- if ( ! cwdForOpen ) {
4019- return ;
4020- }
4021- const terminalId = allocateTerminalId ( ) ;
4022- storeEnsureTerminal ( activeThreadRef , terminalId , { open : true } ) ;
4023- void openTerminal ( {
4024- environmentId,
4025- input : {
4026- threadId : activeThreadId ,
4027- terminalId,
4028- cwd : cwdForOpen ,
4029- ...( activeThreadWorktreePath != null ? { worktreePath : activeThreadWorktreePath } : { } ) ,
4030- env : projectScriptRuntimeEnv ( {
4031- project : { cwd : activeProject . workspaceRoot } ,
4032- worktreePath : activeThreadWorktreePath ,
4033- } ) ,
4034- } ,
4035- } ) ;
3996+ void terminalActions . launchTerminal ( {
3997+ place : ( terminalId ) => storeEnsureTerminal ( activeThreadRef , terminalId , { open : true } ) ,
3998+ } ) ?. result ;
40363999 return ;
40374000 }
40384001 setTerminalOpen ( nextOpen ) ;
40394002 } ;
40404003 const toggleTerminalVisibilityStable = useStableCallback ( toggleTerminalVisibility ) ;
40414004 const splitTerminal = ( direction : "horizontal" | "vertical" = "horizontal" ) => {
4042- if (
4043- ! hasTerminalWriteAccess ( ) ||
4044- ! activeThreadRef ||
4045- hasReachedSplitLimit ||
4046- ! activeThreadId ||
4047- ! activeProject
4048- ) {
4049- return ;
4050- }
4051- const cwdForOpen = gitCwd ?? activeProject . workspaceRoot ;
4052- if ( ! cwdForOpen ) {
4053- return ;
4054- }
4055- const terminalId = allocateTerminalId ( ) ;
4056- if ( direction === "vertical" ) {
4057- storeSplitTerminalVertical ( activeThreadRef , terminalId ) ;
4058- } else {
4059- storeSplitTerminal ( activeThreadRef , terminalId ) ;
4060- }
4061- setTerminalFocusRequestId ( ( value ) => value + 1 ) ;
4062- void openTerminal ( {
4063- environmentId,
4064- input : {
4065- threadId : activeThreadId ,
4066- terminalId,
4067- cwd : cwdForOpen ,
4068- ...( activeThreadWorktreePath != null ? { worktreePath : activeThreadWorktreePath } : { } ) ,
4069- env : projectScriptRuntimeEnv ( {
4070- project : { cwd : activeProject . workspaceRoot } ,
4071- worktreePath : activeThreadWorktreePath ,
4072- } ) ,
4073- } ,
4005+ if ( ! activeThreadRef || hasReachedSplitLimit ) return ;
4006+ const launched = terminalActions . launchTerminal ( {
4007+ place : ( terminalId ) =>
4008+ direction === "vertical"
4009+ ? storeSplitTerminalVertical ( activeThreadRef , terminalId )
4010+ : storeSplitTerminal ( activeThreadRef , terminalId ) ,
40744011 } ) ;
4012+ if ( ! launched ) return ;
4013+ setTerminalFocusRequestId ( ( value ) => value + 1 ) ;
4014+ void launched . result ;
40754015 } ;
40764016 const splitTerminalStable = useStableCallback ( splitTerminal ) ;
40774017 const createNewTerminal = ( ) => {
4078- if ( ! hasTerminalWriteAccess ( ) || ! activeThreadRef || ! activeThreadId || ! activeProject ) {
4079- return ;
4080- }
4081- const cwdForOpen = gitCwd ?? activeProject . workspaceRoot ;
4082- if ( ! cwdForOpen ) {
4083- return ;
4084- }
4085- const terminalId = allocateTerminalId ( ) ;
4086- storeNewTerminal ( activeThreadRef , terminalId ) ;
4087- setTerminalFocusRequestId ( ( value ) => value + 1 ) ;
4088- void openTerminal ( {
4089- environmentId,
4090- input : {
4091- threadId : activeThreadId ,
4092- terminalId,
4093- cwd : cwdForOpen ,
4094- ...( activeThreadWorktreePath != null ? { worktreePath : activeThreadWorktreePath } : { } ) ,
4095- env : projectScriptRuntimeEnv ( {
4096- project : { cwd : activeProject . workspaceRoot } ,
4097- worktreePath : activeThreadWorktreePath ,
4098- } ) ,
4099- } ,
4018+ if ( ! activeThreadRef ) return ;
4019+ const launched = terminalActions . launchTerminal ( {
4020+ place : ( terminalId ) => storeNewTerminal ( activeThreadRef , terminalId ) ,
41004021 } ) ;
4022+ if ( ! launched ) return ;
4023+ setTerminalFocusRequestId ( ( value ) => value + 1 ) ;
4024+ void launched . result ;
41014025 } ;
41024026 const createNewTerminalStable = useStableCallback ( createNewTerminal ) ;
41034027 const closeTerminal = ( terminalId : string ) => {
4104- if ( ! hasTerminalWriteAccess ( ) || ! activeThreadId || ! activeThreadRef ) return ;
4105- const fallbackExitWrite = ( ) =>
4106- writeTerminal ( {
4107- environmentId,
4108- input : { threadId : activeThreadId , terminalId, data : "exit\n" } ,
4109- } ) ;
4110- void ( async ( ) => {
4111- const closeResult = await closeTerminalMutation ( {
4112- environmentId,
4113- input : {
4114- threadId : activeThreadId ,
4115- terminalId,
4116- deleteHistory : true ,
4117- } ,
4118- } ) ;
4119- if (
4120- closeResult . _tag === "Failure" &&
4121- ! isAtomCommandInterrupted ( closeResult ) &&
4122- hasTerminalWriteAccess ( )
4123- ) {
4124- await fallbackExitWrite ( ) ;
4125- }
4126- } ) ( ) ;
4028+ if ( ! hasTerminalWriteAccess ( ) || ! activeThreadRef ) return ;
4029+ terminalActions . closeTerminalSession ( terminalId ) ;
41274030 storeCloseTerminal ( activeThreadRef , terminalId ) ;
41284031 setTerminalFocusRequestId ( ( value ) => value + 1 ) ;
41294032 } ;
@@ -4166,39 +4069,20 @@ export default function ChatView(props: ChatViewProps) {
41664069 }
41674070 setTerminalFocusRequestId ( ( value ) => value + 1 ) ;
41684071
4169- const runtimeEnv = projectScriptRuntimeEnv ( {
4170- project : {
4171- cwd : activeProject . workspaceRoot ,
4172- } ,
4173- worktreePath : targetWorktreePath ,
4072+ const launched = terminalActions . launchTerminal ( {
4073+ place : ( terminalId ) =>
4074+ shouldCreateNewTerminal
4075+ ? storeNewTerminal ( activeThreadRef , terminalId )
4076+ : storeSetActiveTerminal ( activeThreadRef , terminalId ) ,
4077+ ...( shouldCreateNewTerminal
4078+ ? { size : { cols : SCRIPT_TERMINAL_COLS , rows : SCRIPT_TERMINAL_ROWS } }
4079+ : { terminalId : baseTerminalId } ) ,
4080+ location : { cwd : targetCwd , worktreePath : targetWorktreePath } ,
41744081 ...( options ?. env ? { extraEnv : options . env } : { } ) ,
41754082 } ) ;
4176- const targetTerminalId = shouldCreateNewTerminal ? allocateTerminalId ( ) : baseTerminalId ;
4177- const openTerminalInput : TerminalOpenInput = shouldCreateNewTerminal
4178- ? {
4179- threadId : activeThreadId ,
4180- terminalId : targetTerminalId ,
4181- cwd : targetCwd ,
4182- ...( targetWorktreePath !== null ? { worktreePath : targetWorktreePath } : { } ) ,
4183- env : runtimeEnv ,
4184- cols : SCRIPT_TERMINAL_COLS ,
4185- rows : SCRIPT_TERMINAL_ROWS ,
4186- }
4187- : {
4188- threadId : activeThreadId ,
4189- terminalId : targetTerminalId ,
4190- cwd : targetCwd ,
4191- ...( targetWorktreePath !== null ? { worktreePath : targetWorktreePath } : { } ) ,
4192- env : runtimeEnv ,
4193- } ;
4194-
4195- if ( shouldCreateNewTerminal ) {
4196- storeNewTerminal ( activeThreadRef , targetTerminalId ) ;
4197- } else {
4198- storeSetActiveTerminal ( activeThreadRef , targetTerminalId ) ;
4199- }
4200-
4201- const openResult = await openTerminal ( { environmentId, input : openTerminalInput } ) ;
4083+ if ( ! launched ) return ;
4084+ const targetTerminalId = launched . terminalId ;
4085+ const openResult = await launched . result ;
42024086 if ( openResult . _tag === "Failure" ) {
42034087 if ( ! isAtomCommandInterrupted ( openResult ) ) {
42044088 const error = squashAtomCommandFailure ( openResult ) ;
@@ -4210,15 +4094,11 @@ export default function ChatView(props: ChatViewProps) {
42104094 return ;
42114095 }
42124096
4213- if ( ! hasTerminalWriteAccess ( ) ) return ;
4214- const writeResult = await writeTerminal ( {
4215- environmentId,
4216- input : {
4217- threadId : activeThreadId ,
4218- terminalId : targetTerminalId ,
4219- data : `${ script . command } \r` ,
4220- } ,
4221- } ) ;
4097+ const writeResult = await terminalActions . writeToTerminal (
4098+ targetTerminalId ,
4099+ `${ script . command } \r` ,
4100+ ) ;
4101+ if ( ! writeResult ) return ;
42224102 if ( writeResult . _tag === "Failure" ) {
42234103 if ( ! isAtomCommandInterrupted ( writeResult ) ) {
42244104 const error = squashAtomCommandFailure ( writeResult ) ;
@@ -5003,56 +4883,34 @@ export default function ChatView(props: ChatViewProps) {
50034883 } ;
50044884 const togglePreviewPanelStable = useStableCallback ( togglePreviewPanel ) ;
50054885 const addTerminalSurface = ( ) => {
5006- if ( ! hasTerminalWriteAccess ( ) || ! activeThreadRef || ! activeThreadId || ! activeProject ) return ;
5007- const cwd = gitCwd ?? activeProject . workspaceRoot ;
5008- const terminalId = allocateTerminalId ( ) ;
5009- useRightPanelStore . getState ( ) . openTerminal ( activeThreadRef , terminalId ) ;
5010- setTerminalFocusRequestId ( ( value ) => value + 1 ) ;
5011- void openTerminal ( {
5012- environmentId : activeThreadRef . environmentId ,
5013- input : {
5014- threadId : activeThreadId ,
5015- terminalId,
5016- cwd,
5017- ...( activeThreadWorktreePath != null ? { worktreePath : activeThreadWorktreePath } : { } ) ,
5018- env : projectScriptRuntimeEnv ( {
5019- project : { cwd : activeProject . workspaceRoot } ,
5020- worktreePath : activeThreadWorktreePath ,
5021- } ) ,
5022- } ,
4886+ if ( ! activeThreadRef ) return ;
4887+ const launched = terminalActions . launchTerminal ( {
4888+ place : ( terminalId ) =>
4889+ useRightPanelStore . getState ( ) . openTerminal ( activeThreadRef , terminalId ) ,
50234890 } ) ;
4891+ if ( ! launched ) return ;
4892+ setTerminalFocusRequestId ( ( value ) => value + 1 ) ;
4893+ void launched . result ;
50244894 } ;
50254895 const addTerminalSurfaceStable = useStableCallback ( addTerminalSurface ) ;
50264896 const splitPanelTerminal = ( direction : "horizontal" | "vertical" = "horizontal" ) => {
50274897 if (
5028- ! hasTerminalWriteAccess ( ) ||
50294898 ! activeThreadRef ||
5030- ! activeThreadId ||
5031- ! activeProject ||
50324899 activeRightPanelSurface ?. kind !== "terminal" ||
50334900 activeRightPanelSurface . terminalIds . length >= MAX_TERMINALS_PER_GROUP
50344901 ) {
50354902 return ;
50364903 }
5037- const terminalId = allocateTerminalId ( ) ;
5038- const cwd = gitCwd ?? activeProject . workspaceRoot ;
5039- useRightPanelStore
5040- . getState ( )
5041- . splitTerminal ( activeThreadRef , activeRightPanelSurface . id , terminalId , direction ) ;
5042- setTerminalFocusRequestId ( ( value ) => value + 1 ) ;
5043- void openTerminal ( {
5044- environmentId : activeThreadRef . environmentId ,
5045- input : {
5046- threadId : activeThreadId ,
5047- terminalId,
5048- cwd,
5049- ...( activeThreadWorktreePath != null ? { worktreePath : activeThreadWorktreePath } : { } ) ,
5050- env : projectScriptRuntimeEnv ( {
5051- project : { cwd : activeProject . workspaceRoot } ,
5052- worktreePath : activeThreadWorktreePath ,
5053- } ) ,
5054- } ,
4904+ const surfaceId = activeRightPanelSurface . id ;
4905+ const launched = terminalActions . launchTerminal ( {
4906+ place : ( terminalId ) =>
4907+ useRightPanelStore
4908+ . getState ( )
4909+ . splitTerminal ( activeThreadRef , surfaceId , terminalId , direction ) ,
50554910 } ) ;
4911+ if ( ! launched ) return ;
4912+ setTerminalFocusRequestId ( ( value ) => value + 1 ) ;
4913+ void launched . result ;
50564914 } ;
50574915 const splitPanelTerminalStable = useStableCallback ( splitPanelTerminal ) ;
50584916 const splitPanelTerminalVertical = ( ) => {
@@ -5074,10 +4932,7 @@ export default function ChatView(props: ChatViewProps) {
50744932 activeRightPanelSurface ?. kind !== "terminal"
50754933 )
50764934 return ;
5077- void closeTerminalMutation ( {
5078- environmentId : activeThreadRef . environmentId ,
5079- input : { threadId : activeThreadRef . threadId , terminalId, deleteHistory : true } ,
5080- } ) ;
4935+ terminalActions . closeTerminalSession ( terminalId ) ;
50814936 storeCloseTerminal ( activeThreadRef , terminalId ) ;
50824937 useRightPanelStore
50834938 . getState ( )
@@ -5165,10 +5020,7 @@ export default function ChatView(props: ChatViewProps) {
51655020 ) {
51665021 for ( const terminalId of surface . terminalIds ) {
51675022 storeCloseTerminal ( activeThreadRef , terminalId ) ;
5168- void closeTerminalMutation ( {
5169- environmentId : activeThreadRef . environmentId ,
5170- input : { threadId : activeThreadRef . threadId , terminalId, deleteHistory : true } ,
5171- } ) ;
5023+ terminalActions . closeTerminalSession ( terminalId ) ;
51725024 }
51735025 }
51745026 }
0 commit comments