@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.
- package/dist/Theme/Palettes.d.ts +0 -9
- package/dist/Theme/Palettes.js +0 -18
- package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +11 -7
- package/dist/components/AgentChat/ChatCommon/ChatCommon.js +119 -90
- package/dist/components/AgentChat/ChatCommon/Const.d.ts +1 -0
- package/dist/components/AgentChat/ChatCommon/Const.js +2 -0
- package/dist/components/AgentChat/ChatCommon/ControlButtons.d.ts +1 -1
- package/dist/components/AgentChat/ChatCommon/ControlButtons.js +7 -4
- package/dist/components/AgentChat/ChatCommon/FormattedMarkdown.js +3 -0
- package/dist/components/AgentChat/ChatCommon/SendButton.js +7 -6
- package/dist/components/AgentChat/ChatCommon/SyntaxHighlighterThemes.js +1 -1
- package/dist/components/AgentChat/ChatCommon/Thinking.d.ts +1 -0
- package/dist/components/AgentChat/ChatCommon/Thinking.js +10 -7
- package/dist/components/AgentChat/Common/LlmChatButton.d.ts +0 -2
- package/dist/components/AgentChat/Common/LlmChatButton.js +6 -9
- package/dist/components/AgentChat/VoiceChat/MicrophoneButton.js +12 -10
- package/dist/components/ChatBot/ChatBot.d.ts +0 -4
- package/dist/components/ChatBot/ChatBot.js +2 -2
- package/dist/components/Common/Navbar.js +24 -22
- package/dist/components/Common/StatusLight.d.ts +9 -0
- package/dist/components/Common/StatusLight.js +21 -0
- package/dist/components/ErrorPage/ErrorBoundary.js +1 -1
- package/dist/components/ErrorPage/ErrorPage.d.ts +3 -3
- package/dist/components/ErrorPage/ErrorPage.js +4 -3
- package/dist/components/Logo/Common.d.ts +4 -0
- package/dist/components/Logo/Common.js +8 -0
- package/dist/components/Logo/CustomerLogo.d.ts +23 -0
- package/dist/components/Logo/CustomerLogo.js +41 -0
- package/dist/components/MultiAgentAccelerator/AgentConversations.js +0 -1
- package/dist/components/MultiAgentAccelerator/AgentFlow.js +85 -53
- package/dist/components/MultiAgentAccelerator/AgentNode.js +23 -27
- package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +17 -10
- package/dist/components/MultiAgentAccelerator/GraphLayouts.js +4 -6
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +3 -9
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +102 -47
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +0 -5
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +30 -25
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +3 -4
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +84 -168
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +30 -13
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +157 -100
- package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +10 -8
- package/dist/components/MultiAgentAccelerator/const.d.ts +2 -0
- package/dist/components/MultiAgentAccelerator/const.js +4 -0
- package/dist/components/Settings/ApiKeyInput.d.ts +16 -0
- package/dist/components/Settings/ApiKeyInput.js +69 -0
- package/dist/components/Settings/InfoTip.d.ts +8 -0
- package/dist/components/Settings/InfoTip.js +14 -0
- package/dist/components/Settings/SettingsDialog.js +379 -178
- package/dist/components/Settings/SettingsRow.d.ts +18 -0
- package/dist/components/Settings/SettingsRow.js +35 -0
- package/dist/controller/agent/Agent.d.ts +3 -20
- package/dist/controller/agent/Agent.js +9 -43
- package/dist/controller/agent/IconSuggestions.d.ts +35 -0
- package/dist/controller/agent/IconSuggestions.js +85 -0
- package/dist/controller/llm/Providers.d.ts +2 -0
- package/dist/controller/llm/Providers.js +41 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/state/IconSuggestions.d.ts +27 -0
- package/dist/state/IconSuggestions.js +50 -0
- package/dist/state/Settings.d.ts +19 -3
- package/dist/state/Settings.js +24 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/title.js +1 -1
- package/package.json +5 -1
- package/dist/components/Common/CustomerLogo.d.ts +0 -17
- package/dist/components/Common/CustomerLogo.js +0 -49
- package/dist/components/Common/MUIAccordion.d.ts +0 -17
- package/dist/components/Common/MUIAccordion.js +0 -66
- package/dist/utils/useLocalStorage.d.ts +0 -1
- 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
|
|
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 {
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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:
|
|
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: [
|
|
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
|
};
|