// src/renderer/theme.ts // 08-design-system.md SSOT 기반 MUI 테마. // CSS Custom Properties 기반: d3roPalette가 var()를 사용하여 테마 전환 시 자동 반응. import { createTheme, type Theme } from '@mui/material/styles' import type { ThemeMode } from '@d3ro/core/types' // ── 테마 키 타입 ──────────────────────────────────────────── type ThemeKey = 'dark' | 'light' | 'nord' | 'solarized' | 'catppuccin' | 'dracula' // ── 원시 색상값 구조 타입 ──────────────────────────────────── interface RawTheme { bg: { app: string card: string cardHover: string elevated: string input: string sidebar: string inset: string chassis: string crtBezel: string crtGlass: string } text: { primary: string secondary: string label: string disabled: string engraving: string inactive: string dimLabel: string muted: string hover: string } border: { subtle: string default: string strong: string } shadow: { card: string buttonBase: string chassis: string inset: string insetDeep: string buttonRaised: string buttonPressed: string screenGlow: string tooltip: string } accent: { main: string dim: string glow: string light: string dark: string crtPhosphor: string crtPhosphorDim: string } } // ── 원시 색상값 (raw values) ──────────────────────────────────── // CSS 변수에 주입되는 실제 색상값. 컴포넌트에서 직접 사용하지 않는다. const RAW: Record = { dark: { bg: { app: '#19191b', card: '#242427', cardHover: '#2a2a2d', elevated: '#2e2e32', input: '#1e1e21', sidebar: '#1e1f21', inset: '#1b1c1e', chassis: '#242528', crtBezel: '#1a1a1c', crtGlass: '#050605' }, text: { primary: '#ffffff', secondary: '#8e8e93', label: '#7c7c82', disabled: '#4a4a4e', engraving: '#1a1a1c', inactive: '#77797c', dimLabel: '#5c2615', muted: '#3a3b3f', hover: '#aaaaaa' }, border: { subtle: 'rgba(255,255,255,0.04)', default: 'rgba(255,255,255,0.08)', strong: 'rgba(255,255,255,0.12)' }, shadow: { card: '0 8px 30px rgba(0,0,0,0.3)', buttonBase: '0 2px 0 rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.06)', chassis: '0 60px 100px -20px rgba(0,0,0,0.8), 0 12px 0 #111111, 0 13px 4px rgba(0,0,0,0.5), inset 0 1px 1px rgba(255,255,255,0.15), inset 0 -1px 2px rgba(0,0,0,0.4)', inset: 'inset 0 2px 6px rgba(0,0,0,0.6), 0 1px 1px rgba(255,255,255,0.05)', insetDeep: 'inset 0 4px 12px rgba(0,0,0,0.9), inset 0 0 0 1px #000, 0 1px 1px rgba(255,255,255,0.1)', buttonRaised: '0 3px 6px rgba(0,0,0,0.4), inset 0 1px 1px rgba(255,255,255,0.1), inset 0 -1px 2px rgba(0,0,0,0.2)', buttonPressed: 'inset 0 2px 6px rgba(0,0,0,0.8), inset 0 0 0 1px #000', screenGlow: 'inset 0 0 20px rgba(0,0,0,0.8)', tooltip: '0 8px 24px rgba(0,0,0,0.4)', }, accent: { main: '#f25b29', dim: 'rgba(242,91,41,0.15)', glow: 'rgba(242,91,41,0.6)', light: '#ff7a4d', dark: '#c44a22', crtPhosphor: '#f25b29', crtPhosphorDim: '#c44a22' }, }, light: { bg: { app: '#f5f5f7', card: '#ffffff', cardHover: '#f7f7f9', elevated: '#f0f0f2', input: '#ffffff', sidebar: '#eeeef0', inset: '#e8e8ea', chassis: '#e2e2e5', crtBezel: '#d5d5d8', crtGlass: '#f0f0f2' }, text: { primary: '#1a1a1c', secondary: '#6e6e73', label: '#8e8e93', disabled: '#c7c7cc', engraving: '#d0d0d3', inactive: '#8e8e93', dimLabel: '#b07040', muted: '#b0b0b4', hover: '#555555' }, border: { subtle: 'rgba(0,0,0,0.06)', default: 'rgba(0,0,0,0.10)', strong: 'rgba(0,0,0,0.16)' }, shadow: { card: '0 4px 20px rgba(0,0,0,0.06)', buttonBase: '0 2px 0 rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.8)', chassis: '0 40px 80px -20px rgba(0,0,0,0.08), 0 12px 0 #d5d5d8, 0 13px 4px rgba(0,0,0,0.06), inset 0 1px 1px rgba(255,255,255,0.8), inset 0 -1px 2px rgba(0,0,0,0.04)', inset: 'inset 0 2px 6px rgba(0,0,0,0.08), 0 1px 1px rgba(255,255,255,0.6)', insetDeep: 'inset 0 4px 12px rgba(0,0,0,0.12), inset 0 0 0 1px rgba(0,0,0,0.06), 0 1px 1px rgba(255,255,255,0.8)', buttonRaised: '0 3px 6px rgba(0,0,0,0.08), inset 0 1px 1px rgba(255,255,255,0.8), inset 0 -1px 2px rgba(0,0,0,0.04)', buttonPressed: 'inset 0 2px 6px rgba(0,0,0,0.12), inset 0 0 0 1px rgba(0,0,0,0.06)', screenGlow: 'inset 0 0 20px rgba(0,0,0,0.06)', tooltip: '0 8px 24px rgba(0,0,0,0.08)', }, accent: { main: '#f25b29', dim: 'rgba(242,91,41,0.15)', glow: 'rgba(242,91,41,0.6)', light: '#ff7a4d', dark: '#c44a22', crtPhosphor: '#f25b29', crtPhosphorDim: '#c44a22' }, }, // ── Nord (https://www.nordtheme.com/) ────────────────────── nord: { bg: { app: '#2e3440', card: '#3b4252', cardHover: '#434c5e', elevated: '#3b4252', input: '#3b4252', sidebar: '#2e3440', inset: '#2e3440', chassis: '#3b4252', crtBezel: '#2e3440', crtGlass: '#242831' }, text: { primary: '#eceff4', secondary: '#d8dee9', label: '#adb5c7', disabled: '#4c566a', engraving: '#2e3440', inactive: '#7b88a1', dimLabel: '#5e81ac', muted: '#434c5e', hover: '#e5e9f0' }, border: { subtle: 'rgba(216,222,233,0.06)', default: 'rgba(216,222,233,0.10)', strong: 'rgba(216,222,233,0.16)' }, shadow: { card: '0 8px 30px rgba(0,0,0,0.35)', buttonBase: '0 2px 0 rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.06)', chassis: '0 60px 100px -20px rgba(0,0,0,0.8), 0 12px 0 #242831, 0 13px 4px rgba(0,0,0,0.5), inset 0 1px 1px rgba(255,255,255,0.1), inset 0 -1px 2px rgba(0,0,0,0.4)', inset: 'inset 0 2px 6px rgba(0,0,0,0.5), 0 1px 1px rgba(255,255,255,0.04)', insetDeep: 'inset 0 4px 12px rgba(0,0,0,0.8), inset 0 0 0 1px #242831, 0 1px 1px rgba(255,255,255,0.08)', buttonRaised: '0 3px 6px rgba(0,0,0,0.35), inset 0 1px 1px rgba(255,255,255,0.08), inset 0 -1px 2px rgba(0,0,0,0.2)', buttonPressed: 'inset 0 2px 6px rgba(0,0,0,0.7), inset 0 0 0 1px #242831', screenGlow: 'inset 0 0 20px rgba(0,0,0,0.6)', tooltip: '0 8px 24px rgba(0,0,0,0.4)', }, // Nord Frost: #88c0d0 (차가운 시안) accent: { main: '#88c0d0', dim: 'rgba(136,192,208,0.15)', glow: 'rgba(136,192,208,0.6)', light: '#a3d0de', dark: '#6aacbe', crtPhosphor: '#88c0d0', crtPhosphorDim: '#5e9daf' }, }, // ── Solarized Dark (https://ethanschoonover.com/solarized/) ─ solarized: { bg: { app: '#002b36', card: '#073642', cardHover: '#0d4250', elevated: '#073642', input: '#073642', sidebar: '#002b36', inset: '#001f28', chassis: '#073642', crtBezel: '#002b36', crtGlass: '#001f28' }, text: { primary: '#eee8d5', secondary: '#93a1a1', label: '#839496', disabled: '#586e75', engraving: '#002b36', inactive: '#657b83', dimLabel: '#7a6c2e', muted: '#073642', hover: '#b2c0bf' }, border: { subtle: 'rgba(131,148,150,0.08)', default: 'rgba(131,148,150,0.13)', strong: 'rgba(131,148,150,0.20)' }, shadow: { card: '0 8px 30px rgba(0,0,0,0.4)', buttonBase: '0 2px 0 rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.05)', chassis: '0 60px 100px -20px rgba(0,0,0,0.8), 0 12px 0 #001f28, 0 13px 4px rgba(0,0,0,0.5), inset 0 1px 1px rgba(255,255,255,0.08), inset 0 -1px 2px rgba(0,0,0,0.4)', inset: 'inset 0 2px 6px rgba(0,0,0,0.6), 0 1px 1px rgba(255,255,255,0.04)', insetDeep: 'inset 0 4px 12px rgba(0,0,0,0.9), inset 0 0 0 1px #001f28, 0 1px 1px rgba(255,255,255,0.06)', buttonRaised: '0 3px 6px rgba(0,0,0,0.4), inset 0 1px 1px rgba(255,255,255,0.06), inset 0 -1px 2px rgba(0,0,0,0.2)', buttonPressed: 'inset 0 2px 6px rgba(0,0,0,0.8), inset 0 0 0 1px #001f28', screenGlow: 'inset 0 0 20px rgba(0,0,0,0.7)', tooltip: '0 8px 24px rgba(0,0,0,0.4)', }, // Solarized yellow: #b58900 (따뜻한 골드) accent: { main: '#b58900', dim: 'rgba(181,137,0,0.15)', glow: 'rgba(181,137,0,0.6)', light: '#d4a017', dark: '#8a6800', crtPhosphor: '#b58900', crtPhosphorDim: '#8a6800' }, }, // ── Catppuccin Mocha (https://catppuccin.com/) ────────────── catppuccin: { bg: { app: '#1e1e2e', card: '#313244', cardHover: '#3a3a54', elevated: '#313244', input: '#181825', sidebar: '#181825', inset: '#11111b', chassis: '#313244', crtBezel: '#1e1e2e', crtGlass: '#11111b' }, text: { primary: '#cdd6f4', secondary: '#bac2de', label: '#a6adc8', disabled: '#585b70', engraving: '#11111b', inactive: '#7f849c', dimLabel: '#585b70', muted: '#313244', hover: '#e6e9f8' }, border: { subtle: 'rgba(205,214,244,0.04)', default: 'rgba(205,214,244,0.08)', strong: 'rgba(205,214,244,0.14)' }, shadow: { card: '0 8px 30px rgba(0,0,0,0.4)', buttonBase: '0 2px 0 rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.05)', chassis: '0 60px 100px -20px rgba(0,0,0,0.8), 0 12px 0 #11111b, 0 13px 4px rgba(0,0,0,0.5), inset 0 1px 1px rgba(255,255,255,0.08), inset 0 -1px 2px rgba(0,0,0,0.4)', inset: 'inset 0 2px 6px rgba(0,0,0,0.6), 0 1px 1px rgba(255,255,255,0.04)', insetDeep: 'inset 0 4px 12px rgba(0,0,0,0.9), inset 0 0 0 1px #11111b, 0 1px 1px rgba(255,255,255,0.06)', buttonRaised: '0 3px 6px rgba(0,0,0,0.4), inset 0 1px 1px rgba(255,255,255,0.06), inset 0 -1px 2px rgba(0,0,0,0.2)', buttonPressed: 'inset 0 2px 6px rgba(0,0,0,0.8), inset 0 0 0 1px #11111b', screenGlow: 'inset 0 0 20px rgba(0,0,0,0.7)', tooltip: '0 8px 24px rgba(0,0,0,0.4)', }, // Catppuccin Mauve: #cba6f7 (보라 파스텔) accent: { main: '#cba6f7', dim: 'rgba(203,166,247,0.15)', glow: 'rgba(203,166,247,0.6)', light: '#dfc0ff', dark: '#a67fd4', crtPhosphor: '#cba6f7', crtPhosphorDim: '#a67fd4' }, }, // ── Dracula (https://draculatheme.com/) ───────────────────── dracula: { bg: { app: '#282a36', card: '#44475a', cardHover: '#4f5266', elevated: '#383a4a', input: '#383a4a', sidebar: '#21222c', inset: '#21222c', chassis: '#44475a', crtBezel: '#282a36', crtGlass: '#1e2029' }, text: { primary: '#f8f8f2', secondary: '#a9b0d0', label: '#8891b5', disabled: '#6272a4', engraving: '#21222c', inactive: '#6272a4', dimLabel: '#6272a4', muted: '#44475a', hover: '#ffffff' }, border: { subtle: 'rgba(248,248,242,0.04)', default: 'rgba(248,248,242,0.08)', strong: 'rgba(248,248,242,0.14)' }, shadow: { card: '0 8px 30px rgba(0,0,0,0.4)', buttonBase: '0 2px 0 rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.06)', chassis: '0 60px 100px -20px rgba(0,0,0,0.8), 0 12px 0 #1e2029, 0 13px 4px rgba(0,0,0,0.5), inset 0 1px 1px rgba(255,255,255,0.1), inset 0 -1px 2px rgba(0,0,0,0.4)', inset: 'inset 0 2px 6px rgba(0,0,0,0.6), 0 1px 1px rgba(255,255,255,0.04)', insetDeep: 'inset 0 4px 12px rgba(0,0,0,0.9), inset 0 0 0 1px #1e2029, 0 1px 1px rgba(255,255,255,0.08)', buttonRaised: '0 3px 6px rgba(0,0,0,0.4), inset 0 1px 1px rgba(255,255,255,0.08), inset 0 -1px 2px rgba(0,0,0,0.2)', buttonPressed: 'inset 0 2px 6px rgba(0,0,0,0.8), inset 0 0 0 1px #1e2029', screenGlow: 'inset 0 0 20px rgba(0,0,0,0.7)', tooltip: '0 8px 24px rgba(0,0,0,0.4)', }, // Dracula Purple: #bd93f9 (시그니처 퍼플) accent: { main: '#bd93f9', dim: 'rgba(189,147,249,0.15)', glow: 'rgba(189,147,249,0.6)', light: '#d4b8ff', dark: '#9a70e0', crtPhosphor: '#bd93f9', crtPhosphorDim: '#9a70e0' }, }, } // ── SSOT: d3roPalette — CSS Custom Properties로 테마 반응형 ── // 컴포넌트에서 이 객체만 import하면 테마 자동 전환. export const d3roPalette = { bg: { app: 'var(--d3-bg-app)', card: 'var(--d3-bg-card)', cardHover: 'var(--d3-bg-cardHover)', elevated: 'var(--d3-bg-elevated)', input: 'var(--d3-bg-input)', sidebar: 'var(--d3-bg-sidebar)', inset: 'var(--d3-bg-inset)', chassis: 'var(--d3-bg-chassis)', crtBezel: 'var(--d3-bg-crtBezel)', crtGlass: 'var(--d3-bg-crtGlass)', }, accent: { amber: 'var(--d3-accent-main)', amberDim: 'var(--d3-accent-dim)', amberGlow: 'var(--d3-accent-glow)', }, tag: { purple: '#b854f5', purpleBg: 'rgba(184, 84, 245, 0.12)', orange: '#f59e0b', orangeBg: 'rgba(245, 158, 11, 0.12)', red: '#ef4444', redBg: 'rgba(239, 68, 68, 0.12)', green: '#22c55e', greenBg: 'rgba(34, 197, 94, 0.12)', greenGlow: 'rgba(34, 197, 94, 0.6)', redGlow: 'rgba(239, 68, 68, 0.6)', orangeGlow: 'rgba(245, 158, 11, 0.6)', }, text: { primary: 'var(--d3-text-primary)', secondary: 'var(--d3-text-secondary)', label: 'var(--d3-text-label)', disabled: 'var(--d3-text-disabled)', engraving: 'var(--d3-text-engraving)', inactive: 'var(--d3-text-inactive)', dimLabel: 'var(--d3-text-dimLabel)', muted: 'var(--d3-text-muted)', hover: 'var(--d3-text-hover)', }, border: { subtle: 'var(--d3-border-subtle)', default: 'var(--d3-border-default)', strong: 'var(--d3-border-strong)', }, crt: { phosphor: 'var(--d3-accent-crtPhosphor)', phosphorDim: 'var(--d3-accent-crtPhosphorDim)', scanline: 'rgba(0, 0, 0, 0.15)', bg: 'var(--d3-bg-chassis)', }, led: { off: '#111111', }, } as const export const d3roFontSans = [ '-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Roboto', '"Helvetica Neue"', 'Arial', 'sans-serif', ].join(',') export const d3roFontMono = [ 'ui-monospace', 'SFMono-Regular', '"SF Mono"', 'Menlo', 'Consolas', '"Liberation Mono"', 'monospace', ].join(',') // ── SSOT: 타이포그래피 토큰 ───────────────────────────── export const d3roTypo = { hero: { size: '42px', weight: 300, spacing: '-2px', line: 1 }, title: { size: '28px', weight: 300, spacing: '-1px', line: 1.2 }, value: { size: '20px', weight: 400, spacing: '0.02em', line: 1 }, heading: { size: '16px', weight: 600, spacing: '0.02em', line: 1.4 }, body: { size: '14px', weight: 400, spacing: '0.01em', line: 1.5 }, compact: { size: '13px', weight: 400, spacing: '0.01em', line: 1.5 }, small: { size: '12px', weight: 600, spacing: '0.03em', line: 1.4 }, meta: { size: '11px', weight: 600, spacing: '0.05em', line: 1.3 }, label: { size: '10px', weight: 700, spacing: '2px', line: 1.2 }, engrave: { size: '9px', weight: 700, spacing: '1.5px', line: 1 }, micro: { size: '8px', weight: 700, spacing: '1.5px', line: 1 }, nano: { size: '7px', weight: 700, spacing: '0.5px', line: 1 }, } as const // ── SSOT: 그림자 토큰 (CSS Custom Properties 기반, 테마 자동 전환) ─── export const d3roShadow = { chassis: 'var(--d3-shadow-chassis)', card: 'var(--d3-shadow-card)', inset: 'var(--d3-shadow-inset)', insetDeep: 'var(--d3-shadow-insetDeep)', buttonRaised: 'var(--d3-shadow-buttonRaised)', buttonPressed: 'var(--d3-shadow-buttonPressed)', buttonActive: 'var(--d3-shadow-buttonPressed)', dialog: 'var(--d3-shadow-card)', tooltip: 'var(--d3-shadow-tooltip)', screenGlow: 'var(--d3-shadow-screenGlow)', } as const // ── SSOT: 반경 토큰 ──────────────────────────────────── export const d3roRadius = { outer: '24px', card: '22px', inner: '12px', button: '8px', small: '6px', xs: '4px', pill: '999px', } as const // ── CSS Custom Properties 생성 ────────────────────────── function buildCssVars(key: ThemeKey): Record { const r = RAW[key] return { '--d3-bg-app': r.bg.app, '--d3-bg-card': r.bg.card, '--d3-bg-cardHover': r.bg.cardHover, '--d3-bg-elevated': r.bg.elevated, '--d3-bg-input': r.bg.input, '--d3-bg-sidebar': r.bg.sidebar, '--d3-bg-inset': r.bg.inset, '--d3-bg-chassis': r.bg.chassis, '--d3-bg-crtBezel': r.bg.crtBezel, '--d3-bg-crtGlass': r.bg.crtGlass, '--d3-text-primary': r.text.primary, '--d3-text-secondary': r.text.secondary, '--d3-text-label': r.text.label, '--d3-text-disabled': r.text.disabled, '--d3-text-engraving': r.text.engraving, '--d3-text-inactive': r.text.inactive, '--d3-text-dimLabel': r.text.dimLabel, '--d3-text-muted': r.text.muted, '--d3-text-hover': r.text.hover, '--d3-border-subtle': r.border.subtle, '--d3-border-default': r.border.default, '--d3-border-strong': r.border.strong, '--d3-shadow-card': r.shadow.card, '--d3-shadow-buttonBase': r.shadow.buttonBase, '--d3-shadow-chassis': r.shadow.chassis, '--d3-shadow-inset': r.shadow.inset, '--d3-shadow-insetDeep': r.shadow.insetDeep, '--d3-shadow-buttonRaised': r.shadow.buttonRaised, '--d3-shadow-buttonPressed': r.shadow.buttonPressed, '--d3-shadow-screenGlow': r.shadow.screenGlow, '--d3-shadow-tooltip': r.shadow.tooltip, '--d3-accent-main': r.accent.main, '--d3-accent-dim': r.accent.dim, '--d3-accent-glow': r.accent.glow, '--d3-accent-light': r.accent.light, '--d3-accent-dark': r.accent.dark, '--d3-accent-crtPhosphor': r.accent.crtPhosphor, '--d3-accent-crtPhosphorDim': r.accent.crtPhosphorDim, } } // ── 테마 팩토리 ─────────────────────────────────────────── function createD3ROTheme(key: ThemeKey): Theme { // MUI palette.mode는 'dark' | 'light'만 허용. light 외 모두 dark 기반. const muiMode: 'dark' | 'light' = key === 'light' ? 'light' : 'dark' const r = RAW[key] const cssVars = buildCssVars(key) // 동적 accent 색상 (CSS 변수가 아직 주입되기 전이므로 RAW 값 직접 사용) const accentMain = r.accent.main const accentDim = r.accent.dim const accentLight = r.accent.light const accentDark = r.accent.dark return createTheme({ palette: { mode: muiMode, primary: { main: accentMain, light: accentLight, dark: accentDark, contrastText: '#fff' }, secondary: { main: d3roPalette.tag.purple, light: '#d084ff', dark: '#8a3cc4' }, error: { main: d3roPalette.tag.red }, warning: { main: d3roPalette.tag.orange }, success: { main: d3roPalette.tag.green }, background: { default: r.bg.app, paper: r.bg.card }, text: { primary: r.text.primary, secondary: r.text.secondary, disabled: r.text.disabled }, divider: r.border.default, }, typography: { fontFamily: d3roFontSans, h4: { fontWeight: 700, fontSize: '22px', lineHeight: 1.3 }, h5: { fontWeight: 700, fontSize: '18px', lineHeight: 1.4 }, h6: { fontWeight: 600, fontSize: '14px', lineHeight: 1.5 }, subtitle1: { fontWeight: 500, fontSize: '18px', lineHeight: 1.4 }, body1: { fontSize: '14px', lineHeight: 1.5 }, body2: { fontSize: '12px', lineHeight: 1.4 }, button: { textTransform: 'none' as const, fontWeight: 600, fontSize: '14px' }, caption: { fontSize: '11px', fontWeight: 600, letterSpacing: '0.1em', textTransform: 'uppercase' as const, color: r.text.label }, overline: { fontSize: '11px', fontWeight: 600, letterSpacing: '0.1em', textTransform: 'uppercase' as const, lineHeight: 1.2 }, }, shape: { borderRadius: 22 }, components: { MuiCssBaseline: { styleOverrides: { ':root': cssVars, body: { backgroundColor: r.bg.app, color: r.text.primary }, }, }, MuiButton: { defaultProps: { disableElevation: true }, styleOverrides: { root: { textTransform: 'none', fontWeight: 600, borderRadius: 10, padding: '10px 20px', transition: 'transform 0.05s linear, box-shadow 0.05s linear', boxShadow: r.shadow.buttonBase, '&:active': { transform: 'translateY(2px)', boxShadow: 'var(--d3-shadow-buttonPressed)' }, }, containedPrimary: { color: '#fff', '&:hover': { backgroundColor: accentDark }, }, outlined: { borderColor: r.border.strong, color: r.text.primary, '&:hover': { borderColor: accentMain, color: accentMain }, }, containedSecondary: { backgroundColor: r.bg.elevated, color: r.text.primary, '&:hover': { backgroundColor: muiMode === 'dark' ? '#353539' : '#e5e5e7' }, }, }, }, MuiCard: { defaultProps: { elevation: 0 }, styleOverrides: { root: { backgroundColor: r.bg.card, borderRadius: 22, borderTop: `1px solid ${r.border.subtle}`, boxShadow: r.shadow.card, transition: 'background-color 0.2s ease', '&:hover': { backgroundColor: r.bg.cardHover }, }, }, }, MuiChip: { styleOverrides: { root: { borderRadius: 999, fontWeight: 700, fontSize: '11px', letterSpacing: '0.1em', textTransform: 'uppercase', height: 24 }, colorPrimary: { backgroundColor: accentDim, color: accentMain }, colorSecondary: { backgroundColor: d3roPalette.tag.purpleBg, color: d3roPalette.tag.purple }, colorSuccess: { backgroundColor: d3roPalette.tag.greenBg, color: d3roPalette.tag.green }, colorError: { backgroundColor: d3roPalette.tag.redBg, color: d3roPalette.tag.red }, colorWarning: { backgroundColor: d3roPalette.tag.orangeBg, color: d3roPalette.tag.orange }, }, }, MuiDrawer: { styleOverrides: { paper: { width: 240, backgroundColor: r.bg.app, borderRight: `1px solid ${r.border.subtle}` } } }, MuiListItemButton: { styleOverrides: { root: { borderRadius: 10, marginLeft: 8, marginRight: 8, '&.Mui-selected': { backgroundColor: accentDim, color: accentMain, fontWeight: 600, '&:hover': { backgroundColor: accentDim } }, }, }, }, MuiDialog: { styleOverrides: { paper: { backgroundColor: r.bg.card, borderRadius: 22, border: `1px solid ${r.border.subtle}`, boxShadow: 'var(--d3-shadow-card)', }, }, }, MuiTextField: { defaultProps: { size: 'small', variant: 'outlined' }, styleOverrides: { root: { '& .MuiOutlinedInput-root': { backgroundColor: r.bg.input, borderRadius: 10, color: r.text.primary, '& fieldset': { borderColor: r.border.default }, '&:hover fieldset': { borderColor: r.border.strong }, '&.Mui-focused fieldset': { borderColor: accentMain }, }, }, }, }, MuiSelect: { styleOverrides: { select: { color: r.text.primary }, icon: { color: r.text.secondary }, }, }, MuiInputLabel: { styleOverrides: { root: { color: r.text.secondary, '&.Mui-focused': { color: accentMain } }, }, }, MuiFormControlLabel: { styleOverrides: { label: { color: r.text.primary, fontSize: '14px' }, }, }, MuiSwitch: { styleOverrides: { switchBase: { '&.Mui-checked': { color: accentMain }, '&.Mui-checked + .MuiSwitch-track': { backgroundColor: accentMain }, }, track: { backgroundColor: r.text.disabled }, }, }, MuiMenuItem: { styleOverrides: { root: { color: r.text.primary, '&.Mui-selected': { backgroundColor: accentDim }, }, }, }, MuiTooltip: { defaultProps: { arrow: true }, styleOverrides: { tooltip: { backgroundColor: r.bg.elevated, color: r.text.primary, fontSize: '12px', borderRadius: 8, border: `1px solid ${r.border.subtle}` }, }, }, MuiTabs: { styleOverrides: { indicator: { backgroundColor: accentMain } } }, MuiTab: { styleOverrides: { root: { textTransform: 'none', fontWeight: 500, fontSize: '14px', '&.Mui-selected': { color: accentMain, fontWeight: 600 } } } }, }, }) } // ── Export ───────────────────────────────────────────────── export const themes: Record = { dark: createD3ROTheme('dark'), light: createD3ROTheme('light'), nord: createD3ROTheme('nord'), solarized: createD3ROTheme('solarized'), catppuccin: createD3ROTheme('catppuccin'), dracula: createD3ROTheme('dracula'), } // 하위 호환 export export const darkTheme = themes.dark export const lightTheme = themes.light export function getTheme(mode: ThemeMode, prefersDark = true): Theme { if (mode === 'auto') return prefersDark ? themes.dark : themes.light return themes[mode as ThemeKey] ?? themes.dark } export function getModePalette(mode: ThemeMode): RawTheme { return RAW[mode as ThemeKey] ?? RAW.dark }