Replace scroll-wheel trigger with middle-click toggle #4029
4 changed files+343−0
AddedSTRATEGY.md+173−0View fileUnifiedSplit
@@ -0,0 +1,173 @@
1# 48co Strategy & Roadmap
2> Last updated: 2026-03-25
3> Status: EXECUTE MODE
4
5## The Hook
6
7**"You're not selling dictation. You're selling a thinking amplifier."**
8
9Every competitor just transcribes. 48co REWRITES. You ramble, it writes professionally. You describe code intent, it writes actual code. You say "email Sarah about being late", it drafts the entire email.
10
11Once users try AI rewrite, raw dictation feels broken. That's the addiction.
12
13---
14
15## Market Position
16
17| | 48co | Wispr Flow | SuperWhisper | WhisperTyping |
18|--|------|-----------|--------------|---------------|
19| AI Rewrite | YES | Basic cleanup | Basic | No |
20| Live Claude Sidebar | YES | No | No | No |
21| Context-Aware | YES | No | No | No |
22| Offline (whisper.cpp) | YES | No | Yes (Mac only) | No |
23| Cross-platform | Mac + Win | Mac + Win + iOS | Mac + Win + iOS | Windows |
24| Price/month | $12 | $15 | $7 | Subscription |
25| Privacy (local-first) | YES | Cloud-only | YES | Cloud |
26| Lifetime deal | $89 | Never | $249 | Never |
27
28---
29
30## Product Roadmap
31
32### Phase 1: Foundation (Weeks 1-4) — 40% ahead
33- [x] Desktop app (Electron, system tray, global hotkey)
34- [x] Whisper API transcription
35- [x] Keyboard simulation (types into any app)
36- [x] Chrome extension (secondary)
37- [ ] **AI Rewrite Mode** (Claude API) — THE differentiator
38- [ ] **Context-Aware Detection** (knows Slack vs Email vs Code)
39- [ ] Voice Macros/Templates
40
41### Phase 2: Multiplication (Weeks 5-10) — 60-70% ahead
42- [ ] Offline Whisper (whisper.cpp local model)
43- [ ] Live Claude Sidebar (dictate question, get AI answer)
44- [ ] Real-time Streaming (Deepgram, words appear as you speak)
45- [ ] Real-time Translation (speak one language, types another)
46
47### Phase 3: Market Domination (Weeks 11-16) — 80%+ ahead
48- [ ] Meeting Transcription with Speaker ID
49- [ ] Enterprise/Team features
50- [ ] Accessibility features
51- [ ] Mobile companion (React Native)
52
53---
54
55## Pricing
56
57| Tier | Price | Features |
58|------|-------|----------|
59| Free | $0 | Basic dictation, 60 min/month, Web Speech API |
60| Pro | $12/mo or $99/year | Unlimited, AI rewrite, offline, context-aware |
61| Teams | $24.99/user/mo | Meeting transcription, speaker ID, shared macros |
62| Lifetime | $89 (launch only, 1000 units) | Pro features forever |
63
64---
65
66## Launch Strategy (30-Day Plan)
67
68### Week 1: Foundation
69- Landing page live at 48co.nz
70- Blog: "Voice to Text vs Manual Typing: 23 Studies"
71- Comparison guide: "Wispr Flow vs SuperWhisper vs 48co"
72- YouTube how-to video (60 seconds)
73
74### Week 2: Authority
75- Deep guide: "Complete Guide to Voice Dictation 2026"
76- Developer blog on Dev.to + Hacker News
77- TikTok content (3 x 21-34 sec videos)
78
79### Week 3: LAUNCH WEEK
80- **Tuesday:** Product Hunt + Hacker News + Reddit blitz + Email blast
81- **Wednesday-Thursday:** YouTube + TikTok + comment engagement
82- **Friday:** Second wave, influencer DMs
83- Target: Top 5 on Product Hunt, 150+ HN points
84
85### Week 4: Community
86- User story videos, AMA on Reddit
87- App Store submissions (Mac App Store + Microsoft Store)
88- Performance review, iterate
89
90---
91
92## SEO Keywords (Top 20 Priority)
93
94### Quick Wins (Low competition, high intent)
951. best voice to text app mac
962. best dictation software windows
973. offline voice to text dictation
984. private voice to text no cloud
995. voice typing into any app
1006. wispr flow alternative
1017. superwhisper alternative
1028. voice to text for writers
1039. voice typing for developers
10410. code dictation voice to text developer
105
106### Head Terms (Build authority over time)
10711. voice to text software
10812. dictation software
10913. speech to text
11014. ai dictation
11115. voice typing app
112
113### Problem-Focused
11416. windows 11 voice typing not working
11517. mac dictation accuracy problems
11618. improve voice to text accuracy
11719. voice dictation filler words removal
11820. voice to text punctuation formatting
119
120---
121
122## International Expansion Priority
123
1241. **Month 1-2:** US, UK, Canada, Australia (English)
1252. **Month 2-3:** Germany, Netherlands, Nordics (Europe)
1263. **Month 3-4:** India, Southeast Asia (Growth markets, lower pricing)
1274. **Month 4+:** Spanish, French, Portuguese, Japanese, Chinese
128
129---
130
131## Revenue Targets
132
133| Month | Customers | MRR | Notes |
134|-------|-----------|-----|-------|
135| 1 | 600-900 paid + 300-500 lifetime | $7-11K MRR + $27-45K one-time | Launch momentum |
136| 2 | 1,500-2,000 | $25-30K MRR | Optimize + expand |
137| 3 | 2,500-3,500 | $40-50K MRR | $150K+ ARR |
138
139---
140
141## Technical Architecture
142
143### Desktop App (PRIMARY)
144- Electron 28+ with system tray
145- @nut-tree-fork/nut-js for keyboard simulation
146- OpenAI Whisper API (primary) + whisper.cpp (offline)
147- Claude API for AI rewrite mode
148- electron-active-window for context detection
149- electron-store for settings
150- electron-updater for auto-updates
151
152### Chrome Extension (SECONDARY)
153- Manifest V3
154- Content script + offscreen document
155- Web Speech API (free) + Whisper API
156- Works on Claude, ChatGPT, Gemini, DeepSeek, any site
157
158### Website (48co.nz)
159- Next.js 14, React 18, Tailwind CSS
160- Live demo, download page, SEO content
161- Comparison pages for each competitor
162
163---
164
165## The 90-Day Window
166
167Wispr Flow has $81M in funding. They will notice us eventually. We have 90 days to:
1681. Nail the product (AI rewrite + context-aware)
1692. Build distribution (Product Hunt, HN, Reddit, SEO)
1703. Get to $20K+ MRR
1714. Own "privacy-first voice dictation" positioning
172
173After that, we're too established to kill.
Modifieddesktop/main/index.js+114−0View fileUnifiedSplit
@@ -43,6 +43,9 @@ const store = new Store({
4343 launchAtLogin: true,
4444 showOverlay: true,
4545 typingSpeed: 0, // 0 = instant (paste), >0 = ms between chars
46 aiRewrite: false, // AI rewrite mode — polishes dictated text
47 aiRewriteMode: 'professional', // professional | casual | concise | auto
48 claudeApiKey: '', // Anthropic API key for AI rewrite
4649 },
4750})
4851
@@ -439,6 +442,104 @@ function wrapCode(text) {
439442 return '```' + detectLang(text) + '\n' + text + '\n```'
440443}
441444
445// ─── AI Rewrite Mode (Claude API) ─────────────────────────────
446// Takes rough dictated text and rewrites it into polished output.
447// The mode changes based on context (email=professional, slack=casual, code=technical).
448
449async function rewriteWithAI(text, settings) {
450 const apiKey = settings.claudeApiKey
451 if (!apiKey) return text
452
453 const mode = settings.aiRewriteMode || 'professional'
454 const appContext = detectAppContext()
455
456 const systemPrompts = {
457 professional: 'You are a writing assistant. Rewrite the user\'s dictated text into clean, professional prose. Fix grammar, remove filler words (um, uh, like, you know), improve clarity. Keep the original meaning and tone. Do NOT add information the user didn\'t say. Return ONLY the rewritten text, nothing else.',
458 casual: 'You are a writing assistant. Clean up the user\'s dictated text into natural, casual writing. Fix obvious errors but keep the conversational tone. Remove filler words. Return ONLY the cleaned text.',
459 concise: 'You are a writing assistant. Rewrite the user\'s dictated text to be as concise as possible. Remove all filler, redundancy, and unnecessary words. Make every word count. Return ONLY the rewritten text.',
460 email: 'You are a writing assistant. Rewrite this dictated text as a professional email. Add appropriate greeting and sign-off if not present. Fix grammar and tone. Return ONLY the email text.',
461 slack: 'You are a writing assistant. Clean up this dictated text for a Slack message. Keep it casual and brief. Use line breaks for readability. Return ONLY the message text.',
462 code: 'You are a coding assistant. The user dictated instructions about code. Convert their spoken description into a clear, well-formatted technical request or code comment. Return ONLY the formatted text.',
463 }
464
465 // Auto-select mode based on detected app context
466 let activeMode = mode
467 if (appContext === 'email') activeMode = 'email'
468 else if (appContext === 'slack' || appContext === 'discord') activeMode = 'slack'
469 else if (appContext === 'code') activeMode = 'code'
470
471 const controller = new AbortController()
472 const timeout = setTimeout(() => controller.abort(), 8000) // 8s max
473
474 try {
475 const response = await fetch('https://api.anthropic.com/v1/messages', {
476 method: 'POST',
477 headers: {
478 'Content-Type': 'application/json',
479 'x-api-key': apiKey,
480 'anthropic-version': '2023-06-01',
481 },
482 body: JSON.stringify({
483 model: 'claude-sonnet-4-20250514',
484 max_tokens: 1024,
485 system: systemPrompts[activeMode] || systemPrompts.professional,
486 messages: [{ role: 'user', content: text }],
487 }),
488 signal: controller.signal,
489 })
490
491 clearTimeout(timeout)
492
493 if (!response.ok) {
494 const err = await response.text().catch(() => '')
495 if (response.status === 401) throw new Error('Invalid Claude API key')
496 throw new Error(`Claude API error (${response.status}): ${err}`)
497 }
498
499 const data = await response.json()
500 const rewritten = data.content?.[0]?.text?.trim()
501
502 // Safety: if AI returns empty or way too different, use original
503 if (!rewritten || rewritten.length < text.length * 0.2) {
504 return text
505 }
506
507 return rewritten
508 } catch (err) {
509 clearTimeout(timeout)
510 if (err.name === 'AbortError') {
511 console.warn('[48co] AI rewrite timed out (8s), using original text')
512 return text
513 }
514 throw err
515 }
516}
517
518// ─── App Context Detection ────────────────────────────────────
519// Detects which app is currently focused to auto-adjust AI rewrite mode.
520// Returns: 'email' | 'slack' | 'discord' | 'code' | 'terminal' | 'general'
521
522function detectAppContext() {
523 try {
524 // Try to detect focused window title/process
525 // This works cross-platform via BrowserWindow.getFocusedWindow fallback
526 const focused = BrowserWindow.getFocusedWindow()
527 const title = focused?.getTitle()?.toLowerCase() || ''
528
529 // Check window title for app hints
530 if (title.includes('gmail') || title.includes('outlook') || title.includes('mail') || title.includes('thunderbird')) return 'email'
531 if (title.includes('slack')) return 'slack'
532 if (title.includes('discord')) return 'discord'
533 if (title.includes('vs code') || title.includes('visual studio') || title.includes('intellij') || title.includes('webstorm') || title.includes('sublime') || title.includes('vim') || title.includes('neovim')) return 'code'
534 if (title.includes('terminal') || title.includes('iterm') || title.includes('powershell') || title.includes('cmd') || title.includes('warp')) return 'terminal'
535 if (title.includes('notion') || title.includes('google docs') || title.includes('word')) return 'general'
536
537 return 'general'
538 } catch {
539 return 'general'
540 }
541}
542
442543// ─── IPC Handlers ──────────────────────────────────────────────
443544ipcMain.handle('get-settings', () => {
444545 return store.store
@@ -482,6 +583,19 @@ ipcMain.on('transcription-ready', async (_, text) => {
482583 output = wrapCode(output)
483584 }
484585
586 // AI Rewrite Mode — the killer feature
587 if (settings.aiRewrite && settings.claudeApiKey && !cmd) {
588 if (overlayWindow) {
589 overlayWindow.webContents.send('recording-state', { status: 'rewriting', text: output })
590 }
591 try {
592 output = await rewriteWithAI(output, settings)
593 } catch (err) {
594 console.error('[48co] AI rewrite failed, using original:', err.message)
595 // Fall through with original text — never lose the user's words
596 }
597 }
598
485599 // Show typing state
486600 if (overlayWindow) {
487601 overlayWindow.webContents.send('recording-state', { status: 'typing', text: output })
Modifieddesktop/renderer/overlay.html+1−0View fileUnifiedSplit
@@ -172,6 +172,7 @@
172172 idle: { label: 'READY', class: 'recording' },
173173 recording: { label: 'LISTENING', class: 'recording' },
174174 processing: { label: 'TRANSCRIBING', class: 'processing' },
175 rewriting: { label: 'AI REWRITE', class: 'typing' },
175176 typing: { label: 'TYPING', class: 'typing' },
176177 done: { label: 'DONE', class: 'done' },
177178 error: { label: 'ERROR', class: 'error' },
Modifieddesktop/renderer/settings.html+55−0View fileUnifiedSplit
@@ -281,6 +281,35 @@
281281 </div>
282282 </div>
283283
284 <!-- AI Rewrite Mode -->
285 <div class="section">
286 <div class="section-title">AI Rewrite Mode</div>
287
288 <div class="toggle-row">
289 <div>
290 <span class="toggle-label">Enable AI Rewrite</span>
291 <div class="desc">Polishes your dictated text using Claude AI. Removes filler words, fixes grammar, adjusts tone automatically based on which app you're in.</div>
292 </div>
293 <button class="toggle" id="aiRewrite"></button>
294 </div>
295
296 <div class="input-row" id="claude-key-row" style="display:none">
297 <span class="input-label">Claude API Key</span>
298 <input type="password" id="claudeApiKey" placeholder="sk-ant-..." />
299 <div class="desc">Get your key at <a href="https://console.anthropic.com/settings/keys" style="color:#00f0ff" target="_blank">console.anthropic.com</a>. Costs ~$0.003 per rewrite.</div>
300 </div>
301
302 <div class="input-row" id="rewrite-mode-row" style="display:none">
303 <span class="input-label">Default Mode</span>
304 <select id="aiRewriteMode">
305 <option value="professional">Professional</option>
306 <option value="casual">Casual</option>
307 <option value="concise">Concise</option>
308 </select>
309 <div class="desc">Auto-overridden when app context is detected (Email → Professional, Slack → Casual, Code → Technical)</div>
310 </div>
311 </div>
312
284313 <!-- Behaviour -->
285314 <div class="section">
286315 <div class="section-title">Behaviour</div>
@@ -394,6 +423,32 @@
394423 await window.foureightco.setSetting('language', e.target.value)
395424 })
396425
426 // AI Rewrite settings
427 const aiRewriteToggle = $('aiRewrite')
428 if (settings.aiRewrite) aiRewriteToggle.classList.add('on')
429 function updateAIRewriteVisibility() {
430 const show = aiRewriteToggle.classList.contains('on') ? 'block' : 'none'
431 $('claude-key-row').style.display = show
432 $('rewrite-mode-row').style.display = show
433 }
434 updateAIRewriteVisibility()
435
436 aiRewriteToggle.addEventListener('click', async () => {
437 aiRewriteToggle.classList.toggle('on')
438 await window.foureightco.setSetting('aiRewrite', aiRewriteToggle.classList.contains('on'))
439 updateAIRewriteVisibility()
440 })
441
442 $('claudeApiKey').value = settings.claudeApiKey || ''
443 $('claudeApiKey').addEventListener('change', async (e) => {
444 await window.foureightco.setSetting('claudeApiKey', e.target.value)
445 })
446
447 $('aiRewriteMode').value = settings.aiRewriteMode || 'professional'
448 $('aiRewriteMode').addEventListener('change', async (e) => {
449 await window.foureightco.setSetting('aiRewriteMode', e.target.value)
450 })
451
397452 // Vocabulary
398453 renderList('vocab', settings.vocabulary || [], 'vocabulary')
399454 $('vocab-add').addEventListener('click', async () => {
400455
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts