Skip to content
This repository was archived by the owner on Nov 30, 2023. It is now read-only.

fix: display tones in newer anki #209

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
99 changes: 94 additions & 5 deletions chinese/edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@
# You should have received a copy of the GNU General Public License along with
# Chinese Support Redux. If not, see <https://www.gnu.org/licenses/>.

import re
import json

import anki.buildinfo
from anki.hooks import addHook
from aqt.utils import showWarning
from aqt import mw
from aqt.editor import Editor

from .behavior import update_fields
from .main import config
Expand Down Expand Up @@ -80,12 +86,95 @@ def onFocusLost(self, _, note, index):
return False


def append_tone_styling(editor):
js = 'var css = document.styleSheets[0];'
TONE_CSS_RULE = re.compile("(\\.tone\\d) *\\{([^}]*)\\}")

# append_tone_styling(editor: Editor)
#
# Extracts the CSS rules for tones (i.e. matching TONE_CSS_RULE) from the
# user defined CSS style sheet. For the sake of simplicity, a tone CSS rule
# must be a one liner.
#
# IMPLEMENTATION NOTES:
#
# The code makes heavily use of internal APIs in Anki that may change in
# future releases. Hopefully, these notes are useful to adapt the code to
# new releases in case of breaking.
#
# The solution is based on Anki 2.1.54.
#
# The Javascript code being evaluated in the QWebView executes the following steps:
# 1. Wait until the UI has been loaded. The code for that is based on [1].
# 2. Loop through all RichTextInput Svelte component instances. They are
# reachable via "require" because they have been registered before here [2].
# Unfortunately, this method is only available since 2.1.54.
# 3. Using the RichTextInputAPI [3], we can query the associated CustomStyles
# instance. A CustomStyles instance has a `styleMap` [4] that contains an
# "userBase" entry, which wraps a <style> HTML element. This style element's
# intended function is to apply color, font family, font size, etc. [5,6].
# It is the perfect place to add our own CSS tone rules.
#
# [1] https://github.com/ankitects/anki/blob/2.1.54/qt/aqt/editor.py#L184
# [2] https://github.com/ankitects/anki/blob/2.1.54/ts/editor/rich-text-input/RichTextInput.svelte#L40
# [3] https://github.com/ankitects/anki/blob/2.1.54/ts/editor/rich-text-input/RichTextInput.svelte#L21
# [4] https://github.com/ankitects/anki/blob/2.1.54/ts/editor/rich-text-input/CustomStyles.svelte#L37
# [5] https://github.com/ankitects/anki/blob/2.1.54/ts/editor/rich-text-input/RichTextStyles.svelte#L17
# [6] https://github.com/ankitects/anki/blob/2.1.54/ts/editor/rich-text-input/RichTextStyles.svelte#L33

def append_tone_styling_anki2_1_54(editor: Editor):
rules = []
for line in editor.note.note_type()['css'].split('\n'):
if '.tone' in line:
m = TONE_CSS_RULE.search(line)
if m:
rules.append(line)
else:
showWarning("WARN: could not parse CSS tone rule. "
"Currently, tone CSS rules need to be one liners.")

js = f"var CSSRULES = {json.dumps(rules)};"
js += """
require("anki/ui").loaded.then(() =>
require("anki/RichTextInput").instances.forEach(inst =>
inst.customStyles.then(styles => {
var sheet = styles.styleMap.get("userBase").element.sheet;
CSSRULES.forEach(rule =>
sheet.insertRule(rule)
);
})
)
);
"""
editor.web.eval(js)

for line in editor.note.model()['css'].split('\n'):
def append_tone_styling_anki2_1_49(editor):
rules = []
for line in editor.note.note_type()['css'].split('\n'):
if line.startswith('.tone'):
js += 'css.insertRule("{}", css.cssRules.length);'.format(
line.rstrip())
m = TONE_CSS_RULE.search(line)
if m:
rules.append((m.group(1), m.group(2)))
else:
showWarning("WARN: could not parse CSS tone rule. "
"Currently, tone CSS rules need to be one liners.")

inner_js = ""
for rulename, ruledef in rules:
for part in ruledef.split(';'):
if ':' in part:
[property, value] = part.split(':', 1)
inner_js += f"jQuery('{rulename.strip()}', this.shadowRoot).css('{property.strip()}', '{value.strip()}');\n"
js = "jQuery('div.field').each(function () {\n%s})" % inner_js

editor.web.eval(js)


__version = [int(x) for x in anki.buildinfo.version.split('.')]
if __version < [2,1,50]:
append_tone_styling = append_tone_styling_anki2_1_49
elif __version >= [2,1,54]:
append_tone_styling = append_tone_styling_anki2_1_54
else:
showWarning("Chinese tone styling has not been implemented for your current Anki version. "
"Supported versions are Anki 2.1.49 as well as 2.1.54 and later.")
def append_tone_styling(editor):
pass