feat(ui): Midnight Glass v2 전면 리디자인 — 기반+셸+대시보드
- 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)
This commit is contained in:
parent
f3ca0eafd4
commit
a8a1d6e546
25 changed files with 1638 additions and 960 deletions
97
apps/desktop/src/renderer/components/TitleBar.tsx
Normal file
97
apps/desktop/src/renderer/components/TitleBar.tsx
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// src/renderer/components/TitleBar.tsx
|
||||
// v2 보더리스 UI: 커스텀 타이틀바 — 투명 드래그 스트립 + 우측 윈도우 컨트롤.
|
||||
// 콘텐츠 위에 오버레이되며(레퍼런스처럼 컨트롤이 콘텐츠 위에 떠 있음),
|
||||
// 드래그 영역은 -webkit-app-region: drag, 버튼은 no-drag.
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { Box } from '@mui/material'
|
||||
import { Minus, Square, Copy, X } from 'lucide-react'
|
||||
import { d3roPalette } from '@d3ro/ui/theme'
|
||||
|
||||
const BAR_HEIGHT = 40
|
||||
|
||||
export function TitleBar(): React.ReactElement {
|
||||
const [isMaximized, setIsMaximized] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
window.electronAPI.window.isMaximized().then((max) => setIsMaximized(max))
|
||||
}, [])
|
||||
|
||||
const handleMaximize = useCallback(() => {
|
||||
window.electronAPI.window.maximize()
|
||||
// 토글 후 상태 재조회 (이벤트 채널이 없어 폴링 1회)
|
||||
setTimeout(() => {
|
||||
window.electronAPI.window.isMaximized().then((max) => setIsMaximized(max))
|
||||
}, 120)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: BAR_HEIGHT,
|
||||
zIndex: (theme) => theme.zIndex.drawer + 2,
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'flex-start',
|
||||
WebkitAppRegion: 'drag',
|
||||
pointerEvents: 'auto',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
WebkitAppRegion: 'no-drag',
|
||||
m: '6px 8px 0 0',
|
||||
gap: '2px',
|
||||
}}
|
||||
>
|
||||
<ControlButton onClick={() => window.electronAPI.window.minimize()}>
|
||||
<Minus size={14} />
|
||||
</ControlButton>
|
||||
<ControlButton onClick={handleMaximize}>
|
||||
{isMaximized ? <Copy size={12} style={{ transform: 'scaleX(-1)' }} /> : <Square size={11} />}
|
||||
</ControlButton>
|
||||
<ControlButton danger onClick={() => window.electronAPI.window.close()}>
|
||||
<X size={14} />
|
||||
</ControlButton>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function ControlButton({
|
||||
children,
|
||||
danger = false,
|
||||
onClick,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
danger?: boolean
|
||||
onClick: () => void
|
||||
}): React.ReactElement {
|
||||
return (
|
||||
<Box
|
||||
onClick={onClick}
|
||||
sx={{
|
||||
width: 34,
|
||||
height: 28,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer',
|
||||
color: d3roPalette.text.inactive,
|
||||
transition: 'background-color 0.12s ease, color 0.12s ease',
|
||||
'&:hover': {
|
||||
bgcolor: danger ? d3roPalette.tag.redBg : d3roPalette.glass.raised,
|
||||
color: danger ? d3roPalette.tag.red : d3roPalette.text.primary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue