@cognizant-ai-lab/ui-common 1.3.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -0
- package/dist/Theme/Palettes.d.ts +18 -0
- package/dist/Theme/Palettes.js +94 -0
- package/dist/Theme/Theme.d.ts +22 -0
- package/dist/Theme/Theme.js +58 -0
- package/dist/components/AgentChat/ChatCommon.d.ts +4 -2
- package/dist/components/AgentChat/ChatCommon.js +141 -105
- package/dist/components/AgentChat/ControlButtons.js +3 -1
- package/dist/components/AgentChat/FormattedMarkdown.d.ts +4 -4
- package/dist/components/AgentChat/FormattedMarkdown.js +5 -7
- package/dist/components/AgentChat/LlmChatButton.d.ts +2 -6
- package/dist/components/AgentChat/LlmChatButton.js +3 -3
- package/dist/components/AgentChat/UserQueryDisplay.js +2 -4
- package/dist/components/AgentChat/Utils.d.ts +2 -1
- package/dist/components/AgentChat/Utils.js +4 -1
- package/dist/components/AgentChat/VoiceChat/MicrophoneButton.d.ts +2 -2
- package/dist/components/AgentChat/VoiceChat/VoiceChat.d.ts +3 -3
- package/dist/components/AgentChat/VoiceChat/VoiceChat.js +5 -5
- package/dist/components/ChatBot/ChatBot.js +2 -2
- package/dist/components/Common/Breadcrumbs.js +1 -1
- package/dist/components/Common/{confirmationModal.js → ConfirmationModal.js} +2 -5
- package/dist/components/Common/CustomerLogo.d.ts +17 -0
- package/dist/components/Common/CustomerLogo.js +49 -0
- package/dist/components/Common/Footer.d.ts +18 -0
- package/dist/components/Common/Footer.js +59 -0
- package/dist/components/Common/LlmChatOptionsButton.d.ts +1 -4
- package/dist/components/Common/LlmChatOptionsButton.js +4 -4
- package/dist/components/Common/LoadingSpinner.js +1 -1
- package/dist/components/Common/MUIAccordion.d.ts +2 -2
- package/dist/components/Common/MUIAccordion.js +2 -12
- package/dist/components/Common/MUIAlert.d.ts +2 -1
- package/dist/components/Common/MUIAlert.js +4 -1
- package/dist/components/Common/MUIDialog.d.ts +1 -1
- package/dist/components/Common/MUIDialog.js +1 -1
- package/dist/components/Common/Navbar.d.ts +3 -1
- package/dist/components/Common/Navbar.js +60 -35
- package/dist/components/Common/PageLoader.js +3 -4
- package/dist/components/Common/Snackbar.d.ts +4 -1
- package/dist/components/Common/Snackbar.js +11 -19
- package/dist/components/Common/notification.d.ts +3 -3
- package/dist/components/Common/notification.js +6 -6
- package/dist/components/ErrorPage/ErrorBoundary.d.ts +2 -2
- package/dist/components/ErrorPage/ErrorBoundary.js +1 -1
- package/dist/components/ErrorPage/ErrorPage.js +6 -5
- package/dist/components/MultiAgentAccelerator/AgentConversations.d.ts +17 -0
- package/dist/components/MultiAgentAccelerator/AgentConversations.js +77 -0
- package/dist/components/MultiAgentAccelerator/AgentCounts.d.ts +12 -0
- package/dist/components/MultiAgentAccelerator/AgentCounts.js +21 -0
- package/dist/components/MultiAgentAccelerator/AgentFlow.d.ts +6 -4
- package/dist/components/MultiAgentAccelerator/AgentFlow.js +106 -185
- package/dist/components/MultiAgentAccelerator/AgentNode.d.ts +7 -5
- package/dist/components/MultiAgentAccelerator/AgentNode.js +93 -50
- package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +20 -17
- package/dist/components/MultiAgentAccelerator/GraphLayouts.js +16 -14
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +2 -3
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +214 -55
- package/dist/components/MultiAgentAccelerator/PlasmaEdge.d.ts +1 -1
- package/dist/components/MultiAgentAccelerator/PlasmaEdge.js +14 -12
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +15 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +104 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +17 -0
- package/dist/components/MultiAgentAccelerator/{Sidebar.js → Sidebar/Sidebar.js} +146 -59
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +19 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +113 -0
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +26 -0
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +20 -0
- package/dist/components/MultiAgentAccelerator/ThoughtBubbleEdge.d.ts +10 -8
- package/dist/components/MultiAgentAccelerator/ThoughtBubbleEdge.js +1 -1
- package/dist/components/MultiAgentAccelerator/ThoughtBubbleOverlay.d.ts +3 -2
- package/dist/components/MultiAgentAccelerator/ThoughtBubbleOverlay.js +10 -13
- package/dist/components/MultiAgentAccelerator/const.d.ts +1 -3
- package/dist/components/MultiAgentAccelerator/const.js +4 -18
- package/dist/components/Settings/FadingCheckmark.d.ts +14 -0
- package/dist/components/Settings/FadingCheckmark.js +43 -0
- package/dist/components/Settings/SettingsDialog.d.ts +9 -0
- package/dist/components/Settings/SettingsDialog.js +265 -0
- package/dist/const.d.ts +1 -2
- package/dist/const.js +2 -3
- package/dist/controller/Types/AgentIconSuggestions.d.ts +4 -0
- package/dist/controller/Types/AgentIconSuggestions.js +1 -0
- package/dist/controller/Types/Branding.d.ts +12 -0
- package/dist/controller/Types/Branding.js +1 -0
- package/dist/controller/Types/NetworkIconSuggestions.d.ts +4 -0
- package/dist/controller/Types/NetworkIconSuggestions.js +1 -0
- package/dist/controller/agent/Agent.d.ts +32 -12
- package/dist/controller/agent/Agent.js +71 -19
- package/dist/controller/llm/LlmChat.d.ts +1 -1
- package/dist/controller/llm/LlmChat.js +2 -2
- package/dist/index.d.ts +10 -5
- package/dist/index.js +10 -5
- package/dist/state/{environment.d.ts → Environment.d.ts} +2 -0
- package/dist/state/{environment.js → Environment.js} +2 -0
- package/dist/state/Settings.d.ts +62 -0
- package/dist/state/Settings.js +62 -0
- package/dist/state/TemporaryNetworks.d.ts +32 -0
- package/dist/state/TemporaryNetworks.js +26 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/Authentication.d.ts +2 -2
- package/dist/utils/Authentication.js +6 -6
- package/dist/utils/text.d.ts +2 -2
- package/dist/utils/text.js +3 -5
- package/dist/utils/title.d.ts +1 -1
- package/dist/utils/title.js +2 -2
- package/dist/utils/useLocalStorage.d.ts +1 -1
- package/dist/utils/useLocalStorage.js +3 -3
- package/dist/utils/zIndexLayers.d.ts +1 -1
- package/dist/utils/zIndexLayers.js +3 -15
- package/package.json +23 -21
- package/dist/components/MultiAgentAccelerator/Sidebar.d.ts +0 -12
- package/dist/utils/Theme.d.ts +0 -7
- package/dist/utils/Theme.js +0 -7
- package/dist/utils/agentConversations.d.ts +0 -24
- package/dist/utils/agentConversations.js +0 -113
- /package/dist/components/Common/{confirmationModal.d.ts → ConfirmationModal.d.ts} +0 -0
|
@@ -18,45 +18,30 @@ import AdjustRoundedIcon from "@mui/icons-material/AdjustRounded";
|
|
|
18
18
|
import ChatBubbleOutlineIcon from "@mui/icons-material/ChatBubbleOutline";
|
|
19
19
|
import HubOutlinedIcon from "@mui/icons-material/HubOutlined";
|
|
20
20
|
import ScatterPlotOutlinedIcon from "@mui/icons-material/ScatterPlotOutlined";
|
|
21
|
-
import { ToggleButton, ToggleButtonGroup, useColorScheme, useTheme } from "@mui/material";
|
|
22
21
|
import Box from "@mui/material/Box";
|
|
22
|
+
import { useTheme } from "@mui/material/styles";
|
|
23
|
+
import ToggleButton from "@mui/material/ToggleButton";
|
|
24
|
+
import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
|
|
23
25
|
import Tooltip from "@mui/material/Tooltip";
|
|
24
26
|
import Typography from "@mui/material/Typography";
|
|
27
|
+
import { applyNodeChanges, Background, ConnectionMode, ControlButton, Controls, ReactFlow, useReactFlow, useStore, } from "@xyflow/react";
|
|
25
28
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
26
|
-
import { applyNodeChanges, Background, ConnectionMode, ControlButton, Controls, ReactFlow, useReactFlow, useStore, } from "reactflow";
|
|
27
29
|
import { AgentNode, NODE_HEIGHT, NODE_WIDTH } from "./AgentNode.js";
|
|
28
|
-
import {
|
|
29
|
-
import { addThoughtBubbleEdge, layoutLinear, layoutRadial
|
|
30
|
+
import { BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, LEVEL_SPACING } from "./const.js";
|
|
31
|
+
import { addThoughtBubbleEdge, layoutLinear, layoutRadial } from "./GraphLayouts.js";
|
|
30
32
|
import { PlasmaEdge } from "./PlasmaEdge.js";
|
|
31
33
|
import { ThoughtBubbleEdge } from "./ThoughtBubbleEdge.js";
|
|
32
34
|
import { ThoughtBubbleOverlay } from "./ThoughtBubbleOverlay.js";
|
|
33
|
-
import {
|
|
35
|
+
import { usePalette } from "../../Theme/Palettes.js";
|
|
34
36
|
import { getZIndex } from "../../utils/zIndexLayers.js";
|
|
35
37
|
// #endregion: Types
|
|
36
38
|
// #region: Constants
|
|
37
39
|
// Timeout for thought bubbles is set to 10 seconds
|
|
38
40
|
const THOUGHT_BUBBLE_TIMEOUT_MS = 10_000;
|
|
39
|
-
// Maximum number of thought bubbles allowed on screen at any time. As more arrive, older ones are removed.
|
|
40
|
-
const MAX_THOUGHT_BUBBLES = 5;
|
|
41
41
|
// #endregion: Constants
|
|
42
|
-
export const AgentFlow = ({ agentCounts, agentsInNetwork, currentConversations, id, isAwaitingLlm, isStreaming, thoughtBubbleEdges, setThoughtBubbleEdges, }) => {
|
|
42
|
+
export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork, currentConversations, id, isAwaitingLlm, isStreaming, thoughtBubbleEdges, setThoughtBubbleEdges, }) => {
|
|
43
43
|
const theme = useTheme();
|
|
44
44
|
const { fitView } = useReactFlow();
|
|
45
|
-
// Helper functions to update thought bubble edges state immutably
|
|
46
|
-
const addThoughtBubbleEdgeHelper = useCallback((conversationId, edge) => {
|
|
47
|
-
setThoughtBubbleEdges((prev) => {
|
|
48
|
-
const newMap = new Map(prev);
|
|
49
|
-
addThoughtBubbleEdge(newMap, conversationId, edge);
|
|
50
|
-
return newMap;
|
|
51
|
-
});
|
|
52
|
-
}, [setThoughtBubbleEdges]);
|
|
53
|
-
const removeThoughtBubbleEdgeHelper = useCallback((conversationId) => {
|
|
54
|
-
setThoughtBubbleEdges((prev) => {
|
|
55
|
-
const newMap = new Map(prev);
|
|
56
|
-
removeThoughtBubbleEdge(newMap, conversationId);
|
|
57
|
-
return newMap;
|
|
58
|
-
});
|
|
59
|
-
}, [setThoughtBubbleEdges]);
|
|
60
45
|
const handleResize = useCallback(() => {
|
|
61
46
|
fitView(); // Adjusts the view to fit after resizing
|
|
62
47
|
}, [fitView, isAwaitingLlm]);
|
|
@@ -64,18 +49,10 @@ export const AgentFlow = ({ agentCounts, agentsInNetwork, currentConversations,
|
|
|
64
49
|
window.addEventListener("resize", handleResize);
|
|
65
50
|
return () => window.removeEventListener("resize", handleResize);
|
|
66
51
|
}, [handleResize]);
|
|
67
|
-
// Save this as a mutable ref so child nodes see updates
|
|
68
|
-
const conversationsRef = useRef(currentConversations);
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
conversationsRef.current = currentConversations;
|
|
71
|
-
}, [currentConversations]);
|
|
72
52
|
const [layout, setLayout] = useState("radial");
|
|
73
53
|
const [coloringOption, setColoringOption] = useState("depth");
|
|
74
54
|
const [enableRadialGuides, setEnableRadialGuides] = useState(true);
|
|
75
55
|
const [showThoughtBubbles, setShowThoughtBubbles] = useState(true);
|
|
76
|
-
// State for managing active thought bubbles with timers. Use ActiveThoughtBubble which
|
|
77
|
-
// references the original conversation via `conversationId`.
|
|
78
|
-
const [activeThoughtBubbles, setActiveThoughtBubbles] = useState([]);
|
|
79
56
|
// Track conversation IDs we've already processed to prevent re-adding after expiry
|
|
80
57
|
const processedConversationIdsRef = useRef(new Set());
|
|
81
58
|
// Track which bubble is currently being hovered
|
|
@@ -83,159 +60,96 @@ export const AgentFlow = ({ agentCounts, agentsInNetwork, currentConversations,
|
|
|
83
60
|
const handleBubbleHoverChange = useCallback((bubbleId) => {
|
|
84
61
|
hoveredBubbleIdRef.current = bubbleId;
|
|
85
62
|
}, []);
|
|
63
|
+
// Ref for isStreaming, read inside the cleanup interval.
|
|
64
|
+
const isStreamingRef = useRef(isStreaming);
|
|
65
|
+
// Keep the ref current after every render.
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
isStreamingRef.current = isStreaming;
|
|
68
|
+
});
|
|
86
69
|
// Clear processed conversation IDs when thought bubble edges are cleared (streaming ends)
|
|
87
70
|
useEffect(() => {
|
|
88
71
|
if (thoughtBubbleEdges.size === 0) {
|
|
89
72
|
processedConversationIdsRef.current.clear();
|
|
90
73
|
}
|
|
91
74
|
}, [thoughtBubbleEdges.size]);
|
|
92
|
-
//
|
|
75
|
+
// Add new thought bubble edges for incoming conversations.
|
|
93
76
|
useEffect(() => {
|
|
94
|
-
if (!currentConversations || currentConversations.length === 0)
|
|
95
|
-
return;
|
|
96
|
-
|
|
97
|
-
setActiveThoughtBubbles((prevBubbles) => {
|
|
98
|
-
const newBubbles = [];
|
|
99
|
-
// Check the text that user sees as one level of duplicate prevention
|
|
77
|
+
if (!currentConversations || currentConversations.length === 0)
|
|
78
|
+
return;
|
|
79
|
+
setThoughtBubbleEdges((prev) => {
|
|
100
80
|
const processedText = new Set();
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
});
|
|
108
|
-
// Only add bubbles for conversations with unique parsed content
|
|
109
|
-
// TODO: Even though there are two duplicate checks here, some duplicates still get through
|
|
81
|
+
for (const entry of prev.values()) {
|
|
82
|
+
const text = entry.edge.data?.text?.trim();
|
|
83
|
+
if (text)
|
|
84
|
+
processedText.add(text);
|
|
85
|
+
}
|
|
86
|
+
let edgesMap = null;
|
|
110
87
|
for (const conv of currentConversations) {
|
|
111
88
|
const convText = conv.text?.trim();
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// Haven't already displayed this text (duplicate check #1)
|
|
89
|
+
const agentList = Array.from(conv.agents);
|
|
90
|
+
if (convText &&
|
|
91
|
+
agentList.length >= 2 &&
|
|
116
92
|
!processedText.has(convText) &&
|
|
117
|
-
// Haven't already displayed this conversation ID (duplicate check #2)
|
|
118
93
|
!processedConversationIdsRef.current.has(conv.id)) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// to "now" so the bubble timeout is measured from when the bubble appeared.
|
|
122
|
-
newBubbles.push({
|
|
123
|
-
agents: new Set(conv.agents),
|
|
124
|
-
conversationId: conv.id,
|
|
125
|
-
startedAt: new Date(),
|
|
126
|
-
type: conv.type,
|
|
127
|
-
// No text needed. This is for tracking which conversations are active.
|
|
128
|
-
});
|
|
129
|
-
// Mark this conversation ID as processed. This protects against duplicates if conversations have
|
|
130
|
-
// the same ID, although some duplicates still get through.
|
|
94
|
+
if (!edgesMap)
|
|
95
|
+
edgesMap = new Map(prev);
|
|
131
96
|
processedConversationIdsRef.current.add(conv.id);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
type:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// Include full agent list so overlays can point to all participants
|
|
149
|
-
agents: agentList,
|
|
150
|
-
type: conv.type, // Add conversation type for filtering
|
|
151
|
-
},
|
|
152
|
-
style: { pointerEvents: "none" },
|
|
153
|
-
};
|
|
154
|
-
addThoughtBubbleEdgeHelper(conv.id, edge);
|
|
155
|
-
}
|
|
97
|
+
processedText.add(convText);
|
|
98
|
+
const edge = {
|
|
99
|
+
id: `thought-bubble-${conv.id}`,
|
|
100
|
+
source: agentList[0],
|
|
101
|
+
target: agentList[1],
|
|
102
|
+
type: "thoughtBubbleEdge",
|
|
103
|
+
data: {
|
|
104
|
+
text: conv.text,
|
|
105
|
+
showAlways: showThoughtBubbles,
|
|
106
|
+
conversationId: conv.id,
|
|
107
|
+
agents: agentList,
|
|
108
|
+
type: conv.type,
|
|
109
|
+
},
|
|
110
|
+
style: { pointerEvents: "none" },
|
|
111
|
+
};
|
|
112
|
+
addThoughtBubbleEdge(edgesMap, conv.id, edge); // also enforces MAX_GLOBAL_THOUGHT_BUBBLES
|
|
156
113
|
}
|
|
157
114
|
}
|
|
158
|
-
|
|
159
|
-
return prevBubbles; // No changes
|
|
160
|
-
}
|
|
161
|
-
const allBubbles = [...prevBubbles, ...newBubbles];
|
|
162
|
-
// If we're over the limit, first remove expired bubbles, then oldest if still needed
|
|
163
|
-
if (allBubbles.length > MAX_THOUGHT_BUBBLES) {
|
|
164
|
-
const now = Date.now();
|
|
165
|
-
const dropped = [];
|
|
166
|
-
// First, filter out expired bubbles by checking THOUGHT_BUBBLE_TIMEOUT_MS
|
|
167
|
-
const activeBubbles = allBubbles.filter((bubble) => {
|
|
168
|
-
const age = now - bubble.startedAt.getTime();
|
|
169
|
-
const isExpired = age >= THOUGHT_BUBBLE_TIMEOUT_MS;
|
|
170
|
-
if (isExpired) {
|
|
171
|
-
dropped.push(bubble);
|
|
172
|
-
}
|
|
173
|
-
return !isExpired;
|
|
174
|
-
});
|
|
175
|
-
// If still over the limit after removing expired, remove oldest non-expired
|
|
176
|
-
let finalBubbles = activeBubbles;
|
|
177
|
-
if (activeBubbles.length > MAX_THOUGHT_BUBBLES) {
|
|
178
|
-
const sorted = activeBubbles.sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime());
|
|
179
|
-
finalBubbles = sorted.slice(-MAX_THOUGHT_BUBBLES);
|
|
180
|
-
const additionalDropped = sorted.slice(0, -MAX_THOUGHT_BUBBLES);
|
|
181
|
-
dropped.push(...additionalDropped);
|
|
182
|
-
}
|
|
183
|
-
// Clean up all dropped bubbles from edge cache
|
|
184
|
-
dropped.forEach((bubble) => {
|
|
185
|
-
removeThoughtBubbleEdgeHelper(bubble.conversationId);
|
|
186
|
-
});
|
|
187
|
-
return finalBubbles;
|
|
188
|
-
}
|
|
189
|
-
return allBubbles;
|
|
115
|
+
return edgesMap ?? prev;
|
|
190
116
|
});
|
|
191
|
-
}, [currentConversations,
|
|
192
|
-
//
|
|
117
|
+
}, [currentConversations, showThoughtBubbles, setThoughtBubbleEdges]);
|
|
118
|
+
// Cleanup expired thought bubble edges — created once on mount, reads isStreaming via ref.
|
|
193
119
|
useEffect(() => {
|
|
194
120
|
const cleanupInterval = setInterval(() => {
|
|
195
|
-
|
|
196
|
-
if (!isStreaming)
|
|
121
|
+
if (!isStreamingRef.current)
|
|
197
122
|
return;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (isHovered) {
|
|
208
|
-
return true;
|
|
209
|
-
}
|
|
210
|
-
// If bubble is expiring, remove it from the edge cache
|
|
211
|
-
if (!shouldKeep) {
|
|
212
|
-
removeThoughtBubbleEdgeHelper(bubble.conversationId);
|
|
123
|
+
const now = Date.now();
|
|
124
|
+
setThoughtBubbleEdges((prev) => {
|
|
125
|
+
let changed = false;
|
|
126
|
+
const edgesMap = new Map(prev);
|
|
127
|
+
for (const [convId, entry] of prev) {
|
|
128
|
+
const isHovered = hoveredBubbleIdRef.current === `thought-bubble-${convId}`;
|
|
129
|
+
if (!isHovered && now - entry.timestamp >= THOUGHT_BUBBLE_TIMEOUT_MS) {
|
|
130
|
+
edgesMap.delete(convId);
|
|
131
|
+
changed = true;
|
|
213
132
|
}
|
|
214
|
-
return shouldKeep;
|
|
215
|
-
});
|
|
216
|
-
// Only update if there are changes to prevent unnecessary re-renders
|
|
217
|
-
if (filteredBubbles.length !== prevBubbles.length) {
|
|
218
|
-
return filteredBubbles;
|
|
219
133
|
}
|
|
220
|
-
return
|
|
134
|
+
return changed ? edgesMap : prev;
|
|
221
135
|
});
|
|
222
|
-
}, 1000);
|
|
136
|
+
}, 1000);
|
|
223
137
|
return () => clearInterval(cleanupInterval);
|
|
224
|
-
}, [
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
// Shadow color for icon. TODO: use MUI theme system instead.
|
|
228
|
-
const shadowColor = darkMode ? "255, 255, 255" : "0, 0, 0";
|
|
138
|
+
}, []); // mount/unmount only
|
|
139
|
+
// Shadow color for icon
|
|
140
|
+
const shadowColor = theme.palette.mode === "dark" ? theme.palette.common.white : theme.palette.common.black;
|
|
229
141
|
const isHeatmap = coloringOption === "heatmap";
|
|
142
|
+
const palette = usePalette();
|
|
230
143
|
// Merge agents from active thought bubbles with agentsInNetwork for layout
|
|
231
144
|
// This ensures bubble edges persist even when agents disappear from the network
|
|
232
145
|
const bubbleAgentIds = useMemo(() => {
|
|
233
146
|
const ids = new Set();
|
|
234
|
-
|
|
235
|
-
|
|
147
|
+
thoughtBubbleEdges.forEach(({ edge }) => {
|
|
148
|
+
const agents = edge.data?.agents ?? [];
|
|
149
|
+
agents.forEach((agentId) => ids.add(agentId));
|
|
236
150
|
});
|
|
237
151
|
return ids;
|
|
238
|
-
}, [
|
|
152
|
+
}, [thoughtBubbleEdges]);
|
|
239
153
|
const mergedAgentsInNetwork = useMemo(() => {
|
|
240
154
|
// Add any missing agents from bubbles as minimal ConnectivityInfo
|
|
241
155
|
const existingIds = new Set(agentsInNetwork.map((a) => a.origin));
|
|
@@ -250,17 +164,17 @@ export const AgentFlow = ({ agentCounts, agentsInNetwork, currentConversations,
|
|
|
250
164
|
// Create the flow layout depending on user preference
|
|
251
165
|
// Memoize layoutResult so it only recalculates when relevant data changes
|
|
252
166
|
const layoutResult = useMemo(() => layout === "linear"
|
|
253
|
-
? layoutLinear(isHeatmap ? agentCounts : undefined, mergedAgentsInNetwork, currentConversations, isAwaitingLlm, thoughtBubbleEdges)
|
|
254
|
-
: layoutRadial(isHeatmap ? agentCounts : undefined, mergedAgentsInNetwork, currentConversations, isAwaitingLlm, thoughtBubbleEdges), [
|
|
255
|
-
layout,
|
|
256
|
-
coloringOption,
|
|
167
|
+
? layoutLinear(isHeatmap ? agentCounts : undefined, mergedAgentsInNetwork, currentConversations, isAwaitingLlm, thoughtBubbleEdges, agentIconSuggestions)
|
|
168
|
+
: layoutRadial(isHeatmap ? agentCounts : undefined, mergedAgentsInNetwork, currentConversations, isAwaitingLlm, thoughtBubbleEdges, agentIconSuggestions), [
|
|
257
169
|
agentCounts,
|
|
258
|
-
|
|
170
|
+
agentIconSuggestions,
|
|
171
|
+
coloringOption,
|
|
259
172
|
currentConversations,
|
|
260
|
-
activeThoughtBubbles,
|
|
261
|
-
thoughtBubbleEdges,
|
|
262
173
|
isAwaitingLlm,
|
|
174
|
+
layout,
|
|
175
|
+
mergedAgentsInNetwork,
|
|
263
176
|
showThoughtBubbles,
|
|
177
|
+
thoughtBubbleEdges,
|
|
264
178
|
]);
|
|
265
179
|
const [nodes, setNodes] = useState(layoutResult.nodes);
|
|
266
180
|
// Sync up the nodes with the layout result
|
|
@@ -268,6 +182,8 @@ export const AgentFlow = ({ agentCounts, agentsInNetwork, currentConversations,
|
|
|
268
182
|
setNodes(layoutResult.nodes);
|
|
269
183
|
}, [layoutResult.nodes]);
|
|
270
184
|
const edges = layoutResult.edges;
|
|
185
|
+
// Make sure to extract only thought bubble edges for the overlay.
|
|
186
|
+
const thoughtBubbleEdgesForOverlay = useMemo(() => edges.filter((e) => e.type === "thoughtBubbleEdge"), [edges]);
|
|
271
187
|
useEffect(() => {
|
|
272
188
|
// Schedule a fitView after the layout is set to ensure the view is adjusted correctly
|
|
273
189
|
setTimeout(() => {
|
|
@@ -306,28 +222,23 @@ export const AgentFlow = ({ agentCounts, agentsInNetwork, currentConversations,
|
|
|
306
222
|
}, children: _jsx("g", { id: `${id}-radial-guides-group`, transform: `translate(${transform[0]}, ${transform[1]}) scale(${transform[2]})`, children: circles }) }));
|
|
307
223
|
};
|
|
308
224
|
// Generate Legend for depth or heatmap colors
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
const title = isHeatmap ? "Heat" : "Depth";
|
|
312
|
-
const length = isHeatmap ? HEATMAP_COLORS.length : Math.min(maxDepth, BACKGROUND_COLORS.length);
|
|
225
|
+
const getLegend = () => {
|
|
226
|
+
const length = isHeatmap ? palette.length : Math.min(maxDepth, palette.length);
|
|
313
227
|
return (_jsxs(Box, { id: `${id}-legend`, sx: {
|
|
314
228
|
position: "absolute",
|
|
315
229
|
top: "5px",
|
|
316
230
|
right: "10px",
|
|
317
231
|
padding: "5px",
|
|
318
232
|
borderRadius: "5px",
|
|
319
|
-
boxShadow: `0 0 5px
|
|
233
|
+
boxShadow: `0 0 5px color-mix(in srgb, ${shadowColor} 30%, transparent)`,
|
|
320
234
|
display: "flex",
|
|
321
235
|
alignItems: "center",
|
|
322
236
|
zIndex: getZIndex(2, theme),
|
|
323
|
-
}, children: [_jsx(
|
|
324
|
-
fontSize: "10px",
|
|
325
|
-
marginLeft: "0.2rem",
|
|
326
|
-
}, children: title }), Array.from({ length }, (_, i) => (_jsx(Box, { id: `${id}-legend-depth-${i}`, style: {
|
|
237
|
+
}, children: [Array.from({ length }, (_, i) => (_jsx(Box, { id: `${id}-legend-depth-${i}`, style: {
|
|
327
238
|
alignItems: "center",
|
|
328
239
|
backgroundColor: palette[i],
|
|
329
240
|
borderRadius: "50%",
|
|
330
|
-
color: i
|
|
241
|
+
color: theme.palette.getContrastText(palette[i]),
|
|
331
242
|
display: "flex",
|
|
332
243
|
height: "15px",
|
|
333
244
|
justifyContent: "center",
|
|
@@ -346,26 +257,27 @@ export const AgentFlow = ({ agentCounts, agentsInNetwork, currentConversations,
|
|
|
346
257
|
}, size: "small", children: [_jsx(ToggleButton, { id: `${id}-depth-toggle`, size: "small", value: "depth", sx: {
|
|
347
258
|
fontSize: "0.5rem",
|
|
348
259
|
height: "1rem",
|
|
349
|
-
backgroundColor: darkMode && coloringOption === "depth" ? "var(--bs-gray-medium)" : undefined,
|
|
350
260
|
}, children: _jsx(Typography, { id: `${id}-depth-label`, sx: {
|
|
351
261
|
fontSize: "10px",
|
|
352
262
|
}, children: "Depth" }) }), _jsx(ToggleButton, { id: `${id}-heatmap-toggle`, size: "small", value: "heatmap", sx: {
|
|
353
263
|
fontSize: "0.5rem",
|
|
354
264
|
height: "1rem",
|
|
355
|
-
backgroundColor: darkMode && isHeatmap ? "var(--bs-gray-medium)" : undefined,
|
|
356
265
|
}, children: _jsx(Typography, { id: `${id}-heatmap-label`, sx: {
|
|
357
266
|
fontSize: "10px",
|
|
358
267
|
}, children: "Heatmap" }) })] })] }));
|
|
359
|
-
}
|
|
268
|
+
};
|
|
360
269
|
// Get the background color for the control buttons based on the layout and dark mode setting
|
|
361
270
|
const getControlButtonBackgroundColor = (isActive) => {
|
|
362
|
-
|
|
271
|
+
if (!isActive) {
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
return theme.palette.mode === "dark" ? theme.palette.grey[800] : theme.palette.grey[200];
|
|
363
275
|
};
|
|
364
276
|
// Only show radial guides if radial layout is selected, radial guides are enabled, and it's not just Frontman
|
|
365
277
|
const shouldShowRadialGuides = enableRadialGuides && layout === "radial" && maxDepth > 1;
|
|
366
278
|
// Generate the control bar for the flow, including layout and radial guides toggles
|
|
367
279
|
const getControls = () => {
|
|
368
|
-
return (_jsxs(Controls, {
|
|
280
|
+
return (_jsxs(Controls, { position: "top-left", style: {
|
|
369
281
|
position: "absolute",
|
|
370
282
|
top: "0px",
|
|
371
283
|
left: "0px",
|
|
@@ -373,22 +285,31 @@ export const AgentFlow = ({ agentCounts, agentsInNetwork, currentConversations,
|
|
|
373
285
|
width: "auto",
|
|
374
286
|
}, showInteractive: true, children: [_jsx(Tooltip, { id: "radial-layout-tooltip", title: "Radial layout", placement: "right", children: _jsx("span", { id: "radial-layout-span", children: _jsx(ControlButton, { id: "radial-layout-button", onClick: () => setLayout("radial"), style: {
|
|
375
287
|
backgroundColor: getControlButtonBackgroundColor(layout === "radial"),
|
|
376
|
-
}, children: _jsx(HubOutlinedIcon, { id: "radial-layout-icon"
|
|
288
|
+
}, children: _jsx(HubOutlinedIcon, { id: "radial-layout-icon" }) }) }) }), _jsx(Tooltip, { id: "linear-layout-tooltip", title: "Linear layout", placement: "right", children: _jsx("span", { id: "linear-layout-span", children: _jsx(ControlButton, { id: "linear-layout-button", onClick: () => setLayout("linear"), style: {
|
|
377
289
|
backgroundColor: getControlButtonBackgroundColor(layout === "linear"),
|
|
378
|
-
}, children: _jsx(ScatterPlotOutlinedIcon, { id: "linear-layout-icon"
|
|
290
|
+
}, children: _jsx(ScatterPlotOutlinedIcon, { id: "linear-layout-icon" }) }) }) }), _jsx(Tooltip, { id: "radial-guides-tooltip", title: `Enable/disable radial guides${layout === "radial" ? "" : " (only available in radial layout)"}`, placement: "right", children: _jsx("span", { id: "radial-guides-span", children: _jsx(ControlButton, { id: "radial-guides-button", onClick: () => setEnableRadialGuides(!enableRadialGuides), style: {
|
|
379
291
|
backgroundColor: getControlButtonBackgroundColor(enableRadialGuides),
|
|
380
|
-
}, disabled: layout !== "radial", children: _jsx(AdjustRoundedIcon, { id: "radial-guides-icon"
|
|
292
|
+
}, disabled: layout !== "radial", children: _jsx(AdjustRoundedIcon, { id: "radial-guides-icon" }) }) }) }), _jsx(Tooltip, { id: "thought-bubble-tooltip", title: `Toggle thought bubbles ${showThoughtBubbles ? "off" : "on"}`, placement: "right", children: _jsx("span", { id: "thought-bubble-span", children: _jsx(ControlButton, { id: "thought-bubble-button", onClick: () => setShowThoughtBubbles(!showThoughtBubbles), style: {
|
|
381
293
|
backgroundColor: getControlButtonBackgroundColor(showThoughtBubbles),
|
|
382
|
-
}, children: _jsx(ChatBubbleOutlineIcon, { id: "thought-bubble-icon"
|
|
294
|
+
}, children: _jsx(ChatBubbleOutlineIcon, { id: "thought-bubble-icon" }) }) }) })] }));
|
|
383
295
|
};
|
|
384
296
|
return (_jsxs(Box, { id: `${id}-outer-box`, sx: {
|
|
385
297
|
height: "100%",
|
|
386
298
|
width: "100%",
|
|
299
|
+
backgroundColor: theme.palette.background.default,
|
|
387
300
|
"& .react-flow__node": {
|
|
388
|
-
border:
|
|
389
|
-
|
|
301
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
302
|
+
},
|
|
303
|
+
"& .react-flow__panel": {
|
|
304
|
+
backgroundColor: theme.palette.background.paper,
|
|
305
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
306
|
+
color: theme.palette.text.primary,
|
|
307
|
+
},
|
|
308
|
+
"& .react-flow__controls-button": {
|
|
309
|
+
backgroundColor: theme.palette.background.paper,
|
|
310
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
311
|
+
color: theme.palette.text.primary,
|
|
312
|
+
fill: theme.palette.text.primary,
|
|
390
313
|
},
|
|
391
|
-
},
|
|
392
|
-
// Theme the "React Flow" attribution logo according to dark mode.
|
|
393
|
-
className: darkMode ? "dark" : undefined, children: [_jsx(ReactFlow, { id: `${id}-react-flow`, nodes: nodes, edges: edges, onNodesChange: onNodesChange, fitView: true, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionMode: ConnectionMode.Loose, children: !isAwaitingLlm && (_jsxs(_Fragment, { children: [agentsInNetwork?.length ? getLegend() : null, _jsx(Background, { id: `${id}-background` }), getControls(), shouldShowRadialGuides ? getRadialGuides() : null] })) }), _jsx(ThoughtBubbleOverlay, { nodes: nodes, edges: edges, showThoughtBubbles: showThoughtBubbles, isStreaming: isStreaming, onBubbleHoverChange: handleBubbleHoverChange })] }));
|
|
314
|
+
}, children: [_jsx(ReactFlow, { id: `${id}-react-flow`, nodes: nodes, edges: edges, onNodesChange: onNodesChange, fitView: true, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionMode: ConnectionMode.Loose, children: !isAwaitingLlm && (_jsxs(_Fragment, { children: [agentsInNetwork?.length ? getLegend() : null, _jsx(Background, { id: `${id}-background` }), getControls(), shouldShowRadialGuides ? getRadialGuides() : null] })) }), _jsx(ThoughtBubbleOverlay, { nodes: nodes, edges: thoughtBubbleEdgesForOverlay, showThoughtBubbles: showThoughtBubbles, isStreaming: isStreaming, onBubbleHoverChange: handleBubbleHoverChange })] }));
|
|
394
315
|
};
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { NodeProps } from "@xyflow/react";
|
|
2
|
+
import type { Node as RFNode } from "@xyflow/react";
|
|
1
3
|
import { FC } from "react";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export interface AgentNodeProps {
|
|
4
|
+
import { AgentConversation } from "./AgentConversations.js";
|
|
5
|
+
export interface AgentNodeProps extends Record<string, unknown> {
|
|
5
6
|
readonly agentCounts?: Map<string, number>;
|
|
6
7
|
readonly agentName: string;
|
|
7
8
|
readonly depth: number;
|
|
9
|
+
readonly displayAs?: string;
|
|
8
10
|
readonly getConversations: () => AgentConversation[] | null;
|
|
9
11
|
readonly isAwaitingLlm?: boolean;
|
|
10
|
-
readonly
|
|
12
|
+
readonly agentIconSuggestion?: string;
|
|
11
13
|
}
|
|
12
14
|
export declare const NODE_HEIGHT = 100;
|
|
13
15
|
export declare const NODE_WIDTH = 100;
|
|
@@ -15,4 +17,4 @@ export declare const NODE_WIDTH = 100;
|
|
|
15
17
|
* A node representing an agent in the network for use in react-flow.
|
|
16
18
|
* @param props See AgentNodeProps
|
|
17
19
|
*/
|
|
18
|
-
export declare const AgentNode: FC<NodeProps<AgentNodeProps
|
|
20
|
+
export declare const AgentNode: FC<NodeProps<RFNode<AgentNodeProps>>>;
|