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,142 @@
1
+ /**
2
+ * TierManager Expanded Tests
3
+ *
4
+ * Extends the basic 4 tests with:
5
+ * - Failover chain behavior (whisper -> timer-only)
6
+ * - Timer-only emergency mode
7
+ * - Tier quality metadata
8
+ * - hasTranscriptionCapability checks
9
+ * - Edge cases
10
+ */
11
+
12
+ import { describe, expect, it, vi } from 'vitest';
13
+ import { TierManager } from '../../src/main/transcription/TierManager';
14
+ import type { TierStatus } from '../../src/main/transcription/types';
15
+
16
+ function makeStatuses(
17
+ overrides: Partial<Record<TierStatus['tier'], Partial<TierStatus>>>
18
+ ): TierStatus[] {
19
+ const defaults: TierStatus[] = [
20
+ { tier: 'whisper', available: false, reason: 'Model not downloaded' },
21
+ { tier: 'timer-only', available: true },
22
+ ];
23
+
24
+ return defaults.map((status) => ({
25
+ ...status,
26
+ ...(overrides[status.tier] ?? {}),
27
+ }));
28
+ }
29
+
30
+ describe('TierManager failover chain', () => {
31
+ it('falls through to timer-only when whisper is unavailable', async () => {
32
+ const manager = new TierManager();
33
+ vi.spyOn(manager, 'getTierStatuses').mockResolvedValue(
34
+ makeStatuses({
35
+ whisper: { tier: 'whisper', available: false, reason: 'No model' },
36
+ })
37
+ );
38
+
39
+ const selected = await manager.selectBestTier();
40
+ expect(selected).toBe('timer-only');
41
+ });
42
+
43
+ it('prefers Whisper in auto mode when available', async () => {
44
+ const manager = new TierManager();
45
+ vi.spyOn(manager, 'getTierStatuses').mockResolvedValue(
46
+ makeStatuses({
47
+ whisper: { tier: 'whisper', available: true },
48
+ })
49
+ );
50
+
51
+ const selected = await manager.selectBestTier();
52
+ expect(selected).toBe('whisper');
53
+ });
54
+
55
+ it('selects Whisper when only Whisper is available', async () => {
56
+ const manager = new TierManager();
57
+ vi.spyOn(manager, 'getTierStatuses').mockResolvedValue(
58
+ makeStatuses({
59
+ whisper: { tier: 'whisper', available: true },
60
+ })
61
+ );
62
+
63
+ const selected = await manager.selectBestTier();
64
+ expect(selected).toBe('whisper');
65
+ });
66
+ });
67
+
68
+ describe('TierManager preference edge cases', () => {
69
+ it('rejects timer-only as preferred tier', () => {
70
+ const manager = new TierManager();
71
+ expect(() => manager.setPreferredTier('timer-only')).toThrow(
72
+ 'does not provide transcription'
73
+ );
74
+ });
75
+
76
+ it('accepts auto as preferred tier', () => {
77
+ const manager = new TierManager();
78
+ expect(() => manager.setPreferredTier('auto')).not.toThrow();
79
+ });
80
+
81
+ it('accepts whisper as preferred tier', () => {
82
+ const manager = new TierManager();
83
+ expect(() => manager.setPreferredTier('whisper')).not.toThrow();
84
+ });
85
+ });
86
+
87
+ describe('TierManager hasTranscriptionCapability', () => {
88
+ it('returns true when Whisper is available', async () => {
89
+ const manager = new TierManager();
90
+ vi.spyOn(manager, 'getTierStatuses').mockResolvedValue(
91
+ makeStatuses({
92
+ whisper: { tier: 'whisper', available: true },
93
+ })
94
+ );
95
+
96
+ const hasCap = await manager.hasTranscriptionCapability();
97
+ expect(hasCap).toBe(true);
98
+ });
99
+
100
+ it('returns false when only timer-only is available and no OpenAI key', async () => {
101
+ const manager = new TierManager();
102
+ vi.spyOn(manager, 'getTierStatuses').mockResolvedValue(
103
+ makeStatuses({
104
+ whisper: { tier: 'whisper', available: false },
105
+ 'timer-only': { tier: 'timer-only', available: true },
106
+ })
107
+ );
108
+
109
+ const hasCap = await manager.hasTranscriptionCapability();
110
+ expect(hasCap).toBe(false);
111
+ });
112
+ });
113
+
114
+ describe('TierManager status reporting', () => {
115
+ it('returns all 2 tier statuses', async () => {
116
+ const manager = new TierManager();
117
+ vi.spyOn(manager, 'getTierStatuses').mockResolvedValue(
118
+ makeStatuses({})
119
+ );
120
+
121
+ const statuses = await manager.getTierStatuses();
122
+ expect(statuses).toHaveLength(2);
123
+
124
+ const tiers = statuses.map((s) => s.tier);
125
+ expect(tiers).toContain('whisper');
126
+ expect(tiers).toContain('timer-only');
127
+ });
128
+
129
+ it('includes availability reason when tier is unavailable', async () => {
130
+ const manager = new TierManager();
131
+ vi.spyOn(manager, 'getTierStatuses').mockResolvedValue(
132
+ makeStatuses({
133
+ whisper: { tier: 'whisper', available: false, reason: 'Model not downloaded' },
134
+ })
135
+ );
136
+
137
+ const statuses = await manager.getTierStatuses();
138
+ const whisper = statuses.find((s) => s.tier === 'whisper');
139
+ expect(whisper?.available).toBe(false);
140
+ expect(whisper?.reason).toBe('Model not downloaded');
141
+ });
142
+ });
@@ -0,0 +1,64 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { TranscriptAnalyzer, type KeyMoment } from '../../src/main/pipeline/TranscriptAnalyzer';
3
+ import type { TranscriptSegment } from '../../src/main/pipeline/PostProcessor';
4
+
5
+ function makeSegments(): TranscriptSegment[] {
6
+ return [
7
+ { text: 'Start feedback', startTime: 0, endTime: 2, confidence: 0.9 },
8
+ { text: 'More context', startTime: 4, endTime: 7, confidence: 0.9 },
9
+ ];
10
+ }
11
+
12
+ describe('TranscriptAnalyzer', () => {
13
+ it('adds periodic captures only when no AI hints are present', () => {
14
+ const analyzer = new TranscriptAnalyzer();
15
+ const segments: TranscriptSegment[] = [
16
+ { text: 'session start', startTime: 0, endTime: 10, confidence: 0.9 },
17
+ { text: 'session end', startTime: 11, endTime: 30, confidence: 0.9 },
18
+ ];
19
+
20
+ const withoutHints = analyzer.analyze(segments, []);
21
+ const withHints = analyzer.analyze(segments, [
22
+ { timestamp: 30, reason: 'AI-highlighted context', confidence: 0.9 },
23
+ ]);
24
+
25
+ expect(withoutHints.some((moment) => moment.reason === 'Periodic capture')).toBe(true);
26
+ expect(withHints.some((moment) => moment.reason === 'Periodic capture')).toBe(false);
27
+ });
28
+
29
+ it('keeps AI hint when it overlaps a periodic candidate', () => {
30
+ const analyzer = new TranscriptAnalyzer();
31
+ const moments = analyzer.analyze(makeSegments(), [
32
+ { timestamp: 2.05, reason: 'AI-timestamped issue context', confidence: 0.92 },
33
+ ]);
34
+
35
+ const overlapping = moments.find((moment) => Math.abs(moment.timestamp - 2.05) < 0.2);
36
+ expect(overlapping).toBeDefined();
37
+ expect(overlapping?.reason).toMatch(/AI/i);
38
+ });
39
+
40
+ it('prioritizes AI hints when capping a large set', () => {
41
+ const analyzer = new TranscriptAnalyzer();
42
+ const segments: TranscriptSegment[] = Array.from({ length: 30 }, (_, index) => {
43
+ const start = index * 3;
44
+ return {
45
+ text: `segment ${index + 1}`,
46
+ startTime: start,
47
+ endTime: start + 1.8,
48
+ confidence: 0.8,
49
+ };
50
+ });
51
+
52
+ const aiHints: KeyMoment[] = [
53
+ { timestamp: 12, reason: 'AI-highlighted context', confidence: 0.9 },
54
+ { timestamp: 33, reason: 'AI-highlighted context', confidence: 0.9 },
55
+ { timestamp: 54, reason: 'AI-highlighted context', confidence: 0.9 },
56
+ ];
57
+
58
+ const result = analyzer.analyze(segments, aiHints);
59
+ const aiCount = result.filter((moment) => /AI/i.test(moment.reason)).length;
60
+
61
+ expect(result.length).toBeLessThanOrEqual(20);
62
+ expect(aiCount).toBeGreaterThanOrEqual(2);
63
+ });
64
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "noEmit": true,
14
+ "jsx": "react-jsx",
15
+ "baseUrl": ".",
16
+ "paths": {
17
+ "@/*": ["src/*"],
18
+ "@main/*": ["src/main/*"],
19
+ "@renderer/*": ["src/renderer/*"],
20
+ "@shared/*": ["src/shared/*"]
21
+ }
22
+ },
23
+ "include": ["src/**/*"],
24
+ "exclude": ["node_modules", "dist", "release"]
25
+ }
@@ -0,0 +1,46 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ setupFiles: ['./tests/setup.ts'],
8
+ include: ['tests/**/*.test.ts'],
9
+ exclude: ['node_modules', 'dist'],
10
+ // Coverage configuration
11
+ coverage: {
12
+ provider: 'v8',
13
+ reporter: ['text', 'text-summary', 'json', 'html'],
14
+ reportsDirectory: './coverage',
15
+ exclude: [
16
+ 'node_modules/**',
17
+ 'dist/**',
18
+ 'tests/**',
19
+ '**/*.d.ts',
20
+ '**/types.ts',
21
+ '**/*.config.ts',
22
+ '**/index.ts', // Re-exports
23
+ ],
24
+ thresholds: {
25
+ lines: 8,
26
+ functions: 30,
27
+ branches: 55,
28
+ statements: 8,
29
+ },
30
+ },
31
+ // Test timeouts
32
+ testTimeout: 10000,
33
+ hookTimeout: 10000,
34
+ // Watch mode settings
35
+ watchExclude: ['node_modules/**', 'dist/**'],
36
+ // Reporter
37
+ reporters: ['default'],
38
+ // Pool settings for better isolation
39
+ pool: 'forks',
40
+ poolOptions: {
41
+ forks: {
42
+ singleFork: true,
43
+ },
44
+ },
45
+ },
46
+ });