Commit Graph

2295 Commits

Author SHA1 Message Date
krMaynard
fb83be1285 fix: prevent panic when merging a notetype with duplicate fields (#5070)
## Linked issue (required)

Fixes #4345.

## Summary / motivation (required)

`Notetype::merge_fields` and `merge_templates`
(`rslib/src/notetype/merge.rs`) used the **incoming** notetype's index
directly as a position into **our** own `fields`/`templates`. When the
incoming notetype has more fields than ours — e.g. a corrupt notetype
whose field matches one of ours a second time at an index past the end
of our list — `Vec::swap`/`Vec::insert` received an out-of-bounds index
and panicked:

```
thread '<unnamed>' panicked at rslib/src/notetype/merge.rs:42:33:
index out of bounds: the len is 22 but the index is 22
```

This made the affected deck impossible to import.

The fix clamps the target index to the valid range so the merge degrades
gracefully instead of panicking. All incoming fields/templates are still
merged in; a duplicate match simply collapses onto the existing entry
rather than crashing.

## Steps to reproduce (required, use N/A if not applicable)

1. Import a deck whose notetype has more fields than the local notetype
it maps to, where one incoming field matches an existing field a second
time (reported in the wild on corrupt decks —
https://forums.ankiweb.net/t/im-unable-to-import-any-decks/66497).
2. The import panics with `index out of bounds` in `notetype/merge.rs`.

## How to test (required)

### Checklist (minimum)

- [ ] 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

Added two regression tests — `merge_longer_other_with_duplicate_field`
and `merge_longer_other_with_duplicate_template`. Verified they **panic
on the unfixed code** and **pass with the fix**, and that the whole
`notetype::merge` module passes:

```
cargo test -p anki --lib notetype::merge::
test result: ok. 8 passed; 0 failed
```

`rustfmt --check` is clean on the changed file.

## Before / after behavior (optional)

Before: importing such a notetype panics and aborts the import. After:
the merge completes, mapping the duplicate onto the existing
field/template.

## Risk / compatibility / migration (optional)

Low — the clamp only changes behavior in the previously-panicking
out-of-bounds case; in-range merges are unaffected.

## UI evidence (required for visual changes; otherwise N/A)

N/A

## Scope

- [x] This PR is focused on one change (no unrelated edits).
2026-07-15 06:29:30 +08:00
Christos Longros
8a99c47639 fix: strip FSRS params from deck config on export without scheduling (#4721)
Closes #4524.

Strip `fsrs_params_4/5/6` from each deck config when exporting with
`with_scheduling=false`. These weights are persistent on the exporter's
review history and shouldn't be exported.

Tested with new gather-layer unit tests and a round-trip apkg
export-and-reimport test; all 65 existing `import_export` tests pass.

---------

Signed-off-by: Christos Longros <chris.longros@gmail.com>
2026-07-10 01:19:02 +08:00
Ashish Yadav
57eb9f49af fix: omit empty "Error details:" line from network errors (#5120)
<!--
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 #5117

## Summary / motivation (required)
`NetworkError::message` always appended the `network-details` line, so
an error with an empty `info` string rendered a dangling "Error
details:" label with nothing after it. Only append the details when
there is something to show.

The message is built in the shared backend, so the empty label showed up
on every client (it was reported on AnkiDroid) is still used unchanged
when details are present.

## Steps to reproduce (required, use N/A if not applicable)
Any network error

## How to test (required)
Added tests

### Checklist (minimum)

- [ ] 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

<!-- 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).
2026-07-09 18:57:27 +03:00
Abdo
f6ac1985ba fix: sync_status() reporting FULL_SYNC for new empty collections (#5112)
## Linked issue

Fixes #5109

## Summary / motivation

This changes the `sync_status()` method so that it reports `NoChanges`
for new empty collections. Currently it reports `FullSync` on a fresh
collection and this status persists even after syncs because the process
works as follows:
1. Anki checks the local collection's modification and schema change
timestamps against the time of the last sync (See
`sync_status_offline()`).
2. If the collection was never synced (`last_sync=0`)
`sync_status_offline()` returns `FullSync` as there's no special
handling for this case.
3. `sync_status_inner()` returns the value returned by
`sync_status_offline()` if it's not `NoChanges`, skipping the AnkiWeb
network request, so the remote collection's modification time is never
compared to the local modification time (they are both 0 on a fresh
collection & account, so `online_sync_status_check()` would have
returned `NoChanges` if not for the early return).
4. When the user clicks the sync button, the action required is
determined solely by `online_sync_status_check()`, which returns
`NoChanges` as noted earlier, so no sync is done and `last_sync` never
gets updated, causing the offline sync status to get stuck on
`FullSync`.

## Steps to reproduce (before)

1. Create a new AnkiWeb account and log in to it in a fresh Anki
profile. Notice the sync button is now red.
2. Click the sync button (without creating notes or making any DB
modifications to the collection) and confirm no prompt to accept the
"fake" full sync.
3. Notice the button is still red, indicating the full sync status
persists and won't be cleared until you make changes to the either the
local or remote collection.

## How to test (after)

Repeat the same process and confirm the sync button is never red for a
fresh collection.
2026-07-09 15:31:23 +03:00
Abdo
62efee43f0 fix: Disallow Qt translation strings in Rust/TS code (#5127)
## 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.
2026-07-08 21:21:33 +03:00
Abdo
531875e67e fix: Add new string for "No Updates available" (#5122)
We were using a Qt Fluent string in the core, which is not pulled by
AnkiMobile. See https://github.com/ankitects/ankimobile-ftl/pull/4
2026-07-08 09:53:49 -05:00
user1823
5d51ca02b2 Feat/Check database fixes incorrect last review time entries in cards (#4648)
The value saved in the card can be incorrect, for example, due to a
previous sync conflict.
2026-07-07 16:07:54 +01:00
Luc Mcgrady
a0bf0b3e5e fix: dr incorrectly set in simulators (#5101)
closes #5100
2026-07-07 15:11:39 +03:00
Marcus Lo
3186cc31c4 fix: detect empty cards when template uses special field conditionals (#4807)
## Linked issue (required)

Fixes #4750

## Summary / motivation (required)

`new_cards_required_normal()` builds a set of non-empty fields from the
note's actual fields, but only special-cases `Tags`. Other special
fields like `Deck`, `Subdeck`, `Card`, `CardFlag`, `Type`, and `CardID`
are always populated at render time but were missing from this set. This
caused `{{^Deck}}` conditionals to be incorrectly evaluated as
potentially rendering, making cards appear non-empty to the checker. The
fix adds all always-present special fields to `nonempty_fields`.

## Steps to reproduce (required)

1. Create a Basic (and reversed card) note with content in both fields
2. Change Card 1's front template to `{{^Deck}}{{Back}}{{/Deck}}`
3. Observe the previewer shows a blank card
4. Run Tools → Check → Empty Cards — no empty cards are reported

## How to test (required)

Run `./ninja check:pytest` — a new regression test
`test_empty_cards_special_fields` in `pylib/tests/test_cards.py` covers
this case.

### 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
New test verifies that a card with `{{^Deck}}{{Back}}{{/Deck}}` as its
front template is correctly detected as empty.

## Before / after behavior (optional)

Before: cards using special field conditionals like `{{^Deck}}` were not
detected as empty. After: they are correctly flagged.

## Risk / compatibility / migration (optional)

Low risk. Only affects empty card detection logic, not card rendering or
scheduling.

## UI evidence (required for visual changes; otherwise N/A)

N/A

## Scope
- [x] This PR is focused on one change (no unrelated edits).
2026-07-06 01:44:19 +08:00
helmut carter
9b5346a507 fix(bug): race condition (#4635)
Publish the media sync abort handle before spawning the worker, clear it
with a scope guard, and return `CollectionNotOpen` instead of panicking
if the collection is gone before the worker starts. This avoids a race
where closing the collection during media sync startup could panic.
2026-07-06 00:18:45 +08:00
Abdo
643187a050 Shift editor control to TypeScript (#4029)
Closes https://github.com/ankitects/anki/issues/3830

---------

Co-authored-by: Fernando Lins <fernandolins@users.noreply.github.com>
2026-07-03 21:56:43 +03:00
Luc Mcgrady
1d12b11cdd fix(fsrs): Use fsrs config bool for set due date (#4792)
<!--
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 #4791
<!-- Fixes #123 / Closes #123 / Refs #123 -->

## Summary / motivation (required)

<!-- What this PR does and why. For larger changes, add enough context
for reviewers. -->

Previously, using set due date on a new card was broken for FSRS because
new cards didn't have a memory state which was how the set due date
function determined if FSRS is enabled or not. See
https://github.com/ankitects/anki/pull/4035#discussion_r2107488134.

This PR also makes it so that new cards behave as new cards after set
due date is used on them (Only while FSRS is enabled).

## 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. create a new card
2. set it's due date
3. set it's due date again
4. the due date won't change
5. use grade now
6. the card will be scheduled based on the future date due date rather
than now

## How to test (required)

<!--- How to test: how you verified the change (checks, unit tests,
manual steps, edge cases — the "after" or general validation). --->
I followed the reproduction steps and instead, after using set due date
the card remains in a new state, and the saved interval is the inputted
number (with a minimum of one to prevent it appearing as a reset)

### 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
-->
If there are any ways I have not foreseen that an interval can be set to
0, then it may cause problems with this approach.

## 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).
2026-07-03 17:36:29 +03:00
Asuka Minato
fd65f20fcd chore: bump fsrs version (#4956)
<!--
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 -->
Refs #4080

## Summary / motivation (required)

<!-- What this PR does and why. For larger changes, add enough context
for reviewers. -->

bump fsrs version

## 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). --->

### 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

<!-- 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).
2026-07-03 10:45:00 +01:00
user1823
c6aafbd2e6 Perf: Cache FSRS config flags in CardQueues to avoid per-answer DB reads (#4755)
<!--
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 #4394

## Summary / motivation (required)

Currently, `card_state_updater` calls `get_config_bool` for
`BoolKey::Fsrs` and `BoolKey::FsrsShortTermWithStepsEnabled` on every
card answer, incurring a SQLite read each time.

With this PR, the flags are read once at queue-build time and then
threaded into `CardQueues` so that `card_state_updater` can read the
cached value instead of hitting the config DB on every answer.

When `card_queues` is `None` (unit tests, direct API calls outside the
reviewer), fall back to `get_config_bool` to preserve existing behavior.

This is the same pattern used for `load_balancer` in #3860.

## Steps to reproduce (required, use N/A if not applicable)

N/A — performance optimization, no user-facing change.

## How to test (required)

Not sure

### Checklist (minimum)

- [ ] 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).
2026-07-03 09:43:37 +01:00
Tim Gatzke
8127fd2488 fix(fsrs): avoid interval regressions from fuzz (#4826)
## Linked issue (required)

Fixes #4431

## Summary / motivation (required)

Fix an FSRS edge case where fuzz can make a `Good` interval go
backwards, such as `4 -> 3`.

The existing safeguard only handled cases where the newly rounded
interval was already greater than the previous one. This change
preserves the previous interval when it still falls inside the current
fuzz range, and adds a regression test for the reported case.

## Steps to reproduce (required, use N/A if not applicable)

1. Start from an FSRS review state where the previous `Good` interval
was fuzzed up from 3 days to 4 days.
2. Review the card again in a state where FSRS computes a new `Good`
interval that rounds to 3 days but still has a fuzz range that includes
4.
3. Observe that the old logic schedules the next `Good` interval as 3
days.

## 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

- Ran `cargo test -p anki scheduler::states::review::test --
--nocapture`
- Added a regression test for the reported `4 -> 3` case
- Ran `./ninja check` successfully

## Before / after behavior (optional)

Before: a card whose previous interval had been fuzzed up to 4 days
could later show a `Good` interval of 3 days.

After: when the previous interval still lies inside the current fuzz
range, the interval is preserved instead of moving backwards.

## Risk / compatibility / migration (optional)

Low risk, as the change is limited to FSRS passing review interval
handling and no migration steps are required.

## UI evidence (required for visual changes; otherwise N/A)

N/A

## Scope

- [x] This PR is focused on one change (no unrelated edits).
2026-07-01 15:27:12 +01:00
user1823
b00308e551 fix(search): normalize whitespace in search query parser (#4853)
## Linked issue (required)

Fixes https://github.com/ankitects/anki/issues/5046
Fixes https://github.com/ankitects/anki/issues/4215

Related:
https://forums.ankiweb.net/t/search-search-problem-in-browse-windows/38603

## Summary / motivation (required)

When the browser search query contains non-breaking spaces (for e.g.,
when text is copied and pasted from a webpage or a card rendered in the
reviewer webview), the parser embeds the \u{a0} characters inside the
SQL query rather than treating them as word separators. SQL then tries
to search them as literal substrings, which never matches the note's
stored field text (which uses normal spaces), resulting in zero results
even though matching notes exist.

The fix normalizes \u{a0} to a regular space in parse(), before any
parsing begins, so it is consistently treated as a word separator
everywhere.

## Steps to reproduce (required, use N/A if not applicable)

1. Create a note with the text "hello world".
2. Copy text having a non-breaking space between "hello world" into the
browser's search bar (I am unable to paste a sample here) and press
Enter.
3. See that no results are returned.

## How to test (required)

In this branch, the same steps return the note as expected.

Checking the SQL query:
Apply this patch, set environment variable `set RUST_LOG=anki=debug`
before running Anki and then repeat the repro steps:
```diff
diff --git a/rslib/src/search/mod.rs b/rslib/src/search/mod.rs
index 0dd52dbc3..4fdab340c 100644
--- a/rslib/src/search/mod.rs
+++ b/rslib/src/search/mod.rs
@@ -24,6 +24,7 @@ use rusqlite::params_from_iter;
 use rusqlite::types::FromSql;
 use sqlwriter::RequiredTable;
 use sqlwriter::SqlWriter;
+use tracing::debug;
 pub use writer::replace_search_node;

 use crate::browser_table::Column;
@@ -189,6 +190,7 @@ impl Collection {

         let (mut sql, args) = writer.build_query(&top_node, mode.required_table())?;
         self.add_order(&mut sql, item_type, mode)?;
+        debug!(sql = %sql, args = ?args);

         let mut stmt = self.storage.db.prepare(&sql)?;
         let ids: Vec<_> = stmt
```

On the main branch, the terminal prints:
```
DEBUG sql=select c.id from cards c, notes n where c.nid=n.id and ((n.sfld like ?1 escape '\' or n.flds like ?1 escape '\')) order by c.mod desc args=["%hello\u{a0}world%"]
```

On this branch, the terminal prints:
```
DEBUG sql=select c.id from cards c, notes n where c.nid=n.id and ((n.sfld like ?1 escape '\' or n.flds like ?1 escape '\') and (n.sfld like ?2 escape '\' or n.flds like ?2 escape '\')) order by c.mod desc args=["%hello%", "%world%"]
```

### Checklist (minimum)

- [ ] 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

<!-- 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).

---------

Co-authored-by: Abdo <abdo@abdnh.net>
2026-06-29 15:29:56 +03:00
krMaynard
c881800f77 Fix flaky scheduler test (#5030)
## Linked issue (required)

Fixes #5025

## Summary / motivation (required)

`scheduler::answering::test::new_limited_by_reviews` in
`rslib/src/scheduler/answering/mod.rs` is time-of-day dependent and
fails when
run between roughly 3:50–4:00 GMT. The source carried a
`// FIXME: This fails between 3:50-4:00 GMT` comment documenting the
flakiness.

The test's final `answer_good()` schedules a learning card a 10-minute
step
ahead. Run shortly before the default 4am rollover, that step crosses
into the
next day, so the card is no longer counted as intraday learning and the
final
`assert_counts!(col, 0, 0, 0)` breaks.

This guards the test with the same `if col.timing_today()?.near_cutoff()
{ return Ok(()); }`
early-return that the sibling timing-sensitive tests in this file
(`state_application`, `elapsed_secs`) already use, and replaces the
FIXME with a
comment explaining the cause. `near_cutoff()`'s 25-minute margin safely
covers
the 10-minute step window.

## Steps to reproduce (required, use N/A if not applicable)

1. Set the system clock to a few minutes before the collection's daily
cutoff
(the default 4am rollover — i.e. ~03:50–03:59 in the collection's
timezone).
2. Run `cargo test -p anki --lib
scheduler::answering::test::new_limited_by_reviews`.
3. The final `assert_counts!(col, 0, 0, 0)` fails because the last
learning
   card's 10-minute step lands after the cutoff.

## How to test (required)

### Checklist (minimum)

- [ ] 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

`cargo test -p anki --lib
scheduler::answering::test::new_limited_by_reviews`
passes, and the full `scheduler::answering` module passes (5/5). The fix
mirrors
the existing `near_cutoff()` guard already used by the two sibling
timing-sensitive tests in the same file.

## Before / after behavior (optional)

Before: the test fails non-deterministically in the ~10-minute window
before the
daily cutoff. After: it early-returns in the near-cutoff window
(25-minute
margin) and runs normally the rest of the day.

## Risk / compatibility / migration (optional)

N/A — test-only change, no production behavior affected.

## UI evidence (required for visual changes; otherwise N/A)

N/A

## Scope

- [x] This PR is focused on one change (no unrelated edits).

---------

Co-authored-by: Fernando Lins <1887601+fernandolins@users.noreply.github.com>
2026-06-25 16:38:24 -03:00
Fernando Lins
d8ce60c272 test: add service-layer unit tests for scheduler and cards (#4924) (#5042)
## 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.
2026-06-23 11:07:29 -03:00
Fernando Lins
d13f487bea test: add unit and integration tests for search service (#5024)
## 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
```
2026-06-17 13:50:10 -03:00
Fernando Lins
2a715b211f test(notetype): add unit tests for notetype service layer (#5014)
## 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>
2026-06-17 17:32:00 +03:00
Abdo
0d7b3fdd96 feat: Remove the uv launcher and old packaging code (#5019)
## Linked issue

Closes #4556
Closes #4557
Closes #4144
Closes #4151
Closes #4152
Closes #4153
Closes #4229
Closes #4356
Closes #4401
Closes #4403
Closes #4519
Closes #4523
Closes #4390
Closes #4414
Closes #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.
2026-06-17 03:02:52 +03:00
Fernando Lins
02e16b5473 fix(csv): use csv reader in column position tests to handle quoted fields (#4994)
## 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.
2026-06-11 11:00:15 -03:00
Fernando Lins
a5086f1def test: add unit tests for CSV export and import-export service layer (#4976)
## 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.
2026-06-10 14:23:05 -03:00
user1823
83d711ff0b Feat: Expose card's decay and DR to custom scheduler (#4880)
I wanted to use the card's decay in my custom scheduling script. But,
Anki doesn't expose it to JS. This small change exposes the values to
JS.
2026-06-08 20:26:42 +03:00
Fernando Lins
02210ec450 test(scheduler): add unit tests for learning and relearning state transitions (#4963)
## 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).
2026-06-08 11:59:54 -03:00
Abdo
56d93bb3da fix: Parameterize inputs of prop:cd* queries (#4905)
It was possible to construct invalid SQLite queries for browser searches
in some cases, for example by searching for `prop:cds:foo=';`.
2026-05-27 16:29:42 +03:00
Abdo
5d1266eb8e fix: Strip HTML from note type names in Empty Cards (#4902)
Strip HTML from note type and template names in the Empty Cards screen.

## How to test
```
cargo test -p anki --lib notetype::emptycards
```

---------

Co-authored-by: Fernando Lins <1887601+fernandolins@users.noreply.github.com>
2026-05-26 18:55:43 +03:00
llama
e9830169b7 feat(check-media): case-insensitively dedupe media filenames (#4852)
<!--
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)

- #4433
- #4716 

## Summary / motivation (required)

This PR modifies `Check Media` to dedupe filenames case-insensitively so
as to avoid files that were added on case-sensitive filesystems
potentially being overwritten on case-insensitive ones

## Steps to reproduce (required, use N/A if not applicable)

N/A

## How to test (required)

Given a.jpg (hash 1), A.JPG (hash 2) and a.JPG (hash 1) as existing
media files on a case-sensitive fs, see that `Check Media` renames A.JPG
to a-2.jpg and a.JPG to a.jpg

### 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.

## Scope

- [x] This PR is focused on one change (no unrelated edits).
2026-05-25 14:05:34 +03:00
llama
57e67f8408 fix(check-media): case-sensitivity in unused/missing check (#4861)
<!--
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)

- #4716

## Summary / motivation (required)

Currently, on filesystems where the media folder is treated as
case-insensitive, `[sound:BLAH.mp3]` and `[sound:blah.mp3]` point to the
same file on disk (if any), but one of the two would be considered as a
ref pointing to a missing file by the media checker which assumes
case-sensitivity

## Steps to reproduce (required, use N/A if not applicable)

See linked pr

## How to test (required)

On windows/macos, add `blah.mp3`, rename `blah.mp3` to `Blah.mp3` in the
ref, run `Check Media` and see that `Blah.mp3` isn't considered missing

### 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.

## Scope

- [x] This PR is focused on one change (no unrelated edits).
2026-05-25 12:09:14 +03:00
llama
dfbd7d2399 fix(import): force lowercasing before adding hash suffix as well (#4860)
<!--
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)

- #4851

## Summary / motivation (required)

I made a mistake in #4851 by not lowercasing in the case where a file
with the same name but different contents already exists, leading to
inconsistent behaviour across platforms

## Steps to reproduce (required, use N/A if not applicable)

See linked pr

## How to test (required)

Run ./check on linux and windows and see that the same lowercasing
behaviour occurs on both

### 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.

## Scope

- [x] This PR is focused on one change (no unrelated edits).
2026-05-19 06:58:47 +03:00
llama
3479499569 fix(import): avoid case-folding already existing media filenames (#4851)
<!--
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 #4716 

## Summary / motivation (required)

#4435 made it so that all new added media would have lowercased names,
but this was bugged and led to media refs pointing to inexistent files
on case-insensitive filesystems

The fix proposed is to try adding new files with lowercased names only
if they don't already exist, using the existing file otherwise

## Steps to reproduce (required, use N/A if not applicable)

See linked issue

## How to test (required)

Try copy-pasting a media file from the Edit window of an existing note
to the Add window and see that the filename in the resulting media ref
isn't forced to be lowercased

When pasting a new file, see that the filename is now lowercased

### 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.

## Scope

- [x] This PR is focused on one change (no unrelated edits).
2026-05-18 18:56:54 +03:00
Jeremy Fleischman
1482fb0937 fix: atomic_rename now works with single component relative paths (#4773)
## Linked issue (required)

[[<!-- Fixes #123 / Closes #123 / Refs #123
-->](https://forums.ankiweb.net/t/anki-collection-collection-export-anki-package-crashes-if-given-a-relative-path-with-one-component/69562)](https://forums.ankiweb.net/t/anki-collection-collection-export-anki-package-crashes-if-given-a-relative-path-with-one-component/69562)

## Summary / motivation (required)

See discussion about this on [the

forum](https://forums.ankiweb.net/t/anki-collection-collection-export-anki-package-crashes-if-given-a-relative-path-with-one-component/69562)

## Steps to reproduce (required, use N/A if not applicable)

`demo.py`:

```python
import tempfile
from pathlib import Path

import anki.collection

with tempfile.TemporaryDirectory() as tempdir:
    tempdir = Path(tempdir)
    col = anki.collection.Collection(str(tempdir / "temp.anki2"))
    col.export_anki_package(
        out_path="output.apkg",
        options=anki.collection.ExportAnkiPackageOptions(),
        limit=None,
    )
```

```console
$ python demo.py
Traceback (most recent call last):
  [... some noise elided here ...]
  File "/nix/store/1w7swjzmjp7171yspih1q07a9vgacjzb-dev-env/lib/python3.14/site-packages/anki/_backend_generated.py", line 2037, in export_anki_package
    raw_bytes = self._run_command(37, 4, message.SerializeToString())
  File "/nix/store/1w7swjzmjp7171yspih1q07a9vgacjzb-dev-env/lib/python3.14/site-packages/anki/_backend.py", line 171, in _run_command
    raise backend_exception_to_pylib(err)
anki.errors.BackendIOError: Failed to open '': No such file or directory (os error 2)
```

## How to test (required)

I added a unit test. It passes.

### Checklist (minimum)

- [ ] 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

<!-- 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).

---------

Co-authored-by: Abdo <abdo@abdnh.net>
2026-05-15 16:53:17 +03:00
Abdo
cd2f15b4ee feat: Enable Windows ARM64 support for Briefcase (#4798)
## Linked issue

#4678

## Summary

This enables native Windows ARM64 builds for Briefcase. Depends on #4797

## How to test

- Run `./tools/ninja installer` in a Windows ARM64 machine.
- Check the architecture of the installer under `./out/installer/dist`
by going to Properties > Compatibility and confirming emulation settings
are disabled.
- Install the package and confirm Anki.exe is a native binary.
- Open Anki, go to the [debug
console](https://docs.ankiweb.net/misc.html#debug-console) and run the
following code to check the architecture of the Python build:
```python
import platform

print(platform.machine(), platform.python_compiler())
```
2026-05-08 16:58:40 +03:00
Andrew Sanchez
5a9b54e938 Briefcase Installer (#4629)
migrates Anki Desktop packaging from the legacy
NSIS/uv-based installer to [BeeWare
Briefcase](https://briefcase.readthedocs.io/). This branch integrates
work from many related issues and PRs to deliver cross-platform native
installers (MSI on Windows, .app on macOS, PyInstaller on Linux) with
code signing, notarization, and file association support.

## Integrated PRs

- #4585 — Set up Briefcase
- #4596 — Add Briefcase icons
- #4598 — Handle Briefcase file associations
- #4601 — Add Briefcase app permissions
- #4609 — Customize Briefcase's MSI installer
- #4616 — Set up Briefcase code signing and notarization
- #4618 — Fix Briefcase packaging for x86 Macs
- #4623 — Customize Briefcase's Linux template
- #4627 — List required Debian packages for Briefcase installer
- #4630 — Update Briefcase's Windows template
- #4631 — Rewrite Linux install/uninstall scripts for PyInstaller
- #4638 — Use PyInstaller on Linux
- #4645 — Update installer docs
- #4654 — Disable Briefcase's universal builds for macOS
- #4672 — Deal with existing NSIS installations in MSI installer
- #4676 — Remove duplicate Briefcase icons
- #4677 — Tweak Linux scripts for new installer
- #4709 — Add anki-console.bat to Briefcase's Windows package

## Related Issues

- #4557 — Evaluate BeeWare Briefcase for Anki packaging and distribution
- #4678 — Support native Windows ARM64 builds for Briefcase
- #4688 — Linux installer: migrate to PyInstaller and rewrite install
scripts
- #4689 — Investigate startup performance with Briefcase
- #4690 — Specify required Linux system packages for Briefcase
- #4691 — Investigate Windows ARM64 support with Briefcase
- #4692 — Test on Linux ARM with Briefcase
- #4693 — Separate ARM and Intel macOS releases
- #4694 — Update developer documentation for Briefcase installer
- #4695 — Support upgrade/downgrade with the Briefcase installer
- #4696 — Update user documentation for new installer
- #4702 — Update Briefcase's Windows template with upstream security fix
and OS version check
- #4703 — Follow-up tweaks to Linux install/uninstall scripts

## Related PRs

- #4619 — Enable Windows ARM64 support
- #4632 — Release action

---------

Co-authored-by: Abdo <abdo@abdnh.net>
Co-authored-by: Andrew Sanchez <andrewsanchez@users.noreply.github.com>
Co-authored-by: Fernando Lins <1887601+fernandolins@users.noreply.github.com>
2026-05-05 17:29:18 -04:00
user1823
abc5d10c55 Feat/Prioritize previously attempted cards in intraday learning queue (#4393)
The Problem
The "learn now" add-on can bulk-add many new cards to the learn queue
with due == now. When a user answers some of those and fails them, the
failed cards are scheduled for a few minutes later, but the current
queue-building behavior requires the user to finish the remaining "learn
now" cards (due == now) before the failed cards are shown again. This
increases the chance of repeated failures and reduces learning
efficiency.

The Change
The queue builder now partitions intraday learning cards into two
groups: previously-attempted (reps > 0) and never-attempted (reps == 0).
Within each group cards remain ordered by due time. The builder
concatenates the groups with previously-attempted cards first.

Why this fixes the problem
Failed learning cards (which now have reps > 0 and a later due
timestamp) will be prioritized ahead of never-attempted cards added by
"learn now". This ensures the user sees the failed cards as soon as they
are due, reducing repeated failures.

Why changes to Anki rather than the add-on
As far as I know, there is nothing that the add-on can do to improve
this situation. Secondly, I think that Anki should, at some point,
natively include the "Learn now" feature as it is quite useful when the
user wants to introduce specific cards (for e.g., for an upcoming exam)
without creating a temporary deck for that purpose. "Learn now" is also
a good way to solve one aspect of the Set Due Date misuse problem (see
https://github.com/open-spaced-repetition/fsrs4anki/issues/675#issuecomment-2548839346).
2026-04-20 17:03:39 +03:00
maskady
d051be6290 fix: contains_cloze_replacement now checks nested conditionals (#4581) (#4651)
Fixes #4581

`contains_cloze_replacement()` was doing a shallow check, missing cloze
fields nested inside conditionals. Delegating to
`all_referenced_cloze_field_names()` fixes the inconsistency with
`contains_field_replacement()`
2026-04-14 21:37:49 +03:00
Luc Mcgrady
922953acf4 Feat/Different method for ratio graph in Help Me Decide. (#4452)
Forum link:

-
https://forums.ankiweb.net/t/replace-cmrr-with-workload-vs-dr-graph-more/63234/27?u=a_blokee

@snorpdorp notified me that the logic behind the ratio graph is flawed
as it doesn't properly account for the number of cards that are
memorized to begin with.

I have implemented his (and @1DWalker's) proposed solution

<img width="1267" height="657" alt="image"
src="https://github.com/user-attachments/assets/41eded79-8f76-41dd-b98f-0bf1a968ab9b"
/>

Now the graph is inverted so that the higher the value, the more
efficient it is, in @snorpdorp's own words:

> The previous equation was flawed in that it displayed the total
knowledge at the end of the simulation, not the relative gain in
knowledge from the amount of studying done during the simulation
timeframe. The new (correct) equation is (relative gain in knowledge as
a result of studying during the simulation) / (time spent studying
during simulation) -
https://discord.com/channels/368267295601983490/1443273721878937650/1444447278344437833
2026-04-03 19:59:18 +03:00
Jake Probst
b5f15323c3 load balancer: improve sibling dispersal (#4640)
the load balancer only looks at cards in the same preset when balancing
cards. which means that if a sibling is in a different preset, it
doesn't get avoided. this is not ideal. so heres a fix.

and while I was here I added some extra weighting so it tries to spread
siblings out a bit.
2026-04-02 22:31:37 +03:00
Moamen Abdelsattar
c8f97d3c8a Fix deck-specific load balancer is not used in filtered deck study (#4574)
Originally I reported here >1mo ago
[https://forums.ankiweb.net/t/bug-filtered-decks-are-possibly-not-respecting-easy-days-settings/68257](https://forums.ankiweb.net/t/bug-filtered-decks-are-possibly-not-respecting-easy-days-settings/68257)

Note: I can not build and test Anki. I only assume (by reading code)
that this is the source of the bug. If this does not help, feel free to
reject.
2026-04-02 16:22:28 +03:00
Abdo
23dcc4158c Update AV_TAGS regex (#4580)
The AV_TAGS regex matches an old format of the `anki:tts` tag
(`[anki:tts][lang=en_US]` rather than `[anki:tts lang=en_US]`). It was
originally updated when the feature got introduced in
0942ffbff6
but then was never adjusted to reflect later changes to the syntax.
I removed it from the pattern rather than updating it to match, because
AV_TAGS is only used for matching filenames nowadays (stripping is
handled by a different module).
2026-03-31 17:33:01 +03:00
Daniel Pechersky
9679923046 Speed up update_memory_state by batching FSRS memory state calculation (#4335)
* Speed up update_memory_state by batching FSRS memory state calculation

* Replace [].as_slice() with &[]

* Improve test to test fsrs param clearing behaviour

* Rename test

* Fix bug introduced during resolving merge conflict

* Update rslib/src/scheduler/fsrs/memory_state.rs

Fix comment

Co-authored-by: user1823 <92206575+user1823@users.noreply.github.com>

---------

Co-authored-by: user1823 <92206575+user1823@users.noreply.github.com>
2026-03-04 18:23:40 +03:00
Jarrett Ye
5fa8544f23 Fix/don't reschedule suspended cards (#4453)
* Fix/don't reschedule suspended cards

* format
2026-02-11 16:59:47 +07:00
user1823
db811351e5 Fix/Make descending R a pure R sort (#4400)
* Feat/Add pure R-based review sorting orders

https://forums.ankiweb.net/t/follow-up-make-descending-retrievability-a-pure-r-sort/67082

* cleanup

* fix

* Update test
2026-02-11 16:54:37 +07:00
Luc Mcgrady
c12fe7c507 Feat/Quantiles for FSRS percentage stats (#4425)
* Added: Difficulty zoom

* Fix: quantiles, Extract: PercentageRange component
(I accidentally staged the second commit before comiting the first)

* Add to retrievability graph

* Fix: Typo

* Fix: Half the percentiles

* Remove 50% option

* Fix: Add minimum bin width

* Revert "Remove 50% option"

This reverts commit 372d15578f.

* Remove magic number

* Fix: tickSize not updated

* unfix sqlite.rs

* remove console.log
2026-02-11 16:37:55 +07:00
Jarrett Ye
d64ced8846 Fix/FSRS short-term schedule doesn't work with default params (#4412) 2026-02-11 15:56:39 +07:00
Yuuki
3d813c83c3 Adding Tests for the Card Generation in the Rust side (#4551)
* Adding Tests for the Card Generation in the Rust side

* Fix rustfmt formatting
2026-02-10 17:00:04 +03:00
Yuuki
c41668026f Fix {{#Tags}} conditional not generating cards. Fixes #4522 (#4533) 2026-02-09 21:07:57 +03:00
Brett Schwartz
046063595d Add regression test for #1909 (media check long filenames) (#4476)
* Add regression test for #1909 (long filename rename not reported as unused)

* Add Brett Schwartz to CONTRIBUTORS

* Use MAX_MEDIA_FILENAME_LENGTH constant and 'a'
2026-01-17 16:19:02 +03:00
Abdo
93c44335f6 Update to Rust 1.92 (#4461)
* Update to Rust 1.90

* Fix clippy errors

* Update to 1.92

* Update cargo-deny again

Deno crate was using a CVSS version 4.0, which wasn't supported

---------

Co-authored-by: Damien Elmes <gpg@ankiweb.net>
2026-01-11 18:50:16 +07:00
Luc Mcgrady
62252f7216 Fix/Retrievability SQL (#4424)
* Fix/SQL Retrievability Underflow

* Added: Reminder

* ./check

* Apply code diff changes

Co-authored-by: user1823 <92206575+user1823@users.noreply.github.com>

* Slight cleanup

* fix bug again

* extra label comment

* Update rslib/src/storage/sqlite.rs

Co-authored-by: user1823 <92206575+user1823@users.noreply.github.com>

* Fix: Ignore new cards

* use const

---------

Co-authored-by: user1823 <92206575+user1823@users.noreply.github.com>
2025-12-16 19:51:13 +03:00