Compare commits

..

20 Commits

Author SHA1 Message Date
2d45296b14
Merge pull request #23 from gugutu/master
All checks were successful
continuous-integration/drone/tag Build is passing
2024-03-05 00:56:47 +01:00
gugutu
def0045819
Fix the issue of incorrect JS file names introduced under Anki 23.12.1 2024-03-05 03:47:12 +08:00
gugutu
6acf2d5bb5
Fix for Anki version 24.04+ 2024-03-01 16:30:31 +08:00
gugutu
d6034c6dcf
Fix for Anki version 24.04+ 2024-03-01 16:19:38 +08:00
gugutu
65cc6c0b12
Modify the implementation method of "avoiding secondary refresh"
The previous implementation may encounter issues in specific scenarios and could potentially become invalid after Anki updates. Now, the approach has been modified to check whether the note content has been modified, which is a simpler and more understandable method.
2024-02-25 05:11:17 +08:00
gugutu
8487bb86d0
Increase the setSizes parameter
If the parameter given to setSizes is very small and smaller than QSplitter, the sub windows within QSplitter will be stretched. It is advisable to set a larger parameter for setSizes as much as possible.
2024-02-02 09:12:49 +08:00
gugutu
74d12576cd
Fix memory leakage caused by hook not releasing
Due to Anki not providing a hook when closing the editor, refactoring to use set filter to check which editors are not active and avoid creating a large number of hooks. By traversing the set, it is determined whether the current editor needs to be refreshed.
2024-02-02 08:50:17 +08:00
gugutu
deca35908a
Improve compatibility with other plugins
Wrap a widget on the outer layer of the webview so that other plugins can continue to modify the layout.
2024-02-02 07:28:16 +08:00
gugutu
2023cea60c
Fix second refresh after loading note 2024-02-02 07:10:47 +08:00
cefa65eadf
Merge pull request #20 from spacelord47/fix/use_qt6_enums_properly
All checks were successful
continuous-integration/drone/tag Build is passing
Fixes: #19 
Fixes: #18
2023-11-05 16:02:06 +01:00
spacelord47
766d246f46
fix: use Qt6 enums properly
New Anki version(23.10) dropped compatibility for Qt5: https://forums.ankiweb.net/t/porting-tips-for-anki-23-10/35916#enumerations-6
2023-11-05 13:53:17 +00:00
23f9c0cb68
Patch version check for new versioning scheme
I haven't taken a look at anything in the new version yet.
But that closes #16.
2023-09-25 11:50:44 +02:00
ab68523be6
Merge PR #10 2023-03-24 18:16:25 +01:00
af073e0498
Build addon automatically on push
All checks were successful
continuous-integration/drone/tag Build is passing
Signed-off-by: Tobias Manske <tobias.manske@mailbox.org>
2023-03-24 18:02:29 +01:00
4c966c1f5f
Format
Signed-off-by: Tobias Manske <tobias.manske@mailbox.org>
2023-03-24 18:02:28 +01:00
8e2815bc87
Add option to split side-by-side view. Also fixes split ratio handling
Signed-off-by: Tobias Manske <tobias.manske@mailbox.org>
2023-03-24 18:02:25 +01:00
Premysl Bednarek
734f24646e Remove the usage of deprecated function.
mv.pm.night_mode() is deprecated (see
1ed2cce648/qt/aqt/profiles.py (L537)).
Furthermore, on anki Version ⁨2.1.54, Python 3.9.10 Qt 6.3.1 PyQt 6.3.1, this function returns false, even when the dark mode is set.
2022-12-14 08:36:12 +01:00
58cd3cec42
Build changes for PR #7 2022-10-10 18:59:30 +02:00
Kevin Johansson
a577e785be removed .DS_Store 2022-10-09 17:21:56 +02:00
Kevin Johansson
ef413c20ee Implemented: config file, default open/close state, default split ratio, toolbar icon 2022-10-09 17:19:37 +02:00
7 changed files with 163 additions and 32 deletions

31
.drone.yml Normal file
View File

