diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 1f6c7e308..b03108e16 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -241,6 +241,7 @@ Siyuan Mattuwu Yan Lee Doughty <32392044+leedoughty@users.noreply.github.com> memchr Max Romanowski +Aldlss ******************** diff --git a/ftl/core/adding.ftl b/ftl/core/adding.ftl index e94333008..a090576aa 100644 --- a/ftl/core/adding.ftl +++ b/ftl/core/adding.ftl @@ -1,5 +1,10 @@ adding-add-shortcut-ctrlandenter = Add (shortcut: ctrl+enter) adding-added = Added +adding-added-cards = + Added { $count -> + [one] { $count } card + *[other] { $count } cards + } adding-discard-current-input = Discard current input? adding-keep-editing = Keep Editing adding-edit = Edit "{ $val }" diff --git a/proto/anki/notes.proto b/proto/anki/notes.proto index 39bbcf1e2..f147a599d 100644 --- a/proto/anki/notes.proto +++ b/proto/anki/notes.proto @@ -59,7 +59,7 @@ message AddNoteRequest { } message AddNoteResponse { - collection.OpChanges changes = 1; + collection.OpChangesWithCount changes = 1; int64 note_id = 2; } diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index c64ffdb8b..60360470c 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -528,7 +528,7 @@ class Collection(DeprecatedNamesMixin): def new_note(self, notetype: NotetypeDict) -> Note: return Note(self, notetype) - def add_note(self, note: Note, deck_id: DeckId) -> OpChanges: + def add_note(self, note: Note, deck_id: DeckId) -> OpChangesWithCount: hooks.note_will_be_added(self, note, deck_id) out = self._backend.add_note(note=note._to_backend_note(), deck_id=deck_id) note.id = NoteId(out.note_id) diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index 86e8a25b1..68f5c4259 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -8,7 +8,7 @@ from collections.abc import Callable import aqt.editor import aqt.forms from anki._legacy import deprecated -from anki.collection import OpChanges, SearchNode +from anki.collection import OpChanges, OpChangesWithCount, SearchNode from anki.decks import DeckId from anki.models import NotetypeId from anki.notes import Note, NoteFieldsCheckResult, NoteId @@ -294,13 +294,13 @@ class AddCards(QMainWindow): target_deck_id = self.deck_chooser.selected_deck_id - def on_success(changes: OpChanges) -> None: + def on_success(changes: OpChangesWithCount) -> None: # only used for detecting changed sticky fields on close self._last_added_note = note self.addHistory(note) - tooltip(tr.adding_added(), period=500) + tooltip(tr.adding_added_cards(count=changes.count), period=500) av_player.stop_and_clear_queue() self._load_new_note(sticky_fields_from=note) gui_hooks.add_cards_did_add_note(note) diff --git a/qt/aqt/operations/note.py b/qt/aqt/operations/note.py index 4a27c1e21..e36822553 100644 --- a/qt/aqt/operations/note.py +++ b/qt/aqt/operations/note.py @@ -18,7 +18,7 @@ def add_note( parent: QWidget, note: Note, target_deck_id: DeckId, -) -> CollectionOp[OpChanges]: +) -> CollectionOp[OpChangesWithCount]: return CollectionOp(parent, lambda col: col.add_note(note, target_deck_id)) diff --git a/qt/aqt/theme.py b/qt/aqt/theme.py index 675eb9345..1e7b1f568 100644 --- a/qt/aqt/theme.py +++ b/qt/aqt/theme.py @@ -115,7 +115,7 @@ class ThemeManager: # Workaround for Qt bug. First attempt was percent-escaping the chars, # but Qt can't handle that. # https://forum.qt.io/topic/55274/solved-qss-with-special-characters/11 - path = re.sub(r"([\u00A1-\u00FF])", r"\\\1", path) + path = re.sub(r"(['\u00A1-\u00FF])", r"\\\1", path) return path def icon_from_resources(self, path: str | ColoredIcon) -> QIcon: diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs index 297df5b8b..ccc4022b7 100644 --- a/qt/launcher/src/main.rs +++ b/qt/launcher/src/main.rs @@ -309,6 +309,13 @@ fn handle_version_install_or_update(state: &State, choice: MainMenuChoice) -> Re command.env("UV_NO_CACHE", "1"); } + // Add mirror environment variable if enabled + if let Some((python_mirror, pypi_mirror)) = get_mirror_urls(state)? { + command + .env("UV_PYTHON_INSTALL_MIRROR", &python_mirror) + .env("UV_DEFAULT_INDEX", &pypi_mirror); + } + match command.ensure_success() { Ok(_) => { // Sync succeeded @@ -673,6 +680,12 @@ fn fetch_versions(state: &State) -> Result> { cmd.arg(&versions_script); + // Add mirror environment variable if enabled + if let Some((python_mirror, pypi_mirror)) = get_mirror_urls(state)? { + cmd.env("UV_PYTHON_INSTALL_MIRROR", &python_mirror) + .env("UV_DEFAULT_INDEX", &pypi_mirror); + } + let output = match cmd.utf8_output() { Ok(output) => output, Err(e) => { @@ -725,15 +738,7 @@ fn apply_version_kind(version_kind: &VersionKind, state: &State) -> Result<()> { &format!("anki-release=={version}\",\n \"anki=={version}\",\n \"aqt=={version}"), ), }; - - // Add mirror configuration if enabled - let final_content = if let Some((python_mirror, pypi_mirror)) = get_mirror_urls(state)? { - format!("{updated_content}\n\n[[tool.uv.index]]\nname = \"mirror\"\nurl = \"{pypi_mirror}\"\ndefault = true\n\n[tool.uv]\npython-install-mirror = \"{python_mirror}\"\n") - } else { - updated_content - }; - - write_file(&state.user_pyproject_path, &final_content)?; + write_file(&state.user_pyproject_path, &updated_content)?; // Update .python-version based on version kind match version_kind { diff --git a/rslib/src/decks/schema11.rs b/rslib/src/decks/schema11.rs index 5cd4094f0..3d4e30b96 100644 --- a/rslib/src/decks/schema11.rs +++ b/rslib/src/decks/schema11.rs @@ -135,6 +135,8 @@ pub struct NormalDeckSchema11 { review_limit_today: Option, #[serde(default, deserialize_with = "default_on_invalid")] new_limit_today: Option, + #[serde(default, deserialize_with = "default_on_invalid")] + desired_retention: Option, } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] @@ -249,6 +251,7 @@ impl Default for NormalDeckSchema11 { new_limit: None, review_limit_today: None, new_limit_today: None, + desired_retention: None, } } } @@ -325,7 +328,7 @@ impl From for NormalDeck { new_limit: deck.new_limit, review_limit_today: deck.review_limit_today, new_limit_today: deck.new_limit_today, - desired_retention: None, + desired_retention: deck.desired_retention.map(|v| v as f32 / 100.0), } } } @@ -367,6 +370,7 @@ impl From for DeckSchema11 { new_limit: norm.new_limit, review_limit_today: norm.review_limit_today, new_limit_today: norm.new_limit_today, + desired_retention: norm.desired_retention.map(|v| (v * 100.0) as u32), common: deck.into(), }), DeckKind::Filtered(ref filt) => DeckSchema11::Filtered(FilteredDeckSchema11 { @@ -431,7 +435,8 @@ static RESERVED_DECK_KEYS: Set<&'static str> = phf_set! { "browserCollapsed", "extendRev", "id", - "collapsed" + "collapsed", + "desiredRetention", }; impl From<&Deck> for DeckTodaySchema11 { diff --git a/rslib/src/notes/mod.rs b/rslib/src/notes/mod.rs index 932022e99..2b5ea2921 100644 --- a/rslib/src/notes/mod.rs +++ b/rslib/src/notes/mod.rs @@ -87,7 +87,7 @@ impl TryFrom for AddNoteRequest { } impl Collection { - pub fn add_note(&mut self, note: &mut Note, did: DeckId) -> Result> { + pub fn add_note(&mut self, note: &mut Note, did: DeckId) -> Result> { self.transact(Op::AddNote, |col| col.add_note_inner(note, did)) } @@ -372,7 +372,7 @@ impl Collection { Ok(()) } - pub(crate) fn add_note_inner(&mut self, note: &mut Note, did: DeckId) -> Result<()> { + pub(crate) fn add_note_inner(&mut self, note: &mut Note, did: DeckId) -> Result { let nt = self .get_notetype(note.notetype_id)? .or_invalid("missing note type")?; @@ -383,10 +383,11 @@ impl Collection { note.prepare_for_update(ctx.notetype, normalize_text)?; note.set_modified(ctx.usn); self.add_note_only_undoable(note)?; - self.generate_cards_for_new_note(&ctx, note, did)?; + let count = self.generate_cards_for_new_note(&ctx, note, did)?; self.set_last_deck_for_notetype(note.notetype_id, did)?; self.set_last_notetype_for_deck(did, note.notetype_id)?; - self.set_current_notetype_id(note.notetype_id) + self.set_current_notetype_id(note.notetype_id)?; + Ok(count) } pub fn update_note(&mut self, note: &mut Note) -> Result> { diff --git a/rslib/src/notetype/cardgen.rs b/rslib/src/notetype/cardgen.rs index 8e03d8ee4..b2a100054 100644 --- a/rslib/src/notetype/cardgen.rs +++ b/rslib/src/notetype/cardgen.rs @@ -215,7 +215,7 @@ impl Collection { ctx: &CardGenContext>, note: &Note, target_deck_id: DeckId, - ) -> Result<()> { + ) -> Result { self.generate_cards_for_note( ctx, note, @@ -231,7 +231,8 @@ impl Collection { note: &Note, ) -> Result<()> { let existing = self.storage.existing_cards_for_note(note.id)?; - self.generate_cards_for_note(ctx, note, &existing, ctx.last_deck, &mut Default::default()) + self.generate_cards_for_note(ctx, note, &existing, ctx.last_deck, &mut Default::default())?; + Ok(()) } fn generate_cards_for_note( @@ -241,12 +242,13 @@ impl Collection { existing: &[AlreadyGeneratedCardInfo], target_deck_id: Option, cache: &mut CardGenCache, - ) -> Result<()> { + ) -> Result { let cards = ctx.new_cards_required(note, existing, true); if cards.is_empty() { - return Ok(()); + return Ok(0); } - self.add_generated_cards(note.id, &cards, target_deck_id, cache) + self.add_generated_cards(note.id, &cards, target_deck_id, cache)?; + Ok(cards.len()) } pub(crate) fn generate_cards_for_notetype(