diff --git a/.idea.dist/repo.iml b/.idea.dist/repo.iml
new file mode 100644
index 000000000..a9ec5ee1a
--- /dev/null
+++ b/.idea.dist/repo.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.vscode.dist/tasks.json b/.vscode.dist/tasks.json
index 72eab9604..b89704d2e 100644
--- a/.vscode.dist/tasks.json
+++ b/.vscode.dist/tasks.json
@@ -12,8 +12,7 @@
"command": "tools/ninja.bat",
"args": [
"pylib",
- "qt",
- "extract:win_amd64_audio"
+ "qt"
]
}
}
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index a6334b0d9..01573b419 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -247,6 +247,12 @@ Hanna Nilsén
Elias Johansson Lara
Toby Penner
Danilo Spillebeen
+Matbe766
+Amanda Sternberg
+arold0
+nav1s
+Ranjit Odedra
+Eltaurus
********************
diff --git a/docs/editing.md b/docs/editing.md
index ba3fd6fce..42a92c5a8 100644
--- a/docs/editing.md
+++ b/docs/editing.md
@@ -46,10 +46,14 @@ see and install a number of recommended extensions.
## PyCharm/IntelliJ
-If you decide to use PyCharm instead of VS Code, there are somethings to be
-aware of.
+### Setting up Python environment
-### Pylib References
+To make PyCharm recognize `anki` and `aqt` imports, you need to add source paths to _Settings > Project Structure_.
+You can copy the provided .idea.dist directory to set up the paths automatically:
-You'll need to use File>Project Structure to tell IntelliJ that pylib/ is a
-sources root, so it knows references to 'anki' in aqt are valid.
+```
+mkdir .idea && cd .idea
+ln -sf ../.idea.dist/* .
+```
+
+You also need to add a new Python interpreter under _Settings > Python > Interpreter_ pointing to the Python executable under `out/pyenv` (available after building Anki).
diff --git a/ftl/core/deck-config.ftl b/ftl/core/deck-config.ftl
index 1e193dc04..4930dbe0e 100644
--- a/ftl/core/deck-config.ftl
+++ b/ftl/core/deck-config.ftl
@@ -382,7 +382,7 @@ deck-config-which-deck = Which deck would you like to display options for?
## Messages related to the FSRS scheduler
deck-config-updating-cards = Updating cards: { $current_cards_count }/{ $total_cards_count }...
-deck-config-invalid-parameters = The provided FSRS parameters are invalid. Leave them blank to use the default parameters.
+deck-config-invalid-parameters = The provided FSRS parameters are invalid. Leave them blank to use the default values.
deck-config-not-enough-history = Insufficient review history to perform this operation.
deck-config-must-have-400-reviews =
{ $count ->
diff --git a/ftl/core/launcher.ftl b/ftl/core/launcher.ftl
index 13d419a0c..ee3aa6320 100644
--- a/ftl/core/launcher.ftl
+++ b/ftl/core/launcher.ftl
@@ -1,9 +1,10 @@
launcher-title = Anki Launcher
+launcher-press-enter-to-install = Press the Enter/Return key on your keyboard to install or update Anki.
launcher-press-enter-to-start = Press enter to start Anki.
launcher-anki-will-start-shortly = Anki will start shortly.
launcher-you-can-close-this-window = You can close this window.
launcher-updating-anki = Updating Anki...
-launcher-latest-anki = Latest Anki (just press Enter)
+launcher-latest-anki = Install Latest Anki (default)
launcher-choose-a-version = Choose a version
launcher-sync-project-changes = Sync project changes
launcher-keep-existing-version = Keep existing version ({ $current })
@@ -13,7 +14,7 @@ launcher-on = on
launcher-off = off
launcher-cache-downloads = Cache downloads: { $state }
launcher-download-mirror = Download mirror: { $state }
-launcher-uninstall = Uninstall
+launcher-uninstall = Uninstall Anki
launcher-invalid-input = Invalid input. Please try again.
launcher-latest-releases = Latest releases: { $releases }
launcher-enter-the-version-you-want = Enter the version you want to install:
diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py
index a27d86234..01d7423d8 100644
--- a/qt/aqt/addcards.py
+++ b/qt/aqt/addcards.py
@@ -289,6 +289,10 @@ class AddCards(QMainWindow):
def _add_current_note(self) -> None:
note = self.editor.note
+ # Prevent adding a note that has already been added (e.g., from double-clicking)
+ if note.id != 0:
+ return
+
if not self._note_can_be_added(note):
return
diff --git a/qt/aqt/browser/sidebar/item.py b/qt/aqt/browser/sidebar/item.py
index ce5ccb62f..b51910d4b 100644
--- a/qt/aqt/browser/sidebar/item.py
+++ b/qt/aqt/browser/sidebar/item.py
@@ -80,7 +80,7 @@ class SidebarItem:
self.search_node = search_node
self.on_expanded = on_expanded
self.children: list[SidebarItem] = []
- self.tooltip: str | None = None
+ self.tooltip: str = name
self._parent_item: SidebarItem | None = None
self._expanded = expanded
self._row_in_parent: int | None = None
diff --git a/qt/aqt/forms/filtered_deck.ui b/qt/aqt/forms/filtered_deck.ui
index 0a90c40e5..a64a3968a 100644
--- a/qt/aqt/forms/filtered_deck.ui
+++ b/qt/aqt/forms/filtered_deck.ui
@@ -85,11 +85,11 @@
-
-
-
- 60
- 16777215
-
+
+
+ 0
+ 0
+
1
@@ -168,11 +168,11 @@
-
-
-
- 60
- 16777215
-
+
+
+ 0
+ 0
+
1
diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py
index afce6d489..939dd8c2c 100644
--- a/qt/aqt/preferences.py
+++ b/qt/aqt/preferences.py
@@ -260,6 +260,7 @@ class Preferences(QDialog):
self.update_login_status()
self.confirm_sync_after_login()
+ self.update_network()
sync_login(self.mw, on_success)
def sync_logout(self) -> None:
diff --git a/qt/aqt/sync.py b/qt/aqt/sync.py
index 9b29ada20..75bdeca89 100644
--- a/qt/aqt/sync.py
+++ b/qt/aqt/sync.py
@@ -209,11 +209,20 @@ def on_full_sync_timer(mw: aqt.main.AnkiQt, label: str) -> None:
return
sync_progress = progress.full_sync
+ # If we've reached total, show the "checking" label
if sync_progress.transferred == sync_progress.total:
label = tr.sync_checking()
+
+ total = sync_progress.total
+ transferred = sync_progress.transferred
+
+ # Scale both to kilobytes with floor division
+ max_for_bar = total // 1024
+ value_for_bar = transferred // 1024
+
mw.progress.update(
- value=sync_progress.transferred,
- max=sync_progress.total,
+ value=value_for_bar,
+ max=max_for_bar,
process=False,
label=label,
)
diff --git a/qt/launcher/build.rs b/qt/launcher/build.rs
index 3ba75b0e1..bc30f8dff 100644
--- a/qt/launcher/build.rs
+++ b/qt/launcher/build.rs
@@ -7,4 +7,7 @@ fn main() {
.manifest_required()
.unwrap();
}
+ println!("cargo:rerun-if-changed=../../out/buildhash");
+ let buildhash = std::fs::read_to_string("../../out/buildhash").unwrap_or_default();
+ println!("cargo:rustc-env=BUILDHASH={buildhash}");
}
diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs
index cdfc54d8c..dab9435ea 100644
--- a/qt/launcher/src/main.rs
+++ b/qt/launcher/src/main.rs
@@ -152,7 +152,9 @@ fn run() -> Result<()> {
let sync_time = file_timestamp_secs(&state.sync_complete_marker);
state.pyproject_modified_by_user = pyproject_time > sync_time;
let pyproject_has_changed = state.pyproject_modified_by_user;
- if !launcher_requested && !pyproject_has_changed {
+ let different_launcher = diff_launcher_was_installed(&state)?;
+
+ if !launcher_requested && !pyproject_has_changed && !different_launcher {
// If no launcher request and venv is already up to date, launch Anki normally
let args: Vec = std::env::args().skip(1).collect();
let cmd = build_python_command(&state, &args)?;
@@ -173,6 +175,8 @@ fn run() -> Result<()> {
ensure_os_supported()?;
+ println!("{}\n", state.tr.launcher_press_enter_to_install());
+
check_versions(&mut state);
main_menu_loop(&state)?;
@@ -323,7 +327,6 @@ fn handle_version_install_or_update(state: &State, choice: MainMenuChoice) -> Re
}
command
- .env("UV_CACHE_DIR", &state.uv_cache_dir)
.env("UV_PYTHON_INSTALL_DIR", &state.uv_python_install_dir)
.env(
"UV_HTTP_TIMEOUT",
@@ -342,10 +345,6 @@ fn handle_version_install_or_update(state: &State, choice: MainMenuChoice) -> Re
}
}
- if state.no_cache_marker.exists() {
- command.env("UV_NO_CACHE", "1");
- }
-
match command.ensure_success() {
Ok(_) => {
// Sync succeeded
@@ -601,18 +600,27 @@ fn get_version_kind(state: &State) -> Result