mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
Perfect Composer Mic shortcuts, focus, logic.
This commit is contained in:
@@ -114,6 +114,7 @@ function ShortcutItem(props: { shortcut: ShortcutObject }) {
|
||||
{/*{!!props.shortcut.altForNonMac && <ShortcutKey onClick={handleClicked}>{_platformAwareModifier('Alt')}</ShortcutKey>}*/}
|
||||
<ShortcutKey onClick={handleClicked}>{props.shortcut.key === 'Escape' ? 'Esc' : props.shortcut.key.toUpperCase()}</ShortcutKey>
|
||||
<Typography level='body-xs'>{props.shortcut.description}</Typography>
|
||||
{props.shortcut.endDecoratorIcon && <props.shortcut.endDecoratorIcon sx={{ fontSize: 'md' }} />}
|
||||
</ShortcutContainer>
|
||||
);
|
||||
}
|
||||
@@ -128,7 +129,10 @@ export function StatusBar() {
|
||||
// external state
|
||||
const labsShowShortcutBar = useUXLabsStore(state => state.labsShowShortcutBar);
|
||||
const shortcuts = useGlobalShortcutsStore(useShallow(state => {
|
||||
const visibleShortcuts = !labsShowShortcutBar ? [] : state.getAllShortcuts().filter(shortcut => !!shortcut.description);
|
||||
let visibleShortcuts = !labsShowShortcutBar ? [] : state.getAllShortcuts().filter(shortcut => !!shortcut.description);
|
||||
const maxLevel = Math.max(...visibleShortcuts.map(s => s.level ?? 0));
|
||||
if (maxLevel > 0)
|
||||
visibleShortcuts = visibleShortcuts.filter(s => s.level === maxLevel);
|
||||
visibleShortcuts.sort((a, b) => {
|
||||
// if they don't have a 'shift', they are sorted first
|
||||
if (a.shift !== b.shift)
|
||||
|
||||
@@ -42,7 +42,7 @@ import { supportsScreenCapture } from '~/common/util/screenCaptureUtils';
|
||||
import { useAppStateStore } from '~/common/state/store-appstate';
|
||||
import { useChatOverlayStore } from '~/common/chats/store-chat-overlay';
|
||||
import { useDebouncer } from '~/common/components/useDebouncer';
|
||||
import { useGlobalShortcuts } from '~/common/components/shortcuts/useGlobalShortcuts';
|
||||
import { ShortcutKey, ShortcutObject, useGlobalShortcuts } from '~/common/components/shortcuts/useGlobalShortcuts';
|
||||
import { useUICounter, useUIPreferencesStore } from '~/common/state/store-ui';
|
||||
import { useUXLabsStore } from '~/common/state/store-ux-labs';
|
||||
|
||||
@@ -419,10 +419,28 @@ export function Composer(props: {
|
||||
|
||||
// useMediaSessionCallbacks({ play: toggleRecognition, pause: toggleRecognition });
|
||||
|
||||
useGlobalShortcuts('Composer', React.useMemo(() => [
|
||||
...(browserSpeechRecognitionCapability().mayWork ? [{ key: 'm', ctrl: true, action: () => toggleRecognition(true), description: recognitionState.isActive ? 'Microphone · Stop & Send' : 'Microphone' }] : []),
|
||||
...(supportsClipboardRead ? [{ key: 'v', ctrl: true, shift: true, action: attachAppendClipboardItems, description: 'Attach Clipboard' }] : []),
|
||||
], [attachAppendClipboardItems, recognitionState.isActive, toggleRecognition]));
|
||||
useGlobalShortcuts('Composer', React.useMemo(() => {
|
||||
const composerShortcuts: ShortcutObject[] = [];
|
||||
if (supportsClipboardRead)
|
||||
composerShortcuts.push({ key: 'v', ctrl: true, shift: true, action: attachAppendClipboardItems, description: 'Attach Clipboard' });
|
||||
if (recognitionState.isActive) {
|
||||
composerShortcuts.push({ key: 'm', ctrl: true, action: () => toggleRecognition(true), description: 'Mic · Send', disabled: !recognitionState.hasSpeech, endDecoratorIcon: TelegramIcon as any, level: 1 });
|
||||
composerShortcuts.push({
|
||||
key: ShortcutKey.Esc, action: () => {
|
||||
setMicContinuation(false);
|
||||
toggleRecognition(false);
|
||||
}, description: 'Mic · Stop', level: 1,
|
||||
});
|
||||
} else if (browserSpeechRecognitionCapability().mayWork)
|
||||
composerShortcuts.push({
|
||||
key: 'm', ctrl: true, action: () => {
|
||||
// steal focus from the textarea, in case it has - so that enter cannot work against us
|
||||
(document.activeElement as HTMLElement)?.blur?.();
|
||||
toggleRecognition(false);
|
||||
}, description: 'Microphone',
|
||||
});
|
||||
return composerShortcuts;
|
||||
}, [attachAppendClipboardItems, recognitionState.hasSpeech, recognitionState.isActive, toggleRecognition]));
|
||||
|
||||
const micIsRunning = !!speechInterimResult;
|
||||
const micContinuationTrigger = micContinuation && !micIsRunning && !assistantAbortible && !recognitionState.errorMessage;
|
||||
|
||||
@@ -20,7 +20,7 @@ function _handleGlobalShortcutKeyDown(event: KeyboardEvent) {
|
||||
// Quick-out: either the key is escape/left/right, or we have a modifier key pressed -- otherwise we exit
|
||||
const lcEventKey = event.key.toLowerCase();
|
||||
if (lcEventKey !== 'escape' && lcEventKey !== 'arrowleft' && lcEventKey !== 'arrowright' &&
|
||||
!event.ctrlKey && !event.shiftKey && !event.altKey)
|
||||
!event.ctrlKey && !event.shiftKey && !event.altKey && lcEventKey !== 'enter')
|
||||
return;
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { SvgIcon } from '@mui/joy';
|
||||
|
||||
import { useGlobalShortcutsStore } from './store-global-shortcuts';
|
||||
|
||||
import { ensureGlobalShortcutHandler } from './globalShortcutsHandler';
|
||||
|
||||
|
||||
export const ShortcutKey = {
|
||||
Enter: 'Enter',
|
||||
Esc: 'Escape',
|
||||
Left: 'ArrowLeft',
|
||||
Right: 'ArrowRight',
|
||||
@@ -19,6 +22,8 @@ export interface ShortcutObject {
|
||||
disabled?: boolean;
|
||||
action: (() => void) | '_specialPrintShortcuts';
|
||||
description?: string;
|
||||
endDecoratorIcon?: typeof SvgIcon;
|
||||
level?: number; // if set, it will exclusively show icons at that level of priority and hide the others
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user