diff --git a/gadget-code/frontend/src/components/LogPanel.tsx b/gadget-code/frontend/src/components/LogPanel.tsx index 5c5f93a..7fda189 100644 --- a/gadget-code/frontend/src/components/LogPanel.tsx +++ b/gadget-code/frontend/src/components/LogPanel.tsx @@ -19,62 +19,17 @@ export default function LogPanel({ logs, expanded, workspaceMode, onToggleExpand const scrollRef = useRef(null); useEffect(() => { - if (scrollRef.current && !expanded) { + if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } - }, [logs, expanded]); - - if (expanded) { - return ( -
-
-

- Log -

- -
-
- {logs.length === 0 ? ( -
No log entries
- ) : ( - logs.map((entry) => ( -
- - {entry.timestamp.toLocaleTimeString()} - - - [{entry.level.toUpperCase()}] - - {entry.message} -
- )) - )} -
-
- ); - } + }, [logs]); return ( -
+

Log @@ -82,9 +37,9 @@ export default function LogPanel({ logs, expanded, workspaceMode, onToggleExpand

{ if (!session || !project) return; - const registration = session.drone as any; - const success = await socketClient.requestWorkspaceMode( - registration, - project, - session, - mode, - ); - if (!success) { - showToast(`Cannot switch to ${mode} mode: workspace is not idle`); + try { + const droneJson = localStorage.getItem('dtp_drone_registration'); + if (!droneJson) { + showToast('No drone registration found'); + return; + } + const registration = JSON.parse(droneJson); + const success = await socketClient.requestWorkspaceMode( + registration, + project, + session, + mode, + ); + if (!success) { + showToast(`Cannot switch to ${mode} mode: workspace is not idle`); + } + } catch (err) { + showToast(`Failed to change workspace mode: ${err instanceof Error ? err.message : 'Unknown error'}`); } }; diff --git a/gadget-code/frontend/src/pages/ProjectManager.tsx b/gadget-code/frontend/src/pages/ProjectManager.tsx index ea640ad..2b4573c 100644 --- a/gadget-code/frontend/src/pages/ProjectManager.tsx +++ b/gadget-code/frontend/src/pages/ProjectManager.tsx @@ -703,6 +703,7 @@ export default function ProjectManager({ user }: ProjectManagerProps) { console.error("Failed to lock drone session"); return; } + localStorage.setItem('dtp_drone_registration', JSON.stringify(selectedDrone)); navigate(`/projects/${selectedProject._id}/chat-session/${sessionId}`); } catch (err) { console.error("Failed to open chat session", err); diff --git a/gadget-code/src/lib/code-session.ts b/gadget-code/src/lib/code-session.ts index c2d1929..6708c39 100644 --- a/gadget-code/src/lib/code-session.ts +++ b/gadget-code/src/lib/code-session.ts @@ -37,6 +37,7 @@ export class CodeSession extends SocketSession { super.register(); this.socket.on("requestSessionLock", this.onRequestSessionLock.bind(this)); + this.socket.on("requestWorkspaceMode", this.onRequestWorkspaceMode.bind(this)); this.socket.on("submitPrompt", this.onSubmitPrompt.bind(this)); } @@ -90,6 +91,43 @@ export class CodeSession extends SocketSession { ); } + /** + * Called when the IDE sends a requestWorkspaceMode event to change the drone's + * workspace mode. + * @param registration the gadget-drone registration + * @param project the project + * @param chatSession the chat session + * @param mode the requested workspace mode + * @param cb response callback with success and current mode + */ + onRequestWorkspaceMode( + registration: IDroneRegistration, + project: IProject, + chatSession: IChatSession, + mode: WorkspaceMode, + cb: (success: boolean, currentMode: WorkspaceMode) => void, + ) { + if (!this.selectedDrone) { + this.log.warn("workspace mode request rejected: no drone selected"); + return cb(false, this.workspaceMode); + } + + const droneSession = SocketService.getDroneSession(this.selectedDrone); + droneSession.socket.emit( + "requestWorkspaceMode", + registration, + project, + chatSession, + mode, + (success: boolean, currentMode: WorkspaceMode) => { + if (success) { + this.workspaceMode = currentMode; + } + cb(success, currentMode); + }, + ); + } + /** * Called when the IDE submits a prompt to be processed by the agent. * Creates a ChatTurn document and sends a work order to the selected drone.