@cognizant-ai-lab/ui-common 1.7.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 (67) 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 +4 -1
  4. package/dist/components/AgentChat/ChatCommon/ChatCommon.js +74 -47
  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/Common/Navbar.js +24 -22
  18. package/dist/components/Common/StatusLight.d.ts +9 -0
  19. package/dist/components/Common/StatusLight.js +21 -0
  20. package/dist/components/ErrorPage/ErrorBoundary.js +1 -1
  21. package/dist/components/ErrorPage/ErrorPage.d.ts +3 -3
  22. package/dist/components/ErrorPage/ErrorPage.js +4 -3
  23. package/dist/components/Logo/Common.d.ts +4 -0
  24. package/dist/components/Logo/Common.js +8 -0
  25. package/dist/components/Logo/CustomerLogo.d.ts +23 -0
  26. package/dist/components/Logo/CustomerLogo.js +41 -0
  27. package/dist/components/MultiAgentAccelerator/AgentConversations.js +0 -1
  28. package/dist/components/MultiAgentAccelerator/AgentFlow.js +85 -53
  29. package/dist/components/MultiAgentAccelerator/AgentNode.js +23 -27
  30. package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +17 -10
  31. package/dist/components/MultiAgentAccelerator/GraphLayouts.js +4 -6
  32. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +3 -9
  33. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +28 -32
  34. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +0 -5
  35. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +30 -25
  36. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +3 -4
  37. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +84 -168
  38. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +30 -13
  39. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +157 -100
  40. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +10 -8
  41. package/dist/components/MultiAgentAccelerator/const.d.ts +2 -0
  42. package/dist/components/MultiAgentAccelerator/const.js +4 -0
  43. package/dist/components/Settings/ApiKeyInput.js +7 -8
  44. package/dist/components/Settings/InfoTip.d.ts +8 -0
  45. package/dist/components/Settings/InfoTip.js +14 -0
  46. package/dist/components/Settings/SettingsDialog.js +355 -181
  47. package/dist/components/Settings/SettingsRow.d.ts +18 -0
  48. package/dist/components/Settings/SettingsRow.js +35 -0
  49. package/dist/controller/agent/Agent.d.ts +3 -20
  50. package/dist/controller/agent/Agent.js +9 -43
  51. package/dist/controller/agent/IconSuggestions.d.ts +35 -0
  52. package/dist/controller/agent/IconSuggestions.js +85 -0
  53. package/dist/index.d.ts +0 -2
  54. package/dist/index.js +0 -2
  55. package/dist/state/IconSuggestions.d.ts +27 -0
  56. package/dist/state/IconSuggestions.js +50 -0
  57. package/dist/state/Settings.d.ts +17 -3
  58. package/dist/state/Settings.js +23 -1
  59. package/dist/tsconfig.build.tsbuildinfo +1 -1
  60. package/dist/utils/title.js +1 -1
  61. package/package.json +5 -1
  62. package/dist/components/Common/CustomerLogo.d.ts +0 -17
  63. package/dist/components/Common/CustomerLogo.js +0 -47
  64. package/dist/components/Common/MUIAccordion.d.ts +0 -17
  65. package/dist/components/Common/MUIAccordion.js +0 -66
  66. package/dist/utils/useLocalStorage.d.ts +0 -1
  67. package/dist/utils/useLocalStorage.js +0 -55
@@ -1,27 +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 Divider from "@mui/material/Divider";
7
8
  import FormLabel from "@mui/material/FormLabel";
8
- 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";
9
14
  import TextField from "@mui/material/TextField";
10
15
  import ToggleButton from "@mui/material/ToggleButton";
11
16
  import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
12
17
  import Tooltip from "@mui/material/Tooltip";
13
18
  import Typography from "@mui/material/Typography";
14
- import { useEffect, useState } from "react";
19
+ import { useEffect, useMemo, useState, } from "react";
15
20
  import { ApiKeyInput } from "./ApiKeyInput.js";
16
- import { FadingCheckmark, useCheckmarkFade } from "./FadingCheckmark.js";
17
- import { getBrandingSuggestions } from "../../controller/agent/Agent.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";
18
25
  import { isAnthropicKeyValid, isOpenAIKeyValid } from "../../controller/llm/Providers.js";
