@cognizant-ai-lab/ui-common 1.6.0 → 1.8.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 (72) hide show
  1. package/dist/Theme/Palettes.d.ts +0 -9
  2. package/dist/Theme/Palettes.js +0 -18
  3. package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +11 -7
  4. package/dist/components/AgentChat/ChatCommon/ChatCommon.js +119 -90
  5. package/dist/components/AgentChat/ChatCommon/Const.d.ts +1 -0
  6. package/dist/components/AgentChat/ChatCommon/Const.js +2 -0
  7. package/dist/components/AgentChat/ChatCommon/ControlButtons.d.ts +1 -1
  8. package/dist/components/AgentChat/ChatCommon/ControlButtons.js +7 -4
  9. package/dist/components/AgentChat/ChatCommon/FormattedMarkdown.js +3 -0
  10. package/dist/components/AgentChat/ChatCommon/SendButton.js +7 -6
  11. package/dist/components/AgentChat/ChatCommon/SyntaxHighlighterThemes.js +1 -1
  12. package/dist/components/AgentChat/ChatCommon/Thinking.d.ts +1 -0
  13. package/dist/components/AgentChat/ChatCommon/Thinking.js +10 -7
  14. package/dist/components/AgentChat/Common/LlmChatButton.d.ts +0 -2
  15. package/dist/components/AgentChat/Common/LlmChatButton.js +6 -9
  16. package/dist/components/AgentChat/VoiceChat/MicrophoneButton.js +12 -10
  17. package/dist/components/ChatBot/ChatBot.d.ts +0 -4
  18. package/dist/components/ChatBot/ChatBot.js +2 -2
  19. package/dist/components/Common/Navbar.js +24 -22
  20. package/dist/components/Common/StatusLight.d.ts +9 -0
  21. package/dist/components/Common/StatusLight.js +21 -0
  22. package/dist/components/ErrorPage/ErrorBoundary.js +1 -1
  23. package/dist/components/ErrorPage/ErrorPage.d.ts +3 -3
  24. package/dist/components/ErrorPage/ErrorPage.js +4 -3
  25. package/dist/components/Logo/Common.d.ts +4 -0
  26. package/dist/components/Logo/Common.js +8 -0
  27. package/dist/components/Logo/CustomerLogo.d.ts +23 -0
  28. package/dist/components/Logo/CustomerLogo.js +41 -0
  29. package/dist/components/MultiAgentAccelerator/AgentConversations.js +0 -1
  30. package/dist/components/MultiAgentAccelerator/AgentFlow.js +85 -53
  31. package/dist/components/MultiAgentAccelerator/AgentNode.js +23 -27
  32. package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +17 -10
  33. package/dist/components/MultiAgentAccelerator/GraphLayouts.js +4 -6
  34. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +3 -9
  35. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +102 -47
  36. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +0 -5
  37. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +30 -25
  38. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +3 -4
  39. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +84 -168
  40. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +30 -13
  41. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +157 -100
  42. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +10 -8
  43. package/dist/components/MultiAgentAccelerator/const.d.ts +2 -0
  44. package/dist/components/MultiAgentAccelerator/const.js +4 -0
  45. package/dist/components/Settings/ApiKeyInput.d.ts +16 -0
  46. package/dist/components/Settings/ApiKeyInput.js +69 -0
  47. package/dist/components/Settings/InfoTip.d.ts +8 -0
  48. package/dist/components/Settings/InfoTip.js +14 -0
  49. package/dist/components/Settings/SettingsDialog.js +379 -178
  50. package/dist/components/Settings/SettingsRow.d.ts +18 -0
  51. package/dist/components/Settings/SettingsRow.js +35 -0
  52. package/dist/controller/agent/Agent.d.ts +3 -20
  53. package/dist/controller/agent/Agent.js +9 -43
  54. package/dist/controller/agent/IconSuggestions.d.ts +35 -0
  55. package/dist/controller/agent/IconSuggestions.js +85 -0
  56. package/dist/controller/llm/Providers.d.ts +2 -0
  57. package/dist/controller/llm/Providers.js +41 -0
  58. package/dist/index.d.ts +0 -2
  59. package/dist/index.js +0 -2
  60. package/dist/state/IconSuggestions.d.ts +27 -0
  61. package/dist/state/IconSuggestions.js +50 -0
  62. package/dist/state/Settings.d.ts +19 -3
  63. package/dist/state/Settings.js +24 -1
  64. package/dist/tsconfig.build.tsbuildinfo +1 -1
  65. package/dist/utils/title.js +1 -1
  66. package/package.json +5 -1
  67. package/dist/components/Common/CustomerLogo.d.ts +0 -17
  68. package/dist/components/Common/CustomerLogo.js +0 -49
  69. package/dist/components/Common/MUIAccordion.d.ts +0 -17
  70. package/dist/components/Common/MUIAccordion.js +0 -66
  71. package/dist/utils/useLocalStorage.d.ts +0 -1
  72. package/dist/utils/useLocalStorage.js +0 -55
@@ -1,26 +1,90 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Business from "@mui/icons-material/Business";
3
+ import ClearIcon from "@mui/icons-material/Clear";
2
4
  import RestoreIcon from "@mui/icons-material/SettingsBackupRestore";
3
5
  import Box from "@mui/material/Box";
4
6
  import Button from "@mui/material/Button";
5
7
  import Checkbox from "@mui/material/Checkbox";
6
- import CircularProgress from "@mui/material/CircularProgress";
7
- import Divider from "@mui/material/Divider";
8
8
  import FormLabel from "@mui/material/FormLabel";
9
- import { createTheme, ThemeProvider, useTheme } from "@mui/material/styles";
9
+ import IconButton from "@mui/material/IconButton";
10
+ import InputAdornment from "@mui/material/InputAdornment";
11
+ import MenuItem from "@mui/material/MenuItem";
12
+ import Select from "@mui/material/Select";
13
+ import { createTheme, styled, ThemeProvider, useTheme } from "@mui/material/styles";
10
14
  import TextField from "@mui/material/TextField";
11
15
  import ToggleButton from "@mui/material/ToggleButton";
12
16
  import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
13
17
  import Tooltip from "@mui/material/Tooltip";
14
18
  import Typography from "@mui/material/Typography";
