@cognizant-ai-lab/ui-common 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/Theme/Palettes.d.ts +0 -9
- package/dist/Theme/Palettes.js +0 -18
- package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +13 -4
- package/dist/components/AgentChat/ChatCommon/ChatCommon.js +131 -38
- package/dist/components/AgentChat/ChatCommon/ChatHistory.d.ts +7 -0
- package/dist/components/AgentChat/ChatCommon/ChatHistory.js +1 -1
- 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 +5 -3
- package/dist/components/AgentChat/ChatCommon/ControlButtons.js +13 -4
- package/dist/components/AgentChat/ChatCommon/ConversationTurn.d.ts +5 -5
- package/dist/components/AgentChat/ChatCommon/ConversationTurn.js +1 -0
- 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/Common/Utils.d.ts +4 -0
- package/dist/components/AgentChat/Common/Utils.js +5 -1
- package/dist/components/AgentChat/VoiceChat/MicrophoneButton.js +12 -10
- package/dist/components/Common/Breadcrumbs.js +1 -1
- 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/Common/notification.d.ts +4 -4
- 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.d.ts → AgentFlow/AgentFlow.d.ts} +15 -5
- package/dist/components/MultiAgentAccelerator/{AgentFlow.js → AgentFlow/AgentFlow.js} +125 -84
- package/dist/components/MultiAgentAccelerator/{AgentNode.d.ts → AgentFlow/AgentNode.d.ts} +1 -1
- package/dist/components/MultiAgentAccelerator/{AgentNode.js → AgentFlow/AgentNode.js} +26 -30
- package/dist/components/MultiAgentAccelerator/AgentFlow/GraphLayouts.d.ts +43 -0
- package/dist/components/MultiAgentAccelerator/{GraphLayouts.js → AgentFlow/GraphLayouts.js} +10 -41
- package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.d.ts +21 -0
- package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.js +27 -0
- package/dist/components/MultiAgentAccelerator/AgentFlow/PlasmaEdge.d.ts +7 -0
- package/dist/components/MultiAgentAccelerator/{PlasmaEdge.js → AgentFlow/PlasmaEdge.js} +13 -8
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +3 -9
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +137 -103
- package/dist/components/MultiAgentAccelerator/Schema/SlyData.d.ts +17 -0
- package/dist/components/MultiAgentAccelerator/Schema/SlyData.js +23 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +0 -5
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +38 -28
- package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.d.ts +67 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.js +585 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +5 -4
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +77 -168
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +31 -13
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +177 -100
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +22 -0
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +74 -3
- package/dist/components/MultiAgentAccelerator/{ThoughtBubbleEdge.d.ts → ThoughtBubbles/ThoughtBubbleEdge.d.ts} +1 -1
- package/dist/components/MultiAgentAccelerator/{ThoughtBubbleOverlay.js → ThoughtBubbles/ThoughtBubbleOverlay.js} +1 -1
- package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +15 -8
- package/dist/components/MultiAgentAccelerator/const.d.ts +3 -0
- package/dist/components/MultiAgentAccelerator/const.js +6 -0
- package/dist/components/Settings/ApiKeyInput.js +7 -8
- package/dist/components/Settings/InfoTip.d.ts +8 -0
- package/dist/components/Settings/InfoTip.js +14 -0
- package/dist/components/Settings/SettingsDialog.js +373 -183
- package/dist/components/Settings/SettingsRow.d.ts +18 -0
- package/dist/components/Settings/SettingsRow.js +35 -0
- package/dist/controller/agent/Agent.d.ts +16 -25
- package/dist/controller/agent/Agent.js +24 -54
- package/dist/controller/agent/IconSuggestions.d.ts +35 -0
- package/dist/controller/agent/IconSuggestions.js +85 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -3
- package/dist/state/IconSuggestions.d.ts +27 -0
- package/dist/state/IconSuggestions.js +50 -0
- package/dist/state/Settings.d.ts +97 -6
- package/dist/state/Settings.js +110 -8
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/BrowserNavigation.js +2 -0
- package/dist/utils/File.d.ts +4 -1
- package/dist/utils/File.js +4 -9
- package/dist/utils/text.js +3 -7
- package/dist/utils/title.js +1 -1
- package/package.json +9 -6
- package/dist/components/Common/CustomerLogo.d.ts +0 -17
- package/dist/components/Common/CustomerLogo.js +0 -47
- package/dist/components/Common/MUIAccordion.d.ts +0 -17
- package/dist/components/Common/MUIAccordion.js +0 -66
- package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +0 -36
- package/dist/components/MultiAgentAccelerator/PlasmaEdge.d.ts +0 -3
- package/dist/utils/useLocalStorage.d.ts +0 -1
- package/dist/utils/useLocalStorage.js +0 -55
- /package/dist/components/MultiAgentAccelerator/{ThoughtBubbleEdge.js → ThoughtBubbles/ThoughtBubbleEdge.js} +0 -0
- /package/dist/components/MultiAgentAccelerator/{ThoughtBubbleOverlay.d.ts → ThoughtBubbles/ThoughtBubbleOverlay.d.ts} +0 -0
|
@@ -1,27 +1,89 @@
|
|
|
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
|
|
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 {
|
|
17
|
-
import {
|
|
21
|
+
import { useCheckmarkFade } from "./FadingCheckmark.js";
|
|
22
|
+
import { SettingsRow } from "./SettingsRow.js";
|
|
23
|
+
import { getBrandingSuggestions, testConnection } from "../../controller/agent/Agent.js";
|
|
18
24
|
import { isAnthropicKeyValid, isOpenAIKeyValid } from "../../controller/llm/Providers.js";
|
|
19
|
-
import {
|
|
25
|
+
import { useEnvironmentStore } from "../../state/Environment.js";
|
|
26
|
+
import { API_KEYS_TTL_MS, DEFAULT_SETTINGS, getApiKey, useSettingsStore, } from "../../state/Settings.js";
|
|
20
27
|
import { PALETTES } from "../../Theme/Palettes.js";
|
|
21
28
|
import { ConfirmationModal } from "../Common/ConfirmationModal.js";
|
|
22
|
-
import { CustomerLogo } from "../Common/CustomerLogo.js";
|
|
23
29
|
import { MUIDialog } from "../Common/MUIDialog.js";
|
|
24
30
|
import { NotificationType, sendNotification } from "../Common/notification.js";
|
|
31
|
+
import { StatusLight } from "../Common/StatusLight.js";
|
|
32
|
+
import { CustomerLogo } from "../Logo/CustomerLogo.js";
|
|
33
|
+
//#region: Styled Components
|
|
34
|
+
const SettingsSectionTitleBase = (props) => (_jsx(Typography, { variant: "h6", ...props }));
|
|
35
|
+
const SettingsSectionTitle = styled(SettingsSectionTitleBase)(({ theme }) => ({
|
|
36
|
+
marginBottom: theme.spacing(0.5),
|
|
37
|
+
}));
|
|
38
|
+
const Section = styled(Box)(({ theme }) => ({
|
|
39
|
+
display: "flex",
|
|
40
|
+
flexDirection: "column",
|
|
41
|
+
gap: theme.spacing(2),
|
|
42
|
+
marginBottom: theme.spacing(2),
|
|
43
|
+
}));
|
|
44
|
+
const SubsectionTitle = styled(Typography)(({ theme }) => ({
|
|
45
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
46
|
+
color: theme.palette.text.secondary,
|
|
47
|
+
fontWeight: 600,
|
|
48
|
+
paddingBottom: theme.spacing(0.5),
|
|
49
|
+
marginBottom: theme.spacing(1.5),
|
|
50
|
+
width: "100%",
|
|
51
|
+
}));
|
|
52
|
+
const SubSection = styled(Box)(({ theme }) => ({
|
|
53
|
+
alignItems: "start",
|
|
54
|
+
display: "flex",
|
|
55
|
+
flexDirection: "column",
|
|
56
|
+
gap: theme.spacing(2),
|
|
57
|
+
marginBottom: theme.spacing(4),
|
|
58
|
+
}));
|
|
59
|
+
const SubSectionBody = styled(Box)(({ theme }) => ({
|
|
60
|
+
alignContent: "center",
|
|
61
|
+
display: "flex",
|
|
62
|
+
flexDirection: "column",
|
|
63
|
+
gap: theme.spacing(2.5),
|
|
64
|
+
width: "100%",
|
|
65
|
+
}));
|
|
66
|
+
//#endregion: Types and Interfaces
|
|
67
|
+
// eslint-disable-next-line react/no-multi-comp -- only relevant to this module.
|
|
68
|
+
const SettingsSubsection = ({ title, children }) => (_jsxs(SubSection, { children: [_jsx(SubsectionTitle, { variant: "subtitle1", children: title }), _jsx(SubSectionBody, { children: children })] }));
|
|
69
|
+
const URL_PROTOCOL_REGEX = /^https?:\/\//iu;
|
|
70
|
+
const HTTP_PROTOCOL_REGEX = /^http:\/\//iu;
|
|
71
|
+
const stripProtocol = (value) => (value ?? "").replace(URL_PROTOCOL_REGEX, "");
|
|
72
|
+
/**
|
|
73
|
+
* Normalize a Neuro SAN URL input by stripping the protocol and determining the protocol to use.
|
|
74
|
+
* @param value The input URL value to normalize.
|
|
75
|
+
* @param fallbackProtocol The protocol to use if the input value does not specify one.
|
|
76
|
+
* @return An object containing the normalized host and protocol.
|
|
77
|
+
*/
|
|
78
|
+
const normalizeNeuroSanUrlInput = (value, fallbackProtocol) => {
|
|
79
|
+
const trimmedValue = value.trim();
|
|
80
|
+
const hasProtocol = URL_PROTOCOL_REGEX.exec(trimmedValue);
|
|
81
|
+
return {
|
|
82
|
+
host: stripProtocol(trimmedValue),
|
|
83
|
+
protocol: hasProtocol ? (HTTP_PROTOCOL_REGEX.test(trimmedValue) ? "http" : "https") : fallbackProtocol,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
// eslint-disable-next-line react/no-multi-comp -- styled component shim is only used in this module
|
|
25
87
|
export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
|
|
26
88
|
// Settings store actions
|
|
27
89
|
const updateSettings = useSettingsStore((state) => state.updateSettings);
|
|
@@ -46,14 +108,42 @@ export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
|
|
|
46
108
|
const customer = useSettingsStore((state) => state.settings.branding.customer);
|
|
47
109
|
const brandingCheckmark = useCheckmarkFade();
|
|
48
110
|
const logoCheckmark = useCheckmarkFade();
|
|
49
|
-
const [customerInput, setCustomerInput] = useState(customer);
|
|
111
|
+
const [customerInput, setCustomerInput] = useState(customer ?? "");
|
|
50
112
|
const [isBrandingApplying, setIsBrandingApplying] = useState(false);
|
|
51
113
|
const logoSource = useSettingsStore((state) => state.settings.branding.logoSource);
|
|
114
|
+
const iconSuggestion = useSettingsStore((state) => state.settings.branding.iconSuggestion);
|
|
52
115
|
// Zen mode
|
|
53
116
|
const enableZenMode = useSettingsStore((state) => state.settings.behavior.enableZenMode);
|
|
54
117
|
const enableZenModeCheckmark = useCheckmarkFade();
|
|
55
118
|
// API keys
|
|
56
119
|
const apiKeys = useSettingsStore((state) => state.settings.apiKeys);
|
|
120
|
+
const openAIKeyCheckmark = useCheckmarkFade();
|
|
121
|
+
const anthropicKeyCheckmark = useCheckmarkFade();
|
|
122
|
+
// Native names setting
|
|
123
|
+
const useNativeNames = useSettingsStore((state) => state.settings.appearance.useNativeNames);
|
|
124
|
+
const nativeNamesCheckmark = useCheckmarkFade();
|
|
125
|
+
/*
|
|
126
|
+
Neuro SAN URL
|
|
127
|
+
This can come from three sources:
|
|
128
|
+
1) The default URL from server environment variables
|
|
129
|
+
2) The persisted value in the Zustand store
|
|
130
|
+
3) The current state value, from the user's input in the settings dialog
|
|
131
|
+
*/
|
|
132
|
+
const defaultNeuroSanUrl = useEnvironmentStore((state) => state.backendNeuroSanApiUrl ?? "");
|
|
133
|
+
const persistedNeuroSanUrl = useSettingsStore((state) => state.settings.externalServices.neuroSanUrl);
|
|
134
|
+
const effectiveNeuroSanUrl = persistedNeuroSanUrl || defaultNeuroSanUrl;
|
|
135
|
+
const effectiveNeuroSanProtocol = effectiveNeuroSanUrl.startsWith("http://") ? "http" : "https";
|
|
136
|
+
const neuroSanUrlNoProtocol = stripProtocol(effectiveNeuroSanUrl);
|
|
137
|
+
const [neuroSanUrlInput, setNeuroSanUrlInput] = useState(neuroSanUrlNoProtocol);
|
|
138
|
+
const [neuroSanProtocol, setNeuroSanProtocol] = useState(effectiveNeuroSanProtocol);
|
|
139
|
+
const [neuroSanUrlValidated, setNeuroSanUrlValidated] = useState(null);
|
|
140
|
+
const neuroSanURLCheckmark = useCheckmarkFade();
|
|
141
|
+
const neuroSanHostChanged = neuroSanUrlInput.trim() !== neuroSanUrlNoProtocol;
|
|
142
|
+
const neuroSanProtocolChanged = neuroSanProtocol !== effectiveNeuroSanProtocol;
|
|
143
|
+
const neuroSanUrlSaveDisabled = neuroSanUrlInput.trim().length === 0 || (!neuroSanHostChanged && !neuroSanProtocolChanged);
|
|
144
|
+
const neuroSanUrlResetDisabled = neuroSanUrlInput === stripProtocol(defaultNeuroSanUrl) &&
|
|
145
|
+
neuroSanProtocol === (defaultNeuroSanUrl.startsWith("http://") ? "http" : "https");
|
|
146
|
+
const statusLightValue = neuroSanUrlValidated === null ? "unknown" : neuroSanUrlValidated ? "green" : "red";
|
|
57
147
|
// Record user's current theme so at least the settings dialog (with default MUI theme) matches that
|
|
58
148
|
const theme = useTheme();
|
|
59
149
|
const paletteMode = theme.palette.mode;
|
|
@@ -67,94 +157,137 @@ export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
|
|
|
67
157
|
}
|
|
68
158
|
rangePaletteCheckmark.trigger();
|
|
69
159
|
};
|
|
160
|
+
const updateBranding = (brandingSuggestions) => {
|
|
161
|
+
const { plasma, nodeColor, primary, secondary, background, rangePalette, iconSuggestion: newIconSuggestion, } = brandingSuggestions;
|
|
162
|
+
// Update persisted settings with new branding suggestions
|
|
163
|
+
const updates = {
|
|
164
|
+
branding: {
|
|
165
|
+
customer: customerInput,
|
|
166
|
+
...(primary ? { primary } : {}),
|
|
167
|
+
...(secondary ? { secondary } : {}),
|
|
168
|
+
...(background ? { background } : {}),
|
|
169
|
+
...(Array.isArray(rangePalette) ? { rangePalette } : {}),
|
|
170
|
+
...(newIconSuggestion
|
|
171
|
+
? {
|
|
172
|
+
iconSuggestion: newIconSuggestion,
|
|
173
|
+
logoSource: logoServiceToken ? "auto" : "generic",
|
|
174
|
+
}
|
|
175
|
+
: {}),
|
|
176
|
+
},
|
|
177
|
+
appearance: {
|
|
178
|
+
rangePalette: "brand",
|
|
179
|
+
...(plasma ? { plasmaColor: plasma } : {}),
|
|
180
|
+
...(nodeColor ? { agentNodeColor: nodeColor } : {}),
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
updateSettings(updates);
|
|
184
|
+
// Trigger checkmarks for items we changed
|
|
185
|
+
brandingCheckmark.trigger();
|
|
186
|
+
rangePaletteCheckmark.trigger();
|
|
187
|
+
if (plasma) {
|
|
188
|
+
plasmaColorCheckmark.trigger();
|
|
189
|
+
}
|
|
190
|
+
if (nodeColor) {
|
|
191
|
+
agentNodeColorCheckmark.trigger();
|
|
192
|
+
}
|
|
193
|
+
if (newIconSuggestion) {
|
|
194
|
+
logoCheckmark.trigger();
|
|
195
|
+
}
|
|
196
|
+
};
|
|
70
197
|
/**
|
|
71
198
|
* Handle applying branding based on customer input. Calls backend to get colors then updates settings store.
|
|
72
199
|
*/
|
|
73
200
|
const handleBrandingApply = async () => {
|
|
74
201
|
setIsBrandingApplying(true);
|
|
75
|
-
updateSettings({
|
|
76
|
-
branding: {
|
|
77
|
-
customer: customerInput,
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
let brandingSuggestions;
|
|
81
202
|
try {
|
|
82
|
-
brandingSuggestions = await getBrandingSuggestions(customerInput);
|
|
203
|
+
const brandingSuggestions = await getBrandingSuggestions(customerInput);
|
|
204
|
+
if (brandingSuggestions) {
|
|
205
|
+
updateBranding(brandingSuggestions);
|
|
206
|
+
}
|
|
83
207
|
}
|
|
84
208
|
catch (e) {
|
|
85
|
-
console.
|
|
209
|
+
console.error(`Failed to fetch branding suggestions for customer "${customerInput}"`, e);
|
|
86
210
|
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
211
|
"branding service.");
|
|
88
|
-
return;
|
|
89
212
|
}
|
|
213
|
+
finally {
|
|
214
|
+
setIsBrandingApplying(false);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const handleBrandingClear = () => {
|
|
218
|
+
// Clear branding settings
|
|
90
219
|
updateSettings({
|
|
91
220
|
appearance: {
|
|
92
|
-
|
|
221
|
+
agentIconColor: DEFAULT_SETTINGS.appearance.agentIconColor,
|
|
222
|
+
agentNodeColor: DEFAULT_SETTINGS.appearance.agentNodeColor,
|
|
223
|
+
autoAgentIconColor: DEFAULT_SETTINGS.appearance.autoAgentIconColor,
|
|
224
|
+
plasmaColor: DEFAULT_SETTINGS.appearance.plasmaColor,
|
|
225
|
+
rangePalette: DEFAULT_SETTINGS.appearance.rangePalette,
|
|
93
226
|
},
|
|
227
|
+
branding: { ...DEFAULT_SETTINGS.branding },
|
|
94
228
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
}
|
|
229
|
+
// Trigger checkmarks for all the settings that are affected by branding
|
|
230
|
+
agentIconColorCheckmark.trigger();
|
|
231
|
+
agentNodeColorCheckmark.trigger();
|
|
232
|
+
plasmaColorCheckmark.trigger();
|
|
233
|
+
rangePaletteCheckmark.trigger();
|
|
149
234
|
brandingCheckmark.trigger();
|
|
150
|
-
|
|
235
|
+
logoCheckmark.trigger();
|
|
151
236
|
};
|
|
152
|
-
|
|
237
|
+
/**
|
|
238
|
+
* Used for both saving and removing keys.
|
|
239
|
+
* @param vendor Vendor for this key: see the list in LLMProvider type
|
|
240
|
+
* @param key Value of the key
|
|
241
|
+
* @param checkmark Checkmark to trigger on save
|
|
242
|
+
* @param now Timestamp for "now"
|
|
243
|
+
*/
|
|
244
|
+
const persistKey = (vendor, key, checkmark, now) => {
|
|
153
245
|
updateSettings({
|
|
154
246
|
apiKeys: {
|
|
155
|
-
[vendor]:
|
|
247
|
+
[vendor]: {
|
|
248
|
+
value: key,
|
|
249
|
+
expiresAt: key ? now + API_KEYS_TTL_MS : 0,
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
checkmark.trigger();
|
|
254
|
+
};
|
|
255
|
+
const handleTestConnection = async () => {
|
|
256
|
+
const normalized = normalizeNeuroSanUrlInput(neuroSanUrlInput, neuroSanProtocol);
|
|
257
|
+
setNeuroSanProtocol(normalized.protocol);
|
|
258
|
+
setNeuroSanUrlInput(normalized.host);
|
|
259
|
+
const result = await testConnection(`${normalized.protocol}://${normalized.host}`);
|
|
260
|
+
setNeuroSanUrlValidated(result.success);
|
|
261
|
+
};
|
|
262
|
+
const handleNeuroSanUrlChange = (e) => {
|
|
263
|
+
setNeuroSanUrlInput(e.target.value);
|
|
264
|
+
setNeuroSanUrlValidated(null);
|
|
265
|
+
};
|
|
266
|
+
const handleSaveNeuroSanUrl = () => {
|
|
267
|
+
const normalized = normalizeNeuroSanUrlInput(neuroSanUrlInput, neuroSanProtocol);
|
|
268
|
+
setNeuroSanProtocol(normalized.protocol);
|
|
269
|
+
setNeuroSanUrlInput(normalized.host);
|
|
270
|
+
updateSettings({
|
|
271
|
+
externalServices: {
|
|
272
|
+
neuroSanUrl: `${normalized.protocol}://${normalized.host}`,
|
|
156
273
|
},
|
|
157
274
|
});
|
|
275
|
+
neuroSanURLCheckmark.trigger();
|
|
276
|
+
};
|
|
277
|
+
const handleNeuroSanProtocolChange = (e) => {
|
|
278
|
+
const newProtocol = e.target.value;
|
|
279
|
+
setNeuroSanProtocol(newProtocol);
|
|
280
|
+
setNeuroSanUrlValidated(null);
|
|
281
|
+
};
|
|
282
|
+
const handleNeuroSanUrlBlur = () => {
|
|
283
|
+
const normalized = normalizeNeuroSanUrlInput(neuroSanUrlInput, neuroSanProtocol);
|
|
284
|
+
setNeuroSanProtocol(normalized.protocol);
|
|
285
|
+
setNeuroSanUrlInput(normalized.host);
|
|
286
|
+
};
|
|
287
|
+
const handleResetNeuroSanUrl = () => {
|
|
288
|
+
const normalized = normalizeNeuroSanUrlInput(defaultNeuroSanUrl, "https");
|
|
289
|
+
setNeuroSanUrlInput(normalized.host);
|
|
290
|
+
setNeuroSanProtocol(normalized.protocol);
|
|
158
291
|
};
|
|
159
292
|
// Effect to keep input in sync with state store
|
|
160
293
|
useEffect(() => {
|
|
@@ -162,131 +295,188 @@ export const SettingsDialog = ({ id, isOpen, logoServiceToken, onClose }) => {
|
|
|
162
295
|
}, [customer]);
|
|
163
296
|
const availablePalettes = customer && brandingRangePalette?.length > 0 ? { brand: brandingRangePalette } : PALETTES;
|
|
164
297
|
const paletteKeys = Object.keys(availablePalettes);
|
|
298
|
+
// Config for API key inputs, so we can easily add new providers in the future by just adding to this array
|
|
165
299
|
const apiKeyConfigs = [
|
|
166
300
|
{
|
|
167
|
-
|
|
301
|
+
checkmark: openAIKeyCheckmark,
|
|
168
302
|
idSuffix: "openai",
|
|
169
303
|
logo: theme.palette.mode === "dark" ? "/OpenAI-white.png" : "/OpenAI-black.png",
|
|
170
304
|
onTest: isOpenAIKeyValid,
|
|
171
305
|
placeholder: "sk-...",
|
|
306
|
+
vendor: "OpenAI",
|
|
172
307
|
},
|
|
173
308
|
{
|
|
174
|
-
|
|
309
|
+
checkmark: anthropicKeyCheckmark,
|
|
175
310
|
idSuffix: "anthropic",
|
|
176
311
|
logo: "/claude.png",
|
|
177
312
|
onTest: isAnthropicKeyValid,
|
|
178
313
|
placeholder: "sk-ant-...",
|
|
314
|
+
vendor: "Anthropic",
|
|
179
315
|
},
|
|
180
316
|
];
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
317
|
+
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. " +
|
|
318
|
+
"Are you sure you want to proceed?", handleCancel: () => {
|
|
319
|
+
setResetToDefaultSettingsOpen(false);
|
|
320
|
+
}, handleOk: () => {
|
|
321
|
+
setResetToDefaultSettingsOpen(false);
|
|
322
|
+
resetSettings();
|
|
323
|
+
sendNotification(NotificationType.success, "Settings have been reset to default values.");
|
|
324
|
+
}, title: "Reset to default settings" }));
|
|
325
|
+
const getNeuroSanSubsection = () => {
|
|
326
|
+
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: {
|
|
327
|
+
input: {
|
|
328
|
+
endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { "aria-label": "Clear input", edge: "end", size: "small", onClick: () => {
|
|
329
|
+
setNeuroSanUrlInput("");
|
|
330
|
+
setNeuroSanUrlValidated(null);
|
|
331
|
+
}, children: _jsx(ClearIcon, { fontSize: "small" }) }) })),
|
|
332
|
+
},
|
|
333
|
+
}, 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`) }));
|
|
334
|
+
};
|
|
335
|
+
const getServicesSection = () => (_jsxs(Section, { children: [_jsx(SettingsSectionTitle, { children: "External Services" }), getNeuroSanSubsection()] }));
|
|
336
|
+
const getApiKeysSection = () => (_jsxs(Section, { children: [_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: theme.spacing(1) }, children: [_jsx(SettingsSectionTitle, { children: "API Keys" }), _jsx(Tooltip, { title: "API keys are stored locally in your browser's memory for the duration of your session only\n and are only sent to the Neuro SAN service for use with the associated LLM provider when you use\n this application to interact with networks.\n Your keys are never sent to any other servers or services and are not stored on our servers.\n When you close this tab or your browser, your keys will be permanently cleared from memory and\n you will need to enter them again to use services that require them.", children: _jsxs(Typography, { sx: {
|
|
337
|
+
alignSelf: "flex-start",
|
|
338
|
+
color: "var(--bs-secondary)",
|
|
339
|
+
cursor: "help",
|
|
340
|
+
fontSize: "0.7rem",
|
|
341
|
+
lineHeight: 1,
|
|
342
|
+
mt: 0.5,
|
|
343
|
+
}, variant: "caption", children: [_jsx(Box, { component: "span", sx: { mr: 0.5 }, children: "\u24D8" }), _jsx(Box, { component: "span", sx: {
|
|
344
|
+
borderBottom: "1px dashed",
|
|
345
|
+
pb: 0.25,
|
|
346
|
+
}, children: "How are my keys stored and used?" })] }) })] }), _jsx(SettingsSubsection, { title: "LLM Providers", 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, 0), id: `${id}-${idSuffix}`, logo: logo, onSave: (key) => persistKey(vendor, key, checkmark, Date.now()), onTest: onTest, persistedValue: getApiKey(apiKeys, vendor), placeholder: placeholder, vendor: vendor }) }, `${id}-${idSuffix}`))) }) })] }));
|
|
347
|
+
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 " +
|
|
348
|
+
"experience.", children: _jsx(Checkbox, { checked: enableZenMode, "data-testid": "zen-mode-checkbox", onChange: (_, checked) => {
|
|
349
|
+
updateSettings({ behavior: { enableZenMode: checked } });
|
|
350
|
+
enableZenModeCheckmark.trigger();
|
|
351
|
+
}, sx: { p: 0.0 } }) }) })] }));
|
|
352
|
+
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. " +
|
|
353
|
+
'"Native" shows the original agent name as provided by the system, while "Beautified" applies ' +
|
|
354
|
+
"formatting to make names more human-readable. This setting does not affect the actual names of " +
|
|
355
|
+
"agents, only how they are displayed in the UI.", children: [_jsxs(ToggleButtonGroup, { "aria-label": "agent-name-format-selection", exclusive: true, onChange: (_, value) => {
|
|
356
|
+
if (value !== null) {
|
|
357
|
+
updateSettings({
|
|
358
|
+
appearance: {
|
|
359
|
+
useNativeNames: value === "native",
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
nativeNamesCheckmark.trigger();
|
|
363
|
+
}
|
|
364
|
+
}, 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: {
|
|
365
|
+
border: "1px solid",
|
|
366
|
+
borderRadius: 1,
|
|
367
|
+
backgroundColor: "background.paper",
|
|
368
|
+
ml: 0.5,
|
|
369
|
+
px: 1,
|
|
370
|
+
py: 0.25,
|
|
371
|
+
lineHeight: 0,
|
|
372
|
+
fontSize: "0.7rem",
|
|
373
|
+
maxWidth: "100%",
|
|
374
|
+
}, children: _jsx("pre", { children: `category/some_agent_name → ${useNativeNames ? "category/some_agent_name [unchanged]" : "Category Some Agent Name"}` }) })] }) }));
|
|
375
|
+
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 " +
|
|
376
|
+
"logo to the network", children: [_jsx(TextField, { "aria-label": "branding-input", onChange: (e) => setCustomerInput(e.target.value), onKeyDown: (e) => {
|
|
377
|
+
if (e.key === "Enter" && customerInput?.trim().length > 0) {
|
|
378
|
+
void handleBrandingApply();
|
|
379
|
+
}
|
|
380
|
+
}, 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. " +
|
|
381
|
+
'"None" will display no logo, "Generic" will display a simple generic logo, and "Auto" will ' +
|
|
382
|
+
"attempt to find a suitable logo based on the customer name using an external service (if " +
|
|
383
|
+
"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) => {
|
|
384
|
+
if (value !== null) {
|
|
385
|
+
updateSettings({
|
|
386
|
+
branding: {
|
|
387
|
+
logoSource: value,
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
logoCheckmark.trigger();
|
|
391
|
+
}
|
|
392
|
+
}, 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
|
|
393
|
+
? logoServiceToken
|
|
394
|
+
? "Use a service to attempt to automatically find a suitable logo based " +
|
|
395
|
+
"on the customer name."
|
|
396
|
+
: "Logo service not configured on the server. Cannot use Auto logo source"
|
|
397
|
+
: 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)") })] })] }));
|
|
398
|
+
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: {
|
|
399
|
+
cursor: customer ? "not-allowed" : "pointer",
|
|
400
|
+
opacity: customer ? 0.5 : 1,
|
|
401
|
+
}, value: paletteKey, children: paletteKeys.map((key) => {
|
|
402
|
+
const palette = availablePalettes[key];
|
|
403
|
+
if (!palette || !Array.isArray(palette))
|
|
404
|
+
return null;
|
|
405
|
+
const paletteArray = palette;
|
|
406
|
+
return (_jsx(ToggleButton, { "aria-label": `${key}-palette-button`, value: key, children: _jsxs(Box, { sx: {
|
|
407
|
+
display: "flex",
|
|
408
|
+
flexDirection: "column",
|
|
409
|
+
gap: 0.5,
|
|
410
|
+
alignItems: "center",
|
|
411
|
+
}, children: [_jsx(Typography, { variant: "caption", sx: { textTransform: "capitalize" }, children: key }), _jsx(Box, { sx: { display: "flex", gap: 0.5 }, children: Array.from({ length: 5 }, (_, i) => {
|
|
412
|
+
const paletteLength = paletteArray.length;
|
|
413
|
+
const index = Math.floor((i * paletteLength) / 5);
|
|
414
|
+
return paletteArray[index];
|
|
415
|
+
}).map((color) => (_jsx(Box, { sx: {
|
|
416
|
+
width: "0.75rem",
|
|
417
|
+
height: "0.75rem",
|
|
418
|
+
backgroundColor: color,
|
|
419
|
+
border: "1px solid",
|
|
420
|
+
} }, color))) })] }) }, key));
|
|
421
|
+
}) }) }) }) }) }));
|
|
422
|
+
const getNetworkAnimationSubsection = () => (_jsxs(SettingsSubsection, { title: "Network animation", children: [_jsx(SettingsRow, { checkmark: plasmaColorCheckmark, label: "Plasma animation color:", tooltip: customer
|
|
423
|
+
? "Plasma color is locked when branding is applied"
|
|
424
|
+
: "Select the color for the plasma animation.", children: _jsx("input", { "aria-label": "plasma-color-picker", disabled: Boolean(customer), onChange: (e) => {
|
|
425
|
+
updateSettings({
|
|
426
|
+
appearance: {
|
|
427
|
+
plasmaColor: e.target.value,
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
plasmaColorCheckmark.trigger();
|
|
431
|
+
}, 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
|
|
432
|
+
? "Agent node color is locked when branding is applied"
|
|
433
|
+
: "Select the color for the agent node animation.", children: _jsx("input", { "aria-label": "agent-node-color-picker", disabled: Boolean(customer), onChange: (e) => {
|
|
434
|
+
updateSettings({
|
|
435
|
+
appearance: {
|
|
436
|
+
agentNodeColor: e.target.value,
|
|
437
|
+
},
|
|
438
|
+
});
|
|
439
|
+
agentNodeColorCheckmark.trigger();
|
|
440
|
+
}, 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
|
|
441
|
+
? "Agent icon color is locked when branding is applied"
|
|
442
|
+
: "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) => {
|
|
443
|
+
if (value !== null) {
|
|
444
|
+
updateSettings({
|
|
445
|
+
appearance: {
|
|
446
|
+
autoAgentIconColor: value === "auto",
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
agentIconColorCheckmark.trigger();
|
|
450
|
+
}
|
|
451
|
+
}, size: "small", style: {
|
|
452
|
+
cursor: customer ? "not-allowed" : "pointer",
|
|
453
|
+
opacity: customer ? 0.5 : 1,
|
|
454
|
+
}, 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) => {
|
|
455
|
+
updateSettings({
|
|
456
|
+
appearance: {
|
|
457
|
+
agentIconColor: e.target.value,
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
agentIconColorCheckmark.trigger();
|
|
461
|
+
}, style: { cursor: customer ? "not-allowed" : "pointer", opacity: customer ? 0.5 : 1 }, type: "color", value: agentIconColor })] })] }));
|
|
462
|
+
const getAppearanceSection = () => (_jsxs(Section, { children: [_jsx(SettingsSectionTitle, { children: "Appearance" }), getNamingSubsection(), getBrandingSubsection(), getNetworkDisplaySubsection(), getNetworkAnimationSubsection()] }));
|
|
463
|
+
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: () => {
|
|
464
|
+
setResetToDefaultSettingsOpen(true);
|
|
465
|
+
}, sx: { color: "var(--bs-secondary)" }, children: "Reset to defaults" }) }) }));
|
|
466
|
+
const settingsTheme = useMemo(() => createTheme({
|
|
467
|
+
palette: {
|
|
468
|
+
mode: paletteMode,
|
|
469
|
+
},
|
|
470
|
+
typography: {
|
|
471
|
+
fontSize: 12,
|
|
472
|
+
},
|
|
473
|
+
}), [paletteMode]);
|
|
187
474
|
return (
|
|
188
|
-
// Always use default theme for settings dialog so user can always see to reset. It's possible that with
|
|
475
|
+
// Always use the default theme for settings dialog so user can always see to reset. It's possible that with
|
|
189
476
|
// certain custom themes the dialog would be unreadable.
|
|
190
|
-
_jsxs(ThemeProvider, { theme:
|
|
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: {
|
|
477
|
+
_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
478
|
minWidth: "50%",
|
|
203
479
|
minHeight: "50%",
|
|
204
480
|
border: "1px solid",
|
|
205
|
-
}, children: [
|
|
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" }) })] })] }));
|
|
481
|
+
}, children: [getServicesSection(), getApiKeysSection(), getBehaviorSection(), getAppearanceSection(), getResetSettingsSection()] })] }));
|
|
292
482
|
};
|