diff --git a/Cargo.lock b/Cargo.lock index eff55c907d..13b40505da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "accesskit" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5351dcebb14b579ccab05f288596b2ae097005be7ee50a7c3d4ca9d0d5a66f6a" +checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320" dependencies = [ "uuid", ] @@ -27,9 +27,9 @@ dependencies = [ [[package]] name = "accesskit_consumer" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53cf47daed85312e763fbf85ceca136e0d7abc68e0a7e12abe11f48172bc3b10" +checksum = "25e0d7e25d06f4dc21d1774d67146e9e80d6789216cbd4d1e88185b0095dba60" dependencies = [ "accesskit", "hashbrown 0.16.1", @@ -37,9 +37,9 @@ dependencies = [ [[package]] name = "accesskit_consumer" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e0d7e25d06f4dc21d1774d67146e9e80d6789216cbd4d1e88185b0095dba60" +checksum = "f950720ce064757a1b629caad3a408e8d2c63bb01f29b8a3ff8daa331053ffeb" dependencies = [ "accesskit", "hashbrown 0.16.1", @@ -67,11 +67,11 @@ checksum = "b016ca8db0ea0ea2ceff29a9d6240391492d960716aa471967c00e8cc8cb197c" dependencies = [ "accesskit", "accesskit_atspi_common", - "async-channel 2.5.0", + "async-channel", "async-executor", "async-task", "atspi", - "futures-lite 2.6.1", + "futures-lite", "futures-util", "serde", "zbus", @@ -79,12 +79,12 @@ dependencies = [ [[package]] name = "accesskit_windows" -version = "0.32.1" +version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff7009f1a532e917d66970a1e80c965140c6cfbbabbdde3d64e5431e6c78e21" +checksum = "36e93ac7bf50b964f1cbb75f741629a4e950571baa1ef1274457ab5a80d9bcc2" dependencies = [ "accesskit", - "accesskit_consumer 0.35.0", + "accesskit_consumer 0.37.0", "hashbrown 0.16.1", "static_assertions", "windows 0.62.2", @@ -296,23 +296,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite", ] -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - [[package]] name = "async-channel" version = "2.5.0" @@ -345,8 +334,8 @@ checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.4.1", - "futures-lite 2.6.1", + "fastrand", + "futures-lite", "pin-project-lite", "slab", ] @@ -359,22 +348,7 @@ checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" dependencies = [ "async-lock", "blocking", - "futures-lite 2.6.1", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.5.0", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite 2.6.1", - "once_cell", + "futures-lite", ] [[package]] @@ -387,7 +361,7 @@ dependencies = [ "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "parking", "polling", "rustix 1.1.4", @@ -401,7 +375,7 @@ version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "pin-project-lite", ] @@ -414,7 +388,7 @@ checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ "async-io", "blocking", - "futures-lite 2.6.1", + "futures-lite", ] [[package]] @@ -423,15 +397,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" dependencies = [ - "async-channel 2.5.0", + "async-channel", "async-io", "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener 5.4.1", - "futures-lite 2.6.1", + "event-listener", + "futures-lite", "rustix 1.1.4", ] @@ -464,47 +438,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "async-std" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io", - "async-lock", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite 2.6.1", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-tar" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1937db2d56578aa3919b9bdb0e5100693fd7d1c0f145c53eb81fbb03e217550" -dependencies = [ - "async-std", - "filetime", - "libc", - "pin-project", - "redox_syscall 0.2.16", - "xattr", -] - [[package]] name = "async-task" version = "4.7.1" @@ -522,19 +455,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "async_zip" -version = "0.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c50d65ce1b0e0cb65a785ff615f78860d7754290647d3b983208daa4f85e6" -dependencies = [ - "async-compression", - "crc32fast", - "futures-lite 2.6.1", - "pin-project", - "thiserror 2.0.18", -] - [[package]] name = "atk" version = "0.18.2" @@ -757,7 +677,7 @@ dependencies = [ "lazycell", "libc", "mach", - "nix 0.19.1", + "nix", "num-traits", "uom", "winapi", @@ -772,7 +692,7 @@ dependencies = [ "bitflags 2.11.1", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.11.0", "log", "prettyplease", "proc-macro2", @@ -900,10 +820,10 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.5.0", + "async-channel", "async-task", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "piper", ] @@ -1276,7 +1196,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "gpui_util", "indexmap", @@ -1311,16 +1231,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "command-fds" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b60b5124979fccd9addd89d8b97a1d6eebb4950694520c75ddd722535ea443f" -dependencies = [ - "nix 0.31.2", - "thiserror 2.0.18", -] - [[package]] name = "compression-codecs" version = "0.4.37" @@ -1329,7 +1239,6 @@ checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" dependencies = [ "bzip2", "compression-core", - "deflate64", "flate2", "memchr", ] @@ -1795,12 +1704,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" -[[package]] -name = "deflate64" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" - [[package]] name = "deranged" version = "0.5.8" @@ -1849,7 +1752,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "proc-macro2", "quote", @@ -2163,12 +2066,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "5.4.1" @@ -2186,7 +2083,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] @@ -2216,15 +2113,6 @@ dependencies = [ "rand 0.8.6", ] -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.4.1" @@ -2396,7 +2284,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" dependencies = [ - "roxmltree", + "roxmltree 0.20.0", ] [[package]] @@ -2524,7 +2412,7 @@ checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" dependencies = [ "fixedbitset", "futures-core", - "futures-lite 2.6.1", + "futures-lite", "pin-project", "smallvec", ] @@ -2552,28 +2440,13 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.4.1", + "fastrand", "futures-core", "futures-io", "parking", @@ -2931,18 +2804,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "glow" version = "0.17.0" @@ -2986,7 +2847,7 @@ dependencies = [ "log", "presser", "thiserror 2.0.18", - "windows 0.62.2", + "windows 0.58.0", ] [[package]] @@ -3012,11 +2873,11 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "accesskit", "anyhow", - "async-channel 2.5.0", + "async-channel", "async-task", "backtrace", "bindgen", @@ -3067,7 +2928,7 @@ dependencies = [ "raw-window-handle", "refineable", "regex", - "resvg", + "resvg 0.46.0", "scheduler", "schemars", "seahash", @@ -3083,7 +2944,7 @@ dependencies = [ "thiserror 2.0.18", "ttf-parser", "url", - "usvg", + "usvg 0.46.0", "util_macros", "uuid", "waker-fn", @@ -3099,7 +2960,7 @@ version = "0.5.2" dependencies = [ "aho-corasick", "anyhow", - "async-channel 2.5.0", + "async-channel", "chrono", "core-text", "enum-iterator", @@ -3125,7 +2986,7 @@ dependencies = [ "paste", "raw-window-handle", "regex", - "resvg", + "resvg 0.45.1", "ropey", "rust-i18n", "rust_decimal", @@ -3259,7 +3120,7 @@ dependencies = [ [[package]] name = "gpui_linux" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "accesskit", "accesskit_unix", @@ -3274,6 +3135,7 @@ dependencies = [ "filedescriptor", "futures", "gpui", + "gpui_util", "gpui_wgpu", "http_client", "image", @@ -3292,7 +3154,6 @@ dependencies = [ "strum", "swash", "url", - "util", "uuid", "wayland-backend", "wayland-client", @@ -3310,7 +3171,7 @@ dependencies = [ [[package]] name = "gpui_macos" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "accesskit", "accesskit_macos", @@ -3332,6 +3193,7 @@ dependencies = [ "foreign-types", "futures", "gpui", + "gpui_util", "image", "itertools 0.14.0", "libc", @@ -3349,7 +3211,6 @@ dependencies = [ "semver", "smallvec", "strum", - "util", "uuid", "zed-font-kit", ] @@ -3357,7 +3218,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -3368,7 +3229,7 @@ dependencies = [ [[package]] name = "gpui_platform" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "console_error_panic_hook", "gpui", @@ -3381,7 +3242,7 @@ dependencies = [ [[package]] name = "gpui_shared_string" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "schemars", "serde", @@ -3391,16 +3252,17 @@ dependencies = [ [[package]] name = "gpui_util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "anyhow", "log", + "which", ] [[package]] name = "gpui_web" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "anyhow", "console_error_panic_hook", @@ -3424,7 +3286,7 @@ dependencies = [ [[package]] name = "gpui_wgpu" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "anyhow", "bytemuck", @@ -3453,15 +3315,17 @@ dependencies = [ [[package]] name = "gpui_windows" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "accesskit", "accesskit_windows", "anyhow", "collections", + "dunce", "etagere", "futures", "gpui", + "gpui_util", "image", "itertools 0.14.0", "log", @@ -3469,7 +3333,6 @@ dependencies = [ "rand 0.9.4", "raw-window-handle", "smallvec", - "util", "uuid", "windows 0.61.3", "windows-core 0.61.2", @@ -3768,12 +3631,10 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "anyhow", "async-compression", - "async-fs", - "async-tar", "bytes", "derive_more 2.1.1", "futures", @@ -3784,16 +3645,13 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sha2", - "tempfile", "url", - "util", ] [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "rustls", "rustls-platform-verifier", @@ -3874,7 +3732,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.58.0", ] [[package]] @@ -4057,6 +3915,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" +[[package]] +name = "imagesize" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" + [[package]] name = "imgref" version = "1.12.0" @@ -4377,12 +4241,15 @@ dependencies = [ ] [[package]] -name = "kv-log-macro" -version = "1.0.7" +name = "kurbo" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" dependencies = [ - "log", + "arrayvec", + "euclid", + "polycool", + "smallvec", ] [[package]] @@ -4806,7 +4673,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "anyhow", "bindgen", @@ -4913,8 +4780,9 @@ dependencies = [ [[package]] name = "naga" -version = "29.0.3" -source = "git+https://github.com/zed-industries/wgpu.git?rev=357a0c56e0070480ad9daea5d2eaa83150b79e88#357a0c56e0070480ad9daea5d2eaa83150b79e88" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2bf919621e7975acb27d881bae2fb993e0d45c8e0446e85e6272971e00dc8df" dependencies = [ "arrayvec", "bit-set 0.9.1", @@ -4987,30 +4855,6 @@ dependencies = [ "libc", ] -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.11.1", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "nix" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" -dependencies = [ - "bitflags 2.11.1", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "no_std_io2" version = "0.9.3" @@ -5240,7 +5084,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.117", @@ -5621,7 +5465,7 @@ dependencies = [ "cipher", "digest", "endi", - "futures-lite 2.6.1", + "futures-lite", "futures-util", "getrandom 0.4.2", "hkdf", @@ -5798,7 +5642,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "collections", "serde", @@ -5945,7 +5789,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ - "fastrand 2.4.1", + "fastrand", "phf_shared 0.13.1", ] @@ -6057,12 +5901,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "piper" version = "0.2.5" @@ -6070,7 +5908,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", - "fastrand 2.4.1", + "fastrand", "futures-io", ] @@ -6138,6 +5976,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" +[[package]] +name = "polycool" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" +dependencies = [ + "arrayvec", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -6741,15 +6588,6 @@ dependencies = [ "font-types", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.18" @@ -6802,7 +6640,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "derive_refineable", ] @@ -6854,7 +6692,7 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "anyhow", "bytes", @@ -6880,12 +6718,29 @@ dependencies = [ "log", "pico-args", "rgb", - "svgtypes", + "svgtypes 0.15.3", "tiny-skia", - "usvg", + "usvg 0.45.1", "zune-jpeg 0.4.21", ] +[[package]] +name = "resvg" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" +dependencies = [ + "gif 0.14.2", + "image-webp", + "log", + "pico-args", + "rgb", + "svgtypes 0.16.1", + "tiny-skia", + "usvg 0.46.0", + "zune-jpeg 0.5.15", +] + [[package]] name = "rgb" version = "0.8.53" @@ -6962,6 +6817,15 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" +[[package]] +name = "roxmltree" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" +dependencies = [ + "memchr", +] + [[package]] name = "rust-embed" version = "8.11.0" @@ -7268,7 +7132,7 @@ dependencies = [ [[package]] name = "scheduler" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "async-task", "backtrace", @@ -7478,19 +7342,6 @@ dependencies = [ "zmij", ] -[[package]] -name = "serde_json_lenient" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e033097bf0d2b59a62b42c18ebbb797503839b26afdda2c4e1415cb6c813540" -dependencies = [ - "indexmap", - "itoa", - "memchr", - "ryu", - "serde", -] - [[package]] name = "serde_repr" version = "0.1.20" @@ -7696,7 +7547,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" dependencies = [ - "async-channel 2.5.0", + "async-channel", "async-executor", "async-fs", "async-io", @@ -7704,7 +7555,7 @@ dependencies = [ "async-net", "async-process", "blocking", - "futures-lite 2.6.1", + "futures-lite", ] [[package]] @@ -7902,7 +7753,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "heapless", "log", @@ -8001,7 +7852,17 @@ version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" dependencies = [ - "kurbo", + "kurbo 0.11.3", + "siphasher 1.0.2", +] + +[[package]] +name = "svgtypes" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" +dependencies = [ + "kurbo 0.13.1", "siphasher 1.0.2", ] @@ -8158,12 +8019,6 @@ dependencies = [ "slotmap", ] -[[package]] -name = "take-until" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bdb6fa0dfa67b38c1e66b7041ba9dcf23b99d8121907cd31c807a332f7a0bbb" - [[package]] name = "tao-core-video-sys" version = "0.2.0" @@ -8205,7 +8060,7 @@ version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.4.1", + "fastrand", "getrandom 0.4.2", "once_cell", "rustix 1.1.4", @@ -9300,16 +9155,43 @@ dependencies = [ "data-url", "flate2", "fontdb", - "imagesize", - "kurbo", + "imagesize 0.13.0", + "kurbo 0.11.3", + "log", + "pico-args", + "roxmltree 0.20.0", + "rustybuzz", + "simplecss", + "siphasher 1.0.2", + "strict-num", + "svgtypes 0.15.3", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + +[[package]] +name = "usvg" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e419dff010bb12512b0ae9e3d2f318dfbdf0167fde7eb05465134d4e8756076f" +dependencies = [ + "base64", + "data-url", + "flate2", + "fontdb", + "imagesize 0.14.0", + "kurbo 0.13.1", "log", "pico-args", - "roxmltree", + "roxmltree 0.21.1", "rustybuzz", "simplecss", "siphasher 1.0.2", "strict-num", - "svgtypes", + "svgtypes 0.16.1", "tiny-skia-path", "unicode-bidi", "unicode-script", @@ -9329,49 +9211,10 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "util" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" -dependencies = [ - "anyhow", - "async-fs", - "async_zip", - "collections", - "command-fds", - "dirs", - "dunce", - "futures", - "futures-lite 1.13.0", - "globset", - "gpui_util", - "itertools 0.14.0", - "libc", - "log", - "mach2", - "nix 0.29.0", - "percent-encoding", - "regex", - "rust-embed", - "schemars", - "serde", - "serde_json", - "serde_json_lenient", - "shlex", - "smol", - "take-until", - "tempfile", - "tendril", - "unicase", - "url", - "walkdir", - "which", -] - [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "perf", "quote", @@ -9893,8 +9736,9 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "29.0.3" -source = "git+https://github.com/zed-industries/wgpu.git?rev=357a0c56e0070480ad9daea5d2eaa83150b79e88#357a0c56e0070480ad9daea5d2eaa83150b79e88" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e8840e1ba2881d4cbb18d2147627a56af426ff064c0401eb0c8410c6325d07" dependencies = [ "arrayvec", "bitflags 2.11.1", @@ -9922,8 +9766,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "29.0.3" -source = "git+https://github.com/zed-industries/wgpu.git?rev=357a0c56e0070480ad9daea5d2eaa83150b79e88#357a0c56e0070480ad9daea5d2eaa83150b79e88" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f519832254e56965a9940c4af57dcb75f702b6f6fa4a0b172f685395843a4d7" dependencies = [ "arrayvec", "bit-set 0.9.1", @@ -9954,32 +9799,36 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "29.0.3" -source = "git+https://github.com/zed-industries/wgpu.git?rev=357a0c56e0070480ad9daea5d2eaa83150b79e88#357a0c56e0070480ad9daea5d2eaa83150b79e88" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e39e26c4c0e07589e67d18546cf79ff45383659fc72fca4dd293358a0347f3" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "29.0.3" -source = "git+https://github.com/zed-industries/wgpu.git?rev=357a0c56e0070480ad9daea5d2eaa83150b79e88#357a0c56e0070480ad9daea5d2eaa83150b79e88" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e09be551dc939498bdd5f6b2c66e55ab275dad25825267a08605a80fc9f0af" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "29.0.3" -source = "git+https://github.com/zed-industries/wgpu.git?rev=357a0c56e0070480ad9daea5d2eaa83150b79e88#357a0c56e0070480ad9daea5d2eaa83150b79e88" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e592c1bbef6ad047647ae6e666ebd8cee7a32bb4544d9700ec96cbf73230257" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "29.0.3" -source = "git+https://github.com/zed-industries/wgpu.git?rev=357a0c56e0070480ad9daea5d2eaa83150b79e88#357a0c56e0070480ad9daea5d2eaa83150b79e88" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ace1c17727311c22a46e4e3faf56ea6de81af99dcc839bdfb54857b94d448d" dependencies = [ "android_system_properties", "arrayvec", @@ -10031,8 +9880,9 @@ dependencies = [ [[package]] name = "wgpu-naga-bridge" -version = "29.0.3" -source = "git+https://github.com/zed-industries/wgpu.git?rev=357a0c56e0070480ad9daea5d2eaa83150b79e88#357a0c56e0070480ad9daea5d2eaa83150b79e88" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95226013f547544b223281cd16a4fb549aa9dcb562adbda0faae4c73ffbbc161" dependencies = [ "naga", "wgpu-types", @@ -10040,8 +9890,9 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "29.0.3" -source = "git+https://github.com/zed-industries/wgpu.git?rev=357a0c56e0070480ad9daea5d2eaa83150b79e88#357a0c56e0070480ad9daea5d2eaa83150b79e88" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84bf84cd9ca8ca45e2b223a3868f1adf9bfc0c66aeac212e76ee7e40fdadf8f5" dependencies = [ "bitflags 2.11.1", "bytemuck", @@ -10923,15 +10774,6 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" -[[package]] -name = "xattr" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" -dependencies = [ - "libc", -] - [[package]] name = "xcb" version = "1.7.0" @@ -11069,9 +10911,9 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener 5.4.1", + "event-listener", "futures-core", - "futures-lite 2.6.1", + "futures-lite", "hex", "libc", "ordered-stream", @@ -11372,7 +11214,7 @@ dependencies = [ [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "anyhow", "chrono", @@ -11389,7 +11231,7 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" dependencies = [ "tracing", "tracing-subscriber", @@ -11400,7 +11242,7 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=1d217ee39d381ac101b7cf49d3d22451ac1093fe#1d217ee39d381ac101b7cf49d3d22451ac1093fe" +source = "git+https://github.com/zed-industries/zed#c545fb67d0ce13e335bff76f7c08986000333f2c" [[package]] name = "zune-core" diff --git a/Cargo.toml b/Cargo.toml index ea55474efe..0bc3fabb25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,11 +32,11 @@ gpui-component-macros = { path = "crates/macros", version = "0.5.1" } gpui-component-assets = { path = "crates/assets", version = "0.5.1" } story = { path = "crates/story" } -gpui = { git = "https://github.com/zed-industries/zed", rev = "1d217ee39d381ac101b7cf49d3d22451ac1093fe" } -gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "1d217ee39d381ac101b7cf49d3d22451ac1093fe", features = ["font-kit", "x11", "wayland", "runtime_shaders"] } -gpui_web = { git = "https://github.com/zed-industries/zed", rev = "1d217ee39d381ac101b7cf49d3d22451ac1093fe" } -gpui_macros = { git = "https://github.com/zed-industries/zed", rev = "1d217ee39d381ac101b7cf49d3d22451ac1093fe" } -reqwest_client = { git = "https://github.com/zed-industries/zed", rev = "1d217ee39d381ac101b7cf49d3d22451ac1093fe" } +gpui = { git = "https://github.com/zed-industries/zed" } +gpui_platform = { git = "https://github.com/zed-industries/zed", features = ["font-kit", "x11", "wayland", "runtime_shaders"] } +gpui_web = { git = "https://github.com/zed-industries/zed" } +gpui_macros = { git = "https://github.com/zed-industries/zed" } +reqwest_client = { git = "https://github.com/zed-industries/zed" } sum-tree = { version = "0.2.0", package = "zed-sum-tree" } # reqwest = { version = "0.12.15-zed", package = "zed-reqwest" } reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "c15662463bda39148ba154100dd44d3fba5873a4", default-features = false, features = [ diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index e591626cf8..e655c37086 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -1,5 +1,5 @@ use gpui_component_assets::Assets; -use gpui_component_story::{Gallery, init, create_new_window}; +use gpui_component_story::{Gallery, create_new_window, init}; fn main() { let app = gpui_platform::application().with_assets(Assets); diff --git a/crates/story/src/stories/button_story.rs b/crates/story/src/stories/button_story.rs index 749ce0aed3..012aa42823 100644 --- a/crates/story/src/stories/button_story.rs +++ b/crates/story/src/stories/button_story.rs @@ -491,7 +491,7 @@ impl Render for ButtonStory { section("With Dropdown Caret") .max_w_lg() .child( - Button::new("button-outline-1") + Button::new("button-dropdown-caret-primary") .primary() .dropdown_caret(true) .label("Primary Button") @@ -502,7 +502,7 @@ impl Render for ButtonStory { .on_click(Self::on_click), ) .child( - Button::new("button-outline-2") + Button::new("button-dropdown-caret-default") .label("Default Button") .dropdown_caret(true) .disabled(disabled) @@ -523,7 +523,7 @@ impl Render for ButtonStory { .on_click(Self::on_click), ) .child( - Button::new("button-outline-5-ghost") + Button::new("button-dropdown-caret-ghost") .ghost() .dropdown_caret(true) .label("Ghost Button") @@ -534,7 +534,7 @@ impl Render for ButtonStory { .on_click(Self::on_click), ) .child( - Button::new("button-outline-5-link") + Button::new("button-dropdown-caret-link") .link() .dropdown_caret(true) .label("Link Button") @@ -545,7 +545,7 @@ impl Render for ButtonStory { .on_click(Self::on_click), ) .child( - Button::new("button-outline-5-text") + Button::new("button-dropdown-caret-small") .outline() .small() .dropdown_caret(true) @@ -735,7 +735,7 @@ impl Render for ButtonStory { .layout(Axis::Vertical) .disabled(disabled) .child( - Button::new("button-one") + Button::new("button-vertical-one") .label("One") .disabled(disabled) .selected(selected) @@ -743,7 +743,7 @@ impl Render for ButtonStory { .on_click(Self::on_click), ) .child( - Button::new("button-two") + Button::new("button-vertical-two") .label("Two") .disabled(disabled) .selected(selected) @@ -751,7 +751,7 @@ impl Render for ButtonStory { .on_click(Self::on_click), ) .child( - Button::new("button-three") + Button::new("button-vertical-three") .label("Three") .disabled(disabled) .selected(selected) diff --git a/crates/ui/Cargo.toml b/crates/ui/Cargo.toml index c0dcc418e9..a9d4ca51c3 100644 --- a/crates/ui/Cargo.toml +++ b/crates/ui/Cargo.toml @@ -193,6 +193,7 @@ objc2-app-kit = { version = "0.3", features = [ "NSMenu", "NSMenuItem", "NSView", + "NSWindow", "NSResponder", "NSEvent", "NSImage", diff --git a/crates/ui/src/accordion.rs b/crates/ui/src/accordion.rs index 6c8d907b2b..00222b967b 100644 --- a/crates/ui/src/accordion.rs +++ b/crates/ui/src/accordion.rs @@ -2,7 +2,7 @@ use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc}; use gpui::{ AnyElement, App, ElementId, InteractiveElement as _, IntoElement, ParentElement, RenderOnce, - SharedString, StatefulInteractiveElement as _, Styled, Window, div, + Role, SharedString, StatefulInteractiveElement as _, Styled, Window, div, prelude::FluentBuilder as _, rems, }; @@ -240,6 +240,8 @@ impl RenderOnce for AccordionItem { .child( h_flex() .id(self.index) + .role(Role::Button) + .aria_expanded(self.open) .justify_between() .gap_3() .map(|this| match self.size { diff --git a/crates/ui/src/alert.rs b/crates/ui/src/alert.rs index dab867bfe6..7ceb3a9f2d 100644 --- a/crates/ui/src/alert.rs +++ b/crates/ui/src/alert.rs @@ -2,8 +2,8 @@ use std::rc::Rc; use gpui::{ App, ClickEvent, ElementId, Empty, Hsla, InteractiveElement, IntoElement, ParentElement as _, - RenderOnce, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Window, div, - prelude::FluentBuilder as _, px, rems, transparent_white, + RenderOnce, Role, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Window, + div, prelude::FluentBuilder as _, px, rems, transparent_white, }; use crate::{ @@ -189,6 +189,7 @@ impl RenderOnce for Alert { h_flex() .id(self.id) + .role(Role::Alert) .w_full() .text_color(fg) .bg(bg) diff --git a/crates/ui/src/breadcrumb.rs b/crates/ui/src/breadcrumb.rs index 328c110fbd..f579a26289 100644 --- a/crates/ui/src/breadcrumb.rs +++ b/crates/ui/src/breadcrumb.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use gpui::{ div, prelude::FluentBuilder as _, App, ClickEvent, ElementId, InteractiveElement as _, - IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, + IntoElement, ParentElement, RenderOnce, Role, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Window, }; @@ -92,6 +92,11 @@ impl RenderOnce for BreadcrumbItem { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { div() .id(self.id) + .role(if self.on_click.is_some() && !self.disabled { + Role::Link + } else { + Role::ListItem + }) .child(self.label) .text_color(cx.theme().muted_foreground) .when(self.is_last, |this| this.text_color(cx.theme().foreground)) diff --git a/crates/ui/src/button/button.rs b/crates/ui/src/button/button.rs index 0da2553915..26cda784a8 100644 --- a/crates/ui/src/button/button.rs +++ b/crates/ui/src/button/button.rs @@ -10,8 +10,8 @@ use crate::{ use gpui::{ AnyElement, App, Background, ClickEvent, Corners, Div, Edges, ElementId, Hsla, InteractiveElement, Interactivity, IntoElement, MouseButton, ParentElement, Pixels, RenderOnce, - SharedString, Stateful, StatefulInteractiveElement as _, StyleRefinement, Styled, Window, div, - prelude::FluentBuilder as _, px, relative, transparent_white, + Role, SharedString, Stateful, StatefulInteractiveElement as _, StyleRefinement, Styled, Window, + div, prelude::FluentBuilder as _, px, relative, transparent_white, }; #[derive(Default, Clone, Copy)] @@ -461,6 +461,15 @@ impl RenderOnce for Button { }; self.base + .role(if self.variant.is_link() { + Role::Link + } else { + Role::Button + }) + .when_some(self.label.as_ref(), |this, label| { + this.aria_label(label.clone()) + }) + .aria_selected(self.selected) .when(!self.disabled, |this| { this.track_focus( &focus_handle diff --git a/crates/ui/src/button/dropdown_button.rs b/crates/ui/src/button/dropdown_button.rs index b64b633f64..035366f27a 100644 --- a/crates/ui/src/button/dropdown_button.rs +++ b/crates/ui/src/button/dropdown_button.rs @@ -1,8 +1,7 @@ use gpui::Corners; use gpui::{ - Anchor, App, Context, Edges, ElementId, InteractiveElement as _, IntoElement, - ParentElement, RenderOnce, SharedString, StyleRefinement, Styled, Window, div, - prelude::FluentBuilder, + Anchor, App, Context, Edges, ElementId, InteractiveElement as _, IntoElement, ParentElement, + RenderOnce, SharedString, StyleRefinement, Styled, Window, div, prelude::FluentBuilder, }; use crate::{ diff --git a/crates/ui/src/button/toggle.rs b/crates/ui/src/button/toggle.rs index 9afe36ff66..6ab303fbc2 100644 --- a/crates/ui/src/button/toggle.rs +++ b/crates/ui/src/button/toggle.rs @@ -2,8 +2,8 @@ use std::{cell::Cell, rc::Rc}; use gpui::{ AnyElement, App, Corners, Edges, ElementId, InteractiveElement, IntoElement, ParentElement, - RenderOnce, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Window, div, - prelude::FluentBuilder as _, + RenderOnce, Role, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Toggled, + Window, div, prelude::FluentBuilder as _, }; use smallvec::{SmallVec, smallvec}; @@ -156,6 +156,12 @@ impl RenderOnce for Toggle { div() .id(self.id) + .role(Role::Button) + .aria_toggled(if checked { Toggled::True } else { Toggled::False }) + .when_some( + self.tooltip.text.as_ref().map(|(text, _)| text.clone()), + |this, label| this.aria_label(label), + ) .flex() .flex_row() .items_center() @@ -309,6 +315,7 @@ impl RenderOnce for ToggleGroup { h_flex() .id(self.id) + .role(Role::Toolbar) .when(!self.segmented, |this| this.gap_2()) .refine_style(&self.style) .children(self.items.into_iter().enumerate().map({ diff --git a/crates/ui/src/checkbox.rs b/crates/ui/src/checkbox.rs index 0dae528d69..7ad21250e6 100644 --- a/crates/ui/src/checkbox.rs +++ b/crates/ui/src/checkbox.rs @@ -6,8 +6,8 @@ use crate::{ }; use gpui::{ Animation, AnimationExt, AnyElement, App, Div, ElementId, InteractiveElement, IntoElement, - ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, - Window, div, prelude::FluentBuilder as _, px, relative, rems, svg, + ParentElement, RenderOnce, Role, SharedString, StatefulInteractiveElement, StyleRefinement, + Styled, Toggled, Window, div, prelude::FluentBuilder as _, px, relative, rems, svg, }; /// A Checkbox element. @@ -222,6 +222,12 @@ impl RenderOnce for Checkbox { div().child( self.base .id(self.id.clone()) + .role(Role::CheckBox) + .aria_toggled(if checked { Toggled::True } else { Toggled::False }) + .when_some( + self.label.as_ref().map(|l| l.get_text(cx)), + |this, label| this.aria_label(label), + ) .when(!self.disabled, |this| { this.track_focus( &focus_handle diff --git a/crates/ui/src/combobox.rs b/crates/ui/src/combobox.rs index cea5324621..6474c58f33 100644 --- a/crates/ui/src/combobox.rs +++ b/crates/ui/src/combobox.rs @@ -1,7 +1,7 @@ use gpui::{ AnyElement, App, Bounds, ClickEvent, Context, DismissEvent, Edges, ElementId, Entity, EventEmitter, FocusHandle, Focusable, Hsla, InteractiveElement, IntoElement, KeyBinding, - Length, MouseDownEvent, ParentElement, Pixels, Render, RenderOnce, SharedString, + Length, MouseDownEvent, ParentElement, Pixels, Render, RenderOnce, Role, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Window, anchored, deferred, div, prelude::FluentBuilder, px, rems, }; @@ -881,8 +881,12 @@ where } }); + let is_open = self.state.read(cx).state.open; + div() .id(self.id.clone()) + .role(Role::ComboBox) + .aria_expanded(is_open) .key_context(CONTEXT) .when(!disabled, |this| { this.track_focus(&focus_handle.tab_stop(true)) diff --git a/crates/ui/src/dialog/alert_dialog.rs b/crates/ui/src/dialog/alert_dialog.rs index f855d2f16a..3640eac009 100644 --- a/crates/ui/src/dialog/alert_dialog.rs +++ b/crates/ui/src/dialog/alert_dialog.rs @@ -275,6 +275,7 @@ impl AlertDialog { self.base .button_props(button_props.clone()) + .alert_dialog_role() .when(has_header, |this| { this.header( DialogHeader::new().child( diff --git a/crates/ui/src/dialog/dialog.rs b/crates/ui/src/dialog/dialog.rs index d7e0e94903..52b5a42c95 100644 --- a/crates/ui/src/dialog/dialog.rs +++ b/crates/ui/src/dialog/dialog.rs @@ -3,8 +3,9 @@ use std::{rc::Rc, sync::LazyLock, time::Duration}; use gpui::{ Animation, AnimationExt as _, AnyElement, App, Bounds, BoxShadow, ClickEvent, Edges, FocusHandle, Hsla, InteractiveElement, IntoElement, KeyBinding, MouseButton, ParentElement, - Pixels, Point, RenderOnce, SharedString, StyleRefinement, Styled, Window, WindowControlArea, - actions, anchored, div, hsla, point, prelude::FluentBuilder, px, + Pixels, Point, RenderOnce, Role, SharedString, StatefulInteractiveElement as _, StyleRefinement, + Styled, Window, WindowControlArea, actions, anchored, div, hsla, point, prelude::FluentBuilder, + px, }; use rust_i18n::t; @@ -208,6 +209,7 @@ pub struct Dialog { pub(crate) footer: Option, pub(crate) content_builder: Option, pub(crate) props: DialogProps, + pub(crate) a11y_role: Role, button_props: DialogButtonProps, @@ -239,6 +241,7 @@ impl Dialog { children: Vec::new(), layer_ix: 0, button_props: DialogButtonProps::default(), + a11y_role: Role::Dialog, } } @@ -287,6 +290,11 @@ impl Dialog { self } + pub(crate) fn alert_dialog_role(mut self) -> Self { + self.a11y_role = Role::AlertDialog; + self + } + /// Sets the callback for when the dialog is closed. /// /// Called after [`Self::on_ok`] or [`Self::on_cancel`] callback. @@ -523,6 +531,7 @@ impl RenderOnce for Dialog { .child( v_flex() .id(layer_ix) + .role(self.a11y_role) .track_focus(&self.focus_handle) .focus_trap(format!("dialog-{}", layer_ix), &self.focus_handle) .bg(cx.theme().tokens.background) diff --git a/crates/ui/src/input/input.rs b/crates/ui/src/input/input.rs index c86c896b1d..97dc5a9023 100644 --- a/crates/ui/src/input/input.rs +++ b/crates/ui/src/input/input.rs @@ -3,8 +3,8 @@ use std::rc::Rc; use gpui::prelude::FluentBuilder as _; use gpui::{ AnyElement, App, DefiniteLength, Edges, EdgesRefinement, Entity, Hsla, InteractiveElement as _, - IntoElement, MouseButton, ParentElement as _, Rems, RenderOnce, StyleRefinement, Styled, - TextAlign, Window, div, px, relative, + IntoElement, MouseButton, ParentElement as _, Rems, RenderOnce, Role, + StatefulInteractiveElement as _, StyleRefinement, Styled, TextAlign, Window, div, px, relative, }; use crate::button::{Button, ButtonVariants as _}; @@ -289,6 +289,11 @@ impl RenderOnce for Input { div() .id(("input", self.state.entity_id())) + .role(if state.mode.is_multi_line() { + Role::MultilineTextInput + } else { + Role::TextInput + }) .flex() .key_context(crate::input::CONTEXT) .track_focus(&state.focus_handle.clone()) diff --git a/crates/ui/src/input/number_input.rs b/crates/ui/src/input/number_input.rs index ffecc68dfa..b92ba239ad 100644 --- a/crates/ui/src/input/number_input.rs +++ b/crates/ui/src/input/number_input.rs @@ -5,8 +5,9 @@ use gpui::Corners; use gpui::Window; use gpui::{AnyElement, App, Context, Edges, Entity, EventEmitter, FocusHandle, Focusable}; use gpui::{ - InteractiveElement, IntoElement, KeyBinding, ParentElement, RenderOnce, SharedString, - StyleRefinement, Styled, TextAlign, actions, prelude::FluentBuilder as _, + InteractiveElement, IntoElement, KeyBinding, ParentElement, RenderOnce, Role, SharedString, + StatefulInteractiveElement as _, StyleRefinement, Styled, TextAlign, actions, + prelude::FluentBuilder as _, }; use crate::{ @@ -296,8 +297,12 @@ impl RenderOnce for NumberInput { }); } + let numeric_value = self.state.read(cx).value().parse::().ok(); + h_flex() .id(("number-input", self.state.entity_id())) + .role(Role::SpinButton) + .when_some(numeric_value, |this, v| this.aria_numeric_value(v)) .key_context(CONTEXT) .on_action(window.listener_for(&self.state, InputState::on_action_increment)) .on_action(window.listener_for(&self.state, InputState::on_action_decrement)) diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index a6fe7f6e99..6c79bf3b13 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -11,6 +11,8 @@ mod icon; mod index_path; #[cfg(any(feature = "inspector", debug_assertions))] mod inspector; +#[cfg(target_os = "macos")] +mod macos_accessibility; mod root; mod styled; mod time; diff --git a/crates/ui/src/list/list.rs b/crates/ui/src/list/list.rs index 4b114aa250..8016c3d649 100644 --- a/crates/ui/src/list/list.rs +++ b/crates/ui/src/list/list.rs @@ -14,7 +14,7 @@ use crate::{Icon, IndexPath, Selectable, Sizable, StyledExt}; use crate::{VirtualListScrollHandle, list::ListDelegate, v_virtual_list}; use gpui::{ App, AvailableSpace, ClickEvent, Context, DefiniteLength, EdgesRefinement, EventEmitter, - ListSizingBehavior, RenderOnce, ScrollStrategy, SharedString, StatefulInteractiveElement, + ListSizingBehavior, RenderOnce, Role, ScrollStrategy, SharedString, StatefulInteractiveElement, StyleRefinement, Subscription, px, size, }; use gpui::{ @@ -463,8 +463,14 @@ where .unwrap_or(false); let id = SharedString::from(format!("list-item-{}", ix)); + let total_items = self.rows_cache.items_count(); + div() .id(id) + .role(Role::ListItem) + .aria_position_in_set(ix.row + 1) + .aria_size_of_set(total_items) + .aria_selected(selected) .w_full() .relative() .overflow_hidden() @@ -754,6 +760,7 @@ where div() .id("list") + .role(Role::List) .size_full() .refine_style(&self.style) .child(self.state.clone()) diff --git a/crates/ui/src/macos_accessibility.rs b/crates/ui/src/macos_accessibility.rs new file mode 100644 index 0000000000..4df64bae30 --- /dev/null +++ b/crates/ui/src/macos_accessibility.rs @@ -0,0 +1,81 @@ +use std::{ffi::CString, ptr::null_mut}; + +use gpui::Window; +use objc2::{ + Message, + encode::{Encode, EncodeArguments, EncodeReturn, Encoding}, + ffi::{class_addMethod, object_getClass}, + msg_send, + runtime::{AnyClass, AnyObject, MethodImplementation, Sel}, + sel, +}; +use objc2_app_kit::{NSView, NSWindow}; +use objc2_foundation::NSPoint; +use raw_window_handle::{HasWindowHandle, RawWindowHandle}; + +pub(crate) fn install_window_hit_test_forwarder(window: &Window) { + let Some(view) = ns_view(window) else { + return; + }; + let Some(window) = view.window() else { + return; + }; + + unsafe { + let class = object_getClass((&*window as *const NSWindow).cast::()); + if !class.is_null() { + add_method( + class.cast_mut(), + sel!(accessibilityHitTest:), + hit_test_forwarder as extern "C" fn(_, _, _) -> _, + ); + } + } +} + +extern "C" fn hit_test_forwarder(this: &NSWindow, _cmd: Sel, point: NSPoint) -> *mut AnyObject { + this.contentView().map_or_else(null_mut, |view| unsafe { + msg_send![&*view, accessibilityHitTest: point] + }) +} + +fn ns_view(window: &Window) -> Option<&NSView> { + let handle = HasWindowHandle::window_handle(window).ok()?; + let RawWindowHandle::AppKit(handle) = handle.as_raw() else { + return None; + }; + unsafe { (handle.ns_view.as_ptr() as *const NSView).as_ref() } +} + +unsafe fn add_method(class: *mut AnyClass, sel: Sel, func: F) +where + T: Message + ?Sized, + F: MethodImplementation, +{ + let encs = F::Arguments::ENCODINGS; + debug_assert_eq!( + count_args(sel), + encs.len(), + "selector {sel:?} argument count does not match method implementation" + ); + + let types = method_type_encoding(&F::Return::ENCODING_RETURN, encs); + let success = unsafe { class_addMethod(class, sel, func.__imp(), types.as_ptr()) }; + + // A false result usually means the class already has this method, for example + // after GPUI grows its own hit-test forwarder. Keep the shim non-invasive. + let _ = success.as_bool(); +} + +fn count_args(sel: Sel) -> usize { + sel.name().to_bytes().iter().filter(|&&c| c == b':').count() +} + +fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString { + let mut types = format!("{ret}{}{}", <*mut AnyObject>::ENCODING, Sel::ENCODING); + for enc in args { + use core::fmt::Write; + write!(&mut types, "{enc}").unwrap(); + } + CString::new(types).unwrap() +} diff --git a/crates/ui/src/menu/app_menu_bar.rs b/crates/ui/src/menu/app_menu_bar.rs index ee3d600fb5..ac2f12e06b 100644 --- a/crates/ui/src/menu/app_menu_bar.rs +++ b/crates/ui/src/menu/app_menu_bar.rs @@ -9,7 +9,7 @@ use crate::{ use gpui::{ App, AppContext as _, ClickEvent, Context, DismissEvent, Entity, FocusHandle, Focusable, InteractiveElement as _, IntoElement, KeyBinding, MouseButton, OwnedMenu, ParentElement, - Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Window, anchored, + Render, Role, SharedString, StatefulInteractiveElement, Styled, Subscription, Window, anchored, deferred, div, prelude::FluentBuilder, px, }; @@ -120,6 +120,7 @@ impl Render for AppMenuBar { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { h_flex() .id("app-menu-bar") + .role(Role::MenuBar) .key_context(CONTEXT) .on_action(cx.listener(Self::on_move_left)) .on_action(cx.listener(Self::on_move_right)) diff --git a/crates/ui/src/menu/menu_item.rs b/crates/ui/src/menu/menu_item.rs index d590a3aae1..29eea9d158 100644 --- a/crates/ui/src/menu/menu_item.rs +++ b/crates/ui/src/menu/menu_item.rs @@ -1,8 +1,8 @@ use crate::{ActiveTheme, Disableable, StyledExt, h_flex}; use gpui::{ AnyElement, App, ClickEvent, ElementId, InteractiveElement, IntoElement, MouseButton, - ParentElement, RenderOnce, SharedString, StatefulInteractiveElement as _, StyleRefinement, - Styled, Window, prelude::FluentBuilder as _, + ParentElement, RenderOnce, Role, SharedString, StatefulInteractiveElement as _, + StyleRefinement, Styled, Window, prelude::FluentBuilder as _, }; use smallvec::SmallVec; @@ -10,6 +10,7 @@ use smallvec::SmallVec; pub(crate) struct MenuItemElement { id: ElementId, group_name: SharedString, + aria_label: Option, style: StyleRefinement, disabled: bool, selected: bool, @@ -25,6 +26,7 @@ impl MenuItemElement { Self { id: id.clone(), group_name: group_name.into(), + aria_label: None, style: StyleRefinement::default(), disabled: false, selected: false, @@ -40,6 +42,12 @@ impl MenuItemElement { self } + /// Set the accessible label for the menu item. + pub(crate) fn aria_label(mut self, label: impl Into) -> Self { + self.aria_label = Some(label.into()); + self + } + /// Set the disabled state of the MenuItem. pub(crate) fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; @@ -86,6 +94,9 @@ impl RenderOnce for MenuItemElement { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { h_flex() .id(self.id) + .role(Role::MenuItem) + .when_some(self.aria_label, |this, label| this.aria_label(label)) + .aria_selected(self.selected) .group(&self.group_name) .gap_x_1() .py_1() @@ -121,3 +132,15 @@ impl RenderOnce for MenuItemElement { .children(self.children) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[gpui::test] + fn aria_label_sets_accessible_name(_cx: &mut gpui::TestAppContext) { + let item = MenuItemElement::new("open", "menu").aria_label("Open"); + + assert_eq!(item.aria_label, Some("Open".into())); + } +} diff --git a/crates/ui/src/menu/popup_menu.rs b/crates/ui/src/menu/popup_menu.rs index 78d32b8903..0d564e54c9 100644 --- a/crates/ui/src/menu/popup_menu.rs +++ b/crates/ui/src/menu/popup_menu.rs @@ -7,8 +7,8 @@ use crate::{Side, Size, StyledExt, kbd::Kbd}; use gpui::{ Action, Anchor, AnyElement, App, AppContext, Bounds, Context, DismissEvent, Edges, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding, - ParentElement, Pixels, Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, - WeakEntity, Window, anchored, div, prelude::FluentBuilder, px, rems, + ParentElement, Pixels, Render, Role, ScrollHandle, SharedString, StatefulInteractiveElement, + Styled, WeakEntity, Window, anchored, div, prelude::FluentBuilder, px, rems, }; use gpui::{ClickEvent, Half, MouseDownEvent, OwnedMenuItem, Point, Subscription}; @@ -268,6 +268,15 @@ impl PopupMenuItem { _ => false, } } + + fn a11y_label(&self) -> Option { + match self { + PopupMenuItem::Item { label, .. } + | PopupMenuItem::Label(label) + | PopupMenuItem::Submenu { label, .. } => Some(label.clone()), + PopupMenuItem::Separator | PopupMenuItem::ElementItem { .. } => None, + } + } } pub struct PopupMenu { @@ -1107,7 +1116,8 @@ impl PopupMenu { } cx.notify(); - })); + })) + .when_some(item.a11y_label(), |this, label| this.aria_label(label)); match item { PopupMenuItem::Separator => this @@ -1306,6 +1316,7 @@ impl Render for PopupMenu { v_flex() .id("popup-menu") + .role(Role::Menu) .key_context(CONTEXT) .track_focus(&self.focus_handle) .on_action(cx.listener(Self::select_up)) @@ -1348,3 +1359,29 @@ impl Render for PopupMenu { }) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[gpui::test] + fn popup_menu_item_a11y_label_uses_visible_label(cx: &mut gpui::TestAppContext) { + let submenu = cx.update(|cx| cx.new(|cx| PopupMenu::new(cx))); + + assert_eq!(PopupMenuItem::new("Open").a11y_label(), Some("Open".into())); + assert_eq!( + PopupMenuItem::link("Docs", "https://example.com").a11y_label(), + Some("Docs".into()) + ); + assert_eq!( + PopupMenuItem::label("Recent files").a11y_label(), + Some("Recent files".into()) + ); + assert_eq!( + PopupMenuItem::submenu("More", submenu).a11y_label(), + Some("More".into()) + ); + assert_eq!(PopupMenuItem::separator().a11y_label(), None); + assert_eq!(PopupMenuItem::element(|_, _| div()).a11y_label(), None); + } +} diff --git a/crates/ui/src/progress/progress.rs b/crates/ui/src/progress/progress.rs index 46140d0dd4..4ffae3068d 100644 --- a/crates/ui/src/progress/progress.rs +++ b/crates/ui/src/progress/progress.rs @@ -1,8 +1,8 @@ use crate::{ActiveTheme, Sizable, Size, StyledExt}; use gpui::{ Animation, AnimationExt as _, App, Background, ElementId, Hsla, InteractiveElement as _, - IntoElement, ParentElement, RenderOnce, StyleRefinement, Styled, Window, div, ease_in_out, - prelude::FluentBuilder, px, relative, + IntoElement, ParentElement, RenderOnce, Role, StatefulInteractiveElement as _, StyleRefinement, + Styled, Window, div, ease_in_out, prelude::FluentBuilder, px, relative, }; use instant::Duration; @@ -96,6 +96,10 @@ impl RenderOnce for Progress { div() .id(self.id) + .role(Role::ProgressIndicator) + .aria_numeric_value(value as f64) + .aria_min_numeric_value(0.0) + .aria_max_numeric_value(100.0) .w_full() .relative() .h(height) diff --git a/crates/ui/src/radio.rs b/crates/ui/src/radio.rs index 72408e4348..8e9c2cc651 100644 --- a/crates/ui/src/radio.rs +++ b/crates/ui/src/radio.rs @@ -6,8 +6,8 @@ use crate::{ }; use gpui::{ AnyElement, App, Axis, Div, ElementId, InteractiveElement, IntoElement, ParentElement, - RenderOnce, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Window, div, - prelude::FluentBuilder, px, relative, rems, + RenderOnce, Role, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Window, + div, prelude::FluentBuilder, px, relative, rems, }; /// A Radio element. @@ -27,6 +27,8 @@ pub struct Radio { size: Size, on_click: Option>, tooltip: ComponentTooltip, + position_in_set: Option, + size_of_set: Option, } impl Radio { @@ -45,6 +47,8 @@ impl Radio { size: Size::default(), on_click: None, tooltip: ComponentTooltip::default(), + position_in_set: None, + size_of_set: None, } } @@ -157,6 +161,18 @@ impl RenderOnce for Radio { div().child( self.base .id(self.id.clone()) + .role(Role::RadioButton) + .aria_selected(self.checked) + .when_some( + self.label.as_ref().map(|l| l.get_text(cx)), + |this, label| this.aria_label(label), + ) + .when_some(self.position_in_set, |this, pos| { + this.aria_position_in_set(pos) + }) + .when_some(self.size_of_set, |this, size| { + this.aria_size_of_set(size) + }) .when(!self.disabled, |this| { this.track_focus( &focus_handle @@ -351,7 +367,8 @@ impl RenderOnce for RadioGroup { h_flex().w_full().flex_wrap() }; - let mut container = div().id(self.id); + let total = self.radios.len(); + let mut container = div().id(self.id).role(Role::RadioGroup); *container.style() = self.style; container.child( @@ -360,6 +377,8 @@ impl RenderOnce for RadioGroup { let checked = selected_ix == Some(ix); radio.id = ix.into(); + radio.position_in_set = Some(ix + 1); + radio.size_of_set = Some(total); radio.disabled(disabled).checked(checked).when_some( on_click.clone(), |this, on_click| { diff --git a/crates/ui/src/root.rs b/crates/ui/src/root.rs index 425fe1b736..3e8ae32e50 100644 --- a/crates/ui/src/root.rs +++ b/crates/ui/src/root.rs @@ -94,6 +94,9 @@ impl ActiveDialog { impl Root { /// Create a new Root view. pub fn new(view: impl Into, window: &mut Window, cx: &mut Context) -> Self { + #[cfg(target_os = "macos")] + crate::macos_accessibility::install_window_hit_test_forwarder(window); + Self { style: StyleRefinement::default(), view: view.into(), diff --git a/crates/ui/src/slider.rs b/crates/ui/src/slider.rs index 8d9c65d632..364146f098 100644 --- a/crates/ui/src/slider.rs +++ b/crates/ui/src/slider.rs @@ -2,11 +2,11 @@ use std::ops::Range; use crate::{ActiveTheme, AxisExt, ElementExt, StyledExt, h_flex}; use gpui::{ - Along, App, AppContext as _, Axis, Background, Bounds, Context, Corners, DefiniteLength, - DragMoveEvent, Empty, Entity, EntityId, EventEmitter, InteractiveElement, IntoElement, IsZero, - MouseButton, MouseDownEvent, ParentElement as _, Pixels, Point, Render, RenderOnce, - StatefulInteractiveElement as _, StyleRefinement, Styled, Window, div, - prelude::FluentBuilder as _, px, relative, + AccessibleAction, Along, App, AppContext as _, Axis, Background, Bounds, Context, Corners, + DefiniteLength, DragMoveEvent, Empty, Entity, EntityId, EventEmitter, InteractiveElement, + IntoElement, IsZero, MouseButton, MouseDownEvent, Orientation, ParentElement as _, Pixels, + Point, Render, RenderOnce, Role, StatefulInteractiveElement as _, StyleRefinement, Styled, + Window, div, prelude::FluentBuilder as _, px, relative, }; #[derive(Clone)] @@ -290,6 +290,21 @@ impl SliderState { self.value } + /// Get the minimum value. + pub fn min_value(&self) -> f32 { + self.min + } + + /// Get the maximum value. + pub fn max_value(&self) -> f32 { + self.max + } + + /// Get the step value. + pub fn step_value(&self) -> f32 { + self.step + } + /// Converts a value between 0.0 and 1.0 to a value between the minimum and maximum value, /// depending on the chosen scale. fn percentage_to_value(&self, percentage: f32) -> f32 { @@ -562,8 +577,43 @@ impl RenderOnce for Slider { radius.bottom_right = px(0.); } + let slider_min = state.min_value() as f64; + let slider_max = state.max_value() as f64; + let _slider_step = state.step_value() as f64; + let slider_value = state.value().end() as f64; + let slider_state_ref = self.state.clone(); + div() .id(("slider", self.state.entity_id())) + .role(Role::Slider) + .aria_numeric_value(slider_value) + .aria_min_numeric_value(slider_min) + .aria_max_numeric_value(slider_max) + .aria_orientation(if axis.is_vertical() { + Orientation::Vertical + } else { + Orientation::Horizontal + }) + .on_a11y_action(AccessibleAction::Increment, { + let state = slider_state_ref.clone(); + move |_, window, cx| { + state.update(cx, |state, cx| { + let new_val = + (state.value().end() + state.step_value()).min(state.max_value()); + state.set_value(new_val, window, cx); + }); + } + }) + .on_a11y_action(AccessibleAction::Decrement, { + let state = slider_state_ref.clone(); + move |_, window, cx| { + state.update(cx, |state, cx| { + let new_val = + (state.value().end() - state.step_value()).max(state.min_value()); + state.set_value(new_val, window, cx); + }); + } + }) .flex() .flex_1() .items_center() diff --git a/crates/ui/src/stepper/item.rs b/crates/ui/src/stepper/item.rs index 6e1278a28f..f26fc90cae 100644 --- a/crates/ui/src/stepper/item.rs +++ b/crates/ui/src/stepper/item.rs @@ -1,7 +1,7 @@ use gpui::{ AnyElement, App, Axis, ClickEvent, Half, InteractiveElement as _, IntoElement, ParentElement, - Pixels, RenderOnce, StyleRefinement, Styled, Window, div, prelude::FluentBuilder as _, px, - relative, + Pixels, RenderOnce, Role, StatefulInteractiveElement as _, StyleRefinement, Styled, Window, + div, prelude::FluentBuilder as _, px, relative, }; use crate::{ @@ -123,6 +123,8 @@ impl RenderOnce for StepperItem { div() .id(("stepper-item", self.step)) + .role(Role::ListItem) + .aria_position_in_set(self.step + 1) .relative() .when(self.layout.is_horizontal(), |this| this.h_flex()) .when(self.layout.is_vertical(), |this| this.v_flex()) diff --git a/crates/ui/src/tab/tab.rs b/crates/ui/src/tab/tab.rs index d5d360ca64..aa7a09c8a9 100644 --- a/crates/ui/src/tab/tab.rs +++ b/crates/ui/src/tab/tab.rs @@ -5,7 +5,7 @@ use crate::{ActiveTheme, Icon, IconName, Selectable, Sizable, Size, StyledExt, h use gpui::prelude::FluentBuilder as _; use gpui::{ Animation, AnimationExt as _, AnyElement, App, Background, ClickEvent, Div, Edges, ElementId, - Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels, RenderOnce, + Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels, RenderOnce, Role, SharedString, StatefulInteractiveElement, Styled, Window, div, px, relative, }; @@ -395,6 +395,7 @@ pub struct Tab { ix: usize, base: Div, pub(super) label: Option, + aria_label: Option, pub(super) icon: Option, prefix: Option, pub(super) tab_bar_prefix: Option, @@ -449,6 +450,7 @@ impl Default for Tab { ix: 0, base: div(), label: None, + aria_label: None, icon: None, tab_bar_prefix: None, children: Vec::new(), @@ -478,6 +480,16 @@ impl Tab { self } + /// Set the accessible label for the tab. + pub fn aria_label(mut self, label: impl Into) -> Self { + self.aria_label = Some(label.into()); + self + } + + fn a11y_label(&self) -> Option { + self.aria_label.clone().or_else(|| self.label.clone()) + } + /// Set icon for the tab. pub fn icon(mut self, icon: impl Into) -> Self { self.icon = Some(icon.into()); @@ -617,6 +629,7 @@ impl RenderOnce for Tab { let inner_margins = self.variant.inner_margins(self.size); let inner_height = self.variant.inner_height(self.size); let height = self.variant.height(self.size); + let aria_label = self.a11y_label(); let segmented_indicator_active = self.variant == TabVariant::Segmented && self.indicator_active; @@ -714,6 +727,9 @@ impl RenderOnce for Tab { self.base .id(self.ix) + .role(Role::Tab) + .when_some(aria_label, |this, label| this.aria_label(label)) + .aria_selected(self.selected) .relative() .flex() .flex_wrap() @@ -782,3 +798,22 @@ impl RenderOnce for Tab { }) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[gpui::test] + fn a11y_label_defaults_to_visible_label(_cx: &mut gpui::TestAppContext) { + let tab = Tab::new().label("Account"); + + assert_eq!(tab.a11y_label(), Some("Account".into())); + } + + #[gpui::test] + fn explicit_a11y_label_overrides_visible_label(_cx: &mut gpui::TestAppContext) { + let tab = Tab::new().label("Acct").aria_label("Account settings"); + + assert_eq!(tab.a11y_label(), Some("Account settings".into())); + } +} diff --git a/crates/ui/src/tab/tab_bar.rs b/crates/ui/src/tab/tab_bar.rs index b2d25b2242..01843fba4f 100644 --- a/crates/ui/src/tab/tab_bar.rs +++ b/crates/ui/src/tab/tab_bar.rs @@ -2,9 +2,9 @@ use std::{cell::RefCell, rc::Rc, time::Duration}; use gpui::{ Anchor, Animation, AnimationExt as _, AnyElement, App, Background, Bounds, Div, Edges, - ElementId, InteractiveElement, IntoElement, ParentElement, Pixels, RenderOnce, ScrollHandle, - SharedString, Stateful, StatefulInteractiveElement as _, StyleRefinement, Styled, Window, div, - prelude::FluentBuilder as _, px, + ElementId, InteractiveElement, IntoElement, ParentElement, Pixels, RenderOnce, Role, + ScrollHandle, SharedString, Stateful, StatefulInteractiveElement as _, StyleRefinement, Styled, + Window, div, prelude::FluentBuilder as _, px, }; use rust_i18n::t; use smallvec::SmallVec; @@ -416,6 +416,7 @@ impl RenderOnce for TabBar { let on_click = self.on_click.clone(); self.base + .role(Role::TabList) .group("tab-bar") .relative() .flex() diff --git a/crates/ui/src/table/table.rs b/crates/ui/src/table/table.rs index a74f2789b9..940c9a330e 100644 --- a/crates/ui/src/table/table.rs +++ b/crates/ui/src/table/table.rs @@ -1,6 +1,7 @@ use gpui::{ - AnyElement, App, InteractiveElement as _, IntoElement, ParentElement, Pixels, RenderOnce, - StyleRefinement, Styled, TextAlign, Window, div, prelude::FluentBuilder as _, px, relative, + AnyElement, App, InteractiveElement as _, IntoElement, ParentElement, Pixels, RenderOnce, Role, + StatefulInteractiveElement as _, StyleRefinement, Styled, TextAlign, Window, div, + prelude::FluentBuilder as _, px, relative, }; use crate::{ActiveTheme as _, AnyChildElement, ChildElement, Sizable, Size, StyledExt as _}; @@ -88,6 +89,7 @@ impl RenderOnce for Table { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { div() .id(("table", self.ix)) + .role(Role::Table) .w_full() .text_sm() .overflow_hidden() @@ -160,6 +162,7 @@ impl RenderOnce for TableHeader { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { div() .id(("table-header", self.ix)) + .role(Role::RowGroup) .w_full() .bg(cx.theme().tokens.table_head) .text_color(cx.theme().table_head_foreground) @@ -233,6 +236,7 @@ impl RenderOnce for TableBody { fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { div() .id(("table-body", self.ix)) + .role(Role::RowGroup) .w_full() .refine_style(&self.style) .children( @@ -375,6 +379,8 @@ impl RenderOnce for TableRow { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { div() .id(("table-row", self.ix)) + .role(Role::Row) + .aria_row_index(self.ix + 1) .w_full() .flex() .flex_row() @@ -464,6 +470,8 @@ impl RenderOnce for TableHead { div() .id(("table-head", self.ix)) + .role(Role::ColumnHeader) + .aria_column_index(self.ix + 1) .flex() .items_center() .when(self.style.size.width.is_none(), |this| { @@ -556,6 +564,8 @@ impl RenderOnce for TableCell { div() .id(("table-cell", self.ix)) + .role(Role::Cell) + .aria_column_index(self.ix + 1) .flex() .items_center() .when(self.style.size.width.is_none(), |this| { diff --git a/script/run-story-macos b/script/run-story-macos new file mode 100755 index 0000000000..284c32cf2f --- /dev/null +++ b/script/run-story-macos @@ -0,0 +1,45 @@ +#!/bin/bash +# Build and launch the Story gallery for AccessKit / VoiceOver testing. +# Run from the repo root: +# +# ./script/run-story-macos +# +# We set up a proper .app bundle (so macOS and VoiceOver see the correct +# bundle ID) but execute the binary directly instead of going through +# LaunchServices (`open`). Using `open` causes the macOS bundle lifecycle +# to conflict with GPUI's async window-open (cx.spawn), leading to a +# 100% CPU busy-loop. Direct execution behaves identically to `cargo run`. + +set -euxo pipefail + +APP=/tmp/GPUIComponentStory.app +BIN=gpui-component-story + +cargo build -p gpui-component-story + +mkdir -p "$APP/Contents/MacOS" +cp "target/debug/$BIN" "$APP/Contents/MacOS/" + +cat > "$APP/Contents/Info.plist" <<'PLIST' + + + + + CFBundleExecutablegpui-component-story + CFBundleIdentifiercom.longbridge.gpui-component-story + CFBundleNameGPUI Component Story + CFBundleVersion1.0 + CFBundlePackageTypeAPPL + NSPrincipalClassNSApplication + NSHighResolutionCapable + + +PLIST + +codesign --sign - --force "$APP" + +# Execute the binary directly — same as `cargo run`. +# Do NOT use `open "$APP"`: LaunchServices triggers a macOS bundle lifecycle +# that conflicts with GPUI's deferred window creation (cx.spawn), causing +# the run loop to spin at 100% CPU. +exec "$APP/Contents/MacOS/$BIN" "$@"