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

This commit is contained in:
2026-09-04 01:40:56 -07:00
committed by GitHub
parent 99266294b8
commit 84f718043a
78 changed files with 7022 additions and 135 deletions
+73
View File
@@ -11,6 +11,78 @@ export type CardKind = 'sentence' | 'audio' | 'word-and-sentence' | 'click';
/** Card kind SubMiner flags on word cards; 'none' leaves the flag fields untouched. */
export type WordCardKind = CardKind | 'none';
export type MediaTimingReviewKind = 'word' | 'sentence' | 'audio';
export interface MediaTimingReviewRequest {
kind: MediaTimingReviewKind;
text: string;
startTime: number;
endTime: number;
noteId?: number;
audioPadding: number;
maxMediaDuration: number;
}
/** A subtitle line adjacent to the mined one that the review can pull onto the card. */
export interface MediaTimingReviewContextLine {
text: string;
startTime: number;
endTime: number;
}
export type MediaTimingReviewDecision =
/** `text` is set when the review combined adjacent lines into the card sentence. */
| { action: 'confirm'; startTime: number; endTime: number; text?: string }
| { action: 'use-original' }
| { action: 'skip-media' }
| { action: 'discard' };
export interface MediaTimingReviewOpenPayload {
reviewId: string;
kind: MediaTimingReviewKind;
text: string;
/** Lines before/after the mined one, both chronological: nearest previous line is last, nearest next line is first. */
previousLines: MediaTimingReviewContextLine[];
nextLines: MediaTimingReviewContextLine[];
noteId?: number;
originalStartTime: number;
originalEndTime: number;
selectionStartTime: number;
selectionEndTime: number;
timelineStartTime: number;
timelineEndTime: number;
mediaDuration?: number;
maxMediaDuration: number;
}
export interface MediaTimingReviewPreviewRequest {
reviewId: string;
startTime: number;
endTime: number;
}
export interface MediaTimingReviewWaveformRequest {
reviewId: string;
startTime: number;
endTime: number;
}
export interface MediaTimingReviewWaveformResult extends MediaTimingReviewActionResult {
peaks?: number[];
}
export interface MediaTimingReviewResolveRequest {
reviewId: string;
decision: MediaTimingReviewDecision;
}
export interface MediaTimingReviewActionResult {
ok: boolean;
message?: string;
/** The review this request targeted has already ended; the renderer should close. */
stale?: boolean;
}
export interface NotificationOptions {
body?: string;
icon?: string;
@@ -85,6 +157,7 @@ export interface AnkiConnectConfig {
syncAnimatedImageToWordAudio?: boolean;
normalizeAudio?: boolean;
mirrorMpvVolume?: boolean;
reviewTiming?: boolean;
audioPadding?: number;
fallbackDuration?: number;
maxMediaDuration?: number;