fix(dictation): gate ⌘D on focus staying inside composer

The focus-in handler registers a composer as the active dictation target
but never clears it on focus-out, so it stays active until unmount. Since
AppShell dispatches the primary-D shortcut globally (outside settings),
pressing ⌘D while focus is in quick search, a create-channel dialog, or
any other mounted input could start mic capture and append transcript
into the background composer's draft. Gate the shortcut on focus still
being contained in the composer element at keydown time.
This commit is contained in:
npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5
2026-07-11 16:20:30 +01:00
parent 5d847e5767
commit 4021694be6
@@ -121,13 +121,20 @@ export function useComposerDictation({
// ⌘D push-to-talk — hold to record, release to stop.
// Dispatched from AppShell's keydown/keyup handlers.
// Only the active (most recently focused) composer responds, and only when
// not disabled/send-blocked.
// Only the active (most recently focused) composer responds, and only while
// focus remains inside it and it is not disabled/send-blocked.
// biome-ignore lint/correctness/useExhaustiveDependencies: disabledRef/isSendBlockedRef are stable refs read at call time
useEffect(() => {
function handleKeyDown() {
// Only respond if this is the active composer instance.
if (!isActiveDictationComposer(instanceId)) return;
// Only respond if focus is still inside this composer. The active-composer
// registration persists after focusout (until unmount), so without this
// check, focusing a composer and then moving to another mounted input or
// dialog (quick search, create-channel, etc.) would still let ⌘D start
// microphone capture and append transcript into the background draft.
const el = composerRef?.current;
if (el && !el.contains(document.activeElement)) return;
// Don't start dictation in disabled/blocked composers.
if (disabledRef.current || isSendBlockedRef.current) return;
if (!dictation.isRecording && !dictation.isStarting) {
@@ -147,6 +154,7 @@ export function useComposerDictation({
};
}, [
instanceId,
composerRef,
dictation.isRecording,
dictation.isStarting,
dictation.startRecording,