19
- import { useSettingsStore } from "../../state/Settings.js";
26
+ import { useEnvironmentStore } from "../../state/Environment.js";
27
+ import { DEFAULT_SETTINGS, useSettingsStore } from "../../state/Settings.js";
20
28
  import { PALETTES } from "../../Theme/Palettes.js";
21
29
  import { ConfirmationModal } from "../Common/ConfirmationModal.js";
22
- import { CustomerLogo } from "../Common/CustomerLogo.js";
23
30
  import { MUIDialog } from "../Common/MUIDialog.js";
24
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
25
88
  export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
26
89
  // Settings store actions
27
90
  const updateSettings = useSettingsStore((state) => state.updateSettings);
@@ -46,14 +109,42 @@ export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
46
109
  const customer = useSettingsStore((state) => state.settings.branding.customer);
47
110
  const brandingCheckmark = useCheckmarkFade();
48
111
  const logoCheckmark = useCheckmarkFade();
49
- const [customerInput, setCustomerInput] = useState(customer);
112
+ const [customerInput, setCustomerInput] = useState(customer ?? "");
50
113
  const [isBrandingApplying, setIsBrandingApplying] = useState(false);
51
114
  const logoSource = useSettingsStore((state) => state.settings.branding.logoSource);
115
+ const iconSuggestion = useSettingsStore((state) => state.settings.branding.iconSuggestion);
52
116
  // Zen mode
53
117
  const enableZenMode = useSettingsStore((state) => state.settings.behavior.enableZenMode);
54
118
  const enableZenModeCheckmark = useCheckmarkFade();
55
119
  // API keys
56
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";
57
148
  // Record user's current theme so at least the settings dialog (with default MUI theme) matches that
58
149
  const theme = useTheme();
59
150
  const paletteMode = theme.palette.mode;
@@ -67,94 +158,127 @@ export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
67
158
  }
68
159
  rangePaletteCheckmark.trigger();
69
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
+ };
70
198
  /**
71
199
  * Handle applying branding based on customer input. Calls backend to get colors then updates settings store.
72
200
  */
73
201
  const handleBrandingApply = async () => {
74
202
  setIsBrandingApplying(true);
75
- updateSettings({
76
- branding: {
77
- customer: customerInput,
78
- },
79
- });
80
- let brandingSuggestions;
81
203
  try {
82
- brandingSuggestions = await getBrandingSuggestions(customerInput);
204
+ const brandingSuggestions = await getBrandingSuggestions(customerInput);
205
+ if (brandingSuggestions) {
206
+ updateBranding(brandingSuggestions);
207
+ }
83
208
  }
84
209
  catch (e) {
85
- console.warn(`Failed to fetch branding suggestions for customer "${customerInput}"`, e);
210
+ console.error(`Failed to fetch branding suggestions for customer "${customerInput}"`, e);
86
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 " +
87
212
  "branding service.");
88
- return;
89
213
  }
214
+ finally {
215
+ setIsBrandingApplying(false);
216
+ }
217
+ };
218
+ const handleBrandingClear = () => {
219
+ // Clear branding settings
90
220
  updateSettings({
91
221
  appearance: {
92
- 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,
93
227
  },
228
+ branding: { ...DEFAULT_SETTINGS.branding },
94
229
  });
95
- if (brandingSuggestions["plasma"]) {
96
- updateSettings({
97
- appearance: {
98
- plasmaColor: brandingSuggestions["plasma"],
99
- },
100
- });
101
- plasmaColorCheckmark.trigger();
102
- }
103
- if (brandingSuggestions["nodeColor"]) {
104
- updateSettings({
105
- appearance: {
106
- agentNodeColor: brandingSuggestions["nodeColor"],
107
- },
108
- });
109
- agentNodeColorCheckmark.trigger();
110
- }
111
- // primary
112
- if (brandingSuggestions["primary"]) {
113
- updateSettings({
114
- branding: {
115
- primary: brandingSuggestions["primary"],
116
- },
117
- });
118
- }
119
- // secondary
120
- if (brandingSuggestions["secondary"]) {
121
- updateSettings({
122
- branding: {
123
- secondary: brandingSuggestions["secondary"],
124
- },
125
- });
126
- }
127
- // background
128
- if (brandingSuggestions["background"]) {
129
- updateSettings({
130
- branding: {
131
- background: brandingSuggestions["background"],
132
- },
133
- });
134
- }
135
- if (Array.isArray(brandingSuggestions["rangePalette"])) {
136
- updateSettings({
137
- branding: {
138
- rangePalette: brandingSuggestions["rangePalette"],
139
- },
140
- });
141
- }
142
- if (brandingSuggestions["iconSuggestion"]) {
143
- updateSettings({
144
- branding: {
145
- iconSuggestion: brandingSuggestions["iconSuggestion"],
146
- },
147
- });
148
- }
230
+ // Trigger checkmarks for all the settings that are affected by branding
231
+ agentIconColorCheckmark.trigger();
232
+ agentNodeColorCheckmark.trigger();
233
+ plasmaColorCheckmark.trigger();
234
+ rangePaletteCheckmark.trigger();
149
235
  brandingCheckmark.trigger();
150
- setIsBrandingApplying(false);
236
+ logoCheckmark.trigger();
151
237
  };
