From 58deb1402867a455bfee02de6dfbb8c458832272 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 4 Sep 2025 16:18:11 +1000 Subject: [PATCH 01/14] Ensure the newly-spawned terminal doesn't inherit the env var It seems like the open call was leaking it into the newly spawned process. Follow-up fix to 2491eb0316283abe010a0e908b4dab17c5dba37f --- qt/launcher/src/platform/mac.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/qt/launcher/src/platform/mac.rs b/qt/launcher/src/platform/mac.rs index 8662ba9f5..3f5b0ce2e 100644 --- a/qt/launcher/src/platform/mac.rs +++ b/qt/launcher/src/platform/mac.rs @@ -64,6 +64,7 @@ pub fn relaunch_in_terminal() -> Result<()> { Command::new("open") .args(["-na", "Terminal"]) .arg(current_exe) + .env_remove("ANKI_LAUNCHER_WANT_TERMINAL") .ensure_spawn()?; std::process::exit(0); } From 3b0297d14d3df2844b45504188452c1d86ec7b97 Mon Sep 17 00:00:00 2001 From: Lukas Sommer Date: Sat, 6 Sep 2025 11:15:42 +0000 Subject: [PATCH 02/14] Update deck-config.ftl (#4319) --- ftl/core/deck-config.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftl/core/deck-config.ftl b/ftl/core/deck-config.ftl index e365d9553..1e193dc04 100644 --- a/ftl/core/deck-config.ftl +++ b/ftl/core/deck-config.ftl @@ -498,7 +498,7 @@ deck-config-desired-retention-below-optimal = Your desired retention is below op # cards that can be recalled or retrieved on a specific date. deck-config-fsrs-simulator-experimental = FSRS Simulator (Experimental) deck-config-fsrs-simulate-desired-retention-experimental = FSRS Desired Retention Simulator (Experimental) -deck-config-fsrs-simulate-save-preset = After optimizing, please save your config before running the simulator. +deck-config-fsrs-simulate-save-preset = After optimizing, please save your deck preset before running the simulator. deck-config-fsrs-desired-retention-help-me-decide-experimental = Help Me Decide (Experimental) deck-config-additional-new-cards-to-simulate = Additional new cards to simulate deck-config-simulate = Simulate From cf12c201d8a68575dd1695912ab5392c0935a566 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 6 Sep 2025 21:16:13 +1000 Subject: [PATCH 03/14] Update translations --- ftl/core-repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftl/core-repo b/ftl/core-repo index d255301b5..6552c95a8 160000 --- a/ftl/core-repo +++ b/ftl/core-repo @@ -1 +1 @@ -Subproject commit d255301b5a815ebac73c380b48507440d2f5dcce +Subproject commit 6552c95a81d162422b2a50126547cc7f1b50c2fd From 539054c34dccf8b89ca7ea9c9c40ecaf172de759 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 6 Sep 2025 21:17:08 +1000 Subject: [PATCH 04/14] Bump version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 280125b32..834db75c6 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -25.09rc1 +25.09 From 4506ad0c97dc543b2142bf9ee8f9717e92eab1fd Mon Sep 17 00:00:00 2001 From: jcznk <60730312+jcznk@users.noreply.github.com> Date: Sun, 14 Sep 2025 19:44:16 +0200 Subject: [PATCH 05/14] Prevent clipping for QPushButton:default (#4323) --- qt/aqt/stylesheets.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qt/aqt/stylesheets.py b/qt/aqt/stylesheets.py index 6b4eff1f5..6817b7063 100644 --- a/qt/aqt/stylesheets.py +++ b/qt/aqt/stylesheets.py @@ -180,7 +180,7 @@ class CustomStyles: QPushButton {{ margin: 1px; }} - QPushButton:focus {{ + QPushButton:focus, QPushButton:default:hover {{ border: 2px solid {tm.var(colors.BORDER_FOCUS)}; outline: none; margin: 0px; @@ -199,9 +199,6 @@ class CustomStyles: ) }; }} - QPushButton:default:hover {{ - border-width: 2px; - }} QPushButton:pressed, QPushButton:checked, QSpinBox::up-button:pressed, From 90ed4cc115c6030ead910d408150869d7e265c26 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 17 Sep 2025 09:31:06 +1000 Subject: [PATCH 06/14] Disable NPM package scripts, and assert lockfile unchanged With all the recent supply chain attacks, this seems prudent. There are three in our current package list. esbuild's is just a performance optimization (https://github.com/evanw/esbuild/issues/4085), and dprint's gets done when we invoke .bin/dprint anyway. svelte-preprocess simply prints something to the screen. --- .yarnrc.yml | 1 + build/runner/src/yarn.rs | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.yarnrc.yml b/.yarnrc.yml index 3186f3f07..94f5c254e 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1 +1,2 @@ nodeLinker: node-modules +enableScripts: false diff --git a/build/runner/src/yarn.rs b/build/runner/src/yarn.rs index 9e1bd5b58..7724ed04a 100644 --- a/build/runner/src/yarn.rs +++ b/build/runner/src/yarn.rs @@ -28,7 +28,11 @@ pub fn setup_yarn(args: YarnArgs) { .arg("--ignore-scripts"), ); } else { - run_command(Command::new(&args.yarn_bin).arg("install")); + run_command( + Command::new(&args.yarn_bin) + .arg("install") + .arg("--immutable"), + ); } std::fs::write(args.stamp, b"").unwrap(); From 61094d387a3d545b72d96f007588b02f53c9f2a5 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 17 Sep 2025 09:31:47 +1000 Subject: [PATCH 07/14] Update translations --- ftl/core-repo | 2 +- ftl/qt-repo | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ftl/core-repo b/ftl/core-repo index 6552c95a8..60bd4d483 160000 --- a/ftl/core-repo +++ b/ftl/core-repo @@ -1 +1 @@ -Subproject commit 6552c95a81d162422b2a50126547cc7f1b50c2fd +Subproject commit 60bd4d4834b57b85915f52eb978935ff6b8c1425 diff --git a/ftl/qt-repo b/ftl/qt-repo index dad4e2736..fd5f98478 160000 --- a/ftl/qt-repo +++ b/ftl/qt-repo @@ -1 +1 @@ -Subproject commit dad4e2736a2b53dcdb52d79b5703dd464c05d666 +Subproject commit fd5f984785ad07a0d3dbd893ee3d7e3671eaebd6 From b97fb45e06cd9dfea5d4003b2318af2249f2b789 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:08:11 +1000 Subject: [PATCH 08/14] Bump vite from 6.3.5 to 6.3.6 (#4328) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.5 to 6.3.6. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v6.3.6/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v6.3.6/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 6.3.6 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 84bd46e31..761f20972 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6939,8 +6939,8 @@ __metadata: linkType: hard "vite@npm:6": - version: 6.3.5 - resolution: "vite@npm:6.3.5" + version: 6.3.6 + resolution: "vite@npm:6.3.6" dependencies: esbuild: "npm:^0.25.0" fdir: "npm:^6.4.4" @@ -6989,7 +6989,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/df70201659085133abffc6b88dcdb8a57ef35f742a01311fc56a4cfcda6a404202860729cc65a2c401a724f6e25f9ab40ce4339ed4946f550541531ced6fe41c + checksum: 10c0/add701f1e72596c002275782e38d0389ab400c1be330c93a3009804d62db68097a936ca1c53c3301df3aaacfe5e328eab547060f31ef9c49a277ae50df6ad4fb languageName: node linkType: hard From 7e8a1076c167b72ab2d2822878f94b9f44598fe8 Mon Sep 17 00:00:00 2001 From: Emil Hamrin <68200971+e-hamrin@users.noreply.github.com> Date: Wed, 17 Sep 2025 06:02:09 +0200 Subject: [PATCH 09/14] Updated Dockerfile to use Ninja build system (#4321) * Updated Dockerfile to support ninja build * Install python using uv * Bumped python version * Add disclaimer (dae) --- CONTRIBUTORS | 1 + docs/docker/Dockerfile | 83 ++++++++++++++++++++++++++++++++---------- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index b03108e16..7064c6885 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -49,6 +49,7 @@ Sander Santema Thomas Brownback Andrew Gaul kenden +Emil Hamrin Nickolay Yudin neitrinoweb Andreas Reis diff --git a/docs/docker/Dockerfile b/docs/docker/Dockerfile index 6682f70f6..381d27d1c 100644 --- a/docs/docker/Dockerfile +++ b/docs/docker/Dockerfile @@ -1,35 +1,78 @@ -# This Dockerfile uses three stages. -# 1. Compile anki (and dependencies) and build python wheels. -# 2. Create a virtual environment containing anki and its dependencies. -# 3. Create a final image that only includes anki's virtual environment and required -# system packages. +# This is a user-contributed Dockerfile. No official support is available. -ARG PYTHON_VERSION="3.9" ARG DEBIAN_FRONTEND="noninteractive" -# Build anki. -FROM python:$PYTHON_VERSION AS build -RUN curl -fsSL https://github.com/bazelbuild/bazelisk/releases/download/v1.7.4/bazelisk-linux-amd64 \ - > /usr/local/bin/bazel \ - && chmod +x /usr/local/bin/bazel \ - # Bazel expects /usr/bin/python - && ln -s /usr/local/bin/python /usr/bin/python +FROM ubuntu:24.04 AS build WORKDIR /opt/anki -COPY . . -# Build python wheels. +ENV PYTHON_VERSION="3.13" + + +# System deps +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl \ + git \ + build-essential \ + pkg-config \ + libssl-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + libffi-dev \ + zlib1g-dev \ + liblzma-dev \ + ca-certificates \ + ninja-build \ + rsync \ + libglib2.0-0 \ + libgl1 \ + libx11-6 \ + libxext6 \ + libxrender1 \ + libxkbcommon0 \ + libxkbcommon-x11-0 \ + libxcb1 \ + libxcb-render0 \ + libxcb-shm0 \ + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-randr0 \ + libxcb-shape0 \ + libxcb-xfixes0 \ + libxcb-xinerama0 \ + libxcb-xinput0 \ + libsm6 \ + libice6 \ + && rm -rf /var/lib/apt/lists/* + +# install rust with rustup +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" + +# Install uv and Python 3.13 with uv +RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ + && ln -s /root/.local/bin/uv /usr/local/bin/uv +ENV PATH="/root/.local/bin:${PATH}" + +RUN uv python install ${PYTHON_VERSION} --default + +COPY . . + RUN ./tools/build + # Install pre-compiled Anki. -FROM python:${PYTHON_VERSION}-slim as installer +FROM python:3.13-slim AS installer WORKDIR /opt/anki/ -COPY --from=build /opt/anki/wheels/ wheels/ +COPY --from=build /opt/anki/out/wheels/ wheels/ # Use virtual environment. RUN python -m venv venv \ && ./venv/bin/python -m pip install --no-cache-dir setuptools wheel \ && ./venv/bin/python -m pip install --no-cache-dir /opt/anki/wheels/*.whl + # We use another build stage here so we don't include the wheels in the final image. -FROM python:${PYTHON_VERSION}-slim as final +FROM python:3.13-slim AS final COPY --from=installer /opt/anki/venv /opt/anki/venv ENV PATH=/opt/anki/venv/bin:$PATH # Install run-time dependencies. @@ -59,9 +102,9 @@ RUN apt-get update \ libxrender1 \ libxtst6 \ && rm -rf /var/lib/apt/lists/* + # Add non-root user. RUN useradd --create-home anki USER anki WORKDIR /work -ENTRYPOINT ["/opt/anki/venv/bin/anki"] -LABEL maintainer="Jakub Kaczmarzyk " +ENTRYPOINT ["/opt/anki/venv/bin/anki"] \ No newline at end of file From 9e415869b883589c67021dabaf65ee688139a9d7 Mon Sep 17 00:00:00 2001 From: Luc Mcgrady Date: Wed, 17 Sep 2025 05:04:27 +0100 Subject: [PATCH 10/14] Fix/Add lower review limit to health check. (#4334) --- rslib/src/scheduler/fsrs/params.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rslib/src/scheduler/fsrs/params.rs b/rslib/src/scheduler/fsrs/params.rs index 726870fe1..d7bb56f5b 100644 --- a/rslib/src/scheduler/fsrs/params.rs +++ b/rslib/src/scheduler/fsrs/params.rs @@ -174,7 +174,7 @@ impl Collection { } } - let health_check_passed = if health_check { + let health_check_passed = if health_check && input.train_set.len() > 300 { let fsrs = FSRS::new(None)?; fsrs.evaluate_with_time_series_splits(input, |_| true) .ok() From c2957746f4c4eb14598da3977f1193d45588ecda Mon Sep 17 00:00:00 2001 From: snowtimeglass Date: Wed, 17 Sep 2025 14:13:59 +0900 Subject: [PATCH 11/14] Make timebox message translatable with flexible variable order (#4338) * Make timebox message translatable with flexible variable order Currently, the timebox dialog message is built from two separate strings, each containing one variable: "{ $count } cards studied in" + "{ $count } minutes." As a result, translators cannot freely reorder the variables in their translations. This change introduces a single string with both variables, allowing translators to adjust the order for more natural expressions in their languages. * Preserve old string for now * Ensure message doesn't display over two lines --------- Co-authored-by: Damien Elmes --- ftl/core/studying.ftl | 16 ++++++++++++++-- qt/aqt/reviewer.py | 13 +++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/ftl/core/studying.ftl b/ftl/core/studying.ftl index ed3f8eb30..a317a68ba 100644 --- a/ftl/core/studying.ftl +++ b/ftl/core/studying.ftl @@ -46,6 +46,20 @@ studying-type-answer-unknown-field = Type answer: unknown field { $val } studying-unbury = Unbury studying-what-would-you-like-to-unbury = What would you like to unbury? studying-you-havent-recorded-your-voice-yet = You haven't recorded your voice yet. +studying-card-studied-in-minute = + { $cards -> + [one] { $cards } card + *[other] { $cards } cards + } studied in + { $minutes -> + [one] { $minutes } minute. + *[other] { $minutes } minutes. + } +studying-question-time-elapsed = Question time elapsed +studying-answer-time-elapsed = Answer time elapsed + +## OBSOLETE; you do not need to translate this + studying-card-studied-in = { $count -> [one] { $count } card studied in @@ -56,5 +70,3 @@ studying-minute = [one] { $count } minute. *[other] { $count } minutes. } -studying-question-time-elapsed = Question time elapsed -studying-answer-time-elapsed = Answer time elapsed diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py index a8839c598..6d68f9e3a 100644 --- a/qt/aqt/reviewer.py +++ b/qt/aqt/reviewer.py @@ -17,6 +17,7 @@ import aqt.browser import aqt.operations from anki.cards import Card, CardId from anki.collection import Config, OpChanges, OpChangesWithCount +from anki.lang import with_collapsed_whitespace from anki.scheduler.base import ScheduleCardsAsNew from anki.scheduler.v3 import ( CardAnswer, @@ -966,11 +967,15 @@ timerStopped = false; elapsed = self.mw.col.timeboxReached() if elapsed: assert not isinstance(elapsed, bool) - part1 = tr.studying_card_studied_in(count=elapsed[1]) - mins = int(round(elapsed[0] / 60)) - part2 = tr.studying_minute(count=mins) + cards_val = elapsed[1] + minutes_val = int(round(elapsed[0] / 60)) + message = with_collapsed_whitespace( + tr.studying_card_studied_in_minute( + cards=cards_val, minutes=str(minutes_val) + ) + ) fin = tr.studying_finish() - diag = askUserDialog(f"{part1} {part2}", [tr.studying_continue(), fin]) + diag = askUserDialog(message, [tr.studying_continue(), fin]) diag.setIcon(QMessageBox.Icon.Information) if diag.run() == fin: self.mw.moveToState("deckBrowser") From ec6f09958a12d7f29e5f2c5b37589564e8ea4ced Mon Sep 17 00:00:00 2001 From: jcznk <60730312+jcznk@users.noreply.github.com> Date: Wed, 17 Sep 2025 07:30:22 +0200 Subject: [PATCH 12/14] (UI polish) Improved margins in Card Browser's "Previewer" (#4337) * Improved margins in Card Browser's "Preview" pane * Alternate approach that looks good on Mac too --------- Co-authored-by: Damien Elmes Co-authored-by: Damien Elmes --- qt/aqt/browser/previewer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qt/aqt/browser/previewer.py b/qt/aqt/browser/previewer.py index 4c9a97fb8..61096b5b3 100644 --- a/qt/aqt/browser/previewer.py +++ b/qt/aqt/browser/previewer.py @@ -13,7 +13,7 @@ import aqt.browser from anki.cards import Card from anki.collection import Config from anki.tags import MARKED_TAG -from aqt import AnkiQt, gui_hooks +from aqt import AnkiQt, gui_hooks, is_mac from aqt.qt import ( QCheckBox, QDialog, @@ -81,10 +81,15 @@ class Previewer(QDialog): qconnect(self.finished, self._on_finished) self.silentlyClose = True self.vbox = QVBoxLayout() + spacing = 6 self.vbox.setContentsMargins(0, 0, 0, 0) + self.vbox.setSpacing(spacing) self._web: AnkiWebView | None = AnkiWebView(kind=AnkiWebViewKind.PREVIEWER) self.vbox.addWidget(self._web) self.bbox = QDialogButtonBox() + self.bbox.setContentsMargins( + spacing, spacing if is_mac else 0, spacing, spacing + ) self.bbox.setLayoutDirection(Qt.LayoutDirection.LeftToRight) gui_hooks.card_review_webview_did_init(self._web, AnkiWebViewKind.PREVIEWER) From 29072654db1fa477be7bf958794865e1dec1b0ad Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 17 Sep 2025 15:50:02 +1000 Subject: [PATCH 13/14] Update translations --- ftl/core-repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftl/core-repo b/ftl/core-repo index 60bd4d483..480ef0da7 160000 --- a/ftl/core-repo +++ b/ftl/core-repo @@ -1 +1 @@ -Subproject commit 60bd4d4834b57b85915f52eb978935ff6b8c1425 +Subproject commit 480ef0da728c7ea3485c58529ae7ee02be3e5dba From 6854d13b8842ea1166c58c33a30501b7198a4774 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 17 Sep 2025 15:50:16 +1000 Subject: [PATCH 14/14] Bump version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 834db75c6..739af04dc 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -25.09 +25.09.1