초기 프로젝트 설정: 하네스 시스템 + 설계서 + RE 노하우
- CLAUDE.md: 프로젝트 규칙, 기술 스택, 코딩 규칙, 페이즈 로드맵 - .claude/settings.json: 권한, 강제 훅 (매 프롬프트 설계서 규칙 주입) - .claude/skills/: implement-phase, review-phase, scaffold, test-commit, debug - .claude/agents/: electron-architect, voice-pipeline-expert, ui-specialist - docs/design/00-09: 마스터 아키텍처, 서비스 명세(16개), IPC(113채널), DB스키마, UI컴포넌트, 검증리포트, 외부엔진연동, 갭분석, VoiceMode패턴, 디자인시스템, 히스토리팝업 - docs/phases/1-7+3.5: 전체 구현 페이즈 문서 - docs/re-findings/: Speakly RE 노하우 5개 문서
This commit is contained in:
commit
e24bb8378c
35 changed files with 11452 additions and 0 deletions
40
docs/re-findings/native-dll-patterns.md
Normal file
40
docs/re-findings/native-dll-patterns.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# NativeHelper.dll 디컴파일 결과 핵심
|
||||
|
||||
## Ghidra 분석: 2,692개 함수, 196,683줄 디컴파일 C코드
|
||||
전체 결과: C:/tmp/ghidra_decompile_output/_all_decompiled.c
|
||||
|
||||
## 37개 Action 디스패치 테이블
|
||||
ping, insertText, getCursorState, clipboardPaste, getActiveApp,
|
||||
isAppRunning, activateApp, getClipboardText, setClipboardText, clearClipboard,
|
||||
captureScreenshot, startKeyMonitoring, stopKeyMonitoring, startAppMonitoring,
|
||||
stopAppMonitoring, setKeyRecordingMode, setFnKeySuppression, startEditMonitor,
|
||||
stopEditMonitor, updateHotkeys, simulateKeyPress, simulateKeys,
|
||||
muteSystemAudio, unmuteSystemAudio, isSystemAudioMuted,
|
||||
showNotification, removeNotification, initializeNotifications, getNotificationPermission,
|
||||
checkAccessibilityPermission, requestAccessibilityPermission, enableSelfAccessibility,
|
||||
checkMicrophonePermission, requestMicrophonePermission,
|
||||
getMicrophoneDevices, startMicrophoneCapture, stopMicrophoneCapture,
|
||||
setTextOperationConfig, exploreAccessibilityTree
|
||||
|
||||
## 키보드 후킹 (D3RO-VOICE에서 uiohook-napi로 대체)
|
||||
- WH_KEYBOARD_LL 전역 후크
|
||||
- LLKHF_INJECTED 플래그로 자체 이벤트 바이패스
|
||||
- Win키 핫키 시 합성 keyup 주입 (시작 메뉴 방지)
|
||||
- 8초 stale key 정리
|
||||
|
||||
## WASAPI 캡처 (D3RO-VOICE에서 Web Audio API로 대체)
|
||||
- 이벤트 구동: WaitForMultipleObjects(captureEvent, exitEvent)
|
||||
- 리샘플링: 디바이스 포맷 → 24kHz mono
|
||||
- XAudioProcessor: Opus 인코딩 (로컬 앱에서는 불필요)
|
||||
|
||||
## 대체 매핑
|
||||
| Speakly (NativeHelper.dll) | D3RO-VOICE (npm 패키지) |
|
||||
|---------------------------|------------------------|
|
||||
| WASAPI 마이크 캡처 | Web Audio API / node-record-lpcm16 |
|
||||
| Opus 인코딩 | 불필요 (PCM 직접 전달) |
|
||||
| WH_KEYBOARD_LL | uiohook-napi |
|
||||
| SendInput (Ctrl+V) | @nut-tree/nut-js |
|
||||
| Win32 Clipboard API | electron clipboard API |
|
||||
| UI Automation | 초기 미구현, 추후 추가 |
|
||||
| IAudioEndpointVolume | loudness npm |
|
||||
| Toast Notification | electron Notification API |
|
||||
33
docs/re-findings/speakly-architecture.md
Normal file
33
docs/re-findings/speakly-architecture.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Speakly 아키텍처 분석 결과
|
||||
|
||||
## 전체 구조
|
||||
```
|
||||
Renderer (React+MUI) ↔ Preload (IPC Bridge) ↔ Main Process
|
||||
↓ koffi FFI
|
||||
NativeHelper.dll
|
||||
↓
|
||||
Genspark Cloud (WSS)
|
||||
```
|
||||
|
||||
## 서비스 목록 (25개)
|
||||
VoiceModeService(오케스트레이터), VoiceRecognitionService(WebSocket STT),
|
||||
AudioService, MicNativeService, NativeService(FFI), AuthService,
|
||||
ContextService, CustomInstructionService, HotkeyService, HotkeyConfig,
|
||||
HistoryService(SQLite), DictionaryService, TextOperationStrategy,
|
||||
UserConfigService, DeviceConfigService, UserInfoService, GensparkService,
|
||||
I18nService, FeedbackService, ReportService, UpdateService,
|
||||
AutoLaunchService, PermissionService, SoundEffectService, DebugProvider
|
||||
|
||||
## IPC 채널 수
|
||||
- ipcMain.handle: ~130개 (양방향)
|
||||
- ipcMain.on: ~30개 (단방향 renderer→main)
|
||||
- webContents.send: ~40개 (단방향 main→renderer)
|
||||
|
||||
## 초기화 순서 (22단계)
|
||||
1. Logger → 2. User-Agent/CSP → 3. Accessibility → 4. DeepLink →
|
||||
5. UserConfigService → 6. I18nService → 7. AuthService → 8. ReportService →
|
||||
9. UserInfoService → 10. DebugProvider → 11. TextOperationStrategy →
|
||||
12. RecordStatsService → 13. HistoryService → 14. SoundEffectService →
|
||||
15. CustomInstructionConfigService → 16. AutoLaunchService → 17. HotkeyConfig →
|
||||
18. createWindow → 19. HotkeyService 포워딩 → 20. Tray → 21. IPC 핸들러 →
|
||||
22. UpdateService (3초 지연)
|
||||
39
docs/re-findings/text-insertion-patterns.md
Normal file
39
docs/re-findings/text-insertion-patterns.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# 텍스트 삽입 핵심 패턴
|
||||
|
||||
## ClipboardPaste 의사코드 (NativeHelper.dll에서 추출)
|
||||
```pseudocode
|
||||
function clipboardPaste(text):
|
||||
// 1. 기존 클립보드 저장 (모든 포맷 열거)
|
||||
savedClipboard = saveClipboard() // EnumClipboardFormats → GetClipboardData
|
||||
|
||||
// 2. 클립보드에 텍스트 설정
|
||||
OpenClipboard(NULL)
|
||||
EmptyClipboard()
|
||||
hMem = GlobalAlloc(GMEM_MOVEABLE, size)
|
||||
memcpy(GlobalLock(hMem), text, size)
|
||||
SetClipboardData(CF_UNICODETEXT, hMem)
|
||||
CloseClipboard()
|
||||
|
||||
// 3. Ctrl+V 시뮬레이션
|
||||
SendInput([KeyDown(VK_CONTROL), KeyDown(VK_V), KeyUp(VK_V), KeyUp(VK_CONTROL)])
|
||||
Sleep(delay)
|
||||
|
||||
// 4. 클립보드 복원
|
||||
restoreClipboard(savedClipboard)
|
||||
```
|
||||
|
||||
## 삽입 전략 (TextOperationStrategy)
|
||||
- insertMethod: 'clipboard' (기본) | 'keyboard'
|
||||
- selectMethod: 'clipboard' | 'ax' | 'none'
|
||||
- verifyMode: 'auto' | 'skip'
|
||||
- 앱별 레벨(level0-3): bundleId 기반 오버라이드
|
||||
|
||||
## EditMonitor (삽입 검증)
|
||||
- 삽입 후 5초 대기 → 커서 주변 텍스트 재캡처
|
||||
- before_input / system_input / after_input 비교
|
||||
- 서버 corrections → 사전 자동 추가
|
||||
|
||||
## D3RO-VOICE 적용
|
||||
- @nut-tree/nut-js의 keyboard.type() 또는 clipboard + keyboard.pressKey(Key.LeftControl, Key.V)
|
||||
- electron clipboard API로 save/restore
|
||||
- 검증은 초기에는 skip, 추후 UI Automation 추가 가능
|
||||
55
docs/re-findings/ui-patterns.md
Normal file
55
docs/re-findings/ui-patterns.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# UI 구현 핵심 패턴
|
||||
|
||||
## 1. 메인 앱 = React + MUI, 팝업 = Vanilla JS
|
||||
- 메인 윈도우만 React 번들 (781KB)
|
||||
- 팝업들은 개별 HTML + 순수 JS (빠른 로딩)
|
||||
- 각 팝업: recording-tip, result-popup, info-tip, mic-tip, mode-tip, ask-window
|
||||
|
||||
## 2. MUI 테마 설정
|
||||
```javascript
|
||||
createTheme({
|
||||
palette: {
|
||||
mode: 'light' | 'dark',
|
||||
primary: { main: 'rgb(31, 93, 242)' },
|
||||
background: { default: '#F9F9F9'/'#121212', paper: '#FFFFFF'/'#1E1E1E' }
|
||||
},
|
||||
typography: { fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI"' },
|
||||
shape: { borderRadius: 12 },
|
||||
components: { MuiButton: { root: { textTransform: 'none', fontWeight: 500 } } }
|
||||
})
|
||||
```
|
||||
|
||||
## 3. RecordingTip 웨이브 바
|
||||
- 9개 바, 코사인 분포: `cos(n * PI / 2)` 가중치
|
||||
- 100ms 간격 setInterval
|
||||
- 오디오 레벨 → targetAmplitude, smoothing: `v += (S - v) * 0.5`
|
||||
- 최소 2px, 최대 28px + 랜덤 변동 ±35%
|
||||
|
||||
## 4. Thinking 프로그레스 바
|
||||
- 시간 기반: `min(95, (1 - 1/(1 + 1.5*t)) * 100)%` — 95%에 점근 수렴
|
||||
- requestAnimationFrame 루프
|
||||
|
||||
## 5. 2-Phase 윈도우 리사이즈 (깜빡임 방지)
|
||||
```
|
||||
prepare(state, params) → 숨겨진 span으로 폭 측정 → measured(width) IPC
|
||||
→ 메인 프로세스에서 윈도우 리사이즈
|
||||
→ show(state) → CSS 클래스 적용 → 보이기
|
||||
```
|
||||
|
||||
## 6. 앱 구조 (React 컴포넌트 트리)
|
||||
```
|
||||
App
|
||||
├── ThemeProvider (light/dark/auto, localStorage)
|
||||
├── Drawer (240px, permanent)
|
||||
│ ├── NavItems [Dashboard, Dictionary, CustomCommand]
|
||||
│ └── BottomBar [Account, Settings]
|
||||
└── Content Area
|
||||
├── Dashboard (통계 + 최근 세션)
|
||||
├── History (검색 + 재시도)
|
||||
├── Dictionary
|
||||
└── Settings (Modal)
|
||||
```
|
||||
|
||||
## 7. 라우팅
|
||||
- React Router 미사용, 순수 useState 기반
|
||||
- `currentRoute` state → switch 문으로 렌더링
|
||||
44
docs/re-findings/voice-pipeline-patterns.md
Normal file
44
docs/re-findings/voice-pipeline-patterns.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# 음성 파이프라인 핵심 패턴
|
||||
|
||||
## 1. 상태 머신 (RecognitionState)
|
||||
```
|
||||
IDLE → PREPARING → CONNECTING → READY → RECOGNIZING → COMPLETED/CANCELLED/ERROR/DESTROYED
|
||||
```
|
||||
- AudioState는 별도: IDLE → INITIALIZING → STREAMING → STOPPED
|
||||
- 모든 진입점에 `_isInTerminalState()` 가드
|
||||
- `errorEmitted` 플래그: error 이벤트 후 finish 중복 방지
|
||||
- `settled` boolean: Promise 이중 resolve/reject 방지
|
||||
|
||||
## 2. 이중 조건 플러시 (가장 영리한 패턴)
|
||||
```
|
||||
_connect() ← 비동기 (모델 로딩 대체)
|
||||
_captureContextInBackground() ← 비동기 (오디오 버퍼링 대체)
|
||||
|
||||
각 완료 시 → _tryFlushAll() 호출
|
||||
if (isReady AND isConnected):
|
||||
flushMessageQueue() // 설정 먼저
|
||||
flushAudioBuffer() // 오디오 후
|
||||
```
|
||||
- 오디오 손실 방지가 최우선
|
||||
|
||||
## 3. 재연결 3계층
|
||||
```
|
||||
_connect (고수준, 리포팅)
|
||||
→ _connectWithRetry (1초 간격, 60초 제한)
|
||||
→ _connectOnce (5초 하드 타임아웃, DNS/TCP/TLS 계측)
|
||||
```
|
||||
- 403 인증 실패: 즉시 포기
|
||||
- 오프라인: 즉시 포기
|
||||
- 기타: 1초 후 재시도
|
||||
|
||||
## 4. 타이밍 상수
|
||||
- 하트비트: 5초 ping, 15초 타임아웃 (3x 규칙)
|
||||
- 녹음 후 대기: 4초 (버퍼 있으면 6초)
|
||||
- 완료 아이들 타임아웃: 30초
|
||||
- 절대 최대 대기: 120초
|
||||
- 최소 오디오: 700ms (이하 취소)
|
||||
- 더블프레스: 300ms 이내
|
||||
|
||||
## 5. 오디오 포맷
|
||||
- Speakly: 24kHz mono, Opus 24kbps, 60ms 프레임
|
||||
- D3RO-VOICE: 16kHz mono, PCM16 (Whisper 기본, Opus 불필요)
|
||||
Loading…
Add table
Add a link
Reference in a new issue