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,342 @@
1
+ /**
2
+ * markupr Landing Page Scripts
3
+ * Handles navigation, animations, and interactions
4
+ */
5
+
6
+ document.addEventListener('DOMContentLoaded', () => {
7
+ initNavigation();
8
+ initScrollAnimations();
9
+ initSmoothScroll();
10
+ initTypingEffect();
11
+ initVideoPlayer();
12
+ });
13
+
14
+ /**
15
+ * Navigation functionality
16
+ * - Scroll-based style changes
17
+ * - Mobile menu toggle
18
+ */
19
+ function initNavigation() {
20
+ const nav = document.getElementById('nav');
21
+ const mobileToggle = document.querySelector('.nav-mobile-toggle');
22
+ const navLinks = document.querySelector('.nav-links');
23
+
24
+ // Scroll-based navigation styling
25
+ let lastScroll = 0;
26
+ const scrollThreshold = 50;
27
+
28
+ function handleScroll() {
29
+ const currentScroll = window.pageYOffset;
30
+
31
+ // Add/remove scrolled class
32
+ if (currentScroll > scrollThreshold) {
33
+ nav.classList.add('scrolled');
34
+ } else {
35
+ nav.classList.remove('scrolled');
36
+ }
37
+
38
+ // Hide/show nav on scroll (optional)
39
+ if (currentScroll > lastScroll && currentScroll > 200) {
40
+ nav.style.transform = 'translateY(-100%)';
41
+ } else {
42
+ nav.style.transform = 'translateY(0)';
43
+ }
44
+
45
+ lastScroll = currentScroll;
46
+ }
47
+
48
+ window.addEventListener('scroll', throttle(handleScroll, 100));
49
+
50
+ // Mobile menu toggle
51
+ if (mobileToggle && navLinks) {
52
+ mobileToggle.addEventListener('click', () => {
53
+ mobileToggle.classList.toggle('active');
54
+ navLinks.classList.toggle('active');
55
+ });
56
+
57
+ // Close mobile menu when clicking a link
58
+ navLinks.querySelectorAll('a').forEach(link => {
59
+ link.addEventListener('click', () => {
60
+ mobileToggle.classList.remove('active');
61
+ navLinks.classList.remove('active');
62
+ });
63
+ });
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Animate on Scroll (AOS) implementation
69
+ * Lightweight custom implementation
70
+ */
71
+ function initScrollAnimations() {
72
+ const animatedElements = document.querySelectorAll('[data-aos]');
73
+
74
+ if (!animatedElements.length) return;
75
+
76
+ const observerOptions = {
77
+ root: null,
78
+ rootMargin: '0px 0px -10% 0px',
79
+ threshold: 0.1
80
+ };
81
+
82
+ const observer = new IntersectionObserver((entries) => {
83
+ entries.forEach(entry => {
84
+ if (entry.isIntersecting) {
85
+ const delay = entry.target.dataset.aosDelay || 0;
86
+ setTimeout(() => {
87
+ entry.target.classList.add('aos-animate');
88
+ }, delay);
89
+ // Optionally unobserve after animation
90
+ // observer.unobserve(entry.target);
91
+ }
92
+ });
93
+ }, observerOptions);
94
+
95
+ animatedElements.forEach(el => {
96
+ observer.observe(el);
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Smooth scroll for anchor links
102
+ */
103
+ function initSmoothScroll() {
104
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
105
+ anchor.addEventListener('click', function(e) {
106
+ const href = this.getAttribute('href');
107
+ if (href === '#') return;
108
+
109
+ const target = document.querySelector(href);
110
+ if (!target) return;
111
+
112
+ e.preventDefault();
113
+
114
+ const navHeight = document.getElementById('nav').offsetHeight;
115
+ const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - navHeight;
116
+
117
+ window.scrollTo({
118
+ top: targetPosition,
119
+ behavior: 'smooth'
120
+ });
121
+
122
+ // Update URL without jumping
123
+ history.pushState(null, null, href);
124
+ });
125
+ });
126
+ }
127
+
128
+ /**
129
+ * Typing effect for hero transcript
130
+ */
131
+ function initTypingEffect() {
132
+ const transcript = document.querySelector('.transcript-line.typing');
133
+ if (!transcript) return;
134
+
135
+ const text = transcript.textContent;
136
+ transcript.textContent = '';
137
+ transcript.style.opacity = '1';
138
+
139
+ let charIndex = 0;
140
+ const typingSpeed = 30;
141
+
142
+ function type() {
143
+ if (charIndex < text.length) {
144
+ transcript.textContent = text.slice(0, charIndex + 1);
145
+ charIndex++;
146
+ setTimeout(type, typingSpeed);
147
+ } else {
148
+ // Restart after pause
149
+ setTimeout(() => {
150
+ charIndex = 0;
151
+ transcript.textContent = '';
152
+ type();
153
+ }, 5000);
154
+ }
155
+ }
156
+
157
+ // Start typing when element is in view
158
+ const observer = new IntersectionObserver((entries) => {
159
+ entries.forEach(entry => {
160
+ if (entry.isIntersecting) {
161
+ setTimeout(type, 1000);
162
+ observer.unobserve(entry.target);
163
+ }
164
+ });
165
+ }, { threshold: 0.5 });
166
+
167
+ observer.observe(transcript);
168
+ }
169
+
170
+ /**
171
+ * Video player placeholder
172
+ * Click to play functionality
173
+ */
174
+ function initVideoPlayer() {
175
+ const playButton = document.querySelector('.play-button');
176
+ const videoPlaceholder = document.querySelector('.video-placeholder');
177
+
178
+ if (!playButton || !videoPlaceholder) return;
179
+
180
+ playButton.addEventListener('click', () => {
181
+ // Replace with actual video embed when available
182
+ const videoContainer = document.createElement('div');
183
+ videoContainer.style.cssText = `
184
+ position: absolute;
185
+ inset: 0;
186
+ display: flex;
187
+ align-items: center;
188
+ justify-content: center;
189
+ background: var(--bg-card);
190
+ `;
191
+ videoContainer.innerHTML = `
192
+ <p style="color: var(--text-secondary); text-align: center; padding: 2rem;">
193
+ Video coming soon!<br>
194
+ <small>The demo video will be embedded here.</small>
195
+ </p>
196
+ `;
197
+
198
+ videoPlaceholder.appendChild(videoContainer);
199
+ playButton.style.display = 'none';
200
+ });
201
+ }
202
+
203
+ /**
204
+ * Utility: Throttle function
205
+ * Limits how often a function can be called
206
+ */
207
+ function throttle(func, limit) {
208
+ let inThrottle;
209
+ return function(...args) {
210
+ if (!inThrottle) {
211
+ func.apply(this, args);
212
+ inThrottle = true;
213
+ setTimeout(() => inThrottle = false, limit);
214
+ }
215
+ };
216
+ }
217
+
218
+ /**
219
+ * Utility: Debounce function
220
+ * Delays function execution until after wait period
221
+ */
222
+ function debounce(func, wait) {
223
+ let timeout;
224
+ return function(...args) {
225
+ clearTimeout(timeout);
226
+ timeout = setTimeout(() => func.apply(this, args), wait);
227
+ };
228
+ }
229
+
230
+ /**
231
+ * Easter egg: Konami code unlocks something fun
232
+ */
233
+ const konamiCode = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
234
+ let konamiIndex = 0;
235
+
236
+ document.addEventListener('keydown', (e) => {
237
+ if (e.key === konamiCode[konamiIndex]) {
238
+ konamiIndex++;
239
+ if (konamiIndex === konamiCode.length) {
240
+ activateEasterEgg();
241
+ konamiIndex = 0;
242
+ }
243
+ } else {
244
+ konamiIndex = 0;
245
+ }
246
+ });
247
+
248
+ function activateEasterEgg() {
249
+ document.body.style.transition = 'filter 0.5s ease';
250
+ document.body.style.filter = 'hue-rotate(180deg)';
251
+
252
+ setTimeout(() => {
253
+ document.body.style.filter = '';
254
+ }, 3000);
255
+
256
+ console.log('🎙️ markupr Easter Egg Activated!');
257
+ }
258
+
259
+ /**
260
+ * Waveform animation enhancement
261
+ * Randomize heights for more natural look
262
+ */
263
+ function animateWaveforms() {
264
+ const waveforms = document.querySelectorAll('.waveform span, .voice-wave span');
265
+
266
+ waveforms.forEach(bar => {
267
+ setInterval(() => {
268
+ const randomHeight = Math.floor(Math.random() * 20) + 8;
269
+ bar.style.height = `${randomHeight}px`;
270
+ }, 200);
271
+ });
272
+ }
273
+
274
+ // Initialize waveform animation after page load
275
+ window.addEventListener('load', () => {
276
+ setTimeout(animateWaveforms, 1000);
277
+ });
278
+
279
+ /**
280
+ * Preload critical resources
281
+ */
282
+ function preloadResources() {
283
+ // Preload fonts
284
+ const fontPreloads = [
285
+ 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap'
286
+ ];
287
+
288
+ fontPreloads.forEach(href => {
289
+ const link = document.createElement('link');
290
+ link.rel = 'preload';
291
+ link.as = 'style';
292
+ link.href = href;
293
+ document.head.appendChild(link);
294
+ });
295
+ }
296
+
297
+ // Run preload
298
+ preloadResources();
299
+
300
+ /**
301
+ * Track download button clicks (analytics placeholder)
302
+ */
303
+ document.querySelectorAll('.download-btn').forEach(btn => {
304
+ btn.addEventListener('click', function(e) {
305
+ const platform = this.closest('.download-card').querySelector('.download-title').textContent;
306
+ console.log(`Download clicked: ${platform}`);
307
+
308
+ // Placeholder for actual analytics
309
+ // analytics.track('download_click', { platform });
310
+ });
311
+ });
312
+
313
+ /**
314
+ * Add loading state to CTA buttons
315
+ */
316
+ document.querySelectorAll('.btn-primary, .btn-secondary').forEach(btn => {
317
+ btn.addEventListener('click', function(e) {
318
+ // Don't add loading state to anchor links
319
+ if (this.getAttribute('href')?.startsWith('#')) return;
320
+
321
+ this.classList.add('loading');
322
+ this.style.pointerEvents = 'none';
323
+
324
+ // Remove loading state after animation
325
+ setTimeout(() => {
326
+ this.classList.remove('loading');
327
+ this.style.pointerEvents = '';
328
+ }, 1000);
329
+ });
330
+ });
331
+
332
+ /**
333
+ * Console welcome message
334
+ */
335
+ console.log(`
336
+ %c🎙️ markupr
337
+ %cCapture developer feedback in seconds
338
+
339
+ Looking for a job? We're hiring!
340
+ Check out markupr.com/careers
341
+
342
+ `, 'font-size: 24px; font-weight: bold; color: #6366F1;', 'font-size: 14px; color: #94A3B8;');