markupr 2.1.8 → 2.5.0

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 (300) hide show
  1. package/README.md +292 -15
  2. package/dist/cli/index.mjs +3593 -0
  3. package/dist/main/index.mjs +743 -220
  4. package/dist/mcp/index.mjs +4053 -0
  5. package/package.json +32 -7
  6. package/.claude/commands/review-feedback.md +0 -47
  7. package/.eslintrc.json +0 -35
  8. package/.github/CODEOWNERS +0 -16
  9. package/.github/FUNDING.yml +0 -1
  10. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -56
  11. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -54
  12. package/.github/PULL_REQUEST_TEMPLATE.md +0 -89
  13. package/.github/dependabot.yml +0 -70
  14. package/.github/workflows/ci.yml +0 -184
  15. package/.github/workflows/deploy-landing.yml +0 -134
  16. package/.github/workflows/nightly.yml +0 -288
  17. package/.github/workflows/release.yml +0 -318
  18. package/CHANGELOG.md +0 -127
  19. package/CLAUDE.md +0 -137
  20. package/CODE_OF_CONDUCT.md +0 -9
  21. package/CONTRIBUTING.md +0 -390
  22. package/PRODUCT_VISION.md +0 -277
  23. package/SECURITY.md +0 -51
  24. package/SIGNING_INSTRUCTIONS.md +0 -284
  25. package/assets/DMG_BACKGROUND_INSTRUCTIONS.md +0 -130
  26. package/assets/svg-source/dmg-background.svg +0 -70
  27. package/assets/svg-source/icon.svg +0 -20
  28. package/assets/svg-source/tray-icon-processing.svg +0 -7
  29. package/assets/svg-source/tray-icon-recording.svg +0 -7
  30. package/assets/svg-source/tray-icon.svg +0 -6
  31. package/assets/tray-complete.png +0 -0
  32. package/assets/tray-complete@2x.png +0 -0
  33. package/assets/tray-completeTemplate.png +0 -0
  34. package/assets/tray-completeTemplate@2x.png +0 -0
  35. package/assets/tray-error.png +0 -0
  36. package/assets/tray-error@2x.png +0 -0
  37. package/assets/tray-errorTemplate.png +0 -0
  38. package/assets/tray-errorTemplate@2x.png +0 -0
  39. package/assets/tray-icon-processing.png +0 -0
  40. package/assets/tray-icon-processing@2x.png +0 -0
  41. package/assets/tray-icon-processingTemplate.png +0 -0
  42. package/assets/tray-icon-processingTemplate@2x.png +0 -0
  43. package/assets/tray-icon-recording.png +0 -0
  44. package/assets/tray-icon-recording@2x.png +0 -0
  45. package/assets/tray-icon-recordingTemplate.png +0 -0
  46. package/assets/tray-icon-recordingTemplate@2x.png +0 -0
  47. package/assets/tray-icon.png +0 -0
  48. package/assets/tray-icon@2x.png +0 -0
  49. package/assets/tray-iconTemplate.png +0 -0
  50. package/assets/tray-iconTemplate@2x.png +0 -0
  51. package/assets/tray-idle.png +0 -0
  52. package/assets/tray-idle@2x.png +0 -0
  53. package/assets/tray-idleTemplate.png +0 -0
  54. package/assets/tray-idleTemplate@2x.png +0 -0
  55. package/assets/tray-processing-0.png +0 -0
  56. package/assets/tray-processing-0@2x.png +0 -0
  57. package/assets/tray-processing-0Template.png +0 -0
  58. package/assets/tray-processing-0Template@2x.png +0 -0
  59. package/assets/tray-processing-1.png +0 -0
  60. package/assets/tray-processing-1@2x.png +0 -0
  61. package/assets/tray-processing-1Template.png +0 -0
  62. package/assets/tray-processing-1Template@2x.png +0 -0
  63. package/assets/tray-processing-2.png +0 -0
  64. package/assets/tray-processing-2@2x.png +0 -0
  65. package/assets/tray-processing-2Template.png +0 -0
  66. package/assets/tray-processing-2Template@2x.png +0 -0
  67. package/assets/tray-processing-3.png +0 -0
  68. package/assets/tray-processing-3@2x.png +0 -0
  69. package/assets/tray-processing-3Template.png +0 -0
  70. package/assets/tray-processing-3Template@2x.png +0 -0
  71. package/assets/tray-processing.png +0 -0
  72. package/assets/tray-processing@2x.png +0 -0
  73. package/assets/tray-processingTemplate.png +0 -0
  74. package/assets/tray-processingTemplate@2x.png +0 -0
  75. package/assets/tray-recording.png +0 -0
  76. package/assets/tray-recording@2x.png +0 -0
  77. package/assets/tray-recordingTemplate.png +0 -0
  78. package/assets/tray-recordingTemplate@2x.png +0 -0
  79. package/build/DMG_BACKGROUND_SPEC.md +0 -50
  80. package/build/dmg-background.png +0 -0
  81. package/build/dmg-background@2x.png +0 -0
  82. package/build/entitlements.mac.inherit.plist +0 -27
  83. package/build/entitlements.mac.plist +0 -41
  84. package/build/favicon-16.png +0 -0
  85. package/build/favicon-180.png +0 -0
  86. package/build/favicon-192.png +0 -0
  87. package/build/favicon-32.png +0 -0
  88. package/build/favicon-48.png +0 -0
  89. package/build/favicon-512.png +0 -0
  90. package/build/favicon-64.png +0 -0
  91. package/build/icon-128.png +0 -0
  92. package/build/icon-16.png +0 -0
  93. package/build/icon-24.png +0 -0
  94. package/build/icon-256.png +0 -0
  95. package/build/icon-32.png +0 -0
  96. package/build/icon-48.png +0 -0
  97. package/build/icon-64.png +0 -0
  98. package/build/icon.icns +0 -0
  99. package/build/icon.ico +0 -0
  100. package/build/icon.iconset/icon_128x128.png +0 -0
  101. package/build/icon.iconset/icon_128x128@2x.png +0 -0
  102. package/build/icon.iconset/icon_16x16.png +0 -0
  103. package/build/icon.iconset/icon_16x16@2x.png +0 -0
  104. package/build/icon.iconset/icon_256x256.png +0 -0
  105. package/build/icon.iconset/icon_256x256@2x.png +0 -0
  106. package/build/icon.iconset/icon_32x32.png +0 -0
  107. package/build/icon.iconset/icon_32x32@2x.png +0 -0
  108. package/build/icon.iconset/icon_512x512.png +0 -0
  109. package/build/icon.iconset/icon_512x512@2x.png +0 -0
  110. package/build/icon.png +0 -0
  111. package/build/installer-header.bmp +0 -0
  112. package/build/installer-header.png +0 -0
  113. package/build/installer-sidebar.bmp +0 -0
  114. package/build/installer-sidebar.png +0 -0
  115. package/build/installer.nsh +0 -45
  116. package/build/overlay-processing.png +0 -0
  117. package/build/overlay-recording.png +0 -0
  118. package/build/toolbar-record.png +0 -0
  119. package/build/toolbar-screenshot.png +0 -0
  120. package/build/toolbar-settings.png +0 -0
  121. package/build/toolbar-stop.png +0 -0
  122. package/dist/preload/index.mjs +0 -907
  123. package/dist/renderer/assets/index-CCmUjl9K.js +0 -19495
  124. package/dist/renderer/assets/index-CUqz_Gs6.css +0 -2270
  125. package/dist/renderer/index.html +0 -27
  126. package/docs/AI_AGENT_QUICKSTART.md +0 -42
  127. package/docs/AI_PIPELINE_DESIGN.md +0 -595
  128. package/docs/API.md +0 -514
  129. package/docs/ARCHITECTURE.md +0 -460
  130. package/docs/CONFIGURATION.md +0 -336
  131. package/docs/DEVELOPMENT.md +0 -508
  132. package/docs/EXPORT_FORMATS.md +0 -451
  133. package/docs/GETTING_STARTED.md +0 -236
  134. package/docs/KEYBOARD_SHORTCUTS.md +0 -334
  135. package/docs/TROUBLESHOOTING.md +0 -418
  136. package/docs/landing/index.html +0 -672
  137. package/docs/landing/script.js +0 -342
  138. package/docs/landing/styles.css +0 -1543
  139. package/electron-builder.yml +0 -140
  140. package/electron.vite.config.ts +0 -63
  141. package/railway.json +0 -12
  142. package/scripts/build.mjs +0 -51
  143. package/scripts/generate-icons.mjs +0 -314
  144. package/scripts/generate-installer-images.cjs +0 -253
  145. package/scripts/generate-tray-icons.mjs +0 -258
  146. package/scripts/notarize.cjs +0 -180
  147. package/scripts/one-click-clean-test.sh +0 -147
  148. package/scripts/postinstall.mjs +0 -36
  149. package/scripts/setup-markupr.sh +0 -55
  150. package/setup +0 -17
  151. package/site/index.html +0 -1835
  152. package/site/package.json +0 -11
  153. package/site/railway.json +0 -12
  154. package/site/server.js +0 -31
  155. package/src/main/AutoUpdater.ts +0 -392
  156. package/src/main/CrashRecovery.ts +0 -655
  157. package/src/main/ErrorHandler.ts +0 -703
  158. package/src/main/HotkeyManager.ts +0 -399
  159. package/src/main/MenuManager.ts +0 -529
  160. package/src/main/PermissionManager.ts +0 -420
  161. package/src/main/SessionController.ts +0 -1465
  162. package/src/main/TrayManager.ts +0 -540
  163. package/src/main/ai/AIPipelineManager.ts +0 -199
  164. package/src/main/ai/ClaudeAnalyzer.ts +0 -339
  165. package/src/main/ai/ImageOptimizer.ts +0 -176
  166. package/src/main/ai/StructuredMarkdownBuilder.ts +0 -379
  167. package/src/main/ai/index.ts +0 -16
  168. package/src/main/ai/types.ts +0 -258
  169. package/src/main/analysis/ClarificationGenerator.ts +0 -385
  170. package/src/main/analysis/FeedbackAnalyzer.ts +0 -531
  171. package/src/main/analysis/index.ts +0 -19
  172. package/src/main/audio/AudioCapture.ts +0 -978
  173. package/src/main/audio/audioUtils.ts +0 -100
  174. package/src/main/audio/index.ts +0 -20
  175. package/src/main/capture/index.ts +0 -1
  176. package/src/main/index.ts +0 -1693
  177. package/src/main/ipc/captureHandlers.ts +0 -272
  178. package/src/main/ipc/index.ts +0 -45
  179. package/src/main/ipc/outputHandlers.ts +0 -302
  180. package/src/main/ipc/sessionHandlers.ts +0 -56
  181. package/src/main/ipc/settingsHandlers.ts +0 -471
  182. package/src/main/ipc/types.ts +0 -56
  183. package/src/main/ipc/windowHandlers.ts +0 -277
  184. package/src/main/output/ClipboardService.ts +0 -369
  185. package/src/main/output/ExportService.ts +0 -539
  186. package/src/main/output/FileManager.ts +0 -416
  187. package/src/main/output/MarkdownGenerator.ts +0 -791
  188. package/src/main/output/MarkdownPatcher.ts +0 -299
  189. package/src/main/output/index.ts +0 -186
  190. package/src/main/output/sessionAdapter.ts +0 -207
  191. package/src/main/output/templates/html-template.ts +0 -553
  192. package/src/main/pipeline/FrameExtractor.ts +0 -330
  193. package/src/main/pipeline/PostProcessor.ts +0 -399
  194. package/src/main/pipeline/TranscriptAnalyzer.ts +0 -226
  195. package/src/main/pipeline/index.ts +0 -36
  196. package/src/main/platform/WindowsTaskbar.ts +0 -600
  197. package/src/main/platform/index.ts +0 -16
  198. package/src/main/settings/SettingsManager.ts +0 -730
  199. package/src/main/settings/index.ts +0 -19
  200. package/src/main/transcription/ModelDownloadManager.ts +0 -494
  201. package/src/main/transcription/TierManager.ts +0 -219
  202. package/src/main/transcription/TranscriptionRecoveryService.ts +0 -340
  203. package/src/main/transcription/WhisperService.ts +0 -748
  204. package/src/main/transcription/index.ts +0 -56
  205. package/src/main/transcription/types.ts +0 -135
  206. package/src/main/windows/PopoverManager.ts +0 -284
  207. package/src/main/windows/TaskbarIntegration.ts +0 -452
  208. package/src/main/windows/index.ts +0 -23
  209. package/src/preload/index.ts +0 -1047
  210. package/src/renderer/App.tsx +0 -515
  211. package/src/renderer/AppWrapper.tsx +0 -28
  212. package/src/renderer/assets/logo-dark.svg +0 -7
  213. package/src/renderer/assets/logo.svg +0 -7
  214. package/src/renderer/audio/AudioCaptureRenderer.ts +0 -454
  215. package/src/renderer/capture/ScreenRecordingRenderer.ts +0 -492
  216. package/src/renderer/components/AnnotationOverlay.tsx +0 -836
  217. package/src/renderer/components/AudioWaveform.tsx +0 -811
  218. package/src/renderer/components/ClarificationQuestions.tsx +0 -656
  219. package/src/renderer/components/CountdownTimer.tsx +0 -495
  220. package/src/renderer/components/CrashRecoveryDialog.tsx +0 -632
  221. package/src/renderer/components/DonateButton.tsx +0 -127
  222. package/src/renderer/components/ErrorBoundary.tsx +0 -308
  223. package/src/renderer/components/ExportDialog.tsx +0 -872
  224. package/src/renderer/components/HotkeyHint.tsx +0 -261
  225. package/src/renderer/components/KeyboardShortcuts.tsx +0 -787
  226. package/src/renderer/components/ModelDownloadDialog.tsx +0 -844
  227. package/src/renderer/components/Onboarding.tsx +0 -1830
  228. package/src/renderer/components/ProcessingOverlay.tsx +0 -157
  229. package/src/renderer/components/RecordingOverlay.tsx +0 -423
  230. package/src/renderer/components/SessionHistory.tsx +0 -1746
  231. package/src/renderer/components/SessionReview.tsx +0 -1321
  232. package/src/renderer/components/SettingsPanel.tsx +0 -217
  233. package/src/renderer/components/Skeleton.tsx +0 -347
  234. package/src/renderer/components/StatusIndicator.tsx +0 -86
  235. package/src/renderer/components/ThemeProvider.tsx +0 -429
  236. package/src/renderer/components/Tooltip.tsx +0 -370
  237. package/src/renderer/components/TranscriptionPreview.tsx +0 -183
  238. package/src/renderer/components/TranscriptionTierSelector.tsx +0 -640
  239. package/src/renderer/components/UpdateNotification.tsx +0 -377
  240. package/src/renderer/components/WindowSelector.tsx +0 -947
  241. package/src/renderer/components/index.ts +0 -99
  242. package/src/renderer/components/primitives/ApiKeyInput.tsx +0 -98
  243. package/src/renderer/components/primitives/ColorPicker.tsx +0 -65
  244. package/src/renderer/components/primitives/DangerButton.tsx +0 -45
  245. package/src/renderer/components/primitives/DirectoryPicker.tsx +0 -41
  246. package/src/renderer/components/primitives/Dropdown.tsx +0 -34
  247. package/src/renderer/components/primitives/KeyRecorder.tsx +0 -117
  248. package/src/renderer/components/primitives/SettingsSection.tsx +0 -32
  249. package/src/renderer/components/primitives/Slider.tsx +0 -43
  250. package/src/renderer/components/primitives/Toggle.tsx +0 -36
  251. package/src/renderer/components/primitives/index.ts +0 -10
  252. package/src/renderer/components/settings/AdvancedTab.tsx +0 -174
  253. package/src/renderer/components/settings/AppearanceTab.tsx +0 -77
  254. package/src/renderer/components/settings/GeneralTab.tsx +0 -40
  255. package/src/renderer/components/settings/HotkeysTab.tsx +0 -79
  256. package/src/renderer/components/settings/RecordingTab.tsx +0 -84
  257. package/src/renderer/components/settings/index.ts +0 -9
  258. package/src/renderer/components/settings/settingsStyles.ts +0 -673
  259. package/src/renderer/components/settings/tabConfig.tsx +0 -85
  260. package/src/renderer/components/settings/useSettingsPanel.ts +0 -447
  261. package/src/renderer/contexts/ProcessingContext.tsx +0 -227
  262. package/src/renderer/contexts/RecordingContext.tsx +0 -683
  263. package/src/renderer/contexts/UIContext.tsx +0 -326
  264. package/src/renderer/contexts/index.ts +0 -24
  265. package/src/renderer/donateMessages.ts +0 -69
  266. package/src/renderer/hooks/index.ts +0 -75
  267. package/src/renderer/hooks/useAnimation.tsx +0 -544
  268. package/src/renderer/hooks/useTheme.ts +0 -313
  269. package/src/renderer/index.html +0 -26
  270. package/src/renderer/main.tsx +0 -52
  271. package/src/renderer/styles/animations.css +0 -1093
  272. package/src/renderer/styles/app-shell.css +0 -662
  273. package/src/renderer/styles/globals.css +0 -515
  274. package/src/renderer/styles/theme.ts +0 -578
  275. package/src/renderer/types/electron.d.ts +0 -385
  276. package/src/shared/hotkeys.ts +0 -283
  277. package/src/shared/types.ts +0 -809
  278. package/tests/clipboard.test.ts +0 -228
  279. package/tests/e2e/criticalPaths.test.ts +0 -594
  280. package/tests/feedbackAnalyzer.test.ts +0 -303
  281. package/tests/integration/sessionFlow.test.ts +0 -583
  282. package/tests/markdownGenerator.test.ts +0 -418
  283. package/tests/output.test.ts +0 -96
  284. package/tests/setup.ts +0 -486
  285. package/tests/unit/appIntegration.test.ts +0 -676
  286. package/tests/unit/appViewState.test.ts +0 -281
  287. package/tests/unit/audioIpcChannels.test.ts +0 -17
  288. package/tests/unit/exportService.test.ts +0 -492
  289. package/tests/unit/hotkeys.test.ts +0 -92
  290. package/tests/unit/navigationPreload.test.ts +0 -94
  291. package/tests/unit/onboardingFlow.test.ts +0 -345
  292. package/tests/unit/permissionManager.test.ts +0 -175
  293. package/tests/unit/permissionManagerExpanded.test.ts +0 -296
  294. package/tests/unit/screenRecordingRenderer.test.ts +0 -368
  295. package/tests/unit/sessionController.test.ts +0 -515
  296. package/tests/unit/tierManager.test.ts +0 -61
  297. package/tests/unit/tierManagerExpanded.test.ts +0 -142
  298. package/tests/unit/transcriptAnalyzer.test.ts +0 -64
  299. package/tsconfig.json +0 -25
  300. package/vitest.config.ts +0 -46
