- Replace fixed 3s reconnect with exponential backoff (1s/2s/4s/8s/16s/30s, capped). Reduces console noise when upstream WebSocket is blocked (e.g. DSM reverse proxy without WS upgrade headers). - ws.onerror swallowed (onclose still schedules reconnect) so the browser stops printing an unhandled-error pair per attempt. - Expose reconnectAttempt in hook; TopBar shows 'Connecting…' pre-first-attempt and 'Disconnected · 재연결 시도 #N' after. Root cause of WS failure is upstream (curl proves the endpoint itself is fine — see DSM reverse proxy WebSocket headers). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
76 lines
2.2 KiB
JavaScript
76 lines
2.2 KiB
JavaScript
// src/pages/agent-office/AgentOffice.jsx
|
|
import { useState, useCallback } from 'react';
|
|
import { useAgentManager } from './hooks/useAgentManager.js';
|
|
import { AGENT_META } from './constants.js';
|
|
import TopBar from './components/TopBar.jsx';
|
|
import AgentGrid from './components/AgentGrid.jsx';
|
|
import SidePanel from './components/SidePanel.jsx';
|
|
import EmptyDetailPanel from './components/EmptyDetailPanel.jsx';
|
|
import './AgentOffice.css';
|
|
|
|
export default function AgentOffice() {
|
|
const {
|
|
agents, pendingTasks, notifications, connected, reconnectAttempt,
|
|
refreshTrigger, clearNotifications
|
|
} = useAgentManager();
|
|
|
|
// selectedAgent: null | active agent id | "placeholder-N"
|
|
const [selectedAgent, setSelectedAgent] = useState(null);
|
|
|
|
const handleSelectAgent = useCallback((agentId) => {
|
|
setSelectedAgent(agentId);
|
|
clearNotifications(agentId);
|
|
}, [clearNotifications]);
|
|
|
|
const handleSelectPlaceholder = useCallback((placeholderKey) => {
|
|
setSelectedAgent(placeholderKey);
|
|
}, []);
|
|
|
|
const handleClose = useCallback(() => {
|
|
setSelectedAgent(null);
|
|
}, []);
|
|
|
|
const pendingTask = selectedAgent && AGENT_META[selectedAgent]
|
|
? pendingTasks.find(t => t.agent_id === selectedAgent)
|
|
: null;
|
|
|
|
let rightPanel;
|
|
if (selectedAgent === null) {
|
|
rightPanel = <EmptyDetailPanel variant="initial" />;
|
|
} else if (selectedAgent.startsWith('placeholder-')) {
|
|
rightPanel = <EmptyDetailPanel variant="placeholder" onClose={handleClose} />;
|
|
} else {
|
|
rightPanel = (
|
|
<SidePanel
|
|
agentId={selectedAgent}
|
|
agentState={agents[selectedAgent]}
|
|
pendingTask={pendingTask}
|
|
onClose={handleClose}
|
|
refreshTrigger={refreshTrigger}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="ao-root">
|
|
<TopBar connected={connected} reconnectAttempt={reconnectAttempt} />
|
|
<div className="ao-main">
|
|
<div className="ao-grid-wrap">
|
|
<AgentGrid
|
|
agents={agents}
|
|
notifications={notifications}
|
|
selectedAgent={selectedAgent}
|
|
onSelectAgent={handleSelectAgent}
|
|
onSelectPlaceholder={handleSelectPlaceholder}
|
|
/>
|
|
</div>
|
|
{rightPanel}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function Component() {
|
|
return <AgentOffice />;
|
|
}
|