152
- const persistKey = (vendor, key) => {
238
+ const persistKey = (vendor, key, checkmark) => {
153
239
  updateSettings({
154
240
  apiKeys: {
155
241
  [vendor]: key,
156
242
  },
157
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);
158
282
  };
159
283
  // Effect to keep input in sync with state store
160
284
  useEffect(() => {
@@ -162,131 +286,181 @@ export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
162
286
  }, [customer]);
163
287
  const availablePalettes = customer && brandingRangePalette?.length > 0 ? { brand: brandingRangePalette } : PALETTES;
164
288
  const paletteKeys = Object.keys(availablePalettes);
289
+ // Config for API key inputs, so we can easily add new providers in the future by just adding to this array
165
290
  const apiKeyConfigs = [
166
291
  {
167
- vendor: "OpenAI",
292
+ checkmark: openAIKeyCheckmark,
168
293
  idSuffix: "openai",
169
294
  logo: theme.palette.mode === "dark" ? "/OpenAI-white.png" : "/OpenAI-black.png",
170
295
  onTest: isOpenAIKeyValid,
171
296
  placeholder: "sk-...",
297
+ vendor: "OpenAI",
172
298
  },
173
299
  {
174
- vendor: "Anthropic",
300
+ checkmark: anthropicKeyCheckmark,
175
301
  idSuffix: "anthropic",
176
302
  logo: "/claude.png",
177
303
  onTest: isAnthropicKeyValid,
178
304
  placeholder: "sk-ant-...",
305
+ vendor: "Anthropic",
179
306
  },
180
307
  ];
181
- /* Dev note:
182
- Before you go removing the "useless" spans in code below that wrap MUI elements: they are required because
183
- MUI's disabled state on certain components sets pointer-events: none, which prevents tooltips from working.
184
- Wrapping in a span allows the tooltip to still function while the inner component is disabled.
185
- See: https://github.com/mui/material-ui/issues/8416
186
- */
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]);
187
458
  return (
188
459
  // Always use default theme for settings dialog so user can always see to reset. It's possible that with
189
460
  // certain custom themes the dialog would be unreadable.
190
- _jsxs(ThemeProvider, { theme: createTheme({
191
- palette: {
192
- mode: paletteMode,
193
- },
194
- }), 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. " +
195
- "Are you sure you want to proceed?", handleCancel: () => {
196
- setResetToDefaultSettingsOpen(false);
197
- }, handleOk: () => {
198
- setResetToDefaultSettingsOpen(false);
199
- resetSettings();
200
- sendNotification(NotificationType.success, "Settings have been reset to default values.");
201
- }, 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: {
202
462
  minWidth: "50%",
203
463
  minHeight: "50%",
204
464
  border: "1px solid",
205
- }, children: [_jsxs(Box, { sx: { marginBottom: 3 }, children: [_jsx(Typography, { variant: "h6", sx: { marginBottom: 1 }, children: "API Keys" }), _jsx(Box, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: apiKeyConfigs.map(({ vendor, idSuffix, logo, onTest, placeholder }) => (_jsx(ApiKeyInput, { forgetKey: () => persistKey(vendor, ""), id: `${id}-${idSuffix}`, logo: logo, onSave: (key) => persistKey(vendor, key), onTest: onTest, persistedValue: apiKeys[vendor], placeholder: placeholder, vendor: vendor }, idSuffix))) })] }), _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, " +
206
- "providing a more immersive experience.", children: _jsx(Checkbox, { checked: enableZenMode, "data-testid": "zen-mode-checkbox", onChange: (_, checked) => {
207
- updateSettings({ behavior: { enableZenMode: checked } });
208
- enableZenModeCheckmark.trigger();
209
- } }) }), _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) => {
210
- if (e.key === "Enter" && customerInput?.trim().length > 0) {
211
- void handleBrandingApply();
212
- }
213
- }, value: customerInput ?? "", placeholder: "Company or organization name", size: "small", sx: { width: "100%" }, variant: "outlined" }), _jsx(Button, { disabled: customerInput?.trim().length === 0 ||
214
- isBrandingApplying ||
215
- customerInput === customer, variant: "contained", size: "small", onClick: handleBrandingApply, loading: isBrandingApplying, sx: { minWidth: "8rem" }, children: "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) => {
216
- if (value !== null) {
217
- updateSettings({
218
- branding: {
219
- logoSource: value,
220
- },
221
- });
222
- logoCheckmark.trigger();
223
- }
224
- }, size: "small", sx: { marginRight: "1rem" }, value: logoSource || "none", children: [_jsx(Tooltip, { title: "No logo will be displayed",
225
- // Avoids having overlapping tooltips
226
- 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",
227
- // Avoids having overlapping tooltips
228
- 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
229
- ? "Use a service to attempt to automatically find a suitable " +
230
- "logo based on the customer name. Results may vary based " +
231
- "on the uniqueness of the name and availability of logos online."
232
- : "No Logo.dev token found, cannot use Auto logo source",
233
- // Avoids having overlapping tooltips
234
- disableHoverListener: !customer, disableFocusListener: !customer, disableTouchListener: !customer, children: _jsx("span", { style: {
235
- cursor: customer && logoServiceToken ? "pointer" : "not-allowed",
236
- }, 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: {
237
- cursor: customer ? "not-allowed" : "pointer",
238
- marginLeft: "1rem",
239
- marginRight: "1rem",
240
- opacity: customer ? 0.5 : 1,
241
- }, value: paletteKey, children: paletteKeys.map((key) => {
242
- const palette = availablePalettes[key];
243
- if (!palette || !Array.isArray(palette))
244
- return null;
245
- const paletteArray = palette;
246
- return (_jsx(ToggleButton, { "aria-label": `${key}-palette-button`, value: key, children: _jsxs(Box, { sx: {
247
- display: "flex",
248
- flexDirection: "column",
249
- gap: 0.5,
250
- alignItems: "center",
251
- }, children: [_jsx(Typography, { variant: "caption", sx: { textTransform: "capitalize" }, children: key }), _jsx(Box, { sx: { display: "flex", gap: 0.5 }, children: Array.from({ length: 5 }, (_, i) => {
252
- const paletteLength = paletteArray.length;
253
- const index = Math.floor((i * paletteLength) / 5);
254
- return paletteArray[index];
255
- }).map((color) => (_jsx(Box, { sx: {
256
- width: "0.75rem",
257
- height: "0.75rem",
258
- backgroundColor: color,
259
- border: "1px solid",
260
- } }, color))) })] }) }, key));
261
- }) }) }) }), _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({
262
- appearance: {
263
- plasmaColor: e.target.value,
264
- },
265
- }), 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({
266
- appearance: {
267
- agentNodeColor: e.target.value,
268
- },
269
- }), 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) => {
270
- if (value !== null) {
271
- updateSettings({
272
- appearance: {
273
- autoAgentIconColor: value === "auto",
274
- },
275
- });
276
- agentIconColorCheckmark.trigger();
277
- }
278
- }, 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
279
- ? "Agent icon color is locked when branding is applied"
280
- : autoAgentIconColor
281
- ? "Disabled when Auto is selected"
282
- : "", children: _jsx("input", { "aria-label": "agent-icon-color-picker", disabled: Boolean(customer) || autoAgentIconColor, onChange: (e) => {
283
- updateSettings({
284
- appearance: {
285
- agentIconColor: e.target.value,
286
- },
287
- });
288
- agentIconColorCheckmark.trigger();
289
- }, 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: () => {
290
- setResetToDefaultSettingsOpen(true);
291
- }, sx: { color: "var(--bs-secondary)" }, children: "Reset to defaults" }) })] })] }));
465
+ }, children: [getServicesSection(), getApiKeysSection(), getBehaviorSection(), getAppearanceSection(), getResetSettingsSection()] })] }));
292
466
  };