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.
Files changed (299) hide show
  1. package/.claude/commands/review-feedback.md +47 -0
  2. package/.eslintrc.json +35 -0
  3. package/.github/CODEOWNERS +16 -0
  4. package/.github/FUNDING.yml +1 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.md +56 -0
  6. package/.github/ISSUE_TEMPLATE/feature_request.md +54 -0
  7. package/.github/PULL_REQUEST_TEMPLATE.md +89 -0
  8. package/.github/dependabot.yml +70 -0
  9. package/.github/workflows/ci.yml +184 -0
  10. package/.github/workflows/deploy-landing.yml +134 -0
  11. package/.github/workflows/nightly.yml +288 -0
  12. package/.github/workflows/release.yml +318 -0
  13. package/CHANGELOG.md +127 -0
  14. package/CLAUDE.md +137 -0
  15. package/CODE_OF_CONDUCT.md +9 -0
  16. package/CONTRIBUTING.md +390 -0
  17. package/LICENSE +21 -0
  18. package/PRODUCT_VISION.md +277 -0
  19. package/README.md +517 -0
  20. package/SECURITY.md +51 -0
  21. package/SIGNING_INSTRUCTIONS.md +284 -0
  22. package/assets/DMG_BACKGROUND_INSTRUCTIONS.md +130 -0
  23. package/assets/svg-source/dmg-background.svg +70 -0
  24. package/assets/svg-source/icon.svg +20 -0
  25. package/assets/svg-source/tray-icon-processing.svg +7 -0
  26. package/assets/svg-source/tray-icon-recording.svg +7 -0
  27. package/assets/svg-source/tray-icon.svg +6 -0
  28. package/assets/tray-complete.png +0 -0
  29. package/assets/tray-complete@2x.png +0 -0
  30. package/assets/tray-completeTemplate.png +0 -0
  31. package/assets/tray-completeTemplate@2x.png +0 -0
  32. package/assets/tray-error.png +0 -0
  33. package/assets/tray-error@2x.png +0 -0
  34. package/assets/tray-errorTemplate.png +0 -0
  35. package/assets/tray-errorTemplate@2x.png +0 -0
  36. package/assets/tray-icon-processing.png +0 -0
  37. package/assets/tray-icon-processing@2x.png +0 -0
  38. package/assets/tray-icon-processingTemplate.png +0 -0
  39. package/assets/tray-icon-processingTemplate@2x.png +0 -0
  40. package/assets/tray-icon-recording.png +0 -0
  41. package/assets/tray-icon-recording@2x.png +0 -0
  42. package/assets/tray-icon-recordingTemplate.png +0 -0
  43. package/assets/tray-icon-recordingTemplate@2x.png +0 -0
  44. package/assets/tray-icon.png +0 -0
  45. package/assets/tray-icon@2x.png +0 -0
  46. package/assets/tray-iconTemplate.png +0 -0
  47. package/assets/tray-iconTemplate@2x.png +0 -0
  48. package/assets/tray-idle.png +0 -0
  49. package/assets/tray-idle@2x.png +0 -0
  50. package/assets/tray-idleTemplate.png +0 -0
  51. package/assets/tray-idleTemplate@2x.png +0 -0
  52. package/assets/tray-processing-0.png +0 -0
  53. package/assets/tray-processing-0@2x.png +0 -0
  54. package/assets/tray-processing-0Template.png +0 -0
  55. package/assets/tray-processing-0Template@2x.png +0 -0
  56. package/assets/tray-processing-1.png +0 -0
  57. package/assets/tray-processing-1@2x.png +0 -0
  58. package/assets/tray-processing-1Template.png +0 -0
  59. package/assets/tray-processing-1Template@2x.png +0 -0
  60. package/assets/tray-processing-2.png +0 -0
  61. package/assets/tray-processing-2@2x.png +0 -0
  62. package/assets/tray-processing-2Template.png +0 -0
  63. package/assets/tray-processing-2Template@2x.png +0 -0
  64. package/assets/tray-processing-3.png +0 -0
  65. package/assets/tray-processing-3@2x.png +0 -0
  66. package/assets/tray-processing-3Template.png +0 -0
  67. package/assets/tray-processing-3Template@2x.png +0 -0
  68. package/assets/tray-processing.png +0 -0
  69. package/assets/tray-processing@2x.png +0 -0
  70. package/assets/tray-processingTemplate.png +0 -0
  71. package/assets/tray-processingTemplate@2x.png +0 -0
  72. package/assets/tray-recording.png +0 -0
  73. package/assets/tray-recording@2x.png +0 -0
  74. package/assets/tray-recordingTemplate.png +0 -0
  75. package/assets/tray-recordingTemplate@2x.png +0 -0
  76. package/build/DMG_BACKGROUND_SPEC.md +50 -0
  77. package/build/dmg-background.png +0 -0
  78. package/build/dmg-background@2x.png +0 -0
  79. package/build/entitlements.mac.inherit.plist +27 -0
  80. package/build/entitlements.mac.plist +41 -0
  81. package/build/favicon-16.png +0 -0
  82. package/build/favicon-180.png +0 -0
  83. package/build/favicon-192.png +0 -0
  84. package/build/favicon-32.png +0 -0
  85. package/build/favicon-48.png +0 -0
  86. package/build/favicon-512.png +0 -0
  87. package/build/favicon-64.png +0 -0
  88. package/build/icon-128.png +0 -0
  89. package/build/icon-16.png +0 -0
  90. package/build/icon-24.png +0 -0
  91. package/build/icon-256.png +0 -0
  92. package/build/icon-32.png +0 -0
  93. package/build/icon-48.png +0 -0
  94. package/build/icon-64.png +0 -0
  95. package/build/icon.icns +0 -0
  96. package/build/icon.ico +0 -0
  97. package/build/icon.iconset/icon_128x128.png +0 -0
  98. package/build/icon.iconset/icon_128x128@2x.png +0 -0
  99. package/build/icon.iconset/icon_16x16.png +0 -0
  100. package/build/icon.iconset/icon_16x16@2x.png +0 -0
  101. package/build/icon.iconset/icon_256x256.png +0 -0
  102. package/build/icon.iconset/icon_256x256@2x.png +0 -0
  103. package/build/icon.iconset/icon_32x32.png +0 -0
  104. package/build/icon.iconset/icon_32x32@2x.png +0 -0
  105. package/build/icon.iconset/icon_512x512.png +0 -0
  106. package/build/icon.iconset/icon_512x512@2x.png +0 -0
  107. package/build/icon.png +0 -0
  108. package/build/installer-header.bmp +0 -0
  109. package/build/installer-header.png +0 -0
  110. package/build/installer-sidebar.bmp +0 -0
  111. package/build/installer-sidebar.png +0 -0
  112. package/build/installer.nsh +45 -0
  113. package/build/overlay-processing.png +0 -0
  114. package/build/overlay-recording.png +0 -0
  115. package/build/toolbar-record.png +0 -0
  116. package/build/toolbar-screenshot.png +0 -0
  117. package/build/toolbar-settings.png +0 -0
  118. package/build/toolbar-stop.png +0 -0
  119. package/dist/main/index.mjs +12612 -0
  120. package/dist/preload/index.mjs +907 -0
  121. package/dist/renderer/assets/index-CCmUjl9K.js +19495 -0
  122. package/dist/renderer/assets/index-CUqz_Gs6.css +2270 -0
  123. package/dist/renderer/index.html +27 -0
  124. package/docs/AI_AGENT_QUICKSTART.md +42 -0
  125. package/docs/AI_PIPELINE_DESIGN.md +595 -0
  126. package/docs/API.md +514 -0
  127. package/docs/ARCHITECTURE.md +460 -0
  128. package/docs/CONFIGURATION.md +336 -0
  129. package/docs/DEVELOPMENT.md +508 -0
  130. package/docs/EXPORT_FORMATS.md +451 -0
  131. package/docs/GETTING_STARTED.md +236 -0
  132. package/docs/KEYBOARD_SHORTCUTS.md +334 -0
  133. package/docs/TROUBLESHOOTING.md +418 -0
  134. package/docs/landing/index.html +672 -0
  135. package/docs/landing/script.js +342 -0
  136. package/docs/landing/styles.css +1543 -0
  137. package/electron-builder.yml +140 -0
  138. package/electron.vite.config.ts +63 -0
  139. package/package.json +108 -0
  140. package/railway.json +12 -0
  141. package/scripts/build.mjs +51 -0
  142. package/scripts/generate-icons.mjs +314 -0
  143. package/scripts/generate-installer-images.cjs +253 -0
  144. package/scripts/generate-tray-icons.mjs +258 -0
  145. package/scripts/notarize.cjs +180 -0
  146. package/scripts/one-click-clean-test.sh +147 -0
  147. package/scripts/postinstall.mjs +36 -0
  148. package/scripts/setup-markupr.sh +55 -0
  149. package/setup +17 -0
  150. package/site/index.html +1835 -0
  151. package/site/package.json +11 -0
  152. package/site/railway.json +12 -0
  153. package/site/server.js +31 -0
  154. package/src/main/AutoUpdater.ts +392 -0
  155. package/src/main/CrashRecovery.ts +655 -0
  156. package/src/main/ErrorHandler.ts +703 -0
  157. package/src/main/HotkeyManager.ts +399 -0
  158. package/src/main/MenuManager.ts +529 -0
  159. package/src/main/PermissionManager.ts +420 -0
  160. package/src/main/SessionController.ts +1465 -0
  161. package/src/main/TrayManager.ts +540 -0
  162. package/src/main/ai/AIPipelineManager.ts +199 -0
  163. package/src/main/ai/ClaudeAnalyzer.ts +339 -0
  164. package/src/main/ai/ImageOptimizer.ts +176 -0
  165. package/src/main/ai/StructuredMarkdownBuilder.ts +379 -0
  166. package/src/main/ai/index.ts +16 -0
  167. package/src/main/ai/types.ts +258 -0
  168. package/src/main/analysis/ClarificationGenerator.ts +385 -0
  169. package/src/main/analysis/FeedbackAnalyzer.ts +531 -0
  170. package/src/main/analysis/index.ts +19 -0
  171. package/src/main/audio/AudioCapture.ts +978 -0
  172. package/src/main/audio/audioUtils.ts +100 -0
  173. package/src/main/audio/index.ts +20 -0
  174. package/src/main/capture/index.ts +1 -0
  175. package/src/main/index.ts +1693 -0
  176. package/src/main/ipc/captureHandlers.ts +272 -0
  177. package/src/main/ipc/index.ts +45 -0
  178. package/src/main/ipc/outputHandlers.ts +302 -0
  179. package/src/main/ipc/sessionHandlers.ts +56 -0
  180. package/src/main/ipc/settingsHandlers.ts +471 -0
  181. package/src/main/ipc/types.ts +56 -0
  182. package/src/main/ipc/windowHandlers.ts +277 -0
  183. package/src/main/output/ClipboardService.ts +369 -0
  184. package/src/main/output/ExportService.ts +539 -0
  185. package/src/main/output/FileManager.ts +416 -0
  186. package/src/main/output/MarkdownGenerator.ts +791 -0
  187. package/src/main/output/MarkdownPatcher.ts +299 -0
  188. package/src/main/output/index.ts +186 -0
  189. package/src/main/output/sessionAdapter.ts +207 -0
  190. package/src/main/output/templates/html-template.ts +553 -0
  191. package/src/main/pipeline/FrameExtractor.ts +330 -0
  192. package/src/main/pipeline/PostProcessor.ts +399 -0
  193. package/src/main/pipeline/TranscriptAnalyzer.ts +226 -0
  194. package/src/main/pipeline/index.ts +36 -0
  195. package/src/main/platform/WindowsTaskbar.ts +600 -0
  196. package/src/main/platform/index.ts +16 -0
  197. package/src/main/settings/SettingsManager.ts +730 -0
  198. package/src/main/settings/index.ts +19 -0
  199. package/src/main/transcription/ModelDownloadManager.ts +494 -0
  200. package/src/main/transcription/TierManager.ts +219 -0
  201. package/src/main/transcription/TranscriptionRecoveryService.ts +340 -0
  202. package/src/main/transcription/WhisperService.ts +748 -0
  203. package/src/main/transcription/index.ts +56 -0
  204. package/src/main/transcription/types.ts +135 -0
  205. package/src/main/windows/PopoverManager.ts +284 -0
  206. package/src/main/windows/TaskbarIntegration.ts +452 -0
  207. package/src/main/windows/index.ts +23 -0
  208. package/src/preload/index.ts +1047 -0
  209. package/src/renderer/App.tsx +515 -0
  210. package/src/renderer/AppWrapper.tsx +28 -0
  211. package/src/renderer/assets/logo-dark.svg +7 -0
  212. package/src/renderer/assets/logo.svg +7 -0
  213. package/src/renderer/audio/AudioCaptureRenderer.ts +454 -0
  214. package/src/renderer/capture/ScreenRecordingRenderer.ts +492 -0
  215. package/src/renderer/components/AnnotationOverlay.tsx +836 -0
  216. package/src/renderer/components/AudioWaveform.tsx +811 -0
  217. package/src/renderer/components/ClarificationQuestions.tsx +656 -0
  218. package/src/renderer/components/CountdownTimer.tsx +495 -0
  219. package/src/renderer/components/CrashRecoveryDialog.tsx +632 -0
  220. package/src/renderer/components/DonateButton.tsx +127 -0
  221. package/src/renderer/components/ErrorBoundary.tsx +308 -0
  222. package/src/renderer/components/ExportDialog.tsx +872 -0
  223. package/src/renderer/components/HotkeyHint.tsx +261 -0
  224. package/src/renderer/components/KeyboardShortcuts.tsx +787 -0
  225. package/src/renderer/components/ModelDownloadDialog.tsx +844 -0
  226. package/src/renderer/components/Onboarding.tsx +1830 -0
  227. package/src/renderer/components/ProcessingOverlay.tsx +157 -0
  228. package/src/renderer/components/RecordingOverlay.tsx +423 -0
  229. package/src/renderer/components/SessionHistory.tsx +1746 -0
  230. package/src/renderer/components/SessionReview.tsx +1321 -0
  231. package/src/renderer/components/SettingsPanel.tsx +217 -0
  232. package/src/renderer/components/Skeleton.tsx +347 -0
  233. package/src/renderer/components/StatusIndicator.tsx +86 -0
  234. package/src/renderer/components/ThemeProvider.tsx +429 -0
  235. package/src/renderer/components/Tooltip.tsx +370 -0
  236. package/src/renderer/components/TranscriptionPreview.tsx +183 -0
  237. package/src/renderer/components/TranscriptionTierSelector.tsx +640 -0
  238. package/src/renderer/components/UpdateNotification.tsx +377 -0
  239. package/src/renderer/components/WindowSelector.tsx +947 -0
  240. package/src/renderer/components/index.ts +99 -0
  241. package/src/renderer/components/primitives/ApiKeyInput.tsx +98 -0
  242. package/src/renderer/components/primitives/ColorPicker.tsx +65 -0
  243. package/src/renderer/components/primitives/DangerButton.tsx +45 -0
  244. package/src/renderer/components/primitives/DirectoryPicker.tsx +41 -0
  245. package/src/renderer/components/primitives/Dropdown.tsx +34 -0
  246. package/src/renderer/components/primitives/KeyRecorder.tsx +117 -0
  247. package/src/renderer/components/primitives/SettingsSection.tsx +32 -0
  248. package/src/renderer/components/primitives/Slider.tsx +43 -0
  249. package/src/renderer/components/primitives/Toggle.tsx +36 -0
  250. package/src/renderer/components/primitives/index.ts +10 -0
  251. package/src/renderer/components/settings/AdvancedTab.tsx +174 -0
  252. package/src/renderer/components/settings/AppearanceTab.tsx +77 -0
  253. package/src/renderer/components/settings/GeneralTab.tsx +40 -0
  254. package/src/renderer/components/settings/HotkeysTab.tsx +79 -0
  255. package/src/renderer/components/settings/RecordingTab.tsx +84 -0
  256. package/src/renderer/components/settings/index.ts +9 -0
  257. package/src/renderer/components/settings/settingsStyles.ts +673 -0
  258. package/src/renderer/components/settings/tabConfig.tsx +85 -0
  259. package/src/renderer/components/settings/useSettingsPanel.ts +447 -0
  260. package/src/renderer/contexts/ProcessingContext.tsx +227 -0
  261. package/src/renderer/contexts/RecordingContext.tsx +683 -0
  262. package/src/renderer/contexts/UIContext.tsx +326 -0
  263. package/src/renderer/contexts/index.ts +24 -0
  264. package/src/renderer/donateMessages.ts +69 -0
  265. package/src/renderer/hooks/index.ts +75 -0
  266. package/src/renderer/hooks/useAnimation.tsx +544 -0
  267. package/src/renderer/hooks/useTheme.ts +313 -0
  268. package/src/renderer/index.html +26 -0
  269. package/src/renderer/main.tsx +52 -0
  270. package/src/renderer/styles/animations.css +1093 -0
  271. package/src/renderer/styles/app-shell.css +662 -0
  272. package/src/renderer/styles/globals.css +515 -0
  273. package/src/renderer/styles/theme.ts +578 -0
  274. package/src/renderer/types/electron.d.ts +385 -0
  275. package/src/shared/hotkeys.ts +283 -0
  276. package/src/shared/types.ts +809 -0
  277. package/tests/clipboard.test.ts +228 -0
  278. package/tests/e2e/criticalPaths.test.ts +594 -0
  279. package/tests/feedbackAnalyzer.test.ts +303 -0
  280. package/tests/integration/sessionFlow.test.ts +583 -0
  281. package/tests/markdownGenerator.test.ts +418 -0
  282. package/tests/output.test.ts +96 -0
  283. package/tests/setup.ts +486 -0
  284. package/tests/unit/appIntegration.test.ts +676 -0
  285. package/tests/unit/appViewState.test.ts +281 -0
  286. package/tests/unit/audioIpcChannels.test.ts +17 -0
  287. package/tests/unit/exportService.test.ts +492 -0
  288. package/tests/unit/hotkeys.test.ts +92 -0
  289. package/tests/unit/navigationPreload.test.ts +94 -0
  290. package/tests/unit/onboardingFlow.test.ts +345 -0
  291. package/tests/unit/permissionManager.test.ts +175 -0
  292. package/tests/unit/permissionManagerExpanded.test.ts +296 -0
  293. package/tests/unit/screenRecordingRenderer.test.ts +368 -0
  294. package/tests/unit/sessionController.test.ts +515 -0
  295. package/tests/unit/tierManager.test.ts +61 -0
  296. package/tests/unit/tierManagerExpanded.test.ts +142 -0
  297. package/tests/unit/transcriptAnalyzer.test.ts +64 -0
  298. package/tsconfig.json +25 -0
  299. package/vitest.config.ts +46 -0
