@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.
Files changed (114) hide show
  1. package/README.md +287 -0
  2. package/dist/Theme/Palettes.d.ts +18 -0
  3. package/dist/Theme/Palettes.js +94 -0
  4. package/dist/Theme/Theme.d.ts +22 -0
  5. package/dist/Theme/Theme.js +58 -0
  6. package/dist/components/AgentChat/ChatCommon.d.ts +4 -2
  7. package/dist/components/AgentChat/ChatCommon.js +141 -105
  8. package/dist/components/AgentChat/ControlButtons.js +3 -1
  9. package/dist/components/AgentChat/FormattedMarkdown.d.ts +4 -4
  10. package/dist/components/AgentChat/FormattedMarkdown.js +5 -7
  11. package/dist/components/AgentChat/LlmChatButton.d.ts +2 -6
  12. package/dist/components/AgentChat/LlmChatButton.js +3 -3
  13. package/dist/components/AgentChat/UserQueryDisplay.js +2 -4
  14. package/dist/components/AgentChat/Utils.d.ts +2 -1
  15. package/dist/components/AgentChat/Utils.js +4 -1
  16. package/dist/components/AgentChat/VoiceChat/MicrophoneButton.d.ts +2 -2
  17. package/dist/components/AgentChat/VoiceChat/VoiceChat.d.ts +3 -3
  18. package/dist/components/AgentChat/VoiceChat/VoiceChat.js +5 -5
  19. package/dist/components/ChatBot/ChatBot.js +2 -2
  20. package/dist/components/Common/Breadcrumbs.js +1 -1
  21. package/dist/components/Common/{confirmationModal.js → ConfirmationModal.js} +2 -5
  22. package/dist/components/Common/CustomerLogo.d.ts +17 -0
  23. package/dist/components/Common/CustomerLogo.js +49 -0
  24. package/dist/components/Common/Footer.d.ts +18 -0
  25. package/dist/components/Common/Footer.js +59 -0
  26. package/dist/components/Common/LlmChatOptionsButton.d.ts +1 -4
  27. package/dist/components/Common/LlmChatOptionsButton.js +4 -4
  28. package/dist/components/Common/LoadingSpinner.js +1 -1
  29. package/dist/components/Common/MUIAccordion.d.ts +2 -2
  30. package/dist/components/Common/MUIAccordion.js +2 -12
  31. package/dist/components/Common/MUIAlert.d.ts +2 -1
  32. package/dist/components/Common/MUIAlert.js +4 -1
  33. package/dist/components/Common/MUIDialog.d.ts +1 -1
  34. package/dist/components/Common/MUIDialog.js +1 -1
  35. package/dist/components/Common/Navbar.d.ts +3 -1
  36. package/dist/components/Common/Navbar.js +60 -35
  37. package/dist/components/Common/PageLoader.js +3 -4
  38. package/dist/components/Common/Snackbar.d.ts +4 -1
  39. package/dist/components/Common/Snackbar.js +11 -19
  40. package/dist/components/Common/notification.d.ts +3 -3
  41. package/dist/components/Common/notification.js +6 -6
  42. package/dist/components/ErrorPage/ErrorBoundary.d.ts +2 -2
  43. package/dist/components/ErrorPage/ErrorBoundary.js +1 -1
  44. package/dist/components/ErrorPage/ErrorPage.js +6 -5
  45. package/dist/components/MultiAgentAccelerator/AgentConversations.d.ts +17 -0
  46. package/dist/components/MultiAgentAccelerator/AgentConversations.js +77 -0
  47. package/dist/components/MultiAgentAccelerator/AgentCounts.d.ts +12 -0
  48. package/dist/components/MultiAgentAccelerator/AgentCounts.js +21 -0
  49. package/dist/components/MultiAgentAccelerator/AgentFlow.d.ts +6 -4
  50. package/dist/components/MultiAgentAccelerator/AgentFlow.js +106 -185
  51. package/dist/components/MultiAgentAccelerator/AgentNode.d.ts +7 -5
  52. package/dist/components/MultiAgentAccelerator/AgentNode.js +93 -50
  53. package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +20 -17
  54. package/dist/components/MultiAgentAccelerator/GraphLayouts.js +16 -14
  55. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +2 -3
  56. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +214 -55
  57. package/dist/components/MultiAgentAccelerator/PlasmaEdge.d.ts +1 -1
  58. package/dist/components/MultiAgentAccelerator/PlasmaEdge.js +14 -12
  59. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +15 -0
  60. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +104 -0
  61. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +17 -0
  62. package/dist/components/MultiAgentAccelerator/{Sidebar.js → Sidebar/Sidebar.js} +146 -59
  63. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +19 -0
  64. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +113 -0
  65. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +26 -0
  66. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +20 -0
  67. package/dist/components/MultiAgentAccelerator/ThoughtBubbleEdge.d.ts +10 -8
  68. package/dist/components/MultiAgentAccelerator/ThoughtBubbleEdge.js +1 -1
  69. package/dist/components/MultiAgentAccelerator/ThoughtBubbleOverlay.d.ts +3 -2
  70. package/dist/components/MultiAgentAccelerator/ThoughtBubbleOverlay.js +10 -13
  71. package/dist/components/MultiAgentAccelerator/const.d.ts +1 -3
  72. package/dist/components/MultiAgentAccelerator/const.js +4 -18
  73. package/dist/components/Settings/FadingCheckmark.d.ts +14 -0
  74. package/dist/components/Settings/FadingCheckmark.js +43 -0
  75. package/dist/components/Settings/SettingsDialog.d.ts +9 -0
  76. package/dist/components/Settings/SettingsDialog.js +265 -0
  77. package/dist/const.d.ts +1 -2
  78. package/dist/const.js +2 -3
  79. package/dist/controller/Types/AgentIconSuggestions.d.ts +4 -0
  80. package/dist/controller/Types/AgentIconSuggestions.js +1 -0
  81. package/dist/controller/Types/Branding.d.ts +12 -0
  82. package/dist/controller/Types/Branding.js +1 -0
  83. package/dist/controller/Types/NetworkIconSuggestions.d.ts +4 -0
  84. package/dist/controller/Types/NetworkIconSuggestions.js +1 -0
  85. package/dist/controller/agent/Agent.d.ts +32 -12
  86. package/dist/controller/agent/Agent.js +71 -19
  87. package/dist/controller/llm/LlmChat.d.ts +1 -1
  88. package/dist/controller/llm/LlmChat.js +2 -2
  89. package/dist/index.d.ts +10 -5
  90. package/dist/index.js +10 -5
  91. package/dist/state/{environment.d.ts → Environment.d.ts} +2 -0
  92. package/dist/state/{environment.js → Environment.js} +2 -0
  93. package/dist/state/Settings.d.ts +62 -0
  94. package/dist/state/Settings.js +62 -0
  95. package/dist/state/TemporaryNetworks.d.ts +32 -0
  96. package/dist/state/TemporaryNetworks.js +26 -0
  97. package/dist/tsconfig.build.tsbuildinfo +1 -1
  98. package/dist/utils/Authentication.d.ts +2 -2
  99. package/dist/utils/Authentication.js +6 -6
  100. package/dist/utils/text.d.ts +2 -2
  101. package/dist/utils/text.js +3 -5
  102. package/dist/utils/title.d.ts +1 -1
  103. package/dist/utils/title.js +2 -2
  104. package/dist/utils/useLocalStorage.d.ts +1 -1
  105. package/dist/utils/useLocalStorage.js +3 -3
  106. package/dist/utils/zIndexLayers.d.ts +1 -1
  107. package/dist/utils/zIndexLayers.js +3 -15
  108. package/package.json +23 -21
  109. package/dist/components/MultiAgentAccelerator/Sidebar.d.ts +0 -12
  110. package/dist/utils/Theme.d.ts +0 -7
  111. package/dist/utils/Theme.js +0 -7
  112. package/dist/utils/agentConversations.d.ts +0 -24
  113. package/dist/utils/agentConversations.js +0 -113
  114. /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 { BACKGROUND_COLORS, BACKGROUND_COLORS_DARK_IDX, BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, HEATMAP_COLORS, LEVEL_SPACING, } from "./const.js";
