diff --git a/pylib/anki/exporting.py b/pylib/anki/exporting.py
index 1c6398aaa..8e5954d84 100644
--- a/pylib/anki/exporting.py
+++ b/pylib/anki/exporting.py
@@ -370,7 +370,6 @@ class AnkiPackageExporter(AnkiExporter):
p = path.replace(".apkg", ".media.db2")
if os.path.exists(p):
os.unlink(p)
- os.chdir(self.mediaDir)
shutil.rmtree(path.replace(".apkg", ".media"))
return media
diff --git a/qt/aqt/browser/table/model.py b/qt/aqt/browser/table/model.py
index e1ff290a6..6d50d6174 100644
--- a/qt/aqt/browser/table/model.py
+++ b/qt/aqt/browser/table/model.py
@@ -309,13 +309,15 @@ class DataModel(QAbstractTableModel):
return 0
return self.len_columns()
+ _QFont = without_qt5_compat_wrapper(QFont)
+
def data(self, index: QModelIndex = QModelIndex(), role: int = 0) -> Any:
if not index.isValid():
return QVariant()
if role == Qt.ItemDataRole.FontRole:
if not self.column_at(index).uses_cell_font:
return QVariant()
- qfont = QFont()
+ qfont = self._QFont()
row = self.get_row(index)
qfont.setFamily(row.font_name)
qfont.setPixelSize(row.font_size)
diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py
index 073696c9a..7d081cde4 100644
--- a/qt/aqt/editor.py
+++ b/qt/aqt/editor.py
@@ -758,7 +758,9 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
def urlToLink(self, url: str) -> str | None:
fname = self.urlToFile(url)
if not fname:
- return None
+ return '{}'.format(
+ url, html.escape(urllib.parse.unquote(url))
+ )
return self.fnameToLink(fname)
def fnameToLink(self, fname: str) -> str:
@@ -1261,7 +1263,7 @@ class EditorWebView(AnkiWebView):
url = qurl.toString()
# chrome likes to give us the URL twice with a \n
url = url.splitlines()[0]
- buf += self.editor.urlToLink(url) or ""
+ buf += self.editor.urlToLink(url)
return buf
@@ -1279,16 +1281,9 @@ class EditorWebView(AnkiWebView):
if extended and token.startswith("data:image/"):
processed.append(self.editor.inlinedImageToLink(token))
elif extended and self.editor.isURL(token):
- # if the user is pasting an image or sound link, convert it to local
+ # if the user is pasting an image or sound link, convert it to local, otherwise paste as a hyperlink
link = self.editor.urlToLink(token)
- if link:
- processed.append(link)
- else:
- # not media; add it as a normal link
- link = '{}'.format(
- token, html.escape(urllib.parse.unquote(token))
- )
- processed.append(link)
+ processed.append(link)
else:
token = html.escape(token).replace("\t", " " * 4)
# if there's more than one consecutive space,
diff --git a/qt/aqt/importing.py b/qt/aqt/importing.py
index c45570486..0168d2c9c 100644
--- a/qt/aqt/importing.py
+++ b/qt/aqt/importing.py
@@ -14,7 +14,7 @@ import aqt.modelchooser
from anki.errors import Interrupted
from anki.importing.anki2 import V2ImportIntoV1
from anki.importing.apkg import AnkiPackageImporter
-from aqt import AnkiQt, gui_hooks
+from aqt.main import AnkiQt, gui_hooks
from aqt.operations import QueryOp
from aqt.qt import *
from aqt.utils import (
@@ -460,12 +460,13 @@ def full_apkg_import(mw: AnkiQt, file: str) -> None:
def replace_with_apkg(
- mw: aqt.AnkiQt, filename: str, callback: Callable[[bool], None]
+ mw: AnkiQt, filename: str, callback: Callable[[bool], None]
) -> None:
"""Tries to replace the provided collection with the provided backup,
then calls the callback. True if success.
"""
- dialog = mw.progress.start(immediate=True)
+ if not (dialog := mw.progress.start(immediate=True)):
+ print("No progress dialog during import; aborting will not work")
timer = QTimer()
timer.setSingleShot(False)
timer.setInterval(100)
diff --git a/qt/aqt/operations/__init__.py b/qt/aqt/operations/__init__.py
index 0fd4dbad1..7d7ad8b91 100644
--- a/qt/aqt/operations/__init__.py
+++ b/qt/aqt/operations/__init__.py
@@ -217,6 +217,10 @@ class QueryOp(Generic[T]):
def wrapped_done(future: Future) -> None:
assert mw
+
+ if self._progress:
+ mw.progress.finish()
+
mw._decrease_background_ops()
# did something go wrong?
if exception := future.exception():
@@ -230,11 +234,6 @@ class QueryOp(Generic[T]):
# BaseException like SystemExit; rethrow it
future.result()
- result = future.result()
- try:
- self._success(result)
- finally:
- if self._progress:
- mw.progress.finish()
+ self._success(future.result())
mw.taskman.run_in_background(wrapped_op, wrapped_done)
diff --git a/qt/aqt/qt/__init__.py b/qt/aqt/qt/__init__.py
index 631d12cf1..c60601fa8 100644
--- a/qt/aqt/qt/__init__.py
+++ b/qt/aqt/qt/__init__.py
@@ -7,7 +7,7 @@
import os
import sys
import traceback
-from typing import Callable, Union
+from typing import Callable, TypeVar, Union
try:
import PyQt6
@@ -58,3 +58,16 @@ def qconnect(
) -> None:
"""Helper to work around type checking not working with signal.connect(func)."""
signal.connect(func) # type: ignore
+
+
+_T = TypeVar("_T")
+
+
+def without_qt5_compat_wrapper(cls: _T) -> _T:
+ """Remove Qt5 compat wrapper from Qt class, if active.
+
+ Only needed for a few Qt APIs that deal with QVariants."""
+ if fn := getattr(cls, "_without_compat_wrapper", None):
+ return fn()
+ else:
+ return cls
diff --git a/qt/aqt/qt/qt5_compat.py b/qt/aqt/qt/qt5_compat.py
index 98a054b1a..e549994b8 100644
--- a/qt/aqt/qt/qt5_compat.py
+++ b/qt/aqt/qt/qt5_compat.py
@@ -352,7 +352,9 @@ def _instrument_type(
class QtClassProxy(
type, metaclass=QtClassProxyType
): # pylint: disable=invalid-metaclass
- pass
+ @staticmethod
+ def _without_compat_wrapper():
+ return type
setattr(module, type_name, QtClassProxy)
diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py
index 955893e83..b2975fdeb 100644
--- a/qt/aqt/webview.py
+++ b/qt/aqt/webview.py
@@ -330,6 +330,7 @@ class AnkiWebView(QWebEngineView):
self._domDone = True
self._queueAction("setHtml", html)
self.set_open_links_externally(True)
+ self.setZoomFactor(1)
self.show()
def _setHtml(self, html: str) -> None:
@@ -681,6 +682,7 @@ html {{ {font} }}
else:
extra = ""
self.hide_while_preserving_layout()
+ self.setZoomFactor(1)
self.load_url(QUrl(f"{mw.serverURL()}_anki/pages/{name}.html{extra}"))
self.inject_dynamic_style_and_show()
diff --git a/qt/bundle/build.py b/qt/bundle/build.py
index b21294f53..72627ef97 100644
--- a/qt/bundle/build.py
+++ b/qt/bundle/build.py
@@ -343,13 +343,13 @@ def build_tarball(src_path: Path, variant: str) -> None:
subprocess.run(
[
"tar",
- "--zstd",
+ "-I",
+ "zstd -c --long -T0 -18",
"-cf",
dist_folder / (dest_path.name + ".tar.zst"),
dest_path.name,
],
check=True,
- env=dict(ZSTD_CLEVEL="9"),
cwd=dest_path.parent,
)
diff --git a/ts/reviewer/reviewer.scss b/ts/reviewer/reviewer.scss
index 24f24a3cf..dab602efd 100644
--- a/ts/reviewer/reviewer.scss
+++ b/ts/reviewer/reviewer.scss
@@ -23,6 +23,10 @@ img {
max-height: 95vh;
}
+li {
+ text-align: start;
+}
+
#_flag {
position: fixed;
right: 10px;
@@ -48,11 +52,6 @@ img {
box-sizing: border-box;
}
-.night-mode #typeans {
- background-color: var(--frame-bg);
- color: var(--text-fg);
-}
-
.typeGood {
background: #afa;
color: black;