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,174 @@
1
+ import React from 'react';
2
+ import type { AppSettings } from '../../../shared/types';
3
+ import { useTheme } from '../../hooks/useTheme';
4
+ import { SettingsSection, ToggleSetting, ApiKeyInput, DangerButton } from '../primitives';
5
+ import type { ApiKeyState } from '../primitives';
6
+ import { styles } from './settingsStyles';
7
+
8
+ export const AdvancedTab: React.FC<{
9
+ settings: AppSettings;
10
+ openAiApiKey: ApiKeyState;
11
+ anthropicApiKey: ApiKeyState;
12
+ onSettingChange: <K extends keyof AppSettings>(key: K, value: AppSettings[K]) => void;
13
+ onOpenAiApiKeyChange: (value: string) => void;
14
+ onToggleOpenAiApiKeyVisibility: () => void;
15
+ onTestOpenAiApiKey: () => void;
16
+ onAnthropicApiKeyChange: (value: string) => void;
17
+ onToggleAnthropicApiKeyVisibility: () => void;
18
+ onTestAnthropicApiKey: () => void;
19
+ onClearAllData: () => void;
20
+ onExportSettings: () => void;
21
+ onImportSettings: () => void;
22
+ onResetSection: () => void;
23
+ }> = ({
24
+ settings,
25
+ openAiApiKey,
26
+ anthropicApiKey,
27
+ onSettingChange,
28
+ onOpenAiApiKeyChange,
29
+ onToggleOpenAiApiKeyVisibility,
30
+ onTestOpenAiApiKey,
31
+ onAnthropicApiKeyChange,
32
+ onToggleAnthropicApiKeyVisibility,
33
+ onTestAnthropicApiKey,
34
+ onClearAllData,
35
+ onExportSettings,
36
+ onImportSettings,
37
+ onResetSection,
38
+ }) => {
39
+ const { colors } = useTheme();
40
+ return (
41
+ <div style={styles.tabContent}>
42
+ {/* Transcription workflow */}
43
+ <SettingsSection
44
+ title="Transcription Workflow"
45
+ description="Simple and reliable capture pipeline"
46
+ onReset={onResetSection}
47
+ >
48
+ <div style={styles.settingDescription}>
49
+ markupr records screen + microphone first, then runs transcription after you stop.
50
+ OpenAI is the primary cloud path. Local Whisper is optional fallback when available.
51
+ </div>
52
+ </SettingsSection>
53
+
54
+ <SettingsSection
55
+ title="BYOK Mode"
56
+ description="This open-source build uses your own keys for both transcription and AI analysis."
57
+ >
58
+ <div style={styles.settingDescription}>
59
+ Set both the OpenAI key (transcription) and Anthropic key (analysis) below for full end-to-end reports.
60
+ </div>
61
+ </SettingsSection>
62
+
63
+ {/* OpenAI API Key (BYOK primary transcription fallback) */}
64
+ <SettingsSection
65
+ title="OpenAI API Key"
66
+ description="Required for BYOK post-session transcription"
67
+ >
68
+ <div style={styles.serviceInfo}>
69
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
70
+ <path
71
+ d="M12 3l2.4 1.4 2.8-.3 1.2 2.6 2.3 1.6-.8 2.7.8 2.7-2.3 1.6-1.2 2.6-2.8-.3L12 21l-2.4-1.4-2.8.3-1.2-2.6-2.3-1.6.8-2.7-.8-2.7 2.3-1.6 1.2-2.6 2.8.3L12 3z"
72
+ stroke={colors.status.success}
73
+ strokeWidth="1.5"
74
+ strokeLinecap="round"
75
+ strokeLinejoin="round"
76
+ />
77
+ <path d="M9.5 12h5M12 9.5v5" stroke={colors.status.success} strokeWidth="1.5" strokeLinecap="round" />
78
+ </svg>
79
+ <div>
80
+ <span style={styles.serviceName}>OpenAI Audio Transcription</span>
81
+ <span style={styles.serviceDescription}>
82
+ Used for reliable post-session narration transcription when local models are unavailable.
83
+ </span>
84
+ </div>
85
+ </div>
86
+ <ApiKeyInput
87
+ label="API Key"
88
+ description="Required for BYOK transcription"
89
+ serviceName="OpenAI"
90
+ apiKey={openAiApiKey}
91
+ onApiKeyChange={onOpenAiApiKeyChange}
92
+ onToggleVisibility={onToggleOpenAiApiKeyVisibility}
93
+ onTest={onTestOpenAiApiKey}
94
+ />
95
+ </SettingsSection>
96
+
97
+ {/* Anthropic API Key (BYOK AI analysis) */}
98
+ <SettingsSection
99
+ title="Anthropic API Key"
100
+ description="Required for BYOK AI analysis in this version"
101
+ >
102
+ <div style={styles.serviceInfo}>
103
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
104
+ <path
105
+ d="M4 20L10.5 4h3L20 20h-3.5l-1.3-3.3H8.8L7.5 20H4zM9.9 13.9h4.2L12 8.4l-2.1 5.5z"
106
+ fill={colors.status.warning}
107
+ />
108
+ </svg>
109
+ <div>
110
+ <span style={styles.serviceName}>Anthropic Analysis</span>
111
+ <span style={styles.serviceDescription}>
112
+ Used to generate structured, agent-ready markdown insights from your capture session.
113
+ </span>
114
+ </div>
115
+ </div>
116
+ <ApiKeyInput
117
+ label="API Key"
118
+ description="Required for BYOK AI analysis"
119
+ serviceName="Anthropic"
120
+ apiKey={anthropicApiKey}
121
+ onApiKeyChange={onAnthropicApiKeyChange}
122
+ onToggleVisibility={onToggleAnthropicApiKeyVisibility}
123
+ onTest={onTestAnthropicApiKey}
124
+ />
125
+ </SettingsSection>
126
+
127
+ <SettingsSection title="Debug & Backup">
128
+ <ToggleSetting
129
+ label="Debug Mode"
130
+ description="Enable verbose logging for troubleshooting"
131
+ value={settings.debugMode}
132
+ onChange={(value) => onSettingChange('debugMode', value)}
133
+ />
134
+ <ToggleSetting
135
+ label="Keep Audio Backups"
136
+ description="Save audio recordings alongside transcriptions"
137
+ value={settings.keepAudioBackups}
138
+ onChange={(value) => onSettingChange('keepAudioBackups', value)}
139
+ />
140
+ </SettingsSection>
141
+
142
+ <SettingsSection title="Settings Management">
143
+ <div style={styles.settingRow}>
144
+ <div style={styles.settingInfo}>
145
+ <span style={styles.settingLabel}>Export Settings</span>
146
+ <span style={styles.settingDescription}>Save your settings to a file</span>
147
+ </div>
148
+ <button style={styles.secondaryButton} onClick={onExportSettings}>
149
+ Export
150
+ </button>
151
+ </div>
152
+ <div style={styles.settingRow}>
153
+ <div style={styles.settingInfo}>
154
+ <span style={styles.settingLabel}>Import Settings</span>
155
+ <span style={styles.settingDescription}>Load settings from a file</span>
156
+ </div>
157
+ <button style={styles.secondaryButton} onClick={onImportSettings}>
158
+ Import
159
+ </button>
160
+ </div>
161
+ </SettingsSection>
162
+
163
+ <SettingsSection title="Danger Zone">
164
+ <DangerButton
165
+ label="Clear All Data"
166
+ description="Delete all sessions, screenshots, and reset settings"
167
+ buttonText="Clear All Data"
168
+ confirmText="Click to confirm deletion"
169
+ onConfirm={onClearAllData}
170
+ />
171
+ </SettingsSection>
172
+ </div>
173
+ );
174
+ };
@@ -0,0 +1,77 @@
1
+ import React from 'react';
2
+ import type { AppSettings } from '../../../shared/types';
3
+ // useTheme available for future theme-aware styling
4
+ import { darkTheme, lightTheme } from '../../styles/theme';
5
+ import { SettingsSection, DropdownSetting, ColorPicker } from '../primitives';
6
+ import { styles } from './settingsStyles';
7
+
8
+ export const AppearanceTab: React.FC<{
9
+ settings: AppSettings;
10
+ onSettingChange: <K extends keyof AppSettings>(key: K, value: AppSettings[K]) => void;
11
+ onResetSection: () => void;
12
+ }> = ({ settings, onSettingChange, onResetSection }) => {
13
+ const previewColors = settings.theme === 'light' ? lightTheme : darkTheme;
14
+ return (
15
+ <div style={styles.tabContent}>
16
+ <SettingsSection
17
+ title="Theme"
18
+ description="Choose how markupr looks"
19
+ onReset={onResetSection}
20
+ >
21
+ <DropdownSetting
22
+ label="Theme Mode"
23
+ description="Match your system or choose a specific theme"
24
+ value={settings.theme}
25
+ options={[
26
+ { value: 'system', label: 'System' },
27
+ { value: 'dark', label: 'Dark' },
28
+ { value: 'light', label: 'Light' },
29
+ ]}
30
+ onChange={(value) => onSettingChange('theme', value as 'system' | 'dark' | 'light')}
31
+ />
32
+ </SettingsSection>
33
+
34
+ <SettingsSection title="Accent Color">
35
+ <ColorPicker
36
+ label="Accent Color"
37
+ description="Used for buttons, links, and highlights"
38
+ value={settings.accentColor}
39
+ onChange={(value) => onSettingChange('accentColor', value)}
40
+ />
41
+ </SettingsSection>
42
+
43
+ {/* Live Preview */}
44
+ <SettingsSection title="Preview">
45
+ <div style={styles.themePreview}>
46
+ <div
47
+ style={{
48
+ ...styles.previewCard,
49
+ backgroundColor: previewColors.bg.primary,
50
+ borderColor: previewColors.border.default,
51
+ }}
52
+ >
53
+ <div style={styles.previewHeader}>
54
+ <div style={{ ...styles.previewDot, backgroundColor: settings.accentColor }} />
55
+ <span
56
+ style={{
57
+ ...styles.previewTitle,
58
+ color: previewColors.text.primary,
59
+ }}
60
+ >
61
+ Recording Session
62
+ </span>
63
+ </div>
64
+ <button
65
+ style={{
66
+ ...styles.previewButton,
67
+ backgroundColor: settings.accentColor,
68
+ }}
69
+ >
70
+ Start Recording
71
+ </button>
72
+ </div>
73
+ </div>
74
+ </SettingsSection>
75
+ </div>
76
+ );
77
+ };
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import type { AppSettings } from '../../../shared/types';
3
+ import { SettingsSection, ToggleSetting, DirectoryPicker } from '../primitives';
4
+ import { styles } from './settingsStyles';
5
+
6
+ export const GeneralTab: React.FC<{
7
+ settings: AppSettings;
8
+ onSettingChange: <K extends keyof AppSettings>(key: K, value: AppSettings[K]) => void;
9
+ onResetSection: () => void;
10
+ }> = ({ settings, onSettingChange, onResetSection }) => (
11
+ <div style={styles.tabContent}>
12
+ <SettingsSection
13
+ title="Output"
14
+ description="Where your feedback sessions are saved"
15
+ onReset={onResetSection}
16
+ >
17
+ <DirectoryPicker
18
+ label="Output Directory"
19
+ description="Screenshots and markdown files will be saved here"
20
+ value={settings.outputDirectory}
21
+ onChange={(value) => onSettingChange('outputDirectory', value)}
22
+ />
23
+ </SettingsSection>
24
+
25
+ <SettingsSection title="Startup">
26
+ <ToggleSetting
27
+ label="Launch at Login"
28
+ description="Start markupr automatically when you log in"
29
+ value={settings.launchAtLogin}
30
+ onChange={(value) => onSettingChange('launchAtLogin', value)}
31
+ />
32
+ <ToggleSetting
33
+ label="Check for Updates"
34
+ description="Automatically check on launch and while the app is running"
35
+ value={settings.checkForUpdates}
36
+ onChange={(value) => onSettingChange('checkForUpdates', value)}
37
+ />
38
+ </SettingsSection>
39
+ </div>
40
+ );
@@ -0,0 +1,79 @@
1
+ import React, { useCallback } from 'react';
2
+ import type { AppSettings, HotkeyConfig } from '../../../shared/types';
3
+ import { SettingsSection, KeyRecorder } from '../primitives';
4
+ import { HotkeyHint } from '../HotkeyHint';
5
+ import { styles } from './settingsStyles';
6
+
7
+ export const HotkeysTab: React.FC<{
8
+ settings: AppSettings;
9
+ onHotkeyChange: (key: keyof HotkeyConfig, value: string) => void;
10
+ onResetSection: () => void;
11
+ }> = ({ settings, onHotkeyChange, onResetSection }) => {
12
+ // Detect conflicts
13
+ const findConflict = useCallback(
14
+ (currentKey: keyof HotkeyConfig, value: string): string | null => {
15
+ const entries = Object.entries(settings.hotkeys) as [keyof HotkeyConfig, string][];
16
+ for (const [key, hotkey] of entries) {
17
+ if (key !== currentKey && hotkey === value) {
18
+ const labels: Record<keyof HotkeyConfig, string> = {
19
+ toggleRecording: 'Start/Stop Recording',
20
+ manualScreenshot: 'Manual Screenshot',
21
+ pauseResume: 'Pause/Resume',
22
+ };
23
+ return labels[key];
24
+ }
25
+ }
26
+ return null;
27
+ },
28
+ [settings.hotkeys]
29
+ );
30
+
31
+ return (
32
+ <div style={styles.tabContent}>
33
+ <SettingsSection
34
+ title="Keyboard Shortcuts"
35
+ description="Customize global hotkeys for markupr"
36
+ onReset={onResetSection}
37
+ >
38
+ <KeyRecorder
39
+ label="Start/Stop Recording"
40
+ description="Toggle recording on and off"
41
+ value={settings.hotkeys.toggleRecording}
42
+ onChange={(value) => onHotkeyChange('toggleRecording', value)}
43
+ conflict={findConflict('toggleRecording', settings.hotkeys.toggleRecording)}
44
+ />
45
+ <KeyRecorder
46
+ label="Manual Screenshot"
47
+ description="Capture a screenshot immediately"
48
+ value={settings.hotkeys.manualScreenshot}
49
+ onChange={(value) => onHotkeyChange('manualScreenshot', value)}
50
+ conflict={findConflict('manualScreenshot', settings.hotkeys.manualScreenshot)}
51
+ />
52
+ <KeyRecorder
53
+ label="Pause/Resume"
54
+ description="Temporarily pause active capture"
55
+ value={settings.hotkeys.pauseResume}
56
+ onChange={(value) => onHotkeyChange('pauseResume', value)}
57
+ conflict={findConflict('pauseResume', settings.hotkeys.pauseResume)}
58
+ />
59
+ </SettingsSection>
60
+
61
+ <SettingsSection title="Quick Reference">
62
+ <div style={styles.hotkeyReference}>
63
+ <div style={styles.hotkeyRefItem}>
64
+ <span style={styles.hotkeyRefLabel}>Start/Stop Recording</span>
65
+ <HotkeyHint keys={settings.hotkeys.toggleRecording} size="medium" />
66
+ </div>
67
+ <div style={styles.hotkeyRefItem}>
68
+ <span style={styles.hotkeyRefLabel}>Manual Screenshot</span>
69
+ <HotkeyHint keys={settings.hotkeys.manualScreenshot} size="medium" />
70
+ </div>
71
+ <div style={styles.hotkeyRefItem}>
72
+ <span style={styles.hotkeyRefLabel}>Pause/Resume</span>
73
+ <HotkeyHint keys={settings.hotkeys.pauseResume} size="medium" />
74
+ </div>
75
+ </div>
76
+ </SettingsSection>
77
+ </div>
78
+ );
79
+ };
@@ -0,0 +1,84 @@
1
+ import React from 'react';
2
+ import type { AppSettings, AudioDevice } from '../../../shared/types';
3
+ import { SettingsSection, ToggleSetting, DropdownSetting, SliderSetting } from '../primitives';
4
+ import { styles } from './settingsStyles';
5
+
6
+ export const RecordingTab: React.FC<{
7
+ settings: AppSettings;
8
+ audioDevices: AudioDevice[];
9
+ onSettingChange: <K extends keyof AppSettings>(key: K, value: AppSettings[K]) => void;
10
+ onResetSection: () => void;
11
+ }> = ({ settings, audioDevices, onSettingChange, onResetSection }) => (
12
+ <div style={styles.tabContent}>
13
+ <SettingsSection
14
+ title="Recording Behavior"
15
+ description="Customize how recording sessions work"
16
+ onReset={onResetSection}
17
+ >
18
+ <DropdownSetting
19
+ label="Countdown Before Recording"
20
+ description="Give yourself time to prepare"
21
+ value={settings.defaultCountdown}
22
+ options={[
23
+ { value: 0, label: 'No countdown' },
24
+ { value: 3, label: '3 seconds' },
25
+ { value: 5, label: '5 seconds' },
26
+ ]}
27
+ onChange={(value) => onSettingChange('defaultCountdown', Number(value) as 0 | 3 | 5)}
28
+ />
29
+ <ToggleSetting
30
+ label="Show Recording HUD"
31
+ description="Display mic activity and shortcut hints while recording"
32
+ value={settings.showTranscriptionPreview}
33
+ onChange={(value) => onSettingChange('showTranscriptionPreview', value)}
34
+ />
35
+ <ToggleSetting
36
+ label="Show Audio Waveform"
37
+ description="Visual feedback of your voice levels"
38
+ value={settings.showAudioWaveform}
39
+ onChange={(value) => onSettingChange('showAudioWaveform', value)}
40
+ />
41
+ </SettingsSection>
42
+
43
+ <SettingsSection title="Audio Input">
44
+ <DropdownSetting
45
+ label="Microphone"
46
+ description="Select which microphone to use for voice capture"
47
+ value={settings.audioDeviceId || 'default'}
48
+ options={[
49
+ { value: 'default', label: 'System Default' },
50
+ ...audioDevices.map((device) => ({
51
+ value: device.id,
52
+ label: device.name + (device.isDefault ? ' (Default)' : ''),
53
+ })),
54
+ ]}
55
+ onChange={(value) => onSettingChange('audioDeviceId', value === 'default' ? null : String(value))}
56
+ />
57
+ </SettingsSection>
58
+
59
+ <SettingsSection title="Screenshot Timing">
60
+ <SliderSetting
61
+ label="Pause Threshold"
62
+ description="How long to pause before capturing a screenshot"
63
+ value={settings.pauseThreshold}
64
+ min={500}
65
+ max={3000}
66
+ step={100}
67
+ unit="ms"
68
+ formatValue={(v) => `${(v / 1000).toFixed(1)}s`}
69
+ onChange={(value) => onSettingChange('pauseThreshold', value)}
70
+ />
71
+ <SliderSetting
72
+ label="Minimum Time Between Captures"
73
+ description="Prevent too many screenshots in quick succession"
74
+ value={settings.minTimeBetweenCaptures}
75
+ min={300}
76
+ max={2000}
77
+ step={100}
78
+ unit="ms"
79
+ formatValue={(v) => `${(v / 1000).toFixed(1)}s`}
80
+ onChange={(value) => onSettingChange('minTimeBetweenCaptures', value)}
81
+ />
82
+ </SettingsSection>
83
+ </div>
84
+ );
@@ -0,0 +1,9 @@
1
+ export { GeneralTab } from './GeneralTab';
2
+ export { RecordingTab } from './RecordingTab';
3
+ export { AppearanceTab } from './AppearanceTab';
4
+ export { HotkeysTab } from './HotkeysTab';
5
+ export { AdvancedTab } from './AdvancedTab';
6
+ export { TABS } from './tabConfig';
7
+ export type { SettingsTab } from './tabConfig';
8
+ export { styles as settingsStyles } from './settingsStyles';
9
+ export { useSettingsPanel } from './useSettingsPanel';