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,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App.tsx View State & Integration Logic Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests the view-state pattern and component wiring logic added to App.tsx.
|
|
5
|
+
* Since we're in a Node environment (no DOM), we test the logic layer:
|
|
6
|
+
* - View state transitions
|
|
7
|
+
* - mapPopoverState mapping
|
|
8
|
+
* - formatDuration helper
|
|
9
|
+
* - formatRelativeTime helper
|
|
10
|
+
* - State machine integration behavior
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { describe, it, expect } from 'vitest';
|
|
14
|
+
import type { SessionState } from '../../src/shared/types';
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Extract and test pure functions from App.tsx
|
|
18
|
+
// (These are module-scoped functions we can test without rendering)
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
// Replicate the pure functions from App.tsx for testing
|
|
22
|
+
function formatDuration(durationMs: number): string {
|
|
23
|
+
const seconds = Math.floor(durationMs / 1000);
|
|
24
|
+
const mins = Math.floor(seconds / 60);
|
|
25
|
+
const secs = seconds % 60;
|
|
26
|
+
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function formatRelativeTime(timestamp: number): string {
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
const diff = now - timestamp;
|
|
32
|
+
const minute = 60_000;
|
|
33
|
+
const hour = 60 * minute;
|
|
34
|
+
const day = 24 * hour;
|
|
35
|
+
|
|
36
|
+
if (diff < minute) return 'just now';
|
|
37
|
+
if (diff < hour) return `${Math.floor(diff / minute)}m ago`;
|
|
38
|
+
if (diff < day) return `${Math.floor(diff / hour)}h ago`;
|
|
39
|
+
return `${Math.floor(diff / day)}d ago`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function formatCaptureTrigger(trigger?: 'pause' | 'manual' | 'voice-command'): string {
|
|
43
|
+
switch (trigger) {
|
|
44
|
+
case 'manual':
|
|
45
|
+
return 'Manual Capture';
|
|
46
|
+
case 'voice-command':
|
|
47
|
+
return 'Voice Cue Capture';
|
|
48
|
+
default:
|
|
49
|
+
return 'Auto Pause Capture';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function mapPopoverState(state: SessionState): 'idle' | 'recording' | 'processing' | 'complete' | 'error' {
|
|
54
|
+
if (state === 'recording' || state === 'starting') return 'recording';
|
|
55
|
+
if (state === 'stopping' || state === 'processing') return 'processing';
|
|
56
|
+
if (state === 'complete') return 'complete';
|
|
57
|
+
if (state === 'error') return 'error';
|
|
58
|
+
return 'idle';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// AppView type definition (mirrors App.tsx)
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
type AppView = 'main' | 'settings' | 'history' | 'shortcuts';
|
|
66
|
+
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Tests
|
|
69
|
+
// ============================================================================
|
|
70
|
+
|
|
71
|
+
describe('formatDuration', () => {
|
|
72
|
+
it('formats zero duration', () => {
|
|
73
|
+
expect(formatDuration(0)).toBe('00:00');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('formats seconds only', () => {
|
|
77
|
+
expect(formatDuration(45000)).toBe('00:45');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('formats minutes and seconds', () => {
|
|
81
|
+
expect(formatDuration(125000)).toBe('02:05');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('formats large durations', () => {
|
|
85
|
+
expect(formatDuration(3600000)).toBe('60:00');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('truncates partial seconds', () => {
|
|
89
|
+
expect(formatDuration(1500)).toBe('00:01');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('formatRelativeTime', () => {
|
|
94
|
+
it('formats recent timestamps as "just now"', () => {
|
|
95
|
+
const recent = Date.now() - 30000; // 30 seconds ago
|
|
96
|
+
expect(formatRelativeTime(recent)).toBe('just now');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('formats minutes ago', () => {
|
|
100
|
+
const fiveMinAgo = Date.now() - 300000;
|
|
101
|
+
expect(formatRelativeTime(fiveMinAgo)).toBe('5m ago');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('formats hours ago', () => {
|
|
105
|
+
const twoHoursAgo = Date.now() - 7200000;
|
|
106
|
+
expect(formatRelativeTime(twoHoursAgo)).toBe('2h ago');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('formats days ago', () => {
|
|
110
|
+
const threeDaysAgo = Date.now() - 259200000;
|
|
111
|
+
expect(formatRelativeTime(threeDaysAgo)).toBe('3d ago');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('formatCaptureTrigger', () => {
|
|
116
|
+
it('formats manual trigger', () => {
|
|
117
|
+
expect(formatCaptureTrigger('manual')).toBe('Manual Capture');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('formats voice-command trigger', () => {
|
|
121
|
+
expect(formatCaptureTrigger('voice-command')).toBe('Voice Cue Capture');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('formats undefined trigger as pause', () => {
|
|
125
|
+
expect(formatCaptureTrigger(undefined)).toBe('Auto Pause Capture');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('formats pause trigger as auto', () => {
|
|
129
|
+
expect(formatCaptureTrigger('pause')).toBe('Auto Pause Capture');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('mapPopoverState', () => {
|
|
134
|
+
it('maps idle to idle', () => {
|
|
135
|
+
expect(mapPopoverState('idle')).toBe('idle');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('maps starting to recording', () => {
|
|
139
|
+
expect(mapPopoverState('starting')).toBe('recording');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('maps recording to recording', () => {
|
|
143
|
+
expect(mapPopoverState('recording')).toBe('recording');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('maps stopping to processing', () => {
|
|
147
|
+
expect(mapPopoverState('stopping')).toBe('processing');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('maps processing to processing', () => {
|
|
151
|
+
expect(mapPopoverState('processing')).toBe('processing');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('maps complete to complete', () => {
|
|
155
|
+
expect(mapPopoverState('complete')).toBe('complete');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('maps error to error', () => {
|
|
159
|
+
expect(mapPopoverState('error')).toBe('error');
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('AppView type system', () => {
|
|
164
|
+
it('has valid view states', () => {
|
|
165
|
+
const views: AppView[] = ['main', 'settings', 'history', 'shortcuts'];
|
|
166
|
+
expect(views).toHaveLength(4);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('view transitions are valid', () => {
|
|
170
|
+
// Simulate navigation flow
|
|
171
|
+
let currentView: AppView = 'main';
|
|
172
|
+
|
|
173
|
+
// Navigate to settings
|
|
174
|
+
currentView = 'settings';
|
|
175
|
+
expect(currentView).toBe('settings');
|
|
176
|
+
|
|
177
|
+
// Navigate back
|
|
178
|
+
currentView = 'main';
|
|
179
|
+
expect(currentView).toBe('main');
|
|
180
|
+
|
|
181
|
+
// Navigate to history
|
|
182
|
+
currentView = 'history';
|
|
183
|
+
expect(currentView).toBe('history');
|
|
184
|
+
|
|
185
|
+
// Navigate to shortcuts
|
|
186
|
+
currentView = 'shortcuts';
|
|
187
|
+
expect(currentView).toBe('shortcuts');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('recording forces view back to main', () => {
|
|
191
|
+
// Simulate the behavior: when recording starts, currentView resets to main
|
|
192
|
+
let currentView: AppView = 'settings';
|
|
193
|
+
const sessionState: SessionState = 'recording';
|
|
194
|
+
|
|
195
|
+
// This mirrors the logic in App.tsx onStateChange handler
|
|
196
|
+
if (sessionState === 'recording') {
|
|
197
|
+
currentView = 'main';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
expect(currentView).toBe('main');
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('Component rendering conditions', () => {
|
|
205
|
+
it('CountdownTimer shows only when idle and countdown > 0', () => {
|
|
206
|
+
const state: SessionState = 'idle';
|
|
207
|
+
const countdownDuration = 3;
|
|
208
|
+
const showCountdown = true;
|
|
209
|
+
|
|
210
|
+
const shouldRender = showCountdown && countdownDuration > 0 && state === 'idle';
|
|
211
|
+
expect(shouldRender).toBe(true);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('CountdownTimer hidden when countdown is 0', () => {
|
|
215
|
+
const countdownDuration = 0;
|
|
216
|
+
const showCountdown = true;
|
|
217
|
+
|
|
218
|
+
const shouldRender = showCountdown && countdownDuration > 0;
|
|
219
|
+
expect(shouldRender).toBe(false);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('CompactAudioIndicator shows during recording when setting enabled', () => {
|
|
223
|
+
const state: SessionState = 'recording';
|
|
224
|
+
const showAudioWaveform = true;
|
|
225
|
+
|
|
226
|
+
const shouldRender = state === 'recording' && showAudioWaveform;
|
|
227
|
+
expect(shouldRender).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('CompactAudioIndicator hidden when setting disabled', () => {
|
|
231
|
+
const state: SessionState = 'recording';
|
|
232
|
+
const showAudioWaveform = false;
|
|
233
|
+
|
|
234
|
+
const shouldRender = state === 'recording' && showAudioWaveform;
|
|
235
|
+
expect(shouldRender).toBe(false);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('RecordingOverlay shows only during recording', () => {
|
|
239
|
+
const states: SessionState[] = ['idle', 'starting', 'recording', 'stopping', 'processing', 'complete', 'error'];
|
|
240
|
+
const results = states.map((s) => ({ state: s, shows: s === 'recording' }));
|
|
241
|
+
|
|
242
|
+
expect(results.find((r) => r.state === 'recording')?.shows).toBe(true);
|
|
243
|
+
expect(results.find((r) => r.state === 'idle')?.shows).toBe(false);
|
|
244
|
+
expect(results.find((r) => r.state === 'complete')?.shows).toBe(false);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('SettingsPanel visibility tied to currentView', () => {
|
|
248
|
+
const views: AppView[] = ['main', 'settings', 'history', 'shortcuts'];
|
|
249
|
+
const settingsVisible = views.map((v) => v === 'settings');
|
|
250
|
+
|
|
251
|
+
expect(settingsVisible).toEqual([false, true, false, false]);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('SessionHistory visibility tied to currentView', () => {
|
|
255
|
+
const views: AppView[] = ['main', 'settings', 'history', 'shortcuts'];
|
|
256
|
+
const historyVisible = views.map((v) => v === 'history');
|
|
257
|
+
|
|
258
|
+
expect(historyVisible).toEqual([false, false, true, false]);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('KeyboardShortcuts visibility tied to currentView', () => {
|
|
262
|
+
const views: AppView[] = ['main', 'settings', 'history', 'shortcuts'];
|
|
263
|
+
const shortcutsVisible = views.map((v) => v === 'shortcuts');
|
|
264
|
+
|
|
265
|
+
expect(shortcutsVisible).toEqual([false, false, false, true]);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('primary button disabled during transient states', () => {
|
|
269
|
+
const states: SessionState[] = ['idle', 'starting', 'recording', 'stopping', 'processing', 'complete', 'error'];
|
|
270
|
+
const disabled = states.map((s) => s === 'starting' || s === 'stopping' || s === 'processing');
|
|
271
|
+
|
|
272
|
+
expect(disabled).toEqual([false, true, false, true, true, false, false]);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('manual capture only available during recording', () => {
|
|
276
|
+
const states: SessionState[] = ['idle', 'starting', 'recording', 'stopping', 'processing', 'complete', 'error'];
|
|
277
|
+
const available = states.map((s) => s === 'recording');
|
|
278
|
+
|
|
279
|
+
expect(available).toEqual([false, false, true, false, false, false, false]);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { AUDIO_IPC_CHANNELS } from '../../src/main/audio/AudioCapture';
|
|
3
|
+
import { IPC_CHANNELS } from '../../src/shared/types';
|
|
4
|
+
|
|
5
|
+
describe('Audio IPC Channel Mapping', () => {
|
|
6
|
+
it('uses the shared IPC channel names expected by preload', () => {
|
|
7
|
+
expect(AUDIO_IPC_CHANNELS.REQUEST_DEVICES).toBe(IPC_CHANNELS.AUDIO_REQUEST_DEVICES);
|
|
8
|
+
expect(AUDIO_IPC_CHANNELS.START_CAPTURE).toBe(IPC_CHANNELS.AUDIO_START_CAPTURE);
|
|
9
|
+
expect(AUDIO_IPC_CHANNELS.STOP_CAPTURE).toBe(IPC_CHANNELS.AUDIO_STOP_CAPTURE);
|
|
10
|
+
expect(AUDIO_IPC_CHANNELS.SET_DEVICE).toBe(IPC_CHANNELS.AUDIO_SET_DEVICE);
|
|
11
|
+
expect(AUDIO_IPC_CHANNELS.AUDIO_CHUNK).toBe(IPC_CHANNELS.AUDIO_CHUNK);
|
|
12
|
+
expect(AUDIO_IPC_CHANNELS.DEVICES_RESPONSE).toBe(IPC_CHANNELS.AUDIO_DEVICES_RESPONSE);
|
|
13
|
+
expect(AUDIO_IPC_CHANNELS.CAPTURE_ERROR).toBe(IPC_CHANNELS.AUDIO_CAPTURE_ERROR);
|
|
14
|
+
expect(AUDIO_IPC_CHANNELS.CAPTURE_STARTED).toBe(IPC_CHANNELS.AUDIO_CAPTURE_STARTED);
|
|
15
|
+
expect(AUDIO_IPC_CHANNELS.CAPTURE_STOPPED).toBe(IPC_CHANNELS.AUDIO_CAPTURE_STOPPED);
|
|
16
|
+
});
|
|
17
|
+
});
|