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,460 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
This document provides a high-level overview of markupr's architecture.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Process Model](#process-model)
|
|
9
|
+
- [State Management](#state-management)
|
|
10
|
+
- [Data Flow](#data-flow)
|
|
11
|
+
- [Service Architecture](#service-architecture)
|
|
12
|
+
- [Security Model](#security-model)
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
markupr is an Electron application with a React frontend. It follows Electron's multi-process architecture with clear separation between the main process (Node.js) and renderer process (Chromium).
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
20
|
+
│ markupr │
|
|
21
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
22
|
+
│ │
|
|
23
|
+
│ ┌──────────────────┐ ┌──────────────────────┐ │
|
|
24
|
+
│ │ Main Process │◄──── IPC ─────►│ Renderer Process │ │
|
|
25
|
+
│ │ (Node.js) │ │ (Chromium) │ │
|
|
26
|
+
│ │ │ │ │ │
|
|
27
|
+
│ │ - Capture │ │ - React UI │ │
|
|
28
|
+
│ │ - Transcription │ │ - Audio Capture │ │
|
|
29
|
+
│ │ - File I/O │ │ - Visualization │ │
|
|
30
|
+
│ │ - Settings │ │ │ │
|
|
31
|
+
│ │ - Hotkeys │ │ │ │
|
|
32
|
+
│ └──────────────────┘ └──────────────────────┘ │
|
|
33
|
+
│ │
|
|
34
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Process Model
|
|
38
|
+
|
|
39
|
+
### Main Process
|
|
40
|
+
|
|
41
|
+
The main process handles:
|
|
42
|
+
|
|
43
|
+
- **System Integration**: Tray icons, hotkeys, native menus
|
|
44
|
+
- **Screen Capture**: Using Electron's desktopCapturer
|
|
45
|
+
- **File Operations**: Saving sessions, screenshots
|
|
46
|
+
- **Settings**: Persistent storage via electron-store
|
|
47
|
+
- **Secure Storage**: API keys via keytar
|
|
48
|
+
- **Updates**: Auto-updater
|
|
49
|
+
- **Session Orchestration**: State machine, service coordination
|
|
50
|
+
|
|
51
|
+
### Renderer Process
|
|
52
|
+
|
|
53
|
+
The renderer process handles:
|
|
54
|
+
|
|
55
|
+
- **User Interface**: React components
|
|
56
|
+
- **Audio Capture**: Web Audio API (requires renderer for MediaDevices)
|
|
57
|
+
- **Visualization**: Waveforms, transcription preview
|
|
58
|
+
- **User Input**: Buttons, forms, drag operations
|
|
59
|
+
|
|
60
|
+
### Preload Script
|
|
61
|
+
|
|
62
|
+
The preload script bridges the two processes:
|
|
63
|
+
|
|
64
|
+
- **Context Isolation**: Enabled for security
|
|
65
|
+
- **Node Integration**: Disabled in renderer
|
|
66
|
+
- **API Exposure**: via `contextBridge.exposeInMainWorld`
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Preload exposes a safe API
|
|
70
|
+
contextBridge.exposeInMainWorld('markupr', {
|
|
71
|
+
session: {
|
|
72
|
+
start: (sourceId) => ipcRenderer.invoke('markupr:session:start', sourceId),
|
|
73
|
+
// ...
|
|
74
|
+
},
|
|
75
|
+
// ...
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## State Management
|
|
80
|
+
|
|
81
|
+
### Session State Machine
|
|
82
|
+
|
|
83
|
+
The session follows a finite state machine pattern:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
┌───────────────────────────────────────┐
|
|
87
|
+
│ │
|
|
88
|
+
▼ │
|
|
89
|
+
┌──────┐ start ┌───────────┐ stop ┌────────────┐ │
|
|
90
|
+
│ idle │────────►│ recording │───────►│ processing │─────┘
|
|
91
|
+
└──────┘ └───────────┘ └────────────┘
|
|
92
|
+
▲ │ │
|
|
93
|
+
│ │ cancel │ complete
|
|
94
|
+
│ │ │
|
|
95
|
+
│ ▼ ▼
|
|
96
|
+
│ ┌──────┐ ┌──────────┐
|
|
97
|
+
└──────────────│ idle │◄────────────│ complete │
|
|
98
|
+
└──────┘ └──────────┘
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**States**:
|
|
102
|
+
- `idle`: No active session
|
|
103
|
+
- `recording`: Capturing audio and screenshots
|
|
104
|
+
- `processing`: Generating output
|
|
105
|
+
- `complete`: Session saved
|
|
106
|
+
|
|
107
|
+
### State in Main Process
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// SessionController manages state
|
|
111
|
+
class SessionController {
|
|
112
|
+
private state: SessionState = 'idle';
|
|
113
|
+
private session: Session | null = null;
|
|
114
|
+
|
|
115
|
+
async start(sourceId: string): Promise<void> {
|
|
116
|
+
if (this.state !== 'idle') throw new Error('Invalid state');
|
|
117
|
+
this.state = 'recording';
|
|
118
|
+
// ...
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### State in Renderer
|
|
124
|
+
|
|
125
|
+
React components subscribe to state changes:
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
function App() {
|
|
129
|
+
const [state, setState] = useState<SessionState>('idle');
|
|
130
|
+
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
const unsubscribe = window.markupr.session.onStateChange(({ state }) => {
|
|
133
|
+
setState(state);
|
|
134
|
+
});
|
|
135
|
+
return unsubscribe;
|
|
136
|
+
}, []);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Data Flow
|
|
141
|
+
|
|
142
|
+
### Recording Flow
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
146
|
+
│ Recording Flow │
|
|
147
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
148
|
+
|
|
149
|
+
User speaks
|
|
150
|
+
│
|
|
151
|
+
▼
|
|
152
|
+
┌──────────────────┐
|
|
153
|
+
│ AudioCapture │ (Renderer - Web Audio API)
|
|
154
|
+
│ (Renderer) │
|
|
155
|
+
└────────┬─────────┘
|
|
156
|
+
│ Audio chunks (100ms)
|
|
157
|
+
▼
|
|
158
|
+
┌──────────────────┐
|
|
159
|
+
│ Main Process │ IPC: AUDIO_CHUNK
|
|
160
|
+
│ │
|
|
161
|
+
└────────┬─────────┘
|
|
162
|
+
│
|
|
163
|
+
├────────────────────────┐
|
|
164
|
+
│ │
|
|
165
|
+
▼ ▼
|
|
166
|
+
┌──────────────────┐ ┌──────────────────┐
|
|
167
|
+
│ Transcription │ │ Intelligent │
|
|
168
|
+
│ Service │ │ Capture │
|
|
169
|
+
│ (OpenAI WS) │ │ │
|
|
170
|
+
└────────┬─────────┘ └────────┬─────────┘
|
|
171
|
+
│ │
|
|
172
|
+
│ Transcript │ Voice pause detected
|
|
173
|
+
│ │
|
|
174
|
+
▼ ▼
|
|
175
|
+
┌──────────────────┐ ┌──────────────────┐
|
|
176
|
+
│ Session │ │ Screen │
|
|
177
|
+
│ Controller │◄───│ Capture │
|
|
178
|
+
│ │ │ │
|
|
179
|
+
└────────┬─────────┘ └──────────────────┘
|
|
180
|
+
│
|
|
181
|
+
│ Feedback item
|
|
182
|
+
│
|
|
183
|
+
▼
|
|
184
|
+
┌──────────────────┐
|
|
185
|
+
│ Renderer │ IPC: SESSION_FEEDBACK_ITEM
|
|
186
|
+
│ (UI Update) │
|
|
187
|
+
└──────────────────┘
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Export Flow
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
User clicks Export
|
|
194
|
+
│
|
|
195
|
+
▼
|
|
196
|
+
┌──────────────────┐
|
|
197
|
+
│ ExportDialog │ User selects format
|
|
198
|
+
│ (Renderer) │
|
|
199
|
+
└────────┬─────────┘
|
|
200
|
+
│
|
|
201
|
+
▼
|
|
202
|
+
┌──────────────────┐
|
|
203
|
+
│ Main Process │ IPC: OUTPUT_EXPORT
|
|
204
|
+
│ │
|
|
205
|
+
└────────┬─────────┘
|
|
206
|
+
│
|
|
207
|
+
▼
|
|
208
|
+
┌──────────────────┐
|
|
209
|
+
│ ExportService │ Generates document
|
|
210
|
+
│ │
|
|
211
|
+
└────────┬─────────┘
|
|
212
|
+
│
|
|
213
|
+
├───────────────────────────────┐
|
|
214
|
+
│ │
|
|
215
|
+
▼ ▼
|
|
216
|
+
┌──────────────────┐ ┌──────────────────┐
|
|
217
|
+
│ Markdown │ │ PDF / HTML │
|
|
218
|
+
│ Generator │ │ Generator │
|
|
219
|
+
└────────┬─────────┘ └────────┬─────────┘
|
|
220
|
+
│ │
|
|
221
|
+
└───────────────┬───────────────┘
|
|
222
|
+
│
|
|
223
|
+
▼
|
|
224
|
+
┌──────────────────┐
|
|
225
|
+
│ FileManager │ Saves to disk
|
|
226
|
+
└──────────────────┘
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Service Architecture
|
|
230
|
+
|
|
231
|
+
### Main Process Services
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
235
|
+
│ Main Process Services │
|
|
236
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
237
|
+
│ │
|
|
238
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
239
|
+
│ │ SessionController │ │
|
|
240
|
+
│ │ - State machine │ │
|
|
241
|
+
│ │ - Service coordination │ │
|
|
242
|
+
│ │ - Event emission │ │
|
|
243
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
244
|
+
│ │ │ │ │
|
|
245
|
+
│ │ │ │ │
|
|
246
|
+
│ ▼ ▼ ▼ │
|
|
247
|
+
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
|
|
248
|
+
│ │ Capture │ │Transcript │ │ Output │ │
|
|
249
|
+
│ │ Service │ │ Service │ │ Service │ │
|
|
250
|
+
│ └───────────┘ └───────────┘ └───────────┘ │
|
|
251
|
+
│ │
|
|
252
|
+
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
|
|
253
|
+
│ │ Hotkey │ │ Tray │ │ Settings │ │
|
|
254
|
+
│ │ Manager │ │ Manager │ │ Manager │ │
|
|
255
|
+
│ └───────────┘ └───────────┘ └───────────┘ │
|
|
256
|
+
│ │
|
|
257
|
+
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
|
|
258
|
+
│ │ Menu │ │ Crash │ │ Auto │ │
|
|
259
|
+
│ │ Manager │ │ Recovery │ │ Updater │ │
|
|
260
|
+
│ └───────────┘ └───────────┘ └───────────┘ │
|
|
261
|
+
│ │
|
|
262
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Service Descriptions
|
|
266
|
+
|
|
267
|
+
| Service | Responsibility |
|
|
268
|
+
|---------|----------------|
|
|
269
|
+
| **SessionController** | Orchestrates recording sessions, manages state |
|
|
270
|
+
| **CaptureService** | Screen capture via desktopCapturer |
|
|
271
|
+
| **IntelligentCapture** | Voice-triggered screenshot timing |
|
|
272
|
+
| **TranscriptionService** | OpenAI WebSocket integration |
|
|
273
|
+
| **OutputService** | Document generation coordination |
|
|
274
|
+
| **HotkeyManager** | Global hotkey registration |
|
|
275
|
+
| **TrayManager** | System tray icon and menu |
|
|
276
|
+
| **MenuManager** | Native application menu |
|
|
277
|
+
| **SettingsManager** | Persistent settings via electron-store |
|
|
278
|
+
| **CrashRecovery** | Session recovery after crashes |
|
|
279
|
+
| **AutoUpdater** | Application updates |
|
|
280
|
+
|
|
281
|
+
### Service Communication
|
|
282
|
+
|
|
283
|
+
Services communicate through:
|
|
284
|
+
|
|
285
|
+
1. **Direct method calls** (same process)
|
|
286
|
+
2. **Events** (pub/sub pattern)
|
|
287
|
+
3. **IPC** (between processes)
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// SessionController coordinates services
|
|
291
|
+
class SessionController {
|
|
292
|
+
constructor(
|
|
293
|
+
private capture: CaptureService,
|
|
294
|
+
private transcription: TranscriptionService,
|
|
295
|
+
private output: OutputService,
|
|
296
|
+
) {}
|
|
297
|
+
|
|
298
|
+
async start(sourceId: string) {
|
|
299
|
+
// Direct method calls
|
|
300
|
+
await this.capture.start(sourceId);
|
|
301
|
+
await this.transcription.connect();
|
|
302
|
+
|
|
303
|
+
// Event subscription
|
|
304
|
+
this.transcription.on('transcript', (text) => {
|
|
305
|
+
this.handleTranscript(text);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Security Model
|
|
312
|
+
|
|
313
|
+
### Context Isolation
|
|
314
|
+
|
|
315
|
+
The renderer process is sandboxed:
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
// main/index.ts
|
|
319
|
+
new BrowserWindow({
|
|
320
|
+
webPreferences: {
|
|
321
|
+
contextIsolation: true, // Enabled
|
|
322
|
+
nodeIntegration: false, // Disabled
|
|
323
|
+
preload: 'preload.js', // Bridge
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Secure API Key Storage
|
|
329
|
+
|
|
330
|
+
API keys are stored using system keychain:
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
// macOS: Keychain
|
|
334
|
+
// Windows: Credential Manager
|
|
335
|
+
// Linux: Secret Service
|
|
336
|
+
|
|
337
|
+
import keytar from 'keytar';
|
|
338
|
+
|
|
339
|
+
await keytar.setPassword('markupr', 'openai', apiKey);
|
|
340
|
+
const key = await keytar.getPassword('markupr', 'openai');
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Content Security Policy
|
|
344
|
+
|
|
345
|
+
```html
|
|
346
|
+
<meta http-equiv="Content-Security-Policy"
|
|
347
|
+
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### IPC Security
|
|
351
|
+
|
|
352
|
+
All IPC channels are:
|
|
353
|
+
- Explicitly defined in shared types
|
|
354
|
+
- Validated in handlers
|
|
355
|
+
- Never expose raw Node.js APIs
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// Safe: Defined channel with specific handler
|
|
359
|
+
ipcMain.handle('markupr:session:start', async (_, sourceId: string) => {
|
|
360
|
+
// Validate input
|
|
361
|
+
if (typeof sourceId !== 'string') throw new Error('Invalid sourceId');
|
|
362
|
+
return sessionController.start(sourceId);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Unsafe (never do this): Exposing arbitrary execution
|
|
366
|
+
ipcMain.handle('execute', (_, code) => eval(code)); // NEVER!
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Diagrams
|
|
370
|
+
|
|
371
|
+
### Component Interaction
|
|
372
|
+
|
|
373
|
+
```mermaid
|
|
374
|
+
graph TD
|
|
375
|
+
subgraph Renderer
|
|
376
|
+
UI[React UI]
|
|
377
|
+
AC[Audio Capture]
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
subgraph Preload
|
|
381
|
+
API[markupr API]
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
subgraph Main
|
|
385
|
+
SC[Session Controller]
|
|
386
|
+
TC[Transcription]
|
|
387
|
+
CC[Capture]
|
|
388
|
+
HK[Hotkeys]
|
|
389
|
+
TR[Tray]
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
UI --> API
|
|
393
|
+
AC --> API
|
|
394
|
+
API --> SC
|
|
395
|
+
SC --> TC
|
|
396
|
+
SC --> CC
|
|
397
|
+
HK --> SC
|
|
398
|
+
TR --> SC
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Recording Sequence
|
|
402
|
+
|
|
403
|
+
```mermaid
|
|
404
|
+
sequenceDiagram
|
|
405
|
+
participant U as User
|
|
406
|
+
participant R as Renderer
|
|
407
|
+
participant M as Main
|
|
408
|
+
participant D as OpenAI
|
|
409
|
+
|
|
410
|
+
U->>R: Press hotkey
|
|
411
|
+
R->>M: session.start(sourceId)
|
|
412
|
+
M->>M: Initialize capture
|
|
413
|
+
M->>D: Connect WebSocket
|
|
414
|
+
D-->>M: Connection ready
|
|
415
|
+
|
|
416
|
+
loop Recording
|
|
417
|
+
R->>M: Audio chunk
|
|
418
|
+
M->>D: Send audio
|
|
419
|
+
D-->>M: Transcript
|
|
420
|
+
M-->>R: Feedback item
|
|
421
|
+
Note over M: Voice pause detected
|
|
422
|
+
M->>M: Capture screenshot
|
|
423
|
+
M-->>R: Screenshot captured
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
U->>R: Press hotkey
|
|
427
|
+
R->>M: session.stop()
|
|
428
|
+
M->>D: Close connection
|
|
429
|
+
M->>M: Generate output
|
|
430
|
+
M-->>R: Session complete
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Data Model
|
|
434
|
+
|
|
435
|
+
```mermaid
|
|
436
|
+
erDiagram
|
|
437
|
+
Session ||--o{ FeedbackItem : contains
|
|
438
|
+
FeedbackItem ||--o| Screenshot : has
|
|
439
|
+
Session ||--o{ Screenshot : buffers
|
|
440
|
+
Session {
|
|
441
|
+
string id
|
|
442
|
+
number startTime
|
|
443
|
+
number endTime
|
|
444
|
+
string sourceId
|
|
445
|
+
SessionState state
|
|
446
|
+
}
|
|
447
|
+
FeedbackItem {
|
|
448
|
+
string id
|
|
449
|
+
number timestamp
|
|
450
|
+
string text
|
|
451
|
+
number confidence
|
|
452
|
+
}
|
|
453
|
+
Screenshot {
|
|
454
|
+
string id
|
|
455
|
+
number timestamp
|
|
456
|
+
Buffer data
|
|
457
|
+
number width
|
|
458
|
+
number height
|
|
459
|
+
}
|
|
460
|
+
```
|