- press가 STT 초기화를 await하며 action queue 점유 → release 수십 초 지연·
유령 세션 반복 버그 수정 (initSTT fire-and-forget)
- 프리플라이트: STT 모델 미설치 시 즉시 에러 + 메인 UI 경고 + 온보딩 오픈
- 사이드카: 기동 중 프로세스 사망 시 30초 대기 없이 즉시 실패,
restartCount 리셋, error 리스너 부재 미처리 예외 방지
- 실시간 부분 전사: 1.5s 간격 interim → RecordingTip에 말하는 내용 미리보기
- Ollama 미가용 후처리 스킵 시 warning 배너, voice:error 브로드캐스트 신설
- 앱 아이콘: icon.svg 마스터(d3ro 브랜드 — 메탈 섀시+오렌지 웨이브) → png/ico
생성 스크립트(generate-icons.mjs, sharp) + electron-builder win/mac 연결
- 트레이: createEmpty() 빈 아이콘 → 실제 앱 아이콘 (getAppIconPath)
- 마이크 테스트: 초기 무음 level:0을 종료로 오인하던 조기 종료 버그 —
AUDIO.TEST_LEVEL에 done 플래그 신설(SSOT), STOP 버튼이 실제 캡처 중지,
testDevice 실패 시 상태 롤백
- LicenseTab/LicenseModal: LemonSqueezy 키 입력 삭제, Payple 구독 UI로 전환
- useLicenseState 훅: 라이선스+클라우드 인증 공용 상태 관리 추출
- PREMIUM_MODEL_LIMITS: 3곳 중복 → @d3ro/core/constants 단일 소스
- i18n: LemonSqueezy 전용 키 18개 삭제, 구독 관련 키 13개 추가 (12 locale)
- DashboardPage: dashboard.model* → license.model* 키 통일
listening 상태에서 풀 몰입 계측기 모드로 전환되는 VoiceRecordingPanel 추가.
recording-tip 팝업의 9바 cos-분포 waveform(BAR_COUNT=9, SMOOTHING=0.5,
RANDOM_FACTOR=0.35, 100ms)을 React로 포팅해 REC LED + elapsed 타이머 +
"SPEAK NOW" 힌트까지 구성. thinking/speaking 상태에서는 메시지 리스트로
복귀해 대화 맥락 유지 + 점 3개 typing indicator 버블 추가.
VoiceConversationService에 AudioCaptureService audio-level forwarding과
사운드 훅 4개(recording-start / recording-stop / chime / error)를 삽입.
chime은 recording-stop.wav 재사용(SoundEffectService SoundName 확장).
VOICE_CONVERSATION.AUDIO_LEVEL 채널 신설 + preload onAudioLevel API.
U8 Bug 13 동반 해소: finishListening에서 minBytes 미달 또는 VAD 무음 판정으로
빈 텍스트가 나오는 경우 조용히 listening으로 복귀하던 것을 _emitError('stt')로
사용자 피드백(에러 사운드 + 에러 이벤트)을 노출하도록 수정. 사용자가 "⏹ 눌러도
반응 없음"으로 오해하던 증상 해소.
Phase 3 재정의: Supabase = source of truth 방향 철회.
로컬 entry point 철학 하에 "로컬이 SoT, 클라우드는 로그인 시 mirror"로 확정.
CloudSyncService._initialSync():
- _onAuthenticated 끝에 fire-and-forget 호출
- pushAll → pullAll 순차 실행
- signin: 익명 로컬 데이터를 사용자 계정으로 업로드
- restore: 다른 기기 변경 반영
- 실패 시 warn만, 수동 Sync 버튼으로 재시도 가능
types.ts:
- FeatureAccess.reason: 'login_required' 추가
- UpgradePromptEvent.reason: 'login_required' 추가
LicenseService:
- consumeQuota(): login_required 케이스 — promptUpgrade + D3ROError(TierRequired)
- promptUpgrade(): login_required 시 requiredTier='free'
(로그인만 하면 free로 바로 사용 가능)
- quota_exceeded 시 현재 tier 상위로 승격 제안
UpgradePromptModal:
- isLoginRequired 분기 — title/desc 교체
- Primary button: "로그인하기" → d3ro:open-settings event (tab=cloud)
i18n ko/en:
- license.loginRequired.title/desc/signIn 추가
묶음 A — UI 정리 + i18n 완성:
[A1] desktop SettingsModal에 CloudSyncSection 통합 (탭 6번째)
- CloudIcon import, settings.tabs.cloud 키 추가
- 기존 about 탭은 index 5 -> 6
[A2] apps/web Sidebar 공유 layout 리팩터링
- app/(app)/layout.tsx 신규 (route group)
- dashboard/meetings/record/teams/billing을 (app)/ 아래로 git mv
- app/(app)/layout.tsx에 auth 가드 + Sidebar 통합
- 기존 개별 page.tsx에서 Sidebar/auth 중복 제거
- app/(app)/dashboard/layout.tsx 제거 (루트 layout이 처리)
[A3] 11개 locale에 V2 새 키 추가 (en/ja/zh/zh-TW/es/fr/de/pt/ru/vi/th)
- nav.meetings/record/teams/billing/logout
- login.subtitle/google/github/terms
- settings.tabs.cloud
묶음 B — V2-4b pull 동기화:
- CloudSyncService.pullAll() 신규
- history/dictionary 테이블 원격에서 fetch
- last_sync_at 이후 updated_at만 필터
- Last-Write-Wins 충돌 해결 (remote.updated_at > local.updated_at)
- 로컬에 없는 행은 INSERT, 있는 행은 UPDATE (구체적 컬럼 지정)
- IPC CLOUD_SYNC.PULL_ALL 채널 + handler + preload api
- CloudSyncSection에 Pull 버튼 추가 (Push 옆에 위치)
묶음 C — V2-8b Stripe webhook 서명 검증:
- stripe-webhook Edge Function에 Web Crypto API 기반 HMAC-SHA256 검증
- Stripe-Signature 헤더 파싱 (t=, v1= 엔트리)
- Replay 방지 (timestamp tolerance 300초)
- constantTimeEqual로 타이밍 공격 방지
- crypto.subtle.importKey/sign으로 HMAC 계산
- stripe-portal Edge Function 신규 (Customer Portal)
- JWT 인증 -> 기존 customer_id 조회 -> billing_portal/sessions 생성
- return_url 지원
- apps/web/components/billing/portal-button.tsx (구독 관리 버튼)
- billing 페이지에 Free 외 tier 사용자에게 PortalButton 표시
- config.toml에 stripe-portal 함수 등록 (verify_jwt=true)
묶음 D — V2-6b packages/ui-native:
- @d3ro/ui-native 신규 패키지 (React Native 전용 DS)
- theme.ts: d3roNativePalette/Typo/Radius (MUI 없는 정적 값)
- components/MetalCard.tsx: View + 섀시 섀도우
- components/PhosphorText.tsx: Text + 앰버 glow (textShadow)
- components/Led.tsx: View 원 + glow
- components/PhysicalButton.tsx: Pressable + 누름 느낌
- React/React-Native는 peerDependencies
- apps/mobile/package.json에 @d3ro/ui-native를 file: 의존성으로 추가
(mobile은 npm workspace 제외이므로 file path 필요)
검증:
- desktop typecheck OK
- web typecheck OK
- web next build OK (11 라우트, (app) 그룹 반영)
- 회귀 없음