diff --git a/Cargo.lock b/Cargo.lock index 5ca63a6..2079127 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,53 +20,53 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "accesskit" -version = "0.16.3" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b76d84ee70e30a4a7e39ab9018e2b17a6a09e31084176cc7c0b2dec036ba45" +checksum = "cf203f9d3bd8f29f98833d1fbef628df18f759248a547e7e01cfbf63cda36a99" [[package]] name = "accesskit_atspi_common" -version = "0.9.3" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5393c75d4666f580f4cac0a968bc97c36076bb536a129f28210dac54ee127ed" +checksum = "890d241cf51fc784f0ac5ac34dfc847421f8d39da6c7c91a0fcc987db62a8267" dependencies = [ "accesskit", "accesskit_consumer", "atspi-common", "serde", "thiserror 1.0.69", - "zvariant 4.2.0", + "zvariant", ] [[package]] name = "accesskit_consumer" -version = "0.24.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a12dc159d52233c43d9fe5415969433cbdd52c3d6e0df51bda7d447427b9986" +checksum = "db81010a6895d8707f9072e6ce98070579b43b717193d2614014abd5cb17dd43" dependencies = [ "accesskit", - "immutable-chunkmap", + "hashbrown 0.15.5", ] [[package]] name = "accesskit_macos" -version = "0.17.4" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc6c1ecd82053d127961ad80a8beaa6004fb851a3a5b96506d7a6bd462403f6" +checksum = "a0089e5c0ac0ca281e13ea374773898d9354cc28d15af9f0f7394d44a495b575" dependencies = [ "accesskit", "accesskit_consumer", + "hashbrown 0.15.5", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", - "once_cell", ] [[package]] name = "accesskit_unix" -version = "0.12.3" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be7f5cf6165be10a54b2655fa2e0e12b2509f38ed6fc43e11c31fdb7ee6230bb" +checksum = "301e55b39cfc15d9c48943ce5f572204a551646700d0e8efa424585f94fec528" dependencies = [ "accesskit", "accesskit_atspi_common", @@ -77,28 +77,28 @@ dependencies = [ "futures-lite", "futures-util", "serde", - "zbus 4.4.0", + "zbus", ] [[package]] name = "accesskit_windows" -version = "0.23.2" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "974e96c347384d9133427167fb8a58c340cb0496988dacceebdc1ed27071023b" +checksum = "d2d63dd5041e49c363d83f5419a896ecb074d309c414036f616dc0b04faca971" dependencies = [ "accesskit", "accesskit_consumer", - "paste", + "hashbrown 0.15.5", "static_assertions", - "windows 0.58.0", - "windows-core 0.58.0", + "windows 0.61.3", + "windows-core 0.61.2", ] [[package]] name = "accesskit_winit" -version = "0.22.4" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aea3522719f1c44564d03e9469a8e2f3a98b3a8a880bd66d0789c6b9c4a669dd" +checksum = "c8cfabe59d0eaca7412bfb1f70198dd31e3b0496fee7e15b066f9c36a1a140a0" dependencies = [ "accesskit", "accesskit_macos", @@ -160,7 +160,7 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "thiserror 1.0.69", ] @@ -196,9 +196,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" dependencies = [ "clipboard-win", + "image", "log", "objc2 0.6.4", "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", "objc2-foundation 0.3.2", "parking_lot", "percent-encoding", @@ -249,28 +252,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "ashpd" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" -dependencies = [ - "async-fs", - "async-net", - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.9.2", - "raw-window-handle", - "serde", - "serde_repr", - "url", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "zbus 5.14.0", -] - [[package]] name = "async-broadcast" version = "0.7.2" @@ -309,17 +290,6 @@ dependencies = [ "slab", ] -[[package]] -name = "async-fs" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - [[package]] name = "async-io" version = "2.6.0" @@ -349,17 +319,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - [[package]] name = "async-process" version = "2.5.0" @@ -432,9 +391,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atspi" -version = "0.22.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be534b16650e35237bb1ed189ba2aab86ce65e88cc84c66f4935ba38575cecbf" +checksum = "c83247582e7508838caf5f316c00791eee0e15c0bf743e6880585b867e16815c" dependencies = [ "atspi-common", "atspi-connection", @@ -443,42 +402,41 @@ dependencies = [ [[package]] name = "atspi-common" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1909ed2dc01d0a17505d89311d192518507e8a056a48148e3598fef5e7bb6ba7" +checksum = "33dfc05e7cdf90988a197803bf24f5788f94f7c94a69efa95683e8ffe76cfdfb" dependencies = [ "enumflags2", "serde", "static_assertions", - "zbus 4.4.0", + "zbus", "zbus-lockstep", "zbus-lockstep-macros", - "zbus_names 3.0.0", - "zvariant 4.2.0", + "zbus_names", + "zvariant", ] [[package]] name = "atspi-connection" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430c5960624a4baaa511c9c0fcc2218e3b58f5dbcc47e6190cafee344b873333" +checksum = "4193d51303d8332304056ae0004714256b46b6635a5c556109b319c0d3784938" dependencies = [ "atspi-common", "atspi-proxies", "futures-lite", - "zbus 4.4.0", + "zbus", ] [[package]] name = "atspi-proxies" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496" +checksum = "d2eebcb9e7e76f26d0bcfd6f0295e1cd1e6f33bedbc5698a971db8dc43d7751c" dependencies = [ "atspi-common", "serde", - "zbus 4.4.0", - "zvariant 4.2.0", + "zbus", ] [[package]] @@ -487,30 +445,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "bit-set" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" -dependencies = [ - "bit-vec 0.7.0", -] - [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec 0.8.0", + "bit-vec", ] -[[package]] -name = "bit-vec" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" - [[package]] name = "bit-vec" version = "0.8.0" @@ -547,15 +490,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[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" @@ -729,12 +663,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "cfg_aliases" version = "0.2.1" @@ -771,43 +699,13 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ + "serde", "termcolor", - "unicode-width 0.1.14", -] - -[[package]] -name = "com" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" -dependencies = [ - "com_macros", -] - -[[package]] -name = "com_macros" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" -dependencies = [ - "com_macros_support", - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "com_macros_support" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "unicode-width 0.2.2", ] [[package]] @@ -875,7 +773,7 @@ checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", - "core-graphics-types", + "core-graphics-types 0.1.3", "foreign-types", "libc", ] @@ -892,11 +790,13 @@ dependencies = [ ] [[package]] -name = "cpufeatures" -version = "0.2.17" +name = "core-graphics-types" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.10.1", "libc", ] @@ -966,14 +866,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "crypto-common" -version = "0.1.7" +name = "crunchy" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cursor-icon" @@ -981,16 +877,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" -[[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 = "dispatch" version = "0.2.0" @@ -1052,9 +938,9 @@ checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "ecolor" -version = "0.29.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775cfde491852059e386c4e1deb4aef381c617dc364184c6f6afee99b87c402b" +checksum = "71ddb8ac7643d1dba1bb02110e804406dd459a838efcb14011ced10556711a8e" dependencies = [ "bytemuck", "emath", @@ -1062,9 +948,9 @@ dependencies = [ [[package]] name = "eframe" -version = "0.29.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ac2645a9bf4826eb4e91488b1f17b8eaddeef09396706b2f14066461338e24f" +checksum = "457481173e6db5ca9fa2be93a58df8f4c7be639587aeb4853b526c6cf87db4e6" dependencies = [ "ahash 0.8.12", "bytemuck", @@ -1073,7 +959,7 @@ dependencies = [ "egui-wgpu", "egui-winit", "egui_glow", - "glow 0.14.2", + "glow", "glutin", "glutin-winit", "image", @@ -1084,14 +970,14 @@ dependencies = [ "objc2-foundation 0.2.2", "parking_lot", "percent-encoding", + "profiling", "raw-window-handle", "static_assertions", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", "web-time", - "winapi", - "windows-sys 0.52.0", + "windows-sys 0.61.2", "winit", ] @@ -1127,23 +1013,27 @@ dependencies = [ [[package]] name = "egui" -version = "0.29.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53eafabcce0cb2325a59a98736efe0bf060585b437763f8c476957fb274bb974" +checksum = "6a9b567d356674e9a5121ed3fedfb0a7c31e059fe71f6972b691bcd0bfc284e3" dependencies = [ "accesskit", "ahash 0.8.12", + "bitflags 2.10.0", "emath", "epaint", "log", "nohash-hasher", + "profiling", + "smallvec", + "unicode-segmentation", ] [[package]] name = "egui-wgpu" -version = "0.29.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00fd5d06d8405397e64a928fa0ef3934b3c30273ea7603e3dc4627b1f7a1a82" +checksum = "5e4d209971c84b2352a06174abdba701af1e552ce56b144d96f2bd50a3c91236" dependencies = [ "ahash 0.8.12", "bytemuck", @@ -1151,7 +1041,8 @@ dependencies = [ "egui", "epaint", "log", - "thiserror 1.0.69", + "profiling", + "thiserror 2.0.18", "type-map", "web-time", "wgpu", @@ -1160,15 +1051,19 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.29.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9c430f4f816340e8e8c1b20eec274186b1be6bc4c7dfc467ed50d57abc36c6" +checksum = "ec6687e5bb551702f4ad10ac428bab12acf9d53047ebb1082d4a0ed8c6251a29" dependencies = [ "accesskit_winit", - "ahash 0.8.12", "arboard", + "bytemuck", "egui", "log", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-ui-kit", + "profiling", "raw-window-handle", "smithay-clipboard", "web-time", @@ -1178,29 +1073,30 @@ dependencies = [ [[package]] name = "egui_extras" -version = "0.29.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3c1f5cd8dfe2ade470a218696c66cf556fcfd701e7830fa2e9f4428292a2a1" +checksum = "d01d34e845f01c62e3fded726961092e70417d66570c499b9817ab24674ca4ed" dependencies = [ "ahash 0.8.12", "egui", "enum-map", "log", "mime_guess2", + "profiling", ] [[package]] name = "egui_glow" -version = "0.29.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e39bccc683cd43adab530d8f21a13eb91e80de10bcc38c3f1c16601b6f62b26" +checksum = "6420863ea1d90e750f75075231a260030ad8a9f30a7cef82cdc966492dc4c4eb" dependencies = [ - "ahash 0.8.12", "bytemuck", "egui", - "glow 0.14.2", + "glow", "log", "memoffset", + "profiling", "wasm-bindgen", "web-sys", "winit", @@ -1214,9 +1110,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "emath" -version = "0.29.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fe0049ce51d0fb414d029e668dd72eb30bc2b739bf34296ed97bd33df544f3" +checksum = "491bdf728bf25ddd9ad60d4cf1c48588fa82c013a2440b91aa7fc43e34a07c32" dependencies = [ "bytemuck", ] @@ -1246,7 +1142,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" dependencies = [ "enum-map-derive", - "serde", ] [[package]] @@ -1283,9 +1178,9 @@ dependencies = [ [[package]] name = "epaint" -version = "0.29.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a32af8da821bd4f43f2c137e295459ee2e1661d87ca8779dfa0eaf45d870e20f" +checksum = "009d0dd3c2163823a0abdb899451ecbc78798dec545ee91b43aff1fa790bab62" dependencies = [ "ab_glyph", "ahash 0.8.12", @@ -1296,13 +1191,14 @@ dependencies = [ "log", "nohash-hasher", "parking_lot", + "profiling", ] [[package]] name = "epaint_default_fonts" -version = "0.29.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483440db0b7993cf77a20314f08311dbe95675092405518c0677aa08c151a3ea" +checksum = "5c4fbe202b6578d3d56428fa185cdf114a05e49da05f477b3c7f0fbb221f1862" [[package]] name = "equivalent" @@ -1353,6 +1249,26 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "fd-lock" version = "4.0.4" @@ -1401,6 +1317,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.5.0" @@ -1443,15 +1365,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", -] - [[package]] name = "futures-core" version = "0.3.32" @@ -1488,12 +1401,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - [[package]] name = "futures-task" version = "0.3.32" @@ -1507,25 +1414,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", - "futures-io", "futures-macro", - "futures-sink", "futures-task", - "memchr", "pin-project-lite", "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 = "geolog" version = "0.1.0" @@ -1561,7 +1455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.2", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -1600,21 +1494,9 @@ dependencies = [ [[package]] name = "glow" -version = "0.13.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "glow" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" dependencies = [ "js-sys", "slotmap", @@ -1629,7 +1511,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12124de845cacfebedff80e877bb37b5b75c34c5a4c89e47e1cdd67fb6041325" dependencies = [ "bitflags 2.10.0", - "cfg_aliases 0.2.1", + "cfg_aliases", "cgl", "dispatch2", "glutin_egl_sys", @@ -1653,7 +1535,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases", "glutin", "raw-window-handle", "winit", @@ -1709,15 +1591,14 @@ dependencies = [ [[package]] name = "gpu-allocator" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd4240fc91d3433d5e5b0fc5b67672d771850dc19bbee03c1381e19322803d7" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" dependencies = [ "log", "presser", "thiserror 1.0.69", - "winapi", - "windows 0.52.0", + "windows 0.58.0", ] [[package]] @@ -1740,6 +1621,18 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1765,7 +1658,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1773,20 +1666,8 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "hassle-rs" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" dependencies = [ - "bitflags 2.10.0", - "com", - "libc", - "libloading", - "thiserror 1.0.69", - "widestring", - "winapi", + "foldhash 0.2.0", ] [[package]] @@ -1929,15 +1810,7 @@ dependencies = [ "moxcms", "num-traits", "png", -] - -[[package]] -name = "immutable-chunkmap" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3e98b1520e49e252237edc238a39869da9f3241f2ec19dc788c1d24694d1e4" -dependencies = [ - "arrayvec", + "tiff", ] [[package]] @@ -2010,7 +1883,7 @@ dependencies = [ "simd_cesu8", "thiserror 2.0.18", "walkdir", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2101,9 +1974,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link", + "windows-link 0.2.1", ] +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "libredox" version = "0.1.14" @@ -2190,13 +2069,13 @@ dependencies = [ [[package]] name = "metal" -version = "0.29.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ "bitflags 2.10.0", "block", - "core-graphics-types", + "core-graphics-types 0.2.0", "foreign-types", "log", "objc", @@ -2243,23 +2122,28 @@ dependencies = [ [[package]] name = "naga" -version = "22.1.0" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad" +checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" dependencies = [ "arrayvec", - "bit-set 0.6.0", + "bit-set", "bitflags 2.10.0", - "cfg_aliases 0.1.1", + "cfg-if", + "cfg_aliases", "codespan-reporting", + "half", + "hashbrown 0.16.1", "hexf-parse", "indexmap 2.12.1", + "libm", "log", + "num-traits", + "once_cell", "rustc-hash 1.1.0", "spirv", - "termcolor", - "thiserror 1.0.69", - "unicode-xid", + "thiserror 2.0.18", + "unicode-ident", ] [[package]] @@ -2271,7 +2155,7 @@ dependencies = [ "bitflags 2.10.0", "jni-sys 0.3.0", "log", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "raw-window-handle", "thiserror 1.0.69", @@ -2283,15 +2167,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" -[[package]] -name = "ndk-sys" -version = "0.5.0+25.2.9519653" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" -dependencies = [ - "jni-sys 0.3.0", -] - [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -2318,9 +2193,8 @@ checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.10.0", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases", "libc", - "memoffset", ] [[package]] @@ -2342,6 +2216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2672,6 +2547,15 @@ dependencies = [ "libredox", ] +[[package]] +name = "ordered-float" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -2717,7 +2601,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2858,6 +2742,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -2912,12 +2811,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ - "bit-set 0.8.0", - "bit-vec 0.8.0", + "bit-set", + "bit-vec", "bitflags 2.10.0", "num-traits", "rand 0.9.2", - "rand_chacha 0.9.0", + "rand_chacha", "rand_xorshift", "regex-syntax", "rusty-fork", @@ -2968,10 +2867,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] -name = "quick-xml" -version = "0.30.0" +name = "quick-error" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", "serde", @@ -3023,8 +2928,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "libc", - "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -3034,20 +2937,10 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.3", ] -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - [[package]] name = "rand_chacha" version = "0.9.0" @@ -3063,9 +2956,6 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] [[package]] name = "rand_core" @@ -3085,6 +2975,12 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -3161,26 +3057,29 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "rfd" -version = "0.15.4" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" +checksum = "20dafead71c16a34e1ff357ddefc8afc11e7d51d6d2b9fbd07eaa48e3e540220" dependencies = [ - "ashpd", "block2 0.6.2", "dispatch2", "js-sys", + "libc", "log", "objc2 0.6.4", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", + "percent-encoding", "pollster", "raw-window-handle", - "urlencoding", "wasm-bindgen", "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", "web-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3283,7 +3182,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", - "quick-error", + "quick-error 1.2.3", "tempfile", "wait-timeout", ] @@ -3406,17 +3305,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" @@ -3701,6 +3589,20 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error 2.0.1", + "weezl", + "zune-jpeg", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -3869,12 +3771,6 @@ dependencies = [ "rustc-hash 2.1.1", ] -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - [[package]] name = "uds_windows" version = "1.2.1" @@ -3922,12 +3818,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "url" version = "2.5.8" @@ -3938,15 +3828,8 @@ dependencies = [ "idna", "percent-encoding", "serde", - "serde_derive", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -4241,17 +4124,28 @@ dependencies = [ ] [[package]] -name = "wgpu" -version = "22.1.0" +name = "weezl" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d1c4ba43f80542cf63a0a6ed3134629ae73e8ab51e4b765a67f3aa062eb433" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wgpu" +version = "27.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" dependencies = [ "arrayvec", - "cfg_aliases 0.1.1", + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", "document-features", + "hashbrown 0.16.1", "js-sys", "log", + "naga", "parking_lot", + "portable-atomic", "profiling", "raw-window-handle", "smallvec", @@ -4266,47 +4160,85 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "22.1.0" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0348c840d1051b8e86c3bcd31206080c5e71e5933dabd79be1ce732b0b2f089a" +checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" dependencies = [ "arrayvec", - "bit-vec 0.7.0", + "bit-set", + "bit-vec", "bitflags 2.10.0", - "cfg_aliases 0.1.1", + "bytemuck", + "cfg_aliases", "document-features", + "hashbrown 0.16.1", "indexmap 2.12.1", "log", "naga", "once_cell", "parking_lot", + "portable-atomic", "profiling", "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.18", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-windows-linux-android", "wgpu-hal", "wgpu-types", ] [[package]] -name = "wgpu-hal" -version = "22.0.0" +name = "wgpu-core-deps-apple" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f" +checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "27.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "27.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "27.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" dependencies = [ "android_system_properties", "arrayvec", "ash", + "bit-set", "bitflags 2.10.0", - "cfg_aliases 0.1.1", - "core-graphics-types", - "glow 0.13.1", + "block", + "bytemuck", + "cfg-if", + "cfg_aliases", + "core-graphics-types 0.2.0", + "glow", "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hassle-rs", + "hashbrown 0.16.1", "js-sys", "khronos-egl", "libc", @@ -4314,55 +4246,40 @@ dependencies = [ "log", "metal", "naga", - "ndk-sys 0.5.0+25.2.9519653", + "ndk-sys", "objc", "once_cell", + "ordered-float", "parking_lot", + "portable-atomic", + "portable-atomic-util", "profiling", + "range-alloc", "raw-window-handle", "renderdoc-sys", - "rustc-hash 1.1.0", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.18", "wasm-bindgen", "web-sys", "wgpu-types", - "winapi", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] name = "wgpu-types" -version = "22.0.0" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9d91f0e2c4b51434dfa6db77846f2793149d8e73f800fa2e41f52b8eac3c5d" +checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" dependencies = [ "bitflags 2.10.0", + "bytemuck", "js-sys", + "log", + "thiserror 2.0.18", "web-sys", ] -[[package]] -name = "widestring" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.11" @@ -4372,22 +4289,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core 0.52.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.58.0" @@ -4399,12 +4300,25 @@ dependencies = [ ] [[package]] -name = "windows-core" -version = "0.52.0" +name = "windows" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-targets 0.52.6", + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", ] [[package]] @@ -4413,13 +4327,37 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -4431,6 +4369,17 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -4442,12 +4391,39 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -4457,16 +4433,34 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -4500,7 +4494,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -4534,6 +4528,15 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -4637,7 +4640,7 @@ dependencies = [ "block2 0.5.1", "bytemuck", "calloop 0.13.0", - "cfg_aliases 0.2.1", + "cfg_aliases", "concurrent-queue", "core-foundation 0.9.4", "core-graphics", @@ -4753,16 +4756,6 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -4817,44 +4810,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zbus" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix", - "ordered-stream", - "rand 0.8.5", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "windows-sys 0.52.0", - "xdg-home", - "zbus_macros 4.4.0", - "zbus_names 3.0.0", - "zvariant 4.2.0", -] - [[package]] name = "zbus" version = "5.14.0" @@ -4885,46 +4840,33 @@ dependencies = [ "uuid", "windows-sys 0.61.2", "winnow 0.7.14", - "zbus_macros 5.14.0", - "zbus_names 4.3.1", - "zvariant 5.10.0", + "zbus_macros", + "zbus_names", + "zvariant", ] [[package]] name = "zbus-lockstep" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d" +checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863" dependencies = [ "zbus_xml", - "zvariant 4.2.0", + "zvariant", ] [[package]] name = "zbus-lockstep-macros" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" +checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" dependencies = [ "proc-macro2", "quote", "syn 2.0.111", "zbus-lockstep", "zbus_xml", - "zvariant 4.2.0", -] - -[[package]] -name = "zbus_macros" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.111", - "zvariant_utils 2.1.0", + "zvariant", ] [[package]] @@ -4937,20 +4879,9 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.111", - "zbus_names 4.3.1", - "zvariant 5.10.0", - "zvariant_utils 3.3.0", -] - -[[package]] -name = "zbus_names" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" -dependencies = [ - "serde", - "static_assertions", - "zvariant 4.2.0", + "zbus_names", + "zvariant", + "zvariant_utils", ] [[package]] @@ -4961,20 +4892,19 @@ checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", "winnow 0.7.14", - "zvariant 5.10.0", + "zvariant", ] [[package]] name = "zbus_xml" -version = "4.0.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f374552b954f6abb4bd6ce979e6c9b38fb9d0cd7cc68a7d796e70c9f3a233" +checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" dependencies = [ - "quick-xml 0.30.0", + "quick-xml 0.38.4", "serde", - "static_assertions", - "zbus_names 3.0.0", - "zvariant 4.2.0", + "zbus_names", + "zvariant", ] [[package]] @@ -5052,16 +4982,18 @@ dependencies = [ ] [[package]] -name = "zvariant" -version = "4.2.0" +name = "zune-core" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-jpeg" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec5f41c76397b7da451efd19915684f727d7e1d516384ca6bd0ec43ec94de23c" dependencies = [ - "endi", - "enumflags2", - "serde", - "static_assertions", - "zvariant_derive 4.2.0", + "zune-core", ] [[package]] @@ -5073,23 +5005,9 @@ dependencies = [ "endi", "enumflags2", "serde", - "url", "winnow 0.7.14", - "zvariant_derive 5.10.0", - "zvariant_utils 3.3.0", -] - -[[package]] -name = "zvariant_derive" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.111", - "zvariant_utils 2.1.0", + "zvariant_derive", + "zvariant_utils", ] [[package]] @@ -5102,18 +5020,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.111", - "zvariant_utils 3.3.0", -] - -[[package]] -name = "zvariant_utils" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", + "zvariant_utils", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 158456b..97b142d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,15 +31,15 @@ default = [] gui = ["eframe", "egui_extras", "rfd"] [dependencies.eframe] -version = "0.29" +version = "0.33.3" optional = true [dependencies.egui_extras] -version = "0.29" +version = "0.33.3" optional = true [dependencies.rfd] -version = "0.15" +version = "0.17.2" optional = true [[bin]] diff --git a/README2.md b/README2.md index 70d54a4..2e192b5 100644 --- a/README2.md +++ b/README2.md @@ -37,6 +37,10 @@ Usage in REPL: A full-featured graphical interface built with egui. +Current design note: +- The GUI uses a rigid docked layout rather than draggable in-window splitters. +- This was done to reduce hover/drag-induced layout jitter and keep the workspace visually stable. + #### Building the GUI ```bash @@ -79,18 +83,19 @@ src/gui/ - Filter/search functionality - Click to select, right-click for context menu - Shows counts (sorts, functions, relations, axioms, elements) +- Fixed-width docked sidebar -**Editor Panel (Center Top)** +**Editor Panel (Center)** - Multi-line code editor -- Line numbers - Run button (or Ctrl+Enter) to execute code - Clear button -**Inspector Panel (Center Bottom)** +**Inspector Panel (Right)** - Theory details: parameters, sorts, functions, relations, instance fields, axioms - Instance details: elements by sort, function values, relation tuples, nested instances - Collapsible sections - "View Graph" button for instances +- Fixed-width docked sidebar in the main workspace view **Console Panel (Bottom)** - Scrollable message log @@ -103,11 +108,13 @@ src/gui/ - Type `:inspect ` to inspect - Type `:chase ` to run chase - Or type geolog code directly +- Fixed-height docked footer -**Adjustable Panels** -- Browser panel (left): drag right edge to resize -- Console panel (bottom): drag top edge to resize -- Editor/Inspector split: drag the separator bar between them +**Layout** +- Docked workspace layout for stability +- Browser on the left, editor in the center, inspector on the right +- Console fixed at the bottom +- Chase and graph visualizations replace the central workspace when active **Chase Visualization** - Step/Run All/Stop controls @@ -129,22 +136,14 @@ src/gui/ ``` ┌─────────────────────────────────────────────────────────────┐ │ File Edit View Chase Help │ -├────────────┬────────────────────────────────────────────────┤ -│ ▼ Theories │ ┌─────────────────────────────────────────┐ │ -│ Preorder │ │ Editor │ │ -│ Graph │ │ theory Foo { │ │ -│ ▼ Instances│ │ X : Sort; │ │ -│ Chain3 │ │ } [Run] [Clear] │ │ -│ │ ├─────────────────────────────────────────┤ │ -│ │ │ ═══════════ [drag to resize] ═══════════│ │ -│ │ ├─────────────────────────────────────────┤ │ -│ │ │ Inspector: Chain3 : Preorder │ │ -│ │ │ ▼ Elements (3) │ │ -│ │ │ X: bot, mid, top │ │ -│ │ │ ▼ Relations │ │ -│ │ │ leq: 6 tuples [View Graph] │ │ -│ │ └─────────────────────────────────────────┘ │ -├────────────┴────────────────────────────────────────────────┤ +├──────────────┬──────────────────────────────┬───────────────┤ +│ Browser │ Editor │ Inspector │ +│ ▼ Theories │ theory Foo { │ Chain3 │ +│ Preorder │ X : Sort; │ : Preorder │ +│ ▼ Instances │ } [Run] [Clear] │ ▼ Elements │ +│ Chain3 │ │ ▼ Relations │ +│ │ │ [View Graph] │ +├──────────────┴──────────────────────────────┴───────────────┤ │ Console [Clear]│ │ > Defined theory Preorder (1 sorts, 1 relations, 2 axioms) │ │ + Defined instance Chain3 : Preorder (3 elements) │ diff --git a/src/bin/geolog-gui.rs b/src/bin/geolog-gui.rs index bfedff2..51cd594 100644 --- a/src/bin/geolog-gui.rs +++ b/src/bin/geolog-gui.rs @@ -113,22 +113,24 @@ fn parse_args(args: &[String]) -> (Option, Vec) { fn setup_fonts(ctx: &egui::Context) { // Use a slightly larger default font let mut style = (*ctx.style()).clone(); - style.text_styles.insert( - egui::TextStyle::Body, - egui::FontId::proportional(14.0), - ); - style.text_styles.insert( - egui::TextStyle::Heading, - egui::FontId::proportional(18.0), - ); - style.text_styles.insert( - egui::TextStyle::Monospace, - egui::FontId::monospace(13.0), - ); + style.animation_time = 0.0; + style + .text_styles + .insert(egui::TextStyle::Body, egui::FontId::proportional(14.0)); + style + .text_styles + .insert(egui::TextStyle::Heading, egui::FontId::proportional(18.0)); + style + .text_styles + .insert(egui::TextStyle::Monospace, egui::FontId::monospace(13.0)); - // Set up dark theme with custom colors - style.visuals = egui::Visuals::dark(); - style.visuals.override_text_color = Some(egui::Color32::from_gray(220)); + // Force a light theme for the GUI. + style.visuals = egui::Visuals::light(); + style.visuals.widgets.noninteractive.expansion = 0.0; + style.visuals.widgets.inactive.expansion = 0.0; + style.visuals.widgets.hovered.expansion = 0.0; + style.visuals.widgets.active.expansion = 0.0; + style.visuals.widgets.open.expansion = 0.0; ctx.set_style(style); } diff --git a/src/gui/app.rs b/src/gui/app.rs index dc9e1e1..f006587 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -21,9 +21,6 @@ pub struct GeologApp { console: ConsolePanel, chase_viz: ChaseVisualization, graph_view: GraphView, - - /// Editor/Inspector split ratio (0.0 to 1.0, where 0.5 = equal) - editor_split: f32, } impl GeologApp { @@ -37,7 +34,6 @@ impl GeologApp { console: ConsolePanel::new(), chase_viz: ChaseVisualization::new(), graph_view: GraphView::new(), - editor_split: 0.5, // 50% editor, 50% inspector } } @@ -50,21 +46,21 @@ impl GeologApp { /// Render the menu bar fn menu_bar(&mut self, ui: &mut egui::Ui) { - egui::menu::bar(ui, |ui| { + egui::MenuBar::new().ui(ui, |ui| { ui.menu_button("File", |ui| { if ui.button("Open...").clicked() { self.handle_open_file(); - ui.close_menu(); + ui.close(); } if ui.button("Save").clicked() { self.handle_save_file(); - ui.close_menu(); + ui.close(); } ui.separator(); if ui.button("Reset State").clicked() { - self.state.repl.reset(); + self.state.reset_runtime_state(); self.state.log_info("State reset"); - ui.close_menu(); + ui.close(); } ui.separator(); if ui.button("Quit").clicked() { @@ -75,11 +71,11 @@ impl GeologApp { ui.menu_button("Edit", |ui| { if ui.button("Clear Editor").clicked() { self.state.editor_content.clear(); - ui.close_menu(); + ui.close(); } if ui.button("Clear Console").clicked() { self.state.clear_console(); - ui.close_menu(); + ui.close(); } }); @@ -92,7 +88,7 @@ impl GeologApp { .clicked() { self.state.central_view = CentralView::EditorAndInspector; - ui.close_menu(); + ui.close(); } if ui .selectable_label( @@ -102,7 +98,7 @@ impl GeologApp { .clicked() { self.state.central_view = CentralView::ChaseVisualization; - ui.close_menu(); + ui.close(); } if ui .selectable_label( @@ -112,7 +108,7 @@ impl GeologApp { .clicked() { self.state.central_view = CentralView::GraphVisualization; - ui.close_menu(); + ui.close(); } }); @@ -123,11 +119,11 @@ impl GeologApp { let name = name.clone(); if ui.button(format!("Run Chase on {}", name)).clicked() { self.handle_run_chase(&name); - ui.close_menu(); + ui.close(); } if ui.button(format!("Debug Chase on {}", name)).clicked() { self.state.start_chase(&name); - ui.close_menu(); + ui.close(); } } else { ui.label("(Select an instance first)"); @@ -136,15 +132,13 @@ impl GeologApp { ui.menu_button("Help", |ui| { if ui.button("About").clicked() { - self.state.log_info(format!( - "Geolog GUI v{}", - env!("CARGO_PKG_VERSION") - )); - ui.close_menu(); + self.state + .log_info(format!("Geolog GUI v{}", env!("CARGO_PKG_VERSION"))); + ui.close(); } if ui.button("Syntax Help").clicked() { self.show_syntax_help(); - ui.close_menu(); + ui.close(); } }); }); @@ -246,8 +240,7 @@ impl GeologApp { Ok(iterations) => { let elapsed = start.elapsed(); // Get structure info before releasing borrow - let total_tuples: usize = - entry.structure.relations.iter().map(|r| r.len()).sum(); + let total_tuples: usize = entry.structure.relations.iter().map(|r| r.len()).sum(); let num_elements = entry.structure.len(); self.state.log_success(format!( @@ -259,8 +252,7 @@ impl GeologApp { // Show structure summary self.state.log_info(format!( "Structure: {} elements, {} relation tuples", - num_elements, - total_tuples + num_elements, total_tuples )); } Err(e) => { @@ -290,80 +282,7 @@ impl GeologApp { fn central_panel(&mut self, ui: &mut egui::Ui) { match self.state.central_view { CentralView::EditorAndInspector => { - // Split vertically: editor on top, inspector on bottom - let available_height = ui.available_height(); - let splitter_height = 8.0; - // Ensure heights are never negative - let usable_height = (available_height - splitter_height).max(0.0); - let editor_height = (usable_height * self.editor_split).max(0.0); - let inspector_height = (usable_height * (1.0 - self.editor_split)).max(0.0); - - // Editor section - ui.allocate_ui_with_layout( - egui::vec2(ui.available_width(), editor_height), - egui::Layout::top_down(egui::Align::LEFT), - |ui| { - self.editor.show(ui, &mut self.state); - }, - ); - - // Draggable splitter - let splitter_response = ui.allocate_response( - egui::vec2(ui.available_width(), splitter_height), - egui::Sense::drag(), - ); - - // Draw splitter bar - let splitter_rect = splitter_response.rect; - let is_dragging = splitter_response.dragged(); - let is_hovered = splitter_response.hovered(); - - let splitter_color = if is_dragging { - egui::Color32::from_rgb(100, 150, 255) - } else if is_hovered { - egui::Color32::from_rgb(80, 80, 100) - } else { - egui::Color32::from_rgb(60, 60, 70) - }; - - ui.painter().rect_filled(splitter_rect, 0.0, splitter_color); - - // Draw grip lines - let center_y = splitter_rect.center().y; - let grip_width = 30.0; - let grip_x = splitter_rect.center().x; - for i in -1..=1 { - let y = center_y + (i as f32) * 2.0; - ui.painter().line_segment( - [ - egui::pos2(grip_x - grip_width / 2.0, y), - egui::pos2(grip_x + grip_width / 2.0, y), - ], - egui::Stroke::new(1.0, egui::Color32::GRAY), - ); - } - - // Handle dragging - if is_dragging { - let delta = splitter_response.drag_delta().y; - let divisor = (available_height - splitter_height).max(1.0); - let new_split = self.editor_split + delta / divisor; - self.editor_split = new_split.clamp(0.1, 0.9); - } - - // Change cursor when hovering - if is_hovered || is_dragging { - ui.ctx().set_cursor_icon(egui::CursorIcon::ResizeVertical); - } - - // Inspector section - ui.allocate_ui_with_layout( - egui::vec2(ui.available_width(), inspector_height), - egui::Layout::top_down(egui::Align::LEFT), - |ui| { - self.inspector.show(ui, &mut self.state); - }, - ); + self.editor.show(ui, &mut self.state); } CentralView::ChaseVisualization => { self.chase_viz.show(ui, &mut self.state); @@ -384,18 +303,28 @@ impl eframe::App for GeologApp { // Left panel: Browser egui::SidePanel::left("browser_panel") - .default_width(200.0) - .min_width(150.0) - .resizable(true) + .default_width(240.0) + .min_width(240.0) + .resizable(false) .show(ctx, |ui| { self.browser.show(ui, &mut self.state); }); + if self.state.central_view == CentralView::EditorAndInspector { + egui::SidePanel::right("inspector_panel") + .default_width(340.0) + .min_width(340.0) + .resizable(false) + .show(ctx, |ui| { + self.inspector.show(ui, &mut self.state); + }); + } + // Bottom panel: Console egui::TopBottomPanel::bottom("console_panel") - .default_height(150.0) - .min_height(100.0) - .resizable(true) + .default_height(200.0) + .min_height(200.0) + .resizable(false) .show(ctx, |ui| { self.console.show(ui, &mut self.state); }); diff --git a/src/gui/panels/browser.rs b/src/gui/panels/browser.rs index 7180e19..fe55320 100644 --- a/src/gui/panels/browser.rs +++ b/src/gui/panels/browser.rs @@ -147,7 +147,7 @@ impl BrowserPanel { if ui.button("Inspect").clicked() { state.selected_item = Some(SelectedItem::Instance(instance.name.clone())); - ui.close_menu(); + ui.close(); } if ui.button("Run Chase").clicked() { // This will be handled by the app @@ -157,11 +157,11 @@ impl BrowserPanel { "Use Chase menu to run chase on '{}'", instance.name )); - ui.close_menu(); + ui.close(); } if ui.button("View Graph").clicked() { state.show_graph(&instance.name, None); - ui.close_menu(); + ui.close(); } }); } diff --git a/src/gui/panels/console.rs b/src/gui/panels/console.rs index d16b69d..6d00419 100644 --- a/src/gui/panels/console.rs +++ b/src/gui/panels/console.rs @@ -3,9 +3,10 @@ //! Shows a scrollable log of messages with color coding and a REPL input. use eframe::egui; +use egui_extras::{Size, StripBuilder}; use crate::gui::state::{GuiState, MessageKind}; -use crate::repl::{MetaCommand, ListTarget, ExecuteResult}; +use crate::repl::{ExecuteResult, ListTarget, MetaCommand}; /// Console panel for output messages and REPL input pub struct ConsolePanel { @@ -30,101 +31,122 @@ impl ConsolePanel { } pub fn show(&mut self, ui: &mut egui::Ui, state: &mut GuiState) { - // Header with title and buttons - ui.horizontal(|ui| { - ui.heading("Console"); - - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - if ui.button("Clear").clicked() { - state.clear_console(); - } - - ui.checkbox(&mut self.auto_scroll, "Auto-scroll"); - - ui.label(format!("{} messages", state.console_messages.len())); - }); - }); - - ui.separator(); - - // Scrollable message area (takes most of the space) - let available_height = (ui.available_height() - 30.0).max(50.0); // Reserve space for input - let scroll_area = egui::ScrollArea::vertical() - .auto_shrink([false, false]) - .max_height(available_height) - .stick_to_bottom(self.auto_scroll); - - scroll_area.show(ui, |ui| { - if state.console_messages.is_empty() { - ui.label("Type commands below (e.g., :help, :list, or geolog code)"); - } else { - for message in &state.console_messages { - let color = match message.kind { - MessageKind::Info => egui::Color32::LIGHT_GRAY, - MessageKind::Success => egui::Color32::from_rgb(100, 200, 100), - MessageKind::Error => egui::Color32::from_rgb(255, 100, 100), - MessageKind::Warning => egui::Color32::from_rgb(255, 200, 100), - }; - - let prefix = match message.kind { - MessageKind::Info => ">", - MessageKind::Success => "+", - MessageKind::Error => "!", - MessageKind::Warning => "?", - }; - + StripBuilder::new(ui) + .size(Size::exact(28.0)) + .size(Size::remainder().at_least(80.0)) + .size(Size::exact(28.0)) + .vertical(|mut strip| { + strip.cell(|ui| { ui.horizontal(|ui| { - ui.label( - egui::RichText::new(prefix) - .color(color) - .monospace(), - ); - ui.label( - egui::RichText::new(&message.text) - .color(color) - .monospace(), - ); + ui.heading("Console"); + + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + if ui + .add_sized([64.0, 22.0], egui::Button::new("Clear")) + .clicked() + { + state.clear_console(); + } + + ui.add_sized( + [112.0, 22.0], + egui::Checkbox::new(&mut self.auto_scroll, "Auto-scroll"), + ); + + ui.add_sized( + [96.0, 22.0], + egui::Label::new(format!("{} messages", state.console_messages.len())), + ); + }); }); - } - } - }); + }); - // REPL input at the bottom - ui.separator(); - ui.horizontal(|ui| { - ui.label( - egui::RichText::new("geolog>") - .monospace() - .color(egui::Color32::from_rgb(100, 150, 255)), - ); + strip.cell(|ui| { + egui::Frame::default() + .inner_margin(egui::Margin::same(4)) + .show(ui, |ui| { + egui::ScrollArea::vertical() + .auto_shrink([false, false]) + .stick_to_bottom(self.auto_scroll) + .show(ui, |ui| { + if state.console_messages.is_empty() { + ui.label("Type commands below (e.g., :help, :list, or geolog code)"); + } else { + for message in &state.console_messages { + let color = message_color(ui, message.kind); - let response = ui.add( - egui::TextEdit::singleline(&mut self.input) - .font(egui::FontId::monospace(13.0)) - .desired_width((ui.available_width() - 60.0).max(50.0)) - .hint_text(":help for commands"), - ); + let prefix = match message.kind { + MessageKind::Info => ">", + MessageKind::Success => "+", + MessageKind::Error => "!", + MessageKind::Warning => "?", + }; - // Handle Enter key - if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { - self.execute_input(state); - response.request_focus(); - } + ui.horizontal(|ui| { + ui.add_sized( + [12.0, 18.0], + egui::Label::new( + egui::RichText::new(prefix) + .color(color) + .monospace(), + ), + ); + ui.label( + egui::RichText::new(&message.text) + .color(color) + .monospace(), + ); + }); + } + } + }); + }); + }); - // Handle Up/Down for history - if response.has_focus() { - if ui.input(|i| i.key_pressed(egui::Key::ArrowUp)) { - self.history_up(); - } - if ui.input(|i| i.key_pressed(egui::Key::ArrowDown)) { - self.history_down(); - } - } + strip.cell(|ui| { + ui.horizontal(|ui| { + ui.add_sized( + [68.0, 22.0], + egui::Label::new( + egui::RichText::new("geolog>") + .monospace() + .color(ui.visuals().hyperlink_color), + ), + ); - if ui.button("Run").clicked() { - self.execute_input(state); - } - }); + let run_width = 56.0; + let spacing = ui.spacing().item_spacing.x; + let input_width = (ui.available_width() - run_width - spacing).max(120.0); + let response = ui.add_sized( + [input_width, 22.0], + egui::TextEdit::singleline(&mut self.input) + .font(egui::FontId::monospace(13.0)) + .hint_text(":help for commands"), + ); + + if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { + self.execute_input(state); + response.request_focus(); + } + + if response.has_focus() { + if ui.input(|i| i.key_pressed(egui::Key::ArrowUp)) { + self.history_up(); + } + if ui.input(|i| i.key_pressed(egui::Key::ArrowDown)) { + self.history_down(); + } + } + + if ui + .add_sized([run_width, 22.0], egui::Button::new("Run")) + .clicked() + { + self.execute_input(state); + } + }); + }); + }); } fn execute_input(&mut self, state: &mut GuiState) { @@ -204,24 +226,22 @@ impl ConsolePanel { fn handle_meta_command(&mut self, state: &mut GuiState, cmd: MetaCommand) { match cmd { - MetaCommand::Help(topic) => { - match topic.as_deref() { - None => { - state.log_info("Commands:"); - state.log_info(" :help Show this help"); - state.log_info(" :list List theories and instances"); - state.log_info(" :inspect Inspect a theory or instance"); - state.log_info(" :chase Run chase on instance"); - state.log_info(" :reset Reset all state"); - state.log_info(" :clear Clear console"); - state.log_info(""); - state.log_info("Or type geolog code directly."); - } - Some(t) => { - state.log_info(format!("Help topic: {}", t)); - } + MetaCommand::Help(topic) => match topic.as_deref() { + None => { + state.log_info("Commands:"); + state.log_info(" :help Show this help"); + state.log_info(" :list List theories and instances"); + state.log_info(" :inspect Inspect a theory or instance"); + state.log_info(" :chase Run chase on instance"); + state.log_info(" :reset Reset all state"); + state.log_info(" :clear Clear console"); + state.log_info(""); + state.log_info("Or type geolog code directly."); } - } + Some(t) => { + state.log_info(format!("Help topic: {}", t)); + } + }, MetaCommand::List(target) => { match target { ListTarget::Theories | ListTarget::All => { @@ -259,7 +279,7 @@ impl ConsolePanel { } } MetaCommand::Inspect(name) => { - use crate::repl::{InspectResult, format_theory_detail, format_instance_detail}; + use crate::repl::{format_instance_detail, format_theory_detail, InspectResult}; match state.repl.inspect(&name) { Some(InspectResult::Theory(detail)) => { for line in format_theory_detail(&detail).lines() { @@ -280,26 +300,26 @@ impl ConsolePanel { state.clear_console(); } MetaCommand::Reset => { - state.repl.reset(); - state.selected_item = None; + state.reset_runtime_state(); state.log_success("State reset."); } - MetaCommand::Chase { instance, max_iterations } => { + MetaCommand::Chase { + instance, + max_iterations, + } => { self.run_chase(state, &instance, max_iterations); } - MetaCommand::Source(path) => { - match std::fs::read_to_string(&path) { - Ok(content) => { - state.editor_content = content; - state.current_file = Some(path.clone()); - state.log_info(format!("Loaded: {}", path.display())); - state.execute_editor(); - } - Err(e) => { - state.log_error(format!("Failed to load: {}", e)); - } + MetaCommand::Source(path) => match std::fs::read_to_string(&path) { + Ok(content) => { + state.editor_content = content; + state.current_file = Some(path.clone()); + state.log_info(format!("Loaded: {}", path.display())); + state.execute_editor(); } - } + Err(e) => { + state.log_error(format!("Failed to load: {}", e)); + } + }, _ => { state.log_warning("Command not yet implemented in GUI"); } @@ -334,7 +354,11 @@ impl ConsolePanel { return; } - state.log_info(format!("Running chase on '{}' ({} axioms)...", instance_name, axioms.len())); + state.log_info(format!( + "Running chase on '{}' ({} axioms)...", + instance_name, + axioms.len() + )); let entry = state.repl.instances.get_mut(instance_name).unwrap(); let max_iter = max_iterations.unwrap_or(100); @@ -362,8 +386,7 @@ impl ConsolePanel { )); state.log_info(format!( "Structure: {} elements, {} relation tuples", - num_elements, - total_tuples + num_elements, total_tuples )); } Err(e) => { @@ -405,6 +428,15 @@ impl ConsolePanel { } } +fn message_color(ui: &egui::Ui, kind: MessageKind) -> egui::Color32 { + match kind { + MessageKind::Info => ui.visuals().text_color(), + MessageKind::Success => egui::Color32::from_rgb(25, 110, 55), + MessageKind::Error => egui::Color32::from_rgb(170, 30, 45), + MessageKind::Warning => egui::Color32::from_rgb(145, 95, 15), + } +} + impl Default for ConsolePanel { fn default() -> Self { Self::new() diff --git a/src/gui/panels/editor.rs b/src/gui/panels/editor.rs index 73c9455..b76709b 100644 --- a/src/gui/panels/editor.rs +++ b/src/gui/panels/editor.rs @@ -7,16 +7,11 @@ use eframe::egui; use crate::gui::state::GuiState; /// Code editor panel -pub struct EditorPanel { - /// Whether to show line numbers - show_line_numbers: bool, -} +pub struct EditorPanel {} impl EditorPanel { pub fn new() -> Self { - Self { - show_line_numbers: true, - } + Self {} } pub fn show(&mut self, ui: &mut egui::Ui, state: &mut GuiState) { @@ -59,58 +54,26 @@ impl EditorPanel { // Use a monospace font for code let font_id = egui::FontId::monospace(14.0); - // Calculate line numbers if needed - let line_count = state.editor_content.lines().count().max(1); - let line_number_width = if self.show_line_numbers { - let digits = (line_count as f32).log10().floor() as usize + 1; - digits.max(2) as f32 * 10.0 + 8.0 - } else { - 0.0 - }; + // Main editor area - simplified layout without line numbers column + // to avoid layout instability from horizontal split calculations + let text_edit = egui::TextEdit::multiline(&mut state.editor_content) + .font(font_id) + .code_editor() + .desired_width(f32::INFINITY) + .desired_rows(20) + .lock_focus(true); - ui.horizontal(|ui| { - // Line numbers column - if self.show_line_numbers { - ui.allocate_ui_with_layout( - egui::vec2(line_number_width, ui.available_height().max(0.0)), - egui::Layout::top_down(egui::Align::RIGHT), - |ui| { - ui.style_mut().visuals.override_text_color = - Some(egui::Color32::GRAY); + let response = ui.add(text_edit); - for i in 1..=line_count { - ui.label( - egui::RichText::new(format!("{}", i)) - .font(font_id.clone()) - .color(egui::Color32::GRAY), - ); - } - }, - ); - - ui.separator(); - } - - // Main editor area - let text_edit = egui::TextEdit::multiline(&mut state.editor_content) - .font(font_id) - .code_editor() - .desired_width(f32::INFINITY) - .desired_rows(20) - .lock_focus(true); - - let response = ui.add(text_edit); - - // Handle keyboard shortcuts - if response.has_focus() { - let modifiers = ui.input(|i| i.modifiers); - if modifiers.ctrl || modifiers.command { - if ui.input(|i| i.key_pressed(egui::Key::Enter)) { - state.execute_editor(); - } + // Handle keyboard shortcuts + if response.has_focus() { + let modifiers = ui.input(|i| i.modifiers); + if modifiers.ctrl || modifiers.command { + if ui.input(|i| i.key_pressed(egui::Key::Enter)) { + state.execute_editor(); } } - }); + } } } diff --git a/src/gui/state.rs b/src/gui/state.rs index c6048f7..ed0ee24 100644 --- a/src/gui/state.rs +++ b/src/gui/state.rs @@ -5,6 +5,7 @@ use std::collections::HashSet; use std::path::PathBuf; +use crate::core::Structure; use crate::repl::{InstanceDetail, ReplState, TheoryDetail}; /// What item is currently selected in the browser @@ -57,6 +58,7 @@ pub enum ChaseMode { pub struct ChaseVisualizationState { pub mode: ChaseMode, pub instance_name: String, + pub initial_structure: Structure, pub events: Vec, pub current_event: usize, pub current_iteration: usize, @@ -65,10 +67,11 @@ pub struct ChaseVisualizationState { } impl ChaseVisualizationState { - pub fn new(instance_name: String) -> Self { + pub fn new(instance_name: String, initial_structure: Structure) -> Self { Self { mode: ChaseMode::Idle, instance_name, + initial_structure, events: Vec::new(), current_event: 0, current_iteration: 0, @@ -176,6 +179,16 @@ impl GuiState { } } + /// Reset the loaded theories/instances and clear any stale GUI selections. + pub fn reset_runtime_state(&mut self) { + self.repl.reset(); + self.selected_item = None; + self.chase_state = None; + self.graph_instance = None; + self.graph_relation = None; + self.central_view = CentralView::EditorAndInspector; + } + /// Add an info message to the console pub fn log_info(&mut self, text: impl Into) { self.console_messages.push(ConsoleMessage { @@ -308,7 +321,15 @@ impl GuiState { /// Start a chase visualization for an instance pub fn start_chase(&mut self, instance_name: &str) { - self.chase_state = Some(ChaseVisualizationState::new(instance_name.to_string())); + let Some(entry) = self.repl.instances.get(instance_name) else { + self.log_error(format!("Instance '{}' not found", instance_name)); + return; + }; + + self.chase_state = Some(ChaseVisualizationState::new( + instance_name.to_string(), + entry.structure.clone(), + )); self.central_view = CentralView::ChaseVisualization; } @@ -319,3 +340,30 @@ impl GuiState { self.central_view = CentralView::GraphVisualization; } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn reset_runtime_state_clears_gui_navigation_state() { + let mut state = GuiState::new(); + state.selected_item = Some(SelectedItem::Theory("Demo".into())); + state.chase_state = Some(ChaseVisualizationState::new("I".into(), Structure::new(0))); + state.graph_instance = Some("I".into()); + state.graph_relation = Some("R".into()); + state.central_view = CentralView::GraphVisualization; + state.editor_content = "theory Demo {}".into(); + state.current_file = Some(PathBuf::from("demo.geolog")); + + state.reset_runtime_state(); + + assert!(state.selected_item.is_none()); + assert!(state.chase_state.is_none()); + assert!(state.graph_instance.is_none()); + assert!(state.graph_relation.is_none()); + assert_eq!(state.central_view, CentralView::EditorAndInspector); + assert_eq!(state.editor_content, "theory Demo {}"); + assert_eq!(state.current_file, Some(PathBuf::from("demo.geolog"))); + } +} diff --git a/src/gui/visualizations/chase.rs b/src/gui/visualizations/chase.rs index 190faeb..1398f00 100644 --- a/src/gui/visualizations/chase.rs +++ b/src/gui/visualizations/chase.rs @@ -3,9 +3,12 @@ //! Provides step-through debugging and visualization of the chase algorithm. use eframe::egui; +use std::collections::HashSet; use crate::core::RelationStorage; -use crate::gui::state::{ChaseEventRecord, ChaseMode, ChaseVisualizationState, CentralView, GuiState, SelectedItem}; +use crate::gui::state::{ + CentralView, ChaseEventRecord, ChaseMode, ChaseVisualizationState, GuiState, SelectedItem, +}; use crate::query::chase::{ChaseControl, ChaseEvent, ChaseObserver}; /// Chase visualization panel @@ -49,7 +52,10 @@ impl ChaseVisualization { ui.label("No instances defined. Create an instance first."); } else { for instance in instances { - if ui.button(format!("{} : {}", instance.name, instance.theory_name)).clicked() { + if ui + .button(format!("{} : {}", instance.name, instance.theory_name)) + .clicked() + { state.start_chase(&instance.name); } } @@ -59,7 +65,10 @@ impl ChaseVisualization { if let Some(SelectedItem::Instance(name)) = &state.selected_item { ui.add_space(16.0); let name = name.clone(); - if ui.button(format!("Start Chase on selected: {}", name)).clicked() { + if ui + .button(format!("Start Chase on selected: {}", name)) + .clicked() + { state.start_chase(&name); } } @@ -67,9 +76,9 @@ impl ChaseVisualization { fn show_chase_panel(&mut self, ui: &mut egui::Ui, state: &mut GuiState) { // Read mode and instance_name before mutable operations - let (mode, instance_name) = { + let mode = { let chase_state = state.chase_state.as_ref().unwrap(); - (chase_state.mode, chase_state.instance_name.clone()) + chase_state.mode }; // Control buttons @@ -130,7 +139,7 @@ impl ChaseVisualization { } } if reset_clicked { - state.chase_state = Some(ChaseVisualizationState::new(instance_name)); + self.reset_chase(state); } if close_clicked { state.chase_state = None; @@ -185,11 +194,11 @@ impl ChaseVisualization { ); let color = if is_current { - egui::Color32::YELLOW + ui.visuals().hyperlink_color } else if event.changed { - egui::Color32::from_rgb(100, 200, 100) + egui::Color32::from_rgb(25, 110, 55) } else { - egui::Color32::LIGHT_GRAY + ui.visuals().weak_text_color() }; ui.label(egui::RichText::new(text).color(color).monospace()); @@ -283,14 +292,40 @@ impl ChaseVisualization { } } + fn reset_chase(&mut self, state: &mut GuiState) { + let Some(chase_state) = &state.chase_state else { + return; + }; + + let instance_name = chase_state.instance_name.clone(); + let initial_structure = chase_state.initial_structure.clone(); + let breakpoints = chase_state.breakpoints.clone(); + + let Some(entry) = state.repl.instances.get_mut(&instance_name) else { + state.log_error(format!("Instance '{}' not found", instance_name)); + return; + }; + + entry.structure = initial_structure.clone(); + + let mut new_state = ChaseVisualizationState::new(instance_name, initial_structure); + new_state.breakpoints = breakpoints; + state.chase_state = Some(new_state); + self.new_breakpoint.clear(); + state.log_info("Chase visualization reset"); + } + /// Execute a single step of the chase fn step_chase(&mut self, state: &mut GuiState) { let instance_name = match &state.chase_state { Some(s) => s.instance_name.clone(), None => return, }; - - let current_iteration = state.chase_state.as_ref().map(|s| s.current_iteration).unwrap_or(0); + let breakpoints = state + .chase_state + .as_ref() + .map(|s| s.breakpoints.clone()) + .unwrap_or_default(); // Get theory name first (immutable borrow) let theory_name = match state.repl.instances.get(&instance_name) { @@ -313,13 +348,7 @@ impl ChaseVisualization { let entry = state.repl.instances.get_mut(&instance_name).unwrap(); // Create a GUI observer that records events - let mut observer = GuiChaseObserver { - events: Vec::new(), - slid_to_name: &entry.slid_to_name, - iteration: current_iteration, - step_mode: true, - steps_remaining: 1, - }; + let mut observer = GuiChaseObserver::new(&entry.slid_to_name, Some(1), breakpoints); // Run chase with observer (just one step) let sig = &theory.theory.signature; @@ -332,33 +361,43 @@ impl ChaseVisualization { &mut entry.structure, &mut state.repl.store.universe, sig, - 1, // Just one iteration + 100, &mut observer, ); - // Record events - let events = observer.events; + let GuiChaseObserver { + events, + iteration: current_iteration, + breakpoint_hit: paused_on_breakpoint, + completed_iterations, + .. + } = observer; if let Some(chase_state) = &mut state.chase_state { chase_state.mode = ChaseMode::Stepping; - for event in events { - chase_state.events.push(event); + chase_state.events.extend(events); + if !chase_state.events.is_empty() { + chase_state.current_event = chase_state.events.len() - 1; } - chase_state.current_event = chase_state.events.len().saturating_sub(1); - chase_state.current_iteration += 1; + chase_state.current_iteration = chase_state.current_iteration.max(current_iteration); - if let Ok(iterations) = result { - if iterations == 0 { - chase_state.mode = ChaseMode::Completed; - chase_state.total_iterations = Some(chase_state.current_iteration); - } + if let Some(iterations) = completed_iterations { + chase_state.mode = ChaseMode::Completed; + chase_state.current_iteration = iterations; + chase_state.total_iterations = Some(iterations); } } - // Log completion separately - if let Ok(iterations) = result { - if iterations == 0 { - let total = state.chase_state.as_ref().map(|s| s.current_iteration).unwrap_or(0); - state.log_success(format!("Chase completed after {} iterations", total)); + match result { + Ok(_) => { + if let Some(bp) = paused_on_breakpoint { + state.log_info(format!("Paused at breakpoint '{}'", bp)); + } + if let Some(iterations) = completed_iterations { + state.log_success(format!("Chase completed after {} iterations", iterations)); + } + } + Err(e) => { + state.log_error(format!("Chase error: {}", e)); } } } @@ -387,34 +426,70 @@ impl ChaseVisualization { } }; + let breakpoints = state + .chase_state + .as_ref() + .map(|s| s.breakpoints.clone()) + .unwrap_or_default(); + // Now get mutable access to entry let entry = state.repl.instances.get_mut(&instance_name).unwrap(); // Run chase to fixpoint let sig = &theory.theory.signature; let axioms = &theory.theory.axioms; + let axiom_names = &theory.theory.axiom_names; let start = std::time::Instant::now(); - let result = crate::query::chase::chase_fixpoint( + let mut observer = GuiChaseObserver::new(&entry.slid_to_name, None, breakpoints); + let result = crate::query::chase::chase_fixpoint_with_observer( axioms, + axiom_names, &mut entry.structure, &mut state.repl.store.universe, sig, 100, + &mut observer, ); + let GuiChaseObserver { + events, + iteration: current_iteration, + breakpoint_hit, + completed_iterations, + .. + } = observer; + match result { - Ok(iterations) => { + Ok(_) => { let elapsed = start.elapsed(); if let Some(chase_state) = &mut state.chase_state { - chase_state.mode = ChaseMode::Completed; - chase_state.total_iterations = Some(iterations); + chase_state.events.extend(events); + if !chase_state.events.is_empty() { + chase_state.current_event = chase_state.events.len() - 1; + } + chase_state.current_iteration = + chase_state.current_iteration.max(current_iteration); + + if let Some(iterations) = completed_iterations { + chase_state.mode = ChaseMode::Completed; + chase_state.current_iteration = iterations; + chase_state.total_iterations = Some(iterations); + } else { + chase_state.mode = ChaseMode::Stepping; + chase_state.total_iterations = None; + } + } + + if let Some(bp) = breakpoint_hit { + state.log_info(format!("Paused at breakpoint '{}'", bp)); + } else if let Some(iterations) = completed_iterations { + state.log_success(format!( + "Chase completed in {} iterations ({:.2}ms)", + iterations, + elapsed.as_secs_f64() * 1000.0 + )); } - state.log_success(format!( - "Chase completed in {} iterations ({:.2}ms)", - iterations, - elapsed.as_secs_f64() * 1000.0 - )); } Err(e) => { state.log_error(format!("Chase error: {}", e)); @@ -434,8 +509,30 @@ struct GuiChaseObserver<'a> { events: Vec, slid_to_name: &'a std::collections::HashMap, iteration: usize, - step_mode: bool, - steps_remaining: usize, + step_budget: Option, + breakpoints: HashSet, + pending_breakpoint_hit: Option, + breakpoint_hit: Option, + completed_iterations: Option, +} + +impl<'a> GuiChaseObserver<'a> { + fn new( + slid_to_name: &'a std::collections::HashMap, + step_budget: Option, + breakpoints: HashSet, + ) -> Self { + Self { + events: Vec::new(), + slid_to_name, + iteration: 0, + step_budget, + breakpoints, + pending_breakpoint_hit: None, + breakpoint_hit: None, + completed_iterations: None, + } + } } impl<'a> ChaseObserver for GuiChaseObserver<'a> { @@ -471,27 +568,189 @@ impl<'a> ChaseObserver for GuiChaseObserver<'a> { binding: binding_vec, changed: false, // Will be updated in AxiomFired }); - - if self.step_mode { - self.steps_remaining = self.steps_remaining.saturating_sub(1); - if self.steps_remaining == 0 { - ChaseControl::Stop - } else { - ChaseControl::Continue - } - } else { - ChaseControl::Continue - } + self.pending_breakpoint_hit = axiom_name + .filter(|name| self.breakpoints.iter().any(|bp| name.contains(bp))) + .map(str::to_string); + ChaseControl::Continue } - ChaseEvent::AxiomFired { changed, .. } => { + ChaseEvent::AxiomFired { + axiom_name, + changed, + .. + } => { // Update the last event with the changed status if let Some(last) = self.events.last_mut() { last.changed = changed; } - ChaseControl::Continue + + let mut should_stop = false; + + if let Some(remaining) = &mut self.step_budget { + *remaining = remaining.saturating_sub(1); + if *remaining == 0 { + should_stop = true; + } + } + + if let Some(bp) = self.pending_breakpoint_hit.take() { + self.breakpoint_hit = Some(bp); + should_stop = true; + } else if self.breakpoint_hit.is_none() { + self.breakpoint_hit = axiom_name + .filter(|name| self.breakpoints.iter().any(|bp| name.contains(bp))) + .map(str::to_string); + should_stop |= self.breakpoint_hit.is_some(); + } + + if should_stop { + ChaseControl::Stop + } else { + ChaseControl::Continue + } } ChaseEvent::IterationEnd { .. } => ChaseControl::Continue, - ChaseEvent::ChaseComplete { .. } => ChaseControl::Continue, + ChaseEvent::ChaseComplete { iterations } => { + self.completed_iterations = Some(iterations); + ChaseControl::Continue + } } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::core::{ + Context, DerivedSort, ElaboratedTheory, Formula, Sequent, Signature, Structure, Term, + Theory, + }; + use crate::repl::InstanceEntry; + use std::rc::Rc; + + fn build_test_state() -> GuiState { + let mut state = GuiState::new(); + let mut sig = Signature::default(); + sig.add_sort("X".to_string()); + sig.add_relation( + "leq".to_string(), + DerivedSort::Product(vec![ + ("x".to_string(), DerivedSort::Base(0)), + ("y".to_string(), DerivedSort::Base(0)), + ]), + ); + + let theory = Theory { + name: "Preorder".to_string(), + signature: sig, + axioms: vec![Sequent { + context: Context { + vars: vec![("x".to_string(), DerivedSort::Base(0))], + }, + premise: Formula::True, + conclusion: Formula::Rel( + 0, + Term::Record(vec![ + ( + "x".to_string(), + Term::Var("x".to_string(), DerivedSort::Base(0)), + ), + ( + "y".to_string(), + Term::Var("x".to_string(), DerivedSort::Base(0)), + ), + ]), + ), + }], + axiom_names: vec!["ax/refl".to_string()], + }; + + state.repl.theories.insert( + "Preorder".to_string(), + Rc::new(ElaboratedTheory { + params: vec![], + theory, + }), + ); + + let mut structure = Structure::new(1); + let (a, _) = structure.add_element(&mut state.repl.store.universe, 0); + let (b, _) = structure.add_element(&mut state.repl.store.universe, 0); + structure.init_relations(&[2]); + + let mut entry = + InstanceEntry::new(structure, "Preorder".to_string(), "Preorder".to_string()); + entry.register_element("a".to_string(), a); + entry.register_element("b".to_string(), b); + state.repl.instances.insert("Chain".to_string(), entry); + state.start_chase("Chain"); + state + } + + #[test] + fn gui_step_executes_exactly_one_firing() { + let mut state = build_test_state(); + let mut viz = ChaseVisualization::new(); + + viz.step_chase(&mut state); + + let entry = state.repl.instances.get("Chain").unwrap(); + assert_eq!(entry.structure.relations[0].len(), 1); + + let chase_state = state.chase_state.as_ref().unwrap(); + assert_eq!(chase_state.events.len(), 1); + assert_eq!(chase_state.current_iteration, 1); + assert_eq!(chase_state.mode, ChaseMode::Stepping); + assert!(chase_state.events[0].changed); + } + + #[test] + fn gui_run_all_pauses_after_breakpoint_and_records_event() { + let mut state = build_test_state(); + let mut viz = ChaseVisualization::new(); + state + .chase_state + .as_mut() + .unwrap() + .breakpoints + .insert("refl".to_string()); + + viz.run_chase_to_completion(&mut state); + + let entry = state.repl.instances.get("Chain").unwrap(); + assert_eq!(entry.structure.relations[0].len(), 1); + + let chase_state = state.chase_state.as_ref().unwrap(); + assert_eq!(chase_state.events.len(), 1); + assert_eq!(chase_state.current_iteration, 1); + assert_eq!(chase_state.mode, ChaseMode::Stepping); + } + + #[test] + fn gui_reset_restores_initial_structure() { + let mut state = build_test_state(); + let mut viz = ChaseVisualization::new(); + + viz.step_chase(&mut state); + assert_eq!( + state + .repl + .instances + .get("Chain") + .unwrap() + .structure + .relations[0] + .len(), + 1 + ); + + viz.reset_chase(&mut state); + + let entry = state.repl.instances.get("Chain").unwrap(); + assert_eq!(entry.structure.relations[0].len(), 0); + + let chase_state = state.chase_state.as_ref().unwrap(); + assert!(chase_state.events.is_empty()); + assert_eq!(chase_state.current_iteration, 0); + assert_eq!(chase_state.mode, ChaseMode::Idle); + } +} diff --git a/src/gui/visualizations/graph.rs b/src/gui/visualizations/graph.rs index 86c106b..9633437 100644 --- a/src/gui/visualizations/graph.rs +++ b/src/gui/visualizations/graph.rs @@ -83,7 +83,8 @@ impl GraphView { ui.separator(); // Check if we need to rebuild the graph - if state.graph_instance != self.last_instance || state.graph_relation != self.last_relation { + if state.graph_instance != self.last_instance || state.graph_relation != self.last_relation + { self.rebuild_graph(state); } @@ -109,21 +110,23 @@ impl GraphView { ui.label("Relation:"); egui::ComboBox::from_label("") - .selected_text( - self.selected_relation - .as_deref() - .unwrap_or("(all)"), - ) + .selected_text(self.selected_relation.as_deref().unwrap_or("(all)")) .show_ui(ui, |ui| { - if ui.selectable_label(self.selected_relation.is_none(), "(all)").clicked() { + if ui + .selectable_label(self.selected_relation.is_none(), "(all)") + .clicked() + { self.selected_relation = None; self.needs_layout = true; } for rel in relations { - if ui.selectable_label( - self.selected_relation.as_deref() == Some(rel), - rel, - ).clicked() { + if ui + .selectable_label( + self.selected_relation.as_deref() == Some(rel), + rel, + ) + .clicked() + { self.selected_relation = Some(rel.to_string()); self.needs_layout = true; } @@ -183,7 +186,10 @@ impl GraphView { ui.label("No instances defined."); } else { for instance in instances { - if ui.button(format!("{} : {}", instance.name, instance.theory_name)).clicked() { + if ui + .button(format!("{} : {}", instance.name, instance.theory_name)) + .clicked() + { state.graph_instance = Some(instance.name.clone()); self.needs_layout = true; } @@ -230,7 +236,10 @@ impl GraphView { .unwrap_or_else(|| format!("#{}", slid_idx)); let sort_name = sig.sorts.get(sort_id).cloned().unwrap_or_default(); - let color = sort_colors.get(sort_id).copied().unwrap_or(egui::Color32::GRAY); + let color = sort_colors + .get(sort_id) + .copied() + .unwrap_or(egui::Color32::GRAY); self.nodes.push(GraphNode { id: slid, @@ -326,7 +335,11 @@ impl GraphView { let mut max_y = f32::MIN; for node in &self.nodes { - let pos = self.node_positions.get(&node.id).copied().unwrap_or(node.position); + let pos = self + .node_positions + .get(&node.id) + .copied() + .unwrap_or(node.position); min_x = min_x.min(pos.x); min_y = min_y.min(pos.y); max_x = max_x.max(pos.x); @@ -346,23 +359,58 @@ impl GraphView { // Reset pan to center the content (since our world origin is already at center) let world_center_x = (min_x + max_x) / 2.0; let world_center_y = (min_y + max_y) / 2.0; - self.pan = egui::Vec2::new( - -world_center_x * self.zoom, - -world_center_y * self.zoom, - ); + self.pan = egui::Vec2::new(-world_center_x * self.zoom, -world_center_y * self.zoom); } fn draw_graph(&mut self, ui: &mut egui::Ui) { - let (response, painter) = ui.allocate_painter( - ui.available_size(), - egui::Sense::click_and_drag(), - ); + let (response, painter) = + ui.allocate_painter(ui.available_size(), egui::Sense::click_and_drag()); + let edge_color = ui.visuals().weak_text_color(); + let border_color = ui.visuals().text_color(); + let hint_color = ui.visuals().weak_text_color(); let rect = response.rect; + let pointer_delta = ui.input(|i| i.pointer.delta()); + let pointer_pos = ui.input(|i| i.pointer.hover_pos()); + let initial_zoom = self.zoom; + let initial_pan = self.pan; + + let center = rect.center(); + let hit_test_transform = |pos: egui::Pos2| -> egui::Pos2 { + egui::Pos2::new( + center.x + pos.x * initial_zoom + initial_pan.x, + center.y + pos.y * initial_zoom + initial_pan.y, + ) + }; + + let node_radius = 20.0 * initial_zoom; + let hovered_node = if response.hovered() { + pointer_pos.and_then(|pointer| { + self.nodes.iter().find_map(|node| { + let pos = self + .node_positions + .get(&node.id) + .copied() + .unwrap_or(node.position); + let screen_pos = hit_test_transform(pos); + let node_rect = egui::Rect::from_center_size( + screen_pos, + egui::Vec2::splat(node_radius * 2.0), + ); + node_rect.contains(pointer).then_some(node.id) + }) + }) + } else { + None + }; + + if response.drag_started() && hovered_node.is_some() { + self.dragged_node = hovered_node; + } // Handle panning if response.dragged() && self.dragged_node.is_none() { - self.pan += response.drag_delta(); + self.pan += pointer_delta; } // Handle zooming with scroll @@ -381,8 +429,6 @@ impl GraphView { } } - // Transform helper - centers (0,0) world coordinates in the view - let center = rect.center(); let transform = |pos: egui::Pos2| -> egui::Pos2 { egui::Pos2::new( center.x + pos.x * self.zoom + self.pan.x, @@ -406,14 +452,12 @@ impl GraphView { painter.circle_stroke( loop_center, 10.0 * self.zoom, - egui::Stroke::new(1.5, egui::Color32::LIGHT_GRAY), + egui::Stroke::new(1.5, edge_color), ); } else { // Regular edge - painter.line_segment( - [from_screen, to_screen], - egui::Stroke::new(1.5, egui::Color32::LIGHT_GRAY), - ); + painter + .line_segment([from_screen, to_screen], egui::Stroke::new(1.5, edge_color)); // Draw arrowhead let dir = (to_screen - from_screen).normalized(); @@ -428,7 +472,7 @@ impl GraphView { painter.add(egui::Shape::convex_polygon( vec![p1, p2, p3], - egui::Color32::LIGHT_GRAY, + edge_color, egui::Stroke::NONE, )); } @@ -436,31 +480,18 @@ impl GraphView { } // Draw nodes - let node_radius = 20.0 * self.zoom; - for node in &self.nodes { - let pos = self.node_positions.get(&node.id).copied().unwrap_or(node.position); + let pos = self + .node_positions + .get(&node.id) + .copied() + .unwrap_or(node.position); let screen_pos = transform(pos); - - // Check if node is being hovered or dragged - let node_rect = egui::Rect::from_center_size( - screen_pos, - egui::Vec2::splat(node_radius * 2.0), - ); - - let is_hovered = response.hovered() - && ui.input(|i| i.pointer.hover_pos()) - .map(|p| node_rect.contains(p)) - .unwrap_or(false); - - // Handle node dragging - if is_hovered && response.drag_started() { - self.dragged_node = Some(node.id); - } + let is_hovered = hovered_node == Some(node.id); if self.dragged_node == Some(node.id) { if response.dragged() { - let delta = response.drag_delta() / self.zoom; + let delta = pointer_delta / self.zoom; if let Some(pos) = self.node_positions.get_mut(&node.id) { *pos += delta; } @@ -481,7 +512,7 @@ impl GraphView { painter.circle_stroke( screen_pos, node_radius, - egui::Stroke::new(2.0, egui::Color32::WHITE), + egui::Stroke::new(2.0, border_color), ); // Draw node label @@ -491,7 +522,7 @@ impl GraphView { egui::Align2::CENTER_CENTER, &node.name, egui::FontId::proportional(font_size), - egui::Color32::WHITE, + text_color_for_fill(fill_color), ); // Draw sort label below @@ -501,7 +532,7 @@ impl GraphView { egui::Align2::CENTER_TOP, &node.sort, egui::FontId::proportional(10.0 * self.zoom), - egui::Color32::LIGHT_GRAY, + hint_color, ); } } @@ -513,7 +544,7 @@ impl GraphView { egui::Align2::CENTER_CENTER, "No elements to display", egui::FontId::proportional(16.0), - egui::Color32::GRAY, + hint_color, ); } } @@ -538,6 +569,16 @@ fn generate_sort_colors(count: usize) -> Vec { colors } +fn text_color_for_fill(fill: egui::Color32) -> egui::Color32 { + let luminance = + (0.2126 * fill.r() as f32 + 0.7152 * fill.g() as f32 + 0.0722 * fill.b() as f32) / 255.0; + if luminance > 0.6 { + egui::Color32::BLACK + } else { + egui::Color32::WHITE + } +} + /// Convert HSV to RGB fn hsv_to_rgb(h: f32, s: f32, v: f32) -> (u8, u8, u8) { let c = v * s;