## Linked issue (required)
Fixes#5204
## Summary / motivation (required)
This pr fixes incorrect IO text label escaping logic
## Steps to reproduce (required, use N/A if not applicable)
1. In the IO editor, make a text label with `🔤`
2. Reload the note and observe that its gone
## How to test (required)
`./check` and IO notes with text labels like `🔤`, `\:a\b\:`, `::\::`
etc.
### 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
behavior changed.
### Details
Please see commit messages
## Scope
- [x] This PR is focused on one change (no unrelated edits).
## Linked issue (required)
* Closes#5172
## Summary / motivation (required)
Loading MathJax makes the reviewer initialization slower, which is
relevant for people who don't use MathJax and slower devices (such as
phones)
## Steps to reproduce (required, use N/A if not applicable)
N/A
## How to test (required)
1. Opened a deck with a card with no MathJax
2. In developer tools' console,, check whether `MathJax` is not defined
3. Answer some cards until a card with MathJax appears -> Check if it is
correctly loaded
### 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. -->
## 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)
N/A
## Scope
- [X] This PR is focused on one change (no unrelated edits).
## Linked issue (required)
Closes#4754
## Summary / motivation (required)
Image occlusion masks could silently go missing from a note at add time.
Two separate problems caused this, both timing dependent, which is why
the bug was rare and hard to reproduce.
**Problem 1: the editor reset races the add (desktop).** Clicking Add on
an image occlusion note saved the note, queued the actual add, and reset
the mask editor all at once. The reset clears the canvas, and clearing
the canvas writes an empty value into the Occlusions field of the same
note object the add was about to read. Depending on which message
arrived first: the add worked normally (the common case), the note was
added with no masks and no warning, or the add was wrongly rejected with
"no occlusion created" while the masks were plainly visible. Fix: reset
the mask editor only after the add succeeds, matching the ordering the
non-legacy TS editor page already uses. This also means a rejected add
no longer wipes the masks you drew.
**Problem 2: one field's save cancels another's.** All fields committed
through a single shared save timer, and scheduling a save cancelled the
timer's previous pending action. Mask edits commit the Occlusions field
through that timer with a 600 ms debounce, so typing into Header or Back
Extra within 600 ms of the last mask edit threw away the pending
occlusions write. It was never retried, because later saves saw the
store already held the same value and skipped notifying. Every mask
drawn since the last committed write was then missing from the added
note. Fix: give each field its own timer. This also surfaced a second
bug in `ChangeTimer` itself: an action scheduled while
`fireImmediately()` was awaiting a previous action was silently
discarded when that await finished; fixed by taking the action before
running it and letting a concurrent flush wait for the run in flight.
## Steps to reproduce (required, use N/A if not applicable)
Problem 2 (reliably reproducible):
1. Add an Image Occlusion note, draw a mask, wait for it to auto-commit
(~1s).
2. Draw a second mask.
3. Immediately (within ~600 ms) switch to the fields view and type a
character into Header.
4. Wait a second, click Add.
5. Before this fix: the added note is missing the second mask. No
warning is shown.
Problem 1 (event-loop timing dependent, not reliably reproducible by
hand):
1. Add an Image Occlusion note, paste an image, draw one or more masks.
2. Click Add.
3. Before this fix: rarely, the note is added with an empty Occlusions
field and no warning, or Add is rejected with "no occlusion created"
while masks are visible on screen.
## How to test (required)
### 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
behavior changed.
### Details
- `ts/tests/e2e/io-mask-save-race.spec.ts` reproduces problem 2 with
real mouse drawing and real keystrokes at the real 600 ms timing. Fails
on current `main` (payload contains 1 cloze instead of 2, consistently
across repeated runs) and passes with this change.
- `ts/lib/editable/change-timer.test.ts` covers the discarded-action
case in `ChangeTimer` and fails without the fix.
- Full Playwright e2e suite (`just test-e2e`) passes repeatedly across
fresh profiles.
- `just fmt`, `just lint`, and the vitest suite are clean.
- Problem 1 has no reliable automated test: the outcome depends on how
the Qt event loop interleaves two webChannel messages and a background
task. I verified the mechanism with a scripted headless Anki that drives
the real `AddCards` flow and delivers the racing write inside the
vulnerable window, and confirmed by inspection that after this change
the empty write can no longer be issued while an add is in flight.
## Before / after behavior (optional)
Before: masks could be silently dropped from a newly added note (problem
2, common), or the note could be added completely empty of masks /
spuriously rejected (problem 1, rare). After: neither race is possible;
a rejected add also no longer clears the masks you've drawn, so you can
fix the note and retry instead of redrawing.
## Risk / compatibility / migration (optional)
Low risk. Both fixes are ordering/scoping changes with no behavior
change outside the affected race windows. No schema, API, or migration
changes.
## UI evidence (required for visual changes; otherwise N/A)
N/A (no visual/UI changes).
## Scope
- [x] This PR is focused on one change (no unrelated edits).
This PR contains two fixes rather than one. I considered splitting them,
but both are root causes of the same reported bug (#4754) discovered
during the same investigation, they are already isolated into two
separate commits for review, and neither is a complete fix for the issue
on its own. Happy to split into two PRs if you'd prefer.
---------
Co-authored-by: llama <gh@siid.sh>
<!--
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#4871
## How to test (required)
Find "Labs" as a new option in the preferences
### 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 -->
I've implemented the "Labs" feature in a web-view as I presume we are
moving away from QT and
Currently these options have no function. Before we can add the
functionality we should try and come up with the pattern that we should
use to register new flags.
## UI evidence (required for visual changes; otherwise N/A)
<img width="746" height="736" alt="image"
src="https://github.com/user-attachments/assets/28c4fd28-a593-4599-b43e-c2c23881251e"
/>
<img width="746" height="736" alt="image"
src="https://github.com/user-attachments/assets/c1cd69f9-3d8d-4866-a784-030b4274b4e7"
/>
## Scope
- [X] This PR is focused on one change (no unrelated edits).
## Linked issue (required)
Fixes#5114
## Summary / motivation (required)
This fixes an issue I've noticed where if a card repeats, the card info
does not update. This was not optimal, because card info should provide
the most up-to-date info about a card possible, and the previous logic
was not evident to users. This issue could be triggered by answering
"Again" at the end of a study session, at which point a card would
repeat, but the card info would remain the same. Other methods of
triggering included, but were not limited to learning only 1 card at a
time.
## Steps to reproduce (required, use N/A if not applicable)
1. Open card info
2. Answering "Again" at the end of a study session, at which point a
card would repeat
3. The card info would remain the same, contrary to desire
## How to test (required)
Manual tests
### 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
behavior changed. (N/A)
## Before / after behavior (optional)
Before: On direct card repeats, card info pane would not update.
After: On direct card repeats, card info does repeat
## UI evidence (required for visual changes; otherwise N/A)
https://github.com/user-attachments/assets/bf1409cb-fb27-4c50-9ca2-68c0cc61cecehttps://github.com/user-attachments/assets/810dfa53-cee8-4d6c-b22f-3b6daf025cda
## Scope
- [X] This PR is focused on one change (no unrelated edits).
## Linked issue (required)
Closes#4234
<!-- Fixes#123 / Closes#123 / Refs #123 -->
## Summary / motivation (required)
When the HTML editor (</>) is open and focused in the browser's note
editor, switching to a different note and then scrolling and then
clicking (anywhere) causes the view to jump back to the HTML editor's
position. This happens because the `refocus()` triggers both the
browser's native scroll to focused element and CodeMirror's internal
scroll to cursor behavior.
The fix prevents scrolling during refocus in two places:
`PlainTextInput.svelte`: use focus({ preventScroll: true }) instead of
editor.focus() in refocus() to prevent the browser from scrolling the
focused element into view
`code-mirror.ts`: pass { scroll: false } to setSelections() in the focus
handler to prevent CodeMirror from internally scrolling to the restored
cursor position.
<!-- What this PR does and why. For larger changes, add enough context
for reviewers. -->
## 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.
-->
(Following the original issue instructions)
In the browser:
1. Open HTML editor for any field
2. Click inside, putting focus in the HTML editor input
3. Select a different note
4. Scroll to a different part in the note editor and click anywhere
The view jumps to the position of the HTML editor where the focus was
previously
## How to test (required)
Follow the original issue related to this pull request. There is a video
showing the problem.
<!--- How to test: how you verified the change (checks, unit tests,
manual steps, edge cases — the "after" or general validation). --->
### Checklist (minimum)
- [x] I ran `./ninja check` or an equivalent relevant check locally.
### Details
<!-- Commands, manual steps, edge cases, and what you observed -->
<!-- For bugfixes: behavior before vs after. For other types: N/A or a
short note. -->
## Risk / compatibility / migration (optional)
Changes are scoped to two lines in two files. Only the refocus() code
path is affected. User's focus behavior is unchanged.
<!-- Breaking changes, rollout notes, or N/A for small / low-risk PRs
-->
## Scope
- [x] This PR is focused on one change (no unrelated edits).
## Linked issue
Closes#5126
## Summary
This prevents translation strings from the
https://github.com/ankitects/anki-desktop-ftl to be used in Rust/TS code
as AnkiMobile does not pull this module.
## Steps to reproduce (before)
Qt strings such as `addons-no-updates-available` were allowed to be used
in Rust: #5122
## How to test (after)
Revert #5122 and confirm you get a compile error with this PR.
<!--
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 -->
## Summary / motivation (required)
closes https://github.com/ankitects/anki/issues/4978
<!-- What this PR does and why. For larger changes, add enough context
for reviewers. -->
## 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. press "optimize all presets"
2. the window will save the current settings and cloze after it is done.
## How to test (required)
<!--- How to test: how you verified the change (checks, unit tests,
manual steps, edge cases — the "after" or general validation). --->
after pressing optimize all presets, you should be prompted if you want
to save. After confirming you want to save, the window will remain open
when the process completes.
### 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. -->
## 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).
<!--
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).
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>
## 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#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"
/>
<!--
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
Closes#4951
## Summary / motivation
Removes two dead `@import` statements from `_button-mixins.scss`.
Bootstrap's functions and variables were imported but never used, since
all mixins rely exclusively on CSS custom properties (`var(--)`) and
local mixin parameters.
This eliminates ~35 Dart Sass deprecation warnings (`[import]`,
`[color-functions]`) that appeared during `just test-ts --coverage`.
## Test plan
- [x] `just lint` passes
- [x] `just test-ts` passes with no deprecation warnings
## Before / after behavior (optional)
### current behavior (at main branch)
<img width="1424" height="979" alt="image"
src="https://github.com/user-attachments/assets/89fafb5e-e811-48ca-b7e0-dbae36f63d7a"
/>
### Expected behavior (at this branch)
<img width="1489" height="870" alt="image"
src="https://github.com/user-attachments/assets/3eca3296-d31b-4e02-ad5f-e9287e59ee3d"
/>
## Linked issue
Closes#4863
## Summary / motivation
Adds Playwright as the e2e test framework so contributors can write
browser-based tests against a real headless Anki instance. There was no
automated way to exercise mediasrv pages, SvelteKit routes, or the
`/_anki/` RPC surface from a browser, this PR establishes that harness.
Key pieces:
- `qt/tests/launch_anki_for_e2e.py` — spawns a throwaway Anki instance
(temp `ANKI_BASE`, `QT_QPA_PLATFORM=offscreen`). Pre-seeds `prefs21.db`
so Anki skips the language picker and profile chooser and goes straight
to serving mediasrv.
- `playwright.config.ts` — points `webServer` at the launcher; polls
`/favicon.ico` as the readiness probe.
- `ts/tests/e2e/` — `fixtures.ts` base and a sanity spec that verifies
mediasrv is reachable and a SvelteKit page hydrates.
- `justfile` — `just test-e2e` recipe; Chromium installed to
`out/playwright-browsers/`.
- CI — e2e step in `check-linux`; failed-run artifacts uploaded for 7
days.
- `docs/e2e-testing.md` — contributor guide covering setup, managed vs
reuse-server modes, and writing new tests.
## How to test
Build the project once, then run the e2e suite in managed mode (no
separate `./run` needed — the launcher is started automatically):
```shell
just build
just test-e2e
```
## Before / after behavior (optional)
Before: no browser-level test harness existed.
After: `just test-e2e` drives a real headless Anki instance via
Playwright.
## Risk / compatibility / migration
No production code changed. New dev-only files and CI step only.
Chromium is installed to `out/playwright-browsers/` (gitignored) and
does not affect the regular build.
---------
Co-authored-by: Abdo <abdo@abdnh.net>
## Linked issue
Closes#4840
## Summary / motivation
Adds Vitest V8 coverage for TypeScript/Svelte tests via
`@vitest/coverage-v8`.
Introduces `just test-ts --coverage` and `just test-ts --coverage
--html`,
and wires TypeScript into the `just test --coverage` umbrella —
completing
coverage support across all three stacks (Python, Rust, TypeScript).
The threshold is set to 5% — intentionally low because the Vitest test
count is small relative to the TypeScript/Svelte source surface. It is
meant to be raised as more tests are added.
## How to test
```sh
# Existing behavior unchanged
just test-ts
# Terminal summary + enforces 5% line coverage threshold
just test-ts --coverage
# Terminal summary + HTML report under out/coverage/typescript/
just test-ts --coverage --html
# Full umbrella — all three stacks
just test --coverage
just test --coverage --html
```
### Checklist
- [x] I ran `./ninja check` or an equivalent relevant check locally.
### Details
- `@vitest/coverage-v8` pinned at `3.2.4` in `package.json`.
- Reports are written to `out/coverage/typescript/` via
`--coverage.reportsDirectory=../out/coverage/typescript` (relative to
the `ts/` working directory where vitest runs).
- V8 provider is preferred over Istanbul: faster and requires no Babel
transform for TypeScript projects.
- Coverage measures only code reachable through Vitest's module graph —
Svelte component rendering is not covered.
- The `yarn` justfile variable is added for platform-aware yarn
invocation (Windows vs Unix).
## Before / after behavior
Before: no `just test-ts`, no TypeScript coverage.
After: `just test-ts` runs Vitest via ninja; `just test-ts --coverage`
runs with V8 instrumentation.
`just test --coverage` now spans all three stacks.
---------
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)
<!-- Fixes#123 / Closes#123 / Refs #123 -->
fixes https://github.com/ankitects/anki/issues/4795
## Summary / motivation (required)
An error appeared in the console of the deck options. with this change
it doesn't.
<!-- What this PR does and why. For larger changes, add enough context
for reviewers. -->
## 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. open the deck options
2. check the console either by using the Anki command line or by using
[this addon](https://ankiweb.net/shared/info/31746032)
## How to test (required)
<!--- How to test: how you verified the change (checks, unit tests,
manual steps, edge cases — the "after" or general validation). --->
The console should be clear on the deck options page
### 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. -->
## 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).
<!--
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 https://github.com/ankitects/anki/issues/4809
<!-- Fixes#123 / Closes#123 / Refs #123 -->
## Summary / motivation (required)
<!-- What this PR does and why. For larger changes, add enough context
for reviewers. -->
This exposes a JS api to set the speed at which you click to unlock the
FSRS parameters (See #4372)
## 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.
-->
N/A
## How to test (required)
<!--- How to test: how you verified the change (checks, unit tests,
manual steps, edge cases — the "after" or general validation). --->
Open the console in the deck options and run
`anki.setParameterUnlockClickTimeoutMs(0)`. If you set it to 0 then it
will be impossible to unlock. if you set it to something very high it
will be very easy.
### 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. -->
## 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).
## Fix FSRS desired retention not updating on deck preset change (#4469)
### Description
This PR fixes issue #4469, where the "Desired Retention" (DR) setting in
the deck options UI failed to update and could be accidentally
overwritten when switching between configuration presets.
**The Cause:**
Previously, an `onPresetChange` method was being bound in
`DeckOptionsPage.svelte` and passed down to `FsrsOptionsOuter.svelte`.
However, this method was not actually implemented within
`FsrsOptionsOuter.svelte`
**Changes Made:**
* Removed the unimplemented `onPresetChange` prop binding from
`FsrsOptionsOuter.svelte`.
* Implemented the `onPresetChange` logic directly inside
`FsrsOptions.svelte` so that the component internally handles updating
its own FSRS values (including Desired Retention) whenever a new preset
is selected.
**Testing:**
* Additionally, I have tested this change in the simulator component and
didn't observe any side effects.
I would like to get your feedback on this.
Fixes#4469
I'm opening this pr to undo my changes from #4501 and setting the word
break property as well as looking for suggestions on the Easy Days
section, and any other additional sections that may need improvement
(such as placement of buttons or text).
https://github.com/user-attachments/assets/38e65f48-2399-4bd3-9800-375ef4947625
I've tried out adjusting the font size but discovered that a potential
issue could be due to the content's width of the box model being small
which causes the words to be divided. However, if I set the font size's
viewport width to 1vw, the headers fit but at a smaller font.
<img width="200" height="auto" alt="image"
src="https://github.com/user-attachments/assets/303206f2-9dea-4d94-9962-3a70f6f93071"
/>
Perhaps this pr can be closed if setting word break ultimately isn't a
viable option.
Problem:
When the HTML editor is active on a field in the Add Cards dialog,
toggling the pin/sticky on any field and then adding a note (Ctrl+Enter)
causes all active HTML editor fields to collapse unexpectedly.
The editor saves the UI state inside a session object. When the sticky
field is toggled, `update_notetype_legacy()` is called and the
notetype's modTime is changed. When we add the new card we call the
`loadNote()` function that does the following:
1. `saveSession()` saves the current state with the modTime.
2. `setNotetypeMeta() `sees the new modTime and detect a mismatch,
deleting the entire session.
3. `setPlainTexts()` finds no saved sessions and returns the defaults.
Solution:
My solution revolves around changing how we check if the session is
changed or not.
I removed the modTime comparison in `setNotetypeMeta()`. The check not
only causes the fallback of the html after toggling the sticky but also
when changing fields names or font changes.
I added a field count check inside `setPlainTexts()` so now the saved
session is discarded only if the number of fields changes.
So now the modTime is used for the syncing in the backend but not for
the frontend UI.
I also thought about maybe a solution while still using the modTime as
updating the saved modTime in `saveSession()` instead of removing the
check, but this would cause a call from the backend instead of using the
cached value, adding complexity to the code.
Fix: #4468
Closes#4490
The italian and Catalan translations for the
importing-note-updated-as-file-had-newer string contain a variable
reference (probably from an older version of the string). We can avoid
throwing an exception in such cases by passing the `errors` argument to
`FluentBundle.formatpattern()`:
https://projectfluent.org/fluent.js/classes/_fluent_bundle.FluentBundle.html#formatpattern
* Fixed overlapping ranges when all history is selected and days calcolation to prevent overlapping labels
* fix statistics calendar and review grapgh range labeling
* update graph so that the tooltip day label uses integer day boundaries
* fix(import): support variable field count with notetype column (#4421)
* update graph so that the tooltip day label uses integer day boundaries
and removed earlier fix which didnt work
* Fixed overlapping ranges when all history is selected and days calcolation to prevent overlapping labels
* fix statistics calendar and review graph range labeling and removed earlier changes
* reverted changes in time.ts
* reverted changes in calendar.ts
* oldest bin width modified to match other bins in reviews.ts
* modified youngest bar in reviews to include today aswell
* Fix reviews graph: correct Year range to 365 days
* fixed formating for ninja testing
* bin labels and totals correct for year, review
* fixed structure for ninja checks
* reverted changes made to unrelated files
* reviewed code and simplified functions in review.ts
* changed xMax, so today is included in month and 3months
* removed a whitespace
* reverted unneccessary changes to xMax
* simplified code in time.ts and review.rs and pass bin boundaries directly to time.ts
* fixed labels for bins, and made sure year is 365 days
* fixed even bin sizes (0-4 days and 360-364 days) for year review graph
* ninja check fix, let -> const and removed empty line
* added comments to review.ts
* bin merging logic removed, since totals are correctly calculated anyways
* corrections from ninja check
* Update ts/routes/graphs/reviews.ts
Co-authored-by: user1823 <92206575+user1823@users.noreply.github.com>
* Update ts/routes/graphs/reviews.ts
Co-authored-by: user1823 <92206575+user1823@users.noreply.github.com>
* adjusted last bin labeling with xmin
* suggested changes to minimize difference from main
* small review changes
---------
Co-authored-by: junlu592 <junlu592@liu.student.se>
Co-authored-by: user1823 <92206575+user1823@users.noreply.github.com>
* Feat/Disable parameter editing until right clicked 3 times
* wording
* use left click
* Change placeholder to text
* use diabled agaain
* Add warning box
* FSRS
* Added: Timeout ms
* a11y
* Fix: typo
* Fix: Text selection issue
* add GetCustomColours rpc method
* save colours as rgb instead of argb
* show saved custom colours as possible options in colour picker
this is primarily for mobile clients, as qt currently ignores this
* save custom colours on colour picker change (for desktop)
* 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>