diff --git a/.mypy.ini b/.mypy.ini index f9fdf42f5..648c6a6ea 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -38,6 +38,78 @@ strict_optional = True strict_optional = True [mypy-aqt.operations.*] strict_optional = True +[mypy-aqt.editor] +strict_optional = True +[mypy-aqt.importing] +strict_optional = True +[mypy-aqt.preferences] +strict_optional = True +[mypy-aqt.overview] +strict_optional = True +[mypy-aqt.customstudy] +strict_optional = True +[mypy-aqt.taglimit] +strict_optional = True +[mypy-aqt.modelchooser] +strict_optional = True +[mypy-aqt.deckdescription] +strict_optional = True +[mypy-aqt.deckbrowser] +strict_optional = True +[mypy-aqt.studydeck] +strict_optional = True +[mypy-aqt.tts] +strict_optional = True +[mypy-aqt.mediasrv] +strict_optional = True +[mypy-aqt.changenotetype] +strict_optional = True +[mypy-aqt.clayout] +strict_optional = True +[mypy-aqt.fields] +strict_optional = True +[mypy-aqt.filtered_deck] +strict_optional = True +[mypy-aqt.editcurrent] +strict_optional = True +[mypy-aqt.deckoptions] +strict_optional = True +[mypy-aqt.notetypechooser] +strict_optional = True +[mypy-aqt.stats] +strict_optional = True +[mypy-aqt.switch] +strict_optional = True +[mypy-aqt.debug_console] +strict_optional = True +[mypy-aqt.emptycards] +strict_optional = True +[mypy-aqt.flags] +strict_optional = True +[mypy-aqt.mediacheck] +strict_optional = True +[mypy-aqt.theme] +strict_optional = True +[mypy-aqt.toolbar] +strict_optional = True +[mypy-aqt.deckchooser] +strict_optional = True +[mypy-aqt.about] +strict_optional = True +[mypy-aqt.webview] +strict_optional = True +[mypy-aqt.mediasync] +strict_optional = True +[mypy-aqt.package] +strict_optional = True +[mypy-aqt.progress] +strict_optional = True +[mypy-aqt.tagedit] +strict_optional = True +[mypy-aqt.utils] +strict_optional = True +[mypy-aqt.sync] +strict_optional = True [mypy-anki.scheduler.base] strict_optional = True [mypy-anki._backend.rsbridge] diff --git a/.version b/.version index 250af06b5..cd158b7c5 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -24.10 \ No newline at end of file +24.11 \ No newline at end of file diff --git a/CONTRIBUTORS b/CONTRIBUTORS index b0002241c..e6e1da9a2 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -194,9 +194,13 @@ Gregory Abrasaldo Taylor Obyen <162023405+taylorobyen@users.noreply.github.com> Kris Cherven twwn -Shirish Pokhrel +Cy Pokhrel Park Hyunwoo Tomas Fabrizio Orsi +Dongjin Ouyang <1113117424@qq.com> +Sawan Sunar +hideo aoyama +Ross Brown ******************** diff --git a/Cargo.lock b/Cargo.lock index f0673c568..e9134352c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -130,7 +130,7 @@ dependencies = [ "futures", "hex", "htmlescape", - "hyper 1.4.1", + "hyper 1.5.0", "id_tree", "inflections", "itertools 0.13.0", @@ -148,9 +148,9 @@ dependencies = [ "pulldown-cmark 0.9.6", "rand", "regex", - "reqwest 0.12.7", + "reqwest 0.12.8", "rusqlite", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "scopeguard", "serde", "serde-aux", @@ -160,7 +160,7 @@ dependencies = [ "sha1", "snafu", "strum 0.26.3", - "syn 2.0.77", + "syn 2.0.82", "tempfile", "tokio", "tokio-util", @@ -171,7 +171,6 @@ dependencies = [ "unic-ucd-category", "unicase", "unicode-normalization", - "utime", "windows 0.56.0", "wiremock", "zip", @@ -305,9 +304,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" [[package]] name = "apple-bundles" @@ -323,9 +322,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -354,9 +353,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.12" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" +checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" dependencies = [ "futures-core", "memchr", @@ -368,9 +367,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -379,24 +378,24 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -407,15 +406,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -425,7 +424,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "itoa", "matchit", @@ -449,9 +448,9 @@ dependencies = [ [[package]] name = "axum-client-ip" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72188bed20deb981f3a4a9fe674e5980fd9e9c2bd880baa94715ad5d60d64c67" +checksum = "9eefda7e2b27e1bda4d6fa8a06b50803b8793769045918bc37ad062d48a6efac" dependencies = [ "axum", "forwarded-header-value", @@ -460,9 +459,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -473,7 +472,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -481,9 +480,9 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" +checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" dependencies = [ "axum", "axum-core", @@ -504,29 +503,28 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ - "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.4", + "miniz_oxide 0.8.0", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -639,7 +637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.8", "serde", ] @@ -782,7 +780,7 @@ dependencies = [ "derive-new", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -906,22 +904,22 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" +checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -932,9 +930,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "bzip2" @@ -978,7 +976,7 @@ dependencies = [ "rand", "rand_distr", "rayon", - "safetensors 0.4.4", + "safetensors 0.4.5", "thiserror", "yoke", "zip", @@ -1001,9 +999,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.15" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", @@ -1073,9 +1071,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -1083,9 +1081,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -1096,23 +1094,23 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.24" +version = "4.5.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7db6eca8c205649e8d3ccd05aa5042b1800a784e56bc7c43524fde8abbfa9b" +checksum = "9646e2e245bf62f45d39a0f3f36f1171ad1ea0d6967fd114bca72cb02a8fcdfb" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -1255,9 +1253,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -1454,7 +1452,7 @@ checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -1512,7 +1510,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -1572,14 +1570,14 @@ dependencies = [ [[package]] name = "enum-as-inner" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -1675,9 +1673,9 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" dependencies = [ "simd-adler32", ] @@ -1713,9 +1711,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -1767,9 +1765,9 @@ dependencies = [ [[package]] name = "flume" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "spin 0.9.8", ] @@ -1807,7 +1805,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -1862,15 +1860,16 @@ dependencies = [ [[package]] name = "fsrs" -version = "1.3.1" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2434366942bf285f3c0691e68d731e56f4e1fc1d8ec7b6a0e9411e94eda6ffbd" +checksum = "de570dbbc7b95c5cf5f77c905b3b54b16352c1b48694e6761d716fb92b8db67c" dependencies = [ "burn", "itertools 0.12.1", "log", "ndarray", "ndarray-rand", + "priority-queue", "rand", "rayon", "serde", @@ -1907,9 +1906,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1922,9 +1921,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1932,15 +1931,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1960,38 +1959,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -2167,9 +2166,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gix-features" @@ -2219,9 +2218,9 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e" +checksum = "6cae0e8661c3ff92688ce1c8b8058b3efb312aba9492bbe93661a21705ab431b" [[package]] name = "gl_generator" @@ -2242,15 +2241,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -2413,6 +2412,12 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + [[package]] name = "hashlink" version = "0.8.4" @@ -2564,7 +2569,7 @@ dependencies = [ "markup5ever 0.12.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -2631,9 +2636,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -2649,9 +2654,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" dependencies = [ "bytes", "futures-channel", @@ -2673,9 +2678,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -2700,7 +2705,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.31", "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", @@ -2708,21 +2713,21 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", - "rustls 0.23.12", + "rustls 0.23.18", "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", "tower-service", - "webpki-roots 0.26.5", + "webpki-roots 0.26.6", ] [[package]] @@ -2732,7 +2737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper 0.14.30", + "hyper 0.14.31", "native-tls", "tokio", "tokio-native-tls", @@ -2746,7 +2751,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "native-tls", "tokio", @@ -2756,29 +2761,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.4.1", + "hyper 1.5.0", "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2818,15 +2822,15 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.8", "same-file", "walkdir", "winapi-util", @@ -2852,12 +2856,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -2923,9 +2927,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is-terminal" @@ -3003,18 +3007,18 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] [[package]] name = "junction" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9c415a9b7b1e86cd5738f39d34c9e78c765da7fb1756dbd7d31b3b0d2e7afa" +checksum = "72bbdfd737a243da3dfc1f99ee8d6e166480f17ab4ac84d7c34aacd73fc7bd16" dependencies = [ "scopeguard", "windows-sys 0.52.0", @@ -3071,9 +3075,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libdbus-sys" @@ -3102,7 +3106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3166,7 +3170,7 @@ dependencies = [ "itertools 0.13.0", "linkcheck", "regex", - "reqwest 0.12.7", + "reqwest 0.12.8", "strum 0.26.3", "tokio", ] @@ -3385,9 +3389,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", "stable_deref_trait", @@ -3458,7 +3462,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", - "simd-adler32", ] [[package]] @@ -3468,6 +3471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -3748,7 +3752,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -3772,18 +3776,18 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" @@ -3805,9 +3809,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -3826,7 +3830,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -3837,9 +3841,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -3939,9 +3943,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" [[package]] name = "pbkdf2" @@ -3989,9 +3993,9 @@ source = "git+https://github.com/ankitects/rust-url.git?rev=bb930b8d089f4d30d7d1 [[package]] name = "pest" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", "thiserror", @@ -4000,9 +4004,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" dependencies = [ "pest", "pest_generator", @@ -4010,22 +4014,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] name = "pest_meta" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" dependencies = [ "once_cell", "pest", @@ -4111,7 +4115,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -4134,22 +4138,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -4166,9 +4170,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "plist" @@ -4185,9 +4189,9 @@ dependencies = [ [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -4198,30 +4202,30 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] [[package]] name = "png" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.4", + "miniz_oxide 0.8.0", ] [[package]] @@ -4232,9 +4236,9 @@ checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" [[package]] name = "portable-atomic" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "powerfmt" @@ -4265,12 +4269,23 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "prettyplease" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.82", +] + +[[package]] +name = "priority-queue" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714c75db297bc88a63783ffc6ab9f830698a6705aa0201416931759ef4c8183d" +dependencies = [ + "autocfg", + "equivalent", + "indexmap", ] [[package]] @@ -4290,24 +4305,24 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] [[package]] name = "profiling" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" [[package]] name = "prost" -version = "0.12.6" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" dependencies = [ "bytes", "prost-derive", @@ -4315,13 +4330,13 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.12.6" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15" dependencies = [ "bytes", "heck 0.5.0", - "itertools 0.12.1", + "itertools 0.13.0", "log", "multimap", "once_cell", @@ -4330,28 +4345,28 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.77", + "syn 2.0.82", "tempfile", ] [[package]] name = "prost-derive" -version = "0.12.6" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] name = "prost-reflect" -version = "0.12.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057237efdb71cf4b3f9396302a3d6599a92fa94063ba537b66130980ea9909f3" +checksum = "4b7535b02f0e5efe3e1dbfcb428be152226ed0c66cad9541f2274c8ba8d4cd40" dependencies = [ "once_cell", "prost", @@ -4360,9 +4375,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.6" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" dependencies = [ "prost", ] @@ -4423,9 +4438,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.22.2" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831e8e819a138c36e212f3af3fd9eeffed6bf1510a805af35b0edee5ffa59433" +checksum = "3d922163ba1f79c04bc49073ba7b32fd5a8d3b76a87c955921234b8e77333c51" dependencies = [ "cfg-if", "indoc", @@ -4441,9 +4456,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.22.2" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8730e591b14492a8945cdff32f089250b05f5accecf74aeddf9e8272ce1fa8" +checksum = "bc38c5feeb496c8321091edf3d63e9a6829eab4b863b4a6a65f26f3e9cc6b179" dependencies = [ "once_cell", "target-lexicon", @@ -4451,9 +4466,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.22.2" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e97e919d2df92eb88ca80a037969f44e5e70356559654962cbb3316d00300c6" +checksum = "94845622d88ae274d2729fcefc850e63d7a3ddff5e3ce11bd88486db9f1d357d" dependencies = [ "libc", "pyo3-build-config", @@ -4461,27 +4476,27 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.22.2" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb57983022ad41f9e683a599f2fd13c3664d7063a3ac5714cae4b7bee7d3f206" +checksum = "e655aad15e09b94ffdb3ce3d217acf652e26bbc37697ef012f5e5e348c716e5e" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] name = "pyo3-macros-backend" -version = "0.22.2" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec480c0c51ddec81019531705acac51bcdbeae563557c982aa8263bb96880372" +checksum = "ae1e3f09eecd94618f60a455a23def79f79eba4dc561a97324bf9ac8c6df30ce" dependencies = [ "heck 0.5.0", "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -4513,7 +4528,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.0.0", - "rustls 0.23.12", + "rustls 0.23.18", "socket2", "thiserror", "tokio", @@ -4530,7 +4545,7 @@ dependencies = [ "rand", "ring 0.17.8", "rustc-hash 2.0.0", - "rustls 0.23.12", + "rustls 0.23.18", "slab", "thiserror", "tinyvec", @@ -4697,9 +4712,9 @@ checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -4717,14 +4732,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -4738,13 +4753,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -4755,9 +4770,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "renderdoc-sys" @@ -4779,7 +4794,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.30", + "hyper 0.14.31", "hyper-rustls 0.24.2", "hyper-tls 0.5.0", "ipnet", @@ -4811,9 +4826,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" dependencies = [ "base64 0.22.1", "bytes", @@ -4822,8 +4837,8 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", - "hyper-rustls 0.27.2", + "hyper 1.5.0", + "hyper-rustls 0.27.3", "hyper-tls 0.6.0", "hyper-util", "ipnet", @@ -4836,9 +4851,9 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.12", + "rustls 0.23.18", "rustls-native-certs", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", @@ -4855,7 +4870,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.5", + "webpki-roots 0.26.6", "windows-registry", ] @@ -4930,7 +4945,7 @@ dependencies = [ "clap", "flate2", "junction", - "reqwest 0.12.7", + "reqwest 0.12.8", "sha2", "tar", "termcolor", @@ -4975,9 +4990,9 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustix" -version = "0.38.35" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -5000,27 +5015,27 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" dependencies = [ "log", "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.7", + "rustls-webpki 0.102.8", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", "security-framework", @@ -5037,19 +5052,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -5063,9 +5077,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.7" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -5074,9 +5088,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" @@ -5096,9 +5110,9 @@ dependencies = [ [[package]] name = "safetensors" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7725d4d98fa515472f43a6e2bbf956c48e06b89bb50593a040e5945160214450" +checksum = "44560c11236a6130a46ce36c836a62936dc81ebf8c36a37947423571be0e55b6" dependencies = [ "serde", "serde_json", @@ -5125,11 +5139,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5178,9 +5192,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -5215,9 +5229,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -5235,20 +5249,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -5274,7 +5288,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -5430,23 +5444,23 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snafu" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b835cb902660db3415a672d862905e791e54d306c6e8189168c7f3d9ae1c79d" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" dependencies = [ "snafu-derive", ] [[package]] name = "snafu-derive" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d1e02fca405f6280643174a50c942219f0bbf4dbf7d480f1dd864d6f211ae5" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -5558,7 +5572,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -5571,7 +5585,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -5593,9 +5607,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" dependencies = [ "proc-macro2", "quote", @@ -5625,7 +5639,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -5665,9 +5679,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" +checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" dependencies = [ "filetime", "libc", @@ -5699,9 +5713,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -5732,12 +5746,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ "rustix", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -5753,22 +5767,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -5891,7 +5905,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -5920,7 +5934,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.12", + "rustls 0.23.18", "rustls-pki-types", "tokio", ] @@ -5951,9 +5965,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -5979,9 +5993,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", @@ -6011,14 +6025,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper 0.1.2", "tokio", "tower-layer", "tower-service", @@ -6086,7 +6100,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -6222,9 +6236,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unic-char-property" @@ -6286,7 +6300,7 @@ checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.77", + "syn 2.0.82", "unic-langid-impl", ] @@ -6322,42 +6336,42 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unindent" @@ -6387,12 +6401,12 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls 0.23.12", + "rustls 0.23.18", "rustls-pki-types", "serde", "serde_json", "url", - "webpki-roots 0.26.5", + "webpki-roots 0.26.6", ] [[package]] @@ -6419,21 +6433,11 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "utime" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91baa0c65eabd12fcbdac8cc35ff16159cab95cae96d0222d6d0271db6193cef" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", "rand", @@ -6487,7 +6491,7 @@ dependencies = [ "futures-util", "headers 0.3.9", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.31", "log", "mime", "mime_guess", @@ -6521,9 +6525,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -6532,24 +6536,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -6559,9 +6563,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6569,28 +6573,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" dependencies = [ "futures-util", "js-sys", @@ -6601,9 +6605,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", @@ -6627,9 +6631,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.5" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] @@ -6846,7 +6850,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -6857,7 +6861,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -7049,9 +7053,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -7078,18 +7082,18 @@ dependencies = [ [[package]] name = "wiremock" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a59f8ae78a4737fb724f20106fb35ccb7cfe61ff335665d3042b3aa98e34717" +checksum = "7fff469918e7ca034884c7fd8f93fe27bacb7fcb599fd879df6c7b429a29b646" dependencies = [ "assert-json-diff", "async-trait", - "base64 0.21.7", + "base64 0.22.1", "deadpool", "futures", "http 1.1.0", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "log", "once_cell", @@ -7113,9 +7117,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601" +checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" [[package]] name = "xml5ever" @@ -7166,7 +7170,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", "synstructure", ] @@ -7188,7 +7192,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", ] [[package]] @@ -7208,7 +7212,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.82", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index 78552be52..da39632a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ git = "https://github.com/ankitects/linkcheck.git" rev = "184b2ca50ed39ca43da13f0b830a463861adb9ca" [workspace.dependencies.fsrs] -version = "=1.3.1" +version = "=1.4.3" # git = "https://github.com/open-spaced-repetition/fsrs-rs.git" # rev = "58ca25ed2bc4bb1dc376208bbcaed7f5a501b941" # path = "../open-spaced-repetition/fsrs-rs" @@ -55,19 +55,19 @@ unicase = "=2.6.0" # any changes could invalidate sqlite indexes # normal ammonia = "4.0.0" -anyhow = "1.0.86" +anyhow = "1.0.90" apple-bundles = "0.17.0" -async-compression = { version = "0.4.12", features = ["zstd", "tokio"] } -async-stream = "0.3.5" -async-trait = "0.1.82" +async-compression = { version = "0.4.17", features = ["zstd", "tokio"] } +async-stream = "0.3.6" +async-trait = "0.1.83" axum = { version = "0.7", features = ["multipart", "macros"] } axum-client-ip = "0.6" -axum-extra = { version = "0.9.3", features = ["typed-header"] } +axum-extra = { version = "0.9.4", features = ["typed-header"] } blake3 = "1.5.4" -bytes = "1.7.1" +bytes = "1.7.2" camino = "1.1.9" chrono = { version = "0.4.38", default-features = false, features = ["std", "clock"] } -clap = { version = "4.3.24", features = ["derive"] } +clap = { version = "4.5.20", features = ["derive"] } coarsetime = "0.1.34" convert_case = "0.6.0" criterion = { version = "0.5.1" } @@ -77,14 +77,14 @@ difflib = "0.4.0" dirs = "5.0.1" dunce = "1.0.5" envy = "0.4.2" -flate2 = "1.0.33" +flate2 = "1.0.34" fluent = "0.16.1" fluent-bundle = "0.15.3" fluent-syntax = "0.11.1" fnv = "1.0.7" -futures = "0.3.30" +futures = "0.3.31" glob = "0.3.1" -globset = "0.4.14" +globset = "0.4.15" hex = "0.4.3" htmlescape = "0.3.1" hyper = "1" @@ -92,47 +92,47 @@ id_tree = "1.8.0" inflections = "1.1.1" intl-memoizer = "0.5.2" itertools = "0.13.0" -junction = "1.1.0" +junction = "1.2.0" lazy_static = "1.5.0" maplit = "1.0.2" nom = "7.1.3" num-format = "0.4.4" num_cpus = "1.16.0" -num_enum = "0.7.2" -once_cell = "1.19.0" +num_enum = "0.7.3" +once_cell = "1.20.2" pbkdf2 = { version = "0.12", features = ["simple"] } phf = { version = "0.11.2", features = ["macros"] } -pin-project = "1.1.5" -plist = "1.5.1" -prettyplease = "0.2.22" -prost = "0.12.3" -prost-build = "0.12.3" -prost-reflect = "0.12.0" -prost-types = "0.12.3" +pin-project = "1.1.6" +plist = "1.7.0" +prettyplease = "0.2.24" +prost = "0.13" +prost-build = "0.13" +prost-reflect = "0.14" +prost-types = "0.13" pulldown-cmark = "0.9.6" -pyo3 = { version = "0.22.2", features = ["extension-module", "abi3", "abi3-py39"] } +pyo3 = { version = "0.22.5", features = ["extension-module", "abi3", "abi3-py39"] } rand = "0.8.5" -regex = "1.10.6" -reqwest = { version = "0.12.7", default-features = false, features = ["json", "socks", "stream", "multipart"] } +regex = "1.11.0" +reqwest = { version = "0.12.8", default-features = false, features = ["json", "socks", "stream", "multipart"] } rusqlite = { version = "0.30.0", features = ["trace", "functions", "collation", "bundled"] } -rustls-pemfile = "2.1.3" +rustls-pemfile = "2.2.0" scopeguard = "1.2.0" -serde = { version = "1.0.209", features = ["derive"] } +serde = { version = "1.0.210", features = ["derive"] } serde-aux = "4.5.0" -serde_json = "1.0.127" +serde_json = "1.0.132" serde_repr = "0.1.19" serde_tuple = "0.5.0" sha1 = "0.10.6" sha2 = { version = "0.10.8" } simple-file-manifest = "0.11.0" -snafu = { version = "0.8.4", features = ["rust_1_61"] } +snafu = { version = "0.8.5", features = ["rust_1_61"] } strum = { version = "0.26.3", features = ["derive"] } -syn = { version = "2.0.77", features = ["parsing", "printing"] } -tar = "0.4.41" -tempfile = "3.12.0" +syn = { version = "2.0.82", features = ["parsing", "printing"] } +tar = "0.4.42" +tempfile = "3.13.0" termcolor = "1.4.1" -tokio = { version = "1.38", features = ["fs", "rt-multi-thread", "macros", "signal"] } -tokio-util = { version = "0.7.11", features = ["io"] } +tokio = { version = "1.40", features = ["fs", "rt-multi-thread", "macros", "signal"] } +tokio-util = { version = "0.7.12", features = ["io"] } tower-http = { version = "0.5", features = ["trace"] } tracing = { version = "0.1.40", features = ["max_level_trace", "release_max_level_debug"] } tracing-appender = "0.2.3" @@ -140,11 +140,10 @@ tracing-subscriber = { version = "0.3.18", features = ["fmt", "env-filter"] } tugger-windows-codesign = "0.10.0" unic-langid = { version = "0.9.5", features = ["macros"] } unic-ucd-category = "0.9.0" -unicode-normalization = "0.1.23" -utime = "0.3.1" +unicode-normalization = "0.1.24" walkdir = "2.5.0" which = "5.0.0" -wiremock = "0.6.1" +wiremock = "0.6.2" xz2 = "0.1.7" zip = { version = "0.6.6", default-features = false, features = ["deflate", "time"] } zstd = { version = "0.13.2", features = ["zstdmt"] } diff --git a/build/configure/src/python.rs b/build/configure/src/python.rs index 02f064b5c..ba304de2a 100644 --- a/build/configure/src/python.rs +++ b/build/configure/src/python.rs @@ -142,7 +142,7 @@ impl BuildAction for BuildWheel { let tag = if let Some(platform) = self.platform { let platform = match platform { - Platform::LinuxX64 => "manylinux_2_28_x86_64", + Platform::LinuxX64 => "manylinux_2_31_x86_64", Platform::LinuxArm => "manylinux_2_31_aarch64", Platform::MacX64 => "macosx_10_13_x86_64", Platform::MacArm => "macosx_11_0_arm64", diff --git a/cargo/licenses.json b/cargo/licenses.json index eb75a72e7..619f5bb5c 100644 --- a/cargo/licenses.json +++ b/cargo/licenses.json @@ -1,7 +1,7 @@ [ { "name": "addr2line", - "version": "0.22.0", + "version": "0.24.2", "authors": null, "repository": "https://github.com/gimli-rs/addr2line", "license": "Apache-2.0 OR MIT", @@ -136,7 +136,7 @@ }, { "name": "anyhow", - "version": "1.0.86", + "version": "1.0.90", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/anyhow", "license": "Apache-2.0 OR MIT", @@ -145,7 +145,7 @@ }, { "name": "arrayref", - "version": "0.3.8", + "version": "0.3.9", "authors": "David Roundy ", "repository": "https://github.com/droundy/arrayref", "license": "BSD-2-Clause", @@ -181,7 +181,7 @@ }, { "name": "async-compression", - "version": "0.4.12", + "version": "0.4.17", "authors": "Wim Looman |Allen Bui ", "repository": "https://github.com/Nullus157/async-compression", "license": "Apache-2.0 OR MIT", @@ -190,7 +190,7 @@ }, { "name": "async-stream", - "version": "0.3.5", + "version": "0.3.6", "authors": "Carl Lerche ", "repository": "https://github.com/tokio-rs/async-stream", "license": "MIT", @@ -199,7 +199,7 @@ }, { "name": "async-stream-impl", - "version": "0.3.5", + "version": "0.3.6", "authors": "Carl Lerche ", "repository": "https://github.com/tokio-rs/async-stream", "license": "MIT", @@ -208,7 +208,7 @@ }, { "name": "async-trait", - "version": "0.1.82", + "version": "0.1.83", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/async-trait", "license": "Apache-2.0 OR MIT", @@ -226,7 +226,7 @@ }, { "name": "autocfg", - "version": "1.3.0", + "version": "1.4.0", "authors": "Josh Stone ", "repository": "https://github.com/cuviper/autocfg", "license": "Apache-2.0 OR MIT", @@ -235,7 +235,7 @@ }, { "name": "axum", - "version": "0.7.5", + "version": "0.7.7", "authors": null, "repository": "https://github.com/tokio-rs/axum", "license": "MIT", @@ -244,7 +244,7 @@ }, { "name": "axum-client-ip", - "version": "0.6.0", + "version": "0.6.1", "authors": null, "repository": "https://github.com/imbolc/axum-client-ip", "license": "MIT", @@ -253,7 +253,7 @@ }, { "name": "axum-core", - "version": "0.4.3", + "version": "0.4.5", "authors": null, "repository": "https://github.com/tokio-rs/axum", "license": "MIT", @@ -262,7 +262,7 @@ }, { "name": "axum-extra", - "version": "0.9.3", + "version": "0.9.4", "authors": null, "repository": "https://github.com/tokio-rs/axum", "license": "MIT", @@ -271,7 +271,7 @@ }, { "name": "axum-macros", - "version": "0.4.1", + "version": "0.4.2", "authors": null, "repository": "https://github.com/tokio-rs/axum", "license": "MIT", @@ -280,7 +280,7 @@ }, { "name": "backtrace", - "version": "0.3.73", + "version": "0.3.74", "authors": "The Rust Project Developers", "repository": "https://github.com/rust-lang/backtrace-rs", "license": "Apache-2.0 OR MIT", @@ -550,7 +550,7 @@ }, { "name": "bytemuck", - "version": "1.17.1", + "version": "1.19.0", "authors": "Lokathor ", "repository": "https://github.com/Lokathor/bytemuck", "license": "Apache-2.0 OR MIT OR Zlib", @@ -559,7 +559,7 @@ }, { "name": "bytemuck_derive", - "version": "1.7.1", + "version": "1.8.0", "authors": "Lokathor ", "repository": "https://github.com/Lokathor/bytemuck", "license": "Apache-2.0 OR MIT OR Zlib", @@ -577,7 +577,7 @@ }, { "name": "bytes", - "version": "1.7.1", + "version": "1.7.2", "authors": "Carl Lerche |Sean McArthur ", "repository": "https://github.com/tokio-rs/bytes", "license": "MIT", @@ -622,7 +622,7 @@ }, { "name": "cc", - "version": "1.1.15", + "version": "1.1.31", "authors": "Alex Crichton ", "repository": "https://github.com/rust-lang/cc-rs", "license": "Apache-2.0 OR MIT", @@ -775,7 +775,7 @@ }, { "name": "cpufeatures", - "version": "0.2.13", + "version": "0.2.14", "authors": "RustCrypto Developers", "repository": "https://github.com/RustCrypto/utils", "license": "Apache-2.0 OR MIT", @@ -1000,7 +1000,7 @@ }, { "name": "enum-as-inner", - "version": "0.6.0", + "version": "0.6.1", "authors": "Benjamin Fry ", "repository": "https://github.com/bluejekyll/enum-as-inner", "license": "Apache-2.0 OR MIT", @@ -1081,7 +1081,7 @@ }, { "name": "fdeflate", - "version": "0.3.4", + "version": "0.3.5", "authors": "The image-rs Developers", "repository": "https://github.com/image-rs/fdeflate", "license": "Apache-2.0 OR MIT", @@ -1099,7 +1099,7 @@ }, { "name": "flate2", - "version": "1.0.33", + "version": "1.0.34", "authors": "Alex Crichton |Josh Triplett ", "repository": "https://github.com/rust-lang/flate2-rs", "license": "Apache-2.0 OR MIT", @@ -1144,7 +1144,7 @@ }, { "name": "flume", - "version": "0.11.0", + "version": "0.11.1", "authors": "Joshua Barretto ", "repository": "https://github.com/zesterer/flume", "license": "Apache-2.0 OR MIT", @@ -1225,7 +1225,7 @@ }, { "name": "fsrs", - "version": "1.3.1", + "version": "1.4.3", "authors": "Open Spaced Repetition", "repository": "https://github.com/open-spaced-repetition/fsrs-rs", "license": "BSD-3-Clause", @@ -1243,7 +1243,7 @@ }, { "name": "futures", - "version": "0.3.30", + "version": "0.3.31", "authors": null, "repository": "https://github.com/rust-lang/futures-rs", "license": "Apache-2.0 OR MIT", @@ -1252,7 +1252,7 @@ }, { "name": "futures-channel", - "version": "0.3.30", + "version": "0.3.31", "authors": null, "repository": "https://github.com/rust-lang/futures-rs", "license": "Apache-2.0 OR MIT", @@ -1261,7 +1261,7 @@ }, { "name": "futures-core", - "version": "0.3.30", + "version": "0.3.31", "authors": null, "repository": "https://github.com/rust-lang/futures-rs", "license": "Apache-2.0 OR MIT", @@ -1270,7 +1270,7 @@ }, { "name": "futures-executor", - "version": "0.3.30", + "version": "0.3.31", "authors": null, "repository": "https://github.com/rust-lang/futures-rs", "license": "Apache-2.0 OR MIT", @@ -1288,7 +1288,7 @@ }, { "name": "futures-io", - "version": "0.3.30", + "version": "0.3.31", "authors": null, "repository": "https://github.com/rust-lang/futures-rs", "license": "Apache-2.0 OR MIT", @@ -1297,7 +1297,7 @@ }, { "name": "futures-macro", - "version": "0.3.30", + "version": "0.3.31", "authors": null, "repository": "https://github.com/rust-lang/futures-rs", "license": "Apache-2.0 OR MIT", @@ -1306,7 +1306,7 @@ }, { "name": "futures-sink", - "version": "0.3.30", + "version": "0.3.31", "authors": null, "repository": "https://github.com/rust-lang/futures-rs", "license": "Apache-2.0 OR MIT", @@ -1315,7 +1315,7 @@ }, { "name": "futures-task", - "version": "0.3.30", + "version": "0.3.31", "authors": null, "repository": "https://github.com/rust-lang/futures-rs", "license": "Apache-2.0 OR MIT", @@ -1324,7 +1324,7 @@ }, { "name": "futures-util", - "version": "0.3.30", + "version": "0.3.31", "authors": null, "repository": "https://github.com/rust-lang/futures-rs", "license": "Apache-2.0 OR MIT", @@ -1432,7 +1432,7 @@ }, { "name": "gimli", - "version": "0.29.0", + "version": "0.31.1", "authors": null, "repository": "https://github.com/gimli-rs/gimli", "license": "Apache-2.0 OR MIT", @@ -1477,7 +1477,7 @@ }, { "name": "gix-trace", - "version": "0.1.9", + "version": "0.1.10", "authors": "Sebastian Thiel ", "repository": "https://github.com/Byron/gitoxide", "license": "Apache-2.0 OR MIT", @@ -1592,6 +1592,15 @@ "license_file": null, "description": "A Rust port of Google's SwissTable hash map" }, + { + "name": "hashbrown", + "version": "0.15.2", + "authors": "Amanieu d'Antras ", + "repository": "https://github.com/rust-lang/hashbrown", + "license": "Apache-2.0 OR MIT", + "license_file": null, + "description": "A Rust port of Google's SwissTable hash map" + }, { "name": "hashlink", "version": "0.8.4", @@ -1729,7 +1738,7 @@ }, { "name": "httparse", - "version": "1.9.4", + "version": "1.9.5", "authors": "Sean McArthur ", "repository": "https://github.com/seanmonstar/httparse", "license": "Apache-2.0 OR MIT", @@ -1747,7 +1756,7 @@ }, { "name": "hyper", - "version": "1.4.1", + "version": "1.5.0", "authors": "Sean McArthur ", "repository": "https://github.com/hyperium/hyper", "license": "MIT", @@ -1756,7 +1765,7 @@ }, { "name": "hyper-rustls", - "version": "0.27.2", + "version": "0.27.3", "authors": null, "repository": "https://github.com/rustls/hyper-rustls", "license": "Apache-2.0 OR ISC OR MIT", @@ -1774,7 +1783,7 @@ }, { "name": "hyper-util", - "version": "0.1.7", + "version": "0.1.9", "authors": "Sean McArthur ", "repository": "https://github.com/hyperium/hyper-util", "license": "MIT", @@ -1783,7 +1792,7 @@ }, { "name": "iana-time-zone", - "version": "0.1.60", + "version": "0.1.61", "authors": "Andrew Straw |René Kijewski |Ryan Lopopolo ", "repository": "https://github.com/strawlab/iana-time-zone", "license": "Apache-2.0 OR MIT", @@ -1828,7 +1837,7 @@ }, { "name": "indexmap", - "version": "2.5.0", + "version": "2.6.0", "authors": null, "repository": "https://github.com/indexmap-rs/indexmap", "license": "Apache-2.0 OR MIT", @@ -1873,7 +1882,7 @@ }, { "name": "ipnet", - "version": "2.9.0", + "version": "2.10.1", "authors": "Kris Price ", "repository": "https://github.com/krisprice/ipnet", "license": "Apache-2.0 OR MIT", @@ -1936,7 +1945,7 @@ }, { "name": "js-sys", - "version": "0.3.70", + "version": "0.3.72", "authors": "The wasm-bindgen Developers", "repository": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/js-sys", "license": "Apache-2.0 OR MIT", @@ -1981,7 +1990,7 @@ }, { "name": "libc", - "version": "0.2.158", + "version": "0.2.161", "authors": "The Rust Project Developers", "repository": "https://github.com/rust-lang/libc", "license": "Apache-2.0 OR MIT", @@ -2152,7 +2161,7 @@ }, { "name": "memmap2", - "version": "0.9.4", + "version": "0.9.5", "authors": "Dan Burkert |Yevhenii Reizner ", "repository": "https://github.com/RazrFalcon/memmap2-rs", "license": "Apache-2.0 OR MIT", @@ -2413,7 +2422,7 @@ }, { "name": "object", - "version": "0.36.4", + "version": "0.36.5", "authors": null, "repository": "https://github.com/gimli-rs/object", "license": "Apache-2.0 OR MIT", @@ -2422,7 +2431,7 @@ }, { "name": "once_cell", - "version": "1.19.0", + "version": "1.20.2", "authors": "Aleksey Kladov ", "repository": "https://github.com/matklad/once_cell", "license": "Apache-2.0 OR MIT", @@ -2431,7 +2440,7 @@ }, { "name": "openssl", - "version": "0.10.66", + "version": "0.10.68", "authors": "Steven Fackler ", "repository": "https://github.com/sfackler/rust-openssl", "license": "Apache-2.0", @@ -2458,7 +2467,7 @@ }, { "name": "openssl-sys", - "version": "0.9.103", + "version": "0.9.104", "authors": "Alex Crichton |Steven Fackler ", "repository": "https://github.com/sfackler/rust-openssl", "license": "MIT", @@ -2638,7 +2647,7 @@ }, { "name": "pin-project", - "version": "1.1.5", + "version": "1.1.6", "authors": null, "repository": "https://github.com/taiki-e/pin-project", "license": "Apache-2.0 OR MIT", @@ -2647,7 +2656,7 @@ }, { "name": "pin-project-internal", - "version": "1.1.5", + "version": "1.1.6", "authors": null, "repository": "https://github.com/taiki-e/pin-project", "license": "Apache-2.0 OR MIT", @@ -2674,7 +2683,7 @@ }, { "name": "pkg-config", - "version": "0.3.30", + "version": "0.3.31", "authors": "Alex Crichton ", "repository": "https://github.com/rust-lang/pkg-config-rs", "license": "Apache-2.0 OR MIT", @@ -2683,7 +2692,7 @@ }, { "name": "png", - "version": "0.17.13", + "version": "0.17.14", "authors": "The image-rs Developers", "repository": "https://github.com/image-rs/image-png", "license": "Apache-2.0 OR MIT", @@ -2737,13 +2746,22 @@ }, { "name": "prettyplease", - "version": "0.2.22", + "version": "0.2.24", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/prettyplease", "license": "Apache-2.0 OR MIT", "license_file": null, "description": "A minimal `syn` syntax tree pretty-printer" }, + { + "name": "priority-queue", + "version": "2.1.1", + "authors": "Gianmarco Garrisi ", + "repository": "https://github.com/garro95/priority-queue", + "license": "LGPL-3.0-or-later OR MPL-2.0", + "license_file": null, + "description": "A Priority Queue implemented as a heap with a function to efficiently change the priority of an item." + }, { "name": "proc-macro-crate", "version": "3.2.0", @@ -2764,7 +2782,7 @@ }, { "name": "proc-macro2", - "version": "1.0.86", + "version": "1.0.88", "authors": "David Tolnay |Alex Crichton ", "repository": "https://github.com/dtolnay/proc-macro2", "license": "Apache-2.0 OR MIT", @@ -2773,7 +2791,7 @@ }, { "name": "profiling", - "version": "1.0.15", + "version": "1.0.16", "authors": "Philip Degarmo ", "repository": "https://github.com/aclysma/profiling", "license": "Apache-2.0 OR MIT", @@ -2782,7 +2800,7 @@ }, { "name": "prost", - "version": "0.12.6", + "version": "0.13.3", "authors": "Dan Burkert |Lucio Franco |Casper Meijn |Tokio Contributors ", "repository": "https://github.com/tokio-rs/prost", "license": "Apache-2.0", @@ -2791,7 +2809,7 @@ }, { "name": "prost-build", - "version": "0.12.6", + "version": "0.13.3", "authors": "Dan Burkert |Lucio Franco |Casper Meijn |Tokio Contributors ", "repository": "https://github.com/tokio-rs/prost", "license": "Apache-2.0", @@ -2800,7 +2818,7 @@ }, { "name": "prost-derive", - "version": "0.12.6", + "version": "0.13.3", "authors": "Dan Burkert |Lucio Franco |Casper Meijn |Tokio Contributors ", "repository": "https://github.com/tokio-rs/prost", "license": "Apache-2.0", @@ -2809,7 +2827,7 @@ }, { "name": "prost-reflect", - "version": "0.12.0", + "version": "0.14.2", "authors": "Andrew Hickman ", "repository": "https://github.com/andrewhickman/prost-reflect", "license": "Apache-2.0 OR MIT", @@ -2818,7 +2836,7 @@ }, { "name": "prost-types", - "version": "0.12.6", + "version": "0.13.3", "authors": "Dan Burkert |Lucio Franco |Casper Meijn |Tokio Contributors ", "repository": "https://github.com/tokio-rs/prost", "license": "Apache-2.0", @@ -3007,7 +3025,7 @@ }, { "name": "redox_syscall", - "version": "0.5.3", + "version": "0.5.7", "authors": "Jeremy Soller ", "repository": "https://gitlab.redox-os.org/redox-os/syscall", "license": "MIT", @@ -3025,7 +3043,7 @@ }, { "name": "regex", - "version": "1.10.6", + "version": "1.11.0", "authors": "The Rust Project Developers|Andrew Gallant ", "repository": "https://github.com/rust-lang/regex", "license": "Apache-2.0 OR MIT", @@ -3043,7 +3061,7 @@ }, { "name": "regex-automata", - "version": "0.4.7", + "version": "0.4.8", "authors": "The Rust Project Developers|Andrew Gallant ", "repository": "https://github.com/rust-lang/regex/tree/master/regex-automata", "license": "Apache-2.0 OR MIT", @@ -3061,7 +3079,7 @@ }, { "name": "regex-syntax", - "version": "0.8.4", + "version": "0.8.5", "authors": "The Rust Project Developers|Andrew Gallant ", "repository": "https://github.com/rust-lang/regex/tree/master/regex-syntax", "license": "Apache-2.0 OR MIT", @@ -3079,7 +3097,7 @@ }, { "name": "reqwest", - "version": "0.12.7", + "version": "0.12.8", "authors": "Sean McArthur ", "repository": "https://github.com/seanmonstar/reqwest", "license": "Apache-2.0 OR MIT", @@ -3151,7 +3169,7 @@ }, { "name": "rustix", - "version": "0.38.35", + "version": "0.38.37", "authors": "Dan Gohman |Jakub Konka ", "repository": "https://github.com/bytecodealliance/rustix", "license": "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT", @@ -3160,7 +3178,7 @@ }, { "name": "rustls", - "version": "0.23.12", + "version": "0.23.18", "authors": null, "repository": "https://github.com/rustls/rustls", "license": "Apache-2.0 OR ISC OR MIT", @@ -3169,7 +3187,7 @@ }, { "name": "rustls-native-certs", - "version": "0.7.3", + "version": "0.8.0", "authors": null, "repository": "https://github.com/rustls/rustls-native-certs", "license": "Apache-2.0 OR ISC OR MIT", @@ -3178,7 +3196,7 @@ }, { "name": "rustls-pemfile", - "version": "2.1.3", + "version": "2.2.0", "authors": null, "repository": "https://github.com/rustls/pemfile", "license": "Apache-2.0 OR ISC OR MIT", @@ -3187,7 +3205,7 @@ }, { "name": "rustls-pki-types", - "version": "1.8.0", + "version": "1.10.0", "authors": null, "repository": "https://github.com/rustls/pki-types", "license": "Apache-2.0 OR MIT", @@ -3196,7 +3214,7 @@ }, { "name": "rustls-webpki", - "version": "0.102.7", + "version": "0.102.8", "authors": null, "repository": "https://github.com/rustls/webpki", "license": "ISC", @@ -3205,7 +3223,7 @@ }, { "name": "rustversion", - "version": "1.0.17", + "version": "1.0.18", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/rustversion", "license": "Apache-2.0 OR MIT", @@ -3232,7 +3250,7 @@ }, { "name": "safetensors", - "version": "0.4.4", + "version": "0.4.5", "authors": null, "repository": "https://github.com/huggingface/safetensors", "license": "Apache-2.0", @@ -3259,7 +3277,7 @@ }, { "name": "schannel", - "version": "0.1.23", + "version": "0.1.26", "authors": "Steven Fackler |Steffen Butzer ", "repository": "https://github.com/steffengy/schannel-rs", "license": "MIT", @@ -3295,7 +3313,7 @@ }, { "name": "security-framework-sys", - "version": "2.11.1", + "version": "2.12.0", "authors": "Steven Fackler |Kornel ", "repository": "https://github.com/kornelski/rust-security-framework", "license": "Apache-2.0 OR MIT", @@ -3331,7 +3349,7 @@ }, { "name": "serde", - "version": "1.0.209", + "version": "1.0.210", "authors": "Erick Tryzelaar |David Tolnay ", "repository": "https://github.com/serde-rs/serde", "license": "Apache-2.0 OR MIT", @@ -3349,7 +3367,7 @@ }, { "name": "serde_derive", - "version": "1.0.209", + "version": "1.0.210", "authors": "Erick Tryzelaar |David Tolnay ", "repository": "https://github.com/serde-rs/serde", "license": "Apache-2.0 OR MIT", @@ -3358,7 +3376,7 @@ }, { "name": "serde_json", - "version": "1.0.127", + "version": "1.0.132", "authors": "Erick Tryzelaar |David Tolnay ", "repository": "https://github.com/serde-rs/json", "license": "Apache-2.0 OR MIT", @@ -3520,7 +3538,7 @@ }, { "name": "snafu", - "version": "0.8.4", + "version": "0.8.5", "authors": "Jake Goulding ", "repository": "https://github.com/shepmaster/snafu", "license": "Apache-2.0 OR MIT", @@ -3529,7 +3547,7 @@ }, { "name": "snafu-derive", - "version": "0.8.4", + "version": "0.8.5", "authors": "Jake Goulding ", "repository": "https://github.com/shepmaster/snafu", "license": "Apache-2.0 OR MIT", @@ -3664,7 +3682,7 @@ }, { "name": "syn", - "version": "2.0.77", + "version": "2.0.82", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/syn", "license": "Apache-2.0 OR MIT", @@ -3718,7 +3736,7 @@ }, { "name": "tempfile", - "version": "3.12.0", + "version": "3.13.0", "authors": "Steven Allen |The Rust Project Developers|Ashley Mannix |Jason White ", "repository": "https://github.com/Stebalien/tempfile", "license": "Apache-2.0 OR MIT", @@ -3754,7 +3772,7 @@ }, { "name": "thiserror", - "version": "1.0.63", + "version": "1.0.64", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/thiserror", "license": "Apache-2.0 OR MIT", @@ -3763,7 +3781,7 @@ }, { "name": "thiserror-impl", - "version": "1.0.63", + "version": "1.0.64", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/thiserror", "license": "Apache-2.0 OR MIT", @@ -3898,7 +3916,7 @@ }, { "name": "tokio-util", - "version": "0.7.11", + "version": "0.7.12", "authors": "Tokio Contributors ", "repository": "https://github.com/tokio-rs/tokio", "license": "MIT", @@ -3916,7 +3934,7 @@ }, { "name": "toml_edit", - "version": "0.22.20", + "version": "0.22.22", "authors": "Andronik Ordian |Ed Page ", "repository": "https://github.com/toml-rs/toml", "license": "Apache-2.0 OR MIT", @@ -3934,7 +3952,7 @@ }, { "name": "tower", - "version": "0.4.13", + "version": "0.5.1", "authors": "Tower Maintainers ", "repository": "https://github.com/tower-rs/tower", "license": "MIT", @@ -4141,7 +4159,7 @@ }, { "name": "unicode-bidi", - "version": "0.3.15", + "version": "0.3.17", "authors": "The Servo Project Developers", "repository": "https://github.com/servo/unicode-bidi", "license": "Apache-2.0 OR MIT", @@ -4150,7 +4168,7 @@ }, { "name": "unicode-ident", - "version": "1.0.12", + "version": "1.0.13", "authors": "David Tolnay ", "repository": "https://github.com/dtolnay/unicode-ident", "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", @@ -4159,7 +4177,7 @@ }, { "name": "unicode-normalization", - "version": "0.1.23", + "version": "0.1.24", "authors": "kwantam |Manish Goregaokar ", "repository": "https://github.com/unicode-rs/unicode-normalization", "license": "Apache-2.0 OR MIT", @@ -4168,7 +4186,7 @@ }, { "name": "unicode-segmentation", - "version": "1.11.0", + "version": "1.12.0", "authors": "kwantam |Manish Goregaokar ", "repository": "https://github.com/unicode-rs/unicode-segmentation", "license": "Apache-2.0 OR MIT", @@ -4177,7 +4195,7 @@ }, { "name": "unicode-width", - "version": "0.1.13", + "version": "0.1.14", "authors": "kwantam |Manish Goregaokar ", "repository": "https://github.com/unicode-rs/unicode-width", "license": "Apache-2.0 OR MIT", @@ -4186,7 +4204,7 @@ }, { "name": "unicode-xid", - "version": "0.2.5", + "version": "0.2.6", "authors": "erick.tryzelaar |kwantam |Manish Goregaokar ", "repository": "https://github.com/unicode-rs/unicode-xid", "license": "Apache-2.0 OR MIT", @@ -4229,18 +4247,9 @@ "license_file": null, "description": "Incremental, zero-copy UTF-8 decoding with error handling" }, - { - "name": "utime", - "version": "0.3.1", - "authors": "Hyeon Kim ", - "repository": "https://github.com/simnalamburt/utime", - "license": "Apache-2.0 OR MIT", - "license_file": null, - "description": "A missing utime function for Rust." - }, { "name": "uuid", - "version": "1.10.0", + "version": "1.11.0", "authors": "Ashley Mannix|Dylan DPC|Hunar Roop Kahlon", "repository": "https://github.com/uuid-rs/uuid", "license": "Apache-2.0 OR MIT", @@ -4312,7 +4321,7 @@ }, { "name": "wasm-bindgen", - "version": "0.2.93", + "version": "0.2.95", "authors": "The wasm-bindgen Developers", "repository": "https://github.com/rustwasm/wasm-bindgen", "license": "Apache-2.0 OR MIT", @@ -4321,7 +4330,7 @@ }, { "name": "wasm-bindgen-backend", - "version": "0.2.93", + "version": "0.2.95", "authors": "The wasm-bindgen Developers", "repository": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend", "license": "Apache-2.0 OR MIT", @@ -4330,7 +4339,7 @@ }, { "name": "wasm-bindgen-futures", - "version": "0.4.43", + "version": "0.4.45", "authors": "The wasm-bindgen Developers", "repository": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures", "license": "Apache-2.0 OR MIT", @@ -4339,7 +4348,7 @@ }, { "name": "wasm-bindgen-macro", - "version": "0.2.93", + "version": "0.2.95", "authors": "The wasm-bindgen Developers", "repository": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro", "license": "Apache-2.0 OR MIT", @@ -4348,7 +4357,7 @@ }, { "name": "wasm-bindgen-macro-support", - "version": "0.2.93", + "version": "0.2.95", "authors": "The wasm-bindgen Developers", "repository": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro-support", "license": "Apache-2.0 OR MIT", @@ -4357,7 +4366,7 @@ }, { "name": "wasm-bindgen-shared", - "version": "0.2.93", + "version": "0.2.95", "authors": "The wasm-bindgen Developers", "repository": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/shared", "license": "Apache-2.0 OR MIT", @@ -4366,7 +4375,7 @@ }, { "name": "wasm-streams", - "version": "0.4.0", + "version": "0.4.1", "authors": "Mattias Buelens ", "repository": "https://github.com/MattiasBuelens/wasm-streams/", "license": "Apache-2.0 OR MIT", @@ -4375,7 +4384,7 @@ }, { "name": "web-sys", - "version": "0.3.70", + "version": "0.3.72", "authors": "The wasm-bindgen Developers", "repository": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/web-sys", "license": "Apache-2.0 OR MIT", @@ -4393,7 +4402,7 @@ }, { "name": "webpki-roots", - "version": "0.26.5", + "version": "0.26.6", "authors": null, "repository": "https://github.com/rustls/webpki-roots", "license": "MPL-2.0", @@ -4762,7 +4771,7 @@ }, { "name": "winnow", - "version": "0.6.18", + "version": "0.6.20", "authors": null, "repository": "https://github.com/winnow-rs/winnow", "license": "MIT", @@ -4771,7 +4780,7 @@ }, { "name": "wiremock", - "version": "0.6.1", + "version": "0.6.2", "authors": "Luca Palmieri ", "repository": "https://github.com/LukeMathWalker/wiremock-rs", "license": "Apache-2.0 OR MIT", @@ -4780,7 +4789,7 @@ }, { "name": "xml-rs", - "version": "0.8.21", + "version": "0.8.22", "authors": "Vladimir Matveev ", "repository": "https://github.com/kornelski/xml-rs", "license": "MIT", diff --git a/docs/syncserver/Dockerfile b/docs/syncserver/Dockerfile index 2080f7778..21e343ca6 100644 --- a/docs/syncserver/Dockerfile +++ b/docs/syncserver/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.80.1-alpine3.20 AS builder +FROM rust:1.82.0-alpine3.20 AS builder ARG ANKI_VERSION diff --git a/docs/syncserver/Dockerfile.distroless b/docs/syncserver/Dockerfile.distroless index b53a9c62b..6bfdf9e07 100644 --- a/docs/syncserver/Dockerfile.distroless +++ b/docs/syncserver/Dockerfile.distroless @@ -1,4 +1,4 @@ -FROM rust:1.80.1 AS builder +FROM rust:1.82.0 AS builder ARG ANKI_VERSION diff --git a/ftl/core-repo b/ftl/core-repo index aa374dce9..e1545f7f0 160000 --- a/ftl/core-repo +++ b/ftl/core-repo @@ -1 +1 @@ -Subproject commit aa374dce927cb12ac6623f1bf3f5c5c14dddf95c +Subproject commit e1545f7f0ddeb617eeb1ca86e82862e552843578 diff --git a/ftl/core/actions.ftl b/ftl/core/actions.ftl index 82c31e1ed..8fd3df0f7 100644 --- a/ftl/core/actions.ftl +++ b/ftl/core/actions.ftl @@ -48,9 +48,9 @@ actions-update-card = Update Card actions-update-deck = Update Deck actions-forget-card = Reset Card actions-build-filtered-deck = Build Deck -actions-add-notetype = Add Notetype -actions-remove-notetype = Remove Notetype -actions-update-notetype = Update Notetype +actions-add-notetype = Add Note Type +actions-remove-notetype = Remove Note Type +actions-update-notetype = Update Note Type actions-update-config = Update Config actions-card-info = Card Info actions-previous-card-info = Previous Card Info diff --git a/ftl/core/adding.ftl b/ftl/core/adding.ftl index b95210ee9..e94333008 100644 --- a/ftl/core/adding.ftl +++ b/ftl/core/adding.ftl @@ -7,6 +7,6 @@ adding-history = History adding-note-deleted = (Note deleted) adding-shortcut = Shortcut: { $val } adding-the-first-field-is-empty = The first field is empty. -adding-you-have-a-cloze-deletion-note = You have a cloze notetype but have not made any cloze deletions. Proceed? -adding-cloze-outside-cloze-notetype = Cloze deletion can only be used on cloze notetypes. +adding-you-have-a-cloze-deletion-note = You have a cloze note type but have not made any cloze deletions. Proceed? +adding-cloze-outside-cloze-notetype = Cloze deletion can only be used on cloze note types. adding-cloze-outside-cloze-field = Cloze deletion can only be used in fields which use the 'cloze:' filter. This is typically the first field. diff --git a/ftl/core/browsing.ftl b/ftl/core/browsing.ftl index 644d158ac..c04d3c8e9 100644 --- a/ftl/core/browsing.ftl +++ b/ftl/core/browsing.ftl @@ -29,7 +29,7 @@ browsing-change-deck = Change Deck browsing-change-deck2 = Change Deck... browsing-change-note-type = Change Note Type browsing-change-note-type2 = Change Note Type... -browsing-change-notetype = Change Notetype +browsing-change-notetype = Change Note Type browsing-clear-unused-tags = Clear Unused Tags browsing-confirm-saved-search-overwrite = A saved search with the name { $name } already exists. Do you want to overwrite it? browsing-created = Created @@ -142,7 +142,7 @@ browsing-tooltip-card-modified = The last time changes were made to a card, incl browsing-tooltip-note-modified = The last time changes were made to a note, usually field content or tag edits browsing-tooltip-card = The name of a card's card template browsing-tooltip-cards = The number of cards a note has -browsing-tooltip-notetype = The name of a note's notetype +browsing-tooltip-notetype = The name of a note's note type browsing-tooltip-question = The front side of a card, customisable in the card template editor browsing-tooltip-answer = The back side of a card, customisable in the card template editor browsing-studied-today = Studied diff --git a/ftl/core/card-stats.ftl b/ftl/core/card-stats.ftl index ece08b20a..65c3b95f2 100644 --- a/ftl/core/card-stats.ftl +++ b/ftl/core/card-stats.ftl @@ -23,6 +23,7 @@ card-stats-review-log-type-review = Review card-stats-review-log-type-relearn = Relearn card-stats-review-log-type-filtered = Filtered card-stats-review-log-type-manual = Manual +card-stats-review-log-type-rescheduled = Rescheduled card-stats-review-log-elapsed-time = Elapsed Time card-stats-no-card = (No card to display.) card-stats-custom-data = Custom Data @@ -34,6 +35,8 @@ card-stats-fsrs-forgetting-curve-first-week = First Week card-stats-fsrs-forgetting-curve-first-month = First Month card-stats-fsrs-forgetting-curve-first-year = First Year card-stats-fsrs-forgetting-curve-all-time = All Time +card-stats-fsrs-forgetting-curve-probability-of-recalling = Probability of Recall +card-stats-fsrs-forgetting-curve-desired-retention = Desired Retention ## Window Titles diff --git a/ftl/core/card-templates.ftl b/ftl/core/card-templates.ftl index b61fcec90..18a7a76b4 100644 --- a/ftl/core/card-templates.ftl +++ b/ftl/core/card-templates.ftl @@ -20,11 +20,11 @@ card-templates-night-mode = Night Mode # on a mobile device. card-templates-add-mobile-class = Add Mobile Class card-templates-preview-settings = Options -card-templates-invalid-template-number = Card template { $number } in notetype '{ $notetype }' has a problem. +card-templates-invalid-template-number = Card template { $number } in note type '{ $notetype }' has a problem. card-templates-identical-front = The front side is identical to card template { $number }. card-templates-no-front-field = Expected to find a field replacement on the front of the card template. card-templates-missing-cloze = Expected to find '{ "{{" }cloze:Text{ "}}" }' or similar on the front and back of the card template. -card-templates-extraneous-cloze = 'cloze:' can only be used on cloze notetypes. +card-templates-extraneous-cloze = 'cloze:' can only be used on cloze note types. card-templates-see-preview = See the preview for more information. card-templates-field-not-found = Field '{ $field }' not found. card-templates-changes-saved = Changes saved. @@ -59,7 +59,7 @@ card-templates-this-will-create-card-proceed = } card-templates-type-boxes-warning = Only one typing box per card template is supported. card-templates-restore-to-default = Restore to Default -card-templates-restore-to-default-confirmation = This will reset all fields and templates in this notetype to their default +card-templates-restore-to-default-confirmation = This will reset all fields and templates in this note type to their default values, removing any extra fields/templates and their content, and any custom styling. Do you wish to proceed? -card-templates-restored-to-default = Notetype has been restored to its original state. +card-templates-restored-to-default = Note type has been restored to its original state. diff --git a/ftl/core/change-notetype.ftl b/ftl/core/change-notetype.ftl index 419030389..f21abd3d2 100644 --- a/ftl/core/change-notetype.ftl +++ b/ftl/core/change-notetype.ftl @@ -8,7 +8,7 @@ change-notetype-will-discard-cards = Will remove the following cards: change-notetype-fields = Fields change-notetype-templates = Templates change-notetype-to-from-cloze = - When changing to or from a Cloze notetype, card numbers remain unchanged. + When changing to or from a Cloze note type, card numbers remain unchanged. - If changing to a regular notetype, and there are more cloze deletions + If changing to a regular note type, and there are more cloze deletions than available card templates, any extra cards will be removed. diff --git a/ftl/core/database-check.ftl b/ftl/core/database-check.ftl index 67d6812e7..8a9e4e178 100644 --- a/ftl/core/database-check.ftl +++ b/ftl/core/database-check.ftl @@ -51,7 +51,7 @@ database-check-fixed-invalid-ids = *[other] Fixed { $count } objects with timestamps in the future. } # "db-check" is always in English -database-check-notetypes-recovered = One or more notetypes were missing. The notes that used them have been given new notetypes starting with "db-check", but field names and card design have been lost, so you may be better off restoring from an automatic backup. +database-check-notetypes-recovered = One or more note types were missing. The notes that used them have been given new note types starting with "db-check", but field names and card design have been lost, so you may be better off restoring from an automatic backup. ## Progress info diff --git a/ftl/core/deck-config.ftl b/ftl/core/deck-config.ftl index 1d202cb82..15e87d776 100644 --- a/ftl/core/deck-config.ftl +++ b/ftl/core/deck-config.ftl @@ -152,7 +152,7 @@ deck-config-new-gather-priority-tooltip-2 = `Random cards`: Gathers cards in a random order. deck-config-new-gather-priority-deck = Deck -deck-config-new-gather-priority-deck-then-random-notes = Deck then random notes +deck-config-new-gather-priority-deck-then-random-notes = Deck, then random notes deck-config-new-gather-priority-position-lowest-first = Ascending position deck-config-new-gather-priority-position-highest-first = Descending position deck-config-new-gather-priority-random-notes = Random notes @@ -207,9 +207,11 @@ deck-config-sort-order-ascending-intervals = Ascending intervals deck-config-sort-order-descending-intervals = Descending intervals deck-config-sort-order-ascending-ease = Ascending ease deck-config-sort-order-descending-ease = Descending ease -deck-config-sort-order-ascending-difficulty = Ascending difficulty -deck-config-sort-order-descending-difficulty = Descending difficulty -deck-config-sort-order-relative-overdueness = Relative overdueness +deck-config-sort-order-ascending-difficulty = Easy cards first +deck-config-sort-order-descending-difficulty = Difficult cards first +deck-config-sort-order-retrievability-ascending = Ascending retrievability +deck-config-sort-order-retrievability-descending = Descending retrievability + deck-config-display-order-will-use-current-deck = Anki will use the display order from the deck you select to study, and not any subdecks it may have. @@ -290,6 +292,7 @@ deck-config-easy-days-sunday = Sunday deck-config-easy-days-normal = Normal deck-config-easy-days-reduced = Reduced deck-config-easy-days-minimum = Minimum +deck-config-easy-days-no-normal-days = At least one day should be set to '{ deck-config-easy-days-normal }'. ## Adding/renaming @@ -312,7 +315,7 @@ deck-config-confirm-remove-name = Remove { $name }? deck-config-save-button = Save deck-config-save-to-all-subdecks = Save to All Subdecks deck-config-save-and-optimize = Optimize All Presets -deck-config-revert-button-tooltip = Restore this setting to its default value. +deck-config-revert-button-tooltip = Restore this setting to its default value? ## These strings are shown via the Description button at the bottom of the ## overview screen. @@ -370,9 +373,6 @@ deck-config-historical-retention = Historical retention deck-config-smaller-is-better = Smaller numbers indicate a better fit to your review history. deck-config-steps-too-large-for-fsrs = When FSRS is enabled, steps of 1 day or more are not recommended. deck-config-get-params = Get Params -deck-config-fsrs-on-all-clients = - Please ensure all of your Anki clients are Anki(Mobile) 23.10+ or AnkiDroid 2.17+. FSRS will - not work correctly if one of your clients is older. deck-config-predicted-minimum-recommended-retention = Minimum recommended retention: { $num } deck-config-complete = { $num }% complete. deck-config-iterations = Iteration: { $count }... @@ -435,8 +435,8 @@ deck-config-compute-optimal-weights-tooltip2 = deck-config-compute-optimal-retention-tooltip4 = This tool will attempt to find the desired retention value that will lead to the most material learnt, in the least amount of time. The calculated number can serve as a reference - when deciding what to set your desired retention to. You may wish to choose a higher desired retention, if you’re - willing to trade more study time for a greater retention rate. Setting your desired retention lower than the minimum + when deciding what to set your desired retention to. You may wish to choose a higher desired retention if you’re + willing to invest more study time to achieve it. Setting your desired retention lower than the minimum is not recommended, as it will lead to a higher workload, because of the high forgetting rate. deck-config-please-save-your-changes-first = Please save your changes first. deck-config-a-100-day-interval = @@ -452,6 +452,7 @@ deck-config-percent-of-reviews = deck-config-optimizing-preset = Optimizing preset { $current_count }/{ $total_count }... deck-config-fsrs-must-be-enabled = FSRS must be enabled first. deck-config-fsrs-params-optimal = The FSRS parameters currently appear to be optimal. +deck-config-fsrs-params-no-reviews = No reviews found. Please check that this preset is assigned to all decks you want to optimize (including subdecks) and try again. deck-config-wait-for-audio = Wait for audio deck-config-show-reminder = Show Reminder @@ -523,3 +524,6 @@ deck-config-compute-optimal-retention-tooltip3 = lead to a higher workload, because of the high forgetting rate. deck-config-seconds-to-show-question-tooltip-2 = When auto advance is activated, the number of seconds to wait before revealing the answer. Set to 0 to disable. deck-config-invalid-weights = Parameters must be either left blank to use the defaults, or must be 17 comma-separated numbers. +deck-config-fsrs-on-all-clients = + Please ensure all of your Anki clients are Anki(Mobile) 23.10+ or AnkiDroid 2.17+. FSRS will + not work correctly if one of your clients is older. diff --git a/ftl/core/decks.ftl b/ftl/core/decks.ftl index e4369884b..48ecdb569 100644 --- a/ftl/core/decks.ftl +++ b/ftl/core/decks.ftl @@ -24,7 +24,6 @@ decks-order-added = Order added decks-order-due = Order due decks-please-select-something = Please select something. decks-random = Random -decks-relative-overdueness = Relative overdueness decks-repeat-failed-cards-after = Delay Repeat failed cards after # e.g. "Delay for Again", "Delay for Hard", "Delay for Good" decks-delay-for-button = Delay for { $button } @@ -37,3 +36,8 @@ decks-learn-header = Learn # The count of cards waiting to be reviewed decks-review-header = Due decks-zero-minutes-hint = (0 = return card to original deck) + +## These strings are no longer used - you do not need to translate them if they +## are not already translated. + +decks-relative-overdueness = Relative overdueness diff --git a/ftl/core/errors.ftl b/ftl/core/errors.ftl index c3f5a3b18..65ee5365b 100644 --- a/ftl/core/errors.ftl +++ b/ftl/core/errors.ftl @@ -5,7 +5,7 @@ errors-100-tags-max = A maximum of 100 tags can be selected. Listing the tags you want instead of the ones you don't want is usually simpler, and there is no need to select child tags if you have selected a parent tag. -errors-multiple-notetypes-selected = Please select notes from only one notetype. +errors-multiple-notetypes-selected = Please select notes from only one note type. errors-please-check-database = Please use the Check Database action, then try again. errors-please-check-media = Please use the Check Media action, then try again. errors-collection-too-new = This collection requires a newer version of Anki to open. diff --git a/ftl/core/exporting.ftl b/ftl/core/exporting.ftl index be612f7da..422a580e4 100644 --- a/ftl/core/exporting.ftl +++ b/ftl/core/exporting.ftl @@ -40,5 +40,5 @@ exporting-processed-media-files = *[other] Processed { $count } media files... } exporting-include-deck = Include deck name -exporting-include-notetype = Include notetype name +exporting-include-notetype = Include note type name exporting-include-guid = Include unique identifier diff --git a/ftl/core/importing.ftl b/ftl/core/importing.ftl index 0987818e1..bf3162809 100644 --- a/ftl/core/importing.ftl +++ b/ftl/core/importing.ftl @@ -8,7 +8,7 @@ importing-anki2-files-are-not-directly-importable = .anki2 files are not directl importing-appeared-twice-in-file = Appeared twice in file: { $val } importing-by-default-anki-will-detect-the = By default, Anki will detect the character between fields, such as a tab, comma, and so on. If Anki is detecting the character incorrectly, you can enter it here. Use \t to represent tab. importing-cannot-merge-notetypes-of-different-kinds = - Cloze notetypes cannot be merged with regular notetypes. + Cloze note types cannot be merged with regular note types. You may still import the file with '{ importing-merge-notetypes }' disabled. importing-change = Change importing-colon = Colon @@ -33,13 +33,13 @@ importing-map-to = Map to { $val } importing-map-to-tags = Map to Tags importing-mapped-to = mapped to { $val } importing-mapped-to-tags = mapped to Tags -# the action of combining two existing notetypes to create a new one -importing-merge-notetypes = Merge notetypes +# the action of combining two existing note types to create a new one +importing-merge-notetypes = Merge note types importing-merge-notetypes-help = - If checked, and you or the deck author altered the schema of a notetype, Anki will + If checked, and you or the deck author altered the schema of a note type, Anki will merge the two versions instead of keeping both. - Altering a notetype's schema means adding, removing, or reordering fields or templates, + Altering a note type's schema means adding, removing, or reordering fields or templates, or changing the sort field. As a counterexample, changing the front side of an existing template does *not* constitute a schema change. @@ -50,7 +50,7 @@ importing-multicharacter-separators-are-not-supported-please = Multi-character s importing-notes-added-from-file = Notes added from file: { $val } importing-notes-found-in-file = Notes found in file: { $val } importing-notes-skipped-as-theyre-already-in = Notes skipped, as up-to-date copies are already in your collection: { $val } -importing-notes-skipped-update-due-to-notetype = Notes not updated, as notetype has been modified since you first imported the notes: { $val } +importing-notes-skipped-update-due-to-notetype = Notes not updated, as note type has been modified since you first imported the notes: { $val } importing-notes-updated-as-file-had-newer = Notes updated, as file had newer version: { $val } importing-include-reviews = Include reviews importing-also-import-progress = Import any learning progress @@ -90,10 +90,10 @@ importing-update-notes = Update notes importing-update-notes-help = When to update an existing note in your collection. By default, this is only done if the matching imported note was more recently modified. -importing-update-notetypes = Update notetypes +importing-update-notetypes = Update note types importing-update-notetypes-help = - When to update an existing notetype in your collection. By default, this is only done - if the matching imported notetype was more recently modified. Changes to template text + When to update an existing note type in your collection. By default, this is only done + if the matching imported note type was more recently modified. Changes to template text and styling can always be imported, but for schema changes (e.g. the number or order of fields has changed), the '{ importing-merge-notetypes }' option will also need to be enabled. importing-note-added = @@ -148,7 +148,7 @@ importing-file = File # "Match scope: notetype / notetype and deck". Controls how duplicates are matched. importing-match-scope = Match scope # Used with the 'match scope' option -importing-notetype-and-deck = Notetype and deck +importing-notetype-and-deck = Note type and deck importing-cards-added = { $count -> [one] { $count } card added. @@ -182,8 +182,8 @@ importing-conflicting-notes-skipped = } importing-conflicting-notes-skipped2 = { $count -> - [one] { $count } note was not imported, because its notetype has changed, and '{ importing-merge-notetypes }' was not enabled. - *[other] { $count } notes were not imported, because their notetype has changed, and '{ importing-merge-notetypes }' was not enabled. + [one] { $count } note was not imported, because its note type has changed, and '{ importing-merge-notetypes }' was not enabled. + *[other] { $count } notes were not imported, because their note type has changed, and '{ importing-merge-notetypes }' was not enabled. } importing-import-log = Import Log importing-no-notes-in-file = No notes found in file. @@ -198,8 +198,8 @@ importing-status = Status importing-duplicate-note-added = Duplicate note added importing-added-new-note = New note added importing-existing-note-skipped = Note skipped, as an up-to-date copy is already in your collection -importing-note-skipped-update-due-to-notetype = Note not updated, as notetype has been modified since you first imported the note -importing-note-skipped-update-due-to-notetype2 = Note not updated, as notetype has been modified since you first imported the note, and '{ importing-merge-notetypes }' was not enabled +importing-note-skipped-update-due-to-notetype = Note not updated, as note type has been modified since you first imported the note +importing-note-skipped-update-due-to-notetype2 = Note not updated, as note type has been modified since you first imported the note, and '{ importing-merge-notetypes }' was not enabled importing-note-updated-as-file-had-newer = Note updated, as file had newer version importing-note-skipped-due-to-missing-notetype = Note skipped, as its notetype was missing importing-note-skipped-due-to-missing-deck = Note skipped, as its deck was missing @@ -216,10 +216,10 @@ importing-allow-html-in-fields-help = '<br>', it will appear as a line break on your card. On the other hand, with this option disabled, the literal characters '<br>' will be rendered. importing-notetype-help = - Newly-imported notes will have this notetype, and only existing notes with this - notetype will be updated. + Newly-imported notes will have this note type, and only existing notes with this + note type will be updated. - You can choose which fields in the file correspond to which notetype fields with the + You can choose which fields in the file correspond to which note type fields with the mapping tool. importing-deck-help = Imported cards will be placed in this deck. importing-existing-notes-help = @@ -229,7 +229,7 @@ importing-existing-notes-help = - `{ importing-preserve }`: Do nothing. - `{ importing-duplicate }`: Create a new note. importing-match-scope-help = - Only existing notes with the same notetype will be checked for duplicates. This can + Only existing notes with the same note type will be checked for duplicates. This can additionally be restricted to notes with cards in the same deck. importing-tag-all-notes-help = These tags will be added to both newly-imported and updated notes. diff --git a/ftl/core/notetypes.ftl b/ftl/core/notetypes.ftl index 665b9f409..f7eccd08b 100644 --- a/ftl/core/notetypes.ftl +++ b/ftl/core/notetypes.ftl @@ -1,4 +1,4 @@ -notetypes-notetype = Notetype +notetypes-notetype = Note Type ## Default field names in newly created note types diff --git a/ftl/core/statistics.ftl b/ftl/core/statistics.ftl index f84e5a13f..006db0cf5 100644 --- a/ftl/core/statistics.ftl +++ b/ftl/core/statistics.ftl @@ -48,6 +48,11 @@ statistics-cards = [one] { $cards } card *[other] { $cards } cards } +statistics-notes = + { $notes -> + [one] { $notes } note + *[other] { $notes } notes + } # a count of how many cards have been answered, eg "Total: 34 reviews" statistics-reviews = { $reviews -> @@ -220,6 +225,7 @@ statistics-average-answer-time-label = Average answer time statistics-average = Average statistics-average-interval = Average interval statistics-due-tomorrow = Due tomorrow +statistics-daily-load = Daily load # eg 5 of 15 (33.3%) statistics-amount-of-total-with-percentage = { $amount } of { $total } ({ $percent }%) statistics-average-over-period = Average over period diff --git a/ftl/qt-repo b/ftl/qt-repo index 2c629a554..e0f9724f7 160000 --- a/ftl/qt-repo +++ b/ftl/qt-repo @@ -1 +1 @@ -Subproject commit 2c629a5543be76a0136bffb428868d946b4c0e3a +Subproject commit e0f9724f75f6248f4e74558b25c3182d4f348bce diff --git a/package.json b/package.json index 2a70df9aa..9cef4a805 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "@poppanator/sveltekit-svg": "^5.0.0-svelte5.5", "@sqltools/formatter": "^1.2.2", "@sveltejs/adapter-static": "^3.0.0", - "@sveltejs/kit": "^2.4.1", - "@sveltejs/vite-plugin-svelte": "4.0.0-next.7", + "@sveltejs/kit": "^2.8.3", + "@sveltejs/vite-plugin-svelte": "4.0.0", "@types/bootstrap": "^5.0.12", "@types/codemirror": "^5.60.0", "@types/d3": "^7.0.0", @@ -47,7 +47,8 @@ "license-checker-rseidelsohn": "=4.3.0", "prettier": "^2.4.1", "prettier-plugin-svelte": "^3.2.6", - "svelte": "5.0.0-next.179", + "sass": "<1.77", + "svelte": "5.0.0", "svelte-check": "^3.4.4", "svelte-preprocess": "^5.0.4", "svelte-preprocess-esbuild": "^3.0.1", @@ -55,9 +56,8 @@ "tslib": "^2.0.3", "tsx": "^3.12.0", "typescript": "^5.0.4", - "vite": "=5.4.7", - "vitest": "^1.2.1", - "sass": "<1.77" + "vite": "^5.4.10", + "vitest": "^2" }, "dependencies": { "@bufbuild/protobuf": "^1.2.1", @@ -81,7 +81,7 @@ }, "resolutions": { "canvas": "npm:empty-npm-package", - "vite": "=5.4.7" + "cookie": "0.7.0" }, "browserslist": [ "defaults", diff --git a/proto/anki/backend.proto b/proto/anki/backend.proto index 9a3cab781..36098ab8b 100644 --- a/proto/anki/backend.proto +++ b/proto/anki/backend.proto @@ -30,6 +30,7 @@ message BackendError { DB_ERROR = 5; NETWORK_ERROR = 6; SYNC_AUTH_ERROR = 7; + SYNC_SERVER_MESSAGE = 23; SYNC_OTHER_ERROR = 8; JSON_ERROR = 9; PROTO_ERROR = 10; diff --git a/proto/anki/collection.proto b/proto/anki/collection.proto index c08c36e27..fea70a787 100644 --- a/proto/anki/collection.proto +++ b/proto/anki/collection.proto @@ -127,21 +127,21 @@ message Progress { DatabaseCheck database_check = 6; string importing = 7; string exporting = 8; - ComputeWeightsProgress compute_weights = 9; + ComputeParamsProgress compute_params = 9; ComputeRetentionProgress compute_retention = 10; ComputeMemoryProgress compute_memory = 11; } } -message ComputeWeightsProgress { +message ComputeParamsProgress { // Current iteration uint32 current = 1; // Total iterations uint32 total = 2; uint32 reviews = 3; - // Only used in 'compute all weights' case + // Only used in 'compute all params' case uint32 current_preset = 4; - // Only used in 'compute all weights' case + // Only used in 'compute all params' case uint32 total_presets = 5; } diff --git a/proto/anki/config.proto b/proto/anki/config.proto index 9924ab90f..d61f139d6 100644 --- a/proto/anki/config.proto +++ b/proto/anki/config.proto @@ -55,6 +55,7 @@ message ConfigKey { SHIFT_POSITION_OF_EXISTING_CARDS = 24; RENDER_LATEX = 25; LOAD_BALANCER_ENABLED = 26; + FSRS_SHORT_TERM_WITH_STEPS_ENABLED = 27; } enum String { SET_DUE_BROWSER = 0; @@ -117,6 +118,7 @@ message Preferences { bool show_intervals_on_buttons = 4; uint32 time_limit_secs = 5; bool load_balancer_enabled = 6; + bool fsrs_short_term_with_steps_enabled = 7; } message Editing { bool adding_defaults_to_current_deck = 1; diff --git a/proto/anki/deck_config.proto b/proto/anki/deck_config.proto index 179e98b3e..efd8a80d0 100644 --- a/proto/anki/deck_config.proto +++ b/proto/anki/deck_config.proto @@ -79,7 +79,8 @@ message DeckConfig { REVIEW_CARD_ORDER_INTERVALS_DESCENDING = 4; REVIEW_CARD_ORDER_EASE_ASCENDING = 5; REVIEW_CARD_ORDER_EASE_DESCENDING = 6; - REVIEW_CARD_ORDER_RELATIVE_OVERDUENESS = 7; + REVIEW_CARD_ORDER_RETRIEVABILITY_ASCENDING = 7; + REVIEW_CARD_ORDER_RETRIEVABILITY_DESCENDING = 11; REVIEW_CARD_ORDER_RANDOM = 8; REVIEW_CARD_ORDER_ADDED = 9; REVIEW_CARD_ORDER_REVERSE_ADDED = 10; @@ -107,9 +108,11 @@ message DeckConfig { repeated float learn_steps = 1; repeated float relearn_steps = 2; - repeated float fsrs_weights = 3; + repeated float fsrs_params_4 = 3; + repeated float fsrs_params_5 = 5; - reserved 5 to 8; + // consider saving remaining ones for fsrs param changes + reserved 6 to 8; uint32 new_per_day = 9; uint32 reviews_per_day = 10; @@ -163,7 +166,7 @@ message DeckConfig { // used for fsrs_reschedule in the past reserved 39; float historical_retention = 40; - string weight_search = 45; + string param_search = 45; bytes other = 255; } @@ -213,7 +216,7 @@ message DeckConfigsForUpdate { enum UpdateDeckConfigsMode { UPDATE_DECK_CONFIGS_MODE_NORMAL = 0; UPDATE_DECK_CONFIGS_MODE_APPLY_TO_CHILDREN = 1; - UPDATE_DECK_CONFIGS_MODE_COMPUTE_ALL_WEIGHTS = 2; + UPDATE_DECK_CONFIGS_MODE_COMPUTE_ALL_PARAMS = 2; } message UpdateDeckConfigsRequest { diff --git a/proto/anki/decks.proto b/proto/anki/decks.proto index c91abb39a..bcd206b06 100644 --- a/proto/anki/decks.proto +++ b/proto/anki/decks.proto @@ -97,7 +97,8 @@ message Deck { ADDED = 5; DUE = 6; REVERSE_ADDED = 7; - DUE_PRIORITY = 8; + RETRIEVABILITY_ASCENDING = 8; + RETRIEVABILITY_DESCENDING = 9; } string search = 1; diff --git a/proto/anki/links.proto b/proto/anki/links.proto index dc3bd0552..6dd9ad8c6 100644 --- a/proto/anki/links.proto +++ b/proto/anki/links.proto @@ -40,9 +40,8 @@ message HelpPageLinkRequest { CARD_TYPE_DUPLICATE = 18; CARD_TYPE_NO_FRONT_FIELD = 19; CARD_TYPE_MISSING_CLOZE = 20; - CARD_TYPE_EXTRANEOUS_CLOZE = 21; + TROUBLESHOOTING = 21; CARD_TYPE_TEMPLATE_ERROR = 22; - TROUBLESHOOTING = 23; } HelpPage page = 1; } diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index 1bacd0d1f..947a7812c 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -45,15 +45,15 @@ service SchedulerService { rpc CustomStudyDefaults(CustomStudyDefaultsRequest) returns (CustomStudyDefaultsResponse); rpc RepositionDefaults(generic.Empty) returns (RepositionDefaultsResponse); - rpc ComputeFsrsWeights(ComputeFsrsWeightsRequest) - returns (ComputeFsrsWeightsResponse); + rpc ComputeFsrsParams(ComputeFsrsParamsRequest) + returns (ComputeFsrsParamsResponse); rpc GetOptimalRetentionParameters(GetOptimalRetentionParametersRequest) returns (GetOptimalRetentionParametersResponse); rpc ComputeOptimalRetention(ComputeOptimalRetentionRequest) returns (ComputeOptimalRetentionResponse); rpc SimulateFsrsReview(SimulateFsrsReviewRequest) returns (SimulateFsrsReviewResponse); - rpc EvaluateWeights(EvaluateWeightsRequest) returns (EvaluateWeightsResponse); + rpc EvaluateParams(EvaluateParamsRequest) returns (EvaluateParamsResponse); rpc ComputeMemoryState(cards.CardId) returns (ComputeMemoryStateResponse); // The number of days the calculated interval was fuzzed by on the previous // review (if any). Utilized by the FSRS add-on. @@ -63,10 +63,12 @@ service SchedulerService { // Implicitly includes any of the above methods that are not listed in the // backend service. service BackendSchedulerService { - rpc ComputeFsrsWeightsFromItems(ComputeFsrsWeightsFromItemsRequest) - returns (ComputeFsrsWeightsResponse); + rpc ComputeFsrsParamsFromItems(ComputeFsrsParamsFromItemsRequest) + returns (ComputeFsrsParamsResponse); // Generates parameters used for FSRS's scheduler benchmarks. rpc FsrsBenchmark(FsrsBenchmarkRequest) returns (FsrsBenchmarkResponse); + // Used for exporting revlogs for algorithm research. + rpc ExportDataset(ExportDatasetRequest) returns (generic.Empty); } message SchedulingState { @@ -339,19 +341,19 @@ message RepositionDefaultsResponse { bool shift = 2; } -message ComputeFsrsWeightsRequest { +message ComputeFsrsParamsRequest { /// The search used to gather cards for training string search = 1; - repeated float current_weights = 2; + repeated float current_params = 2; int64 ignore_revlogs_before_ms = 3; } -message ComputeFsrsWeightsResponse { - repeated float weights = 1; +message ComputeFsrsParamsResponse { + repeated float params = 1; uint32 fsrs_items = 2; } -message ComputeFsrsWeightsFromItemsRequest { +message ComputeFsrsParamsFromItemsRequest { repeated FsrsItem items = 1; } @@ -360,7 +362,12 @@ message FsrsBenchmarkRequest { } message FsrsBenchmarkResponse { - repeated float weights = 1; + repeated float params = 1; +} + +message ExportDatasetRequest { + uint32 min_entries = 1; + string target_path = 2; } message FsrsItem { @@ -373,7 +380,7 @@ message FsrsReview { } message SimulateFsrsReviewRequest { - repeated float weights = 1; + repeated float params = 1; float desired_retention = 2; uint32 deck_size = 3; uint32 days_to_simulate = 4; @@ -391,7 +398,7 @@ message SimulateFsrsReviewResponse { } message ComputeOptimalRetentionRequest { - repeated float weights = 1; + repeated float params = 1; uint32 days_to_simulate = 2; uint32 max_interval = 3; string search = 4; @@ -424,13 +431,13 @@ message GetOptimalRetentionParametersResponse { uint32 review_limit = 15; } -message EvaluateWeightsRequest { - repeated float weights = 1; +message EvaluateParamsRequest { + repeated float params = 1; string search = 2; int64 ignore_revlogs_before_ms = 3; } -message EvaluateWeightsResponse { +message EvaluateParamsResponse { float log_loss = 1; float rmse_bins = 2; } diff --git a/proto/anki/stats.proto b/proto/anki/stats.proto index 4ef811df9..42d02029c 100644 --- a/proto/anki/stats.proto +++ b/proto/anki/stats.proto @@ -64,6 +64,7 @@ message CardStatsResponse { string custom_data = 20; string preset = 21; optional string original_deck = 22; + optional float desired_retention = 23; } message GraphsRequest { @@ -85,11 +86,13 @@ message GraphsResponse { message Retrievability { map retrievability = 1; float average = 2; - float sum = 3; + float sum_by_card = 3; + float sum_by_note = 4; } message FutureDue { map future_due = 1; bool have_backlog = 2; + uint32 daily_load = 3; } message Today { uint32 answer_count = 1; @@ -205,6 +208,7 @@ message RevlogEntry { RELEARNING = 2; FILTERED = 3; MANUAL = 4; + RESCHEDULED = 5; } int64 id = 1; int64 cid = 2; @@ -217,7 +221,21 @@ message RevlogEntry { ReviewKind review_kind = 9; } -message RevlogEntries { - repeated RevlogEntry entries = 1; - int64 next_day_at = 2; +message CardEntry { + int64 id = 1; + int64 note_id = 2; + int64 deck_id = 3; +} + +message DeckEntry { + int64 id = 1; + int64 parent_id = 2; + int64 preset_id = 3; +} + +message Dataset { + repeated RevlogEntry revlogs = 1; + repeated CardEntry cards = 2; + repeated DeckEntry decks = 3; + int64 next_day_at = 4; } diff --git a/pylib/anki/_backend.py b/pylib/anki/_backend.py index c3ac2a114..a0d8f8949 100644 --- a/pylib/anki/_backend.py +++ b/pylib/anki/_backend.py @@ -149,8 +149,8 @@ class RustBackend(RustBackendGenerated): ) return self.format_timespan(seconds=seconds, context=context) - def compute_weights_from_items(self, items: Iterable[FsrsItem]) -> Sequence[float]: - return self.compute_fsrs_weights_from_items(items).weights + def compute_params_from_items(self, items: Iterable[FsrsItem]) -> Sequence[float]: + return self.compute_fsrs_params_from_items(items).params def benchmark(self, train_set: Iterable[FsrsItem]) -> Sequence[float]: return self.fsrs_benchmark(train_set=train_set) diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 27c01e0e1..43e443eef 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -420,6 +420,11 @@ class Collection(DeprecatedNamesMixin): def import_json_string(self, json: str) -> ImportLogWithChanges: return self._backend.import_json_string(json) + def export_dataset_for_research( + self, target_path: str, min_entries: int = 0 + ) -> None: + self._backend.export_dataset(min_entries=min_entries, target_path=target_path) + # Image Occlusion ########################################################################## @@ -987,6 +992,16 @@ class Collection(DeprecatedNamesMixin): fget=_get_enable_load_balancer, fset=_set_enable_load_balancer ) + def _get_enable_fsrs_short_term_with_steps(self) -> bool: + return self.get_config_bool(Config.Bool.FSRS_SHORT_TERM_WITH_STEPS_ENABLED) + + def _set_enable_fsrs_short_term_with_steps(self, value: bool) -> None: + self.set_config_bool(Config.Bool.FSRS_SHORT_TERM_WITH_STEPS_ENABLED, value) + + fsrs_short_term_with_steps_enabled = property( + fget=_get_enable_fsrs_short_term_with_steps, + fset=_set_enable_fsrs_short_term_with_steps, + ) # Stats ########################################################################## @@ -1113,7 +1128,7 @@ class Collection(DeprecatedNamesMixin): self._backend.abort_sync() def full_upload_or_download( - self, *, auth: SyncAuth, server_usn: int | None, upload: bool + self, *, auth: SyncAuth | None, server_usn: int | None, upload: bool ) -> None: self._backend.full_upload_or_download( sync_pb2.FullUploadOrDownloadRequest( diff --git a/pylib/anki/template.py b/pylib/anki/template.py index 2a145c9e5..3e993b9ea 100644 --- a/pylib/anki/template.py +++ b/pylib/anki/template.py @@ -43,10 +43,6 @@ from anki.models import NotetypeDict from anki.sound import AVTag, SoundOrVideoTag, TTSTag from anki.utils import to_json_bytes -CARD_BLANK_HELP = ( - "https://anki.tenderapp.com/kb/card-appearance/the-front-of-this-card-is-blank" -) - @dataclass class TemplateReplacement: diff --git a/python/requirements.bundle.txt b/python/requirements.bundle.txt index 5a473f286..a50f9c50d 100644 --- a/python/requirements.bundle.txt +++ b/python/requirements.bundle.txt @@ -386,13 +386,13 @@ urllib3==2.2.2 \ --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \ --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168 # via requests -waitress==3.0.0 \ - --hash=sha256:005da479b04134cdd9dd602d1ee7c49d79de0537610d653674cc6cbde222b8a1 \ - --hash=sha256:2a06f242f4ba0cc563444ca3d1998959447477363a2d7e9b8b4d75d35cfd1669 +waitress==3.0.1 \ + --hash=sha256:26cdbc593093a15119351690752c99adc13cbc6786d75f7b6341d1234a3730ac \ + --hash=sha256:ef0c1f020d9f12a515c4ec65c07920a702613afcad1dbfdc3bcec256b6c072b3 # via -r requirements.aqt.in -werkzeug==3.0.4 \ - --hash=sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c \ - --hash=sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306 +werkzeug==3.0.6 \ + --hash=sha256:1bc0c2310d2fbb07b1dd1105eba2f7af72f322e1e455f2f93c993bee8c8a5f17 \ + --hash=sha256:a8dd59d4de28ca70471a34cba79bed5f7ef2e036a76b3ab0835474246eb41f8d # via flask wheel==0.44.0 \ --hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \ diff --git a/python/requirements.dev.txt b/python/requirements.dev.txt index c441f99b1..ea6a57f3b 100644 --- a/python/requirements.dev.txt +++ b/python/requirements.dev.txt @@ -608,17 +608,17 @@ urllib3==2.2.2 \ # via # requests # types-requests -waitress==3.0.0 \ - --hash=sha256:005da479b04134cdd9dd602d1ee7c49d79de0537610d653674cc6cbde222b8a1 \ - --hash=sha256:2a06f242f4ba0cc563444ca3d1998959447477363a2d7e9b8b4d75d35cfd1669 +waitress==3.0.1 \ + --hash=sha256:26cdbc593093a15119351690752c99adc13cbc6786d75f7b6341d1234a3730ac \ + --hash=sha256:ef0c1f020d9f12a515c4ec65c07920a702613afcad1dbfdc3bcec256b6c072b3 # via -r requirements.aqt.in websocket-client==1.8.0 \ --hash=sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526 \ --hash=sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da # via pychromedevtools -werkzeug==3.0.4 \ - --hash=sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c \ - --hash=sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306 +werkzeug==3.0.6 \ + --hash=sha256:1bc0c2310d2fbb07b1dd1105eba2f7af72f322e1e455f2f93c993bee8c8a5f17 \ + --hash=sha256:a8dd59d4de28ca70471a34cba79bed5f7ef2e036a76b3ab0835474246eb41f8d # via flask wheel==0.44.0 \ --hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \ diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index 2c39253c3..eca04b261 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -3,6 +3,7 @@ from __future__ import annotations +import atexit import logging import sys from collections.abc import Callable @@ -525,6 +526,7 @@ def setupGL(pm: aqt.profiles.ProfileManager) -> None: print(f"Qt {category}: {msg} {context}") qInstallMessageHandler(msgHandler) + atexit.register(qInstallMessageHandler, None) if driver == VideoDriver.OpenGL: # Leaving QT_OPENGL unset appears to sometimes produce different results diff --git a/qt/aqt/about.py b/qt/aqt/about.py index cf0173592..bf1ff41f8 100644 --- a/qt/aqt/about.py +++ b/qt/aqt/about.py @@ -37,14 +37,21 @@ def show(mw: aqt.AnkiQt) -> QDialog: txt = supportText() if mw.addonManager.dirty: txt += "\n" + addon_debug_info() - QApplication.clipboard().setText(txt) + clipboard = QApplication.clipboard() + assert clipboard is not None + clipboard.setText(txt) tooltip(tr.about_copied_to_clipboard(), parent=dialog) btn = QPushButton(tr.about_copy_debug_info()) qconnect(btn.clicked, on_copy) abt.buttonBox.addButton(btn, QDialogButtonBox.ButtonRole.ActionRole) - abt.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setFocus() + + ok_button = abt.buttonBox.button(QDialogButtonBox.StandardButton.Ok) + assert ok_button is not None + ok_button.setFocus() + btnLayout = abt.buttonBox.layout() + assert btnLayout is not None btnLayout.setContentsMargins(12, 12, 12, 12) # WebView cleanup @@ -52,7 +59,7 @@ def show(mw: aqt.AnkiQt) -> QDialog: def on_dialog_destroyed() -> None: abt.label.cleanup() - abt.label = None + abt.label = None # type: ignore qconnect(dialog.destroyed, on_dialog_destroyed) diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index f99829fd0..a447b95c1 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -152,10 +152,13 @@ class AddCards(QMainWindow): def on_deck_changed(self, deck_id: int) -> None: gui_hooks.add_cards_did_change_deck(deck_id) - def on_notetype_change(self, notetype_id: NotetypeId) -> None: + def on_notetype_change( + self, notetype_id: NotetypeId, update_deck: bool = True + ) -> None: # need to adjust current deck? - if deck_id := self.col.default_deck_for_notetype(notetype_id): - self.deck_chooser.selected_deck_id = deck_id + if update_deck: + if deck_id := self.col.default_deck_for_notetype(notetype_id): + self.deck_chooser.selected_deck_id = deck_id # only used for detecting changed sticky fields on close self._last_added_note = None @@ -224,7 +227,8 @@ class AddCards(QMainWindow): self.col.defaults_for_adding( current_review_card=self.mw.reviewer.card ).notetype_id - ) + ), + update_deck=False, ) def _new_note(self) -> Note: diff --git a/qt/aqt/changenotetype.py b/qt/aqt/changenotetype.py index 0484c2ff8..3ab9a87e8 100644 --- a/qt/aqt/changenotetype.py +++ b/qt/aqt/changenotetype.py @@ -63,7 +63,7 @@ class ChangeNotetypeDialog(QDialog): def reject(self) -> None: self.web.cleanup() - self.web = None + self.web = None # type: ignore saveGeom(self, self.TITLE) QDialog.reject(self) diff --git a/qt/aqt/clayout.py b/qt/aqt/clayout.py index bdd7f02d3..8f467e379 100644 --- a/qt/aqt/clayout.py +++ b/qt/aqt/clayout.py @@ -61,7 +61,9 @@ class CardLayout(QDialog): self.ord = ord self.col = self.mw.col.weakref() self.mm = self.mw.col.models - self.model = note.note_type() + note_type = note.note_type() + assert note_type is not None + self.model = note_type self.templates = self.model["tmpls"] self.fill_empty_action_toggled = fill_empty self.night_mode_is_enabled = theme_manager.night_mode @@ -404,6 +406,7 @@ class CardLayout(QDialog): m = QMenu(self) a = m.addAction(tr.card_templates_fill_empty()) + assert a is not None a.setCheckable(True) a.setChecked(self.fill_empty_action_toggled) qconnect(a.triggered, self.on_fill_empty_action_toggled) @@ -411,11 +414,13 @@ class CardLayout(QDialog): a.setVisible(False) a = m.addAction(tr.card_templates_night_mode()) + assert a is not None a.setCheckable(True) a.setChecked(self.night_mode_is_enabled) qconnect(a.triggered, self.on_night_mode_action_toggled) a = m.addAction(tr.card_templates_add_mobile_class()) + assert a is not None a.setCheckable(True) a.setChecked(self.mobile_emulation_enabled) qconnect(a.toggled, self.on_mobile_class_action_toggled) @@ -754,6 +759,7 @@ class CardLayout(QDialog): a = m.addAction( tr.actions_with_ellipsis(action=tr.card_templates_restore_to_default()) ) + assert a is not None qconnect( a.triggered, lambda: self.on_restore_to_default(), # pylint: disable=unnecessary-lambda @@ -761,15 +767,19 @@ class CardLayout(QDialog): if not self._isCloze(): a = m.addAction(tr.card_templates_add_card_type()) + assert a is not None qconnect(a.triggered, self.onAddCard) a = m.addAction(tr.card_templates_remove_card_type()) + assert a is not None qconnect(a.triggered, self.onRemove) a = m.addAction(tr.card_templates_rename_card_type()) + assert a is not None qconnect(a.triggered, self.onRename) a = m.addAction(tr.card_templates_reposition_card_type()) + assert a is not None qconnect(a.triggered, self.onReorder) m.addSeparator() @@ -780,9 +790,11 @@ class CardLayout(QDialog): else: s = tr.card_templates_off() a = m.addAction(tr.card_templates_deck_override() + s) + assert a is not None qconnect(a.triggered, self.onTargetDeck) a = m.addAction(tr.card_templates_browser_appearance()) + assert a is not None qconnect(a.triggered, self.onBrowserDisplay) m.popup(self.topAreaForm.templateOptions.mapToGlobal(QPoint(0, 0))) @@ -834,7 +846,9 @@ class CardLayout(QDialog): te.setCol(self.col) l.addWidget(te) if t["did"]: - te.setText(self.col.decks.get(t["did"])["name"]) + deck = self.col.decks.get(t["did"]) + assert deck is not None + te.setText(deck["name"]) te.selectAll() bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Close) qconnect(bb.rejected, d.close) @@ -927,10 +941,10 @@ class CardLayout(QDialog): saveGeom(self, "CardLayout") saveSplitter(self.mainArea, "CardLayoutMainArea") self.preview_web.cleanup() - self.preview_web = None - self.model = None - self.rendered_card = None - self.mw = None + self.preview_web = None # type: ignore + self.model = None # type: ignore + self.rendered_card = None # type: ignore + self.mw = None # type: ignore def onHelp(self) -> None: openHelp(HelpPage.TEMPLATES) diff --git a/qt/aqt/customstudy.py b/qt/aqt/customstudy.py index 730f3843b..eb8921627 100644 --- a/qt/aqt/customstudy.py +++ b/qt/aqt/customstudy.py @@ -144,7 +144,11 @@ class CustomStudy(QDialog): form.spin.setValue(current_spinner_value) form.preSpin.setText(text_before_spinner) form.postSpin.setText(text_after_spinner) - form.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(ok) + + ok_button = form.buttonBox.button(QDialogButtonBox.StandardButton.Ok) + assert ok_button is not None + ok_button.setText(ok) + self.radioIdx = idx def accept(self) -> None: diff --git a/qt/aqt/debug_console.py b/qt/aqt/debug_console.py index 76fead38b..d709b25e2 100644 --- a/qt/aqt/debug_console.py +++ b/qt/aqt/debug_console.py @@ -72,8 +72,10 @@ class DebugConsole(QDialog): qconnect(self._script.currentIndexChanged, self._on_script_change) def _setup_text_edits(self): - font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont) - font.setPointSize(self._text.font().pointSize() + 1) + font = QFont("Consolas") + if not font.exactMatch(): + font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont) + font.setPointSize(self._text.font().pointSize()) self._text.setFont(font) self._log.setFont(font) @@ -196,6 +198,7 @@ class DebugConsole(QDialog): def _on_context_menu(self, text_edit: QPlainTextEdit) -> None: menu = text_edit.createStandardContextMenu() + assert menu is not None menu.addSeparator() for action in self._actions(): entry = menu.addAction(action.name) @@ -227,7 +230,7 @@ class DebugConsole(QDialog): sys.stderr = self._oldStderr sys.stdout = self._oldStdout - def _card_repr(self, card: anki.cards.Card) -> None: + def _card_repr(self, card: anki.cards.Card | None) -> None: import copy import pprint @@ -316,6 +319,7 @@ class DebugConsole(QDialog): ) self._log.appendPlainText(to_append) slider = self._log.verticalScrollBar() + assert slider is not None slider.setValue(slider.maximum()) self._log.ensureCursorVisible() diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py index a9e450871..a3969cedf 100644 --- a/qt/aqt/deckbrowser.py +++ b/qt/aqt/deckbrowser.py @@ -309,12 +309,16 @@ class DeckBrowser: def _showOptions(self, did: str) -> None: m = QMenu(self.mw) a = m.addAction(tr.actions_rename()) + assert a is not None qconnect(a.triggered, lambda b, did=did: self._rename(DeckId(int(did)))) a = m.addAction(tr.actions_options()) + assert a is not None qconnect(a.triggered, lambda b, did=did: self._options(DeckId(int(did)))) a = m.addAction(tr.actions_export()) + assert a is not None qconnect(a.triggered, lambda b, did=did: self._export(DeckId(int(did)))) a = m.addAction(tr.actions_delete()) + assert a is not None qconnect(a.triggered, lambda b, did=did: self._delete(DeckId(int(did)))) gui_hooks.deck_browser_will_show_options_menu(m, int(did)) m.popup(QCursor.pos()) @@ -357,9 +361,9 @@ class DeckBrowser: ).run_in_background() def _delete(self, did: DeckId) -> None: - deck_name = self.mw.col.decks.find_deck_in_tree( - self._render_data.tree, did - ).name + deck = self.mw.col.decks.find_deck_in_tree(self._render_data.tree, did) + assert deck is not None + deck_name = deck.name remove_decks( parent=self.mw, deck_ids=[did], deck_name=deck_name ).run_in_background() diff --git a/qt/aqt/deckchooser.py b/qt/aqt/deckchooser.py index 72894ed82..c25fcad1e 100644 --- a/qt/aqt/deckchooser.py +++ b/qt/aqt/deckchooser.py @@ -99,7 +99,9 @@ class DeckChooser(QHBoxLayout): def callback(ret: StudyDeck) -> None: if not ret.name: return - new_selected_deck_id = self.mw.col.decks.by_name(ret.name)["id"] + deck = self.mw.col.decks.by_name(ret.name) + assert deck is not None + new_selected_deck_id = deck["id"] if self.selected_deck_id != new_selected_deck_id: self.selected_deck_id = new_selected_deck_id if func := self.on_deck_changed: diff --git a/qt/aqt/deckdescription.py b/qt/aqt/deckdescription.py index 01b41eaaf..261f52ea2 100644 --- a/qt/aqt/deckdescription.py +++ b/qt/aqt/deckdescription.py @@ -60,6 +60,7 @@ class DeckDescriptionDialog(QDialog): button_box = QDialogButtonBox() ok = button_box.addButton(QDialogButtonBox.StandardButton.Ok) + assert ok is not None qconnect(ok.clicked, self.save_and_accept) box.addWidget(button_box) diff --git a/qt/aqt/deckoptions.py b/qt/aqt/deckoptions.py index e4802f98a..9cc3d8e45 100644 --- a/qt/aqt/deckoptions.py +++ b/qt/aqt/deckoptions.py @@ -67,10 +67,10 @@ class DeckOptionsDialog(QDialog): elif cmd == "_close": self._close() - def closeEvent(self, evt: QCloseEvent) -> None: + def closeEvent(self, evt: QCloseEvent | None) -> None: if self._close_event_has_cleaned_up: - evt.accept() - return + return super().closeEvent(evt) + assert evt is not None evt.ignore() self.check_pending_changes() @@ -98,7 +98,7 @@ class DeckOptionsDialog(QDialog): def reject(self) -> None: self.mw.col.set_wants_abort() self.web.cleanup() - self.web = None + self.web = None # type: ignore saveGeom(self, self.TITLE) QDialog.reject(self) @@ -113,10 +113,14 @@ def confirm_deck_then_display_options(active_card: Card | None = None) -> None: decks = [aqt.mw.col.decks.current()] if card := active_card: if card.odid and card.odid != decks[0]["id"]: - decks.append(aqt.mw.col.decks.get(card.odid)) + deck = aqt.mw.col.decks.get(card.odid) + assert deck is not None + decks.append(deck) if not any(d["id"] == card.did for d in decks): - decks.append(aqt.mw.col.decks.get(card.did)) + deck = aqt.mw.col.decks.get(card.did) + assert deck is not None + decks.append(deck) if len(decks) == 1: display_options_for_deck(decks[0]) @@ -143,13 +147,16 @@ def _deck_prompt_dialog(decks: list[DeckDict]) -> None: def display_options_for_deck_id(deck_id: DeckId) -> None: - display_options_for_deck(aqt.mw.col.decks.get(deck_id)) + deck = aqt.mw.col.decks.get(deck_id) + assert deck is not None + display_options_for_deck(deck) def display_options_for_deck(deck: DeckDict) -> None: if not deck["dyn"]: if KeyboardModifiersPressed().shift or not aqt.mw.col.v3_scheduler(): deck_legacy = aqt.mw.col.decks.get(DeckId(deck["id"])) + assert deck_legacy is not None aqt.deckconf.DeckConf(aqt.mw, deck_legacy) else: DeckOptionsDialog(aqt.mw, deck) diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py index 9befcdeac..6f54245d2 100644 --- a/qt/aqt/editcurrent.py +++ b/qt/aqt/editcurrent.py @@ -28,10 +28,12 @@ class EditCurrent(QMainWindow): self, editor_mode=aqt.editor.EditorMode.EDIT_CURRENT, ) + assert self.mw.reviewer.card is not None self.editor.card = self.mw.reviewer.card self.editor.set_note(self.mw.reviewer.card.note(), focusTo=0) restoreGeom(self, "editcurrent") close_button = self.form.buttonBox.button(QDialogButtonBox.StandardButton.Close) + assert close_button is not None close_button.setShortcut(QKeySequence("Ctrl+Return")) # qt5.14+ doesn't handle numpad enter on Windows self.compat_add_shorcut = QShortcut(QKeySequence("Ctrl+Enter"), self) @@ -46,6 +48,7 @@ class EditCurrent(QMainWindow): # reload note note = self.editor.note try: + assert note is not None note.load() except NotFoundError: # note's been deleted @@ -65,7 +68,7 @@ class EditCurrent(QMainWindow): if card := self.mw.reviewer.card: self.editor.set_note(card.note()) - def closeEvent(self, evt: QCloseEvent) -> None: + def closeEvent(self, evt: QCloseEvent | None) -> None: self.editor.call_after_note_saved(self.cleanup) def _saveAndClose(self) -> None: diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index 4f64c5f2f..ff07de711 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -34,7 +34,7 @@ from anki.collection import Config, SearchNode from anki.consts import MODEL_CLOZE from anki.hooks import runFilter from anki.httpclient import HttpClient -from anki.models import NotetypeId, StockNotetype +from anki.models import NotetypeDict, NotetypeId, StockNotetype from anki.notes import Note, NoteFieldsCheckResult, NoteId from anki.utils import checksum, is_lin, is_mac, is_win, namedtmp from aqt import AnkiQt, colors, gui_hooks @@ -242,38 +242,35 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too rightside: bool = True, ) -> str: """Assign func to bridge cmd, register shortcut, return button""" - if func: - def wrapped_func(editor: Editor) -> None: - self.call_after_note_saved( - functools.partial(func, editor), keepFocus=True - ) + def wrapped_func(editor: Editor) -> None: + self.call_after_note_saved(functools.partial(func, editor), keepFocus=True) - self._links[cmd] = wrapped_func + self._links[cmd] = wrapped_func - if keys: + if keys: - def on_activated() -> None: - wrapped_func(self) + def on_activated() -> None: + wrapped_func(self) - if toggleable: - # generate a random id for triggering toggle - id = id or str(randrange(1_000_000)) + if toggleable: + # generate a random id for triggering toggle + id = id or str(randrange(1_000_000)) - def on_hotkey() -> None: - on_activated() - self.web.eval( - f'toggleEditorButton(document.getElementById("{id}"));' - ) + def on_hotkey() -> None: + on_activated() + self.web.eval( + f'toggleEditorButton(document.getElementById("{id}"));' + ) - else: - on_hotkey = on_activated + else: + on_hotkey = on_activated - QShortcut( # type: ignore - QKeySequence(keys), - self.widget, - activated=on_hotkey, - ) + QShortcut( # type: ignore + QKeySequence(keys), + self.widget, + activated=on_hotkey, + ) btn = self._addButton( icon, @@ -363,7 +360,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too def _onFields(self) -> None: from aqt.fields import FieldDialog - FieldDialog(self.mw, self.note.note_type(), parent=self.parentWindow) + FieldDialog(self.mw, self.note_type(), parent=self.parentWindow) def onCardLayout(self) -> None: self.call_after_note_saved(self._onCardLayout) @@ -375,6 +372,8 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too ord = self.card.ord else: ord = 0 + + assert self.note is not None CardLayout( self.mw, self.note, @@ -435,7 +434,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too gui_hooks.editor_did_focus_field(self.note, self.currentField) elif cmd.startswith("toggleStickyAll"): - model = self.note.note_type() + model = self.note_type() flds = model["flds"] any_sticky = any([fld["sticky"] for fld in flds]) @@ -456,7 +455,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too (type, num) = cmd.split(":", 1) ord = int(num) - model = self.note.note_type() + model = self.note_type() fld = model["flds"][ord] new_state = not fld["sticky"] fld["sticky"] = new_state @@ -469,10 +468,12 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too elif cmd.startswith("lastTextColor"): (_, textColor) = cmd.split(":", 1) + assert self.mw.pm.profile is not None self.mw.pm.profile["lastTextColor"] = textColor elif cmd.startswith("lastHighlightColor"): (_, highlightColor) = cmd.split(":", 1) + assert self.mw.pm.profile is not None self.mw.pm.profile["lastHighlightColor"] = highlightColor elif cmd.startswith("saveTags"): @@ -545,11 +546,12 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too for fld, val in self.note.items() ] - flds = self.note.note_type()["flds"] + note_type = self.note_type() + flds = note_type["flds"] collapsed = [fld["collapsed"] for fld in flds] plain_texts = [fld.get("plainText", False) for fld in flds] descriptions = [fld.get("description", "") for fld in flds] - notetype_meta = {"id": self.note.mid, "modTime": self.note.note_type()["mod"]} + notetype_meta = {"id": self.note.mid, "modTime": note_type["mod"]} self.widget.show() @@ -566,6 +568,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too self.web.setFocus() gui_hooks.editor_did_load_note(self) + assert self.mw.pm.profile is not None text_color = self.mw.pm.profile.get("lastTextColor", "#0000ff") highlight_color = self.mw.pm.profile.get("lastHighlightColor", "#0000ff") @@ -590,7 +593,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too """ if self.addMode: - sticky = [field["sticky"] for field in self.note.note_type()["flds"]] + sticky = [field["sticky"] for field in self.note_type()["flds"]] js += " setSticky(%s);" % json.dumps(sticky) if ( @@ -607,6 +610,9 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too def _save_current_note(self) -> None: "Call after note is updated with data from webview." + if not self.note: + return + update_note(parent=self.widget, note=self.note).run_in_background( initiator=self ) @@ -614,7 +620,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too def fonts(self) -> list[tuple[str, int, bool]]: return [ (gui_hooks.editor_will_use_font_for_field(f["font"]), f["size"], f["rtl"]) - for f in self.note.note_type()["flds"] + for f in self.note_type()["flds"] ] def call_after_note_saved( @@ -648,6 +654,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too checkValid = _check_and_update_duplicate_display_async def _update_duplicate_display(self, result: NoteFieldsCheckResult.V) -> None: + assert self.note is not None cols = [""] * len(self.note.fields) cloze_hint = "" if result == NoteFieldsCheckResult.DUPLICATE: @@ -665,13 +672,14 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too ) def showDupes(self) -> None: + assert self.note is not None aqt.dialogs.open( "Browser", self.mw, search=( SearchNode( dupe=SearchNode.Dupe( - notetype_id=self.note.note_type()["id"], + notetype_id=self.note_type()["id"], first_field=self.note.fields[0], ) ), @@ -681,7 +689,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too def fieldsAreBlank(self, previousNote: Note | None = None) -> bool: if not self.note: return True - m = self.note.note_type() + m = self.note_type() for c, f in enumerate(self.note.fields): f = f.replace("
", "").strip() notChangedvalues = {"", "
"} @@ -696,7 +704,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too # prevent any remaining evalWithCallback() events from firing after C++ object deleted if self.web: self.web.cleanup() - self.web = None + self.web = None # type: ignore # legacy @@ -729,9 +737,11 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too if self.tags.col != self.mw.col: self.tags.setCol(self.mw.col) if not self.tags.text() or not self.addMode: + assert self.note is not None self.tags.setText(self.note.string_tags().strip()) def on_tag_focus_lost(self) -> None: + assert self.note is not None self.note.tags = self.mw.col.tags.split(self.tags.text()) gui_hooks.editor_did_update_tags(self.note) if not self.addMode: @@ -826,7 +836,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too # Media downloads ###################################################################### - def urlToLink(self, url: str) -> str | None: + def urlToLink(self, url: str) -> str: fname = self.urlToFile(url) if not fname: return '{}'.format( @@ -1037,8 +1047,11 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too ###################################################################### def current_notetype_is_image_occlusion(self) -> bool: - return bool(self.note) and ( - self.note.note_type().get("originalStockKind", None) + if not self.note: + return False + + return ( + self.note_type().get("originalStockKind", None) == StockNotetype.OriginalStockKind.ORIGINAL_STOCK_KIND_IMAGE_OCCLUSION ) @@ -1049,6 +1062,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too image_path=image_path, notetype_id=0 ) else: + assert self.note is not None self.setup_mask_editor_for_existing_note( note_id=self.note.id, image_path=image_path ) @@ -1075,8 +1089,10 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too def select_image_from_clipboard_and_occlude(self) -> None: """Set up the mask editor for the image in the clipboard.""" - clipoard = self.mw.app.clipboard() - mime = clipoard.mimeData() + clipboard = self.mw.app.clipboard() + assert clipboard is not None + mime = clipboard.mimeData() + assert mime is not None if not mime.hasImage(): showWarning(tr.editing_no_image_found_on_clipboard()) return @@ -1160,6 +1176,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too @deprecated(info=_js_legacy) def _onHtmlEdit(self, field: int) -> None: + assert self.note is not None d = QDialog(self.widget, Qt.WindowType.Window) form = aqt.forms.edithtml.Ui_Dialog() form.setupUi(d) @@ -1223,7 +1240,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too @deprecated(info=_js_legacy) def _onCloze(self) -> None: # check that the model is set up for cloze deletion - if self.note.note_type()["type"] != MODEL_CLOZE: + if self.note_type()["type"] != MODEL_CLOZE: if self.addMode: tooltip(tr.editing_warning_cloze_deletions_will_not_work()) else: @@ -1231,7 +1248,8 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too return # find the highest existing cloze highest = 0 - for name, val in list(self.note.items()): + assert self.note is not None + for _, val in list(self.note.items()): m = re.findall(r"\{\{c(\d+)::", val) if m: highest = max(highest, sorted(int(x) for x in m)[-1]) @@ -1243,6 +1261,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too self.web.eval("wrap('{{c%d::', '}}');" % highest) def setupForegroundButton(self) -> None: + assert self.mw.pm.profile is not None self.fcolour = self.mw.pm.profile.get("lastColour", "#00f") # use last colour @@ -1276,6 +1295,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too @deprecated(info=_js_legacy) def onColourChanged(self) -> None: self._updateForegroundButton() + assert self.mw.pm.profile is not None self.mw.pm.profile["lastColour"] = self.fcolour @deprecated(info=_js_legacy) @@ -1300,6 +1320,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too (tr.editing_edit_html(), self.onHtmlEdit, "Ctrl+Shift+X"), ): a = m.addAction(text) + assert a is not None qconnect(a.triggered, handler) a.setShortcut(QKeySequence(shortcut)) @@ -1387,6 +1408,12 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too addImageForOcclusionFromClipboard=Editor.select_image_from_clipboard_and_occlude, ) + def note_type(self) -> NotetypeDict: + assert self.note is not None + note_type = self.note.note_type() + assert note_type is not None + return note_type + # Pasting, drag & drop, and keyboard layouts ###################################################################### @@ -1403,6 +1430,7 @@ class EditorWebView(AnkiWebView): self._internal_field_text_for_paste: str | None = None self._last_known_clipboard_mime: QMimeData | None = None clip = self.editor.mw.app.clipboard() + assert clip is not None clip.dataChanged.connect(self._on_clipboard_change) gui_hooks.editor_web_view_did_init(self) @@ -1410,23 +1438,28 @@ class EditorWebView(AnkiWebView): self._store_field_content_on_next_clipboard_change = True self._internal_field_text_for_paste = None - def _on_clipboard_change(self) -> None: - self._last_known_clipboard_mime = self.editor.mw.app.clipboard().mimeData() + def _on_clipboard_change( + self, mode: QClipboard.Mode = QClipboard.Mode.Clipboard + ) -> None: + self._last_known_clipboard_mime = self._clipboard().mimeData(mode) if self._store_field_content_on_next_clipboard_change: # if the flag was set, save the field data - self._internal_field_text_for_paste = self._get_clipboard_html_for_field() + self._internal_field_text_for_paste = self._get_clipboard_html_for_field( + mode + ) self._store_field_content_on_next_clipboard_change = False - elif ( - self._internal_field_text_for_paste != self._get_clipboard_html_for_field() + elif self._internal_field_text_for_paste != self._get_clipboard_html_for_field( + mode ): # if we've previously saved the field, blank it out if the clipboard state has changed self._internal_field_text_for_paste = None - def _get_clipboard_html_for_field(self): - clip = self.editor.mw.app.clipboard() - mime = clip.mimeData() + def _get_clipboard_html_for_field(self, mode: QClipboard.Mode) -> str | None: + clip = self._clipboard() + mime = clip.mimeData(mode) + assert mime is not None if not mime.hasHtml(): - return + return None return mime.html() def onCut(self) -> None: @@ -1440,6 +1473,7 @@ class EditorWebView(AnkiWebView): def _opened_context_menu_on_image(self) -> bool: context_menu_request = self.lastContextMenuRequest() + assert context_menu_request is not None return ( context_menu_request.mediaType() == context_menu_request.MediaType.MediaTypeImage @@ -1455,15 +1489,17 @@ class EditorWebView(AnkiWebView): def _onPaste(self, mode: QClipboard.Mode) -> None: # Since _on_clipboard_change doesn't always trigger properly on macOS, we do a double check if any changes were made before pasting - if self._last_known_clipboard_mime != self.editor.mw.app.clipboard().mimeData(): - self._on_clipboard_change() + clipboard = self._clipboard() + if self._last_known_clipboard_mime != clipboard.mimeData(mode): + self._on_clipboard_change(mode) extended = self._wantsExtendedPaste() if html := self._internal_field_text_for_paste: print("reuse internal") self.editor.doPaste(html, True, extended) else: print("use clipboard") - mime = self.editor.mw.app.clipboard().mimeData(mode=mode) + mime = clipboard.mimeData(mode=mode) + assert mime is not None html, internal = self._processMime(mime, extended) if html: self.editor.doPaste(html, internal, extended) @@ -1474,12 +1510,15 @@ class EditorWebView(AnkiWebView): def onMiddleClickPaste(self) -> None: self._onPaste(QClipboard.Mode.Selection) - def dragEnterEvent(self, evt: QDragEnterEvent) -> None: + def dragEnterEvent(self, evt: QDragEnterEvent | None) -> None: + assert evt is not None evt.accept() - def dropEvent(self, evt: QDropEvent) -> None: + def dropEvent(self, evt: QDropEvent | None) -> None: + assert evt is not None extended = self._wantsExtendedPaste() mime = evt.mimeData() + assert mime is not None cursor_pos = self.mapFromGlobal(QCursor.pos()) if evt.source() and mime.hasHtml(): @@ -1585,12 +1624,13 @@ class EditorWebView(AnkiWebView): return fname - def contextMenuEvent(self, evt: QContextMenuEvent) -> None: + def contextMenuEvent(self, evt: QContextMenuEvent | None) -> None: m = QMenu(self) if self.hasSelection(): self._add_cut_action(m) self._add_copy_action(m) a = m.addAction(tr.editing_paste()) + assert a is not None qconnect(a.triggered, self.onPaste) if self._opened_context_menu_on_image(): self._add_image_menu(m) @@ -1599,26 +1639,38 @@ class EditorWebView(AnkiWebView): def _add_cut_action(self, menu: QMenu) -> None: a = menu.addAction(tr.editing_cut()) + assert a is not None qconnect(a.triggered, self.onCut) def _add_copy_action(self, menu: QMenu) -> None: a = menu.addAction(tr.actions_copy()) + assert a is not None qconnect(a.triggered, self.onCopy) def _add_image_menu(self, menu: QMenu) -> None: a = menu.addAction(tr.editing_copy_image()) + assert a is not None qconnect(a.triggered, self.on_copy_image) - url = self.lastContextMenuRequest().mediaUrl() + context_menu_request = self.lastContextMenuRequest() + assert context_menu_request is not None + url = context_menu_request.mediaUrl() file_name = url.fileName() path = os.path.join(self.editor.mw.col.media.dir(), file_name) a = menu.addAction(tr.editing_open_image()) + assert a is not None qconnect(a.triggered, lambda: openFolder(path)) if is_win or is_mac: a = menu.addAction(tr.editing_show_in_folder()) + assert a is not None qconnect(a.triggered, lambda: show_in_folder(path)) + def _clipboard(self) -> QClipboard: + clipboard = self.editor.mw.app.clipboard() + assert clipboard is not None + return clipboard + # QFont returns "Kozuka Gothic Pro L" but WebEngine expects "Kozuka Gothic Pro Light" # - there may be other cases like a trailing 'Bold' that need fixing, but will @@ -1648,7 +1700,7 @@ gui_hooks.editor_will_munge_html.append(reverse_url_quoting) def set_cloze_button(editor: Editor) -> None: - action = "show" if editor.note.note_type()["type"] == MODEL_CLOZE else "hide" + action = "show" if editor.note_type()["type"] == MODEL_CLOZE else "hide" editor.web.eval( 'require("anki/ui").loaded.then(() =>' f'require("anki/NoteEditor").instances[0].toolbar.toolbar.{action}("cloze")' diff --git a/qt/aqt/emptycards.py b/qt/aqt/emptycards.py index eeab4a63b..78436239e 100644 --- a/qt/aqt/emptycards.py +++ b/qt/aqt/emptycards.py @@ -38,7 +38,7 @@ class EmptyCardsDialog(QDialog): def __init__(self, mw: aqt.main.AnkiQt, report: EmptyCardsReport) -> None: super().__init__(mw) - self.mw = mw.weakref() + self.mw = mw self.mw.garbage_collect_on_dialog_finish(self) self.report = report self.form = aqt.forms.emptycards.Ui_Dialog() @@ -63,7 +63,7 @@ class EmptyCardsDialog(QDialog): def on_finished(code: Any) -> None: self.form.webview.cleanup() - self.form.webview = None + self.form.webview = None # type: ignore saveGeom(self, "emptycards") qconnect(self.finished, on_finished) @@ -71,6 +71,7 @@ class EmptyCardsDialog(QDialog): self._delete_button = self.form.buttonBox.addButton( tr.empty_cards_delete_button(), QDialogButtonBox.ButtonRole.ActionRole ) + assert self._delete_button is not None self._delete_button.setAutoDefault(False) qconnect(self._delete_button.clicked, self._on_delete) diff --git a/qt/aqt/fields.py b/qt/aqt/fields.py index da81c9ff7..652e5f3a2 100644 --- a/qt/aqt/fields.py +++ b/qt/aqt/fields.py @@ -41,25 +41,30 @@ class FieldDialog(QDialog): self.model = nt self.mm._remove_from_cache(self.model["id"]) self.change_tracker = ChangeTracker(self.mw) + self.webview = None + + self.form = aqt.forms.fields.Ui_Dialog() + self.form.setupUi(self) self.setWindowTitle( without_unicode_isolation(tr.fields_fields_for(val=self.model["name"])) ) - self.form = aqt.forms.fields.Ui_Dialog() - self.form.setupUi(self) - self.webview = None - disable_help_button(self) - self.form.buttonBox.button(QDialogButtonBox.StandardButton.Help).setAutoDefault( - False - ) - self.form.buttonBox.button( + help_button = self.form.buttonBox.button(QDialogButtonBox.StandardButton.Help) + assert help_button is not None + help_button.setAutoDefault(False) + + cancel_button = self.form.buttonBox.button( QDialogButtonBox.StandardButton.Cancel - ).setAutoDefault(False) - self.form.buttonBox.button(QDialogButtonBox.StandardButton.Save).setAutoDefault( - False ) + assert cancel_button is not None + cancel_button.setAutoDefault(False) + + save_button = self.form.buttonBox.button(QDialogButtonBox.StandardButton.Save) + assert save_button is not None + save_button.setAutoDefault(False) + self.currentIdx: int | None = None self.fillFields() self.setupSignals() @@ -112,6 +117,7 @@ class FieldDialog(QDialog): # for pylint return # the item in idx is removed thus subtract 1. + assert idx is not None if idx < dropPos: movePos -= 1 self.moveField(movePos + 1) # convert to 1 based. @@ -144,6 +150,9 @@ class FieldDialog(QDialog): return txt def onRename(self) -> None: + if self.currentIdx is None: + return + idx = self.currentIdx f = self.model["flds"][idx] name = self._uniqueName(tr.actions_new_name(), self.currentIdx, f["name"]) @@ -195,6 +204,7 @@ class FieldDialog(QDialog): def onPosition(self, delta: int = -1) -> None: idx = self.currentIdx + assert idx is not None l = len(self.model["flds"]) txt = getOnlyText(tr.fields_new_position_1(val=l), default=str(idx + 1)) if not txt: diff --git a/qt/aqt/filtered_deck.py b/qt/aqt/filtered_deck.py index acda4ef86..fdf63f1d3 100644 --- a/qt/aqt/filtered_deck.py +++ b/qt/aqt/filtered_deck.py @@ -130,9 +130,10 @@ class FilteredDeckConfigDialog(QDialog): build_label = tr.actions_rebuild() else: build_label = tr.decks_build() - self.form.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText( - build_label - ) + + ok_button = self.form.buttonBox.button(QDialogButtonBox.StandardButton.Ok) + assert ok_button is not None + ok_button.setText(build_label) form.resched.setChecked(config.reschedule) self._onReschedToggled(0) diff --git a/qt/aqt/flags.py b/qt/aqt/flags.py index 5373206ab..7a1b9aef1 100644 --- a/qt/aqt/flags.py +++ b/qt/aqt/flags.py @@ -34,11 +34,11 @@ class Flag: class FlagManager: def __init__(self, mw: aqt.main.AnkiQt) -> None: self.mw = mw - self._flags: list[Flag] | None = None + self._flags: list[Flag] = [] def all(self) -> list[Flag]: """Return a list of all flags.""" - if self._flags is None: + if not self._flags: self._load_flags() return self._flags @@ -57,7 +57,7 @@ class FlagManager: def require_refresh(self) -> None: "Discard cached labels." - self._flags = None + self._flags = [] def _load_flags(self) -> None: labels = cast(dict[str, str], self.mw.col.get_config("flagLabels", {})) diff --git a/qt/aqt/forms/preferences.ui b/qt/aqt/forms/preferences.ui index 58db104c7..34de8c80e 100644 --- a/qt/aqt/forms/preferences.ui +++ b/qt/aqt/forms/preferences.ui @@ -611,28 +611,6 @@ - - - - preferences_import_export - - - - - - - 0 - 0 - - - - Legacy import/export handling - - - - - - @@ -1293,7 +1271,6 @@ useCurrent default_search_text ignore_accents_in_search - legacy_import_export syncMedia syncOnProgramOpen autoSyncMedia diff --git a/qt/aqt/importing.py b/qt/aqt/importing.py index b4a73b553..8f9741a77 100644 --- a/qt/aqt/importing.py +++ b/qt/aqt/importing.py @@ -60,7 +60,7 @@ class ChangeMap(QDialog): self.frm.fields.setCurrentRow(n + 1) self.field: str | None = None - def getField(self) -> str: + def getField(self) -> str | None: self.exec() return self.field @@ -91,8 +91,10 @@ class ImportDialog(QDialog): self.importer = importer self.frm = aqt.forms.importing.Ui_ImportDialog() self.frm.setupUi(self) + help_button = self.frm.buttonBox.button(QDialogButtonBox.StandardButton.Help) + assert help_button is not None qconnect( - self.frm.buttonBox.button(QDialogButtonBox.StandardButton.Help).clicked, + help_button.clicked, self.helpRequested, ) disable_help_button(self) @@ -103,6 +105,7 @@ class ImportDialog(QDialog): gui_hooks.current_note_type_did_change.append(self.modelChanged) qconnect(self.frm.autoDetect.clicked, self.onDelimiter) self.updateDelimiterButtonText() + assert self.mw.pm.profile is not None self.frm.allowHTML.setChecked(self.mw.pm.profile.get("allowHTML", True)) qconnect(self.frm.importMode.currentIndexChanged, self.importModeChanged) self.frm.importMode.setCurrentIndex(self.mw.pm.profile.get("importMode", 1)) @@ -187,6 +190,7 @@ class ImportDialog(QDialog): showWarning(tr.importing_the_first_field_of_the_note()) return self.importer.importMode = self.frm.importMode.currentIndex() + assert self.mw.pm.profile is not None self.mw.pm.profile["importMode"] = self.importer.importMode self.importer.allowHTML = self.frm.allowHTML.isChecked() self.mw.pm.profile["allowHTML"] = self.importer.allowHTML @@ -390,7 +394,7 @@ def importFile(mw: AnkiQt, file: str) -> None: showWarning(invalidZipMsg()) except MediaMapInvalid: showWarning( - "Unable to read file. It probably requires a newer version of Anki to import. Try unchecking 'Legacy import/export Handling' under Preferences > Editing > Import/Export and see if the problem persists." + "Unable to read file. It probably requires a newer version of Anki to import." ) except V2ImportIntoV1: showWarning( diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 2936d7969..9482a27e7 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -112,7 +112,6 @@ class MainWebView(AnkiWebView): self.setFocusPolicy(Qt.FocusPolicy.WheelFocus) self.setMinimumWidth(400) self.setAcceptDrops(True) - print("todo: windows paths in import screen") # Importing files via drag & drop ########################################################################## diff --git a/qt/aqt/mediacheck.py b/qt/aqt/mediacheck.py index ec47550d8..71ab5a07d 100644 --- a/qt/aqt/mediacheck.py +++ b/qt/aqt/mediacheck.py @@ -80,6 +80,7 @@ class MediaChecker: label = progress.media_check try: + assert self.progress_dialog is not None if self.progress_dialog.wantCancel: self.mw.col.set_wants_abort() except AttributeError: @@ -165,6 +166,7 @@ class MediaChecker: def _on_render_latex(self) -> None: self.progress_dialog = self.mw.progress.start() + assert self.progress_dialog is not None try: out = self.mw.col.media.render_all_latex(self._on_render_latex_progress) if self.progress_dialog.wantCancel: @@ -181,6 +183,7 @@ class MediaChecker: tooltip(tr.media_check_all_latex_rendered()) def _on_render_latex_progress(self, count: int) -> bool: + assert self.progress_dialog is not None if self.progress_dialog.wantCancel: return False diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index 26e52f503..15d9a0fd1 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -457,8 +457,8 @@ def update_deck_configs() -> bytes: update.max = val.total_cards update.value = val.current_cards update.label = val.label - elif progress.HasField("compute_weights"): - val2 = progress.compute_weights + elif progress.HasField("compute_params"): + val2 = progress.compute_params # prevent an indeterminate progress bar from appearing at the start of each preset update.max = max(val2.total, 1) update.value = val2.current @@ -621,10 +621,10 @@ exposed_backend_list = [ "update_image_occlusion_note", "get_image_occlusion_fields", # SchedulerService - "compute_fsrs_weights", + "compute_fsrs_params", "compute_optimal_retention", "set_wants_abort", - "evaluate_weights", + "evaluate_params", "get_optimal_retention_parameters", "simulate_fsrs_review", ] @@ -735,8 +735,12 @@ def _extract_page_context() -> PageContext: return PageContext.NON_LEGACY_PAGE elif referer.path == "/_anki/legacyPageData": query_params = parse_qs(referer.query) - id = int(query_params.get("id", [None])[0]) - return aqt.mw.mediaServer.get_page_context(id) + query_id = query_params.get("id") + if not query_id: + return PageContext.UNKNOWN + id = int(query_id[0]) + page_context = aqt.mw.mediaServer.get_page_context(id) + return page_context if page_context else PageContext.UNKNOWN else: return PageContext.UNKNOWN diff --git a/qt/aqt/mediasync.py b/qt/aqt/mediasync.py index 96054832c..7cfb6d4a7 100644 --- a/qt/aqt/mediasync.py +++ b/qt/aqt/mediasync.py @@ -119,7 +119,7 @@ class MediaSyncer: diag: MediaSyncDialog = aqt.dialogs.open("sync_log", self.mw, self, True) diag.show() - timer: QTimer | None = None + timer: QTimer def check_finished() -> None: if not self.is_syncing(): diff --git a/qt/aqt/modelchooser.py b/qt/aqt/modelchooser.py index cdc75cd06..15c5347e4 100644 --- a/qt/aqt/modelchooser.py +++ b/qt/aqt/modelchooser.py @@ -84,6 +84,7 @@ class ModelChooser(QHBoxLayout): if not ret.name: return m = self.deck.models.by_name(ret.name) + assert m is not None self.deck.conf["curModel"] = m["id"] cdeck = self.deck.decks.current() cdeck["mid"] = m["id"] diff --git a/qt/aqt/notetypechooser.py b/qt/aqt/notetypechooser.py index 395af193b..cb282109d 100644 --- a/qt/aqt/notetypechooser.py +++ b/qt/aqt/notetypechooser.py @@ -111,6 +111,7 @@ class NotetypeChooser(QHBoxLayout): if not ret.name: return notetype = self.mw.col.models.by_name(ret.name) + assert notetype is not None if (id := notetype["id"]) != self._selected_notetype_id: self.selected_notetype_id = id @@ -146,7 +147,9 @@ class NotetypeChooser(QHBoxLayout): func(self._selected_notetype_id) def selected_notetype_name(self) -> str: - return self.mw.col.models.get(self.selected_notetype_id)["name"] + selected_notetype = self.mw.col.models.get(self.selected_notetype_id) + assert selected_notetype is not None + return selected_notetype["name"] def _ensure_selected_notetype_valid(self) -> None: if not self.mw.col.models.get(self._selected_notetype_id): diff --git a/qt/aqt/overview.py b/qt/aqt/overview.py index 20d276dbb..da017714f 100644 --- a/qt/aqt/overview.py +++ b/qt/aqt/overview.py @@ -224,13 +224,14 @@ class Overview: dyn = "" return f'
{desc}
' - def _table(self) -> str | None: + def _table(self) -> str: counts = list(self.mw.col.sched.counts()) current_did = self.mw.col.decks.get_current_id() deck_node = self.mw.col.sched.deck_due_tree(current_did) but = self.mw.button if self.mw.col.v3_scheduler(): + assert deck_node is not None buried_new = deck_node.new_count - counts[0] buried_learning = deck_node.learn_count - counts[1] buried_review = deck_node.review_count - counts[2] diff --git a/qt/aqt/package.py b/qt/aqt/package.py index 3f89366a2..f1ee8cd79 100644 --- a/qt/aqt/package.py +++ b/qt/aqt/package.py @@ -41,7 +41,7 @@ def _patch_pkgutil() -> None: def get_data_custom(package: str, resource: str) -> bytes | None: try: module = importlib.import_module(package) - reader = module.__loader__.get_resource_reader(package) # type: ignore[attr-defined] + reader = module.__loader__.get_resource_reader(package) # type: ignore with reader.open_resource(resource) as f: return f.read() except Exception: diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index 3676fe169..7befe4ca2 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -46,13 +46,16 @@ class Preferences(QDialog): self.form.network_timeout, ): spinbox.setSuffix(f" {spinbox.suffix()}") + disable_help_button(self) - self.form.buttonBox.button(QDialogButtonBox.StandardButton.Help).setAutoDefault( - False - ) - self.form.buttonBox.button( - QDialogButtonBox.StandardButton.Close - ).setAutoDefault(False) + help_button = self.form.buttonBox.button(QDialogButtonBox.StandardButton.Help) + assert help_button is not None + help_button.setAutoDefault(False) + + close_button = self.form.buttonBox.button(QDialogButtonBox.StandardButton.Close) + assert close_button is not None + close_button.setAutoDefault(False) + qconnect( self.form.buttonBox.helpRequested, lambda: openHelp(HelpPage.PREFERENCES) ) @@ -218,6 +221,7 @@ class Preferences(QDialog): qconnect(self.form.syncAnkiHubLogin.clicked, self.ankihub_sync_login) def update_login_status(self) -> None: + assert self.prof is not None if not self.prof.get("syncKey"): self.form.syncUser.setText(tr.preferences_ankiweb_intro()) self.form.syncLogin.setVisible(True) @@ -241,6 +245,7 @@ class Preferences(QDialog): def sync_login(self) -> None: def on_success(): + assert self.prof is not None if self.prof.get("syncKey"): self.update_login_status() self.confirm_sync_after_login() @@ -251,6 +256,7 @@ class Preferences(QDialog): if self.mw.media_syncer.is_syncing(): showWarning("Can't log out while sync in progress.") return + assert self.prof is not None self.prof["syncKey"] = None self.mw.col.media.force_resync() self.update_login_status() @@ -263,7 +269,10 @@ class Preferences(QDialog): ankihub_login(self.mw, on_success) def ankihub_sync_logout(self) -> None: - ankihub_logout(self.mw, self.update_login_status, self.mw.pm.ankihub_token()) + ankihub_token = self.mw.pm.ankihub_token() + if ankihub_token is None: + return + ankihub_logout(self.mw, self.update_login_status, ankihub_token) def confirm_sync_after_login(self) -> None: from aqt import mw @@ -272,6 +281,7 @@ class Preferences(QDialog): self.accept_with_callback(self.mw.on_sync_button_clicked) def update_network(self) -> None: + assert self.prof is not None self.prof["autoSync"] = self.form.syncOnProgramOpen.isChecked() self.prof["syncMedia"] = self.form.syncMedia.isChecked() self.mw.pm.set_periodic_sync_media_minutes( @@ -349,7 +359,6 @@ class Preferences(QDialog): ) self.form.styleLabel.setVisible(not is_win) self.form.styleComboBox.setVisible(not is_win) - self.form.legacy_import_export.setChecked(self.mw.pm.legacy_import_export()) qconnect(self.form.resetWindowSizes.clicked, self.on_reset_window_sizes) self.setup_language() @@ -367,8 +376,6 @@ class Preferences(QDialog): self.mw.pm.setUiScale(newScale) restart_required = True - self.mw.pm.set_legacy_import_export(self.form.legacy_import_export.isChecked()) - if restart_required: showInfo(tr.preferences_changes_will_take_effect_when_you()) @@ -378,6 +385,7 @@ class Preferences(QDialog): self.mw.set_theme(Theme(index)) def on_reset_window_sizes(self) -> None: + assert self.prof is not None regexp = re.compile(r"(Geom(etry)?|State|Splitter|Header)(\d+.\d+)?$") for key in list(self.prof.keys()): if regexp.search(key): diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py index 469908c1b..817e00139 100644 --- a/qt/aqt/profiles.py +++ b/qt/aqt/profiles.py @@ -636,7 +636,8 @@ create table if not exists profiles self.meta[f"{self.editor_key(mode)}TagsCollapsed"] = collapsed def legacy_import_export(self) -> bool: - return self.meta.get("legacy_import", False) + "Always returns False so users with this option enabled are not stuck on the legacy importer after the UI option is removed." + return False def set_legacy_import_export(self, enabled: bool) -> None: self.meta["legacy_import"] = enabled diff --git a/qt/aqt/progress.py b/qt/aqt/progress.py index 84d23a22e..4d23b33f4 100644 --- a/qt/aqt/progress.py +++ b/qt/aqt/progress.py @@ -216,6 +216,8 @@ class ProgressManager: self._maybeShow() if not self._shown: return + + assert self._win is not None if label: self._win.form.label.setText(label) @@ -290,6 +292,7 @@ class ProgressManager: self._showWin() def _showWin(self) -> None: + assert self._win is not None self._shown = time.monotonic() self._win.show() @@ -297,6 +300,7 @@ class ProgressManager: # if the parent window has been deleted, the progress dialog may have # already been dropped; delete it if it hasn't been if not sip.isdeleted(self._win): + assert self._win is not None self._win.cancel() self._win = None self._shown = 0 @@ -314,6 +318,7 @@ class ProgressManager: def _on_show_timer(self) -> None: if self.mw.app.focusWindow() is None: # if no window is focused (eg app is minimized), defer display + assert self._show_timer is not None self._show_timer.start(10) return @@ -334,7 +339,7 @@ class ProgressManager: class ProgressDialog(QDialog): - def __init__(self, parent: QWidget) -> None: + def __init__(self, parent: QWidget | None) -> None: QDialog.__init__(self, parent) disable_help_button(self) self.form = aqt.forms.progress.Ui_Dialog() @@ -349,14 +354,16 @@ class ProgressDialog(QDialog): self.hide() self.deleteLater() - def closeEvent(self, evt: QCloseEvent) -> None: + def closeEvent(self, evt: QCloseEvent | None) -> None: + assert evt is not None if self._closingDown: evt.accept() else: self.wantCancel = True evt.ignore() - def keyPressEvent(self, evt: QKeyEvent) -> None: + def keyPressEvent(self, evt: QKeyEvent | None) -> None: + assert evt is not None if evt.key() == Qt.Key.Key_Escape: evt.ignore() self.wantCancel = True diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index 20b608e10..36e3b2914 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -245,7 +245,8 @@ av_player = AVPlayer() def _packagedCmd(cmd: list[str]) -> tuple[Any, dict[str, str]]: cmd = cmd[:] env = os.environ.copy() - if "LD_LIBRARY_PATH" in env: + # keep LD_LIBRARY_PATH when in snap environment + if "LD_LIBRARY_PATH" in env and "SNAP" not in env: del env["LD_LIBRARY_PATH"] if is_win: diff --git a/qt/aqt/stats.py b/qt/aqt/stats.py index 206205349..7842b27a9 100644 --- a/qt/aqt/stats.py +++ b/qt/aqt/stats.py @@ -61,9 +61,11 @@ class NewDeckStats(QDialog): b = f.buttonBox.addButton( tr.statistics_save_pdf(), QDialogButtonBox.ButtonRole.ActionRole ) + assert b is not None qconnect(b.clicked, self.saveImage) b.setAutoDefault(False) b = f.buttonBox.button(QDialogButtonBox.StandardButton.Close) + assert b is not None b.setAutoDefault(False) maybeHideClose(self.form.buttonBox) addCloseShortcut(self) @@ -78,7 +80,7 @@ class NewDeckStats(QDialog): def reject(self) -> None: self.deck_chooser.cleanup() self.form.web.cleanup() - self.form.web = None + self.form.web = None # type: ignore saveGeom(self, self.name) aqt.dialogs.markClosed("NewDeckStats") QDialog.reject(self) @@ -92,7 +94,7 @@ class NewDeckStats(QDialog): lambda _: self.refresh() ).run_in_background() - def _imagePath(self) -> str: + def _imagePath(self) -> str | None: name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time())) name = f"anki-{tr.statistics_stats()}{name}" file = getSaveFile( @@ -115,7 +117,9 @@ class NewDeckStats(QDialog): # unreadable. A simple fix for now is to scroll to the top of the # page first. def after_scroll(arg: Any) -> None: - self.form.web.page().printToPdf(path) + form_web_page = self.form.web.page() + assert form_web_page is not None + form_web_page.printToPdf(path) tooltip(tr.statistics_saved()) self.form.web.evalWithCallback("window.scrollTo(0, 0);", after_scroll) @@ -165,6 +169,7 @@ class DeckStats(QDialog): b = f.buttonBox.addButton( tr.statistics_save_pdf(), QDialogButtonBox.ButtonRole.ActionRole ) + assert b is not None qconnect(b.clicked, self.saveImage) b.setAutoDefault(False) qconnect(f.groups.clicked, lambda: self.changeScope("deck")) @@ -182,7 +187,7 @@ class DeckStats(QDialog): def reject(self) -> None: self.form.web.cleanup() - self.form.web = None + self.form.web = None # type: ignore saveGeom(self, self.name) aqt.dialogs.markClosed("DeckStats") QDialog.reject(self) @@ -191,7 +196,7 @@ class DeckStats(QDialog): self.reject() callback() - def _imagePath(self) -> str: + def _imagePath(self) -> str | None: name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time())) name = f"anki-{tr.statistics_stats()}{name}" file = getSaveFile( @@ -208,7 +213,9 @@ class DeckStats(QDialog): path = self._imagePath() if not path: return - self.form.web.page().printToPdf(path) + form_web_page = self.form.web.page() + assert form_web_page is not None + form_web_page.printToPdf(path) tooltip(tr.statistics_saved()) def changePeriod(self, n: int) -> None: diff --git a/qt/aqt/studydeck.py b/qt/aqt/studydeck.py index 537c7d151..d1c0a5c47 100644 --- a/qt/aqt/studydeck.py +++ b/qt/aqt/studydeck.py @@ -101,7 +101,7 @@ class StudyDeck(QDialog): else: self.exec() - def eventFilter(self, obj: QObject, evt: QEvent) -> bool: + def eventFilter(self, obj: QObject | None, evt: QEvent | None) -> bool: if isinstance(evt, QKeyEvent) and evt.type() == QEvent.Type.KeyPress: new_row = current_row = self.form.list.currentRow() rows_count = self.form.list.count() @@ -178,6 +178,7 @@ class StudyDeck(QDialog): def success(out: OpChangesWithId) -> None: deck = self.mw.col.decks.get(DeckId(out.id)) + assert deck is not None self.name = deck["name"] self.accept_with_callback() diff --git a/qt/aqt/switch.py b/qt/aqt/switch.py index fb3c2da6c..214d20bc7 100644 --- a/qt/aqt/switch.py +++ b/qt/aqt/switch.py @@ -103,7 +103,7 @@ class Switch(QAbstractButton): self._position = self.end_position self.update() - def paintEvent(self, _event: QPaintEvent) -> None: + def paintEvent(self, _event: QPaintEvent | None) -> None: painter = QPainter(self) painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) painter.setPen(Qt.PenStyle.NoPen) @@ -162,12 +162,13 @@ class Switch(QAbstractButton): self._current_label_rectangle(), Qt.AlignmentFlag.AlignCenter, self.label ) - def mouseReleaseEvent(self, event: QMouseEvent) -> None: + def mouseReleaseEvent(self, event: QMouseEvent | None) -> None: super().mouseReleaseEvent(event) + assert event is not None if event.button() == Qt.MouseButton.LeftButton: self._animate_toggle() - def enterEvent(self, event: QEnterEvent) -> None: + def enterEvent(self, event: QEnterEvent | None) -> None: self.setCursor(Qt.CursorShape.PointingHandCursor) super().enterEvent(event) diff --git a/qt/aqt/sync.py b/qt/aqt/sync.py index cea64fadc..2b8bd59af 100644 --- a/qt/aqt/sync.py +++ b/qt/aqt/sync.py @@ -168,7 +168,7 @@ def full_sync( def confirm_full_download( - mw: aqt.main.AnkiQt, server_usn: int, on_done: Callable[[], None] + mw: aqt.main.AnkiQt, server_usn: int | None, on_done: Callable[[], None] ) -> None: # confirmation step required, as some users customize their notetypes # in an empty collection, then want to upload them @@ -184,7 +184,7 @@ def confirm_full_download( def confirm_full_upload( - mw: aqt.main.AnkiQt, server_usn: int, on_done: Callable[[], None] + mw: aqt.main.AnkiQt, server_usn: int | None, on_done: Callable[[], None] ) -> None: # confirmation step required, as some users have reported an upload # happening despite having their AnkiWeb collection not being empty @@ -220,7 +220,7 @@ def on_full_sync_timer(mw: aqt.main.AnkiQt, label: str) -> None: def full_download( - mw: aqt.main.AnkiQt, server_usn: int, on_done: Callable[[], None] + mw: aqt.main.AnkiQt, server_usn: int | None, on_done: Callable[[], None] ) -> None: label = tr.sync_downloading_from_ankiweb() @@ -372,7 +372,9 @@ def get_id_and_pass_from_user( l2.setBuddy(passwd) vbox.addLayout(g) bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) # type: ignore - bb.button(QDialogButtonBox.StandardButton.Ok).setAutoDefault(True) + ok_button = bb.button(QDialogButtonBox.StandardButton.Ok) + assert ok_button is not None + ok_button.setAutoDefault(True) qconnect(bb.accepted, diag.accept) qconnect(bb.rejected, diag.reject) vbox.addWidget(bb) diff --git a/qt/aqt/tagedit.py b/qt/aqt/tagedit.py index 5c9686210..d85d6655a 100644 --- a/qt/aqt/tagedit.py +++ b/qt/aqt/tagedit.py @@ -42,13 +42,17 @@ class TagEdit(QLineEdit): l = (d.name for d in self.col.decks.all_names_and_ids()) self.model.setStringList(l) - def focusInEvent(self, evt: QFocusEvent) -> None: + def focusInEvent(self, evt: QFocusEvent | None) -> None: QLineEdit.focusInEvent(self, evt) - def keyPressEvent(self, evt: QKeyEvent) -> None: + def keyPressEvent(self, evt: QKeyEvent | None) -> None: + assert evt is not None + popup = self._completer.popup() + assert popup is not None + if evt.key() in (Qt.Key.Key_Up, Qt.Key.Key_Down): # show completer on arrow key up/down - if not self._completer.popup().isVisible(): + if not popup.isVisible(): self.showCompleter() return if ( @@ -56,24 +60,21 @@ class TagEdit(QLineEdit): and evt.modifiers() & Qt.KeyboardModifier.ControlModifier ): # select next completion - if not self._completer.popup().isVisible(): + if not popup.isVisible(): self.showCompleter() index = self._completer.currentIndex() - self._completer.popup().setCurrentIndex(index) + popup.setCurrentIndex(index) cur_row = index.row() if not self._completer.setCurrentRow(cur_row + 1): self._completer.setCurrentRow(0) return - if ( - evt.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return) - and self._completer.popup().isVisible() - ): + if evt.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return) and popup.isVisible(): # apply first completion if no suggestion selected - selected_row = self._completer.popup().currentIndex().row() + selected_row = popup.currentIndex().row() if selected_row == -1: self._completer.setCurrentRow(0) index = self._completer.currentIndex() - self._completer.popup().setCurrentIndex(index) + popup.setCurrentIndex(index) self.hideCompleter() QWidget.keyPressEvent(self, evt) return @@ -97,15 +98,19 @@ class TagEdit(QLineEdit): self._completer.setCompletionPrefix(self.text()) self._completer.complete() - def focusOutEvent(self, evt: QFocusEvent) -> None: + def focusOutEvent(self, evt: QFocusEvent | None) -> None: QLineEdit.focusOutEvent(self, evt) self.lostFocus.emit() # type: ignore - self._completer.popup().hide() + popup = self._completer.popup() + assert popup is not None + popup.hide() def hideCompleter(self) -> None: if sip.isdeleted(self._completer): # type: ignore return - self._completer.popup().hide() + popup = self._completer.popup() + assert popup is not None + popup.hide() class TagCompleter(QCompleter): @@ -120,7 +125,9 @@ class TagCompleter(QCompleter): self.edit = edit self.cursor: int | None = None - def splitPath(self, tags: str) -> list[str]: + def splitPath(self, tags: str | None) -> list[str]: + assert tags is not None + assert self.edit.col is not None stripped_tags = tags.strip() stripped_tags = re.sub(" +", " ", stripped_tags) self.tags = self.edit.col.tags.split(stripped_tags) diff --git a/qt/aqt/taglimit.py b/qt/aqt/taglimit.py index 4132deafa..3893ed0d6 100644 --- a/qt/aqt/taglimit.py +++ b/qt/aqt/taglimit.py @@ -50,7 +50,9 @@ class TagLimit(QDialog): list.addItem(item) if select: idx = list.indexFromItem(item) - list.selectionModel().select( + list_selection_model = list.selectionModel() + assert list_selection_model is not None + list_selection_model.select( idx, QItemSelectionModel.SelectionFlag.Select ) @@ -77,12 +79,16 @@ class TagLimit(QDialog): if want_active: item = self.form.activeList.item(c) idx = self.form.activeList.indexFromItem(item) - if self.form.activeList.selectionModel().isSelected(idx): + active_list_selection_model = self.form.activeList.selectionModel() + assert active_list_selection_model is not None + if active_list_selection_model.isSelected(idx): include_tags.append(tag.name) # inactive item = self.form.inactiveList.item(c) idx = self.form.inactiveList.indexFromItem(item) - if self.form.inactiveList.selectionModel().isSelected(idx): + inactive_list_selection_model = self.form.inactiveList.selectionModel() + assert inactive_list_selection_model is not None + if inactive_list_selection_model.isSelected(idx): exclude_tags.append(tag.name) if (len(include_tags) + len(exclude_tags)) > 100: diff --git a/qt/aqt/theme.py b/qt/aqt/theme.py index 735f720c4..c43cf8f86 100644 --- a/qt/aqt/theme.py +++ b/qt/aqt/theme.py @@ -231,7 +231,9 @@ class ThemeManager: self._current_widget_style = new_widget_style app = aqt.mw.app if not self._default_style: - self._default_style = app.style().objectName() + style = app.style() + assert style is not None + self._default_style = style.objectName() self._apply_palette(app) self._apply_style(app) gui_hooks.theme_did_change() diff --git a/qt/aqt/toolbar.py b/qt/aqt/toolbar.py index 7313f2a39..2ae8e6d72 100644 --- a/qt/aqt/toolbar.py +++ b/qt/aqt/toolbar.py @@ -37,7 +37,9 @@ class BottomToolbar: class ToolbarWebView(AnkiWebView): hide_condition: Callable[..., bool] - def __init__(self, mw: aqt.AnkiQt, kind: AnkiWebViewKind | None = None) -> None: + def __init__( + self, mw: aqt.AnkiQt, kind: AnkiWebViewKind = AnkiWebViewKind.DEFAULT + ) -> None: AnkiWebView.__init__(self, mw, kind=kind) self.mw = mw self.setFocusPolicy(Qt.FocusPolicy.WheelFocus) @@ -172,7 +174,7 @@ class TopWebView(ToolbarWebView): self.eval("""document.body.style.setProperty("min-height", "0px"); """) self.evalWithCallback("document.documentElement.offsetHeight", self._onHeight) - def resizeEvent(self, event: QResizeEvent) -> None: + def resizeEvent(self, event: QResizeEvent | None) -> None: super().resizeEvent(event) self.mw.web.evalWithCallback( diff --git a/qt/aqt/tts.py b/qt/aqt/tts.py index cd2884795..3cd0e5ddf 100644 --- a/qt/aqt/tts.py +++ b/qt/aqt/tts.py @@ -189,6 +189,7 @@ class MacTTSPlayer(TTSProcessPlayer): stderr=subprocess.DEVNULL, ) # write the input text to stdin + assert self._process.stdin is not None self._process.stdin.write(tag.field_text.encode("utf8")) self._process.stdin.close() self._wait_for_termination(tag) @@ -247,6 +248,7 @@ class MacTTSFilePlayer(MacTTSPlayer): stderr=subprocess.DEVNULL, ) # write the input text to stdin + assert self._process.stdin is not None self._process.stdin.write(tag.field_text.encode("utf8")) self._process.stdin.close() self._wait_for_termination(tag) diff --git a/qt/aqt/update.py b/qt/aqt/update.py index 1b60ded48..fd0e4eafd 100644 --- a/qt/aqt/update.py +++ b/qt/aqt/update.py @@ -52,7 +52,7 @@ def check_for_update() -> None: QueryOp(parent=mw, op=do_check, success=on_done).failure( on_fail - ).run_in_background() + ).without_collection().run_in_background() def prompt_to_update(mw: aqt.AnkiQt, ver: str) -> None: diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index ac11694e1..23266ebaf 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -118,10 +118,13 @@ HelpPageArgument = Union["HelpPage.V", str] def openHelp(section: HelpPageArgument) -> None: + assert tr.backend is not None + backend = tr.backend() + assert backend is not None if isinstance(section, str): - link = tr.backend().help_page_link(page=HelpPage.INDEX) + section + link = backend.help_page_link(page=HelpPage.INDEX) + section else: - link = tr.backend().help_page_link(page=section) + link = backend.help_page_link(page=section) openLink(link) @@ -170,17 +173,20 @@ class MessageBox(QMessageBox): b = self.addButton(button) # a translator has complained the default Qt translation is inappropriate, so we override it if button == QMessageBox.StandardButton.Discard: + assert b is not None b.setText(tr.actions_discard()) elif isinstance(button, tuple): b = self.addButton(button[0], button[1]) else: continue if callback is not None: + assert b is not None qconnect(b.clicked, partial(callback, i)) if i == default_button: self.setDefaultButton(b) if help is not None: b = self.addButton(QMessageBox.StandardButton.Help) + assert b is not None qconnect(b.clicked, lambda: openHelp(help)) self.open() @@ -316,9 +322,11 @@ def showInfo( mb.setDefaultButton(default) else: b = mb.addButton(QMessageBox.StandardButton.Ok) + assert b is not None b.setDefault(True) if help is not None: b = mb.addButton(QMessageBox.StandardButton.Help) + assert b is not None qconnect(b.clicked, lambda: openHelp(help)) b.setAutoDefault(False) return mb.exec() @@ -363,7 +371,9 @@ def showText( if copyBtn: def onCopy() -> None: - QApplication.clipboard().setText(text.toPlainText()) + clipboard = QApplication.clipboard() + assert clipboard is not None + clipboard.setText(text.toPlainText()) btn = QPushButton(tr.qt_misc_copy_to_clipboard()) qconnect(btn.clicked, onCopy) @@ -415,6 +425,7 @@ def askUser( default = QMessageBox.StandardButton.Yes r = msgfunc(parent, title, text, sb, default) if r == QMessageBox.StandardButton.Help: + assert help is not None openHelp(help) else: break @@ -431,7 +442,7 @@ class ButtonedDialog(QMessageBox): title: str = "Anki", ): QMessageBox.__init__(self, parent) - self._buttons: list[QPushButton] = [] + self._buttons: list[QPushButton | None] = [] self.setWindowTitle(title) self.help = help self.setIcon(QMessageBox.Icon.Warning) @@ -444,11 +455,13 @@ class ButtonedDialog(QMessageBox): def run(self) -> str: self.exec() - but = self.clickedButton().text() - if but == "Help": + clicked_button = self.clickedButton() + assert clicked_button is not None + txt = clicked_button.text() + if txt == "Help": # FIXME stop dialog closing? + assert self.help is not None openHelp(self.help) - txt = self.clickedButton().text() # work around KDE 'helpfully' adding accelerators to button text of Qt apps return txt.replace("&", "") @@ -504,13 +517,18 @@ class GetTextDialog(QDialog): b = QDialogButtonBox(buts) # type: ignore v.addWidget(b) self.setLayout(v) - qconnect(b.button(QDialogButtonBox.StandardButton.Ok).clicked, self.accept) - qconnect(b.button(QDialogButtonBox.StandardButton.Cancel).clicked, self.reject) + ok_button = b.button(QDialogButtonBox.StandardButton.Ok) + assert ok_button is not None + qconnect(ok_button.clicked, self.accept) + + cancel_button = b.button(QDialogButtonBox.StandardButton.Cancel) + assert cancel_button is not None + qconnect(cancel_button.clicked, self.reject) + if help: - qconnect( - b.button(QDialogButtonBox.StandardButton.Help).clicked, - self.helpRequested, - ) + help_button = b.button(QDialogButtonBox.StandardButton.Help) + assert help_button is not None + qconnect(help_button.clicked, self.helpRequested) self.l.setFocus() def accept(self) -> None: @@ -520,7 +538,8 @@ class GetTextDialog(QDialog): return QDialog.reject(self) def helpRequested(self) -> None: - openHelp(self.help) + if self.help is not None: + openHelp(self.help) def getText( @@ -624,6 +643,7 @@ def getFile( if dir and key: raise Exception("expected dir or key") if not dir: + assert aqt.mw.pm.profile is not None dirkey = f"{key}Directory" dir = aqt.mw.pm.profile.get(dirkey, "") else: @@ -635,6 +655,7 @@ def getFile( else QFileDialog.FileMode.ExistingFile ) d.setFileMode(mode) + assert dir is not None if os.path.exists(dir): d.setDirectory(dir) d.setWindowTitle(title) @@ -644,6 +665,7 @@ def getFile( def accept() -> None: files = list(d.selectedFiles()) if dirkey: + assert aqt.mw.pm.profile is not None dir = os.path.dirname(files[0]) aqt.mw.pm.profile[dirkey] = dir result = files if multi else files[0] @@ -683,10 +705,11 @@ def getSaveFile( dir_description: str, key: str, ext: str, - fname: str | None = None, -) -> str: + fname: str = "", +) -> str | None: """Ask the user for a file to save. Use DIR_DESCRIPTION as config variable. The file dialog will default to open with FNAME.""" + assert aqt.mw.pm.profile is not None config_key = f"{dir_description}Directory" defaultPath = QStandardPaths.writableLocation( @@ -709,9 +732,10 @@ def getSaveFile( dir = os.path.dirname(file) aqt.mw.pm.profile[config_key] = dir # check if it exists - if os.path.exists(file): - if not askUser(tr.qt_misc_this_file_exists_are_you_sure(), parent): - return None + if os.path.exists(file) and not askUser( + tr.qt_misc_this_file_exists_are_you_sure(), parent + ): + return None return file @@ -735,6 +759,7 @@ def _qt_state_key(kind: _QtStateKeyKind, key: str) -> str: def saveGeom(widget: QWidget, key: str) -> None: # restoring a fullscreen window breaks the tab functionality of 5.15 if not widget.isFullScreen() or qtmajor == 6: + assert aqt.mw.pm.profile is not None key = _qt_state_key(_QtStateKeyKind.GEOMETRY, key) aqt.mw.pm.profile[key] = widget.saveGeometry() @@ -745,6 +770,7 @@ def restoreGeom( adjustSize: bool = False, default_size: tuple[int, int] | None = None, ) -> None: + assert aqt.mw.pm.profile is not None key = _qt_state_key(_QtStateKeyKind.GEOMETRY, key) if existing_geom := aqt.mw.pm.profile.get(key): widget.restoreGeometry(existing_geom) @@ -756,7 +782,9 @@ def restoreGeom( def ensureWidgetInScreenBoundaries(widget: QWidget) -> None: - handle = widget.window().windowHandle() + window = widget.window() + assert window is not None + handle = window.windowHandle() if not handle: # window has not yet been shown, retry later aqt.mw.progress.timer( @@ -765,7 +793,9 @@ def ensureWidgetInScreenBoundaries(widget: QWidget) -> None: return # ensure widget is smaller than screen bounds - geom = handle.screen().availableGeometry() + screen = handle.screen() + assert screen is not None + geom = screen.availableGeometry() wsize = widget.size() cappedWidth = min(geom.width(), wsize.width()) cappedHeight = min(geom.height(), wsize.height()) @@ -784,44 +814,52 @@ def ensureWidgetInScreenBoundaries(widget: QWidget) -> None: def saveState(widget: QFileDialog | QMainWindow, key: str) -> None: + assert aqt.mw.pm.profile is not None key = _qt_state_key(_QtStateKeyKind.STATE, key) aqt.mw.pm.profile[key] = widget.saveState() def restoreState(widget: QFileDialog | QMainWindow, key: str) -> None: + assert aqt.mw.pm.profile is not None key = _qt_state_key(_QtStateKeyKind.STATE, key) if data := aqt.mw.pm.profile.get(key): widget.restoreState(data) def saveSplitter(widget: QSplitter, key: str) -> None: + assert aqt.mw.pm.profile is not None key = _qt_state_key(_QtStateKeyKind.SPLITTER, key) aqt.mw.pm.profile[key] = widget.saveState() def restoreSplitter(widget: QSplitter, key: str) -> None: + assert aqt.mw.pm.profile is not None key = _qt_state_key(_QtStateKeyKind.SPLITTER, key) if data := aqt.mw.pm.profile.get(key): widget.restoreState(data) def saveHeader(widget: QHeaderView, key: str) -> None: + assert aqt.mw.pm.profile is not None key = _qt_state_key(_QtStateKeyKind.HEADER, key) aqt.mw.pm.profile[key] = widget.saveState() def restoreHeader(widget: QHeaderView, key: str) -> None: + assert aqt.mw.pm.profile is not None key = _qt_state_key(_QtStateKeyKind.HEADER, key) if state := aqt.mw.pm.profile.get(key): widget.restoreState(state) def save_is_checked(widget: QCheckBox, key: str) -> None: + assert aqt.mw.pm.profile is not None key += "IsChecked" aqt.mw.pm.profile[key] = widget.isChecked() def restore_is_checked(widget: QCheckBox, key: str) -> None: + assert aqt.mw.pm.profile is not None key += "IsChecked" if aqt.mw.pm.profile.get(key) is not None: widget.setChecked(aqt.mw.pm.profile[key]) @@ -847,8 +885,11 @@ def restore_combo_index_for_session( def save_combo_history(comboBox: QComboBox, history: list[str], name: str) -> str: + assert aqt.mw.pm.profile is not None name += "BoxHistory" - text_input = comboBox.lineEdit().text() + line_edit = comboBox.lineEdit() + assert line_edit is not None + text_input = line_edit.text() if text_input in history: history.remove(text_input) history.insert(0, text_input) @@ -861,14 +902,17 @@ def save_combo_history(comboBox: QComboBox, history: list[str], name: str) -> st def restore_combo_history(comboBox: QComboBox, name: str) -> list[str]: + assert aqt.mw.pm.profile is not None name += "BoxHistory" history = aqt.mw.pm.profile.get(name, []) comboBox.addItems([""] + history) if history: session_input = aqt.mw.pm.session.get(name) if session_input and session_input == history[0]: - comboBox.lineEdit().setText(session_input) - comboBox.lineEdit().selectAll() + line_edit = comboBox.lineEdit() + assert line_edit is not None + line_edit.setText(session_input) + line_edit.selectAll() return history @@ -980,7 +1024,7 @@ def send_to_trash(path: Path) -> None: except Exception as exc: # Linux users may not have a trash folder set up print("trash failure:", path, exc) - if path.is_dir: + if path.is_dir(): shutil.rmtree(path) else: path.unlink() @@ -1005,7 +1049,8 @@ def tooltip( class CustomLabel(QLabel): silentlyClose = True - def mousePressEvent(self, evt: QMouseEvent) -> None: + def mousePressEvent(self, evt: QMouseEvent | None) -> None: + assert evt is not None evt.accept() self.hide() @@ -1074,7 +1119,7 @@ class MenuList: print( "MenuList will be removed; please copy it into your add-on's code if you need it." ) - self.children: list[MenuListChild] = [] + self.children: list[MenuListChild | None] = [] def addItem(self, title: str, func: Callable) -> MenuItem: item = MenuItem(title, func) @@ -1114,6 +1159,7 @@ class SubMenu(MenuList): def renderTo(self, menu: QMenu) -> None: submenu = menu.addMenu(self.title) + assert submenu is not None super().renderTo(submenu) @@ -1124,6 +1170,7 @@ class MenuItem: def renderTo(self, qmenu: QMenu) -> None: a = qmenu.addAction(self.title) + assert a is not None qconnect(a.triggered, self.func) diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py index 55b7f3971..aac3fb1b6 100644 --- a/qt/aqt/webview.py +++ b/qt/aqt/webview.py @@ -8,9 +8,9 @@ import json import os import re import sys -from collections.abc import Callable +from collections.abc import Callable, Sequence from enum import Enum -from typing import TYPE_CHECKING, Any, Optional, cast +from typing import TYPE_CHECKING, Any, cast import anki import anki.lang @@ -89,17 +89,23 @@ class AnkiWebPage(QWebEnginePage): script.setWorldId(QWebEngineScript.ScriptWorldId.MainWorld) script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentReady) script.setRunsOnSubFrames(False) - self.profile().scripts().insert(script) + + profile = self.profile() + assert profile is not None + scripts = profile.scripts() + assert scripts is not None + scripts.insert(script) def javaScriptConsoleMessage( self, level: QWebEnginePage.JavaScriptConsoleMessageLevel, - msg: str, + msg: str | None, line: int, - srcID: str, + srcID: str | None, ) -> None: # not translated because console usually not visible, # and may only accept ascii text + assert srcID is not None if srcID.startswith("data"): srcID = "" else: @@ -162,10 +168,16 @@ class AnkiWebPage(QWebEnginePage): def _onCmd(self, str: str) -> Any: return self._onBridgeCmd(str) - def javaScriptAlert(self, frame: Any, text: str) -> None: + def javaScriptAlert(self, frame: Any, text: str | None) -> None: + if text is None: + return + showInfo(text) - def javaScriptConfirm(self, frame: Any, text: str) -> bool: + def javaScriptConfirm(self, frame: Any, text: str | None) -> bool: + if text is None: + return False + return askUser(text) @@ -285,7 +297,7 @@ class AnkiWebView(QWebEngineView): self.onBridgeCmd: Callable[[str], Any] = self.defaultOnBridgeCmd self._domDone = True - self._pendingActions: list[Callable[[], None]] = [] + self._pendingActions: list[tuple[str, Sequence[Any]]] = [] self.requiresCol = True self.setPage(self._page) self._disable_zoom = False @@ -328,7 +340,9 @@ class AnkiWebView(QWebEngineView): # with target="_blank") and return view return AnkiWebView() - def eventFilter(self, obj: QObject, evt: QEvent) -> bool: + def eventFilter(self, obj: QObject | None, evt: QEvent | None) -> bool: + if evt is None: + return False if self._disable_zoom and is_gesture_or_zoom_event(evt): return True @@ -377,31 +391,34 @@ class AnkiWebView(QWebEngineView): def onSelectAll(self) -> None: self.triggerPageAction(QWebEnginePage.WebAction.SelectAll) - def contextMenuEvent(self, evt: QContextMenuEvent) -> None: + def contextMenuEvent(self, evt: QContextMenuEvent | None) -> None: m = QMenu(self) self._maybe_add_copy_action(m) gui_hooks.webview_will_show_context_menu(self, m) - m.popup(QCursor.pos()) + if m.actions(): + m.popup(QCursor.pos()) def _maybe_add_copy_action(self, menu: QMenu) -> None: if self.hasSelection(): a = menu.addAction(tr.actions_copy()) + assert a is not None qconnect(a.triggered, self.onCopy) - def dropEvent(self, evt: QDropEvent) -> None: + def dropEvent(self, evt: QDropEvent | None) -> None: if self.allow_drops: super().dropEvent(evt) def setHtml( # type: ignore[override] self, html: str, context: PageContext | None = None ) -> None: + from aqt.mediasrv import PageContext # discard any previous pending actions self._pendingActions = [] self._domDone = True if context is None: context = PageContext.UNKNOWN - self._queueAction(lambda: self._setHtml(html, context)) + self._queueAction("setHtml", html, context) self.set_open_links_externally(True) self.allow_drops = False self.show() @@ -453,7 +470,9 @@ class AnkiWebView(QWebEngineView): return 1 def setPlaybackRequiresGesture(self, value: bool) -> None: - self.settings().setAttribute( + settings = self.settings() + assert settings is not None + settings.setAttribute( QWebEngineSettings.WebAttribute.PlaybackRequiresUserGesture, value ) @@ -630,10 +649,13 @@ html {{ {font} }} def eval(self, js: str) -> None: self.evalWithCallback(js, None) - def evalWithCallback(self, js: str, cb: Optional[Callable]) -> None: - self._queueAction(lambda: self._evalWithCallback(js, cb)) + def evalWithCallback(self, js: str, cb: Callable | None) -> None: + self._queueAction("eval", js, cb) + + def _evalWithCallback(self, js: str, cb: Callable[[Any], Any] | None) -> None: + page = self.page() + assert page is not None - def _evalWithCallback(self, js: str, cb: Optional[Callable[[Any], Any]]) -> None: if cb: def handler(val: Any) -> None: @@ -642,20 +664,26 @@ html {{ {font} }} return cb(val) - self.page().runJavaScript(js, handler) + page.runJavaScript(js, handler) else: - self.page().runJavaScript(js) + page.runJavaScript(js) - def _queueAction(self, action: Callable[[], None]) -> None: - self._pendingActions.append(action) + def _queueAction(self, name: str, *args: Any) -> None: + self._pendingActions.append((name, args)) self._maybeRunActions() def _maybeRunActions(self) -> None: if sip.isdeleted(self): return while self._pendingActions and self._domDone: - action = self._pendingActions.pop(0) - action() + name, args = self._pendingActions.pop(0) + + if name == "eval": + self._evalWithCallback(*args) + elif name == "setHtml": + self._setHtml(*args) + else: + raise Exception(f"unknown action: {name}") def _openLinksExternally(self, url: str) -> None: openLink(url) @@ -677,7 +705,9 @@ html {{ {font} }} return if not self._filterSet: - self.focusProxy().installEventFilter(self) + focus_proxy = self.focusProxy() + assert focus_proxy is not None + focus_proxy.installEventFilter(self) self._filterSet = True if cmd == "domDone": diff --git a/qt/bundle/Cargo.lock b/qt/bundle/Cargo.lock index 4a64a0115..544276d6f 100644 --- a/qt/bundle/Cargo.lock +++ b/qt/bundle/Cargo.lock @@ -202,12 +202,13 @@ dependencies = [ [[package]] name = "libmimalloc-sys" -version = "0.1.22" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1b8479c593dba88c2741fc50b92e13dbabbbe0bd504d979f244ccc1a5b1c01" +checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44" dependencies = [ "cc", "cty", + "libc", ] [[package]] @@ -267,9 +268,9 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.26" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb74897ce508e6c49156fd1476fc5922cbc6e75183c65e399c765a09122e5130" +checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633" dependencies = [ "libmimalloc-sys", ] diff --git a/rslib/Cargo.toml b/rslib/Cargo.toml index fb14826b4..1c4abf46f 100644 --- a/rslib/Cargo.toml +++ b/rslib/Cargo.toml @@ -103,7 +103,6 @@ tracing-subscriber.workspace = true unic-ucd-category.workspace = true unicase.workspace = true unicode-normalization.workspace = true -utime.workspace = true zip.workspace = true zstd.workspace = true diff --git a/rslib/io/src/error.rs b/rslib/io/src/error.rs index 6df493940..c66a29a41 100644 --- a/rslib/io/src/error.rs +++ b/rslib/io/src/error.rs @@ -35,6 +35,7 @@ pub enum FileOp { Sync, Metadata, DecodeUtf8Filename, + SetFileTimes, /// For legacy errors without any context. Unknown, } @@ -61,6 +62,7 @@ impl FileIoError { FileOp::Sync => "sync".into(), FileOp::Metadata => "get metadata".into(), FileOp::DecodeUtf8Filename => "decode utf8 filename".into(), + FileOp::SetFileTimes => "set file times".into(), }, self.path.to_string_lossy(), self.source diff --git a/rslib/io/src/lib.rs b/rslib/io/src/lib.rs index 7420da3cb..c1d4c0205 100644 --- a/rslib/io/src/lib.rs +++ b/rslib/io/src/lib.rs @@ -4,8 +4,11 @@ mod error; use std::fs::File; +use std::fs::FileTimes; +use std::fs::OpenOptions; use std::io::Read; use std::io::Seek; +use std::io::Write; use std::path::Component; use std::path::Path; use std::path::PathBuf; @@ -37,6 +40,13 @@ pub fn open_file(path: impl AsRef) -> Result { }) } +pub fn open_file_ext(path: impl AsRef, options: OpenOptions) -> Result { + options.open(&path).context(FileIoSnafu { + path: path.as_ref(), + op: FileOp::Open, + }) +} + /// See [std::fs::write]. pub fn write_file(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result<()> { std::fs::write(&path, contents).context(FileIoSnafu { @@ -45,6 +55,45 @@ pub fn write_file(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result< }) } +pub fn write_file_and_flush( + path: impl AsRef + Clone, + contents: impl AsRef<[u8]>, +) -> Result<()> { + let mut file = create_file(path.clone())?; + file.write_all(contents.as_ref()).context(FileIoSnafu { + path: path.clone().as_ref(), + op: FileOp::Write, + })?; + file.sync_all().context(FileIoSnafu { + path: path.as_ref(), + op: FileOp::Sync, + }) +} + +/// See [File::set_times]. +pub fn set_file_times(path: impl AsRef, times: FileTimes) -> Result<()> { + #[cfg(not(windows))] + let file = open_file(&path)?; + + #[cfg(windows)] + let file = { + use std::os::windows::fs::OpenOptionsExt; + open_file_ext( + &path, + OpenOptions::new() + .write(true) + // It's required to modify the time attributes of a directory in windows system. + .custom_flags(0x02000000) // FILE_FLAG_BACKUP_SEMANTICS + .to_owned(), + )? + }; + + file.set_times(times).context(FileIoSnafu { + path: path.as_ref(), + op: FileOp::SetFileTimes, + }) +} + /// See [std::fs::remove_file]. #[allow(dead_code)] pub fn remove_file(path: impl AsRef) -> Result<()> { diff --git a/rslib/proto/rust.rs b/rslib/proto/rust.rs index 93012874a..85834f19f 100644 --- a/rslib/proto/rust.rs +++ b/rslib/proto/rust.rs @@ -29,13 +29,13 @@ pub fn write_rust_protos(descriptors_path: PathBuf) -> Result { ) .type_attribute( "Deck.Normal.DayLimit", - "#[derive(Copy, Eq, serde::Deserialize, serde::Serialize)]", + "#[derive(Eq, serde::Deserialize, serde::Serialize)]", ) .type_attribute("HelpPageLinkRequest.HelpPage", "#[derive(strum::EnumIter)]") .type_attribute("CsvMetadata.Delimiter", "#[derive(strum::EnumIter)]") .type_attribute( "Preferences.BackupLimits", - "#[derive(Copy, serde::Deserialize, serde::Serialize)]", + "#[derive(serde::Deserialize, serde::Serialize)]", ) .type_attribute( "CsvMetadata.DupeResolution", diff --git a/rslib/src/ankihub/http_client/mod.rs b/rslib/src/ankihub/http_client/mod.rs index e5e374463..00d275c90 100644 --- a/rslib/src/ankihub/http_client/mod.rs +++ b/rslib/src/ankihub/http_client/mod.rs @@ -11,7 +11,7 @@ use serde::Serialize; use crate::ankihub::login::LoginRequest; -static API_VERSION: &str = "18.0"; +static API_VERSION: &str = "19.0"; static DEFAULT_API_URL: &str = "https://app.ankihub.net/api/"; #[derive(Clone)] diff --git a/rslib/src/backend/config.rs b/rslib/src/backend/config.rs index 137124b45..6e0bed153 100644 --- a/rslib/src/backend/config.rs +++ b/rslib/src/backend/config.rs @@ -38,6 +38,7 @@ impl From for BoolKey { BoolKeyProto::ShiftPositionOfExistingCards => BoolKey::ShiftPositionOfExistingCards, BoolKeyProto::RenderLatex => BoolKey::RenderLatex, BoolKeyProto::LoadBalancerEnabled => BoolKey::LoadBalancerEnabled, + BoolKeyProto::FsrsShortTermWithStepsEnabled => BoolKey::FsrsShortTermWithStepsEnabled, } } } diff --git a/rslib/src/backend/error.rs b/rslib/src/backend/error.rs index 9e23c47ce..c538ec1f7 100644 --- a/rslib/src/backend/error.rs +++ b/rslib/src/backend/error.rs @@ -42,7 +42,7 @@ impl AnkiError { AnkiError::InvalidId => Kind::InvalidInput, AnkiError::InvalidMethodIndex | AnkiError::InvalidServiceIndex - | AnkiError::FsrsWeightsInvalid + | AnkiError::FsrsParamsInvalid | AnkiError::FsrsUnableToDetermineDesiredRetention | AnkiError::FsrsInsufficientData => Kind::InvalidInput, #[cfg(windows)] @@ -66,6 +66,7 @@ impl From for Kind { fn from(err: SyncErrorKind) -> Self { match err { SyncErrorKind::AuthFailed => Kind::SyncAuthError, + SyncErrorKind::ServerMessage => Kind::SyncServerMessage, _ => Kind::SyncOtherError, } } diff --git a/rslib/src/browser_table.rs b/rslib/src/browser_table.rs index 19fdb11f6..4680caa0d 100644 --- a/rslib/src/browser_table.rs +++ b/rslib/src/browser_table.rs @@ -111,12 +111,12 @@ impl Card { /// Returns the card's due date as a timestamp if it has one. fn due_time(&self, timing: &SchedTimingToday) -> Option { if self.queue == CardQueue::Learn { - Some(TimestampSecs(self.due as i64)) + Some(TimestampSecs(self.original_or_current_due() as i64)) } else if self.is_due_in_days() { Some( TimestampSecs::now().adding_secs( - ((self.original_or_current_due() - timing.days_elapsed as i32) - .saturating_mul(86400)) as i64, + (self.original_or_current_due() as i64 - timing.days_elapsed as i64) + .saturating_mul(86400), ), ) } else { @@ -128,12 +128,15 @@ impl Card { /// date' or an add-on has changed the due date, this won't be accurate. pub(crate) fn days_since_last_review(&self, timing: &SchedTimingToday) -> Option { if !self.is_due_in_days() { - Some((timing.next_day_at.0 as u32).saturating_sub(self.due.max(0) as u32) / 86_400) + Some( + (timing.next_day_at.0 as u32).saturating_sub(self.original_or_current_due() as u32) + / 86_400, + ) } else { self.due_time(timing).map(|due| { - due.adding_secs(-86_400 * self.interval as i64) - .elapsed_secs() as u32 - / 86_400 + (due.adding_secs(-86_400 * self.interval as i64) + .elapsed_secs() + / 86_400) as u32 }) } } diff --git a/rslib/src/config/bool.rs b/rslib/src/config/bool.rs index c946f7c34..b430babe4 100644 --- a/rslib/src/config/bool.rs +++ b/rslib/src/config/bool.rs @@ -41,6 +41,7 @@ pub enum BoolKey { WithDeckConfigs, Fsrs, LoadBalancerEnabled, + FsrsShortTermWithStepsEnabled, #[strum(to_string = "normalize_note_text")] NormalizeNoteText, #[strum(to_string = "dayLearnFirst")] diff --git a/rslib/src/deckconfig/mod.rs b/rslib/src/deckconfig/mod.rs index 489537d42..21a75f521 100644 --- a/rslib/src/deckconfig/mod.rs +++ b/rslib/src/deckconfig/mod.rs @@ -74,11 +74,12 @@ const DEFAULT_DECK_CONFIG_INNER: DeckConfigInner = DeckConfigInner { bury_new: false, bury_reviews: false, bury_interday_learning: false, - fsrs_weights: vec![], + fsrs_params_4: vec![], + fsrs_params_5: vec![], desired_retention: 0.9, other: Vec::new(), historical_retention: 0.9, - weight_search: String::new(), + param_search: String::new(), ignore_revlogs_before_date: String::new(), easy_days_percentages: Vec::new(), }; @@ -105,6 +106,15 @@ impl DeckConfig { self.mtime_secs = TimestampSecs::now(); self.usn = usn; } + + /// Retrieve the FSRS 5.0 params, falling back on 4.x ones. + pub fn fsrs_params(&self) -> &Vec { + if self.inner.fsrs_params_5.len() == 19 { + &self.inner.fsrs_params_5 + } else { + &self.inner.fsrs_params_4 + } + } } impl Collection { diff --git a/rslib/src/deckconfig/schema11.rs b/rslib/src/deckconfig/schema11.rs index c03c62d14..73800f38e 100644 --- a/rslib/src/deckconfig/schema11.rs +++ b/rslib/src/deckconfig/schema11.rs @@ -69,8 +69,10 @@ pub struct DeckConfSchema11 { #[serde(default)] bury_interday_learning: bool, + #[serde(default, rename = "fsrsWeights")] + fsrs_params_4: Vec, #[serde(default)] - fsrs_weights: Vec, + fsrs_params_5: Vec, #[serde(default)] desired_retention: f32, #[serde(default)] @@ -92,8 +94,8 @@ pub struct DeckConfSchema11 { #[serde(default)] /// historical retention sm2_retention: f32, - #[serde(default)] - weight_search: String, + #[serde(default, rename = "weightSearch")] + param_search: String, #[serde(flatten)] other: HashMap, @@ -306,10 +308,11 @@ impl Default for DeckConfSchema11 { new_sort_order: 0, new_gather_priority: 0, bury_interday_learning: false, - fsrs_weights: vec![], + fsrs_params_4: vec![], + fsrs_params_5: vec![], desired_retention: 0.9, sm2_retention: 0.9, - weight_search: "".to_string(), + param_search: "".to_string(), ignore_revlogs_before_date: "".to_string(), easy_days_percentages: vec![1.0; 7], } @@ -386,12 +389,13 @@ impl From for DeckConfig { bury_new: c.new.bury, bury_reviews: c.rev.bury, bury_interday_learning: c.bury_interday_learning, - fsrs_weights: c.fsrs_weights, + fsrs_params_4: c.fsrs_params_4, + fsrs_params_5: c.fsrs_params_5, ignore_revlogs_before_date: c.ignore_revlogs_before_date, easy_days_percentages: c.easy_days_percentages, desired_retention: c.desired_retention, historical_retention: c.sm2_retention, - weight_search: c.weight_search, + param_search: c.param_search, other: other_bytes, }, } @@ -498,10 +502,11 @@ impl From for DeckConfSchema11 { new_sort_order: i.new_card_sort_order, new_gather_priority: i.new_card_gather_priority, bury_interday_learning: i.bury_interday_learning, - fsrs_weights: i.fsrs_weights, + fsrs_params_4: i.fsrs_params_4, + fsrs_params_5: i.fsrs_params_5, desired_retention: i.desired_retention, sm2_retention: i.historical_retention, - weight_search: i.weight_search, + param_search: i.param_search, ignore_revlogs_before_date: i.ignore_revlogs_before_date, easy_days_percentages: i.easy_days_percentages, } @@ -526,6 +531,7 @@ static RESERVED_DECKCONF_KEYS: Set<&'static str> = phf_set! { "interdayLearningMix", "newGatherPriority", "fsrsWeights", + "fsrsParams5", "desiredRetention", "stopTimerOnAnswer", "secondsToShowQuestion", diff --git a/rslib/src/deckconfig/update.rs b/rslib/src/deckconfig/update.rs index fe7121425..f56d0bf47 100644 --- a/rslib/src/deckconfig/update.rs +++ b/rslib/src/deckconfig/update.rs @@ -21,7 +21,7 @@ use crate::decks::NormalDeck; use crate::prelude::*; use crate::scheduler::fsrs::memory_state::UpdateMemoryStateEntry; use crate::scheduler::fsrs::memory_state::UpdateMemoryStateRequest; -use crate::scheduler::fsrs::weights::ignore_revlogs_before_ms_from_config; +use crate::scheduler::fsrs::params::ignore_revlogs_before_ms_from_config; use crate::search::JoinSearches; use crate::search::Negated; use crate::search::SearchNode; @@ -50,7 +50,7 @@ impl Collection { deck: DeckId, ) -> Result { let mut defaults = DeckConfig::default(); - defaults.inner.fsrs_weights = DEFAULT_PARAMETERS.into(); + defaults.inner.fsrs_params_5 = DEFAULT_PARAMETERS.into(); let last_optimize = self.get_config_i32(I32ConfigKey::LastFsrsOptimize) as u32; let days_since_last_fsrs_optimize = if last_optimize > 0 { self.timing_today()? @@ -88,6 +88,12 @@ impl Collection { // grab the config and sort it let mut config = self.storage.all_deck_config()?; config.sort_unstable_by(|a, b| a.name.cmp(&b.name)); + // pre-fill empty fsrs 5 params with 4 params + config.iter_mut().for_each(|c| { + if c.inner.fsrs_params_5.is_empty() { + c.inner.fsrs_params_5 = c.inner.fsrs_params_4.clone(); + } + }); // combine with use counts let counts = self.get_deck_config_use_counts()?; @@ -153,14 +159,20 @@ impl Collection { configs_after_update.remove(dcid); } - if req.mode == UpdateDeckConfigsMode::ComputeAllWeights { - self.compute_all_weights(&mut req)?; + if req.mode == UpdateDeckConfigsMode::ComputeAllParams { + self.compute_all_params(&mut req)?; } // add/update provided configs for conf in &mut req.configs { + // If the user has provided empty FSRS5 params, zero out any + // old params as well, so we don't fall back on them, which would + // be surprising as they're not shown in the GUI. + if conf.inner.fsrs_params_5.is_empty() { + conf.inner.fsrs_params_4.clear(); + } // check the provided parameters are valid before we save them - FSRS::new(Some(&conf.inner.fsrs_weights))?; + FSRS::new(Some(conf.fsrs_params()))?; self.add_or_update_deck_config(conf)?; configs_after_update.insert(conf.id, conf.clone()); } @@ -195,13 +207,13 @@ impl Collection { if let Ok(normal) = deck.normal() { let deck_id = deck.id; - // previous order & weights + // previous order & params let previous_config_id = DeckConfigId(normal.config_id); let previous_config = configs_before_update.get(&previous_config_id); let previous_order = previous_config .map(|c| c.inner.new_card_insert_order()) .unwrap_or_default(); - let previous_weights = previous_config.map(|c| &c.inner.fsrs_weights); + let previous_params = previous_config.map(|c| c.fsrs_params()); let previous_retention = previous_config.map(|c| c.inner.desired_retention); // if a selected (sub)deck, or its old config was removed, update deck to point @@ -227,11 +239,11 @@ impl Collection { self.sort_deck(deck_id, current_order, usn)?; } - // if weights differ, memory state needs to be recomputed - let current_weights = current_config.map(|c| &c.inner.fsrs_weights); + // if params differ, memory state needs to be recomputed + let current_params = current_config.map(|c| c.fsrs_params()); let current_retention = current_config.map(|c| c.inner.desired_retention); if fsrs_toggled - || previous_weights != current_weights + || previous_params != current_params || previous_retention != current_retention { decks_needing_memory_recompute @@ -249,10 +261,10 @@ impl Collection { .into_iter() .map(|(conf_id, search)| { let config = configs_after_update.get(&conf_id); - let weights = config.and_then(|c| { + let params = config.and_then(|c| { if req.fsrs { Some(UpdateMemoryStateRequest { - weights: c.inner.fsrs_weights.clone(), + params: c.fsrs_params().clone(), desired_retention: c.inner.desired_retention, max_interval: c.inner.maximum_review_interval, reschedule: req.fsrs_reschedule, @@ -262,12 +274,9 @@ impl Collection { None } }); - let search = SearchNode::DeckIdsWithoutChildren(comma_separated_ids(&search)) - .and(SearchNode::State(StateKind::Suspended).negated()) - .try_into_search()?; Ok(UpdateMemoryStateEntry { - req: weights, - search, + req: params, + search: SearchNode::DeckIdsWithoutChildren(comma_separated_ids(&search)), ignore_before: config .map(ignore_revlogs_before_ms_from_config) .unwrap_or(Ok(0.into()))?, @@ -320,7 +329,7 @@ impl Collection { } Ok(()) } - fn compute_all_weights(&mut self, req: &mut UpdateDeckConfigsRequest) -> Result<()> { + fn compute_all_params(&mut self, req: &mut UpdateDeckConfigsRequest) -> Result<()> { require!(req.fsrs, "FSRS must be enabled"); // frontend didn't include any unmodified deck configs, so we need to fill them @@ -335,28 +344,28 @@ impl Collection { // other parts of the code expect the currently-selected preset to come last req.configs.push(previous_last); - // calculate and apply weights to each preset + // calculate and apply params to each preset let config_len = req.configs.len() as u32; for (idx, config) in req.configs.iter_mut().enumerate() { - let search = if config.inner.weight_search.trim().is_empty() { + let search = if config.inner.param_search.trim().is_empty() { SearchNode::Preset(config.name.clone()) .and(SearchNode::State(StateKind::Suspended).negated()) .try_into_search()? .to_string() } else { - config.inner.weight_search.clone() + config.inner.param_search.clone() }; let ignore_revlogs_before_ms = ignore_revlogs_before_ms_from_config(config)?; - match self.compute_weights( + match self.compute_params( &search, ignore_revlogs_before_ms, idx as u32 + 1, config_len, - &config.inner.fsrs_weights, + config.fsrs_params(), ) { - Ok(weights) => { - println!("{}: {:?}", config.name, weights.weights); - config.inner.fsrs_weights = weights.weights; + Ok(params) => { + println!("{}: {:?}", config.name, params.params); + config.inner.fsrs_params_5 = params.params; } Err(AnkiError::Interrupted) => return Err(AnkiError::Interrupted), Err(err) => { diff --git a/rslib/src/decks/filtered.rs b/rslib/src/decks/filtered.rs index dcc8585bb..ef77076af 100644 --- a/rslib/src/decks/filtered.rs +++ b/rslib/src/decks/filtered.rs @@ -60,7 +60,12 @@ fn search_order_label(order: FilteredSearchOrder, tr: &I18n) -> String { FilteredSearchOrder::Added => tr.decks_order_added(), FilteredSearchOrder::Due => tr.decks_order_due(), FilteredSearchOrder::ReverseAdded => tr.decks_latest_added_first(), - FilteredSearchOrder::DuePriority => tr.decks_relative_overdueness(), + FilteredSearchOrder::RetrievabilityAscending => { + tr.deck_config_sort_order_retrievability_ascending() + } + FilteredSearchOrder::RetrievabilityDescending => { + tr.deck_config_sort_order_retrievability_descending() + } } .into() } diff --git a/rslib/src/error/mod.rs b/rslib/src/error/mod.rs index 416690e8b..0da89e0ff 100644 --- a/rslib/src/error/mod.rs +++ b/rslib/src/error/mod.rs @@ -113,7 +113,7 @@ pub enum AnkiError { }, InvalidMethodIndex, InvalidServiceIndex, - FsrsWeightsInvalid, + FsrsParamsInvalid, /// Returned by fsrs-rs; may happen even if 400+ reviews FsrsInsufficientData, /// Generated by our backend if count < 400 @@ -148,7 +148,6 @@ impl AnkiError { tr.card_templates_identical_front(index + 1) } CardTypeErrorDetails::MissingCloze => tr.card_templates_missing_cloze(), - CardTypeErrorDetails::ExtraneousCloze => tr.card_templates_extraneous_cloze(), }; format!("{}
{}", header, details) } @@ -181,7 +180,7 @@ impl AnkiError { AnkiError::FsrsInsufficientReviews { count } => { tr.deck_config_must_have_400_reviews(*count).into() } - AnkiError::FsrsWeightsInvalid => tr.deck_config_invalid_parameters().into(), + AnkiError::FsrsParamsInvalid => tr.deck_config_invalid_parameters().into(), AnkiError::SchedulerUpgradeRequired => { tr.scheduling_update_required().replace("V2", "v3") } @@ -203,7 +202,6 @@ impl AnkiError { CardTypeErrorDetails::Duplicate { .. } => HelpPage::CardTypeDuplicate, CardTypeErrorDetails::NoFrontField => HelpPage::CardTypeNoFrontField, CardTypeErrorDetails::MissingCloze => HelpPage::CardTypeMissingCloze, - CardTypeErrorDetails::ExtraneousCloze => HelpPage::CardTypeExtraneousCloze, }), _ => None, } @@ -323,5 +321,4 @@ pub enum CardTypeErrorDetails { NoFrontField, NoSuchField { field: String }, MissingCloze, - ExtraneousCloze, } diff --git a/rslib/src/import_export/text/csv/metadata.rs b/rslib/src/import_export/text/csv/metadata.rs index 17db20eae..0c624958e 100644 --- a/rslib/src/import_export/text/csv/metadata.rs +++ b/rslib/src/import_export/text/csv/metadata.rs @@ -497,9 +497,9 @@ fn maybe_set_tags_column(metadata: &mut CsvMetadata, meta_columns: &HashSet &'static str { "templates/errors.html#no-field-replacement-on-front-side" } HelpPage::CardTypeMissingCloze => "templates/errors.html#no-cloze-filter-on-cloze-notetype", - HelpPage::CardTypeExtraneousCloze => { - "templates/errors.html#cloze-filter-outside-cloze-notetype" - } HelpPage::Troubleshooting => "troubleshooting.html", } } diff --git a/rslib/src/media/check.rs b/rslib/src/media/check.rs index c87fadb91..bc85b177b 100644 --- a/rslib/src/media/check.rs +++ b/rslib/src/media/check.rs @@ -546,6 +546,7 @@ pub(crate) mod test { use anki_io::create_dir; use anki_io::read_to_string; use anki_io::write_file; + use anki_io::write_file_and_flush; use tempfile::tempdir; use tempfile::TempDir; @@ -696,7 +697,7 @@ Unused: unused.jpg fn unicode_normalization() -> Result<()> { let (_dir, mgr, mut col) = common_setup()?; - write_file(mgr.media_folder.join("ぱぱ.jpg"), "nfd encoding")?; + write_file_and_flush(mgr.media_folder.join("ぱぱ.jpg"), "nfd encoding")?; let mut output = { let mut checker = col.media_checker()?; diff --git a/rslib/src/media/files.rs b/rslib/src/media/files.rs index 2cbef1a89..9fd3bc85f 100644 --- a/rslib/src/media/files.rs +++ b/rslib/src/media/files.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use std::fs; +use std::fs::FileTimes; use std::io; use std::io::Read; use std::path::Path; @@ -12,6 +13,7 @@ use std::time; use anki_io::create_dir; use anki_io::open_file; +use anki_io::set_file_times; use anki_io::write_file; use anki_io::FileIoError; use anki_io::FileIoSnafu; @@ -345,11 +347,9 @@ where fs::rename(&src_path, &dst_path)?; // mark it as modified, so we can expire it in the future - let secs = time::SystemTime::now() - .duration_since(time::UNIX_EPOCH) - .unwrap() - .as_secs() as i64; - if let Err(err) = utime::set_file_times(&dst_path, secs, secs) { + let secs = time::SystemTime::now(); + let times = FileTimes::new().set_accessed(secs).set_modified(secs); + if let Err(err) = set_file_times(&dst_path, times) { // The libc utimes() call fails on (some? all?) Android devices. Since we don't // do automatic expiry yet, we can safely ignore the error. if !cfg!(target_os = "android") { diff --git a/rslib/src/preferences.rs b/rslib/src/preferences.rs index e3b7e6f3c..96be8e461 100644 --- a/rslib/src/preferences.rs +++ b/rslib/src/preferences.rs @@ -99,6 +99,8 @@ impl Collection { .get_config_bool(BoolKey::ShowIntervalsAboveAnswerButtons), time_limit_secs: self.get_answer_time_limit_secs(), load_balancer_enabled: self.get_config_bool(BoolKey::LoadBalancerEnabled), + fsrs_short_term_with_steps_enabled: self + .get_config_bool(BoolKey::FsrsShortTermWithStepsEnabled), }) } @@ -119,7 +121,10 @@ impl Collection { )?; self.set_answer_time_limit_secs(s.time_limit_secs)?; self.set_config_bool_inner(BoolKey::LoadBalancerEnabled, s.load_balancer_enabled)?; - + self.set_config_bool_inner( + BoolKey::FsrsShortTermWithStepsEnabled, + s.fsrs_short_term_with_steps_enabled, + )?; Ok(()) } diff --git a/rslib/src/progress.rs b/rslib/src/progress.rs index c1412251f..a404808bf 100644 --- a/rslib/src/progress.rs +++ b/rslib/src/progress.rs @@ -15,8 +15,8 @@ use crate::import_export::ExportProgress; use crate::import_export::ImportProgress; use crate::prelude::Collection; use crate::scheduler::fsrs::memory_state::ComputeMemoryProgress; +use crate::scheduler::fsrs::params::ComputeParamsProgress; use crate::scheduler::fsrs::retention::ComputeRetentionProgress; -use crate::scheduler::fsrs::weights::ComputeWeightsProgress; use crate::sync::collection::normal::NormalSyncProgress; use crate::sync::collection::progress::FullSyncProgress; use crate::sync::collection::progress::SyncStage; @@ -131,7 +131,7 @@ pub enum Progress { DatabaseCheck(DatabaseCheckProgress), Import(ImportProgress), Export(ExportProgress), - ComputeWeights(ComputeWeightsProgress), + ComputeParams(ComputeParamsProgress), ComputeRetention(ComputeRetentionProgress), ComputeMemory(ComputeMemoryProgress), } @@ -209,8 +209,8 @@ pub(crate) fn progress_to_proto( } .into(), ), - Progress::ComputeWeights(progress) => { - Value::ComputeWeights(anki_proto::collection::ComputeWeightsProgress { + Progress::ComputeParams(progress) => { + Value::ComputeParams(anki_proto::collection::ComputeParamsProgress { current: progress.current_iteration, total: progress.total_iterations, reviews: progress.reviews, @@ -296,9 +296,9 @@ impl From for Progress { } } -impl From for Progress { - fn from(p: ComputeWeightsProgress) -> Self { - Progress::ComputeWeights(p) +impl From for Progress { + fn from(p: ComputeParamsProgress) -> Self { + Progress::ComputeParams(p) } } diff --git a/rslib/src/revlog/mod.rs b/rslib/src/revlog/mod.rs index 128beab64..ad7f30261 100644 --- a/rslib/src/revlog/mod.rs +++ b/rslib/src/revlog/mod.rs @@ -72,6 +72,7 @@ pub enum RevlogReviewKind { /// disabled. Filtered = 3, Manual = 4, + Rescheduled = 5, } impl RevlogEntry { @@ -86,11 +87,32 @@ impl RevlogEntry { } impl Collection { + // set due date or reset pub(crate) fn log_manually_scheduled_review( &mut self, card: &Card, original_interval: u32, usn: Usn, + ) -> Result<()> { + self.log_scheduled_review(card, original_interval, usn, RevlogReviewKind::Manual) + } + + // reschedule cards on change + pub(crate) fn log_rescheduled_review( + &mut self, + card: &Card, + original_interval: u32, + usn: Usn, + ) -> Result<()> { + self.log_scheduled_review(card, original_interval, usn, RevlogReviewKind::Rescheduled) + } + + fn log_scheduled_review( + &mut self, + card: &Card, + original_interval: u32, + usn: Usn, + review_kind: RevlogReviewKind, ) -> Result<()> { let ease_factor = u32::from( card.memory_state @@ -106,7 +128,7 @@ impl Collection { last_interval: i32::try_from(original_interval).unwrap_or(i32::MAX), ease_factor, taken_millis: 0, - review_kind: RevlogReviewKind::Manual, + review_kind, }; self.add_revlog_entry_undoable(entry)?; Ok(()) diff --git a/rslib/src/scheduler/answering/mod.rs b/rslib/src/scheduler/answering/mod.rs index 5e85dce1f..2d3c34a28 100644 --- a/rslib/src/scheduler/answering/mod.rs +++ b/rslib/src/scheduler/answering/mod.rs @@ -14,7 +14,7 @@ use rand::prelude::*; use rand::rngs::StdRng; use revlog::RevlogEntryPartial; -use super::fsrs::weights::ignore_revlogs_before_ms_from_config; +use super::fsrs::params::ignore_revlogs_before_ms_from_config; use super::queue::BuryMode; use super::states::load_balancer::LoadBalancerContext; use super::states::steps::LearningSteps; @@ -73,6 +73,7 @@ struct CardStateUpdater { fsrs_next_states: Option, /// Set if FSRS is enabled. desired_retention: Option, + fsrs_short_term_with_steps: bool, } impl CardStateUpdater { @@ -110,6 +111,7 @@ impl CardStateUpdater { Default::default() }, fsrs_next_states: self.fsrs_next_states.clone(), + fsrs_short_term_with_steps_enabled: self.fsrs_short_term_with_steps, } } @@ -429,9 +431,9 @@ impl Collection { let config = self.home_deck_config(deck.config_id(), card.original_deck_id)?; let fsrs_enabled = self.get_config_bool(BoolKey::Fsrs); let fsrs_next_states = if fsrs_enabled { - let fsrs = FSRS::new(Some(&config.inner.fsrs_weights))?; + let fsrs = FSRS::new(Some(config.fsrs_params()))?; if card.memory_state.is_none() && card.ctype != CardType::New { - // Card has been moved or imported into an FSRS deck after weights were set, + // Card has been moved or imported into an FSRS deck after params were set, // and will need its initial memory state to be calculated based on review // history. let revlog = self.revlog_for_srs(SearchNode::CardIds(card.id.to_string()))?; @@ -458,6 +460,8 @@ impl Collection { None }; let desired_retention = fsrs_enabled.then_some(config.inner.desired_retention); + let fsrs_short_term_with_steps = + self.get_config_bool(BoolKey::FsrsShortTermWithStepsEnabled); Ok(CardStateUpdater { fuzz_seed: get_fuzz_seed(&card, false), card, @@ -467,6 +471,7 @@ impl Collection { now: TimestampSecs::now(), fsrs_next_states, desired_retention, + fsrs_short_term_with_steps, }) } @@ -639,10 +644,9 @@ mod test { // new->learning let post_answer = col.answer_again(); - assert_eq!( - post_answer.new_state, - current_state(&mut col, post_answer.card_id) - ); + let mut current = current_state(&mut col, post_answer.card_id); + col.set_elapsed_secs_equal(&post_answer.new_state, &mut current); + assert_eq!(post_answer.new_state, current); let card = col.storage.get_card(post_answer.card_id)?.unwrap(); assert_eq!(card.queue, CardQueue::Learn); assert_eq!(card.remaining_steps, 2); @@ -651,10 +655,9 @@ mod test { col.storage.db.execute_batch("update cards set due=0")?; col.clear_study_queues(); let post_answer = col.answer_good(); - assert_eq!( - post_answer.new_state, - current_state(&mut col, post_answer.card_id) - ); + let mut current = current_state(&mut col, post_answer.card_id); + col.set_elapsed_secs_equal(&post_answer.new_state, &mut current); + assert_eq!(post_answer.new_state, current); let card = col.storage.get_card(post_answer.card_id)?.unwrap(); assert_eq!(card.queue, CardQueue::Learn); assert_eq!(card.remaining_steps, 1); @@ -667,10 +670,9 @@ mod test { if let CardState::Normal(NormalState::Review(state)) = &mut post_answer.new_state { state.elapsed_days = 1; }; - assert_eq!( - post_answer.new_state, - current_state(&mut col, post_answer.card_id) - ); + let mut current = current_state(&mut col, post_answer.card_id); + col.set_elapsed_secs_equal(&post_answer.new_state, &mut current); + assert_eq!(post_answer.new_state, current); let card = col.storage.get_card(post_answer.card_id)?.unwrap(); assert_eq!(card.queue, CardQueue::Review); assert_eq!(card.interval, 1); @@ -683,10 +685,9 @@ mod test { if let CardState::Normal(NormalState::Review(state)) = &mut post_answer.new_state { state.elapsed_days = 4; }; - assert_eq!( - post_answer.new_state, - current_state(&mut col, post_answer.card_id) - ); + let mut current = current_state(&mut col, post_answer.card_id); + col.set_elapsed_secs_equal(&post_answer.new_state, &mut current); + assert_eq!(post_answer.new_state, current); let card = col.storage.get_card(post_answer.card_id)?.unwrap(); assert_eq!(card.queue, CardQueue::Review); assert_eq!(card.interval, 4); @@ -699,10 +700,9 @@ mod test { if let CardState::Normal(NormalState::Relearning(state)) = &mut post_answer.new_state { state.review.elapsed_days = 1; }; - assert_eq!( - post_answer.new_state, - current_state(&mut col, post_answer.card_id) - ); + let mut current = current_state(&mut col, post_answer.card_id); + col.set_elapsed_secs_equal(&post_answer.new_state, &mut current); + assert_eq!(post_answer.new_state, current); let card = col.storage.get_card(post_answer.card_id)?.unwrap(); assert_eq!(card.queue, CardQueue::Learn); assert_eq!(card.ctype, CardType::Relearn); @@ -717,10 +717,9 @@ mod test { if let CardState::Normal(NormalState::Relearning(state)) = &mut post_answer.new_state { state.review.elapsed_days = 1; }; - assert_eq!( - post_answer.new_state, - current_state(&mut col, post_answer.card_id) - ); + let mut current = current_state(&mut col, post_answer.card_id); + col.set_elapsed_secs_equal(&post_answer.new_state, &mut current); + assert_eq!(post_answer.new_state, current); let card = col.storage.get_card(post_answer.card_id)?.unwrap(); assert_eq!(card.queue, CardQueue::Learn); assert_eq!(card.lapses, 1); @@ -732,10 +731,9 @@ mod test { if let CardState::Normal(NormalState::Review(state)) = &mut post_answer.new_state { state.elapsed_days = 1; }; - assert_eq!( - post_answer.new_state, - current_state(&mut col, post_answer.card_id) - ); + let mut current = current_state(&mut col, post_answer.card_id); + col.set_elapsed_secs_equal(&post_answer.new_state, &mut current); + assert_eq!(post_answer.new_state, current); let card = col.storage.get_card(post_answer.card_id)?.unwrap(); assert_eq!(card.queue, CardQueue::Review); assert_eq!(card.interval, 1); diff --git a/rslib/src/scheduler/fsrs/error.rs b/rslib/src/scheduler/fsrs/error.rs index 5c9f030ee..d5b596a36 100644 --- a/rslib/src/scheduler/fsrs/error.rs +++ b/rslib/src/scheduler/fsrs/error.rs @@ -12,10 +12,10 @@ impl From for AnkiError { FSRSError::NotEnoughData => AnkiError::FsrsInsufficientData, FSRSError::OptimalNotFound => AnkiError::FsrsUnableToDetermineDesiredRetention, FSRSError::Interrupted => AnkiError::Interrupted, - FSRSError::InvalidParameters => AnkiError::FsrsWeightsInvalid, + FSRSError::InvalidParameters => AnkiError::FsrsParamsInvalid, FSRSError::InvalidInput => AnkiError::InvalidInput { source: InvalidInputError { - message: "invalid weights provided".to_string(), + message: "invalid params provided".to_string(), source: None, backtrace: None, }, diff --git a/rslib/src/scheduler/fsrs/memory_state.rs b/rslib/src/scheduler/fsrs/memory_state.rs index ce20d8925..4f1ea90c8 100644 --- a/rslib/src/scheduler/fsrs/memory_state.rs +++ b/rslib/src/scheduler/fsrs/memory_state.rs @@ -9,16 +9,15 @@ use fsrs::MemoryState; use fsrs::FSRS; use itertools::Itertools; -use super::weights::ignore_revlogs_before_ms_from_config; +use super::params::ignore_revlogs_before_ms_from_config; use crate::card::CardType; use crate::prelude::*; use crate::revlog::RevlogEntry; use crate::revlog::RevlogReviewKind; -use crate::scheduler::fsrs::weights::single_card_revlog_to_items; -use crate::scheduler::fsrs::weights::Weights; +use crate::scheduler::fsrs::params::single_card_revlog_to_items; +use crate::scheduler::fsrs::params::Params; use crate::scheduler::states::fuzz::with_review_fuzz; use crate::search::Negated; -use crate::search::Node; use crate::search::SearchNode; use crate::search::StateKind; @@ -30,7 +29,7 @@ pub struct ComputeMemoryProgress { #[derive(Debug)] pub(crate) struct UpdateMemoryStateRequest { - pub weights: Weights, + pub params: Params, pub desired_retention: f32, pub historical_retention: f32, pub max_interval: u32, @@ -39,15 +38,15 @@ pub(crate) struct UpdateMemoryStateRequest { pub(crate) struct UpdateMemoryStateEntry { pub req: Option, - pub search: Node, + pub search: SearchNode, pub ignore_before: TimestampMillis, } impl Collection { - /// For each provided set of weights, locate cards with the provided search, + /// For each provided set of params, locate cards with the provided search, /// and update their memory state. /// Should be called inside a transaction. - /// If Weights are None, it means the user disabled FSRS, and the existing + /// If Params are None, it means the user disabled FSRS, and the existing /// memory state should be removed. pub(crate) fn update_memory_state( &mut self, @@ -61,7 +60,8 @@ impl Collection { ignore_before, } in entries { - let search = SearchBuilder::all([search, SearchNode::State(StateKind::New).negated()]); + let search = + SearchBuilder::all([search.into(), SearchNode::State(StateKind::New).negated()]); let revlog = self.revlog_for_srs(search)?; let reschedule = req.as_ref().map(|e| e.reschedule).unwrap_or_default(); let last_revlog_info = if reschedule { @@ -69,7 +69,7 @@ impl Collection { } else { None }; - let fsrs = FSRS::new(req.as_ref().map(|w| &w.weights[..]).or(Some([].as_slice())))?; + let fsrs = FSRS::new(req.as_ref().map(|w| &w.params[..]).or(Some([].as_slice())))?; let historical_retention = req.as_ref().map(|w| w.historical_retention); let items = fsrs_items_for_memory_state( &fsrs, @@ -119,9 +119,10 @@ impl Collection { }; *due = (timing.days_elapsed as i32) - days_elapsed + card.interval as i32; - // Add a manual revlog entry if the last entry wasn't manual - if !last_info.last_revlog_is_manual { - self.log_manually_scheduled_review( + // Add a rescheduled revlog entry if the last entry wasn't + // rescheduled + if !last_info.last_revlog_is_rescheduled { + self.log_rescheduled_review( &card, original_interval, usn, @@ -153,7 +154,7 @@ impl Collection { .or_not_found(conf_id)?; let desired_retention = config.inner.desired_retention; let historical_retention = config.inner.historical_retention; - let fsrs = FSRS::new(Some(&config.inner.fsrs_weights))?; + let fsrs = FSRS::new(Some(config.fsrs_params()))?; let revlog = self.revlog_for_srs(SearchNode::CardIds(card.id.to_string()))?; let item = single_card_revlog_to_item( &fsrs, @@ -237,7 +238,7 @@ struct LastRevlogInfo { last_reviewed_at: Option, /// If true, the last action on this card was a reschedule, so we /// can avoid writing an extra revlog entry on another reschedule. - last_revlog_is_manual: bool, + last_revlog_is_rescheduled: bool, } /// Return a map of cards to info about last review/reschedule. @@ -249,18 +250,18 @@ fn get_last_revlog_info(revlogs: &[RevlogEntry]) -> HashMap= 1 { last_reviewed_at = Some(e.id.as_secs()); } - last_revlog_is_manual = e.review_kind == RevlogReviewKind::Manual; + last_revlog_is_rescheduled = e.review_kind == RevlogReviewKind::Rescheduled; } out.insert( card_id, LastRevlogInfo { last_reviewed_at, - last_revlog_is_manual, + last_revlog_is_rescheduled, }, ); }); @@ -285,7 +286,8 @@ pub(crate) fn single_card_revlog_to_item( } let first_review = entries .iter() - .find(|e| e.button_chosen > 0) + // ignore manual and rescheduled revlogs and revlogs before the cutoff + .find(|e| e.button_chosen > 0 && e.id.0 >= ignore_revlogs_before.0) .map(|e| FirstReview { interval: e.interval.max(1) as f32, ease_factor: if e.ease_factor == 0 { @@ -306,11 +308,15 @@ pub(crate) fn single_card_revlog_to_item( })) } else if let Some(first_review) = first_review { // the revlog has been truncated, but not fully - let starting_state = fsrs.memory_state_from_sm2( + let mut starting_state = fsrs.memory_state_from_sm2( first_review.ease_factor, first_review.interval, historical_retention, )?; + // if the ease factor is less than 1.1, the revlog entry is generated by FSRS + if first_review.ease_factor <= 1.1 { + starting_state.difficulty = (first_review.ease_factor - 0.1) * 9.0 + 1.0; + } item.reviews.remove(0); if item.reviews.is_empty() { Ok(None) @@ -321,7 +327,7 @@ pub(crate) fn single_card_revlog_to_item( })) } } else { - // only manual rescheduling; treat like empty + // only manual and rescheduled revlogs; treat like empty Ok(None) } } else { @@ -336,8 +342,8 @@ mod tests { use super::*; use crate::card::FsrsMemoryState; use crate::revlog::RevlogReviewKind; - use crate::scheduler::fsrs::weights::tests::convert; - use crate::scheduler::fsrs::weights::tests::revlog; + use crate::scheduler::fsrs::params::tests::convert; + use crate::scheduler::fsrs::params::tests::revlog; /// Floating point precision can vary between platforms, and each FSRS /// update tends to result in small changes to these numbers, so we diff --git a/rslib/src/scheduler/fsrs/mod.rs b/rslib/src/scheduler/fsrs/mod.rs index 8a0bd51fe..aa324dddb 100644 --- a/rslib/src/scheduler/fsrs/mod.rs +++ b/rslib/src/scheduler/fsrs/mod.rs @@ -2,7 +2,7 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html mod error; pub mod memory_state; +pub mod params; pub mod retention; pub mod simulator; pub mod try_collect; -pub mod weights; diff --git a/rslib/src/scheduler/fsrs/weights.rs b/rslib/src/scheduler/fsrs/params.rs similarity index 85% rename from rslib/src/scheduler/fsrs/weights.rs rename to rslib/src/scheduler/fsrs/params.rs index 51bd7107d..e01b260d6 100644 --- a/rslib/src/scheduler/fsrs/weights.rs +++ b/rslib/src/scheduler/fsrs/params.rs @@ -1,14 +1,16 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +use std::collections::HashMap; use std::iter; use std::path::Path; use std::thread; use std::time::Duration; use anki_io::write_file; -use anki_proto::scheduler::ComputeFsrsWeightsResponse; +use anki_proto::scheduler::ComputeFsrsParamsResponse; use anki_proto::stats::revlog_entry; -use anki_proto::stats::RevlogEntries; +use anki_proto::stats::Dataset; +use anki_proto::stats::DeckEntry; use chrono::NaiveDate; use chrono::NaiveTime; use fsrs::CombinedProgressState; @@ -19,6 +21,7 @@ use fsrs::FSRS; use itertools::Itertools; use prost::Message; +use crate::decks::immediate_parent_name; use crate::prelude::*; use crate::revlog::RevlogEntry; use crate::revlog::RevlogReviewKind; @@ -26,7 +29,7 @@ use crate::search::Node; use crate::search::SearchNode; use crate::search::SortMode; -pub(crate) type Weights = Vec; +pub(crate) type Params = Vec; fn ignore_revlogs_before_date_to_ms( ignore_revlogs_before_date: &String, @@ -50,15 +53,15 @@ impl Collection { /// Note this does not return an error if there are less than 400 items - /// the caller should instead check the fsrs_items count in the return /// value. - pub fn compute_weights( + pub fn compute_params( &mut self, search: &str, ignore_revlogs_before: TimestampMillis, current_preset: u32, total_presets: u32, - current_weights: &Weights, - ) -> Result { - let mut anki_progress = self.new_progress_handler::(); + current_params: &Params, + ) -> Result { + let mut anki_progress = self.new_progress_handler::(); let timing = self.timing_today()?; let revlogs = self.revlog_for_srs(search)?; let (items, review_count) = @@ -66,8 +69,8 @@ impl Collection { let fsrs_items = items.len() as u32; if fsrs_items == 0 { - return Ok(ComputeFsrsWeightsResponse { - weights: current_weights.to_vec(), + return Ok(ComputeFsrsParamsResponse { + params: current_params.to_vec(), fsrs_items, }); } @@ -78,7 +81,7 @@ impl Collection { // adapt the progress handler to our built-in progress handling let progress = CombinedProgressState::new_shared(); let progress2 = progress.clone(); - thread::spawn(move || { + let progress_thread = thread::spawn(move || { let mut finished = false; while !finished { thread::sleep(Duration::from_millis(100)); @@ -94,19 +97,18 @@ impl Collection { } } }); - let fsrs = FSRS::new(Some(current_weights))?; - let current_rmse = fsrs.evaluate(items.clone(), |_| true)?.rmse_bins; - let mut weights = fsrs.compute_parameters(items.clone(), Some(progress2))?; - let optimized_fsrs = FSRS::new(Some(&weights))?; - let optimized_rmse = optimized_fsrs.evaluate(items.clone(), |_| true)?.rmse_bins; - if current_rmse <= optimized_rmse { - weights = current_weights.to_vec(); + let mut params = FSRS::new(None)?.compute_parameters(items.clone(), Some(progress2))?; + progress_thread.join().ok(); + if let Ok(fsrs) = FSRS::new(Some(current_params)) { + let current_rmse = fsrs.evaluate(items.clone(), |_| true)?.rmse_bins; + let optimized_fsrs = FSRS::new(Some(¶ms))?; + let optimized_rmse = optimized_fsrs.evaluate(items.clone(), |_| true)?.rmse_bins; + if current_rmse <= optimized_rmse { + params = current_params.to_vec(); + } } - Ok(ComputeFsrsWeightsResponse { - weights, - fsrs_items, - }) + Ok(ComputeFsrsParamsResponse { params, fsrs_items }) } pub(crate) fn revlog_for_srs( @@ -127,34 +129,63 @@ impl Collection { } /// Used for exporting revlogs for algorithm research. - pub fn export_revlog_entries_to_protobuf( - &mut self, - min_entries: usize, - target_path: &Path, - ) -> Result<()> { - let entries = self.storage.get_all_revlog_entries_in_card_order()?; - if entries.len() < min_entries { + pub fn export_dataset(&mut self, min_entries: usize, target_path: &Path) -> Result<()> { + let revlog_entries = self.storage.get_revlog_entries_for_export_dataset()?; + if revlog_entries.len() < min_entries { return Err(AnkiError::FsrsInsufficientData); } - let entries = entries.into_iter().map(revlog_entry_to_proto).collect_vec(); + let revlogs = revlog_entries + .into_iter() + .map(revlog_entry_to_proto) + .collect_vec(); + let cards = self.storage.get_all_card_entries()?; + + let decks_map = self.storage.get_decks_map()?; + let deck_name_to_id: HashMap = decks_map + .into_iter() + .map(|(id, deck)| (deck.name.to_string(), id)) + .collect(); + + let decks = self + .storage + .get_all_decks()? + .into_iter() + .filter_map(|deck| { + if let Some(preset_id) = deck.config_id().map(|id| id.0) { + let parent_id = immediate_parent_name(&deck.name.to_string()) + .and_then(|parent_name| deck_name_to_id.get(parent_name)) + .map(|id| id.0) + .unwrap_or(0); + Some(DeckEntry { + id: deck.id.0, + parent_id, + preset_id, + }) + } else { + None + } + }) + .collect_vec(); let next_day_at = self.timing_today()?.next_day_at.0; - let entries = RevlogEntries { - entries, + let dataset = Dataset { + revlogs, + cards, + decks, next_day_at, }; - let data = entries.encode_to_vec(); + let data = dataset.encode_to_vec(); write_file(target_path, data)?; Ok(()) } - pub fn evaluate_weights( + pub fn evaluate_params( &mut self, - weights: &Weights, + params: &Params, search: &str, ignore_revlogs_before: TimestampMillis, ) -> Result { let timing = self.timing_today()?; - let mut anki_progress = self.new_progress_handler::(); + let mut anki_progress = self.new_progress_handler::(); let guard = self.search_cards_into_table(search, SortMode::NoOrder)?; let revlogs: Vec = guard .col @@ -163,7 +194,7 @@ impl Collection { let (items, review_count) = fsrs_items_for_training(revlogs, timing.next_day_at, ignore_revlogs_before); anki_progress.state.reviews = review_count as u32; - let fsrs = FSRS::new(Some(weights))?; + let fsrs = FSRS::new(Some(params))?; Ok(fsrs.evaluate(items, |ip| { anki_progress .update(false, |p| { @@ -176,13 +207,13 @@ impl Collection { } #[derive(Default, Clone, Copy, Debug)] -pub struct ComputeWeightsProgress { +pub struct ComputeParamsProgress { pub current_iteration: u32, pub total_iterations: u32, pub reviews: u32, - /// Only used in 'compute all weights' case + /// Only used in 'compute all params' case pub current_preset: u32, - /// Only used in 'compute all weights' case + /// Only used in 'compute all params' case pub total_presets: u32, } @@ -261,6 +292,9 @@ pub(crate) fn single_card_revlog_to_items( Some(RevlogEntry { review_kind: RevlogReviewKind::Manual, .. + }) | Some(RevlogEntry { + review_kind: RevlogReviewKind::Rescheduled, + .. }) ); } @@ -310,10 +344,12 @@ pub(crate) fn single_card_revlog_to_items( // Filter out unwanted entries entries.retain(|entry| { !( - // manually rescheduled + // set due date, reset or rescheduled (entry.review_kind == RevlogReviewKind::Manual || entry.button_chosen == 0) || // cram (entry.review_kind == RevlogReviewKind::Filtered && entry.ease_factor == 0) + || // rescheduled + (entry.review_kind == RevlogReviewKind::Rescheduled) ) }); @@ -374,6 +410,7 @@ fn revlog_entry_to_proto(e: RevlogEntry) -> anki_proto::stats::RevlogEntry { RevlogReviewKind::Relearning => revlog_entry::ReviewKind::Relearning, RevlogReviewKind::Filtered => revlog_entry::ReviewKind::Filtered, RevlogReviewKind::Manual => revlog_entry::ReviewKind::Manual, + RevlogReviewKind::Rescheduled => revlog_entry::ReviewKind::Rescheduled, } as i32, } } diff --git a/rslib/src/scheduler/fsrs/retention.rs b/rslib/src/scheduler/fsrs/retention.rs index 60506c233..a408b6013 100644 --- a/rslib/src/scheduler/fsrs/retention.rs +++ b/rslib/src/scheduler/fsrs/retention.rs @@ -54,7 +54,7 @@ impl Collection { learn_limit, review_limit: usize::MAX, }, - &req.weights, + &req.params, |ip| { anki_progress .update(false, |p| { @@ -63,7 +63,7 @@ impl Collection { .is_ok() }, )? - .clamp(0.75, 0.95)) + .clamp(0.7, 0.95)) } pub fn get_optimal_retention_parameters( @@ -84,7 +84,8 @@ impl From for fsrs::RevlogReviewKind { crate::revlog::RevlogReviewKind::Review => fsrs::RevlogReviewKind::Review, crate::revlog::RevlogReviewKind::Relearning => fsrs::RevlogReviewKind::Relearning, crate::revlog::RevlogReviewKind::Filtered => fsrs::RevlogReviewKind::Filtered, - crate::revlog::RevlogReviewKind::Manual => fsrs::RevlogReviewKind::Manual, + crate::revlog::RevlogReviewKind::Manual + | crate::revlog::RevlogReviewKind::Rescheduled => fsrs::RevlogReviewKind::Manual, } } } diff --git a/rslib/src/scheduler/fsrs/simulator.rs b/rslib/src/scheduler/fsrs/simulator.rs index 9b1c0d072..70a6d9001 100644 --- a/rslib/src/scheduler/fsrs/simulator.rs +++ b/rslib/src/scheduler/fsrs/simulator.rs @@ -54,7 +54,7 @@ impl Collection { daily_time_cost, ) = simulate( &config, - &req.weights, + &req.params, req.desired_retention, None, Some(converted_cards), diff --git a/rslib/src/scheduler/queue/builder/mod.rs b/rslib/src/scheduler/queue/builder/mod.rs index a95d056a8..c707f8df1 100644 --- a/rslib/src/scheduler/queue/builder/mod.rs +++ b/rslib/src/scheduler/queue/builder/mod.rs @@ -464,7 +464,7 @@ mod test { cards.push(card); } col.update_cards_maybe_undoable(cards, false)?; - col.set_deck_review_order(&mut deck, ReviewCardOrder::RelativeOverdueness); + col.set_deck_review_order(&mut deck, ReviewCardOrder::RetrievabilityAscending); assert_eq!(col.queue_as_due_and_ivl(deck.id), expected_queue); Ok(()) diff --git a/rslib/src/scheduler/service/mod.rs b/rslib/src/scheduler/service/mod.rs index 9cce0c44e..59a9aa503 100644 --- a/rslib/src/scheduler/service/mod.rs +++ b/rslib/src/scheduler/service/mod.rs @@ -7,7 +7,7 @@ mod states; use anki_proto::cards; use anki_proto::generic; use anki_proto::scheduler; -use anki_proto::scheduler::ComputeFsrsWeightsResponse; +use anki_proto::scheduler::ComputeFsrsParamsResponse; use anki_proto::scheduler::ComputeMemoryStateResponse; use anki_proto::scheduler::ComputeOptimalRetentionRequest; use anki_proto::scheduler::ComputeOptimalRetentionResponse; @@ -254,16 +254,16 @@ impl crate::services::SchedulerService for Collection { self.custom_study_defaults(input.deck_id.into()) } - fn compute_fsrs_weights( + fn compute_fsrs_params( &mut self, - input: scheduler::ComputeFsrsWeightsRequest, - ) -> Result { - self.compute_weights( + input: scheduler::ComputeFsrsParamsRequest, + ) -> Result { + self.compute_params( &input.search, input.ignore_revlogs_before_ms.into(), 1, 1, - &input.current_weights, + &input.current_params, ) } @@ -283,16 +283,16 @@ impl crate::services::SchedulerService for Collection { }) } - fn evaluate_weights( + fn evaluate_params( &mut self, - input: scheduler::EvaluateWeightsRequest, - ) -> Result { - let ret = self.evaluate_weights( - &input.weights, + input: scheduler::EvaluateParamsRequest, + ) -> Result { + let ret = self.evaluate_params( + &input.params, &input.search, input.ignore_revlogs_before_ms.into(), )?; - Ok(scheduler::EvaluateWeightsResponse { + Ok(scheduler::EvaluateParamsResponse { log_loss: ret.log_loss, rmse_bins: ret.rmse_bins, }) @@ -339,20 +339,17 @@ impl crate::services::SchedulerService for Collection { } impl crate::services::BackendSchedulerService for Backend { - fn compute_fsrs_weights_from_items( + fn compute_fsrs_params_from_items( &self, - req: scheduler::ComputeFsrsWeightsFromItemsRequest, - ) -> Result { + req: scheduler::ComputeFsrsParamsFromItemsRequest, + ) -> Result { let fsrs = FSRS::new(None)?; let fsrs_items = req.items.len() as u32; - let weights = fsrs.compute_parameters( + let params = fsrs.compute_parameters( req.items.into_iter().map(fsrs_item_proto_to_fsrs).collect(), None, )?; - Ok(ComputeFsrsWeightsResponse { - weights, - fsrs_items, - }) + Ok(ComputeFsrsParamsResponse { params, fsrs_items }) } fn fsrs_benchmark( @@ -365,8 +362,17 @@ impl crate::services::BackendSchedulerService for Backend { .into_iter() .map(fsrs_item_proto_to_fsrs) .collect(); - let weights = fsrs.benchmark(train_set); - Ok(FsrsBenchmarkResponse { weights }) + let params = fsrs.benchmark(train_set); + Ok(FsrsBenchmarkResponse { params }) + } + + fn export_dataset(&self, req: scheduler::ExportDatasetRequest) -> Result<()> { + self.with_col(|col| { + col.export_dataset( + req.min_entries.try_into().unwrap(), + req.target_path.as_ref(), + ) + }) } } diff --git a/rslib/src/scheduler/states/learning.rs b/rslib/src/scheduler/states/learning.rs index 83a2f7b32..0dc5782be 100644 --- a/rslib/src/scheduler/states/learning.rs +++ b/rslib/src/scheduler/states/learning.rs @@ -49,7 +49,11 @@ impl LearnState { } else { let (minimum, maximum) = ctx.min_and_max_review_intervals(1); let (interval, short_term) = if let Some(states) = &ctx.fsrs_next_states { - (states.again.interval, states.again.interval < 0.5) + ( + states.again.interval, + (ctx.fsrs_short_term_with_steps_enabled || ctx.steps.is_empty()) + && states.again.interval < 0.5, + ) } else { (ctx.graduating_interval_good as f32, false) }; @@ -91,7 +95,11 @@ impl LearnState { } else { let (minimum, maximum) = ctx.min_and_max_review_intervals(1); let (interval, short_term) = if let Some(states) = &ctx.fsrs_next_states { - (states.hard.interval, states.hard.interval < 0.5) + ( + states.hard.interval, + (ctx.fsrs_short_term_with_steps_enabled || ctx.steps.is_empty()) + && states.hard.interval < 0.5, + ) } else { (ctx.graduating_interval_good as f32, false) }; @@ -133,7 +141,11 @@ impl LearnState { } else { let (minimum, maximum) = ctx.min_and_max_review_intervals(1); let (interval, short_term) = if let Some(states) = &ctx.fsrs_next_states { - (states.good.interval, states.good.interval < 0.5) + ( + states.good.interval, + (ctx.fsrs_short_term_with_steps_enabled || ctx.steps.is_empty()) + && states.good.interval < 0.5, + ) } else { (ctx.graduating_interval_good as f32, false) }; diff --git a/rslib/src/scheduler/states/load_balancer.rs b/rslib/src/scheduler/states/load_balancer.rs index 5ba2103e1..6cd59d538 100644 --- a/rslib/src/scheduler/states/load_balancer.rs +++ b/rslib/src/scheduler/states/load_balancer.rs @@ -226,8 +226,8 @@ impl LoadBalancer { .collect::>(); let expected_distribution = check_review_distribution(&review_counts, &percentages); - // calculate weights for each day - let intervals_and_weights = interval_days + // calculate params for each day + let intervals_and_params = interval_days .iter() .enumerate() .map(|(interval_index, interval_day)| { @@ -262,10 +262,10 @@ impl LoadBalancer { let mut rng = StdRng::seed_from_u64(fuzz_seed?); let weighted_intervals = - WeightedIndex::new(intervals_and_weights.iter().map(|k| k.1)).ok()?; + WeightedIndex::new(intervals_and_params.iter().map(|k| k.1)).ok()?; let selected_interval_index = weighted_intervals.sample(&mut rng); - Some(intervals_and_weights[selected_interval_index].0) + Some(intervals_and_params[selected_interval_index].0) } pub fn add_card(&mut self, cid: CardId, nid: NoteId, dcid: DeckConfigId, interval: u32) { diff --git a/rslib/src/scheduler/states/mod.rs b/rslib/src/scheduler/states/mod.rs index b721e1da2..527298a87 100644 --- a/rslib/src/scheduler/states/mod.rs +++ b/rslib/src/scheduler/states/mod.rs @@ -88,6 +88,7 @@ pub(crate) struct StateContext<'a> { /// range. pub fuzz_factor: Option, pub fsrs_next_states: Option, + pub fsrs_short_term_with_steps_enabled: bool, // learning pub steps: LearningSteps<'a>, @@ -147,6 +148,7 @@ impl<'a> StateContext<'a> { good: 0, }, fsrs_next_states: None, + fsrs_short_term_with_steps_enabled: false, } } } diff --git a/rslib/src/scheduler/states/relearning.rs b/rslib/src/scheduler/states/relearning.rs index 79c021b10..31ffe5710 100644 --- a/rslib/src/scheduler/states/relearning.rs +++ b/rslib/src/scheduler/states/relearning.rs @@ -68,10 +68,12 @@ impl RelearnState { }, review: again_review, }; - if interval > 0.5 { - again_review.into() - } else { + if (ctx.fsrs_short_term_with_steps_enabled || ctx.relearn_steps.is_empty()) + && interval < 0.5 + { again_relearn.into() + } else { + again_review.into() } } else { self.review.into() @@ -112,10 +114,12 @@ impl RelearnState { }, review: hard_review, }; - if interval > 0.5 { - hard_review.into() - } else { + if (ctx.fsrs_short_term_with_steps_enabled || ctx.relearn_steps.is_empty()) + && interval < 0.5 + { hard_relearn.into() + } else { + hard_review.into() } } else { self.review.into() @@ -162,10 +166,12 @@ impl RelearnState { }, review: good_review, }; - if interval > 0.5 { - good_review.into() - } else { + if (ctx.fsrs_short_term_with_steps_enabled || ctx.relearn_steps.is_empty()) + && interval < 0.5 + { good_relearn.into() + } else { + good_review.into() } } else { self.review.into() diff --git a/rslib/src/scheduler/states/review.rs b/rslib/src/scheduler/states/review.rs index 9382d9264..f173c2310 100644 --- a/rslib/src/scheduler/states/review.rs +++ b/rslib/src/scheduler/states/review.rs @@ -124,7 +124,9 @@ impl ReviewState { review: again_review, } .into() - } else if scheduled_days < 0.5 { + } else if (ctx.fsrs_short_term_with_steps_enabled || ctx.relearn_steps.is_empty()) + && scheduled_days < 0.5 + { again_relearn.into() } else { again_review.into() diff --git a/rslib/src/scheduler/states/steps.rs b/rslib/src/scheduler/states/steps.rs index 1dd70532c..6c62bd0d1 100644 --- a/rslib/src/scheduler/states/steps.rs +++ b/rslib/src/scheduler/states/steps.rs @@ -83,6 +83,10 @@ impl<'a> LearningSteps<'a> { pub(crate) fn remaining_for_failed(self) -> u32 { self.steps.len() as u32 } + + pub(crate) fn is_empty(&self) -> bool { + self.steps.is_empty() + } } /// If the given interval in seconds surpasses 1 day, rounds it to a whole diff --git a/rslib/src/search/sqlwriter.rs b/rslib/src/search/sqlwriter.rs index 97a6a29a3..7bd06dda2 100644 --- a/rslib/src/search/sqlwriter.rs +++ b/rslib/src/search/sqlwriter.rs @@ -842,8 +842,14 @@ impl SqlWriter<'_> { self.col.get_config_bool(BoolKey::IgnoreAccentsInSearch), ) } + fn write_deck_preset(&mut self, name: &str) -> Result<()> { let dcid = self.col.storage.get_deck_config_id_by_name(name)?; + if dcid.is_none() { + write!(self.sql, "false").unwrap(); + return Ok(()); + }; + let mut str_ids = String::new(); let deck_ids = self .col @@ -1284,6 +1290,13 @@ c.odue != 0 then c.odue else c.due end) != {days}) or (c.queue in (1,4) and &s(ctx, "has-cd:r").0, "(extract_custom_data(c.data, 'r') is not null)" ); + + // preset search + assert_eq!( + &s(ctx, "preset:default").0, + "((c.did in (1) or c.odid in (1)))" + ); + assert_eq!(&s(ctx, "preset:typo").0, "(false)"); } #[test] diff --git a/rslib/src/stats/card.rs b/rslib/src/stats/card.rs index b5da86d2b..c2bf9e562 100644 --- a/rslib/src/stats/card.rs +++ b/rslib/src/stats/card.rs @@ -7,7 +7,7 @@ use crate::card::CardType; use crate::prelude::*; use crate::revlog::RevlogEntry; use crate::scheduler::fsrs::memory_state::single_card_revlog_to_item; -use crate::scheduler::fsrs::weights::ignore_revlogs_before_ms_from_config; +use crate::scheduler::fsrs::params::ignore_revlogs_before_ms_from_config; use crate::scheduler::timing::is_unix_epoch_timestamp; impl Collection { @@ -80,6 +80,7 @@ impl Collection { } else { None }, + desired_retention: card.desired_retention, }) } @@ -94,7 +95,11 @@ impl Collection { Ok(match card.ctype { CardType::New => None, CardType::Review | CardType::Learn | CardType::Relearn => { - let due = card.due; + let due = if card.original_due != 0 { + card.original_due + } else { + card.due + }; if !is_unix_epoch_timestamp(due) { let days_remaining = due - (self.timing_today()?.days_elapsed as i32); let mut due_timestamp = TimestampSecs::now(); @@ -130,7 +135,7 @@ impl Collection { .get_deck_config(conf_id)? .or_not_found(conf_id)?; let historical_retention = config.inner.historical_retention; - let fsrs = FSRS::new(Some(&config.inner.fsrs_weights))?; + let fsrs = FSRS::new(Some(config.fsrs_params()))?; let next_day_at = self.timing_today()?.next_day_at; let ignore_before = ignore_revlogs_before_ms_from_config(&config)?; diff --git a/rslib/src/stats/graphs/buttons.rs b/rslib/src/stats/graphs/buttons.rs index 1fa3de759..7c6fd03c8 100644 --- a/rslib/src/stats/graphs/buttons.rs +++ b/rslib/src/stats/graphs/buttons.rs @@ -79,7 +79,7 @@ fn interval_bucket(review: &RevlogEntry) -> Option { } else { IntervalBucket::Mature }), - RevlogReviewKind::Manual => None, + RevlogReviewKind::Manual | RevlogReviewKind::Rescheduled => None, } } diff --git a/rslib/src/stats/graphs/future_due.rs b/rslib/src/stats/graphs/future_due.rs index 4671766e5..6a00a07f1 100644 --- a/rslib/src/stats/graphs/future_due.rs +++ b/rslib/src/stats/graphs/future_due.rs @@ -7,14 +7,20 @@ use anki_proto::stats::graphs_response::FutureDue; use super::GraphsContext; use crate::card::CardQueue; +use crate::card::CardType; use crate::scheduler::timing::is_unix_epoch_timestamp; impl GraphsContext { pub(super) fn future_due(&self) -> FutureDue { let mut have_backlog = false; let mut due_by_day: HashMap = Default::default(); + let mut daily_load = 0.0; for c in &self.cards { - if matches!(c.queue, CardQueue::New | CardQueue::Suspended) { + // matched on type because queue changes on burying or suspending a new card + if c.ctype == CardType::New { + continue; + } + if c.queue == CardQueue::Suspended { continue; } let due = c.original_or_current_due(); @@ -25,8 +31,10 @@ impl GraphsContext { due - (self.days_elapsed as i32) }; + daily_load += 1.0 / c.interval.max(1) as f32; + // still want to filtered out buried cards that are due today - if due_day == 0 && matches!(c.queue, CardQueue::UserBuried | CardQueue::SchedBuried) { + if due_day <= 0 && matches!(c.queue, CardQueue::UserBuried | CardQueue::SchedBuried) { continue; } have_backlog |= due_day < 0; @@ -35,6 +43,7 @@ impl GraphsContext { FutureDue { future_due: due_by_day, have_backlog, + daily_load: daily_load as u32, } } } diff --git a/rslib/src/stats/graphs/hours.rs b/rslib/src/stats/graphs/hours.rs index b24e5082d..5fcd2ea2d 100644 --- a/rslib/src/stats/graphs/hours.rs +++ b/rslib/src/stats/graphs/hours.rs @@ -32,7 +32,9 @@ impl GraphsContext { 'outer: for review in &self.revlog { if matches!( review.review_kind, - RevlogReviewKind::Filtered | RevlogReviewKind::Manual + RevlogReviewKind::Filtered + | RevlogReviewKind::Manual + | RevlogReviewKind::Rescheduled ) { continue; } diff --git a/rslib/src/stats/graphs/retention.rs b/rslib/src/stats/graphs/retention.rs index 886e42ae5..c21f43301 100644 --- a/rslib/src/stats/graphs/retention.rs +++ b/rslib/src/stats/graphs/retention.rs @@ -53,7 +53,7 @@ impl GraphsContext { self.revlog .iter() .filter(|review| { - // not manually rescheduled + // not rescheduled/set due date/reset review.button_chosen > 0 // not cramming && (review.review_kind != RevlogReviewKind::Filtered || review.ease_factor != 0) @@ -77,12 +77,12 @@ impl GraphsContext { } }); - stats.today = Some(period_stats["today"].clone()); - stats.yesterday = Some(period_stats["yesterday"].clone()); - stats.week = Some(period_stats["week"].clone()); - stats.month = Some(period_stats["month"].clone()); - stats.year = Some(period_stats["year"].clone()); - stats.all_time = Some(period_stats["all_time"].clone()); + stats.today = Some(period_stats["today"]); + stats.yesterday = Some(period_stats["yesterday"]); + stats.week = Some(period_stats["week"]); + stats.month = Some(period_stats["month"]); + stats.year = Some(period_stats["year"]); + stats.all_time = Some(period_stats["all_time"]); stats } diff --git a/rslib/src/stats/graphs/retrievability.rs b/rslib/src/stats/graphs/retrievability.rs index da2830e8f..2c47830e3 100644 --- a/rslib/src/stats/graphs/retrievability.rs +++ b/rslib/src/stats/graphs/retrievability.rs @@ -4,6 +4,7 @@ use anki_proto::stats::graphs_response::Retrievability; use fsrs::FSRS; +use crate::card::CardQueue; use crate::scheduler::timing::SchedTimingToday; use crate::stats::graphs::eases::percent_to_bin; use crate::stats::graphs::GraphsContext; @@ -19,25 +20,40 @@ impl GraphsContext { next_day_at: Default::default(), }; let fsrs = FSRS::new(None).unwrap(); + // note id -> (sum, count) + let mut note_retrievability: std::collections::HashMap = + std::collections::HashMap::new(); for card in &self.cards { + if card.queue == CardQueue::Suspended { + continue; + } + let entry = note_retrievability + .entry(card.note_id.0) + .or_insert((0.0, 0)); + entry.1 += 1; if let Some(state) = card.memory_state { - let r = fsrs.current_retrievability( - state.into(), - card.days_since_last_review(&timing).unwrap_or_default(), - ); + let elapsed_days = card.days_since_last_review(&timing).unwrap_or_default(); + let r = fsrs.current_retrievability(state.into(), elapsed_days); + *retrievability .retrievability .entry(percent_to_bin(r * 100.0)) .or_insert_with(Default::default) += 1; - retrievability.sum += r; + retrievability.sum_by_card += r; card_with_retrievability_count += 1; + entry.0 += r; + } else { + entry.0 += 0.0; } } if card_with_retrievability_count != 0 { retrievability.average = - retrievability.sum * 100.0 / card_with_retrievability_count as f32; + retrievability.sum_by_card * 100.0 / card_with_retrievability_count as f32; } - + retrievability.sum_by_note = note_retrievability + .values() + .map(|(sum, count)| sum / *count as f32) + .sum(); retrievability } } diff --git a/rslib/src/stats/graphs/reviews.rs b/rslib/src/stats/graphs/reviews.rs index 9db4b9e5d..7c78dabae 100644 --- a/rslib/src/stats/graphs/reviews.rs +++ b/rslib/src/stats/graphs/reviews.rs @@ -10,7 +10,9 @@ impl GraphsContext { pub(super) fn review_counts_and_times(&self) -> ReviewCountsAndTimes { let mut data = ReviewCountsAndTimes::default(); for review in &self.revlog { - if review.review_kind == RevlogReviewKind::Manual { + if review.review_kind == RevlogReviewKind::Manual + || review.review_kind == RevlogReviewKind::Rescheduled + { continue; } let day = (review.id.as_secs().elapsed_secs_since(self.next_day_start) / 86_400) as i32; @@ -38,7 +40,7 @@ impl GraphsContext { count.filtered += 1; time.filtered += review.taken_millis; } - RevlogReviewKind::Manual => unreachable!(), + RevlogReviewKind::Manual | RevlogReviewKind::Rescheduled => unreachable!(), } } data diff --git a/rslib/src/stats/graphs/today.rs b/rslib/src/stats/graphs/today.rs index e2544509a..5ae4d2937 100644 --- a/rslib/src/stats/graphs/today.rs +++ b/rslib/src/stats/graphs/today.rs @@ -14,7 +14,9 @@ impl GraphsContext { if review.id.0 < start_of_today_ms { continue; } - if review.review_kind == RevlogReviewKind::Manual { + if review.review_kind == RevlogReviewKind::Manual + || review.review_kind == RevlogReviewKind::Rescheduled + { continue; } // total @@ -37,7 +39,7 @@ impl GraphsContext { RevlogReviewKind::Review => today.review_count += 1, RevlogReviewKind::Relearning => today.relearn_count += 1, RevlogReviewKind::Filtered => today.early_review_count += 1, - RevlogReviewKind::Manual => unreachable!(), + RevlogReviewKind::Manual | RevlogReviewKind::Rescheduled => unreachable!(), } } today diff --git a/rslib/src/stats/service.rs b/rslib/src/stats/service.rs index 2611ae0bf..077fd38fa 100644 --- a/rslib/src/stats/service.rs +++ b/rslib/src/stats/service.rs @@ -46,6 +46,9 @@ impl From for i32 { RevlogReviewKind::Relearning => anki_proto::stats::revlog_entry::ReviewKind::Relearning, RevlogReviewKind::Filtered => anki_proto::stats::revlog_entry::ReviewKind::Filtered, RevlogReviewKind::Manual => anki_proto::stats::revlog_entry::ReviewKind::Manual, + RevlogReviewKind::Rescheduled => { + anki_proto::stats::revlog_entry::ReviewKind::Rescheduled + } }) as i32 } } diff --git a/rslib/src/storage/card/data.rs b/rslib/src/storage/card/data.rs index bbce38bb6..ea529c495 100644 --- a/rslib/src/storage/card/data.rs +++ b/rslib/src/storage/card/data.rs @@ -79,7 +79,7 @@ impl CardData { pub(crate) fn convert_to_json(&mut self) -> Result { if let Some(v) = &mut self.fsrs_stability { - round_to_places(v, 2) + round_to_places(v, 3) } if let Some(v) = &mut self.fsrs_difficulty { round_to_places(v, 3) @@ -163,7 +163,7 @@ mod test { }; assert_eq!( data.convert_to_json().unwrap(), - r#"{"s":123.46,"d":1.235,"dr":0.99}"# + r#"{"s":123.457,"d":1.235,"dr":0.99}"# ); } } diff --git a/rslib/src/storage/card/filtered.rs b/rslib/src/storage/card/filtered.rs index b4668bbec..ea935c18c 100644 --- a/rslib/src/storage/card/filtered.rs +++ b/rslib/src/storage/card/filtered.rs @@ -5,6 +5,7 @@ use crate::card::CardQueue; use crate::decks::FilteredSearchOrder; use crate::decks::FilteredSearchTerm; use crate::scheduler::timing::SchedTimingToday; +use crate::storage::sqlite::SqlSortOrder; pub(crate) fn order_and_limit_for_search( term: &FilteredSearchTerm, @@ -27,24 +28,40 @@ pub(crate) fn order_and_limit_for_search( "(case when c.due > 1000000000 then due else (due - {today}) * 86400 + {current_timestamp} end), c.ord"); &temp_string } - FilteredSearchOrder::DuePriority => { + FilteredSearchOrder::RetrievabilityAscending => { let next_day_at = timing.next_day_at.0; - temp_string = if fsrs { - format!( - "extract_fsrs_relative_overdueness(c.data, due, {today}, ivl, {next_day_at}) desc" - ) - } else { - format!( - " -(case when queue={rev_queue} and due <= {today} -then (ivl / cast({today}-due+0.001 as real)) else 100000+due end)", - rev_queue = CardQueue::Review as i8, - today = today - ) - }; + temp_string = + build_retrievability_query(fsrs, today, next_day_at, SqlSortOrder::Ascending); + &temp_string + } + FilteredSearchOrder::RetrievabilityDescending => { + let next_day_at = timing.next_day_at.0; + temp_string = + build_retrievability_query(fsrs, today, next_day_at, SqlSortOrder::Descending); &temp_string } }; format!("{}, fnvhash(c.id, c.mod) limit {}", order, term.limit) } + +fn build_retrievability_query( + fsrs: bool, + today: u32, + next_day_at: i64, + order: SqlSortOrder, +) -> String { + if fsrs { + format!( + "extract_fsrs_relative_retrievability(c.data, case when c.odue !=0 then c.odue else c.due end, {today}, ivl, {next_day_at}) {order}" + ) + } else { + format!( + " +(case when queue={rev_queue} and due <= {today} +then (ivl / cast({today}-due+0.001 as real)) else 100000+due end) {order}", + rev_queue = CardQueue::Review as i8, + today = today + ) + } +} diff --git a/rslib/src/storage/card/get_card_entry.sql b/rslib/src/storage/card/get_card_entry.sql new file mode 100644 index 000000000..d7d76ab87 --- /dev/null +++ b/rslib/src/storage/card/get_card_entry.sql @@ -0,0 +1,7 @@ +SELECT id, + nid, + CASE + WHEN odid = 0 THEN did + ELSE odid + END AS did +FROM cards; \ No newline at end of file diff --git a/rslib/src/storage/card/mod.rs b/rslib/src/storage/card/mod.rs index f290a7f71..93c689292 100644 --- a/rslib/src/storage/card/mod.rs +++ b/rslib/src/storage/card/mod.rs @@ -9,6 +9,7 @@ use std::convert::TryFrom; use std::fmt; use std::result; +use anki_proto::stats::CardEntry; use rusqlite::named_params; use rusqlite::params; use rusqlite::types::FromSql; @@ -19,6 +20,7 @@ use rusqlite::Row; use self::data::CardData; use super::ids_to_string; +use super::sqlite::SqlSortOrder; use crate::card::Card; use crate::card::CardId; use crate::card::CardQueue; @@ -87,6 +89,14 @@ fn row_to_card(row: &Row) -> result::Result { }) } +fn row_to_card_entry(row: &Row) -> Result { + Ok(CardEntry { + id: row.get(0)?, + note_id: row.get(1)?, + deck_id: row.get(2)?, + }) +} + fn row_to_new_card(row: &Row) -> result::Result { Ok(NewCard { id: row.get(0)?, @@ -108,6 +118,13 @@ impl super::SqliteStorage { .map_err(Into::into) } + pub(crate) fn get_all_card_entries(&self) -> Result> { + self.db + .prepare_cached(include_str!("get_card_entry.sql"))? + .query_and_then([], row_to_card_entry)? + .collect() + } + pub(crate) fn update_card(&self, card: &Card) -> Result<()> { let mut stmt = self.db.prepare_cached(include_str!("update_card.sql"))?; stmt.execute(params![ @@ -731,11 +748,13 @@ enum ReviewOrderSubclause { DifficultyAscending, /// FSRS DifficultyDescending, - RelativeOverdueness { + RetrievabilitySm2 { today: u32, + order: SqlSortOrder, }, - RelativeOverduenessFsrs { + RetrievabilityFsrs { timing: SchedTimingToday, + order: SqlSortOrder, }, Added, ReverseAdded, @@ -754,15 +773,18 @@ impl fmt::Display for ReviewOrderSubclause { ReviewOrderSubclause::EaseDescending => "factor desc", ReviewOrderSubclause::DifficultyAscending => "extract_fsrs_variable(data, 'd') asc", ReviewOrderSubclause::DifficultyDescending => "extract_fsrs_variable(data, 'd') desc", - ReviewOrderSubclause::RelativeOverdueness { today } => { - temp_string = format!("ivl / cast({today}-due+0.001 as real)", today = today); + ReviewOrderSubclause::RetrievabilitySm2 { today, order } => { + temp_string = format!( + "ivl / cast({today}-due+0.001 as real) {order}", + today = today + ); &temp_string } - ReviewOrderSubclause::RelativeOverduenessFsrs { timing } => { + ReviewOrderSubclause::RetrievabilityFsrs { timing, order } => { let today = timing.days_elapsed; let next_day_at = timing.next_day_at.0; temp_string = - format!("extract_fsrs_relative_overdueness(data, due, {today}, ivl, {next_day_at}) desc"); + format!("extract_fsrs_relative_retrievability(data, case when odue !=0 then odue else due end, {today}, ivl, {next_day_at}) {order}"); &temp_string } ReviewOrderSubclause::Added => "nid asc, ord asc", @@ -791,14 +813,11 @@ fn review_order_sql(order: ReviewCardOrder, timing: SchedTimingToday, fsrs: bool } else { ReviewOrderSubclause::EaseDescending }], - ReviewCardOrder::RelativeOverdueness => { - vec![if fsrs { - ReviewOrderSubclause::RelativeOverduenessFsrs { timing } - } else { - ReviewOrderSubclause::RelativeOverdueness { - today: timing.days_elapsed, - } - }] + ReviewCardOrder::RetrievabilityAscending => { + build_retrievability_clauses(fsrs, timing, SqlSortOrder::Ascending) + } + ReviewCardOrder::RetrievabilityDescending => { + build_retrievability_clauses(fsrs, timing, SqlSortOrder::Descending) } ReviewCardOrder::Random => vec![], ReviewCardOrder::Added => vec![ReviewOrderSubclause::Added], @@ -813,6 +832,21 @@ fn review_order_sql(order: ReviewCardOrder, timing: SchedTimingToday, fsrs: bool v.join(", ") } +fn build_retrievability_clauses( + fsrs: bool, + timing: SchedTimingToday, + order: SqlSortOrder, +) -> Vec { + vec![if fsrs { + ReviewOrderSubclause::RetrievabilityFsrs { timing, order } + } else { + ReviewOrderSubclause::RetrievabilitySm2 { + today: timing.days_elapsed, + order, + } + }] +} + #[derive(Debug, Clone, Copy)] pub(crate) enum NewCardSorting { /// Ascending position, consecutive siblings, diff --git a/rslib/src/storage/revlog/mod.rs b/rslib/src/storage/revlog/mod.rs index 03b72b8b1..c6ff49271 100644 --- a/rslib/src/storage/revlog/mod.rs +++ b/rslib/src/storage/revlog/mod.rs @@ -154,6 +154,17 @@ impl SqliteStorage { .collect() } + pub(crate) fn get_revlog_entries_for_export_dataset(&self) -> Result> { + self.db + .prepare_cached(concat!( + include_str!("get.sql"), + " where ease between 1 and 4", + " order by cid, id" + ))? + .query_and_then([], row_to_revlog_entry)? + .collect() + } + pub(crate) fn get_all_revlog_entries_in_card_order(&self) -> Result> { self.db .prepare_cached(concat!(include_str!("get.sql"), " order by cid, id"))? @@ -172,12 +183,19 @@ impl SqliteStorage { let start = day_cutoff.adding_secs(-86_400).as_millis(); self.db .prepare_cached(include_str!("studied_today.sql"))? - .query_map([start.0, RevlogReviewKind::Manual as i64], |row| { - Ok(StudiedToday { - cards: row.get(0)?, - seconds: row.get(1)?, - }) - })? + .query_map( + [ + start.0, + RevlogReviewKind::Manual as i64, + RevlogReviewKind::Rescheduled as i64, + ], + |row| { + Ok(StudiedToday { + cards: row.get(0)?, + seconds: row.get(1)?, + }) + }, + )? .next() .unwrap() .map_err(Into::into) diff --git a/rslib/src/storage/revlog/studied_today.sql b/rslib/src/storage/revlog/studied_today.sql index 2a341f632..7772992d8 100644 --- a/rslib/src/storage/revlog/studied_today.sql +++ b/rslib/src/storage/revlog/studied_today.sql @@ -2,4 +2,5 @@ SELECT COUNT(), coalesce(sum(time) / 1000.0, 0.0) FROM revlog WHERE id > ? + AND type != ? AND type != ? \ No newline at end of file diff --git a/rslib/src/storage/sqlite.rs b/rslib/src/storage/sqlite.rs index 42d46c520..f0341f699 100644 --- a/rslib/src/storage/sqlite.rs +++ b/rslib/src/storage/sqlite.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::HashSet; +use std::fmt::Display; use std::hash::Hasher; use std::path::Path; use std::sync::Arc; @@ -74,7 +75,7 @@ fn open_or_create_collection_db(path: &Path) -> Result { add_extract_custom_data_function(&db)?; add_extract_fsrs_variable(&db)?; add_extract_fsrs_retrievability(&db)?; - add_extract_fsrs_relative_overdueness(&db)?; + add_extract_fsrs_relative_retrievability(&db)?; db.create_collation("unicase", unicase_compare)?; @@ -313,7 +314,7 @@ fn add_extract_fsrs_retrievability(db: &Connection) -> rusqlite::Result<()> { let Ok(next_day_at) = ctx.get_raw(4).as_i64() else { return Ok(None); }; - (next_day_at as u32).saturating_sub(due.max(0) as u32) / 86_400 + (next_day_at).saturating_sub(due) as u32 / 86_400 } else { let Ok(ivl) = ctx.get_raw(2).as_i64() else { return Ok(None); @@ -321,8 +322,8 @@ fn add_extract_fsrs_retrievability(db: &Connection) -> rusqlite::Result<()> { let Ok(days_elapsed) = ctx.get_raw(3).as_i64() else { return Ok(None); }; - let review_day = (due.max(0) as u32).saturating_sub(ivl as u32); - (days_elapsed.max(0) as u32).saturating_sub(review_day) + let review_day = due.saturating_sub(ivl); + days_elapsed.saturating_sub(review_day) as u32 }; Ok(card_data.memory_state().map(|state| { FSRS::new(None) @@ -333,12 +334,13 @@ fn add_extract_fsrs_retrievability(db: &Connection) -> rusqlite::Result<()> { ) } -/// eg. extract_fsrs_relative_overdueness(card.data, card.due, +/// eg. extract_fsrs_relative_retrievability(card.data, card.due, /// timing.days_elapsed, card.ivl, timing.next_day_at) -> float | null. The -/// higher the number, the more overdue. -fn add_extract_fsrs_relative_overdueness(db: &Connection) -> rusqlite::Result<()> { +/// higher the number, the higher the card's retrievability relative to the +/// configured desired retention. +fn add_extract_fsrs_relative_retrievability(db: &Connection) -> rusqlite::Result<()> { db.create_scalar_function( - "extract_fsrs_relative_overdueness", + "extract_fsrs_relative_retrievability", 5, FunctionFlags::SQLITE_DETERMINISTIC, move |ctx| { @@ -359,7 +361,7 @@ fn add_extract_fsrs_relative_overdueness(db: &Connection) -> rusqlite::Result<() let Ok(next_day_at) = ctx.get_raw(4).as_i64() else { return Ok(None); }; - (next_day_at as u32).saturating_sub(due.max(0) as u32) / 86_400 + next_day_at.saturating_sub(due) as u32 / 86_400 } else { let Ok(days_elapsed) = ctx.get_raw(2).as_i64() else { return Ok(None); @@ -386,7 +388,7 @@ fn add_extract_fsrs_relative_overdueness(db: &Connection) -> rusqlite::Result<() .max(0.0001); Ok(Some( - (1. / current_retrievability - 1.) / (1. / desired_retrievability - 1.), + -(1. / current_retrievability - 1.) / (1. / desired_retrievability - 1.), )) }, ) @@ -590,3 +592,22 @@ impl SqliteStorage { self.db.query_row(sql, [], |r| r.get(0)).map_err(Into::into) } } + +#[derive(Debug, Clone, Copy)] +pub enum SqlSortOrder { + Ascending, + Descending, +} + +impl Display for SqlSortOrder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + SqlSortOrder::Ascending => "asc", + SqlSortOrder::Descending => "desc", + } + ) + } +} diff --git a/rslib/src/sync/media/database/client/changetracker.rs b/rslib/src/sync/media/database/client/changetracker.rs index 5e34d324f..2d2220c7c 100644 --- a/rslib/src/sync/media/database/client/changetracker.rs +++ b/rslib/src/sync/media/database/client/changetracker.rs @@ -239,11 +239,13 @@ where #[cfg(test)] mod test { use std::fs; + use std::fs::FileTimes; use std::path::Path; use std::time; use std::time::Duration; use anki_io::create_dir; + use anki_io::set_file_times; use anki_io::write_file; use tempfile::tempdir; @@ -257,11 +259,10 @@ mod test { fn change_mtime(p: &Path) { let mtime = p.metadata().unwrap().modified().unwrap(); let new_mtime = mtime - Duration::from_secs(3); - let secs = new_mtime - .duration_since(time::UNIX_EPOCH) - .unwrap() - .as_secs() as i64; - utime::set_file_times(p, secs, secs).unwrap(); + let times = FileTimes::new() + .set_accessed(new_mtime) + .set_modified(new_mtime); + set_file_times(p, times).unwrap(); } #[test] diff --git a/rslib/src/template.rs b/rslib/src/template.rs index 4ae93e4f8..b72831851 100644 --- a/rslib/src/template.rs +++ b/rslib/src/template.rs @@ -33,7 +33,7 @@ static TEMPLATE_ERROR_LINK: &str = static TEMPLATE_BLANK_LINK: &str = "https://docs.ankiweb.net/templates/errors.html#front-of-card-is-blank"; static TEMPLATE_BLANK_CLOZE_LINK: &str = - "https://docs.ankiweb.net/templates/errors.html#no-cloze-filter-on-cloze-notetype"; + "https://docs.ankiweb.net/templates/errors.html#no-cloze-filter-on-cloze-note-type"; // Lexing //---------------------------------------- @@ -628,7 +628,7 @@ pub fn render_card( TEMPLATE_BLANK_CLOZE_LINK, tr.card_template_rendering_more_info() )) - } else if !is_cloze && !qtmpl.renders_with_fields(context.nonempty_fields) { + } else if !is_cloze && !browser && !qtmpl.renders_with_fields(context.nonempty_fields) { Some(format!( "
{}
{}
", tr.card_template_rendering_empty_front(), diff --git a/rslib/src/template_filters.rs b/rslib/src/template_filters.rs index 13d092134..f1aac788c 100644 --- a/rslib/src/template_filters.rs +++ b/rslib/src/template_filters.rs @@ -31,12 +31,12 @@ pub(crate) fn apply_filters<'a>( let mut text: Cow = text.into(); // type:cloze & type:nc are handled specially - let filters = if filters == ["cloze", "type"] { - &["type-cloze"] - } else if filters == ["nc", "type"] { - &["type-nc"] - } else { - filters + // other type: are passed as the default one + let filters = match filters { + ["cloze", "type"] => &["type-cloze"], + ["nc", "type"] => &["type-nc"], + [.., "type"] => &["type"], + _ => filters, }; for (idx, &filter_name) in filters.iter().enumerate() { @@ -259,6 +259,10 @@ field apply_filters("ignored", &["nc", "type"], "Text", &ctx), ("[[type:nc:Text]]".into(), vec![]) ); + assert_eq!( + apply_filters("ignored", &["some", "unknown", "type"], "Text", &ctx), + ("[[type:Text]]".into(), vec![]) + ); } #[test] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 3c1f3ff7a..2c8c4172d 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] # older versions may fail to compile; newer versions may fail the clippy tests -channel = "1.81.0" +channel = "1.82.0" diff --git a/ts/editor/editor-toolbar/CommandIconButton.svelte b/ts/editor/editor-toolbar/CommandIconButton.svelte index e81d8a793..83f4a9a93 100644 --- a/ts/editor/editor-toolbar/CommandIconButton.svelte +++ b/ts/editor/editor-toolbar/CommandIconButton.svelte @@ -27,6 +27,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html function action() { execCommand(key); + $focusedInput?.focus(); } $: disabled = !$focusedInput || !editingInputIsRichText($focusedInput); diff --git a/ts/editor/image-overlay/ImageOverlay.svelte b/ts/editor/image-overlay/ImageOverlay.svelte index 5d5db7c93..8376f3330 100644 --- a/ts/editor/image-overlay/ImageOverlay.svelte +++ b/ts/editor/image-overlay/ImageOverlay.svelte @@ -224,7 +224,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html Number(actualWidth) <= maxWidth && Number(actualHeight) <= maxHeight; let restoringDisabled: boolean; - $: restoringDisabled = !activeImage?.hasAttribute("width") ?? true; + $: restoringDisabled = !(activeImage?.hasAttribute("width") ?? true); const widthObserver = new MutationObserver(() => { restoringDisabled = !activeImage!.hasAttribute("width"); diff --git a/ts/editor/rich-text-input/RichTextInput.svelte b/ts/editor/rich-text-input/RichTextInput.svelte index bf9656fc1..74abed4e9 100644 --- a/ts/editor/rich-text-input/RichTextInput.svelte +++ b/ts/editor/rich-text-input/RichTextInput.svelte @@ -117,6 +117,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html async function focus(): Promise { const richText = await richTextPromise; + richText.blur(); richText.focus(); } diff --git a/ts/lib/components/HelpModal.svelte b/ts/lib/components/HelpModal.svelte index 2a565362b..5fff619df 100644 --- a/ts/lib/components/HelpModal.svelte +++ b/ts/lib/components/HelpModal.svelte @@ -208,7 +208,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html text-align: start; min-width: 250px; background-color: var(--canvas); - border: none; + border: 1px solid transparent; cursor: pointer; border-radius: 0; &:hover { diff --git a/ts/lib/components/Portal.svelte b/ts/lib/components/Portal.svelte new file mode 100644 index 000000000..a71788ae2 --- /dev/null +++ b/ts/lib/components/Portal.svelte @@ -0,0 +1,45 @@ + + + + + + +{#if !target} + + + +{/if} diff --git a/ts/lib/components/RenderChildren.svelte b/ts/lib/components/RenderChildren.svelte new file mode 100644 index 000000000..e09533eae --- /dev/null +++ b/ts/lib/components/RenderChildren.svelte @@ -0,0 +1,6 @@ + + + diff --git a/ts/lib/components/RevertButton.svelte b/ts/lib/components/RevertButton.svelte index b62bb53ad..a1d6af06d 100644 --- a/ts/lib/components/RevertButton.svelte +++ b/ts/lib/components/RevertButton.svelte @@ -5,12 +5,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html @@ -31,7 +32,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html {/if} {#if fsrsEnabled} - + {/if} {:else} diff --git a/ts/routes/card-info/ForgettingCurve.svelte b/ts/routes/card-info/ForgettingCurve.svelte index 227d2c108..b3fffc16a 100644 --- a/ts/routes/card-info/ForgettingCurve.svelte +++ b/ts/routes/card-info/ForgettingCurve.svelte @@ -20,6 +20,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import HoverColumns from "../graphs/HoverColumns.svelte"; export let revlog: RevlogEntry[]; + export let desiredRetention: number; let svg = null as HTMLElement | SVGElement | null; const bounds = defaultGraphBounds(); const title = tr.cardStatsFsrsForgettingCurveTitle(); @@ -35,7 +36,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } const timeRange = writable(defaultTimeRange); - $: renderForgettingCurve(filteredRevlog, $timeRange, svg as SVGElement, bounds); + $: renderForgettingCurve( + filteredRevlog, + $timeRange, + svg as SVGElement, + bounds, + desiredRetention, + );
diff --git a/ts/routes/card-info/Revlog.svelte b/ts/routes/card-info/Revlog.svelte index 61f4a5978..dc410b4ab 100644 --- a/ts/routes/card-info/Revlog.svelte +++ b/ts/routes/card-info/Revlog.svelte @@ -36,6 +36,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html return tr2.cardStatsReviewLogTypeFiltered(); case ReviewKind.MANUAL: return tr2.cardStatsReviewLogTypeManual(); + case ReviewKind.RESCHEDULED: + return tr2.cardStatsReviewLogTypeRescheduled(); } } diff --git a/ts/routes/card-info/forgetting-curve.ts b/ts/routes/card-info/forgetting-curve.ts index 7331f7dc5..b5c71b761 100644 --- a/ts/routes/card-info/forgetting-curve.ts +++ b/ts/routes/card-info/forgetting-curve.ts @@ -48,6 +48,7 @@ function filterDataByTimeRange(data: DataPoint[], maxDays: number): DataPoint[] export function filterRevlogEntryByReviewKind(entry: RevlogEntry): boolean { return ( entry.reviewKind !== RevlogEntry_ReviewKind.MANUAL + && entry.reviewKind !== RevlogEntry_ReviewKind.RESCHEDULED && (entry.reviewKind !== RevlogEntry_ReviewKind.FILTERED || entry.ease !== 0) ); } @@ -160,6 +161,7 @@ export function renderForgettingCurve( timeRange: TimeRange, svgElem: SVGElement, bounds: GraphBounds, + desiredRetention: number, ) { const svg = select(svgElem); const trans = svg.transition().duration(600) as any; @@ -203,18 +205,63 @@ export function renderForgettingCurve( .call((selection) => selection.transition(trans).call(axisLeft(y).tickSizeOuter(0))) .attr("direction", "ltr"); + svg.select(".y-ticks .y-axis-title").remove(); + svg.select(".y-ticks") + .append("text") + .attr("class", "y-axis-title") + .attr("transform", "rotate(-90)") + .attr("y", 0 - bounds.marginLeft) + .attr("x", 0 - (bounds.height / 2)) + .attr("font-size", "1rem") + .attr("dy", "1.1em") + .attr("fill", "currentColor") + .style("text-anchor", "middle") + .text(`${tr.cardStatsFsrsForgettingCurveProbabilityOfRecalling()}(%)`); + const lineGenerator = line() .x((d) => x(d.date)) .y((d) => y(d.retrievability)); + // gradient color + const desiredRetentionY = desiredRetention * 100; + svg.append("linearGradient") + .attr("id", "line-gradient") + .attr("gradientUnits", "userSpaceOnUse") + .attr("x1", 0) + .attr("y1", y(0)) + .attr("x2", 0) + .attr("y2", y(100)) + .selectAll("stop") + .data([ + { offset: "0%", color: "tomato" }, + { offset: `${desiredRetentionY}%`, color: "steelblue" }, + { offset: "100%", color: "green" }, + ]) + .enter().append("stop") + .attr("offset", d => d.offset) + .attr("stop-color", d => d.color); + svg.append("path") .datum(data) .attr("class", "forgetting-curve-line") .attr("fill", "none") - .attr("stroke", "steelblue") + .attr("stroke", "url(#line-gradient)") .attr("stroke-width", 1.5) .attr("d", lineGenerator); + svg.select(".desired-retention-line").remove(); + if (desiredRetentionY > yMin) { + svg.append("line") + .attr("class", "desired-retention-line") + .attr("x1", bounds.marginLeft) + .attr("x2", bounds.width - bounds.marginRight) + .attr("y1", y(desiredRetentionY)) + .attr("y2", y(desiredRetentionY)) + .attr("stroke", "steelblue") + .attr("stroke-dasharray", "4 4") + .attr("stroke-width", 1.2); + } + const focusLine = svg.append("line") .attr("class", "focus-line") .attr("y1", bounds.marginTop) @@ -247,11 +294,18 @@ export function renderForgettingCurve( .attr("fill", "transparent") .on("mousemove", (event: MouseEvent, d: DataPoint) => { const [x1, y1] = pointer(event, document.body); + const [_, y2] = pointer(event, svg.node()); + + const lineY = y(desiredRetentionY); focusLine.attr("x1", x(d.date) - 1).attr("x2", x(d.date) + 1).style( "opacity", 1, ); - showTooltip(tooltipText(d), x1, y1); + let text = tooltipText(d); + if (y2 >= lineY - 10 && y2 <= lineY + 10) { + text += `
${tr.cardStatsFsrsForgettingCurveDesiredRetention()}: ${desiredRetention.toFixed(2)}`; + } + showTooltip(text, x1, y1); }) .on("mouseout", () => { focusLine.style("opacity", 0); diff --git a/ts/routes/change-notetype/Alert.svelte b/ts/routes/change-notetype/Alert.svelte index 78b129346..29ce89e2d 100644 --- a/ts/routes/change-notetype/Alert.svelte +++ b/ts/routes/change-notetype/Alert.svelte @@ -17,6 +17,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html export let ctx: MapContext; let unusedMsg: string; + // svelte-ignore reactive_declaration_non_reactive_property $: unusedMsg = ctx === MapContext.Field ? tr.changeNotetypeWillDiscardContent() diff --git a/ts/routes/change-notetype/StickyHeader.svelte b/ts/routes/change-notetype/StickyHeader.svelte index 116cfa444..25dad31eb 100644 --- a/ts/routes/change-notetype/StickyHeader.svelte +++ b/ts/routes/change-notetype/StickyHeader.svelte @@ -18,6 +18,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html $: info = state.info; + // svelte-ignore reactive_declaration_non_reactive_property $: unused = $info.isCloze && ctx === MapContext.Template ? [] : $info.unusedItems(ctx); diff --git a/ts/routes/congrats/CongratsPage.svelte b/ts/routes/congrats/CongratsPage.svelte index bd05e10d9..cc4eee861 100644 --- a/ts/routes/congrats/CongratsPage.svelte +++ b/ts/routes/congrats/CongratsPage.svelte @@ -4,6 +4,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> diff --git a/ts/routes/congrats/index.ts b/ts/routes/congrats/index.ts index 2e2b28d75..52fc74a3d 100644 --- a/ts/routes/congrats/index.ts +++ b/ts/routes/congrats/index.ts @@ -20,21 +20,9 @@ export async function setupCongrats(): Promise { const page = new CongratsPage({ // use #congrats if it exists, otherwise entire body target: customMountPoint ?? document.body, - props: { info }, + props: { info, refreshPeriodically: !customMountPoint }, }); - // refresh automatically if a custom area not provided - if (!customMountPoint) { - setInterval(async () => { - try { - const info = await congratsInfo({}); - page.$set({ info }); - } catch { - console.log("congrats fetch failed"); - } - }, 60000); - } - return page; } diff --git a/ts/routes/deck-options/DisplayOrder.svelte b/ts/routes/deck-options/DisplayOrder.svelte index 5ea8d1d89..88fc97fc3 100644 --- a/ts/routes/deck-options/DisplayOrder.svelte +++ b/ts/routes/deck-options/DisplayOrder.svelte @@ -38,6 +38,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html const currentDeck = "\n\n" + tr.deckConfigDisplayOrderWillUseCurrentDeck(); let disabledNewSortOrders: number[] = []; + // svelte-ignore reactive_declaration_non_reactive_property $: { switch ($config.newCardGatherPriority) { case GatherOrder.RANDOM_NOTES: diff --git a/ts/routes/deck-options/EasyDays.svelte b/ts/routes/deck-options/EasyDays.svelte index cfa0c85fa..eb64d569f 100644 --- a/ts/routes/deck-options/EasyDays.svelte +++ b/ts/routes/deck-options/EasyDays.svelte @@ -8,6 +8,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import Item from "$lib/components/Item.svelte"; import TitledContainer from "$lib/components/TitledContainer.svelte"; import type { DeckOptionsState } from "./lib"; + import Warning from "./Warning.svelte"; export let state: DeckOptionsState; export let api: Record; @@ -15,10 +16,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html const config = state.currentConfig; const defaults = state.defaults; - if ($config.easyDaysPercentages.length !== 7) { + // svelte-ignore reactive_declaration_non_reactive_property + $: if ($config.easyDaysPercentages.length !== 7) { $config.easyDaysPercentages = defaults.easyDaysPercentages; } + $: noNormalDay = $config.easyDaysPercentages.some((p) => p === 1.0) + ? "" + : tr.deckConfigEasyDaysNoNormalDays(); + const easyDays = [ tr.deckConfigEasyDaysMonday(), tr.deckConfigEasyDaysTuesday(), @@ -30,6 +36,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html ]; + + + + @@ -38,40 +48,27 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - {tr.deckConfigEasyDaysNormal()} - {tr.deckConfigEasyDaysReduced()} - {tr.deckConfigEasyDaysMinimum()} + +
+ {tr.deckConfigEasyDaysMinimum()} + {tr.deckConfigEasyDaysReduced()} + {tr.deckConfigEasyDaysNormal()} +
+ {#each easyDays as day, index} {day} - + - - - - - - @@ -80,6 +77,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
+ + + @@ -92,6 +92,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html .easy-days-settings td { padding: 8px; text-align: center; - border: 1px solid #ddd; + border-bottom: var(--border) solid 1px; + } + .header { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + } + .header span:nth-child(1) { + text-align: left; + } + .header span:nth-child(3) { + text-align: right; + } + .easy-days-settings input[type="range"] { + width: 100%; } diff --git a/ts/routes/deck-options/FsrsOptions.svelte b/ts/routes/deck-options/FsrsOptions.svelte index c2d29baa7..cf3389798 100644 --- a/ts/routes/deck-options/FsrsOptions.svelte +++ b/ts/routes/deck-options/FsrsOptions.svelte @@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
- - openHelpModal("modelWeights")}> + openHelpModal("modelParams")}> {tr.deckConfigWeights()} - + -
- {#if computingWeights || checkingWeights} - {computeWeightsProgressString} + {#if computingParams || checkingParams} + {computeParamsProgressString} {:else if totalReviews !== undefined} {tr.statisticsReviews({ reviews: totalReviews })} {/if} @@ -430,7 +458,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html max={3650} > openHelpModal("computeOptimalRetention")}> - Days to simulate + {tr.deckConfigDaysToSimulate()} @@ -455,7 +483,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html /> {/if} {/if} -
{computeRetentionProgressString}
+ + {#if computingRetention} +
{computeRetentionProgressString}
+ {/if}
@@ -470,7 +501,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html max={3650} > openHelpModal("simulateFsrsReview")}> - Days to simulate + {tr.deckConfigDaysToSimulate()} @@ -489,10 +520,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html bind:value={simulateFsrsRequest.newLimit} defaultValue={$config.newPerDay} min={0} - max={1000} + max={9999} > openHelpModal("simulateFsrsReview")}> - New cards/day + {tr.schedulingNewCardsday()} @@ -500,10 +531,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html bind:value={simulateFsrsRequest.reviewLimit} defaultValue={defaults.reviewsPerDay} min={0} - max={1000} + max={9999} > openHelpModal("simulateFsrsReview")}> - Maximum reviews/day + {tr.schedulingMaximumReviewsday()} @@ -514,7 +545,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html max={36500} > openHelpModal("simulateFsrsReview")}> - Maximum interval + {tr.schedulingMaximumInterval()} @@ -536,6 +567,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{simulateProgressString}
+ + + + diff --git a/ts/routes/deck-options/FsrsOptionsOuter.svelte b/ts/routes/deck-options/FsrsOptionsOuter.svelte index 06a50b3cb..f21d7ff3c 100644 --- a/ts/routes/deck-options/FsrsOptionsOuter.svelte +++ b/ts/routes/deck-options/FsrsOptionsOuter.svelte @@ -19,7 +19,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import FsrsOptions from "./FsrsOptions.svelte"; import GlobalLabel from "./GlobalLabel.svelte"; import type { DeckOptionsState } from "./lib"; - import Warning from "./Warning.svelte"; export let state: DeckOptionsState; export let api: Record; @@ -37,7 +36,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html help: tr.deckConfigDesiredRetentionTooltip(), sched: HelpItemScheduler.FSRS, }, - modelWeights: { + modelParams: { title: tr.deckConfigWeights(), help: tr.deckConfigWeightsTooltip2() + @@ -65,8 +64,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html modal.show(); carousel.to(index); } - - $: fsrsClientWarning = $fsrs ? tr.deckConfigFsrsOnAllClients() : ""; @@ -93,8 +90,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - {#if $fsrs} v.toFixed(4)).join(", "); + function render(params: number[]): string { + return params.map((v) => v.toFixed(4)).join(", "); } function update(this: HTMLInputElement): void { diff --git a/ts/routes/deck-options/WeightsInputRow.svelte b/ts/routes/deck-options/ParamsInputRow.svelte similarity index 84% rename from ts/routes/deck-options/WeightsInputRow.svelte rename to ts/routes/deck-options/ParamsInputRow.svelte index 38884937c..864bc4fbc 100644 --- a/ts/routes/deck-options/WeightsInputRow.svelte +++ b/ts/routes/deck-options/ParamsInputRow.svelte @@ -6,7 +6,7 @@ import ConfigInput from "$lib/components/ConfigInput.svelte"; import RevertButton from "$lib/components/RevertButton.svelte"; - import WeightsInput from "./WeightsInput.svelte"; + import ParamsInput from "./ParamsInput.svelte"; export let value: number[]; export let defaultValue: number[]; @@ -15,6 +15,6 @@ - + diff --git a/ts/routes/deck-options/WeightsSearchRow.svelte b/ts/routes/deck-options/ParamsSearchRow.svelte similarity index 100% rename from ts/routes/deck-options/WeightsSearchRow.svelte rename to ts/routes/deck-options/ParamsSearchRow.svelte diff --git a/ts/routes/deck-options/SaveButton.svelte b/ts/routes/deck-options/SaveButton.svelte index 166ed003f..8efb8db3a 100644 --- a/ts/routes/deck-options/SaveButton.svelte +++ b/ts/routes/deck-options/SaveButton.svelte @@ -56,7 +56,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html async function save(mode: UpdateDeckConfigsMode): Promise { await commitEditing(); - if (mode === UpdateDeckConfigsMode.COMPUTE_ALL_WEIGHTS && !get(state.fsrs)) { + if (mode === UpdateDeckConfigsMode.COMPUTE_ALL_PARAMS && !get(state.fsrs)) { alert(tr.deckConfigFsrsMustBeEnabled()); return; } @@ -119,7 +119,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html {tr.deckConfigSaveToAllSubdecks()} save(UpdateDeckConfigsMode.COMPUTE_ALL_WEIGHTS)} + on:click={() => save(UpdateDeckConfigsMode.COMPUTE_ALL_PARAMS)} > {tr.deckConfigSaveAndOptimize()} diff --git a/ts/routes/deck-options/TabbedValue.svelte b/ts/routes/deck-options/TabbedValue.svelte index f948951e6..3545860c2 100644 --- a/ts/routes/deck-options/TabbedValue.svelte +++ b/ts/routes/deck-options/TabbedValue.svelte @@ -66,7 +66,7 @@ white-space: nowrap; cursor: pointer; color: var(--fg-subtle); - border: none; + border: 1px solid transparent; background-color: transparent; /* remove default macOS styling */ box-shadow: none; diff --git a/ts/routes/deck-options/[deckId]/+page.svelte b/ts/routes/deck-options/[deckId]/+page.svelte index ec99facc1..c5064a950 100644 --- a/ts/routes/deck-options/[deckId]/+page.svelte +++ b/ts/routes/deck-options/[deckId]/+page.svelte @@ -16,7 +16,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html globalThis.anki.deckOptionsPendingChanges = async (): Promise => { await commitEditing(); if (bridgeCommandsAvailable()) { - if (data.state.isModified()) { + if (await data.state.isModified()) { bridgeCommand("confirmDiscardChanges"); } else { bridgeCommand("_close"); @@ -28,6 +28,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html globalThis.$deckOptions = new Promise((resolve, _reject) => { resolve(page); }); + data.state.resolveOriginalConfigs(); if (bridgeCommandsAvailable()) { bridgeCommand("deckOptionsReady"); } diff --git a/ts/routes/deck-options/choices.ts b/ts/routes/deck-options/choices.ts index 0b4611528..6f34eae0e 100644 --- a/ts/routes/deck-options/choices.ts +++ b/ts/routes/deck-options/choices.ts @@ -96,8 +96,12 @@ export function reviewOrderChoices(fsrs: boolean): Choice, @@ -48,10 +49,6 @@ type Configs = > & { currentConfig: DeckConfig_Config }; -type DeepPartial = { - [key in keyof T]: key extends K ? Partial : T[key]; -}; - export class DeckOptionsState { readonly currentConfig: Writable; readonly currentAuxData: Writable>; @@ -68,7 +65,8 @@ export class DeckOptionsState { readonly daysSinceLastOptimization: Writable; readonly currentPresetName: Writable; /** Used to detect if there are any pending changes */ - readonly originalConfigs: Configs; + readonly originalConfigsPromise: Promise; + readonly originalConfigsResolve: (value: AllConfigs) => void; private targetDeckId: DeckOptionsId; private configs: ConfigWithCount[]; @@ -124,16 +122,9 @@ export class DeckOptionsState { this.currentConfig.subscribe((val) => this.onCurrentConfigChanged(val)); this.currentAuxData.subscribe((val) => this.onCurrentAuxDataChanged(val)); - this.originalConfigs = cloneDeep({ - configs: this.configs.map(c => c.config!), - cardStateCustomizer: data.cardStateCustomizer, - limits: get(this.deckLimits), - newCardsIgnoreReviewLimit: data.newCardsIgnoreReviewLimit, - applyAllParentLimits: data.applyAllParentLimits, - fsrs: data.fsrs, - fsrsReschedule: get(this.fsrsReschedule), - currentConfig: get(this.currentConfig), - }); + // Must be resolved after all components are mounted, as some components + // may modify the config during their initialization. + [this.originalConfigsPromise, this.originalConfigsResolve] = promiseWithResolver(); } setCurrentIndex(index: number): void { @@ -148,6 +139,10 @@ export class DeckOptionsState { return this.configs[this.selectedIdx].config.name; } + getCurrentNameForSearch(): string { + return this.getCurrentName().replace(/([\\"])/g, "\\$1"); + } + setCurrentName(name: string): void { if (this.configs[this.selectedIdx].config.name === name) { return; @@ -326,24 +321,28 @@ export class DeckOptionsState { return list; } - isModified(): boolean { - const original: DeepPartial = { - ...this.originalConfigs, - limits: omitUndefined(this.originalConfigs.limits), - }; - const current: typeof original = { + private getAllConfigs(): AllConfigs { + return cloneDeep({ configs: this.configs.map(c => c.config), cardStateCustomizer: get(this.cardStateCustomizer), - limits: omitUndefined(get(this.deckLimits)), + limits: get(this.deckLimits), newCardsIgnoreReviewLimit: get(this.newCardsIgnoreReviewLimit), applyAllParentLimits: get(this.applyAllParentLimits), fsrs: get(this.fsrs), fsrsReschedule: get(this.fsrsReschedule), currentConfig: get(this.currentConfig), - }; + }); + } + async isModified(): Promise { + const original = await this.originalConfigsPromise; + const current = this.getAllConfigs(); return !isEqual(original, current); } + + resolveOriginalConfigs(): void { + this.originalConfigsResolve(this.getAllConfigs()); + } } function bytesToObject(bytes: Uint8Array): Record { @@ -413,11 +412,6 @@ export class ValueTab { } } -/** Returns a copy of the given object with the properties whose values are 'undefined' omitted */ -function omitUndefined(obj: T): Partial { - return pickBy(obj, val => val !== undefined); -} - /** Ensure blur handler has fired so changes get committed. */ export async function commitEditing(): Promise { if (document.activeElement instanceof HTMLElement) { @@ -425,3 +419,11 @@ export async function commitEditing(): Promise { } await tick(); } + +export function fsrsParams(config: DeckConfig_Config): number[] { + if (config.fsrsParams5) { + return config.fsrsParams5; + } else { + return config.fsrsParams4; + } +} diff --git a/ts/routes/graphs/CalendarGraph.svelte b/ts/routes/graphs/CalendarGraph.svelte index a8907ee00..009ed0c88 100644 --- a/ts/routes/graphs/CalendarGraph.svelte +++ b/ts/routes/graphs/CalendarGraph.svelte @@ -49,6 +49,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html ); } + // svelte-ignore reactive_declaration_non_reactive_property $: { if (revlogRange < RevlogRange.Year) { minYear = maxYear; diff --git a/ts/routes/graphs/RangeBox.svelte b/ts/routes/graphs/RangeBox.svelte index ac74703f9..a0c895e8e 100644 --- a/ts/routes/graphs/RangeBox.svelte +++ b/ts/routes/graphs/RangeBox.svelte @@ -44,6 +44,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } } + // svelte-ignore reactive_declaration_non_reactive_property $: { switch (revlogRange as RevlogRange) { case RevlogRange.Year: @@ -143,11 +144,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html animation: spin; animation-duration: 1s; animation-iteration-count: infinite; + z-index: -1; opacity: 0; &.loading { opacity: 0.5; + z-index: 1; transition: opacity var(--transition-slow); } } diff --git a/ts/routes/graphs/future-due.ts b/ts/routes/graphs/future-due.ts index 1acd221f5..6100470b7 100644 --- a/ts/routes/graphs/future-due.ts +++ b/ts/routes/graphs/future-due.ts @@ -19,11 +19,16 @@ import type { HistogramData } from "./histogram-graph"; export interface GraphData { dueCounts: Map; haveBacklog: boolean; + dailyLoad: number; } export function gatherData(data: GraphsResponse): GraphData { const msg = data.futureDue!; - return { dueCounts: numericMap(msg.futureDue), haveBacklog: msg.haveBacklog }; + return { + dueCounts: numericMap(msg.futureDue), + haveBacklog: msg.haveBacklog, + dailyLoad: msg.dailyLoad, + }; } export interface FutureDueResponse { @@ -41,23 +46,33 @@ function makeQuery(start: number, end: number): string { } } +function withoutBacklog(data: Map): Map { + const map = new Map(); + for (const [day, count] of data.entries()) { + if (day >= 0) { + map.set(day, count); + } + } + return map; +} + export function buildHistogram( sourceData: GraphData, range: GraphRange, - backlog: boolean, + includeBacklog: boolean, dispatch: SearchDispatch, browserLinksSupported: boolean, ): FutureDueResponse { const output = { histogramData: null, tableData: [] }; // get min/max - const data = sourceData.dueCounts; + const data = includeBacklog ? sourceData.dueCounts : withoutBacklog(sourceData.dueCounts); if (!data) { return output; } const [xMinOrig, origXMax] = extent(data.keys()); let xMin = xMinOrig; - if (!backlog) { + if (!includeBacklog) { xMin = 0; } let xMax = origXMax; @@ -138,6 +153,12 @@ export function buildHistogram( reviews: sourceData.dueCounts.get(1) ?? 0, }), }, + { + label: tr.statisticsDailyLoad(), + value: tr.statisticsReviewsPerDay({ + count: sourceData.dailyLoad, + }), + }, ]; return { diff --git a/ts/routes/graphs/retrievability.ts b/ts/routes/graphs/retrievability.ts index 072e6ab73..dd54c2b8d 100644 --- a/ts/routes/graphs/retrievability.ts +++ b/ts/routes/graphs/retrievability.ts @@ -18,14 +18,16 @@ import type { HistogramData } from "./histogram-graph"; export interface GraphData { retrievability: Map; average: number; - sum: number; + sumByCard: number; + sumByNote: number; } export function gatherData(data: GraphsResponse): GraphData { return { retrievability: numericMap(data.retrievability!.retrievability), average: data.retrievability!.average, - sum: data.retrievability!.sum, + sumByCard: data.retrievability!.sumByCard, + sumByNote: data.retrievability!.sumByNote, }; } @@ -111,7 +113,9 @@ export function prepareData( }, { label: tr.statisticsEstimatedTotalKnowledge(), - value: tr.statisticsCards({ cards: +data.sum.toFixed(0) }), + value: `${tr.statisticsCards({ cards: +data.sumByCard.toFixed(0) })} / ${ + tr.statisticsNotes({ notes: +data.sumByNote.toFixed(0) }) + }`, }, ]; diff --git a/ts/routes/graphs/simulator.ts b/ts/routes/graphs/simulator.ts index 1ebd0a4ae..db26cf65c 100644 --- a/ts/routes/graphs/simulator.ts +++ b/ts/routes/graphs/simulator.ts @@ -16,6 +16,7 @@ import { select, } from "d3"; +import * as tr from "@generated/ftl"; import { timeSpan } from "@tslib/time"; import type { GraphBounds, TableDatum } from "./graph-helpers"; import { setDataAvailable } from "./graph-helpers"; @@ -23,7 +24,8 @@ import { hideTooltip, showTooltip } from "./tooltip-utils.svelte"; export interface Point { x: number; - y: number; + timeCost: number; + count: number; label: number; } @@ -31,6 +33,7 @@ export function renderSimulationChart( svgElem: SVGElement, bounds: GraphBounds, data: Point[], + showTime: boolean, ): TableDatum[] { const svg = select(svgElem); svg.selectAll(".lines").remove(); @@ -62,10 +65,10 @@ export function renderSimulationChart( // y scale const yTickFormat = (n: number): string => { - return timeSpan(n, true); + return showTime ? timeSpan(n, true) : n.toString(); }; - const yMax = max(convertedData, d => d.y)!; + const yMax = showTime ? max(convertedData, d => d.timeCost)! : max(convertedData, d => d.count)!; const y = scaleLinear() .range([bounds.height - bounds.marginBottom, bounds.marginTop]) .domain([0, yMax]) @@ -91,10 +94,10 @@ export function renderSimulationChart( .attr("dy", "1em") .attr("fill", "currentColor") .style("text-anchor", "middle") - .text("Review Time per day"); + .text(showTime ? "Review Time per day" : "Review Count per day"); // x lines - const points = convertedData.map((d) => [x(d.date), y(d.y), d.label]); + const points = convertedData.map((d) => [x(d.date), y(showTime ? d.timeCost : d.count), d.label]); const groups = rollup(points, v => Object.assign(v, { z: v[0][2] }), d => d[2]); const color = schemeCategory10; @@ -161,7 +164,9 @@ export function renderSimulationChart( const days = +((date.getTime() - Date.now()) / (60 * 60 * 24 * 1000)).toFixed(); let tooltipContent = `Date: ${localizedDate(date)}
In ${days} Days
`; for (const [key, value] of Object.entries(groupData)) { - tooltipContent += `#${key}: ${timeSpan(value)}
`; + tooltipContent += `#${key}: ${ + showTime ? timeSpan(value) : tr.statisticsReviews({ reviews: Math.round(value) }) + }
`; } showTooltip(tooltipContent, event.pageX, event.pageY); diff --git a/ts/vite.config.ts b/ts/vite.config.ts index 8e1da60b9..233b6c8e1 100644 --- a/ts/vite.config.ts +++ b/ts/vite.config.ts @@ -3,7 +3,8 @@ import svg from "@poppanator/sveltekit-svg"; import { sveltekit } from "@sveltejs/kit/vite"; import { realpathSync } from "fs"; -import { defineConfig } from "vite"; +import { defineConfig as defineViteConfig, mergeConfig } from "vite"; +import { defineConfig as defineVitestConfig } from "vitest/config"; const configure = (proxy: any, _options: any) => { proxy.on("error", (err: any) => { @@ -17,15 +18,8 @@ const configure = (proxy: any, _options: any) => { }); }; -export default defineConfig({ +const viteConfig = defineViteConfig({ plugins: [sveltekit(), svg({})], - test: { - include: ["**/*.{test,spec}.{js,ts}"], - cache: { - // prevent vitest from creating ts/node_modules/.vitest - dir: "../node_modules/.vitest", - }, - }, build: { reportCompressedSize: false, // defaults use chrome87, but we need 77 for qt 5.14 @@ -52,3 +46,15 @@ export default defineConfig({ }, }, }); + +const vitestConfig = defineVitestConfig({ + test: { + include: ["**/*.{test,spec}.{js,ts}"], + cache: { + // prevent vitest from creating ts/node_modules/.vitest + dir: "../node_modules/.vitest", + }, + }, +}); + +export default mergeConfig(viteConfig, vitestConfig); diff --git a/yarn.lock b/yarn.lock index 85d82cc20..dd7450751 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.2.1": +"@ampproject/remapping@^2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== @@ -32,45 +32,45 @@ "@typescript/vfs" "^1.4.0" typescript "4.5.2" -"@dprint/darwin-arm64@0.47.2": - version "0.47.2" - resolved "https://registry.yarnpkg.com/@dprint/darwin-arm64/-/darwin-arm64-0.47.2.tgz#dea8bfa146159e565e510266a2f1d7ebd2676090" - integrity sha512-mVPFBJsXxGDKHHCAY8wbqOyS4028g1bN15H9tivCnPAjwaZhkUimZHXWejXADjhGn+Xm2SlakugY9PY/68pH3Q== +"@dprint/darwin-arm64@0.47.4": + version "0.47.4" + resolved "https://registry.yarnpkg.com/@dprint/darwin-arm64/-/darwin-arm64-0.47.4.tgz#bf2088f71c968447a06e68469bc5eb03c3a42c61" + integrity sha512-TZKDhU3YfPwij66sMAnFjyqACwz0jrJoVLV1x00IR7CluWBas2hOBWQd2UhhncF+llnltQ8U0+m7lU7jrBHEew== -"@dprint/darwin-x64@0.47.2": - version "0.47.2" - resolved "https://registry.yarnpkg.com/@dprint/darwin-x64/-/darwin-x64-0.47.2.tgz#9e03a7ebb1a38ffcd46fbade96e98e8326d4c77c" - integrity sha512-T7wzlc+rBV+6BRRiBjoqoy5Hj4TR2Nv2p2s9+ycyPGs10Kj/JXOWD8dnEHeBgUr2r4qe/ZdcxmsFQ5Hf2n0WuA== +"@dprint/darwin-x64@0.47.4": + version "0.47.4" + resolved "https://registry.yarnpkg.com/@dprint/darwin-x64/-/darwin-x64-0.47.4.tgz#93fc5654d0b515ab00aff7972be80fb6d92a4000" + integrity sha512-6C0p8x9lN8Y81OZHQWCFCn7oE7NDFkeARvG1TIgO9EouKWoa9vNUNoxA5DNhDhlFPSejWfxiwLXn0ZKrPEnCHQ== -"@dprint/linux-arm64-glibc@0.47.2": - version "0.47.2" - resolved "https://registry.yarnpkg.com/@dprint/linux-arm64-glibc/-/linux-arm64-glibc-0.47.2.tgz#7d6c70b1c097d8dd8756cb4cdff40afdde4d42f3" - integrity sha512-B0m1vT5LdVtrNOVdkqpLPrSxuCD+l5bTIgRzPaDoIB1ChWQkler9IlX8C+RStpujjPj6SYvwo5vTzjQSvRdQkA== +"@dprint/linux-arm64-glibc@0.47.4": + version "0.47.4" + resolved "https://registry.yarnpkg.com/@dprint/linux-arm64-glibc/-/linux-arm64-glibc-0.47.4.tgz#b25cd6667b9a4789cfa5474e695fae9d7c79a0f6" + integrity sha512-eQk6DCHNKtvZMmpaBKnmJABUx5uKR3gWR+2I7XCCGaIS5VCM4RV2bA7x9gCUAUaLzmvm17rragHnSkzX+672oQ== -"@dprint/linux-arm64-musl@0.47.2": - version "0.47.2" - resolved "https://registry.yarnpkg.com/@dprint/linux-arm64-musl/-/linux-arm64-musl-0.47.2.tgz#76158f3400848d6d00023559c7b1fbf1cf1198c9" - integrity sha512-zID6wZZqpg2/Q2Us+ERQkbhLwlW3p3xaeEr00MPf49bpydmEjMiPuSjWPkNv+slQSIyIsVovOxF4lbNZjsdtvw== +"@dprint/linux-arm64-musl@0.47.4": + version "0.47.4" + resolved "https://registry.yarnpkg.com/@dprint/linux-arm64-musl/-/linux-arm64-musl-0.47.4.tgz#e0ace501bc638da9af2472b59adc1841fbe6e9ef" + integrity sha512-bHnohA+pnU1ybIYNcw28Z761OXt9q149Izo30gwi2GgY6JJR+3h9Nl4NAsvIPzd0kwL1Unz1ZFGcVMp+wI82Fg== -"@dprint/linux-x64-glibc@0.47.2": - version "0.47.2" - resolved "https://registry.yarnpkg.com/@dprint/linux-x64-glibc/-/linux-x64-glibc-0.47.2.tgz#1c30261e7a085bcbd6d673c7ac4064a4e50c1a02" - integrity sha512-rB3WXMdINnRd33DItIp7mObS7dzHW90ZzeJSsoKJLPp+Z7wXjjb27UUowfqVI4baa/1pd7sdbX54DPohMtfu/A== +"@dprint/linux-x64-glibc@0.47.4": + version "0.47.4" + resolved "https://registry.yarnpkg.com/@dprint/linux-x64-glibc/-/linux-x64-glibc-0.47.4.tgz#bc79f9600d09b8177971a4efcf6f2aaab455eb68" + integrity sha512-hmOA/edKJErs0yw8hHubzBjGuYasvWNRvv3y7FBl5Ega9nq7hCfUhupTs/4g0lTWabcdU2xFA8xVJnaT95uOQw== -"@dprint/linux-x64-musl@0.47.2": - version "0.47.2" - resolved "https://registry.yarnpkg.com/@dprint/linux-x64-musl/-/linux-x64-musl-0.47.2.tgz#27d50baca893bbaa8d5ff6448621a1d68d5eba1b" - integrity sha512-E0+TNbzYdTXJ/jCVjUctVxkda/faw++aDQLfyWGcmdMJnbM7NZz+W4fUpDXzMPsjy+zTWxXcPK7/q2DZz2gnbg== +"@dprint/linux-x64-musl@0.47.4": + version "0.47.4" + resolved "https://registry.yarnpkg.com/@dprint/linux-x64-musl/-/linux-x64-musl-0.47.4.tgz#5d9c514ac1391bbf0bd2affadcb0f185bedfa772" + integrity sha512-1gBQ7uyMw+Ix8uJd1WmdCXRm53VpIjZsQraPZUr+uOtUitZFMFK+dw1bNFpjxGTalahFDHp3XEnTQxLlw2MD1A== -"@dprint/win32-arm64@0.47.2": - version "0.47.2" - resolved "https://registry.yarnpkg.com/@dprint/win32-arm64/-/win32-arm64-0.47.2.tgz#eb7eb2b4acd17d8221ae85c835c80aa1b6b3110c" - integrity sha512-K1EieTCFjfOCmyIhw9zFSduE6qVCNHEveupqZEfbSkVGw5T9MJQ1I9+n7MDb3RIDYEUk0enJ58/w82q8oDKCyA== +"@dprint/win32-arm64@0.47.4": + version "0.47.4" + resolved "https://registry.yarnpkg.com/@dprint/win32-arm64/-/win32-arm64-0.47.4.tgz#315516e5c4497c41f60d772ec5624d92a78e8609" + integrity sha512-05UJS2ggH65BmdJGDkSaUF61CJyau65oqSlpPydmu+RKK5/sEUj/b6A0QCpR57fLh0Q3e9CqkwzfYf4qUJ55ew== -"@dprint/win32-x64@0.47.2": - version "0.47.2" - resolved "https://registry.yarnpkg.com/@dprint/win32-x64/-/win32-x64-0.47.2.tgz#117c4b22de19dd11991851f27424ccc332d2266a" - integrity sha512-LhizWr8VrhHvq4ump8HwOERyFmdLiE8C6A42QSntGXzKdaa2nEOq20x/o56ZIiDcesiV+1TmosMKimPcOZHa+Q== +"@dprint/win32-x64@0.47.4": + version "0.47.4" + resolved "https://registry.yarnpkg.com/@dprint/win32-x64/-/win32-x64-0.47.4.tgz#ccac38bf7221a4c3cd50cc1f1947596605a940f3" + integrity sha512-aQtujd8xtJ84I90pLCTwAl4SvkAhhQPRfDE5BkZYnK+5iMkigA1eQd7UCqAEUKeCJrO5N41OXaEWgmOhBctvJg== "@esbuild/aix-ppc64@0.21.5": version "0.21.5" @@ -298,16 +298,16 @@ integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" - integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== dependencies: - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.3" "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": - version "4.11.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" - integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== + version "4.11.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.2.tgz#dc6925ab4ea52d3b9d6da204a3f0dd9c3852e3ad" + integrity sha512-2WwyTYNVaMNUWPZTOJdkax9iqTdirrApgTbk+Qoq5EPX6myqZvG8QGFRgdKmkjKVG6/G/a565vpPauHk0+hpBA== "@eslint/eslintrc@^2.1.4": version "2.1.4" @@ -324,42 +324,42 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.57.0": - version "8.57.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" - integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== "@floating-ui/core@^1.6.0": - version "1.6.7" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.7.tgz#7602367795a390ff0662efd1c7ae8ca74e75fb12" - integrity sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g== + version "1.6.8" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.8.tgz#aa43561be075815879305965020f492cdb43da12" + integrity sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA== dependencies: - "@floating-ui/utils" "^0.2.7" + "@floating-ui/utils" "^0.2.8" "@floating-ui/dom@^1.4.3": - version "1.6.10" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.10.tgz#b74c32f34a50336c86dcf1f1c845cf3a39e26d6f" - integrity sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A== + version "1.6.11" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.11.tgz#8631857838d34ee5712339eb7cbdfb8ad34da723" + integrity sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ== dependencies: "@floating-ui/core" "^1.6.0" - "@floating-ui/utils" "^0.2.7" + "@floating-ui/utils" "^0.2.8" -"@floating-ui/utils@^0.2.7": - version "0.2.7" - resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.7.tgz#d0ece53ce99ab5a8e37ebdfe5e32452a2bfc073e" - integrity sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA== +"@floating-ui/utils@^0.2.8": + version "0.2.8" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62" + integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig== "@fluent/bundle@^0.18.0": version "0.18.0" resolved "https://registry.yarnpkg.com/@fluent/bundle/-/bundle-0.18.0.tgz#bf67e306719a33baf3b66c88af5ac427d7800dbd" integrity sha512-8Wfwu9q8F9g2FNnv82g6Ch/E1AW1wwljsUOolH5NEtdJdv0sZTuWvfCM7c3teB9dzNaJA8rn4khpidpozHWYEA== -"@humanwhocodes/config-array@^0.11.14": - version "0.11.14" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" - integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== dependencies: - "@humanwhocodes/object-schema" "^2.0.2" + "@humanwhocodes/object-schema" "^2.0.3" debug "^4.3.1" minimatch "^3.0.5" @@ -368,7 +368,7 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^2.0.2": +"@humanwhocodes/object-schema@^2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== @@ -385,13 +385,6 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" -"@jest/schemas@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" - integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== - dependencies: - "@sinclair/typebox" "^0.27.8" - "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -430,9 +423,9 @@ integrity sha512-WQ2gDll12T9WD34fdRFgQVgO8bag3gavrAgJ0frN4phlwdJARpE6gO1YvLEMJR0KKgoc+/Ea/A0Pp11I00xBvw== "@mdn/browser-compat-data@^5.2.34", "@mdn/browser-compat-data@^5.3.13": - version "5.5.50" - resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.5.50.tgz#4a51fa4d64dc8e921649e99e676a5b0973dce45b" - integrity sha512-7Vlybx9Vao3kQNJGps2sFnBSq5uWcUsZtlwU2bUzeeKbwzKO507V7K+vv0TlfQ1qunDy1PiRz6BGtdxyMMD5tQ== + version "5.6.10" + resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.6.10.tgz#5602542b6cb151b8489e5dcd73faa046a0603770" + integrity sha512-plMgRN6pGGaHxvH5Y5DJcWR3C8tC/HHN18saHAWu395XfNRtFMrKPiDeRGQWIHjTXFbQVLra2nFG5GKyezXy+Q== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -462,20 +455,103 @@ dependencies: semver "^7.3.5" +"@parcel/watcher-android-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" + integrity sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg== + +"@parcel/watcher-darwin-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz#c817c7a3b4f3a79c1535bfe54a1c2818d9ffdc34" + integrity sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA== + +"@parcel/watcher-darwin-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz#1a3f69d9323eae4f1c61a5f480a59c478d2cb020" + integrity sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg== + +"@parcel/watcher-freebsd-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz#0d67fef1609f90ba6a8a662bc76a55fc93706fc8" + integrity sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w== + +"@parcel/watcher-linux-arm-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz#ce5b340da5829b8e546bd00f752ae5292e1c702d" + integrity sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA== + +"@parcel/watcher-linux-arm64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz#6d7c00dde6d40608f9554e73998db11b2b1ff7c7" + integrity sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA== + +"@parcel/watcher-linux-arm64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz#bd39bc71015f08a4a31a47cd89c236b9d6a7f635" + integrity sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA== + +"@parcel/watcher-linux-x64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz#0ce29966b082fb6cdd3de44f2f74057eef2c9e39" + integrity sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg== + +"@parcel/watcher-linux-x64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz#d2ebbf60e407170bb647cd6e447f4f2bab19ad16" + integrity sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ== + +"@parcel/watcher-win32-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz#eb4deef37e80f0b5e2f215dd6d7a6d40a85f8adc" + integrity sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg== + +"@parcel/watcher-win32-ia32@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz#94fbd4b497be39fd5c8c71ba05436927842c9df7" + integrity sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw== + +"@parcel/watcher-win32-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz#4bf920912f67cae5f2d264f58df81abfea68dadf" + integrity sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A== + +"@parcel/watcher@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.1.tgz#a50275151a1bb110879c6123589dba90c19f1bf8" + integrity sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA== + dependencies: + detect-libc "^1.0.3" + is-glob "^4.0.3" + micromatch "^4.0.5" + node-addon-api "^7.0.0" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.4.1" + "@parcel/watcher-darwin-arm64" "2.4.1" + "@parcel/watcher-darwin-x64" "2.4.1" + "@parcel/watcher-freebsd-x64" "2.4.1" + "@parcel/watcher-linux-arm-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-musl" "2.4.1" + "@parcel/watcher-linux-x64-glibc" "2.4.1" + "@parcel/watcher-linux-x64-musl" "2.4.1" + "@parcel/watcher-win32-arm64" "2.4.1" + "@parcel/watcher-win32-ia32" "2.4.1" + "@parcel/watcher-win32-x64" "2.4.1" + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@polka/url@^1.0.0-next.24": - version "1.0.0-next.25" - resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.25.tgz#f077fdc0b5d0078d30893396ff4827a13f99e817" - integrity sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ== + version "1.0.0-next.28" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" + integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== "@poppanator/sveltekit-svg@^5.0.0-svelte5.5": - version "5.0.0-svelte5.5" - resolved "https://registry.yarnpkg.com/@poppanator/sveltekit-svg/-/sveltekit-svg-5.0.0-svelte5.5.tgz#0d9fc6d010a645cb4dde18cdf972baab481677b9" - integrity sha512-4OmrM0Q9keA/QyEqwfnq2d69mq/M+9cMCvdPXQ5nIzEIx4D2d/6DthJC9aaX+fNCygZkaJ77yaarVVrZRQ8cEA== + version "5.0.0" + resolved "https://registry.yarnpkg.com/@poppanator/sveltekit-svg/-/sveltekit-svg-5.0.0.tgz#9bb43de812871261de18926b4a1b36c32a91ed04" + integrity sha512-b7hk55SF0HjTS+xFgMG20hy6W0F/m+yRA/ZWcjnsa391rB3Ys3desCiUyIKQYcfvcyuRiQCPedUJMYgu00VdCA== dependencies: "@rollup/pluginutils" "^5.1.0" @@ -485,122 +561,117 @@ integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== "@rollup/pluginutils@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" - integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + version "5.1.3" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.3.tgz#3001bf1a03f3ad24457591f2c259c8e514e0dbdf" + integrity sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A== dependencies: "@types/estree" "^1.0.0" estree-walker "^2.0.2" - picomatch "^2.3.1" + picomatch "^4.0.2" -"@rollup/rollup-android-arm-eabi@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz#8b613b9725e8f9479d142970b106b6ae878610d5" - integrity sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w== +"@rollup/rollup-android-arm-eabi@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz#1661ff5ea9beb362795304cb916049aba7ac9c54" + integrity sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA== -"@rollup/rollup-android-arm64@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz#654ca1049189132ff602bfcf8df14c18da1f15fb" - integrity sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA== +"@rollup/rollup-android-arm64@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz#2ffaa91f1b55a0082b8a722525741aadcbd3971e" + integrity sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA== -"@rollup/rollup-darwin-arm64@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz#6d241d099d1518ef0c2205d96b3fa52e0fe1954b" - integrity sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q== +"@rollup/rollup-darwin-arm64@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz#627007221b24b8cc3063703eee0b9177edf49c1f" + integrity sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA== -"@rollup/rollup-darwin-x64@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz#42bd19d292a57ee11734c980c4650de26b457791" - integrity sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw== +"@rollup/rollup-darwin-x64@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz#0605506142b9e796c370d59c5984ae95b9758724" + integrity sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ== -"@rollup/rollup-linux-arm-gnueabihf@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz#f23555ee3d8fe941c5c5fd458cd22b65eb1c2232" - integrity sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ== +"@rollup/rollup-linux-arm-gnueabihf@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz#62dfd196d4b10c0c2db833897164d2d319ee0cbb" + integrity sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA== -"@rollup/rollup-linux-arm-musleabihf@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz#f3bbd1ae2420f5539d40ac1fde2b38da67779baa" - integrity sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg== +"@rollup/rollup-linux-arm-musleabihf@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz#53ce72aeb982f1f34b58b380baafaf6a240fddb3" + integrity sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw== -"@rollup/rollup-linux-arm64-gnu@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz#7abe900120113e08a1f90afb84c7c28774054d15" - integrity sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw== +"@rollup/rollup-linux-arm64-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz#1632990f62a75c74f43e4b14ab3597d7ed416496" + integrity sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA== -"@rollup/rollup-linux-arm64-musl@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz#9e655285c8175cd44f57d6a1e8e5dedfbba1d820" - integrity sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA== +"@rollup/rollup-linux-arm64-musl@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz#8c03a996efb41e257b414b2e0560b7a21f2d9065" + integrity sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw== -"@rollup/rollup-linux-powerpc64le-gnu@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz#9a79ae6c9e9d8fe83d49e2712ecf4302db5bef5e" - integrity sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg== +"@rollup/rollup-linux-powerpc64le-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz#5b98729628d5bcc8f7f37b58b04d6845f85c7b5d" + integrity sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw== -"@rollup/rollup-linux-riscv64-gnu@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz#67ac70eca4ace8e2942fabca95164e8874ab8128" - integrity sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA== +"@rollup/rollup-linux-riscv64-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz#48e42e41f4cabf3573cfefcb448599c512e22983" + integrity sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg== -"@rollup/rollup-linux-s390x-gnu@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz#9f883a7440f51a22ed7f99e1d070bd84ea5005fc" - integrity sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q== +"@rollup/rollup-linux-s390x-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz#e0b4f9a966872cb7d3e21b9e412a4b7efd7f0b58" + integrity sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g== -"@rollup/rollup-linux-x64-gnu@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz#70116ae6c577fe367f58559e2cffb5641a1dd9d0" - integrity sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg== +"@rollup/rollup-linux-x64-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz#78144741993100f47bd3da72fce215e077ae036b" + integrity sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A== -"@rollup/rollup-linux-x64-musl@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz#f473f88219feb07b0b98b53a7923be716d1d182f" - integrity sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g== +"@rollup/rollup-linux-x64-musl@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz#d9fe32971883cd1bd858336bd33a1c3ca6146127" + integrity sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ== -"@rollup/rollup-win32-arm64-msvc@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz#4349482d17f5d1c58604d1c8900540d676f420e0" - integrity sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw== +"@rollup/rollup-win32-arm64-msvc@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz#71fa3ea369316db703a909c790743972e98afae5" + integrity sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ== -"@rollup/rollup-win32-ia32-msvc@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz#a6fc39a15db618040ec3c2a24c1e26cb5f4d7422" - integrity sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g== +"@rollup/rollup-win32-ia32-msvc@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz#653f5989a60658e17d7576a3996deb3902e342e2" + integrity sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ== -"@rollup/rollup-win32-x64-msvc@4.22.4": - version "4.22.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz#3dd5d53e900df2a40841882c02e56f866c04d202" - integrity sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q== +"@rollup/rollup-win32-x64-msvc@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz#0574d7e87b44ee8511d08cc7f914bcb802b70818" + integrity sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw== "@rtsao/scc@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== - "@sqltools/formatter@^1.2.2": version "1.2.5" resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.5.tgz#3abc203c79b8c3e90fd6c156a0c62d5403520e12" integrity sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw== "@sveltejs/adapter-static@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@sveltejs/adapter-static/-/adapter-static-3.0.4.tgz#5ab50410156c1c71dc78404e498fc38207aa59e4" - integrity sha512-Qm4GAHCnRXwfWG9/AtnQ7mqjyjTs7i0Opyb8H2KH9rMR7fLxqiPx/tXeoE6HHo66+72CjyOb4nFH3lrejY4vzA== + version "3.0.6" + resolved "https://registry.yarnpkg.com/@sveltejs/adapter-static/-/adapter-static-3.0.6.tgz#a580ad86aa90a52b19b6440f3c9521bd731211c1" + integrity sha512-MGJcesnJWj7FxDcB/GbrdYD3q24Uk0PIL4QIX149ku+hlJuj//nxUbb0HxUTpjkecWfHjVveSUnUaQWnPRXlpg== -"@sveltejs/kit@^2.4.1": - version "2.5.25" - resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-2.5.25.tgz#618bc81b0f0c9afad73d2d8e5352b7462b949de3" - integrity sha512-5hBSEN8XEjDZ5+2bHkFh8Z0QyOk0C187cyb12aANe1c8aeKbfu5ZD5XaC2vEH4h0alJFDXPdUkXQBmeeXeMr1A== +"@sveltejs/kit@^2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-2.8.3.tgz#e67f7af2b1f792819877c2cbbc9f8ab7dee411a7" + integrity sha512-DVBVwugfzzn0SxKA+eAmKqcZ7aHZROCHxH7/pyrOi+HLtQ721eEsctGb9MkhEuqj6q/9S/OFYdn37vdxzFPdvw== dependencies: "@types/cookie" "^0.6.0" cookie "^0.6.0" - devalue "^5.0.0" + devalue "^5.1.0" esm-env "^1.0.0" import-meta-resolve "^4.1.0" kleur "^4.1.5" @@ -608,27 +679,27 @@ mrmime "^2.0.0" sade "^1.8.1" set-cookie-parser "^2.6.0" - sirv "^2.0.4" + sirv "^3.0.0" tiny-glob "^0.2.9" "@sveltejs/vite-plugin-svelte-inspector@^3.0.0-next.0||^3.0.0": - version "3.0.0-next.3" - resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.0-next.3.tgz#1891b5e22e49c0e8dfbde83d573d0eeb8365697f" - integrity sha512-kuGJ2CZ5lAw3gKF8Kw0AfKtUJWbwdlDHY14K413B0MCyrzvQvsKTorwmwZcky0+QqY6RnVIZ/5FttB9bQmkLXg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.1.tgz#006bcab6ea90e09c65459133d4e3eaa6b1e83e28" + integrity sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ== dependencies: - debug "^4.3.5" + debug "^4.3.7" -"@sveltejs/vite-plugin-svelte@4.0.0-next.7": - version "4.0.0-next.7" - resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.0-next.7.tgz#2139be5083d2b1d117740c69e4e700156b1abeb2" - integrity sha512-yMUnAqquoayvBDztk1rWUgdtvjv7YcHgopCAB7sWl9SQht8U/7lqwTlJU0ZTAY09pFFRe6bbakd7YoiyyIvJiA== +"@sveltejs/vite-plugin-svelte@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.0.tgz#4e7c2fe6fd262f6bbd7dc82085a76654cbaeafe5" + integrity sha512-kpVJwF+gNiMEsoHaw+FJL76IYiwBikkxYU83+BpqQLdVMff19KeRKLd2wisS8niNBMJ2omv5gG+iGDDwd8jzag== dependencies: "@sveltejs/vite-plugin-svelte-inspector" "^3.0.0-next.0||^3.0.0" - debug "^4.3.6" + debug "^4.3.7" deepmerge "^4.3.1" kleur "^4.1.5" - magic-string "^0.30.11" - vitefu "^1.0.2" + magic-string "^0.30.12" + vitefu "^1.0.3" "@tootallnate/once@2": version "2.0.0" @@ -792,9 +863,9 @@ "@types/d3-time" "*" "@types/d3-selection@*": - version "3.0.10" - resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.10.tgz#98cdcf986d0986de6912b5892e7c015a95ca27fe" - integrity sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg== + version "3.0.11" + resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.11.tgz#bd7a45fc0a8c3167a631675e61bc2ca2b058d4a3" + integrity sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w== "@types/d3-shape@*": version "3.1.6" @@ -819,9 +890,9 @@ integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== "@types/d3-transition@*": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.8.tgz#677707f5eed5b24c66a1918cde05963021351a8f" - integrity sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ== + version "3.0.9" + resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.9.tgz#1136bc57e9ddb3c390dccc9b5ff3b7d2b8d94706" + integrity sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg== dependencies: "@types/d3-selection" "*" @@ -870,14 +941,14 @@ "@types/d3-zoom" "*" "@types/diff@^5.0.0": - version "5.2.2" - resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.2.2.tgz#d430dbb1da6739f1e2565c2c80b54184d4c77658" - integrity sha512-qVqLpd49rmJA2nZzLVsmfS/aiiBpfVE95dHhPVwG0NmSBAt+riPxnj53wq2oBq5m4Q2RF1IWFEUpnZTgrQZfEQ== + version "5.2.3" + resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.2.3.tgz#dcdcfa40df9f011f9465180e0196dfbd921971d9" + integrity sha512-K0Oqlrq3kQMaO2RhfrNQX5trmt+XLyom88zS0u84nnIcLvFnRUMRRHmrGny5GSM+kNO9IZLARsdQHDzkhAgmrQ== -"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0", "@types/estree@^1.0.1", "@types/estree@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" - integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/estree@*", "@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.1", "@types/estree@^1.0.5": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== "@types/fabric@^5.3.7": version "5.3.9" @@ -890,9 +961,9 @@ integrity sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg== "@types/jquery@*", "@types/jquery@^3.5.0": - version "3.5.30" - resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.30.tgz#888d584cbf844d3df56834b69925085038fd80f7" - integrity sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A== + version "3.5.32" + resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.32.tgz#3eb0da20611b92c7c49ebed6163b52a4fdc57def" + integrity sha512-b9Xbf4CkMqS02YH8zACqN1xzdxc3cO735Qe5AbSUFmyOiaWAbcpqh9Wna+Uk0vgACvoQHpWDg2rGdHkYPLmCiQ== dependencies: "@types/sizzle" "*" @@ -921,9 +992,9 @@ "@types/lodash" "*" "@types/lodash@*": - version "4.17.7" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.7.tgz#2f776bcb53adc9e13b2c0dfd493dfcbd7de43612" - integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== + version "4.17.12" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.12.tgz#25d71312bf66512105d71e55d42e22c36bcfc689" + integrity sha512-sviUmCE8AYdaF/KIHLDJBQgeYzPBI0vf/17NaYehBJfYD1j6/L95Slh07NlyK2iNyBNaEkb3En2jRt+a8y3xZQ== "@types/marked@^5.0.0": version "5.0.2" @@ -931,9 +1002,9 @@ integrity sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg== "@types/node@^16.10.2": - version "16.18.107" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.107.tgz#9185eaebaf7d002a67976d61ae8a9a808fc84829" - integrity sha512-VSha8UIBpCpETub8FZ1nXkODXm+k+YRwpuVQsF3zOuD6QyPQeuIdPRm6IBVa2E5en58CUFJfaw6GmYHP2q/vkQ== + version "16.18.115" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.115.tgz#0bb385c4b1a1a996d6bf9d79e5ae786ce03cae51" + integrity sha512-NF5ajYn+dq0tRfswdyp8Df75h7D9z+L8TCIwrXoh46ZLK6KZVXkRhf/luXaZytvm/keUo9vU4m1Bg39St91a5w== "@types/pug@^2.0.6": version "2.0.10" @@ -946,9 +1017,9 @@ integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== "@types/sizzle@*": - version "2.3.8" - resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.8.tgz#518609aefb797da19bf222feb199e8f653ff7627" - integrity sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg== + version "2.3.9" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.9.tgz#d4597dbd4618264c414d7429363e3f50acb66ea2" + integrity sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w== "@types/tern@*": version "0.23.9" @@ -1053,49 +1124,64 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@vitest/expect@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.6.0.tgz#0b3ba0914f738508464983f4d811bc122b51fb30" - integrity sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ== +"@vitest/expect@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.3.tgz#4b9a6fff22be4c4cd5d57e687cfda611b514b0ad" + integrity sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ== dependencies: - "@vitest/spy" "1.6.0" - "@vitest/utils" "1.6.0" - chai "^4.3.10" + "@vitest/spy" "2.1.3" + "@vitest/utils" "2.1.3" + chai "^5.1.1" + tinyrainbow "^1.2.0" -"@vitest/runner@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.6.0.tgz#a6de49a96cb33b0e3ba0d9064a3e8d6ce2f08825" - integrity sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg== +"@vitest/mocker@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.3.tgz#a3593b426551be5715fa108faf04f8a9ddb0a9cc" + integrity sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ== dependencies: - "@vitest/utils" "1.6.0" - p-limit "^5.0.0" - pathe "^1.1.1" - -"@vitest/snapshot@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.6.0.tgz#deb7e4498a5299c1198136f56e6e0f692e6af470" - integrity sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ== - dependencies: - magic-string "^0.30.5" - pathe "^1.1.1" - pretty-format "^29.7.0" - -"@vitest/spy@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.6.0.tgz#362cbd42ccdb03f1613798fde99799649516906d" - integrity sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw== - dependencies: - tinyspy "^2.2.0" - -"@vitest/utils@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.6.0.tgz#5c5675ca7d6f546a7b4337de9ae882e6c57896a1" - integrity sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw== - dependencies: - diff-sequences "^29.6.3" + "@vitest/spy" "2.1.3" estree-walker "^3.0.3" - loupe "^2.3.7" - pretty-format "^29.7.0" + magic-string "^0.30.11" + +"@vitest/pretty-format@2.1.3", "@vitest/pretty-format@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.3.tgz#48b9b03de75507d1d493df7beb48dc39a1946a3e" + integrity sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ== + dependencies: + tinyrainbow "^1.2.0" + +"@vitest/runner@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.3.tgz#20a6da112007dfd92969951df189c6da66c9dac4" + integrity sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ== + dependencies: + "@vitest/utils" "2.1.3" + pathe "^1.1.2" + +"@vitest/snapshot@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.3.tgz#1b405a9c40a82563605b13fdc045217751069e58" + integrity sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg== + dependencies: + "@vitest/pretty-format" "2.1.3" + magic-string "^0.30.11" + pathe "^1.1.2" + +"@vitest/spy@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.3.tgz#2c8a457673094ec4c1ab7c50cb11c58e3624ada2" + integrity sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ== + dependencies: + tinyspy "^3.0.0" + +"@vitest/utils@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.3.tgz#e52aa5745384091b151cbdf79bb5a3ad2bea88d2" + integrity sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA== + dependencies: + "@vitest/pretty-format" "2.1.3" + loupe "^3.1.1" + tinyrainbow "^1.2.0" abab@^2.0.5, abab@^2.0.6: version "2.0.6" @@ -1130,22 +1216,15 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.3.2: - version "8.3.3" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" - integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== - dependencies: - acorn "^8.11.0" - acorn@^7.1.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.11.0, acorn@^8.11.3, acorn@^8.5.0, acorn@^8.9.0: - version "8.12.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" - integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== +acorn@^8.12.1, acorn@^8.5.0, acorn@^8.9.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.13.0.tgz#2a30d670818ad16ddd6a35d3842dacec9e5d7ca3" + integrity sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w== agent-base@6: version "6.0.2" @@ -1170,9 +1249,9 @@ ansi-regex@^5.0.1: integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" @@ -1181,11 +1260,6 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - ansi-styles@^6.1.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" @@ -1204,7 +1278,7 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.3.0: +aria-query@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== @@ -1285,10 +1359,10 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== ast-metadata-inferer@^0.8.0: version "0.8.0" @@ -1309,7 +1383,7 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -axobject-query@^4.0.0: +axobject-query@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== @@ -1367,14 +1441,14 @@ browser-process-hrtime@^1.0.0: integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserslist@^4.21.10: - version "4.23.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" - integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== dependencies: - caniuse-lite "^1.0.30001646" - electron-to-chromium "^1.5.4" + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" node-releases "^2.0.18" - update-browserslist-db "^1.1.0" + update-browserslist-db "^1.1.1" buffer-crc32@^1.0.0: version "1.0.0" @@ -1407,28 +1481,26 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -caniuse-lite@^1.0.30001431, caniuse-lite@^1.0.30001524, caniuse-lite@^1.0.30001646: - version "1.0.30001655" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz#0ce881f5a19a2dcfda2ecd927df4d5c1684b982f" - integrity sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg== +caniuse-lite@^1.0.30001431, caniuse-lite@^1.0.30001524, caniuse-lite@^1.0.30001669: + version "1.0.30001671" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001671.tgz#c660a8a0bf6bb8eedaac683d29074e455e84e3f1" + integrity sha512-jocyVaSSfXg2faluE6hrWkMgDOiULBMca4QLtDT39hw1YxaIPHWc1CcTCKkPmHgGH6tKji6ZNbMSmUAvENf2/A== canvas@^2.8.0, "canvas@npm:empty-npm-package": version "1.0.0" resolved "https://registry.yarnpkg.com/empty-npm-package/-/empty-npm-package-1.0.0.tgz#fda29eb6de5efa391f73d578697853af55f6793a" integrity sha512-q4Mq/+XO7UNDdMiPpR/LIBIW1Zl4V0Z6UT9aKGqIAnBCtCb3lvZJM1KbDbdzdC8fKflwflModfjR29Nt0EpcwA== -chai@^4.3.10: - version "4.5.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8" - integrity sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw== +chai@^5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d" + integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw== dependencies: - assertion-error "^1.1.0" - check-error "^1.0.3" - deep-eql "^4.1.3" - get-func-name "^2.0.2" - loupe "^2.3.6" - pathval "^1.1.1" - type-detect "^4.1.0" + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" chalk@4.1.2, chalk@^4.0.0: version "4.1.2" @@ -1438,12 +1510,10 @@ chalk@4.1.2, chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -check-error@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" - integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== - dependencies: - get-func-name "^2.0.2" +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== "chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.1: version "3.6.0" @@ -1461,16 +1531,16 @@ check-error@^1.0.3: fsevents "~2.3.2" chokidar@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.0.tgz#4d603963e5dd762dc5c7bb1cb5664e53a3002225" - integrity sha512-mxIojEAQcuEvT/lyXq+jf/3cO/KoA6z4CeNDGGevTybECPOMFCnQy3OPahluUkbqgPNGw5Bi78UC7Po6Lhy+NA== + version "4.0.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41" + integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA== dependencies: readdirp "^4.0.1" codemirror@^5.63.1: - version "5.65.17" - resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.17.tgz#00d71f34c3518471ae4c0de23a2f8bb39a6df6ca" - integrity sha512-1zOsUx3lzAOu/gnMAZkQ9kpIHcPYOc9y1Fbm2UVk5UBPkdq380nhkelG0qUwm1f7wPvTbndu9ZYlug35EwAZRQ== + version "5.65.18" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.18.tgz#d7146e4271135a9b4adcd023a270185457c9c428" + integrity sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA== color-convert@^2.0.1: version "2.0.1" @@ -1501,15 +1571,10 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -confbox@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.7.tgz#ccfc0a2bcae36a84838e83a3b7f770fb17d6c579" - integrity sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA== - -cookie@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" - integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== +cookie@0.7.0, cookie@^0.6.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.0.tgz#2148f68a77245d5c2c0005d264bc3e08cfa0655d" + integrity sha512-qCf+V4dtlNhSRXGAZatc1TasyFO6GjohcOul807YOb5ik3+kQSnb4d7iajeCL8QHaJ4uZEjCgiCJerKXwdRVlQ== cross-env@^7.0.2: version "7.0.3" @@ -1518,10 +1583,10 @@ cross-env@^7.0.2: dependencies: cross-spawn "^7.0.1" -cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -1868,12 +1933,12 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" - integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== +debug@4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.6, debug@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: - ms "2.1.2" + ms "^2.1.3" debug@^3.2.7: version "3.2.7" @@ -1882,24 +1947,15 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.3.5, debug@^4.3.6: - version "4.3.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" - integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== - dependencies: - ms "^2.1.3" - decimal.js@^10.3.1: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== -deep-eql@^4.1.3: - version "4.1.4" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.4.tgz#d0d3912865911bb8fac5afb4e3acfa6a28dc72b7" - integrity sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg== - dependencies: - type-detect "^4.0.0" +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== deep-is@^0.1.3: version "0.1.4" @@ -1946,15 +2002,15 @@ detect-indent@^6.1.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== -devalue@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/devalue/-/devalue-5.0.0.tgz#1ca0099a7d715b4d6cac3924e770ccbbc584ad98" - integrity sha512-gO+/OMXF7488D+u3ue+G7Y4AA3ZmUnB3eHJXmBTgNHvr4ZNzl36A0ZtG+XCRNYCkYx/bFmw4qtkoFLa+wSrwAA== +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== -diff-sequences@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== +devalue@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/devalue/-/devalue-5.1.1.tgz#a71887ac0f354652851752654e4bd435a53891ae" + integrity sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw== diff@^5.0.0: version "5.2.0" @@ -2020,28 +2076,28 @@ domutils@^3.0.1: domhandler "^5.0.3" dprint@^0.47.2: - version "0.47.2" - resolved "https://registry.yarnpkg.com/dprint/-/dprint-0.47.2.tgz#f3aca518324b9948066652c87e4c4a3bc509869d" - integrity sha512-geUcVIIrmLaY+YtuOl4gD7J/QCjsXZa5gUqre9sO6cgH0X/Fa9heBN3l/AWVII6rKPw45ATuCSDWz1pyO+HkPQ== + version "0.47.4" + resolved "https://registry.yarnpkg.com/dprint/-/dprint-0.47.4.tgz#6b944110d5dc942ae5787f6d76931624cb4756cc" + integrity sha512-eyjiV7+aW34tTSTwXe0v6aQtl9zM3UrjAMc3VggcvvvwWBlzHXA4651w1odnY58wja3kxiwnP6ok0kzfOZHmrg== optionalDependencies: - "@dprint/darwin-arm64" "0.47.2" - "@dprint/darwin-x64" "0.47.2" - "@dprint/linux-arm64-glibc" "0.47.2" - "@dprint/linux-arm64-musl" "0.47.2" - "@dprint/linux-x64-glibc" "0.47.2" - "@dprint/linux-x64-musl" "0.47.2" - "@dprint/win32-arm64" "0.47.2" - "@dprint/win32-x64" "0.47.2" + "@dprint/darwin-arm64" "0.47.4" + "@dprint/darwin-x64" "0.47.4" + "@dprint/linux-arm64-glibc" "0.47.4" + "@dprint/linux-arm64-musl" "0.47.4" + "@dprint/linux-x64-glibc" "0.47.4" + "@dprint/linux-x64-musl" "0.47.4" + "@dprint/win32-arm64" "0.47.4" + "@dprint/win32-x64" "0.47.4" eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -electron-to-chromium@^1.5.4: - version "1.5.13" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz#1abf0410c5344b2b829b7247e031f02810d442e6" - integrity sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q== +electron-to-chromium@^1.5.41: + version "1.5.47" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz#ef0751bc19b28be8ee44cd8405309de3bf3b20c7" + integrity sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ== emoji-regex@^8.0.0: version "8.0.0" @@ -2168,9 +2224,9 @@ esbuild-sass-plugin@^2: sass "^1.7.3" esbuild-svelte@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/esbuild-svelte/-/esbuild-svelte-0.8.1.tgz#12d716f420e0b9516ba3cf9f7c264432afac8a1a" - integrity sha512-iswZSetqRxYaQoWMd38Gu6AanIL6KFsVj8/unei7qTaxjAkRDulW62/Bc5nmeogKBWekBvrPOE106wui7gYARQ== + version "0.8.2" + resolved "https://registry.yarnpkg.com/esbuild-svelte/-/esbuild-svelte-0.8.2.tgz#f6c09b57879caabd068685684b47f42dbd266396" + integrity sha512-tG97WrhH/OH8wFCmRBk6sRggMVMPNZDktGx1jJcvh9Obvjwm8M1UYoXmliuLL+4fs/wfyVVwM2fvyNYz2e6UPQ== dependencies: "@jridgewell/trace-mapping" "^0.3.19" @@ -2231,7 +2287,7 @@ esbuild@^0.21.3: "@esbuild/win32-ia32" "0.21.5" "@esbuild/win32-x64" "0.21.5" -escalade@^3.1.2: +escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== @@ -2268,10 +2324,10 @@ eslint-import-resolver-node@^0.3.9: is-core-module "^2.13.0" resolve "^1.22.4" -eslint-module-utils@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.9.0.tgz#95d4ac038a68cd3f63482659dffe0883900eb342" - integrity sha512-McVbYmwA3NEKwRQY5g4aWMdcZE5xZxV8i8l7CqJSrameuGSQJtSWaL/LxTEzSKKaCcOhlpDR8XEfYXWPrdo/ZQ== +eslint-module-utils@^2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b" + integrity sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg== dependencies: debug "^3.2.7" @@ -2289,9 +2345,9 @@ eslint-plugin-compat@^4.1.4: semver "^7.5.4" eslint-plugin-import@^2.25.4: - version "2.30.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz#21ceea0fc462657195989dd780e50c92fe95f449" - integrity sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw== + version "2.31.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" + integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== dependencies: "@rtsao/scc" "^1.1.0" array-includes "^3.1.8" @@ -2301,7 +2357,7 @@ eslint-plugin-import@^2.25.4: debug "^3.2.7" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.9.0" + eslint-module-utils "^2.12.0" hasown "^2.0.2" is-core-module "^2.15.1" is-glob "^4.0.3" @@ -2310,24 +2366,25 @@ eslint-plugin-import@^2.25.4: object.groupby "^1.0.3" object.values "^1.2.0" semver "^6.3.1" + string.prototype.trimend "^1.0.8" tsconfig-paths "^3.15.0" eslint-plugin-svelte@^2: - version "2.43.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-svelte/-/eslint-plugin-svelte-2.43.0.tgz#bcfaec0e114e3450071415c3ef9c57dcf7ce118f" - integrity sha512-REkxQWvg2pp7QVLxQNa+dJ97xUqRe7Y2JJbSWkHSuszu0VcblZtXkPBPckkivk99y5CdLw4slqfPylL2d/X4jQ== + version "2.46.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-svelte/-/eslint-plugin-svelte-2.46.0.tgz#87bcc2820233065f79114012203b082319ff03e9" + integrity sha512-1A7iEMkzmCZ9/Iz+EAfOGYL8IoIG6zeKEq1SmpxGeM5SXmoQq+ZNnCpXFVJpsxPWYx8jIVGMerQMzX20cqUl0g== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@jridgewell/sourcemap-codec" "^1.4.15" eslint-compat-utils "^0.5.1" esutils "^2.0.3" - known-css-properties "^0.34.0" + known-css-properties "^0.35.0" postcss "^8.4.38" postcss-load-config "^3.1.4" postcss-safe-parser "^6.0.0" postcss-selector-parser "^6.1.0" semver "^7.6.2" - svelte-eslint-parser "^0.41.0" + svelte-eslint-parser "^0.43.0" eslint-scope@^5.1.1: version "5.1.1" @@ -2351,15 +2408,15 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint@^8.44.0: - version "8.57.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" - integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.57.0" - "@humanwhocodes/config-array" "^0.11.14" + "@eslint/js" "8.57.1" + "@humanwhocodes/config-array" "^0.13.0" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" "@ungap/structured-clone" "^1.2.0" @@ -2462,21 +2519,6 @@ esutils@^2.0.2, esutils@^2.0.3: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -execa@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" - integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^8.0.1" - human-signals "^5.0.0" - is-stream "^3.0.0" - merge-stream "^2.0.0" - npm-run-path "^5.1.0" - onetime "^6.0.0" - signal-exit "^4.1.0" - strip-final-newline "^3.0.0" - fabric@^5.3.0: version "5.4.0" resolved "https://registry.yarnpkg.com/fabric/-/fabric-5.4.0.tgz#314d0b31e6ae0c4b2b097dc5ed7fb0bd57cc79e5" @@ -2570,9 +2612,9 @@ foreground-child@^3.1.0: signal-exit "^4.0.1" form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + version "4.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" @@ -2608,11 +2650,6 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -get-func-name@^2.0.1, get-func-name@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" - integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== - get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" @@ -2624,11 +2661,6 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" -get-stream@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" - integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== - get-symbol-description@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" @@ -2639,9 +2671,9 @@ get-symbol-description@^1.0.2: get-intrinsic "^1.2.4" get-tsconfig@^4.7.2: - version "4.8.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.0.tgz#125dc13a316f61650a12b20c97c11b8fd996fedd" - integrity sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw== + version "4.8.1" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.1.tgz#8995eb391ae6e1638d251118c7b56de7eb425471" + integrity sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg== dependencies: resolve-pkg-maps "^1.0.0" @@ -2814,11 +2846,6 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -human-signals@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" - integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== - iconv-lite@0.6, iconv-lite@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" @@ -3008,11 +3035,6 @@ is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: dependencies: call-bind "^1.0.7" -is-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" - integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== - is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -3072,11 +3094,6 @@ jquery-ui-dist@^1.12.1: resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== -js-tokens@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.0.tgz#0f893996d6f3ed46df7f0a3b12a03f5fd84223c1" - integrity sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ== - js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -3156,10 +3173,10 @@ kleur@^4.1.5: resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== -known-css-properties@^0.34.0: - version "0.34.0" - resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.34.0.tgz#ccd7e9f4388302231b3f174a8b1d5b1f7b576cea" - integrity sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ== +known-css-properties@^0.35.0: + version "0.35.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.35.0.tgz#f6f8e40ab4e5700fa32f5b2ef5218a56bc853bd6" + integrity sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A== levn@^0.4.1: version "0.4.1" @@ -3191,14 +3208,6 @@ lilconfig@^2.0.5: resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== -local-pkg@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.5.0.tgz#093d25a346bae59a99f80e75f6e9d36d7e8c925c" - integrity sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg== - dependencies: - mlly "^1.4.2" - pkg-types "^1.0.3" - locate-character@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-character/-/locate-character-3.0.0.tgz#0305c5b8744f61028ef5d01f444009e00779f974" @@ -3231,12 +3240,10 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -loupe@^2.3.6, loupe@^2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" - integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== - dependencies: - get-func-name "^2.0.1" +loupe@^3.1.0, loupe@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" + integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== lru-cache@^10.2.0: version "10.4.3" @@ -3248,10 +3255,10 @@ lru-cache@^7.5.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== -magic-string@^0.30.11, magic-string@^0.30.5: - version "0.30.11" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.11.tgz#301a6f93b3e8c2cb13ac1a7a673492c0dfd12954" - integrity sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A== +magic-string@^0.30.11, magic-string@^0.30.12, magic-string@^0.30.5: + version "0.30.12" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.12.tgz#9eb11c9d072b9bcb4940a5b2c2e1a217e4ee1a60" + integrity sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" @@ -3275,17 +3282,12 @@ mdn-data@2.0.30: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^4.0.4: +micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -3305,11 +3307,6 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" -mimic-fn@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" - integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== - min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -3351,16 +3348,6 @@ mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mlly@^1.4.2, mlly@^1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.1.tgz#e0336429bb0731b6a8e887b438cbdae522c8f32f" - integrity sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA== - dependencies: - acorn "^8.11.3" - pathe "^1.1.2" - pkg-types "^1.1.1" - ufo "^1.5.3" - mri@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" @@ -3371,11 +3358,6 @@ mrmime@^2.0.0: resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -3396,6 +3378,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== + node-releases@^2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" @@ -3428,13 +3415,6 @@ npm-normalize-package-bin@^3.0.0: resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832" integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== -npm-run-path@^5.1.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" - integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== - dependencies: - path-key "^4.0.0" - nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" @@ -3443,9 +3423,9 @@ nth-check@^2.0.1: boolbase "^1.0.0" nwsapi@^2.2.0: - version "2.2.12" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.12.tgz#fb6af5c0ec35b27b4581eb3bbad34ec9e5c696f8" - integrity sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w== + version "2.2.13" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.13.tgz#e56b4e98960e7a040e5474536587e599c4ff4655" + integrity sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ== object-inspect@^1.13.1: version "1.13.2" @@ -3502,13 +3482,6 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" - integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== - dependencies: - mimic-fn "^4.0.0" - optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -3528,13 +3501,6 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" -p-limit@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985" - integrity sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ== - dependencies: - yocto-queue "^1.0.0" - p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" @@ -3543,9 +3509,9 @@ p-locate@^5.0.0: p-limit "^3.0.2" package-json-from-dist@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" - integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== parent-module@^1.0.0: version "1.0.1" @@ -3574,11 +3540,6 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-key@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" - integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== - path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -3597,34 +3558,30 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pathe@^1.1.1, pathe@^1.1.2: +pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== -pathval@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" - integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== +pathval@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== -picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" - integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== +picocolors@^1.0.0, picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pkg-types@^1.0.3, pkg-types@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.2.0.tgz#d0268e894e93acff11a6279de147e83354ebd42d" - integrity sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA== - dependencies: - confbox "^0.1.7" - mlly "^1.7.1" - pathe "^1.1.2" +picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== possible-typed-array-names@^1.0.0: version "1.0.0" @@ -3657,16 +3614,7 @@ postcss-selector-parser@^6.1.0: cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss@^8.4.38, postcss@^8.4.39: - version "8.4.44" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.44.tgz#d56834ef6508610ba224bb22b2457b2169ed0480" - integrity sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw== - dependencies: - nanoid "^3.3.7" - picocolors "^1.0.1" - source-map-js "^1.2.0" - -postcss@^8.4.43: +postcss@^8.4.38, postcss@^8.4.39, postcss@^8.4.43: version "8.4.47" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== @@ -3681,24 +3629,15 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier-plugin-svelte@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-3.2.6.tgz#22e5b6783a2c87bfe112e0f4b1886ea3db236bf4" - integrity sha512-Y1XWLw7vXUQQZmgv1JAEiLcErqUniAF2wO7QJsw8BVMvpLET2dI5WpEIEJx1r11iHVdSMzQxivyfrH9On9t2IQ== + version "3.2.7" + resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-3.2.7.tgz#10db2d553b48c6ed412e2d00688f8d2eaa274f8a" + integrity sha512-/Dswx/ea0lV34If1eDcG3nulQ63YNr5KPDfMsjbdtpSWOxKKJ7nAc2qlVuYwEvCr4raIuredNoR7K4JCkmTGaQ== prettier@^2.4.1: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== -pretty-format@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" - integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== - dependencies: - "@jest/schemas" "^29.6.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -3719,11 +3658,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -react-is@^18.0.0: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" - integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== - read-installed-packages@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/read-installed-packages/-/read-installed-packages-2.0.1.tgz#8ba63a9382a5158c37a288c2f21268d6eb9c85eb" @@ -3748,9 +3682,9 @@ read-package-json@^6.0.0: npm-normalize-package-bin "^3.0.0" readdirp@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.1.tgz#b2fe35f8dca63183cd3b86883ecc8f720ea96ae6" - integrity sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw== + version "4.0.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a" + integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA== readdirp@~3.6.0: version "3.6.0" @@ -3760,14 +3694,14 @@ readdirp@~3.6.0: picomatch "^2.2.1" regexp.prototype.flags@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" - integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + version "1.5.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" + integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== dependencies: - call-bind "^1.0.6" + call-bind "^1.0.7" define-properties "^1.2.1" es-errors "^1.3.0" - set-function-name "^2.0.1" + set-function-name "^2.0.2" requires-port@^1.0.0: version "1.0.0" @@ -3818,28 +3752,28 @@ robust-predicates@^3.0.2: integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== rollup@^4.20.0: - version "4.22.4" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.22.4.tgz#4135a6446671cd2a2453e1ad42a45d5973ec3a0f" - integrity sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A== + version "4.24.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.24.0.tgz#c14a3576f20622ea6a5c9cad7caca5e6e9555d05" + integrity sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg== dependencies: - "@types/estree" "1.0.5" + "@types/estree" "1.0.6" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.22.4" - "@rollup/rollup-android-arm64" "4.22.4" - "@rollup/rollup-darwin-arm64" "4.22.4" - "@rollup/rollup-darwin-x64" "4.22.4" - "@rollup/rollup-linux-arm-gnueabihf" "4.22.4" - "@rollup/rollup-linux-arm-musleabihf" "4.22.4" - "@rollup/rollup-linux-arm64-gnu" "4.22.4" - "@rollup/rollup-linux-arm64-musl" "4.22.4" - "@rollup/rollup-linux-powerpc64le-gnu" "4.22.4" - "@rollup/rollup-linux-riscv64-gnu" "4.22.4" - "@rollup/rollup-linux-s390x-gnu" "4.22.4" - "@rollup/rollup-linux-x64-gnu" "4.22.4" - "@rollup/rollup-linux-x64-musl" "4.22.4" - "@rollup/rollup-win32-arm64-msvc" "4.22.4" - "@rollup/rollup-win32-ia32-msvc" "4.22.4" - "@rollup/rollup-win32-x64-msvc" "4.22.4" + "@rollup/rollup-android-arm-eabi" "4.24.0" + "@rollup/rollup-android-arm64" "4.24.0" + "@rollup/rollup-darwin-arm64" "4.24.0" + "@rollup/rollup-darwin-x64" "4.24.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.24.0" + "@rollup/rollup-linux-arm-musleabihf" "4.24.0" + "@rollup/rollup-linux-arm64-gnu" "4.24.0" + "@rollup/rollup-linux-arm64-musl" "4.24.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.24.0" + "@rollup/rollup-linux-riscv64-gnu" "4.24.0" + "@rollup/rollup-linux-s390x-gnu" "4.24.0" + "@rollup/rollup-linux-x64-gnu" "4.24.0" + "@rollup/rollup-linux-x64-musl" "4.24.0" + "@rollup/rollup-win32-arm64-msvc" "4.24.0" + "@rollup/rollup-win32-ia32-msvc" "4.24.0" + "@rollup/rollup-win32-x64-msvc" "4.24.0" fsevents "~2.3.2" run-parallel@^1.1.9: @@ -3905,10 +3839,11 @@ sass@<1.77: source-map-js ">=0.6.2 <2.0.0" sass@^1.7.3: - version "1.79.3" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.79.3.tgz#7811b000eb68195fe51dea89177e73e7ef7f546f" - integrity sha512-m7dZxh0W9EZ3cw50Me5GOuYm/tVAJAn91SUnohLRo9cXBixGUOdvmryN+dXpwR831bhoY3Zv7rEFt85PUwTmzA== + version "1.80.4" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.80.4.tgz#bc0418fd796cad2f1a1309d8b4d7fe44b7027de0" + integrity sha512-rhMQ2tSF5CsuuspvC94nPM9rToiAFw2h3JTrLlgmNw1MH79v8Cr3DH6KF6o6r+8oofY3iYVPUf66KzC8yuVN1w== dependencies: + "@parcel/watcher" "^2.4.1" chokidar "^4.0.0" immutable "^4.0.0" source-map-js ">=0.6.2 <2.0.0" @@ -3931,9 +3866,9 @@ semver@^6.3.1: integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== set-cookie-parser@^2.6.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz#ef5552b56dc01baae102acb5fc9fb8cd060c30f9" - integrity sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ== + version "2.7.1" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943" + integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ== set-function-length@^1.2.1: version "1.2.2" @@ -3947,7 +3882,7 @@ set-function-length@^1.2.1: gopd "^1.0.1" has-property-descriptors "^1.0.2" -set-function-name@^2.0.1: +set-function-name@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== @@ -3984,15 +3919,15 @@ siginfo@^2.0.0: resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== -signal-exit@^4.0.1, signal-exit@^4.1.0: +signal-exit@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -sirv@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" - integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== +sirv@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-3.0.0.tgz#f8d90fc528f65dff04cb597a88609d4e8a4361ce" + integrity sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg== dependencies: "@polka/url" "^1.0.0-next.24" mrmime "^2.0.0" @@ -4018,12 +3953,7 @@ sorcery@^0.11.0: minimist "^1.2.0" sander "^0.5.0" -"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== - -source-map-js@^1.2.1: +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -4095,13 +4025,21 @@ stackback@0.0.2: resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== -std-env@^3.5.0: +std-env@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: - name string-width-cjs +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4147,7 +4085,14 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -4166,11 +4111,6 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-final-newline@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" - integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== - strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -4183,13 +4123,6 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-literal@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-2.1.0.tgz#6d82ade5e2e74f5c7e8739b6c84692bd65f0bd2a" - integrity sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw== - dependencies: - js-tokens "^9.0.0" - supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -4214,10 +4147,10 @@ svelte-check@^3.4.4: svelte-preprocess "^5.1.3" typescript "^5.0.3" -svelte-eslint-parser@^0.41.0: - version "0.41.0" - resolved "https://registry.yarnpkg.com/svelte-eslint-parser/-/svelte-eslint-parser-0.41.0.tgz#7d02c2314abe7dc4fe0e935bf4fcc28078c590f2" - integrity sha512-L6f4hOL+AbgfBIB52Z310pg1d2QjRqm7wy3kI1W6hhdhX5bvu7+f0R6w4ykp5HoDdzq+vGhIJmsisaiJDGmVfA== +svelte-eslint-parser@^0.43.0: + version "0.43.0" + resolved "https://registry.yarnpkg.com/svelte-eslint-parser/-/svelte-eslint-parser-0.43.0.tgz#649e80f65183c4c1d1536d03dcb903e0632f4da4" + integrity sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA== dependencies: eslint-scope "^7.2.2" eslint-visitor-keys "^3.4.3" @@ -4241,23 +4174,23 @@ svelte-preprocess@^5.0.4, svelte-preprocess@^5.1.3: sorcery "^0.11.0" strip-indent "^3.0.0" -svelte@5.0.0-next.179: - version "5.0.0-next.179" - resolved "https://registry.yarnpkg.com/svelte/-/svelte-5.0.0-next.179.tgz#5e74b02afe7ac3b8b7d5f38ab0a7bddf58472b35" - integrity sha512-z2x/RcctbQ95vNt52p/+fCJzyU2RVmK03V6thiFT/gblNHw/qONb6hZXfxfCgIsCWjm8thDcgDcmRClZj/Ging== +svelte@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-5.0.0.tgz#509fa4e90034a4dd8f0ee19215af373f4d3be793" + integrity sha512-jv2IvTtakG58DqZMo6fY3T6HFmGV4iDQH2lSUyfmCEYaoa+aCNcF+9rERbdDvT4XDF0nQBg6TEoJn0dirED8VQ== dependencies: - "@ampproject/remapping" "^2.2.1" - "@jridgewell/sourcemap-codec" "^1.4.15" + "@ampproject/remapping" "^2.3.0" + "@jridgewell/sourcemap-codec" "^1.5.0" "@types/estree" "^1.0.5" - acorn "^8.11.3" + acorn "^8.12.1" acorn-typescript "^1.4.13" - aria-query "^5.3.0" - axobject-query "^4.0.0" + aria-query "^5.3.1" + axobject-query "^4.1.0" esm-env "^1.0.0" esrap "^1.2.2" is-reference "^3.0.2" locate-character "^3.0.0" - magic-string "^0.30.5" + magic-string "^0.30.11" zimmerframe "^1.1.2" svgo@^3.2.0: @@ -4291,20 +4224,30 @@ tiny-glob@^0.2.9: globalyzer "0.1.0" globrex "^0.1.2" -tinybench@^2.5.1: +tinybench@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== -tinypool@^0.8.3: - version "0.8.4" - resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.4.tgz#e217fe1270d941b39e98c625dcecebb1408c9aa8" - integrity sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ== +tinyexec@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98" + integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ== -tinyspy@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.1.tgz#117b2342f1f38a0dbdcc73a50a454883adf861d1" - integrity sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A== +tinypool@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.1.tgz#c64233c4fac4304e109a64340178760116dbe1fe" + integrity sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA== + +tinyrainbow@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" + integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== + +tinyspy@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" + integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== to-regex-range@^5.0.1: version "5.0.1" @@ -4356,9 +4299,9 @@ tslib@^1.8.1: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.3: - version "2.7.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" - integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + version "2.8.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b" + integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== tsutils@^3.21.0: version "3.21.0" @@ -4385,11 +4328,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" - integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== - type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -4445,14 +4383,9 @@ typescript@4.5.2: integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== typescript@^5.0.3, typescript@^5.0.4: - version "5.5.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" - integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== - -ufo@^1.5.3: - version "1.5.4" - resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.4.tgz#16d6949674ca0c9e0fbbae1fa20a71d7b1ded754" - integrity sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ== + version "5.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" + integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== unbox-primitive@^1.0.2: version "1.0.2" @@ -4469,13 +4402,13 @@ universalify@^0.2.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== -update-browserslist-db@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" - integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== dependencies: - escalade "^3.1.2" - picocolors "^1.0.1" + escalade "^3.2.0" + picocolors "^1.1.0" uri-js@^4.2.2: version "4.4.1" @@ -4505,18 +4438,17 @@ validate-npm-package-license@^3.0.4: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -vite-node@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.6.0.tgz#2c7e61129bfecc759478fa592754fd9704aaba7f" - integrity sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw== +vite-node@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.3.tgz#8291d31f91c69dc22fea7909f4394c2b3cc2e2d9" + integrity sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA== dependencies: cac "^6.7.14" - debug "^4.3.4" - pathe "^1.1.1" - picocolors "^1.0.0" + debug "^4.3.6" + pathe "^1.1.2" vite "^5.0.0" -vite@=5.4.7, vite@^5.0.0: +vite@^5.0.0: version "5.4.7" resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.7.tgz#d226f57c08b61379e955f3836253ed3efb2dcf00" integrity sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ== @@ -4527,36 +4459,46 @@ vite@=5.4.7, vite@^5.0.0: optionalDependencies: fsevents "~2.3.3" -vitefu@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-1.0.2.tgz#fbe9f7c7478be51678214bf468d7296ce29bf8ff" - integrity sha512-0/iAvbXyM3RiPPJ4lyD4w6Mjgtf4ejTK6TPvTNG3H32PLwuT0N/ZjJLiXug7ETE/LWtTeHw9WRv7uX/tIKYyKg== - -vitest@^1.2.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.6.0.tgz#9d5ad4752a3c451be919e412c597126cffb9892f" - integrity sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA== +vite@^5.4.10: + version "5.4.10" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.10.tgz#d358a7bd8beda6cf0f3b7a450a8c7693a4f80c18" + integrity sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ== dependencies: - "@vitest/expect" "1.6.0" - "@vitest/runner" "1.6.0" - "@vitest/snapshot" "1.6.0" - "@vitest/spy" "1.6.0" - "@vitest/utils" "1.6.0" - acorn-walk "^8.3.2" - chai "^4.3.10" - debug "^4.3.4" - execa "^8.0.1" - local-pkg "^0.5.0" - magic-string "^0.30.5" - pathe "^1.1.1" - picocolors "^1.0.0" - std-env "^3.5.0" - strip-literal "^2.0.0" - tinybench "^2.5.1" - tinypool "^0.8.3" + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" + optionalDependencies: + fsevents "~2.3.3" + +vitefu@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-1.0.3.tgz#0467c75ee2be951c35246605b7fdbdbfd03b65d1" + integrity sha512-iKKfOMBHob2WxEJbqbJjHAkmYgvFDPhuqrO82om83S8RLk+17FtyMBfcyeH8GqD0ihShtkMW/zzJgiA51hCNCQ== + +vitest@^2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.3.tgz#dae1055dd328621b59fc6e594fd988fbf2e5370e" + integrity sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA== + dependencies: + "@vitest/expect" "2.1.3" + "@vitest/mocker" "2.1.3" + "@vitest/pretty-format" "^2.1.3" + "@vitest/runner" "2.1.3" + "@vitest/snapshot" "2.1.3" + "@vitest/spy" "2.1.3" + "@vitest/utils" "2.1.3" + chai "^5.1.1" + debug "^4.3.6" + magic-string "^0.30.11" + pathe "^1.1.2" + std-env "^3.7.0" + tinybench "^2.9.0" + tinyexec "^0.3.0" + tinypool "^1.0.0" + tinyrainbow "^1.2.0" vite "^5.0.0" - vite-node "1.6.0" - why-is-node-running "^2.2.2" + vite-node "2.1.3" + why-is-node-running "^2.3.0" w3c-hr-time@^1.0.2: version "1.0.2" @@ -4634,7 +4576,7 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -why-is-node-running@^2.2.2: +why-is-node-running@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== @@ -4695,11 +4637,6 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -yocto-queue@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" - integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== - zimmerframe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/zimmerframe/-/zimmerframe-1.1.2.tgz#5b75f1fa83b07ae2a428d51e50f58e2ae6855e5e"