29
- import { addThoughtBubbleEdge, layoutLinear, layoutRadial, removeThoughtBubbleEdge } from "./GraphLayouts.js";
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 { isDarkMode } from "../../utils/Theme.js";
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
- // Effect to add and remove thought bubbles - only when we actually have conversations to process
75
+ // Add new thought bubble edges for incoming conversations.
93
76
  useEffect(() => {
94
- if (!currentConversations || currentConversations.length === 0) {
95
- return; // Skip processing when no conversations
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
- thoughtBubbleEdges.forEach((thoughtBubbleEdge) => {
102
- const edgeText = thoughtBubbleEdge.edge.data?.text?.trim();
103
- if (edgeText) {
104
- // Add edge text to prevent duplicates
105
- processedText.add(edgeText);
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
- if (
113
- // Has text
114
- convText &&
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
- // Create an AgentConversation object representing the active thought bubble.
120
- // Use the conversation id from the incoming conversation and set startedAt
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
- const agentList = Array.from(conv.agents);
133
- // TODO: Check if agentList has at least 2 agents. This allows us to set a source and target.
134
- // However, this can also have more than 2 agents. We may want to think more about that case.
135
- if (agentList.length >= 2) {
136
- // These source and target agents are going to be useful later when we point bubbles to nodes.
137
- const sourceAgent = agentList[0];
138
- const targetAgent = agentList[1];
139
- const edge = {
140
- id: `thought-bubble-${conv.id}`,
141
- source: sourceAgent,
142
- target: targetAgent,
143
- type: "thoughtBubbleEdge",
144
- data: {
145
- text: conv.text,
146
- showAlways: showThoughtBubbles,
147
- conversationId: conv.id,
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
- if (newBubbles.length === 0) {
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, addThoughtBubbleEdgeHelper, removeThoughtBubbleEdgeHelper]);
192
- // Independent thought bubble cleanup - run timer only during streaming
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
- // Only clean up if we're currently streaming
196
- if (!isStreaming)
121
+ if (!isStreamingRef.current)
197
122
  return;
198
- setActiveThoughtBubbles((prevBubbles) => {
199
- if (prevBubbles.length === 0)
200
- return prevBubbles;
201
- const now = Date.now();
202
- const filteredBubbles = prevBubbles.filter((bubble) => {
203
- const age = now - bubble.startedAt.getTime();
204
- const shouldKeep = age < THOUGHT_BUBBLE_TIMEOUT_MS;
205
- // Keep bubble if it's being hovered, even if expired
206
- const isHovered = hoveredBubbleIdRef.current === `thought-bubble-${bubble.conversationId}`;
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 prevBubbles;
134
+ return changed ? edgesMap : prev;
221
135
  });
222
- }, 1000); // Check every second
136
+ }, 1000);
223
137
  return () => clearInterval(cleanupInterval);
224
- }, [isStreaming, removeThoughtBubbleEdgeHelper]);
225
- const { mode, systemMode } = useColorScheme();
226
- const darkMode = isDarkMode(mode, systemMode);
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
- activeThoughtBubbles.forEach((bubble) => {
235
- bubble.agents.forEach((agentId) => ids.add(agentId));
147
+ thoughtBubbleEdges.forEach(({ edge }) => {
148
+ const agents = edge.data?.agents ?? [];
149
+ agents.forEach((agentId) => ids.add(agentId));
236
150
  });
237
151
  return ids;
238
- }, [activeThoughtBubbles]);
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
- mergedAgentsInNetwork,
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
- function getLegend() {
310
- const palette = isHeatmap ? HEATMAP_COLORS : BACKGROUND_COLORS;
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 rgba(${shadowColor}, 0.3)`,
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(Typography, { id: `${id}-legend-label`, sx: {
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 < BACKGROUND_COLORS_DARK_IDX ? "var(--bs-primary)" : "var(--bs-white)",
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
- return isActive ? (darkMode ? "var(--bs-gray-dark)" : "var(--bs-gray-lighter)") : undefined;
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, { id: "react-flow-controls", position: "top-left", style: {
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", sx: { color: darkMode ? "var(--bs-white)" : "var(--bs-dark-mode-dim)" } }) }) }) }), _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: {
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", sx: { color: darkMode ? "var(--bs-white)" : "var(--bs-dark-mode-dim)" } }) }) }) }), _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: {
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", sx: { color: darkMode ? "var(--bs-white)" : "var(--bs-dark-mode-dim)" } }) }) }) }), _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: {
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", sx: { color: darkMode ? "var(--bs-white)" : "var(--bs-dark-mode-dim)" } }) }) }) })] }));
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: "var(--bs-border-width) var(--bs-border-style) var(--bs-black)",
389
- borderRadius: "var(--bs-border-radius-2xl)",
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 { NodeProps } from "reactflow";
3
- import { AgentConversation } from "../../utils/agentConversations.js";
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 displayAs?: string;
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>>>;