mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 23:12:21 -04:00
Merge branch 'main' into apkg
This commit is contained in:
commit
4837e52fe9
10 changed files with 43 additions and 31 deletions
|
@ -370,7 +370,6 @@ class AnkiPackageExporter(AnkiExporter):
|
||||||
p = path.replace(".apkg", ".media.db2")
|
p = path.replace(".apkg", ".media.db2")
|
||||||
if os.path.exists(p):
|
if os.path.exists(p):
|
||||||
os.unlink(p)
|
os.unlink(p)
|
||||||
os.chdir(self.mediaDir)
|
|
||||||
shutil.rmtree(path.replace(".apkg", ".media"))
|
shutil.rmtree(path.replace(".apkg", ".media"))
|
||||||
return media
|
return media
|
||||||
|
|
||||||
|
|
|
@ -309,13 +309,15 @@ class DataModel(QAbstractTableModel):
|
||||||
return 0
|
return 0
|
||||||
return self.len_columns()
|
return self.len_columns()
|
||||||
|
|
||||||
|
_QFont = without_qt5_compat_wrapper(QFont)
|
||||||
|
|
||||||
def data(self, index: QModelIndex = QModelIndex(), role: int = 0) -> Any:
|
def data(self, index: QModelIndex = QModelIndex(), role: int = 0) -> Any:
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return QVariant()
|
return QVariant()
|
||||||
if role == Qt.ItemDataRole.FontRole:
|
if role == Qt.ItemDataRole.FontRole:
|
||||||
if not self.column_at(index).uses_cell_font:
|
if not self.column_at(index).uses_cell_font:
|
||||||
return QVariant()
|
return QVariant()
|
||||||
qfont = QFont()
|
qfont = self._QFont()
|
||||||
row = self.get_row(index)
|
row = self.get_row(index)
|
||||||
qfont.setFamily(row.font_name)
|
qfont.setFamily(row.font_name)
|
||||||
qfont.setPixelSize(row.font_size)
|
qfont.setPixelSize(row.font_size)
|
||||||
|
|
|
@ -758,7 +758,9 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
|
||||||
def urlToLink(self, url: str) -> str | None:
|
def urlToLink(self, url: str) -> str | None:
|
||||||
fname = self.urlToFile(url)
|
fname = self.urlToFile(url)
|
||||||
if not fname:
|
if not fname:
|
||||||
return None
|
return '<a href="{}">{}</a>'.format(
|
||||||
|
url, html.escape(urllib.parse.unquote(url))
|
||||||
|
)
|
||||||
return self.fnameToLink(fname)
|
return self.fnameToLink(fname)
|
||||||
|
|
||||||
def fnameToLink(self, fname: str) -> str:
|
def fnameToLink(self, fname: str) -> str:
|
||||||
|
@ -1261,7 +1263,7 @@ class EditorWebView(AnkiWebView):
|
||||||
url = qurl.toString()
|
url = qurl.toString()
|
||||||
# chrome likes to give us the URL twice with a \n
|
# chrome likes to give us the URL twice with a \n
|
||||||
url = url.splitlines()[0]
|
url = url.splitlines()[0]
|
||||||
buf += self.editor.urlToLink(url) or ""
|
buf += self.editor.urlToLink(url)
|
||||||
|
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
|
@ -1279,16 +1281,9 @@ class EditorWebView(AnkiWebView):
|
||||||
if extended and token.startswith("data:image/"):
|
if extended and token.startswith("data:image/"):
|
||||||
processed.append(self.editor.inlinedImageToLink(token))
|
processed.append(self.editor.inlinedImageToLink(token))
|
||||||
elif extended and self.editor.isURL(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)
|
link = self.editor.urlToLink(token)
|
||||||
if link:
|
processed.append(link)
|
||||||
processed.append(link)
|
|
||||||
else:
|
|
||||||
# not media; add it as a normal link
|
|
||||||
link = '<a href="{}">{}</a>'.format(
|
|
||||||
token, html.escape(urllib.parse.unquote(token))
|
|
||||||
)
|
|
||||||
processed.append(link)
|
|
||||||
else:
|
else:
|
||||||
token = html.escape(token).replace("\t", " " * 4)
|
token = html.escape(token).replace("\t", " " * 4)
|
||||||
# if there's more than one consecutive space,
|
# if there's more than one consecutive space,
|
||||||
|
|
|
@ -14,7 +14,7 @@ import aqt.modelchooser
|
||||||
from anki.errors import Interrupted
|
from anki.errors import Interrupted
|
||||||
from anki.importing.anki2 import V2ImportIntoV1
|
from anki.importing.anki2 import V2ImportIntoV1
|
||||||
from anki.importing.apkg import AnkiPackageImporter
|
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.operations import QueryOp
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from aqt.utils import (
|
from aqt.utils import (
|
||||||
|
@ -460,12 +460,13 @@ def full_apkg_import(mw: AnkiQt, file: str) -> None:
|
||||||
|
|
||||||
|
|
||||||
def replace_with_apkg(
|
def replace_with_apkg(
|
||||||
mw: aqt.AnkiQt, filename: str, callback: Callable[[bool], None]
|
mw: AnkiQt, filename: str, callback: Callable[[bool], None]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Tries to replace the provided collection with the provided backup,
|
"""Tries to replace the provided collection with the provided backup,
|
||||||
then calls the callback. True if success.
|
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 = QTimer()
|
||||||
timer.setSingleShot(False)
|
timer.setSingleShot(False)
|
||||||
timer.setInterval(100)
|
timer.setInterval(100)
|
||||||
|
|
|
@ -217,6 +217,10 @@ class QueryOp(Generic[T]):
|
||||||
|
|
||||||
def wrapped_done(future: Future) -> None:
|
def wrapped_done(future: Future) -> None:
|
||||||
assert mw
|
assert mw
|
||||||
|
|
||||||
|
if self._progress:
|
||||||
|
mw.progress.finish()
|
||||||
|
|
||||||
mw._decrease_background_ops()
|
mw._decrease_background_ops()
|
||||||
# did something go wrong?
|
# did something go wrong?
|
||||||
if exception := future.exception():
|
if exception := future.exception():
|
||||||
|
@ -230,11 +234,6 @@ class QueryOp(Generic[T]):
|
||||||
# BaseException like SystemExit; rethrow it
|
# BaseException like SystemExit; rethrow it
|
||||||
future.result()
|
future.result()
|
||||||
|
|
||||||
result = future.result()
|
self._success(future.result())
|
||||||
try:
|
|
||||||
self._success(result)
|
|
||||||
finally:
|
|
||||||
if self._progress:
|
|
||||||
mw.progress.finish()
|
|
||||||
|
|
||||||
mw.taskman.run_in_background(wrapped_op, wrapped_done)
|
mw.taskman.run_in_background(wrapped_op, wrapped_done)
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Callable, Union
|
from typing import Callable, TypeVar, Union
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import PyQt6
|
import PyQt6
|
||||||
|
@ -58,3 +58,16 @@ def qconnect(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Helper to work around type checking not working with signal.connect(func)."""
|
"""Helper to work around type checking not working with signal.connect(func)."""
|
||||||
signal.connect(func) # type: ignore
|
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
|
||||||
|
|
|
@ -352,7 +352,9 @@ def _instrument_type(
|
||||||
class QtClassProxy(
|
class QtClassProxy(
|
||||||
type, metaclass=QtClassProxyType
|
type, metaclass=QtClassProxyType
|
||||||
): # pylint: disable=invalid-metaclass
|
): # pylint: disable=invalid-metaclass
|
||||||
pass
|
@staticmethod
|
||||||
|
def _without_compat_wrapper():
|
||||||
|
return type
|
||||||
|
|
||||||
setattr(module, type_name, QtClassProxy)
|
setattr(module, type_name, QtClassProxy)
|
||||||
|
|
||||||
|
|
|
@ -330,6 +330,7 @@ class AnkiWebView(QWebEngineView):
|
||||||
self._domDone = True
|
self._domDone = True
|
||||||
self._queueAction("setHtml", html)
|
self._queueAction("setHtml", html)
|
||||||
self.set_open_links_externally(True)
|
self.set_open_links_externally(True)
|
||||||
|
self.setZoomFactor(1)
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
def _setHtml(self, html: str) -> None:
|
def _setHtml(self, html: str) -> None:
|
||||||
|
@ -681,6 +682,7 @@ html {{ {font} }}
|
||||||
else:
|
else:
|
||||||
extra = ""
|
extra = ""
|
||||||
self.hide_while_preserving_layout()
|
self.hide_while_preserving_layout()
|
||||||
|
self.setZoomFactor(1)
|
||||||
self.load_url(QUrl(f"{mw.serverURL()}_anki/pages/{name}.html{extra}"))
|
self.load_url(QUrl(f"{mw.serverURL()}_anki/pages/{name}.html{extra}"))
|
||||||
self.inject_dynamic_style_and_show()
|
self.inject_dynamic_style_and_show()
|
||||||
|
|
||||||
|
|
|
@ -343,13 +343,13 @@ def build_tarball(src_path: Path, variant: str) -> None:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
"tar",
|
"tar",
|
||||||
"--zstd",
|
"-I",
|
||||||
|
"zstd -c --long -T0 -18",
|
||||||
"-cf",
|
"-cf",
|
||||||
dist_folder / (dest_path.name + ".tar.zst"),
|
dist_folder / (dest_path.name + ".tar.zst"),
|
||||||
dest_path.name,
|
dest_path.name,
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
env=dict(ZSTD_CLEVEL="9"),
|
|
||||||
cwd=dest_path.parent,
|
cwd=dest_path.parent,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,10 @@ img {
|
||||||
max-height: 95vh;
|
max-height: 95vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
|
||||||
#_flag {
|
#_flag {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
|
@ -48,11 +52,6 @@ img {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.night-mode #typeans {
|
|
||||||
background-color: var(--frame-bg);
|
|
||||||
color: var(--text-fg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.typeGood {
|
.typeGood {
|
||||||
background: #afa;
|
background: #afa;
|
||||||
color: black;
|
color: black;
|
||||||
|
|
Loading…
Reference in a new issue