diff --git a/src/renderer/src/components/feature-tips/FeatureTipActions.tsx b/src/renderer/src/components/feature-tips/FeatureTipActions.tsx index 985732d2e50..f12f2613405 100644 --- a/src/renderer/src/components/feature-tips/FeatureTipActions.tsx +++ b/src/renderer/src/components/feature-tips/FeatureTipActions.tsx @@ -1,4 +1,4 @@ -import type { JSX } from 'react' +import type { JSX, Ref } from 'react' import { Loader2 } from 'lucide-react' import type { FeatureTip, FeatureTipAction } from '../../../../shared/feature-tips' import { Button } from '@/components/ui/button' @@ -17,7 +17,8 @@ export function FeatureTipActions({ onPrimaryAction, onSkip, showSkip = true, - fullWidth = false + fullWidth = false, + primaryButtonRef }: { currentTip: FeatureTip primaryBusy: boolean @@ -25,6 +26,7 @@ export function FeatureTipActions({ onSkip: () => void showSkip?: boolean fullWidth?: boolean + primaryButtonRef?: Ref }): JSX.Element { return ( <> @@ -34,6 +36,7 @@ export function FeatureTipActions({ ) : null} + } })) function getVoiceTip(): FeatureTip { @@ -56,7 +69,8 @@ function renderDialog(): string { describe('VoiceDictationTipDialog', () => { beforeEach(() => { shortcutMock.mockReturnValue({ keys: ['⌘', 'E'], doubleTap: false }) - formatShortcutMock.mockReturnValue([{ keys: ['⌘', 'E'], doubleTap: false }]) + dialogContentPropsMock.mockClear() + featureTipActionsPropsMock.mockClear() }) it('uses the established feature-tip layout and durable copy', () => { @@ -78,13 +92,43 @@ describe('VoiceDictationTipDialog', () => { expect(html).toContain('Settings → Voice') }) - it('falls back to the platform default shortcut when unassigned', () => { + it('uses generic instructions instead of a shortcut the user has unassigned', () => { shortcutMock.mockReturnValue({ keys: [], doubleTap: false }) - formatShortcutMock.mockReturnValue([{ keys: ['Ctrl', 'E'], doubleTap: false }]) const html = renderDialog() - expect(formatShortcutMock).toHaveBeenCalledWith('voice.dictation') - expect(html).toContain('Ctrl') + expect(html).toContain( + 'Assign a dictation shortcut before starting voice dictation in a focused pane.' + ) + expect(html).not.toContain('to start voice dictation. Press') + expect(html).not.toContain('Ctrl') + }) + + it('moves initial focus to the primary action', async () => { + const container = document.createElement('div') + document.body.appendChild(container) + const root = createRoot(container) + await act(async () => { + root.render( + + ) + }) + const event = { preventDefault: vi.fn() } as unknown as Event + + dialogContentPropsMock.mock.lastCall?.[0].onOpenAutoFocus(event) + + expect(event.preventDefault).toHaveBeenCalledOnce() + expect(document.activeElement?.textContent).toBe('Primary action') + + await act(async () => root.unmount()) + container.remove() }) }) diff --git a/src/renderer/src/components/feature-tips/VoiceDictationTipDialog.tsx b/src/renderer/src/components/feature-tips/VoiceDictationTipDialog.tsx index aa1cd3a41e2..5cdb98445f4 100644 --- a/src/renderer/src/components/feature-tips/VoiceDictationTipDialog.tsx +++ b/src/renderer/src/components/feature-tips/VoiceDictationTipDialog.tsx @@ -1,4 +1,4 @@ -import type { JSX } from 'react' +import { useRef, type JSX } from 'react' import type { FeatureTip } from '../../../../shared/feature-tips' import { ShortcutKeyCombo } from '@/components/ShortcutKeyCombo' import { Badge } from '@/components/ui/badge' @@ -10,7 +10,7 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog' -import { formatShortcutKeyComboDetails, useShortcutKeyDetails } from '@/hooks/useShortcutLabel' +import { useShortcutKeyDetails } from '@/hooks/useShortcutLabel' import { translate } from '@/i18n/i18n' import { FeatureTipActions } from './FeatureTipActions' import { VoiceDictationFeatureTipVisual } from './VoiceDictationFeatureTipVisual' @@ -33,15 +33,17 @@ export function VoiceDictationTipDialog({ onVoiceSettingsClick: () => void }): JSX.Element { const shortcut = useShortcutKeyDetails('voice.dictation') - const displayShortcut = - shortcut.keys.length > 0 ? shortcut : formatShortcutKeyComboDetails('voice.dictation')[0] + const primaryButtonRef = useRef(null) return ( event.preventDefault()} + onOpenAutoFocus={(event) => { + event.preventDefault() + primaryButtonRef.current?.focus() + }} >
@@ -56,37 +58,38 @@ export function VoiceDictationTipDialog({ {tip.title} - - {translate( - 'featureTips.voice.focusPaneInstruction', - 'Focus a terminal, editor, or agent prompt, then press' - )}{' '} - {displayShortcut && displayShortcut.keys.length > 0 ? ( + {shortcut.keys.length > 0 ? ( + + {translate( + 'featureTips.voice.focusPaneInstruction', + 'Focus a terminal, editor, or agent prompt, then press' + )}{' '} - ) : ( - translate('featureTips.voice.theShortcut', 'the dictation shortcut') - )}{' '} - {translate( - 'featureTips.voice.startInstruction', - 'to start voice dictation. Press' - )}{' '} - {displayShortcut && displayShortcut.keys.length > 0 ? ( + />{' '} + {translate( + 'featureTips.voice.startInstruction', + 'to start voice dictation. Press' + )}{' '} - ) : ( - translate('featureTips.voice.theShortcut', 'the dictation shortcut') - )}{' '} - {translate('featureTips.voice.stopInstruction', 'again to stop.')} - + />{' '} + {translate('featureTips.voice.stopInstruction', 'again to stop.')} + + ) : ( + + {translate( + 'featureTips.voice.unassignedInstruction', + 'Assign a dictation shortcut before starting voice dictation in a focused pane.' + )} + + )} {translate( 'featureTips.voice.settingsInstruction', @@ -113,6 +116,7 @@ export function VoiceDictationTipDialog({ onSkip={onSkip} showSkip={false} fullWidth + primaryButtonRef={primaryButtonRef} />
diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index a3310e6c540..0a86c43680b 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -14694,9 +14694,9 @@ "agentPromptTitle": "Agent prompt", "listening": "Listening...", "focusPaneInstruction": "Focus a terminal, editor, or agent prompt, then press", - "theShortcut": "the dictation shortcut", "startInstruction": "to start voice dictation. Press", "stopInstruction": "again to stop.", + "unassignedInstruction": "Assign a dictation shortcut before starting voice dictation in a focused pane.", "settingsInstruction": "Change the model, dictation mode, or shortcut anytime in", "settingsLink": "Settings → Voice" }