diff --git a/Cargo.lock b/Cargo.lock index f4c1111d..f5aba6b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,14 +209,14 @@ dependencies = [ "clipboard-win", "image", "log", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-core-graphics", "objc2-foundation 0.3.2", "parking_lot", "percent-encoding", - "windows-sys 0.60.2", + "windows-sys 0.59.0", "wl-clipboard-rs", "x11rb", ] @@ -437,15 +437,6 @@ version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec 0.8.0", -] - [[package]] name = "bit-set" version = "0.10.0" @@ -456,10 +447,13 @@ dependencies = [ ] [[package]] -name = "bit-vec" -version = "0.8.0" +name = "bit-set" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +checksum = "56d87354e4229f54a44f7bf2435906a4656dba36026ab6eaca629a2c436a691c" +dependencies = [ + "bit-vec 0.10.1", +] [[package]] name = "bit-vec" @@ -467,6 +461,16 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" +[[package]] +name = "bit-vec" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5727b15fa97d4f4fee0a3b7c3d550ed0269f54329207b86388de918604e31269" +dependencies = [ + "borsh", + "serde", +] + [[package]] name = "bit_field" version = "0.10.3" @@ -509,7 +513,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "objc2 0.6.4", + "objc2 0.6.5", ] [[package]] @@ -578,7 +582,7 @@ dependencies = [ "oneshot", "pastey", "portable-atomic", - "rand 0.10.3", + "rand", "regress", "rustc-hash 2.1.3", "ryu-js", @@ -669,6 +673,30 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "borsh" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "553c5d846a6ba5150c65e3b1b8ec073bcf1abc20f9b7220de384a4443ea4e20a" +dependencies = [ + "borsh-derive", + "bytes", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cdfe656708a01f89b451a7d36466e6fe6c414de0aa18fc54f864f6f9ca9f56" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 3.0.6", +] + [[package]] name = "bumpalo" version = "3.20.3" @@ -811,7 +839,7 @@ checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", "cpufeatures 0.3.1", - "rand_core 0.10.1", + "rand_core", ] [[package]] @@ -1010,12 +1038,12 @@ dependencies = [ [[package]] name = "craft-docpath" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f" +source = "git+https://github.com/storytold/craft-libs?rev=37c907d23e608aaa098d7a48fa483670e90c6f33#37c907d23e608aaa098d7a48fa483670e90c6f33" [[package]] name = "craft-pdf" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f" +source = "git+https://github.com/storytold/craft-libs?rev=37c907d23e608aaa098d7a48fa483670e90c6f33#37c907d23e608aaa098d7a48fa483670e90c6f33" dependencies = [ "hayro-syntax", "thiserror 2.0.21", @@ -1024,11 +1052,11 @@ dependencies = [ [[package]] name = "craft-ui" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f" +source = "git+https://github.com/storytold/craft-libs?rev=37c907d23e608aaa098d7a48fa483670e90c6f33#37c907d23e608aaa098d7a48fa483670e90c6f33" dependencies = [ "egui", "log", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "serde", @@ -1037,7 +1065,7 @@ dependencies = [ [[package]] name = "craft-update" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=db19c373c8d59a545972ca340987aecfb3611a4b#db19c373c8d59a545972ca340987aecfb3611a4b" +source = "git+https://github.com/storytold/craft-libs?rev=37c907d23e608aaa098d7a48fa483670e90c6f33#37c907d23e608aaa098d7a48fa483670e90c6f33" dependencies = [ "chrono", "dunce", @@ -1175,7 +1203,7 @@ dependencies = [ "bitflags 2.13.2", "block2 0.6.2", "libc", - "objc2 0.6.4", + "objc2 0.6.5", ] [[package]] @@ -1268,10 +1296,10 @@ dependencies = [ "fmv-macos-events", "log", "muda", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", - "pollster 0.4.0", + "pollster 1.0.1", "rfd", "serde", "serde_json", @@ -1447,7 +1475,7 @@ dependencies = [ "effectcraft-time", "js-sys", "log", - "pollster 0.4.0", + "pollster 1.0.1", "wasm-bindgen", "web-time", "wgpu", @@ -1569,7 +1597,7 @@ version = "0.17.0" dependencies = [ "effectcraft-geom", "image", - "miniz_oxide 0.8.9", + "miniz_oxide 0.9.1", "serde", "serde_json", "thiserror 2.0.21", @@ -1604,7 +1632,7 @@ dependencies = [ "effectcraft-text", "image", "kurbo", - "miniz_oxide 0.8.9", + "miniz_oxide 0.9.1", "skrifa", "wasm-bindgen-test", "zune-jpeg", @@ -1639,7 +1667,7 @@ dependencies = [ name = "effectcraft-psd" version = "0.17.0" dependencies = [ - "miniz_oxide 0.8.9", + "miniz_oxide 0.9.1", ] [[package]] @@ -1695,7 +1723,7 @@ dependencies = [ name = "effectcraft-segment" version = "0.17.0" dependencies = [ - "miniz_oxide 0.8.9", + "miniz_oxide 0.9.1", "ndarray", "rayon", ] @@ -1769,7 +1797,7 @@ dependencies = [ "image", "kurbo", "log", - "pollster 0.4.0", + "pollster 1.0.1", "rayon", "serde", "serde_json", @@ -1822,7 +1850,7 @@ dependencies = [ "image", "js-sys", "log", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "parking_lot", @@ -1890,7 +1918,7 @@ dependencies = [ "egui", "image", "log", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "objc2-ui-kit 0.3.2", @@ -2441,7 +2469,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90642cf66dde7f205dc8b42dfe57b47d0f3a29f571b832742883ef512e23fe55" dependencies = [ - "objc2 0.6.4", + "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", ] @@ -2630,7 +2658,7 @@ dependencies = [ "js-sys", "libc", "r-efi 6.0.0", - "rand_core 0.10.1", + "rand_core", "wasm-bindgen", ] @@ -2697,7 +2725,7 @@ dependencies = [ "glutin_glx_sys", "glutin_wgl_sys", "libloading", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -2842,7 +2870,7 @@ checksum = "9f4d0e94ddd48749f06bbe4e5389fb9799a0c45bcaf00495042076ef05e3241a" [[package]] name = "hayro-jbig2" version = "0.3.0" -source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f" +source = "git+https://github.com/storytold/craft-libs?rev=37c907d23e608aaa098d7a48fa483670e90c6f33#37c907d23e608aaa098d7a48fa483670e90c6f33" dependencies = [ "hayro-ccitt", ] @@ -2856,7 +2884,7 @@ checksum = "c75ab947623ef4ccaa7acf0579edf7cbb5a73838e3839a7be73335e522f433a1" [[package]] name = "hayro-syntax" version = "0.7.2" -source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f" +source = "git+https://github.com/storytold/craft-libs?rev=37c907d23e608aaa098d7a48fa483670e90c6f33#37c907d23e608aaa098d7a48fa483670e90c6f33" dependencies = [ "hayro-jbig2", "hayro-jpeg2000", @@ -3272,9 +3300,9 @@ checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" [[package]] name = "linesweeper" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c19728333c060c6569a53c9a0e56c4be0df52cb4e6e07a8fbe16084cecce769" +checksum = "a25df1b0c5432893f41bba817150e323703ee5f6d974f3a6808ac7fd7f046d18" dependencies = [ "arrayvec", "kurbo", @@ -3324,9 +3352,9 @@ checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "lz4_flex" -version = "0.11.6" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373f5eceeeab7925e0c1098212f2fbc4d416adec9d35051a6ab251e824c1854a" +checksum = "ecbdfe44b1bd960b68170b417450a628c43f7cf56bb3c5317e61cb230ee7f226" [[package]] name = "mach2" @@ -3413,18 +3441,17 @@ dependencies = [ [[package]] name = "muda" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6744ab645ebe79f9c428455c7cb9dfa4275fa3b50a25408e08f927da8dc618c" +checksum = "4340e89c178916e62879c5c36b7f699019369be413e496178a6983ce91b8bc03" dependencies = [ "crossbeam-channel", "dpi", "keyboard-types", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", - "once_cell", "png", "thiserror 2.0.21", "windows-sys 0.61.2", @@ -3661,9 +3688,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +checksum = "08849bbd4767dfae9457696856ae1c84fe4e0281bbe4a7abff2d0e06fb7981f8" dependencies = [ "objc2-encode", ] @@ -3692,7 +3719,7 @@ checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.13.2", "block2 0.6.2", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-core-foundation", "objc2-core-graphics", "objc2-foundation 0.3.2", @@ -3706,7 +3733,7 @@ checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" dependencies = [ "bitflags 2.13.2", "libc", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-core-audio", "objc2-core-audio-types", "objc2-core-foundation", @@ -3744,7 +3771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" dependencies = [ "dispatch2", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-core-audio-types", "objc2-core-foundation", ] @@ -3756,7 +3783,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" dependencies = [ "bitflags 2.13.2", - "objc2 0.6.4", + "objc2 0.6.5", ] [[package]] @@ -3779,7 +3806,7 @@ checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.13.2", "dispatch2", - "objc2 0.6.4", + "objc2 0.6.5", ] [[package]] @@ -3790,7 +3817,7 @@ checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ "bitflags 2.13.2", "dispatch2", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-core-foundation", "objc2-io-surface", ] @@ -3846,7 +3873,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.13.2", "block2 0.6.2", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-core-foundation", ] @@ -3857,7 +3884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ "bitflags 2.13.2", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-core-foundation", ] @@ -3893,7 +3920,7 @@ checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" dependencies = [ "bitflags 2.13.2", "block2 0.6.2", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-foundation 0.3.2", ] @@ -3917,7 +3944,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ "bitflags 2.13.2", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-core-foundation", "objc2-core-graphics", "objc2-foundation 0.3.2", @@ -3962,7 +3989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.13.2", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-core-foundation", "objc2-foundation 0.3.2", ] @@ -4308,15 +4335,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "presser" version = "0.3.1" @@ -4349,16 +4367,17 @@ checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" [[package]] name = "proptest" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +checksum = "b8530004ccb15eae51c7e40009fbe317f341f804db54dc033eec1c50be28cfa0" dependencies = [ - "bit-set 0.8.0", - "bit-vec 0.8.0", + "bit-set 0.11.1", + "bit-vec 0.10.1", "bitflags 2.13.2", + "chacha20", + "core_detect", "num-traits", - "rand 0.9.5", - "rand_chacha", + "rand", "rand_xorshift", "regex-syntax", "rusty-fork", @@ -4446,16 +4465,6 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "rand" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" -dependencies = [ - "rand_chacha", - "rand_core 0.9.5", -] - [[package]] name = "rand" version = "0.10.3" @@ -4464,26 +4473,7 @@ checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" dependencies = [ "chacha20", "getrandom 0.4.3", - "rand_core 0.10.1", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", + "rand_core", ] [[package]] @@ -4494,11 +4484,11 @@ checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_xorshift" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +checksum = "60aa6af80be32871323012e02e6e65f8a7cc7890931ae421d217ad8fe0df2ccf" dependencies = [ - "rand_core 0.9.5", + "rand_core", ] [[package]] @@ -4528,7 +4518,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" dependencies = [ - "objc2 0.6.4", + "objc2 0.6.5", "objc2-core-foundation", "objc2-foundation 0.3.2", "objc2-quartz-core 0.3.2", @@ -4636,7 +4626,7 @@ dependencies = [ "js-sys", "libc", "log", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -4927,9 +4917,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" dependencies = [ "itoa", "memchr", @@ -6294,7 +6284,7 @@ dependencies = [ "jni 0.22.4", "log", "ndk-context", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "url", @@ -6444,7 +6434,7 @@ dependencies = [ "naga", "naga-types", "ndk-sys", - "objc2 0.6.4", + "objc2 0.6.5", "objc2-core-foundation", "objc2-core-graphics", "objc2-foundation 0.3.2", @@ -6687,15 +6677,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -6729,30 +6710,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows-threading" version = "0.2.1" @@ -6774,12 +6738,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -6792,12 +6750,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -6810,24 +6762,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -6840,12 +6780,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -6858,12 +6792,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -6876,12 +6804,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -6894,12 +6816,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winit" version = "0.30.13" @@ -6971,9 +6887,9 @@ dependencies = [ [[package]] name = "winresource" -version = "0.1.31" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087" +checksum = "a2bc6d36480b829272a5bdabf08e6dca8004761c1da7e35efef800540c66221b" dependencies = [ "toml", "version_check", diff --git a/Cargo.toml b/Cargo.toml index 3b05d9cf..607f94fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,9 @@ rust-version = "1.95" repository = "https://github.com/storytold/effectcraft" [workspace.dependencies] -craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f", version = "=0.1.0", default-features = false, features = ["read"] } -craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f" } -craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "db19c373c8d59a545972ca340987aecfb3611a4b", features = ["egui"] } +craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "37c907d23e608aaa098d7a48fa483670e90c6f33", version = "=0.1.0", default-features = false, features = ["read"] } +craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "37c907d23e608aaa098d7a48fa483670e90c6f33" } +craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "37c907d23e608aaa098d7a48fa483670e90c6f33", features = ["egui"] } # internal effectcraft-time = { path = "crates/time" } effectcraft-geom = { path = "crates/geom" } @@ -103,12 +103,12 @@ symphonia = { version = "0.5.5", default-features = false, features = ["aiff"] } # gpu (the same wgpu eframe / egui-wgpu use) wgpu = { version = "30.0.1", features = ["fragile-send-sync-non-atomic-wasm"] } bytemuck = "1" -pollster = "0.4" +pollster = "1" # ui # Shared frontend components; keep this dependency above the engine boundary. # Shared UI and the complete egui family select reviewed immutable commits below. -craft-ui = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f", features = ["serde"] } +craft-ui = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "37c907d23e608aaa098d7a48fa483670e90c6f33", features = ["serde"] } # `default_fonts` and `monochrome_emoji_fonts` are explicit: the app's `FontDefinitions::default()` # and its `✔`/`▣` chrome glyphs need both, and kittest's own dev-dependencies do not propagate. egui = { version = "0.37", default-features = false, features = ["default_fonts", "monochrome_emoji_fonts"] } @@ -220,8 +220,8 @@ lto = "thin" codegen-units = 1 [patch.crates-io] -hayro-syntax = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f", version = "=0.7.2" } -hayro-jbig2 = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f", version = "=0.3.0" } +hayro-syntax = { git = "https://github.com/storytold/craft-libs", rev = "37c907d23e608aaa098d7a48fa483670e90c6f33", version = "=0.7.2" } +hayro-jbig2 = { git = "https://github.com/storytold/craft-libs", rev = "37c907d23e608aaa098d7a48fa483670e90c6f33", version = "=0.3.0" } ecolor = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" } emath = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" } epaint = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" } diff --git a/apps/effectcraft-web/js/host.js b/apps/effectcraft-web/js/host.js index 12cb90bf..9346acc2 100644 --- a/apps/effectcraft-web/js/host.js +++ b/apps/effectcraft-web/js/host.js @@ -19,7 +19,7 @@ const enc = (k) => encodeURIComponent(k); const dec = (n) => decodeURIComponent(n); async function opfs() { - if (!navigator.storage || !navigator.storage.getDirectory) throw new Error("no OPFS"); + if (!navigator.storage?.getDirectory) throw new Error("no OPFS"); const root = await navigator.storage.getDirectory(); const dir = await root.getDirectoryHandle(DIR, { create: true }); // Probe a write: some browsers expose OPFS but only write it from workers. @@ -116,7 +116,7 @@ export async function storeOpen(prefer) { try { backend = await make(); break; } catch (e) { console.info(`storage: ${make.name}: ${e.message ?? e}`); } } // Ask the browser not to evict the store under storage pressure (granted for installed apps). - try { if (navigator.storage && navigator.storage.persist) await navigator.storage.persist(); } catch {} + try { if (navigator.storage?.persist) await navigator.storage.persist(); } catch {} return backend.name; } @@ -128,7 +128,7 @@ export async function storeDelete(key) { return backend.del(key); } /// Ask the browser to keep the origin's storage under pressure; resolves with whether it is. export async function storePersist() { try { - if (navigator.storage && navigator.storage.persist) return await navigator.storage.persist(); + if (navigator.storage?.persist) return await navigator.storage.persist(); } catch {} return false; } @@ -227,7 +227,7 @@ export async function audioStart(workletUrl) { } if (gen !== generation) return; node = new AudioWorkletNode(c, "effectcraft-output", { numberOfInputs: 0, outputChannelCount: [2] }); - node.port.onmessage = (e) => { if (e.data && e.data.underrun) underruns += e.data.underrun; }; + node.port.onmessage = (e) => { if (e.data?.underrun) underruns += e.data.underrun; }; kind = "worklet"; } catch (e) { console.warn("audio: AudioWorklet unavailable, using ScriptProcessorNode", e); @@ -318,11 +318,11 @@ function spawn(base) { const w = { worker, files: new Map(), ready: null, gpu: null }; w.ready = new Promise((res, rej) => { worker.onmessage = (e) => { - if (e.data && e.data.type === "gpu") { + if (e.data?.type === "gpu") { w.gpu = e.data.gpu ? { adapter: e.data.gpu } : { cpu: e.data.error || "no GPU" }; jobGpu = w.gpu; } - if (e.data && e.data.type === "ready") res(); + if (e.data?.type === "ready") res(); }; worker.onerror = (e) => rej(new Error(e.message || "worker failed to start")); }); @@ -341,7 +341,7 @@ export function workerRun(id, json, files, base, onMessage) { } catch (e) { // `new Worker` throws at once when the page's origin is opaque (a sandboxed iframe): the job // fails like one whose worker didn't start. - setTimeout(() => onMessage("error", String((e && e.message) || e), null, null)); + setTimeout(() => onMessage("error", String(e?.message || e), null, null)); return; } running.set(id, w); @@ -416,12 +416,12 @@ export function frameWorkerStart(base, onMessage, gpu) { // slot stays, and the failure is reported like a worker that died, after the caller has // recorded the index: frames then render on the page's thread (as with `?noworkers`). frameWorkers.push(null); - setTimeout(() => onMessage(index, undefined, undefined, String((e && e.message) || e || "frame worker failed"))); + setTimeout(() => onMessage(index, undefined, undefined, String(e?.message || e || "frame worker failed"))); return index; } worker.onmessage = (e) => { const m = e.data; - if (m && m.type === "frameReply") onMessage(index, m.json, m.bytes ? new Uint8Array(m.bytes) : null, undefined); + if (m?.type === "frameReply") onMessage(index, m.json, m.bytes ? new Uint8Array(m.bytes) : null, undefined); }; worker.onerror = (e) => { onMessage(index, undefined, undefined, String(e.message || "frame worker failed")); @@ -447,7 +447,7 @@ const cacheDirHandles = {}; async function cacheDir(kind = "frames") { if (cacheDirHandles[kind]) return cacheDirHandles[kind]; - if (!navigator.storage || !navigator.storage.getDirectory) throw new Error("no Origin Private File System"); + if (!navigator.storage?.getDirectory) throw new Error("no Origin Private File System"); let d = await navigator.storage.getDirectory(); for (const n of [CACHE_ROOT, "v1", kind]) d = await d.getDirectoryHandle(n, { create: true }); cacheDirHandles[kind] = d; @@ -544,7 +544,7 @@ export async function cacheClear() { /// Whether this thread has WebGPU (`navigator.gpu`). export function hasWebGpu() { - return !!(self.navigator && self.navigator.gpu); + return !!self.navigator?.gpu; } /// Post a message ({type: "frame", json} or {type: "file", path, bytes}) to frame worker `index`. @@ -652,7 +652,7 @@ export async function browseForget(name) { export async function browseRead(path) { const rel = path.replace(/^\/Folders\//, ""); const folder = folders.get(rel.split("/")[0]); - const h = folder && folder.files.get(rel); + const h = folder?.files.get(rel); if (!h) throw new Error(`${path}: its folder is not open (reconnect it)`); return new Uint8Array(await (await h.getFile()).arrayBuffer()); } @@ -706,7 +706,7 @@ export function showDeviceLost(message, kept) { self.effectcraft .execute("file.save", {}) .catch(() => self.effectcraft.execute("file.saveAs", { path: "/Untitled Project.ecproj" })) - .catch((e) => (text.textContent = `Saving failed: ${(e && e.message) || e}`)); + .catch((e) => (text.textContent = `Saving failed: ${e?.message || e}`)); const reload = document.createElement("button"); reload.textContent = "Reload"; reload.onclick = async () => { @@ -714,7 +714,7 @@ export function showDeviceLost(message, kept) { try { await self.effectcraft.flush(); } catch (e) { - text.textContent = `Writing to browser storage failed (${(e && e.message) || e}). Save the project, or reload anyway and lose the changes.`; + text.textContent = `Writing to browser storage failed (${e?.message || e}). Save the project, or reload anyway and lose the changes.`; reload.textContent = "Reload Anyway"; reload.disabled = false; reload.onclick = () => location.reload(); diff --git a/apps/effectcraft-web/src/browse.rs b/apps/effectcraft-web/src/browse.rs index ad7500fe..2fd680dc 100644 --- a/apps/effectcraft-web/src/browse.rs +++ b/apps/effectcraft-web/src/browse.rs @@ -52,7 +52,7 @@ struct State { static STATE: LazyLock> = LazyLock::new(Mutex::default); fn state() -> std::sync::MutexGuard<'static, State> { - STATE.lock().unwrap_or_else(|e| e.into_inner()) + STATE.lock().unwrap_or_else(std::sync::PoisonError::into_inner) } /// The web app's Media Browser. diff --git a/apps/effectcraft-web/src/files.rs b/apps/effectcraft-web/src/files.rs index 3f3f3a99..206314e4 100644 --- a/apps/effectcraft-web/src/files.rs +++ b/apps/effectcraft-web/src/files.rs @@ -48,14 +48,14 @@ pub fn list() -> Vec<(String, usize)> { /// The next write of `path` goes to browser storage only (no download). pub fn save_quietly(path: &str) { - QUIET.lock().unwrap_or_else(|e| e.into_inner()).push(path.to_string()); + QUIET.lock().unwrap_or_else(std::sync::PoisonError::into_inner).push(path.to_string()); } /// A rendered file (from the page's exporter or a worker): kept in the table unpersisted and /// downloaded at the next [`flush_downloads`]. pub fn add_output(path: &str, data: Arc<[u8]>) { STORE.put_file(path, data, false); - OUTPUTS.lock().unwrap_or_else(|e| e.into_inner()).push(path.to_string()); + OUTPUTS.lock().unwrap_or_else(std::sync::PoisonError::into_inner).push(path.to_string()); } /// The table path for a picked/dropped file name. @@ -120,7 +120,7 @@ impl Services for WebServices { fn write_file(&self, path: &str, data: &[u8]) -> std::io::Result<()> { put(path, data.into()); let quiet = { - let mut q = QUIET.lock().unwrap_or_else(|e| e.into_inner()); + let mut q = QUIET.lock().unwrap_or_else(std::sync::PoisonError::into_inner); let n = q.len(); q.retain(|p| p != path); q.len() != n @@ -184,7 +184,7 @@ pub fn export_sink() -> Arc { /// Download the render outputs written since the last call. pub fn flush_downloads() { - let paths = std::mem::take(&mut *OUTPUTS.lock().unwrap_or_else(|e| e.into_inner())); + let paths = std::mem::take(&mut *OUTPUTS.lock().unwrap_or_else(std::sync::PoisonError::into_inner)); let mut out: Vec<(String, Arc<[u8]>)> = paths.into_iter().filter_map(|p| get(&p).map(|d| (file_name(&p).to_string(), d))).collect(); out.sort_by(|a, b| a.0.cmp(&b.0)); out.dedup_by(|a, b| a.0 == b.0); diff --git a/apps/effectcraft-web/src/store.rs b/apps/effectcraft-web/src/store.rs index a44a2eac..8aafdac1 100644 --- a/apps/effectcraft-web/src/store.rs +++ b/apps/effectcraft-web/src/store.rs @@ -181,7 +181,7 @@ impl Default for Store { impl Store { pub fn lock(&self) -> std::sync::MutexGuard<'_, Mirror> { - self.mirror.lock().unwrap_or_else(|e| e.into_inner()) + self.mirror.lock().unwrap_or_else(std::sync::PoisonError::into_inner) } fn now(&self) -> f64 { diff --git a/apps/effectcraft-web/tests/smoke.mjs b/apps/effectcraft-web/tests/smoke.mjs index 3a6c1337..83091a72 100644 --- a/apps/effectcraft-web/tests/smoke.mjs +++ b/apps/effectcraft-web/tests/smoke.mjs @@ -162,7 +162,7 @@ try { const info = await js("effectcraft.info()"); report.steps.gpu = { webgpu: info.webgpu, backend: info.backend, ...info.gpu }; if (info.webgpu && info.backend === "BrowserWebGpu") { - check(info.gpu && info.gpu.compositor, "GPU compositor on WebGPU"); + check(info.gpu?.compositor, "GPU compositor on WebGPU"); check(info.gpu.viewerOnGpu, "viewer frames on the GPU"); } @@ -250,7 +250,7 @@ try { const st = await js(`(async () => { await new Promise((r) => setTimeout(r, 1200)); return effectcraft.execute("storage.info", {}); })()`); report.steps.storage = st; check(st.available && st.quota > 0 && st.usage > 0, `storage.info: ${JSON.stringify(st)}`); - check(st.diskCache && st.diskCache.enabled && st.diskCache.writes > 0, `disk cache in OPFS: ${JSON.stringify(st.diskCache)}`); + check(st.diskCache?.enabled && st.diskCache.writes > 0, `disk cache in OPFS: ${JSON.stringify(st.diskCache)}`); await js(`effectcraft.execute("prefs.open", {page: "disk"})`); await sleep(800); report.steps.storage.screenshot = await shot("01c-storage-manager"); @@ -378,14 +378,14 @@ try { const jobs = gj.workers.jobs || []; const rj = jobs.filter((j) => j.kind === "Render").pop(); const wj = jobs.filter((j) => j.kind === "Warp").pop(); - check(gj.workers.gpu && gj.workers.gpu.adapter, `job workers have a GPU: ${JSON.stringify(gj.workers)}`); + check(gj.workers.gpu?.adapter, `job workers have a GPU: ${JSON.stringify(gj.workers)}`); check(gj.status.every((st) => st === "Done"), `GPU job render: ${JSON.stringify(gj.status)}`); - check(rj && rj.gpu && rj.readbacks >= 3 && rj.passes >= 6, `Render Queue frames on the job worker's GPU, in passes: ${JSON.stringify(rj)}`); + check(rj?.gpu && rj.readbacks >= 3 && rj.passes >= 6, `Render Queue frames on the job worker's GPU, in passes: ${JSON.stringify(rj)}`); check(gj.warp.analyzed, `Warp Stabilizer analysed in a job worker: ${JSON.stringify(gj.warp)}`); - check(wj && wj.gpu && wj.readbacks > 0, `Warp Stabilizer input frames on the job worker's GPU: ${JSON.stringify(wj)}`); + check(wj?.gpu && wj.readbacks > 0, `Warp Stabilizer input frames on the job worker's GPU: ${JSON.stringify(wj)}`); const fj = jobs.filter((j) => j.kind === "ContentFill").pop(); check(!gj.fill.error && gj.fillLayers.length > 0, `Content-Aware Fill in a job worker: ${JSON.stringify(gj.fill)} ${JSON.stringify(gj.fillLayers)}`); - check(fj && fj.gpu && fj.readbacks > 0, `Content-Aware Fill frames on the job worker's GPU: ${JSON.stringify(fj)}`); + check(fj?.gpu && fj.readbacks > 0, `Content-Aware Fill frames on the job worker's GPU: ${JSON.stringify(fj)}`); check(gj.maxGapMs < 400, `event loop blocked ${gj.maxGapMs} ms during the GPU jobs: ${JSON.stringify(gj.gaps)}`); check(gj.gaps.prepareFill < 50, `event loop blocked ${gj.gaps.prepareFill} ms while editing after the analysis (pre-compose, mask): ${JSON.stringify(gj.gaps)}`); } else { @@ -418,7 +418,7 @@ try { check(mb.path === "/Browser Storage" && mb.entries.some((e) => e.name === "tone.wav" && e.kind === "audio"), `Media Browser: ${JSON.stringify(report.steps.mediaBrowser)}`); check(mb.actions.some((a) => a.id === "addFiles"), "Media Browser: Add Files…"); const mbImport = await js(`effectcraft.execute("mediaBrowser.import", {paths: [${JSON.stringify(mb.entries.find((e) => e.name === "tone.wav")?.path ?? "/tone.wav")}]})`); - check(!mbImport.pending && mbImport.items && mbImport.items.length === 1, `Media Browser import: ${JSON.stringify(mbImport)}`); + check(!mbImport.pending && mbImport.items?.length === 1, `Media Browser import: ${JSON.stringify(mbImport)}`); await js(`effectcraft.request("ui.playback", {action: "stop"})`); await js(`effectcraft.execute("time.set", {time: 0})`); await js(`effectcraft.request("ui.playback", {action: "play"})`); @@ -579,7 +579,7 @@ try { report.steps.final2 = { screenshot: await shot("03-offline") }; } catch (e) { failed = true; - report.error = String(e && e.stack || e); + report.error = String(e?.stack || e); try { report.errorShot = await shot("error"); } catch {} } report.console = logs.slice(-40); diff --git a/apps/effectcraft/src/main.rs b/apps/effectcraft/src/main.rs index 9ec08efc..5e3e14c6 100644 --- a/apps/effectcraft/src/main.rs +++ b/apps/effectcraft/src/main.rs @@ -50,7 +50,7 @@ fn app_icon() -> egui::IconData { fn is_project_file(path: &str) -> bool { std::path::Path::new(path) .extension() - .and_then(|ext| ext.to_str()) + .and_then(std::ffi::OsStr::to_str) .is_some_and(|ext| ext.eq_ignore_ascii_case("ecproj") || ext.eq_ignore_ascii_case("ecprojx")) } diff --git a/crates/color/src/space.rs b/crates/color/src/space.rs index ee7590ec..b570975a 100644 --- a/crates/color/src/space.rs +++ b/crates/color/src/space.rs @@ -177,7 +177,7 @@ impl ColorSpace { /// Parse an id or label (case-insensitive, loose). pub fn parse(s: &str) -> Option { - let k: String = s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::().to_ascii_lowercase(); + let k: String = s.chars().filter(char::is_ascii_alphanumeric).collect::().to_ascii_lowercase(); match k.as_str() { "srgb" | "srgbiec6196621" | "srgbiec61966" | "iec6196621" => Some(ColorSpace::Srgb), "rec709" | "bt709" | "hdtvrec709" | "hdtv" | "709" => Some(ColorSpace::Rec709), diff --git a/crates/effects/src/camera_tracker.rs b/crates/effects/src/camera_tracker.rs index ff4e51fe..ba7878ab 100644 --- a/crates/effects/src/camera_tracker.rs +++ b/crates/effects/src/camera_tracker.rs @@ -136,11 +136,11 @@ type Cache = Mutex>>; fn cached(c: &'static OnceLock>, json: &str, parse: impl Fn(&str) -> Option) -> Option> { let key = fnv(json.as_bytes()); let c = c.get_or_init(Default::default); - if let Some(a) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&key) { + if let Some(a) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key) { return Some(a.clone()); } let a = Arc::new(parse(json)?); - let mut m = c.lock().unwrap_or_else(|e| e.into_inner()); + let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner); if m.len() > 8 { m.clear(); } diff --git a/crates/effects/src/ocio.rs b/crates/effects/src/ocio.rs index 74816b11..e84720a3 100644 --- a/crates/effects/src/ocio.rs +++ b/crates/effects/src/ocio.rs @@ -617,7 +617,7 @@ pub fn load_file(src: &str) -> Option> { let parsed = if inline { parse_file(src, "") } else { - let ext = std::path::Path::new(src_t).extension().and_then(|e| e.to_str()).unwrap_or(""); + let ext = std::path::Path::new(src_t).extension().and_then(std::ffi::OsStr::to_str).unwrap_or(""); std::fs::read_to_string(src_t).ok().and_then(|t| parse_file(&t, ext)) } .map(Arc::new); diff --git a/crates/effects/src/util.rs b/crates/effects/src/util.rs index 1527e221..ae55d095 100644 --- a/crates/effects/src/util.rs +++ b/crates/effects/src/util.rs @@ -537,7 +537,7 @@ impl SimCache { pub fn run(&self, key: u64, target: u64, init: impl FnOnce() -> S, mut step: impl FnMut(&mut S, u64)) -> std::sync::Arc { use std::sync::Arc; let start = { - let mut g = self.inner.lock().unwrap_or_else(|e| e.into_inner()); + let mut g = self.inner.lock().unwrap_or_else(std::sync::PoisonError::into_inner); let tick = g.iter().map(|e| e.used).max().unwrap_or(0) + 1; g.iter_mut().find(|e| e.key == key).and_then(|e| { e.used = tick; @@ -563,7 +563,7 @@ impl SimCache { } let out = Arc::new(st); fresh.push((target, out.clone())); - let mut g = self.inner.lock().unwrap_or_else(|e| e.into_inner()); + let mut g = self.inner.lock().unwrap_or_else(std::sync::PoisonError::into_inner); let tick = g.iter().map(|e| e.used).max().unwrap_or(0) + 1; let idx = match g.iter().position(|e| e.key == key) { Some(ix) => ix, diff --git a/crates/effects/src/warp_stab.rs b/crates/effects/src/warp_stab.rs index 595cb520..f850bdcf 100644 --- a/crates/effects/src/warp_stab.rs +++ b/crates/effects/src/warp_stab.rs @@ -191,11 +191,11 @@ fn analysis_cached(json: &str) -> Option<(u64, Arc)> { static C: OnceLock = OnceLock::new(); let key = fnv(json.as_bytes()); let c = C.get_or_init(Default::default); - if let Some(a) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&key) { + if let Some(a) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key) { return Some((key, a.clone())); } let a = Arc::new(WarpAnalysis::from_json(json)?); - let mut m = c.lock().unwrap_or_else(|e| e.into_inner()); + let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner); if m.len() > 8 { m.clear(); } @@ -294,7 +294,7 @@ pub fn store_summary(key: [u64; 2], summary: PlanSummary) { } fn keep_summary(key: (u64, u64), summary: Arc) { - let mut m = summaries().lock().unwrap_or_else(|e| e.into_inner()); + let mut m = summaries().lock().unwrap_or_else(std::sync::PoisonError::into_inner); if m.len() > 32 { m.clear(); } @@ -305,7 +305,7 @@ fn keep_summary(key: (u64, u64), summary: Arc) { /// cached (seconds for a long Subspace Warp clip). `None` when not analysed. pub fn summary(params: &Params) -> Option<([u64; 2], Arc)> { let (key, a, st) = plan_key(params)?; - let cached = summaries().lock().unwrap_or_else(|e| e.into_inner()).get(&key).cloned(); + let cached = summaries().lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key).cloned(); let sum = match cached { Some(s) => s, None => { @@ -333,7 +333,7 @@ pub fn summary_at(params: &Params, time: f64) -> Option<(usize, Arc pub fn cached_summary_at(params: &Params, time: f64) -> Option<(usize, Arc)> { let (key, a, _) = plan_key(params)?; let k = a.frame_at(time)?; - let sum = summaries().lock().unwrap_or_else(|e| e.into_inner()).get(&key).cloned(); + let sum = summaries().lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key).cloned(); let sum = match sum { Some(s) => s, None => { @@ -445,13 +445,13 @@ fn render(ctx: &EffectCtx, buf: Buf) -> Buf { } } if !local.is_empty() { - let mut n = need_m.lock().unwrap_or_else(|e| e.into_inner()); + let mut n = need_m.lock().unwrap_or_else(std::sync::PoisonError::into_inner); for (x, w) in local { n[y * ow as usize + x] = w; } } }); - let need = need_m.into_inner().unwrap_or_else(|e| e.into_inner()); + let need = need_m.into_inner().unwrap_or_else(std::sync::PoisonError::into_inner); if synth && need.iter().any(|w| *w > 0.0) { synthesize(ctx, &a, k, &src_of, &crop, sc, &mut out, &need); } diff --git a/crates/engine/src/commands/edit.rs b/crates/engine/src/commands/edit.rs index 05015e6b..6a819439 100644 --- a/crates/engine/src/commands/edit.rs +++ b/crates/engine/src/commands/edit.rs @@ -850,7 +850,7 @@ fn original_path(s: &Session) -> Option { /// importer takes. Anything else (a folder or app bundle, a script, a shortcut) is refused. pub(crate) fn openable_original(path: &str) -> std::result::Result<(), String> { let media = |p: &std::path::Path| { - let ext = p.extension().and_then(|e| e.to_str()).map(str::to_ascii_lowercase).unwrap_or_default(); + let ext = p.extension().and_then(std::ffi::OsStr::to_str).map(str::to_ascii_lowercase).unwrap_or_default(); crate::media_browser::kind_of(&p.to_string_lossy()).is_some_and(|k| k != "project") || matches!(ext.as_str(), "pdf" | "ai" | "eps") }; let name = std::path::Path::new(path).file_name().map(|n| n.to_string_lossy().into_owned()).unwrap_or_else(|| path.to_string()); diff --git a/crates/engine/src/commands/file.rs b/crates/engine/src/commands/file.rs index 0fea452d..b3922c7d 100644 --- a/crates/engine/src/commands/file.rs +++ b/crates/engine/src/commands/file.rs @@ -560,7 +560,7 @@ pub const DATA_EXTENSIONS: &[&str] = &["json", "csv", "tsv"]; /// A data footage item for `path` (JSON / CSV / TSV), `None` for other files. pub(crate) fn data_footage(services: &dyn crate::Services, path: &str) -> Option> { - let ext = std::path::Path::new(path).extension().and_then(|e| e.to_str()).unwrap_or("").to_ascii_lowercase(); + let ext = std::path::Path::new(path).extension().and_then(std::ffi::OsStr::to_str).unwrap_or("").to_ascii_lowercase(); if !DATA_EXTENSIONS.contains(&ext.as_str()) { return None; } diff --git a/crates/engine/src/commands/layer_menu.rs b/crates/engine/src/commands/layer_menu.rs index 0a044937..33d17bb4 100644 --- a/crates/engine/src/commands/layer_menu.rs +++ b/crates/engine/src/commands/layer_menu.rs @@ -708,7 +708,7 @@ const BUNDLE_EXTENSIONS: &[&str] = &[ /// Whether the system file manager would open folder `dir` as something other than a folder: a /// bundle (`Tool.app`), or a Windows folder with a `{CLSID}` extension. fn opens_as_bundle(dir: &std::path::Path) -> bool { - let ext = dir.extension().and_then(|e| e.to_str()).unwrap_or_default().to_ascii_lowercase(); + let ext = dir.extension().and_then(std::ffi::OsStr::to_str).unwrap_or_default().to_ascii_lowercase(); ext.starts_with('{') || BUNDLE_EXTENSIONS.contains(&ext.as_str()) } diff --git a/crates/engine/src/commands/paint.rs b/crates/engine/src/commands/paint.rs index 73f600b5..c02a1849 100644 --- a/crates/engine/src/commands/paint.rs +++ b/crates/engine/src/commands/paint.rs @@ -107,7 +107,7 @@ impl Default for PaintOptions { } fn norm(s: &str) -> String { - s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::().to_ascii_lowercase() + s.chars().filter(char::is_ascii_alphanumeric).collect::().to_ascii_lowercase() } /// Index of `v` in `names` (by number or case/space-insensitive name). diff --git a/crates/engine/src/commands/relink.rs b/crates/engine/src/commands/relink.rs index 2d7c2ea1..d899decb 100644 --- a/crates/engine/src/commands/relink.rs +++ b/crates/engine/src/commands/relink.rs @@ -103,7 +103,7 @@ fn run(s: &mut Session, p: &Value) -> Result { } let supported = matches!(old.kind, FootageKind::Still | FootageKind::Sequence | FootageKind::Video | FootageKind::Audio) && old.layer.is_none() && old.page == 0; - let name = Path::new(&old.path).file_name().and_then(|n| n.to_str()).unwrap_or(""); + let name = Path::new(&old.path).file_name().and_then(std::ffi::OsStr::to_str).unwrap_or(""); let paths = index.get(name).cloned().unwrap_or_default(); let mut candidates = vec![]; let mut accepted = vec![]; diff --git a/crates/engine/src/mask_track.rs b/crates/engine/src/mask_track.rs index f85ba6b7..79c2b464 100644 --- a/crates/engine/src/mask_track.rs +++ b/crates/engine/src/mask_track.rs @@ -80,10 +80,10 @@ impl MaskMethod { } } pub fn from_name(s: &str) -> Option { - let n: String = s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::().to_ascii_lowercase(); + let n: String = s.chars().filter(char::is_ascii_alphanumeric).collect::().to_ascii_lowercase(); MaskMethod::ALL .into_iter() - .find(|m| m.id().to_ascii_lowercase() == n || m.label().chars().filter(|c| c.is_ascii_alphanumeric()).collect::().to_ascii_lowercase() == n) + .find(|m| m.id().to_ascii_lowercase() == n || m.label().chars().filter(char::is_ascii_alphanumeric).collect::().to_ascii_lowercase() == n) } pub fn model(self) -> Model { match self { diff --git a/crates/engine/src/offload.rs b/crates/engine/src/offload.rs index ad2c0cde..135822d5 100644 --- a/crates/engine/src/offload.rs +++ b/crates/engine/src/offload.rs @@ -289,10 +289,10 @@ pub struct Inbox(Mutex>); impl Inbox { pub fn push(&self, r: WorkerReply) { - self.0.lock().unwrap_or_else(|e| e.into_inner()).push(r); + self.0.lock().unwrap_or_else(std::sync::PoisonError::into_inner).push(r); } pub fn take(&self) -> Vec { - std::mem::take(&mut *self.0.lock().unwrap_or_else(|e| e.into_inner())) + std::mem::take(&mut *self.0.lock().unwrap_or_else(std::sync::PoisonError::into_inner)) } } diff --git a/crates/engine/src/render_queue.rs b/crates/engine/src/render_queue.rs index 74b33315..27e9a7dc 100644 --- a/crates/engine/src/render_queue.rs +++ b/crates/engine/src/render_queue.rs @@ -110,7 +110,7 @@ pub struct JobShared { } pub(crate) fn lock(m: &Mutex) -> std::sync::MutexGuard<'_, T> { - m.lock().unwrap_or_else(|e| e.into_inner()) + m.lock().unwrap_or_else(std::sync::PoisonError::into_inner) } impl JobShared { diff --git a/crates/engine/src/tests_proxy.rs b/crates/engine/src/tests_proxy.rs index 82cd1dcf..16e0cb00 100644 --- a/crates/engine/src/tests_proxy.rs +++ b/crates/engine/src/tests_proxy.rs @@ -20,7 +20,7 @@ use crate::render::{FootageSource, RenderOpts}; struct Synthetic; fn file_px(path: &str, x: u32, y: u32) -> [f32; 4] { - let name = std::path::Path::new(path).file_name().and_then(|n| n.to_str()).unwrap_or(""); + let name = std::path::Path::new(path).file_name().and_then(std::ffi::OsStr::to_str).unwrap_or(""); match name { "red16.png" => [1.0, 0.0, 0.0, 1.0], "blue8.png" => [0.0, 0.0, 1.0, 1.0], @@ -43,7 +43,7 @@ fn file_px(path: &str, x: u32, y: u32) -> [f32; 4] { } fn size_of(path: &str) -> (u32, u32) { - match std::path::Path::new(path).file_name().and_then(|n| n.to_str()).unwrap_or("") { + match std::path::Path::new(path).file_name().and_then(std::ffi::OsStr::to_str).unwrap_or("") { "red16.png" => (16, 16), "blue8.png" => (8, 8), "lines.mov" | "matte_black.png" | "matte_white.png" | "half.png" => (4, 4), diff --git a/crates/engine/src/tracking.rs b/crates/engine/src/tracking.rs index cfdd1244..434cdd8a 100644 --- a/crates/engine/src/tracking.rs +++ b/crates/engine/src/tracking.rs @@ -157,7 +157,7 @@ pub struct TrackShared { } pub(crate) fn lock(m: &Mutex) -> std::sync::MutexGuard<'_, T> { - m.lock().unwrap_or_else(|e| e.into_inner()) + m.lock().unwrap_or_else(std::sync::PoisonError::into_inner) } /// A running (or finished, not yet polled) analysis. diff --git a/crates/engine/src/viewer.rs b/crates/engine/src/viewer.rs index 997c2023..6b6e5ce9 100644 --- a/crates/engine/src/viewer.rs +++ b/crates/engine/src/viewer.rs @@ -663,10 +663,10 @@ impl SimProfile { } pub fn parse(s: &str) -> Option { - let k: String = s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::().to_ascii_lowercase(); + let k: String = s.chars().filter(char::is_ascii_alphanumeric).collect::().to_ascii_lowercase(); SimProfile::ALL .into_iter() - .find(|p| p.id().to_ascii_lowercase() == k || p.label().chars().filter(|c| c.is_ascii_alphanumeric()).collect::().to_ascii_lowercase() == k) + .find(|p| p.id().to_ascii_lowercase() == k || p.label().chars().filter(char::is_ascii_alphanumeric).collect::().to_ascii_lowercase() == k) .or(match k.as_str() { "off" => Some(SimProfile::None), "hdtv" | "bt709" => Some(SimProfile::Rec709), diff --git a/crates/export/src/pipeline.rs b/crates/export/src/pipeline.rs index 8dcee03e..9fd13147 100644 --- a/crates/export/src/pipeline.rs +++ b/crates/export/src/pipeline.rs @@ -247,7 +247,7 @@ impl<'a> Cx<'a> { } let img = self.crop_resize(img); if let Some(ft) = &self.frame_times { - ft.lock().unwrap_or_else(|e| e.into_inner()).push((i, t0.elapsed().as_secs_f64())); + ft.lock().unwrap_or_else(std::sync::PoisonError::into_inner).push((i, t0.elapsed().as_secs_f64())); } img } @@ -327,7 +327,7 @@ impl<'a> Cx<'a> { for dir in &self.job.options.overflow { let p = std::path::Path::new(dir).join(&name).to_string_lossy().to_string(); if q.has_room(&p, bytes) { - self.overflowed.lock().unwrap_or_else(|e| e.into_inner()).push(p.clone()); + self.overflowed.lock().unwrap_or_else(std::sync::PoisonError::into_inner).push(p.clone()); return p; } } @@ -351,7 +351,7 @@ impl<'a> Cx<'a> { Ok(r) => s.push_str(&format!("Result: Done ({} frames, {}×{}, {} bytes) in {seconds:.2} s\n", r.frames, r.width, r.height, r.bytes)), Err(e) => s.push_str(&format!("Result: Failed\nError: {e}\n")), } - let over = self.overflowed.lock().unwrap_or_else(|e| e.into_inner()); + let over = self.overflowed.lock().unwrap_or_else(std::sync::PoisonError::into_inner); if !over.is_empty() { s.push_str(&format!("Storage overflow: {} file(s) written to overflow folders (first: {})\n", over.len(), over[0])); } @@ -366,7 +366,7 @@ impl<'a> Cx<'a> { } } if let (RenderLog::PlusPerFrameInfo, Some(ft), Some(c)) = (level, &self.frame_times, comp) { - let mut v = ft.lock().unwrap_or_else(|e| e.into_inner()).clone(); + let mut v = ft.lock().unwrap_or_else(std::sync::PoisonError::into_inner).clone(); v.sort_by_key(|(i, _)| *i); s.push_str("\nFrames:\n"); let f0 = self.job.settings.first_frame(c); diff --git a/crates/expr/src/prelude.js b/crates/expr/src/prelude.js index df6ed3ff..6611d36a 100644 --- a/crates/expr/src/prelude.js +++ b/crates/expr/src/prelude.js @@ -840,14 +840,14 @@ var __propHandler = { // A layer-valued property (a Layer Control's Layer) is named as its layer (#809). if (k === 'name' && t.__info[2] === 'layer') { var lv = t.value; - if (lv !== null && lv !== undefined && lv.__isLayer) return lv.name; + if (lv?.__isLayer) return lv.name; } if (k in t) return t[k]; var v = t.value; if (v === null || v === undefined) return undefined; var x = v[k]; // Property groups are callable proxies: hand them over as they are, not bound like methods (#809). - if (x !== null && x !== undefined && x.__isGroup) return x; + if (x?.__isGroup) return x; return typeof x === 'function' ? x.bind(v) : x; }, }; @@ -904,8 +904,8 @@ __TextStyle.prototype = { }, get text() { var t = this.__d.text; - for (var k = 0; k < this.__ops.length; k++) { - var o = this.__ops[k]; + for (const elem of this.__ops) { + var o = elem; if (o[0] !== 'text') continue; if (o[2] < 0) t = String(o[1]); else t = t.slice(0, o[2]) + String(o[1]) + (o[3] < 0 ? '' : t.slice(o[2] + o[3])); diff --git a/crates/gpu/src/tests_deferred.rs b/crates/gpu/src/tests_deferred.rs index b6e43b55..e8778b62 100644 --- a/crates/gpu/src/tests_deferred.rs +++ b/crates/gpu/src/tests_deferred.rs @@ -30,7 +30,7 @@ pub(crate) fn deferred_gpu() -> Option<(&'static Gpu, std::sync::MutexGuard<'sta g }); let g = crate::tests::require_gpu("deferred gpu tests", g.as_ref())?; - Some((g, LOCK.lock().unwrap_or_else(|e| e.into_inner()))) + Some((g, LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner))) } /// Footage with GPU effects around a CPU-only one, a solid with Glow, and a precomp whose diff --git a/crates/media/src/layered.rs b/crates/media/src/layered.rs index 0a70d1f7..055c3feb 100644 --- a/crates/media/src/layered.rs +++ b/crates/media/src/layered.rs @@ -36,7 +36,7 @@ pub fn vector_doc( } fn is_pdf_like(path: &str, bytes: &[u8]) -> bool { - let ext = std::path::Path::new(path).extension().and_then(|e| e.to_str()).unwrap_or("").to_ascii_lowercase(); + let ext = std::path::Path::new(path).extension().and_then(std::ffi::OsStr::to_str).unwrap_or("").to_ascii_lowercase(); effectcraft_pdf::sniff(bytes).is_some() && (effectcraft_pdf::EXTENSIONS.contains(&ext.as_str()) || bytes.starts_with(b"%PDF")) } diff --git a/crates/media/src/lib.rs b/crates/media/src/lib.rs index 04a9d95e..9b5b491a 100644 --- a/crates/media/src/lib.rs +++ b/crates/media/src/lib.rs @@ -80,7 +80,7 @@ pub fn is_importable(path: &std::path::Path) -> bool { } pub(crate) fn ext_of(path: &std::path::Path) -> String { - path.extension().and_then(|e| e.to_str()).unwrap_or("").to_ascii_lowercase() + path.extension().and_then(std::ffi::OsStr::to_str).unwrap_or("").to_ascii_lowercase() } /// The `image` crate format of a still: from its signature, else from its extension (Targa has diff --git a/crates/media/src/pool.rs b/crates/media/src/pool.rs index 4deefed3..7f602465 100644 --- a/crates/media/src/pool.rs +++ b/crates/media/src/pool.rs @@ -756,7 +756,7 @@ impl Inner { drop(c); return Ok(Arc::new(self.decode(&loc, footage)?)); } - c = self.done.wait(c).unwrap_or_else(|e| e.into_inner()); + c = self.done.wait(c).unwrap_or_else(std::sync::PoisonError::into_inner); } self.misses.fetch_add(1, Ordering::Relaxed); c.inflight.insert(loc.key.clone()); @@ -972,7 +972,7 @@ fn describe_problem(codec: &str, e: &MediaError) -> String { } fn lock(m: &Mutex) -> std::sync::MutexGuard<'_, T> { - m.lock().unwrap_or_else(|e| e.into_inner()) + m.lock().unwrap_or_else(std::sync::PoisonError::into_inner) } #[cfg(test)] diff --git a/crates/model/Cargo.toml b/crates/model/Cargo.toml index 25397921..33ee2672 100644 --- a/crates/model/Cargo.toml +++ b/crates/model/Cargo.toml @@ -15,4 +15,4 @@ serde = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true } image = { workspace = true } -miniz_oxide = "0.8" +miniz_oxide = "0.9" diff --git a/crates/path/Cargo.toml b/crates/path/Cargo.toml index 46cf0a83..2e9bc85d 100644 --- a/crates/path/Cargo.toml +++ b/crates/path/Cargo.toml @@ -15,4 +15,4 @@ effectcraft-keyframe = { workspace = true } effectcraft-raster = { workspace = true } kurbo = { workspace = true } tiny-skia = { workspace = true } -linesweeper = "0.4" +linesweeper = "0.5" diff --git a/crates/pdf/Cargo.toml b/crates/pdf/Cargo.toml index b37dff50..bf614856 100644 --- a/crates/pdf/Cargo.toml +++ b/crates/pdf/Cargo.toml @@ -14,7 +14,7 @@ craft-pdf.workspace = true effectcraft-svg = { workspace = true } kurbo = { workspace = true } effectcraft-text = { workspace = true } -miniz_oxide = "0.8" +miniz_oxide = "0.9" # Embedded TrueType / OpenType font programs (MIT OR Apache-2.0). skrifa = { workspace = true } # DCT (JPEG) images (MIT OR Apache-2.0 OR Zlib; pure Rust). diff --git a/crates/pdf/README.md b/crates/pdf/README.md index bce90ab3..93be6c0e 100644 --- a/crates/pdf/README.md +++ b/crates/pdf/README.md @@ -138,7 +138,7 @@ ffmpeg elsewhere) to check the code tables against an independent implementation ## Shared parser setup and bounds The workspace pins `craft-pdf` to `craft-libs` Git revision -`a0916808f7d78bed8c7b90cdf937176015454a7f`, with `default-features = false` and only the explicit `read` +`37c907d23e608aaa098d7a48fa483670e90c6f33`, with `default-features = false` and only the explicit `read` feature. This excludes the backend's bundled fonts and Adobe CMaps and preserves EffectCraft's font and asset policies. The shared parser does not change the supported drawing operators, image codecs, continuously rasterized SVG tree, editable shapes, or EPS interpreter. diff --git a/crates/project/src/lib.rs b/crates/project/src/lib.rs index 8a4bbeb3..822fc7f7 100644 --- a/crates/project/src/lib.rs +++ b/crates/project/src/lib.rs @@ -201,7 +201,7 @@ impl TimeDisplayStyle { } } pub fn parse(s: &str) -> Option { - let k: String = s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::().to_ascii_lowercase(); + let k: String = s.chars().filter(char::is_ascii_alphanumeric).collect::().to_ascii_lowercase(); match k.as_str() { "timecode" => Some(TimeDisplayStyle::Timecode), "frames" => Some(TimeDisplayStyle::Frames), @@ -1292,7 +1292,7 @@ impl Footage { if self.kind != FootageKind::Data { return None; } - let ext = std::path::Path::new(&self.path).extension().and_then(|e| e.to_str()).unwrap_or("").to_ascii_lowercase(); + let ext = std::path::Path::new(&self.path).extension().and_then(std::ffi::OsStr::to_str).unwrap_or("").to_ascii_lowercase(); Some(match ext.as_str() { "csv" => "csv", "tsv" | "tab" => "tsv", diff --git a/crates/psd/Cargo.toml b/crates/psd/Cargo.toml index 49dda6a3..b6849041 100644 --- a/crates/psd/Cargo.toml +++ b/crates/psd/Cargo.toml @@ -10,4 +10,4 @@ rust-version.workspace = true workspace = true [dependencies] -miniz_oxide = "0.8" +miniz_oxide = "0.9" diff --git a/crates/render/Cargo.toml b/crates/render/Cargo.toml index db2fa9dd..efbd1fc1 100644 --- a/crates/render/Cargo.toml +++ b/crates/render/Cargo.toml @@ -19,7 +19,7 @@ effectcraft-raster = { workspace = true } effectcraft-path = { workspace = true } effectcraft-text = { workspace = true } effectcraft-effects = { workspace = true } -lz4_flex = { version = "0.11", default-features = false, features = ["std", "safe-encode", "safe-decode", "checked-decode"] } +lz4_flex = { version = "0.14", default-features = false, features = ["std", "safe-encode", "safe-decode", "checked-decode"] } effectcraft-model = { workspace = true } kurbo = { workspace = true } rayon = { workspace = true } diff --git a/crates/render/src/disk_cache.rs b/crates/render/src/disk_cache.rs index f867a614..4fa2e1cb 100644 --- a/crates/render/src/disk_cache.rs +++ b/crates/render/src/disk_cache.rs @@ -331,7 +331,7 @@ impl DiskCache { let Ok(files) = std::fs::read_dir(shard.path()) else { continue }; for f in files.flatten() { let p = f.path(); - let name = p.file_name().and_then(|n| n.to_str()).unwrap_or(""); + let name = p.file_name().and_then(std::ffi::OsStr::to_str).unwrap_or(""); if name.contains(".tmp") { // A writer that died mid-write; old temporaries are removed. if f.metadata().and_then(|m| m.modified()).is_ok_and(|t| t.elapsed().is_ok_and(|e| e.as_secs() > 600)) { diff --git a/crates/render/src/three_d/camera.rs b/crates/render/src/three_d/camera.rs index 5e1f983b..30873b6a 100644 --- a/crates/render/src/three_d/camera.rs +++ b/crates/render/src/three_d/camera.rs @@ -408,7 +408,7 @@ impl View3D { } } pub fn from_id(s: &str) -> Option { - let k: String = s.chars().filter(|c| c.is_ascii_alphanumeric()).collect::().to_ascii_lowercase(); + let k: String = s.chars().filter(char::is_ascii_alphanumeric).collect::().to_ascii_lowercase(); View3D::ALL.into_iter().find(|v| v.id().eq_ignore_ascii_case(&k) || v.label().replace(' ', "").eq_ignore_ascii_case(&k)) } pub fn is_ortho(self) -> bool { diff --git a/crates/script/src/prelude.js b/crates/script/src/prelude.js index 5216db34..ce64e221 100644 --- a/crates/script/src/prelude.js +++ b/crates/script/src/prelude.js @@ -420,7 +420,7 @@ function __callable(o) { delete f.length; Object.setPrototypeOf(f, Object.getPrototypeOf(o)); var keys = Object.keys(o); - for (var i = 0; i < keys.length; i++) f[keys[i]] = o[keys[i]]; + for (const elem of keys) f[elem] = o[elem]; var self = new Proxy(f, { get: __attr.get, apply: function (t, thisArg, args) { return self.property(args[0]); }, @@ -473,7 +473,7 @@ function MarkerValue(comment, chapter, url, frameTarget, cuePointName, params) { } MarkerValue.prototype.getParameters = function () { var o = {}; - for (var i = 0; i < this.__params.length; i++) o[this.__params[i][0]] = this.__params[i][1]; + for (const elem of this.__params) o[elem[0]] = elem[1]; return o; }; MarkerValue.prototype.setParameters = function (o) { @@ -513,8 +513,8 @@ TextDocument.prototype = { set text(v) { this.__text = String(v); this.__textSet = true; }, __style: function (k, d) { if (k in this.__changed) return this.__changed[k]; - if (this.__base && this.__base.first && k in this.__base.first) return this.__base.first[k]; - if (this.__base && this.__base.para && k in this.__base.para) return this.__base.para[k]; + if (this.__base?.first && k in this.__base.first) return this.__base.first[k]; + if (this.__base?.para && k in this.__base.para) return this.__base.para[k]; return d; }, __set: function (k, v) { this.__changed[k] = v; }, @@ -580,14 +580,14 @@ TextDocument.prototype = { }, get tsume() { return this.__style("tsume", 0); }, set tsume(v) { this.__set("tsume", __num(v)); }, - get boxText() { return !!(this.__base && this.__base.box_size); }, + get boxText() { return !!this.__base?.box_size; }, get pointText() { return !this.boxText; }, - get boxTextSize() { return this.__base && this.__base.box_size ? this.__base.box_size.slice() : undefined; }, + get boxTextSize() { return this.__base?.box_size ? this.__base.box_size.slice() : undefined; }, set boxTextSize(v) { var p = this.boxTextPos || [0, 0]; this.__set("box", [p[0], p[1], v[0], v[1]]); }, - get boxTextPos() { return this.__base && this.__base.box_size ? this.__base.box_pos.slice() : undefined; }, + get boxTextPos() { return this.__base?.box_size ? this.__base.box_pos.slice() : undefined; }, get startIndent() { return this.__style("indentLeft", 0); }, set startIndent(v) { this.__set("indentLeft", __num(v)); }, get endIndent() { return this.__style("indentRight", 0); }, @@ -665,7 +665,7 @@ PropertyBase.prototype = { get isMask() { return !!this.__info().isMask; }, get selected() { var c = __get("item", { id: this.__layer.__comp }).selectedProperties; - for (var i = 0; i < c.length; i++) if (c[i][0] === this.__layer.__id && c[i][1] === this.__uid) return true; + for (const elem of c) if (elem[0] === this.__layer.__id && elem[1] === this.__uid) return true; return false; }, set selected(v) { @@ -842,7 +842,7 @@ Property.prototype.property = function () { return null; }; i = __num(i, "keyIndex"); // Only the key asked for: fetching the whole list made every key call linear (#814). var r = i >= 1 && Math.floor(i) === i ? __get("keys", { comp: this.__layer.__comp, layer: this.__layer.__id, uid: this.__uid, index: i - 1 }) : null; - if (!r || !r.key) throw __err("keyIndex " + i + " is out of range 1.." + (r ? r.count : this.numKeys)); + if (!r?.key) throw __err("keyIndex " + i + " is out of range 1.." + (r ? r.count : this.numKeys)); return r.key; }; // Script value → engine JSON for `prop.set` / `prop.addKey`. @@ -853,13 +853,13 @@ Property.prototype.property = function () { return null; }; return __get("textdoc", { comp: this.__layer.__comp, layer: this.__layer.__id, uid: this.__uid, time: time, attrs: td.__attrs() }); } if (t === "SHAPE") { - if (!v || !v.vertices) throw __err("value must be a Shape"); + if (!v?.vertices) throw __err("value must be a Shape"); var n = v.vertices.length; - function fit(a) { var o = []; for (var i = 0; i < n; i++) o.push(a && a[i] ? [a[i][0], a[i][1]] : [0, 0]); return o; } + function fit(a) { var o = []; for (var i = 0; i < n; i++) o.push(a?.[i] ? [a[i][0], a[i][1]] : [0, 0]); return o; } return { vertices: fit(v.vertices), in_tangents: fit(v.inTangents), out_tangents: fit(v.outTangents), closed: v.closed !== false }; } if (t === "LAYER_INDEX") { - var idx = v && v.__id !== undefined ? v.index : __num(v); + var idx = v?.__id !== undefined ? v.index : __num(v); if (!idx) return null; return this.__layer.containingComp.layer(idx).__id; } @@ -1177,7 +1177,7 @@ Layer.prototype = { }, get selectedProperties() { var c = this.containingComp.__info().selectedProperties, out = []; - for (var i = 0; i < c.length; i++) if (c[i][0] === this.__id) out.push(__propOf(this, __get("node", { comp: this.__comp, layer: this.__id, uid: c[i][1] }))); + for (const elem of c) if (elem[0] === this.__id) out.push(__propOf(this, __get("node", { comp: this.__comp, layer: this.__id, uid: elem[1] }))); return out; }, get parent() { var p = this.__info().parent; return p === null ? null : __layer(this.__comp, p); }, @@ -1205,7 +1205,7 @@ Layer.prototype = { var before = this.containingComp.__info().layers; __call("edit.duplicate", this.__p()); var after = this.containingComp.__info().layers; - for (var i = 0; i < after.length; i++) if (before.indexOf(after[i]) < 0) return __layer(this.__comp, after[i]); + for (const elem of after) if (before.indexOf(elem) < 0) return __layer(this.__comp, elem); return null; }, copyToComp: function (comp) { @@ -1310,7 +1310,7 @@ AVLayer.prototype.constructor = AVLayer; // AVLayer.replaceSource). `fixExpressions` is accepted for compatibility: only AV sources are // replaced, so the property tree and the layer's name stay and expressions keep resolving. P.replaceSource = function (newSource, fixExpressions) { - if (!newSource || newSource.__id === undefined) throw __err("replaceSource needs a project item"); + if (newSource?.__id === undefined) throw __err("replaceSource needs a project item"); __call("layer.replaceSource", { comp: this.__comp, layers: [this.__id], item: newSource.__id, fixExpressions: !!fixExpressions }); return null; }; @@ -1371,7 +1371,7 @@ LayerCollection.prototype = { return layer; }, add: function (item, duration) { - if (!item || item.id === undefined) throw __err("add(): expected an item"); + if (item?.id === undefined) throw __err("add(): expected an item"); var l = this.__new(__call("layer.addItem", { comp: this.__comp, item: item.id, time: 0 })); return this.__dur(l, duration); }, @@ -1414,15 +1414,15 @@ LayerCollection.prototype = { }, precompose: function (indices, name, moveAllAttributes) { var ids = this.__ids(), sel = []; - for (var i = 0; i < indices.length; i++) sel.push(ids[indices[i] - 1]); + for (const elem of indices) sel.push(ids[elem - 1]); var r = __call("layer.precompose", { comp: this.__comp, layers: sel, name: String(name), mode: moveAllAttributes === false ? "leave" : "move" }); return __item(r.comp); }, byName: function (name) { var ids = this.__ids(); - for (var i = 0; i < ids.length; i++) { - var l = __get("layer", { comp: this.__comp, layer: ids[i] }); - if (l.name === name) return __layer(this.__comp, ids[i]); + for (const elem of ids) { + var l = __get("layer", { comp: this.__comp, layer: elem }); + if (l.name === name) return __layer(this.__comp, elem); } return null; }, @@ -1493,7 +1493,7 @@ Item.prototype = { get label() { return this.__info().label; }, set label(v) { __call("project.setLabel", { items: [this.__id], label: __num(v, "label") }); }, get parentFolder() { return __item(this.__info().parent); }, - set parentFolder(f) { __call("project.move", { items: [this.__id], folder: f && f.__id ? f.__id : null }); }, + set parentFolder(f) { __call("project.move", { items: [this.__id], folder: f?.__id ? f.__id : null }); }, get selected() { return this.__info().selected; }, set selected(v) { var selection = __get("project").selection; @@ -1527,7 +1527,7 @@ AVItem.prototype = Object.create(Item.prototype); def("hasVideo", function () { return this.__info().hasVideo; }); def("hasAudio", function () { return this.__info().hasAudio; }); def("footageMissing", function () { return !!this.__info().footageMissing; }); - def("useProxy", function () { var p = this.__info().proxy; return !!(p && p.enabled); }, function (v) { + def("useProxy", function () { var p = this.__info().proxy; return !!p?.enabled; }, function (v) { if (!this.__info().proxy) throw __err("\"" + this.name + "\" has no proxy"); __call("file.useProxy", { item: this.__id, on: !!v }); }); @@ -1564,9 +1564,9 @@ CompItem.prototype.constructor = CompItem; }); def("activeCamera", function () { var ids = this.__info().layers; - for (var i = 0; i < ids.length; i++) { - var l = __get("layer", { comp: this.__id, layer: ids[i] }); - if (l.kind === "camera" && l.enabled && l.active) return __layer(this.__id, ids[i]); + for (const elem of ids) { + var l = __get("layer", { comp: this.__id, layer: elem }); + if (l.kind === "camera" && l.enabled && l.active) return __layer(this.__id, elem); } return null; }); @@ -1606,7 +1606,7 @@ CompItem.prototype.constructor = CompItem; if (a < 1 || a > ids.length || Math.floor(a) !== a) throw __err("layer index " + a + " is out of range 1.." + ids.length); return __layer(this.__id, ids[a - 1]); } - if (a && a.__id !== undefined && typeof b === "number") { + if (a?.__id !== undefined && typeof b === "number") { var j = a.index + b; return j >= 1 && j <= ids.length ? __layer(this.__id, ids[j - 1]) : null; } @@ -1619,9 +1619,9 @@ CompItem.prototype.constructor = CompItem; P.__controllers = function () { var out = []; (function walk(list) { - for (var i = 0; i < list.length; i++) { - if (list[i].kind === "property" || list[i].kind === "mirror" || list[i].kind === "media") out.push(list[i]); - if (list[i].children) walk(list[i].children); + for (const elem of list) { + if (elem.kind === "property" || elem.kind === "mirror" || elem.kind === "media") out.push(elem); + if (elem.children) walk(elem.children); } })(this.__eg().controls); return out; @@ -1732,7 +1732,7 @@ FolderItem.prototype.constructor = FolderItem; Object.defineProperty(P, "numItems", { get: function () { return new ItemCollection(this.__id).length; } }); Object.defineProperty(P, "parentFolder", { get: function () { return this.__id === 0 ? null : __item(this.__info().parent); }, - set: function (f) { __call("project.move", { items: [this.__id], folder: f && f.__id ? f.__id : null }); }, + set: function (f) { __call("project.move", { items: [this.__id], folder: f?.__id ? f.__id : null }); }, }); P.item = function (i) { var v = new ItemCollection(this.__id).__at(__num(i, "index")); @@ -1776,7 +1776,7 @@ RenderQueueItem.prototype = { constructor: RenderQueueItem, __info: function () { var l = __call("renderQueue.list", {}).items; - for (var i = 0; i < l.length; i++) if (l[i].id === this.__id) return l[i]; + for (const elem of l) if (elem.id === this.__id) return elem; throw __err("the render queue item no longer exists"); }, get comp() { return __item(this.__info().comp); }, @@ -1812,7 +1812,7 @@ RenderQueueItem.prototype = { var before = __call("renderQueue.list", {}).items.map(function (i) { return i.id; }); __call("renderQueue.duplicate", { item: this.__id }); var after = __call("renderQueue.list", {}).items; - for (var i = 0; i < after.length; i++) if (before.indexOf(after[i].id) < 0) return new RenderQueueItem(after[i].id); + for (const elem of after) if (before.indexOf(elem.id) < 0) return new RenderQueueItem(elem.id); return null; }, applyTemplate: function () {}, @@ -1945,9 +1945,9 @@ Project.prototype = { } } var r = __call("file.import", args); - if (r.errors && r.errors.length) throw __err(r.errors.join("; ")); + if (r.errors?.length) throw __err(r.errors.join("; ")); // Composition imports create several footage items, but callers need the comp. - return __item(r.comps && r.comps.length ? r.comps[0] : r.items[0]); + return __item(r.comps?.length ? r.comps[0] : r.items[0]); }, importFileWithDialog: function () { return null; }, importPlaceholder: function (name, w, h, rate, dur) { diff --git a/crates/script/src/scriptui.js b/crates/script/src/scriptui.js index 52c65efb..4760d058 100644 --- a/crates/script/src/scriptui.js +++ b/crates/script/src/scriptui.js @@ -213,7 +213,7 @@ function __uiResPlain(v) { if (v instanceof Array) return v.map(__uiResPlain); if (v && typeof v === "object" && v.__object) { var o = {}; - for (var k = 0; k < v.__object.length; k++) o[v.__object[k][0]] = __uiResPlain(v.__object[k][1]); + for (const elem of v.__object) o[elem[0]] = __uiResPlain(elem[1]); return o; } return v; @@ -222,8 +222,8 @@ function __uiResPlain(v) { // A resource's own properties and its child elements (in source order). function __uiResProps(res) { var props = {}, kids = []; - for (var k = 0; k < res.list.length; k++) { - var e = res.list[k]; + for (const elem2 of res.list) { + var e = elem2; if (e[1] && typeof e[1] === "object" && e[1].__element) kids.push([e[0], { type: e[1].__element, list: e[1].list }]); else props[e[0]] = __uiResPlain(e[1]); } @@ -239,17 +239,17 @@ function __uiApplyRes(c, res) { // Ranges before values, items before selections. if (props.minvalue !== undefined) c.minvalue = __num(props.minvalue); if (props.maxvalue !== undefined) c.maxvalue = __num(props.maxvalue); - if (props.items instanceof Array && c.items) for (var n = 0; n < props.items.length; n++) c.add("item", props.items[n]); + if (props.items instanceof Array && c.items) for (const elem of props.items) c.add("item", elem); for (var k in props) { if (__uiResSkip[k]) continue; var v = props[k]; if (__uiSizes[k] && v instanceof Array) v = __dim(v[0], v[1]); c[k] = v; } - for (var j = 0; j < pk.kids.length; j++) { - var child = __uiAddRes(c, pk.kids[j][1]); - child.__key = pk.kids[j][0]; - c[pk.kids[j][0]] = child; + for (const elem of pk.kids) { + var child = __uiAddRes(c, elem[1]); + child.__key = elem[0]; + c[elem[0]] = child; } return c; } @@ -268,7 +268,7 @@ Window.alert = function (s) { alert(s); }; Window.confirm = function (s) { return confirm(s); }; Window.prompt = function (m, d) { return prompt(m, d); }; Window.find = function (type, title) { - for (var i = 0; i < __uiWins.length; i++) if (__uiWins[i].text === title) return __uiWins[i]; + for (const elem of __uiWins) if (elem.text === title) return elem; return null; }; @@ -335,7 +335,7 @@ function __uiColor(c) { return null; } function __uiFontSize(f, g) { - var x = f && f.size !== undefined ? Number(f.size) : g && g.font && g.font.size !== undefined ? Number(g.font.size) : 12; + var x = f?.size !== undefined ? Number(f.size) : g?.font && g.font.size !== undefined ? Number(g.font.size) : 12; return x > 0 ? x : 12; } @@ -371,11 +371,11 @@ function __uiGraphics() { this.__push({ op: "fill", color: brush ? brush.color : null, path: (path || this.currentPath).slice() }); }, strokePath: function (pen, path) { - this.__push({ op: "stroke", color: pen ? pen.color : null, width: pen && pen.lineWidth !== undefined ? pen.lineWidth : 1, path: (path || this.currentPath).slice() }); + this.__push({ op: "stroke", color: pen ? pen.color : null, width: pen?.lineWidth !== undefined ? pen.lineWidth : 1, path: (path || this.currentPath).slice() }); }, drawString: function (text, pen, x, y, font) { var f = font || this.font; - this.__push({ op: "text", text: String(text), color: pen ? pen.color : null, x: __num(x || 0), y: __num(y || 0), size: __uiFontSize(f, this), style: String(f && f.style !== undefined ? f.style : "") }); + this.__push({ op: "text", text: String(text), color: pen ? pen.color : null, x: __num(x || 0), y: __num(y || 0), size: __uiFontSize(f, this), style: String(f?.style !== undefined ? f.style : "") }); }, drawOSControl: function () { this.__push({ op: "os" }); }, drawImage: function (img, x, y, w, h) { this.__push({ op: "image", image: __uiImageJson(img), x: __num(x || 0), y: __num(y || 0), w: w === undefined ? 0 : __num(w), h: h === undefined ? 0 : __num(h) }); }, @@ -384,7 +384,7 @@ function __uiGraphics() { var size = __uiFontSize(font, this); var lines = String(s).split("\n"); var cw = size * 0.58, w = 0; - for (var i = 0; i < lines.length; i++) w = Math.max(w, Math.ceil(lines[i].length * cw)); + for (const elem of lines) w = Math.max(w, Math.ceil(elem.length * cw)); var n = lines.length; if (boundaryWidth > 0 && w > boundaryWidth) { n = Math.ceil(w / boundaryWidth) + lines.length - 1; @@ -405,14 +405,14 @@ function __uiRunDraws(win) { })(win); if (!drawn.length) return; __uiLayout(win); - for (var k = 0; k < drawn.length; k++) { - var c = drawn[k]; + for (const elem of drawn) { + var c = elem; c.__draw = []; c.graphics.__rec = c.__draw; try { c.onDraw.call(c, { type: "draw", target: c }); } catch (e) { - try { writeLn("onDraw: " + (e && e.message ? e.message : e)); } catch (e2) {} + try { writeLn("onDraw: " + (e?.message ? e.message : e)); } catch (e2) {} } c.graphics.__rec = null; } @@ -498,7 +498,7 @@ function __uiProto() { var arg = t === "image" || t === "iconbutton" ? text : typeof text === "object" && text !== null && !(text instanceof Array) ? "" : text; __uiInit(c, t, this.window, this, bounds, arg, props); if ((t === "dropdownlist" || t === "listbox") && text instanceof Array) { - for (var i = 0; i < text.length; i++) c.add("item", text[i]); + for (const elem of text) c.add("item", elem); c.text = ""; } if (t === "slider" || t === "scrollbar") { @@ -521,7 +521,7 @@ function __uiProto() { if (t === "checkbox" || t === "radiobutton") c.value = false; if (t === "edittext" && c.properties.multiline) c.__multiline = true; if (t === "edittext" && c.properties.readonly) c.__readonly = true; - if (c.properties && c.properties.name) c.name = c.properties.name; + if (c.properties?.name) c.name = c.properties.name; this.children.push(c); if (t === "tab" && this.type === "tabbedpanel" && this.__sel.length === 0) this.__sel = [this.children.length - 1]; return c; @@ -608,21 +608,21 @@ function __uiProto() { removeEventListener: function (name, fn, capture) { var k = String(name).toLowerCase(); var m = capture ? this.__capture : this.__listeners; - if (m && m[k]) m[k] = m[k].filter(function (f) { return f !== fn; }); + if (m?.[k]) m[k] = m[k].filter(function (f) { return f !== fn; }); }, dispatchEvent: function (e) { this.__fire("on" + String(e.type || e).charAt(0).toUpperCase() + String(e.type || e).slice(1)); }, __fire: function (name) { var r; if (typeof this[name] === "function") r = this[name].call(this); var l = this.__listeners[name.slice(2).toLowerCase()]; - if (l) for (var i = 0; i < l.length; i++) l[i].call(this, { type: name.slice(2).toLowerCase(), target: this }); + if (l) for (const elem of l) elem.call(this, { type: name.slice(2).toLowerCase(), target: this }); return r; }, __handlers: function () { var out = []; var names = ["onClick", "onChange", "onChanging", "onDoubleClick", "onClose", "onShow", "onActivate", "onDeactivate", "onResize", "onDraw"]; - for (var i = 0; i < names.length; i++) { - var n = names[i]; + for (const elem of names) { + var n = elem; if (typeof this[n] === "function" || (this.__listeners[n.slice(2).toLowerCase()] || []).length) out.push(n); } return out; @@ -634,8 +634,8 @@ function __uiProto() { (function () { var all = ["Window", "Panel", "Group", "Button", "IconButton", "StaticText", "EditText", "Checkbox", "RadioButton", "Slider", "Scrollbar", "Progressbar", "DropDownList", "ListBox", "TabbedPanel", "Tab", "ScriptUIImage"]; - for (var i = 0; i < all.length; i++) { - var C = __ecGlobal[all[i]]; + for (const elem of all) { + var C = __ecGlobal[elem]; var p = __uiProto(); for (var k in p) C.prototype[k] = p[k]; Object.defineProperty(C.prototype, "bounds", { @@ -737,8 +737,8 @@ function __uiMouseListeners(c, type, capture) { // The mouse events that reach a listener at `c`: its own, and its containers' (`inherited`). function __uiMouseWanted(c, inherited) { var out = inherited.slice(); - for (var i = 0; i < __uiMouseKinds.length; i++) { - var t = __uiMouseKinds[i]; + for (const elem of __uiMouseKinds) { + var t = elem; if (out.indexOf(t) < 0 && (__uiMouseListeners(c, t, false).length || __uiMouseListeners(c, t, true).length)) out.push(t); } return out; @@ -793,7 +793,7 @@ function __uiMouseFire(target, type, v) { var l = __uiMouseListeners(c, type, capture); e.currentTarget = c; e.eventPhase = phase; - for (var i = 0; i < l.length; i++) if (typeof l[i] === "function") l[i].call(c, e); + for (const elem of l) if (typeof elem === "function") elem.call(c, e); }; var n = path.length - 1; for (var i = 0; i < n; i++) { @@ -832,7 +832,7 @@ function __uiJson(c, inherited) { if (m) o.margins = m; if (typeof c.spacing === "number") o.spacing = c.spacing; var kids = c.children; - for (var i = 0; i < kids.length; i++) o.children.push(__uiJson(kids[i], mouse)); + for (const elem of kids) o.children.push(__uiJson(elem, mouse)); } if (c.alignment !== null && c.alignment !== undefined) o.alignment = __uiAlign(c.alignment); if (c.type === "checkbox" || c.type === "radiobutton") o.checked = !!c.value; @@ -859,8 +859,8 @@ function __uiJson(c, inherited) { // The open windows of this script, for the host. function __uiSnapshot() { var out = []; - for (var i = 0; i < __uiWins.length; i++) { - var w = __uiWins[i]; + for (const elem of __uiWins) { + var w = elem; if (w.__shown && !w.__closed) __uiRunDraws(w); out.push({ id: w.__wid, kind: w.__kind, title: String(w.text || ""), visible: !!w.__shown && !w.__closed, modal: !!w.__modal, root: __uiJson(w) }); } @@ -871,12 +871,12 @@ function __uiSnapshot() { function __uiOpenCount() { var n = 0; - for (var i = 0; i < __uiWins.length; i++) if (__uiWins[i].__shown && !__uiWins[i].__closed) n++; + for (const elem of __uiWins) if (elem.__shown && !elem.__closed) n++; return n; } function __uiWin(id) { - for (var i = 0; i < __uiWins.length; i++) if (__uiWins[i].__wid === id) return __uiWins[i]; + for (const elem of __uiWins) if (elem.__wid === id) return elem; return null; } function __uiFind(c, id) { @@ -914,7 +914,7 @@ function __uiDispatch(winId, id, kind, valueJson) { if (!c || c.enabled === false) return; var radio = function () { var sib = c.parent ? c.parent.children : []; - for (var i = 0; i < sib.length; i++) if (sib[i] !== c && sib[i].type === "radiobutton") sib[i].value = false; + for (const elem of sib) if (elem !== c && elem.type === "radiobutton") elem.value = false; c.value = true; }; // A mouse click carries the pointer's details ({x, y, button, detail, …keys}). diff --git a/crates/script/src/socket.rs b/crates/script/src/socket.rs index 9d1f8798..6b2976dc 100644 --- a/crates/script/src/socket.rs +++ b/crates/script/src/socket.rs @@ -89,7 +89,7 @@ mod imp { Sock::Listener(_) => None, }; let entry = Arc::new(Entry { sock: Mutex::new(s), wake, closed: AtomicBool::new(false) }); - let mut t = table().lock().unwrap_or_else(|e| e.into_inner()); + let mut t = table().lock().unwrap_or_else(std::sync::PoisonError::into_inner); let id = t.next; t.next += 1; t.socks.insert(id, entry); @@ -98,12 +98,12 @@ mod imp { /// The socket `id`, if open. The table is locked only for the lookup. fn get(id: u32) -> Option> { - table().lock().unwrap_or_else(|e| e.into_inner()).socks.get(&id).cloned() + table().lock().unwrap_or_else(std::sync::PoisonError::into_inner).socks.get(&id).cloned() } /// Forget socket `id`; a call blocked on it fails at once. fn close(id: u32) { - let entry = table().lock().unwrap_or_else(|e| e.into_inner()).socks.remove(&id); + let entry = table().lock().unwrap_or_else(std::sync::PoisonError::into_inner).socks.remove(&id); if let Some(entry) = entry { entry.closed.store(true, Ordering::Release); if let Some(w) = &entry.wake { @@ -261,7 +261,7 @@ mod imp { _ => err("the socket is not open"), }; }; - let mut sock = entry.sock.lock().unwrap_or_else(|e| e.into_inner()); + let mut sock = entry.sock.lock().unwrap_or_else(std::sync::PoisonError::into_inner); // Recheck after locking: close or a failed mode transition may have retired it // while this caller waited with an Arc obtained from the table. if entry.closed.load(Ordering::Acquire) { diff --git a/crates/segment/Cargo.toml b/crates/segment/Cargo.toml index 98f3d1a1..515635db 100644 --- a/crates/segment/Cargo.toml +++ b/crates/segment/Cargo.toml @@ -10,6 +10,6 @@ rust-version.workspace = true workspace = true [dependencies] -miniz_oxide = "0.8" +miniz_oxide = "0.9" ndarray = { version = "0.17", default-features = false, features = ["std"] } rayon = { workspace = true } diff --git a/crates/text/src/font_dirs.rs b/crates/text/src/font_dirs.rs index 0561d8f8..30694946 100644 --- a/crates/text/src/font_dirs.rs +++ b/crates/text/src/font_dirs.rs @@ -150,7 +150,9 @@ impl Reader<'_> { let mut files: Vec = rd .flatten() .map(|e| e.path()) - .filter(|p| p.file_name().and_then(|n| n.to_str()).is_some_and(|n| n.ends_with(".conf") && n.starts_with(|c: char| c.is_ascii_digit()))) + .filter(|p| { + p.file_name().and_then(std::ffi::OsStr::to_str).is_some_and(|n| n.ends_with(".conf") && n.starts_with(|c: char| c.is_ascii_digit())) + }) .collect(); files.sort(); for f in files { diff --git a/crates/text/src/fonts.rs b/crates/text/src/fonts.rs index 724fc4aa..e20ffcbd 100644 --- a/crates/text/src/fonts.rs +++ b/crates/text/src/fonts.rs @@ -116,7 +116,7 @@ fn walk(dir: &std::path::Path, depth: u32, out: &mut Vec) { if depth < 8 { walk(&p, depth + 1, out); } - } else if p.extension().and_then(|x| x.to_str()).is_some_and(|x| matches!(x.to_ascii_lowercase().as_str(), "ttf" | "otf" | "ttc" | "otc")) { + } else if p.extension().and_then(std::ffi::OsStr::to_str).is_some_and(|x| matches!(x.to_ascii_lowercase().as_str(), "ttf" | "otf" | "ttc" | "otc")) { out.push(p); } } @@ -319,7 +319,7 @@ fn push(db: &mut Db, info: FaceInfo) -> FaceId { /// added. pub fn add_source(src: &dyn FontSource) -> usize { let faces = src.faces(); - let mut d = db().write().unwrap_or_else(|e| e.into_inner()); + let mut d = db().write().unwrap_or_else(std::sync::PoisonError::into_inner); let mut n = 0; for f in faces { let dup = d.faces.iter().any(|g| g.info.family.eq_ignore_ascii_case(&f.family) && g.info.style.eq_ignore_ascii_case(&f.style)); @@ -329,7 +329,7 @@ pub fn add_source(src: &dyn FontSource) -> usize { } } // A new face can belong to one of a script's usual families. - family_faces_cache().write().unwrap_or_else(|e| e.into_inner()).clear(); + family_faces_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).clear(); n } @@ -337,9 +337,9 @@ pub fn add_source(src: &dyn FontSource) -> usize { pub fn add_font_data(data: Vec) -> Vec { let data = Arc::new(data); let names = read_faces_bytes(&data); - let mut d = db().write().unwrap_or_else(|e| e.into_inner()); + let mut d = db().write().unwrap_or_else(std::sync::PoisonError::into_inner); let ids = names.into_iter().map(|n| push(&mut d, info_from(n, FaceData::Shared(data.clone()), "user"))).collect(); - family_faces_cache().write().unwrap_or_else(|e| e.into_inner()).clear(); + family_faces_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).clear(); ids } @@ -353,21 +353,21 @@ pub fn scan_system() -> usize { if system_scanned() { return 0; } - let _scan = SCAN.lock().unwrap_or_else(|e| e.into_inner()); + let _scan = SCAN.lock().unwrap_or_else(std::sync::PoisonError::into_inner); if system_scanned() { return 0; } let n = if cfg!(target_arch = "wasm32") { 0 } else { add_source(&DirectorySource::system()) }; - db().write().unwrap_or_else(|e| e.into_inner()).scanned = true; + db().write().unwrap_or_else(std::sync::PoisonError::into_inner).scanned = true; n } /// Scan the system font folders again, adding fonts installed since the last scan (faces already /// known are kept, so font ids stay valid). Returns the number of faces added. pub fn rescan_system() -> usize { - let _scan = SCAN.lock().unwrap_or_else(|e| e.into_inner()); + let _scan = SCAN.lock().unwrap_or_else(std::sync::PoisonError::into_inner); let n = if cfg!(target_arch = "wasm32") { 0 } else { add_source(&DirectorySource::system()) }; - db().write().unwrap_or_else(|e| e.into_inner()).scanned = true; + db().write().unwrap_or_else(std::sync::PoisonError::into_inner).scanned = true; n } @@ -388,12 +388,12 @@ pub fn scan_system_in_background() { } pub fn system_scanned() -> bool { - db().read().unwrap_or_else(|e| e.into_inner()).scanned + db().read().unwrap_or_else(std::sync::PoisonError::into_inner).scanned } /// A face by id (panics never: unknown ids give the default face). pub fn face(id: FaceId) -> Arc { - let d = db().read().unwrap_or_else(|e| e.into_inner()); + let d = db().read().unwrap_or_else(std::sync::PoisonError::into_inner); // The bundled fonts are compiled in and always parse (`tests::bundled_families_and_resolution` checks them), so // the database is never empty. #[allow(clippy::expect_used)] @@ -402,7 +402,7 @@ pub fn face(id: FaceId) -> Arc { /// All faces (bundled first). pub fn all_faces() -> Vec> { - db().read().unwrap_or_else(|e| e.into_inner()).faces.clone() + db().read().unwrap_or_else(std::sync::PoisonError::into_inner).faces.clone() } /// Family names in the fonts' own languages: English family → native family (only families @@ -743,15 +743,15 @@ static REQUESTED_CJK_LOCALE: RwLock> = RwLock::new(None); pub fn set_cjk_locale(tag: &str) { let tag = tag.trim().to_ascii_lowercase(); let requested = if tag.is_empty() || tag == "c" || tag == "posix" { None } else { Some(tag) }; - *REQUESTED_CJK_LOCALE.write().unwrap_or_else(|e| e.into_inner()) = requested; + *REQUESTED_CJK_LOCALE.write().unwrap_or_else(std::sync::PoisonError::into_inner) = requested; // The order of the CJK families follows the language, so a change of language drops the cached // fallbacks. - fallback_cache().write().unwrap_or_else(|e| e.into_inner()).clear(); - family_faces_cache().write().unwrap_or_else(|e| e.into_inner()).clear(); + fallback_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).clear(); + family_faces_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).clear(); } fn requested_cjk_locale() -> Option { - REQUESTED_CJK_LOCALE.read().unwrap_or_else(|e| e.into_inner()).clone() + REQUESTED_CJK_LOCALE.read().unwrap_or_else(std::sync::PoisonError::into_inner).clone() } /// The CJK locale to use: [`set_cjk_locale`] when the host asked for one, otherwise the locale @@ -981,7 +981,7 @@ fn fallback_with_locale(c: char, prefer: FaceId, locale: impl FnOnce() -> CjkLoc } } let key = (c as u32 >> 8, prefer); - if let Some(cached) = fallback_cache().read().unwrap_or_else(|e| e.into_inner()).get(&key).copied() { + if let Some(cached) = fallback_cache().read().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key).copied() { match cached { Some(id) if face(id).has_char(c) => return id, Some(_) => {} @@ -992,7 +992,7 @@ fn fallback_with_locale(c: char, prefer: FaceId, locale: impl FnOnce() -> CjkLoc scan_system(); } let found = system_fallback(c, &p.info.style); - fallback_cache().write().unwrap_or_else(|e| e.into_inner()).insert(key, found); + fallback_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).insert(key, found); found.unwrap_or(prefer) } @@ -1000,7 +1000,7 @@ fn fallback_with_locale(c: char, prefer: FaceId, locale: impl FnOnce() -> CjkLoc /// for `script`, in the order they are tried, each in the style of `prefer`. For CJK, the other CJK /// languages' families follow in the locale's order. fn family_faces(script: FallbackScript, prefer: &Face, locale: CjkLocale) -> Arc> { - if let Some(faces) = family_faces_cache().read().unwrap_or_else(|e| e.into_inner()).get(&(script, prefer.id, locale)) { + if let Some(faces) = family_faces_cache().read().unwrap_or_else(std::sync::PoisonError::into_inner).get(&(script, prefer.id, locale)) { return faces.clone(); } let order: Vec = match script { @@ -1016,7 +1016,7 @@ fn family_faces(script: FallbackScript, prefer: &Face, locale: CjkLocale) -> Arc let style = &prefer.info.style; let faces: Arc> = Arc::new(order.into_iter().flat_map(|s| families_for(s, locale)).filter_map(|fam| resolve_in(fam, style)).map(|r| r.face).collect()); - family_faces_cache().write().unwrap_or_else(|e| e.into_inner()).insert((script, prefer.id, locale), faces.clone()); + family_faces_cache().write().unwrap_or_else(std::sync::PoisonError::into_inner).insert((script, prefer.id, locale), faces.clone()); faces } diff --git a/crates/text/src/layout.rs b/crates/text/src/layout.rs index 7ca5bd13..0730e444 100644 --- a/crates/text/src/layout.rs +++ b/crates/text/src/layout.rs @@ -399,7 +399,7 @@ enum Form { pub fn feature_substitutes(face: FaceId, tag: &[u8; 4], c: char) -> bool { static C: OnceLock>> = OnceLock::new(); let cache = C.get_or_init(Default::default); - if let Some(v) = cache.lock().unwrap_or_else(|e| e.into_inner()).get(&(face, *tag, c)) { + if let Some(v) = cache.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&(face, *tag, c)) { return *v; } let f = fonts::face(face); @@ -419,7 +419,7 @@ pub fn feature_substitutes(face: FaceId, tag: &[u8; 4], c: char) -> bool { } _ => false, }; - let mut m = cache.lock().unwrap_or_else(|e| e.into_inner()); + let mut m = cache.lock().unwrap_or_else(std::sync::PoisonError::into_inner); if m.len() > 100_000 { m.clear(); } @@ -1123,12 +1123,12 @@ pub fn layout_rich(text: &str, runs: &[(Range, TextStyle)], paras: &[Para let key = h.finish(); static CLOCK: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); let now = CLOCK.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - if let Some(e) = cache().lock().unwrap_or_else(|e| e.into_inner()).get_mut(&key) { + if let Some(e) = cache().lock().unwrap_or_else(std::sync::PoisonError::into_inner).get_mut(&key) { e.0 = now; return e.1.clone(); } let l = Arc::new(layout_rich_uncached(text, runs, paras)); - let mut c = cache().lock().unwrap_or_else(|e| e.into_inner()); + let mut c = cache().lock().unwrap_or_else(std::sync::PoisonError::into_inner); if c.len() >= 512 { // evict the oldest quarter let mut ages: Vec = c.values().map(|v| v.0).collect(); diff --git a/crates/text/src/lib.rs b/crates/text/src/lib.rs index 18716752..35e7ecc0 100644 --- a/crates/text/src/lib.rs +++ b/crates/text/src/lib.rs @@ -54,7 +54,7 @@ impl OutlinePen for Pen { pub(crate) fn outline_units(face: FaceId, gid: u32) -> Option> { static C: OnceLock>>>> = OnceLock::new(); let c = C.get_or_init(Default::default); - if let Some(v) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&(face, gid)) { + if let Some(v) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&(face, gid)) { return v.clone(); } let f = fonts::face(face); @@ -64,7 +64,7 @@ pub(crate) fn outline_units(face: FaceId, gid: u32) -> Option> { g.draw(DrawSettings::unhinted(Size::unscaled(), LocationRef::default()), &mut pen).ok()?; Some(Arc::new(pen.0)) }); - let mut m = c.lock().unwrap_or_else(|e| e.into_inner()); + let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner); if m.len() > 50_000 { m.clear(); } diff --git a/crates/text/src/variable.rs b/crates/text/src/variable.rs index bf4fde39..55934335 100644 --- a/crates/text/src/variable.rs +++ b/crates/text/src/variable.rs @@ -36,7 +36,7 @@ pub fn intern(coords: &[(String, f32)]) -> u32 { if key.windows(2).any(|w| w[0].0 == w[1].0) { return 0; } - let mut t = table().lock().unwrap_or_else(|e| e.into_inner()); + let mut t = table().lock().unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(id) = t.by_coords.get(&key) { return *id; } @@ -55,7 +55,7 @@ pub fn coords(id: u32) -> Arc> { if id == 0 { return Arc::new(Vec::new()); } - table().lock().unwrap_or_else(|e| e.into_inner()).values.get(id as usize - 1).cloned().unwrap_or_default() + table().lock().unwrap_or_else(std::sync::PoisonError::into_inner).values.get(id as usize - 1).cloned().unwrap_or_default() } fn table() -> &'static Mutex { @@ -109,7 +109,7 @@ pub fn outline_units_at(face: FaceId, gid: u32, coords: &[(String, f32)]) -> Opt let c = C.get_or_init(Default::default); // Quantise to 1/100 of a unit so animation frames reuse outlines. let key: Key = (face, gid, coords.iter().map(|(t, v)| (t.clone(), (v * 100.0).round() as i32)).collect()); - if let Some(v) = c.lock().unwrap_or_else(|e| e.into_inner()).get(&key) { + if let Some(v) = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&key) { return v.clone(); } let f = fonts::face(face); @@ -120,7 +120,7 @@ pub fn outline_units_at(face: FaceId, gid: u32, coords: &[(String, f32)]) -> Opt g.draw(DrawSettings::unhinted(Size::unscaled(), LocationRef::from(&loc)), &mut pen).ok()?; Some(Arc::new(pen.0)) }); - let mut m = c.lock().unwrap_or_else(|e| e.into_inner()); + let mut m = c.lock().unwrap_or_else(std::sync::PoisonError::into_inner); if m.len() > 20_000 { m.clear(); } diff --git a/crates/ui-egui/Cargo.toml b/crates/ui-egui/Cargo.toml index d9addb68..ede0196e 100644 --- a/crates/ui-egui/Cargo.toml +++ b/crates/ui-egui/Cargo.toml @@ -43,7 +43,7 @@ sys-locale = { workspace = true } # here documents the requirement (kittest's own dev-dependencies do not propagate to consumers). egui = { workspace = true, features = ["default_fonts", "monochrome_emoji_fonts"] } egui_kittest = { workspace = true, features = ["eframe", "wgpu"] } -pollster = "0.4" +pollster = "1" effectcraft-host = { workspace = true } exr = { workspace = true } diff --git a/crates/ui-egui/src/panels/home.rs b/crates/ui-egui/src/panels/home.rs index 5522548e..3b735e47 100644 --- a/crates/ui-egui/src/panels/home.rs +++ b/crates/ui-egui/src/panels/home.rs @@ -127,7 +127,7 @@ pub fn format_size(bytes: u64) -> String { /// The Kind column: what a recent file is, by its extension, in the interface language. fn kind_of(path: &str) -> &'static str { - tr(match std::path::Path::new(path).extension().and_then(|e| e.to_str()).map(str::to_ascii_lowercase).as_deref() { + tr(match std::path::Path::new(path).extension().and_then(std::ffi::OsStr::to_str).map(str::to_ascii_lowercase).as_deref() { Some("ecproj") => "EffectCraft project", Some("aep" | "aepx") => "After Effects project", Some("json" | "lottie") => "Lottie animation", diff --git a/crates/ui-egui/src/panels/timeline.rs b/crates/ui-egui/src/panels/timeline.rs index 238071bd..35db679d 100644 --- a/crates/ui-egui/src/panels/timeline.rs +++ b/crates/ui-egui/src/panels/timeline.rs @@ -1747,6 +1747,9 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { p.rect_filled(handle, 2.0, t.accent); let resp = ui.interact(hr, egui::Id::unique(("wa", set)), Sense::drag()); app.auto.add(&format!("timeline.workArea.{set}"), hr, set); + if resp.dragged() || resp.hovered() { + ctx.set_cursor_icon(egui::CursorIcon::ResizeHorizontal); + } if resp.dragged() && let Some(pt) = resp.interact_pointer_pos() { diff --git a/docs/gaps.md b/docs/gaps.md index dc2af3cd..3f59b1f9 100644 --- a/docs/gaps.md +++ b/docs/gaps.md @@ -76,7 +76,7 @@ are grouped by area; the issue tracker has the rest. | 17 | **Media Browser lists files import refuses** | `media_browser.rs` lists AVI; `media::is_importable` has none of them (Targa and Radiance HDR import since #745; DPX and Cineon since #756, log shown as stored: no Cineon Settings conversion yet; MXF OP1a / OP-Atom since #767; FilmCraft has no AVI demuxer); #431 and #324 closed | Confusing failures on common VFX formats | 15–30 h | G6 · [file-format-parity.md](file-format-parity.md) | | 18 | **Relative paths and relinking** | Projects store absolute footage paths; no relink when a project moves | Moving or sharing a project breaks it | 6–12 h | G4 · [file-format-parity.md](file-format-parity.md) | | 19 | **Plug-in ecosystem** | No After Effects SDK hosting (#600, #327 OpenFX, #334, #376 plug-in manager); our WebAssembly API has no third-party plug-ins | Many pro projects depend on Trapcode, Element, Saber… | 40–80 h for API growth; SDK hosting needs an owner decision | G8 · [scripting-parity.md](scripting-parity.md) | -| 20 | **Multi-monitor and video output** | #486: on desktop every floating panel is an OS window of its own (Undock Panel, a drag out of the main window, panel menu ▸ Move Panel to New Window, `window.panelWindow`) and so can go onto another screen; tabs and groups drag between windows, shortcuts work in them, and the current and named workspaces restore their places; still missing: Mercury Transmit to video hardware (we have a second preview window only) | Two-screen setups are standard for professionals | 10–20 h | G2 · [hardware-parity.md](hardware-parity.md) | +| 20 | **Multi-monitor and video output** | #486: on Windows, macOS and X11 every floating panel is an OS window of its own (Undock Panel, a drag out of the main window, panel menu ▸ Move Panel to New Window, `window.panelWindow`) and so can go onto another screen (on Wayland, the web and in embedded viewports floating panels stay in the app window, since Wayland reports no window positions); tabs and groups drag between windows, shortcuts work in them, and the current and named workspaces restore their places; still missing: Mercury Transmit to video hardware (we have a second preview window only) | Two-screen setups are standard for professionals | 10–20 h | G2 · [hardware-parity.md](hardware-parity.md) | | 21 | **Small 26.x features** | Proportional Scrubbing (26.2), pasting Illustrator's own clipboard data as shapes (26.3; SVG markup on the clipboard pastes as a shape layer, #738), variable-font filter (26.3), the rebuilt Effect Controls (26.5). Percentage guides (View ▸ Add Percentage Guide, Convert Guides to Percentage / Pixels) and effect colour labels (Effect Controls header bar and right-click Label menu, `effect.setLabel`) are in (#1095), built from the release notes' one-line descriptions and never compared with After Effects: the Timeline's effect rows wear the label as a colour bar on the left edge (#1509), but After Effects' exact UI there is unknown | Users of the current version expect them | 10–20 h left | — · [ui-parity.md](ui-parity.md) | | 22 | **Encoder efficiency** | HEVC / AV1 have no B-frames, multi-reference or SAO / CDEF; Opus no FEC / DTX; single-file intermediates are 8-bit uncompressed QuickTime only (#372 closed by #778: no 16-bpc, AVI or lossless-compressed codec); #325 render over existing file | Larger files than After Effects + Media Encoder | 20–40 h | G6 · [file-format-parity.md](file-format-parity.md) | | 23 | **Essential Graphics interchange** | Templates are `.ectemplate`, not `.mogrt`; #338 and #611 closed | Premiere editors can't use our templates | 15–30 h | — · [file-format-parity.md](file-format-parity.md) | diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 55c6b5cd..d4473c26 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -16,7 +16,7 @@ feel and platform differences, and the 26.x interface changes are not in yet. | Area | Status | % | Evidence and gaps | |---|---|---:|---| | Menu bar | After Effects' menus, order, submenus and labels; native on macOS; keyboard-operable in-window bar | 95% | 587 entries in `crates/engine/src/menus.rs`; Adobe-service entries omitted by design; `ui_menus` tests (#279) | -| Panels and workspaces | Every After Effects panel; all standard workspaces; drag-to-dock, tabs, floating panels, maximise | 75% | `ui_tabs`, `ui_panels`; on desktop floating panels are OS windows of their own on any screen, with tabs and groups dragging between windows and shortcuts working in them (#486); #302, #301 and #320 (docking, stacked-panel headers, workspace persistence) are closed | +| Panels and workspaces | Every After Effects panel; all standard workspaces; drag-to-dock, tabs, floating panels, maximise | 75% | `ui_tabs`, `ui_panels`; on Windows, macOS and X11 floating panels are OS windows of their own on any screen (on Wayland, the web and in embedded viewports they stay in the app window), with tabs and groups dragging between windows and shortcuts working in them (#486); #302, #301 and #320 (docking, stacked-panel headers, workspace persistence) are closed | | Timeline | Switches, modes, parent & link, twirl-downs, reveal shortcuts (U, UU, P, S, R, T, A, E, M, F, Alt+Shift…), pick whips, graph editor, work area, markers | 75% | `ui_keyframes`, `ui_timeline_*`; expand precomps in place (#601); #456, #457, #569 and #355 (Graph Editor jitter and buttons, negative rotation, bulk keyframing) are closed; Proportional Scrubbing (26.2: Ctrl+Alt / Cmd+Opt scrub of hot text spreads the change over the selected layers by selection order, #770) matched for layers selected as a whole, not yet for individually selected properties; every layer has the Video switch, nulls, cameras and lights included (a hidden null, camera or light has no box or handles in the viewer and isn't picked there; other selected hidden layers keep their layer controls and paths, #1769); a row control (a switch, the label swatch, a Modes cell) clicked on one of several selected layers acts on the whole selection, with the layers the switch doesn't apply to left out (#1849); a shape group's inner Contents (After Effects' elided Vectors group) draws no row — the group's path, fill/stroke and Transform list directly under it | | Composition viewer | Selection, handles with direction-aware cursors, rulers, guides, grids, snapping, channels, exposure, region of interest, snapshots, 3D views, Spacebar hand | 70% | `ui_viewer`, `ui_viewers`; Cmd/Ctrl +/− doesn't zoom (#570); anchor point grab on Linux (#492); #352 (snapping) is closed; no 3D transform gizmos (#392, #347); percentage guides (26.5) exist as View ▸ Add Percentage Guide and Convert Guides to Percentage / Pixels (a guide stores a percent of the comp, so it follows comp-size changes; behaviour inferred from the release note, not compared with After Effects); Copy Frame to Clipboard (26.3) is in the Composition menu and the viewer's context menu | | Tools panel | All After Effects tools; long-press flyouts; Tool Creates Shape / Mask; Fill / Stroke options | 80% | #213, #227 fixed; #553 (pen and vertex tools on motion paths) is closed | @@ -41,9 +41,8 @@ cover shape paths and masks, corner-vertex dragging and undo, and layers without ## Estimate -**≈ 60–120 h**: the open interaction issues above (25–45 h, about 0.2–0.5 h each with a scripted -test), 3D gizmos 8–15 h, multi-monitor panels 10–20 h, the 26.x interface changes 10–20 h, an -accessibility pass 15–30 h. Checking feel against After Effects needs the owner's Mac with After +**≈ 60–110 h**: the open interaction issues above (25–45 h, about 0.2–0.5 h each with a scripted +test), 3D gizmos 8–15 h, the 26.x interface changes 10–20 h, an accessibility pass 15–30 h. Checking feel against After Effects needs the owner's Mac with After Effects running; most of the rest parallelises. ## Revision history diff --git a/packaging/linux/package.sh b/packaging/linux/package.sh index a67a00d4..a1fc5008 100755 --- a/packaging/linux/package.sh +++ b/packaging/linux/package.sh @@ -84,6 +84,11 @@ has() { case " $FORMATS " in *" $format "*) return 0 ;; *) return 1 ;; esac } +# Fails unless FILE ($2) has SHA-256 digest $1. +verify_sha256() { + printf '%s %s\n' "$1" "$2" | sha256sum -c - +} + # ---- .tar.gz ------------------------------------------------------------------------------------ if has tar; then mkdir -p "$WORK/tar" @@ -141,21 +146,21 @@ if has appimage; then download="$TOOL.download" curl --proto '=https' -fsSL -o "$download" \ "https://github.com/AppImage/appimagetool/releases/download/$APPIMAGETOOL_VERSION/appimagetool-$ARCH.AppImage" - printf '%s %s\n' "$APPIMAGETOOL_SHA256" "$download" | sha256sum -c - + verify_sha256 "$APPIMAGETOOL_SHA256" "$download" chmod 755 "$download" mv "$download" "$TOOL" fi - printf '%s %s\n' "$APPIMAGETOOL_SHA256" "$TOOL" | sha256sum -c - + verify_sha256 "$APPIMAGETOOL_SHA256" "$TOOL" fi RUNTIME="$CARGO_TARGET_DIR/appimage-runtime-$APPIMAGE_RUNTIME_VERSION-$ARCH" if [[ ! -f "$RUNTIME" ]]; then download="$RUNTIME.download" curl --proto '=https' -fsSL -o "$download" \ "https://github.com/AppImage/type2-runtime/releases/download/$APPIMAGE_RUNTIME_VERSION/runtime-$ARCH" - printf '%s %s\n' "$APPIMAGE_RUNTIME_SHA256" "$download" | sha256sum -c - + verify_sha256 "$APPIMAGE_RUNTIME_SHA256" "$download" mv "$download" "$RUNTIME" fi - printf '%s %s\n' "$APPIMAGE_RUNTIME_SHA256" "$RUNTIME" | sha256sum -c - + verify_sha256 "$APPIMAGE_RUNTIME_SHA256" "$RUNTIME" # Absolute, because appimagetool runs in $DIST below (CARGO_TARGET_DIR or APPIMAGETOOL may be # relative). OUT="$(cd "$DIST" && pwd)/$BASENAME.AppImage" diff --git a/xtask/src/main.rs b/xtask/src/main.rs index e4ddaef7..da8026d7 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -176,7 +176,7 @@ fn repo_files() -> Result, String> { } fn is_asset(path: &str) -> bool { - std::path::Path::new(path).extension().and_then(|e| e.to_str()).is_some_and(|e| ASSET_EXT.contains(&e.to_ascii_lowercase().as_str())) + std::path::Path::new(path).extension().and_then(std::ffi::OsStr::to_str).is_some_and(|e| ASSET_EXT.contains(&e.to_ascii_lowercase().as_str())) } fn assets() -> Result<(), String> { @@ -384,7 +384,7 @@ fn serve_dir(dir: &std::path::Path, port: u16) -> Result<(), String> { Ok(b) => ("200 OK", b), Err(_) => ("404 Not Found", b"not found".to_vec()), }; - let mime = match file.extension().and_then(|e| e.to_str()).unwrap_or("") { + let mime = match file.extension().and_then(std::ffi::OsStr::to_str).unwrap_or("") { "html" => "text/html; charset=utf-8", "js" => "text/javascript", "wasm" => "application/wasm", diff --git a/xtask/src/pdf_deps.rs b/xtask/src/pdf_deps.rs index 00b913bc..ca355c17 100644 --- a/xtask/src/pdf_deps.rs +++ b/xtask/src/pdf_deps.rs @@ -5,7 +5,7 @@ use std::process::Command; use serde_json::Value; -const REV: &str = "a0916808f7d78bed8c7b90cdf937176015454a7f"; +const REV: &str = "37c907d23e608aaa098d7a48fa483670e90c6f33"; const PACKAGES: &[(&str, &str)] = &[("craft-pdf", "0.1.0"), ("hayro-syntax", "0.7.2"), ("hayro-jbig2", "0.3.0")]; pub fn run(root: &Path) -> Result<(), String> {