BlueGit

golden-age paperdoll-renderer

Merge feature/render-notification (Tim t1025u)

timfa Grok Bot · pushed by timfa

Authored 2026-11-07 · 6 days ago

Hash
ec934272e539f394d083920ed30f6755df47ea18
Pipeline
Passed
7 files changed +331 −5
Cargo.lock +195 −2
@@ -629,6 +629,12 @@ version = "1.2.0"
629 629 source = "registry+https://github.com/rust-lang/crates.io-index"
630 630 checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
631 631
632 [[package]]
633 name = "deranged"
634 version = "0.5.9"
635 source = "registry+https://github.com/rust-lang/crates.io-index"
636 checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e"
637
632 638 [[package]]
633 639 name = "dispatch"
634 640 version = "0.2.0"
@@ -1242,7 +1248,7 @@ dependencies = [
1242 1248 "presser",
1243 1249 "thiserror 1.0.69",
1244 1250 "winapi",
1245 "windows",
1251 "windows 0.52.0",
1246 1252 ]
1247 1253
1248 1254 [[package]]
@@ -1623,6 +1629,20 @@ version = "0.4.35"
1623 1629 source = "registry+https://github.com/rust-lang/crates.io-index"
1624 1630 checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c"
1625 1631
1632 [[package]]
1633 name = "mac-notification-sys"
1634 version = "0.6.15"
1635 source = "registry+https://github.com/rust-lang/crates.io-index"
1636 checksum = "fd604973958ddcc11b561193c0fb96ba146506ef2f231ef2e7c35fd2cbc9beca"
1637 dependencies = [
1638 "cc",
1639 "log",
1640 "objc2 0.6.5",
1641 "objc2-foundation 0.3.2",
1642 "time",
1643 "uuid",
1644 ]
1645
1626 1646 [[package]]
1627 1647 name = "malloc_buf"
1628 1648 version = "0.0.6"
@@ -1767,6 +1787,26 @@ version = "0.2.0"
1767 1787 source = "registry+https://github.com/rust-lang/crates.io-index"
1768 1788 checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
1769 1789
1790 [[package]]
1791 name = "notify-rust"
1792 version = "4.18.2"
1793 source = "registry+https://github.com/rust-lang/crates.io-index"
1794 checksum = "4587364a9a0074333429b3df75a30a205340c56a536ca3eb6ca0e59b87bbf8af"
1795 dependencies = [
1796 "futures-lite",
1797 "log",
1798 "mac-notification-sys",
1799 "serde",
1800 "tauri-winrt-notification",
1801 "zbus",
1802 ]
1803
1804 [[package]]
1805 name = "num-conv"
1806 version = "0.2.2"
1807 source = "registry+https://github.com/rust-lang/crates.io-index"
1808 checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
1809
1770 1810 [[package]]
1771 1811 name = "num-traits"
1772 1812 version = "0.2.19"
@@ -1972,6 +2012,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1972 2012 checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
1973 2013 dependencies = [
1974 2014 "bitflags 2.13.2",
2015 "block2 0.6.2",
2016 "libc",
1975 2017 "objc2 0.6.5",
1976 2018 "objc2-core-foundation",
1977 2019 ]
@@ -2123,6 +2165,7 @@ dependencies = [
2123 2165 "glow 0.14.2",
2124 2166 "gltf",
2125 2167 "libloading",
2168 "notify-rust",
2126 2169 "png",
2127 2170 "rfd",
2128 2171 "serde",
@@ -2265,6 +2308,12 @@ dependencies = [
2265 2308 "zerovec",
2266 2309 ]
2267 2310
2311 [[package]]
2312 name = "powerfmt"
2313 version = "0.2.1"
2314 source = "registry+https://github.com/rust-lang/crates.io-index"
2315 checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958"
2316
2268 2317 [[package]]
2269 2318 name = "ppv-lite86"
2270 2319 version = "0.2.21"
@@ -2771,6 +2820,17 @@ dependencies = [
2771 2820 "syn 3.0.9",
2772 2821 ]
2773 2822
2823 [[package]]
2824 name = "tauri-winrt-notification"
2825 version = "0.8.1"
2826 source = "registry+https://github.com/rust-lang/crates.io-index"
2827 checksum = "f37a6c354fd28fc9e322ed9bd47e3959576dad28c9d58ea1cf888cce1c7ccb36"
2828 dependencies = [
2829 "thiserror 2.0.21",
2830 "windows 0.62.2",
2831 "windows-version",
2832 ]
2833
2774 2834 [[package]]
2775 2835 name = "tempfile"
2776 2836 version = "3.27.0"
@@ -2833,6 +2893,25 @@ dependencies = [
2833 2893 "syn 3.0.9",
2834 2894 ]
2835 2895
2896 [[package]]
2897 name = "time"
2898 version = "0.3.55"
2899 source = "registry+https://github.com/rust-lang/crates.io-index"
2900 checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
2901 dependencies = [
2902 "deranged",
2903 "num-conv",
2904 "powerfmt",
2905 "serde_core",
2906 "time-core",
2907 ]
2908
2909 [[package]]
2910 name = "time-core"
2911 version = "0.1.9"
2912 source = "registry+https://github.com/rust-lang/crates.io-index"
2913 checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
2914
2836 2915 [[package]]
2837 2916 name = "tinystr"
2838 2917 version = "0.8.5"
@@ -3043,6 +3122,7 @@ version = "1.29.0"
3043 3122 source = "registry+https://github.com/rust-lang/crates.io-index"
3044 3123 checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514"
3045 3124 dependencies = [
3125 "getrandom 0.4.3",
3046 3126 "js-sys",
3047 3127 "serde_core",
3048 3128 "wasm-bindgen",
@@ -3443,10 +3523,31 @@ version = "0.52.0"
3443 3523 source = "registry+https://github.com/rust-lang/crates.io-index"
3444 3524 checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
3445 3525 dependencies = [
3446 "windows-core",
3526 "windows-core 0.52.0",
3447 3527 "windows-targets",
3448 3528 ]
3449 3529
3530 [[package]]
3531 name = "windows"
3532 version = "0.62.2"
3533 source = "registry+https://github.com/rust-lang/crates.io-index"
3534 checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
3535 dependencies = [
3536 "windows-collections",
3537 "windows-core 0.62.2",
3538 "windows-future",
3539 "windows-numerics",
3540 ]
3541
3542 [[package]]
3543 name = "windows-collections"
3544 version = "0.3.2"
3545 source = "registry+https://github.com/rust-lang/crates.io-index"
3546 checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
3547 dependencies = [
3548 "windows-core 0.62.2",
3549 ]
3550
3450 3551 [[package]]
3451 3552 name = "windows-core"
3452 3553 version = "0.52.0"
@@ -3456,12 +3557,86 @@ dependencies = [
3456 3557 "windows-targets",
3457 3558 ]
3458 3559
3560 [[package]]
3561 name = "windows-core"
3562 version = "0.62.2"
3563 source = "registry+https://github.com/rust-lang/crates.io-index"
3564 checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
3565 dependencies = [
3566 "windows-implement",
3567 "windows-interface",
3568 "windows-link",
3569 "windows-result",
3570 "windows-strings",
3571 ]
3572
3573 [[package]]
3574 name = "windows-future"
3575 version = "0.3.2"
3576 source = "registry+https://github.com/rust-lang/crates.io-index"
3577 checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
3578 dependencies = [
3579 "windows-core 0.62.2",
3580 "windows-link",
3581 "windows-threading",
3582 ]
3583
3584 [[package]]
3585 name = "windows-implement"
3586 version = "0.60.2"
3587 source = "registry+https://github.com/rust-lang/crates.io-index"
3588 checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
3589 dependencies = [
3590 "proc-macro2",
3591 "quote",
3592 "syn 2.0.119",
3593 ]
3594
3595 [[package]]
3596 name = "windows-interface"
3597 version = "0.59.3"
3598 source = "registry+https://github.com/rust-lang/crates.io-index"
3599 checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
3600 dependencies = [
3601 "proc-macro2",
3602 "quote",
3603 "syn 2.0.119",
3604 ]
3605
3459 3606 [[package]]
3460 3607 name = "windows-link"
3461 3608 version = "0.2.1"
3462 3609 source = "registry+https://github.com/rust-lang/crates.io-index"
3463 3610 checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
3464 3611
3612 [[package]]
3613 name = "windows-numerics"
3614 version = "0.3.1"
3615 source = "registry+https://github.com/rust-lang/crates.io-index"
3616 checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
3617 dependencies = [
3618 "windows-core 0.62.2",
3619 "windows-link",
3620 ]
3621
3622 [[package]]
3623 name = "windows-result"
3624 version = "0.4.1"
3625 source = "registry+https://github.com/rust-lang/crates.io-index"
3626 checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
3627 dependencies = [
3628 "windows-link",
3629 ]
3630
3631 [[package]]
3632 name = "windows-strings"
3633 version = "0.5.1"
3634 source = "registry+https://github.com/rust-lang/crates.io-index"
3635 checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
3636 dependencies = [
3637 "windows-link",
3638 ]
3639
3465 3640 [[package]]
3466 3641 name = "windows-sys"
3467 3642 version = "0.52.0"
@@ -3505,6 +3680,24 @@ dependencies = [
3505 3680 "windows_x86_64_msvc",
3506 3681 ]
3507 3682
3683 [[package]]
3684 name = "windows-threading"
3685 version = "0.2.1"
3686 source = "registry+https://github.com/rust-lang/crates.io-index"
3687 checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
3688 dependencies = [
3689 "windows-link",
3690 ]
3691
3692 [[package]]
3693 name = "windows-version"
3694 version = "0.1.7"
3695 source = "registry+https://github.com/rust-lang/crates.io-index"
3696 checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631"
3697 dependencies = [
3698 "windows-link",
3699 ]
3700
3508 3701 [[package]]
3509 3702 name = "windows_aarch64_gnullvm"
3510 3703 version = "0.52.6"
@@ -629,6 +629,12 @@ version = "1.2.0"
629source = "registry+https://github.com/rust-lang/crates.io-index"
629source = "registry+https://github.com/rust-lang/crates.io-index"
630checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
630checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
631
631
632[[package]]
633name = "deranged"
634version = "0.5.9"
635source = "registry+https://github.com/rust-lang/crates.io-index"
636checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e"
637
632[[package]]
638[[package]]
633name = "dispatch"
639name = "dispatch"
634version = "0.2.0"
640version = "0.2.0"
@@ -1242,7 +1248,7 @@ dependencies = [
1242 "presser",
1248 "presser",
1243 "thiserror 1.0.69",
1249 "thiserror 1.0.69",
1244 "winapi",
1250 "winapi",
1245 "windows",
1251 "windows 0.52.0",
1246]
1252]
1247
1253
1248[[package]]
1254[[package]]
@@ -1623,6 +1629,20 @@ version = "0.4.35"
1623source = "registry+https://github.com/rust-lang/crates.io-index"
1629source = "registry+https://github.com/rust-lang/crates.io-index"
1624checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c"
1630checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c"
1625
1631
1632[[package]]
1633name = "mac-notification-sys"
1634version = "0.6.15"
1635source = "registry+https://github.com/rust-lang/crates.io-index"
1636checksum = "fd604973958ddcc11b561193c0fb96ba146506ef2f231ef2e7c35fd2cbc9beca"
1637dependencies = [
1638 "cc",
1639 "log",
1640 "objc2 0.6.5",
1641 "objc2-foundation 0.3.2",
1642 "time",
1643 "uuid",
1644]
1645
1626[[package]]
1646[[package]]
1627name = "malloc_buf"
1647name = "malloc_buf"
1628version = "0.0.6"
1648version = "0.0.6"
@@ -1767,6 +1787,26 @@ version = "0.2.0"
1767source = "registry+https://github.com/rust-lang/crates.io-index"
1787source = "registry+https://github.com/rust-lang/crates.io-index"
1768checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
1788checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
1769
1789
1790[[package]]
1791name = "notify-rust"
1792version = "4.18.2"
1793source = "registry+https://github.com/rust-lang/crates.io-index"
1794checksum = "4587364a9a0074333429b3df75a30a205340c56a536ca3eb6ca0e59b87bbf8af"
1795dependencies = [
1796 "futures-lite",
1797 "log",
1798 "mac-notification-sys",
1799 "serde",
1800 "tauri-winrt-notification",
1801 "zbus",
1802]
1803
1804[[package]]
1805name = "num-conv"
1806version = "0.2.2"
1807source = "registry+https://github.com/rust-lang/crates.io-index"
1808checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
1809
1770[[package]]
1810[[package]]
1771name = "num-traits"
1811name = "num-traits"
1772version = "0.2.19"
1812version = "0.2.19"
@@ -1972,6 +2012,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1972checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
2012checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
1973dependencies = [
2013dependencies = [
1974 "bitflags 2.13.2",
2014 "bitflags 2.13.2",
2015 "block2 0.6.2",
2016 "libc",
1975 "objc2 0.6.5",
2017 "objc2 0.6.5",
1976 "objc2-core-foundation",
2018 "objc2-core-foundation",
1977]
2019]
@@ -2123,6 +2165,7 @@ dependencies = [
2123 "glow 0.14.2",
2165 "glow 0.14.2",
2124 "gltf",
2166 "gltf",
2125 "libloading",
2167 "libloading",
2168 "notify-rust",
2126 "png",
2169 "png",
2127 "rfd",
2170 "rfd",
2128 "serde",
2171 "serde",
@@ -2265,6 +2308,12 @@ dependencies = [
2265 "zerovec",
2308 "zerovec",
2266]
2309]
2267
2310
2311[[package]]
2312name = "powerfmt"
2313version = "0.2.1"
2314source = "registry+https://github.com/rust-lang/crates.io-index"
2315checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958"
2316
2268[[package]]
2317[[package]]
2269name = "ppv-lite86"
2318name = "ppv-lite86"
2270version = "0.2.21"
2319version = "0.2.21"
@@ -2771,6 +2820,17 @@ dependencies = [
2771 "syn 3.0.9",
2820 "syn 3.0.9",
2772]
2821]
2773
2822
2823[[package]]
2824name = "tauri-winrt-notification"
2825version = "0.8.1"
2826source = "registry+https://github.com/rust-lang/crates.io-index"
2827checksum = "f37a6c354fd28fc9e322ed9bd47e3959576dad28c9d58ea1cf888cce1c7ccb36"
2828dependencies = [
2829 "thiserror 2.0.21",
2830 "windows 0.62.2",
2831 "windows-version",
2832]
2833
2774[[package]]
2834[[package]]
2775name = "tempfile"
2835name = "tempfile"
2776version = "3.27.0"
2836version = "3.27.0"
@@ -2833,6 +2893,25 @@ dependencies = [
2833 "syn 3.0.9",
2893 "syn 3.0.9",
2834]
2894]
2835
2895
2896[[package]]
2897name = "time"
2898version = "0.3.55"
2899source = "registry+https://github.com/rust-lang/crates.io-index"
2900checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
2901dependencies = [
2902 "deranged",
2903 "num-conv",
2904 "powerfmt",
2905 "serde_core",
2906 "time-core",
2907]
2908
2909[[package]]
2910name = "time-core"
2911version = "0.1.9"
2912source = "registry+https://github.com/rust-lang/crates.io-index"
2913checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
2914
2836[[package]]
2915[[package]]
2837name = "tinystr"
2916name = "tinystr"
2838version = "0.8.5"
2917version = "0.8.5"
@@ -3043,6 +3122,7 @@ version = "1.29.0"
3043source = "registry+https://github.com/rust-lang/crates.io-index"
3122source = "registry+https://github.com/rust-lang/crates.io-index"
3044checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514"
3123checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514"
3045dependencies = [
3124dependencies = [
3125 "getrandom 0.4.3",
3046 "js-sys",
3126 "js-sys",
3047 "serde_core",
3127 "serde_core",
3048 "wasm-bindgen",
3128 "wasm-bindgen",
@@ -3443,10 +3523,31 @@ version = "0.52.0"
3443source = "registry+https://github.com/rust-lang/crates.io-index"
3523source = "registry+https://github.com/rust-lang/crates.io-index"
3444checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
3524checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
3445dependencies = [
3525dependencies = [
3446 "windows-core",
3526 "windows-core 0.52.0",
3447 "windows-targets",
3527 "windows-targets",
3448]
3528]
3449
3529
3530[[package]]
3531name = "windows"
3532version = "0.62.2"
3533source = "registry+https://github.com/rust-lang/crates.io-index"
3534checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
3535dependencies = [
3536 "windows-collections",
3537 "windows-core 0.62.2",
3538 "windows-future",
3539 "windows-numerics",
3540]
3541
3542[[package]]
3543name = "windows-collections"
3544version = "0.3.2"
3545source = "registry+https://github.com/rust-lang/crates.io-index"
3546checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
3547dependencies = [
3548 "windows-core 0.62.2",
3549]
3550
3450[[package]]
3551[[package]]
3451name = "windows-core"
3552name = "windows-core"
3452version = "0.52.0"
3553version = "0.52.0"
@@ -3456,12 +3557,86 @@ dependencies = [
3456 "windows-targets",
3557 "windows-targets",
3457]
3558]
3458
3559
3560[[package]]
3561name = "windows-core"
3562version = "0.62.2"
3563source = "registry+https://github.com/rust-lang/crates.io-index"
3564checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
3565dependencies = [
3566 "windows-implement",
3567 "windows-interface",
3568 "windows-link",
3569 "windows-result",
3570 "windows-strings",
3571]
3572
3573[[package]]
3574name = "windows-future"
3575version = "0.3.2"
3576source = "registry+https://github.com/rust-lang/crates.io-index"
3577checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
3578dependencies = [
3579 "windows-core 0.62.2",
3580 "windows-link",
3581 "windows-threading",
3582]
3583
3584[[package]]
3585name = "windows-implement"
3586version = "0.60.2"
3587source = "registry+https://github.com/rust-lang/crates.io-index"
3588checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
3589dependencies = [
3590 "proc-macro2",
3591 "quote",
3592 "syn 2.0.119",
3593]
3594
3595[[package]]
3596name = "windows-interface"
3597version = "0.59.3"
3598source = "registry+https://github.com/rust-lang/crates.io-index"
3599checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
3600dependencies = [
3601 "proc-macro2",
3602 "quote",
3603 "syn 2.0.119",
3604]
3605
3459[[package]]
3606[[package]]
3460name = "windows-link"
3607name = "windows-link"
3461version = "0.2.1"
3608version = "0.2.1"
3462source = "registry+https://github.com/rust-lang/crates.io-index"
3609source = "registry+https://github.com/rust-lang/crates.io-index"
3463checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
3610checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
3464
3611
3612[[package]]
3613name = "windows-numerics"
3614version = "0.3.1"
3615source = "registry+https://github.com/rust-lang/crates.io-index"
3616checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
3617dependencies = [
3618 "windows-core 0.62.2",
3619 "windows-link",
3620]
3621
3622[[package]]
3623name = "windows-result"
3624version = "0.4.1"
3625source = "registry+https://github.com/rust-lang/crates.io-index"
3626checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
3627dependencies = [
3628 "windows-link",
3629]
3630
3631[[package]]
3632name = "windows-strings"
3633version = "0.5.1"
3634source = "registry+https://github.com/rust-lang/crates.io-index"
3635checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
3636dependencies = [
3637 "windows-link",
3638]
3639
3465[[package]]
3640[[package]]
3466name = "windows-sys"
3641name = "windows-sys"
3467version = "0.52.0"
3642version = "0.52.0"
@@ -3505,6 +3680,24 @@ dependencies = [
3505 "windows_x86_64_msvc",
3680 "windows_x86_64_msvc",
3506]
3681]
3507
3682
3683[[package]]
3684name = "windows-threading"
3685version = "0.2.1"
3686source = "registry+https://github.com/rust-lang/crates.io-index"
3687checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
3688dependencies = [
3689 "windows-link",
3690]
3691
3692[[package]]
3693name = "windows-version"
3694version = "0.1.7"
3695source = "registry+https://github.com/rust-lang/crates.io-index"
3696checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631"
3697dependencies = [
3698 "windows-link",
3699]
3700
3508[[package]]
3701[[package]]
3509name = "windows_aarch64_gnullvm"
3702name = "windows_aarch64_gnullvm"
3510version = "0.52.6"
3703version = "0.52.6"
Cargo.toml +1 −0
@@ -12,6 +12,7 @@ egui_glow = "0.29.1"
12 12 glow = "0.14.2"
13 13 gltf = { version = "1.4.1", default-features = false, features = ["import", "names", "utils", "KHR_texture_transform", "KHR_materials_specular"] }
14 14 libloading = "0.8"
15 notify-rust = { version = "4.18.2", default-features = false, features = ["z"] }
15 16 png = "0.18"
16 17 rfd = "0.15.4"
17 18 serde = { version = "1", features = ["derive"] }
@@ -12,6 +12,7 @@ egui_glow = "0.29.1"
12glow = "0.14.2"
12glow = "0.14.2"
13gltf = { version = "1.4.1", default-features = false, features = ["import", "names", "utils", "KHR_texture_transform", "KHR_materials_specular"] }
13gltf = { version = "1.4.1", default-features = false, features = ["import", "names", "utils", "KHR_texture_transform", "KHR_materials_specular"] }
14libloading = "0.8"
14libloading = "0.8"
15notify-rust = { version = "4.18.2", default-features = false, features = ["z"] }
15png = "0.18"
16png = "0.18"
16rfd = "0.15.4"
17rfd = "0.15.4"
17serde = { version = "1", features = ["derive"] }
18serde = { version = "1", features = ["derive"] }
README.md +4 −0
@@ -107,6 +107,10 @@ All clips from the source file are used. glTF interpolation is kept: LINEAR (lin
107 107
108 108 **Render all** in the Body screen renders the whole project in one queue: first the male and female body (if set), then every equipment preset for each sex it is valid for. Each item gets the full export (all clips × frames × 60 angles, four maps, atlases + `.ppda`) into its own `renders/<sex>-<settype>-atlas/`. Progress shows *item k/n* plus the normal pass count; **Cancel render** stops the queue (the running item stays resumable, the rest is marked *not started*). If an item has a matching interrupted run, it is resumed instead of restarted. Items that cannot run (no body, equipment not configured or without parts, invalid settings) are skipped with a reason and the queue continues. During Render all the live pass preview in the Body screen stays visible and follows the item currently rendering, as with a single render. Afterwards a summary lists what was rendered, skipped, failed or not started.
109 109
110 ### Notification when a render ends
111
112 When a single render or **Render all** ends (finished, failed or cancelled) while the app window does not have focus, the app shows one desktop notification with the same status line as the window (one for the whole Render all queue, not one per item). On Linux this is a freedesktop notification (D-Bus) with the app icon and the `paperdoll-renderer` desktop entry; on Windows it is a toast. Without a notification service nothing is shown and the render is not affected.
113
110 114 The body button renders only the body; **Render equipment · all angles** in the equipment editor renders the selected preset, animated on the chosen body but **without body occlusion or body pixels**. The fixed camera of that sex stays the same, also if you zoomed freely in the live preview.
111 115
112 116 Per image **only four target-res PNGs** are stored: `diffuse`, `normal`, `depth`, `specular`. Diffuse is **unlit albedo** (base color factor × texture × vertex color, alpha kept), never baked lighting/shadow/gloss. Depth is linear within a fixed depth range per sex, centered on the orbit point: `near/far = 10 m ∓ total/2`. By default the total is the T-pose arm span plus one arm length of margin on each side (arm = upper arm→hand bone distance × 1.2, span = shoulder width + 2 arms, independent of the bind pose; without recognizable arm/hand bones: span = max(width, height) and arm = 0.4·span). In the body screen, below the render frame, **Depth range** is *automatic* or a custom total in meters (`depth_range_milli` per sex in the `.ppd`), with the resulting near/far. All clips, frames, angles and equipment of that sex share the same range.
@@ -107,6 +107,10 @@ All clips from the source file are used. glTF interpolation is kept: LINEAR (lin
107
107
108**Render all** in the Body screen renders the whole project in one queue: first the male and female body (if set), then every equipment preset for each sex it is valid for. Each item gets the full export (all clips × frames × 60 angles, four maps, atlases + `.ppda`) into its own `renders/<sex>-<settype>-atlas/`. Progress shows *item k/n* plus the normal pass count; **Cancel render** stops the queue (the running item stays resumable, the rest is marked *not started*). If an item has a matching interrupted run, it is resumed instead of restarted. Items that cannot run (no body, equipment not configured or without parts, invalid settings) are skipped with a reason and the queue continues. During Render all the live pass preview in the Body screen stays visible and follows the item currently rendering, as with a single render. Afterwards a summary lists what was rendered, skipped, failed or not started.
108**Render all** in the Body screen renders the whole project in one queue: first the male and female body (if set), then every equipment preset for each sex it is valid for. Each item gets the full export (all clips × frames × 60 angles, four maps, atlases + `.ppda`) into its own `renders/<sex>-<settype>-atlas/`. Progress shows *item k/n* plus the normal pass count; **Cancel render** stops the queue (the running item stays resumable, the rest is marked *not started*). If an item has a matching interrupted run, it is resumed instead of restarted. Items that cannot run (no body, equipment not configured or without parts, invalid settings) are skipped with a reason and the queue continues. During Render all the live pass preview in the Body screen stays visible and follows the item currently rendering, as with a single render. Afterwards a summary lists what was rendered, skipped, failed or not started.
109
109
110### Notification when a render ends
111
112When a single render or **Render all** ends (finished, failed or cancelled) while the app window does not have focus, the app shows one desktop notification with the same status line as the window (one for the whole Render all queue, not one per item). On Linux this is a freedesktop notification (D-Bus) with the app icon and the `paperdoll-renderer` desktop entry; on Windows it is a toast. Without a notification service nothing is shown and the render is not affected.
113
110The body button renders only the body; **Render equipment · all angles** in the equipment editor renders the selected preset, animated on the chosen body but **without body occlusion or body pixels**. The fixed camera of that sex stays the same, also if you zoomed freely in the live preview.
114The body button renders only the body; **Render equipment · all angles** in the equipment editor renders the selected preset, animated on the chosen body but **without body occlusion or body pixels**. The fixed camera of that sex stays the same, also if you zoomed freely in the live preview.
111
115
112Per image **only four target-res PNGs** are stored: `diffuse`, `normal`, `depth`, `specular`. Diffuse is **unlit albedo** (base color factor × texture × vertex color, alpha kept), never baked lighting/shadow/gloss. Depth is linear within a fixed depth range per sex, centered on the orbit point: `near/far = 10 m ∓ total/2`. By default the total is the T-pose arm span plus one arm length of margin on each side (arm = upper arm→hand bone distance × 1.2, span = shoulder width + 2 arms, independent of the bind pose; without recognizable arm/hand bones: span = max(width, height) and arm = 0.4·span). In the body screen, below the render frame, **Depth range** is *automatic* or a custom total in meters (`depth_range_milli` per sex in the `.ppd`), with the resulting near/far. All clips, frames, angles and equipment of that sex share the same range.
116Per image **only four target-res PNGs** are stored: `diffuse`, `normal`, `depth`, `specular`. Diffuse is **unlit albedo** (base color factor × texture × vertex color, alpha kept), never baked lighting/shadow/gloss. Depth is linear within a fixed depth range per sex, centered on the orbit point: `near/far = 10 m ∓ total/2`. By default the total is the T-pose arm span plus one arm length of margin on each side (arm = upper arm→hand bone distance × 1.2, span = shoulder width + 2 arms, independent of the bind pose; without recognizable arm/hand bones: span = max(width, height) and arm = 0.4·span). In the body screen, below the render frame, **Depth range** is *automatic* or a custom total in meters (`depth_range_milli` per sex in the `.ppd`), with the resulting near/far. All clips, frames, angles and equipment of that sex share the same range.
src/editor.rs +43 −3
@@ -68,6 +68,8 @@ pub struct EditorApp {
68 68 resume_scan_error: Option<String>,
69 69 render_all: Option<RenderAll>,
70 70 render_all_summary: Option<RenderAllSummary>,
71 window_focused: bool,
72 last_toast: Option<crate::notify::Toast>,
71 73 }
72 74
73 75 /// One queued item of **Render alles**: a body (0 = male, 1 = female) with
@@ -160,6 +162,8 @@ impl EditorApp {
160 162 resume_scan_error: None,
161 163 render_all: None,
162 164 render_all_summary: None,
165 window_focused: true,
166 last_toast: None,
163 167 }
164 168 }
165 169
@@ -866,6 +870,20 @@ impl EditorApp {
866 870 self.session.notice = summary.text();
867 871 self.session.notice_ok = summary.failed.is_empty() && !summary.cancelled;
868 872 self.render_all_summary = Some(summary);
873 self.notify_render_end();
874 }
875 /// One desktop notification per finished run when the window is unfocused.
876 fn notify_render_end(&mut self) {
877 let outcome = crate::notify::RenderOutcome::Done {
878 ok: self.session.notice_ok,
879 text: self.session.notice.clone(),
880 };
881 if let Some(toast) = crate::notify::toast_for(&outcome, self.window_focused) {
882 self.last_toast = Some(toast.clone());
883 if !cfg!(test) {
884 crate::notify::show(toast);
885 }
886 }
869 887 }
870 888 fn render_all_ui(&mut self, ui: &mut egui::Ui) {
871 889 if let Some(state) = &self.render_all {
@@ -1043,9 +1061,12 @@ impl EditorApp {
1043 1061 self.render_worker = None;
1044 1062 if self.render_all.is_some() {
1045 1063 self.render_all_item_done();
1046 } else if self.render_ui.saved.is_none() && self.render_ui.error.is_none() {
1047 // Cancelled: offer the run for resumption right away.
1048 self.scan_resumable_runs();
1064 } else {
1065 if self.render_ui.saved.is_none() && self.render_ui.error.is_none() {
1066 // Cancelled: offer the run for resumption right away.
1067 self.scan_resumable_runs();
1068 }
1069 self.notify_render_end();
1049 1070 }
1050 1071 } else if ended {
1051 1072 self.render_ui.apply(RenderEvent::Failed(
@@ -1327,6 +1348,7 @@ impl eframe::App for EditorApp {
1327 1348 {
1328 1349 ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);
1329 1350 }
1351 self.window_focused = ctx.input(|i| i.focused);
1330 1352 self.poll_dialogs();
1331 1353 self.drain_render();
1332 1354 if self.render_ui.running
@@ -2847,6 +2869,24 @@ mod tests {
2847 2869 let _ = std::fs::remove_dir_all(root);
2848 2870 }
2849 2871
2872 #[test]
2873 fn render_all_end_toasts_once_only_when_unfocused() {
2874 let root = root();
2875 let opened = crate::session::create_seeded_project(&root).unwrap();
2876 let mut app = EditorApp::default();
2877 app.open_path(opened.manifest_path.clone());
2878 app.window_focused = true;
2879 app.start_render_all();
2880 pump(&mut app);
2881 assert!(app.last_toast.is_none(), "focused: no toast");
2882 app.window_focused = false;
2883 app.start_render_all();
2884 pump(&mut app);
2885 let toast = app.last_toast.clone().expect("unfocused: toast");
2886 assert!(toast.body.contains(" 2 "), "{toast:?}");
2887 let _ = std::fs::remove_dir_all(root);
2888 }
2889
2850 2890 #[test]
2851 2891 fn depth_format_and_range_settings_reach_job_and_persist() {
2852 2892 let root = root();
@@ -68,6 +68,8 @@ pub struct EditorApp {
68 resume_scan_error: Option<String>,
68 resume_scan_error: Option<String>,
69 render_all: Option<RenderAll>,
69 render_all: Option<RenderAll>,
70 render_all_summary: Option<RenderAllSummary>,
70 render_all_summary: Option<RenderAllSummary>,
71 window_focused: bool,
72 last_toast: Option<crate::notify::Toast>,
71}
73}
72
74
73/// One queued item of **Render alles**: a body (0 = male, 1 = female) with
75/// One queued item of **Render alles**: a body (0 = male, 1 = female) with
@@ -160,6 +162,8 @@ impl EditorApp {
160 resume_scan_error: None,
162 resume_scan_error: None,
161 render_all: None,
163 render_all: None,
162 render_all_summary: None,
164 render_all_summary: None,
165 window_focused: true,
166 last_toast: None,
163 }
167 }
164 }
168 }
165
169
@@ -866,6 +870,20 @@ impl EditorApp {
866 self.session.notice = summary.text();
870 self.session.notice = summary.text();
867 self.session.notice_ok = summary.failed.is_empty() && !summary.cancelled;
871 self.session.notice_ok = summary.failed.is_empty() && !summary.cancelled;
868 self.render_all_summary = Some(summary);
872 self.render_all_summary = Some(summary);
873 self.notify_render_end();
874 }
875 /// One desktop notification per finished run when the window is unfocused.
876 fn notify_render_end(&mut self) {
877 let outcome = crate::notify::RenderOutcome::Done {
878 ok: self.session.notice_ok,
879 text: self.session.notice.clone(),
880 };
881 if let Some(toast) = crate::notify::toast_for(&outcome, self.window_focused) {
882 self.last_toast = Some(toast.clone());
883 if !cfg!(test) {
884 crate::notify::show(toast);
885 }
886 }
869 }
887 }
870 fn render_all_ui(&mut self, ui: &mut egui::Ui) {
888 fn render_all_ui(&mut self, ui: &mut egui::Ui) {
871 if let Some(state) = &self.render_all {
889 if let Some(state) = &self.render_all {
@@ -1043,9 +1061,12 @@ impl EditorApp {
1043 self.render_worker = None;
1061 self.render_worker = None;
1044 if self.render_all.is_some() {
1062 if self.render_all.is_some() {
1045 self.render_all_item_done();
1063 self.render_all_item_done();
1046 } else if self.render_ui.saved.is_none() && self.render_ui.error.is_none() {
1064 } else {
1047 // Cancelled: offer the run for resumption right away.
1065 if self.render_ui.saved.is_none() && self.render_ui.error.is_none() {
1048 self.scan_resumable_runs();
1066 // Cancelled: offer the run for resumption right away.
1067 self.scan_resumable_runs();
1068 }
1069 self.notify_render_end();
1049 }
1070 }
1050 } else if ended {
1071 } else if ended {
1051 self.render_ui.apply(RenderEvent::Failed(
1072 self.render_ui.apply(RenderEvent::Failed(
@@ -1327,6 +1348,7 @@ impl eframe::App for EditorApp {
1327 {
1348 {
1328 ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);
1349 ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);
1329 }
1350 }
1351 self.window_focused = ctx.input(|i| i.focused);
1330 self.poll_dialogs();
1352 self.poll_dialogs();
1331 self.drain_render();
1353 self.drain_render();
1332 if self.render_ui.running
1354 if self.render_ui.running
@@ -2847,6 +2869,24 @@ mod tests {
2847 let _ = std::fs::remove_dir_all(root);
2869 let _ = std::fs::remove_dir_all(root);
2848 }
2870 }
2849
2871
2872 #[test]
2873 fn render_all_end_toasts_once_only_when_unfocused() {
2874 let root = root();
2875 let opened = crate::session::create_seeded_project(&root).unwrap();
2876 let mut app = EditorApp::default();
2877 app.open_path(opened.manifest_path.clone());
2878 app.window_focused = true;
2879 app.start_render_all();
2880 pump(&mut app);
2881 assert!(app.last_toast.is_none(), "focused: no toast");
2882 app.window_focused = false;
2883 app.start_render_all();
2884 pump(&mut app);
2885 let toast = app.last_toast.clone().expect("unfocused: toast");
2886 assert!(toast.body.contains(" 2 "), "{toast:?}");
2887 let _ = std::fs::remove_dir_all(root);
2888 }
2889
2850 #[test]
2890 #[test]
2851 fn depth_format_and_range_settings_reach_job_and_persist() {
2891 fn depth_format_and_range_settings_reach_job_and_persist() {
2852 let root = root();
2892 let root = root();
src/i18n_table.rs +2 −0
@@ -160,6 +160,8 @@ pub const TABLE: &[(&str, &str)] = &[
160 160 ("{} · {} · {} van {} PNG's vastgelegd", "{} · {} · {} of {} PNGs recorded"),
161 161 ("Glow-context ontbreekt", "Glow context missing"),
162 162 ("Batch opgeslagen: {path}", "Batch saved: {path}"),
163 ("Render klaar", "Render finished"),
164 ("Render niet gelukt", "Render did not succeed"),
163 165 ("De renderworker stopte zonder eindresultaat.", "The render worker stopped without a final result."),
164 166 ("Annuleer render", "Cancel render"),
165 167 ("Vaste renderhoogte (m)", "Fixed render height (m)"),
@@ -160,6 +160,8 @@ pub const TABLE: &[(&str, &str)] = &[
160 ("{} · {} · {} van {} PNG's vastgelegd", "{} · {} · {} of {} PNGs recorded"),
160 ("{} · {} · {} van {} PNG's vastgelegd", "{} · {} · {} of {} PNGs recorded"),
161 ("Glow-context ontbreekt", "Glow context missing"),
161 ("Glow-context ontbreekt", "Glow context missing"),
162 ("Batch opgeslagen: {path}", "Batch saved: {path}"),
162 ("Batch opgeslagen: {path}", "Batch saved: {path}"),
163 ("Render klaar", "Render finished"),
164 ("Render niet gelukt", "Render did not succeed"),
163 ("De renderworker stopte zonder eindresultaat.", "The render worker stopped without a final result."),
165 ("De renderworker stopte zonder eindresultaat.", "The render worker stopped without a final result."),
164 ("Annuleer render", "Cancel render"),
166 ("Annuleer render", "Cancel render"),
165 ("Vaste renderhoogte (m)", "Fixed render height (m)"),
167 ("Vaste renderhoogte (m)", "Fixed render height (m)"),
src/lib.rs +1 −0
@@ -26,6 +26,7 @@ pub mod i18n;
26 26 pub mod i18n_table;
27 27 pub mod icon;
28 28 pub mod import;
29 pub mod notify;
29 30 pub mod preview;
30 31 pub mod preview2d;
31 32 pub mod preview_gl;
@@ -26,6 +26,7 @@ pub mod i18n;
26pub mod i18n_table;
26pub mod i18n_table;
27pub mod icon;
27pub mod icon;
28pub mod import;
28pub mod import;
29pub mod notify;
29pub mod preview;
30pub mod preview;
30pub mod preview2d;
31pub mod preview2d;
31pub mod preview_gl;
32pub mod preview_gl;
src/notify.rs added +85 −0
@@ -0,0 +1,85 @@
1 //! Desktop notification when a render ends while the window is unfocused
2 //! (Tim t1025u). Freedesktop notifications on Linux/BSD, a toast on Windows.
3
4 /// How a single render or the Render Alles queue ended.
5 #[derive(Clone, Debug, PartialEq, Eq)]
6 pub enum RenderOutcome {
7 /// One render (or the whole Render Alles queue) finished; `text` is the
8 /// status line also shown in the window.
9 Done { ok: bool, text: String },
10 }
11
12 /// The toast to show, if any.
13 #[derive(Clone, Debug, PartialEq, Eq)]
14 pub struct Toast {
15 pub summary: String,
16 pub body: String,
17 }
18
19 /// Decide whether to notify: only when the window is not focused.
20 pub fn toast_for(outcome: &RenderOutcome, window_focused: bool) -> Option<Toast> {
21 if window_focused {
22 return None;
23 }
24 let RenderOutcome::Done { ok, text } = outcome;
25 let summary = if *ok {
26 crate::i18n::t("Render klaar")
27 } else {
28 crate::i18n::t("Render niet gelukt")
29 };
30 Some(Toast {
31 summary,
32 body: crate::i18n::t(text),
33 })
34 }
35
36 /// Show the toast on a short-lived thread; delivery errors are ignored.
37 pub fn show(toast: Toast) {
38 let _ = std::thread::Builder::new()
39 .name("render-notification".into())
40 .spawn(move || {
41 let mut n = notify_rust::Notification::new();
42 n.summary(&["Paperdoll Renderer", &toast.summary].join(": "))
43 .body(&toast.body)
44 .appname("Paperdoll Renderer");
45 #[cfg(all(unix, not(target_os = "macos")))]
46 {
47 n.icon(crate::icon::APP_ID)
48 .hint(notify_rust::Hint::DesktopEntry(crate::icon::APP_ID.into()));
49 }
50 let _ = n.show();
51 });
52 }
53
54 #[cfg(test)]
55 mod tests {
56 use super::*;
57
58 #[test]
59 fn focused_window_gets_no_toast() {
60 let o = RenderOutcome::Done {
61 ok: true,
62 text: "Batch opgeslagen: x".into(),
63 };
64 assert_eq!(toast_for(&o, true), None);
65 }
66
67 #[test]
68 fn unfocused_window_gets_one_toast_with_status() {
69 let o = RenderOutcome::Done {
70 ok: true,
71 text: "Batch opgeslagen: /p/out".into(),
72 };
73 let t = toast_for(&o, false).unwrap();
74 assert!(t.body.contains("/p/out"));
75 let f = toast_for(
76 &RenderOutcome::Done {
77 ok: false,
78 text: "boom".into(),
79 },
80 false,
81 )
82 .unwrap();
83 assert_ne!(t.summary, f.summary);
84 }
85 }
@@ -0,0 +1,85 @@
1//! Desktop notification when a render ends while the window is unfocused
2//! (Tim t1025u). Freedesktop notifications on Linux/BSD, a toast on Windows.
3
4/// How a single render or the Render Alles queue ended.
5#[derive(Clone, Debug, PartialEq, Eq)]
6pub enum RenderOutcome {
7 /// One render (or the whole Render Alles queue) finished; `text` is the
8 /// status line also shown in the window.
9 Done { ok: bool, text: String },
10}
11
12/// The toast to show, if any.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Toast {
15 pub summary: String,
16 pub body: String,
17}
18
19/// Decide whether to notify: only when the window is not focused.
20pub fn toast_for(outcome: &RenderOutcome, window_focused: bool) -> Option<Toast> {
21 if window_focused {
22 return None;
23 }
24 let RenderOutcome::Done { ok, text } = outcome;
25 let summary = if *ok {
26 crate::i18n::t("Render klaar")
27 } else {
28 crate::i18n::t("Render niet gelukt")
29 };
30 Some(Toast {
31 summary,
32 body: crate::i18n::t(text),
33 })
34}
35
36/// Show the toast on a short-lived thread; delivery errors are ignored.
37pub fn show(toast: Toast) {
38 let _ = std::thread::Builder::new()
39 .name("render-notification".into())
40 .spawn(move || {
41 let mut n = notify_rust::Notification::new();
42 n.summary(&["Paperdoll Renderer", &toast.summary].join(": "))
43 .body(&toast.body)
44 .appname("Paperdoll Renderer");
45 #[cfg(all(unix, not(target_os = "macos")))]
46 {
47 n.icon(crate::icon::APP_ID)
48 .hint(notify_rust::Hint::DesktopEntry(crate::icon::APP_ID.into()));
49 }
50 let _ = n.show();
51 });
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn focused_window_gets_no_toast() {
60 let o = RenderOutcome::Done {
61 ok: true,
62 text: "Batch opgeslagen: x".into(),
63 };
64 assert_eq!(toast_for(&o, true), None);
65 }
66
67 #[test]
68 fn unfocused_window_gets_one_toast_with_status() {
69 let o = RenderOutcome::Done {
70 ok: true,
71 text: "Batch opgeslagen: /p/out".into(),
72 };
73 let t = toast_for(&o, false).unwrap();
74 assert!(t.body.contains("/p/out"));
75 let f = toast_for(
76 &RenderOutcome::Done {
77 ok: false,
78 text: "boom".into(),
79 },
80 false,
81 )
82 .unwrap();
83 assert_ne!(t.summary, f.summary);
84 }
85}