@@ -0,0 +1,907 @@
1
+ import { contextBridge, ipcRenderer } from "electron";
2
+ const IPC_CHANNELS = {
3
+ // ---------------------------------------------------------------------------
4
+ // Session Channels (Renderer -> Main)
5
+ // ---------------------------------------------------------------------------
6
+ SESSION_START: "markupr:session:start",
7
+ SESSION_STOP: "markupr:session:stop",
8
+ SESSION_PAUSE: "markupr:session:pause",
9
+ SESSION_RESUME: "markupr:session:resume",
10
+ SESSION_CANCEL: "markupr:session:cancel",
11
+ SESSION_GET_STATUS: "markupr:session:get-status",
12
+ SESSION_GET_CURRENT: "markupr:session:get-current",
13
+ // ---------------------------------------------------------------------------
14
+ // Session Channels (Main -> Renderer)
15
+ // ---------------------------------------------------------------------------
16
+ SESSION_STATE_CHANGED: "markupr:session:state-changed",
17
+ SESSION_STATUS: "markupr:session:status-update",
18
+ SESSION_COMPLETE: "markupr:session:complete",
19
+ SESSION_FEEDBACK_ITEM: "markupr:session:feedback-item",
20
+ SESSION_VOICE_ACTIVITY: "markupr:session:voice-activity",
21
+ SESSION_ERROR: "markupr:session:error",
22
+ // ---------------------------------------------------------------------------
23
+ // Capture Channels (Renderer -> Main)
24
+ // ---------------------------------------------------------------------------
25
+ CAPTURE_GET_SOURCES: "markupr:capture:get-sources",
26
+ CAPTURE_MANUAL_SCREENSHOT: "markupr:capture:manual-screenshot",
27
+ SCREEN_RECORDING_START: "markupr:screen-recording:start",
28
+ SCREEN_RECORDING_CHUNK: "markupr:screen-recording:chunk",
29
+ SCREEN_RECORDING_STOP: "markupr:screen-recording:stop",
30
+ // ---------------------------------------------------------------------------
31
+ // Capture Channels (Main -> Renderer)
32
+ // ---------------------------------------------------------------------------
33
+ SCREENSHOT_CAPTURED: "markupr:capture:screenshot-taken",
34
+ MANUAL_SCREENSHOT: "markupr:capture:manual-triggered",
35
+ // ---------------------------------------------------------------------------
36
+ // Audio Channels (Renderer -> Main)
37
+ // ---------------------------------------------------------------------------
38
+ AUDIO_GET_DEVICES: "markupr:audio:get-devices",
39
+ AUDIO_SET_DEVICE: "markupr:audio:set-device",
40
+ // ---------------------------------------------------------------------------
41
+ // Audio Channels (Main -> Renderer) - Communication with audio capture
42
+ // ---------------------------------------------------------------------------
43
+ AUDIO_REQUEST_DEVICES: "markupr:audio:request-devices",
44
+ AUDIO_START_CAPTURE: "markupr:audio:start-capture",
45
+ AUDIO_STOP_CAPTURE: "markupr:audio:stop-capture",
46
+ AUDIO_CHUNK: "markupr:audio:chunk",
47
+ AUDIO_DEVICES_RESPONSE: "markupr:audio:devices-response",
48
+ AUDIO_CAPTURE_ERROR: "markupr:audio:capture-error",
49
+ AUDIO_CAPTURE_STARTED: "markupr:audio:capture-started",
50
+ AUDIO_CAPTURE_STOPPED: "markupr:audio:capture-stopped",
51
+ AUDIO_LEVEL: "markupr:audio:level",
52
+ AUDIO_VOICE_ACTIVITY: "markupr:audio:voice-activity",
53
+ // ---------------------------------------------------------------------------
54
+ // Transcription Channels (Main -> Renderer)
55
+ // ---------------------------------------------------------------------------
56
+ TRANSCRIPTION_UPDATE: "markupr:transcript:chunk",
57
+ TRANSCRIPTION_FINAL: "markupr:transcript:final",
58
+ // ---------------------------------------------------------------------------
59
+ // Transcription Control Channels (Renderer -> Main)
60
+ // ---------------------------------------------------------------------------
61
+ TRANSCRIPTION_GET_TIER_STATUSES: "markupr:transcription:get-tier-statuses",
62
+ TRANSCRIPTION_GET_CURRENT_TIER: "markupr:transcription:get-current-tier",
63
+ TRANSCRIPTION_SET_TIER: "markupr:transcription:set-tier",
64
+ // ---------------------------------------------------------------------------
65
+ // Settings Channels (Renderer -> Main)
66
+ // ---------------------------------------------------------------------------
67
+ SETTINGS_GET: "markupr:settings:get",
68
+ SETTINGS_GET_ALL: "markupr:settings:get-all",
69
+ SETTINGS_SET: "markupr:settings:set",
70
+ SETTINGS_GET_API_KEY: "markupr:settings:get-api-key",
71
+ SETTINGS_SET_API_KEY: "markupr:settings:set-api-key",
72
+ SETTINGS_DELETE_API_KEY: "markupr:settings:delete-api-key",
73
+ SETTINGS_HAS_API_KEY: "markupr:settings:has-api-key",
74
+ SETTINGS_TEST_API_KEY: "markupr:settings:test-api-key",
75
+ SETTINGS_SELECT_DIRECTORY: "markupr:settings:select-directory",
76
+ SETTINGS_CLEAR_ALL_DATA: "markupr:settings:clear-all-data",
77
+ SETTINGS_EXPORT: "markupr:settings:export",
78
+ SETTINGS_IMPORT: "markupr:settings:import",
79
+ // ---------------------------------------------------------------------------
80
+ // Permissions Channels (Renderer -> Main)
81
+ // ---------------------------------------------------------------------------
82
+ PERMISSIONS_CHECK: "markupr:permissions:check",
83
+ PERMISSIONS_REQUEST: "markupr:permissions:request",
84
+ PERMISSIONS_GET_ALL: "markupr:permissions:get-all",
85
+ // ---------------------------------------------------------------------------
86
+ // Output Channels (Renderer -> Main)
87
+ // ---------------------------------------------------------------------------
88
+ OUTPUT_SAVE: "markupr:output:save",
89
+ OUTPUT_COPY_CLIPBOARD: "markupr:output:copy-clipboard",
90
+ OUTPUT_OPEN_FOLDER: "markupr:output:open-folder",
91
+ // Session History Browser
92
+ OUTPUT_LIST_SESSIONS: "markupr:output:list-sessions",
93
+ OUTPUT_GET_SESSION_METADATA: "markupr:output:get-session-metadata",
94
+ OUTPUT_DELETE_SESSION: "markupr:output:delete-session",
95
+ OUTPUT_DELETE_SESSIONS: "markupr:output:delete-sessions",
96
+ OUTPUT_EXPORT_SESSION: "markupr:output:export-session",
97
+ OUTPUT_EXPORT_SESSIONS: "markupr:output:export-sessions",
98
+ // ---------------------------------------------------------------------------
99
+ // Output Channels (Main -> Renderer)
100
+ // ---------------------------------------------------------------------------
101
+ OUTPUT_READY: "markupr:output:ready",
102
+ OUTPUT_ERROR: "markupr:output:error",
103
+ // ---------------------------------------------------------------------------
104
+ // Hotkey Channels
105
+ // ---------------------------------------------------------------------------
106
+ HOTKEY_TRIGGERED: "markupr:hotkey:triggered",
107
+ HOTKEY_CONFIG: "markupr:hotkey:config",
108
+ HOTKEY_UPDATE: "markupr:hotkey:update",
109
+ // ---------------------------------------------------------------------------
110
+ // Clipboard (Legacy - kept for compatibility)
111
+ // ---------------------------------------------------------------------------
112
+ COPY_TO_CLIPBOARD: "markupr:clipboard:copy",
113
+ // ---------------------------------------------------------------------------
114
+ // Window Control Channels
115
+ // ---------------------------------------------------------------------------
116
+ WINDOW_MINIMIZE: "markupr:window:minimize",
117
+ WINDOW_CLOSE: "markupr:window:close",
118
+ WINDOW_HIDE: "markupr:window:hide",
119
+ // ---------------------------------------------------------------------------
120
+ // Update Channels (Renderer -> Main)
121
+ // ---------------------------------------------------------------------------
122
+ UPDATE_CHECK: "markupr:update:check",
123
+ UPDATE_DOWNLOAD: "markupr:update:download",
124
+ UPDATE_INSTALL: "markupr:update:install",
125
+ // ---------------------------------------------------------------------------
126
+ // Update Channels (Main -> Renderer)
127
+ // ---------------------------------------------------------------------------
128
+ UPDATE_STATUS: "markupr:update:status",
129
+ // ---------------------------------------------------------------------------
130
+ // Crash Recovery Channels (Renderer -> Main)
131
+ // ---------------------------------------------------------------------------
132
+ CRASH_RECOVERY_CHECK: "markupr:crash-recovery:check",
133
+ CRASH_RECOVERY_RECOVER: "markupr:crash-recovery:recover",
134
+ CRASH_RECOVERY_DISCARD: "markupr:crash-recovery:discard",
135
+ CRASH_RECOVERY_GET_LOGS: "markupr:crash-recovery:get-logs",
136
+ CRASH_RECOVERY_CLEAR_LOGS: "markupr:crash-recovery:clear-logs",
137
+ CRASH_RECOVERY_UPDATE_SETTINGS: "markupr:crash-recovery:update-settings",
138
+ // ---------------------------------------------------------------------------
139
+ // Crash Recovery Channels (Main -> Renderer)
140
+ // ---------------------------------------------------------------------------
141
+ CRASH_RECOVERY_FOUND: "markupr:crash-recovery:found",
142
+ // ---------------------------------------------------------------------------
143
+ // Whisper Model Channels (Renderer -> Main)
144
+ // ---------------------------------------------------------------------------
145
+ WHISPER_CHECK_MODEL: "markupr:whisper:check-model",
146
+ WHISPER_DOWNLOAD_MODEL: "markupr:whisper:download-model",
147
+ WHISPER_CANCEL_DOWNLOAD: "markupr:whisper:cancel-download",
148
+ WHISPER_GET_AVAILABLE_MODELS: "markupr:whisper:get-available-models",
149
+ WHISPER_HAS_TRANSCRIPTION_CAPABILITY: "markupr:whisper:has-transcription-capability",
150
+ // ---------------------------------------------------------------------------
151
+ // Whisper Model Channels (Main -> Renderer)
152
+ // ---------------------------------------------------------------------------
153
+ WHISPER_DOWNLOAD_PROGRESS: "markupr:whisper:download-progress",
154
+ WHISPER_DOWNLOAD_COMPLETE: "markupr:whisper:download-complete",
155
+ WHISPER_DOWNLOAD_ERROR: "markupr:whisper:download-error",
156
+ // ---------------------------------------------------------------------------
157
+ // Processing Pipeline Channels (Main -> Renderer)
158
+ // ---------------------------------------------------------------------------
159
+ PROCESSING_PROGRESS: "markupr:processing:progress",
160
+ PROCESSING_COMPLETE: "markupr:processing:complete",
161
+ // ---------------------------------------------------------------------------
162
+ // Navigation Channels (Main -> Renderer)
163
+ // ---------------------------------------------------------------------------
164
+ SHOW_ONBOARDING: "markupr:show-onboarding",
165
+ SHOW_SETTINGS: "markupr:show-settings",
166
+ SHOW_HISTORY: "markupr:show-history",
167
+ SHOW_EXPORT: "markupr:show-export",
168
+ SHOW_SHORTCUTS: "markupr:show-shortcuts",
169
+ SHOW_WINDOW_SELECTOR: "markupr:show-window-selector",
170
+ // ---------------------------------------------------------------------------
171
+ // App Channels (Renderer -> Main)
172
+ // ---------------------------------------------------------------------------
173
+ APP_VERSION: "markupr:app:version",
174
+ // ---------------------------------------------------------------------------
175
+ // Popover Channels (Renderer -> Main)
176
+ // ---------------------------------------------------------------------------
177
+ POPOVER_RESIZE: "markupr:popover:resize",
178
+ POPOVER_RESIZE_TO_STATE: "markupr:popover:resize-to-state",
179
+ // ---------------------------------------------------------------------------
180
+ // Legacy channels (backwards compatibility)
181
+ // ---------------------------------------------------------------------------
182
+ START_SESSION: "session:start",
183
+ STOP_SESSION: "session:stop",
184
+ GET_SETTINGS: "settings:get",
185
+ SET_SETTINGS: "settings:set"
186
+ };
187
+ function createEventSubscriber(channel) {
188
+ return (callback) => {
189
+ const handler = (_, data) => callback(data);
190
+ ipcRenderer.on(channel, handler);
191
+ return () => ipcRenderer.removeListener(channel, handler);
192
+ };
193
+ }
194
+ const markuprApi = {
195
+ // ===========================================================================
196
+ // Session API
197
+ // ===========================================================================
198
+ session: {
199
+ /**
200
+ * Start a recording session
201
+ * @param sourceId - ID of the capture source (screen or window)
202
+ */
203
+ start: (sourceId, sourceName) => {
204
+ return ipcRenderer.invoke(IPC_CHANNELS.SESSION_START, sourceId, sourceName);
205
+ },
206
+ /**
207
+ * Stop the current recording session
208
+ */
209
+ stop: () => {
210
+ return ipcRenderer.invoke(IPC_CHANNELS.SESSION_STOP);
211
+ },
212
+ /**
213
+ * Pause the current recording session
214
+ */
215
+ pause: () => {
216
+ return ipcRenderer.invoke(IPC_CHANNELS.SESSION_PAUSE);
217
+ },
218
+ /**
219
+ * Resume a paused recording session
220
+ */
221
+ resume: () => {
222
+ return ipcRenderer.invoke(IPC_CHANNELS.SESSION_RESUME);
223
+ },
224
+ /**
225
+ * Cancel the current session without saving
226
+ */
227
+ cancel: () => {
228
+ return ipcRenderer.invoke(IPC_CHANNELS.SESSION_CANCEL);
229
+ },
230
+ /**
231
+ * Get current session status
232
+ */
233
+ getStatus: () => {
234
+ return ipcRenderer.invoke(IPC_CHANNELS.SESSION_GET_STATUS);
235
+ },
236
+ /**
237
+ * Get current session data
238
+ */
239
+ getCurrent: () => {
240
+ return ipcRenderer.invoke(IPC_CHANNELS.SESSION_GET_CURRENT);
241
+ },
242
+ /**
243
+ * Subscribe to session state changes
244
+ */
245
+ onStateChange: createEventSubscriber(IPC_CHANNELS.SESSION_STATE_CHANGED),
246
+ /**
247
+ * Subscribe to session status updates (periodic updates during recording)
248
+ */
249
+ onStatusUpdate: createEventSubscriber(IPC_CHANNELS.SESSION_STATUS),
250
+ /**
251
+ * Subscribe to session completion
252
+ */
253
+ onComplete: createEventSubscriber(IPC_CHANNELS.SESSION_COMPLETE),
254
+ /**
255
+ * Subscribe to new feedback items
256
+ */
257
+ onFeedbackItem: createEventSubscriber(IPC_CHANNELS.SESSION_FEEDBACK_ITEM),
258
+ /**
259
+ * Subscribe to voice activity changes
260
+ */
261
+ onVoiceActivity: createEventSubscriber(IPC_CHANNELS.SESSION_VOICE_ACTIVITY),
262
+ /**
263
+ * Subscribe to session errors
264
+ */
265
+ onError: createEventSubscriber(IPC_CHANNELS.SESSION_ERROR)
266
+ },
267
+ // ===========================================================================
268
+ // Capture API
269
+ // ===========================================================================
270
+ capture: {
271
+ /**
272
+ * Get available capture sources (screens and windows)
273
+ */
274
+ getSources: () => {
275
+ return ipcRenderer.invoke(IPC_CHANNELS.CAPTURE_GET_SOURCES);
276
+ },
277
+ /**
278
+ * Trigger a manual screenshot during recording
279
+ */
280
+ manualScreenshot: () => {
281
+ return ipcRenderer.invoke(IPC_CHANNELS.CAPTURE_MANUAL_SCREENSHOT);
282
+ },
283
+ /**
284
+ * Subscribe to screenshot captured events
285
+ */
286
+ onScreenshot: createEventSubscriber(IPC_CHANNELS.SCREENSHOT_CAPTURED),
287
+ /**
288
+ * Subscribe to manual screenshot trigger events (from hotkey)
289
+ */
290
+ onManualTrigger: createEventSubscriber(IPC_CHANNELS.MANUAL_SCREENSHOT)
291
+ },
292
+ // ===========================================================================
293
+ // Audio API
294
+ // ===========================================================================
295
+ audio: {
296
+ /**
297
+ * Get available audio input devices
298
+ * Note: Device enumeration happens in renderer via Web Audio API
299
+ */
300
+ getDevices: () => {
301
+ return ipcRenderer.invoke(IPC_CHANNELS.AUDIO_GET_DEVICES);
302
+ },
303
+ /**
304
+ * Set the preferred audio input device
305
+ */
306
+ setDevice: (deviceId) => {
307
+ return ipcRenderer.invoke(IPC_CHANNELS.AUDIO_SET_DEVICE, deviceId);
308
+ },
309
+ /**
310
+ * Subscribe to audio level updates (for visualization)
311
+ */
312
+ onLevel: createEventSubscriber(IPC_CHANNELS.AUDIO_LEVEL),
313
+ /**
314
+ * Subscribe to voice activity detection updates
315
+ */
316
+ onVoiceActivity: createEventSubscriber(IPC_CHANNELS.AUDIO_VOICE_ACTIVITY),
317
+ // -------------------------------------------------------------------------
318
+ // Audio Capture Bridge (Renderer -> Main communication)
319
+ // These are used by the AudioCaptureRenderer to communicate with AudioCapture in main
320
+ // -------------------------------------------------------------------------
321
+ /**
322
+ * Respond to device enumeration request from main
323
+ */
324
+ onRequestDevices: (callback) => {
325
+ const handler = () => callback();
326
+ ipcRenderer.on(IPC_CHANNELS.AUDIO_REQUEST_DEVICES, handler);
327
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.AUDIO_REQUEST_DEVICES, handler);
328
+ },
329
+ /**
330
+ * Send device list back to main
331
+ */
332
+ sendDevices: (devices) => {
333
+ ipcRenderer.send(IPC_CHANNELS.AUDIO_DEVICES_RESPONSE, devices);
334
+ },
335
+ /**
336
+ * Handle start capture command from main
337
+ */
338
+ onStartCapture: (callback) => {
339
+ const handler = (_, config) => callback(config);
340
+ ipcRenderer.on(IPC_CHANNELS.AUDIO_START_CAPTURE, handler);
341
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.AUDIO_START_CAPTURE, handler);
342
+ },
343
+ /**
344
+ * Handle stop capture command from main
345
+ */
346
+ onStopCapture: (callback) => {
347
+ const handler = () => callback();
348
+ ipcRenderer.on(IPC_CHANNELS.AUDIO_STOP_CAPTURE, handler);
349
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.AUDIO_STOP_CAPTURE, handler);
350
+ },
351
+ /**
352
+ * Handle device change command from main
353
+ */
354
+ onSetDevice: (callback) => {
355
+ const handler = (_, deviceId) => callback(deviceId);
356
+ ipcRenderer.on(IPC_CHANNELS.AUDIO_SET_DEVICE, handler);
357
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.AUDIO_SET_DEVICE, handler);
358
+ },
359
+ /**
360
+ * Send audio chunk to main for transcription
361
+ */
362
+ sendAudioChunk: (data) => {
363
+ ipcRenderer.send(IPC_CHANNELS.AUDIO_CHUNK, data);
364
+ },
365
+ /**
366
+ * Notify main that capture started
367
+ */
368
+ notifyCaptureStarted: () => {
369
+ ipcRenderer.send(IPC_CHANNELS.AUDIO_CAPTURE_STARTED);
370
+ },
371
+ /**
372
+ * Notify main that capture stopped
373
+ */
374
+ notifyCaptureStopped: () => {
375
+ ipcRenderer.send(IPC_CHANNELS.AUDIO_CAPTURE_STOPPED);
376
+ },
377
+ /**
378
+ * Send capture error to main
379
+ */
380
+ sendCaptureError: (error) => {
381
+ ipcRenderer.send(IPC_CHANNELS.AUDIO_CAPTURE_ERROR, error);
382
+ }
383
+ },
384
+ // ===========================================================================
385
+ // Screen Recording API
386
+ // ===========================================================================
387
+ screenRecording: {
388
+ /**
389
+ * Start persisted screen recording for a session
390
+ */
391
+ start: (sessionId, mimeType, startTime) => {
392
+ return ipcRenderer.invoke(IPC_CHANNELS.SCREEN_RECORDING_START, sessionId, mimeType, startTime);
393
+ },
394
+ /**
395
+ * Append a video chunk to the active recording file
396
+ */
397
+ appendChunk: (sessionId, chunk) => {
398
+ return ipcRenderer.invoke(IPC_CHANNELS.SCREEN_RECORDING_CHUNK, sessionId, chunk);
399
+ },
400
+ /**
401
+ * Finalize persisted recording for a session
402
+ */
403
+ stop: (sessionId) => {
404
+ return ipcRenderer.invoke(IPC_CHANNELS.SCREEN_RECORDING_STOP, sessionId);
405
+ }
406
+ },
407
+ // ===========================================================================
408
+ // Transcription API
409
+ // ===========================================================================
410
+ transcript: {
411
+ /**
412
+ * Subscribe to transcription chunks (interim and final)
413
+ */
414
+ onChunk: createEventSubscriber(IPC_CHANNELS.TRANSCRIPTION_UPDATE),
415
+ /**
416
+ * Subscribe to final transcription results
417
+ */
418
+ onFinal: createEventSubscriber(IPC_CHANNELS.TRANSCRIPTION_FINAL)
419
+ },
420
+ // ===========================================================================
421
+ // Processing Pipeline API (Main -> Renderer events)
422
+ // ===========================================================================
423
+ processing: {
424
+ /**
425
+ * Subscribe to post-process pipeline progress updates
426
+ */
427
+ onProgress: createEventSubscriber(IPC_CHANNELS.PROCESSING_PROGRESS),
428
+ /**
429
+ * Subscribe to post-process pipeline completion
430
+ */
431
+ onComplete: createEventSubscriber(IPC_CHANNELS.PROCESSING_COMPLETE)
432
+ },
433
+ // ===========================================================================
434
+ // Transcription Control API
435
+ // ===========================================================================
436
+ transcription: {
437
+ /**
438
+ * Get runtime availability for all transcription tiers.
439
+ */
440
+ getTierStatuses: () => {
441
+ return ipcRenderer.invoke(IPC_CHANNELS.TRANSCRIPTION_GET_TIER_STATUSES);
442
+ },
443
+ /**
444
+ * Get current preferred/active transcription tier.
445
+ */
446
+ getCurrentTier: () => {
447
+ return ipcRenderer.invoke(IPC_CHANNELS.TRANSCRIPTION_GET_CURRENT_TIER);
448
+ },
449
+ /**
450
+ * Set preferred transcription tier.
451
+ */
452
+ setTier: (tier) => {
453
+ return ipcRenderer.invoke(IPC_CHANNELS.TRANSCRIPTION_SET_TIER, tier);
454
+ },
455
+ /**
456
+ * Download a specific Whisper model via transcription controls.
457
+ */
458
+ downloadModel: (model) => {
459
+ return ipcRenderer.invoke(IPC_CHANNELS.WHISPER_DOWNLOAD_MODEL, model);
460
+ },
461
+ /**
462
+ * Cancel Whisper model download via transcription controls.
463
+ */
464
+ cancelDownload: (model) => {
465
+ return ipcRenderer.invoke(IPC_CHANNELS.WHISPER_CANCEL_DOWNLOAD, model);
466
+ },
467
+ /**
468
+ * Subscribe to Whisper model progress updates.
469
+ */
470
+ onModelProgress: createEventSubscriber(
471
+ IPC_CHANNELS.WHISPER_DOWNLOAD_PROGRESS
472
+ )
473
+ },
474
+ // ===========================================================================
475
+ // Settings API
476
+ // ===========================================================================
477
+ settings: {
478
+ /**
479
+ * Get a specific setting
480
+ */
481
+ get: (key) => {
482
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_GET, key);
483
+ },
484
+ /**
485
+ * Get all settings
486
+ */
487
+ getAll: () => {
488
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_GET_ALL);
489
+ },
490
+ /**
491
+ * Set a specific setting
492
+ */
493
+ set: (key, value) => {
494
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_SET, key, value);
495
+ },
496
+ /**
497
+ * Get an API key from secure storage
498
+ */
499
+ getApiKey: (service) => {
500
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_GET_API_KEY, service);
501
+ },
502
+ /**
503
+ * Set an API key in secure storage
504
+ */
505
+ setApiKey: (service, key) => {
506
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_SET_API_KEY, service, key);
507
+ },
508
+ /**
509
+ * Delete an API key from secure storage
510
+ */
511
+ deleteApiKey: (service) => {
512
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_DELETE_API_KEY, service);
513
+ },
514
+ /**
515
+ * Check if an API key exists in secure storage
516
+ */
517
+ hasApiKey: (service) => {
518
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_HAS_API_KEY, service);
519
+ },
520
+ /**
521
+ * Validate an API key by performing a provider request from main process.
522
+ */
523
+ testApiKey: (service, key) => {
524
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_TEST_API_KEY, service, key);
525
+ },
526
+ /**
527
+ * Open native directory picker for output path selection
528
+ */
529
+ selectDirectory: () => {
530
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_SELECT_DIRECTORY);
531
+ },
532
+ /**
533
+ * Clear app data and reset settings
534
+ */
535
+ clearAllData: () => {
536
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_CLEAR_ALL_DATA);
537
+ },
538
+ /**
539
+ * Export settings to a JSON file
540
+ */
541
+ export: () => {
542
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_EXPORT);
543
+ },
544
+ /**
545
+ * Import settings from a JSON file
546
+ */
547
+ import: () => {
548
+ return ipcRenderer.invoke(IPC_CHANNELS.SETTINGS_IMPORT);
549
+ }
550
+ },
551
+ // ===========================================================================
552
+ // Hotkey API
553
+ // ===========================================================================
554
+ hotkeys: {
555
+ /**
556
+ * Get current hotkey configuration
557
+ */
558
+ getConfig: () => {
559
+ return ipcRenderer.invoke(IPC_CHANNELS.HOTKEY_CONFIG);
560
+ },
561
+ /**
562
+ * Update hotkey configuration
563
+ */
564
+ updateConfig: (config) => {
565
+ return ipcRenderer.invoke(IPC_CHANNELS.HOTKEY_UPDATE, config);
566
+ },
567
+ /**
568
+ * Subscribe to hotkey triggered events
569
+ */
570
+ onTriggered: createEventSubscriber(
571
+ IPC_CHANNELS.HOTKEY_TRIGGERED
572
+ )
573
+ },
574
+ // ===========================================================================
575
+ // Permissions API
576
+ // ===========================================================================
577
+ permissions: {
578
+ /**
579
+ * Check if a permission is granted
580
+ */
581
+ check: (type) => {
582
+ return ipcRenderer.invoke(IPC_CHANNELS.PERMISSIONS_CHECK, type);
583
+ },
584
+ /**
585
+ * Request a permission
586
+ */
587
+ request: (type) => {
588
+ return ipcRenderer.invoke(IPC_CHANNELS.PERMISSIONS_REQUEST, type);
589
+ },
590
+ /**
591
+ * Get all permission statuses
592
+ */
593
+ getAll: () => {
594
+ return ipcRenderer.invoke(IPC_CHANNELS.PERMISSIONS_GET_ALL);
595
+ }
596
+ },
597
+ // ===========================================================================
598
+ // Output API
599
+ // ===========================================================================
600
+ output: {
601
+ /**
602
+ * Save the current session to disk
603
+ */
604
+ save: (session) => {
605
+ return ipcRenderer.invoke(IPC_CHANNELS.OUTPUT_SAVE, session);
606
+ },
607
+ /**
608
+ * Copy session summary to clipboard
609
+ */
610
+ copyClipboard: () => {
611
+ return ipcRenderer.invoke(IPC_CHANNELS.OUTPUT_COPY_CLIPBOARD);
612
+ },
613
+ /**
614
+ * Open the session output folder in file explorer
615
+ */
616
+ openFolder: (sessionDir) => {
617
+ return ipcRenderer.invoke(IPC_CHANNELS.OUTPUT_OPEN_FOLDER, sessionDir);
618
+ },
619
+ /**
620
+ * Subscribe to output ready events
621
+ */
622
+ onReady: createEventSubscriber(
623
+ IPC_CHANNELS.OUTPUT_READY
624
+ ),
625
+ /**
626
+ * Subscribe to output error events
627
+ */
628
+ onError: createEventSubscriber(IPC_CHANNELS.OUTPUT_ERROR),
629
+ // -------------------------------------------------------------------------
630
+ // Session History Browser API
631
+ // -------------------------------------------------------------------------
632
+ /**
633
+ * List all saved sessions
634
+ */
635
+ listSessions: () => {
636
+ return ipcRenderer.invoke(IPC_CHANNELS.OUTPUT_LIST_SESSIONS);
637
+ },
638
+ /**
639
+ * Get metadata for a specific session
640
+ */
641
+ getSessionMetadata: (sessionId) => {
642
+ return ipcRenderer.invoke(IPC_CHANNELS.OUTPUT_GET_SESSION_METADATA, sessionId);
643
+ },
644
+ /**
645
+ * Delete a single session
646
+ */
647
+ deleteSession: (sessionId) => {
648
+ return ipcRenderer.invoke(IPC_CHANNELS.OUTPUT_DELETE_SESSION, sessionId);
649
+ },
650
+ /**
651
+ * Delete multiple sessions
652
+ */
653
+ deleteSessions: (sessionIds) => {
654
+ return ipcRenderer.invoke(IPC_CHANNELS.OUTPUT_DELETE_SESSIONS, sessionIds);
655
+ },
656
+ /**
657
+ * Export a single session
658
+ */
659
+ exportSession: (sessionId, format = "markdown") => {
660
+ return ipcRenderer.invoke(IPC_CHANNELS.OUTPUT_EXPORT_SESSION, sessionId, format);
661
+ },
662
+ /**
663
+ * Export multiple sessions
664
+ */
665
+ exportSessions: (sessionIds, format = "markdown") => {
666
+ return ipcRenderer.invoke(IPC_CHANNELS.OUTPUT_EXPORT_SESSIONS, sessionIds, format);
667
+ }
668
+ },
669
+ // ===========================================================================
670
+ // Crash Recovery API
671
+ // ===========================================================================
672
+ crashRecovery: {
673
+ /**
674
+ * Check for incomplete sessions from crashes
675
+ */
676
+ check: () => {
677
+ return ipcRenderer.invoke(IPC_CHANNELS.CRASH_RECOVERY_CHECK);
678
+ },
679
+ /**
680
+ * Recover an incomplete session
681
+ */
682
+ recover: (sessionId) => {
683
+ return ipcRenderer.invoke(IPC_CHANNELS.CRASH_RECOVERY_RECOVER, sessionId);
684
+ },
685
+ /**
686
+ * Discard an incomplete session
687
+ */
688
+ discard: () => {
689
+ return ipcRenderer.invoke(IPC_CHANNELS.CRASH_RECOVERY_DISCARD);
690
+ },
691
+ /**
692
+ * Get recent crash logs for debugging
693
+ */
694
+ getLogs: (limit) => {
695
+ return ipcRenderer.invoke(IPC_CHANNELS.CRASH_RECOVERY_GET_LOGS, limit);
696
+ },
697
+ /**
698
+ * Clear crash logs
699
+ */
700
+ clearLogs: () => {
701
+ return ipcRenderer.invoke(IPC_CHANNELS.CRASH_RECOVERY_CLEAR_LOGS);
702
+ },
703
+ /**
704
+ * Update crash recovery settings
705
+ */
706
+ updateSettings: (settings) => {
707
+ return ipcRenderer.invoke(IPC_CHANNELS.CRASH_RECOVERY_UPDATE_SETTINGS, settings);
708
+ },
709
+ /**
710
+ * Subscribe to incomplete session found events (on startup)
711
+ */
712
+ onIncompleteFound: createEventSubscriber(IPC_CHANNELS.CRASH_RECOVERY_FOUND)
713
+ },
714
+ // ===========================================================================
715
+ // Updates API
716
+ // ===========================================================================
717
+ updates: {
718
+ /**
719
+ * Check for available updates
720
+ */
721
+ check: () => {
722
+ return ipcRenderer.invoke(IPC_CHANNELS.UPDATE_CHECK);
723
+ },
724
+ /**
725
+ * Download the available update
726
+ */
727
+ download: () => {
728
+ return ipcRenderer.invoke(IPC_CHANNELS.UPDATE_DOWNLOAD);
729
+ },
730
+ /**
731
+ * Install the downloaded update (quits and restarts app)
732
+ */
733
+ install: () => {
734
+ return ipcRenderer.invoke(IPC_CHANNELS.UPDATE_INSTALL);
735
+ },
736
+ /**
737
+ * Subscribe to update status changes
738
+ */
739
+ onStatus: (callback) => {
740
+ const handler = (_, status) => callback(status);
741
+ ipcRenderer.on(IPC_CHANNELS.UPDATE_STATUS, handler);
742
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.UPDATE_STATUS, handler);
743
+ }
744
+ },
745
+ // ===========================================================================
746
+ // Whisper Model API
747
+ // ===========================================================================
748
+ whisper: {
749
+ /**
750
+ * Check if any Whisper model is downloaded and get recommended model
751
+ */
752
+ checkModel: () => {
753
+ return ipcRenderer.invoke(IPC_CHANNELS.WHISPER_CHECK_MODEL);
754
+ },
755
+ /**
756
+ * Check if we have any tier that can actually transcribe
757
+ * (OpenAI key or Whisper with model)
758
+ */
759
+ hasTranscriptionCapability: () => {
760
+ return ipcRenderer.invoke(IPC_CHANNELS.WHISPER_HAS_TRANSCRIPTION_CAPABILITY);
761
+ },
762
+ /**
763
+ * Get available models with their info
764
+ */
765
+ getAvailableModels: () => {
766
+ return ipcRenderer.invoke(IPC_CHANNELS.WHISPER_GET_AVAILABLE_MODELS);
767
+ },
768
+ /**
769
+ * Download a specific Whisper model
770
+ * @param model - Model name: 'tiny', 'base', 'small', 'medium', or 'large'
771
+ */
772
+ downloadModel: (model) => {
773
+ return ipcRenderer.invoke(IPC_CHANNELS.WHISPER_DOWNLOAD_MODEL, model);
774
+ },
775
+ /**
776
+ * Cancel an active download
777
+ * @param model - Model name to cancel
778
+ */
779
+ cancelDownload: (model) => {
780
+ return ipcRenderer.invoke(IPC_CHANNELS.WHISPER_CANCEL_DOWNLOAD, model);
781
+ },
782
+ /**
783
+ * Subscribe to download progress events
784
+ */
785
+ onDownloadProgress: createEventSubscriber(
786
+ IPC_CHANNELS.WHISPER_DOWNLOAD_PROGRESS
787
+ ),
788
+ /**
789
+ * Subscribe to download complete events
790
+ */
791
+ onDownloadComplete: createEventSubscriber(
792
+ IPC_CHANNELS.WHISPER_DOWNLOAD_COMPLETE
793
+ ),
794
+ /**
795
+ * Subscribe to download error events
796
+ */
797
+ onDownloadError: createEventSubscriber(
798
+ IPC_CHANNELS.WHISPER_DOWNLOAD_ERROR
799
+ )
800
+ },
801
+ // ===========================================================================
802
+ // App Version
803
+ // ===========================================================================
804
+ version: () => {
805
+ return ipcRenderer.invoke(IPC_CHANNELS.APP_VERSION);
806
+ },
807
+ // ===========================================================================
808
+ // Legacy API (for backwards compatibility)
809
+ // ===========================================================================
810
+ startSession: () => {
811
+ return ipcRenderer.invoke(IPC_CHANNELS.START_SESSION);
812
+ },
813
+ stopSession: () => {
814
+ return ipcRenderer.invoke(IPC_CHANNELS.STOP_SESSION);
815
+ },
816
+ getSettings: () => {
817
+ return ipcRenderer.invoke(IPC_CHANNELS.GET_SETTINGS);
818
+ },
819
+ setSettings: (settings) => {
820
+ return ipcRenderer.invoke(IPC_CHANNELS.SET_SETTINGS, settings);
821
+ },
822
+ copyToClipboard: (text) => {
823
+ return ipcRenderer.invoke(IPC_CHANNELS.COPY_TO_CLIPBOARD, text);
824
+ },
825
+ // Window controls
826
+ window: {
827
+ minimize: () => {
828
+ return ipcRenderer.invoke(IPC_CHANNELS.WINDOW_MINIMIZE);
829
+ },
830
+ hide: () => {
831
+ return ipcRenderer.invoke(IPC_CHANNELS.WINDOW_HIDE);
832
+ },
833
+ close: () => {
834
+ return ipcRenderer.invoke(IPC_CHANNELS.WINDOW_CLOSE);
835
+ }
836
+ },
837
+ // Popover controls
838
+ popover: {
839
+ resize: (width, height) => {
840
+ return ipcRenderer.invoke(IPC_CHANNELS.POPOVER_RESIZE, width, height);
841
+ },
842
+ resizeToState: (state) => {
843
+ return ipcRenderer.invoke(IPC_CHANNELS.POPOVER_RESIZE_TO_STATE, state);
844
+ }
845
+ },
846
+ // ===========================================================================
847
+ // Navigation API (Main -> Renderer navigation events)
848
+ // ===========================================================================
849
+ navigation: {
850
+ onShowSettings: (callback) => {
851
+ const handler = () => callback();
852
+ ipcRenderer.on(IPC_CHANNELS.SHOW_SETTINGS, handler);
853
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.SHOW_SETTINGS, handler);
854
+ },
855
+ onShowHistory: (callback) => {
856
+ const handler = () => callback();
857
+ ipcRenderer.on(IPC_CHANNELS.SHOW_HISTORY, handler);
858
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.SHOW_HISTORY, handler);
859
+ },
860
+ onShowShortcuts: (callback) => {
861
+ const handler = () => callback();
862
+ ipcRenderer.on(IPC_CHANNELS.SHOW_SHORTCUTS, handler);
863
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.SHOW_SHORTCUTS, handler);
864
+ },
865
+ onShowOnboarding: (callback) => {
866
+ const handler = () => callback();
867
+ ipcRenderer.on(IPC_CHANNELS.SHOW_ONBOARDING, handler);
868
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.SHOW_ONBOARDING, handler);
869
+ },
870
+ onShowExport: (callback) => {
871
+ const handler = () => callback();
872
+ ipcRenderer.on(IPC_CHANNELS.SHOW_EXPORT, handler);
873
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.SHOW_EXPORT, handler);
874
+ },
875
+ onShowWindowSelector: (callback) => {
876
+ const handler = () => callback();
877
+ ipcRenderer.on(IPC_CHANNELS.SHOW_WINDOW_SELECTOR, handler);
878
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.SHOW_WINDOW_SELECTOR, handler);
879
+ }
880
+ },
881
+ onSessionStatus: (callback) => {
882
+ const handler = (_, data) => callback(data);
883
+ ipcRenderer.on(IPC_CHANNELS.SESSION_STATUS, handler);
884
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.SESSION_STATUS, handler);
885
+ },
886
+ onTranscriptionUpdate: (callback) => {
887
+ const handler = (_, data) => callback(data);
888
+ ipcRenderer.on(IPC_CHANNELS.TRANSCRIPTION_UPDATE, handler);
889
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.TRANSCRIPTION_UPDATE, handler);
890
+ },
891
+ onScreenshotCaptured: (callback) => {
892
+ const handler = (_, data) => callback(data);
893
+ ipcRenderer.on(IPC_CHANNELS.SCREENSHOT_CAPTURED, handler);
894
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.SCREENSHOT_CAPTURED, handler);
895
+ },
896
+ onOutputReady: (callback) => {
897
+ const handler = (_, data) => callback(data);
898
+ ipcRenderer.on(IPC_CHANNELS.OUTPUT_READY, handler);
899
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.OUTPUT_READY, handler);
900
+ },
901
+ onOutputError: (callback) => {
902
+ const handler = (_, error) => callback(error);
903
+ ipcRenderer.on(IPC_CHANNELS.OUTPUT_ERROR, handler);
904
+ return () => ipcRenderer.removeListener(IPC_CHANNELS.OUTPUT_ERROR, handler);
905
+ }
906
+ };
907
+ contextBridge.exposeInMainWorld("markupr", markuprApi);