feat(anki): add media timing review before card creation

- Add draggable timing previews with audio playback and exact media ranges
- Support cancellation choices, including deleting existing cards
This commit is contained in:
2026-08-16 14:18:39 -07:00
parent 2174e689a2
commit 1b1b062803
44 changed files with 2865 additions and 39 deletions
+18
View File
@@ -45,6 +45,7 @@ import { createCharacterDictionaryModal } from './modals/character-dictionary.js
import { createRuntimeOptionsModal } from './modals/runtime-options.js';
import { createSubsyncModal } from './modals/subsync.js';
import { createYoutubeTrackPickerModal } from './modals/youtube-track-picker.js';
import { createMediaTimingReviewModal } from './modals/media-timing-review.js';
import { createPositioningController } from './positioning.js';
import { createOverlayContentMeasurementReporter } from './overlay-content-measurement.js';
import { syncOverlayMouseIgnoreState } from './overlay-mouse-ignore.js';
@@ -114,6 +115,12 @@ const modalDescriptors = [
close: () => youtubePickerModal.closeYoutubePickerModal(),
suppressesSubtitles: true,
},
{
id: 'media-timing-review',
isOpen: () => ctx.state.mediaTimingReviewModalOpen,
close: () => mediaTimingReviewModal.requestCancel(),
suppressesSubtitles: true,
},
{
id: 'playlist-browser',
isOpen: () => ctx.state.playlistBrowserModalOpen,
@@ -265,6 +272,10 @@ const playlistBrowserModal = createPlaylistBrowserModal(ctx, {
modalStateReader: { isAnyModalOpen },
syncSettingsModalSubtitleSuppression,
});
const mediaTimingReviewModal = createMediaTimingReviewModal(ctx, {
modalStateReader: { isAnyModalOpen },
syncSettingsModalSubtitleSuppression,
});
const keyboardHandlers = createKeyboardHandlers(ctx, {
handleRuntimeOptionsKeydown: runtimeOptionsModal.handleRuntimeOptionsKeydown,
handleCharacterDictionaryKeydown: characterDictionaryModal.handleCharacterDictionaryKeydown,
@@ -273,6 +284,7 @@ const keyboardHandlers = createKeyboardHandlers(ctx, {
handleJimakuKeydown: jimakuModal.handleJimakuKeydown,
handleTsukihimeKeydown: tsukihimeModal.handleTsukihimeKeydown,
handleYoutubePickerKeydown: youtubePickerModal.handleYoutubePickerKeydown,
handleMediaTimingReviewKeydown: mediaTimingReviewModal.handleMediaTimingReviewKeydown,
handlePlaylistBrowserKeydown: playlistBrowserModal.handlePlaylistBrowserKeydown,
handleControllerSelectKeydown: controllerSelectModal.handleControllerSelectKeydown,
handleControllerDebugKeydown: controllerDebugModal.handleControllerDebugKeydown,
@@ -574,6 +586,11 @@ function registerModalOpenHandlers(): void {
youtubePickerModal.openYoutubePickerModal(payload);
});
});
window.electronAPI.onOpenMediaTimingReview((payload) => {
runGuarded('media-timing-review:open', () => {
mediaTimingReviewModal.openMediaTimingReviewModal(payload);
});
});
window.electronAPI.onOpenPlaylistBrowser(() => {
runGuardedAsync('playlist-browser:open', async () => {
await playlistBrowserModal.openPlaylistBrowserModal();
@@ -805,6 +822,7 @@ async function init(): Promise<void> {
jimakuModal.wireDomEvents();
tsukihimeModal.wireDomEvents();
youtubePickerModal.wireDomEvents();
mediaTimingReviewModal.wireDomEvents();
playlistBrowserModal.wireDomEvents();
kikuModal.wireDomEvents();
runtimeOptionsModal.wireDomEvents();