Merge branch 'main' into fix-cargo-build-implicit-deps

This commit is contained in:
Abdo 2025-12-16 19:36:19 +03:00 committed by GitHub
commit 962468e39e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 60 additions and 29 deletions

View file

@ -234,7 +234,7 @@ class DeckBrowser:
if node.collapsed:
prefix = "+"
else:
prefix = "-"
prefix = ""
def indent() -> str:
return " " * 6 * (node.level - 1)

View file

@ -14,7 +14,7 @@ from markdown import markdown
import aqt
from anki.collection import HelpPage
from anki.errors import BackendError, Interrupted
from anki.errors import BackendError, CardTypeError, Interrupted
from anki.utils import is_win
from aqt.addons import AddonManager, AddonMeta
from aqt.qt import *
@ -36,6 +36,14 @@ def show_exception(*, parent: QWidget, exception: Exception) -> None:
global _mbox
error_lines = []
help_page = HelpPage.TROUBLESHOOTING
# default to PlainText
text_format = Qt.TextFormat.PlainText
# set CardTypeError messages as rich text to allow HTML formatting
if isinstance(exception, CardTypeError):
text_format = Qt.TextFormat.RichText
if isinstance(exception, BackendError):
if exception.context:
error_lines.append(exception.context)
@ -51,7 +59,7 @@ def show_exception(*, parent: QWidget, exception: Exception) -> None:
)
error_text = "\n".join(error_lines)
print(error_lines)
_mbox = _init_message_box(str(exception), error_text, help_page)
_mbox = _init_message_box(str(exception), error_text, help_page, text_format)
_mbox.show()
@ -171,7 +179,10 @@ if not os.environ.get("DEBUG"):
def _init_message_box(
user_text: str, debug_text: str, help_page=HelpPage.TROUBLESHOOTING
user_text: str,
debug_text: str,
help_page=HelpPage.TROUBLESHOOTING,
text_format=Qt.TextFormat.PlainText,
):
global _mbox
@ -179,7 +190,7 @@ def _init_message_box(
_mbox.setWindowTitle("Anki")
_mbox.setText(user_text)
_mbox.setIcon(QMessageBox.Icon.Warning)
_mbox.setTextFormat(Qt.TextFormat.PlainText)
_mbox.setTextFormat(text_format)
def show_help():
openHelp(help_page)

View file

@ -17,6 +17,7 @@ use crate::import_export::package::media::SafeMediaEntry;
use crate::import_export::ImportProgress;
use crate::media::files::add_hash_suffix_to_file_stem;
use crate::media::files::sha1_of_reader;
use crate::media::Checksums;
use crate::prelude::*;
use crate::progress::ThrottlingProgressHandler;
@ -75,7 +76,7 @@ impl Context<'_> {
fn prepare_media(
media_entries: Vec<SafeMediaEntry>,
archive: &mut ZipArchive<File>,
existing_sha1s: &HashMap<String, Sha1Hash>,
existing_sha1s: &Checksums,
progress: &mut ThrottlingProgressHandler<ImportProgress>,
) -> Result<MediaUseMap> {
let mut media_map = MediaUseMap::default();

View file

@ -173,7 +173,9 @@ pub fn add_data_to_folder_uniquely<'a, P>(
where
P: AsRef<Path>,
{
let normalized_name = normalize_filename(desired_name);
// force lowercase to account for case-insensitive filesystems
// but not within normalize_filename, for existing media refs
let normalized_name: Cow<_> = normalize_filename(desired_name).to_lowercase().into();
let mut target_path = folder.as_ref().join(normalized_name.as_ref());
@ -496,8 +498,14 @@ mod test {
"test.mp3"
);
// different contents
// different contents, filenames differ only by case
let h2 = sha1_of_data(b"hello1");
assert_eq!(
add_data_to_folder_uniquely(dpath, "Test.mp3", b"hello1", h2).unwrap(),
"test-88fdd585121a4ccb3d1540527aee53a77c77abb8.mp3"
);
// same contents, filenames differ only by case
assert_eq!(
add_data_to_folder_uniquely(dpath, "test.mp3", b"hello1", h2).unwrap(),
"test-88fdd585121a4ccb3d1540527aee53a77c77abb8.mp3"

View file

@ -6,7 +6,6 @@ pub mod files;
mod service;
use std::borrow::Cow;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
@ -22,6 +21,7 @@ use crate::progress::ThrottlingProgressHandler;
use crate::sync::http_client::HttpSyncClient;
use crate::sync::login::SyncAuth;
use crate::sync::media::database::client::changetracker::ChangeTracker;
pub use crate::sync::media::database::client::Checksums;
use crate::sync::media::database::client::MediaDatabase;
use crate::sync::media::database::client::MediaEntry;
use crate::sync::media::progress::MediaSyncProgress;
@ -157,7 +157,7 @@ impl MediaManager {
pub fn all_checksums_after_checking(
&self,
progress: impl FnMut(usize) -> bool,
) -> Result<HashMap<String, Sha1Hash>> {
) -> Result<Checksums> {
ChangeTracker::new(&self.media_folder, progress).register_changes(&self.db)?;
self.db.all_registered_checksums()
}
@ -176,7 +176,7 @@ impl MediaManager {
/// All checksums without registering changes first.
#[cfg(test)]
pub(crate) fn all_checksums_as_is(&self) -> HashMap<String, [u8; 20]> {
pub(crate) fn all_checksums_as_is(&self) -> Checksums {
self.db.all_registered_checksums().unwrap()
}
}

View file

@ -18,6 +18,20 @@ use crate::prelude::*;
pub mod changetracker;
pub struct Checksums(HashMap<String, Sha1Hash>);
impl Checksums {
// case-fold filenames when checking files to be imported
// to account for case-insensitive filesystems
pub fn get(&self, key: impl AsRef<str>) -> Option<&Sha1Hash> {
self.0.get(key.as_ref().to_lowercase().as_str())
}
pub fn contains_key(&self, key: impl AsRef<str>) -> bool {
self.get(key).is_some()
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct MediaEntry {
pub fname: String,
@ -175,11 +189,12 @@ delete from media where fname=?",
}
/// Returns all filenames and checksums, where the checksum is not null.
pub(crate) fn all_registered_checksums(&self) -> error::Result<HashMap<String, Sha1Hash>> {
pub(crate) fn all_registered_checksums(&self) -> error::Result<Checksums> {
self.db
.prepare("SELECT fname, csum FROM media WHERE csum IS NOT NULL")?
.query_and_then([], row_to_name_and_checksum)?
.collect()
.collect::<error::Result<_>>()
.map(Checksums)
}
pub(crate) fn force_resync(&self) -> error::Result<()> {

View file

@ -202,7 +202,7 @@ fn sveltekit_temp_file(path: &str) -> bool {
}
fn check_cargo_deny() -> Result<()> {
Command::run("cargo install cargo-deny@0.18.3")?;
Command::run("cargo install cargo-deny@0.18.6")?;
Command::run("cargo deny check")?;
Ok(())
}

View file

@ -4,7 +4,12 @@
import { getRange, getSelection } from "./cross-browser";
function wrappedExceptForWhitespace(text: string, front: string, back: string): string {
const match = text.match(/^(\s*)([^]*?)(\s*)$/)!;
const normalizedText = text
.replace(/&nbsp;/g, " ")
.replace(/&#160;/g, " ")
.replace(/\u00A0/g, " ");
const match = normalizedText.match(/^(\s*)([^]*?)(\s*)$/)!;
return match[1] + front + match[2] + back + match[3];
}

View file

@ -37,7 +37,9 @@ export const addOrUpdateNote = async function(
backExtra,
tags,
});
showResult(mode.noteId, result, noteCount);
if (result.note) {
showResult(mode.noteId, result, noteCount);
}
} else {
const result = await addImageOcclusionNote({
// IOCloningMode is not used on mobile
@ -55,23 +57,12 @@ export const addOrUpdateNote = async function(
// show toast message
const showResult = (noteId: number | null, result: OpChanges, count: number) => {
const props = $state({
message: "",
type: "error" as "error" | "success",
message: noteId ? tr.browsingCardsUpdated({ count: count }) : tr.importingCardsAdded({ count: count }),
type: "success" as "error" | "success",
showToast: true,
});
mount(Toast, {
target: document.body,
props,
});
if (result.note) {
const msg = noteId ? tr.browsingCardsUpdated({ count: count }) : tr.importingCardsAdded({ count: count });
props.message = msg;
props.type = "success";
props.showToast = true;
} else {
const msg = tr.notetypesErrorGeneratingCloze();
props.message = msg;
props.showToast = true;
}
};