From bbf575e491d3e620b0b1a85304f858269bdbd0fe Mon Sep 17 00:00:00 2001 From: Luc Mcgrady Date: Fri, 31 Oct 2025 12:06:58 +0000 Subject: [PATCH] Added: TypeAnswer replacement --- proto/anki/scheduler.proto | 1 + rslib/src/scheduler/service/mod.rs | 39 ++++++++++++++++++++++++++- ts/routes/reviewer/reviewer.ts | 6 ++++- ts/routes/reviewer/reviewerRequest.ts | 7 ++++- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index 3588c4117..2cf0108f8 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -306,6 +306,7 @@ message NextCardDataResponse { string css = 5; string body_class = 6; bool autoplay = 7; + optional string typed_answer = 12; repeated card_rendering.AVTag question_av_tags = 8; repeated card_rendering.AVTag answer_av_tags = 9; diff --git a/rslib/src/scheduler/service/mod.rs b/rslib/src/scheduler/service/mod.rs index addbc4c87..5ba32acb9 100644 --- a/rslib/src/scheduler/service/mod.rs +++ b/rslib/src/scheduler/service/mod.rs @@ -4,6 +4,8 @@ mod answering; mod states; +use std::sync::LazyLock; + use anki_proto::cards; use anki_proto::generic; use anki_proto::scheduler; @@ -25,6 +27,7 @@ use fsrs::ComputeParametersInput; use fsrs::FSRSItem; use fsrs::FSRSReview; use fsrs::FSRS; +use regex::Regex; use crate::backend::Backend; use crate::card_rendering::service::rendered_nodes_to_proto; @@ -34,7 +37,9 @@ use crate::scheduler::new::NewCardDueOrder; use crate::scheduler::states::CardState; use crate::scheduler::states::SchedulingStates; use crate::search::SortMode; +use crate::services::NotesService; use crate::stats::studied_today; +use crate::template::RenderedNode; impl crate::services::SchedulerService for Collection { /// This behaves like _updateCutoff() in older code - it also unburies at @@ -411,16 +416,48 @@ impl crate::services::SchedulerService for Collection { let config = self.deck_config_for_card(&next_card.card)?; + // Typed answer replacements + static ANSWER_REGEX: LazyLock = + LazyLock::new(|| Regex::new(r"\[\[type:(.+?:)?(.+?)\]\]").unwrap()); + + const ANSWER_HTML: &str = "
+ +
"; + + let mut q_nodes = render.qnodes; + let typed_answer_parent_node = q_nodes.iter_mut().find_map(|node| { + if let RenderedNode::Text { text } = node { + let mut out = None; + *text = ANSWER_REGEX + .replace(text, |cap: ®ex::Captures<'_>| { + out = Some(cap[2].to_string()); + ANSWER_HTML + }) + .to_string(); + out + } else { + None + } + }); + + let typed_answer = typed_answer_parent_node.map(|field| { + let note = self.get_note(next_card.card.note_id.into()).unwrap(); + let notetype = self.get_notetype(note.notetype_id.into()).unwrap().unwrap(); + note.fields[notetype.get_field_ord(&field).unwrap()].clone() + }); + Ok(NextCardDataResponse { next_card: Some(NextCardData { queue: Some(queue.into()), css: render.css.clone(), - partial_front: rendered_nodes_to_proto(render.qnodes), + partial_front: rendered_nodes_to_proto(q_nodes), partial_back: rendered_nodes_to_proto(render.anodes), answer_buttons, autoplay: !config.inner.disable_autoplay, + typed_answer, // Filled by python front: "".to_string(), diff --git a/ts/routes/reviewer/reviewer.ts b/ts/routes/reviewer/reviewer.ts index cac8429fd..40149c457 100644 --- a/ts/routes/reviewer/reviewer.ts +++ b/ts/routes/reviewer/reviewer.ts @@ -27,6 +27,7 @@ export function updateNightMode() { export class ReviewerState { answerHtml = ""; + currentTypedAnswer = ""; _cardData: NextCardDataResponse_NextCardData | undefined = undefined; beginAnsweringMs = Date.now(); readonly cardClass = writable(""); @@ -42,13 +43,16 @@ export class ReviewerState { addEventListener("message", this.onMessage.bind(this)); } - onMessage(e: MessageEvent) { + async onMessage(e: MessageEvent) { switch (e.data.type) { case "audio": { const tags = get(this.answerShown) ? this._cardData!.answerAvTags : this._cardData!.questionAvTags; playAvtags({ tags: [tags[e.data.index]] }); break; } + case "typed": { + this.currentTypedAnswer = e.data.value; + } } } diff --git a/ts/routes/reviewer/reviewerRequest.ts b/ts/routes/reviewer/reviewerRequest.ts index aa4d3c786..6cefa1919 100644 --- a/ts/routes/reviewer/reviewerRequest.ts +++ b/ts/routes/reviewer/reviewerRequest.ts @@ -6,4 +6,9 @@ interface AudioMessage { index: number; } -export type ReviewerRequest = AudioMessage; +interface CompareTypedAnswerMessage { + type: "typed"; + value: string; +} + +export type ReviewerRequest = AudioMessage | CompareTypedAnswerMessage;