* add SaveCustomColours rpc method
* restore custom colour palette on editor init
* save custom colour palette on colour picker open and input
there doesn't seem to be an event fired when the picker is
cancelled/closed, so it's still possible for work to be lost
* save colours on `change` instead of `input`
`input` is supposed to be fired on every adjustment to the picker
whereas `change` is only fired when the picker is accepted, but qt
seems to treat both as the latter, so this is currently a no-op
* Store colors in the collection
One minor tweak to the logic while I was there: an invalid color no
longer invalidates all the rest.
---------
Co-authored-by: Damien Elmes <gpg@ankiweb.net>
* Improved margins in Card Browser's "Preview" pane
* Alternate approach that looks good on Mac too
---------
Co-authored-by: Damien Elmes <gpg@ankiweb.net>
Co-authored-by: Damien Elmes <dae@users.noreply.github.com>
* Make timebox message translatable with flexible variable order
Currently, the timebox dialog message is built from two separate strings,
each containing one variable:
"{ $count } cards studied in" + "{ $count } minutes."
As a result, translators cannot freely reorder the variables in their translations.
This change introduces a single string with both variables, allowing translators
to adjust the order for more natural expressions in their languages.
* Preserve old string for now
* Ensure message doesn't display over two lines
---------
Co-authored-by: Damien Elmes <gpg@ankiweb.net>
* Replace media-record.png with SVG icon
- Added SVG version of the microphone icon (from Font Awesome Free v7.0.0)
- Updated sound.py to use QIcon for proper SVG support
- Icon now displays at 60x60 pixels
* Remove obsolete media-record.png icon
The PNG icon has been replaced with an SVG version
* Update CONTRIBUTORS
* Replace icon with AnkiMobile's record icon
CC-BY requires attribution, and we don't currently have a way to describe
those attributions in a way the mobile clients can also include automatically.
For now, I've switched it to an icon I authored myself to avoid the issue.
---------
Co-authored-by: Damien Elmes <gpg@ankiweb.net>
Co-authored-by: Damien Elmes <dae@users.noreply.github.com>
* fix show_exception's messagebox always formatting as plaintext
* Revert "fix show_exception's messagebox always formatting as plaintext"
This reverts commit aec6dd9be8.
* convert SearchError msg to markdown when in browser
While testing the previous PR, I noticed that if stdout is set to None,
the same behaviour is shown as in the following report:
https://forums.ankiweb.net/t/cannot-switch-versions/64565
This leads me to wonder whether IsTerminal is behaving differently
on that user's system, and the use of an env var may be more reliable.
* backend part
* split memorised and cost
* slapdash frontend
* extract some simulator logic
* Add zoomed version of graph
* ./check
* Fix: Tooltip
* Fix: Simulator/workload transition
* remove "time"
* Update ts/routes/graphs/simulator.ts
Co-authored-by: user1823 <92206575+user1823@users.noreply.github.com>
* Added: Mode toggle
* Disable Dr in workload mode
* keep button order consistant between modes
* dont clear points on mode swap
* add review count graph
* Revert "dont clear points on mode swap"
This reverts commit fc89efb1d9.
* "Help me pick" button
* unrelated title case change
* Add translation strings
* fix: missing translation string
* Fix: Layout shift
* Add: Experimental
* Fix Time / Memorized
* per day values
* set review limit to 9999 on open
* keep default at currently set value
* Do DR calculation in parallel (dae)
Approx 5x faster on my machine
---------
Co-authored-by: user1823 <92206575+user1823@users.noreply.github.com>
Co-authored-by: Damien Elmes <gpg@ankiweb.net>
This changes 'keep existing version' to 'sync project changes'
when changes to the pyproject.toml file have been detected that
are newer than the installer's version.
Also adds a way to temporarily enable the launcher, as we needed some
other trigger than the pyproject.toml file anyway, and this approach
also solves #4165.
And removes the 'quit' option, since it's an uncommon operation, and
the user can just close the window instead.
Short-term caveat: users with older launchers/addon will trigger the old
pyproject.toml mtime bump, leading to a 'sync project changes' message
that will not make much sense to a typical user.
* Feat/Add legacy evaluate config bool
* Minor tweaks based on PR suggestions (dae)
New enabling command:
from anki.config import Config
mw.col.set_config_bool(Config.Bool.FSRS_LEGACY_EVALUATE, True)
While something we probably don't want to encourage much of, this
may enable some previously-unshared add-ons.
https://forums.ankiweb.net/t/bundling-numpy-in-an-add-on/62669/5
The 'uv add' command is transaction, so if an add-on tries to inject
incompatible dependencies into the environment, the venv will be
left as-is. And each Anki upgrade/downgrade resets the requirements,
so the requested packages shouldn't cause errors down the line.
Sample add-on:
import subprocess
from aqt import mw
from aqt.operations import QueryOp
from aqt.qt import QAction
from aqt.utils import showInfo
def ensure_spacy(col):
print("trying to import spacy")
try:
import spacy
print("successful import")
return
except Exception as e:
print("error importing:", e)
print("attempting add")
try:
from aqt.package import add_python_requirements as add
except Exception as e:
raise Exception(f"package unavailable, can't install: {e}")
# be explicit about version, or Anki beta users will get
# a beta wheel that may break
(success, output) = add(["spacy==3.8.7", "https://github.com/explosion/spacy-models/releases/download/ko_core_news_sm-3.8.0/ko_core_news_sm-3.8.0-py3-none-any.whl"])
if not success:
raise Exception(f"adding failed: {output}")
print("success")
# alterantively:
# from aqt.package import venv_binary
# subprocess.run([venv_binary("spacy"), "download", "ko_core_news_sm"], check=True)
# print("model added")
# large packages will freeze for a while on first import on macOS
import spacy
print("spacy import successful")
def activate_spacy():
def on_success(res):
mw.progress.single_shot(1000, lambda: showInfo("Spacy installed"))
QueryOp(parent=mw, op=ensure_spacy, success=on_success).with_progress("Installing spacy...").run_in_background()
action = QAction("Activate Spacy", mw)
action.triggered.connect(activate_spacy)
mw.form.menuTools.addAction(action)