@ -0,0 +1,31 @@
---
kind: pipeline
type: docker
name: Build Anki Plugin
trigger:
event:
include:
- tag
steps:
- name: Build Archive
image: debian:bookworm
pull: always
commands:
- apt-get update && apt-get install -y zip
- ./build.sh
- name: Upload Artifact to Gitea
depends_on:
- Build Archive
image: plugins/gitea-release
settings:
api_key:
from_secret: gitea_api_token
checksum: sha256
base_url: https://git.tobiasmanske.de
files: editor-preview.ankiaddon
image_pull_secrets:
- registry

5
.gitignore vendored
View File

@ -117,6 +117,7 @@ venv/
ENV/ ENV/
env.bak/ env.bak/
venv.bak/ venv.bak/
.idea
# Spyder project settings # Spyder project settings
.spyderproject .spyderproject
@ -165,3 +166,7 @@ tags
# End of https://www.toptal.com/developers/gitignore/api/vim,python # End of https://www.toptal.com/developers/gitignore/api/vim,python
*.ankiaddon *.ankiaddon
.DS_Store
# Stores actual addon config if the src directory is symlinked into an anki installation during development
src/meta.json

View File

@ -6,6 +6,6 @@ if [ -d src/__pycache__ ]; then
fi fi
cd src cd src
zip -r ../editor-preview.ankiaddon * zip -r ../editor-preview.ankiaddon --exclude meta.json -- *
cd .. cd ..
unzip -l editor-preview.ankiaddon unzip -l editor-preview.ankiaddon

View File

