(null);
@@ -327,6 +329,7 @@ export function GraphView({
}}
onRequestClose={onRequestClose}
onRequestPinAsTab={onRequestPinAsTab}
+ onRequestFullscreen={onRequestFullscreen}
teamName={data.teamName}
teamColor={data.teamColor}
isAlive={data.isAlive}
diff --git a/src/renderer/components/team/TeamDetailView.tsx b/src/renderer/components/team/TeamDetailView.tsx
index 4a01b5c0..878c9565 100644
--- a/src/renderer/components/team/TeamDetailView.tsx
+++ b/src/renderer/components/team/TeamDetailView.tsx
@@ -214,18 +214,46 @@ export const TeamDetailView = ({ teamName }: TeamDetailViewProps): React.JSX.Ele
}
}, [editorOpen, graphOpen]);
- // Listen for Cmd+Shift+G keyboard shortcut
+ // Listen for Cmd+Shift+G keyboard shortcut — opens graph tab
useEffect(() => {
const handler = (e: Event) => {
const detail = (e as CustomEvent).detail;
if (detail?.teamName === teamName) {
- setGraphOpen((prev) => !prev);
+ useStore.getState().openTab({
+ type: 'graph',
+ label: `${teamName} Graph`,
+ teamName,
+ });
}
};
window.addEventListener('toggle-team-graph', handler);
return () => window.removeEventListener('toggle-team-graph', handler);
}, [teamName]);
+ // Listen for graph tab actions (open task, send message)
+ useEffect(() => {
+ const onOpenTask = (e: Event) => {
+ const { teamName: tn, taskId } = (e as CustomEvent).detail ?? {};
+ if (tn !== teamName || !data) return;
+ const task = data.tasks.find((t: { id: string }) => t.id === taskId);
+ if (task) setSelectedTask(task);
+ };
+ const onSendMsg = (e: Event) => {
+ const { teamName: tn, memberName } = (e as CustomEvent).detail ?? {};
+ if (tn !== teamName) return;
+ setSendDialogRecipient(memberName);
+ setSendDialogDefaultText(undefined);
+ setSendDialogDefaultChip(undefined);
+ setSendDialogOpen(true);
+ };
+ window.addEventListener('graph:open-task', onOpenTask);
+ window.addEventListener('graph:send-message', onSendMsg);
+ return () => {
+ window.removeEventListener('graph:open-task', onOpenTask);
+ window.removeEventListener('graph:send-message', onSendMsg);
+ };
+ });
+
const [sendDialogOpen, setSendDialogOpen] = useState(false);
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
const [stoppingTeam, setStoppingTeam] = useState(false);
@@ -1432,7 +1460,11 @@ export const TeamDetailView = ({ teamName }: TeamDetailViewProps): React.JSX.Ele
className="h-6 gap-1 px-2 text-xs text-[var(--color-text-muted)] hover:text-[var(--color-text)]"
onClick={(e) => {
e.stopPropagation();
- setGraphOpen(true);
+ useStore.getState().openTab({
+ type: 'graph',
+ label: `${data.config.name} Graph`,
+ teamName,
+ });
}}
>
diff --git a/src/renderer/features/agent-graph/ui/TeamGraphTab.tsx b/src/renderer/features/agent-graph/ui/TeamGraphTab.tsx
index 3238ea49..041bac55 100644
--- a/src/renderer/features/agent-graph/ui/TeamGraphTab.tsx
+++ b/src/renderer/features/agent-graph/ui/TeamGraphTab.tsx
@@ -1,8 +1,9 @@
/**
* TeamGraphTab — wraps GraphView for use as a dedicated tab.
+ * Provides Fullscreen button that opens the overlay.
*/
-import { useCallback } from 'react';
+import { useCallback, useState, lazy, Suspense } from 'react';
import { GraphView } from '@claude-teams/agent-graph';
@@ -10,22 +11,73 @@ import { useTeamGraphAdapter } from '../adapters/useTeamGraphAdapter';
import type { GraphDomainRef, GraphEventPort } from '@claude-teams/agent-graph';
+const TeamGraphOverlay = lazy(() =>
+ import('./TeamGraphOverlay').then((m) => ({ default: m.TeamGraphOverlay }))
+);
+
export interface TeamGraphTabProps {
teamName: string;
}
export const TeamGraphTab = ({ teamName }: TeamGraphTabProps): React.JSX.Element => {
const graphData = useTeamGraphAdapter(teamName);
+ const [fullscreen, setFullscreen] = useState(false);
const events: GraphEventPort = {
- onNodeDoubleClick: useCallback((ref: GraphDomainRef) => {
- console.log('Double-click in tab:', ref);
- }, []),
+ onNodeDoubleClick: useCallback(
+ (ref: GraphDomainRef) => {
+ // Dispatch to TeamDetailView's dialog system via CustomEvent
+ if (ref.kind === 'task') {
+ window.dispatchEvent(
+ new CustomEvent('graph:open-task', { detail: { teamName, taskId: ref.taskId } })
+ );
+ } else if (ref.kind === 'member') {
+ window.dispatchEvent(
+ new CustomEvent('graph:send-message', {
+ detail: { teamName, memberName: ref.memberName },
+ })
+ );
+ }
+ },
+ [teamName]
+ ),
+ onSendMessage: useCallback(
+ (memberName: string) => {
+ window.dispatchEvent(
+ new CustomEvent('graph:send-message', { detail: { teamName, memberName } })
+ );
+ },
+ [teamName]
+ ),
+ onOpenTaskDetail: useCallback(
+ (taskId: string) => {
+ window.dispatchEvent(new CustomEvent('graph:open-task', { detail: { teamName, taskId } }));
+ },
+ [teamName]
+ ),
+ onOpenMemberProfile: useCallback(
+ (memberName: string) => {
+ window.dispatchEvent(
+ new CustomEvent('graph:send-message', { detail: { teamName, memberName } })
+ );
+ },
+ [teamName]
+ ),
};
return (
-
+ setFullscreen(true)}
+ />
+ {fullscreen && (
+
+ setFullscreen(false)} />
+
+ )}
);
};