15
- import { useEffect, useState } from "react";
16
- import { FadingCheckmark, useCheckmarkFade } from "./FadingCheckmark.js";
17
- import { getBrandingSuggestions } from "../../controller/agent/Agent.js";
18
- import { useSettingsStore } from "../../state/Settings.js";
19
+ import { useEffect, useMemo, useState, } from "react";
20
+ import { ApiKeyInput } from "./ApiKeyInput.js";
21
+ import { useCheckmarkFade } from "./FadingCheckmark.js";
22
+ import InfoTip from "./InfoTip.js";
23
+ import { SettingsRow } from "./SettingsRow.js";
24
+ import { getBrandingSuggestions, testConnection } from "../../controller/agent/Agent.js";
25
+ import { isAnthropicKeyValid, isOpenAIKeyValid } from "../../controller/llm/Providers.js";
26
+ import { useEnvironmentStore } from "../../state/Environment.js";
27
+ import { DEFAULT_SETTINGS, useSettingsStore } from "../../state/Settings.js";
19
28
  import { PALETTES } from "../../Theme/Palettes.js";
20
29
  import { ConfirmationModal } from "../Common/ConfirmationModal.js";
21
- import { CustomerLogo } from "../Common/CustomerLogo.js";
22
30
  import { MUIDialog } from "../Common/MUIDialog.js";
23
31
  import { NotificationType, sendNotification } from "../Common/notification.js";
32
+ import { StatusLight } from "../Common/StatusLight.js";
33
+ import { CustomerLogo } from "../Logo/CustomerLogo.js";
34
+ //#region: Styled Components
35
+ const SettingsSectionTitleBase = (props) => (_jsx(Typography, { variant: "h6", ...props }));
36
+ const SettingsSectionTitle = styled(SettingsSectionTitleBase)(({ theme }) => ({
37
+ marginBottom: theme.spacing(0.5),
38
+ }));
39
+ const Section = styled(Box)(({ theme }) => ({
40
+ display: "flex",
41
+ flexDirection: "column",
42
+ gap: theme.spacing(2),
43
+ marginBottom: theme.spacing(4),
44
+ }));
45
+ const SubsectionTitle = styled(Typography)(({ theme }) => ({
46
+ borderBottom: `1px solid ${theme.palette.divider}`,
47
+ color: theme.palette.text.secondary,
48
+ fontWeight: 600,
49
+ paddingBottom: theme.spacing(0.5),
50
+ marginBottom: theme.spacing(1.5),
51
+ width: "100%",
52
+ }));
53
+ const SubSection = styled(Box)(({ theme }) => ({
54
+ alignItems: "start",
55
+ display: "flex",
56
+ flexDirection: "column",
57
+ gap: theme.spacing(2),
58
+ marginBottom: theme.spacing(4),
59
+ }));
60
+ const SubSectionBody = styled(Box)(({ theme }) => ({
61
+ alignContent: "center",
62
+ display: "flex",
63
+ flexDirection: "column",
64
+ gap: theme.spacing(2.5),
65
+ width: "100%",
66
+ }));
67
+ //#endregion: Types and Interfaces
68
+ // eslint-disable-next-line react/no-multi-comp -- only relevant to this module.
69
+ const SettingsSubsection = ({ title, children }) => (_jsxs(SubSection, { children: [_jsx(SubsectionTitle, { variant: "subtitle1", children: title }), _jsx(SubSectionBody, { children: children })] }));
70
+ const URL_PROTOCOL_REGEX = /^https?:\/\//iu;
71
+ const HTTP_PROTOCOL_REGEX = /^http:\/\//iu;
72
+ const stripProtocol = (value) => (value ?? "").replace(URL_PROTOCOL_REGEX, "");
73
+ /**
74
+ * Normalize a Neuro SAN URL input by stripping the protocol and determining the protocol to use.
75
+ * @param value The input URL value to normalize.
76
+ * @param fallbackProtocol The protocol to use if the input value does not specify one.
77
+ * @return An object containing the normalized host and protocol.
78
+ */
79
+ const normalizeNeuroSanUrlInput = (value, fallbackProtocol) => {
80
+ const trimmedValue = value.trim();
81
+ const hasProtocol = URL_PROTOCOL_REGEX.exec(trimmedValue);
82
+ return {
83
+ host: stripProtocol(trimmedValue),
84
+ protocol: hasProtocol ? (HTTP_PROTOCOL_REGEX.test(trimmedValue) ? "http" : "https") : fallbackProtocol,
85
+ };
86
+ };
87
+ // eslint-disable-next-line react/no-multi-comp -- styled component shim is only used in this module
24
88
  export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
25
89
  // Settings store actions
26
90
  const updateSettings = useSettingsStore((state) => state.updateSettings);
@@ -45,12 +109,42 @@ export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
45
109
  const customer = useSettingsStore((state) => state.settings.branding.customer);
46
110
  const brandingCheckmark = useCheckmarkFade();
47
111
  const logoCheckmark = useCheckmarkFade();
48
- const [customerInput, setCustomerInput] = useState(customer);
112
+ const [customerInput, setCustomerInput] = useState(customer ?? "");
49
113
  const [isBrandingApplying, setIsBrandingApplying] = useState(false);
50
114
  const logoSource = useSettingsStore((state) => state.settings.branding.logoSource);
115
+ const iconSuggestion = useSettingsStore((state) => state.settings.branding.iconSuggestion);
51
116
  // Zen mode
52
117
  const enableZenMode = useSettingsStore((state) => state.settings.behavior.enableZenMode);
53
118
  const enableZenModeCheckmark = useCheckmarkFade();