@ -1,4 +1,5 @@
import json import json
from typing import Set
from anki import hooks, buildinfo from anki import hooks, buildinfo
from aqt import editor, gui_hooks, mw from aqt import editor, gui_hooks, mw
@ -6,9 +7,12 @@ from aqt.utils import *
from aqt.theme import theme_manager from aqt.theme import theme_manager
from aqt.webview import AnkiWebView from aqt.webview import AnkiWebView
config = mw.addonManager.getConfig(__name__)
class EditorPreview(object): class EditorPreview(object):
js=[ editors: Set[editor.Editor] = set()
js = [
"js/mathjax.js", "js/mathjax.js",
"js/vendor/mathjax/tex-chtml.js", "js/vendor/mathjax/tex-chtml.js",
"js/reviewer.js", "js/reviewer.js",
@ -17,7 +21,18 @@ class EditorPreview(object):
def __init__(self): def __init__(self):
gui_hooks.editor_did_init.append(self.editor_init_hook) gui_hooks.editor_did_init.append(self.editor_init_hook)
gui_hooks.editor_did_init_buttons.append(self.editor_init_button_hook) gui_hooks.editor_did_init_buttons.append(self.editor_init_button_hook)
if int(buildinfo.version.split(".")[2]) < 45: # < 2.1.45 gui_hooks.editor_did_load_note.append(self.editor_note_hook)
gui_hooks.editor_did_fire_typing_timer.append(self.onedit_hook)
buildversion = buildinfo.version.split(".")
# Anki changed their versioning scheme in 2023 to year.month(.patch), causing things to explode here.
if int(buildversion[0]) >= 24 or (int(buildversion[0]) == 23 and int(buildversion[1]) == 12 and 2 < len(buildversion) and int(buildversion[2])) >= 1: # >= 23.12.1
self.js = [
"js/mathjax.js",
"js/vendor/mathjax/tex-chtml-full.js",
"js/reviewer.js",
]
elif int(buildversion[0]) < 23 and int(buildversion[2]) < 45: # < 2.1.45
self.js = [ self.js = [
"js/vendor/jquery.min.js", "js/vendor/jquery.min.js",
"js/vendor/css_browser_selector.min.js", "js/vendor/css_browser_selector.min.js",
@ -26,65 +41,112 @@ class EditorPreview(object):
"js/reviewer.js", "js/reviewer.js",
] ]
def editor_init_hook(self, ed: editor.Editor): def editor_init_hook(self, ed: editor.Editor):
ed.webview = AnkiWebView(title="editor_preview") ed.editor_preview = AnkiWebView(title="editor_preview")
# This is taken out of clayout.py # This is taken out of clayout.py
ed.webview.stdHtml( ed.editor_preview.stdHtml(
ed.mw.reviewer.revHtml(), ed.mw.reviewer.revHtml(),
css=["css/reviewer.css"], css=["css/reviewer.css"],
js=self.js, js=self.js,
context=ed, context=ed,
) )
if not config["showPreviewAutomatically"]:
ed.editor_preview.hide()
self._inject_splitter(ed) self._inject_splitter(ed)
gui_hooks.editor_did_fire_typing_timer.append(lambda o: self.onedit_hook(ed, o))
gui_hooks.editor_did_load_note.append(lambda o: None if o != ed else self.editor_note_hook(o)) def _get_splitter(self, editor):
mainR, editorR = [int(r) * 10000 for r in config["splitRatio"].split(":")]
location = config["location"]
split = QSplitter()
if location == "above":
split.setOrientation(Qt.Orientation.Vertical)
split.addWidget(editor.editor_preview)
split.addWidget(editor.wrapped_web)
sizes = [editorR, mainR]
elif location == "below":
split.setOrientation(Qt.Orientation.Vertical)
split.addWidget(editor.wrapped_web)
split.addWidget(editor.editor_preview)
sizes = [mainR, editorR]
elif location == "left":
split.setOrientation(Qt.Orientation.Horizontal)
split.addWidget(editor.editor_preview)
split.addWidget(editor.wrapped_web)
sizes = [editorR, mainR]
elif location == "right":
split.setOrientation(Qt.Orientation.Horizontal)
split.addWidget(editor.wrapped_web)
split.addWidget(editor.editor_preview)
sizes = [mainR, editorR]
else:
raise ValueError("Invalid value for config key location")
split.setSizes(sizes)
return split
def _inject_splitter(self, editor: editor.Editor): def _inject_splitter(self, editor: editor.Editor):
layout = editor.outerLayout layout = editor.web.parentWidget().layout()
split = QSplitter() if layout is None:
split.setOrientation(Qt.Vertical) layout = QVBoxLayout()
editor.web.parentWidget().setLayout(layout)
web_index = layout.indexOf(editor.web) web_index = layout.indexOf(editor.web)
layout.removeWidget(editor.web) layout.removeWidget(editor.web)
split.addWidget(editor.web)
split.addWidget(editor.webview) # Wrap a widget on the outer layer of the webview
split.setStretchFactor(0, 0) # So that other plugins can continue to modify the layout
split.setStretchFactor(1, 1) editor.wrapped_web = QWidget()
split.setStretchFactor(2, 1) wrapLayout = QHBoxLayout()
editor.wrapped_web.setLayout(wrapLayout)
wrapLayout.addWidget(editor.web)
split = self._get_splitter(editor)
layout.insertWidget(web_index, split) layout.insertWidget(web_index, split)
def editor_note_hook(self, editor): def editor_note_hook(self, editor):
self.onedit_hook(editor, editor.note) self.editors = set(filter(lambda it: it.note is not None, self.editors))
self.editors.add(editor)
# The initial loading of notes will also trigger an editing event
# which will cause a second refresh
# Caching the content of notes here will be used to determine if the content has changed
editor.cached_fields = list(editor.note.fields)
self.refresh(editor)
def editor_init_button_hook(self, buttons, editor): def editor_init_button_hook(self, buttons, editor):
b = editor.addButton(icon=None, cmd="_editor_toggle_preview", label='P', tip='Toggle Live Preview', addon_path = os.path.dirname(__file__)
func=lambda o=editor: self.onEditorPreviewButton(o), disables=False icons_dir = os.path.join(addon_path, "icons")
b = editor.addButton(
icon=os.path.join(icons_dir, "file.svg"),
cmd="_editor_toggle_preview",
tip="Toggle Live Preview",
func=lambda o=editor: self.onEditorPreviewButton(o),
disables=False,
) )
buttons.append(b) buttons.append(b)
def onEditorPreviewButton(self, origin: editor.Editor): def onEditorPreviewButton(self, origin: editor.Editor):
if origin.webview.isHidden(): if origin.editor_preview.isHidden():
origin.webview.show() origin.editor_preview.show()
else: else:
origin.webview.hide() origin.editor_preview.hide()
def _obtainCardText(self, note): def _obtainCardText(self, note):
c = note.ephemeral_card() c = note.ephemeral_card()
a = mw.prepare_card_text_for_display(c.answer()) a = mw.prepare_card_text_for_display(c.answer())
a = gui_hooks.card_will_show(a, c, "clayoutAnswer") a = gui_hooks.card_will_show(a, c, "clayoutAnswer")
if theme_manager.night_mode: bodyclass = theme_manager.body_classes_for_card_ord(c.ord, theme_manager.night_mode)
bodyclass = theme_manager.body_classes_for_card_ord(c.ord, mw.pm.night_mode())
else:
bodyclass = theme_manager.body_classes_for_card_ord(c.ord)
bodyclass += " editor-preview" bodyclass += " editor-preview"
return f"_showAnswer({json.dumps(a)},'{bodyclass}');" return f"_showAnswer({json.dumps(a)},'{bodyclass}');"
def onedit_hook(self, editor, origin): def onedit_hook(self, note):
if editor.note == origin: for editor in self.editors:
editor.webview.eval(self._obtainCardText(editor.note)) if editor.note == note and editor.cached_fields != note.fields:
editor.cached_fields = list(note.fields)
self.refresh(editor)
def refresh(self, editor):
editor.editor_preview.eval(self._obtainCardText(editor.note))
eprev = EditorPreview() eprev = EditorPreview()

5
src/config.json Normal file
View File

@ -0,0 +1,5 @@
{
"showPreviewAutomatically": true,
"splitRatio": "1:1",
"location": "below"
}

9
src/config.md Normal file
View File

@ -0,0 +1,9 @@
### Config
\- `showPreviewAutomatically` [boolean (true | false)]:<br/>
&nbsp;&nbsp;&nbsp;Defines if the preview window should show up automatically as you enter the Editor (default: true)<br/><br/>
\- `splitRatio` [int:int]:<br/>
&nbsp;&nbsp;&nbsp;Defines the default split ratio of the main view and preview view (default: 1:1)<br/>
<br/>
\- `location` [string (above | below | left | right)]:<br/>
&nbsp;&nbsp;&nbsp;Defines where to render the preview (default: below)
<br/>

19
src/icons/file.svg Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
<style type="text/css">
.st0{stroke:#000000;stroke-width:0.5;stroke-miterlimit:10;}
</style>
<g>
<path class="st0" d="M8.7,14.1H2.2c-0.2,0-0.4-0.2-0.4-0.4V2.4c0-0.2,0.2-0.4,0.4-0.4h9.3c0.2,0,0.4,0.2,0.4,0.4v2.8
c0,0.2,0.2,0.4,0.4,0.4c0.2,0,0.4-0.2,0.4-0.4V2.4c0-0.7-0.6-1.3-1.3-1.3H2.2c-0.7,0-1.3,0.6-1.3,1.3v11.3c0,0.7,0.6,1.3,1.3,1.3
h6.5c0.2,0,0.4-0.2,0.4-0.4S9,14.1,8.7,14.1z"/>
<path class="st0" d="M15.4,14.2l-1.9-2c0.5-0.6,0.8-1.4,0.8-2.3c0-2-1.6-3.6-3.6-3.6S7.1,8,7.1,10s1.6,3.6,3.6,3.6
c0.8,0,1.6-0.3,2.2-0.7l1.9,2c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1C15.6,14.7,15.6,14.4,15.4,14.2L15.4,14.2z M10.7,12.7
C9.2,12.7,8,11.5,8,10s1.2-2.7,2.7-2.7s2.7,1.2,2.7,2.7S12.2,12.7,10.7,12.7z M9.1,5.2c0-0.2-0.2-0.4-0.4-0.4H3.6
C3.4,4.8,3.2,5,3.2,5.2c0,0.2,0.2,0.4,0.4,0.4h5.1C9,5.6,9.1,5.4,9.1,5.2z M6.5,7.6c0-0.2-0.2-0.4-0.4-0.4H3.6
c-0.2,0-0.4,0.2-0.4,0.4S3.4,8,3.6,8h2.5C6.3,8,6.5,7.8,6.5,7.6z M3.6,9.6c-0.2,0-0.4,0.2-0.4,0.4c0,0.2,0.2,0.4,0.4,0.4h1.3
c0.2,0,0.4-0.2,0.4-0.4c0-0.2-0.2-0.4-0.4-0.4H3.6z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB