diff --git a/apps/web/src/hooks/useBrowserDetection.tsx b/apps/web/src/hooks/useBrowserDetection.tsx new file mode 100644 index 000000000..da7c5d87d --- /dev/null +++ b/apps/web/src/hooks/useBrowserDetection.tsx @@ -0,0 +1,45 @@ +import { useMemo } from 'react'; + +interface BrowserInfo { + isEdge: boolean; + isChrome: boolean; + isSafari: boolean; + isFirefox: boolean; + browserName: string; +} + +export function useBrowserDetection(): BrowserInfo { + const browserInfo = useMemo(() => { + if (typeof window === 'undefined' || typeof navigator === 'undefined') { + return { + isEdge: false, + isChrome: false, + isSafari: false, + isFirefox: false, + browserName: 'unknown', + }; + } + + const userAgent = navigator.userAgent.toLowerCase(); + const isEdge = userAgent.includes('edg/'); + const isChrome = userAgent.includes('chrome') && !isEdge; + const isSafari = userAgent.includes('safari') && !isChrome && !isEdge; + const isFirefox = userAgent.includes('firefox'); + + let browserName = 'unknown'; + if (isEdge) browserName = 'edge'; + else if (isChrome) browserName = 'chrome'; + else if (isSafari) browserName = 'safari'; + else if (isFirefox) browserName = 'firefox'; + + return { + isEdge, + isChrome, + isSafari, + isFirefox, + browserName, + }; + }, []); + + return browserInfo; +} diff --git a/apps/web/src/hooks/useSpeechRecognition.tsx b/apps/web/src/hooks/useSpeechRecognition.tsx index a5a85cfbc..d70b9d25b 100644 --- a/apps/web/src/hooks/useSpeechRecognition.tsx +++ b/apps/web/src/hooks/useSpeechRecognition.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { openErrorNotification } from '@/context/BusterNotifications'; +import { useBrowserDetection } from './useBrowserDetection'; // Type definitions for Web Speech API interface SpeechRecognitionErrorEvent extends Event { @@ -54,10 +55,14 @@ export function useSpeechRecognition(): UseSpeechRecognitionReturn { const [error, setError] = useState(null); const [hasPermission, setHasPermission] = useState(false); const finalTranscriptRef = useRef(''); + const { isEdge, isFirefox } = useBrowserDetection(); - // Check browser support + // Check browser support - disable for Edge due to language support issues const browserSupportsSpeechRecognition = - typeof window !== 'undefined' && (window.SpeechRecognition || window.webkitSpeechRecognition); + !isEdge && + !isFirefox && + typeof window !== 'undefined' && + (window.SpeechRecognition || window.webkitSpeechRecognition); // Check microphone permission useEffect(() => { @@ -91,7 +96,8 @@ export function useSpeechRecognition(): UseSpeechRecognitionReturn { recognition.continuous = true; recognition.interimResults = true; - recognition.lang = 'en-US'; + // Don't set lang - let it use browser default + // Edge can be particular about language codes recognition.onstart = () => { setListening(true); @@ -131,7 +137,10 @@ export function useSpeechRecognition(): UseSpeechRecognitionReturn { openErrorNotification({ message }); setError(message); - onStopListening(); + // Stop recognition and listening state + recognition.stop(); + recognition.abort(); + setListening(false); }; recognition.onend = () => { diff --git a/apps/web/src/middleware/csp-helper.ts b/apps/web/src/middleware/csp-helper.ts index a69bd2776..0bce81881 100644 --- a/apps/web/src/middleware/csp-helper.ts +++ b/apps/web/src/middleware/csp-helper.ts @@ -48,6 +48,7 @@ export const createCspHeader = (isEmbed = false): string => { (() => { const connectSources = [ "'self'", + 'blob:', 'data:', // Allow data URLs for PDF exports and other data URI downloads localDomains, supabaseOrigin, @@ -63,6 +64,15 @@ export const createCspHeader = (isEmbed = false): string => { 'https://eu-assets.i.posthog.com', 'https://*.cloudflareinsights.com', 'https://*.slack.com', + // Speech recognition API and Google services + 'https://*.google.com', + 'https://*.googleapis.com', + 'https://apis.google.com', + 'https://ssl.gstatic.com', + 'https://www.google.com', + 'https://www.googletagmanager.com', + 'https://www.gstatic.com', + 'https://www.google-analytics.com', // Social media and video platform APIs for embeds 'https://*.twitter.com', 'https://twitter.com',