Added: AutoAdvance

This commit is contained in:
Luc Mcgrady 2025-11-19 17:54:52 +00:00
parent f3324b4a29
commit 549c58f3f3
No known key found for this signature in database
GPG key ID: 4F3D7A0B17CC3D9C
4 changed files with 88 additions and 10 deletions

View file

@ -327,6 +327,13 @@ message NextCardDataResponse {
repeated card_rendering.AVTag question_av_tags = 8;
repeated card_rendering.AVTag answer_av_tags = 9;
float autoAdvanceQuestionSeconds = 16;
float autoAdvanceAnswerSeconds = 17;
bool autoAdvanceWaitForAudio = 20;
deck_config.DeckConfig.Config.QuestionAction autoAdvanceQuestionAction = 18;
deck_config.DeckConfig.Config.AnswerAction autoAdvanceAnswerAction = 19;
// TODO: We can probably make this a little faster by using oneof and
// preventing the partial_front and back being sent to svelte where it isn't
// used. Alternatively we can use a completely different message for both

View file

@ -500,6 +500,13 @@ impl crate::services::SchedulerService for Collection {
marked,
timer,
auto_advance_answer_seconds: deck_config.seconds_to_show_answer,
auto_advance_question_seconds: deck_config.seconds_to_show_question,
auto_advance_wait_for_audio: deck_config.wait_for_audio,
auto_advance_answer_action: deck_config.answer_action,
auto_advance_question_action: deck_config.question_action,
// Filled by python
accept_enter: true,
front: "".to_string(),

View file

@ -24,10 +24,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{ colour: tr.actionsFlagPurple(), shortcut: "Ctrl+7" },
];
function todo() {
alert("Not yet implemented in new reviewer.");
}
const shortcuts: MoreMenuItemInfo[] = [
{
name: tr.studyingBuryCard(),
@ -128,11 +124,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{
name: tr.actionsAutoAdvance(),
shortcut: "Shift+A",
onClick: todo /* checked: autoAdvanceEnabled */,
onClick: () => state.toggleAutoAdvance(),
},
];
$: current_flag = state.flag;
// TOOD: Fix above capitals
$: autoAdvance = state.autoAdvance;
function prepKeycodeForShortcut(keycode: string) {
return keycode.replace("Ctrl", "Control");
@ -212,11 +210,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{#if shortcut == "hr"}
<hr />
{:else}
<div
style:background-color={shortcut.onClick == todo
? "RGBA(255,0,0,0.25)"
: ""}
>
{@const highlighted = shortcut.shortcut == "Shift+A" && $autoAdvance}
<div style:background-color={highlighted ? "RGBA(0,255,0,0.25)" : ""}>
<MoreItem
shortcut={shortcut.shortcut}
on:click={shortcut.onClick}

View file

@ -1,6 +1,7 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import type { UndoStatus } from "@generated/anki/collection_pb";
import { DeckConfig_Config_AnswerAction, DeckConfig_Config_QuestionAction } from "@generated/anki/deck_config_pb";
import { ReviewerActionRequest_ReviewerAction } from "@generated/anki/frontend_pb";
import {
BuryOrSuspendCardsRequest_Mode,
@ -64,10 +65,35 @@ export class ReviewerState {
readonly tooltipShown = writable(false);
readonly flag = writable(0);
readonly marked = writable(false);
readonly autoAdvance = writable(false);
undoStatus: UndoStatus | undefined = undefined;
autoAdvanceQuestionTimeout: ReturnType<typeof setTimeout> | undefined;
autoAdvanceAnswerTimeout: ReturnType<typeof setTimeout> | undefined;
iframe: HTMLIFrameElement | undefined = undefined;
constructor() {
this.autoAdvance.subscribe($autoAdvance => {
if (this._answerShown) {
this.updateAutoAdvanceAnswer();
} else {
this.updateAutoAdvanceQuestion();
}
if (!$autoAdvance) {
clearInterval(this.autoAdvanceQuestionTimeout);
clearInterval(this.autoAdvanceAnswerTimeout);
}
});
}
public toggleAutoAdvance() {
this.autoAdvance.update(($autoAdvance) => {
// Reversed because the $autoAdvance will be flipped by the return.
this.showTooltip($autoAdvance ? tr.actionsAutoAdvanceDeactivated() : tr.actionsAutoAdvanceActivated());
return !$autoAdvance;
});
}
async onReady() {
const { json } = await getConfigJson({ val: "reviewerStorage" });
this.sendInnerRequest({ type: "setstorage", json_buffer: json });
@ -323,6 +349,47 @@ export class ReviewerState {
this.sendInnerRequest({ type: "html", value: htmlString, css, bodyclass });
}
updateAutoAdvanceQuestion() {
clearTimeout(this.autoAdvanceAnswerTimeout);
if (get(this.autoAdvance) && this._cardData!.autoAdvanceQuestionSeconds) {
const action = ({
[DeckConfig_Config_QuestionAction.SHOW_ANSWER]: () => {
this.showAnswer();
},
[DeckConfig_Config_QuestionAction.SHOW_REMINDER]: () => {
this.showTooltip(tr.studyingQuestionTimeElapsed());
},
})[this._cardData!.autoAdvanceQuestionAction];
this.autoAdvanceQuestionTimeout = setTimeout(action, this._cardData!.autoAdvanceQuestionSeconds * 1000);
}
}
updateAutoAdvanceAnswer() {
clearTimeout(this.autoAdvanceQuestionTimeout);
if (get(this.autoAdvance) && this._cardData?.autoAdvanceAnswerSeconds) {
const action = ({
[DeckConfig_Config_AnswerAction.ANSWER_AGAIN]: () => {
this.easeButtonPressed(0);
},
[DeckConfig_Config_AnswerAction.ANSWER_HARD]: () => {
this.easeButtonPressed(1);
},
[DeckConfig_Config_AnswerAction.ANSWER_GOOD]: () => {
this.easeButtonPressed(2);
},
[DeckConfig_Config_AnswerAction.BURY_CARD]: () => {
this.buryOrSuspendCurrentCard(false);
},
[DeckConfig_Config_AnswerAction.SHOW_REMINDER]: () => {
this.showTooltip(tr.studyingAnswerTimeElapsed());
},
})[this._cardData.autoAdvanceAnswerAction];
this.autoAdvanceAnswerTimeout = setTimeout(action, this._cardData.autoAdvanceAnswerSeconds * 1000);
}
}
async showQuestion(answer: CardAnswer | null) {
if (answer !== null) {
this.setUndo(tr.actionsAnswerCard());
@ -352,6 +419,7 @@ export class ReviewerState {
this.beginAnsweringMs = Date.now();
this.answerMs = undefined;
this.updateAutoAdvanceQuestion();
}
get currentCard() {
@ -382,6 +450,7 @@ export class ReviewerState {
}
this.answerMs = Date.now();
this.updateHtml(await this.showTypedAnswer(this._cardData?.back || ""));
this.updateAutoAdvanceAnswer();
}
get _answerShown() {