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.
- package/.claude/commands/review-feedback.md +47 -0
- package/.eslintrc.json +35 -0
- package/.github/CODEOWNERS +16 -0
- package/.github/FUNDING.yml +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +56 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +54 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +89 -0
- package/.github/dependabot.yml +70 -0
- package/.github/workflows/ci.yml +184 -0
- package/.github/workflows/deploy-landing.yml +134 -0
- package/.github/workflows/nightly.yml +288 -0
- package/.github/workflows/release.yml +318 -0
- package/CHANGELOG.md +127 -0
- package/CLAUDE.md +137 -0
- package/CODE_OF_CONDUCT.md +9 -0
- package/CONTRIBUTING.md +390 -0
- package/LICENSE +21 -0
- package/PRODUCT_VISION.md +277 -0
- package/README.md +517 -0
- package/SECURITY.md +51 -0
- package/SIGNING_INSTRUCTIONS.md +284 -0
- package/assets/DMG_BACKGROUND_INSTRUCTIONS.md +130 -0
- package/assets/svg-source/dmg-background.svg +70 -0
- package/assets/svg-source/icon.svg +20 -0
- package/assets/svg-source/tray-icon-processing.svg +7 -0
- package/assets/svg-source/tray-icon-recording.svg +7 -0
- package/assets/svg-source/tray-icon.svg +6 -0
- package/assets/tray-complete.png +0 -0
- package/assets/tray-complete@2x.png +0 -0
- package/assets/tray-completeTemplate.png +0 -0
- package/assets/tray-completeTemplate@2x.png +0 -0
- package/assets/tray-error.png +0 -0
- package/assets/tray-error@2x.png +0 -0
- package/assets/tray-errorTemplate.png +0 -0
- package/assets/tray-errorTemplate@2x.png +0 -0
- package/assets/tray-icon-processing.png +0 -0
- package/assets/tray-icon-processing@2x.png +0 -0
- package/assets/tray-icon-processingTemplate.png +0 -0
- package/assets/tray-icon-processingTemplate@2x.png +0 -0
- package/assets/tray-icon-recording.png +0 -0
- package/assets/tray-icon-recording@2x.png +0 -0
- package/assets/tray-icon-recordingTemplate.png +0 -0
- package/assets/tray-icon-recordingTemplate@2x.png +0 -0
- package/assets/tray-icon.png +0 -0
- package/assets/tray-icon@2x.png +0 -0
- package/assets/tray-iconTemplate.png +0 -0
- package/assets/tray-iconTemplate@2x.png +0 -0
- package/assets/tray-idle.png +0 -0
- package/assets/tray-idle@2x.png +0 -0
- package/assets/tray-idleTemplate.png +0 -0
- package/assets/tray-idleTemplate@2x.png +0 -0
- package/assets/tray-processing-0.png +0 -0
- package/assets/tray-processing-0@2x.png +0 -0
- package/assets/tray-processing-0Template.png +0 -0
- package/assets/tray-processing-0Template@2x.png +0 -0
- package/assets/tray-processing-1.png +0 -0
- package/assets/tray-processing-1@2x.png +0 -0
- package/assets/tray-processing-1Template.png +0 -0
- package/assets/tray-processing-1Template@2x.png +0 -0
- package/assets/tray-processing-2.png +0 -0
- package/assets/tray-processing-2@2x.png +0 -0
- package/assets/tray-processing-2Template.png +0 -0
- package/assets/tray-processing-2Template@2x.png +0 -0
- package/assets/tray-processing-3.png +0 -0
- package/assets/tray-processing-3@2x.png +0 -0
- package/assets/tray-processing-3Template.png +0 -0
- package/assets/tray-processing-3Template@2x.png +0 -0
- package/assets/tray-processing.png +0 -0
- package/assets/tray-processing@2x.png +0 -0
- package/assets/tray-processingTemplate.png +0 -0
- package/assets/tray-processingTemplate@2x.png +0 -0
- package/assets/tray-recording.png +0 -0
- package/assets/tray-recording@2x.png +0 -0
- package/assets/tray-recordingTemplate.png +0 -0
- package/assets/tray-recordingTemplate@2x.png +0 -0
- package/build/DMG_BACKGROUND_SPEC.md +50 -0
- package/build/dmg-background.png +0 -0
- package/build/dmg-background@2x.png +0 -0
- package/build/entitlements.mac.inherit.plist +27 -0
- package/build/entitlements.mac.plist +41 -0
- package/build/favicon-16.png +0 -0
- package/build/favicon-180.png +0 -0
- package/build/favicon-192.png +0 -0
- package/build/favicon-32.png +0 -0
- package/build/favicon-48.png +0 -0
- package/build/favicon-512.png +0 -0
- package/build/favicon-64.png +0 -0
- package/build/icon-128.png +0 -0
- package/build/icon-16.png +0 -0
- package/build/icon-24.png +0 -0
- package/build/icon-256.png +0 -0
- package/build/icon-32.png +0 -0
- package/build/icon-48.png +0 -0
- package/build/icon-64.png +0 -0
- package/build/icon.icns +0 -0
- package/build/icon.ico +0 -0
- package/build/icon.iconset/icon_128x128.png +0 -0
- package/build/icon.iconset/icon_128x128@2x.png +0 -0
- package/build/icon.iconset/icon_16x16.png +0 -0
- package/build/icon.iconset/icon_16x16@2x.png +0 -0
- package/build/icon.iconset/icon_256x256.png +0 -0
- package/build/icon.iconset/icon_256x256@2x.png +0 -0
- package/build/icon.iconset/icon_32x32.png +0 -0
- package/build/icon.iconset/icon_32x32@2x.png +0 -0
- package/build/icon.iconset/icon_512x512.png +0 -0
- package/build/icon.iconset/icon_512x512@2x.png +0 -0
- package/build/icon.png +0 -0
- package/build/installer-header.bmp +0 -0
- package/build/installer-header.png +0 -0
- package/build/installer-sidebar.bmp +0 -0
- package/build/installer-sidebar.png +0 -0
- package/build/installer.nsh +45 -0
- package/build/overlay-processing.png +0 -0
- package/build/overlay-recording.png +0 -0
- package/build/toolbar-record.png +0 -0
- package/build/toolbar-screenshot.png +0 -0
- package/build/toolbar-settings.png +0 -0
- package/build/toolbar-stop.png +0 -0
- package/dist/main/index.mjs +12612 -0
- package/dist/preload/index.mjs +907 -0
- package/dist/renderer/assets/index-CCmUjl9K.js +19495 -0
- package/dist/renderer/assets/index-CUqz_Gs6.css +2270 -0
- package/dist/renderer/index.html +27 -0
- package/docs/AI_AGENT_QUICKSTART.md +42 -0
- package/docs/AI_PIPELINE_DESIGN.md +595 -0
- package/docs/API.md +514 -0
- package/docs/ARCHITECTURE.md +460 -0
- package/docs/CONFIGURATION.md +336 -0
- package/docs/DEVELOPMENT.md +508 -0
- package/docs/EXPORT_FORMATS.md +451 -0
- package/docs/GETTING_STARTED.md +236 -0
- package/docs/KEYBOARD_SHORTCUTS.md +334 -0
- package/docs/TROUBLESHOOTING.md +418 -0
- package/docs/landing/index.html +672 -0
- package/docs/landing/script.js +342 -0
- package/docs/landing/styles.css +1543 -0
- package/electron-builder.yml +140 -0
- package/electron.vite.config.ts +63 -0
- package/package.json +108 -0
- package/railway.json +12 -0
- package/scripts/build.mjs +51 -0
- package/scripts/generate-icons.mjs +314 -0
- package/scripts/generate-installer-images.cjs +253 -0
- package/scripts/generate-tray-icons.mjs +258 -0
- package/scripts/notarize.cjs +180 -0
- package/scripts/one-click-clean-test.sh +147 -0
- package/scripts/postinstall.mjs +36 -0
- package/scripts/setup-markupr.sh +55 -0
- package/setup +17 -0
- package/site/index.html +1835 -0
- package/site/package.json +11 -0
- package/site/railway.json +12 -0
- package/site/server.js +31 -0
- package/src/main/AutoUpdater.ts +392 -0
- package/src/main/CrashRecovery.ts +655 -0
- package/src/main/ErrorHandler.ts +703 -0
- package/src/main/HotkeyManager.ts +399 -0
- package/src/main/MenuManager.ts +529 -0
- package/src/main/PermissionManager.ts +420 -0
- package/src/main/SessionController.ts +1465 -0
- package/src/main/TrayManager.ts +540 -0
- package/src/main/ai/AIPipelineManager.ts +199 -0
- package/src/main/ai/ClaudeAnalyzer.ts +339 -0
- package/src/main/ai/ImageOptimizer.ts +176 -0
- package/src/main/ai/StructuredMarkdownBuilder.ts +379 -0
- package/src/main/ai/index.ts +16 -0
- package/src/main/ai/types.ts +258 -0
- package/src/main/analysis/ClarificationGenerator.ts +385 -0
- package/src/main/analysis/FeedbackAnalyzer.ts +531 -0
- package/src/main/analysis/index.ts +19 -0
- package/src/main/audio/AudioCapture.ts +978 -0
- package/src/main/audio/audioUtils.ts +100 -0
- package/src/main/audio/index.ts +20 -0
- package/src/main/capture/index.ts +1 -0
- package/src/main/index.ts +1693 -0
- package/src/main/ipc/captureHandlers.ts +272 -0
- package/src/main/ipc/index.ts +45 -0
- package/src/main/ipc/outputHandlers.ts +302 -0
- package/src/main/ipc/sessionHandlers.ts +56 -0
- package/src/main/ipc/settingsHandlers.ts +471 -0
- package/src/main/ipc/types.ts +56 -0
- package/src/main/ipc/windowHandlers.ts +277 -0
- package/src/main/output/ClipboardService.ts +369 -0
- package/src/main/output/ExportService.ts +539 -0
- package/src/main/output/FileManager.ts +416 -0
- package/src/main/output/MarkdownGenerator.ts +791 -0
- package/src/main/output/MarkdownPatcher.ts +299 -0
- package/src/main/output/index.ts +186 -0
- package/src/main/output/sessionAdapter.ts +207 -0
- package/src/main/output/templates/html-template.ts +553 -0
- package/src/main/pipeline/FrameExtractor.ts +330 -0
- package/src/main/pipeline/PostProcessor.ts +399 -0
- package/src/main/pipeline/TranscriptAnalyzer.ts +226 -0
- package/src/main/pipeline/index.ts +36 -0
- package/src/main/platform/WindowsTaskbar.ts +600 -0
- package/src/main/platform/index.ts +16 -0
- package/src/main/settings/SettingsManager.ts +730 -0
- package/src/main/settings/index.ts +19 -0
- package/src/main/transcription/ModelDownloadManager.ts +494 -0
- package/src/main/transcription/TierManager.ts +219 -0
- package/src/main/transcription/TranscriptionRecoveryService.ts +340 -0
- package/src/main/transcription/WhisperService.ts +748 -0
- package/src/main/transcription/index.ts +56 -0
- package/src/main/transcription/types.ts +135 -0
- package/src/main/windows/PopoverManager.ts +284 -0
- package/src/main/windows/TaskbarIntegration.ts +452 -0
- package/src/main/windows/index.ts +23 -0
- package/src/preload/index.ts +1047 -0
- package/src/renderer/App.tsx +515 -0
- package/src/renderer/AppWrapper.tsx +28 -0
- package/src/renderer/assets/logo-dark.svg +7 -0
- package/src/renderer/assets/logo.svg +7 -0
- package/src/renderer/audio/AudioCaptureRenderer.ts +454 -0
- package/src/renderer/capture/ScreenRecordingRenderer.ts +492 -0
- package/src/renderer/components/AnnotationOverlay.tsx +836 -0
- package/src/renderer/components/AudioWaveform.tsx +811 -0
- package/src/renderer/components/ClarificationQuestions.tsx +656 -0
- package/src/renderer/components/CountdownTimer.tsx +495 -0
- package/src/renderer/components/CrashRecoveryDialog.tsx +632 -0
- package/src/renderer/components/DonateButton.tsx +127 -0
- package/src/renderer/components/ErrorBoundary.tsx +308 -0
- package/src/renderer/components/ExportDialog.tsx +872 -0
- package/src/renderer/components/HotkeyHint.tsx +261 -0
- package/src/renderer/components/KeyboardShortcuts.tsx +787 -0
- package/src/renderer/components/ModelDownloadDialog.tsx +844 -0
- package/src/renderer/components/Onboarding.tsx +1830 -0
- package/src/renderer/components/ProcessingOverlay.tsx +157 -0
- package/src/renderer/components/RecordingOverlay.tsx +423 -0
- package/src/renderer/components/SessionHistory.tsx +1746 -0
- package/src/renderer/components/SessionReview.tsx +1321 -0
- package/src/renderer/components/SettingsPanel.tsx +217 -0
- package/src/renderer/components/Skeleton.tsx +347 -0
- package/src/renderer/components/StatusIndicator.tsx +86 -0
- package/src/renderer/components/ThemeProvider.tsx +429 -0
- package/src/renderer/components/Tooltip.tsx +370 -0
- package/src/renderer/components/TranscriptionPreview.tsx +183 -0
- package/src/renderer/components/TranscriptionTierSelector.tsx +640 -0
- package/src/renderer/components/UpdateNotification.tsx +377 -0
- package/src/renderer/components/WindowSelector.tsx +947 -0
- package/src/renderer/components/index.ts +99 -0
- package/src/renderer/components/primitives/ApiKeyInput.tsx +98 -0
- package/src/renderer/components/primitives/ColorPicker.tsx +65 -0
- package/src/renderer/components/primitives/DangerButton.tsx +45 -0
- package/src/renderer/components/primitives/DirectoryPicker.tsx +41 -0
- package/src/renderer/components/primitives/Dropdown.tsx +34 -0
- package/src/renderer/components/primitives/KeyRecorder.tsx +117 -0
- package/src/renderer/components/primitives/SettingsSection.tsx +32 -0
- package/src/renderer/components/primitives/Slider.tsx +43 -0
- package/src/renderer/components/primitives/Toggle.tsx +36 -0
- package/src/renderer/components/primitives/index.ts +10 -0
- package/src/renderer/components/settings/AdvancedTab.tsx +174 -0
- package/src/renderer/components/settings/AppearanceTab.tsx +77 -0
- package/src/renderer/components/settings/GeneralTab.tsx +40 -0
- package/src/renderer/components/settings/HotkeysTab.tsx +79 -0
- package/src/renderer/components/settings/RecordingTab.tsx +84 -0
- package/src/renderer/components/settings/index.ts +9 -0
- package/src/renderer/components/settings/settingsStyles.ts +673 -0
- package/src/renderer/components/settings/tabConfig.tsx +85 -0
- package/src/renderer/components/settings/useSettingsPanel.ts +447 -0
- package/src/renderer/contexts/ProcessingContext.tsx +227 -0
- package/src/renderer/contexts/RecordingContext.tsx +683 -0
- package/src/renderer/contexts/UIContext.tsx +326 -0
- package/src/renderer/contexts/index.ts +24 -0
- package/src/renderer/donateMessages.ts +69 -0
- package/src/renderer/hooks/index.ts +75 -0
- package/src/renderer/hooks/useAnimation.tsx +544 -0
- package/src/renderer/hooks/useTheme.ts +313 -0
- package/src/renderer/index.html +26 -0
- package/src/renderer/main.tsx +52 -0
- package/src/renderer/styles/animations.css +1093 -0
- package/src/renderer/styles/app-shell.css +662 -0
- package/src/renderer/styles/globals.css +515 -0
- package/src/renderer/styles/theme.ts +578 -0
- package/src/renderer/types/electron.d.ts +385 -0
- package/src/shared/hotkeys.ts +283 -0
- package/src/shared/types.ts +809 -0
- package/tests/clipboard.test.ts +228 -0
- package/tests/e2e/criticalPaths.test.ts +594 -0
- package/tests/feedbackAnalyzer.test.ts +303 -0
- package/tests/integration/sessionFlow.test.ts +583 -0
- package/tests/markdownGenerator.test.ts +418 -0
- package/tests/output.test.ts +96 -0
- package/tests/setup.ts +486 -0
- package/tests/unit/appIntegration.test.ts +676 -0
- package/tests/unit/appViewState.test.ts +281 -0
- package/tests/unit/audioIpcChannels.test.ts +17 -0
- package/tests/unit/exportService.test.ts +492 -0
- package/tests/unit/hotkeys.test.ts +92 -0
- package/tests/unit/navigationPreload.test.ts +94 -0
- package/tests/unit/onboardingFlow.test.ts +345 -0
- package/tests/unit/permissionManager.test.ts +175 -0
- package/tests/unit/permissionManagerExpanded.test.ts +296 -0
- package/tests/unit/screenRecordingRenderer.test.ts +368 -0
- package/tests/unit/sessionController.test.ts +515 -0
- package/tests/unit/tierManager.test.ts +61 -0
- package/tests/unit/tierManagerExpanded.test.ts +142 -0
- package/tests/unit/transcriptAnalyzer.test.ts +64 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +46 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" />
|
|
7
|
+
<title>markupr</title>
|
|
8
|
+
<style>
|
|
9
|
+
* {
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
14
|
+
html, body, #root {
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: 100%;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
background: transparent;
|
|
19
|
+
}
|
|
20
|
+
</style>
|
|
21
|
+
<script type="module" crossorigin src="./assets/index-CCmUjl9K.js"></script>
|
|
22
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CUqz_Gs6.css">
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<div id="root"></div>
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# AI Agent Quickstart
|
|
2
|
+
|
|
3
|
+
This repo includes a one-command setup path for coding agents.
|
|
4
|
+
|
|
5
|
+
## Command
|
|
6
|
+
|
|
7
|
+
From repo root:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
./setup markupr
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This command:
|
|
14
|
+
- validates Node.js version (18+)
|
|
15
|
+
- installs dependencies if missing
|
|
16
|
+
- runs `npm run typecheck`
|
|
17
|
+
|
|
18
|
+
## Fast Variants
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
./setup markupr --skip-install
|
|
22
|
+
./setup markupr --skip-checks
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## First Run Test Loop
|
|
26
|
+
|
|
27
|
+
After setup:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm run dev
|
|
31
|
+
# or
|
|
32
|
+
bun run dev
|
|
33
|
+
./scripts/one-click-clean-test.sh --skip-checks
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## BYOK Requirements (Open Source Mode)
|
|
37
|
+
|
|
38
|
+
For full report quality in this repo version:
|
|
39
|
+
- OpenAI API key (transcription)
|
|
40
|
+
- Anthropic API key (analysis)
|
|
41
|
+
|
|
42
|
+
Keys are configured in-app under `Settings > Advanced`.
|
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
# AI Analysis Pipeline Design Document
|
|
2
|
+
|
|
3
|
+
**Author**: AI Pipeline Architect
|
|
4
|
+
**Date**: February 2026
|
|
5
|
+
**Status**: Design Complete - Ready for Implementation
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Architecture Overview
|
|
10
|
+
|
|
11
|
+
### Data Flow
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Session End (Cmd+Shift+F)
|
|
15
|
+
|
|
|
16
|
+
v
|
|
17
|
+
+-------------------+
|
|
18
|
+
| SessionController | Produces: Session object
|
|
19
|
+
| stop() returns | - feedbackItems[] (text + screenshots w/ buffers)
|
|
20
|
+
| | - transcriptBuffer[] (all transcript events)
|
|
21
|
+
| | - screenshotBuffer[] (all screenshots)
|
|
22
|
+
| | - metadata (sourceId, sourceName, recordingPath)
|
|
23
|
+
+-------------------+
|
|
24
|
+
|
|
|
25
|
+
v
|
|
26
|
+
+-------------------+
|
|
27
|
+
| AIPipelineManager | NEW MODULE - Orchestrates the pipeline
|
|
28
|
+
| (main process) |
|
|
29
|
+
+-------------------+
|
|
30
|
+
|
|
|
31
|
+
+----+----+
|
|
32
|
+
| |
|
|
33
|
+
v v
|
|
34
|
+
[FREE] [PREMIUM / BYOK]
|
|
35
|
+
| |
|
|
36
|
+
v v
|
|
37
|
+
+----------+ +------------------+
|
|
38
|
+
| Current | | ClaudeAnalyzer | NEW MODULE
|
|
39
|
+
| Pipeline | | - Builds prompt |
|
|
40
|
+
| (as-is) | | - Sends to API |
|
|
41
|
+
| Adapter | | - Parses result |
|
|
42
|
+
| + MD Gen | +------------------+
|
|
43
|
+
+----------+ |
|
|
44
|
+
| v
|
|
45
|
+
| +------------------+
|
|
46
|
+
| | Structured |
|
|
47
|
+
| | MarkdownBuilder | NEW MODULE
|
|
48
|
+
| | Assembles final |
|
|
49
|
+
| | output from AI |
|
|
50
|
+
| | analysis result |
|
|
51
|
+
| +------------------+
|
|
52
|
+
| |
|
|
53
|
+
v v
|
|
54
|
+
+-------------------+
|
|
55
|
+
| FileManager | Saves to disk (existing)
|
|
56
|
+
| saveSession() | ~/markupr/sessions/
|
|
57
|
+
+-------------------+
|
|
58
|
+
|
|
|
59
|
+
v
|
|
60
|
+
Clipboard: file path
|
|
61
|
+
Notification: "Session complete"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Where Processing Happens
|
|
65
|
+
|
|
66
|
+
**Main process.** The AI pipeline runs in the Electron main process, not a separate worker or cloud function. Rationale:
|
|
67
|
+
|
|
68
|
+
1. **Simplicity** - No IPC overhead between workers. The main process already has full access to session data including screenshot buffers.
|
|
69
|
+
2. **Access to Node.js APIs** - Direct file system access for reading screenshot buffers, no serialization cost.
|
|
70
|
+
3. **Non-blocking** - The API call is async/await. The main process UI (tray icon) simply shows "processing" state while waiting. Electron's event loop is not blocked.
|
|
71
|
+
4. **Consistency** - Same process that handles all other session lifecycle stages.
|
|
72
|
+
|
|
73
|
+
The Claude API call is the only network operation and is inherently async. Processing time is dominated by the API round-trip, not local compute.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 2. Claude API Integration
|
|
78
|
+
|
|
79
|
+
### Model Selection
|
|
80
|
+
|
|
81
|
+
**Model**: `claude-sonnet-4-5-20250929` (Claude Sonnet 4.5)
|
|
82
|
+
|
|
83
|
+
Rationale:
|
|
84
|
+
- Vision capability (required for screenshot analysis)
|
|
85
|
+
- Fast enough for interactive use (5-15s response time)
|
|
86
|
+
- Significantly cheaper than Opus for a consumer product
|
|
87
|
+
- Excellent at structured output and following formatting instructions
|
|
88
|
+
- Smart enough to identify patterns, group feedback, and write concise summaries
|
|
89
|
+
|
|
90
|
+
Opus is overkill for this use case. Sonnet 4.5 provides the right quality/cost/speed balance for a ~$12/month consumer product.
|
|
91
|
+
|
|
92
|
+
### System Prompt Design
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
You are markupr's AI analysis engine. You receive a developer's voice-narrated feedback session: a transcript of everything they said while reviewing software, paired with screenshots captured at natural pause points.
|
|
96
|
+
|
|
97
|
+
Your job is to transform this raw narration into a structured, actionable feedback document.
|
|
98
|
+
|
|
99
|
+
## Rules
|
|
100
|
+
|
|
101
|
+
1. **Preserve the user's voice.** Quote their exact words in blockquotes. Never rephrase their observations.
|
|
102
|
+
2. **Group related feedback.** If the user mentions the same area multiple times, combine those into one item.
|
|
103
|
+
3. **Match screenshots to feedback.** Each screenshot was captured during or after the text segment it accompanies. Reference screenshots by their index (e.g., [Screenshot 1]).
|
|
104
|
+
4. **Extract action items.** For each feedback item, write a concrete 1-sentence action item a developer could act on immediately.
|
|
105
|
+
5. **Assign priority.** Use Critical/High/Medium/Low based on the severity of the issue described.
|
|
106
|
+
6. **Categorize.** Use exactly one of: Bug, UX Issue, Performance, Suggestion, Question, Positive Note.
|
|
107
|
+
7. **Write a summary.** 2-3 sentences capturing the most important findings.
|
|
108
|
+
8. **Be concise.** Developers will paste this into AI coding tools. Every word must earn its place.
|
|
109
|
+
|
|
110
|
+
## Output Format
|
|
111
|
+
|
|
112
|
+
Respond with ONLY valid JSON matching this schema:
|
|
113
|
+
|
|
114
|
+
{
|
|
115
|
+
"summary": "2-3 sentence overview of key findings",
|
|
116
|
+
"items": [
|
|
117
|
+
{
|
|
118
|
+
"title": "Short descriptive title (5-10 words)",
|
|
119
|
+
"category": "Bug|UX Issue|Performance|Suggestion|Question|Positive Note",
|
|
120
|
+
"priority": "Critical|High|Medium|Low",
|
|
121
|
+
"quote": "User's exact words (the relevant excerpt)",
|
|
122
|
+
"screenshotIndices": [0, 1],
|
|
123
|
+
"actionItem": "Concrete 1-sentence action for a developer",
|
|
124
|
+
"area": "Component or area of the app this relates to (e.g., 'Navigation', 'Login Form', 'Dashboard')"
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"themes": ["theme1", "theme2"],
|
|
128
|
+
"positiveNotes": ["Things the user explicitly praised"],
|
|
129
|
+
"metadata": {
|
|
130
|
+
"totalItems": 5,
|
|
131
|
+
"criticalCount": 1,
|
|
132
|
+
"highCount": 2
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### User Message Construction
|
|
138
|
+
|
|
139
|
+
The user message is constructed dynamically from session data:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
## Transcript
|
|
143
|
+
|
|
144
|
+
The user narrated the following while reviewing the application "{sourceName}":
|
|
145
|
+
|
|
146
|
+
{segments}
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Screenshots
|
|
151
|
+
|
|
152
|
+
{screenshotCount} screenshots were captured at natural pause points during narration.
|
|
153
|
+
They are provided as images below in chronological order.
|
|
154
|
+
|
|
155
|
+
Screenshot 1 (captured at 00:12):
|
|
156
|
+
[image]
|
|
157
|
+
|
|
158
|
+
Screenshot 2 (captured at 00:34):
|
|
159
|
+
[image]
|
|
160
|
+
|
|
161
|
+
...
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Vision Usage
|
|
165
|
+
|
|
166
|
+
Screenshots are sent as base64 PNG images using Claude's vision API. Each screenshot is included as a content block with `type: "image"` and `media_type: "image/png"`.
|
|
167
|
+
|
|
168
|
+
**Image optimization before sending:**
|
|
169
|
+
- Resize to max 1568px on the longest edge (Claude's recommended limit)
|
|
170
|
+
- Convert to JPEG at 80% quality if the PNG exceeds 500KB (reduces tokens significantly)
|
|
171
|
+
- Strip EXIF/metadata
|
|
172
|
+
|
|
173
|
+
This is done locally in the main process using Electron's `nativeImage` before the API call.
|
|
174
|
+
|
|
175
|
+
### Screenshot Selection Strategy
|
|
176
|
+
|
|
177
|
+
**Send all silence-triggered screenshots** (up to 20). Rationale:
|
|
178
|
+
- A typical 5-minute session captures 5-10 screenshots
|
|
179
|
+
- Even a 15-minute session rarely exceeds 20 screenshots
|
|
180
|
+
- Claude's vision pricing makes 10-20 images affordable per session
|
|
181
|
+
- Sending all screenshots lets Claude make the best grouping decisions
|
|
182
|
+
|
|
183
|
+
**If > 20 screenshots:**
|
|
184
|
+
1. Always include manual/voice-command triggered screenshots (user explicitly wanted these)
|
|
185
|
+
2. From the remaining pause-triggered ones, select evenly spaced screenshots to stay under 20
|
|
186
|
+
3. Include a note in the prompt: "Note: {totalCount} screenshots were captured. The {selectedCount} most representative are shown."
|
|
187
|
+
|
|
188
|
+
**Video frames:** Not extracted. The video file (.webm) is referenced in the output for the user to review, but frames are not extracted for AI analysis in v1. Rationale:
|
|
189
|
+
- Adding ffmpeg dependency is a significant build complexity increase
|
|
190
|
+
- Silence-triggered screenshots already capture the relevant moments
|
|
191
|
+
- Video is a "nice to have" reference, not primary AI input
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 3. Token Cost Estimation
|
|
196
|
+
|
|
197
|
+
### Typical Session (5 minutes, ~500 words spoken, 7 screenshots)
|
|
198
|
+
|
|
199
|
+
| Component | Tokens (est.) | Cost (Sonnet 4.5) |
|
|
200
|
+
|-----------|---------------|-------------------|
|
|
201
|
+
| System prompt | ~500 | $0.0015 |
|
|
202
|
+
| Transcript text (~500 words) | ~700 | $0.0021 |
|
|
203
|
+
| Screenshots (7 x ~1000 tokens each for 1080p JPEG) | ~7,000 | $0.021 |
|
|
204
|
+
| Output (~800 tokens structured JSON) | ~800 | $0.008 |
|
|
205
|
+
| **Total** | **~9,000** | **~$0.032** |
|
|
206
|
+
|
|
207
|
+
**Claude Sonnet 4.5 pricing** (as of Feb 2026):
|
|
208
|
+
- Input: $3/1M tokens
|
|
209
|
+
- Output: $15/1M tokens (with extended thinking disabled)
|
|
210
|
+
|
|
211
|
+
### Cost by Session Length
|
|
212
|
+
|
|
213
|
+
| Session | Transcript | Screenshots | Est. Cost |
|
|
214
|
+
|---------|-----------|-------------|-----------|
|
|
215
|
+
| 2 min, 200 words, 3 screenshots | ~280 | ~3,000 | ~$0.015 |
|
|
216
|
+
| 5 min, 500 words, 7 screenshots | ~700 | ~7,000 | ~$0.032 |
|
|
217
|
+
| 10 min, 1000 words, 12 screenshots | ~1,400 | ~12,000 | ~$0.055 |
|
|
218
|
+
| 15 min, 1500 words, 18 screenshots | ~2,100 | ~18,000 | ~$0.078 |
|
|
219
|
+
| 30 min, 3000 words, 20 screenshots (capped) | ~4,200 | ~20,000 | ~$0.095 |
|
|
220
|
+
|
|
221
|
+
### Business Model Viability
|
|
222
|
+
|
|
223
|
+
At $12/month per user:
|
|
224
|
+
- Break-even at ~375 sessions/month per user (~12/day, far above expected usage)
|
|
225
|
+
- Expected usage: 2-5 sessions/day = $0.06-$0.16/day = $1.80-$4.80/month
|
|
226
|
+
- **Healthy margin of 60-85%** at typical usage levels
|
|
227
|
+
|
|
228
|
+
### Handling Large Sessions
|
|
229
|
+
|
|
230
|
+
For sessions exceeding 200K context window (extremely unlikely with voice sessions):
|
|
231
|
+
1. Truncate transcript to last 3000 words (keep most recent context)
|
|
232
|
+
2. Cap screenshots at 20
|
|
233
|
+
3. Add a note: "This session was very long. Analysis covers the most recent portion."
|
|
234
|
+
|
|
235
|
+
This scenario is practically impossible with the 30-minute recording limit and natural speech rate.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 4. Processing Time Estimation
|
|
240
|
+
|
|
241
|
+
| Phase | Time (est.) |
|
|
242
|
+
|-------|-------------|
|
|
243
|
+
| Image optimization (resize/compress 7 images) | 200-500ms |
|
|
244
|
+
| Prompt construction | <50ms |
|
|
245
|
+
| Claude API round-trip (Sonnet 4.5 with 7 images) | 3-8 seconds |
|
|
246
|
+
| JSON parsing + markdown assembly | <50ms |
|
|
247
|
+
| File system write | <100ms |
|
|
248
|
+
| **Total** | **4-9 seconds** |
|
|
249
|
+
|
|
250
|
+
This fits within the product vision's "A few seconds later, a notification tells you the session is complete." The processing state timeout is 10 seconds, which provides adequate headroom.
|
|
251
|
+
|
|
252
|
+
**Optimization opportunities for future:**
|
|
253
|
+
- Stream the response to show progressive output
|
|
254
|
+
- Pre-optimize images while the session is still recording (on each screenshot capture)
|
|
255
|
+
- Use prompt caching for the system prompt (saves ~$0.001 per session)
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 5. Free vs Premium Tier Differences
|
|
260
|
+
|
|
261
|
+
### Free Tier (Current Behavior - No Changes)
|
|
262
|
+
|
|
263
|
+
The existing pipeline remains untouched:
|
|
264
|
+
|
|
265
|
+
1. `SessionController.stop()` returns `Session`
|
|
266
|
+
2. `sessionAdapter.adaptSessionForMarkdown()` converts types
|
|
267
|
+
3. `FeedbackAnalyzer.analyze()` does rule-based categorization
|
|
268
|
+
4. `MarkdownGenerator.generateFullDocument()` produces output
|
|
269
|
+
5. `FileManager.saveSession()` writes to disk
|
|
270
|
+
|
|
271
|
+
Output: Well-structured markdown with rule-based categories, keyword-derived titles, and template-driven action items. Functional but mechanical.
|
|
272
|
+
|
|
273
|
+
### Premium / BYOK Tier (New Pipeline)
|
|
274
|
+
|
|
275
|
+
1. `SessionController.stop()` returns `Session` (same)
|
|
276
|
+
2. **NEW**: `AIPipelineManager.analyze(session)` is called
|
|
277
|
+
3. **NEW**: `ClaudeAnalyzer` sends transcript + screenshots to Claude API
|
|
278
|
+
4. **NEW**: `StructuredMarkdownBuilder` assembles AI-analyzed output
|
|
279
|
+
5. `FileManager.saveSession()` writes to disk (same)
|
|
280
|
+
|
|
281
|
+
Output: Intelligent markdown with Claude-analyzed groupings, context-aware titles, nuanced priorities, pattern detection, and natural-language action items. The "wow factor."
|
|
282
|
+
|
|
283
|
+
### BYOK Flow
|
|
284
|
+
|
|
285
|
+
Users who bring their own Anthropic API key get the same analysis pipeline. The only difference is the API key source:
|
|
286
|
+
- **Premium**: Key is proxied through a Cloudflare Worker (Eddie's key, metered per subscription)
|
|
287
|
+
- **BYOK**: Key is stored locally in the OS keychain via `SettingsManager.getApiKey('anthropic')`
|
|
288
|
+
|
|
289
|
+
The `ClaudeAnalyzer` accepts an API key parameter and doesn't care where it comes from.
|
|
290
|
+
|
|
291
|
+
### Decision Logic
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
async function processSession(session: Session): Promise<OutputResult> {
|
|
295
|
+
const tier = await determineTier();
|
|
296
|
+
|
|
297
|
+
if (tier === 'premium' || tier === 'byok') {
|
|
298
|
+
try {
|
|
299
|
+
const analysis = await claudeAnalyzer.analyze(session, {
|
|
300
|
+
apiKey: tier === 'byok'
|
|
301
|
+
? await settingsManager.getApiKey('anthropic')
|
|
302
|
+
: await getPremiumProxyKey(),
|
|
303
|
+
baseUrl: tier === 'premium'
|
|
304
|
+
? 'https://api.markupr.com/v1' // Cloudflare Worker proxy
|
|
305
|
+
: 'https://api.anthropic.com',
|
|
306
|
+
});
|
|
307
|
+
return structuredMarkdownBuilder.build(session, analysis);
|
|
308
|
+
} catch (error) {
|
|
309
|
+
// FALLBACK: If AI analysis fails, fall back to free tier output
|
|
310
|
+
console.warn('[AIPipeline] Claude analysis failed, falling back to basic output:', error);
|
|
311
|
+
return generateFreeOutput(session);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return generateFreeOutput(session);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function determineTier(): 'premium' | 'byok' | 'free' {
|
|
319
|
+
if (premiumSubscription.isActive()) return 'premium';
|
|
320
|
+
if (settingsManager.hasApiKey('anthropic')) return 'byok';
|
|
321
|
+
return 'free';
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 6. Error Handling Strategy
|
|
328
|
+
|
|
329
|
+
### Failure Modes and Recovery
|
|
330
|
+
|
|
331
|
+
| Failure | Impact | Recovery |
|
|
332
|
+
|---------|--------|----------|
|
|
333
|
+
| Claude API timeout (>10s) | No AI analysis | Fall back to free tier output. User gets basic markdown. |
|
|
334
|
+
| Claude API rate limit (429) | No AI analysis | Fall back to free tier output. Retry on next session. |
|
|
335
|
+
| Claude API auth error (401) | API key invalid | Fall back to free tier. Show settings notification. |
|
|
336
|
+
| Claude API server error (5xx) | Temporary outage | Fall back to free tier output. |
|
|
337
|
+
| Malformed JSON response | Can't parse AI output | Fall back to free tier output. Log for debugging. |
|
|
338
|
+
| Image optimization fails | Can't resize screenshot | Send original image (may be larger). If still fails, skip that screenshot. |
|
|
339
|
+
| Network offline | No API access | Fall back to free tier output immediately (no timeout wait). |
|
|
340
|
+
| Premium proxy down | Proxy unavailable | Fall back to free tier. Show "Premium service temporarily unavailable." |
|
|
341
|
+
|
|
342
|
+
### Key Principle: Never Lose the Session
|
|
343
|
+
|
|
344
|
+
The AI analysis is an **enhancement layer**. The session data (transcript + screenshots) is always saved regardless of whether AI analysis succeeds. The free tier pipeline is the safety net.
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
async function processSessionSafely(session: Session): Promise<void> {
|
|
348
|
+
// ALWAYS save the basic output first
|
|
349
|
+
const basicDocument = generateFreeOutput(session);
|
|
350
|
+
const saveResult = await fileManager.saveSession(session, basicDocument);
|
|
351
|
+
|
|
352
|
+
// THEN attempt AI enhancement (if eligible)
|
|
353
|
+
const tier = determineTier();
|
|
354
|
+
if (tier !== 'free') {
|
|
355
|
+
try {
|
|
356
|
+
const aiDocument = await runAIPipeline(session);
|
|
357
|
+
// Overwrite the basic output with the AI-enhanced version
|
|
358
|
+
await fileManager.overwriteReport(saveResult.sessionDir, aiDocument);
|
|
359
|
+
} catch (error) {
|
|
360
|
+
// Basic output is already saved - user still gets value
|
|
361
|
+
console.warn('[AIPipeline] Enhancement failed, basic output preserved');
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### User-Facing Error Messages
|
|
368
|
+
|
|
369
|
+
- **Timeout**: "AI analysis is taking longer than expected. Your feedback was saved with basic formatting."
|
|
370
|
+
- **Auth error**: "Your API key appears to be invalid. Check Settings > API Keys. Your feedback was saved with basic formatting."
|
|
371
|
+
- **Offline**: "No internet connection. Your feedback was saved with basic formatting. AI analysis requires a network connection."
|
|
372
|
+
- **Rate limit**: "AI service is temporarily busy. Your feedback was saved with basic formatting."
|
|
373
|
+
|
|
374
|
+
All messages end with the reassurance that the session was saved. Users should never feel like they lost work.
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## 7. Output Markdown Format
|
|
379
|
+
|
|
380
|
+
### AI-Enhanced Output (Premium / BYOK)
|
|
381
|
+
|
|
382
|
+
```markdown
|
|
383
|
+
# Feedback Report: MyApp - Feb 5, 2026
|
|
384
|
+
|
|
385
|
+
> AI-analyzed by Claude | Duration: 5:23 | 7 screenshots | 5 items identified
|
|
386
|
+
|
|
387
|
+
## Summary
|
|
388
|
+
|
|
389
|
+
The user identified a critical layout overlap between the action button and header on mobile viewports, a janky loading transition on the dashboard, and noted that the card animation pattern on the detail page should be reused elsewhere. Overall, the UI needs attention on mobile responsiveness and transition polish.
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Critical Issues
|
|
394
|
+
|
|
395
|
+
### 1. Mobile button overlaps header
|
|
396
|
+
> "This button is way too small on mobile. And it's competing with the header -- look, they're practically overlapping."
|
|
397
|
+
|
|
398
|
+

|
|
399
|
+
|
|
400
|
+
- **Priority:** High
|
|
401
|
+
- **Category:** Bug
|
|
402
|
+
- **Area:** Mobile Layout
|
|
403
|
+
- **Action:** Fix button/header z-index and spacing in the mobile breakpoint. The button needs larger tap target (min 44px) and sufficient margin from the header.
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Improvements Needed
|
|
408
|
+
|
|
409
|
+
### 2. Dashboard loading transition feels janky
|
|
410
|
+
> "The loading spinner here feels sluggish. It shows up but then the content pops in with no transition. Feels janky."
|
|
411
|
+
|
|
412
|
+

|
|
413
|
+
|
|
414
|
+
- **Priority:** Medium
|
|
415
|
+
- **Category:** Performance
|
|
416
|
+
- **Area:** Dashboard
|
|
417
|
+
- **Action:** Add a fade-in transition (200-300ms ease) when content replaces the loading spinner. Consider a skeleton loader instead of a spinner.
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Suggestions
|
|
422
|
+
|
|
423
|
+
### 3. Reuse card animation pattern on dashboard
|
|
424
|
+
> "Actually, I love how this card animation works. We should use this pattern on the dashboard too."
|
|
425
|
+
|
|
426
|
+

|
|
427
|
+
|
|
428
|
+
- **Priority:** Low
|
|
429
|
+
- **Category:** Suggestion
|
|
430
|
+
- **Area:** Dashboard, Detail Page
|
|
431
|
+
- **Action:** Extract the card animation from the detail page into a shared component/utility and apply it to dashboard cards.
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Positive Notes
|
|
436
|
+
|
|
437
|
+
- The card animation on the detail page was praised as a good interaction pattern worth reusing.
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Themes
|
|
442
|
+
|
|
443
|
+
- Mobile responsiveness
|
|
444
|
+
- Transition polish
|
|
445
|
+
- Component reusability
|
|
446
|
+
|
|
447
|
+
## Session Info
|
|
448
|
+
|
|
449
|
+
- **Session ID:** `a1b2c3d4-...`
|
|
450
|
+
- **Source:** MyApp (screen)
|
|
451
|
+
- **Duration:** 5:23
|
|
452
|
+
- **Screenshots:** 7
|
|
453
|
+
- **Recording:** [session-recording.webm](./session-recording.webm)
|
|
454
|
+
- **Analysis:** Claude Sonnet 4.5 (AI-enhanced)
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
*Generated by [markupr](https://github.com/eddiesanjuan/markupr) with AI analysis*
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Key Differences from Free Tier Output
|
|
461
|
+
|
|
462
|
+
| Aspect | Free Tier | Premium/BYOK |
|
|
463
|
+
|--------|-----------|-------------|
|
|
464
|
+
| Summary | Counts-based ("5 items, 2 high priority") | Natural language insight |
|
|
465
|
+
| Grouping | Chronological order | Grouped by priority, then theme |
|
|
466
|
+
| Titles | First sentence of transcript, truncated | AI-generated descriptive title |
|
|
467
|
+
| Action items | Template-based ("Reproduce and patch...") | Context-specific ("Fix button/header z-index...") |
|
|
468
|
+
| Categories | Keyword-matching heuristic | AI-judged with context from screenshots |
|
|
469
|
+
| Positive notes | Not extracted | Explicitly identified section |
|
|
470
|
+
| Themes | Keyword frequency count | AI-identified cross-cutting themes |
|
|
471
|
+
| Cross-references | None | Items linked by shared area/theme |
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## 8. Implementation Plan
|
|
476
|
+
|
|
477
|
+
### New Files to Create
|
|
478
|
+
|
|
479
|
+
```
|
|
480
|
+
src/main/ai/
|
|
481
|
+
AIPipelineManager.ts # Orchestrator - decides tier, runs pipeline
|
|
482
|
+
ClaudeAnalyzer.ts # Claude API integration with vision
|
|
483
|
+
StructuredMarkdownBuilder.ts # Converts AI JSON output to formatted markdown
|
|
484
|
+
ImageOptimizer.ts # Resize/compress screenshots for API
|
|
485
|
+
types.ts # AI pipeline types (AnalysisResult, etc.)
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### Files to Modify
|
|
489
|
+
|
|
490
|
+
```
|
|
491
|
+
src/main/index.ts # Wire AIPipelineManager into session completion flow
|
|
492
|
+
src/main/output/index.ts # Re-export AI pipeline for convenience
|
|
493
|
+
src/shared/types.ts # Add AI tier types, BYOK settings
|
|
494
|
+
src/preload/index.ts # Expose AI tier status to renderer
|
|
495
|
+
src/renderer/components/ # UI for AI status indicator, BYOK key entry
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Implementation Order
|
|
499
|
+
|
|
500
|
+
1. **`src/main/ai/types.ts`** - Define `AIAnalysisResult`, `AITier`, pipeline options
|
|
501
|
+
2. **`src/main/ai/ImageOptimizer.ts`** - Screenshot resize/compress using `nativeImage`
|
|
502
|
+
3. **`src/main/ai/ClaudeAnalyzer.ts`** - Core Claude API integration
|
|
503
|
+
- Build the multi-modal message (text + images)
|
|
504
|
+
- Send request, parse JSON response
|
|
505
|
+
- Handle all error cases with typed errors
|
|
506
|
+
4. **`src/main/ai/StructuredMarkdownBuilder.ts`** - Convert `AIAnalysisResult` to markdown
|
|
507
|
+
- Group items by priority section (Critical, Improvements, Suggestions, Positive)
|
|
508
|
+
- Link screenshots to items using the AI's `screenshotIndices`
|
|
509
|
+
- Generate the final formatted markdown string
|
|
510
|
+
5. **`src/main/ai/AIPipelineManager.ts`** - Orchestrator
|
|
511
|
+
- Determine tier (premium/byok/free)
|
|
512
|
+
- Run the appropriate pipeline
|
|
513
|
+
- Handle fallback on failure
|
|
514
|
+
- Emit progress events to renderer
|
|
515
|
+
6. **`src/main/index.ts`** - Integration
|
|
516
|
+
- Replace direct `generateDocumentForFileManager()` call with `AIPipelineManager.process()`
|
|
517
|
+
- Add IPC handlers for AI tier status
|
|
518
|
+
7. **UI updates** - Settings panel for BYOK Anthropic key, AI status indicator during processing
|
|
519
|
+
|
|
520
|
+
### Dependencies
|
|
521
|
+
|
|
522
|
+
- `@anthropic-ai/sdk` - Official Anthropic TypeScript SDK (already well-maintained, handles retries, streaming)
|
|
523
|
+
- No other new dependencies required. Image optimization uses Electron's built-in `nativeImage`.
|
|
524
|
+
|
|
525
|
+
### Testing Strategy
|
|
526
|
+
|
|
527
|
+
- **Unit tests for `ClaudeAnalyzer`**: Mock the Anthropic SDK, verify prompt construction and JSON parsing
|
|
528
|
+
- **Unit tests for `StructuredMarkdownBuilder`**: Given a known `AIAnalysisResult`, verify markdown output matches expected format
|
|
529
|
+
- **Unit tests for `ImageOptimizer`**: Verify resize behavior, format conversion, error handling
|
|
530
|
+
- **Integration test for `AIPipelineManager`**: Mock `ClaudeAnalyzer`, verify tier selection and fallback logic
|
|
531
|
+
- **Manual test**: Record a real session, verify end-to-end AI output quality
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## 9. API Key Management
|
|
536
|
+
|
|
537
|
+
### Premium Tier (Cloudflare Worker Proxy)
|
|
538
|
+
|
|
539
|
+
```
|
|
540
|
+
User App --> https://api.markupr.com/v1/messages
|
|
541
|
+
|
|
|
542
|
+
Cloudflare Worker
|
|
543
|
+
|
|
|
544
|
+
Validates subscription (Stripe customer ID)
|
|
545
|
+
Adds Eddie's API key to request headers
|
|
546
|
+
Forwards to https://api.anthropic.com/v1/messages
|
|
547
|
+
|
|
|
548
|
+
Claude API
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
The Cloudflare Worker:
|
|
552
|
+
1. Receives the request with the user's subscription token
|
|
553
|
+
2. Validates against Stripe that the subscription is active
|
|
554
|
+
3. Adds the real Anthropic API key (stored as a Worker secret)
|
|
555
|
+
4. Proxies the request to Claude
|
|
556
|
+
5. Returns the response to the user
|
|
557
|
+
|
|
558
|
+
This means Eddie's API key never touches the user's machine.
|
|
559
|
+
|
|
560
|
+
### BYOK (Bring Your Own Key)
|
|
561
|
+
|
|
562
|
+
The user enters their Anthropic API key in Settings. It's stored in the OS keychain via `keytar` (already used for OpenAI keys via `SettingsManager`).
|
|
563
|
+
|
|
564
|
+
```typescript
|
|
565
|
+
// Store
|
|
566
|
+
await settingsManager.setApiKey('anthropic', userKey);
|
|
567
|
+
|
|
568
|
+
// Retrieve
|
|
569
|
+
const key = await settingsManager.getApiKey('anthropic');
|
|
570
|
+
|
|
571
|
+
// Delete
|
|
572
|
+
await settingsManager.deleteApiKey('anthropic');
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
The key is never written to disk in plaintext, never logged, and never sent anywhere except directly to `api.anthropic.com`.
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## 10. Future Considerations (Out of Scope for v1)
|
|
580
|
+
|
|
581
|
+
1. **Streaming responses** - Show AI analysis progressively in a session review view
|
|
582
|
+
2. **Video frame extraction** - Use ffmpeg to extract key frames from the .webm recording
|
|
583
|
+
3. **Multi-session analysis** - "What patterns do you see across my last 10 sessions?"
|
|
584
|
+
4. **Custom system prompts** - Let power users modify the analysis instructions
|
|
585
|
+
5. **Prompt caching** - Cache the system prompt to reduce cost by ~40% on input tokens
|
|
586
|
+
6. **Batch processing** - Re-analyze old sessions with AI after upgrading to premium
|
|
587
|
+
7. **Claude 4 Opus** - Offer as a "deep analysis" option for longer sessions (higher cost, better reasoning)
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## Summary
|
|
592
|
+
|
|
593
|
+
The AI pipeline is a clean enhancement layer that sits between `SessionController.stop()` and `FileManager.saveSession()`. It transforms raw session data into an intelligent document using Claude's vision capabilities. The free tier is unaffected. Failures gracefully degrade to the existing output. The architecture is simple: one new module (`src/main/ai/`) with 5 files, integrated via a single function call in `src/main/index.ts`.
|
|
594
|
+
|
|
595
|
+
Cost is approximately $0.03 per typical session, making the $12/month subscription profitable at any reasonable usage level. Processing time is 4-9 seconds, fitting the product vision's "a few seconds" promise.
|