RSS Git Download  Clone
Raw Blame History 17kB 388 lines
import { createTheme, ThemeOptions } from '@mui/material'

// Extend MUI theme with custom p3xr properties
declare module '@mui/material/styles' {
    interface Theme {
        p3xr: {
            accordionBg: string
            accordionColor: string
            strongBg: string
            strongColor: string
            inputBorderColor: string
            inputBg: string
            inputColor: string
            treeBranchColor: string
            commonWarnColor: string
            treecontrolIconColor: string
            matSysPrimary: string
        }
    }
    interface ThemeOptions {
        p3xr?: {
            accordionBg?: string
            accordionColor?: string
            strongBg?: string
            strongColor?: string
            inputBorderColor?: string
            inputBg?: string
            inputColor?: string
            treeBranchColor?: string
            commonWarnColor?: string
            treecontrolIconColor?: string
            matSysPrimary?: string
        }
    }
}

const TOOLBAR_HEIGHT = 48

// FontAwesome icon sizing
const faIconBase = {
    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
    width: 24, height: 24, fontSize: 20, lineHeight: '24px', verticalAlign: 'middle',
}
const faIconSmaller = { ...faIconBase, fontSize: 18 }

// Toolbar button styles
const toolbarButtonStyles = {
    color: 'inherit !important', letterSpacing: '0.1px !important',
    textTransform: 'uppercase' as const, height: 36, minHeight: 36, minWidth: 'auto',
    padding: '0 4px', margin: '0 4px', display: 'inline-flex', alignItems: 'center',
    justifyContent: 'center', whiteSpace: 'nowrap' as const, gap: 0, borderRadius: '4px',
    '&:hover': { backgroundColor: 'rgba(255, 255, 255, 0.15) !important' },
    '& .MuiSvgIcon-root, & i.fa, & i.fas, & i.far, & i.fab': { marginRight: 4, marginLeft: 0 },
    '& .MuiSvgIcon-root:only-child, & i:only-child': { marginRight: 0 },
}
const toolbarIconButtonStyles = {
    borderRadius: '4px !important', color: 'inherit !important', margin: '0 2px',
    '&:hover': { backgroundColor: 'rgba(255, 255, 255, 0.15) !important', borderRadius: '4px !important' },
    '& .MuiTouchRipple-root': { borderRadius: '4px !important' },
    '& .MuiTouchRipple-child': { borderRadius: '4px !important' },
    '&.Mui-focusVisible': { borderRadius: '4px !important' },
}

function makeShared(toolbarBg: string, toolbarColor: string): ThemeOptions['components'] {
    return {
        MuiCssBaseline: { styleOverrides: `
            body.p3xr-no-main-scroll { overflow: hidden; }
            body.p3xr-no-main-scroll #p3xr-layout-content { overflow: hidden; }
            body.p3xr-no-main-scroll #p3xr-monitoring-content { overflow: hidden; }
            body.p3xr-theme-dark { color-scheme: dark; }
            body.p3xr-theme-light { color-scheme: light; }
            [data-color-scheme="dark"] { color-scheme: dark; }
            [data-color-scheme="light"] { color-scheme: light; }
            body.p3xr-theme-dark ::selection { color: white; background: black; }
            body.p3xr-theme-light fieldset { border-color: rgba(0, 0, 0, 0.5); }
            body.p3xr-theme-dark fieldset { border-color: rgba(255, 255, 255, 0.25); }
            body.p3xr-no-animation *,
            body.p3xr-no-animation *::before,
            body.p3xr-no-animation *::after {
                animation-duration: 0ms !important;
                transition-duration: 0ms !important;
                transition-delay: 0ms !important;
            }
            body.p3xr-no-animation .fa-spin {
                animation-duration: 2s !important;
            }
            .p3xr-console-content-output-item:before {
                content: "> ";
                opacity: 0.5;
            }
            .p3xr-console-ai-result {
                display: block;
            }
            #p3xr-console-content {
                font-family: 'Roboto Mono', monospace;
                text-align: center;
            }
            #p3xr-console-content-output {
                min-width: calc(100% - 20px);
                text-align: left;
                overflow: auto;
            }
            #p3xr-console-content-output pre {
                font-family: 'Roboto Mono', monospace;
                white-space: pre-wrap;
                word-break: break-word;
                overflow-wrap: anywhere;
                margin: 0;
            }
            #p3xr-console-input {
                display: block;
                width: 100%;
                box-sizing: border-box;
                font-family: 'Roboto Mono', monospace;
                resize: none;
                overflow-y: hidden;
                outline: none;
                max-height: 90px;
            }
            .p3xr-key-type-actions {
                display: flex; flex-wrap: wrap; justify-content: flex-end;
                align-items: center; gap: 8px; padding: 4px 8px;
            }
            .p3xr-key-type-content { padding: 8px 16px 24px; }
            .p3xr-key-type-display { padding: 8px; }
            .p3xr-key-type-editor { width: 100%; }
            .p3xr-key-type-editor textarea { font-family: 'Roboto Mono', monospace; font-size: 13px; }
            .p3xr-key-type-buffer-info { padding: 8px; opacity: 0.7; font-style: italic; }
            .p3xr-json-tree-key { color: var(--p3xr-json-key-color, inherit); }
            .p3xr-json-tree-value-string { color: var(--p3xr-json-value-string, #0b7500); }
            .p3xr-json-tree-value-number { color: var(--p3xr-json-value-number, #1a01cc); }
            .p3xr-json-tree-value-boolean { color: var(--p3xr-json-value-boolean, #c41a16); }
            .p3xr-json-tree-value-null { color: var(--p3xr-json-value-null, #808080); font-style: italic; }
            .p3xr-console-hint {
                font-family: 'Roboto Mono', monospace;
                font-size: 12px;
                padding: 2px 6px;
                opacity: 0.6;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
            }
            .p3xr-pager-input::-webkit-outer-spin-button,
            .p3xr-pager-input::-webkit-inner-spin-button {
                -webkit-appearance: none;
                margin: 0;
            }
        `},
        MuiToolbar: { styleOverrides: { root: {
            minHeight: `${TOOLBAR_HEIGHT}px !important`, height: TOOLBAR_HEIGHT,
            '& i.fa, & i.fas, & i.far, & i.fab': faIconBase,
            '& i.fa-power-off': faIconSmaller, '& i.fa-donate': faIconSmaller,
        }}},
        MuiAppBar: { styleOverrides: { root: {
            backgroundColor: toolbarBg, color: toolbarColor,
            '& .MuiButton-root': toolbarButtonStyles,
            '& .MuiIconButton-root': toolbarIconButtonStyles,
        }}},
    }
}

