markupr 2.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/review-feedback.md +47 -0
- package/.eslintrc.json +35 -0
- package/.github/CODEOWNERS +16 -0
- package/.github/FUNDING.yml +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +56 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +54 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +89 -0
- package/.github/dependabot.yml +70 -0
- package/.github/workflows/ci.yml +184 -0
- package/.github/workflows/deploy-landing.yml +134 -0
- package/.github/workflows/nightly.yml +288 -0
- package/.github/workflows/release.yml +318 -0
- package/CHANGELOG.md +127 -0
- package/CLAUDE.md +137 -0
- package/CODE_OF_CONDUCT.md +9 -0
- package/CONTRIBUTING.md +390 -0
- package/LICENSE +21 -0
- package/PRODUCT_VISION.md +277 -0
- package/README.md +517 -0
- package/SECURITY.md +51 -0
- package/SIGNING_INSTRUCTIONS.md +284 -0
- package/assets/DMG_BACKGROUND_INSTRUCTIONS.md +130 -0
- package/assets/svg-source/dmg-background.svg +70 -0
- package/assets/svg-source/icon.svg +20 -0
- package/assets/svg-source/tray-icon-processing.svg +7 -0
- package/assets/svg-source/tray-icon-recording.svg +7 -0
- package/assets/svg-source/tray-icon.svg +6 -0
- package/assets/tray-complete.png +0 -0
- package/assets/tray-complete@2x.png +0 -0
- package/assets/tray-completeTemplate.png +0 -0
- package/assets/tray-completeTemplate@2x.png +0 -0
- package/assets/tray-error.png +0 -0
- package/assets/tray-error@2x.png +0 -0
- package/assets/tray-errorTemplate.png +0 -0
- package/assets/tray-errorTemplate@2x.png +0 -0
- package/assets/tray-icon-processing.png +0 -0
- package/assets/tray-icon-processing@2x.png +0 -0
- package/assets/tray-icon-processingTemplate.png +0 -0
- package/assets/tray-icon-processingTemplate@2x.png +0 -0
- package/assets/tray-icon-recording.png +0 -0
- package/assets/tray-icon-recording@2x.png +0 -0
- package/assets/tray-icon-recordingTemplate.png +0 -0
- package/assets/tray-icon-recordingTemplate@2x.png +0 -0
- package/assets/tray-icon.png +0 -0
- package/assets/tray-icon@2x.png +0 -0
- package/assets/tray-iconTemplate.png +0 -0
- package/assets/tray-iconTemplate@2x.png +0 -0
- package/assets/tray-idle.png +0 -0
- package/assets/tray-idle@2x.png +0 -0
- package/assets/tray-idleTemplate.png +0 -0
- package/assets/tray-idleTemplate@2x.png +0 -0
- package/assets/tray-processing-0.png +0 -0
- package/assets/tray-processing-0@2x.png +0 -0
- package/assets/tray-processing-0Template.png +0 -0
- package/assets/tray-processing-0Template@2x.png +0 -0
- package/assets/tray-processing-1.png +0 -0
- package/assets/tray-processing-1@2x.png +0 -0
- package/assets/tray-processing-1Template.png +0 -0
- package/assets/tray-processing-1Template@2x.png +0 -0
- package/assets/tray-processing-2.png +0 -0
- package/assets/tray-processing-2@2x.png +0 -0
- package/assets/tray-processing-2Template.png +0 -0
- package/assets/tray-processing-2Template@2x.png +0 -0
- package/assets/tray-processing-3.png +0 -0
- package/assets/tray-processing-3@2x.png +0 -0
- package/assets/tray-processing-3Template.png +0 -0
- package/assets/tray-processing-3Template@2x.png +0 -0
- package/assets/tray-processing.png +0 -0
- package/assets/tray-processing@2x.png +0 -0
- package/assets/tray-processingTemplate.png +0 -0
- package/assets/tray-processingTemplate@2x.png +0 -0
- package/assets/tray-recording.png +0 -0
- package/assets/tray-recording@2x.png +0 -0
- package/assets/tray-recordingTemplate.png +0 -0
- package/assets/tray-recordingTemplate@2x.png +0 -0
- package/build/DMG_BACKGROUND_SPEC.md +50 -0
- package/build/dmg-background.png +0 -0
- package/build/dmg-background@2x.png +0 -0
- package/build/entitlements.mac.inherit.plist +27 -0
- package/build/entitlements.mac.plist +41 -0
- package/build/favicon-16.png +0 -0
- package/build/favicon-180.png +0 -0
- package/build/favicon-192.png +0 -0
- package/build/favicon-32.png +0 -0
- package/build/favicon-48.png +0 -0
- package/build/favicon-512.png +0 -0
- package/build/favicon-64.png +0 -0
- package/build/icon-128.png +0 -0
- package/build/icon-16.png +0 -0
- package/build/icon-24.png +0 -0
- package/build/icon-256.png +0 -0
- package/build/icon-32.png +0 -0
- package/build/icon-48.png +0 -0
- package/build/icon-64.png +0 -0
- package/build/icon.icns +0 -0
- package/build/icon.ico +0 -0
- package/build/icon.iconset/icon_128x128.png +0 -0
- package/build/icon.iconset/icon_128x128@2x.png +0 -0
- package/build/icon.iconset/icon_16x16.png +0 -0
- package/build/icon.iconset/icon_16x16@2x.png +0 -0
- package/build/icon.iconset/icon_256x256.png +0 -0
- package/build/icon.iconset/icon_256x256@2x.png +0 -0
- package/build/icon.iconset/icon_32x32.png +0 -0
- package/build/icon.iconset/icon_32x32@2x.png +0 -0
- package/build/icon.iconset/icon_512x512.png +0 -0
- package/build/icon.iconset/icon_512x512@2x.png +0 -0
- package/build/icon.png +0 -0
- package/build/installer-header.bmp +0 -0
- package/build/installer-header.png +0 -0
- package/build/installer-sidebar.bmp +0 -0
- package/build/installer-sidebar.png +0 -0
- package/build/installer.nsh +45 -0
- package/build/overlay-processing.png +0 -0
- package/build/overlay-recording.png +0 -0
- package/build/toolbar-record.png +0 -0
- package/build/toolbar-screenshot.png +0 -0
- package/build/toolbar-settings.png +0 -0
- package/build/toolbar-stop.png +0 -0
- package/dist/main/index.mjs +12612 -0
- package/dist/preload/index.mjs +907 -0
- package/dist/renderer/assets/index-CCmUjl9K.js +19495 -0
- package/dist/renderer/assets/index-CUqz_Gs6.css +2270 -0
- package/dist/renderer/index.html +27 -0
- package/docs/AI_AGENT_QUICKSTART.md +42 -0
- package/docs/AI_PIPELINE_DESIGN.md +595 -0
- package/docs/API.md +514 -0
- package/docs/ARCHITECTURE.md +460 -0
- package/docs/CONFIGURATION.md +336 -0
- package/docs/DEVELOPMENT.md +508 -0
- package/docs/EXPORT_FORMATS.md +451 -0
- package/docs/GETTING_STARTED.md +236 -0
- package/docs/KEYBOARD_SHORTCUTS.md +334 -0
- package/docs/TROUBLESHOOTING.md +418 -0
- package/docs/landing/index.html +672 -0
- package/docs/landing/script.js +342 -0
- package/docs/landing/styles.css +1543 -0
- package/electron-builder.yml +140 -0
- package/electron.vite.config.ts +63 -0
- package/package.json +108 -0
- package/railway.json +12 -0
- package/scripts/build.mjs +51 -0
- package/scripts/generate-icons.mjs +314 -0
- package/scripts/generate-installer-images.cjs +253 -0
- package/scripts/generate-tray-icons.mjs +258 -0
- package/scripts/notarize.cjs +180 -0
- package/scripts/one-click-clean-test.sh +147 -0
- package/scripts/postinstall.mjs +36 -0
- package/scripts/setup-markupr.sh +55 -0
- package/setup +17 -0
- package/site/index.html +1835 -0
- package/site/package.json +11 -0
- package/site/railway.json +12 -0
- package/site/server.js +31 -0
- package/src/main/AutoUpdater.ts +392 -0
- package/src/main/CrashRecovery.ts +655 -0
- package/src/main/ErrorHandler.ts +703 -0
- package/src/main/HotkeyManager.ts +399 -0
- package/src/main/MenuManager.ts +529 -0
- package/src/main/PermissionManager.ts +420 -0
- package/src/main/SessionController.ts +1465 -0
- package/src/main/TrayManager.ts +540 -0
- package/src/main/ai/AIPipelineManager.ts +199 -0
- package/src/main/ai/ClaudeAnalyzer.ts +339 -0
- package/src/main/ai/ImageOptimizer.ts +176 -0
- package/src/main/ai/StructuredMarkdownBuilder.ts +379 -0
- package/src/main/ai/index.ts +16 -0
- package/src/main/ai/types.ts +258 -0
- package/src/main/analysis/ClarificationGenerator.ts +385 -0
- package/src/main/analysis/FeedbackAnalyzer.ts +531 -0
- package/src/main/analysis/index.ts +19 -0
- package/src/main/audio/AudioCapture.ts +978 -0
- package/src/main/audio/audioUtils.ts +100 -0
- package/src/main/audio/index.ts +20 -0
- package/src/main/capture/index.ts +1 -0
- package/src/main/index.ts +1693 -0
- package/src/main/ipc/captureHandlers.ts +272 -0
- package/src/main/ipc/index.ts +45 -0
- package/src/main/ipc/outputHandlers.ts +302 -0
- package/src/main/ipc/sessionHandlers.ts +56 -0
- package/src/main/ipc/settingsHandlers.ts +471 -0
- package/src/main/ipc/types.ts +56 -0
- package/src/main/ipc/windowHandlers.ts +277 -0
- package/src/main/output/ClipboardService.ts +369 -0
- package/src/main/output/ExportService.ts +539 -0
- package/src/main/output/FileManager.ts +416 -0
- package/src/main/output/MarkdownGenerator.ts +791 -0
- package/src/main/output/MarkdownPatcher.ts +299 -0
- package/src/main/output/index.ts +186 -0
- package/src/main/output/sessionAdapter.ts +207 -0
- package/src/main/output/templates/html-template.ts +553 -0
- package/src/main/pipeline/FrameExtractor.ts +330 -0
- package/src/main/pipeline/PostProcessor.ts +399 -0
- package/src/main/pipeline/TranscriptAnalyzer.ts +226 -0
- package/src/main/pipeline/index.ts +36 -0
- package/src/main/platform/WindowsTaskbar.ts +600 -0
- package/src/main/platform/index.ts +16 -0
- package/src/main/settings/SettingsManager.ts +730 -0
- package/src/main/settings/index.ts +19 -0
- package/src/main/transcription/ModelDownloadManager.ts +494 -0
- package/src/main/transcription/TierManager.ts +219 -0
- package/src/main/transcription/TranscriptionRecoveryService.ts +340 -0
- package/src/main/transcription/WhisperService.ts +748 -0
- package/src/main/transcription/index.ts +56 -0
- package/src/main/transcription/types.ts +135 -0
- package/src/main/windows/PopoverManager.ts +284 -0
- package/src/main/windows/TaskbarIntegration.ts +452 -0
- package/src/main/windows/index.ts +23 -0
- package/src/preload/index.ts +1047 -0
- package/src/renderer/App.tsx +515 -0
- package/src/renderer/AppWrapper.tsx +28 -0
- package/src/renderer/assets/logo-dark.svg +7 -0
- package/src/renderer/assets/logo.svg +7 -0
- package/src/renderer/audio/AudioCaptureRenderer.ts +454 -0
- package/src/renderer/capture/ScreenRecordingRenderer.ts +492 -0
- package/src/renderer/components/AnnotationOverlay.tsx +836 -0
- package/src/renderer/components/AudioWaveform.tsx +811 -0
- package/src/renderer/components/ClarificationQuestions.tsx +656 -0
- package/src/renderer/components/CountdownTimer.tsx +495 -0
- package/src/renderer/components/CrashRecoveryDialog.tsx +632 -0
- package/src/renderer/components/DonateButton.tsx +127 -0
- package/src/renderer/components/ErrorBoundary.tsx +308 -0
- package/src/renderer/components/ExportDialog.tsx +872 -0
- package/src/renderer/components/HotkeyHint.tsx +261 -0
- package/src/renderer/components/KeyboardShortcuts.tsx +787 -0
- package/src/renderer/components/ModelDownloadDialog.tsx +844 -0
- package/src/renderer/components/Onboarding.tsx +1830 -0
- package/src/renderer/components/ProcessingOverlay.tsx +157 -0
- package/src/renderer/components/RecordingOverlay.tsx +423 -0
- package/src/renderer/components/SessionHistory.tsx +1746 -0
- package/src/renderer/components/SessionReview.tsx +1321 -0
- package/src/renderer/components/SettingsPanel.tsx +217 -0
- package/src/renderer/components/Skeleton.tsx +347 -0
- package/src/renderer/components/StatusIndicator.tsx +86 -0
- package/src/renderer/components/ThemeProvider.tsx +429 -0
- package/src/renderer/components/Tooltip.tsx +370 -0
- package/src/renderer/components/TranscriptionPreview.tsx +183 -0
- package/src/renderer/components/TranscriptionTierSelector.tsx +640 -0
- package/src/renderer/components/UpdateNotification.tsx +377 -0
- package/src/renderer/components/WindowSelector.tsx +947 -0
- package/src/renderer/components/index.ts +99 -0
- package/src/renderer/components/primitives/ApiKeyInput.tsx +98 -0
- package/src/renderer/components/primitives/ColorPicker.tsx +65 -0
- package/src/renderer/components/primitives/DangerButton.tsx +45 -0
- package/src/renderer/components/primitives/DirectoryPicker.tsx +41 -0
- package/src/renderer/components/primitives/Dropdown.tsx +34 -0
- package/src/renderer/components/primitives/KeyRecorder.tsx +117 -0
- package/src/renderer/components/primitives/SettingsSection.tsx +32 -0
- package/src/renderer/components/primitives/Slider.tsx +43 -0
- package/src/renderer/components/primitives/Toggle.tsx +36 -0
- package/src/renderer/components/primitives/index.ts +10 -0
- package/src/renderer/components/settings/AdvancedTab.tsx +174 -0
- package/src/renderer/components/settings/AppearanceTab.tsx +77 -0
- package/src/renderer/components/settings/GeneralTab.tsx +40 -0
- package/src/renderer/components/settings/HotkeysTab.tsx +79 -0
- package/src/renderer/components/settings/RecordingTab.tsx +84 -0
- package/src/renderer/components/settings/index.ts +9 -0
- package/src/renderer/components/settings/settingsStyles.ts +673 -0
- package/src/renderer/components/settings/tabConfig.tsx +85 -0
- package/src/renderer/components/settings/useSettingsPanel.ts +447 -0
- package/src/renderer/contexts/ProcessingContext.tsx +227 -0
- package/src/renderer/contexts/RecordingContext.tsx +683 -0
- package/src/renderer/contexts/UIContext.tsx +326 -0
- package/src/renderer/contexts/index.ts +24 -0
- package/src/renderer/donateMessages.ts +69 -0
- package/src/renderer/hooks/index.ts +75 -0
- package/src/renderer/hooks/useAnimation.tsx +544 -0
- package/src/renderer/hooks/useTheme.ts +313 -0
- package/src/renderer/index.html +26 -0
- package/src/renderer/main.tsx +52 -0
- package/src/renderer/styles/animations.css +1093 -0
- package/src/renderer/styles/app-shell.css +662 -0
- package/src/renderer/styles/globals.css +515 -0
- package/src/renderer/styles/theme.ts +578 -0
- package/src/renderer/types/electron.d.ts +385 -0
- package/src/shared/hotkeys.ts +283 -0
- package/src/shared/types.ts +809 -0
- package/tests/clipboard.test.ts +228 -0
- package/tests/e2e/criticalPaths.test.ts +594 -0
- package/tests/feedbackAnalyzer.test.ts +303 -0
- package/tests/integration/sessionFlow.test.ts +583 -0
- package/tests/markdownGenerator.test.ts +418 -0
- package/tests/output.test.ts +96 -0
- package/tests/setup.ts +486 -0
- package/tests/unit/appIntegration.test.ts +676 -0
- package/tests/unit/appViewState.test.ts +281 -0
- package/tests/unit/audioIpcChannels.test.ts +17 -0
- package/tests/unit/exportService.test.ts +492 -0
- package/tests/unit/hotkeys.test.ts +92 -0
- package/tests/unit/navigationPreload.test.ts +94 -0
- package/tests/unit/onboardingFlow.test.ts +345 -0
- package/tests/unit/permissionManager.test.ts +175 -0
- package/tests/unit/permissionManagerExpanded.test.ts +296 -0
- package/tests/unit/screenRecordingRenderer.test.ts +368 -0
- package/tests/unit/sessionController.test.ts +515 -0
- package/tests/unit/tierManager.test.ts +61 -0
- package/tests/unit/tierManagerExpanded.test.ts +142 -0
- package/tests/unit/transcriptAnalyzer.test.ts +64 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +46 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcription Module
|
|
3
|
+
*
|
|
4
|
+
* Transcription Tiers (post-process architecture):
|
|
5
|
+
* - Tier 1: Local Whisper (default, post-session batch)
|
|
6
|
+
* - Tier 2: Timer-only (fallback, no transcription)
|
|
7
|
+
*
|
|
8
|
+
* The TierManager tracks tier availability for the UI.
|
|
9
|
+
* App works WITHOUT any API keys using local Whisper.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Primary API - TierManager (use this for transcription)
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
export { TierManager, tierManager } from './TierManager';
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Supporting Services
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Whisper (Tier 1)
|
|
22
|
+
export { WhisperService, whisperService } from './WhisperService';
|
|
23
|
+
|
|
24
|
+
// Model Management
|
|
25
|
+
export { ModelDownloadManager, modelDownloadManager } from './ModelDownloadManager';
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Types
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
export type {
|
|
32
|
+
// Tier types
|
|
33
|
+
TranscriptionTier,
|
|
34
|
+
WhisperModel,
|
|
35
|
+
TierStatus,
|
|
36
|
+
TierQuality,
|
|
37
|
+
// Event types
|
|
38
|
+
TranscriptEvent,
|
|
39
|
+
PauseEvent,
|
|
40
|
+
WhisperTranscriptResult,
|
|
41
|
+
WhisperConfig,
|
|
42
|
+
// Model types
|
|
43
|
+
ModelInfo,
|
|
44
|
+
DownloadProgress,
|
|
45
|
+
DownloadResult,
|
|
46
|
+
// Callbacks
|
|
47
|
+
TranscriptCallback,
|
|
48
|
+
PauseCallback,
|
|
49
|
+
TierChangeCallback,
|
|
50
|
+
ErrorCallback,
|
|
51
|
+
ProgressCallback,
|
|
52
|
+
CompleteCallback,
|
|
53
|
+
} from './types';
|
|
54
|
+
|
|
55
|
+
// Re-export types from shared for convenience
|
|
56
|
+
export type { TranscriptionSegment } from '../../shared/types';
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Types for Transcription Services
|
|
3
|
+
*
|
|
4
|
+
* Available tiers (post-process architecture):
|
|
5
|
+
* - Tier 1: Local Whisper (post-session batch transcription)
|
|
6
|
+
* - Tier 2: Timer-only (fallback, no transcription)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Transcription Tier Types
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Available transcription tiers in priority order
|
|
15
|
+
*/
|
|
16
|
+
export type TranscriptionTier = 'whisper' | 'timer-only';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Whisper model sizes available for download
|
|
20
|
+
*/
|
|
21
|
+
export type WhisperModel = 'tiny' | 'base' | 'small' | 'medium' | 'large';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Status of a transcription tier
|
|
25
|
+
*/
|
|
26
|
+
export interface TierStatus {
|
|
27
|
+
tier: TranscriptionTier;
|
|
28
|
+
available: boolean;
|
|
29
|
+
reason?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Quality information for a tier
|
|
34
|
+
*/
|
|
35
|
+
export interface TierQuality {
|
|
36
|
+
accuracy: string;
|
|
37
|
+
latency: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Transcript Event Types
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Unified transcript event from any tier
|
|
46
|
+
*/
|
|
47
|
+
export interface TranscriptEvent {
|
|
48
|
+
text: string;
|
|
49
|
+
isFinal: boolean;
|
|
50
|
+
confidence: number;
|
|
51
|
+
timestamp: number;
|
|
52
|
+
tier: TranscriptionTier;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Pause detection event (triggers screenshots)
|
|
57
|
+
*/
|
|
58
|
+
export interface PauseEvent {
|
|
59
|
+
timestamp: number;
|
|
60
|
+
tier: TranscriptionTier;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Whisper-Specific Types
|
|
65
|
+
// ============================================================================
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Result from Whisper transcription
|
|
69
|
+
*/
|
|
70
|
+
export interface WhisperTranscriptResult {
|
|
71
|
+
text: string;
|
|
72
|
+
startTime: number;
|
|
73
|
+
endTime: number;
|
|
74
|
+
confidence: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Whisper service configuration
|
|
79
|
+
*/
|
|
80
|
+
export interface WhisperConfig {
|
|
81
|
+
modelPath: string;
|
|
82
|
+
language: string;
|
|
83
|
+
threads: number;
|
|
84
|
+
translateToEnglish: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// Model Download Types
|
|
89
|
+
// ============================================================================
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Information about a Whisper model
|
|
93
|
+
*/
|
|
94
|
+
export interface ModelInfo {
|
|
95
|
+
name: WhisperModel;
|
|
96
|
+
filename: string;
|
|
97
|
+
sizeBytes: number;
|
|
98
|
+
sizeMB: number;
|
|
99
|
+
ramRequired: string;
|
|
100
|
+
quality: string;
|
|
101
|
+
url: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Progress information during model download
|
|
106
|
+
*/
|
|
107
|
+
export interface DownloadProgress {
|
|
108
|
+
model: WhisperModel;
|
|
109
|
+
downloadedBytes: number;
|
|
110
|
+
totalBytes: number;
|
|
111
|
+
percent: number;
|
|
112
|
+
speedBps: number;
|
|
113
|
+
estimatedSecondsRemaining: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Result of a model download operation
|
|
118
|
+
*/
|
|
119
|
+
export interface DownloadResult {
|
|
120
|
+
success: boolean;
|
|
121
|
+
model: WhisperModel;
|
|
122
|
+
path: string;
|
|
123
|
+
error?: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ============================================================================
|
|
127
|
+
// Callback Types
|
|
128
|
+
// ============================================================================
|
|
129
|
+
|
|
130
|
+
export type TranscriptCallback = (event: TranscriptEvent) => void;
|
|
131
|
+
export type PauseCallback = (event: PauseEvent) => void;
|
|
132
|
+
export type TierChangeCallback = (oldTier: TranscriptionTier, newTier: TranscriptionTier, reason: string) => void;
|
|
133
|
+
export type ErrorCallback = (error: Error, tier?: TranscriptionTier) => void;
|
|
134
|
+
export type ProgressCallback = (progress: DownloadProgress) => void;
|
|
135
|
+
export type CompleteCallback = (result: DownloadResult) => void;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PopoverManager - NSPopover-like window for menu bar apps
|
|
3
|
+
*
|
|
4
|
+
* Creates a frameless window that:
|
|
5
|
+
* - Appears anchored below the tray icon
|
|
6
|
+
* - Closes when clicking outside (blur)
|
|
7
|
+
* - Has no shadow (draws its own)
|
|
8
|
+
* - Uses vibrancy on macOS
|
|
9
|
+
* - Supports different sizes per state
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { BrowserWindow, screen, Tray, app } from 'electron';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Popover sizes for different application states
|
|
17
|
+
*/
|
|
18
|
+
export const POPOVER_SIZES = {
|
|
19
|
+
idle: { width: 460, height: 680 },
|
|
20
|
+
recording: { width: 316, height: 90 },
|
|
21
|
+
processing: { width: 320, height: 140 },
|
|
22
|
+
complete: { width: 460, height: 720 },
|
|
23
|
+
settings: { width: 400, height: 520 },
|
|
24
|
+
error: { width: 440, height: 620 },
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
export type PopoverState = keyof typeof POPOVER_SIZES;
|
|
28
|
+
|
|
29
|
+
export interface PopoverConfig {
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
tray: Tray;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* PopoverManager class - Manages the menu bar popover window
|
|
37
|
+
*/
|
|
38
|
+
export class PopoverManager {
|
|
39
|
+
private window: BrowserWindow | null = null;
|
|
40
|
+
private tray: Tray | null = null;
|
|
41
|
+
private config: PopoverConfig;
|
|
42
|
+
private keepVisibleOnBlur = false;
|
|
43
|
+
private currentState: PopoverState = 'idle';
|
|
44
|
+
|
|
45
|
+
constructor(config: PopoverConfig) {
|
|
46
|
+
this.config = config;
|
|
47
|
+
this.tray = config.tray;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create the popover window with proper configuration
|
|
52
|
+
*/
|
|
53
|
+
create(): BrowserWindow {
|
|
54
|
+
const preloadPath = join(app.getAppPath(), 'dist', 'preload', 'index.mjs');
|
|
55
|
+
|
|
56
|
+
this.window = new BrowserWindow({
|
|
57
|
+
width: this.config.width,
|
|
58
|
+
height: this.config.height,
|
|
59
|
+
show: false,
|
|
60
|
+
frame: false,
|
|
61
|
+
fullscreenable: false,
|
|
62
|
+
resizable: false,
|
|
63
|
+
movable: false,
|
|
64
|
+
minimizable: false,
|
|
65
|
+
maximizable: false,
|
|
66
|
+
closable: true,
|
|
67
|
+
alwaysOnTop: true,
|
|
68
|
+
skipTaskbar: true,
|
|
69
|
+
|
|
70
|
+
// macOS-specific vibrancy for native feel
|
|
71
|
+
...(process.platform === 'darwin' && {
|
|
72
|
+
vibrancy: 'popover',
|
|
73
|
+
visualEffectState: 'active',
|
|
74
|
+
transparent: true,
|
|
75
|
+
backgroundColor: '#00000000',
|
|
76
|
+
}),
|
|
77
|
+
|
|
78
|
+
// Windows/Linux fallback with transparency
|
|
79
|
+
...(process.platform !== 'darwin' && {
|
|
80
|
+
transparent: true,
|
|
81
|
+
backgroundColor: '#00000000',
|
|
82
|
+
}),
|
|
83
|
+
|
|
84
|
+
webPreferences: {
|
|
85
|
+
preload: preloadPath,
|
|
86
|
+
nodeIntegration: false,
|
|
87
|
+
contextIsolation: true,
|
|
88
|
+
sandbox: false,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Hide on blur (clicking outside the popover)
|
|
93
|
+
this.window.on('blur', () => {
|
|
94
|
+
if (this.keepVisibleOnBlur) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this.hide();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Prevent window from showing in Mission Control on macOS
|
|
101
|
+
if (process.platform === 'darwin') {
|
|
102
|
+
this.window.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log('[PopoverManager] Popover window created');
|
|
106
|
+
|
|
107
|
+
return this.window;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Show the popover anchored to the tray icon
|
|
112
|
+
*/
|
|
113
|
+
show(): void {
|
|
114
|
+
if (!this.window || !this.tray) return;
|
|
115
|
+
|
|
116
|
+
const position = this.calculatePosition();
|
|
117
|
+
this.window.setPosition(position.x, position.y, false);
|
|
118
|
+
this.window.show();
|
|
119
|
+
this.window.focus();
|
|
120
|
+
|
|
121
|
+
console.log('[PopoverManager] Popover shown at', position);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Hide the popover
|
|
126
|
+
*/
|
|
127
|
+
hide(): void {
|
|
128
|
+
if (!this.window) return;
|
|
129
|
+
this.window.hide();
|
|
130
|
+
console.log('[PopoverManager] Popover hidden');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Toggle popover visibility
|
|
135
|
+
*/
|
|
136
|
+
toggle(): void {
|
|
137
|
+
if (!this.window) return;
|
|
138
|
+
|
|
139
|
+
if (this.window.isVisible()) {
|
|
140
|
+
this.hide();
|
|
141
|
+
} else {
|
|
142
|
+
this.show();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Calculate position to anchor below tray icon
|
|
148
|
+
* Handles multi-monitor setups and taskbar positions
|
|
149
|
+
*/
|
|
150
|
+
private calculatePosition(): { x: number; y: number } {
|
|
151
|
+
if (!this.tray || !this.window) {
|
|
152
|
+
return { x: 0, y: 0 };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const trayBounds = this.tray.getBounds();
|
|
156
|
+
const windowBounds = this.window.getBounds();
|
|
157
|
+
const isHudState = this.currentState === 'recording' || this.currentState === 'processing';
|
|
158
|
+
|
|
159
|
+
if (isHudState) {
|
|
160
|
+
const cursorDisplay = screen.getDisplayNearestPoint(screen.getCursorScreenPoint());
|
|
161
|
+
const workArea = cursorDisplay.workArea;
|
|
162
|
+
const x = Math.round(workArea.x + (workArea.width - windowBounds.width) / 2);
|
|
163
|
+
const y = Math.round(workArea.y + 8);
|
|
164
|
+
return { x, y };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const display = screen.getDisplayMatching(trayBounds);
|
|
168
|
+
|
|
169
|
+
// Center horizontally under tray icon
|
|
170
|
+
let x = Math.round(trayBounds.x + (trayBounds.width / 2) - (windowBounds.width / 2));
|
|
171
|
+
|
|
172
|
+
// Position below tray (macOS menu bar is at top)
|
|
173
|
+
let y: number;
|
|
174
|
+
if (process.platform === 'darwin') {
|
|
175
|
+
// macOS: position below menu bar with small gap
|
|
176
|
+
y = trayBounds.y + trayBounds.height + 4;
|
|
177
|
+
} else {
|
|
178
|
+
// Windows/Linux: detect taskbar position
|
|
179
|
+
const taskbarOnTop = trayBounds.y < display.workArea.y + 50;
|
|
180
|
+
if (taskbarOnTop) {
|
|
181
|
+
// Taskbar at top - position below it
|
|
182
|
+
y = trayBounds.y + trayBounds.height + 4;
|
|
183
|
+
} else {
|
|
184
|
+
// Taskbar at bottom - position above it
|
|
185
|
+
y = trayBounds.y - windowBounds.height - 4;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Ensure window stays on screen (respects work area boundaries)
|
|
190
|
+
const { workArea } = display;
|
|
191
|
+
x = Math.max(workArea.x, Math.min(x, workArea.x + workArea.width - windowBounds.width));
|
|
192
|
+
y = Math.max(workArea.y, Math.min(y, workArea.y + workArea.height - windowBounds.height));
|
|
193
|
+
|
|
194
|
+
return { x, y };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get the BrowserWindow for loading content
|
|
199
|
+
*/
|
|
200
|
+
getWindow(): BrowserWindow | null {
|
|
201
|
+
return this.window;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Check if popover is visible
|
|
206
|
+
*/
|
|
207
|
+
isVisible(): boolean {
|
|
208
|
+
return this.window?.isVisible() ?? false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Resize the popover for different states
|
|
213
|
+
* Re-anchors to tray if visible
|
|
214
|
+
*/
|
|
215
|
+
resize(width: number, height: number): void {
|
|
216
|
+
if (!this.window) return;
|
|
217
|
+
|
|
218
|
+
this.window.setSize(width, height, true);
|
|
219
|
+
|
|
220
|
+
// Reposition to stay anchored to tray
|
|
221
|
+
if (this.window.isVisible()) {
|
|
222
|
+
const position = this.calculatePosition();
|
|
223
|
+
this.window.setPosition(position.x, position.y, false);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
console.log(`[PopoverManager] Resized to ${width}x${height}`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Resize to a predefined state size
|
|
231
|
+
*/
|
|
232
|
+
resizeToState(state: PopoverState): void {
|
|
233
|
+
this.currentState = state;
|
|
234
|
+
this.applyStateAppearance(state);
|
|
235
|
+
const size = POPOVER_SIZES[state];
|
|
236
|
+
this.resize(size.width, size.height);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Keep popover visible while app focus changes.
|
|
241
|
+
* Useful during active recording when users switch to other apps.
|
|
242
|
+
*/
|
|
243
|
+
setKeepVisibleOnBlur(enabled: boolean): void {
|
|
244
|
+
this.keepVisibleOnBlur = enabled;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Update the tray reference (if tray is recreated)
|
|
249
|
+
*/
|
|
250
|
+
setTray(tray: Tray): void {
|
|
251
|
+
this.tray = tray;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Clean up resources
|
|
256
|
+
*/
|
|
257
|
+
destroy(): void {
|
|
258
|
+
if (this.window) {
|
|
259
|
+
this.window.destroy();
|
|
260
|
+
this.window = null;
|
|
261
|
+
}
|
|
262
|
+
this.tray = null;
|
|
263
|
+
console.log('[PopoverManager] Destroyed');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private applyStateAppearance(state: PopoverState): void {
|
|
267
|
+
if (!this.window || process.platform !== 'darwin') {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const isHudState = state === 'recording' || state === 'processing';
|
|
272
|
+
try {
|
|
273
|
+
// During compact HUD states, disable popover vibrancy + shadow so only
|
|
274
|
+
// the overlay chip itself is visible (no boundary rectangle).
|
|
275
|
+
this.window.setVibrancy(isHudState ? null : 'popover');
|
|
276
|
+
this.window.setBackgroundColor('#00000000');
|
|
277
|
+
if (typeof this.window.setHasShadow === 'function') {
|
|
278
|
+
this.window.setHasShadow(!isHudState);
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.warn('[PopoverManager] Failed to apply state appearance:', error);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|