@@ -1,399 +0,0 @@
1
- /**
2
- * HotkeyManager - Global Hotkey Registration for markupr
3
- *
4
- * Handles:
5
- * - Global hotkey registration that works when any app is focused
6
- * - Cross-platform accelerator normalization
7
- * - Conflict detection and fallback handling
8
- * - Hotkey customization via settings
9
- *
10
- * Default hotkeys:
11
- * - Cmd+Shift+F (Ctrl+Shift+F on Windows): Toggle recording
12
- * - Cmd+Shift+S (Ctrl+Shift+S on Windows): Manual screenshot
13
- * - Cmd+Shift+P (Ctrl+Shift+P on Windows): Pause/resume recording
14
- */
15
-
16
- import { globalShortcut, app } from 'electron';
17
- import type { HotkeyConfig } from '../shared/types';
18
-
19
- /**
20
- * Available hotkey actions
21
- */
22
- export type HotkeyAction = 'toggleRecording' | 'manualScreenshot' | 'pauseResume';
23
-
24
- // HotkeyConfig is imported from '../shared/types' (single source of truth)
25
-
26
- /**
27
- * Result of a hotkey registration attempt
28
- */
29
- export interface HotkeyRegistrationResult {
30
- success: boolean;
31
- action: HotkeyAction;
32
- accelerator: string;
33
- fallbackUsed?: string;
34
- error?: string;
35
- }
36
-
37
- /**
38
- * HotkeyManager interface
39
- */
40
- export interface IHotkeyManager {
41
- initialize(): HotkeyRegistrationResult[];
42
- register(action: HotkeyAction, accelerator: string): HotkeyRegistrationResult;
43
- unregister(action: HotkeyAction): void;
44
- unregisterAll(): void;
45
- getAccelerator(action: HotkeyAction): string | undefined;
46
- getConfig(): HotkeyConfig;
47
- isRegistered(action: HotkeyAction): boolean;
48
- onHotkey(callback: (action: HotkeyAction) => void): () => void;
49
- }
50
-
51
- /**
52
- * Default hotkey configuration
53
- * Using CommandOrControl for cross-platform compatibility
54
- */
55
- export const DEFAULT_HOTKEY_CONFIG: HotkeyConfig = {
56
- toggleRecording: 'CommandOrControl+Shift+F',
57
- manualScreenshot: 'CommandOrControl+Shift+S',
58
- pauseResume: 'CommandOrControl+Shift+P',
59
- };
60
-
61
- /**
62
- * Fallback hotkeys if the primary ones are unavailable
63
- */
64
- const FALLBACK_HOTKEYS: Record<HotkeyAction, string[]> = {
65
- toggleRecording: [
66
- 'CommandOrControl+Shift+R',
67
- 'CommandOrControl+Alt+F',
68
- 'CommandOrControl+Alt+R',
69
- ],
70
- manualScreenshot: [
71
- 'CommandOrControl+Shift+P',
72
- 'CommandOrControl+Alt+S',
73
- 'CommandOrControl+Alt+P',
74
- ],
75
- pauseResume: [
76
- 'CommandOrControl+Shift+Space',
77
- 'CommandOrControl+Alt+P',
78
- 'CommandOrControl+Alt+Space',
79
- ],
80
- };
81
-
82
- /**
83
- * HotkeyManager implementation
84
- */
85
- class HotkeyManagerImpl implements IHotkeyManager {
86
- private callbacks: Set<(action: HotkeyAction) => void> = new Set();
87
- private registeredKeys: Map<HotkeyAction, string> = new Map();
88
- private config: HotkeyConfig;
89
- private initialized = false;
90
-
91
- constructor(config?: Partial<HotkeyConfig>) {
92
- this.config = { ...DEFAULT_HOTKEY_CONFIG, ...config };
93
- }
94
-
95
- /**
96
- * Initialize the hotkey manager with default or custom config
97
- * Returns results for each hotkey registration attempt
98
- */
99
- initialize(): HotkeyRegistrationResult[] {
100
- if (this.initialized) {
101
- console.warn('[HotkeyManager] Already initialized, skipping...');
102
- return [];
103
- }
104
-
105
- const results: HotkeyRegistrationResult[] = [];
106
-
107
- // Register toggle recording hotkey
108
- results.push(this.register('toggleRecording', this.config.toggleRecording));
109
-
110
- // Register manual screenshot hotkey
111
- results.push(this.register('manualScreenshot', this.config.manualScreenshot));
112
-
113
- // Register pause/resume hotkey
114
- results.push(this.register('pauseResume', this.config.pauseResume));
115
-
116
- // Setup cleanup on app quit
117
- app.on('will-quit', () => {
118
- this.unregisterAll();
119
- });
120
-
121
- this.initialized = true;
122
- console.log('[HotkeyManager] Initialized with hotkeys:', this.getConfig());
123
-
124
- return results;
125
- }
126
-
127
- /**
128
- * Register a global hotkey for an action
129
- * Will try fallback accelerators if the primary fails
130
- */
131
- register(action: HotkeyAction, accelerator: string): HotkeyRegistrationResult {
132
- // Unregister existing if any
133
- this.unregister(action);
134
-
135
- // Normalize the accelerator
136
- const normalizedAccelerator = this.normalizeAccelerator(accelerator);
137
-
138
- // Try to register the primary accelerator
139
- const success = this.tryRegister(action, normalizedAccelerator);
140
-
141
- if (success) {
142
- return {
143
- success: true,
144
- action,
145
- accelerator: normalizedAccelerator,
146
- };
147
- }
148
-
149
- // Primary failed, try fallbacks
150
- console.warn(
151
- `[HotkeyManager] Failed to register ${normalizedAccelerator} for ${action}, trying fallbacks...`
152
- );
153
-
154
- const fallbacks = FALLBACK_HOTKEYS[action] || [];
155
- for (const fallback of fallbacks) {
156
- const normalizedFallback = this.normalizeAccelerator(fallback);
157
- const fallbackSuccess = this.tryRegister(action, normalizedFallback);
158
-
159
- if (fallbackSuccess) {
160
- return {
161
- success: true,
162
- action,
163
- accelerator: normalizedFallback,
164
- fallbackUsed: normalizedFallback,
165
- };
166
- }
167
- }
168
-
169
- // All attempts failed
170
- return {
171
- success: false,
172
- action,
173
- accelerator: normalizedAccelerator,
174
- error: `Failed to register hotkey. ${normalizedAccelerator} and all fallbacks are unavailable.`,
175
- };
176
- }
177
-
178
- /**
179
- * Attempt to register an accelerator
180
- */
181
- private tryRegister(action: HotkeyAction, accelerator: string): boolean {
182
- try {
183
- // Check if already registered globally
184
- if (globalShortcut.isRegistered(accelerator)) {
185
- console.warn(`[HotkeyManager] Accelerator ${accelerator} is already registered globally`);
186
- return false;
187
- }
188
-
189
- const success = globalShortcut.register(accelerator, () => {
190
- console.log(`[HotkeyManager] Hotkey triggered: ${action}`);
191
- this.emitHotkey(action);
192
- });
193
-
194
- if (success) {
195
- this.registeredKeys.set(action, accelerator);
196
- this.config[action] = accelerator;
197
- console.log(`[HotkeyManager] Registered ${accelerator} for ${action}`);
198
- }
199
-
200
- return success;
201
- } catch (error) {
202
- console.error(`[HotkeyManager] Error registering ${accelerator}:`, error);
203
- return false;
204
- }
205
- }
206
-
207
- /**
208
- * Unregister a hotkey for an action
209
- */
210
- unregister(action: HotkeyAction): void {
211
- const accelerator = this.registeredKeys.get(action);
212
- if (accelerator) {
213
- try {
214
- globalShortcut.unregister(accelerator);
215
- this.registeredKeys.delete(action);
216
- console.log(`[HotkeyManager] Unregistered ${accelerator} for ${action}`);
217
- } catch (error) {
218
- console.error(`[HotkeyManager] Error unregistering ${accelerator}:`, error);
219
- }
220
- }
221
- }
222
-
223
- /**
224
- * Unregister all hotkeys
225
- */
226
- unregisterAll(): void {
227
- const entries = Array.from(this.registeredKeys.entries());
228
- for (const [action, accelerator] of entries) {
229
- try {
230
- globalShortcut.unregister(accelerator);
231
- console.log(`[HotkeyManager] Unregistered ${accelerator} for ${action}`);
232
- } catch (error) {
233
- console.error(`[HotkeyManager] Error unregistering ${accelerator}:`, error);
234
- }
235
- }
236
- this.registeredKeys.clear();
237
- console.log('[HotkeyManager] All hotkeys unregistered');
238
- }
239
-
240
- /**
241
- * Get the registered accelerator for an action
242
- */
243
- getAccelerator(action: HotkeyAction): string | undefined {
244
- return this.registeredKeys.get(action);
245
- }
246
-
247
- /**
248
- * Get the current hotkey configuration
249
- */
250
- getConfig(): HotkeyConfig {
251
- return { ...this.config };
252
- }
253
-
254
- /**
255
- * Check if a hotkey is registered for an action
256
- */
257
- isRegistered(action: HotkeyAction): boolean {
258
- return this.registeredKeys.has(action);
259
- }
260
-
261
- /**
262
- * Subscribe to hotkey events
263
- * Returns an unsubscribe function
264
- */
265
- onHotkey(callback: (action: HotkeyAction) => void): () => void {
266
- this.callbacks.add(callback);
267
- return () => {
268
- this.callbacks.delete(callback);
269
- };
270
- }
271
-
272
- /**
273
- * Emit a hotkey event to all subscribers
274
- */
275
- private emitHotkey(action: HotkeyAction): void {
276
- const callbacksArray = Array.from(this.callbacks);
277
- for (const callback of callbacksArray) {
278
- try {
279
- callback(action);
280
- } catch (error) {
281
- console.error(`[HotkeyManager] Error in hotkey callback:`, error);
282
- }
283
- }
284
- }
285
-
286
- /**
287
- * Normalize accelerator string for consistency
288
- * Handles platform differences and common aliases
289
- */
290
- private normalizeAccelerator(accelerator: string): string {
291
- let normalized = accelerator.trim();
292
-
293
- // Normalize common aliases
294
- normalized = normalized
295
- .replace(/Cmd/gi, 'Command')
296
- .replace(/Ctrl/gi, 'Control')
297
- .replace(/Opt/gi, 'Alt')
298
- .replace(/Option/gi, 'Alt');
299
-
300
- // Ensure proper casing for Electron accelerator keys
301
- const parts = normalized.split('+').map((part) => {
302
- const lower = part.toLowerCase().trim();
303
- switch (lower) {
304
- case 'command':
305
- return 'Command';
306
- case 'control':
307
- return 'Control';
308
- case 'commandorcontrol':
309
- return 'CommandOrControl';
310
- case 'alt':
311
- return 'Alt';
312
- case 'shift':
313
- return 'Shift';
314
- case 'super':
315
- return 'Super';
316
- case 'meta':
317
- return 'Meta';
318
- default:
319
- // For letter keys, ensure uppercase
320
- if (lower.length === 1 && /[a-z]/.test(lower)) {
321
- return lower.toUpperCase();
322
- }
323
- // For special keys, capitalize first letter
324
- return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
325
- }
326
- });
327
-
328
- return parts.join('+');
329
- }
330
-
331
- /**
332
- * Update configuration with new hotkeys
333
- * Will re-register affected hotkeys
334
- */
335
- updateConfig(newConfig: Partial<HotkeyConfig>): HotkeyRegistrationResult[] {
336
- const results: HotkeyRegistrationResult[] = [];
337
-
338
- if (newConfig.toggleRecording && newConfig.toggleRecording !== this.config.toggleRecording) {
339
- results.push(this.register('toggleRecording', newConfig.toggleRecording));
340
- }
341
-
342
- if (newConfig.manualScreenshot && newConfig.manualScreenshot !== this.config.manualScreenshot) {
343
- results.push(this.register('manualScreenshot', newConfig.manualScreenshot));
344
- }
345
-
346
- if (newConfig.pauseResume && newConfig.pauseResume !== this.config.pauseResume) {
347
- results.push(this.register('pauseResume', newConfig.pauseResume));
348
- }
349
-
350
- return results;
351
- }
352
-
353
- /**
354
- * Get display string for an accelerator (user-friendly format)
355
- */
356
- getDisplayString(action: HotkeyAction): string {
357
- const accelerator = this.registeredKeys.get(action);
358
- if (!accelerator) return 'Not set';
359
-
360
- const isMac = process.platform === 'darwin';
361
-
362
- return accelerator
363
- .replace('CommandOrControl', isMac ? '\u2318' : 'Ctrl')
364
- .replace('Command', '\u2318')
365
- .replace('Control', 'Ctrl')
366
- .replace('Shift', isMac ? '\u21E7' : 'Shift')
367
- .replace('Alt', isMac ? '\u2325' : 'Alt')
368
- .replace('Super', isMac ? '\u2318' : 'Win')
369
- .replace(/\+/g, isMac ? '' : '+');
370
- }
371
- }
372
-
373
- // Singleton instance
374
- let hotkeyManagerInstance: HotkeyManagerImpl | null = null;
375
-
376
- /**
377
- * Get or create the HotkeyManager singleton
378
- */
379
- export function getHotkeyManager(config?: Partial<HotkeyConfig>): HotkeyManagerImpl {
380
- if (!hotkeyManagerInstance) {
381
- hotkeyManagerInstance = new HotkeyManagerImpl(config);
382
- }
383
- return hotkeyManagerInstance;
384
- }
385
-
386
- /**
387
- * Create a new HotkeyManager instance (for testing)
388
- */
389
- export function createHotkeyManager(config?: Partial<HotkeyConfig>): HotkeyManagerImpl {
390
- return new HotkeyManagerImpl(config);
391
- }
392
-
393
- // Export the singleton for convenience
394
- export const hotkeyManager = getHotkeyManager();
395
-
396
- export default hotkeyManager;
397
-
398
- // Re-export HotkeyConfig from shared/types for downstream consumers
399
- export type { HotkeyConfig } from '../shared/types';