// Helper to build a theme from exact SCSS hex values
interface ThemeColors {
    mode: 'light' | 'dark'
    toolbarBg: string
    toolbarColor: string
    strongBg: string
    accordionBg: string
    accordionColor: string
    primary: string
    primaryText: string
    accent: string       // → secondary
    accentText: string
    warn: string         // → error
    warnText: string
    success: string
    successText: string
    bodyBg: string
    contentBg: string
    dialogBg: string
    borderColor: string
    hoverBg: string
    selectedBg: string
    linkColor: string
    inputBorderColor: string
    inputBg: string
    inputColor: string
    treeBranchColor: string
    commonWarnColor: string
    treecontrolIconColor: string
    matSysPrimary: string  // M3 system primary from main sub-theme (used for tab indicators)
}

function buildTheme(c: ThemeColors) {
    return createTheme({
        typography: {
            fontFamily: "Roboto, 'Helvetica Neue', sans-serif",
            // Reset letter-spacing to normal (matches Angular's --mat-*-tracking: normal)
            h1: { letterSpacing: 'normal' },
            h2: { letterSpacing: 'normal' },
            h3: { letterSpacing: 'normal' },
            h4: { letterSpacing: 'normal' },
            h5: { letterSpacing: 'normal' },
            h6: { letterSpacing: 'normal' },
            subtitle1: { letterSpacing: 'normal' },
            subtitle2: { letterSpacing: 'normal' },
            body1: { letterSpacing: 'normal' },
            body2: { letterSpacing: 'normal' },
            button: { letterSpacing: 'normal' },
            caption: { letterSpacing: 'normal' },
            overline: { letterSpacing: 'normal' },
        },
        p3xr: {
            accordionBg: c.accordionBg,
            accordionColor: c.accordionColor,
            strongBg: c.strongBg,
            strongColor: 'rgba(255,255,255,0.87)',
            inputBorderColor: c.inputBorderColor,
            inputBg: c.inputBg,
            inputColor: c.inputColor,
            treeBranchColor: c.treeBranchColor,
            commonWarnColor: c.commonWarnColor,
            treecontrolIconColor: c.treecontrolIconColor,
            matSysPrimary: c.matSysPrimary,
        },
        palette: {
            mode: c.mode,
            primary: { main: c.primary, contrastText: c.primaryText },
            secondary: { main: c.accent, contrastText: c.accentText },
            error: { main: c.warn, contrastText: c.warnText },
            success: { main: c.success, contrastText: c.successText },
            warning: { main: c.warn, contrastText: c.warnText },
            background: { default: c.bodyBg, paper: c.contentBg },
            divider: c.mode === 'dark' ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.12)',
            action: { hover: c.hoverBg, selected: c.selectedBg },
        },
        components: {
            ...makeShared(c.toolbarBg, c.toolbarColor),
            MuiDialog: { styleOverrides: { paper: { backgroundColor: c.dialogBg, backgroundImage: 'none' } } },
        },
    })
}

