Adds real-time subprocess monitoring to the IDE via the callback-chain and subscribe/broadcast socket patterns: Shared types (packages/api): - New subprocess.ts message types: SubProcessStat, RequestProcessStatsMessage, SubscribeDroneMessage, UnsubscribeDroneMessage - New socket events in socket.ts: requestProcessStats, subscribeDrone, unsubscribeDrone (ClientToServer); drone:log, drone:status (ServerToClient) Drone (gadget-drone): - onRequestProcessStats handler calls SubProcessService.ps() + summarize(), returns typed SubProcessStat[] with status detection (exitCode/killed) Backend routing (gadget-code): - SocketService: getDroneSessionByRegistrationId(), droneMonitorIndex map, addDroneMonitor()/removeDroneMonitor()/broadcastToMonitors() - CodeSession: requestProcessStats proxy, subscribeDrone/unsubscribeDrone - DroneSession: broadcast drone:log + drone:status to monitor subscribers in addition to existing chat-session routing Frontend socket layer: - SocketClient: requestProcessStats(), subscribeDrone(), unsubscribeDrone() - drone:log + drone:status events forwarded to event bus Frontend components: - LogRenderer — reusable log rendering core extracted from LogPanel - SubProcessTable — btop-style process table with status dots - DroneMonitorGauge — canvas oscilloscope waveform (studio-equipment aesthetic) - DroneMonitor — 3-gauge container (CPU/red, NETWORK/cyan, FILE I/O/green) Frontend pages: - DroneManager: live log streaming, SubProcessTable, DroneMonitor gauges, 1-second polling, auto-scroll log with pause-on-scroll-up - Home/DroneInspector: process count summary, mini LogRenderer, navigate-to-manager link Documentation: - Updated gadget-drone/docs/subprocess.md Phase 2 section - Updated gadget-code/docs/ui-design-guide.md Phase 2 section - Updated docs/socket-protocol.md with new events, sequences, and patterns
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import { useState, useEffect, useRef } from 'react';
|
|
import DroneMonitorGauge from './DroneMonitorGauge';
|
|
|
|
interface DroneMonitorProps {
|
|
visible: boolean;
|
|
}
|
|
|
|
function generateWave(base: number, variance: number, length: number): number[] {
|
|
const wave: number[] = [];
|
|
for (let i = 0; i < length; i++) {
|
|
wave.push(base + Math.sin(i * 0.1) * variance + (Math.random() - 0.5) * variance * 0.5);
|
|
}
|
|
return wave;
|
|
}
|
|
|
|
export default function DroneMonitor({ visible }: DroneMonitorProps) {
|
|
const [cpuData, setCpuData] = useState<number[]>(() => generateWave(35, 20, 60));
|
|
const [netData, setNetData] = useState<number[]>(() => generateWave(20, 15, 60));
|
|
const [ioData, setIoData] = useState<number[]>(() => generateWave(10, 10, 60));
|
|
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!visible) {
|
|
if (intervalRef.current) {
|
|
clearInterval(intervalRef.current);
|
|
intervalRef.current = null;
|
|
}
|
|
return;
|
|
}
|
|
|
|
intervalRef.current = setInterval(() => {
|
|
setCpuData((prev) => {
|
|
const next = [...prev.slice(1), Math.max(0, Math.min(100, prev[prev.length - 1]! + (Math.random() - 0.5) * 10))];
|
|
return next;
|
|
});
|
|
setNetData((prev) => {
|
|
const next = [...prev.slice(1), Math.max(0, Math.min(100, prev[prev.length - 1]! + (Math.random() - 0.5) * 8))];
|
|
return next;
|
|
});
|
|
setIoData((prev) => {
|
|
const next = [...prev.slice(1), Math.max(0, Math.min(100, prev[prev.length - 1]! + (Math.random() - 0.5) * 6))];
|
|
return next;
|
|
});
|
|
}, 200);
|
|
|
|
return () => {
|
|
if (intervalRef.current) {
|
|
clearInterval(intervalRef.current);
|
|
intervalRef.current = null;
|
|
}
|
|
};
|
|
}, [visible]);
|
|
|
|
return (
|
|
<div className="bg-bg-secondary border border-border-default rounded overflow-hidden">
|
|
<div className="p-2 border-b border-border-subtle bg-bg-tertiary">
|
|
<h3 className="text-xs font-semibold text-text-secondary uppercase tracking-wider">
|
|
Resource Monitor
|
|
</h3>
|
|
</div>
|
|
<div className="flex gap-1 p-1 justify-center">
|
|
<DroneMonitorGauge
|
|
label="CPU"
|
|
data={cpuData}
|
|
unit="%"
|
|
color="#c20600"
|
|
/>
|
|
<DroneMonitorGauge
|
|
label="NETWORK"
|
|
data={netData}
|
|
unit="%"
|
|
color="#06b6d4"
|
|
/>
|
|
<DroneMonitorGauge
|
|
label="FILE I/O"
|
|
data={ioData}
|
|
unit="%"
|
|
color="#22c55e"
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|