@@ -12,6 +12,7 @@ import {
1212 type ServerProvider ,
1313 type ResolvedKeybindingsConfig ,
1414 type ScopedThreadRef ,
15+ type TerminalLayout ,
1516 type ThreadId ,
1617 type TurnId ,
1718 type KeybindingCommand ,
@@ -35,7 +36,7 @@ import {
3536import { projectScriptCwd , projectScriptRuntimeEnv } from "@t3tools/shared/projectScripts" ;
3637import { truncate } from "@t3tools/shared/String" ;
3738import { Debouncer } from "@tanstack/react-pacer" ;
38- import { memo , useCallback , useEffect , useMemo , useRef , useState } from "react" ;
39+ import { memo , useCallback , useEffect , useId , useMemo , useRef , useState } from "react" ;
3940import { useNavigate , useSearch } from "@tanstack/react-router" ;
4041import { useShallow } from "zustand/react/shallow" ;
4142import { useGitStatus } from "~/lib/gitStatusState" ;
@@ -104,7 +105,7 @@ import { BranchToolbar } from "./BranchToolbar";
104105import { resolveShortcutCommand , shortcutLabelForCommand } from "../keybindings" ;
105106import PlanSidebar from "./PlanSidebar" ;
106107import ThreadTerminalDrawer from "./ThreadTerminalDrawer" ;
107- import { ChevronDownIcon , TriangleAlertIcon , WifiOffIcon } from "lucide-react" ;
108+ import { ChevronDownIcon , TriangleAlertIcon , WifiOffIcon , XIcon } from "lucide-react" ;
108109import { cn , randomUUID } from "~/lib/utils" ;
109110import { stackedThreadToast , toastManager } from "./ui/toast" ;
110111import { decodeProjectScriptKeybindingRule } from "~/lib/projectScriptKeybindings" ;
@@ -438,6 +439,7 @@ interface PersistentThreadTerminalDrawerProps {
438439 newShortcutLabel : string | undefined ;
439440 closeShortcutLabel : string | undefined ;
440441 keybindings : ResolvedKeybindingsConfig ;
442+ terminalLayout : TerminalLayout ;
441443 onAddTerminalContext : ( selection : TerminalContextSelection ) => void ;
442444}
443445
@@ -451,6 +453,7 @@ const PersistentThreadTerminalDrawer = memo(function PersistentThreadTerminalDra
451453 newShortcutLabel,
452454 closeShortcutLabel,
453455 keybindings,
456+ terminalLayout,
454457 onAddTerminalContext,
455458} : PersistentThreadTerminalDrawerProps ) {
456459 const serverThread = useStore ( useMemo ( ( ) => createThreadSelectorByRef ( threadRef ) , [ threadRef ] ) ) ;
@@ -469,7 +472,9 @@ const PersistentThreadTerminalDrawer = memo(function PersistentThreadTerminalDra
469472 const storeNewTerminal = useTerminalStateStore ( ( state ) => state . newTerminal ) ;
470473 const storeSetActiveTerminal = useTerminalStateStore ( ( state ) => state . setActiveTerminal ) ;
471474 const storeCloseTerminal = useTerminalStateStore ( ( state ) => state . closeTerminal ) ;
475+ const storeSetTerminalOpen = useTerminalStateStore ( ( state ) => state . setTerminalOpen ) ;
472476 const [ localFocusRequestId , setLocalFocusRequestId ] = useState ( 0 ) ;
477+ const floatingTerminalTitleId = useId ( ) ;
473478 const worktreePath = serverThread ?. worktreePath ?? draftThread ?. worktreePath ?? null ;
474479 const effectiveWorktreePath = useMemo ( ( ) => {
475480 if ( launchContext !== null ) {
@@ -569,39 +574,81 @@ const PersistentThreadTerminalDrawer = memo(function PersistentThreadTerminalDra
569574 } ,
570575 [ onAddTerminalContext , visible ] ,
571576 ) ;
577+ const closeTerminalWindow = useCallback ( ( ) => {
578+ storeSetTerminalOpen ( threadRef , false ) ;
579+ } , [ storeSetTerminalOpen , threadRef ] ) ;
572580
573581 if ( ! project || ! terminalState . terminalOpen || ! cwd ) {
574582 return null ;
575583 }
576584
577- return (
578- < div className = { visible ? undefined : "hidden" } >
579- < ThreadTerminalDrawer
580- threadRef = { threadRef }
581- threadId = { threadId }
582- cwd = { cwd }
583- worktreePath = { effectiveWorktreePath }
584- runtimeEnv = { runtimeEnv }
585- visible = { visible }
586- height = { terminalState . terminalHeight }
587- terminalIds = { terminalState . terminalIds }
588- activeTerminalId = { terminalState . activeTerminalId }
589- terminalGroups = { terminalState . terminalGroups }
590- activeTerminalGroupId = { terminalState . activeTerminalGroupId }
591- focusRequestId = { focusRequestId + localFocusRequestId + ( visible ? 1 : 0 ) }
592- onSplitTerminal = { splitTerminal }
593- onNewTerminal = { createNewTerminal }
594- splitShortcutLabel = { visible ? splitShortcutLabel : undefined }
595- newShortcutLabel = { visible ? newShortcutLabel : undefined }
596- closeShortcutLabel = { visible ? closeShortcutLabel : undefined }
597- keybindings = { keybindings }
598- onActiveTerminalChange = { activateTerminal }
599- onCloseTerminal = { closeTerminal }
600- onHeightChange = { setTerminalHeight }
601- onAddTerminalContext = { handleAddTerminalContext }
602- />
603- </ div >
604- ) ;
585+ const drawer = (
586+ < ThreadTerminalDrawer
587+ threadRef = { threadRef }
588+ threadId = { threadId }
589+ cwd = { cwd }
590+ worktreePath = { effectiveWorktreePath }
591+ runtimeEnv = { runtimeEnv }
592+ visible = { visible }
593+ height = { terminalState . terminalHeight }
594+ terminalIds = { terminalState . terminalIds }
595+ activeTerminalId = { terminalState . activeTerminalId }
596+ terminalGroups = { terminalState . terminalGroups }
597+ activeTerminalGroupId = { terminalState . activeTerminalGroupId }
598+ focusRequestId = { focusRequestId + localFocusRequestId + ( visible ? 1 : 0 ) }
599+ onSplitTerminal = { splitTerminal }
600+ onNewTerminal = { createNewTerminal }
601+ splitShortcutLabel = { visible ? splitShortcutLabel : undefined }
602+ newShortcutLabel = { visible ? newShortcutLabel : undefined }
603+ closeShortcutLabel = { visible ? closeShortcutLabel : undefined }
604+ keybindings = { keybindings }
605+ onActiveTerminalChange = { activateTerminal }
606+ onCloseTerminal = { closeTerminal }
607+ onHeightChange = { setTerminalHeight }
608+ onAddTerminalContext = { handleAddTerminalContext }
609+ layout = { terminalLayout }
610+ />
611+ ) ;
612+
613+ if ( terminalLayout === "floating" ) {
614+ return (
615+ < div
616+ className = { cn (
617+ "fixed inset-0 z-50 bg-black/32 backdrop-blur-sm" ,
618+ visible ? "grid grid-rows-[1fr_auto_3fr] justify-items-center p-4" : "hidden" ,
619+ ) }
620+ onMouseDown = { ( event ) => {
621+ if ( event . target === event . currentTarget ) {
622+ closeTerminalWindow ( ) ;
623+ }
624+ } }
625+ >
626+ < div
627+ role = "dialog"
628+ aria-modal = "true"
629+ aria-labelledby = { floatingTerminalTitleId }
630+ className = "row-start-2 w-[min(96vw,72rem)] max-w-[min(96vw,72rem)] overflow-hidden rounded-lg border bg-background p-0 shadow-xl"
631+ >
632+ < div className = "flex h-8 shrink-0 items-center justify-between border-b border-border/80 px-2" >
633+ < h2 id = { floatingTerminalTitleId } className = "text-xs font-medium leading-none" >
634+ Terminal
635+ </ h2 >
636+ < button
637+ type = "button"
638+ className = "inline-flex size-6 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
639+ onClick = { closeTerminalWindow }
640+ aria-label = "Close terminal window"
641+ >
642+ < XIcon className = "size-3.5" />
643+ </ button >
644+ </ div >
645+ { drawer }
646+ </ div >
647+ </ div >
648+ ) ;
649+ }
650+
651+ return < div className = { visible ? undefined : "hidden" } > { drawer } </ div > ;
605652} ) ;
606653
607654export default function ChatView ( props : ChatViewProps ) {
@@ -3527,6 +3574,7 @@ export default function ChatView(props: ChatViewProps) {
35273574 availableEditors = { availableEditors }
35283575 terminalAvailable = { activeProject !== undefined }
35293576 terminalOpen = { terminalState . terminalOpen }
3577+ terminalLayout = { settings . terminalLayout }
35303578 terminalToggleShortcutLabel = { terminalToggleShortcutLabel }
35313579 diffToggleShortcutLabel = { diffPanelShortcutLabel }
35323580 gitCwd = { gitCwd }
@@ -3753,6 +3801,7 @@ export default function ChatView(props: ChatViewProps) {
37533801 newShortcutLabel = { newTerminalShortcutLabel ?? undefined }
37543802 closeShortcutLabel = { closeTerminalShortcutLabel ?? undefined }
37553803 keybindings = { keybindings }
3804+ terminalLayout = { settings . terminalLayout }
37563805 onAddTerminalContext = { addTerminalContextToDraft }
37573806 />
37583807 ) ) }
0 commit comments