more detailed color settings

This commit is contained in:
its-a-feature
2026-05-07 22:05:27 -05:00
parent d8dedb482e
commit 5873fb05fa
20 changed files with 530 additions and 301 deletions
+96
View File
@@ -130,6 +130,14 @@ export const operatorSettingDefaults = {
dark: '#161b22',
light: '#ffffff'
},
surfaceRaised: {
dark: '#1b222c',
light: '#ffffff'
},
surfaceMuted: {
dark: '#121820',
light: '#eef2f6'
},
tableHeader: {
dark: '#484848',
light: '#c4c4c4'
@@ -142,10 +150,94 @@ export const operatorSettingDefaults = {
dark: '#171d25',
light: '#ffffff'
},
pageHeaderText: {
dark: "#e5e7eb",
light: "#111827",
},
sectionHeaderAccent: {
dark: "#8ab4f8",
light: "#2563eb",
},
sectionHeaderGradientStart: {
dark: "#374760",
light: "#d8e3fb",
},
sectionHeaderGradientMiddle: {
dark: "#253040",
light: "#edf3fe",
},
sectionHeaderGradientEnd: {
dark: "#242a31",
light: "#f5f6f7",
},
subtleAccentGradientStart: {
dark: "#1d2b3f",
light: "#edf4ff",
},
subtleAccentGradientEnd: {
dark: "#161b22",
light: "#ffffff",
},
graphGroupColor: {
dark: "#394c5d",
light: "#d3d7e8",
},
speedDialAction: {
dark: "#495054",
light: "#ffffff",
},
chartSeries1: {
dark: "#09bdff",
light: "#09bdff",
},
chartSeries2: {
dark: "#39b86f",
light: "#39b86f",
},
chartSeries3: {
dark: "#d69d2d",
light: "#d69d2d",
},
chartSeries4: {
dark: "#d65c6b",
light: "#d65c6b",
},
chartSeries5: {
dark: "#7b6fd6",
light: "#7b6fd6",
},
chartSeries6: {
dark: "#24a3a3",
light: "#24a3a3",
},
chartSeries7: {
dark: "#9c6ade",
light: "#9c6ade",
},
chartSeries8: {
dark: "#d47f38",
light: "#d47f38",
},
chartSeries9: {
dark: "#4e7ad7",
light: "#4e7ad7",
},
chartSeries10: {
dark: "#90a955",
light: "#90a955",
},
text: {
dark: "#e5e7eb",
light: "#111827",
},
textSecondary: {
dark: "#9ca3af",
light: "#475569",
},
textDisabled: {
dark: "#6b7280",
light: "#94a3b8",
},
selectedCallbackColor: {
dark: '#1e3a5f',
light: '#dbeafe',
@@ -198,6 +290,10 @@ export const operatorSettingDefaults = {
dark: '#94a3b8',
light: '#64748b'
},
folderColor: {
dark: '#f1d592',
light: '#f1d592'
},
outputBackgroundColor: {
dark: '#0b1017',
light: '#f8fafc'
+81 -28
View File
@@ -62,6 +62,22 @@ query getUserSettings {
}
`;
const normalizeBackgroundImageValue = (value) => {
if(typeof value !== "string" || value.length === 0){
return null;
}
if(value.startsWith("data:image/")){
return `url("${value}")`;
}
if(value.startsWith("url(\"data:image/") && !value.endsWith("\")")){
return `${value}")`;
}
if(value.startsWith("url(data:image/") && !value.endsWith(")")){
return `${value})`;
}
return value;
}
const getModernThemeAdditions = (themeMode, preferences = operatorSettingDefaults) => {
const isDark = themeMode === "dark";
const mode = isDark ? "dark" : "light";
@@ -76,15 +92,24 @@ const getModernThemeAdditions = (themeMode, preferences = operatorSettingDefault
return color;
};
const textPrimary = getColor("text");
const textSecondary = isDark ? "#9ca3af" : "#475569";
const textSecondary = getColor("textSecondary");
const borderColor = getColor("borderColor");
const backgroundDefault = getColor("background");
const backgroundPaper = getColor("paper");
const surfaceRaised = getColor("surfaceRaised");
const surfaceMuted = getColor("surfaceMuted");
const navBackground = getColor("navBarColor");
const navAccent = getColor("navBarBottomColor");
const navText = getColor("navBarText");
const navIcon = getColor("navBarIcons");
const primary = getColor("primary");
const sectionHeaderAccent = getColor("sectionHeaderAccent");
const sectionHeaderGradientStart = getColor("sectionHeaderGradientStart");
const sectionHeaderGradientMiddle = getColor("sectionHeaderGradientMiddle");
const sectionHeaderGradientEnd = getColor("sectionHeaderGradientEnd");
const subtleAccentGradientStart = getColor("subtleAccentGradientStart");
const subtleAccentGradientEnd = getColor("subtleAccentGradientEnd");
const chartSeriesColors = Array.from({length: 10}, (_, index) => getColor(`chartSeries${index + 1}`));
const tableHeaderColor = getColor("tableHeader");
const tableHoverColor = getColor("tableHover");
const tableRowStripeColor = withAlpha(tableHoverColor, isDark ? "55" : "66");
@@ -99,7 +124,12 @@ const getModernThemeAdditions = (themeMode, preferences = operatorSettingDefault
borderRadius: 6,
},
pageHeaderText: {
main: textPrimary,
main: getColor("pageHeaderText"),
},
gradients: {
sectionHeader: `linear-gradient(90deg, ${sectionHeaderGradientStart} 0%, ${sectionHeaderGradientMiddle} 48%, ${sectionHeaderGradientEnd} 100%)`,
subtleAccent: `linear-gradient(135deg, ${subtleAccentGradientStart} 0%, ${subtleAccentGradientEnd} 62%)`,
subtleAccentHorizontal: `linear-gradient(90deg, ${subtleAccentGradientStart} 0%, ${subtleAccentGradientEnd} 100%)`,
},
navigation: {
background: `linear-gradient(180deg, ${navBackground}, ${navAccent})`,
@@ -112,11 +142,18 @@ const getModernThemeAdditions = (themeMode, preferences = operatorSettingDefault
muted: withAlpha(navText, "B3"),
accent: navAccent,
},
sectionHeader: {
accent: sectionHeaderAccent,
gradientStart: sectionHeaderGradientStart,
gradientMiddle: sectionHeaderGradientMiddle,
gradientEnd: sectionHeaderGradientEnd,
},
chartSeriesColors,
surfaces: {
app: backgroundDefault,
paper: backgroundPaper,
raised: isDark ? "#1b222c" : "#ffffff",
muted: isDark ? withAlpha(backgroundDefault, "DD") : withAlpha(backgroundDefault, "EE"),
raised: surfaceRaised,
muted: surfaceMuted,
hover: tableRowHoverColor,
selected: tableSelectedColor,
},
@@ -639,27 +676,35 @@ export function App(props) {
preferences?.palette?.background?.light || operatorSettingDefaults.palette.background.light,
paper: themeMode === "dark" ? preferences?.palette?.paper?.dark || operatorSettingDefaults.palette.paper.dark :
preferences?.palette?.paper?.light || operatorSettingDefaults.palette.paper.light,
image: themeMode === "dark" ? preferences?.palette?.backgroundImage?.dark || operatorSettingDefaults.palette.backgroundImage.dark :
preferences?.palette?.backgroundImage?.light || operatorSettingDefaults.palette.backgroundImage.light,
image: normalizeBackgroundImageValue(themeMode === "dark" ? preferences?.palette?.backgroundImage?.dark || operatorSettingDefaults.palette.backgroundImage.dark :
preferences?.palette?.backgroundImage?.light || operatorSettingDefaults.palette.backgroundImage.light),
},
text: {
primary: themeMode === 'dark' ? preferences?.palette?.text?.dark || operatorSettingDefaults.palette.text.dark :
preferences?.palette?.text?.light || operatorSettingDefaults.palette.text.light,
secondary: themeMode === 'dark' ? '#9ca3af' : '#475569',
disabled: themeMode === 'dark' ? '#6b7280' : '#94a3b8',
secondary: themeMode === 'dark' ? preferences?.palette?.textSecondary?.dark || operatorSettingDefaults.palette.textSecondary.dark :
preferences?.palette?.textSecondary?.light || operatorSettingDefaults.palette.textSecondary.light,
disabled: themeMode === 'dark' ? preferences?.palette?.textDisabled?.dark || operatorSettingDefaults.palette.textDisabled.dark :
preferences?.palette?.textDisabled?.light || operatorSettingDefaults.palette.textDisabled.light,
contrast: themeMode === 'dark' ? '#000' : '#fff',
},
action: {
hover: themeMode === 'dark' ? 'rgba(255, 255, 255, 0.07)' : 'rgba(15, 23, 42, 0.06)',
selected: themeMode === 'dark' ? 'rgba(255, 255, 255, 0.11)' : 'rgba(37, 99, 235, 0.10)',
focus: themeMode === 'dark' ? 'rgba(138, 180, 248, 0.20)' : 'rgba(37, 99, 235, 0.18)',
hover: themeMode === 'dark' ? `${preferences?.palette?.tableHover?.dark || operatorSettingDefaults.palette.tableHover.dark}CC` :
`${preferences?.palette?.tableHover?.light || operatorSettingDefaults.palette.tableHover.light}CC`,
selected: themeMode === 'dark' ? `${preferences?.palette?.selectedCallbackColor?.dark || operatorSettingDefaults.palette.selectedCallbackColor.dark}99` :
`${preferences?.palette?.selectedCallbackColor?.light || operatorSettingDefaults.palette.selectedCallbackColor.light}99`,
focus: themeMode === 'dark' ? `${preferences?.palette?.primary?.dark || operatorSettingDefaults.palette.primary.dark}55` :
`${preferences?.palette?.primary?.light || operatorSettingDefaults.palette.primary.light}33`,
},
divider: themeMode === 'dark' ? preferences?.palette?.borderColor?.dark || operatorSettingDefaults.palette.borderColor.dark :
preferences?.palette?.borderColor?.light || operatorSettingDefaults.palette.borderColor.light,
graphGroupRGBA: themeMode === 'dark' ? 'rgba(57, 76, 93, 0.5)' : 'rgba(211, 215, 232, 0.5)',
speedDialAction: themeMode === 'dark' ? '#495054' : '#ffffff',
graphGroupRGBA: themeMode === 'dark' ? `${preferences?.palette?.graphGroupColor?.dark || operatorSettingDefaults.palette.graphGroupColor.dark}80` :
`${preferences?.palette?.graphGroupColor?.light || operatorSettingDefaults.palette.graphGroupColor.light}80`,
speedDialAction: themeMode === 'dark' ? preferences?.palette?.speedDialAction?.dark || operatorSettingDefaults.palette.speedDialAction.dark :
preferences?.palette?.speedDialAction?.light || operatorSettingDefaults.palette.speedDialAction.light,
},
folderColor: '#f1d592',
folderColor: themeMode === 'dark' ? preferences?.palette?.folderColor?.dark || operatorSettingDefaults.palette.folderColor.dark :
preferences?.palette?.folderColor?.light || operatorSettingDefaults.palette.folderColor.light,
tableHeader: themeMode === 'dark' ? preferences?.palette?.tableHeader?.dark || operatorSettingDefaults.palette.tableHeader.dark :
preferences?.palette?.tableHeader?.light || operatorSettingDefaults.palette.tableHeader.light,
selectedCallbackColor: themeMode === 'dark' ? preferences?.palette?.selectedCallbackColor?.dark || operatorSettingDefaults.palette.selectedCallbackColor.dark :
@@ -677,8 +722,8 @@ export function App(props) {
preferences?.palette?.pageHeader?.light || operatorSettingDefaults.palette.pageHeader.light,
},
pageHeaderText: {
main: themeMode === 'dark' ? preferences?.palette?.text?.dark || operatorSettingDefaults.palette.text.dark :
preferences?.palette?.text?.light || operatorSettingDefaults.palette.text.light,
main: themeMode === 'dark' ? preferences?.palette?.pageHeaderText?.dark || operatorSettingDefaults.palette.pageHeaderText.dark :
preferences?.palette?.pageHeaderText?.light || operatorSettingDefaults.palette.pageHeaderText.light,
},
topAppBarColor: themeMode === 'dark' ? (preferences?.palette?.navBarColor?.dark || operatorSettingDefaults.palette.navBarColor.dark) :
(preferences?.palette?.navBarColor?.light || operatorSettingDefaults.palette.navBarColor.light),
@@ -751,27 +796,35 @@ export function App(props) {
operatorSettingDefaults.palette.background.light,
paper: themeMode === "dark" ? operatorSettingDefaults.palette.paper.dark :
operatorSettingDefaults.palette.paper.light,
image: themeMode === "dark" ? operatorSettingDefaults.palette.backgroundImage.dark :
operatorSettingDefaults.palette.backgroundImage.light,
image: normalizeBackgroundImageValue(themeMode === "dark" ? operatorSettingDefaults.palette.backgroundImage.dark :
operatorSettingDefaults.palette.backgroundImage.light),
},
text: {
primary: themeMode === 'dark' ? operatorSettingDefaults.palette.text.dark :
operatorSettingDefaults.palette.text.light,
secondary: themeMode === 'dark' ? '#9ca3af' : '#475569',
disabled: themeMode === 'dark' ? '#6b7280' : '#94a3b8',
secondary: themeMode === 'dark' ? operatorSettingDefaults.palette.textSecondary.dark :
operatorSettingDefaults.palette.textSecondary.light,
disabled: themeMode === 'dark' ? operatorSettingDefaults.palette.textDisabled.dark :
operatorSettingDefaults.palette.textDisabled.light,
contrast: themeMode === 'dark' ? '#000' : '#fff',
},
action: {
hover: themeMode === 'dark' ? 'rgba(255, 255, 255, 0.07)' : 'rgba(15, 23, 42, 0.06)',
selected: themeMode === 'dark' ? 'rgba(255, 255, 255, 0.11)' : 'rgba(37, 99, 235, 0.10)',
focus: themeMode === 'dark' ? 'rgba(138, 180, 248, 0.20)' : 'rgba(37, 99, 235, 0.18)',
hover: themeMode === 'dark' ? `${operatorSettingDefaults.palette.tableHover.dark}CC` :
`${operatorSettingDefaults.palette.tableHover.light}CC`,
selected: themeMode === 'dark' ? `${operatorSettingDefaults.palette.selectedCallbackColor.dark}99` :
`${operatorSettingDefaults.palette.selectedCallbackColor.light}99`,
focus: themeMode === 'dark' ? `${operatorSettingDefaults.palette.primary.dark}55` :
`${operatorSettingDefaults.palette.primary.light}33`,
},
divider: themeMode === 'dark' ? operatorSettingDefaults.palette.borderColor.dark :
operatorSettingDefaults.palette.borderColor.light,
graphGroupRGBA: themeMode === 'dark' ? 'rgba(57, 76, 93, 0.5)' : 'rgba(211, 215, 232, 0.5)',
speedDialAction: themeMode === 'dark' ? '#495054' : '#ffffff',
graphGroupRGBA: themeMode === 'dark' ? `${operatorSettingDefaults.palette.graphGroupColor.dark}80` :
`${operatorSettingDefaults.palette.graphGroupColor.light}80`,
speedDialAction: themeMode === 'dark' ? operatorSettingDefaults.palette.speedDialAction.dark :
operatorSettingDefaults.palette.speedDialAction.light,
},
folderColor: '#f1d592',
folderColor: themeMode === 'dark' ? operatorSettingDefaults.palette.folderColor.dark :
operatorSettingDefaults.palette.folderColor.light,
tableHeader: themeMode === 'dark' ? operatorSettingDefaults.palette.tableHeader.dark :
operatorSettingDefaults.palette.tableHeader.light,
selectedCallbackColor: themeMode === 'dark' ? operatorSettingDefaults.palette.selectedCallbackColor.dark :
@@ -789,8 +842,8 @@ export function App(props) {
operatorSettingDefaults.palette.pageHeader.light,
},
pageHeaderText: {
main: themeMode === 'dark' ? operatorSettingDefaults.palette.text.dark :
operatorSettingDefaults.palette.text.light,
main: themeMode === 'dark' ? operatorSettingDefaults.palette.pageHeaderText.dark :
operatorSettingDefaults.palette.pageHeaderText.light,
},
topAppBarColor: themeMode === 'dark' ? operatorSettingDefaults.palette.navBarColor.dark :
operatorSettingDefaults.palette.navBarColor.light,
@@ -249,10 +249,7 @@ export const MythicSectionHeader = ({
}) => {
const theme = useTheme();
const headerTextColor = theme.pageHeaderText?.main || theme.palette.text.primary;
const sectionAccentColor = theme.palette.primary.main;
const sectionOverlayStart = alpha(sectionAccentColor, theme.palette.mode === "dark" ? 0.28 : 0.18);
const sectionOverlayMiddle = alpha(sectionAccentColor, theme.palette.mode === "dark" ? 0.12 : 0.08);
const sectionOverlayEnd = alpha(headerTextColor, theme.palette.mode === "dark" ? 0.055 : 0.04);
const sectionAccentColor = theme.sectionHeader?.accent || theme.palette.primary.main;
return (
<MythicPageHeader
actions={actions}
@@ -261,7 +258,7 @@ export const MythicSectionHeader = ({
subtitle={subtitle}
title={title}
sx={{
backgroundImage: `linear-gradient(90deg, ${sectionOverlayStart} 0%, ${sectionOverlayMiddle} 48%, ${sectionOverlayEnd} 100%)`,
backgroundImage: theme.gradients?.sectionHeader,
borderColor: alpha(sectionAccentColor, theme.palette.mode === "dark" ? 0.55 : 0.38),
boxShadow: `inset 0 1px 0 ${alpha(headerTextColor, 0.22)}, 0 2px 6px ${alpha(theme.palette.common.black, theme.palette.mode === "dark" ? 0.28 : 0.12)}`,
mb: 0.5,
@@ -59,7 +59,7 @@ const Root = styled('div')((
[`& .${classes.checkIcon}`]: {
fontSize: 20,
color: '#b3b3b3',
color: theme.palette.text.secondary,
paddingRight: 4,
},
@@ -83,4 +83,4 @@ export const MythicSnackDownload = (props) => {
</React.Fragment>
</Root>
);
};
};
@@ -24,8 +24,8 @@ export const StyledDivider = styled(Divider)((
}
) => ({
[`&.${classes.divider}`]: {
backgroundColor: "rgb(100, 170, 204)",
border: "2px solid rgba(100, 170, 204)"
backgroundColor: theme.palette.primary.main,
border: `2px solid ${theme.palette.primary.main}`
}
}));
export const StyledButton = styled(Button)((
@@ -881,7 +881,7 @@ export function TopAppBarVertical(props) {
<StyledListItem className={classes.listSubHeader} style={{display: me?.user?.current_operation_id === 0 ? "" : "none"}}>
<ListItemText primary={
<>
<Link style={{display: "inline-flex", alignItems: "center", paddingRight: "10px", color: "#f84d4d",
<Link style={{display: "inline-flex", alignItems: "center", paddingRight: "10px", color: theme.palette.error.main,
fontWeight: "bold",}} to="/new/operations">
{"CLICK TO SET OPERATION!"}
</Link>
@@ -791,9 +791,9 @@ export const CallbacksTabsCustomFileBasedBrowserTable = (props) => {
left: "35%",
top: "40%",
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
}}>
{"Only PARTIAL data has been collected for this path. "}<br/>
{"Task this callback to list the contents"}
@@ -812,9 +812,9 @@ export const CallbacksTabsCustomFileBasedBrowserTable = (props) => {
left: "35%",
top: "40%",
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
}}>
{"Some data exists for this path, but isn't loaded into the UI. "}
<br/>
@@ -829,9 +829,9 @@ export const CallbacksTabsCustomFileBasedBrowserTable = (props) => {
left: "35%",
top: "40%",
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
}}>
{"No data has been collected for this path. "}
<div style={{display: "flex", alignItems: "center"}}>
@@ -95,6 +95,7 @@ const columnDefaults = [
{ name: 'Comment', type: 'string', key: 'comment', width: 200, visible: false },
];
export const CallbacksTabsFileBrowserTable = (props) => {
const theme = useTheme();
const [updateSetting] = useSetMythicSetting();
const [loading, setLoading] = React.useState(true);
const [allData, setAllData] = React.useState([]);
@@ -682,9 +683,9 @@ export const CallbacksTabsFileBrowserTable = (props) => {
left: "35%",
top: "40%",
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
}}>
{"Only PARTIAL data has been collected for this path. "}<br/>
{"Task this callback to list the contents"}
@@ -703,9 +704,9 @@ export const CallbacksTabsFileBrowserTable = (props) => {
left: "35%",
top: "40%",
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
}}>
{"Some data exists for this path, but isn't loaded into the UI. "}
<br/>
@@ -720,9 +721,9 @@ export const CallbacksTabsFileBrowserTable = (props) => {
left: "35%",
top: "40%",
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
}}>
{"No data has been collected for this path. "}
<div style={{display: "flex", alignItems: "center"}}>
@@ -142,6 +142,7 @@ export function CallbacksTabsProcessBrowserLabel(props){
)
}
export const CallbacksTabsProcessBrowserPanel = ({index, value, tabInfo, me, setNewDataForTab}) =>{
const theme = useTheme();
const fromNow = React.useRef((new Date()));
const [backdropOpen, setBackdropOpen] = React.useState(false);
const [expandOrCollapseAll, setExpandOrCollapseAll] = React.useState(false);
@@ -410,9 +411,9 @@ export const CallbacksTabsProcessBrowserPanel = ({index, value, tabInfo, me, set
<Backdrop open={backdropOpen} style={{zIndex: 2, position: "absolute",}} invisible={false}>
<div style={{
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
alignItems: "center",
display: "flex", flexDirection: "column"}}>
<CircularProgress color="inherit" />
@@ -142,13 +142,16 @@ const handleTerminalCodes = (response) => {
return output;
}
export const GetOutputFormatAll = ({data, useASNIColor, messagesEndRef, showTaskStatus, wrapText, autoScroll=false}) => {
const theme = useTheme();
const errorOutputBackground = theme.palette.error.main + (theme.palette.mode === "dark" ? "33" : "22");
const outputTextColor = theme.outputTextColor;
const [dataElement, setDataElement] = React.useState(null);
React.useEffect( () => {
const elements = data.map( d => {
if(d.response !== undefined) {
// we're looking at response output
if(d.is_error){
return (<pre id={"response" + d.timestamp + d.id} style={{display: "inline",backgroundColor: "#311717", color: "white", margin: "0 0 0 0",
return (<pre id={"response" + d.timestamp + d.id} style={{display: "inline", backgroundColor: errorOutputBackground, color: outputTextColor, margin: "0 0 0 0",
wordBreak: wrapText ? "break-all" : "",
whiteSpace: wrapText ? "pre-wrap" : ""}} key={d.timestamp + d.id}>
{d.response}
@@ -186,7 +189,7 @@ export const GetOutputFormatAll = ({data, useASNIColor, messagesEndRef, showTask
}
})
setDataElement(elements);
}, [data, useASNIColor, showTaskStatus, wrapText]);
}, [data, useASNIColor, showTaskStatus, wrapText, errorOutputBackground, outputTextColor]);
React.useLayoutEffect( () => {
if(autoScroll){
messagesEndRef?.current?.scrollIntoView({ behavior: "auto", block: "nearest" });
@@ -1080,9 +1083,9 @@ export const ResponseDisplayInteractive = (props) =>{
<Backdrop open={backdropOpen} style={{zIndex: 2, position: "absolute",}} invisible={false}>
<div style={{
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
alignItems: "center",
display: "flex", flexDirection: "column"}}>
<CircularProgress color="inherit" />
@@ -24,7 +24,7 @@ export function ResponseDisplayScreenshotModal({onClose, images, startIndex}) {
return (
< >
<DialogContent dividers={true} style={{padding: 0}} >
<div style={{height: "calc(80vh)", display: zoom ? "" : "flex", alignItems: zoom ? "" : "flex-start" , background: theme.palette.mode === "dark" ? "rgb(44, 52, 60)" : "grey"}}>
<div style={{height: "calc(80vh)", display: zoom ? "" : "flex", alignItems: zoom ? "" : "flex-start" , background: theme.surfaces?.muted || theme.palette.background.default}}>
<img onClick={toggleZoom} src={"/api/v1.4/files/screencaptures/" + images[activeStep]}
style={{width: zoom ? "" : "100%", cursor: zoom ? "zoom-out" : "zoom-in", overflow: "auto"}} />
</div>
@@ -59,4 +59,4 @@ export function ResponseDisplayScreenshotModal({onClose, images, startIndex}) {
</DialogActions>
</>
);
}
}
@@ -623,7 +623,7 @@ const TaskActionsToolbarGeneric = ({toggleViewBrowserScript, toggleSelectAllOutp
}
<Paper className="mythic-response-actions-toolbar" elevation={0} sx={{
alignItems: "center",
backgroundColor: theme.palette.mode === "dark" ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.02)",
backgroundColor: theme.surfaces?.muted || theme.palette.background.default,
borderLeft: 0,
borderRight: 0,
borderTop: 0,
@@ -64,7 +64,7 @@ const StyledMythicDialog = styled(MythicDialog)((
[`& .${classes.checkIcon}`]: {
fontSize: 20,
color: '#b3b3b3',
color: theme.palette.text.secondary,
paddingRight: 4,
},
@@ -12,7 +12,7 @@ import DialogTitle from '@mui/material/DialogTitle';
import ToggleButton from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
import Popover from '@mui/material/Popover';
import {CallbackDataCard, DashboardEmptyCard, GaugeCard, LineTimeMultiChartCard, PieChartCard, TableDataCard} from './DashboardComponents';
import {CallbackDataCard, DashboardEmptyCard, GaugeCard, getDashboardColors, LineTimeMultiChartCard, PieChartCard, TableDataCard} from './DashboardComponents';
import {MythicAgentSVGIcon} from "../../MythicComponents/MythicAgentSVGIcon";
import {MythicStyledTooltip} from "../../MythicComponents/MythicStyledTooltip";
import {b64DecodeUnicode} from "../Callbacks/ResponseDisplay";
@@ -226,19 +226,6 @@ function getNormalizedTaskStatusColor (theme, taskStatus) {
}
// Hour in milliseconds
const ONE_HOUR = 60 * 60 * 1000;
const errorColors = [
"#de1212",
"#d56b6b",
"#de6012",
"#de126e",
"#d712de",
"#e06597",
"#de7d54",
"#640909",
"#7e0a86",
"#832f0b",
];
const dashboardOptions = ["operator", "lead", "custom"];
const dashboardElementGridSpans = {
"Activity Timeline": 2,
@@ -1204,6 +1191,7 @@ const MyOperationsDashboardElement = ({me, data, reloadDashboard, editing, remov
)
}
const Top10CommandErrorStatsDashboardElement = ({me, data, editing, removeElement}) => {
const theme = useTheme();
const navigate = useNavigate();
const [commands, setCommands] = React.useState([]);
const handleErrorTaskClick = (event, item) => {
@@ -1231,14 +1219,15 @@ const Top10CommandErrorStatsDashboardElement = ({me, data, editing, removeElemen
value: value
})
}
const chartColors = getDashboardColors(theme);
commandErrorArrayOptions = commandErrorArrayOptions.sort((a, b) => b.value - a.value).slice(0, 10).map( (c, i) => {
return {
...c,
color: errorColors[i]
color: chartColors[(i + 3) % chartColors.length]
}
})
setCommands(commandErrorArrayOptions);
}, [data]);
}, [data, theme]);
return (
<PieChartCard data={commands} onClick={handleErrorTaskClick}
title={"Top 10 Command Error Stats"} hidden={false}
@@ -1363,6 +1352,7 @@ const TaskStatusDashboardElement = ({me, data, editing, removeElement}) => {
)
}
const ActivityPerDayDashboardElement = ({me, data, editing, removeElement}) => {
const theme = useTheme();
const [tasksPerDay, setTasksPerDay] = React.useState({x: [], y: []});
React.useEffect( () => {
let callbackData = {};
@@ -1448,7 +1438,7 @@ const ActivityPerDayDashboardElement = ({me, data, editing, removeElement}) => {
highlighted: "series",
faded: "global",
},
color: '#44b636',
color: theme.palette.success.main,
yAxisId: "callbackAxis",
})
}
@@ -1464,7 +1454,7 @@ const ActivityPerDayDashboardElement = ({me, data, editing, removeElement}) => {
})
}
setTasksPerDay({x: taskDayArrayOptions, y: taskDayArrayOperatorOptions});
}, [data]);
}, [data, theme.palette.success.main]);
return (
<LineTimeMultiChartCard data={tasksPerDay} view_utc_time={me?.user?.view_utc_time} editing={editing}
removeElement={removeElement}/>
@@ -16,27 +16,14 @@ import VisibilityIcon from '@mui/icons-material/Visibility';
import Paper from "@mui/material/Paper";
import {MythicEmptyState} from "../../MythicComponents/MythicStateDisplay";
const fallbackDashboardColors = [
'#09bdff',
'#39b86f',
'#d69d2d',
'#d65c6b',
'#7b6fd6',
'#24a3a3',
'#9c6ade',
'#d47f38',
'#4e7ad7',
'#90a955',
];
const getDashboardColors = (theme) => [
export const getDashboardColors = (theme) => [
theme.palette.info.main,
theme.palette.success.main,
theme.palette.warning.main,
theme.palette.error.main,
theme.palette.secondary.main,
theme.palette.primary.main,
...fallbackDashboardColors,
...(theme.chartSeriesColors || []),
];
const DashboardCard = ({
@@ -14,6 +14,7 @@ import { MythicDisplayTextDialog} from '../../MythicComponents/MythicDisplayText
import { MythicDialog } from '../../MythicComponents/MythicDialog';
import { SelectPayloadTypeDialog } from './SelectPayloadTypeDialog';
import {MythicPageHeader, MythicPageHeaderChip} from "../../MythicComponents/MythicPageHeader";
import {useTheme} from "@mui/material/styles";
export function MitreGrid({entries, onGetCommands, onGetTasks, onGetCommandsFiltered, onGetTasksFiltered, onFilterByTags, showCountGrouping}){
@@ -56,6 +57,7 @@ export function MitreGrid({entries, onGetCommands, onGetTasks, onGetCommandsFilt
}
function PoperDropdown({onGetCommands, onGetTasks, onGetCommandsFiltered, onGetTasksFiltered, onFilterByTags, setBackdropOpen, entries, showCountGrouping}){
const theme = useTheme();
const dropdownAnchorRef = React.useRef(null);
const [dropdownOpen, setDropdownOpen] = React.useState(false);
const [openLicense, setOpenLicense] = React.useState(false);
@@ -164,9 +166,9 @@ function PoperDropdown({onGetCommands, onGetTasks, onGetCommandsFiltered, onGetT
],
"gradient": {
"colors": [
"#ff6666ff",
"#ffe766ff",
"#8ec843ff"
theme.palette.error.main,
theme.palette.warning.main,
theme.palette.success.main
],
"minValue": 0,
"maxValue": 100
@@ -175,7 +177,7 @@ function PoperDropdown({onGetCommands, onGetTasks, onGetCommandsFiltered, onGetT
"metadata": [],
"links": [],
"showTacticRowBackground": false,
"tacticRowBackground": "#dddddd",
"tacticRowBackground": theme.tableHeader,
"selectTechniquesAcrossTactics": true,
"selectSubtechniquesWithParent": false
};
@@ -190,7 +192,7 @@ function PoperDropdown({onGetCommands, onGetTasks, onGetCommandsFiltered, onGetT
{
"techniqueID": entries[key].rows[i].t_num,
"tactic": key.replaceAll(" ", "-").toLowerCase(),
"color": "#bc3b24",
"color": theme.palette.error.main,
"enabled": true,
"comment": "",
"metadata": [],
@@ -206,7 +208,7 @@ function PoperDropdown({onGetCommands, onGetTasks, onGetCommandsFiltered, onGetT
{
"techniqueID": entries[key].rows[i].t_num,
"tactic": key.replaceAll(" ", "-").toLowerCase(),
"color": "#bc3b24",
"color": theme.palette.error.main,
"enabled": true,
"comment": "",
"metadata": [],
@@ -3,8 +3,10 @@ import Button from '@mui/material/Button';
import { Box } from '@mui/material';
import { MythicDialog } from '../../MythicComponents/MythicDialog';
import { MitreMapDisplayDialog } from './MitreMapDisplayDialog';
import {useTheme} from "@mui/material/styles";
export function MitreGridRow({row, showCountGrouping}){
const theme = useTheme();
const [buttonColor, setButtonColor] = React.useState({});
const [openDisplay, setOpenDisplay] = React.useState(false);
React.useEffect( () => {
@@ -14,24 +16,24 @@ export function MitreGridRow({row, showCountGrouping}){
break;
case "command":
if(row.commands.length > 0){
setButtonColor({color: "white", backgroundColor: "rgb(188, 58, 36)"});
setButtonColor({color: theme.palette.error.contrastText, backgroundColor: theme.palette.error.main});
}else{
setButtonColor({});
}
break;
case "task":
if(row.tasks.length > 0){
setButtonColor({color: "white", backgroundColor: "rgb(188, 58, 36)"});
setButtonColor({color: theme.palette.error.contrastText, backgroundColor: theme.palette.error.main});
}else{
setButtonColor({});
}
}
}, [row.commands, row.tasks, showCountGrouping])
}, [row.commands, row.tasks, showCountGrouping, theme.palette.error.contrastText, theme.palette.error.main])
return (
<div style={{display: "flex", flexDirection: "row", alignItems: "center"}}>
{row?.t_num?.includes(".") ?
(
<Box width={"50px"} height={"1px"} style={{border: "1px dashed grey"}} />
<Box width={"50px"} height={"1px"} style={{border: `1px dashed ${theme.palette.text.secondary}`}} />
) : null}
<Button style={{
width: "100%",
@@ -54,4 +56,3 @@ export function MitreGridRow({row, showCountGrouping}){
)
}
@@ -19,6 +19,7 @@ import {CustomBrowserRow} from "./CustomBrowserCard";
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
import {MythicPageHeader, MythicPageHeaderChip} from "../../MythicComponents/MythicPageHeader";
import {MythicToolbarToggle} from "../../MythicComponents/MythicTableToolbar";
import {useTheme} from "@mui/material/styles";
const SUB_Payload_Types = gql`
subscription getPayloadTypesSubscription {
@@ -415,6 +416,7 @@ const ContainersTabBrowsersLabel = (props) => {
const ContainersTabPayloadTypesPanel = (props) => {
const theme = useTheme();
const getEmptyServiceListMessage = () => {
let message = "";
switch(props.type){
@@ -453,9 +455,9 @@ A few examples are github.com/MythicAgents/bloodhound and github.com/MythicAgent
left: "35%",
top: "40%",
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
whiteSpace: "pre-wrap"
}}>
{message}
@@ -492,6 +494,7 @@ A few examples are github.com/MythicAgents/bloodhound and github.com/MythicAgent
)
}
const ContainersTabConsumingServicesPanel = (props) => {
const theme = useTheme();
const getEmptyServiceListMessage = () => {
let message = "";
switch(props.type){
@@ -522,9 +525,9 @@ You can extend this auth capability to support your own LDAP, SSO, or otherwise
left: "35%",
top: "40%",
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
whiteSpace: "pre-wrap"
}}>
{message}
@@ -558,6 +561,7 @@ You can extend this auth capability to support your own LDAP, SSO, or otherwise
)
}
const ContainersTabCustomBrowsersPanel = (props) => {
const theme = useTheme();
const getEmptyServiceListMessage = () => {
let message = "";
switch(props.type){
@@ -572,9 +576,9 @@ const ContainersTabCustomBrowsersPanel = (props) => {
left: "35%",
top: "40%",
borderRadius: "4px",
border: "1px solid black",
border: `1px solid ${theme.palette.divider}`,
padding: "5px",
backgroundColor: "rgba(37,37,37,0.92)", color: "white",
backgroundColor: theme.palette.background.contrast, color: theme.palette.text.contrast,
whiteSpace: "pre-wrap"
}}>
{message}
@@ -29,6 +29,7 @@ import {copyStringToClipboard} from "../../utilities/Clipboard";
import {useLazyQuery } from '@apollo/client';
import PhoneCallbackIcon from '@mui/icons-material/PhoneCallback';
import ColorLensIcon from '@mui/icons-material/ColorLens';
import RestartAltIcon from '@mui/icons-material/RestartAlt';
import {MythicColorSwatchInput} from "../../MythicComponents/MythicColorInput";
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import VisibilityIcon from '@mui/icons-material/Visibility';
@@ -270,11 +271,36 @@ const COLOR_EDITOR_SECTIONS = [
colors: [
{name: "background", display: "App Background", description: "Main page background behind views and dialogs.", preview: "surface"},
{name: "paper", display: "Panel Background", description: "Menus, dialogs, and content surfaces.", preview: "surface"},
{name: "pageHeader", display: "Page Headers", description: "Header bars at the top of major pages.", preview: "surface"},
{name: "text", display: "Text", description: "Primary readable text throughout the UI.", preview: "surface"},
{name: "surfaceRaised", display: "Raised Surface", description: "Elevated cards, menus, and popover-like surfaces.", preview: "surface"},
{name: "surfaceMuted", display: "Muted Surface", description: "Quiet section backgrounds and low-emphasis containers.", preview: "surface"},
{name: "text", display: "Primary Text", description: "Primary readable text throughout the UI.", preview: "typography"},
{name: "textSecondary", display: "Secondary Text", description: "Muted helper text, labels, metadata, and quiet icon states.", preview: "typography"},
{name: "textDisabled", display: "Disabled Text", description: "Disabled controls, empty counts, and intentionally de-emphasized values.", preview: "typography"},
{name: "borderColor", display: "Borders", description: "Outlines around panels, tables, and controls.", preview: "surface"},
],
},
{
title: "Headers and Gradients",
description: "Page headers, section headers, and the subtle accent gradients used on modern cards.",
colors: [
{name: "pageHeader", display: "Page Header Surface", description: "Base surface for page and section headers.", preview: "headerGradient"},
{name: "pageHeaderText", display: "Page Header Text", description: "Text and action icon color inside page and section headers.", preview: "headerGradient"},
{name: "sectionHeaderAccent", display: "Section Accent", description: "The colored strip and border accent on section headers.", preview: "headerGradient"},
{name: "sectionHeaderGradientStart", display: "Header Gradient Start", description: "The left side of section header gradients.", preview: "headerGradient"},
{name: "sectionHeaderGradientMiddle", display: "Header Gradient Middle", description: "The middle stop of section header gradients.", preview: "headerGradient"},
{name: "sectionHeaderGradientEnd", display: "Header Gradient End", description: "The right side of section header gradients.", preview: "headerGradient"},
{name: "subtleAccentGradientStart", display: "Subtle Gradient Start", description: "The visible edge of soft dashboard, overview, and option-card gradients.", preview: "subtleGradient"},
{name: "subtleAccentGradientEnd", display: "Subtle Gradient End", description: "The fade-out side of soft dashboard, overview, and option-card gradients.", preview: "subtleGradient"},
],
},
{
title: "Graphs and Floating Controls",
description: "Graph grouping surfaces and legacy floating action controls.",
colors: [
{name: "graphGroupColor", display: "Graph Group", description: "Grouped node backgrounds in graph-style views.", preview: "graph"},
{name: "speedDialAction", display: "Floating Action", description: "Floating action buttons where SpeedDial controls are still used.", preview: "floatingAction"},
],
},
{
title: "Navigation",
description: "Left navigation gradient, labels, and icons.",
@@ -307,6 +333,22 @@ const COLOR_EDITOR_SECTIONS = [
{name: "error", display: "Error", description: "Danger actions, failed states, and errors.", preview: "status"},
],
},
{
title: "Data Visualization",
description: "Additional chart colors used when dashboard visualizations need more series than the status palette provides.",
colors: [
{name: "chartSeries1", display: "Chart Series 1", description: "Dashboard chart fallback color 1.", preview: "chart"},
{name: "chartSeries2", display: "Chart Series 2", description: "Dashboard chart fallback color 2.", preview: "chart"},
{name: "chartSeries3", display: "Chart Series 3", description: "Dashboard chart fallback color 3.", preview: "chart"},
{name: "chartSeries4", display: "Chart Series 4", description: "Dashboard chart fallback color 4.", preview: "chart"},
{name: "chartSeries5", display: "Chart Series 5", description: "Dashboard chart fallback color 5.", preview: "chart"},
{name: "chartSeries6", display: "Chart Series 6", description: "Dashboard chart fallback color 6.", preview: "chart"},
{name: "chartSeries7", display: "Chart Series 7", description: "Dashboard chart fallback color 7.", preview: "chart"},
{name: "chartSeries8", display: "Chart Series 8", description: "Dashboard chart fallback color 8.", preview: "chart"},
{name: "chartSeries9", display: "Chart Series 9", description: "Dashboard chart fallback color 9.", preview: "chart"},
{name: "chartSeries10", display: "Chart Series 10", description: "Dashboard chart fallback color 10.", preview: "chart"},
],
},
{
title: "Tasking",
description: "Task prompt, context badges, and command output.",
@@ -322,8 +364,9 @@ const COLOR_EDITOR_SECTIONS = [
},
{
title: "File Browsing",
description: "File browser empty-folder treatment.",
description: "File browser folder and empty-folder treatment.",
colors: [
{name: "folderColor", display: "Folder", description: "Normal folder icon color in file browser trees.", preview: "file"},
{name: "emptyFolderColor", display: "Empty Folder", description: "Empty folder icon and text in file-based browsers.", preview: "file"},
],
},
@@ -356,6 +399,39 @@ const getPaletteValue = (palette, name, mode) => {
return mode === "dark" ? "#1f2937" : "#f8fafc";
}
const normalizeBackgroundImageValue = (value) => {
if(typeof value !== "string" || value.length === 0){
return null;
}
if(value.startsWith("data:image/")){
return `url("${value}")`;
}
if(value.startsWith("url(\"data:image/") && !value.endsWith("\")")){
return `${value}")`;
}
if(value.startsWith("url(data:image/") && !value.endsWith(")")){
return `${value})`;
}
return value;
}
const buildInitialPalette = (initialPalette) => {
return Object.entries(operatorSettingDefaults.palette).reduce((newPalette, [name, defaultValue]) => {
if(name === "backgroundImage"){
newPalette[name] = {
dark: normalizeBackgroundImageValue(initialPalette?.[name]?.dark || defaultValue.dark),
light: normalizeBackgroundImageValue(initialPalette?.[name]?.light || defaultValue.light),
};
return newPalette;
}
newPalette[name] = {
dark: isValidColor(initialPalette?.[name]?.dark) ? initialPalette[name].dark : defaultValue.dark,
light: isValidColor(initialPalette?.[name]?.light) ? initialPalette[name].light : defaultValue.light,
};
return newPalette;
}, {});
}
const getReadableTextColor = (backgroundColor) => {
if(!isValidColor(backgroundColor)){
return "#ffffff";
@@ -367,18 +443,45 @@ const getReadableTextColor = (backgroundColor) => {
return brightness >= 140 ? "#111827" : "#ffffff";
}
const ModeColorControl = ({mode, name, color, onChange}) => (
<Box sx={{minWidth: 0}}>
<Typography variant="caption" sx={{display: "block", color: "text.secondary", mb: 0.5}}>
{mode === "dark" ? "Dark" : "Light"}
</Typography>
<MythicColorSwatchInput
color={color}
label={`${name} ${mode} color`}
onChange={(value) => onChange(name, mode, value)}
/>
</Box>
);
const ModeColorControl = ({mode, name, display, color, defaultColor, onChange}) => {
const modeLabel = mode === "dark" ? "Dark" : "Light";
const resetDisabled = defaultColor === undefined || color === defaultColor;
return (
<Box sx={{minWidth: 0}}>
<Box sx={{alignItems: "center", display: "flex", gap: 0.75, justifyContent: "space-between", mb: 0.5}}>
<Typography variant="caption" sx={{display: "block", color: "text.secondary"}}>
{modeLabel}
</Typography>
<MythicStyledTooltip title={`Reset ${display} ${modeLabel.toLowerCase()} color to default`}>
<span>
<IconButton
aria-label={`Reset ${display} ${modeLabel.toLowerCase()} color to default`}
disabled={resetDisabled}
onClick={() => onChange(name, mode, defaultColor)}
size="small"
sx={{
color: resetDisabled ? "text.disabled" : "text.secondary",
height: 24,
width: 24,
"&:hover": {
color: "warning.main",
backgroundColor: "action.hover",
},
}}
>
<RestartAltIcon sx={{fontSize: "1rem"}} />
</IconButton>
</span>
</MythicStyledTooltip>
</Box>
<MythicColorSwatchInput
color={color}
label={`${name} ${mode} color`}
onChange={(value) => onChange(name, mode, value)}
/>
</Box>
);
}
const PreviewLabel = ({children, color}) => (
<Typography
@@ -399,8 +502,23 @@ const PreviewLabel = ({children, color}) => (
const ColorUsagePreview = ({option, palette, mode}) => {
const background = getPaletteValue(palette, "background", mode);
const paper = getPaletteValue(palette, "paper", mode);
const surfaceRaised = getPaletteValue(palette, "surfaceRaised", mode);
const surfaceMuted = getPaletteValue(palette, "surfaceMuted", mode);
const text = getPaletteValue(palette, "text", mode);
const textSecondary = getPaletteValue(palette, "textSecondary", mode);
const textDisabled = getPaletteValue(palette, "textDisabled", mode);
const border = getPaletteValue(palette, "borderColor", mode);
const pageHeader = getPaletteValue(palette, "pageHeader", mode);
const pageHeaderText = getPaletteValue(palette, "pageHeaderText", mode);
const sectionHeaderAccent = getPaletteValue(palette, "sectionHeaderAccent", mode);
const sectionHeaderGradientStart = getPaletteValue(palette, "sectionHeaderGradientStart", mode);
const sectionHeaderGradientMiddle = getPaletteValue(palette, "sectionHeaderGradientMiddle", mode);
const sectionHeaderGradientEnd = getPaletteValue(palette, "sectionHeaderGradientEnd", mode);
const subtleAccentGradientStart = getPaletteValue(palette, "subtleAccentGradientStart", mode);
const subtleAccentGradientEnd = getPaletteValue(palette, "subtleAccentGradientEnd", mode);
const graphGroup = getPaletteValue(palette, "graphGroupColor", mode);
const speedDialAction = getPaletteValue(palette, "speedDialAction", mode);
const chartSeriesColors = Array.from({length: 10}, (_, index) => getPaletteValue(palette, `chartSeries${index + 1}`, mode));
const navTop = getPaletteValue(palette, "navBarColor", mode);
const navBottom = getPaletteValue(palette, "navBarBottomColor", mode);
const navIcon = getPaletteValue(palette, "navBarIcons", mode);
@@ -416,6 +534,8 @@ const ColorUsagePreview = ({option, palette, mode}) => {
const context = getPaletteValue(palette, "taskContextColor", mode);
const impersonation = getPaletteValue(palette, "taskContextImpersonationColor", mode);
const extra = getPaletteValue(palette, "taskContextExtraColor", mode);
const folder = getPaletteValue(palette, "folderColor", mode);
const emptyFolder = getPaletteValue(palette, "emptyFolderColor", mode);
const previewColor = getPaletteValue(palette, option.name, mode);
const shellSx = {
border: `1px solid ${addAlpha(border, "99")}`,
@@ -424,6 +544,8 @@ const ColorUsagePreview = ({option, palette, mode}) => {
minHeight: 74,
backgroundColor: background,
};
const sectionHeaderGradient = `linear-gradient(90deg, ${sectionHeaderGradientStart} 0%, ${sectionHeaderGradientMiddle} 48%, ${sectionHeaderGradientEnd} 100%)`;
const subtleAccentGradient = `linear-gradient(135deg, ${subtleAccentGradientStart} 0%, ${subtleAccentGradientEnd} 62%)`;
switch(option.preview){
case "navigation":
return (
@@ -470,6 +592,75 @@ const ColorUsagePreview = ({option, palette, mode}) => {
<PreviewLabel color={text}>{mode} accent</PreviewLabel>
</Box>
);
case "typography":
return (
<Box sx={{...shellSx, p: 1, backgroundColor: paper}}>
<Typography variant="caption" sx={{color: text, display: "block", fontWeight: option.name === "text" ? 800 : 600, lineHeight: 1.25}}>
Primary task title
</Typography>
<Typography variant="caption" sx={{color: textSecondary, display: "block", fontWeight: option.name === "textSecondary" ? 800 : 500, lineHeight: 1.25}}>
Secondary metadata and helper text
</Typography>
<Typography variant="caption" sx={{color: textDisabled, display: "block", fontWeight: option.name === "textDisabled" ? 800 : 500, lineHeight: 1.25}}>
Disabled or unavailable action
</Typography>
<Box sx={{height: 5, mt: 0.75, borderRadius: "4px", backgroundColor: previewColor}} />
</Box>
);
case "headerGradient":
return (
<Box sx={{...shellSx, p: 0.75, backgroundColor: background}}>
<Box sx={{height: 22, borderRadius: "4px", backgroundColor: pageHeader, color: pageHeaderText, px: 0.75, display: "flex", alignItems: "center", border: `1px solid ${addAlpha(pageHeaderText, "33")}`}}>
<PreviewLabel color={pageHeaderText}>Page header</PreviewLabel>
</Box>
<Box sx={{height: 30, mt: 0.65, borderRadius: "4px", backgroundColor: pageHeader, backgroundImage: sectionHeaderGradient, border: `1px solid ${addAlpha(sectionHeaderAccent, "99")}`, color: pageHeaderText, display: "flex", alignItems: "center", overflow: "hidden"}}>
<Box sx={{alignSelf: "stretch", width: 5, backgroundColor: sectionHeaderAccent, mr: 0.75}} />
<PreviewLabel color={pageHeaderText}>Section header</PreviewLabel>
</Box>
</Box>
);
case "subtleGradient":
return (
<Box sx={{...shellSx, p: 0.75, backgroundColor: surfaceMuted}}>
<Box sx={{height: 48, borderRadius: "5px", backgroundColor: surfaceRaised, backgroundImage: subtleAccentGradient, border: `1px solid ${addAlpha(border, "99")}`, p: 0.75}}>
<PreviewLabel color={text}>Dashboard card</PreviewLabel>
<Box sx={{height: 6, mt: 1, width: "68%", borderRadius: "3px", backgroundColor: previewColor}} />
</Box>
</Box>
);
case "graph":
return (
<Box sx={{...shellSx, p: 0.75, backgroundColor: paper}}>
<Box sx={{height: 48, borderRadius: "5px", backgroundColor: addAlpha(graphGroup, "80"), border: `1px solid ${border}`, display: "grid", placeItems: "center"}}>
<Box sx={{height: 18, width: 58, borderRadius: "4px", backgroundColor: surfaceRaised, border: `1px solid ${addAlpha(border, "99")}`}} />
</Box>
<PreviewLabel color={textSecondary}>Grouped graph node</PreviewLabel>
</Box>
);
case "floatingAction":
return (
<Box sx={{...shellSx, p: 0.75, backgroundColor: surfaceMuted, display: "flex", alignItems: "center", justifyContent: "center"}}>
<Box sx={{height: 34, width: 34, borderRadius: "50%", backgroundColor: speedDialAction, border: `1px solid ${addAlpha(border, "99")}`, boxShadow: `0 4px 10px ${addAlpha(text, "33")}`}} />
</Box>
);
case "chart":
return (
<Box sx={{...shellSx, p: 0.75, backgroundColor: paper}}>
<Box sx={{alignItems: "end", display: "grid", gap: 0.35, gridTemplateColumns: "repeat(10, 1fr)", height: 48}}>
{chartSeriesColors.map((color, index) => (
<Box
key={`${mode}-chart-${index}`}
sx={{
backgroundColor: color,
borderRadius: "3px 3px 0 0",
height: `${18 + ((index % 5) * 6)}px`,
opacity: option.name === `chartSeries${index + 1}` ? 1 : 0.5,
}}
/>
))}
</Box>
</Box>
);
case "task":
return (
<Box sx={{...shellSx, p: 1, backgroundColor: paper}}>
@@ -498,9 +689,22 @@ const ColorUsagePreview = ({option, palette, mode}) => {
case "file":
return (
<Box sx={{...shellSx, p: 1, backgroundColor: paper}}>
<Box sx={{height: 24, borderRadius: "4px", backgroundColor: addAlpha(previewColor, "33"), border: `1px solid ${addAlpha(previewColor, "99")}`}} />
<Typography variant="caption" sx={{color: previewColor, display: "block", mt: 0.75, fontWeight: 700}}>
Empty folder
<Box sx={{display: "grid", gap: 0.5}}>
<Box sx={{alignItems: "center", display: "flex", gap: 0.5}}>
<Box sx={{height: 16, width: 20, borderRadius: "3px", backgroundColor: folder}} />
<Typography variant="caption" sx={{color: text, fontWeight: option.name === "folderColor" ? 800 : 600}}>
Folder with files
</Typography>
</Box>
<Box sx={{alignItems: "center", display: "flex", gap: 0.5}}>
<Box sx={{height: 16, width: 20, borderRadius: "3px", backgroundColor: addAlpha(emptyFolder, "66"), border: `1px solid ${emptyFolder}`}} />
<Typography variant="caption" sx={{color: option.name === "emptyFolderColor" ? emptyFolder : textSecondary, fontWeight: option.name === "emptyFolderColor" ? 800 : 600}}>
Empty folder
</Typography>
</Box>
</Box>
<Typography variant="caption" sx={{color: textSecondary, display: "block", mt: 0.75}}>
File browser tree
</Typography>
</Box>
);
@@ -508,10 +712,16 @@ const ColorUsagePreview = ({option, palette, mode}) => {
default:
return (
<Box sx={{...shellSx, p: 0.75}}>
<Box sx={{height: 16, borderRadius: "4px 4px 0 0", backgroundColor: getPaletteValue(palette, "pageHeader", mode)}} />
<Box sx={{height: 20, borderRadius: "4px 4px 0 0", backgroundColor: pageHeader, color: pageHeaderText, px: 0.75, display: "flex", alignItems: "center"}}>
<PreviewLabel color={pageHeaderText}>Page header</PreviewLabel>
</Box>
<Box sx={{p: 0.75, backgroundColor: paper, border: `1px solid ${addAlpha(border, "99")}`, borderTop: 0, borderRadius: "0 0 4px 4px"}}>
<PreviewLabel color={text}>{mode} surface</PreviewLabel>
<Box sx={{height: 7, mt: 0.75, borderRadius: "4px", backgroundColor: previewColor}} />
<PreviewLabel color={option.name === "textSecondary" ? textSecondary : text}>{mode} surface</PreviewLabel>
<Box sx={{display: "grid", gap: 0.4, gridTemplateColumns: "1fr 1fr", mt: 0.75}}>
<Box sx={{height: 9, borderRadius: "4px", backgroundColor: surfaceRaised, border: `1px solid ${addAlpha(border, "66")}`}} />
<Box sx={{height: 9, borderRadius: "4px", backgroundColor: surfaceMuted, border: `1px solid ${addAlpha(border, "66")}`}} />
</Box>
<Box sx={{height: 6, mt: 0.55, borderRadius: "4px", backgroundColor: previewColor}} />
</Box>
</Box>
);
@@ -543,13 +753,17 @@ const ColorTokenEditor = ({option, palette, onChange}) => (
<ModeColorControl
mode="dark"
name={option.name}
display={option.display}
color={palette?.[option.name]?.dark}
defaultColor={operatorSettingDefaults.palette?.[option.name]?.dark}
onChange={onChange}
/>
<ModeColorControl
mode="light"
name={option.name}
display={option.display}
color={palette?.[option.name]?.light}
defaultColor={operatorSettingDefaults.palette?.[option.name]?.light}
onChange={onChange}
/>
</Box>
@@ -735,120 +949,7 @@ export function SettingsOperatorUIConfigDialog(props) {
const [showOPSECBypassUsername, setShowOPSECBypassUsername] = React.useState(initialShowOPSECBypassUsername);
const initialPalette = GetMythicSetting({setting_name: 'palette', default_value: operatorSettingDefaults.palette});
const [palette, setPalette] = React.useState({
primary: {
dark: isValidColor(initialPalette?.primary?.dark) ? initialPalette?.primary?.dark : operatorSettingDefaults.palette.primary.dark,
light: isValidColor(initialPalette?.primary?.light) ? initialPalette?.primary?.light : operatorSettingDefaults.palette.primary.light,
},
error: {
dark: isValidColor(initialPalette?.error?.dark) ? initialPalette?.error?.dark : operatorSettingDefaults.palette.error.dark,
light: isValidColor(initialPalette?.error?.light) ? initialPalette?.error?.light : operatorSettingDefaults.palette.error.light,
},
success: {
dark: isValidColor(initialPalette?.success?.dark) ? initialPalette?.success?.dark : operatorSettingDefaults.palette.success.dark,
light: isValidColor(initialPalette?.success?.light) ? initialPalette?.success?.light : operatorSettingDefaults.palette.success.light,
},
info: {
dark: isValidColor(initialPalette?.info?.dark) ? initialPalette?.info?.dark : operatorSettingDefaults.palette.info.dark,
light: isValidColor(initialPalette?.info?.light) ? initialPalette?.info?.light : operatorSettingDefaults.palette.info.light,
},
warning: {
dark: isValidColor(initialPalette?.warning?.dark) ? initialPalette?.warning?.dark : operatorSettingDefaults.palette.warning.dark,
light: isValidColor(initialPalette?.warning?.light) ? initialPalette?.warning?.light : operatorSettingDefaults.palette.warning.light,
},
secondary: {
dark: isValidColor(initialPalette?.secondary?.dark) ? initialPalette?.secondary?.dark : operatorSettingDefaults.palette.secondary.dark,
light: isValidColor(initialPalette?.secondary?.light) ? initialPalette?.secondary?.light : operatorSettingDefaults.palette.secondary.light,
},
background: {
dark: isValidColor(initialPalette?.background?.dark) ? initialPalette?.background?.dark : operatorSettingDefaults.palette.background.dark,
light: isValidColor(initialPalette?.background?.light) ? initialPalette?.background?.light : operatorSettingDefaults.palette.background.light,
},
tableHeader: {
dark: isValidColor(initialPalette?.tableHeader?.dark) ? initialPalette?.tableHeader?.dark : operatorSettingDefaults.palette.tableHeader.dark,
light: isValidColor(initialPalette?.tableHeader?.light) ? initialPalette?.tableHeader?.light : operatorSettingDefaults.palette.tableHeader.light,
},
tableHover: {
dark: isValidColor(initialPalette?.tableHover?.dark) ? initialPalette?.tableHover?.dark : operatorSettingDefaults.palette.tableHover.dark,
light: isValidColor(initialPalette?.tableHover?.light) ? initialPalette?.tableHover?.light : operatorSettingDefaults.palette.tableHover.light,
},
pageHeader: {
dark: isValidColor(initialPalette?.pageHeader?.dark) ? initialPalette?.pageHeader?.dark : operatorSettingDefaults.palette.pageHeader.dark,
light: isValidColor(initialPalette?.pageHeader?.light) ? initialPalette?.pageHeader?.light : operatorSettingDefaults.palette.pageHeader.light,
},
text: {
dark: isValidColor(initialPalette?.text?.dark) ? initialPalette?.text?.dark : operatorSettingDefaults.palette.text.dark,
light: isValidColor(initialPalette?.text?.light) ? initialPalette?.text?.light : operatorSettingDefaults.palette.text.light,
},
paper: {
dark: isValidColor(initialPalette?.paper?.dark) ? initialPalette?.paper?.dark : operatorSettingDefaults.palette.paper.dark,
light: isValidColor(initialPalette?.paper?.light) ? initialPalette?.paper?.light : operatorSettingDefaults.palette.paper.light,
},
selectedCallbackColor: {
dark: isValidColor(initialPalette?.selectedCallbackColor?.dark) ? initialPalette?.selectedCallbackColor?.dark : operatorSettingDefaults.palette.selectedCallbackColor.dark,
light: isValidColor(initialPalette?.selectedCallbackColor?.light) ? initialPalette?.selectedCallbackColor?.light : operatorSettingDefaults.palette.selectedCallbackColor.light,
},
selectedCallbackHierarchyColor: {
dark: isValidColor(initialPalette?.selectedCallbackHierarchyColor?.dark) ? initialPalette?.selectedCallbackHierarchyColor?.dark : operatorSettingDefaults.palette.selectedCallbackHierarchyColor.dark,
light: isValidColor(initialPalette?.selectedCallbackHierarchyColor?.light) ? initialPalette?.selectedCallbackHierarchyColor?.light : operatorSettingDefaults.palette.selectedCallbackHierarchyColor.light,
},
backgroundImage: {
dark: initialPalette?.backgroundImage?.dark || operatorSettingDefaults.palette.backgroundImage.dark,
light: initialPalette?.backgroundImage?.light || operatorSettingDefaults.palette.backgroundImage.light,
},
navBarIcons: {
dark: isValidColor(initialPalette?.navBarIcons?.dark) ? initialPalette?.navBarIcons?.dark : operatorSettingDefaults.palette.navBarIcons.dark,
light: isValidColor(initialPalette?.navBarIcons?.light) ? initialPalette?.navBarIcons?.light : operatorSettingDefaults.palette.navBarIcons.light,
},
navBarText: {
dark: isValidColor(initialPalette?.navBarText?.dark) ? initialPalette?.navBarText?.dark : operatorSettingDefaults.palette.navBarText.dark,
light: isValidColor(initialPalette?.navBarText?.light) ? initialPalette?.navBarText?.light : operatorSettingDefaults.palette.navBarText.light,
},
navBarColor: {
dark: isValidColor(initialPalette?.navBarColor?.dark) ? initialPalette?.navBarColor?.dark : operatorSettingDefaults.palette.navBarColor.dark,
light: isValidColor(initialPalette?.navBarColor?.light) ? initialPalette?.navBarColor?.light : operatorSettingDefaults.palette.navBarColor.light,
},
navBarBottomColor: {
dark: isValidColor(initialPalette?.navBarBottomColor?.dark) ? initialPalette?.navBarBottomColor?.dark : operatorSettingDefaults.palette.navBarBottomColor.dark,
light: isValidColor(initialPalette?.navBarBottomColor?.light) ? initialPalette?.navBarBottomColor?.light : operatorSettingDefaults.palette.navBarBottomColor.light,
},
taskPromptTextColor: {
dark: isValidColor(initialPalette?.taskPromptTextColor?.dark) ? initialPalette?.taskPromptTextColor?.dark : operatorSettingDefaults.palette.taskPromptTextColor.dark,
light: isValidColor(initialPalette?.taskPromptTextColor?.light) ? initialPalette?.taskPromptTextColor?.light : operatorSettingDefaults.palette.taskPromptTextColor.light,
},
taskPromptCommandTextColor: {
dark: isValidColor(initialPalette?.taskPromptCommandTextColor?.dark) ? initialPalette?.taskPromptCommandTextColor?.dark : operatorSettingDefaults.palette.taskPromptCommandTextColor.dark,
light: isValidColor(initialPalette?.taskPromptCommandTextColor?.light) ? initialPalette?.taskPromptCommandTextColor?.light : operatorSettingDefaults.palette.taskPromptCommandTextColor.light,
},
taskContextColor: {
dark: isValidColor(initialPalette?.taskContextColor?.dark) ? initialPalette?.taskContextColor?.dark : operatorSettingDefaults.palette.taskContextColor.dark,
light: isValidColor(initialPalette?.taskContextColor?.light) ? initialPalette?.taskContextColor?.light : operatorSettingDefaults.palette.taskContextColor.light,
},
taskContextImpersonationColor: {
dark: isValidColor(initialPalette?.taskContextImpersonationColor?.dark) ? initialPalette?.taskContextImpersonationColor?.dark : operatorSettingDefaults.palette.taskContextImpersonationColor.dark,
light: isValidColor(initialPalette?.taskContextImpersonationColor?.light) ? initialPalette?.taskContextImpersonationColor?.light : operatorSettingDefaults.palette.taskContextImpersonationColor.light,
},
taskContextExtraColor: {
dark: isValidColor(initialPalette?.taskContextExtraColor?.dark) ? initialPalette?.taskContextExtraColor?.dark : operatorSettingDefaults.palette.taskContextExtraColor.dark,
light: isValidColor(initialPalette?.taskContextExtraColor?.light) ? initialPalette?.taskContextExtraColor?.light : operatorSettingDefaults.palette.taskContextExtraColor.light,
},
emptyFolderColor: {
dark: isValidColor(initialPalette?.emptyFolderColor?.dark) ? initialPalette?.emptyFolderColor?.dark : operatorSettingDefaults.palette.emptyFolderColor.dark,
light: isValidColor(initialPalette?.emptyFolderColor?.light) ? initialPalette?.emptyFolderColor?.light : operatorSettingDefaults.palette.emptyFolderColor.light,
},
outputBackgroundColor: {
dark: isValidColor(initialPalette?.outputBackgroundColor?.dark) ? initialPalette?.outputBackgroundColor?.dark : operatorSettingDefaults.palette.outputBackgroundColor.dark,
light: isValidColor(initialPalette?.outputBackgroundColor?.light) ? initialPalette?.outputBackgroundColor?.light : operatorSettingDefaults.palette.outputBackgroundColor.light,
},
outputTextColor: {
dark: isValidColor(initialPalette?.outputTextColor?.dark) ? initialPalette?.outputTextColor?.dark : operatorSettingDefaults.palette.outputTextColor.dark,
light: isValidColor(initialPalette?.outputTextColor?.light) ? initialPalette?.outputTextColor?.light : operatorSettingDefaults.palette.outputTextColor.light,
},
borderColor: {
dark: isValidColor(initialPalette?.borderColor?.dark) ? initialPalette?.borderColor?.dark : operatorSettingDefaults.palette.borderColor.dark,
light: isValidColor(initialPalette?.borderColor?.light) ? initialPalette?.borderColor?.light : operatorSettingDefaults.palette.borderColor.light,
},
});
const [palette, setPalette] = React.useState(() => buildInitialPalette(initialPalette));
const [resumeNotifications, setResumeNotifications] = React.useState(false);
const [, updateSettings, clearSettings] = useSetMythicSetting();
const onChangeFontFamily = (name, value, error) => {
@@ -972,24 +1073,28 @@ export function SettingsOperatorUIConfigDialog(props) {
reader.readAsBinaryString(evt.target.files[0]);
}
const onFileBackgroundImageChangeLight = async (evt) => {
const file = evt.target.files?.[0];
if(!file){
return;
}
const reader = new FileReader();
const filenameExtension = evt.target.files[0].name.split(".")[1];
reader.onload = (e) => {
const contents = e.target.result;
let backgroundImage = `url("data:image/${filenameExtension};base64,${btoa(contents)}`;
let backgroundImage = normalizeBackgroundImageValue(e.target.result);
setPalette({...palette, backgroundImage: {...palette.backgroundImage, light: backgroundImage}});
}
reader.readAsBinaryString(evt.target.files[0]);
reader.readAsDataURL(file);
}
const onFileBackgroundImageChangeDark = async (evt) => {
const file = evt.target.files?.[0];
if(!file){
return;
}
const reader = new FileReader();
const filenameExtension = evt.target.files[0].name.split(".")[1];
reader.onload = (e) => {
const contents = e.target.result;
let backgroundImage = `url("data:image/${filenameExtension};base64,${btoa(contents)}`;
let backgroundImage = normalizeBackgroundImageValue(e.target.result);
setPalette({...palette, backgroundImage: {...palette.backgroundImage, dark: backgroundImage}});
}
reader.readAsBinaryString(evt.target.files[0]);
reader.readAsDataURL(file);
}
const getCurrentPreferences = () => {
+37 -48
View File
@@ -1,6 +1,19 @@
import { createGlobalStyle} from "styled-components"
import {alpha} from "@mui/material/styles";
// hex transparencies https://gist.github.com/lopspower/03fb1cc0ac9f32ef38f4
const getSectionHeaderAccent = (props) => props.theme.sectionHeader?.accent || props.theme.palette.primary.main;
const getSectionHeaderGradient = (props) => {
if(props.theme.gradients?.sectionHeader){
return props.theme.gradients.sectionHeader;
}
const headerTextColor = props.theme.pageHeaderText?.main || props.theme.palette.text.primary;
return `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.28 : 0.18)} 0%, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.12 : 0.08)} 48%, ${alpha(headerTextColor, props.theme.palette.mode === "dark" ? 0.055 : 0.04)} 100%)`;
};
const getSubtleAccentGradient = (props) => props.theme.gradients?.subtleAccent ||
`linear-gradient(135deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.13 : 0.075)} 0%, ${alpha(props.theme.palette.background.paper, 0)} 62%)`;
const getSubtleAccentHorizontalGradient = (props) => props.theme.gradients?.subtleAccentHorizontal ||
`linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)} 0%, ${alpha(props.theme.palette.background.paper, 0)} 100%)`;
export const GlobalStyles = createGlobalStyle`
body {
margin: 0;
@@ -183,7 +196,7 @@ tspan {
}
.resizer.isResizing {
background: blue;
background: ${(props) => props.theme.sectionHeader?.accent || props.theme.palette.primary.main};
opacity: 1;
}
.groupNode {
@@ -608,9 +621,9 @@ tspan {
}
.mythic-grid-filter-dialog-title {
background-color: ${(props) => props.theme.pageHeader?.main || props.theme.surfaces?.muted || props.theme.palette.background.default};
background-image: ${(props) => `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.18 : 0.1)} 0%, ${alpha(props.theme.palette.text.primary, props.theme.palette.mode === "dark" ? 0.045 : 0.035)} 100%)`};
background-image: ${getSectionHeaderGradient};
border-bottom: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
color: ${(props) => props.theme.palette.text.primary};
color: ${(props) => props.theme.pageHeaderText?.main || props.theme.palette.text.primary};
font-size: 0.98rem !important;
font-weight: 850 !important;
line-height: 1.2 !important;
@@ -1839,11 +1852,8 @@ tspan {
.mythic-detail-section-header {
align-items: center;
background-color: ${(props) => props.theme.pageHeader.main};
background-image: ${(props) => {
const headerTextColor = props.theme.pageHeaderText?.main || props.theme.palette.text.primary;
return `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.28 : 0.18)} 0%, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.12 : 0.08)} 48%, ${alpha(headerTextColor, props.theme.palette.mode === "dark" ? 0.055 : 0.04)} 100%)`;
}};
border: 1px solid ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.55 : 0.38)};
background-image: ${getSectionHeaderGradient};
border: 1px solid ${(props) => alpha(getSectionHeaderAccent(props), props.theme.palette.mode === "dark" ? 0.55 : 0.38)};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
box-shadow: inset 0 1px 0 ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.22)}, 0 2px 6px ${(props) => alpha(props.theme.palette.common.black, props.theme.palette.mode === "dark" ? 0.28 : 0.12)};
color: ${(props) => props.theme.pageHeaderText?.main || props.theme.palette.text.primary};
@@ -1860,7 +1870,7 @@ tspan {
width: 100%;
}
.mythic-detail-section-header::before {
background-color: ${(props) => props.theme.palette.primary.main};
background-color: ${getSectionHeaderAccent};
bottom: 0;
box-shadow: 0 0 0 1px ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.2)};
content: "";
@@ -1870,18 +1880,15 @@ tspan {
width: 6px;
}
.mythic-section-header {
background-image: ${(props) => {
const headerTextColor = props.theme.pageHeaderText?.main || props.theme.palette.text.primary;
return `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.28 : 0.18)} 0%, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.12 : 0.08)} 48%, ${alpha(headerTextColor, props.theme.palette.mode === "dark" ? 0.055 : 0.04)} 100%)`;
}} !important;
border-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.55 : 0.38)} !important;
background-image: ${getSectionHeaderGradient} !important;
border-color: ${(props) => alpha(getSectionHeaderAccent(props), props.theme.palette.mode === "dark" ? 0.55 : 0.38)} !important;
box-shadow: inset 0 1px 0 ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.22)}, 0 2px 6px ${(props) => alpha(props.theme.palette.common.black, props.theme.palette.mode === "dark" ? 0.28 : 0.12)} !important;
overflow: hidden !important;
padding-left: 1rem !important;
position: relative !important;
}
.mythic-section-header::before {
background-color: ${(props) => props.theme.palette.primary.main};
background-color: ${getSectionHeaderAccent};
bottom: 0;
box-shadow: 0 0 0 1px ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.2)};
content: "";
@@ -3490,10 +3497,7 @@ tspan {
min-width: 0;
}
.mythic-create-parameter-group-header {
background-image: ${(props) => {
const headerTextColor = props.theme.pageHeaderText?.main || props.theme.palette.text.primary;
return `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.18 : 0.1)} 0%, ${alpha(headerTextColor, props.theme.palette.mode === "dark" ? 0.055 : 0.04)} 100%)`;
}};
background-image: ${getSectionHeaderGradient};
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
color: ${(props) => props.theme.pageHeaderText?.main || props.theme.palette.text.primary};
@@ -3508,10 +3512,7 @@ tspan {
min-width: 0;
}
.mythic-create-summary-group-header {
background-image: ${(props) => {
const headerTextColor = props.theme.pageHeaderText?.main || props.theme.palette.text.primary;
return `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.18 : 0.1)} 0%, ${alpha(headerTextColor, props.theme.palette.mode === "dark" ? 0.055 : 0.04)} 100%)`;
}};
background-image: ${getSectionHeaderGradient};
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
color: ${(props) => props.theme.palette.text.primary};
@@ -3741,11 +3742,8 @@ tspan {
.mythic-dashboard-card-header {
align-items: center;
background-color: ${(props) => props.theme.surfaces?.muted || props.theme.palette.background.default};
background-image: ${(props) => {
const headerTextColor = props.theme.pageHeaderText?.main || props.theme.palette.text.primary;
return `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.16 : 0.09)} 0%, ${alpha(headerTextColor, props.theme.palette.mode === "dark" ? 0.05 : 0.035)} 100%)`;
}};
border-bottom: 1px solid ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.34 : 0.2)};
background-image: ${getSectionHeaderGradient};
border-bottom: 1px solid ${(props) => alpha(getSectionHeaderAccent(props), props.theme.palette.mode === "dark" ? 0.34 : 0.2)};
display: flex;
flex: 0 0 auto;
gap: 0.65rem;
@@ -3757,7 +3755,7 @@ tspan {
position: relative;
}
.mythic-dashboard-card-header::before {
background-color: ${(props) => props.theme.palette.primary.main};
background-color: ${getSectionHeaderAccent};
bottom: 0;
content: "";
left: 0;
@@ -3874,7 +3872,7 @@ tspan {
}
.mythic-dashboard-chart-canvas {
background-color: ${(props) => props.theme.palette.mode === "dark" ? alpha(props.theme.palette.common.white, 0.028) : alpha(props.theme.palette.common.black, 0.014)};
background-image: ${(props) => `linear-gradient(135deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.1 : 0.055)} 0%, ${alpha(props.theme.palette.background.paper, 0)} 62%)`};
background-image: ${getSubtleAccentGradient};
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
display: flex;
@@ -4202,7 +4200,7 @@ tspan {
.mythic-dashboard-callback-kpi,
.mythic-dashboard-service-kpi {
background-color: ${(props) => props.theme.palette.mode === "dark" ? alpha(props.theme.palette.common.white, 0.035) : alpha(props.theme.palette.common.black, 0.018)};
background-image: ${(props) => `linear-gradient(135deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.13 : 0.075)} 0%, ${alpha(props.theme.palette.background.paper, 0)} 58%)`};
background-image: ${getSubtleAccentGradient};
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
display: flex;
@@ -4412,12 +4410,9 @@ tspan {
}
.mythic-dashboard-edit-toolbar {
align-items: center;
background-color: ${(props) => {
const headerSurface = props.theme.pageHeader?.main || props.theme.surfaces?.muted || props.theme.palette.background.paper;
return props.theme.palette.mode === "dark" ? headerSurface : alpha(props.theme.palette.primary.main, 0.16);
}} !important;
background-image: ${(props) => `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.44 : 0.32)} 0%, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.22 : 0.22)} 42%, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.08 : 0.12)} 100%)`} !important;
border: 1px solid ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.55 : 0.38)};
background-color: ${(props) => props.theme.pageHeader?.main || props.theme.surfaces?.muted || props.theme.palette.background.paper} !important;
background-image: ${getSectionHeaderGradient} !important;
border: 1px solid ${(props) => alpha(getSectionHeaderAccent(props), props.theme.palette.mode === "dark" ? 0.55 : 0.38)};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
box-shadow: inset 0 1px 0 ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.22)}, 0 2px 6px ${(props) => alpha(props.theme.palette.common.black, props.theme.palette.mode === "dark" ? 0.24 : 0.1)};
color: ${(props) => props.theme.pageHeaderText?.main || props.theme.palette.text.primary};
@@ -4432,7 +4427,7 @@ tspan {
position: relative;
}
.mythic-dashboard-edit-toolbar::before {
background-color: ${(props) => props.theme.palette.primary.main};
background-color: ${getSectionHeaderAccent};
bottom: 0;
box-shadow: 0 0 0 1px ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.2)};
content: "";
@@ -4483,10 +4478,7 @@ tspan {
.mythic-dashboard-widget-dialog-header {
align-items: center;
background-color: ${(props) => props.theme.surfaces?.raised || props.theme.palette.background.paper};
background-image: ${(props) => {
const headerTextColor = props.theme.palette.text.primary;
return `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)} 0%, ${alpha(headerTextColor, props.theme.palette.mode === "dark" ? 0.045 : 0.035)} 100%)`;
}};
background-image: ${getSectionHeaderGradient};
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
display: flex;
@@ -4537,7 +4529,7 @@ tspan {
outline: none;
}
.mythic-dashboard-widget-option-selected {
background-image: ${(props) => `linear-gradient(135deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.18 : 0.1)} 0%, ${alpha(props.theme.palette.background.paper, 0)} 60%)`};
background-image: ${getSubtleAccentGradient};
border-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.68 : 0.48)};
}
.mythic-dashboard-widget-option-top {
@@ -5704,10 +5696,7 @@ tspan {
overflow: hidden;
}
.mythic-eventing-step-config-section-header {
background-image: ${(props) => {
const headerTextColor = props.theme.pageHeaderText?.main || props.theme.palette.text.primary;
return `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)} 0%, ${alpha(headerTextColor, props.theme.palette.mode === "dark" ? 0.045 : 0.035)} 100%)`;
}};
background-image: ${getSectionHeaderGradient};
border-bottom: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
padding: 0.62rem 0.7rem 0.55rem;
}
@@ -6150,7 +6139,7 @@ tspan {
}
.mythic-eventing-workflow-overview {
background-color: ${(props) => props.theme.surfaces?.raised || props.theme.palette.background.paper};
background-image: ${(props) => `linear-gradient(90deg, ${alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.12 : 0.06)} 0%, ${alpha(props.theme.palette.background.paper, 0)} 48%)`};
background-image: ${getSubtleAccentHorizontalGradient};
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
box-shadow: ${(props) => props.theme.palette.mode === "dark" ? "inset 0 1px 0 rgba(255,255,255,0.055)" : "inset 0 1px 0 rgba(255,255,255,0.78)"};