Bug fixes, browser compat, privacy messaging, and UX improvements#1
Open
hilaryduffrules-hash wants to merge 4 commits intomurdawkmedia:masterfrom
Open
Bug fixes, browser compat, privacy messaging, and UX improvements#1hilaryduffrules-hash wants to merge 4 commits intomurdawkmedia:masterfrom
hilaryduffrules-hash wants to merge 4 commits intomurdawkmedia:masterfrom
Conversation
The TypingArea component declared onInputChange as a required prop in its TypeScript interface, but: 1. The parent App.tsx never passes it (speech handles input, not keyboard) 2. The component body never uses it Making it optional (onInputChange?) eliminates the TypeScript type error and properly reflects that this is a voice-driven app. Also: apply cursor-blink CSS class to the active cursor span for a proper blinking animation instead of a static border.
Critical Bug Fixes: - Fix recognition restart race condition on quote completion. Previously, calling recognition.stop() inside handleSpeechInput would immediately trigger onend, which checked the stale isListening state (still true) and restarted recognition — fighting the intentional stop. Fixed with shouldRestartRef that is explicitly set to false before stopping and re-enabled 150ms after the next quote loads. - Fix hardcoded accuracy=100. Accuracy now tracks correct matched words vs total attempted spoken words, giving real feedback. - Fix resetTest not excluding current quote. resetTest now passes the current quote text to getRandomQuote() so you never immediately see the same quote again. - Fix alert() for errors. All error states now render inline UI banners instead of blocking browser alerts — much better UX. Browser Compatibility: - Add getSpeechRecognitionClass() helper that checks both window.SpeechRecognition and window.webkitSpeechRecognition - Detect unsupported browsers on mount and show a clear banner with browser-specific guidance (Chrome/Edge recommended, Firefox unsupported, Safari experimental feature note) - Handle all SpeechRecognitionError types explicitly: not-allowed/permission-denied → show how to re-enable mic in browser settings audio-capture → no mic connected message network → network error message no-speech → silent, let onend restart naturally aborted → intentional stop, ignore Mobile Support: - Detect mobile user agent and show 'Tap Restart to reset' instead of 'Press Tab to reset' (Tab key doesn't exist on touch keyboards) - Add viewport-fit=cover and overscroll-behavior:none (in index.html) for proper mobile layout Privacy Messaging: - Add a persistent privacy badge at the bottom of main content: '100% private — all speech processing happens locally in your browser. No audio ever leaves your device.' - Disable the Start Speaking button when browser is unsupported UX: - Add microphone emoji to Start Speaking button for clarity - Add aria-label to dark/light mode toggle button - Ellipsis on 'Listening…' (proper Unicode ellipsis) - Update WPM polling interval from 100ms to 250ms (reduces jitter)
The index.html was copied from a SwiftType template and still contained: - All og: and twitter: meta tags referencing SwiftType name/titles - URLs pointing to swifttype.pages.dev (wrong domain) - JSON-LD schema with name 'SwiftType' - Description mentioning 'typing test' not voice typing - Body background defaulting to light (#fbfbfd) contradicting dark-first UI Changes: - Updated all meta title/description/og/twitter tags for SwiftVoice - Updated JSON-LD schema: name, description, featureList - Corrected og:url / twitter:url placeholder to swiftvoice.murdawkmedia.com - Added Permissions-Policy header for microphone access - Added -webkit-backdrop-filter for Safari blur support - Added overscroll-behavior: none to prevent iOS elastic overscroll - Added -webkit-font-smoothing for proper macOS/iOS font rendering - Set body background to dark default (#161617) to match default theme - Removed stale GEMINI_API_KEY reference comment from importmap
…nsense The README was a copy of an AI Studio template that: - Instructed users to set a GEMINI_API_KEY (this app uses zero external APIs) - Did not describe the app at all - Had no browser compatibility table - Had no privacy explanation Rewrote with: - Clear description of what SwiftVoice actually does - Privacy explanation (Web Speech API, local processing) - Browser compatibility table (Chrome/Edge/Safari/Firefox) - Correct run instructions (just npm install && npm run dev, no env vars) - Build instructions - Feature list
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes several real bugs, improves cross-browser compatibility, adds clear privacy messaging, and improves error handling and mobile UX. All changes are based on a full read of every file in the repo.
Critical Bug Fixes
🐛 TypeScript type error:
onInputChangerequired but never passedTypingArea's interface declaredonInputChangeas a required prop, butApp.tsxnever passes it (speech handles input, not the keyboard) and the component never uses it. This was a compile-time TypeScript error. Fixed by making the prop optional (onInputChange?).🐛 Recognition restart race condition on quote completion
When all words were matched,
handleSpeechInputcalledrecognition.stop(), which immediately triggeredonend. Butonendchecked the staleisListeningstate (stilltrueat that point) and tried to restart recognition — fighting itself. Fixed with ashouldRestartRefboolean that is explicitly set tofalsebefore any intentional stop, and re-enabled after a 150ms delay once the new quote has loaded.🐛 Accuracy hardcoded to 100
const accuracy = 100;— the variable was set but never computed. Fixed: accuracy now tracksmatchedWords / attemptedWords × 100giving real feedback on voice recognition quality.🐛
resetTestcould repeat the same quotegetRandomQuote()was called without arguments, so it could pick the current quote again. Fixed: now passesquote.textto exclude it from the random pool.🐛
alert()used for all errorsBrowser
alert()calls for mic denial and unsupported browsers block the UI and have terrible UX on mobile. Replaced with inline, dismissable error banners.Browser Compatibility
Firefox doesn't support Web Speech API
Firefox has never shipped the Web Speech API. The app previously silently fell back to an
alert()pop-up. Now:disabledattributeSafari support improvements
-webkit-backdrop-filterfor nav blur in Safari-webkit-font-smoothingfor correct font renderingExplicit error handling for all
SpeechRecognitionErrortypesnot-allowedaudio-capturenetworkno-speechonendrestart naturally)abortedMobile Support
overscroll-behavior: noneto prevent iOS elastic overscroll ruining the layoutviewport-fit=coverfor proper iPhone notch handlingPrivacy Messaging
Added a persistent privacy badge visible throughout the session:
This is the core value prop of SwiftVoice and it wasn't communicated anywhere in the UI.
index.html: Stale SwiftType metadata fixed
The HTML file was forked from a SwiftType typing test template and still contained:
swifttype.pages.devname: "SwiftType"All updated for SwiftVoice.
README: Complete rewrite
The README was a copy of an AI Studio template that instructed users to set a
GEMINI_API_KEY. This app uses zero external APIs. Rewrote with:npm install && npm run dev, no env vars needed)Minor UX
aria-labelon dark/light toggle for accessibilitycursor-blinkCSS class