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
|
|
@ -5,7 +5,7 @@
|
|||
// Common
|
||||
// ============================================================
|
||||
|
||||
export type ThemeMode = 'light' | 'dark' | 'auto'
|
||||
export type ThemeMode = 'light' | 'dark' | 'auto' | 'nord' | 'solarized' | 'catppuccin' | 'dracula'
|
||||
|
||||
export type VoiceMode = 'dictation' | 'hands-free'
|
||||
|
||||
|
|
@ -267,7 +267,7 @@ export interface LLMModel {
|
|||
modifiedAt: string
|
||||
}
|
||||
|
||||
export type LLMAction = 'refine' | 'translate' | 'summarize' | 'expand' | 'grammar' | 'custom'
|
||||
export type LLMAction = 'refine' | 'translate' | 'summarize' | 'expand' | 'grammar' | 'custom' | 'chain'
|
||||
|
||||
export interface LLMProcessParams {
|
||||
text: string
|
||||
|
|
@ -336,7 +336,7 @@ export interface SetEnabledParams {
|
|||
enabled: boolean
|
||||
}
|
||||
|
||||
export type HotkeyAction = 'dictation' | 'hands-free' | 'command'
|
||||
export type HotkeyAction = 'dictation' | 'hands-free' | 'command' | 'caption'
|
||||
|
||||
export interface HotkeyTriggeredEvent {
|
||||
action: HotkeyAction
|
||||
|
|
@ -370,6 +370,8 @@ export interface AppConfig {
|
|||
dictationShortcut: HotkeyBinding
|
||||
handsFreeShortcut: HotkeyBinding
|
||||
commandShortcut: HotkeyBinding
|
||||
/** 실시간 자막 토글 핫키 (Phase 10.1) */
|
||||
captionShortcut: HotkeyBinding
|
||||
hotkeyEnabled: boolean
|
||||
insertMethod: 'clipboard' | 'keyboard'
|
||||
autoInsert: boolean
|
||||
|
|
@ -380,6 +382,8 @@ export interface AppConfig {
|
|||
agentModeEnabled: boolean
|
||||
/** 핸즈프리 모드 활성화 (토글) */
|
||||
handsFreeEnabled: boolean
|
||||
/** 스크린 컨텍스트 캡처 활성화 (Phase 10.2) */
|
||||
screenContextEnabled: boolean
|
||||
}
|
||||
|
||||
export interface ConfigGetParams {
|
||||
|
|
@ -662,3 +666,284 @@ export interface WeeklyStats {
|
|||
wordCount: number
|
||||
sessionCount: number
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Phase 10: Memo Tags (10.3)
|
||||
// ============================================================
|
||||
|
||||
export interface MemoTag {
|
||||
id: string
|
||||
historyId: string
|
||||
tag: string
|
||||
createdAt: number
|
||||
}
|
||||
|
||||
export interface AddTagParams {
|
||||
historyId: string
|
||||
tag: string
|
||||
}
|
||||
|
||||
export interface RemoveTagParams {
|
||||
historyId: string
|
||||
tag: string
|
||||
}
|
||||
|
||||
export interface GetTagsParams {
|
||||
historyId: string
|
||||
}
|
||||
|
||||
export interface SearchByTagParams {
|
||||
tag: string
|
||||
page: number
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
export interface ExportMemoParams {
|
||||
format: 'markdown'
|
||||
tag?: string
|
||||
from?: string
|
||||
to?: string
|
||||
}
|
||||
|
||||
export interface TagCount {
|
||||
tag: string
|
||||
count: number
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Phase 10: Voice Commands (10.5)
|
||||
// ============================================================
|
||||
|
||||
export type KeywordMatchMode = 'prefix' | 'suffix' | 'contains'
|
||||
|
||||
export interface VoiceCommandKeyword {
|
||||
keyword: string
|
||||
matchMode: KeywordMatchMode
|
||||
}
|
||||
|
||||
export interface VoiceCommandRule {
|
||||
id: string
|
||||
instructionId: string
|
||||
keywords: VoiceCommandKeyword[]
|
||||
enabled: boolean
|
||||
priority: number
|
||||
}
|
||||
|
||||
export interface VoiceCommandMatch {
|
||||
matched: boolean
|
||||
ruleId: string | null
|
||||
instructionId: string | null
|
||||
/** 키워드 제거 후 남은 텍스트 */
|
||||
cleanedText: string
|
||||
/** 매칭된 키워드 */
|
||||
matchedKeyword: string | null
|
||||
}
|
||||
|
||||
export interface SetVoiceCommandKeywordsParams {
|
||||
instructionId: string
|
||||
keywords: VoiceCommandKeyword[]
|
||||
}
|
||||
|
||||
export interface SetVoiceCommandEnabledParams {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Phase 10: Screen Context (10.2)
|
||||
// ============================================================
|
||||
|
||||
export interface ScreenContext {
|
||||
appName: string | null
|
||||
windowTitle: string | null
|
||||
selectedText: string | null
|
||||
capturedAt: number
|
||||
}
|
||||
|
||||
export interface CaptureContextResult {
|
||||
context: ScreenContext
|
||||
/** 선택 텍스트 캡처 시도 여부 */
|
||||
selectedTextAttempted: boolean
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Phase 10: LLM Chain (10.4)
|
||||
// ============================================================
|
||||
|
||||
export interface ChainStep {
|
||||
instructionId: string
|
||||
/** 이전 단계 결과 사용 or 원본 텍스트 사용 */
|
||||
inputSource: 'previous' | 'original'
|
||||
}
|
||||
|
||||
export interface LLMChain {
|
||||
id: string
|
||||
name: string
|
||||
steps: ChainStep[]
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
|
||||
export interface CreateChainParams {
|
||||
name: string
|
||||
steps: ChainStep[]
|
||||
}
|
||||
|
||||
export interface UpdateChainParams {
|
||||
id: string
|
||||
name?: string
|
||||
steps?: ChainStep[]
|
||||
}
|
||||
|
||||
export interface DeleteChainParams {
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface ExecuteChainParams {
|
||||
chainId: string
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface ChainProgress {
|
||||
chainId: string
|
||||
currentStep: number
|
||||
totalSteps: number
|
||||
stepName: string
|
||||
intermediateText: string
|
||||
}
|
||||
|
||||
export interface ChainExecutionResult {
|
||||
chainId: string
|
||||
finalText: string
|
||||
steps: Array<{ instructionId: string; output: string; durationMs: number }>
|
||||
totalDurationMs: number
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Phase 10: Live Caption (10.1)
|
||||
// ============================================================
|
||||
|
||||
export type CaptionState = 'inactive' | 'starting' | 'active' | 'stopping'
|
||||
|
||||
export interface CaptionSegment {
|
||||
id: string
|
||||
text: string
|
||||
timestamp: number
|
||||
isFinal: boolean
|
||||
}
|
||||
|
||||
export type CaptionAudioSource = 'mic' | 'system' | 'both'
|
||||
|
||||
export interface CaptionConfig {
|
||||
fontSize: number
|
||||
opacity: number
|
||||
maxLines: number
|
||||
autoClearMs: number
|
||||
/** 오디오 소스: 마이크 / 시스템 오디오 / 둘 다 */
|
||||
audioSource: CaptionAudioSource
|
||||
}
|
||||
|
||||
export interface CaptionSessionSummary {
|
||||
sessionId: string
|
||||
segments: CaptionSegment[]
|
||||
startedAt: number
|
||||
endedAt: number
|
||||
totalDurationMs: number
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Phase 11: License & Monetization
|
||||
// ============================================================
|
||||
|
||||
/** 라이센스 티어 */
|
||||
export type LicenseTier = 'free' | 'pro' | 'pro_plus'
|
||||
|
||||
/** 기능 게이팅 대상 */
|
||||
export enum Feature {
|
||||
// 쿼터 제한 기능 (Free에서 횟수 제한)
|
||||
DICTATION = 'dictation',
|
||||
LLM_PROCESS = 'llm_process',
|
||||
|
||||
// Pro 이상
|
||||
HISTORY_UNLIMITED = 'history_unlimited',
|
||||
HISTORY_EXPORT = 'history_export',
|
||||
CUSTOM_INSTRUCTION_CREATE = 'custom_instruction_create',
|
||||
LIVE_CAPTION = 'live_caption',
|
||||
SCREEN_CONTEXT = 'screen_context',
|
||||
VOICE_MEMO = 'voice_memo',
|
||||
VOICE_COMMAND = 'voice_command',
|
||||
LLM_CHAIN = 'llm_chain',
|
||||
|
||||
// Pro+ 이상
|
||||
FILE_TRANSCRIPTION = 'file_transcription',
|
||||
VOICE_CONVERSATION = 'voice_conversation',
|
||||
DICTATION_TEMPLATE = 'dictation_template',
|
||||
MEETING_SUMMARY = 'meeting_summary',
|
||||
LOCAL_RAG = 'local_rag',
|
||||
OS_AUTOMATION = 'os_automation',
|
||||
}
|
||||
|
||||
/** 라이센스 정보 (electron-store에 저장) */
|
||||
export interface LicenseInfo {
|
||||
tier: LicenseTier
|
||||
licenseKey: string | null
|
||||
activatedAt: number | null
|
||||
machineId: string
|
||||
/** 마지막 온라인 검증 시각 */
|
||||
lastVerifiedAt: number | null
|
||||
/** 오프라인 유예 만료 (lastVerifiedAt + 30일) */
|
||||
offlineGraceUntil: number | null
|
||||
}
|
||||
|
||||
/** 일일 사용량 */
|
||||
export interface DailyUsage {
|
||||
date: string // 'YYYY-MM-DD'
|
||||
feature: string
|
||||
count: number
|
||||
}
|
||||
|
||||
/** 쿼터 정보 */
|
||||
export interface UsageQuota {
|
||||
feature: Feature
|
||||
used: number
|
||||
limit: number // -1 = 무제한
|
||||
remaining: number // -1 = 무제한
|
||||
resetAt: string // 다음 리셋 시각 (내일 00:00) ISO 8601
|
||||
}
|
||||
|
||||
/** 기능 접근 결과 */
|
||||
export interface FeatureAccess {
|
||||
allowed: boolean
|
||||
reason: 'ok' | 'quota_exceeded' | 'tier_required' | 'license_expired'
|
||||
requiredTier?: LicenseTier
|
||||
quota?: UsageQuota
|
||||
}
|
||||
|
||||
/** 업그레이드 유도 이벤트 */
|
||||
export interface UpgradePromptEvent {
|
||||
feature: Feature
|
||||
reason: 'quota_exceeded' | 'tier_required'
|
||||
currentTier: LicenseTier
|
||||
requiredTier: LicenseTier
|
||||
quota?: UsageQuota
|
||||
}
|
||||
|
||||
/** 라이센스 활성화 파라미터 */
|
||||
export interface ActivateLicenseParams {
|
||||
licenseKey: string
|
||||
}
|
||||
|
||||
/** 라이센스 활성화 결과 */
|
||||
export interface ActivateLicenseResult {
|
||||
success: boolean
|
||||
tier: LicenseTier
|
||||
message: string
|
||||
}
|
||||
|
||||
/** 티어별 기능 비교 항목 */
|
||||
export interface TierComparison {
|
||||
feature: Feature
|
||||
featureLabel: string
|
||||
free: boolean | string
|
||||
pro: boolean | string
|
||||
proPlus: boolean | string
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue