From b820c789bbbcdd6a89f7ff50fbf6efaefd23d08b Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Wed, 22 Jul 2026 01:53:59 +0900 Subject: [PATCH] =?UTF-8?q?refactor(ui):=20=ED=8C=9D=EC=97=85=20=ED=85=8C?= =?UTF-8?q?=EB=A7=88=20SSOT=20=EC=A0=95=ED=95=A9=20+=20DS=20=EB=8D=B0?= =?UTF-8?q?=EB=93=9C=EC=BD=94=EB=93=9C=203=EC=A2=85=20=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=20(WS1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - theme-vars.ts dark/light POPUP_THEME_VARS를 theme.ts RAW와 값 정합: v1 잔존 주황(#f25b29) -> 파랑(dark #3b82f6 / light #2563eb), --d3-bg-card, --d3-text-dimLabel 동기화. 팝업=렌더러 색상 충돌 해소. - DS 미사용 3종 제거: MetalDial, ButtonGroup, CrtDisplay + barrel export (렌더러 전체 import 0건 재검증) - StatRing RING_COLORS hex 10종 -> d3roPalette.gradient.wave/tag 토큰 (다크 고정 -> 테마 전환 대응) SKIP: theme-vars 평행 SSOT 구조 단일화, theme.ts amber 별칭 제거 (web/admin/site가 accent.amber 사용 -> 전체 치환 별도 wave) 정책: docs/REFACTOR_POLICY.md DP1, DP9 --- packages/ui/src/components/ds/ButtonGroup.tsx | 31 ---- packages/ui/src/components/ds/CrtDisplay.tsx | 113 --------------- packages/ui/src/components/ds/MetalDial.tsx | 136 ------------------ packages/ui/src/components/ds/StatRing.tsx | 10 +- packages/ui/src/components/ds/index.ts | 3 - packages/ui/src/theme-vars.ts | 72 +++++----- 6 files changed, 41 insertions(+), 324 deletions(-) delete mode 100644 packages/ui/src/components/ds/ButtonGroup.tsx delete mode 100644 packages/ui/src/components/ds/CrtDisplay.tsx delete mode 100644 packages/ui/src/components/ds/MetalDial.tsx diff --git a/packages/ui/src/components/ds/ButtonGroup.tsx b/packages/ui/src/components/ds/ButtonGroup.tsx deleted file mode 100644 index 613bea7..0000000 --- a/packages/ui/src/components/ds/ButtonGroup.tsx +++ /dev/null @@ -1,31 +0,0 @@ -'use client' - -// src/renderer/components/ds/ButtonGroup.tsx -// v2 "Midnight Glass": 세그먼트 글래스 그룹 — 반투명 표면에 버튼 클러스터 - -import { Box } from '@mui/material' -import { d3roPalette, d3roRadius } from '../../theme' - -interface ButtonGroupProps { - children: React.ReactNode - /** 가로 배치 (기본 세로) */ - horizontal?: boolean -} - -export function ButtonGroup({ children, horizontal = false }: ButtonGroupProps): React.ReactElement { - return ( - - {children} - - ) -} diff --git a/packages/ui/src/components/ds/CrtDisplay.tsx b/packages/ui/src/components/ds/CrtDisplay.tsx deleted file mode 100644 index d453785..0000000 --- a/packages/ui/src/components/ds/CrtDisplay.tsx +++ /dev/null @@ -1,113 +0,0 @@ -'use client' - -// src/renderer/components/ds/CrtDisplay.tsx -// v2 "Midnight Glass": 히어로 디스플레이 — 딥 글래스 패널 + 그라디언트 스펙트럼 웨이브. -// (v1 WebGL 오실로스코프를 대체. props API는 v1과 호환 유지.) - -import { useState, useEffect } from 'react' -import { Box } from '@mui/material' -import { d3roPalette, d3roFontSans, d3roRadius } from '../../theme' -import { GradientWave } from './GradientWave' - -interface CrtDisplayProps { - /** 유휴 파형 진폭 (0.0 ~ 1.0) */ - amplitude?: number - /** 파형 속도 배율 (v1 주파수 개념 호환 — 8 기준 1배) */ - frequency?: number - /** 글리치 트리거 (변경 시 플래시 발생) */ - glitchTrigger?: number - /** 실시간 오디오 레벨 (0.0~1.0) — 파형 진폭에 반영 */ - audioLevel?: number - /** 오버레이 콘텐츠 */ - children?: React.ReactNode - /** 높이 (기본 280px) */ - height?: number | string -} - -export function CrtDisplay({ - amplitude = 0.25, - frequency = 8.0, - glitchTrigger = 0, - audioLevel = 0, - children, - height = 280, -}: CrtDisplayProps): React.ReactElement { - const [flash, setFlash] = useState(false) - - // v1 글리치 → v2 소프트 플래시 - useEffect(() => { - if (glitchTrigger > 0) { - setFlash(true) - const timer = setTimeout(() => setFlash(false), 220) - return () => clearTimeout(timer) - } - return undefined - }, [glitchTrigger]) - - return ( - - {/* 앰비언트 배경 글로우 */} - - - {/* 그라디언트 스펙트럼 웨이브 — 하단 영역 */} - - - - - {/* 상단 시인 */} - - - {/* 플래시 오버레이 (글리치 호환) */} - - - {/* 콘텐츠 오버레이 */} - - {children} - - - ) -} diff --git a/packages/ui/src/components/ds/MetalDial.tsx b/packages/ui/src/components/ds/MetalDial.tsx deleted file mode 100644 index 149d296..0000000 --- a/packages/ui/src/components/ds/MetalDial.tsx +++ /dev/null @@ -1,136 +0,0 @@ -'use client' - -// src/renderer/components/ds/MetalDial.tsx -// 시안 A: 메탈 다이얼 — 정밀기기 회전 노브, 동심원 그루브, 금속 광택, LED 인디케이터 -// 보강: 레퍼런스의 conic-gradient 정적 라이팅 + 방향성 그림자 추가 - -import { Box } from '@mui/material' -import { d3roPalette } from '../../theme' -import { PhosphorText } from './PhosphorText' - -interface MetalDialProps { - /** 0.0 ~ 1.0 값 (다이얼 위치) */ - value?: number - /** 라벨 텍스트 */ - label?: string - /** 크기 (px) */ - size?: number - /** LED 인디케이터 색상 */ - ledColor?: string -} - -export function MetalDial({ - value = 0, - label, - size = 120, - ledColor = d3roPalette.accent.amber, -}: MetalDialProps): React.ReactElement { - const rotation = value * 270 - 135 // -135° ~ +135° 범위 - const knobSize = size - 12 // 웰 패딩 - - return ( - - {/* 다이얼 웰 (inset well) */} - - {/* 노브 회전체 (동심원 그루브) */} - - {/* 포인터 인디케이터 점 */} - - - - {/* 정적 금속 광택 오버레이 (노브와 별개, 회전 안 함) */} - - - - {/* 라벨 */} - {label && ( - - {label} - - )} - - ) -} diff --git a/packages/ui/src/components/ds/StatRing.tsx b/packages/ui/src/components/ds/StatRing.tsx index c568f6c..53af693 100644 --- a/packages/ui/src/components/ds/StatRing.tsx +++ b/packages/ui/src/components/ds/StatRing.tsx @@ -9,11 +9,11 @@ import { d3roPalette } from '../../theme' type RingColor = 'blue' | 'purple' | 'green' | 'orange' | 'red' | 'accent' const RING_COLORS: Record = { - blue: { from: '#22d3ee', to: '#3b82f6', glow: 'rgba(59,130,246,0.35)', icon: d3roPalette.tag.blue }, - purple: { from: '#8b5cf6', to: '#d946ef', glow: 'rgba(167,139,250,0.35)', icon: d3roPalette.tag.purple }, - green: { from: '#34d399', to: '#10b981', glow: 'rgba(52,211,153,0.35)', icon: d3roPalette.tag.green }, - orange: { from: '#fbbf24', to: '#f97316', glow: 'rgba(251,146,60,0.35)', icon: d3roPalette.tag.orange }, - red: { from: '#f87171', to: '#ef4444', glow: 'rgba(248,113,113,0.35)', icon: d3roPalette.tag.red }, + blue: { from: 'var(--d3-gradient-wave1)', to: 'var(--d3-gradient-wave2)', glow: d3roPalette.tag.blueBg, icon: d3roPalette.tag.blue }, + purple: { from: 'var(--d3-gradient-wave3)', to: 'var(--d3-gradient-wave4)', glow: d3roPalette.tag.purpleBg, icon: d3roPalette.tag.purple }, + green: { from: d3roPalette.tag.green, to: d3roPalette.tag.green, glow: d3roPalette.tag.greenGlow, icon: d3roPalette.tag.green }, + orange: { from: d3roPalette.tag.orange, to: d3roPalette.tag.orange, glow: d3roPalette.tag.orangeGlow, icon: d3roPalette.tag.orange }, + red: { from: d3roPalette.tag.red, to: d3roPalette.tag.red, glow: d3roPalette.tag.redGlow, icon: d3roPalette.tag.red }, accent: { from: 'var(--d3-accent-light)', to: 'var(--d3-accent-dark)', glow: 'var(--d3-accent-dim)', icon: d3roPalette.accent.light }, } diff --git a/packages/ui/src/components/ds/index.ts b/packages/ui/src/components/ds/index.ts index b388f76..34b3c90 100644 --- a/packages/ui/src/components/ds/index.ts +++ b/packages/ui/src/components/ds/index.ts @@ -1,16 +1,13 @@ // src/renderer/components/ds/index.ts // 디자인 시스템 컴포넌트 SSOT barrel export -export { CrtDisplay } from './CrtDisplay' export { InstrumentPanel } from './InstrumentPanel' export { Led } from './Led' export { PhysicalButton } from './PhysicalButton' export { MetalCard } from './MetalCard' export { TiltCard } from './TiltCard' export { PhosphorText } from './PhosphorText' -export { MetalDial } from './MetalDial' export { ScreenPanel } from './ScreenPanel' -export { ButtonGroup } from './ButtonGroup' // v2 "Midnight Glass" 신규 export { GradientWave } from './GradientWave' export { StatRing } from './StatRing' diff --git a/packages/ui/src/theme-vars.ts b/packages/ui/src/theme-vars.ts index 4f88853..b17be9e 100644 --- a/packages/ui/src/theme-vars.ts +++ b/packages/ui/src/theme-vars.ts @@ -49,46 +49,46 @@ interface PopupThemeVars { const POPUP_THEME_VARS: Record = { dark: { - '--d3-bg-card': '#242427', - '--d3-bg-tip': 'rgba(0,0,0,0.85)', - '--d3-text-primary': 'rgba(255,255,255,0.87)', - '--d3-text-secondary': 'rgba(255,255,255,0.6)', - '--d3-text-inactive': 'rgba(255,255,255,0.3)', - '--d3-text-muted': 'rgba(255,255,255,0.25)', - '--d3-text-dimLabel': '#5c2615', - '--d3-border-default': 'rgba(255,255,255,0.08)', - '--d3-border-subtle': 'rgba(255,255,255,0.06)', - '--d3-border-strong': 'rgba(255,255,255,0.15)', - '--d3-accent-main': '#f25b29', - '--d3-accent-dim': 'rgba(242,91,41,0.08)', - '--d3-shadow-popup': '0 8px 32px rgba(0,0,0,0.5)', - '--d3-bg-result': '#1e1e1e', - '--d3-border-result': 'rgba(255,255,255,0.08)', - '--d3-text-result': 'rgba(255,255,255,0.87)', - '--d3-action-btn': 'rgba(255,255,255,0.4)', - '--d3-action-btn-hover-bg': 'rgba(255,255,255,0.08)', - '--d3-action-btn-hover': 'rgba(255,255,255,0.7)', + '--d3-bg-card': '#111a30', + '--d3-bg-tip': 'rgba(10,14,28,0.85)', + '--d3-text-primary': '#eef2fb', + '--d3-text-secondary': '#93a4c8', + '--d3-text-inactive': '#7385ab', + '--d3-text-muted': '#2b3550', + '--d3-text-dimLabel': '#4e5f85', + '--d3-border-default': 'rgba(148,180,255,0.12)', + '--d3-border-subtle': 'rgba(148,180,255,0.07)', + '--d3-border-strong': 'rgba(148,180,255,0.20)', + '--d3-accent-main': '#3b82f6', + '--d3-accent-dim': 'rgba(59,130,246,0.14)', + '--d3-shadow-popup': '0 8px 32px rgba(3,7,18,0.5)', + '--d3-bg-result': '#111a30', + '--d3-border-result': 'rgba(148,180,255,0.12)', + '--d3-text-result': '#eef2fb', + '--d3-action-btn': '#60a5fa', + '--d3-action-btn-hover-bg': 'rgba(59,130,246,0.14)', + '--d3-action-btn-hover': '#93a4c8', }, light: { '--d3-bg-card': '#ffffff', - '--d3-bg-tip': 'rgba(255,255,255,0.92)', - '--d3-text-primary': 'rgba(0,0,0,0.87)', - '--d3-text-secondary': 'rgba(0,0,0,0.6)', - '--d3-text-inactive': 'rgba(0,0,0,0.35)', - '--d3-text-muted': 'rgba(0,0,0,0.25)', - '--d3-text-dimLabel': '#b07040', - '--d3-border-default': 'rgba(0,0,0,0.10)', - '--d3-border-subtle': 'rgba(0,0,0,0.06)', - '--d3-border-strong': 'rgba(0,0,0,0.14)', - '--d3-accent-main': '#f25b29', - '--d3-accent-dim': 'rgba(242,91,41,0.08)', - '--d3-shadow-popup': '0 8px 32px rgba(0,0,0,0.12)', + '--d3-bg-tip': 'rgba(242,245,251,0.92)', + '--d3-text-primary': '#111827', + '--d3-text-secondary': '#4b5a75', + '--d3-text-inactive': '#8593ab', + '--d3-text-muted': '#c3cddd', + '--d3-text-dimLabel': '#5a6f96', + '--d3-border-default': 'rgba(30,58,138,0.12)', + '--d3-border-subtle': 'rgba(30,58,138,0.07)', + '--d3-border-strong': 'rgba(30,58,138,0.20)', + '--d3-accent-main': '#2563eb', + '--d3-accent-dim': 'rgba(37,99,235,0.10)', + '--d3-shadow-popup': '0 8px 32px rgba(30,58,138,0.12)', '--d3-bg-result': '#ffffff', - '--d3-border-result': 'rgba(0,0,0,0.08)', - '--d3-text-result': 'rgba(0,0,0,0.87)', - '--d3-action-btn': 'rgba(0,0,0,0.4)', - '--d3-action-btn-hover-bg': 'rgba(0,0,0,0.06)', - '--d3-action-btn-hover': 'rgba(0,0,0,0.7)', + '--d3-border-result': 'rgba(30,58,138,0.12)', + '--d3-text-result': '#111827', + '--d3-action-btn': '#3b82f6', + '--d3-action-btn-hover-bg': 'rgba(37,99,235,0.10)', + '--d3-action-btn-hover': '#4b5a75', }, nord: { '--d3-bg-card': '#3b4252',