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,452 @@
1
+ /**
2
+ * Windows Taskbar Integration for markupr
3
+ *
4
+ * Provides:
5
+ * - Jump list with recent sessions
6
+ * - Taskbar progress indicator during processing
7
+ * - Thumbnail toolbar buttons (Record, Pause, Stop)
8
+ * - Windows toast notifications
9
+ *
10
+ * @module windows/TaskbarIntegration
11
+ */
12
+
13
+ import { app, BrowserWindow, nativeImage, Notification, shell } from 'electron';
14
+ import * as path from 'path';
15
+ import electronLog from 'electron-log';
16
+ const log = electronLog.default ?? electronLog;
17
+
18
+ // Types for Windows taskbar integration
19
+ interface ThumbnailToolbarButton {
20
+ tooltip: string;
21
+ icon: Electron.NativeImage;
22
+ click: () => void;
23
+ flags?: ('enabled' | 'disabled' | 'dismissonclick' | 'nobackground' | 'hidden' | 'noninteractive')[];
24
+ }
25
+
26
+ export interface SessionInfo {
27
+ id: string;
28
+ name: string;
29
+ path: string;
30
+ timestamp: Date;
31
+ }
32
+
33
+ export type RecordingState = 'idle' | 'recording' | 'paused' | 'processing';
34
+
35
+ export class TaskbarIntegration {
36
+ private mainWindow: BrowserWindow | null = null;
37
+ private recentSessions: SessionInfo[] = [];
38
+ private currentState: RecordingState = 'idle';
39
+ private assetsPath: string;
40
+
41
+ constructor() {
42
+ this.assetsPath = app.isPackaged
43
+ ? path.join(process.resourcesPath, 'assets')
44
+ : path.join(__dirname, '../../../assets');
45
+
46
+ log.info('[TaskbarIntegration] Initialized');
47
+ }
48
+
49
+ /**
50
+ * Initialize taskbar integration with the main window
51
+ */
52
+ public initialize(mainWindow: BrowserWindow): void {
53
+ this.mainWindow = mainWindow;
54
+
55
+ if (process.platform !== 'win32') {
56
+ log.info('[TaskbarIntegration] Not on Windows, skipping initialization');
57
+ return;
58
+ }
59
+
60
+ this.setupJumpList();
61
+ this.setupThumbnailToolbar();
62
+ log.info('[TaskbarIntegration] Windows taskbar features enabled');
63
+ }
64
+
65
+ // ===========================================================================
66
+ // Jump List Management
67
+ // ===========================================================================
68
+
69
+ /**
70
+ * Set up the Windows Jump List with recent sessions and quick actions
71
+ */
72
+ private setupJumpList(): void {
73
+ try {
74
+ app.setJumpList([
75
+ {
76
+ type: 'custom',
77
+ name: 'Quick Actions',
78
+ items: [
79
+ {
80
+ type: 'task',
81
+ title: 'New Recording',
82
+ description: 'Start a new feedback recording session',
83
+ program: process.execPath,
84
+ args: '--new-recording',
85
+ iconPath: process.execPath,
86
+ iconIndex: 0
87
+ },
88
+ {
89
+ type: 'task',
90
+ title: 'Quick Screenshot',
91
+ description: 'Take a quick annotated screenshot',
92
+ program: process.execPath,
93
+ args: '--quick-screenshot',
94
+ iconPath: process.execPath,
95
+ iconIndex: 0
96
+ }
97
+ ]
98
+ },
99
+ {
100
+ type: 'recent'
101
+ },
102
+ {
103
+ type: 'tasks',
104
+ items: [
105
+ {
106
+ type: 'task',
107
+ title: 'Open Settings',
108
+ description: 'Configure markupr settings',
109
+ program: process.execPath,
110
+ args: '--settings',
111
+ iconPath: process.execPath,
112
+ iconIndex: 0
113
+ }
114
+ ]
115
+ }
116
+ ]);
117
+ log.info('[TaskbarIntegration] Jump list configured');
118
+ } catch (error) {
119
+ log.error('[TaskbarIntegration] Failed to set jump list:', error);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Update recent sessions in the jump list
125
+ */
126
+ public updateRecentSessions(sessions: SessionInfo[]): void {
127
+ this.recentSessions = sessions.slice(0, 10); // Keep last 10
128
+
129
+ if (process.platform !== 'win32') return;
130
+
131
+ try {
132
+ // Add sessions to Windows recent documents
133
+ for (const session of this.recentSessions) {
134
+ app.addRecentDocument(session.path);
135
+ }
136
+
137
+ // Rebuild jump list with updated recent sessions
138
+ this.setupJumpList();
139
+ log.info(`[TaskbarIntegration] Updated ${sessions.length} recent sessions`);
140
+ } catch (error) {
141
+ log.error('[TaskbarIntegration] Failed to update recent sessions:', error);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Clear recent sessions from jump list
147
+ */
148
+ public clearRecentSessions(): void {
149
+ this.recentSessions = [];
150
+
151
+ if (process.platform === 'win32') {
152
+ app.clearRecentDocuments();
153
+ this.setupJumpList();
154
+ }
155
+ }
156
+
157
+ // ===========================================================================
158
+ // Taskbar Progress Indicator
159
+ // ===========================================================================
160
+
161
+ /**
162
+ * Set taskbar progress bar (0-1 range, or -1 for indeterminate)
163
+ */
164
+ public setProgress(progress: number): void {
165
+ if (!this.mainWindow || process.platform !== 'win32') return;
166
+
167
+ try {
168
+ if (progress < 0) {
169
+ // Indeterminate progress
170
+ this.mainWindow.setProgressBar(2, { mode: 'indeterminate' });
171
+ } else if (progress >= 1) {
172
+ // Complete - briefly show green, then hide
173
+ this.mainWindow.setProgressBar(1, { mode: 'normal' });
174
+ setTimeout(() => {
175
+ this.mainWindow?.setProgressBar(-1); // Hide
176
+ }, 2000);
177
+ } else {
178
+ // Normal progress
179
+ this.mainWindow.setProgressBar(progress, { mode: 'normal' });
180
+ }
181
+ } catch (error) {
182
+ log.error('[TaskbarIntegration] Failed to set progress:', error);
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Show error state in taskbar progress
188
+ */
189
+ public setProgressError(): void {
190
+ if (!this.mainWindow || process.platform !== 'win32') return;
191
+
192
+ try {
193
+ this.mainWindow.setProgressBar(1, { mode: 'error' });
194
+ setTimeout(() => {
195
+ this.mainWindow?.setProgressBar(-1);
196
+ }, 5000);
197
+ } catch (error) {
198
+ log.error('[TaskbarIntegration] Failed to set error progress:', error);
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Clear taskbar progress
204
+ */
205
+ public clearProgress(): void {
206
+ if (!this.mainWindow || process.platform !== 'win32') return;
207
+
208
+ try {
209
+ this.mainWindow.setProgressBar(-1);
210
+ } catch (error) {
211
+ log.error('[TaskbarIntegration] Failed to clear progress:', error);
212
+ }
213
+ }
214
+
215
+ // ===========================================================================
216
+ // Thumbnail Toolbar (Recording Controls)
217
+ // ===========================================================================
218
+
219
+ /**
220
+ * Set up thumbnail toolbar with recording controls
221
+ */
222
+ private setupThumbnailToolbar(): void {
223
+ if (!this.mainWindow) return;
224
+
225
+ this.updateThumbnailToolbar('idle');
226
+ }
227
+
228
+ /**
229
+ * Update thumbnail toolbar based on recording state
230
+ */
231
+ public updateThumbnailToolbar(state: RecordingState): void {
232
+ if (!this.mainWindow || process.platform !== 'win32') return;
233
+
234
+ this.currentState = state;
235
+
236
+ try {
237
+ const buttons = this.getToolbarButtons(state);
238
+ this.mainWindow.setThumbarButtons(buttons);
239
+ log.debug(`[TaskbarIntegration] Toolbar updated for state: ${state}`);
240
+ } catch (error) {
241
+ log.error('[TaskbarIntegration] Failed to update toolbar:', error);
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Get toolbar buttons for current state
247
+ */
248
+ private getToolbarButtons(state: RecordingState): ThumbnailToolbarButton[] {
249
+ const recordIcon = this.loadIcon('tray-recording.png');
250
+ const pauseIcon = this.loadIcon('tray-idle.png');
251
+ const stopIcon = this.loadIcon('tray-error.png');
252
+ const processingIcon = this.loadIcon('tray-processing.png');
253
+
254
+ switch (state) {
255
+ case 'idle':
256
+ return [
257
+ {
258
+ tooltip: 'Start Recording',
259
+ icon: recordIcon,
260
+ click: () => this.emitAction('start-recording'),
261
+ flags: ['enabled']
262
+ }
263
+ ];
264
+
265
+ case 'recording':
266
+ return [
267
+ {
268
+ tooltip: 'Pause Recording',
269
+ icon: pauseIcon,
270
+ click: () => this.emitAction('pause-recording'),
271
+ flags: ['enabled']
272
+ },
273
+ {
274
+ tooltip: 'Stop Recording',
275
+ icon: stopIcon,
276
+ click: () => this.emitAction('stop-recording'),
277
+ flags: ['enabled']
278
+ }
279
+ ];
280
+
281
+ case 'paused':
282
+ return [
283
+ {
284
+ tooltip: 'Resume Recording',
285
+ icon: recordIcon,
286
+ click: () => this.emitAction('resume-recording'),
287
+ flags: ['enabled']
288
+ },
289
+ {
290
+ tooltip: 'Stop Recording',
291
+ icon: stopIcon,
292
+ click: () => this.emitAction('stop-recording'),
293
+ flags: ['enabled']
294
+ }
295
+ ];
296
+
297
+ case 'processing':
298
+ return [
299
+ {
300
+ tooltip: 'Processing...',
301
+ icon: processingIcon,
302
+ click: () => {},
303
+ flags: ['disabled', 'nobackground']
304
+ }
305
+ ];
306
+
307
+ default:
308
+ return [];
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Load icon from assets
314
+ */
315
+ private loadIcon(filename: string): Electron.NativeImage {
316
+ try {
317
+ const iconPath = path.join(this.assetsPath, filename);
318
+ return nativeImage.createFromPath(iconPath);
319
+ } catch (error) {
320
+ log.error(`[TaskbarIntegration] Failed to load icon ${filename}:`, error);
321
+ return nativeImage.createEmpty();
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Emit action to main window
327
+ */
328
+ private emitAction(action: string): void {
329
+ if (!this.mainWindow) return;
330
+ this.mainWindow.webContents.send('taskbar-action', action);
331
+ log.info(`[TaskbarIntegration] Action emitted: ${action}`);
332
+ }
333
+
334
+ // ===========================================================================
335
+ // Windows Toast Notifications
336
+ // ===========================================================================
337
+
338
+ /**
339
+ * Show a Windows toast notification
340
+ */
341
+ public showNotification(options: {
342
+ title: string;
343
+ body: string;
344
+ silent?: boolean;
345
+ urgency?: 'normal' | 'critical' | 'low';
346
+ actions?: { type: 'button'; text: string; }[];
347
+ onClick?: () => void;
348
+ }): void {
349
+ if (!Notification.isSupported()) {
350
+ log.warn('[TaskbarIntegration] Notifications not supported');
351
+ return;
352
+ }
353
+
354
+ try {
355
+ const notification = new Notification({
356
+ title: options.title,
357
+ body: options.body,
358
+ silent: options.silent ?? false,
359
+ urgency: options.urgency ?? 'normal',
360
+ icon: path.join(this.assetsPath, 'tray-idle.png'),
361
+ timeoutType: options.urgency === 'critical' ? 'never' : 'default'
362
+ });
363
+
364
+ if (options.onClick) {
365
+ notification.on('click', options.onClick);
366
+ }
367
+
368
+ notification.show();
369
+ log.debug(`[TaskbarIntegration] Notification shown: ${options.title}`);
370
+ } catch (error) {
371
+ log.error('[TaskbarIntegration] Failed to show notification:', error);
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Show recording started notification
377
+ */
378
+ public notifyRecordingStarted(): void {
379
+ this.showNotification({
380
+ title: 'Recording Started',
381
+ body: 'markupr is now capturing your feedback',
382
+ silent: true,
383
+ urgency: 'low'
384
+ });
385
+ }
386
+
387
+ /**
388
+ * Show recording completed notification
389
+ */
390
+ public notifyRecordingComplete(sessionName: string, outputPath: string): void {
391
+ this.showNotification({
392
+ title: 'Recording Complete',
393
+ body: `Session "${sessionName}" saved successfully`,
394
+ onClick: () => {
395
+ // Open the output folder
396
+ shell.showItemInFolder(outputPath);
397
+ }
398
+ });
399
+ }
400
+
401
+ /**
402
+ * Show export progress notification
403
+ */
404
+ public notifyExportProgress(format: string): void {
405
+ this.showNotification({
406
+ title: 'Exporting...',
407
+ body: `Exporting session to ${format} format`,
408
+ silent: true,
409
+ urgency: 'low'
410
+ });
411
+ }
412
+
413
+ /**
414
+ * Show export complete notification
415
+ */
416
+ public notifyExportComplete(outputPath: string): void {
417
+ this.showNotification({
418
+ title: 'Export Complete',
419
+ body: 'Your feedback session has been exported',
420
+ onClick: () => {
421
+ shell.showItemInFolder(outputPath);
422
+ }
423
+ });
424
+ }
425
+
426
+ /**
427
+ * Show error notification
428
+ */
429
+ public notifyError(title: string, message: string): void {
430
+ this.showNotification({
431
+ title,
432
+ body: message,
433
+ urgency: 'critical'
434
+ });
435
+ }
436
+
437
+ // ===========================================================================
438
+ // Cleanup
439
+ // ===========================================================================
440
+
441
+ /**
442
+ * Clean up resources
443
+ */
444
+ public destroy(): void {
445
+ this.mainWindow = null;
446
+ this.recentSessions = [];
447
+ log.info('[TaskbarIntegration] Destroyed');
448
+ }
449
+ }
450
+
451
+ // Export singleton instance
452
+ export const taskbarIntegration = new TaskbarIntegration();
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Window management for markupr
3
+ *
4
+ * Exports:
5
+ * - PopoverManager: NSPopover-like menu bar window
6
+ * - TaskbarIntegration: Jump lists, progress bar, thumbnail toolbar (Windows)
7
+ *
8
+ * @module windows
9
+ */
10
+
11
+ export {
12
+ PopoverManager,
13
+ POPOVER_SIZES,
14
+ type PopoverConfig,
15
+ type PopoverState
16
+ } from './PopoverManager';
17
+
18
+ export {
19
+ TaskbarIntegration,
20
+ taskbarIntegration,
21
+ type SessionInfo,
22
+ type RecordingState
23
+ } from './TaskbarIntegration';