This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
// Use dynamic import to avoid module resolution issues in test
|
||||
const getDefaults = async () => {
|
||||
const mod = await import('../../helpers/shortcuts');
|
||||
return mod.loadShortcuts();
|
||||
};
|
||||
|
||||
describe('TTS play/pause shortcut', () => {
|
||||
it('should have onTTSPlayPause shortcut with space', async () => {
|
||||
const shortcuts = await getDefaults();
|
||||
expect(shortcuts.onTTSPlayPause).toEqual([' ']);
|
||||
});
|
||||
|
||||
it('should also have space in onGoRight as fallback', async () => {
|
||||
const shortcuts = await getDefaults();
|
||||
expect(shortcuts.onGoRight).toContain(' ');
|
||||
});
|
||||
});
|
||||
|
||||
describe('TTS navigation shortcuts', () => {
|
||||
it('should have onTTSGoNextSentence shortcut with ctrl+] and cmd+]', async () => {
|
||||
const shortcuts = await getDefaults();
|
||||
expect(shortcuts.onTTSGoNextSentence).toEqual(['ctrl+]', 'cmd+]']);
|
||||
});
|
||||
|
||||
it('should have onTTSGoPreviousSentence shortcut with ctrl+[ and cmd+[', async () => {
|
||||
const shortcuts = await getDefaults();
|
||||
expect(shortcuts.onTTSGoPreviousSentence).toEqual(['ctrl+[', 'cmd+[']);
|
||||
});
|
||||
|
||||
it('should have onTTSGoNextParagraph shortcut with ctrl+shift+} and cmd+shift+}', async () => {
|
||||
const shortcuts = await getDefaults();
|
||||
expect(shortcuts.onTTSGoNextParagraph).toEqual(['ctrl+shift+}', 'cmd+shift+}']);
|
||||
});
|
||||
|
||||
it('should have onTTSGoPreviousParagraph shortcut with ctrl+shift+{ and cmd+shift+{', async () => {
|
||||
const shortcuts = await getDefaults();
|
||||
expect(shortcuts.onTTSGoPreviousParagraph).toEqual(['ctrl+shift+{', 'cmd+shift+{']);
|
||||
});
|
||||
});
|
||||
@@ -230,6 +230,34 @@ const useBookShortcuts = ({ sideBarBookKey, bookKeys }: UseBookShortcutsProps) =
|
||||
eventDispatcher.dispatch(viewState?.ttsEnabled ? 'tts-stop' : 'tts-speak', { bookKey });
|
||||
};
|
||||
|
||||
const ttsPlayPause = () => {
|
||||
if (!sideBarBookKey) return false;
|
||||
const viewState = getViewState(sideBarBookKey);
|
||||
if (!viewState?.ttsEnabled) return false;
|
||||
eventDispatcher.dispatch('tts-toggle-play', { bookKey: sideBarBookKey });
|
||||
return true;
|
||||
};
|
||||
|
||||
const ttsGoNextSentence = () => {
|
||||
if (!sideBarBookKey) return;
|
||||
eventDispatcher.dispatch('tts-forward', { bookKey: sideBarBookKey, byMark: true });
|
||||
};
|
||||
|
||||
const ttsGoPreviousSentence = () => {
|
||||
if (!sideBarBookKey) return;
|
||||
eventDispatcher.dispatch('tts-backward', { bookKey: sideBarBookKey, byMark: true });
|
||||
};
|
||||
|
||||
const ttsGoNextParagraph = () => {
|
||||
if (!sideBarBookKey) return;
|
||||
eventDispatcher.dispatch('tts-forward', { bookKey: sideBarBookKey, byMark: false });
|
||||
};
|
||||
|
||||
const ttsGoPreviousParagraph = () => {
|
||||
if (!sideBarBookKey) return;
|
||||
eventDispatcher.dispatch('tts-backward', { bookKey: sideBarBookKey, byMark: false });
|
||||
};
|
||||
|
||||
const toggleBookmark = () => {
|
||||
if (!sideBarBookKey) return;
|
||||
eventDispatcher.dispatch('toggle-bookmark', { bookKey: sideBarBookKey });
|
||||
@@ -280,6 +308,11 @@ const useBookShortcuts = ({ sideBarBookKey, bookKeys }: UseBookShortcutsProps) =
|
||||
onShowSearchBar: showSearchBar,
|
||||
onToggleFullscreen: toggleFullscreen,
|
||||
onToggleTTS: toggleTTS,
|
||||
onTTSPlayPause: ttsPlayPause,
|
||||
onTTSGoNextSentence: ttsGoNextSentence,
|
||||
onTTSGoPreviousSentence: ttsGoPreviousSentence,
|
||||
onTTSGoNextParagraph: ttsGoNextParagraph,
|
||||
onTTSGoPreviousParagraph: ttsGoPreviousParagraph,
|
||||
onReloadPage: reloadPage,
|
||||
onCloseWindow: closeWindow,
|
||||
onQuitApp: quitApp,
|
||||
|
||||
@@ -59,12 +59,56 @@ export const useTTSControl = ({ bookKey, onRequestHidePanel }: UseTTSControlProp
|
||||
deinitMediaSession,
|
||||
} = useTTSMediaSession({ bookKey });
|
||||
|
||||
const handleTTSForward = async (event: CustomEvent) => {
|
||||
const detail = event.detail as { bookKey: string; byMark?: boolean } | undefined;
|
||||
if (detail?.bookKey !== bookKey) return;
|
||||
const ttsController = ttsControllerRef.current;
|
||||
if (ttsController) {
|
||||
await ttsController.forward(detail?.byMark ?? false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTTSBackward = async (event: CustomEvent) => {
|
||||
const detail = event.detail as { bookKey: string; byMark?: boolean } | undefined;
|
||||
if (detail?.bookKey !== bookKey) return;
|
||||
const ttsController = ttsControllerRef.current;
|
||||
if (ttsController) {
|
||||
await ttsController.backward(detail?.byMark ?? false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTTSTogglePlay = async (event: CustomEvent) => {
|
||||
const detail = event.detail as { bookKey: string } | undefined;
|
||||
if (detail?.bookKey !== bookKey) return;
|
||||
const ttsController = ttsControllerRef.current;
|
||||
if (!ttsController) return;
|
||||
if (ttsController.state === 'playing') {
|
||||
setIsPlaying(false);
|
||||
setIsPaused(true);
|
||||
await ttsController.pause();
|
||||
} else {
|
||||
setIsPlaying(true);
|
||||
setIsPaused(false);
|
||||
if (ttsController.state === 'paused') {
|
||||
await ttsController.resume();
|
||||
} else {
|
||||
await ttsController.start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
eventDispatcher.on('tts-speak', handleTTSSpeak);
|
||||
eventDispatcher.on('tts-stop', handleTTSStop);
|
||||
eventDispatcher.on('tts-forward', handleTTSForward);
|
||||
eventDispatcher.on('tts-backward', handleTTSBackward);
|
||||
eventDispatcher.on('tts-toggle-play', handleTTSTogglePlay);
|
||||
return () => {
|
||||
eventDispatcher.off('tts-speak', handleTTSSpeak);
|
||||
eventDispatcher.off('tts-stop', handleTTSStop);
|
||||
eventDispatcher.off('tts-forward', handleTTSForward);
|
||||
eventDispatcher.off('tts-backward', handleTTSBackward);
|
||||
eventDispatcher.off('tts-toggle-play', handleTTSTogglePlay);
|
||||
if (ttsControllerRef.current) {
|
||||
ttsControllerRef.current.shutdown();
|
||||
ttsControllerRef.current = null;
|
||||
|
||||
@@ -7,6 +7,11 @@ const DEFAULT_SHORTCUTS = {
|
||||
onToggleSelectMode: ['shift+s'],
|
||||
onToggleBookmark: ['ctrl+d', 'cmd+d'],
|
||||
onToggleTTS: ['t'],
|
||||
onTTSPlayPause: [' '],
|
||||
onTTSGoNextSentence: ['ctrl+]', 'cmd+]'],
|
||||
onTTSGoPreviousSentence: ['ctrl+[', 'cmd+['],
|
||||
onTTSGoNextParagraph: ['ctrl+shift+}', 'cmd+shift+}'],
|
||||
onTTSGoPreviousParagraph: ['ctrl+shift+{', 'cmd+shift+{'],
|
||||
onToggleParagraphMode: ['shift+p'],
|
||||
onHighlightSelection: ['ctrl+h', 'cmd+h'],
|
||||
onUnderlineSelection: ['ctrl+u', 'cmd+u'],
|
||||
@@ -19,7 +24,7 @@ const DEFAULT_SHORTCUTS = {
|
||||
onReadAloudSelection: ['ctrl+r', 'cmd+r'],
|
||||
onProofreadSelection: ['ctrl+p', 'cmd+p'],
|
||||
onOpenFontLayoutSettings: ['shift+f', 'ctrl+,', 'cmd+,'],
|
||||
onOpenCommandPalette: ['ctrl+Shift+p', 'cmd+Shift+p'],
|
||||
onOpenCommandPalette: ['ctrl+shift+p', 'cmd+shift+p'],
|
||||
onOpenBooks: ['ctrl+o'],
|
||||
onReloadPage: ['shift+r'],
|
||||
onToggleFullscreen: ['F11'],
|
||||
|
||||
Reference in New Issue
Block a user