119
+ // API keys
120
+ const apiKeys = useSettingsStore((state) => state.settings.apiKeys);
121
+ const openAIKeyCheckmark = useCheckmarkFade();
122
+ const anthropicKeyCheckmark = useCheckmarkFade();
123
+ // Native names setting
124
+ const useNativeNames = useSettingsStore((state) => state.settings.appearance.useNativeNames);
125
+ const nativeNamesCheckmark = useCheckmarkFade();
126
+ /*
127
+ Neuro SAN URL
128
+ This can come from three sources:
129
+ 1) The default URL from server environment variables
130
+ 2) The persisted value in the Zustand store
131
+ 3) The current state value, from the user's input in the settings dialog
132
+ */
133
+ const defaultNeuroSanUrl = useEnvironmentStore((state) => state.backendNeuroSanApiUrl ?? "");
134
+ const persistedNeuroSanUrl = useSettingsStore((state) => state.settings.externalServices.neuroSanUrl);
135
+ const effectiveNeuroSanUrl = persistedNeuroSanUrl || defaultNeuroSanUrl;
136
+ const effectiveNeuroSanProtocol = effectiveNeuroSanUrl.startsWith("http://") ? "http" : "https";
137
+ const neuroSanUrlNoProtocol = stripProtocol(effectiveNeuroSanUrl);
138
+ const [neuroSanUrlInput, setNeuroSanUrlInput] = useState(neuroSanUrlNoProtocol);
139
+ const [neuroSanProtocol, setNeuroSanProtocol] = useState(effectiveNeuroSanProtocol);
140
+ const [neuroSanUrlValidated, setNeuroSanUrlValidated] = useState(null);
141
+ const neuroSanURLCheckmark = useCheckmarkFade();
142
+ const neuroSanHostChanged = neuroSanUrlInput.trim() !== neuroSanUrlNoProtocol;
143
+ const neuroSanProtocolChanged = neuroSanProtocol !== effectiveNeuroSanProtocol;
144
+ const neuroSanUrlSaveDisabled = neuroSanUrlInput.trim().length === 0 || (!neuroSanHostChanged && !neuroSanProtocolChanged);
145
+ const neuroSanUrlResetDisabled = neuroSanUrlInput === stripProtocol(defaultNeuroSanUrl) &&
146
+ neuroSanProtocol === (defaultNeuroSanUrl.startsWith("http://") ? "http" : "https");
147
+ const statusLightValue = neuroSanUrlValidated === null ? "unknown" : neuroSanUrlValidated ? "green" : "red";
54
148
  // Record user's current theme so at least the settings dialog (with default MUI theme) matches that
55
149
  const theme = useTheme();
56
150
  const paletteMode = theme.palette.mode;
@@ -64,87 +158,127 @@ export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
64
158
  }
65
159
  rangePaletteCheckmark.trigger();
66
160
  };
161
+ const updateBranding = (brandingSuggestions) => {
162
+ const { plasma, nodeColor, primary, secondary, background, rangePalette, iconSuggestion: newIconSuggestion, } = brandingSuggestions;
163
+ // Update persisted settings with new branding suggestions
164
+ const updates = {
165
+ branding: {
166
+ customer: customerInput,
167
+ ...(primary ? { primary } : {}),
168
+ ...(secondary ? { secondary } : {}),
169
+ ...(background ? { background } : {}),
170
+ ...(Array.isArray(rangePalette) ? { rangePalette } : {}),
171
+ ...(newIconSuggestion
172
+ ? {
173
+ iconSuggestion: newIconSuggestion,
174
+ logoSource: logoServiceToken ? "auto" : "generic",
175
+ }
176
+ : {}),
177
+ },
178
+ appearance: {
179
+ rangePalette: "brand",
180
+ ...(plasma ? { plasmaColor: plasma } : {}),
181
+ ...(nodeColor ? { agentNodeColor: nodeColor } : {}),
182
+ },
183
+ };
184
+ updateSettings(updates);
185
+ // Trigger checkmarks for items we changed
186
+ brandingCheckmark.trigger();
187
+ rangePaletteCheckmark.trigger();
188
+ if (plasma) {
189
+ plasmaColorCheckmark.trigger();
190
+ }
191
+ if (nodeColor) {
192
+ agentNodeColorCheckmark.trigger();
193
+ }
194
+ if (newIconSuggestion) {
195
+ logoCheckmark.trigger();
196
+ }
197
+ };
67
198
  /**
68
199
  * Handle applying branding based on customer input. Calls backend to get colors then updates settings store.
69
200
  */
70
201
  const handleBrandingApply = async () => {
71
202
  setIsBrandingApplying(true);
72
- updateSettings({
73
- branding: {
74
- customer: customerInput,
75
- },
76
- });
77
- let brandingSuggestions;
78
203
  try {
79
- brandingSuggestions = await getBrandingSuggestions(customerInput);
204
+ const brandingSuggestions = await getBrandingSuggestions(customerInput);
205
+ if (brandingSuggestions) {
206
+ updateBranding(brandingSuggestions);
207
+ }
80
208
  }
81
209
  catch (e) {
82
- console.warn(`Failed to fetch branding suggestions for customer "${customerInput}"`, e);
210
+ console.error(`Failed to fetch branding suggestions for customer "${customerInput}"`, e);
83
211
  sendNotification(NotificationType.error, `Failed to fetch branding suggestions for "${customerInput}"`, "Please check the name and try again. If the problem persists, there may be an issue with the " +
84
212
  "branding service.");
85
- return;
86
213
  }
214
+ finally {
215
+ setIsBrandingApplying(false);
216
+ }
217
+ };
218
+ const handleBrandingClear = () => {
219
+ // Clear branding settings
87
220
  updateSettings({
88
221
  appearance: {
89
- rangePalette: "brand",
222
+ agentIconColor: DEFAULT_SETTINGS.appearance.agentIconColor,
223
+ agentNodeColor: DEFAULT_SETTINGS.appearance.agentNodeColor,
224
+ autoAgentIconColor: DEFAULT_SETTINGS.appearance.autoAgentIconColor,
225
+ plasmaColor: DEFAULT_SETTINGS.appearance.plasmaColor,
226
+ rangePalette: DEFAULT_SETTINGS.appearance.rangePalette,
90
227
  },
228
+ branding: { ...DEFAULT_SETTINGS.branding },
91
229
  });
92
- if (brandingSuggestions["plasma"]) {
93
- updateSettings({
94
- appearance: {
95
- plasmaColor: brandingSuggestions["plasma"],
96
- },
97
- });
98
- plasmaColorCheckmark.trigger();
99
- }
100
- if (brandingSuggestions["nodeColor"]) {
101
- updateSettings({
102
- appearance: {
103
- agentNodeColor: brandingSuggestions["nodeColor"],
104
- },
105
- });
106
- agentNodeColorCheckmark.trigger();
107
- }
108
- // primary
109
- if (brandingSuggestions["primary"]) {
110
- updateSettings({
111
- branding: {
112
- primary: brandingSuggestions["primary"],
113
- },
114
- });
115
- }
116
- // secondary
117
- if (brandingSuggestions["secondary"]) {
118
- updateSettings({
119
- branding: {
120
- secondary: brandingSuggestions["secondary"],
121
- },
122
- });
123
- }
124
- // background
125
- if (brandingSuggestions["background"]) {
126
- updateSettings({
127
- branding: {
128
- background: brandingSuggestions["background"],
129
- },
130
- });
131
- }
132
- if (Array.isArray(brandingSuggestions["rangePalette"])) {
133
- updateSettings({
134
- branding: {
135
- rangePalette: brandingSuggestions["rangePalette"],
136
- },
137
- });
138
- }
139
- if (brandingSuggestions["iconSuggestion"]) {
140
- updateSettings({
141
- branding: {
142
- iconSuggestion: brandingSuggestions["iconSuggestion"],
143
- },
144
- });
145
- }
230
+ // Trigger checkmarks for all the settings that are affected by branding
231
+ agentIconColorCheckmark.trigger();
232
+ agentNodeColorCheckmark.trigger();
233
+ plasmaColorCheckmark.trigger();
234
+ rangePaletteCheckmark.trigger();
146
235
  brandingCheckmark.trigger();
147
- setIsBrandingApplying(false);
236
+ logoCheckmark.trigger();
237
+ };
238
+ const persistKey = (vendor, key, checkmark) => {
239
+ updateSettings({
240
+ apiKeys: {
241
+ [vendor]: key,
242
+ },
243
+ });
244
+ checkmark.trigger();
245
+ };
246
+ const handleTestConnection = async () => {
247
+ const normalized = normalizeNeuroSanUrlInput(neuroSanUrlInput, neuroSanProtocol);
248
+ setNeuroSanProtocol(normalized.protocol);
249
+ setNeuroSanUrlInput(normalized.host);
250
+ const result = await testConnection(`${normalized.protocol}://${normalized.host}`);
251
+ setNeuroSanUrlValidated(result.success);
252
+ };
253
+ const handleNeuroSanUrlChange = (e) => {
254
+ setNeuroSanUrlInput(e.target.value);
255
+ setNeuroSanUrlValidated(null);
256
+ };
257
+ const handleSaveNeuroSanUrl = () => {
258
+ const normalized = normalizeNeuroSanUrlInput(neuroSanUrlInput, neuroSanProtocol);
259
+ setNeuroSanProtocol(normalized.protocol);
260
+ setNeuroSanUrlInput(normalized.host);
261
+ updateSettings({
262
+ externalServices: {
263
+ neuroSanUrl: `${normalized.protocol}://${normalized.host}`,
264
+ },
265
+ });
266
+ neuroSanURLCheckmark.trigger();
267
+ };
268
+ const handleNeuroSanProtocolChange = (e) => {
269
+ const newProtocol = e.target.value;
270
+ setNeuroSanProtocol(newProtocol);
271
+ setNeuroSanUrlValidated(null);
272
+ };
273
+ const handleNeuroSanUrlBlur = () => {
274
+ const normalized = normalizeNeuroSanUrlInput(neuroSanUrlInput, neuroSanProtocol);
275
+ setNeuroSanProtocol(normalized.protocol);
276
+ setNeuroSanUrlInput(normalized.host);
277
+ };
278
+ const handleResetNeuroSanUrl = () => {
279
+ const normalized = normalizeNeuroSanUrlInput(defaultNeuroSanUrl, "https");
280
+ setNeuroSanUrlInput(normalized.host);
281
+ setNeuroSanProtocol(normalized.protocol);
148
282
  };
