From adcc3b916b6013180a7a9169bd1f04129c1b3bbd Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sun, 25 Oct 2026 09:57:06 -0400 Subject: [PATCH 1/6] feat: startup update prompt modal and direct self-updating Integrate `craft-update` pinned to storytold/craft-libs fe61243 to check for updates on startup, display the standard suite update modal with changelog notes, and support one-click in-app updating and restart. Ref: storytold/craft-libs#319 --- Cargo.lock | 486 ++++++++++++++++++++++++++++++- Cargo.toml | 1 + crates/ui-egui/Cargo.toml | 1 + crates/ui-egui/src/lib.rs | 5 + crates/ui-egui/src/menus.rs | 4 + crates/ui-egui/src/updates_ui.rs | 235 +++++++++++++++ 6 files changed, 727 insertions(+), 5 deletions(-) create mode 100644 crates/ui-egui/src/updates_ui.rs diff --git a/Cargo.lock b/Cargo.lock index 70fa5a61..163d9186 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -431,6 +431,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "bit-set" version = "0.8.0" @@ -479,6 +485,15 @@ version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block2" version = "0.5.1" @@ -795,7 +810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.3.1", "rand_core 0.10.1", ] @@ -872,6 +887,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -885,7 +910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "core-graphics-types", "foreign-types", "libc", @@ -898,7 +923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "libc", ] @@ -964,6 +989,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "cpufeatures" version = "0.3.1" @@ -996,6 +1030,32 @@ dependencies = [ "serde", ] +[[package]] +name = "craft-update" +version = "0.1.0" +source = "git+https://github.com/storytold/craft-libs?rev=fe61243ebfd7f9a7bea4cc326028e859d6aab052#fe61243ebfd7f9a7bea4cc326028e859d6aab052" +dependencies = [ + "chrono", + "dirs", + "dunce", + "egui", + "flate2", + "getrandom 0.3.4", + "log", + "plist", + "semver", + "serde", + "serde_json", + "sha2", + "tar", + "tempfile", + "ureq", + "verifysign", + "winreg", + "winsafe", + "zip", +] + [[package]] name = "crc32fast" version = "1.5.2" @@ -1045,6 +1105,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "cursor-icon" version = "1.2.0" @@ -1077,6 +1147,37 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.59.0", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -1136,6 +1237,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dynify" version = "0.1.2" @@ -1660,6 +1767,7 @@ version = "0.17.0" dependencies = [ "chrono", "craft-ui", + "craft-update", "effectcraft-color", "effectcraft-engine", "effectcraft-geom", @@ -2056,6 +2164,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97b65636e5b9ef369943878ac74335ba1c55c1cb6adbf1e2c293c624248d693" +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + [[package]] name = "filmcraft-aac" version = "0.1.1" @@ -2476,6 +2594,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "gethostname" version = "1.1.0" @@ -2486,6 +2614,17 @@ dependencies = [ "windows-link", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -2762,6 +2901,22 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + [[package]] name = "icu_collections" version = "2.3.0" @@ -3859,6 +4014,18 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "orbclient" version = "0.3.55" @@ -4061,6 +4228,19 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "plist" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327a4cb2ad49dc48adfdc9da89e22c64d488b508c67a044d71ba729e4521a285" +dependencies = [ + "base64", + "indexmap", + "quick-xml 0.42.0", + "serde", + "time", +] + [[package]] name = "png" version = "0.18.1" @@ -4247,6 +4427,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.47" @@ -4426,6 +4615,16 @@ dependencies = [ "bitflags 2.13.2", ] +[[package]] +name = "redox_users" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60dc65c0ff1a7ae1294b0c67b9f14baf70b644404010370171787bfac1038fc0" +dependencies = [ + "libredox", + "thiserror 2.0.21", +] + [[package]] name = "regex-syntax" version = "0.8.11" @@ -4474,6 +4673,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "roxmltree" version = "0.21.1" @@ -4530,6 +4743,80 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a073f5dc7469f984c52ad2752b63b0807745133b6de880b7b64c1ac4c48aec4" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" + +[[package]] +name = "rustls-webpki" +version = "0.103.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.23" @@ -4563,6 +4850,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb23c424d8ee841bb4d813677ca237e2a610067d594fec130f87d6093495ab5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -4575,6 +4871,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.2", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "self_cell" version = "1.3.0" @@ -4586,6 +4905,10 @@ name = "semver" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] [[package]] name = "send_wrapper" @@ -4656,6 +4979,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + [[package]] name = "shlex" version = "2.0.1" @@ -4900,6 +5234,12 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "symphonia" version = "0.5.5" @@ -5112,6 +5452,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -5380,6 +5730,18 @@ dependencies = [ "rustc-hash 2.1.3", ] +[[package]] +name = "typed-path" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ec2755506471f54baf5e4bb5499d8e510cc1caf271270e778a736bd2d7336c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + [[package]] name = "uds_windows" version = "1.2.1" @@ -5448,6 +5810,41 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde93ff05758a36a851a398a772b0b79a82fbe3c186a46f2696baaeaf84c966b" +dependencies = [ + "base64", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "ureq-proto", + "utf8-zero", +] + +[[package]] +name = "ureq-proto" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841583fc36089d7d59419568ae157f43a5a03a2e5a81ba87c77d1596ef46e922" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + [[package]] name = "url" version = "2.5.8" @@ -5466,6 +5863,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -5510,6 +5913,16 @@ dependencies = [ "vello_common", ] +[[package]] +name = "verifysign" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6acfcc42a8d44f340d001193d97ef56bf33210b51ad760ae8308c97d5241ca51" +dependencies = [ + "core-foundation 0.10.1", + "windows-sys 0.61.2", +] + [[package]] name = "version_check" version = "0.9.5" @@ -5535,6 +5948,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.4+wasi-0.2.12" @@ -5851,7 +6270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.41.0", "quote", ] @@ -5903,6 +6322,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49d0e65abc00c142e8f9428da991da9f1a250c39be97459901eb12b9b49f62a" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "weezl" version = "0.1.12" @@ -6507,7 +6935,7 @@ dependencies = [ "calloop 0.13.0", "cfg_aliases", "concurrent-queue", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "cursor-icon", "dpi", @@ -6552,6 +6980,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + [[package]] name = "winresource" version = "0.1.31" @@ -6562,6 +7000,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "winsafe" +version = "0.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef0ffc427f045c0cc9ebffd6f4f91153dcd2a1547b5c3c29ee3f541e16e95c6" + [[package]] name = "wit-bindgen" version = "0.57.1" @@ -6840,6 +7284,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" + [[package]] name = "zerotrie" version = "0.2.5" @@ -6873,6 +7323,20 @@ dependencies = [ "syn 3.0.6", ] +[[package]] +name = "zip" +version = "8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +dependencies = [ + "crc32fast", + "flate2", + "indexmap", + "memchr", + "typed-path", + "zopfli", +] + [[package]] name = "zlib-rs" version = "0.6.8" @@ -6885,6 +7349,18 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" +[[package]] +name = "zopfli" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf7fc5d30c28483d93805c4a5e12b05bbb52407fa67c5f8bd552374cd01fb11" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + [[package]] name = "zune-core" version = "0.5.3" diff --git a/Cargo.toml b/Cargo.toml index 598a156a..5e9a2f08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ repository = "https://github.com/storytold/effectcraft" [workspace.dependencies] craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "9f75d3dc19b4b514c4f3d4847ed1ec1b0e86b4ef", version = "=0.1.0", default-features = false, features = ["read"] } +craft-update = { git = "https://github.com/storytold/craft-libs", rev = "fe61243ebfd7f9a7bea4cc326028e859d6aab052", features = ["egui"] } craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "82a24b006eefa036b43c09a67fb9a3fbd17ffe63" } # internal effectcraft-time = { path = "crates/time" } diff --git a/crates/ui-egui/Cargo.toml b/crates/ui-egui/Cargo.toml index 009a2c6e..ad910294 100644 --- a/crates/ui-egui/Cargo.toml +++ b/crates/ui-egui/Cargo.toml @@ -33,6 +33,7 @@ kurbo = { workspace = true } image = { workspace = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] +craft-update = { workspace = true } sys-locale = { workspace = true } [dev-dependencies] diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 7f8e0268..49aa9bcb 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -28,6 +28,7 @@ pub mod native_menu; pub mod panels; mod pointer_wrap; pub mod prefs_live; +pub mod updates_ui; pub mod state; pub mod theme; pub mod widgets; @@ -488,6 +489,7 @@ pub struct EffectcraftApp { pub audio: Option, /// Audio scrubbing's open output (Ctrl/Cmd-drag the current time, `playback.scrubAudio`). pub scrub: Option, + pub(crate) updates_rt: updates_ui::Runtime, /// Audio panel VU meters. pub meter: audio::Meter, /// Waveform peak summaries per footage item (see `panels::waveform`). @@ -627,6 +629,7 @@ impl EffectcraftApp { window_title: String::new(), template_thumbs: Default::default(), template_list: None, + updates_rt: updates_ui::Runtime::default(), } .with_ui_commands() } @@ -2205,6 +2208,7 @@ impl EffectcraftApp { } // Frames and worker completions wake the app even while it is hidden. self.frames.set_context(ctx); + updates_ui::tick(self, ctx); if self.session.render_job.is_some() { self.session.poll_render(); ctx.request_repaint_after(std::time::Duration::from_millis(100)); @@ -2456,6 +2460,7 @@ impl EffectcraftApp { panels::precomp::mini_flowchart(self, &ctx); panels::home::capture_thumbnail(self); panels::dialogs::show(self, &ctx); + updates_ui::show(self, &ctx); panels::scriptui_view::sync_panels(self); panels::scriptui_view::show_windows(self, &ctx); panels::learn::coach(self, &ctx); diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 851f6fb0..2e7b3293 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -933,6 +933,10 @@ pub fn frontend(app: &mut EffectcraftApp, ctx: &egui::Context, id: &str, p: Valu ctx.copy_text(path.to_string()); json!({"path": path}) } + "app.checkForUpdates" | "help.checkForUpdates" => { + crate::updates_ui::check(app); + Value::Null + } "app.about" => { app.dialog = Some(crate::Dialog::About); Value::Null diff --git a/crates/ui-egui/src/updates_ui.rs b/crates/ui-egui/src/updates_ui.rs new file mode 100644 index 00000000..563429f2 --- /dev/null +++ b/crates/ui-egui/src/updates_ui.rs @@ -0,0 +1,235 @@ +//! Software updates, background check at startup, and startup prompt modal. + +use crate::EffectcraftApp; + +pub(crate) struct Runtime { + startup_checked: bool, + manual_checked: bool, + pub(crate) manual_window_open: bool, + pub(crate) status_message: Option, + #[cfg(not(target_arch = "wasm32"))] + pub(crate) prompt: Option, + #[cfg(not(target_arch = "wasm32"))] + rx: Option, String>>>, + #[cfg(not(target_arch = "wasm32"))] + checking: bool, +} + +impl Default for Runtime { + fn default() -> Self { + Self { + startup_checked: false, + manual_checked: false, + manual_window_open: false, + status_message: None, + #[cfg(not(target_arch = "wasm32"))] + prompt: None, + #[cfg(not(target_arch = "wasm32"))] + rx: None, + #[cfg(not(target_arch = "wasm32"))] + checking: false, + } + } +} + +pub(crate) fn check(app: &mut EffectcraftApp) { + #[cfg(not(target_arch = "wasm32"))] + { + app.updates_rt.manual_checked = true; + app.updates_rt.manual_window_open = false; + app.updates_rt.status_message = None; + start_check(app); + } + #[cfg(target_arch = "wasm32")] + { + app.updates_rt.manual_window_open = true; + app.updates_rt.status_message = Some(crate::i18n::tr("Update check is not supported in the web browser.")); + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn start_check(app: &mut EffectcraftApp) { + if app.updates_rt.checking { + return; + } + let (tx, rx) = std::sync::mpsc::channel(); + app.updates_rt.rx = Some(rx); + app.updates_rt.checking = true; + + std::thread::spawn(move || { + let http = match craft_update::Http::new("EffectCraft/Update") { + Ok(h) => h, + Err(e) => { + let _ = tx.send(Err(e)); + return; + } + }; + let res = craft_update::check_for_update(&craft_update::suite::EFFECTCRAFT, &http, env!("CARGO_PKG_VERSION")); + let _ = tx.send(res); + }); +} + +pub(crate) fn tick(app: &mut EffectcraftApp, ctx: &egui::Context) { + #[cfg(not(target_arch = "wasm32"))] + { + if !app.updates_rt.startup_checked { + app.updates_rt.startup_checked = true; + start_check(app); + } + + let mut completed = None; + if let Some(rx) = &app.updates_rt.rx { + if let Ok(res) = rx.try_recv() { + completed = Some(res); + } + } + + if let Some(res) = completed { + app.updates_rt.rx = None; + app.updates_rt.checking = false; + match res { + Ok(Some(release)) => { + let mut prompt = + craft_update::StartupPrompt::new(&craft_update::suite::EFFECTCRAFT, release, env!("CARGO_PKG_VERSION")); + prompt.open = true; + app.updates_rt.prompt = Some(prompt); + ctx.request_repaint(); + } + Ok(None) => { + if app.updates_rt.manual_checked { + app.updates_rt.manual_checked = false; + app.updates_rt.status_message = + Some(format!("EffectCraft {} is currently the newest version available.", env!("CARGO_PKG_VERSION"))); + app.updates_rt.manual_window_open = true; + ctx.request_repaint(); + } + } + Err(e) => { + if app.updates_rt.manual_checked { + app.updates_rt.manual_checked = false; + app.updates_rt.status_message = Some(format!("Could not check for updates:\n{e}")); + app.updates_rt.manual_window_open = true; + ctx.request_repaint(); + } + } + } + } + } +} + +pub(crate) fn show(app: &mut EffectcraftApp, ctx: &egui::Context) { + #[cfg(not(target_arch = "wasm32"))] + { + let mut download_url = None; + if let Some(prompt) = &mut app.updates_rt.prompt { + match prompt.show(ctx) { + craft_update::StartupPromptResponse::Pending => {} + craft_update::StartupPromptResponse::UpdateAndRestart => { + prompt.is_installing = true; + if let Some(install) = craft_update::self_update::current(&craft_update::suite::EFFECTCRAFT) { + let mut release = prompt.release.clone(); + std::thread::spawn(move || { + let http = match craft_update::Http::new("EffectCraft/Update") { + Ok(h) => h, + Err(e) => { + log::warn!("could not initialize HTTP: {e}"); + return; + } + }; + if release.assets.is_empty() { + if let Ok(Some(full)) = craft_update::check_for_update( + &craft_update::suite::EFFECTCRAFT, + &http, + env!("CARGO_PKG_VERSION"), + ) { + release = full; + } + } + let paths = match craft_update::Paths::for_app(&craft_update::suite::EFFECTCRAFT) { + Ok(p) => p, + Err(e) => { + log::warn!("could not find paths: {e}"); + return; + } + }; + let stop = craft_update::Stop::default(); + let platform = match craft_update::Platform::current() { + Some(p) => p, + None => { + log::warn!("unsupported platform for self update"); + return; + } + }; + let last = || Ok(()); + match craft_update::self_update::update( + &craft_update::suite::EFFECTCRAFT, + &install, + &release, + platform, + &http, + &paths, + &stop, + &mut |_| {}, + &last, + Some(env!("CARGO_PKG_VERSION")), + ) { + Ok(()) => { + std::process::exit(0); + } + Err(e) => { + log::warn!("EffectCraft self-update failed: {e}"); + } + } + }); + } else { + prompt.status_message = + Some("Cannot auto-update this installation type. Please download manually.".to_string()); + prompt.is_installing = false; + } + } + craft_update::StartupPromptResponse::Download => { + download_url = Some(prompt.release.html_url.clone()); + prompt.open = false; + } + craft_update::StartupPromptResponse::Later => { + prompt.open = false; + } + } + if !prompt.open { + app.updates_rt.prompt = None; + } + } + + if let Some(url) = download_url { + app.updates_rt.prompt = None; + ctx.open_url(egui::OpenUrl::new_tab(url)); + } + } + + if app.updates_rt.manual_window_open { + let mut open = true; + let mut close = false; + egui::Window::new(crate::i18n::tr("Check for Updates")) + .open(&mut open) + .resizable(false) + .collapsible(false) + .anchor(egui::Align2::CENTER_CENTER, egui::vec2(0.0, 0.0)) + .show(ctx, |ui| { + if let Some(msg) = &app.updates_rt.status_message { + ui.label(msg); + } else { + ui.label(crate::i18n::tr("Checking for updates…")); + } + ui.add_space(8.0); + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + if ui.button(crate::i18n::tr("Close")).clicked() { + close = true; + } + }); + }); + if !open || close { + app.updates_rt.manual_window_open = false; + app.updates_rt.status_message = None; + } + } +} From 89a0313ab0d8fa3380b65167c088e4d92fa26a47 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sun, 25 Oct 2026 11:28:13 -0400 Subject: [PATCH 2/6] fix(updates): report self-update results to the prompt and finish through the app's close path Keeps the worker's failure visible instead of leaving the prompt installing, repaints while a check runs, and no longer calls process::exit past the unsaved-work prompt. --- crates/ui-egui/src/lib.rs | 2 +- crates/ui-egui/src/updates_ui.rs | 106 +++++++++++++++---------------- 2 files changed, 53 insertions(+), 55 deletions(-) diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 49aa9bcb..0dbdfdfb 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -28,9 +28,9 @@ pub mod native_menu; pub mod panels; mod pointer_wrap; pub mod prefs_live; -pub mod updates_ui; pub mod state; pub mod theme; +pub mod updates_ui; pub mod widgets; mod workspace_state; diff --git a/crates/ui-egui/src/updates_ui.rs b/crates/ui-egui/src/updates_ui.rs index 563429f2..5d300e6e 100644 --- a/crates/ui-egui/src/updates_ui.rs +++ b/crates/ui-egui/src/updates_ui.rs @@ -13,6 +13,8 @@ pub(crate) struct Runtime { rx: Option, String>>>, #[cfg(not(target_arch = "wasm32"))] checking: bool, + #[cfg(not(target_arch = "wasm32"))] + update_rx: Option>>, } impl Default for Runtime { @@ -28,6 +30,8 @@ impl Default for Runtime { rx: None, #[cfg(not(target_arch = "wasm32"))] checking: false, + #[cfg(not(target_arch = "wasm32"))] + update_rx: None, } } } @@ -43,7 +47,7 @@ pub(crate) fn check(app: &mut EffectcraftApp) { #[cfg(target_arch = "wasm32")] { app.updates_rt.manual_window_open = true; - app.updates_rt.status_message = Some(crate::i18n::tr("Update check is not supported in the web browser.")); + app.updates_rt.status_message = Some(crate::i18n::tr("Update check is not supported in the web browser.").to_string()); } } @@ -77,6 +81,10 @@ pub(crate) fn tick(app: &mut EffectcraftApp, ctx: &egui::Context) { start_check(app); } + if app.updates_rt.checking { + ctx.request_repaint_after(std::time::Duration::from_millis(100)); + } + let mut completed = None; if let Some(rx) = &app.updates_rt.rx { if let Ok(res) = rx.try_recv() { @@ -89,8 +97,7 @@ pub(crate) fn tick(app: &mut EffectcraftApp, ctx: &egui::Context) { app.updates_rt.checking = false; match res { Ok(Some(release)) => { - let mut prompt = - craft_update::StartupPrompt::new(&craft_update::suite::EFFECTCRAFT, release, env!("CARGO_PKG_VERSION")); + let mut prompt = craft_update::StartupPrompt::new(&craft_update::suite::EFFECTCRAFT, release, env!("CARGO_PKG_VERSION")); prompt.open = true; app.updates_rt.prompt = Some(prompt); ctx.request_repaint(); @@ -98,8 +105,7 @@ pub(crate) fn tick(app: &mut EffectcraftApp, ctx: &egui::Context) { Ok(None) => { if app.updates_rt.manual_checked { app.updates_rt.manual_checked = false; - app.updates_rt.status_message = - Some(format!("EffectCraft {} is currently the newest version available.", env!("CARGO_PKG_VERSION"))); + app.updates_rt.status_message = Some(format!("EffectCraft {} is currently the newest version available.", env!("CARGO_PKG_VERSION"))); app.updates_rt.manual_window_open = true; ctx.request_repaint(); } @@ -122,68 +128,60 @@ pub(crate) fn show(app: &mut EffectcraftApp, ctx: &egui::Context) { { let mut download_url = None; if let Some(prompt) = &mut app.updates_rt.prompt { + if let Some(result) = app.updates_rt.update_rx.as_ref().and_then(|rx| rx.try_recv().ok()) { + app.updates_rt.update_rx = None; + match result { + // The app's close path asks about unsaved work; the restart helper relaunches once the process exits. + Ok(()) => ctx.send_viewport_cmd(egui::ViewportCommand::Close), + Err(e) => { + prompt.status_message = Some(e); + prompt.is_installing = false; + } + } + } match prompt.show(ctx) { craft_update::StartupPromptResponse::Pending => {} craft_update::StartupPromptResponse::UpdateAndRestart => { prompt.is_installing = true; if let Some(install) = craft_update::self_update::current(&craft_update::suite::EFFECTCRAFT) { let mut release = prompt.release.clone(); + let (tx, rx) = std::sync::mpsc::channel(); + app.updates_rt.update_rx = Some(rx); + let ctx = ctx.clone(); std::thread::spawn(move || { - let http = match craft_update::Http::new("EffectCraft/Update") { - Ok(h) => h, - Err(e) => { - log::warn!("could not initialize HTTP: {e}"); - return; - } - }; - if release.assets.is_empty() { - if let Ok(Some(full)) = craft_update::check_for_update( - &craft_update::suite::EFFECTCRAFT, - &http, - env!("CARGO_PKG_VERSION"), - ) { + let mut run = || -> Result<(), String> { + let http = craft_update::Http::new("EffectCraft/Update").map_err(|e| format!("could not initialize HTTP: {e}"))?; + if release.assets.is_empty() + && let Some(full) = craft_update::check_for_update(&craft_update::suite::EFFECTCRAFT, &http, env!("CARGO_PKG_VERSION")) + .map_err(|e| format!("could not fetch release assets: {e}"))? + { release = full; } - } - let paths = match craft_update::Paths::for_app(&craft_update::suite::EFFECTCRAFT) { - Ok(p) => p, - Err(e) => { - log::warn!("could not find paths: {e}"); - return; - } + let paths = + craft_update::Paths::for_app(&craft_update::suite::EFFECTCRAFT).map_err(|e| format!("could not find paths: {e}"))?; + let platform = craft_update::Platform::current().ok_or("unsupported platform for self update")?; + craft_update::self_update::update( + &craft_update::suite::EFFECTCRAFT, + &install, + &release, + platform, + &http, + &paths, + &craft_update::Stop::default(), + &mut |_| {}, + &|| Ok(()), + Some(env!("CARGO_PKG_VERSION")), + ) }; - let stop = craft_update::Stop::default(); - let platform = match craft_update::Platform::current() { - Some(p) => p, - None => { - log::warn!("unsupported platform for self update"); - return; - } - }; - let last = || Ok(()); - match craft_update::self_update::update( - &craft_update::suite::EFFECTCRAFT, - &install, - &release, - platform, - &http, - &paths, - &stop, - &mut |_| {}, - &last, - Some(env!("CARGO_PKG_VERSION")), - ) { - Ok(()) => { - std::process::exit(0); - } - Err(e) => { - log::warn!("EffectCraft self-update failed: {e}"); - } + let result = run(); + if let Err(e) = &result { + log::warn!("self-update failed: {e}"); } + let _ = tx.send(result); + ctx.request_repaint(); }); } else { - prompt.status_message = - Some("Cannot auto-update this installation type. Please download manually.".to_string()); + prompt.status_message = Some("Cannot auto-update this installation type. Please download manually.".to_string()); prompt.is_installing = false; } } From 9932ca0cf2183309743661c74e053a74d0e311d2 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sun, 25 Oct 2026 11:58:14 -0400 Subject: [PATCH 3/6] build: pin craft-update to the dirs-free, zip 9 build Satisfies cargo-deny: no MPL-2.0 option-ext, no zip 8 advisories, and a versioned git dependency instead of a wildcard. --- Cargo.lock | 81 +++++++++++++++++++----------------------------------- Cargo.toml | 2 +- 2 files changed, 30 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 163d9186..6f70ae71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1033,10 +1033,9 @@ dependencies = [ [[package]] name = "craft-update" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=fe61243ebfd7f9a7bea4cc326028e859d6aab052#fe61243ebfd7f9a7bea4cc326028e859d6aab052" +source = "git+https://github.com/storytold/craft-libs?rev=fcd3441993ee238144ee56538ad81b4a333f1ed9#fcd3441993ee238144ee56538ad81b4a333f1ed9" dependencies = [ "chrono", - "dirs", "dunce", "egui", "flate2", @@ -1157,27 +1156,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "dirs" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.59.0", -] - [[package]] name = "dispatch" version = "0.2.0" @@ -2070,7 +2048,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3444,7 +3422,7 @@ dependencies = [ "once_cell", "png", "thiserror 2.0.21", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3536,6 +3514,18 @@ dependencies = [ "jni-sys 0.3.1", ] +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.2", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -3557,7 +3547,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4020,12 +4010,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "orbclient" version = "0.3.55" @@ -4615,16 +4599,6 @@ dependencies = [ "bitflags 2.13.2", ] -[[package]] -name = "redox_users" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60dc65c0ff1a7ae1294b0c67b9f14baf70b644404010370171787bfac1038fc0" -dependencies = [ - "libredox", - "thiserror 2.0.21", -] - [[package]] name = "regex-syntax" version = "0.8.11" @@ -4740,7 +4714,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4797,7 +4771,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -5156,7 +5130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5472,7 +5446,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.5", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5732,9 +5706,9 @@ dependencies = [ [[package]] name = "typed-path" -version = "0.12.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ec2755506471f54baf5e4bb5499d8e510cc1caf271270e778a736bd2d7336c" +checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e" [[package]] name = "typenum" @@ -5750,7 +5724,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -6542,7 +6516,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -7325,15 +7299,18 @@ dependencies = [ [[package]] name = "zip" -version = "8.6.0" +version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +checksum = "2886b00003a72c4ccee3f42cfc34908d33b2508a74d827dc2a4244706475c704" dependencies = [ "crc32fast", "flate2", + "getrandom 0.4.3", "indexmap", "memchr", + "nix", "typed-path", + "windows-sys 0.61.2", "zopfli", ] diff --git a/Cargo.toml b/Cargo.toml index 5e9a2f08..1ec15fce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/storytold/effectcraft" [workspace.dependencies] craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "9f75d3dc19b4b514c4f3d4847ed1ec1b0e86b4ef", version = "=0.1.0", default-features = false, features = ["read"] } -craft-update = { git = "https://github.com/storytold/craft-libs", rev = "fe61243ebfd7f9a7bea4cc326028e859d6aab052", features = ["egui"] } +craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "fcd3441993ee238144ee56538ad81b4a333f1ed9", features = ["egui"] } craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "82a24b006eefa036b43c09a67fb9a3fbd17ffe63" } # internal effectcraft-time = { path = "crates/time" } From 6f813b74e94006f8dd2baf3e17517ab5fa9655f6 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sun, 25 Oct 2026 13:58:13 -0400 Subject: [PATCH 4/6] refactor(updates): drive the flow with craft-update's Updater Startup check is opt-in for the desktop app, the Windows update helper is dispatched before argument parsing, and a successful update closes through the app's own close path. --- Cargo.lock | 21 +-- Cargo.toml | 2 +- apps/effectcraft/Cargo.toml | 3 + apps/effectcraft/src/main.rs | 6 + crates/ui-egui/src/updates_ui.rs | 215 ++++++++----------------------- 5 files changed, 72 insertions(+), 175 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f70ae71..175f3690 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1033,7 +1033,7 @@ dependencies = [ [[package]] name = "craft-update" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=fcd3441993ee238144ee56538ad81b4a333f1ed9#fcd3441993ee238144ee56538ad81b4a333f1ed9" +source = "git+https://github.com/storytold/craft-libs?rev=13ceaf2f31620531f39a54f6b994d581a6082a93#13ceaf2f31620531f39a54f6b994d581a6082a93" dependencies = [ "chrono", "dunce", @@ -1255,6 +1255,7 @@ name = "effectcraft" version = "0.17.0" dependencies = [ "cpal", + "craft-update", "effectcraft-engine", "effectcraft-host", "effectcraft-ui-egui", @@ -2048,7 +2049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3422,7 +3423,7 @@ dependencies = [ "once_cell", "png", "thiserror 2.0.21", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3547,7 +3548,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4714,7 +4715,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4771,7 +4772,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5130,7 +5131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5446,7 +5447,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.5", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5724,7 +5725,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6516,7 +6517,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1ec15fce..073e744d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/storytold/effectcraft" [workspace.dependencies] craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "9f75d3dc19b4b514c4f3d4847ed1ec1b0e86b4ef", version = "=0.1.0", default-features = false, features = ["read"] } -craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "fcd3441993ee238144ee56538ad81b4a333f1ed9", features = ["egui"] } +craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "13ceaf2f31620531f39a54f6b994d581a6082a93", features = ["egui"] } craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "82a24b006eefa036b43c09a67fb9a3fbd17ffe63" } # internal effectcraft-time = { path = "crates/time" } diff --git a/apps/effectcraft/Cargo.toml b/apps/effectcraft/Cargo.toml index 8a1ea3ae..e09e7d8b 100644 --- a/apps/effectcraft/Cargo.toml +++ b/apps/effectcraft/Cargo.toml @@ -48,3 +48,6 @@ objc2-app-kit = { version = "0.3", default-features = false, features = ["std", # PhotoCraft and PdfCraft) registers the handler (Objective-C interop needs unsafe, which our # workspace forbids) behind a safe API. Pinned exactly: review the diff before bumping. fmv-macos-events = "=0.1.0" + +[target.'cfg(windows)'.dependencies] +craft-update = { workspace = true } diff --git a/apps/effectcraft/src/main.rs b/apps/effectcraft/src/main.rs index a95e79a4..ab0e219e 100644 --- a/apps/effectcraft/src/main.rs +++ b/apps/effectcraft/src/main.rs @@ -139,6 +139,11 @@ fn install_file_dialogs(app: &mut EffectcraftApp, ctx: &egui::Context) { } fn main() -> eframe::Result { + // Windows: the update helper is a copy of this program started with `--finish-update`. + #[cfg(windows)] + if let Some(result) = craft_update::self_update::finish(&craft_update::suite::EFFECTCRAFT, &std::env::args_os().skip(1).collect::>()) { + std::process::exit(i32::from(result.is_err())); + } // First, so the panic hook and every start-up warning are recorded. Records go to stderr // and the session log; Help ▸ Enable Logging writes through this logger too, and warnings // feed the compatibility report. @@ -264,6 +269,7 @@ fn main() -> eframe::Result { // reported in a toast and forgotten). let _ = session.execute("window.restoreScriptPanels", json!({})); let mut app = EffectcraftApp::new(session); + app.set_startup_update_check(true); app.set_gpu_failure_bridge(gpu_failures); app.ui.start_screen = show_home; if let Some(n) = notice { diff --git a/crates/ui-egui/src/updates_ui.rs b/crates/ui-egui/src/updates_ui.rs index 5d300e6e..a93517c7 100644 --- a/crates/ui-egui/src/updates_ui.rs +++ b/crates/ui-egui/src/updates_ui.rs @@ -2,47 +2,48 @@ use crate::EffectcraftApp; +#[cfg(not(target_arch = "wasm32"))] +use craft_update::updater::{Action, Status, Updater}; + pub(crate) struct Runtime { - startup_checked: bool, - manual_checked: bool, + #[cfg(not(target_arch = "wasm32"))] + updater: Updater, + /// The desktop app turns the check at start on; tests and the web build leave it off. + #[cfg(not(target_arch = "wasm32"))] + startup_check: bool, + #[cfg(not(target_arch = "wasm32"))] + check_requested: bool, pub(crate) manual_window_open: bool, pub(crate) status_message: Option, - #[cfg(not(target_arch = "wasm32"))] - pub(crate) prompt: Option, - #[cfg(not(target_arch = "wasm32"))] - rx: Option, String>>>, - #[cfg(not(target_arch = "wasm32"))] - checking: bool, - #[cfg(not(target_arch = "wasm32"))] - update_rx: Option>>, } impl Default for Runtime { fn default() -> Self { Self { - startup_checked: false, - manual_checked: false, + #[cfg(not(target_arch = "wasm32"))] + updater: Updater::new(&craft_update::suite::EFFECTCRAFT, env!("CARGO_PKG_VERSION")), + #[cfg(not(target_arch = "wasm32"))] + startup_check: false, + #[cfg(not(target_arch = "wasm32"))] + check_requested: false, manual_window_open: false, status_message: None, - #[cfg(not(target_arch = "wasm32"))] - prompt: None, - #[cfg(not(target_arch = "wasm32"))] - rx: None, - #[cfg(not(target_arch = "wasm32"))] - checking: false, - #[cfg(not(target_arch = "wasm32"))] - update_rx: None, } } } +impl EffectcraftApp { + /// Whether the app asks for a newer release when it starts (off until the desktop app turns it on). + #[cfg(not(target_arch = "wasm32"))] + pub fn set_startup_update_check(&mut self, on: bool) { + self.updates_rt.startup_check = on; + } +} + pub(crate) fn check(app: &mut EffectcraftApp) { #[cfg(not(target_arch = "wasm32"))] { - app.updates_rt.manual_checked = true; - app.updates_rt.manual_window_open = false; - app.updates_rt.status_message = None; - start_check(app); + app.updates_rt.check_requested = true; } #[cfg(target_arch = "wasm32")] { @@ -51,157 +52,37 @@ pub(crate) fn check(app: &mut EffectcraftApp) { } } -#[cfg(not(target_arch = "wasm32"))] -fn start_check(app: &mut EffectcraftApp) { - if app.updates_rt.checking { - return; - } - let (tx, rx) = std::sync::mpsc::channel(); - app.updates_rt.rx = Some(rx); - app.updates_rt.checking = true; - - std::thread::spawn(move || { - let http = match craft_update::Http::new("EffectCraft/Update") { - Ok(h) => h, - Err(e) => { - let _ = tx.send(Err(e)); - return; - } - }; - let res = craft_update::check_for_update(&craft_update::suite::EFFECTCRAFT, &http, env!("CARGO_PKG_VERSION")); - let _ = tx.send(res); - }); -} - pub(crate) fn tick(app: &mut EffectcraftApp, ctx: &egui::Context) { #[cfg(not(target_arch = "wasm32"))] { - if !app.updates_rt.startup_checked { - app.updates_rt.startup_checked = true; - start_check(app); - } - - if app.updates_rt.checking { - ctx.request_repaint_after(std::time::Duration::from_millis(100)); - } - - let mut completed = None; - if let Some(rx) = &app.updates_rt.rx { - if let Ok(res) = rx.try_recv() { - completed = Some(res); - } - } - - if let Some(res) = completed { - app.updates_rt.rx = None; - app.updates_rt.checking = false; - match res { - Ok(Some(release)) => { - let mut prompt = craft_update::StartupPrompt::new(&craft_update::suite::EFFECTCRAFT, release, env!("CARGO_PKG_VERSION")); - prompt.open = true; - app.updates_rt.prompt = Some(prompt); - ctx.request_repaint(); - } - Ok(None) => { - if app.updates_rt.manual_checked { - app.updates_rt.manual_checked = false; - app.updates_rt.status_message = Some(format!("EffectCraft {} is currently the newest version available.", env!("CARGO_PKG_VERSION"))); - app.updates_rt.manual_window_open = true; - ctx.request_repaint(); - } - } - Err(e) => { - if app.updates_rt.manual_checked { - app.updates_rt.manual_checked = false; - app.updates_rt.status_message = Some(format!("Could not check for updates:\n{e}")); - app.updates_rt.manual_window_open = true; - ctx.request_repaint(); - } - } - } + let rt = &mut app.updates_rt; + if std::mem::take(&mut rt.check_requested) { + rt.updater.check_now(ctx); + } else if rt.startup_check { + rt.updater.check_at_start(ctx); } + rt.updater.tick(ctx); } + #[cfg(target_arch = "wasm32")] + let _ = (app, ctx); } pub(crate) fn show(app: &mut EffectcraftApp, ctx: &egui::Context) { #[cfg(not(target_arch = "wasm32"))] { - let mut download_url = None; - if let Some(prompt) = &mut app.updates_rt.prompt { - if let Some(result) = app.updates_rt.update_rx.as_ref().and_then(|rx| rx.try_recv().ok()) { - app.updates_rt.update_rx = None; - match result { - // The app's close path asks about unsaved work; the restart helper relaunches once the process exits. - Ok(()) => ctx.send_viewport_cmd(egui::ViewportCommand::Close), - Err(e) => { - prompt.status_message = Some(e); - prompt.is_installing = false; - } - } - } - match prompt.show(ctx) { - craft_update::StartupPromptResponse::Pending => {} - craft_update::StartupPromptResponse::UpdateAndRestart => { - prompt.is_installing = true; - if let Some(install) = craft_update::self_update::current(&craft_update::suite::EFFECTCRAFT) { - let mut release = prompt.release.clone(); - let (tx, rx) = std::sync::mpsc::channel(); - app.updates_rt.update_rx = Some(rx); - let ctx = ctx.clone(); - std::thread::spawn(move || { - let mut run = || -> Result<(), String> { - let http = craft_update::Http::new("EffectCraft/Update").map_err(|e| format!("could not initialize HTTP: {e}"))?; - if release.assets.is_empty() - && let Some(full) = craft_update::check_for_update(&craft_update::suite::EFFECTCRAFT, &http, env!("CARGO_PKG_VERSION")) - .map_err(|e| format!("could not fetch release assets: {e}"))? - { - release = full; - } - let paths = - craft_update::Paths::for_app(&craft_update::suite::EFFECTCRAFT).map_err(|e| format!("could not find paths: {e}"))?; - let platform = craft_update::Platform::current().ok_or("unsupported platform for self update")?; - craft_update::self_update::update( - &craft_update::suite::EFFECTCRAFT, - &install, - &release, - platform, - &http, - &paths, - &craft_update::Stop::default(), - &mut |_| {}, - &|| Ok(()), - Some(env!("CARGO_PKG_VERSION")), - ) - }; - let result = run(); - if let Err(e) = &result { - log::warn!("self-update failed: {e}"); - } - let _ = tx.send(result); - ctx.request_repaint(); - }); - } else { - prompt.status_message = Some("Cannot auto-update this installation type. Please download manually.".to_string()); - prompt.is_installing = false; - } - } - craft_update::StartupPromptResponse::Download => { - download_url = Some(prompt.release.html_url.clone()); - prompt.open = false; - } - craft_update::StartupPromptResponse::Later => { - prompt.open = false; - } - } - if !prompt.open { - app.updates_rt.prompt = None; - } - } - - if let Some(url) = download_url { - app.updates_rt.prompt = None; - ctx.open_url(egui::OpenUrl::new_tab(url)); + match app.updates_rt.updater.show(ctx) { + Action::OpenUrl(url) => ctx.open_url(egui::OpenUrl::new_tab(url)), + Action::Close => ctx.send_viewport_cmd(egui::ViewportCommand::Close), + Action::None => {} } + // The window answers Help ▸ Check for Updates, so it follows the updater's status. + let rt = &mut app.updates_rt; + (rt.manual_window_open, rt.status_message) = match rt.updater.status() { + Status::Idle => (false, None), + Status::Checking => (true, None), + Status::UpToDate => (true, Some(format!("EffectCraft {} is currently the newest version available.", env!("CARGO_PKG_VERSION")))), + Status::Failed(e) => (true, Some(format!("Could not check for updates:\n{e}"))), + }; } if app.updates_rt.manual_window_open { @@ -230,4 +111,10 @@ pub(crate) fn show(app: &mut EffectcraftApp, ctx: &egui::Context) { app.updates_rt.status_message = None; } } + + // Closing the window dismisses the outcome it showed. + #[cfg(not(target_arch = "wasm32"))] + if !app.updates_rt.manual_window_open { + app.updates_rt.updater.dismiss_status(); + } } From 1be30a16073e28c76da4257f9176f5d0b7af0f4b Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sun, 25 Oct 2026 14:23:52 -0400 Subject: [PATCH 5/6] refactor(updates): drive the flow with craft-update's Updater The check at start is opt-in for the desktop app, a successful update closes through the app's own close path, and no update starts over unsaved work. --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 2 +- crates/ui-egui/src/updates_ui.rs | 4 +++- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 175f3690..2c400a50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1033,7 +1033,7 @@ dependencies = [ [[package]] name = "craft-update" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=13ceaf2f31620531f39a54f6b994d581a6082a93#13ceaf2f31620531f39a54f6b994d581a6082a93" +source = "git+https://github.com/storytold/craft-libs?rev=5b0f3c572a3b5c1e51bdb90d7ff788e0212ec1cc#5b0f3c572a3b5c1e51bdb90d7ff788e0212ec1cc" dependencies = [ "chrono", "dunce", @@ -2049,7 +2049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3423,7 +3423,7 @@ dependencies = [ "once_cell", "png", "thiserror 2.0.21", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3548,7 +3548,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4047,7 +4047,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.45.0", ] [[package]] @@ -4715,7 +4715,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4772,7 +4772,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5131,7 +5131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5447,7 +5447,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.5", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5725,7 +5725,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -6517,7 +6517,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 073e744d..ec2b469c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/storytold/effectcraft" [workspace.dependencies] craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "9f75d3dc19b4b514c4f3d4847ed1ec1b0e86b4ef", version = "=0.1.0", default-features = false, features = ["read"] } -craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "13ceaf2f31620531f39a54f6b994d581a6082a93", features = ["egui"] } +craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "5b0f3c572a3b5c1e51bdb90d7ff788e0212ec1cc", features = ["egui"] } craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "82a24b006eefa036b43c09a67fb9a3fbd17ffe63" } # internal effectcraft-time = { path = "crates/time" } diff --git a/crates/ui-egui/src/updates_ui.rs b/crates/ui-egui/src/updates_ui.rs index a93517c7..9ae6d547 100644 --- a/crates/ui-egui/src/updates_ui.rs +++ b/crates/ui-egui/src/updates_ui.rs @@ -70,7 +70,9 @@ pub(crate) fn tick(app: &mut EffectcraftApp, ctx: &egui::Context) { pub(crate) fn show(app: &mut EffectcraftApp, ctx: &egui::Context) { #[cfg(not(target_arch = "wasm32"))] { - match app.updates_rt.updater.show(ctx) { + // The restart helper is scheduled before the app closes, so no update starts over unsaved work. + let clean = !app.session.is_dirty(); + match app.updates_rt.updater.show_if(ctx, clean) { Action::OpenUrl(url) => ctx.open_url(egui::OpenUrl::new_tab(url)), Action::Close => ctx.send_viewport_cmd(egui::ViewportCommand::Close), Action::None => {} From 632162c9a76a5a682e18c80a201b2c097465f3af Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sun, 25 Oct 2026 15:34:22 -0400 Subject: [PATCH 6/6] build: pin craft-update to craft-libs main (db19c37); localize the update status messages Picks up the fix that keeps the close request pending while the app holds unsaved work, the executable-name check in the Windows update helper, and the zip 9 / dirs-free build. Part of tracking issue storytold/craft-libs#319 --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- crates/ui-egui/src/updates_ui.rs | 7 +++++-- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c400a50..09aca49c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1033,7 +1033,7 @@ dependencies = [ [[package]] name = "craft-update" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=5b0f3c572a3b5c1e51bdb90d7ff788e0212ec1cc#5b0f3c572a3b5c1e51bdb90d7ff788e0212ec1cc" +source = "git+https://github.com/storytold/craft-libs?rev=db19c373c8d59a545972ca340987aecfb3611a4b#db19c373c8d59a545972ca340987aecfb3611a4b" dependencies = [ "chrono", "dunce", @@ -2049,7 +2049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3423,7 +3423,7 @@ dependencies = [ "once_cell", "png", "thiserror 2.0.21", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3548,7 +3548,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4715,7 +4715,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4772,7 +4772,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5131,7 +5131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5447,7 +5447,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.5", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5725,7 +5725,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6517,7 +6517,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ec2b469c..beef9208 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/storytold/effectcraft" [workspace.dependencies] craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "9f75d3dc19b4b514c4f3d4847ed1ec1b0e86b4ef", version = "=0.1.0", default-features = false, features = ["read"] } -craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "5b0f3c572a3b5c1e51bdb90d7ff788e0212ec1cc", features = ["egui"] } +craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "db19c373c8d59a545972ca340987aecfb3611a4b", features = ["egui"] } craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "82a24b006eefa036b43c09a67fb9a3fbd17ffe63" } # internal effectcraft-time = { path = "crates/time" } diff --git a/crates/ui-egui/src/updates_ui.rs b/crates/ui-egui/src/updates_ui.rs index 9ae6d547..0101e16a 100644 --- a/crates/ui-egui/src/updates_ui.rs +++ b/crates/ui-egui/src/updates_ui.rs @@ -82,8 +82,11 @@ pub(crate) fn show(app: &mut EffectcraftApp, ctx: &egui::Context) { (rt.manual_window_open, rt.status_message) = match rt.updater.status() { Status::Idle => (false, None), Status::Checking => (true, None), - Status::UpToDate => (true, Some(format!("EffectCraft {} is currently the newest version available.", env!("CARGO_PKG_VERSION")))), - Status::Failed(e) => (true, Some(format!("Could not check for updates:\n{e}"))), + Status::UpToDate => ( + true, + Some(crate::i18n::tr("EffectCraft {version} is currently the newest version available.").replace("{version}", env!("CARGO_PKG_VERSION"))), + ), + Status::Failed(e) => (true, Some(crate::i18n::tr("Could not check for updates:\n{error}").replace("{error}", e))), }; }