// ============================================================
// All 7 themes with exact colors from _theme-custom.scss
// ============================================================

export const themes: Record<string, ReturnType<typeof createTheme>> = {
    // Enterprise (light) — Layout=grey, Main=indigo/blue/red
    enterprise: buildTheme({
        mode: 'light',
        toolbarBg: '#424242', toolbarColor: 'rgba(255,255,255,0.87)',
        strongBg: '#212121', accordionBg: '#9e9e9e', accordionColor: 'rgba(0,0,0,0.87)',
        primary: '#3f51b5', primaryText: '#fff',
        accent: '#1976d2', accentText: '#fff',
        warn: '#d32f2f', warnText: '#fff',
        success: '#4caf50', successText: '#fff',
        bodyBg: '#e0e0e0', contentBg: '#fafafa', dialogBg: '#ffffff',
        borderColor: '#424242', hoverBg: 'rgba(0,0,0,0.1)', selectedBg: 'rgba(0,0,0,0.1)',
        linkColor: '#1a73e8',
        inputBorderColor: '#9e9e9e', inputBg: 'white', inputColor: 'black',
        treeBranchColor: '#343dff', commonWarnColor: '#03a9f4',
        treecontrolIconColor: 'rgba(0,0,0,0.87)',
        matSysPrimary: '#005faf',  // azure tone 40
    }),

    // Light — Layout=blue-grey, Main=deep-purple/purple/red
    light: buildTheme({
        mode: 'light',
        toolbarBg: '#37474f', toolbarColor: 'rgba(255,255,255,0.87)',
        strongBg: '#263238', accordionBg: '#b0bec5', accordionColor: 'rgba(0,0,0,0.87)',
        primary: '#673ab7', primaryText: '#fff',
        accent: '#9c27b0', accentText: '#fff',
        warn: '#d32f2f', warnText: '#fff',
        success: '#4caf50', successText: '#fff',
        bodyBg: '#cfd8dc', contentBg: '#eceff1', dialogBg: '#cfd8dc',
        borderColor: '#37474f', hoverBg: 'rgba(0,0,0,0.1)', selectedBg: 'rgba(0,0,0,0.1)',
        linkColor: '#1a73e8',
        inputBorderColor: '#b0bec5', inputBg: 'white', inputColor: 'black',
        treeBranchColor: '#a900a9', commonWarnColor: '#607d8b',
        treecontrolIconColor: 'rgba(0,0,0,0.87)',
        matSysPrimary: '#6750a4',  // violet tone 40
    }),

    // Redis (light) — Layout=red, Main=grey/amber
    redis: buildTheme({
        mode: 'light',
        toolbarBg: '#c62828', toolbarColor: 'rgba(255,255,255,0.87)',
        strongBg: '#b71c1c', accordionBg: '#ef9a9a', accordionColor: 'rgba(0,0,0,0.87)',
        primary: '#212121', primaryText: 'rgba(255,255,255,0.87)',
        accent: '#757575', accentText: '#fff',
        warn: '#ffc107', warnText: '#fff',
        success: '#4caf50', successText: '#fff',
        bodyBg: '#ffcdd2', contentBg: '#fafafa', dialogBg: '#ffffff',
        borderColor: '#c62828', hoverBg: 'rgba(0,0,0,0.1)', selectedBg: 'rgba(0,0,0,0.1)',
        linkColor: '#1a73e8',
        inputBorderColor: '#ef9a9a', inputBg: 'white', inputColor: 'black',
        treeBranchColor: '#964900', commonWarnColor: '#f44336',
        treecontrolIconColor: 'rgba(0,0,0,0.87)',
        matSysPrimary: '#b0294b',  // rose tone 40
    }),

    // Dark — Layout=grey, Main=indigo-300/blue/orange
    dark: buildTheme({
        mode: 'dark',
        toolbarBg: '#424242', toolbarColor: 'rgba(255,255,255,0.87)',
        strongBg: '#212121', accordionBg: '#9e9e9e', accordionColor: 'rgba(0,0,0,0.87)',
        primary: '#7986cb', primaryText: 'rgba(0,0,0,0.87)',
        accent: '#2196f3', accentText: 'rgba(0,0,0,0.87)',
        warn: '#ff9800', warnText: 'rgba(0,0,0,0.87)',
        success: '#4caf50', successText: 'rgba(0,0,0,0.87)',
        bodyBg: '#212121', contentBg: '#303030', dialogBg: '#424242',
        borderColor: '#424242', hoverBg: 'rgba(255,255,255,0.1)', selectedBg: 'rgba(255,255,255,0.1)',
        linkColor: '#82b1ff',
        inputBorderColor: '#9e9e9e', inputBg: 'rgba(64,64,64,1)', inputColor: 'white',
        treeBranchColor: '#bec2ff', commonWarnColor: '#9fa8da',
        treecontrolIconColor: 'rgba(255,255,255,0.7)',
        matSysPrimary: '#9ecaff',  // azure tone 80
    }),

    // DarkNeu — Layout=blue-grey, Main=cyan/blue/yellow
    darkNeu: buildTheme({
        mode: 'dark',
        toolbarBg: '#37474f', toolbarColor: 'rgba(255,255,255,0.87)',
        strongBg: '#263238', accordionBg: '#90a4ae', accordionColor: 'rgba(0,0,0,0.87)',
        primary: '#00bcd4', primaryText: 'rgba(0,0,0,0.87)',
        accent: '#2196f3', accentText: 'rgba(0,0,0,0.87)',
        warn: '#ffeb3b', warnText: 'rgba(0,0,0,0.87)',
        success: '#4caf50', successText: 'rgba(0,0,0,0.87)',
        bodyBg: '#263238', contentBg: '#303030', dialogBg: '#424242',
        borderColor: '#37474f', hoverBg: 'rgba(255,255,255,0.1)', selectedBg: 'rgba(255,255,255,0.1)',
        linkColor: '#82b1ff',
        inputBorderColor: '#90a4ae', inputBg: 'rgba(64,64,64,1)', inputColor: 'white',
        treeBranchColor: '#bec2ff', commonWarnColor: '#2196f3',
        treecontrolIconColor: 'rgba(255,255,255,0.7)',
        matSysPrimary: '#54d7eb',  // cyan tone 80
    }),

    // DarkoBluo — Layout=indigo, Main=indigo-300/blue/orange
    darkoBluo: buildTheme({
        mode: 'dark',
        toolbarBg: '#1a237e', toolbarColor: 'rgba(255,255,255,0.87)',
        strongBg: '#1a237e', accordionBg: '#3f51b5', accordionColor: '#fff',
        primary: '#7986cb', primaryText: 'rgba(0,0,0,0.87)',
        accent: '#2196f3', accentText: 'rgba(0,0,0,0.87)',
        warn: '#ff9800', warnText: 'rgba(0,0,0,0.87)',
        success: '#4caf50', successText: 'rgba(0,0,0,0.87)',
        bodyBg: '#283593', contentBg: '#303030', dialogBg: '#424242',
        borderColor: '#1a237e', hoverBg: 'rgba(255,255,255,0.1)', selectedBg: 'rgba(255,255,255,0.1)',
        linkColor: '#82b1ff',
        inputBorderColor: '#3f51b5', inputBg: 'rgba(64,64,64,1)', inputColor: 'white',
        treeBranchColor: '#bec2ff', commonWarnColor: '#03a9f4',
        treecontrolIconColor: 'rgba(255,255,255,0.7)',
        matSysPrimary: '#cfbcff',  // violet tone 80
    }),

    // Matrix — Layout=light-green, Main=light-green/lime/green
    matrix: buildTheme({
        mode: 'dark',
        toolbarBg: '#76ff03', toolbarColor: 'rgba(0,0,0,0.87)',
        strongBg: '#33691e', accordionBg: '#76ff03', accordionColor: 'rgba(0,0,0,0.87)',
        primary: '#76ff03', primaryText: 'rgba(0,0,0,0.87)',
        accent: '#c6ff00', accentText: 'rgba(0,0,0,0.87)',
        warn: '#00c853', warnText: 'rgba(0,0,0,0.87)',
        success: '#76ff03', successText: 'rgba(0,0,0,0.87)',
        bodyBg: '#1b5e20', contentBg: '#303030', dialogBg: '#424242',
        borderColor: '#76ff03', hoverBg: 'rgba(255,255,255,0.1)', selectedBg: 'rgba(255,255,255,0.1)',
        linkColor: '#82b1ff',
        inputBorderColor: '#76ff03', inputBg: 'rgba(64,64,64,1)', inputColor: 'white',
        treeBranchColor: '#76ff03', commonWarnColor: '#4caf50',  // override: #76ff03
        treecontrolIconColor: 'rgba(255,255,255,0.7)',
        matSysPrimary: '#a1d45b',  // chartreuse tone 80
    }),
}

// Dark theme keys for classification
export const DARK_THEMES = ['dark', 'darkNeu', 'darkoBluo', 'matrix']
export const LIGHT_THEMES = ['light', 'enterprise', 'redis']
export const ALL_THEME_KEYS = ['light', 'enterprise', 'dark', 'darkNeu', 'darkoBluo', 'matrix', 'redis']

export function isDarkTheme(key: string): boolean {
    return DARK_THEMES.includes(key)
}

// For backward compat
export const enterpriseTheme = themes.enterprise
export const darkTheme = themes.dark