149
283
  // Effect to keep input in sync with state store
150
284
  useEffect(() => {
@@ -152,114 +286,181 @@ export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
152
286
  }, [customer]);
153
287
  const availablePalettes = customer && brandingRangePalette?.length > 0 ? { brand: brandingRangePalette } : PALETTES;
154
288
  const paletteKeys = Object.keys(availablePalettes);
155
- /* Dev note:
156
- Before you go removing the "useless" spans in code below that wrap MUI elements: they are required because
157
- MUI's disabled state on certain components sets pointer-events: none, which prevents tooltips from working.
158
- Wrapping in a span allows the tooltip to still function while the inner component is disabled.
159
- See: https://github.com/mui/material-ui/issues/8416
160
- */
289
+ // Config for API key inputs, so we can easily add new providers in the future by just adding to this array
290
+ const apiKeyConfigs = [
291
+ {
292
+ checkmark: openAIKeyCheckmark,
293
+ idSuffix: "openai",
294
+ logo: theme.palette.mode === "dark" ? "/OpenAI-white.png" : "/OpenAI-black.png",
295
+ onTest: isOpenAIKeyValid,
296
+ placeholder: "sk-...",
297
+ vendor: "OpenAI",
298
+ },
299
+ {
300
+ checkmark: anthropicKeyCheckmark,
301
+ idSuffix: "anthropic",
302
+ logo: "/claude.png",
303
+ onTest: isAnthropicKeyValid,
304
+ placeholder: "sk-ant-...",
305
+ vendor: "Anthropic",
306
+ },
307
+ ];
308
+ const getConfirmationModal = () => (_jsx(ConfirmationModal, { id: `${id}-reset-to-default-settings-confirmation-modal`, content: "This will reset all settings to their default values and cannot be undone. " +
309
+ "Are you sure you want to proceed?", handleCancel: () => {
310
+ setResetToDefaultSettingsOpen(false);
311
+ }, handleOk: () => {
312
+ setResetToDefaultSettingsOpen(false);
313
+ resetSettings();
314
+ sendNotification(NotificationType.success, "Settings have been reset to default values.");
315
+ }, title: "Reset to default settings" }));
316
+ const getNeuroSanSubsection = () => {
317
+ return (_jsx(SettingsSubsection, { title: "Neuro SAN", children: _jsxs(SettingsRow, { id: `${id}-neuro-san-server-url-row`, checkmark: neuroSanURLCheckmark, label: "", tooltip: "URL for the Neuro SAN server.", children: [_jsxs(Select, { "aria-label": "protocol-select", onChange: handleNeuroSanProtocolChange, size: "small", sx: { minWidth: 100, flexShrink: 0 }, value: neuroSanProtocol, children: [_jsx(MenuItem, { value: "https", children: "https://" }), _jsx(MenuItem, { value: "http", children: "http://" })] }), _jsx(TextField, { "aria-label": "neuro-san-server-url-host-input", onBlur: handleNeuroSanUrlBlur, onChange: handleNeuroSanUrlChange, placeholder: "example.com:1234", size: "small", slotProps: {
318
+ input: {
319
+ endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { "aria-label": "Clear input", edge: "end", size: "small", onClick: () => {
320
+ setNeuroSanUrlInput("");
321
+ setNeuroSanUrlValidated(null);
322
+ }, children: _jsx(ClearIcon, { fontSize: "small" }) }) })),
323
+ },
324
+ }, sx: { flex: 1 }, value: neuroSanUrlInput }), _jsx(StatusLight, { id: `${id}-status-light`, statusValue: statusLightValue }), _jsx(Button, { disabled: neuroSanUrlInput.trim().length === 0, onClick: handleTestConnection, size: "small", variant: "contained", children: "Test" }), _jsx(Button, { disabled: neuroSanUrlSaveDisabled, onClick: handleSaveNeuroSanUrl, size: "small", variant: "contained", children: "Save" }), _jsx(Button, { disabled: neuroSanUrlResetDisabled, onClick: handleResetNeuroSanUrl, size: "small", variant: "contained", children: "Default" })] }, `${id}-neuro-san-server-url`) }));
325
+ };
326
+ const getServicesSection = () => (_jsxs(Section, { children: [_jsx(SettingsSectionTitle, { children: "External Services" }), getNeuroSanSubsection()] }));
327
+ const getApiKeysSection = () => (_jsxs(Section, { children: [_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: theme.spacing(2) }, children: [_jsx(SettingsSectionTitle, { children: "API Keys" }), _jsx(InfoTip, { title: "API keys are used to access external services. Some networks may require an API key for " +
328
+ "you to use them. Your keys will be saved in your browser local storage and sent to " +
329
+ "services that require them. Do not use this option if you are on a shared or public computer." })] }), _jsx(SubsectionTitle, { variant: "subtitle1", children: "Providers" }), _jsx(SubSectionBody, { children: _jsx(Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 1.5 }, children: apiKeyConfigs.map(({ checkmark, vendor, idSuffix, logo, onTest, placeholder }) => (_jsx(SettingsRow, { checkmark: checkmark, label: "", tooltip: `API key for ${vendor}.`, children: _jsx(ApiKeyInput, { forgetKey: () => persistKey(vendor, "", checkmark), id: `${id}-${idSuffix}`, logo: logo, onSave: (key) => persistKey(vendor, key, checkmark), onTest: onTest, persistedValue: apiKeys[vendor], placeholder: placeholder, vendor: vendor }) }, `${id}-${idSuffix}`))) }) })] }));
330
+ const getBehaviorSection = () => (_jsxs(Section, { children: [_jsx(SettingsSectionTitle, { children: "Behavior" }), _jsx(SettingsSubsection, { title: "Zen mode", children: _jsx(SettingsRow, { label: 'Enable "Zen" mode:', checkmark: enableZenModeCheckmark, tooltip: "Hides most of the UI during agent network animations providing a more immersive " +
331
+ "experience.", children: _jsx(Checkbox, { checked: enableZenMode, "data-testid": "zen-mode-checkbox", onChange: (_, checked) => {
332
+ updateSettings({ behavior: { enableZenMode: checked } });
333
+ enableZenModeCheckmark.trigger();
334
+ }, sx: { p: 0.0 } }) }) })] }));
335
+ const getNamingSubsection = () => (_jsx(SettingsSubsection, { title: "Agent names", children: _jsxs(SettingsRow, { label: "Display as:", checkmark: nativeNamesCheckmark, tooltip: "Choose how agent names are displayed in the network. " +
336
+ '"Native" shows the original agent name as provided by the system, while "Beautified" applies ' +
337
+ "formatting to make names more human-readable. This setting does not affect the actual names of " +
338
+ "agents, only how they are displayed in the UI.", children: [_jsxs(ToggleButtonGroup, { "aria-label": "agent-name-format-selection", exclusive: true, onChange: (_, value) => {
339
+ if (value !== null) {
340
+ updateSettings({
341
+ appearance: {
342
+ useNativeNames: value === "native",
343
+ },
344
+ });
345
+ nativeNamesCheckmark.trigger();
346
+ }
347
+ }, size: "small", value: useNativeNames ? "native" : "beautified", children: [_jsx(ToggleButton, { value: "native", children: "Native" }), _jsx(ToggleButton, { value: "beautified", children: "Beautified" })] }), _jsx(FormLabel, { children: "Preview: " }), _jsx(FormLabel, { sx: {
348
+ marginBottom: 0,
349
+ border: "1px solid",
350
+ borderRadius: 1,
351
+ backgroundColor: "background.paper",
352
+ ml: 0.5,
353
+ px: 1,
354
+ py: 0.25,
355
+ lineHeight: 0,
356
+ fontSize: "0.7rem",
357
+ maxWidth: "100%",
358
+ }, children: _jsx("pre", { children: `category/some_agent_name → ${useNativeNames ? "category/some_agent_name [unchanged]" : "Category Some Agent Name"}` }) })] }) }));
359
+ const getBrandingSubsection = () => (_jsxs(SettingsSubsection, { title: "Branding", children: [_jsxs(SettingsRow, { checkmark: brandingCheckmark, label: "Customer:", tooltip: "Set a customer or organization name to automatically apply a custom color palette and " +
360
+ "logo to the network", children: [_jsx(TextField, { "aria-label": "branding-input", onChange: (e) => setCustomerInput(e.target.value), onKeyDown: (e) => {
361
+ if (e.key === "Enter" && customerInput?.trim().length > 0) {
362
+ void handleBrandingApply();
363
+ }
364
+ }, value: customerInput, placeholder: "Company or organization name", size: "small", sx: { width: "100%" }, variant: "outlined" }), _jsx(Button, { disabled: customerInput?.trim().length === 0 || isBrandingApplying || customerInput === customer, variant: "contained", size: "small", onClick: handleBrandingApply, loading: isBrandingApplying, children: "Apply" }), _jsx(Button, { disabled: !customer || isBrandingApplying, variant: "contained", size: "small", onClick: handleBrandingClear, loading: isBrandingApplying, children: "Clear" })] }), _jsxs(SettingsRow, { checkmark: logoCheckmark, label: "Logo:", tooltip: "Choose a logo to display in the top-left corner of the network. " +
365
+ '"None" will display no logo, "Generic" will display a simple generic logo, and "Auto" will ' +
366
+ "attempt to find a suitable logo based on the customer name using an external service (if " +
367
+ "configured on the server). Logo is only displayed when customer branding is applied.", children: [_jsx(Tooltip, { title: customer ? undefined : "Set a customer name to enable logo options", children: _jsx("span", { children: _jsxs(ToggleButtonGroup, { "aria-label": "logo-selection", disabled: !customer, exclusive: true, onChange: (_, value) => {
368
+ if (value !== null) {
369
+ updateSettings({
370
+ branding: {
371
+ logoSource: value,
372
+ },
373
+ });
374
+ logoCheckmark.trigger();
375
+ }
376
+ }, size: "small", sx: { cursor: customer && logoServiceToken ? "pointer" : "not-allowed", marginRight: "1rem" }, value: logoSource || "none", children: [_jsx(Tooltip, { title: customer && "No logo will be displayed", children: _jsx("span", { children: _jsx(ToggleButton, { value: "none", children: "None" }) }) }), _jsx(Tooltip, { title: customer && "Display a simple, anonymous generic logo based on a generic brand", children: _jsx("span", { children: _jsx(ToggleButton, { value: "generic", children: "Generic" }) }) }), _jsx(Tooltip, { title: customer
377
+ ? logoServiceToken
378
+ ? "Use a service to attempt to automatically find a suitable logo based " +
379
+ "on the customer name."
380
+ : "Logo service not configured on the server. Cannot use Auto logo source"
381
+ : undefined, children: _jsx("span", { children: _jsx(ToggleButton, { disabled: !logoServiceToken, value: "auto", children: "Auto" }) }) })] }) }) }), _jsx(FormLabel, { children: "Preview:" }), _jsx(Box, { children: logoSource === "auto" || logoSource === "generic" ? (_jsx(CustomerLogo, { customer: customer ?? "", fallbackIcon: Business, iconSuggestion: iconSuggestion ?? undefined, logoServiceToken: logoServiceToken, logoSource: logoSource })) : ("(None)") })] })] }));
382
+ const getNetworkDisplaySubsection = () => (_jsx(SettingsSubsection, { title: "Network display", children: _jsx(SettingsRow, { checkmark: rangePaletteCheckmark, label: "Palette:", tooltip: "Choose the color palette to use for heatmaps and depth display in the network. ", children: _jsx(Tooltip, { title: customer ? "Palette is determined by customer branding and cannot be changed" : "", children: _jsx("span", { children: _jsx(ToggleButtonGroup, { "aria-label": "depth-heatmap-palette-selection", disabled: Boolean(customer), exclusive: true, onChange: handlePaletteChange, size: "small", sx: {
383
+ cursor: customer ? "not-allowed" : "pointer",
384
+ opacity: customer ? 0.5 : 1,
385
+ }, value: paletteKey, children: paletteKeys.map((key) => {
386
+ const palette = availablePalettes[key];
387
+ if (!palette || !Array.isArray(palette))
388
+ return null;
389
+ const paletteArray = palette;
390
+ return (_jsx(ToggleButton, { "aria-label": `${key}-palette-button`, value: key, children: _jsxs(Box, { sx: {
391
+ display: "flex",
392
+ flexDirection: "column",
393
+ gap: 0.5,
394
+ alignItems: "center",
395
+ }, children: [_jsx(Typography, { variant: "caption", sx: { textTransform: "capitalize" }, children: key }), _jsx(Box, { sx: { display: "flex", gap: 0.5 }, children: Array.from({ length: 5 }, (_, i) => {
396
+ const paletteLength = paletteArray.length;
397
+ const index = Math.floor((i * paletteLength) / 5);
398
+ return paletteArray[index];
399
+ }).map((color) => (_jsx(Box, { sx: {
400
+ width: "0.75rem",
401
+ height: "0.75rem",
402
+ backgroundColor: color,
403
+ border: "1px solid",
404
+ } }, color))) })] }) }, key));
405
+ }) }) }) }) }) }));
406
+ const getNetworkAnimationSubsection = () => (_jsxs(SettingsSubsection, { title: "Network animation", children: [_jsx(SettingsRow, { checkmark: plasmaColorCheckmark, label: "Plasma animation color:", tooltip: customer
407
+ ? "Plasma color is locked when branding is applied"
408
+ : "Select the color for the plasma animation.", children: _jsx("input", { "aria-label": "plasma-color-picker", disabled: Boolean(customer), onChange: (e) => {
409
+ updateSettings({
410
+ appearance: {
411
+ plasmaColor: e.target.value,
412
+ },
413
+ });
414
+ plasmaColorCheckmark.trigger();
415
+ }, style: { cursor: customer ? "not-allowed" : "pointer", opacity: customer ? 0.5 : 1 }, type: "color", value: plasmaColor }) }), _jsx(SettingsRow, { checkmark: agentNodeColorCheckmark, label: "Agent node color:", tooltip: customer
416
+ ? "Agent node color is locked when branding is applied"
417
+ : "Select the color for the agent node animation.", children: _jsx("input", { "aria-label": "agent-node-color-picker", disabled: Boolean(customer), onChange: (e) => {
418
+ updateSettings({
419
+ appearance: {
420
+ agentNodeColor: e.target.value,
421
+ },
422
+ });
423
+ agentNodeColorCheckmark.trigger();
424
+ }, style: { cursor: customer ? "not-allowed" : "pointer", opacity: customer ? 0.5 : 1 }, type: "color", value: agentNodeColor }) }), _jsxs(SettingsRow, { checkmark: agentIconColorCheckmark, label: "Agent icon color:", tooltip: customer
425
+ ? "Agent icon color is locked when branding is applied"
426
+ : "Select the color for the agent icon animation.", children: [_jsx("span", { children: _jsxs(ToggleButtonGroup, { disabled: Boolean(customer), exclusive: true, value: autoAgentIconColor ? "auto" : "custom", onChange: (_, value) => {
427
+ if (value !== null) {
428
+ updateSettings({
429
+ appearance: {
430
+ autoAgentIconColor: value === "auto",
431
+ },
432
+ });
433
+ agentIconColorCheckmark.trigger();
434
+ }
435
+ }, size: "small", style: {
436
+ cursor: customer ? "not-allowed" : "pointer",
437
+ opacity: customer ? 0.5 : 1,
438
+ }, children: [_jsx(ToggleButton, { "data-testid": "auto-agent-icon-color-button", value: "auto", children: "Auto" }), _jsx(ToggleButton, { value: "custom", children: "Custom" })] }) }), _jsx("input", { "aria-label": "agent-icon-color-picker", disabled: Boolean(customer) || autoAgentIconColor, onChange: (e) => {
439
+ updateSettings({
440
+ appearance: {
441
+ agentIconColor: e.target.value,
442
+ },
443
+ });
444
+ agentIconColorCheckmark.trigger();
445
+ }, style: { cursor: customer ? "not-allowed" : "pointer", opacity: customer ? 0.5 : 1 }, type: "color", value: agentIconColor })] })] }));
446
+ const getAppearanceSection = () => (_jsxs(Section, { children: [_jsx(SettingsSectionTitle, { children: "Appearance" }), getNamingSubsection(), getBrandingSubsection(), getNetworkDisplaySubsection(), getNetworkAnimationSubsection()] }));
447
+ const getResetSettingsSection = () => (_jsx(SubSection, { sx: { marginTop: 4, paddingTop: 2, borderTop: "4px solid var(--bs-border-color)" }, children: _jsx(SubSectionBody, { children: _jsx(Button, { variant: "text", startIcon: _jsx(RestoreIcon, {}), onClick: () => {
448
+ setResetToDefaultSettingsOpen(true);
449
+ }, sx: { color: "var(--bs-secondary)" }, children: "Reset to defaults" }) }) }));
450
+ const settingsTheme = useMemo(() => createTheme({
451
+ palette: {
452
+ mode: paletteMode,
453
+ },
454
+ typography: {
455
+ fontSize: 12,
456
+ },
457
+ }), [paletteMode]);
161
458
  return (
162
459
  // Always use default theme for settings dialog so user can always see to reset. It's possible that with
163
460
  // certain custom themes the dialog would be unreadable.
164
- _jsxs(ThemeProvider, { theme: createTheme({
165
- palette: {
166
- mode: paletteMode,
167
- },
168
- }), children: [resetToDefaultSettingsOpen ? (_jsx(ConfirmationModal, { id: `${id}-reset-to-default-settings-confirmation-modal`, content: "This will reset all settings to their default values and cannot be undone. " +
169
- "Are you sure you want to proceed?", handleCancel: () => {
170
- setResetToDefaultSettingsOpen(false);
171
- }, handleOk: () => {
172
- setResetToDefaultSettingsOpen(false);
173
- resetSettings();
174
- sendNotification(NotificationType.success, "Settings have been reset to default values.");
175
- }, title: "Reset to default settings" })) : null, _jsxs(MUIDialog, { id: id, title: _jsx(Box, { sx: { fontSize: "1.5rem" }, children: "Settings" }), isOpen: isOpen, onClose: onClose, paperProps: {
461
+ _jsxs(ThemeProvider, { theme: settingsTheme, children: [resetToDefaultSettingsOpen ? getConfirmationModal() : null, _jsxs(MUIDialog, { id: id, title: _jsx(Box, { sx: { fontSize: "1.5rem" }, children: "Settings" }), isOpen: isOpen, onClose: onClose, paperProps: {
176
462
  minWidth: "50%",
177
463
  minHeight: "50%",
178
464
  border: "1px solid",
179
- }, children: [_jsxs(Box, { sx: { marginBottom: 3 }, children: [_jsx(Typography, { variant: "h6", sx: { marginBottom: 1 }, children: "Behavior" }), _jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [_jsx(FormLabel, { children: "Enable \"Zen\" mode:" }), _jsx(Tooltip, { title: "Hides most of the UI during agent network animations, providing a more immersive experience.", children: _jsx(Checkbox, { checked: enableZenMode, "data-testid": "zen-mode-checkbox", onChange: (_, checked) => {
180
- updateSettings({ behavior: { enableZenMode: checked } });
181
- enableZenModeCheckmark.trigger();
182
- } }) }), _jsx(FadingCheckmark, { show: enableZenModeCheckmark.show })] })] }), _jsxs(Box, { sx: { marginBottom: 3 }, children: [_jsx(Typography, { variant: "h6", sx: { marginBottom: 1 }, children: "Appearance" }), _jsx(Typography, { variant: "subtitle1", sx: { marginBottom: 1, marginTop: 2, fontWeight: 600 }, children: "Branding" }), _jsx(Divider, { sx: { marginBottom: 2 } }), _jsx(Box, { sx: { display: "flex", alignItems: "center" }, children: _jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 2, marginBottom: "1rem", width: "100%" }, children: [_jsx(FormLabel, { children: "Customer:" }), _jsx(TextField, { "aria-label": "branding-input", onChange: (e) => setCustomerInput(e.target.value), onKeyDown: (e) => {
183
- if (e.key === "Enter" && customerInput?.trim().length > 0) {
184
- void handleBrandingApply();
185
- }
186
- }, value: customerInput ?? "", placeholder: "Company or organization name", size: "small", sx: { width: "100%" }, variant: "outlined" }), _jsx(Button, { disabled: customerInput?.trim().length === 0 ||
187
- isBrandingApplying ||
188
- customerInput === customer, variant: "contained", size: "small", onClick: handleBrandingApply, startIcon: isBrandingApplying ? (_jsx(CircularProgress, { size: 16, color: "inherit" })) : undefined, sx: { minWidth: "8rem" }, children: isBrandingApplying ? "Applying..." : "Apply" }), _jsx(FadingCheckmark, { show: brandingCheckmark.show })] }) }), _jsx(Box, { sx: { display: "flex", alignItems: "center" }, children: _jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 2, marginBottom: "1rem", width: "100%" }, children: [_jsx(FormLabel, { children: "Logo:" }), _jsx(Tooltip, { title: customer ? null : "Set a customer name to enable logo options", children: _jsx("span", { children: _jsxs(ToggleButtonGroup, { "aria-label": "logo-selection", disabled: !customer, exclusive: true, onChange: (_, value) => {
189
- if (value !== null) {
190
- updateSettings({
191
- branding: {
192
- logoSource: value,
193
- },
194
- });
195
- logoCheckmark.trigger();
196
- }
197
- }, size: "small", sx: { marginRight: "1rem" }, value: logoSource || "none", children: [_jsx(Tooltip, { title: "No logo will be displayed",
198
- // Avoids having overlapping tooltips
199
- disableHoverListener: !customer, disableFocusListener: !customer, disableTouchListener: !customer, children: _jsx("span", { style: { cursor: customer ? "pointer" : "not-allowed" }, children: _jsx(ToggleButton, { value: "none", children: "None" }) }) }), _jsx(Tooltip, { title: "Display a simple, anonymous generic logo based on a generic brand",
200
- // Avoids having overlapping tooltips
201
- disableHoverListener: !customer, disableFocusListener: !customer, disableTouchListener: !customer, children: _jsx("span", { style: { cursor: customer ? "pointer" : "not-allowed" }, children: _jsx(ToggleButton, { value: "generic", children: "Generic" }) }) }), _jsx(Tooltip, { title: logoServiceToken
202
- ? "Use a service to attempt to automatically find a suitable " +
203
- "logo based on the customer name. Results may vary based " +
204
- "on the uniqueness of the name and availability of logos online."
205
- : "No Logo.dev token found, cannot use Auto logo source",
206
- // Avoids having overlapping tooltips
207
- disableHoverListener: !customer, disableFocusListener: !customer, disableTouchListener: !customer, children: _jsx("span", { style: {
208
- cursor: customer && logoServiceToken ? "pointer" : "not-allowed",
209
- }, children: _jsx(ToggleButton, { disabled: !logoServiceToken, value: "auto", children: "Auto" }) }) })] }) }) }), _jsx(FormLabel, { children: "Preview:" }), _jsx(CustomerLogo, { fallbackElement: "(None)", logoServiceToken: logoServiceToken }), _jsx(FadingCheckmark, { show: logoCheckmark.show })] }) }), _jsx(Typography, { variant: "subtitle1", sx: { marginBottom: 1, marginTop: 2, fontWeight: 600 }, children: "Network display" }), _jsx(Divider, { sx: { marginBottom: 2 } }), _jsxs(Box, { sx: { display: "flex", alignItems: "center" }, children: [_jsx(FormLabel, { children: "Palette (heatmap and depth):" }), _jsx(Tooltip, { title: customer ? "Palette is locked when branding is applied" : "", children: _jsx("span", { children: _jsx(ToggleButtonGroup, { "aria-label": "depth-heatmap-palette-selection", disabled: Boolean(customer), exclusive: true, onChange: handlePaletteChange, size: "small", sx: {
210
- cursor: customer ? "not-allowed" : "pointer",
211
- marginLeft: "1rem",
212
- marginRight: "1rem",
213
- opacity: customer ? 0.5 : 1,
214
- }, value: paletteKey, children: paletteKeys.map((key) => {
215
- const palette = availablePalettes[key];
216
- if (!palette || !Array.isArray(palette))
217
- return null;
218
- const paletteArray = palette;
219
- return (_jsx(ToggleButton, { "aria-label": `${key}-palette-button`, value: key, children: _jsxs(Box, { sx: {
220
- display: "flex",
221
- flexDirection: "column",
222
- gap: 0.5,
223
- alignItems: "center",
224
- }, children: [_jsx(Typography, { variant: "caption", sx: { textTransform: "capitalize" }, children: key }), _jsx(Box, { sx: { display: "flex", gap: 0.5 }, children: Array.from({ length: 5 }, (_, i) => {
225
- const paletteLength = paletteArray.length;
226
- const index = Math.floor((i * paletteLength) / 5);
227
- return paletteArray[index];
228
- }).map((color) => (_jsx(Box, { sx: {
229
- width: "0.75rem",
230
- height: "0.75rem",
231
- backgroundColor: color,
232
- border: "1px solid",
233
- } }, color))) })] }) }, key));
234
- }) }) }) }), _jsx(FadingCheckmark, { show: rangePaletteCheckmark.show })] }), _jsx(Typography, { variant: "subtitle1", sx: { marginBottom: 1, marginTop: 2, fontWeight: 600 }, children: "Network animation" }), _jsx(Divider, { sx: { marginBottom: 2 } }), _jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 2 }, children: [_jsx(FormLabel, { children: "Plasma animation color:" }), _jsx(Tooltip, { title: customer ? "Plasma color is locked when branding is applied" : "", children: _jsx("input", { "aria-label": "plasma-color-picker", disabled: Boolean(customer), onChange: (e) => updateSettings({
235
- appearance: {
236
- plasmaColor: e.target.value,
237
- },
238
- }), style: { cursor: customer ? "not-allowed" : "pointer", opacity: customer ? 0.5 : 1 }, type: "color", value: plasmaColor }) }), _jsx(FadingCheckmark, { show: plasmaColorCheckmark.show })] }), _jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 2, marginTop: "1rem" }, children: [_jsx(FormLabel, { children: "Agent node color:" }), _jsx(Tooltip, { title: customer ? "Agent node color is locked when branding is applied" : "", children: _jsx("input", { "aria-label": "agent-node-color-picker", disabled: Boolean(customer), onChange: (e) => updateSettings({
239
- appearance: {
240
- agentNodeColor: e.target.value,
241
- },
242
- }), style: { cursor: customer ? "not-allowed" : "pointer", opacity: customer ? 0.5 : 1 }, type: "color", value: agentNodeColor }) }), _jsx(FadingCheckmark, { show: agentNodeColorCheckmark.show })] }), _jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 2, marginTop: "1rem" }, children: [_jsx(FormLabel, { children: "Agent icon color:" }), _jsx(Tooltip, { title: customer ? "Agent icon color is locked when branding is applied" : "", children: _jsx("span", { children: _jsxs(ToggleButtonGroup, { disabled: Boolean(customer), exclusive: true, value: autoAgentIconColor ? "auto" : "custom", onChange: (_, value) => {
243
- if (value !== null) {
244
- updateSettings({
245
- appearance: {
246
- autoAgentIconColor: value === "auto",
247
- },
248
- });
249
- agentIconColorCheckmark.trigger();
250
- }
251
- }, size: "small", style: { cursor: customer ? "not-allowed" : "pointer", opacity: customer ? 0.5 : 1 }, children: [_jsx(ToggleButton, { "data-testid": "auto-agent-icon-color-button", value: "auto", children: "Auto" }), _jsx(ToggleButton, { value: "custom", children: "Custom" })] }) }) }), _jsx(Tooltip, { title: customer
252
- ? "Agent icon color is locked when branding is applied"
253
- : autoAgentIconColor
254
- ? "Disabled when Auto is selected"
255
- : "", children: _jsx("input", { "aria-label": "agent-icon-color-picker", disabled: Boolean(customer) || autoAgentIconColor, onChange: (e) => {
256
- updateSettings({
257
- appearance: {
258
- agentIconColor: e.target.value,
259
- },
260
- });
261
- agentIconColorCheckmark.trigger();
262
- }, style: { cursor: customer ? "not-allowed" : "pointer", opacity: customer ? 0.5 : 1 }, type: "color", value: agentIconColor }) }), _jsx(FadingCheckmark, { show: agentIconColorCheckmark.show })] })] }), _jsx(Box, { sx: { marginTop: 4, paddingTop: 2, borderTop: "1px solid var(--bs-border-color)" }, children: _jsx(Button, { variant: "text", startIcon: _jsx(RestoreIcon, {}), onClick: () => {
263
- setResetToDefaultSettingsOpen(true);
264
- }, sx: { color: "var(--bs-secondary)" }, children: "Reset to defaults" }) })] })] }));
465
+ }, children: [getServicesSection(), getApiKeysSection(), getBehaviorSection(), getAppearanceSection(), getResetSettingsSection()] })] }));
265
466
  };