@@ -7,11 +7,12 @@ import { LoaderCircle, X } from "lucide-react";
77import { useEffect , useRef } from "react" ;
88import { useT } from "../../i18n" ;
99import type { Bot , ToolItem } from "../../lib/protocol.gen" ;
10+ import { useApi } from "../../store/context" ;
1011import { Button } from "../../ui/Button" ;
1112import { SidePanel } from "../../ui/SidePanel" ;
1213import { commandOf , isCommand } from "../chat/command" ;
1314import { useChat } from "../chat/useChat" ;
14- import { ComputerDock } from "./ComputerDock" ;
15+ import { ComputerFooter } from "./ComputerDock" ;
1516
1617function Command ( { tool } : { tool : ToolItem } ) {
1718 const t = useT ( ) . terminal ;
@@ -45,14 +46,27 @@ function Command({ tool }: { tool: ToolItem }) {
4546 ) ;
4647}
4748
49+ /**
50+ * The commands each terminal showed last, so coming back to it (from the
51+ * dock) shows them at once while the chat loads again.
52+ */
53+ const lastShown = new WeakMap < object , Map < string , { id : string ; tool : ToolItem } [ ] > > ( ) ;
54+
4855export function TerminalPanel ( { bot, onClose } : { bot : Bot ; onClose ( ) : void } ) {
4956 const t = useT ( ) . terminal ;
5057 const chat = useChat ( bot . id ) ;
51- const commands = chat . items . flatMap ( ( item ) =>
58+ const api = useApi ( ) ;
59+ const shown = lastShown . get ( api ) ?? new Map < string , { id : string ; tool : ToolItem } [ ] > ( ) ;
60+ lastShown . set ( api , shown ) ;
61+ const loaded = chat . items . flatMap ( ( item ) =>
5262 item . body . kind === "tool" && isCommand ( item . body . name )
5363 ? [ { id : item . id , tool : item . body } ]
5464 : [ ] ,
5565 ) ;
66+ const commands = chat . loading && loaded . length === 0 ? ( shown . get ( bot . id ) ?? [ ] ) : loaded ;
67+ if ( ! chat . loading ) {
68+ shown . set ( bot . id , loaded ) ;
69+ }
5670 const screen = useRef < HTMLDivElement > ( null ) ;
5771 const last = commands . at ( - 1 ) ;
5872 // Follows the newest command, as a terminal does.
@@ -65,14 +79,14 @@ export function TerminalPanel({ bot, onClose }: { bot: Bot; onClose(): void }) {
6579 } , [ last ?. id , last ?. tool . status ] ) ;
6680
6781 return (
68- < SidePanel label = { t . panel ( bot . name ) } name = "terminal " defaultWidth = { 560 } >
82+ < SidePanel label = { t . panel ( bot . name ) } name = "computer " defaultWidth = { 600 } >
6983 < header className = "flex h-11 shrink-0 items-center justify-between border-line border-b pr-1.5 pl-4" >
7084 < h2 className = "font-semibold text-sm" > { t . heading } </ h2 >
7185 < Button variant = "ghost" size = "sm" icon = { X } label = { t . close } onClick = { onClose } />
7286 </ header >
7387 < div className = "flex min-h-0 flex-1 flex-col p-3" >
7488 < div
75- className = "bot-stage flex min-h-0 flex-1 flex-col gap-3 p-3 .5"
89+ className = "bot-stage flex min-h-0 flex-1 flex-col p-2 .5"
7690 style = { { "--bot" : bot . color } as React . CSSProperties }
7791 >
7892 < div className = "terminal-window flex min-h-0 flex-1 flex-col overflow-hidden rounded-xl" >
@@ -102,9 +116,9 @@ export function TerminalPanel({ bot, onClose }: { bot: Bot; onClose(): void }) {
102116 ) }
103117 </ div >
104118 </ div >
105- < ComputerDock />
106119 </ div >
107120 </ div >
121+ < ComputerFooter />
108122 </ SidePanel >
109123 ) ;
110124}
0 commit comments