workspace management progress
This commit is contained in:
parent
4ec31764d5
commit
8b5df3827b
@ -19,62 +19,17 @@ export default function LogPanel({ logs, expanded, workspaceMode, onToggleExpand
|
|||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (scrollRef.current && !expanded) {
|
if (scrollRef.current) {
|
||||||
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
||||||
}
|
}
|
||||||
}, [logs, expanded]);
|
}, [logs]);
|
||||||
|
|
||||||
if (expanded) {
|
|
||||||
return (
|
|
||||||
<div className="absolute inset-0 z-10 bg-bg-primary flex flex-col border-t border-border-subtle">
|
|
||||||
<div className="flex items-center justify-between px-4 py-2 bg-bg-tertiary shrink-0 border-b border-border-subtle">
|
|
||||||
<h3 className="text-sm font-semibold text-text-secondary uppercase tracking-wider">
|
|
||||||
Log
|
|
||||||
</h3>
|
|
||||||
<button
|
|
||||||
onClick={onToggleExpand}
|
|
||||||
className="w-6 h-6 flex items-center justify-center text-text-muted hover:text-text-secondary transition-colors"
|
|
||||||
title="Collapse"
|
|
||||||
>
|
|
||||||
▾
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
ref={scrollRef}
|
|
||||||
className="flex-1 overflow-y-auto p-2 font-mono text-xs"
|
|
||||||
>
|
|
||||||
{logs.length === 0 ? (
|
|
||||||
<div className="text-text-muted">No log entries</div>
|
|
||||||
) : (
|
|
||||||
logs.map((entry) => (
|
|
||||||
<div key={entry.id} className="flex gap-2 py-0.5">
|
|
||||||
<span className="text-text-muted shrink-0">
|
|
||||||
{entry.timestamp.toLocaleTimeString()}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={`shrink-0 ${
|
|
||||||
entry.level === 'error'
|
|
||||||
? 'text-red-500'
|
|
||||||
: entry.level === 'warn'
|
|
||||||
? 'text-yellow-500'
|
|
||||||
: entry.level === 'info'
|
|
||||||
? 'text-blue-400'
|
|
||||||
: 'text-text-secondary'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
[{entry.level.toUpperCase()}]
|
|
||||||
</span>
|
|
||||||
<span className="text-text-primary">{entry.message}</span>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-48 border-t border-border-subtle bg-bg-secondary flex flex-col shrink-0">
|
<div
|
||||||
|
className={`border-t border-border-subtle bg-bg-secondary flex flex-col ${
|
||||||
|
expanded ? 'flex-1' : 'h-48'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<div className="flex items-center justify-between px-4 py-2 bg-bg-tertiary shrink-0">
|
<div className="flex items-center justify-between px-4 py-2 bg-bg-tertiary shrink-0">
|
||||||
<h3 className="text-sm font-semibold text-text-secondary uppercase tracking-wider">
|
<h3 className="text-sm font-semibold text-text-secondary uppercase tracking-wider">
|
||||||
Log
|
Log
|
||||||
@ -82,9 +37,9 @@ export default function LogPanel({ logs, expanded, workspaceMode, onToggleExpand
|
|||||||
<button
|
<button
|
||||||
onClick={onToggleExpand}
|
onClick={onToggleExpand}
|
||||||
className="w-6 h-6 flex items-center justify-center text-text-muted hover:text-text-secondary transition-colors"
|
className="w-6 h-6 flex items-center justify-center text-text-muted hover:text-text-secondary transition-colors"
|
||||||
title="Expand"
|
title={expanded ? 'Collapse' : 'Expand'}
|
||||||
>
|
>
|
||||||
▴
|
{expanded ? '▾' : '▴'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -223,15 +223,24 @@ export default function ChatSessionView() {
|
|||||||
const handleWorkspaceModeChange = async (mode: WorkspaceMode) => {
|
const handleWorkspaceModeChange = async (mode: WorkspaceMode) => {
|
||||||
if (!session || !project) return;
|
if (!session || !project) return;
|
||||||
|
|
||||||
const registration = session.drone as any;
|
try {
|
||||||
const success = await socketClient.requestWorkspaceMode(
|
const droneJson = localStorage.getItem('dtp_drone_registration');
|
||||||
registration,
|
if (!droneJson) {
|
||||||
project,
|
showToast('No drone registration found');
|
||||||
session,
|
return;
|
||||||
mode,
|
}
|
||||||
);
|
const registration = JSON.parse(droneJson);
|
||||||
if (!success) {
|
const success = await socketClient.requestWorkspaceMode(
|
||||||
showToast(`Cannot switch to ${mode} mode: workspace is not idle`);
|
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'}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -703,6 +703,7 @@ export default function ProjectManager({ user }: ProjectManagerProps) {
|
|||||||
console.error("Failed to lock drone session");
|
console.error("Failed to lock drone session");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
localStorage.setItem('dtp_drone_registration', JSON.stringify(selectedDrone));
|
||||||
navigate(`/projects/${selectedProject._id}/chat-session/${sessionId}`);
|
navigate(`/projects/${selectedProject._id}/chat-session/${sessionId}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to open chat session", err);
|
console.error("Failed to open chat session", err);
|
||||||
|
|||||||
@ -37,6 +37,7 @@ export class CodeSession extends SocketSession {
|
|||||||
super.register();
|
super.register();
|
||||||
|
|
||||||
this.socket.on("requestSessionLock", this.onRequestSessionLock.bind(this));
|
this.socket.on("requestSessionLock", this.onRequestSessionLock.bind(this));
|
||||||
|
this.socket.on("requestWorkspaceMode", this.onRequestWorkspaceMode.bind(this));
|
||||||
this.socket.on("submitPrompt", this.onSubmitPrompt.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.
|
* 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.
|
* Creates a ChatTurn document and sends a work order to the selected drone.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user