초기 프로젝트 설정: 하네스 시스템 + 설계서 + 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:
Yun Chan 2026-04-05 01:03:03 +09:00
commit e24bb8378c
35 changed files with 11452 additions and 0 deletions

View 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 |

View 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초 지연)

View 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 추가 가능

View 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 문으로 렌더링

View 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 불필요)