Merge pull request #1063 from enricoros/claude/issue-1061-20260406-2309

fix: hide voice features in browsers without Speech Recognition support
This commit is contained in:
Enrico Ros
2026-04-08 19:00:22 -07:00
committed by GitHub
3 changed files with 11 additions and 5 deletions
@@ -658,6 +658,7 @@ export function Composer(props: {
const showChatInReferenceTo = !!inReferenceTo?.length;
const showChatExtras = isText && !showChatInReferenceTo && !assistantAbortible && composerQuickButton !== 'off';
const speechMayWork = browserSpeechRecognitionCapability().mayWork;
const sendButtonVariant: VariantProp = (isAppend || (isMobile && isTextBeam)) ? 'outlined' : 'solid';
@@ -964,7 +965,7 @@ export function Composer(props: {
{/* [mobile] bottom-corner secondary button */}
{isMobile && (showChatExtras
? (composerQuickButton === 'call'
? (composerQuickButton === 'call' && speechMayWork
? <ButtonCallMemo isMobile disabled={noConversation || noLLM} onClick={handleCallClicked} />
: <ButtonBeamMemo isMobile disabled={noConversation /*|| noLLM*/} color={beamButtonColor} hasContent={!!composeText} onClick={handleSendTextBeamClicked} />)
: isDraw
@@ -1055,8 +1056,8 @@ export function Composer(props: {
{/* [desktop] secondary bottom-buttons (aligned to bottom for now, and mutually exclusive) */}
{isDesktop && <Box sx={{ mt: 'auto', display: 'grid', gap: 1 }}>
{/* [desktop] Call secondary button */}
{showChatExtras && <ButtonCallMemo disabled={noConversation || noLLM || assistantAbortible} onClick={handleCallClicked} />}
{/* [desktop] Call secondary button - hidden when speech recognition is not available */}
{showChatExtras && speechMayWork && <ButtonCallMemo disabled={noConversation || noLLM || assistantAbortible} onClick={handleCallClicked} />}
{/* [desktop] Draw Options secondary button */}
{isDraw && <ButtonOptionsDraw onClick={handleDrawOptionsClicked} />}
@@ -18,13 +18,17 @@ let cachedCapability: CapabilityBrowserSpeechRecognition | null = null;
export const browserSpeechRecognitionCapability = (): CapabilityBrowserSpeechRecognition => {
if (!cachedCapability) {
const isApiAvailable = !!getSpeechRecognitionClass();
const isBraveBlocked = Is.Browser.Brave; // Brave exposes the API but silently blocks results
const isApiAvailable = !!getSpeechRecognitionClass() && !isBraveBlocked;
const isDeviceNotSupported = false;
cachedCapability = {
mayWork: isApiAvailable && !isDeviceNotSupported,
isApiAvailable,
isDeviceNotSupported,
warnings: Is.OS.iOS ? ['Not tested on this browser/device.'] : [],
warnings: [
...Is.OS.iOS ? ['Not tested on this browser/device.'] : [],
...isBraveBlocked ? ['Speech recognition is not supported in Brave. Please use Chrome, Edge, or Safari.'] : [],
],
};
}
return cachedCapability;
+1
View File
@@ -11,6 +11,7 @@ const _safeUA = isBrowser ? window.navigator?.userAgent.toLowerCase() || '' : ''
export const Is = {
Desktop: !/mobile|android|iphone|ipad|ipod/.test(_safeUA),
Browser: {
Brave: isBrowser && !!(navigator as any).brave,
Chrome: _safeUA.includes('chrome') || _safeUA.includes('crios'),
get Safari() {
return _safeUA.includes('safari') && !this.Chrome && !_safeUA.includes('chromium');