## Linked issue
Fixes#4924
## Summary/motivation
The scheduler/cards service layer that the front-end and gRPC clients
call into was largely untested.
This PR adds unit tests that exercise each method through the
`SchedulerService` / `CardsService` traits (proto-in / proto-out),
mirroring how users actually use them, and asserts the resulting state
in `Collection` storage.
**Out of scope**: pure FSRS math methods are intentionally left out.
## How to test
### Details
- `cargo test -p anki --lib scheduler::service::tests` — 42 passing.
- `cargo test -p anki --lib card::service::tests` — 1 passing.
- `cargo fmt --check` and lints clean.
- Tests are deterministic and isolated (each builds its own in-memory
`Collection`); the randomize test uses a retry loop with a permutation
invariant instead of a probabilistic assertion.
## Linked issue
Closes#5053
## Summary
The MSI installer checks for old elevated installs (pre-2.1.64) via the
Windows registry and asks the user to manually uninstall the old version
(as it requires elevation). There are however cases where the registry
value exists but the path is missing (e.g. if the user manually removes
the Anki folder without running uninstall.exe). This makes it so that
uninstall.exe is confirmed to exist in the folder.
Template change:
8d2756a03a
## How to reproduce (before)
1. Install Anki
[2.1.63](https://github.com/ankitects/anki/releases/tag/2.1.63)
2. Go to the installation folder and delete all files
3. Try to install 26.05 and confirm installation is blocked with the
message: "A previous Anki version needs to be uninstalled first. After
uninstallation completes, please run this installer again."
## How to test (after)
Repeat the same process but with an MSI installer built from this PR and
confirm you're not blocked from installation.
Closes#4931
## Summary
- Adds `pylib/tests/test_hooks.py` with full behavioral coverage of
`pylib/anki/hooks.py`
- Coverage increased from 35% → 100% (issue target was 80%)
- Tests cover: hook registration/firing, `runFilter` chaining,
`remHook`, exception propagation, and `wrap` (before/after/around)
## How to test
```
just test-py --coverage
```
---------
Co-authored-by: Fernando Lins <1887601+fernandolins@users.noreply.github.com>
## Linked issue
Closes#5004
## Summary
This adds intro entries for languages that have manual translations
listed under https://docs.ankiweb.net/#translations
Only placeholder pages with links to the contributor websites for now.
## How to test
Run `mint dev` under docs-site and try switching the language from the
top left.
<!--
Title (for the Pull Request title field at the top):
Use a short prefix so the change type is obvious. You do not need to
repeat it in the body below.
Examples:
- fix: — bugfix
- feat: — feature
- refactor: — internal change without user-facing feature
- docs: — documentation only
- chore: — tooling, CI, deps, build housekeeping
- test: — tests only
-->
## Linked issue (required)
<!-- Fixes#123 / Closes#123 / Refs #123 -->
fixes#5006
## Steps to reproduce (required, use N/A if not applicable)
<!-- Steps to reproduce: how to trigger the bug in the broken state (the
"before").
- Mainly for bugfixes;
- For bugs: numbered steps before the fix. For non-bugs: write N/A.
- use N/A for features, refactors, docs, chore, etc.
-->
> I. Undo duplication
>
> (1) Write into the editor “line”.
>
> (2) In the non html editor, select this line, then press the
unordered/ordered list button in the tool bar (or use keyword shortcut);
the html field of the editor becomes
>
> ```
> <ol><li>line</li></ol>
>
> ```
>
> (3) Press the undo shortcut (command + Z on Mac)
>
> Result: the list is still there, but its content is duplicated after
the list; the html field reads:
>
> ```
> <ol><li>line</li></ol>line
>
> ```
>
> Remark: the same bug occurs when we begin with multiple lines instead
of just one.
## How to test (required)
<!--- How to test: how you verified the change (checks, unit tests,
manual steps, edge cases — the "after" or general validation). --->
Follow the reproduction steps. Undo should remove the text in the order
that it appeared.
### Checklist (minimum)
- [X] I ran `./ninja check` or an equivalent relevant check locally.
- [ ] I added or updated tests when the change is non-trivial or
behavior changed.
## Risk / compatibility / migration (optional)
<!-- Breaking changes, rollout notes, or N/A for small / low-risk PRs
-->
Undoes some of the behaviour from #3483. I couldn't reproduce the bug he
mentioned with this patch but I'm not entirely sure this doesn't re
introduce https://github.com/ankitects/anki/issues/3338 as a bug.
## Scope
- [X] This PR is focused on one change (no unrelated edits).
<!--
Title (for the Pull Request title field at the top):
Use a short prefix so the change type is obvious. You do not need to
repeat it in the body below.
Examples:
- fix: — bugfix
- feat: — feature
- refactor: — internal change without user-facing feature
- docs: — documentation only
- chore: — tooling, CI, deps, build housekeeping
- test: — tests only
-->
These pages are in the repo but are not used by the docs.
closes#5050
<!--
Title (for the Pull Request title field at the top):
Use a short prefix so the change type is obvious. You do not need to
repeat it in the body below.
Examples:
- fix: — bugfix
- feat: — feature
- refactor: — internal change without user-facing feature
- docs: — documentation only
- chore: — tooling, CI, deps, build housekeeping
- test: — tests only
-->
## Linked issue (required)
closes#5011
All the "apps.ankiweb.net" Are broken in the form that they currently
exist which makes it difficult to confirm my changes would link to the
correct files so I've left them as they are in this pr.
---------
Co-authored-by: Abdo <abdo@abdnh.net>
## Linked issue (required)
Fixes#5035
## Summary / motivation (required)
Rust builder is outdated an no longer builds the syncserver
successfully.
Bumping the version to match rust-toolchain.toml version 1.92.0 fixes
this issue.
I also bumped the base image to alpine:3.23 as alpine:3.21.0 is over a
year old.
## Steps to reproduce (required, use N/A if not applicable)
docker buildx build -f <Dockerfile> --no-cache --build-arg
ANKI_VERSION=<version> -t anki-sync-server .
## How to test (required)
docker buildx build -f <Dockerfile> --no-cache --build-arg
ANKI_VERSION=<version> -t anki-sync-server .
### Checklist (minimum)
- [x] I ran `docker buildx build -f Dockerfile --no-cache --build-arg
ANKI_VERSION=26.05 -t anki-sync-server .`
- [x] I ran `docker buildx build -f Dockerfile.distroless --no-cache
--build-arg ANKI_VERSION=26.05 -t anki-sync-server .`
### Details
Docker image compiled as expected following the updated versions.
## Scope
- [x] This PR is focused addressing the syncserver docker image.
Co-authored-by: Abdo <abdo@abdnh.net>
## Summary / motivation (required)
This PR fixes Docker Image builds for syncserver failing due to a Rust
version being used in both available Dockerfiles that is no longer
supported by the two dependencies `time@0.3.47` and `time-core@0.1.8`.
## Issue
Fixes#5022
## Steps to reproduce (required, use N/A if not applicable)
1. `cd docs/syncserver`
2. `docker build -f Dockerfile --no-cache --build-arg ANKI_VERSION=26.05
-t anki-sync-server .`
3. Error (see issue)
## How to test (required)
1. `cd docs/syncserver`
2. `docker build -f Dockerfile --no-cache --build-arg ANKI_VERSION=26.05
-t anki-sync-server .`
3. run Docker image
## Scope
- [x] This PR is focused on one change (no unrelated edits).
Signed-off-by: Konstantin Tutsch <mail@konstantintutsch.com>
<!--
Title (for the Pull Request title field at the top):
Use a short prefix so the change type is obvious. You do not need to
repeat it in the body below.
Examples:
- fix: — bugfix
- feat: — feature
- refactor: — internal change without user-facing feature
- docs: — documentation only
- chore: — tooling, CI, deps, build housekeeping
- test: — tests only
-->
## Linked issue (required)
<!-- Fixes#123 / Closes#123 / Refs #123 -->
Fixes#4992
## Summary / motivation (required)
<!-- What this PR does and why. For larger changes, add enough context
for reviewers. -->
This prevents the webview refreshing from affecting the styling of the
page.
## Steps to reproduce (required, use N/A if not applicable)
<!-- Steps to reproduce: how to trigger the bug in the broken state (the
"before").
- Mainly for bugfixes;
- For bugs: numbered steps before the fix. For non-bugs: write N/A.
- use N/A for features, refactors, docs, chore, etc.
-->
1. Load an anki webview
2. Refresh it (Using the ankiview web inspector addon or otherwise)
3. The body will have none of the classes on it and none of the other
styling will be applied
## How to test (required)
<!--- How to test: how you verified the change (checks, unit tests,
manual steps, edge cases — the "after" or general validation). --->
Follow the reproduction steps and the classes should be the same as
before
### Checklist (minimum)
- [X] I ran `./ninja check` or an equivalent relevant check locally.
- [ ] I added or updated tests when the change is non-trivial or
behavior changed.
### Details
<!-- Commands, manual steps, edge cases, and what you observed -->
## Before / after behavior (optional)
<!-- For bugfixes: behavior before vs after. For other types: N/A or a
short note. -->
Before (After a refresh):
<img width="644" height="103" alt="image"
src="https://github.com/user-attachments/assets/888c1794-ed1a-447f-8b14-6cc631a1a1d3"
/>
After (After a refresh):
<img width="644" height="103" alt="image"
src="https://github.com/user-attachments/assets/49a7d6af-0292-4907-8faa-258d94190663"
/>
## Risk / compatibility / migration (optional)
<!-- Breaking changes, rollout notes, or N/A for small / low-risk PRs
-->
## UI evidence (required for visual changes; otherwise N/A)
<!-- Screenshot or short video -->
## Scope
- [X] This PR is focused on one change (no unrelated edits).
The string was either "Tags" if there is no tags, or "n Tags" if there
were a positive number of tags.
This ensure that for 1 tag, this does uses singular.
You can see the difference by opening the "add note" or the browser and
changing the number of tags.
Fixes: #4975.
---------
Co-authored-by: Abdo <abdo@abdnh.net>
This syncs security fixes and translation/version updates from the 26.05
branch.
Related: #5039
---------
Co-authored-by: Andrew Sanchez <andrewsanchez@users.noreply.github.com>
Co-authored-by: Damien Elmes <gpg@ankiweb.net>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## Linked issue
Closes#5037
## Summary
#4908 introduced a subtle issue for all-users MSI installations: it was
causing the installer to search for existing per-user installations
_only_, regardless of the user's choice. The fix is to set
`Schedule="afterInstallInitialize"` on the `MajorUpgrade` element so
that the search is done after the user's choice is resolved:
https://docs.firegiant.com/wix/schema/wxs/majorupgrade/
See template fix:
e028990d92
## Steps to reproduce (before)
1. Install
[26.05b2](https://github.com/ankitects/anki/releases/tag/26.05b2) and
choose the per-machine option in the Installation Scope page.
2. Install [26.05](https://github.com/ankitects/anki/releases/tag/26.05)
with the same scope.
3. Go to Windows' _Settings > Apps > Installed Apps_ and notice two Anki
entries.
## How to test (after)
Repeat the same process and confirm only one app entry.
## Linked issue
Closes#4926
## Summary / motivation
`rslib/src/search/service/` had no test coverage. This PR adds tests
across two files (`mod.rs` and `search_node.rs`), covering:
- `TryFrom<SearchNode>` proto → domain conversions for all filter
variants
- `From<Rating>` and `From<CardState>` conversions
- `SearchService` RPC methods: `build_search_string`, `search_cards`,
`browser_row_for_id`, `replace_search_node`
- `SortMode` construction from proto, including the `Custom` fallback
- Error path: `build_search_string` with an empty group returns
`InvalidInput`
- Exact-match integration tests for deck, tag, and note-type filters in
`search_cards`
## How to test
```bash
just test-rust
```
## Linked issue
Closes#4925
## Summary / motivation
Adds a comprehensive unit test suite for
`rslib/src/notetype/service.rs`, which previously had zero tests. The
file implements the `NotetypesService` gRPC trait for `Collection` — 20
service methods and 6 `From` trait conversions used by both the Qt
frontend (via the legacy JSON adapter path) and the modern proto path.
Tests are organized into five commits by concern:
- **From trait conversions** (`NotetypeId` round-trip,
`ChangeNotetypeRequest ↔ ChangeNotetypeInput` mappings including the `-1
→ None` sentinel and `is_cloze` flag)
- **Legacy JSON adapters** (`add_notetype_legacy`,
`update_notetype_legacy`, `get_notetype_legacy`,
`get_stock_notetype_legacy` for all 6 stock kinds, and all 3 branches of
`add_or_update_notetype`)
- **CRUD service methods** (add, get, update, remove — happy paths,
error paths, and side effects such as card generation when a template is
added)
- **Auxiliary methods** (`get_notetype_names_and_counts`,
`get_aux_notetype_config_key`, `get_aux_template_config_key` including
the Cloze branch and the error path for unknown IDs)
- **Coverage threshold bump** (60 → 64)
## How to test
### Details
```bash
cargo test -p anki --lib "notetype::service::tests"
```
---------
Co-authored-by: Abdo <abdo@abdnh.net>
<!--
Title (for the Pull Request title field at the top):
Use a short prefix so the change type is obvious. You do not need to
repeat it in the body below.
Examples:
- fix: — bugfix
- feat: — feature
- refactor: — internal change without user-facing feature
- docs: — documentation only
- chore: — tooling, CI, deps, build housekeeping
- test: — tests only
-->
## Linked issue (required)
closes#5018
<!-- Fixes#123 / Closes#123 / Refs #123 -->
## Summary / motivation (required)
This prevents large diffs with prettier being used with the mintlify
editor
A caveat of this is that the docs generated by cog now have to be
prettier compatible. Also for some reason prettier has problems with the
{/* */} multi-line comment format so we have to use <!-- --> instead.
Sadly I don't have a good way to check that the Prettier config does not
differ between this PR and the editor.
---------
Co-authored-by: Abdo <abdo@abdnh.net>
## Linked issue
Closes#4556Closes#4557Closes#4144Closes#4151Closes#4152Closes#4153Closes#4229Closes#4356Closes#4401Closes#4403Closes#4519Closes#4523Closes#4390Closes#4414Closes#4484
## Summary / motivation
After 4 months of exploring Briefcase for packaging, we're confident it
solves most problems with the uv launcher with less complexity and at a
lower maintenance cost, especially with the parallel work on the release
CI, which we already used to make 5 releases.
This also removes platform-specific packaging/signing code used to
produce macOS dmg files and Windows NSIS installers, which is now
largely handled by Briefcase and the release CI.
The custom install scripts for Linux are mostly preserved under
qt/installer/linux-template and used in the Briefcase build.
## How to test
- [ ] No build problems.
- [ ] The `tools/build-installer` scripts still work.
- [ ] No CI/release workflow issues.
- [ ] No user-visible changes in dev environment and Briefcase build.
- Updated docs-site/manual/getting-started.mdx
- Deleted docs-site/manual/index.mdx
- Updated docs-site/docs.json
Mintlify-Source: dashboard-editor
Please note that the Mintlify editor seems to make formatting changes to
files which it edits which makes this diff large than necessary.
closes#5000
Uses the introduction / getting started pages as the index for each
category rather than a list of links.
Also re-introduces some pages that were missing from the FAQ to use as
the index as well.
I'm sorry about the large diff but mintlify makes formatting changes
when you use the editor.
---------
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
<!--
Title (for the Pull Request title field at the top):
Use a short prefix so the change type is obvious. You do not need to
repeat it in the body below.
Examples:
- fix: — bugfix
- feat: — feature
- refactor: — internal change without user-facing feature
- docs: — documentation only
- chore: — tooling, CI, deps, build housekeeping
- test: — tests only
-->
## Linked issue (required)
<!-- Fixes#123 / Closes#123 / Refs #123 -->
What I used to complete #4916
## Summary / motivation (required)
<!-- What this PR does and why. For larger changes, add enough context
for reviewers. -->
If we need to add more github actions in future this should make it easy
---------
Co-authored-by: Abdo <abdo@abdnh.net>
## Linked issue
Closes#4997
## Summary
The MSI installer checks the Windows registry for older NSIS-based
installations and tries to run their uninstall.exe. If the registry
value is found but the uninstall file is missing for any reason, the
installer used to fail.
See template change:
8c4932ee1b
## Steps to reproduce (before)
1. Install
[25.09](https://github.com/ankitects/anki/releases/tag/25.09).
2. Go to the installation folder and delete uninstall.exe
3. Try to install
[26.05b2](https://github.com/ankitects/anki/releases/tag/26.05b2) - you
should see an error message as reported in the linked report.
## How to test (after)
1. Install 25.09.
2. Go to the installation folder and delete uninstall.exe.
3. Build the installer: `./tools/ninja installer:package`.
4. Run the installer and confirm it succeeds.
## Summary
- add a generated `docs-site/` Mintlify proof of concept for a unified
Anki docs site
- migrate the desktop manual, AnkiMobile docs, FAQs, add-on docs,
translation docs, release notes, legacy docs, and repo-local Sphinx/MyST
developer docs into the POC tree
- add a migration helper that preserves mdBook ordering, handles common
MDX incompatibilities, and regenerates the landing-page-inspired
Mintlify styling
- apply minimal styling based on the current Anki landing page: Anki
logo, Hanken Grotesk, blue primary color, subtle surfaces, and compact
nav treatment
## Validation
- `uv run --with ty ty check tools/mintlify_poc_migrate.py`
- `source ~/.nvm/nvm.sh && nvm use 22.15.0 && mint validate`
- previewed locally with `mint dev --port 3000` and checked the home
page/developer docs in browser
## Notes
This is intentionally a draft POC. It does not remove the existing
Sphinx or mdBook docs flows yet; it demonstrates what bringing the
sources into this repo and building from a single Mintlify root could
look like.
---------
Co-authored-by: Andrew Sanchez <andrewsanchez@users.noreply.github.com>
Co-authored-by: Luc Mcgrady <lucmcgrady@gmail.com>
Co-authored-by: Abdo <abdo@abdnh.net>
## Linked issue
Fixes CI failure introduced by #4927
## Summary / motivation
The column-position tests added in the previous PR were failing in CI on
Linux. Anki GUIDs use a base91 charset that includes `"` (double quote).
When a GUID contains `"`, the CSV writer correctly wraps the field in
quotes, but the tests were splitting the raw line by `\t`, which
returned the surrounding quotes as part of the value:
```
left: ""AtI#}YcFn"" ← field value including CSV quotes right: "AtI#}YcFn" ← the actual GUID
```
The fix replaces manual `split('\t')` with `csv::ReaderBuilder` in both
affected tests. This is also the semantically correct approach: the same
parser the importer uses, which automatically unquotes fields.
The bug only manifested on Linux because the randomly generated GUID on
macOS happened not to contain `"`.
## How to test
```bash
cargo test -p anki --lib "import_export::text::csv::export::tests"
```
All 18 tests pass.
## Linked issue
Closes#4986Closes#4985
## Summary
- Move the complexipy-diff check to Ninja so it can be run locally as
part of `./ninja check`.
- Fix
[SARIF](https://docs.github.com/en/code-security/reference/code-scanning/sarif-files/sarif-support)
results not being uploaded if the Complexipy check fails.
## Steps to reproduce (before)
complexipy-diff was only run on CI and pre-push automatically. It was
not covered by ninja check, which is not consistent with most tests and
tools.
## How to test (after)
- Run `./ninja check:complexipy-diff` and confirm it passes.
- Introduce some complex Python change (e.g. add some nested if
statements) so that the check fails now.
- Update your pre-commit config: `./out/extracted/uv/uv run pre-commit
install`.
<!--
Title (for the Pull Request title field at the top):
Use a short prefix so the change type is obvious. You do not need to
repeat it in the body below.
Examples:
- fix: — bugfix
- feat: — feature
- refactor: — internal change without user-facing feature
- docs: — documentation only
- chore: — tooling, CI, deps, build housekeeping
- test: — tests only
-->
## Linked issue (required)
<!-- Fixes#123 / Closes#123 / Refs #123 -->
closes#4983
## Summary / motivation (required)
<!-- What this PR does and why. For larger changes, add enough context
for reviewers. -->
This pr adds links to the aqt section of the docs.
### Details
<!-- Commands, manual steps, edge cases, and what you observed -->
## UI evidence (required for visual changes; otherwise N/A)
<img width="306" height="111" alt="image"
src="https://github.com/user-attachments/assets/39111e4f-8aa1-4865-b193-0da949967618"
/>
## Scope
- [X] This PR is focused on one change (no unrelated edits).
---------
Co-authored-by: Abdo <abdo@abdnh.net>
## Linked issue
Closes#4927
## Summary / motivation
`rslib/src/import_export/service.rs` and
`rslib/src/import_export/text/csv/export.rs` had no unit test coverage.
This PR adds tests for the meaningful logic in both modules: type
conversions, error propagation, config defaults, and CSV
structure/content.
**`service.rs`**
- All 4 branches of `From<ExportLimit> for SearchNode` (None, DeckId,
NoteIds, CardIds)
- `get_import_anki_package_presets` default config values
- `export_card_csv` / `export_note_csv` `UInt32` return type
- Error propagation for all I/O functions (`import_anki_package`,
`import_csv`, `import_json_file`, `import_json_string`,
`export_card_csv`, `export_note_csv`, `export_anki_package`)
- Success paths using a real `.apkg` fixture, temp CSV, and temp JSON
files
- `From<OpOutput<NoteLog>> for ImportResponse` field mapping
**`text/csv/export.rs`**
- Card CSV: header format, empty collection writes header, count
(single/multiple/filtered by deck), front/back in correct tab-separated
columns, HTML preserved and stripped
- Note CSV: count (single/multiple/filtered by deck), exact column index
values, column headers absent when metadata disabled, fields and GUID in
correct columns, GUID omitted when `with_guid: false`, HTML preserved
and stripped
**`text/csv/metadata.rs`**
- Extends `should_detect_valid_delimiters` to cover the last-resort
`Delimiter::Space` fallback when the file has no recognizable separator
in its content
## How to test
```bash
cargo test -p anki --lib "import_export::service::tests"
cargo test -p anki --lib "import_export::text::csv::export::tests"
cargo test -p anki --lib "import_export::text::csv::metadata::test::should_detect_valid_delimiters"
```
All 18 tests in export.rs, all tests in service.rs, and the delimiter
test pass.
This triggers the check-linked-issue.yml workflow on the `labeled`
event, so that if the `hotfix` label is added later after the PR is
opened, the workflow is rerun.
## Summary / motivation
The `check-linked-issue` workflow was triggering with the `pull_request`
event. This caused the step "Apply missing-issue label and comment" to
fail with:
> HttpError: Resource not accessible by integration
Switching to `pull_request_target` makes the workflow run in the context
of the base repository instead of the fork, so the declared write
permissions are honoured.
The change is safe because this workflow never checks out PR code. It
only reads PR metadata (body, labels) through the API. There is no risk
of running untrusted code with elevated permissions.
## Steps to reproduce
1. Open a PR from an external fork that has no linked issue.
2. Observe the "Check Linked Issue" CI job failing with
`HttpError: Resource not accessible by integration`.
## Before / after behavior
**Before:** workflow crashes on every fork PR that lacks a linked issue.
**After:** workflow applies the label and posts the comment as intended.
## Linked issue
Closes#4929
## Summary / motivation
`ts/html-filter/element.ts` is responsible for sanitizing HTML on paste
in the note editor, so it's security-sensitive code. About 8% of it
wasn't covered by tests, including the branches that strip attributes
and tags. This PR adds tests to get it to full coverage (statements,
branches, functions, and lines) so we don't accidentally break the
filtering later. There are no changes to the actual code, just tests.
## Steps to reproduce
N/A - tests only.
## How to test
### Checklist (minimum)
- [x] I ran `./ninja check` or an equivalent relevant check locally.
- [x] I added or updated tests when the change is non-trivial or
behaviour changed.
### Details
Ran `just check` (the full lint and test build) and everything passed.
For coverage specifically, I scoped vitest to the target file:
`npx vitest run html-filter/index.test.ts --coverage
--coverage.include='html-filter/element.ts'`
That reports element.ts at 100% for statements, branches, functions, and
lines.
New tests cover:
- event handler attributes (`onclick`/`onerror`) stripped from allowed
elements
- `<script>` removal, including empty and nested cases
- unknown/disallowed tags dropped (empty) or unwrapped (with content)
- `<span>` styling honouring night mode (the previously uncovered
branch)
## Before / after behavior
N/A - no behaviour change, tests document existing behaviour.
## UI evidence
N/A
## Scope
- [x] This PR is focused on one change (no unrelated edits).
**Note for reviewers:** the originating issue listed "verify `<p>` →
`<div>` conversion" as a criterion, but `element.ts` treats `<p>` as an
allowed tag (`P: allowNone`) and preserves it, so there is no `p`→`div`
conversion in this component. The tests document the actual behaviour
(`<p>` kept, attributes stripped). Happy to adjust if a conversion was
intended elsewhere.
---------
Co-authored-by: Fernando Lins <1887601+fernandolins@users.noreply.github.com>
## Linked issue
Closes#4928
## Summary / motivation
`learning.rs` and `relearning.rs` had 0% test coverage. They implement
the core scheduling logic for new and lapsed cards — a regression here
affects every user's review queue silently. This PR adds unit tests to
establish a safety net before future migrations or FSRS changes touch
this code.
## How to test
```bash
cargo test -p anki --lib "states::learning::tests"
cargo test -p anki --lib "states::relearning::tests"
```
## Details
`learning.rs` — tests covering all four buttons under SM-2: again (reset
to first step, no-steps graduation), hard (same step, no-steps
graduation), good (advance or graduate), easy (always graduates,
interval ordering invariant), plus FSRS routing stubs.
`relearning.rs` — tests covering SM-2 transitions (lapse penalty, step
keep/reset/graduate), FSRS routing (long interval → Review, short-term →
Relearning for all buttons), and multi-step behavior (again resets, hard
keeps, good advances through intermediate steps).
## Linked issue
Closes#4969
## Summary
Add a [QWidget.update()](https://doc.qt.io/qt-6/qwidget.html#update)
call for the main webview after the answer is revealed and images are
loaded. This should reduce UI glitches, especially on cards with images.
## How to test
It's hard to test and reproduce this, because its frequency varies
between operating systems and graphics drivers.
I've seen improvements on my collection on Windows with the default
video driver (Direct3D) while doing my daily reviews today with this
change and I'll continue testing over the week.
## Summary
Closes#4954.
`_functions.scss` already had `@use "sass:map"` at the top but was still
calling the deprecated global functions `map-get` and `map-merge`.
Replaced all occurrences with their namespaced equivalents `map.get` and
`map.merge`.
This eliminates the `[global-builtin]` deprecation warnings visible in
every Check (Linux) CI run, which will become hard errors in Dart Sass
3.0.
## Test plan
- [x] `just lint` passes
- [x] `just test-ts` passes with no deprecation warnings
## Before / after behavior (optional)
### at main branch
<img width="1325" height="906" alt="Image"
src="https://github.com/user-attachments/assets/6a35ac2d-418a-4ded-baf6-f959b06b8d48"
/>
### at this branch
<img width="1156" height="921" alt="image"
src="https://github.com/user-attachments/assets/276a15ce-b319-4ee5-9ae2-122cd4d4d45e"
/>