Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add scripting field to card template #3762

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ftl/core/card-templates.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ card-templates-card-type = Card Type:
card-templates-front-template = Front Template
card-templates-back-template = Back Template
card-templates-template-styling = Styling
card-templates-template-scripting = Scripting
card-templates-front-preview = Front Preview
card-templates-back-preview = Back Preview
card-templates-preview-box = Preview
Expand Down
1 change: 1 addition & 0 deletions proto/anki/card_rendering.proto
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ message RenderCardResponse {
repeated RenderedTemplateNode answer_nodes = 2;
string css = 3;
bool latex_svg = 4;
string js = 5;
}

message RenderedTemplateNode {
Expand Down
1 change: 1 addition & 0 deletions proto/anki/notetypes.proto
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ message Notetype {
StockNotetype.OriginalStockKind original_stock_kind = 9;
// the id in the source collection for imported notetypes (Anki 23.10)
optional int64 original_id = 10;
string js = 11;

bytes other = 255;
}
Expand Down
12 changes: 9 additions & 3 deletions pylib/anki/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class PartiallyRenderedCard:
qnodes: TemplateReplacementList
anodes: TemplateReplacementList
css: str
js: str
latex_svg: bool

@classmethod
Expand All @@ -68,7 +69,7 @@ def from_proto(
qnodes = cls.nodes_from_proto(out.question_nodes)
anodes = cls.nodes_from_proto(out.answer_nodes)

return PartiallyRenderedCard(qnodes, anodes, out.css, out.latex_svg)
return PartiallyRenderedCard(qnodes, anodes, out.css, out.js, out.latex_svg)

@staticmethod
def nodes_from_proto(
Expand Down Expand Up @@ -242,6 +243,7 @@ def render(self) -> TemplateRenderOutput:
question_av_tags=av_tags_to_native(qout.av_tags),
answer_av_tags=av_tags_to_native(aout.av_tags),
css=partial.css,
js=partial.js,
)

self._latex_svg = partial.latex_svg
Expand All @@ -264,6 +266,7 @@ def _partially_render(self) -> PartiallyRenderedCard:
# when rendering card layout, the css changes have not been
# committed; we need the current notetype instance instead
out.css = self._note_type["css"]
out.js = self._note_type["js"]
else:
# existing card (eg study mode)
out = self._col._backend.render_existing_card(
Expand All @@ -280,12 +283,15 @@ class TemplateRenderOutput:
question_av_tags: list[AVTag]
answer_av_tags: list[AVTag]
css: str = ""
js: str = ""

def question_and_style(self) -> str:
return f"<style>{self.css}</style>{self.question_text}"
return (
f"<style>{self.css}</style><script>{self.js}</script>{self.question_text}"
)

def answer_and_style(self) -> str:
return f"<style>{self.css}</style>{self.answer_text}"
return f"<style>{self.css}</style><script>{self.js}</script>{self.answer_text}"


# legacy
Expand Down
51 changes: 40 additions & 11 deletions qt/aqt/clayout.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import re
from collections.abc import Callable
from concurrent.futures import Future
from enum import Enum, auto
from typing import Any, Match, cast

import aqt
Expand Down Expand Up @@ -45,6 +46,13 @@
from aqt.webview import AnkiWebView, AnkiWebViewKind


class CurrentEditor(Enum):
FRONT = auto()
BACK = auto()
STYLE = auto()
SCRIPT = auto()


class CardLayout(QDialog):
def __init__(
self,
Expand Down Expand Up @@ -184,6 +192,7 @@ def setupShortcuts(self) -> None:
self.tform.front_button.setToolTip(shortcut("Ctrl+1"))
self.tform.back_button.setToolTip(shortcut("Ctrl+2"))
self.tform.style_button.setToolTip(shortcut("Ctrl+3"))
self.tform.script_button.setToolTip(shortcut("Ctrl+4"))
QShortcut( # type: ignore
QKeySequence("Ctrl+1"),
self,
Expand All @@ -199,6 +208,11 @@ def setupShortcuts(self) -> None:
self,
activated=self.tform.style_button.click,
)
QShortcut( # type: ignore
QKeySequence("Ctrl+4"),
self,
activated=self.tform.script_button.click,
)
QShortcut( # type: ignore
QKeySequence("F3"),
self,
Expand Down Expand Up @@ -257,6 +271,7 @@ def setup_edit_area(self) -> None:
tform.front_button.setText(tr.card_templates_front_template())
tform.back_button.setText(tr.card_templates_back_template())
tform.style_button.setText(tr.card_templates_template_styling())
tform.script_button.setText(tr.card_templates_template_scripting())
tform.template_box.setTitle(tr.card_templates_template_box())

cnt = self.mw.col.models.use_count(self.model)
Expand All @@ -268,8 +283,9 @@ def setup_edit_area(self) -> None:
qconnect(tform.front_button.clicked, self.on_editor_toggled)
qconnect(tform.back_button.clicked, self.on_editor_toggled)
qconnect(tform.style_button.clicked, self.on_editor_toggled)
qconnect(tform.script_button.clicked, self.on_editor_toggled)

self.current_editor_index = 0
self.current_editor = CurrentEditor.FRONT
editor.setAcceptRichText(False)
font = QFont("Consolas")
if not font.exactMatch():
Expand Down Expand Up @@ -316,17 +332,20 @@ def on_change_cloze(self, idx: int) -> None:

def on_editor_toggled(self) -> None:
if self.tform.front_button.isChecked():
self.current_editor_index = 0
self.current_editor = CurrentEditor.FRONT
self.pform.preview_front.setChecked(True)
self.on_preview_toggled()
self.add_field_button.setHidden(False)
elif self.tform.back_button.isChecked():
self.current_editor_index = 1
self.current_editor = CurrentEditor.BACK
self.pform.preview_back.setChecked(True)
self.on_preview_toggled()
self.add_field_button.setHidden(False)
else:
self.current_editor_index = 2
elif self.tform.style_button.isChecked():
self.current_editor = CurrentEditor.STYLE
self.add_field_button.setHidden(True)
elif self.tform.script_button.isChecked():
self.current_editor = CurrentEditor.SCRIPT
self.add_field_button.setHidden(True)

self.fill_fields_from_template()
Expand Down Expand Up @@ -486,12 +505,16 @@ def fill_fields_from_template(self) -> None:
t = self.current_template()
self.ignore_change_signals = True

if self.current_editor_index == 0:
if self.current_editor == CurrentEditor.FRONT:
text = t["qfmt"]
elif self.current_editor_index == 1:
elif self.current_editor == CurrentEditor.BACK:
text = t["afmt"]
else:
elif self.current_editor == CurrentEditor.STYLE:
text = self.model["css"]
elif self.current_editor == CurrentEditor.SCRIPT:
text = self.model["js"]
else:
raise Exception("invalid editor state")

self.tform.edit_area.setPlainText(text)
self.ignore_change_signals = False
Expand All @@ -504,12 +527,14 @@ def write_edits_to_template_and_redraw(self) -> None:

text = self.tform.edit_area.toPlainText()

if self.current_editor_index == 0:
if self.current_editor == CurrentEditor.FRONT:
self.current_template()["qfmt"] = text
elif self.current_editor_index == 1:
elif self.current_editor == CurrentEditor.BACK:
self.current_template()["afmt"] = text
else:
elif self.current_editor == CurrentEditor.STYLE:
self.model["css"] = text
elif self.current_editor == CurrentEditor.SCRIPT:
self.model["js"] = text

self.renderPreview()

Expand Down Expand Up @@ -773,6 +798,10 @@ def sanitizeMarkdown(md):
"```css\n"
f"{sanitizeMarkdown(self.model['css'])}\n"
"```\n"
f"## Scripting\n"
"```javascript\n"
f"{sanitizeMarkdown(self.model['js'])}\n"
"```\n"
)
clipboard = QApplication.clipboard()
assert clipboard is not None
Expand Down
10 changes: 10 additions & 0 deletions qt/aqt/forms/template.ui
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@
</property>
</widget>
</item>
<item alignment="Qt::AlignLeft">
<widget class="QRadioButton" name="script_button">
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string notr="true">SCRIPT</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
Expand Down
1 change: 1 addition & 0 deletions rslib/src/card_rendering/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ impl From<RenderCardOutput> for anki_proto::card_rendering::RenderCardResponse {
question_nodes: rendered_nodes_to_proto(o.qnodes),
answer_nodes: rendered_nodes_to_proto(o.anodes),
css: o.css,
js: o.js,
latex_svg: o.latex_svg,
}
}
Expand Down
2 changes: 2 additions & 0 deletions rslib/src/notetype/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub struct RenderCardOutput {
pub qnodes: Vec<RenderedNode>,
pub anodes: Vec<RenderedNode>,
pub css: String,
pub js: String,
pub latex_svg: bool,
}

Expand Down Expand Up @@ -150,6 +151,7 @@ impl Collection {
qnodes,
anodes,
css: nt.config.css.clone(),
js: nt.config.js.clone(),
latex_svg: nt.config.latex_svg,
})
}
Expand Down
7 changes: 6 additions & 1 deletion rslib/src/notetype/schema11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ pub struct NotetypeSchema11 {
pub(crate) original_stock_kind: i32,
#[serde(default, skip_serializing_if = "is_default")]
pub(crate) original_id: Option<i64>,
#[serde(default, deserialize_with = "default_on_invalid")]
pub(crate) js: String,
#[serde(flatten)]
pub(crate) other: HashMap<String, Value>,
}
Expand Down Expand Up @@ -112,6 +114,7 @@ impl From<NotetypeSchema11> for Notetype {
reqs: nt.req.0.into_iter().map(Into::into).collect(),
original_stock_kind: nt.original_stock_kind,
original_id: nt.original_id,
js: nt.js,
other: other_to_bytes(&nt.other),
},
fields: nt.flds.into_iter().map(Into::into).collect(),
Expand Down Expand Up @@ -170,6 +173,7 @@ impl From<Notetype> for NotetypeSchema11 {
tmpls: p.templates.into_iter().map(Into::into).collect(),
flds: p.fields.into_iter().map(Into::into).collect(),
css: c.css,
js: c.js,
latex_pre: c.latex_pre,
latex_post: c.latex_post,
latexsvg: c.latex_svg,
Expand Down Expand Up @@ -197,7 +201,8 @@ static RESERVED_NOTETYPE_KEYS: Set<&'static str> = phf_set! {
"tmpls",
"type",
"sortf",
"latexsvg"
"latexsvg",
"js",
};

impl From<CardRequirementSchema11> for CardRequirement {
Expand Down