- theme.ts 재작성: glass/gradient/glow 토큰군, 기본 dark=Midnight(블루), 6종 테마 변형 전부 파생 토큰 유지 (테마 시스템 보존) - DS 리스킨(API 호환): 글래스 카드/버튼, Pretendard 타이포, 소프트 LED - 신규: GradientWave(canvas 스펙트럼 음파), StatRing(컬러 링 타일) - 보더리스: frame:false + 커스텀 TitleBar, 네이티브 스크롤바 제거(폭 불변) + OverlayScrollbars 오버레이 스크롤 - AppLayout 와이드 사이드바(lucide, 그라디언트 액티브 필), 대시보드 재구축, StatusBar(브랜드/로컬시간), Pretendard 번들, i18n +20키(ko/en)
113 lines
3.1 KiB
TypeScript
113 lines
3.1 KiB
TypeScript
'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 (
|
|
<Box
|
|
sx={{
|
|
position: 'relative',
|
|
height,
|
|
bgcolor: d3roPalette.bg.crtGlass,
|
|
borderRadius: d3roRadius.inner,
|
|
border: `1px solid ${d3roPalette.glass.hairline}`,
|
|
overflow: 'hidden',
|
|
}}
|
|
>
|
|
{/* 앰비언트 배경 글로우 */}
|
|
<Box
|
|
sx={{
|
|
position: 'absolute',
|
|
inset: 0,
|
|
background: `radial-gradient(ellipse 70% 55% at 50% 100%, ${d3roPalette.accent.dim} 0%, transparent 70%)`,
|
|
pointerEvents: 'none',
|
|
}}
|
|
/>
|
|
|
|
{/* 그라디언트 스펙트럼 웨이브 — 하단 영역 */}
|
|
<Box sx={{ position: 'absolute', left: 12, right: 12, bottom: 8, height: '38%' }}>
|
|
<GradientWave audioLevel={audioLevel} idleAmplitude={amplitude} speed={frequency / 8} />
|
|
</Box>
|
|
|
|
{/* 상단 시인 */}
|
|
<Box
|
|
sx={{
|
|
position: 'absolute',
|
|
inset: 0,
|
|
background: d3roPalette.glass.sheen,
|
|
pointerEvents: 'none',
|
|
}}
|
|
/>
|
|
|
|
{/* 플래시 오버레이 (글리치 호환) */}
|
|
<Box
|
|
sx={{
|
|
position: 'absolute',
|
|
inset: 0,
|
|
bgcolor: d3roPalette.accent.dim,
|
|
opacity: flash ? 1 : 0,
|
|
transition: 'opacity 0.2s ease',
|
|
pointerEvents: 'none',
|
|
}}
|
|
/>
|
|
|
|
{/* 콘텐츠 오버레이 */}
|
|
<Box
|
|
sx={{
|
|
position: 'absolute',
|
|
inset: '16px',
|
|
zIndex: 2,
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
justifyContent: 'space-between',
|
|
color: d3roPalette.text.primary,
|
|
pointerEvents: 'none',
|
|
fontFamily: d3roFontSans,
|
|
}}
|
|
>
|
|
{children}
|
|
</Box>
|
|
</Box>
|
|
)
|
|
}
|