Phase 10~11 전체 구현: 킬러 피처 5종 + 수익화 시스템
Phase 10 킬러 피처: - MemoService: 태그 CRUD + 마크다운 내보내기 (memo_tags DB) - VoiceCommandService: 키워드→명령어 매칭, 프리셋 4종 - ScreenContextService: PowerShell 활성 윈도우 + Ctrl+C 선택 텍스트 - ChainService: LLM 명령어 순차 실행 파이프라인 - CaptionService: 6초 청크 연속 전사 + 시스템 오디오 루프백 VoiceModeService 파이프라인 통합: - 녹음 시작 → 컨텍스트 캡처 → STT → 키워드 매칭 → LLM(체인/컨텍스트 주입) → 삽입 시스템 오디오 캡처: - setDisplayMediaRequestHandler + audio: 'loopback' (IPC 브릿지) - electron-audio-loopback 패키지 contextIsolation 호환 불가 → 직접 구현 Phase 11 수익화: - LicenseService: Free/Pro/Pro+ 3티어, LemonSqueezy API - Feature Gate: requireFeature/checkFeature/consumeFeature - 일일 쿼터: Free dictation 20/일, LLM 10/일 (SQLite daily_usage) - LicenseModal, ProBadge, UpgradePromptModal UI 디자인 보강: - d3roTypo(13종), d3roShadow(10종), d3roRadius(7종) 토큰 시스템 - ScreenPanel, ButtonGroup DS 컴포넌트 신규 - PhosphorText 4→13종 변형, MetalDial conic-gradient 광택 - 공유 컴포넌트: EmptyStateCard, SearchInput, PageHeader, HistoryEntryCard 기타: - 자막 핫키 SSOT 전체 연동 (Config→Hotkey→VoiceMode→Caption→Settings) - StatusBar 자막 LED + 효과음, 자막 로딩 UI - LLM 상태 이벤트 전파 수정 (폴링 제거 → onStatusChanged) - 커맨드 팝업 "선택 해제" 항목 추가
This commit is contained in:
parent
36d77ca224
commit
a31f96bbb8
97 changed files with 11853 additions and 1143 deletions
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
import { useRef, useEffect, useCallback } from 'react'
|
||||
import { Box } from '@mui/material'
|
||||
import { d3roPalette, d3roFontMono } from '../../theme'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import { d3roPalette, d3roFontMono, d3roShadow } from '../../theme'
|
||||
|
||||
// ── WebGL 유틸 ─────────────────────────────────────────
|
||||
|
||||
|
|
@ -112,6 +113,8 @@ interface CrtDisplayProps {
|
|||
frequency?: number
|
||||
/** 글리치 트리거 (변경 시 글리치 발생) */
|
||||
glitchTrigger?: number
|
||||
/** 실시간 오디오 레벨 (0.0~1.0) — 파형 진폭에 반영 */
|
||||
audioLevel?: number
|
||||
/** 오버레이 콘텐츠 (인광 텍스트 등) */
|
||||
children?: React.ReactNode
|
||||
/** 높이 (기본 280px) */
|
||||
|
|
@ -122,9 +125,12 @@ export function CrtDisplay({
|
|||
amplitude = 0.1,
|
||||
frequency = 8.0,
|
||||
glitchTrigger = 0,
|
||||
audioLevel = 0,
|
||||
children,
|
||||
height = 280,
|
||||
}: CrtDisplayProps): React.ReactElement {
|
||||
const theme = useTheme()
|
||||
const isLight = theme.palette.mode === 'light'
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
const glRef = useRef<{
|
||||
gl: WebGLRenderingContext
|
||||
|
|
@ -138,15 +144,20 @@ export function CrtDisplay({
|
|||
const glitchRef = useRef(0)
|
||||
const ampRef = useRef(amplitude)
|
||||
const freqRef = useRef(frequency)
|
||||
const audioLevelRef = useRef(audioLevel)
|
||||
const currentAmpRef = useRef(amplitude)
|
||||
const currentFreqRef = useRef(frequency)
|
||||
|
||||
// amplitude/frequency 변경 추적
|
||||
// amplitude/frequency/audioLevel 변경 추적
|
||||
useEffect(() => {
|
||||
ampRef.current = amplitude
|
||||
freqRef.current = frequency
|
||||
}, [amplitude, frequency])
|
||||
|
||||
useEffect(() => {
|
||||
audioLevelRef.current = audioLevel
|
||||
}, [audioLevel])
|
||||
|
||||
// 글리치 트리거
|
||||
useEffect(() => {
|
||||
if (glitchTrigger > 0) {
|
||||
|
|
@ -160,8 +171,11 @@ export function CrtDisplay({
|
|||
|
||||
const { gl, uTime, uGlitch, uAmp, uFreq } = ctx
|
||||
|
||||
// audioLevel → amplitude 반영: 기본 amplitude + 오디오 레벨로 증폭
|
||||
const targetAmp = ampRef.current + audioLevelRef.current * 0.35
|
||||
|
||||
// Smoothing
|
||||
currentAmpRef.current += (ampRef.current - currentAmpRef.current) * 0.1
|
||||
currentAmpRef.current += (targetAmp - currentAmpRef.current) * 0.15
|
||||
currentFreqRef.current += (freqRef.current - currentFreqRef.current) * 0.1
|
||||
glitchRef.current *= 0.85
|
||||
|
||||
|
|
@ -221,7 +235,7 @@ export function CrtDisplay({
|
|||
height,
|
||||
bgcolor: d3roPalette.bg.crtBezel,
|
||||
borderRadius: '8px',
|
||||
boxShadow: '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)',
|
||||
boxShadow: d3roShadow.insetDeep,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
|
|
@ -233,12 +247,19 @@ export function CrtDisplay({
|
|||
borderRadius: '6px',
|
||||
bgcolor: d3roPalette.bg.crtGlass,
|
||||
overflow: 'hidden',
|
||||
boxShadow: 'inset 0 0 20px rgba(0,0,0,0.8)',
|
||||
boxShadow: d3roShadow.screenGlow,
|
||||
}}
|
||||
>
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
// 라이트 모드에서 WebGL 셰이더(다크 전용)를 반전하여 밝은 배경에 어울리게 조정
|
||||
...(isLight && { filter: 'invert(0.88) hue-rotate(180deg)', opacity: 0.9 }),
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Glass reflection */}
|
||||
|
|
@ -246,7 +267,9 @@ export function CrtDisplay({
|
|||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0, left: 0, right: 0, bottom: '50%',
|
||||
background: 'linear-gradient(180deg, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0) 100%)',
|
||||
background: isLight
|
||||
? 'linear-gradient(180deg, rgba(255,255,255,0.30) 0%, rgba(255,255,255,0) 100%)'
|
||||
: 'linear-gradient(180deg, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0) 100%)',
|
||||
pointerEvents: 'none',
|
||||
zIndex: 10,
|
||||
}}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue