diff --git a/.gitignore b/.gitignore
index deb2952..d2dd0ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,6 @@ __pycache__/
*.code-workspace
**/.history
+
+node_modules
+yarn-error.log
diff --git a/src/anking_notetypes/__init__.py b/src/anking_notetypes/__init__.py
index 0e5d450..27ac16b 100644
--- a/src/anking_notetypes/__init__.py
+++ b/src/anking_notetypes/__init__.py
@@ -7,20 +7,16 @@
from anki.utils import ids2str
from aqt import mw
-from aqt.qt import QUrl
from aqt.browser import Browser
-from aqt.editor import EditorWebView
from aqt.gui_hooks import (
browser_will_show_context_menu,
card_layout_will_show,
profile_did_open,
- editor_will_show_context_menu,
)
-from aqt.qt import QMenu, QPushButton, qtmajor, QAction, qconnect
+from aqt.qt import QMenu, QPushButton
from aqt.utils import askUserDialog, tooltip
-from bs4 import BeautifulSoup
-
+from . import editor
from .compat import add_compat_aliases
from .gui.config_window import (
NotetypesConfigWindow,
@@ -51,7 +47,7 @@ def setup():
browser_will_show_context_menu.append(on_browser_will_show_context_menu)
- editor_will_show_context_menu.append(on_editor_will_show_context_menu)
+ editor.init()
def on_profile_did_open():
@@ -201,40 +197,5 @@ def on_browser_will_show_context_menu(browser: Browser, context_menu: QMenu) ->
action.setDisabled(True)
-def on_editor_will_show_context_menu(webview: EditorWebView, menu: QMenu) -> None:
- def on_blur_image() -> None:
- editor = webview.editor
- url = data.mediaUrl()
- if url.matches(QUrl(mw.serverURL()), QUrl.UrlFormattingOption.RemovePath):
- src = url.path().strip("/")
- else:
- src = url.toString()
- field = editor.note.fields[editor.currentField]
- soup = BeautifulSoup(field, "html.parser")
- for img in soup("img"):
- if img.get("src", "").strip("/") != src:
- continue
- classes = img.get("class", [])
- if "blur" in classes:
- classes.remove("blur")
- else:
- classes.append("blur")
- if classes:
- img["class"] = classes
- elif "class" in img.attrs:
- del img["class"]
- editor.note.fields[editor.currentField] = soup.decode_contents()
- editor.loadNoteKeepingFocus()
-
- if qtmajor >= 6:
- data = webview.lastContextMenuRequest() # type: ignore
- else:
- data = webview.page().contextMenuData()
- if data.mediaUrl().isValid():
- blur_image_action = QAction("AnKing Notetypes: Blur/Unblur Image", menu)
- qconnect(blur_image_action.triggered, on_blur_image)
- menu.addAction(blur_image_action)
-
-
if mw is not None:
setup()
diff --git a/src/anking_notetypes/editor.py b/src/anking_notetypes/editor.py
new file mode 100644
index 0000000..dd86f67
--- /dev/null
+++ b/src/anking_notetypes/editor.py
@@ -0,0 +1,360 @@
+import json
+import re
+from os.path import dirname, realpath
+from pathlib import Path
+from typing import Any, List, Optional, Tuple
+
+from aqt import mw
+from aqt.editor import Editor, EditorWebView
+from aqt.gui_hooks import (
+ editor_did_init_buttons,
+ editor_did_init_shortcuts,
+ editor_did_load_note,
+ editor_will_load_note,
+ editor_will_munge_html,
+ editor_will_show_context_menu,
+ webview_did_receive_js_message,
+ webview_will_set_content,
+)
+from aqt.qt import QAction, QKeySequence, QMenu, QUrl, qconnect, qtmajor
+from aqt.utils import shortcut, showInfo
+from bs4 import BeautifulSoup
+
+from .notetype_setting_definitions import is_io_note_type
+
+occlude_shortcut = "Ctrl+Shift+O"
+occlusion_behavior = "autopaste"
+
+
+def get_base_top(editor, prefix: str, suffix: str) -> int:
+ matches = []
+ query = re.escape(prefix) + r"(\d+)" + re.escape(suffix)
+
+ for _name, item in editor.note.items():
+ matches.extend(re.findall(query, item))
+
+ values = [0]
+ values.extend([int(x) for x in matches])
+
+ return max(values)
+
+
+def get_top_index(editor, prefix: str, suffix: str) -> int:
+ base_top = get_base_top(editor, prefix, suffix)
+
+ return base_top + 1 if base_top == 0 else base_top
+
+
+def get_incremented_index(editor, prefix: str, suffix: str) -> int:
+ base_top = get_base_top(editor, prefix, suffix)
+
+ return base_top + 1
+
+
+def activate_matching_field(indexer):
+ def get_value(editor, prefix: str, suffix: str) -> int:
+ current_index = indexer(editor, prefix, suffix)
+ was_filled = activate_matching_fields(editor, [current_index])[0]
+
+ return current_index if was_filled else 0
+
+ return get_value
+
+
+def no_index(_editor, _prefix: str, _suffix: str) -> str:
+ return ""
+
+
+def top_index(type_):
+ if type_ == "free":
+ return get_top_index
+ elif type_ == "flashcard":
+ return activate_matching_field(get_top_index)
+ else: # type_ == "none":
+ return no_index
+
+
+def incremented_index(type_):
+ if type_ == "free":
+ return get_incremented_index
+ elif type_ == "flashcard":
+ return activate_matching_field(get_incremented_index)
+ else: # type_ == "none":
+ return no_index
+
+
+def is_text_empty(editor, text) -> bool:
+ return editor.mungeHTML(text) == ""
+
+
+def escape_js_text(text: str) -> str:
+ return text.replace("\\", "\\\\").replace('"', '\\"').replace("'", "\\'")
+
+
+def make_insertion_js(field_index: int, text: str) -> str:
+ escaped = escape_js_text(text)
+
+ cmd = (
+ f"pycmd(`key:{field_index}:${{getNoteId()}}:{escaped}`); "
+ f"EditorIO.setFieldHTML({field_index}, `{escaped}`); "
+ )
+ return cmd
+
+
+def insert_into_zero_indexed(editor, text: str) -> None:
+ for index, (name, _item) in enumerate(editor.note.items()):
+ match = re.search(r"\d+$", name)
+
+ if not match or int(match[0]) != 0:
+ continue
+
+ editor.web.eval(f"EditorIO.insertIntoZeroIndexed(`{text}`, {index}); ")
+ break
+
+
+def activate_matching_fields(editor, indices: List[int]) -> List[bool]:
+ founds = [False for index in indices]
+
+ for index, (name, item) in enumerate(editor.note.items()):
+ match = re.search(r"\d+$", name)
+
+ if not match:
+ continue
+
+ matched = int(match[0])
+
+ if matched not in indices:
+ continue
+
+ founds[indices.index(matched)] = True
+
+ if not is_text_empty(editor, item):
+ continue
+
+ editor.web.eval(make_insertion_js(index, "active"))
+
+ return founds
+
+
+def include_closet_code(webcontent, context) -> None:
+ if not isinstance(context, Editor):
+ return
+
+ addon_package = mw.addonManager.addonFromModule(__name__)
+ webcontent.css.append(f"/_addons/{addon_package}/web/editor.css")
+ webcontent.js.append(f"/_addons/{addon_package}/web/editor.js")
+
+
+def process_occlusion_index_text(index_text: str) -> List[int]:
+ return [] if len(index_text) == 0 else [int(text) for text in index_text.split(",")]
+
+
+old_occlusion_indices = []
+occlusion_editor_active = False
+
+
+def add_occlusion_messages(
+ handled: Tuple[bool, Any], message: str, context
+) -> Tuple[bool, Any]:
+ if isinstance(context, Editor):
+ editor: Editor = context
+ global old_occlusion_indices, occlusion_editor_active # pylint: disable=global-statement
+ if message.startswith("ankingOldOcclusions"):
+ _, _src, index_text = message.split(":", 2)
+ old_occlusion_indices = process_occlusion_index_text(index_text)
+
+ return (True, None)
+
+ elif message.startswith("ankingNewOcclusions"):
+ _, _src, index_text = message.split(":", 2)
+ indices = process_occlusion_index_text(index_text)
+
+ fill_indices = list(set(indices).difference(set(old_occlusion_indices)))
+ could_fill = activate_matching_fields(editor, fill_indices)
+
+ return (True, could_fill)
+
+ elif message.startswith("ankingOcclusionText"):
+ text = message.split(":", 1)[1]
+
+ if occlusion_behavior == "autopaste":
+ insert_into_zero_indexed(editor, text)
+ else: # occlusion_behavior == 'copy':
+ mw.app.clipboard().setText(text)
+
+ return (True, None)
+
+ elif message == "ankingOcclusionEditorActive":
+ occlusion_editor_active = True
+ return (True, None)
+
+ elif message == "ankingOcclusionEditorInactive":
+ occlusion_editor_active = False
+ return (True, None)
+
+ elif message.startswith("ankingClosetRefocus"):
+ refocus(editor)
+ return (True, None)
+
+ elif message.startswith("ankingClosetMultipleImages"):
+ showInfo("Cannot start occlusion editor if field contains multiple images.")
+ return (True, None)
+
+ return handled
+
+
+# code 0 field is optional
+trailing_number = re.compile(r"[123456789]\d*$")
+
+
+def get_max_code_field(editor) -> int:
+ number_suffixes = map(
+ lambda item: trailing_number.search(item[0]), editor.note.items()
+ )
+ indices = [suffix[0] for suffix in number_suffixes if suffix]
+ sorted_indices = sorted(set([int(index) for index in indices]))
+
+ max = 0
+ for index, value in enumerate(sorted_indices):
+ # probably skipped an index
+ if value != index + 1:
+ break
+
+ max = value
+
+ return max
+
+
+def anking_io_js_filename() -> Optional[str]:
+ path = next((Path(__file__).parent / "resources").glob("__ankingio-*.js"), None)
+ if not path:
+ return None
+ return path.name
+
+
+def toggle_occlusion_mode(editor):
+ model = editor.note.note_type()
+
+ if not is_io_note_type(model["name"]):
+ showInfo("Please choose an AnKing image occlusion note type")
+ editor.web.eval("EditorIO.setInactive()")
+ return
+
+ addon_package = mw.addonManager.addonFromModule(__name__)
+ js_path = json.dumps(f"_addons/{addon_package}/resources/{anking_io_js_filename()}")
+ max_code_fields = get_max_code_field(editor)
+
+ if not mw.focusWidget() is editor:
+ refocus(editor)
+
+ editor.web.eval(f"EditorIO.toggleOcclusionMode({js_path}, {max_code_fields})")
+
+
+def add_occlusion_button(buttons, editor):
+ file_path = dirname(realpath(__file__))
+ icon_path = Path(file_path, "icons", "occlude.png")
+
+ shortcut_as_text = shortcut(QKeySequence(occlude_shortcut).toString())
+
+ occlusion_button = editor._addButton( # pylint: disable=protected-access
+ str(icon_path.absolute()),
+ "anking_occlude",
+ f"Put all fields into occlusion mode ({shortcut_as_text})",
+ id="ankingOcclude",
+ disables=False,
+ )
+
+ editor._links[ # pylint: disable=protected-access
+ "anking_occlude"
+ ] = toggle_occlusion_mode
+ buttons.insert(-1, occlusion_button)
+
+
+def wrap_as_option(name: str, code: str, tooltip: str, shortcut_text: str) -> str:
+ return f''
+
+
+def add_buttons(buttons, editor):
+ global occlusion_editor_active # pylint: disable=global-statement
+ occlusion_editor_active = False
+
+ add_occlusion_button(buttons, editor)
+
+
+def add_occlusion_shortcut(cuts, editor):
+ cuts.append((occlude_shortcut, lambda: toggle_occlusion_mode(editor), True))
+
+
+occlusion_container_pattern = re.compile(
+ # remove trailing
to prevent accumulation
+ r'
(
)*'
+)
+
+
+def remove_occlusion_code(txt: str, _editor) -> str:
+ if match := re.search(occlusion_container_pattern, txt):
+ rawImage = match[1]
+
+ return re.sub(occlusion_container_pattern, rawImage, txt)
+
+ return txt
+
+
+def clear_occlusion_mode(js, _note, _editor):
+ return f"EditorIO.clearOcclusionMode().then(() => {{ {js} }}); "
+
+
+def refocus(editor):
+ editor.web.setFocus()
+ editor.web.eval("EditorIO.refocus(); ")
+
+
+def maybe_refocus(editor):
+ editor.web.eval("EditorIO.maybeRefocus(); ")
+
+
+def on_editor_will_show_context_menu(webview: EditorWebView, menu: QMenu) -> None:
+ def on_blur_image() -> None:
+ editor = webview.editor
+ url = data.mediaUrl()
+ if url.matches(QUrl(mw.serverURL()), QUrl.UrlFormattingOption.RemovePath):
+ src = url.path().strip("/")
+ else:
+ src = url.toString()
+ field = editor.note.fields[editor.currentField]
+ soup = BeautifulSoup(field, "html.parser")
+ for img in soup("img"):
+ if img.get("src", "").strip("/") != src:
+ continue
+ classes = img.get("class", [])
+ if "blur" in classes:
+ classes.remove("blur")
+ else:
+ classes.append("blur")
+ if classes:
+ img["class"] = classes
+ elif "class" in img.attrs:
+ del img["class"]
+ editor.note.fields[editor.currentField] = soup.decode_contents()
+ editor.loadNoteKeepingFocus()
+
+ if qtmajor >= 6:
+ data = webview.lastContextMenuRequest() # type: ignore
+ else:
+ data = webview.page().contextMenuData()
+ if data.mediaUrl().isValid():
+ blur_image_action = QAction("AnKing Notetypes: Blur/Unblur Image", menu)
+ qconnect(blur_image_action.triggered, on_blur_image)
+ menu.addAction(blur_image_action)
+
+
+def init():
+ webview_will_set_content.append(include_closet_code)
+ webview_did_receive_js_message.append(add_occlusion_messages)
+ editor_did_init_buttons.append(add_buttons)
+ editor_did_init_shortcuts.append(add_occlusion_shortcut)
+ editor_will_load_note.append(clear_occlusion_mode)
+ editor_did_load_note.append(maybe_refocus)
+ editor_will_munge_html.append(remove_occlusion_code)
+ editor_will_show_context_menu.append(on_editor_will_show_context_menu)
+ mw.addonManager.setWebExports(__name__, r"(web|resources)/.*(css|js)")
diff --git a/src/anking_notetypes/gui/config_window.py b/src/anking_notetypes/gui/config_window.py
index 22ca5e0..3327659 100644
--- a/src/anking_notetypes/gui/config_window.py
+++ b/src/anking_notetypes/gui/config_window.py
@@ -19,6 +19,7 @@
general_settings,
general_settings_defaults_dict,
setting_configs,
+ notetype_base_name,
)
from ..utils import update_notetype_to_newest_version
from .anking_widgets import AnkingIconsLayout, GithubLinkLayout
@@ -109,10 +110,10 @@ def open(self):
new_idx = notetype_base_names.index("IO-one by one") + 1
notetype_base_names.insert(new_idx, "AnKingOverlapping")
- for notetype_base_name in notetype_base_names:
+ for nt_base_name in notetype_base_names:
self.conf.add_config_tab(
- lambda window, notetype_base_name=notetype_base_name: self._add_notetype_settings_tab(
- notetype_base_name=notetype_base_name, window=window
+ lambda window, nt_base_name=nt_base_name: self._add_notetype_settings_tab(
+ nt_base_name=nt_base_name, window=window
)
)
@@ -120,14 +121,14 @@ def open(self):
def live_update_clayout_model(key: str, _: Any):
notetype_base_name_from_setting, setting_name = key.split(".")
model = self.clayout.model
- notetype_base_name_from_model = _notetype_base_name(model["name"])
+ notetype_base_name_from_model = notetype_base_name(model["name"])
if notetype_base_name_from_setting != notetype_base_name_from_model:
return
nts = NotetypeSetting.from_config(setting_configs[setting_name])
self._safe_update_model_settings(
model=model,
- notetype_base_name=notetype_base_name_from_model,
+ nt_base_name=notetype_base_name_from_model,
ntss=[nts],
)
@@ -160,7 +161,7 @@ def on_save(window: ConfigWindow):
window.save_btn.clicked.connect(lambda: on_save(window)) # type: ignore
if self.clayout:
- self._set_active_tab(_notetype_base_name(self.clayout.model["name"]))
+ self._set_active_tab(notetype_base_name(self.clayout.model["name"]))
# add anking links layouts
self.anking_icons_widget = QWidget()
@@ -195,24 +196,24 @@ def on_save(window: ConfigWindow):
# tabs and NotetypeSettings (ntss)
def _add_notetype_settings_tab(
self,
- notetype_base_name: str,
+ nt_base_name: str,
window: ConfigWindow,
index: Optional[int] = None,
):
if (
self.clayout
- and _notetype_base_name(self.clayout.model["name"]) == notetype_base_name
+ and notetype_base_name(self.clayout.model["name"]) == nt_base_name
):
model = self.clayout.model
else:
- model = _most_basic_notetype_version(notetype_base_name)
+ model = _most_basic_notetype_version(nt_base_name)
- tab = window.add_tab(notetype_base_name, index=index)
+ tab = window.add_tab(nt_base_name, index=index)
if model:
ntss = ntss_for_model(model)
ordered_ntss = self._adjust_configurable_field_nts_order(
- ntss=ntss, notetype_base_name=notetype_base_name
+ ntss=ntss, nt_base_name=nt_base_name
)
scroll = tab.scroll_layout()
self._add_nts_widgets_to_layout(scroll, ordered_ntss, model)
@@ -230,9 +231,7 @@ def _add_notetype_settings_tab(
tab.button(
"Import",
- on_click=lambda: self._import_notetype_and_reload_tab(
- notetype_base_name
- ),
+ on_click=lambda: self._import_notetype_and_reload_tab(nt_base_name),
)
def _add_general_tab(self, window: ConfigWindow):
@@ -294,7 +293,7 @@ def _add_nts_widgets_to_layout(
for nts, section in nts_to_section.items():
section_to_ntss[section].append(nts)
- notetype_base_name = _notetype_base_name(model["name"]) if model else None
+ nt_base_name = notetype_base_name(model["name"]) if model else None
for section_name, section_ntss in sorted(section_to_ntss.items()):
section = layout.collapsible_section(section_name)
for nts in section_ntss:
@@ -302,7 +301,7 @@ def _add_nts_widgets_to_layout(
nts.add_widget_to_general_config_layout(section)
else:
nts.add_widget_to_config_layout(
- section, notetype_base_name=notetype_base_name, model=model
+ section, notetype_base_name=nt_base_name, model=model
)
section.space(7)
layout.hseparator()
@@ -316,12 +315,12 @@ def _add_nts_widgets_to_layout(
nts.add_widget_to_general_config_layout(layout)
else:
nts.add_widget_to_config_layout(
- layout, notetype_base_name=notetype_base_name, model=model
+ layout, notetype_base_name=nt_base_name, model=model
)
layout.space(7)
def _adjust_configurable_field_nts_order(
- self, ntss: List[NotetypeSetting], notetype_base_name: str
+ self, ntss: List[NotetypeSetting], nt_base_name: str
) -> List[NotetypeSetting]:
# adjusts the order of the hint button settings to be the same as
# on the original anking card
@@ -331,7 +330,7 @@ def _adjust_configurable_field_nts_order(
field_ntss = [
nts for nts in ntss if nts.config.get("configurable_field_name", False)
]
- ordered_field_names = configurable_fields_for_notetype(notetype_base_name)
+ ordered_field_names = configurable_fields_for_notetype(nt_base_name)
ordered_field_ntss = sorted(
field_ntss,
key=lambda nts: (
@@ -359,7 +358,7 @@ def _reload_tab(self, tab_name: str) -> None:
self._add_general_tab(self.window)
else:
self._add_notetype_settings_tab(
- notetype_base_name=tab_name, window=self.window, index=index
+ nt_base_name=tab_name, window=self.window, index=index
)
self._read_in_settings()
@@ -390,15 +389,15 @@ def _reset_notetype_and_reload_ui(self, model: "NotetypeDict"):
):
return
- notetype_base_name = _notetype_base_name(model["name"])
+ nt_base_name = notetype_base_name(model["name"])
for model_version in _note_type_versions(model["name"]):
- update_notetype_to_newest_version(model_version, notetype_base_name)
+ update_notetype_to_newest_version(model_version, nt_base_name)
mw.col.models.update_dict(model_version) # type: ignore
if self.clayout:
self._update_clayout_model(model)
- self._reload_tab(notetype_base_name)
+ self._reload_tab(nt_base_name)
tooltip("Notetype was reset", parent=self.window, period=1200)
@@ -409,15 +408,15 @@ def _reset_general_settings_and_reload_ui(self):
defaultno=True,
):
return
- for notetype_base_name in anking_notetype_names():
- model = _most_basic_notetype_version(notetype_base_name)
+ for nt_base_name in anking_notetype_names():
+ model = _most_basic_notetype_version(nt_base_name)
if not model:
continue
settings_defaults = general_settings_defaults_dict()
for nts in general_ntss():
value = settings_defaults[nts.name()]
- self.conf[nts.key(notetype_base_name)] = value
+ self.conf[nts.key(nt_base_name)] = value
self.conf.set(f"general.{nts.name()}", value, on_change_trigger=False)
self._apply_setting_changes_for_all_notetypes()
@@ -436,13 +435,13 @@ def task():
to_be_updated = models_with_available_updates()
for model in to_be_updated:
# update the model to the newest version
- base_name = _notetype_base_name(model["name"])
+ base_name = notetype_base_name(model["name"])
update_notetype_to_newest_version(model, base_name)
# restore the values from before the update for the settings that exist in both versions
self._safe_update_model_settings(
model=model,
- notetype_base_name=base_name,
+ nt_base_name=base_name,
ntss=ntss_for_model(model),
show_tooltip_on_exception=False,
)
@@ -461,10 +460,10 @@ def on_done(updated_models_fut: Future):
self._reload_tab("General")
updated_base_models = [
- m for m in updated_models if m["name"] == _notetype_base_name(m["name"])
+ m for m in updated_models if m["name"] == notetype_base_name(m["name"])
]
for model in sorted(updated_base_models, key=lambda m: m["name"]):
- self._reload_tab(_notetype_base_name(model["name"]))
+ self._reload_tab(notetype_base_name(model["name"]))
self._set_active_tab("General")
@@ -478,12 +477,12 @@ def on_done(updated_models_fut: Future):
immediate=True,
)
- def _import_notetype_and_reload_tab(self, notetype_base_name: str) -> None:
- self._import_notetype(notetype_base_name)
- self._reload_tab(notetype_base_name)
+ def _import_notetype_and_reload_tab(self, nt_base_name: str) -> None:
+ self._import_notetype(nt_base_name)
+ self._reload_tab(nt_base_name)
- def _import_notetype(self, notetype_base_name: str) -> None:
- model = anking_notetype_model(notetype_base_name)
+ def _import_notetype(self, nt_base_name: str) -> None:
+ model = anking_notetype_model(nt_base_name)
model["id"] = 0
mw.col.models.add_dict(model) # type: ignore
@@ -498,22 +497,22 @@ def _read_in_settings(self):
def _read_in_settings_from_notetypes(self):
error_msg = ""
- for notetype_base_name in anking_notetype_names():
- if self.clayout and notetype_base_name == _notetype_base_name(
+ for nt_base_name in anking_notetype_names():
+ if self.clayout and nt_base_name == notetype_base_name(
self.clayout.model["name"]
):
# if in live preview mode read in current not confirmed settings
model = self.clayout.model
else:
- model = _most_basic_notetype_version(notetype_base_name)
+ model = _most_basic_notetype_version(nt_base_name)
if not model:
continue
for nts in ntss_for_model(model):
try:
- self.conf[nts.key(notetype_base_name)] = nts.setting_value(model)
+ self.conf[nts.key(nt_base_name)] = nts.setting_value(model)
except NotetypeSettingException as e:
- error_msg += f"failed parsing {notetype_base_name}:\n{str(e)}\n\n"
+ error_msg += f"failed parsing {nt_base_name}:\n{str(e)}\n\n"
if error_msg:
showInfo(error_msg)
@@ -525,8 +524,8 @@ def _read_in_general_settings(self):
# if all notetypes that have a nts have the same value set the value to it
models_by_nts: Dict[NotetypeSetting, "NotetypeDict"] = defaultdict(lambda: [])
- for notetype_base_name in anking_notetype_names():
- model = _most_basic_notetype_version(notetype_base_name)
+ for nt_base_name in anking_notetype_names():
+ model = _most_basic_notetype_version(nt_base_name)
if not model:
continue
@@ -547,13 +546,13 @@ def _read_in_general_settings(self):
def _safe_update_model_settings(
self,
model: "NotetypeDict",
- notetype_base_name: str,
+ nt_base_name: str,
ntss: List["NotetypeSetting"],
show_tooltip_on_exception=True,
) -> bool:
"""Updates the model with the passed settings. Returns True if successful, False if there was an exception.
model: The model to update
- notetype_base_name: The base name of the note type. This is used to get the correct setting values from self.conf
+ nt_base_name: The base name of the note type. This is used to get the correct setting values from self.conf
ntss: The settings to update"""
parse_exception = None
for nts in ntss:
@@ -561,7 +560,7 @@ def _safe_update_model_settings(
model.update(
nts.updated_model(
model=model,
- notetype_base_name=notetype_base_name,
+ notetype_base_name=nt_base_name,
conf=self.conf,
)
)
@@ -578,13 +577,13 @@ def _safe_update_model_settings(
return True
def _apply_setting_changes_for_all_notetypes(self):
- for notetype_base_name in anking_notetype_names():
- for model in _note_type_versions(notetype_base_name):
+ for nt_base_name in anking_notetype_names():
+ for model in _note_type_versions(nt_base_name):
if not model:
continue
ntss = ntss_for_model(model)
self._safe_update_model_settings(
- model=model, notetype_base_name=notetype_base_name, ntss=ntss
+ model=model, nt_base_name=nt_base_name, ntss=ntss
)
mw.col.models.update_dict(model)
@@ -623,32 +622,28 @@ def models_with_available_updates() -> List["NotetypeDict"]:
def _new_version_available_for_model(model: "NotetypeDict") -> bool:
current_version = note_type_version(model)
- base_name = _notetype_base_name(model["name"])
+ base_name = notetype_base_name(model["name"])
newest_version = note_type_version(anking_notetype_model(base_name))
return current_version != newest_version
-def _note_type_versions(notetype_base_name: str) -> List["NotetypeDict"]:
+def _note_type_versions(nt_base_name: str) -> List["NotetypeDict"]:
"""Returns a list of all notetype versions of the notetype in the collection.
Version of a note type are created by the AnkiHub add-on and by copying
the base AnKing note types or importing them from different sources."""
models = [
mw.col.models.get(x.id) # type: ignore
for x in mw.col.models.all_names_and_ids()
- if x.name == notetype_base_name
- or re.match(
- ANKIHUB_NOTETYPE_RE.format(notetype_base_name=notetype_base_name), x.name
- )
- or re.match(
- NOTETYPE_COPY_RE.format(notetype_base_name=notetype_base_name), x.name
- )
+ if x.name == nt_base_name
+ or re.match(ANKIHUB_NOTETYPE_RE.format(notetype_base_name=nt_base_name), x.name)
+ or re.match(NOTETYPE_COPY_RE.format(notetype_base_name=nt_base_name), x.name)
]
return models
-def _most_basic_notetype_version(notetype_base_name: str) -> Optional["NotetypeDict"]:
+def _most_basic_notetype_version(nt_base_name: str) -> Optional["NotetypeDict"]:
"""Returns the most basic version of a note type, that is the version with the shortest name."""
- model_versions = _note_type_versions(notetype_base_name)
+ model_versions = _note_type_versions(nt_base_name)
result = min(
model_versions,
# sort by length of name and then alphabetically
@@ -658,19 +653,6 @@ def _most_basic_notetype_version(notetype_base_name: str) -> Optional["NotetypeD
return result
-def _notetype_base_name(model_name: str) -> str:
- """Returns the base name of a note type, that is if it's a version of a an anking note type
- it will return the base name, otherwise it will return the name itself."""
- return next(
- (
- notetype_base_name
- for notetype_base_name in anking_notetype_names()
- if re.match(rf"{notetype_base_name}($| |-)", model_name)
- ),
- None,
- )
-
-
def _names_of_all_supported_note_types() -> List[str]:
"""Returns a list of names of note types supported by the add-on that are in the collection,
including all versions of the base note types."""
diff --git a/src/anking_notetypes/icons/occlude.png b/src/anking_notetypes/icons/occlude.png
new file mode 100644
index 0000000..9a53170
Binary files /dev/null and b/src/anking_notetypes/icons/occlude.png differ
diff --git a/src/anking_notetypes/note_types/IO-one by one/Back Template.html b/src/anking_notetypes/note_types/IO-one by one/Back Template.html
index d983d08..3250c49 100644
--- a/src/anking_notetypes/note_types/IO-one by one/Back Template.html
+++ b/src/anking_notetypes/note_types/IO-one by one/Back Template.html
@@ -319,11 +319,11 @@
toggleAllButton.title = `Shortcut: ${window.toggleAllShortcut}`;
revealNextButton.title = `Shortcut: ${window.revealNextShortcut}`;
- let rect = document.querySelector(".closet-rect.is-active")
+ let rect = document.querySelector(".anking-rect.is-active")
if (rect) {
activate(rect)
observer.disconnect()
- globalThis.AnkingIORects = document.querySelectorAll(".closet-rect.is-active")
+ globalThis.AnkingIORects = document.querySelectorAll(".anking-rect.is-active")
for (let rect of globalThis.AnkingIORects) {
rect.addEventListener("click", reveal)
}
@@ -366,7 +366,7 @@
window.toggleNext = function() {
- let active = document.querySelector(".closet-rect.is-highlighted")
+ let active = document.querySelector(".anking-rect.is-highlighted")
if (active) incrementalReveal.call(active)
}
@@ -379,7 +379,7 @@
for (let rect of globalThis.AnkingIORects) {
hide.call(rect)
}
- let newActiveRect = document.querySelector(".closet-rect.is-active")
+ let newActiveRect = document.querySelector(".anking-rect.is-active")
activate(newActiveRect)
} else {
for (let rect of globalThis.AnkingIORects) {
@@ -421,7 +421,7 @@
setupIncrementalIO()
-
-
+
+
+
+
+
+
+
+
+
+
+
+