Compare commits

...
Sign in to create a new pull request.

14 commits

Author SHA1 Message Date
Antonio Scandurra
6d9215d937 zed 0.93.4 2023-07-06 10:10:26 +02:00
Antonio Scandurra
65f9c336bf Fix panic when saved conversations directory changes (#2685)
Fixes
https://linear.app/zed-industries/issue/Z-2542/deleting-assistant-conversations-with-zed-open-can-cause-a-crash

We were updating the view's state but missed a `notify`, which caused
the `UniformList` responsible for rendering the saved conversations to
panic when some files were deleted.

Release Notes:

- Fixed a crash that could happen when deleting a saved assistant
conversation from the filesystem.
2023-07-06 10:09:29 +02:00
Nate Butler
0cb77b04d6
Update collaboration sounds, add sounds to screensharing (#2679)
Updates all collab sounds, add screen sharing sounds.

Release Notes:

- Improved collaboration sounds for joining and leaving a call, muting
and unmuting the mic.
- Added a sound when you start and stop screen sharing.
2023-07-05 11:41:00 -07:00
Joseph T. Lyons
d404cc6355 v0.93.x stable 2023-07-05 14:17:22 -04:00
Mikayla Maki
3214a069cd
zed 0.93.3 2023-07-03 13:58:48 -07:00
Mikayla Maki
d5d0c6602f
Add sound effects to calls (#2673)
This PR adds joined, leaving, mute, and unmute sound effects to Zed. 

Release Notes:

- Added joined, leaving, mute, and unmute sound effects (preview-only)
2023-07-03 13:57:52 -07:00
Max Brunsfeld
6c3706add8 zed 0.93.2 2023-06-30 11:46:32 -07:00
Max Brunsfeld
fa604153d9 Fix regression in handling git FS events (#2670)
As part of an optimization in
https://github.com/zed-industries/zed/pull/2663, I changed the way that
the worktree ignores FS events within unloaded directories. But this
accidentally prevented us from detecting some events that occur inside
of `.git` directories.

In this PR, I've made further tweaks to which FS events we can ignore.
We now explicitly opt *in* to scanning `.git` (shallowly) directories
(even though they are ignored). Note that we still don't recursively
scan the git directory (including all of the files inside `objects`
etc). This seems like the correct amount of work to do, and from my
testing (and our unit tests that use the real FS and real git
repositories), it seems to work correctly.

Release Notes:

- Fixed a bug where Zed would not detect some git repository changes
(preview only).
2023-06-30 11:45:14 -07:00
Joseph T. Lyons
e602221dd3 Fix bug preventing the assist command from working in certain keymaps (#2669)
Fixes: https://github.com/zed-industries/community/issues/1712

The keymaps were adding in a `"cmd-enter": "editor::NewlineBelow",`
entry in the context of `Editor`, and this was clobbering the assist
command in the assistant panel context. Zed now defines this command in
the default keymap under the context of `"context": "Editor && mode ==
full"`. All I needed to basically do was remove that command from the
keymaps. I also removed the `"cmd-shift-enter": "editor::NewlineAbove"
from the `Editor` context in those keymaps as wel, as it is also defined
in the default keymap.

Release Notes:

- Fix bug preventing the `assistant: assist` command from working in
certain keymaps
2023-06-30 13:33:35 -04:00
Max Brunsfeld
d9006fa718 zed 0.93.1 2023-06-29 12:57:42 -07:00
Max Brunsfeld
dfa6e96c2d Avoid redundant FS scans when LSPs changed watched files (#2663)
Release Notes:

- Fixed a performance problem that could occur when a language server
requested to watch a set of files (preview only).
2023-06-29 12:57:13 -07:00
Nate Butler
69dfe70ea8 Re-add missing active state (#2664)
[[PR Description]]

Release Notes:

- Fixes project panel active state that was incorrectly removed.
2023-06-29 12:56:44 -07:00
Mikayla Maki
1f0cf01771
Fix project panel bug (#2656)
Release Notes:

* Fix a bug where project panel entries would not be styled correctly
(preview only)
2023-06-28 10:48:07 -07:00
Joseph T. Lyons
fbbf5a5c19 v0.93.x preview 2023-06-28 12:23:29 -04:00
39 changed files with 1220 additions and 435 deletions

480
Cargo.lock generated
View file

@ -177,6 +177,28 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]]
name = "alsa"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8512c9117059663fb5606788fbca3619e2a91dac0e3fe516242eab1fa6be5e44"
dependencies = [
"alsa-sys",
"bitflags",
"libc",
"nix",
]
[[package]]
name = "alsa-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
dependencies = [
"libc",
"pkg-config",
]
[[package]]
name = "ambient-authority"
version = "0.0.1"
@ -590,6 +612,19 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "audio"
version = "0.1.0"
dependencies = [
"anyhow",
"collections",
"gpui",
"log",
"parking_lot 0.11.2",
"rodio",
"util",
]
[[package]]
name = "auto_update"
version = "0.1.0"
@ -756,6 +791,26 @@ dependencies = [
"serde",
]
[[package]]
name = "bindgen"
version = "0.64.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 1.0.109",
]
[[package]]
name = "bindgen"
version = "0.65.1"
@ -857,7 +912,7 @@ checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7"
dependencies = [
"borsh-derive-internal",
"borsh-schema-derive-internal",
"proc-macro-crate",
"proc-macro-crate 0.1.5",
"proc-macro2",
"syn 1.0.109",
]
@ -986,6 +1041,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-broadcast",
"audio",
"client",
"collections",
"fs",
@ -1082,6 +1138,12 @@ dependencies = [
"jobserver",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cexpr"
version = "0.6.0"
@ -1155,7 +1217,7 @@ dependencies = [
"bitflags",
"clap_derive 3.2.25",
"clap_lex 0.2.4",
"indexmap",
"indexmap 1.9.3",
"once_cell",
"strsim",
"termcolor",
@ -1226,6 +1288,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "claxon"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688"
[[package]]
name = "cli"
version = "0.1.0"
@ -1337,6 +1405,7 @@ version = "0.15.0"
dependencies = [
"anyhow",
"async-tungstenite",
"audio",
"axum",
"axum-extra",
"base64 0.13.1",
@ -1444,6 +1513,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "combine"
version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
dependencies = [
"bytes 1.4.0",
"memchr",
]
[[package]]
name = "command_palette"
version = "0.1.0"
@ -1540,11 +1619,17 @@ name = "core-foundation"
version = "0.9.3"
source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85"
dependencies = [
"core-foundation-sys",
"core-foundation-sys 0.8.3",
"libc",
"uuid 0.5.1",
]
[[package]]
name = "core-foundation-sys"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
@ -1594,6 +1679,51 @@ dependencies = [
"libc",
]
[[package]]
name = "coreaudio-rs"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb17e2d1795b1996419648915df94bc7103c28f7b48062d7acf4652fc371b2ff"
dependencies = [
"bitflags",
"core-foundation-sys 0.6.2",
"coreaudio-sys",
]
[[package]]
name = "coreaudio-sys"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f034b2258e6c4ade2f73bf87b21047567fb913ee9550837c2316d139b0262b24"
dependencies = [
"bindgen 0.64.0",
]
[[package]]
name = "cpal"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c"
dependencies = [
"alsa",
"core-foundation-sys 0.8.3",
"coreaudio-rs",
"dasp_sample",
"jni 0.19.0",
"js-sys",
"libc",
"mach2",
"ndk",
"ndk-context",
"oboe",
"once_cell",
"parking_lot 0.12.1",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows 0.46.0",
]
[[package]]
name = "cpp_demangle"
version = "0.3.5"
@ -1924,6 +2054,12 @@ dependencies = [
"parking_lot_core 0.9.7",
]
[[package]]
name = "dasp_sample"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
[[package]]
name = "data-url"
version = "0.1.1"
@ -2233,6 +2369,12 @@ dependencies = [
"serde",
]
[[package]]
name = "equivalent"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
[[package]]
name = "erased-serde"
version = "0.3.25"
@ -2793,7 +2935,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
dependencies = [
"fallible-iterator",
"indexmap",
"indexmap 1.9.3",
"stable_deref_trait",
]
@ -2889,7 +3031,7 @@ dependencies = [
"anyhow",
"async-task",
"backtrace",
"bindgen",
"bindgen 0.65.1",
"block",
"cc",
"cocoa",
@ -2961,7 +3103,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http",
"indexmap",
"indexmap 1.9.3",
"slab",
"tokio",
"tokio-util 0.7.8",
@ -2995,6 +3137,12 @@ dependencies = [
"ahash 0.8.3",
]
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "hashlink"
version = "0.8.1"
@ -3105,6 +3253,12 @@ dependencies = [
"digest 0.10.6",
]
[[package]]
name = "hound"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1"
[[package]]
name = "http"
version = "0.2.9"
@ -3213,11 +3367,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"core-foundation-sys 0.8.3",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows",
"windows 0.48.0",
]
[[package]]
@ -3287,6 +3441,16 @@ dependencies = [
"serde",
]
[[package]]
name = "indexmap"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
"hashbrown 0.14.0",
]
[[package]]
name = "indoc"
version = "1.0.9"
@ -3459,6 +3623,40 @@ dependencies = [
"cc",
]
[[package]]
name = "jni"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
dependencies = [
"cesu8",
"combine",
"jni-sys",
"log",
"thiserror",
"walkdir",
]
[[package]]
name = "jni"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c"
dependencies = [
"cesu8",
"combine",
"jni-sys",
"log",
"thiserror",
"walkdir",
]
[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.26"
@ -3661,6 +3859,17 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "lewton"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030"
dependencies = [
"byteorder",
"ogg",
"tinyvec",
]
[[package]]
name = "libc"
version = "0.2.144"
@ -3893,6 +4102,15 @@ dependencies = [
"libc",
]
[[package]]
name = "mach2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
dependencies = [
"libc",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
@ -3949,7 +4167,7 @@ name = "media"
version = "0.1.0"
dependencies = [
"anyhow",
"bindgen",
"bindgen 0.65.1",
"block",
"bytes 1.4.0",
"core-foundation",
@ -4207,6 +4425,35 @@ dependencies = [
"tempfile",
]
[[package]]
name = "ndk"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"
dependencies = [
"bitflags",
"jni-sys",
"ndk-sys",
"num_enum",
"raw-window-handle",
"thiserror",
]
[[package]]
name = "ndk-context"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
[[package]]
name = "ndk-sys"
version = "0.4.1+23.1.7779620"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3"
dependencies = [
"jni-sys",
]
[[package]]
name = "net2"
version = "0.2.38"
@ -4314,6 +4561,17 @@ dependencies = [
"zeroize",
]
[[package]]
name = "num-derive"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "num-integer"
version = "0.1.45"
@ -4366,6 +4624,27 @@ dependencies = [
"libc",
]
[[package]]
name = "num_enum"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
dependencies = [
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "nvim-rs"
version = "0.5.0"
@ -4408,7 +4687,7 @@ checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
dependencies = [
"crc32fast",
"hashbrown 0.11.2",
"indexmap",
"indexmap 1.9.3",
"memchr",
]
@ -4421,6 +4700,38 @@ dependencies = [
"memchr",
]
[[package]]
name = "oboe"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8868cc237ee02e2d9618539a23a8d228b9bb3fc2e7a5b11eed3831de77c395d0"
dependencies = [
"jni 0.20.0",
"ndk",
"ndk-context",
"num-derive",
"num-traits",
"oboe-sys",
]
[[package]]
name = "oboe-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f44155e7fb718d3cfddcf70690b2b51ac4412f347cd9e4fbe511abe9cd7b5f2"
dependencies = [
"cc",
]
[[package]]
name = "ogg"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e"
dependencies = [
"byteorder",
]
[[package]]
name = "once_cell"
version = "1.17.1"
@ -4710,7 +5021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
dependencies = [
"fixedbitset",
"indexmap",
"indexmap 1.9.3",
]
[[package]]
@ -4787,7 +5098,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590"
dependencies = [
"base64 0.21.0",
"indexmap",
"indexmap 1.9.3",
"line-wrap",
"quick-xml",
"serde",
@ -4920,6 +5231,16 @@ dependencies = [
"toml",
]
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -5331,6 +5652,12 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "raw-window-handle"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "rayon"
version = "1.7.0"
@ -5614,6 +5941,19 @@ dependencies = [
"rmp",
]
[[package]]
name = "rodio"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdf1d4dea18dff2e9eb6dca123724f8b60ef44ad74a9ad283cdfe025df7e73fa"
dependencies = [
"claxon",
"cpal",
"hound",
"lewton",
"symphonia",
]
[[package]]
name = "rope"
version = "0.1.0"
@ -6115,7 +6455,7 @@ checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
dependencies = [
"bitflags",
"core-foundation",
"core-foundation-sys",
"core-foundation-sys 0.8.3",
"libc",
"security-framework-sys",
]
@ -6126,7 +6466,7 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
dependencies = [
"core-foundation-sys",
"core-foundation-sys 0.8.3",
"libc",
]
@ -6200,7 +6540,7 @@ version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
dependencies = [
"indexmap",
"indexmap 1.9.3",
"itoa 1.0.6",
"ryu",
"serde",
@ -6212,7 +6552,7 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d7b9ce5b0a63c6269b9623ed828b39259545a6ec0d8a35d6135ad6af6232add"
dependencies = [
"indexmap",
"indexmap 1.9.3",
"itoa 0.4.8",
"ryu",
"serde",
@ -6247,7 +6587,7 @@ version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
dependencies = [
"indexmap",
"indexmap 1.9.3",
"ryu",
"serde",
"yaml-rust",
@ -6621,7 +6961,7 @@ dependencies = [
"hex",
"hkdf",
"hmac 0.12.1",
"indexmap",
"indexmap 1.9.3",
"itoa 1.0.6",
"libc",
"libsqlite3-sys",
@ -6772,6 +7112,56 @@ dependencies = [
"siphasher",
]
[[package]]
name = "symphonia"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62e48dba70095f265fdb269b99619b95d04c89e619538138383e63310b14d941"
dependencies = [
"lazy_static",
"symphonia-bundle-mp3",
"symphonia-core",
"symphonia-metadata",
]
[[package]]
name = "symphonia-bundle-mp3"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f31d7fece546f1e6973011a9eceae948133bbd18fd3d52f6073b1e38ae6368a"
dependencies = [
"bitflags",
"lazy_static",
"log",
"symphonia-core",
"symphonia-metadata",
]
[[package]]
name = "symphonia-core"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c73eb88fee79705268cc7b742c7bc93a7b76e092ab751d0833866970754142"
dependencies = [
"arrayvec 0.7.2",
"bitflags",
"bytemuck",
"lazy_static",
"log",
]
[[package]]
name = "symphonia-metadata"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89c3e1937e31d0e068bbe829f66b2f2bfaa28d056365279e0ef897172c3320c0"
dependencies = [
"encoding_rs",
"lazy_static",
"log",
"symphonia-core",
]
[[package]]
name = "syn"
version = "1.0.109"
@ -6817,7 +7207,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a902e9050fca0a5d6877550b769abd2bd1ce8c04634b941dbe2809735e1a1e33"
dependencies = [
"cfg-if 1.0.0",
"core-foundation-sys",
"core-foundation-sys 0.8.3",
"libc",
"ntapi 0.4.1",
"once_cell",
@ -6986,7 +7376,7 @@ dependencies = [
"anyhow",
"fs",
"gpui",
"indexmap",
"indexmap 1.9.3",
"parking_lot 0.11.2",
"schemars",
"serde",
@ -7292,6 +7682,23 @@ dependencies = [
"serde",
]
[[package]]
name = "toml_datetime"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
[[package]]
name = "toml_edit"
version = "0.19.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7"
dependencies = [
"indexmap 2.0.0",
"toml_datetime",
"winnow",
]
[[package]]
name = "tonic"
version = "0.6.2"
@ -7331,7 +7738,7 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"indexmap",
"indexmap 1.9.3",
"pin-project",
"pin-project-lite 0.2.9",
"rand 0.8.5",
@ -8188,7 +8595,7 @@ version = "0.85.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "570460c58b21e9150d2df0eaaedbb7816c34bcec009ae0dcc976e40ba81463e7"
dependencies = [
"indexmap",
"indexmap 1.9.3",
]
[[package]]
@ -8202,7 +8609,7 @@ dependencies = [
"backtrace",
"bincode",
"cfg-if 1.0.0",
"indexmap",
"indexmap 1.9.3",
"lazy_static",
"libc",
"log",
@ -8276,7 +8683,7 @@ dependencies = [
"anyhow",
"cranelift-entity",
"gimli 0.26.2",
"indexmap",
"indexmap 1.9.3",
"log",
"more-asserts",
"object 0.28.4",
@ -8346,7 +8753,7 @@ dependencies = [
"backtrace",
"cc",
"cfg-if 1.0.0",
"indexmap",
"indexmap 1.9.3",
"libc",
"log",
"mach",
@ -8602,6 +9009,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows"
version = "0.48.0"
@ -8758,6 +9174,15 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winnow"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448"
dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.10.1"
@ -8909,7 +9334,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.93.0"
version = "0.93.4"
dependencies = [
"activity_indicator",
"ai",
@ -8918,6 +9343,7 @@ dependencies = [
"async-recursion 0.3.2",
"async-tar",
"async-trait",
"audio",
"auto_update",
"backtrace",
"breadcrumbs",
@ -8947,7 +9373,7 @@ dependencies = [
"gpui",
"ignore",
"image",
"indexmap",
"indexmap 1.9.3",
"install_cli",
"isahc",
"journal",

View file

@ -2,6 +2,7 @@
members = [
"crates/activity_indicator",
"crates/ai",
"crates/audio",
"crates/auto_update",
"crates/breadcrumbs",
"crates/call",

View file

@ -24,9 +24,7 @@
],
"ctrl-shift-down": "editor::AddSelectionBelow",
"ctrl-shift-up": "editor::AddSelectionAbove",
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine",
"cmd-shift-enter": "editor::NewlineAbove",
"cmd-enter": "editor::NewlineBelow"
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine"
}
},
{

View file

@ -24,9 +24,7 @@
"ctrl-.": "editor::GoToHunk",
"ctrl-,": "editor::GoToPrevHunk",
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
"ctrl-delete": "editor::DeleteToNextWordEnd",
"cmd-shift-enter": "editor::NewlineAbove",
"cmd-enter": "editor::NewlineBelow"
"ctrl-delete": "editor::DeleteToNextWordEnd"
}
},
{

View file

@ -12,8 +12,6 @@
"ctrl-shift-d": "editor::DuplicateLine",
"cmd-b": "editor::GoToDefinition",
"cmd-j": "editor::ScrollCursorCenter",
"cmd-alt-enter": "editor::NewlineAbove",
"cmd-enter": "editor::NewlineBelow",
"cmd-shift-l": "editor::SelectLine",
"cmd-shift-t": "outline::Toggle",
"alt-backspace": "editor::DeleteToPreviousWordStart",
@ -56,7 +54,9 @@
},
{
"context": "Editor && mode == full",
"bindings": {}
"bindings": {
"cmd-alt-enter": "editor::NewlineAbove"
}
},
{
"context": "BufferSearchBar",

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/mute.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/unmute.wav Normal file

Binary file not shown.

View file

@ -147,8 +147,9 @@ impl AssistantPanel {
.await
.log_err()
.unwrap_or_default();
this.update(&mut cx, |this, _| {
this.saved_conversations = saved_conversations
this.update(&mut cx, |this, cx| {
this.saved_conversations = saved_conversations;
cx.notify();
})
.ok();
}

23
crates/audio/Cargo.toml Normal file
View file

@ -0,0 +1,23 @@
[package]
name = "audio"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/audio.rs"
doctest = false
[dependencies]
gpui = { path = "../gpui" }
collections = { path = "../collections" }
util = { path = "../util" }
rodio = "0.17.1"
log.workspace = true
anyhow.workspace = true
parking_lot.workspace = true
[dev-dependencies]

View file

@ -0,0 +1,44 @@
use std::{io::Cursor, sync::Arc};
use anyhow::Result;
use collections::HashMap;
use gpui::{AppContext, AssetSource};
use rodio::{
source::{Buffered, SamplesConverter},
Decoder, Source,
};
type Sound = Buffered<SamplesConverter<Decoder<Cursor<Vec<u8>>>, f32>>;
pub struct SoundRegistry {
cache: Arc<parking_lot::Mutex<HashMap<String, Sound>>>,
assets: Box<dyn AssetSource>,
}
impl SoundRegistry {
pub fn new(source: impl AssetSource) -> Arc<Self> {
Arc::new(Self {
cache: Default::default(),
assets: Box::new(source),
})
}
pub fn global(cx: &AppContext) -> Arc<Self> {
cx.global::<Arc<Self>>().clone()
}
pub fn get(&self, name: &str) -> Result<impl Source<Item = f32>> {
if let Some(wav) = self.cache.lock().get(name) {
return Ok(wav.clone());
}
let path = format!("sounds/{}.wav", name);
let bytes = self.assets.load(&path)?.into_owned();
let cursor = Cursor::new(bytes);
let source = Decoder::new(cursor)?.convert_samples::<f32>().buffered();
self.cache.lock().insert(name.to_string(), source.clone());
Ok(source)
}
}

67
crates/audio/src/audio.rs Normal file
View file

@ -0,0 +1,67 @@
use assets::SoundRegistry;
use gpui::{AppContext, AssetSource};
use rodio::{OutputStream, OutputStreamHandle};
use util::ResultExt;
mod assets;
pub fn init(source: impl AssetSource, cx: &mut AppContext) {
cx.set_global(SoundRegistry::new(source));
cx.set_global(Audio::new());
}
pub enum Sound {
Joined,
Leave,
Mute,
Unmute,
StartScreenshare,
StopScreenshare,
}
impl Sound {
fn file(&self) -> &'static str {
match self {
Self::Joined => "joined_call",
Self::Leave => "leave_call",
Self::Mute => "mute",
Self::Unmute => "unmute",
Self::StartScreenshare => "start_screenshare",
Self::StopScreenshare => "stop_screenshare",
}
}
}
pub struct Audio {
_output_stream: Option<OutputStream>,
output_handle: Option<OutputStreamHandle>,
}
impl Audio {
pub fn new() -> Self {
let (_output_stream, output_handle) = OutputStream::try_default().log_err().unzip();
Self {
_output_stream,
output_handle,
}
}
pub fn play_sound(sound: Sound, cx: &AppContext) {
if !cx.has_global::<Self>() {
return;
}
let this = cx.global::<Self>();
let Some(output_handle) = this.output_handle.as_ref() else {
return;
};
let Some(source) = SoundRegistry::global(cx).get(sound.file()).log_err() else {
return;
};
output_handle.play_raw(source).log_err();
}
}

View file

@ -19,6 +19,7 @@ test-support = [
]
[dependencies]
audio = { path = "../audio" }
client = { path = "../client" }
collections = { path = "../collections" }
gpui = { path = "../gpui" }

View file

@ -3,6 +3,7 @@ use crate::{
IncomingCall,
};
use anyhow::{anyhow, Result};
use audio::{Audio, Sound};
use client::{
proto::{self, PeerId},
Client, TypedEnvelope, User, UserStore,
@ -151,6 +152,7 @@ impl Room {
let connect = room.connect(&connection_info.server_url, &connection_info.token);
cx.spawn(|this, mut cx| async move {
connect.await?;
this.update(&mut cx, |this, cx| this.share_microphone(cx))
.await?;
@ -176,6 +178,8 @@ impl Room {
let maintain_connection =
cx.spawn_weak(|this, cx| Self::maintain_connection(this, client.clone(), cx).log_err());
Audio::play_sound(Sound::Joined, cx);
Self {
id,
live_kit: live_kit_room,
@ -265,6 +269,7 @@ impl Room {
room.apply_room_update(room_proto, cx)?;
anyhow::Ok(())
})?;
Ok(room)
})
}
@ -306,6 +311,8 @@ impl Room {
}
}
Audio::play_sound(Sound::Leave, cx);
self.status = RoomStatus::Offline;
self.remote_participants.clear();
self.pending_participants.clear();
@ -656,6 +663,8 @@ impl Room {
},
);
Audio::play_sound(Sound::Joined, cx);
if let Some(live_kit) = this.live_kit.as_ref() {
let video_tracks =
live_kit.room.remote_video_tracks(&user.id.to_string());
@ -922,6 +931,7 @@ impl Room {
cx.spawn(|this, mut cx| async move {
let project =
Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?;
this.update(&mut cx, |this, cx| {
this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade(cx) {
@ -1212,6 +1222,9 @@ impl Room {
};
cx.notify();
}
Audio::play_sound(Sound::StartScreenshare, cx);
Ok(())
}
Err(error) => {
@ -1227,38 +1240,20 @@ impl Room {
})
})
}
fn set_mute(
live_kit: &mut LiveKitRoom,
should_mute: bool,
cx: &mut ModelContext<Self>,
) -> Result<Task<Result<()>>> {
if !should_mute {
// clear user muting state.
live_kit.muted_by_user = false;
}
match &mut live_kit.microphone_track {
LocalTrack::None => Err(anyhow!("microphone was not shared")),
LocalTrack::Pending { muted, .. } => {
*muted = should_mute;
cx.notify();
Ok(Task::Ready(Some(Ok(()))))
}
LocalTrack::Published {
track_publication,
muted,
} => {
*muted = should_mute;
cx.notify();
Ok(cx.background().spawn(track_publication.set_mute(*muted)))
}
}
}
pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) -> Result<Task<Result<()>>> {
let should_mute = !self.is_muted();
if let Some(live_kit) = self.live_kit.as_mut() {
let ret = Self::set_mute(live_kit, should_mute, cx);
let (ret_task, old_muted) = live_kit.set_mute(should_mute, cx)?;
live_kit.muted_by_user = should_mute;
ret
if old_muted == true && live_kit.deafened == true {
if let Some(task) = self.toggle_deafen(cx).ok() {
task.detach();
}
}
Ok(ret_task)
} else {
Err(anyhow!("LiveKit not started"))
}
@ -1274,7 +1269,7 @@ impl Room {
// When deafening, mute user's mic as well.
// When undeafening, unmute user's mic unless it was manually muted prior to deafening.
if live_kit.deafened || !live_kit.muted_by_user {
mute_task = Some(Self::set_mute(live_kit, live_kit.deafened, cx)?);
mute_task = Some(live_kit.set_mute(live_kit.deafened, cx)?.0);
};
for participant in self.remote_participants.values() {
for track in live_kit
@ -1319,6 +1314,8 @@ impl Room {
} => {
live_kit.room.unpublish_track(track_publication);
cx.notify();
Audio::play_sound(Sound::StopScreenshare, cx);
Ok(())
}
}
@ -1347,6 +1344,51 @@ struct LiveKitRoom {
_maintain_tracks: [Task<()>; 2],
}
impl LiveKitRoom {
fn set_mute(
self: &mut LiveKitRoom,
should_mute: bool,
cx: &mut ModelContext<Room>,
) -> Result<(Task<Result<()>>, bool)> {
if !should_mute {
// clear user muting state.
self.muted_by_user = false;
}
let (result, old_muted) = match &mut self.microphone_track {
LocalTrack::None => Err(anyhow!("microphone was not shared")),
LocalTrack::Pending { muted, .. } => {
let old_muted = *muted;
*muted = should_mute;
cx.notify();
Ok((Task::Ready(Some(Ok(()))), old_muted))
}
LocalTrack::Published {
track_publication,
muted,
} => {
let old_muted = *muted;
*muted = should_mute;
cx.notify();
Ok((
cx.background().spawn(track_publication.set_mute(*muted)),
old_muted,
))
}
}?;
if old_muted != should_mute {
if should_mute {
Audio::play_sound(Sound::Mute, cx);
} else {
Audio::play_sound(Sound::Unmute, cx);
}
}
Ok((result, old_muted))
}
}
enum LocalTrack {
None,
Pending {

View file

@ -14,6 +14,7 @@ name = "seed"
required-features = ["seed-support"]
[dependencies]
audio = { path = "../audio" }
collections = { path = "../collections" }
live_kit_server = { path = "../live_kit_server" }
rpc = { path = "../rpc" }

View file

@ -203,6 +203,7 @@ impl TestServer {
language::init(cx);
editor::init_settings(cx);
workspace::init(app_state.clone(), cx);
audio::init((), cx);
call::init(client.clone(), user_store.clone(), cx);
});

View file

@ -388,6 +388,7 @@ struct FakeFsState {
event_txs: Vec<smol::channel::Sender<Vec<fsevent::Event>>>,
events_paused: bool,
buffered_events: Vec<fsevent::Event>,
metadata_call_count: usize,
read_dir_call_count: usize,
}
@ -538,6 +539,7 @@ impl FakeFs {
buffered_events: Vec::new(),
events_paused: false,
read_dir_call_count: 0,
metadata_call_count: 0,
}),
})
}
@ -774,10 +776,16 @@ impl FakeFs {
result
}
/// How many `read_dir` calls have been issued.
pub fn read_dir_call_count(&self) -> usize {
self.state.lock().read_dir_call_count
}
/// How many `metadata` calls have been issued.
pub fn metadata_call_count(&self) -> usize {
self.state.lock().metadata_call_count
}
async fn simulate_random_delay(&self) {
self.executor
.upgrade()
@ -1098,7 +1106,8 @@ impl Fs for FakeFs {
async fn metadata(&self, path: &Path) -> Result<Option<Metadata>> {
self.simulate_random_delay().await;
let path = normalize_path(path);
let state = self.state.lock();
let mut state = self.state.lock();
state.metadata_call_count += 1;
if let Some((mut entry, _)) = state.try_read_path(&path, false) {
let is_symlink = entry.lock().is_symlink();
if is_symlink {

View file

@ -596,6 +596,8 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
);
});
let prev_read_dir_count = fs.read_dir_call_count();
// Keep track of the FS events reported to the language server.
let fake_server = fake_servers.next().await.unwrap();
let file_changes = Arc::new(Mutex::new(Vec::new()));
@ -607,6 +609,12 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
register_options: serde_json::to_value(
lsp::DidChangeWatchedFilesRegistrationOptions {
watchers: vec![
lsp::FileSystemWatcher {
glob_pattern: lsp::GlobPattern::String(
"/the-root/Cargo.toml".to_string(),
),
kind: None,
},
lsp::FileSystemWatcher {
glob_pattern: lsp::GlobPattern::String(
"/the-root/src/*.{rs,c}".to_string(),
@ -638,6 +646,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
cx.foreground().run_until_parked();
assert_eq!(mem::take(&mut *file_changes.lock()), &[]);
assert_eq!(fs.read_dir_call_count() - prev_read_dir_count, 4);
// Now the language server has asked us to watch an ignored directory path,
// so we recursively load it.

View file

@ -2140,6 +2140,7 @@ impl LocalSnapshot {
impl BackgroundScannerState {
fn should_scan_directory(&self, entry: &Entry) -> bool {
(!entry.is_external && !entry.is_ignored)
|| entry.path.file_name() == Some(&*DOT_GIT)
|| self.scanned_dirs.contains(&entry.id) // If we've ever scanned it, keep scanning
|| self
.paths_to_scan
@ -2319,6 +2320,7 @@ impl BackgroundScannerState {
.entry_for_id(entry_id)
.map(|entry| RepositoryWorkDirectory(entry.path.clone())) else { continue };
log::info!("reload git repository {:?}", dot_git_dir);
let repository = repository.repo_ptr.lock();
let branch = repository.branch_name();
repository.reload_index();
@ -2359,6 +2361,8 @@ impl BackgroundScannerState {
}
fn build_repository(&mut self, dot_git_path: Arc<Path>, fs: &dyn Fs) -> Option<()> {
log::info!("build git repository {:?}", dot_git_path);
let work_dir_path: Arc<Path> = dot_git_path.parent().unwrap().into();
// Guard against repositories inside the repository metadata
@ -3071,17 +3075,20 @@ impl BackgroundScanner {
path_prefix = self.path_prefixes_to_scan_rx.recv().fuse() => {
let Ok(path_prefix) = path_prefix else { break };
log::trace!("adding path prefix {:?}", path_prefix);
self.forcibly_load_paths(&[path_prefix.clone()]).await;
let did_scan = self.forcibly_load_paths(&[path_prefix.clone()]).await;
if did_scan {
let abs_path =
{
let mut state = self.state.lock();
state.path_prefixes_to_scan.insert(path_prefix.clone());
state.snapshot.abs_path.join(&path_prefix)
};
let abs_path =
{
let mut state = self.state.lock();
state.path_prefixes_to_scan.insert(path_prefix.clone());
state.snapshot.abs_path.join(path_prefix)
};
if let Some(abs_path) = self.fs.canonicalize(&abs_path).await.log_err() {
self.process_events(vec![abs_path]).await;
if let Some(abs_path) = self.fs.canonicalize(&abs_path).await.log_err() {
self.process_events(vec![abs_path]).await;
}
}
}
@ -3097,10 +3104,13 @@ impl BackgroundScanner {
}
}
async fn process_scan_request(&self, request: ScanRequest, scanning: bool) -> bool {
async fn process_scan_request(&self, mut request: ScanRequest, scanning: bool) -> bool {
log::debug!("rescanning paths {:?}", request.relative_paths);
let root_path = self.forcibly_load_paths(&request.relative_paths).await;
request.relative_paths.sort_unstable();
self.forcibly_load_paths(&request.relative_paths).await;
let root_path = self.state.lock().snapshot.abs_path.clone();
let root_canonical_path = match self.fs.canonicalize(&root_path).await {
Ok(path) => path,
Err(err) => {
@ -3108,10 +3118,9 @@ impl BackgroundScanner {
return false;
}
};
let abs_paths = request
.relative_paths
.into_iter()
.iter()
.map(|path| {
if path.file_name().is_some() {
root_canonical_path.join(path)
@ -3120,14 +3129,19 @@ impl BackgroundScanner {
}
})
.collect::<Vec<_>>();
self.reload_entries_for_paths(root_path, root_canonical_path, abs_paths, None)
.await;
self.reload_entries_for_paths(
root_path,
root_canonical_path,
&request.relative_paths,
abs_paths,
None,
)
.await;
self.send_status_update(scanning, Some(request.done))
}
async fn process_events(&mut self, abs_paths: Vec<PathBuf>) {
log::debug!("received fs events {:?}", abs_paths);
async fn process_events(&mut self, mut abs_paths: Vec<PathBuf>) {
let root_path = self.state.lock().snapshot.abs_path.clone();
let root_canonical_path = match self.fs.canonicalize(&root_path).await {
Ok(path) => path,
@ -3137,15 +3151,52 @@ impl BackgroundScanner {
}
};
let mut relative_paths = Vec::with_capacity(abs_paths.len());
abs_paths.sort_unstable();
abs_paths.dedup_by(|a, b| a.starts_with(&b));
abs_paths.retain(|abs_path| {
let snapshot = &self.state.lock().snapshot;
{
let relative_path: Arc<Path> =
if let Ok(path) = abs_path.strip_prefix(&root_canonical_path) {
path.into()
} else {
log::error!(
"ignoring event {abs_path:?} outside of root path {root_canonical_path:?}",
);
return false;
};
let parent_dir_is_loaded = relative_path.parent().map_or(true, |parent| {
snapshot
.entry_for_path(parent)
.map_or(false, |entry| entry.kind == EntryKind::Dir)
});
if !parent_dir_is_loaded {
log::debug!("ignoring event {relative_path:?} within unloaded directory");
return false;
}
relative_paths.push(relative_path);
true
}
});
if relative_paths.is_empty() {
return;
}
log::debug!("received fs events {:?}", relative_paths);
let (scan_job_tx, scan_job_rx) = channel::unbounded();
let paths = self
.reload_entries_for_paths(
root_path,
root_canonical_path,
abs_paths,
Some(scan_job_tx.clone()),
)
.await;
self.reload_entries_for_paths(
root_path,
root_canonical_path,
&relative_paths,
abs_paths,
Some(scan_job_tx.clone()),
)
.await;
drop(scan_job_tx);
self.scan_dirs(false, scan_job_rx).await;
@ -3155,7 +3206,7 @@ impl BackgroundScanner {
{
let mut state = self.state.lock();
state.reload_repositories(&paths, self.fs.as_ref());
state.reload_repositories(&relative_paths, self.fs.as_ref());
state.snapshot.completed_scan_id = state.snapshot.scan_id;
for (_, entry_id) in mem::take(&mut state.removed_entry_ids) {
state.scanned_dirs.remove(&entry_id);
@ -3165,12 +3216,11 @@ impl BackgroundScanner {
self.send_status_update(false, None);
}
async fn forcibly_load_paths(&self, paths: &[Arc<Path>]) -> Arc<Path> {
let root_path;
async fn forcibly_load_paths(&self, paths: &[Arc<Path>]) -> bool {
let (scan_job_tx, mut scan_job_rx) = channel::unbounded();
{
let mut state = self.state.lock();
root_path = state.snapshot.abs_path.clone();
let root_path = state.snapshot.abs_path.clone();
for path in paths {
for ancestor in path.ancestors() {
if let Some(entry) = state.snapshot.entry_for_path(ancestor) {
@ -3201,8 +3251,8 @@ impl BackgroundScanner {
while let Some(job) = scan_job_rx.next().await {
self.scan_dir(&job).await.log_err();
}
self.state.lock().paths_to_scan.clear();
root_path
mem::take(&mut self.state.lock().paths_to_scan).len() > 0
}
async fn scan_dirs(
@ -3475,7 +3525,7 @@ impl BackgroundScanner {
.expect("channel is unbounded");
}
} else {
log::debug!("defer scanning directory {:?} {:?}", entry.path, entry.kind);
log::debug!("defer scanning directory {:?}", entry.path);
entry.kind = EntryKind::UnloadedDir;
}
}
@ -3490,26 +3540,10 @@ impl BackgroundScanner {
&self,
root_abs_path: Arc<Path>,
root_canonical_path: PathBuf,
mut abs_paths: Vec<PathBuf>,
relative_paths: &[Arc<Path>],
abs_paths: Vec<PathBuf>,
scan_queue_tx: Option<Sender<ScanJob>>,
) -> Vec<Arc<Path>> {
let mut event_paths = Vec::<Arc<Path>>::with_capacity(abs_paths.len());
abs_paths.sort_unstable();
abs_paths.dedup_by(|a, b| a.starts_with(&b));
abs_paths.retain(|abs_path| {
if let Ok(path) = abs_path.strip_prefix(&root_canonical_path) {
event_paths.push(path.into());
true
} else {
log::error!(
"unexpected event {:?} for root path {:?}",
abs_path,
root_canonical_path
);
false
}
});
) {
let metadata = futures::future::join_all(
abs_paths
.iter()
@ -3538,30 +3572,15 @@ impl BackgroundScanner {
// Remove any entries for paths that no longer exist or are being recursively
// refreshed. Do this before adding any new entries, so that renames can be
// detected regardless of the order of the paths.
for (path, metadata) in event_paths.iter().zip(metadata.iter()) {
for (path, metadata) in relative_paths.iter().zip(metadata.iter()) {
if matches!(metadata, Ok(None)) || doing_recursive_update {
log::trace!("remove path {:?}", path);
state.remove_path(path);
}
}
for (path, metadata) in event_paths.iter().zip(metadata.iter()) {
if let (Some(parent), true) = (path.parent(), doing_recursive_update) {
if state
.snapshot
.entry_for_path(parent)
.map_or(true, |entry| entry.kind != EntryKind::Dir)
{
log::debug!(
"ignoring event {path:?} within unloaded directory {:?}",
parent
);
continue;
}
}
for (path, metadata) in relative_paths.iter().zip(metadata.iter()) {
let abs_path: Arc<Path> = root_abs_path.join(&path).into();
match metadata {
Ok(Some((metadata, canonical_path))) => {
let ignore_stack = state
@ -3594,23 +3613,28 @@ impl BackgroundScanner {
}
}
let fs_entry = state.insert_entry(fs_entry, self.fs.as_ref());
if let Some(scan_queue_tx) = &scan_queue_tx {
let mut ancestor_inodes = state.snapshot.ancestor_inodes_for_path(&path);
if metadata.is_dir && !ancestor_inodes.contains(&metadata.inode) {
ancestor_inodes.insert(metadata.inode);
smol::block_on(scan_queue_tx.send(ScanJob {
abs_path,
path: path.clone(),
ignore_stack,
ancestor_inodes,
is_external: fs_entry.is_external,
scan_queue: scan_queue_tx.clone(),
}))
.unwrap();
if let (Some(scan_queue_tx), true) = (&scan_queue_tx, fs_entry.is_dir()) {
if state.should_scan_directory(&fs_entry) {
let mut ancestor_inodes =
state.snapshot.ancestor_inodes_for_path(&path);
if !ancestor_inodes.contains(&metadata.inode) {
ancestor_inodes.insert(metadata.inode);
smol::block_on(scan_queue_tx.send(ScanJob {
abs_path,
path: path.clone(),
ignore_stack,
ancestor_inodes,
is_external: fs_entry.is_external,
scan_queue: scan_queue_tx.clone(),
}))
.unwrap();
}
} else {
fs_entry.kind = EntryKind::UnloadedDir;
}
}
state.insert_entry(fs_entry, self.fs.as_ref());
}
Ok(None) => {
self.remove_repo_path(&path, &mut state.snapshot);
@ -3624,12 +3648,10 @@ impl BackgroundScanner {
util::extend_sorted(
&mut state.changed_paths,
event_paths.iter().cloned(),
relative_paths.iter().cloned(),
usize::MAX,
Ord::cmp,
);
event_paths
}
fn remove_repo_path(&self, path: &Path, snapshot: &mut LocalSnapshot) -> Option<()> {
@ -3760,25 +3782,22 @@ impl BackgroundScanner {
// Scan any directories that were previously ignored and weren't
// previously scanned.
if was_ignored
&& !entry.is_ignored
&& !entry.is_external
&& entry.kind == EntryKind::UnloadedDir
{
job.scan_queue
.try_send(ScanJob {
abs_path: abs_path.clone(),
path: entry.path.clone(),
ignore_stack: child_ignore_stack.clone(),
scan_queue: job.scan_queue.clone(),
ancestor_inodes: self
.state
.lock()
.snapshot
.ancestor_inodes_for_path(&entry.path),
is_external: false,
})
.unwrap();
if was_ignored && !entry.is_ignored && entry.kind.is_unloaded() {
let state = self.state.lock();
if state.should_scan_directory(&entry) {
job.scan_queue
.try_send(ScanJob {
abs_path: abs_path.clone(),
path: entry.path.clone(),
ignore_stack: child_ignore_stack.clone(),
scan_queue: job.scan_queue.clone(),
ancestor_inodes: state
.snapshot
.ancestor_inodes_for_path(&entry.path),
is_external: false,
})
.unwrap();
}
}
job.ignore_queue

View file

@ -454,6 +454,10 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) {
"b1.js": "b1",
"b2.js": "b2",
},
"c": {
"c1.js": "c1",
"c2.js": "c2",
}
},
},
"two": {
@ -521,6 +525,7 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) {
(Path::new("one/node_modules/b"), true),
(Path::new("one/node_modules/b/b1.js"), true),
(Path::new("one/node_modules/b/b2.js"), true),
(Path::new("one/node_modules/c"), true),
(Path::new("two"), false),
(Path::new("two/x.js"), false),
(Path::new("two/y.js"), false),
@ -564,6 +569,7 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) {
(Path::new("one/node_modules/b"), true),
(Path::new("one/node_modules/b/b1.js"), true),
(Path::new("one/node_modules/b/b2.js"), true),
(Path::new("one/node_modules/c"), true),
(Path::new("two"), false),
(Path::new("two/x.js"), false),
(Path::new("two/y.js"), false),
@ -578,6 +584,17 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) {
// Only the newly-expanded directory is scanned.
assert_eq!(fs.read_dir_call_count() - prev_read_dir_count, 1);
});
// No work happens when files and directories change within an unloaded directory.
let prev_fs_call_count = fs.read_dir_call_count() + fs.metadata_call_count();
fs.create_dir("/root/one/node_modules/c/lib".as_ref())
.await
.unwrap();
cx.foreground().run_until_parked();
assert_eq!(
fs.read_dir_call_count() + fs.metadata_call_count() - prev_fs_call_count,
0
);
}
#[gpui::test]
@ -1637,6 +1654,23 @@ async fn test_git_status(deterministic: Arc<Deterministic>, cx: &mut TestAppCont
}));
const A_TXT: &'static str = "a.txt";
const B_TXT: &'static str = "b.txt";
const E_TXT: &'static str = "c/d/e.txt";
const F_TXT: &'static str = "f.txt";
const DOTGITIGNORE: &'static str = ".gitignore";
const BUILD_FILE: &'static str = "target/build_file";
let project_path = Path::new("project");
// Set up git repository before creating the worktree.
let work_dir = root.path().join("project");
let mut repo = git_init(work_dir.as_path());
repo.add_ignore_rule(IGNORE_RULE).unwrap();
git_add(A_TXT, &repo);
git_add(E_TXT, &repo);
git_add(DOTGITIGNORE, &repo);
git_commit("Initial commit", &repo);
let tree = Worktree::local(
build_client(cx),
root.path(),
@ -1648,26 +1682,9 @@ async fn test_git_status(deterministic: Arc<Deterministic>, cx: &mut TestAppCont
.await
.unwrap();
tree.flush_fs_events(cx).await;
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
.await;
const A_TXT: &'static str = "a.txt";
const B_TXT: &'static str = "b.txt";
const E_TXT: &'static str = "c/d/e.txt";
const F_TXT: &'static str = "f.txt";
const DOTGITIGNORE: &'static str = ".gitignore";
const BUILD_FILE: &'static str = "target/build_file";
let project_path: &Path = &Path::new("project");
let work_dir = root.path().join("project");
let mut repo = git_init(work_dir.as_path());
repo.add_ignore_rule(IGNORE_RULE).unwrap();
git_add(Path::new(A_TXT), &repo);
git_add(Path::new(E_TXT), &repo);
git_add(Path::new(DOTGITIGNORE), &repo);
git_commit("Initial commit", &repo);
tree.flush_fs_events(cx).await;
deterministic.run_until_parked();
// Check that the right git state is observed on startup
@ -1687,39 +1704,39 @@ async fn test_git_status(deterministic: Arc<Deterministic>, cx: &mut TestAppCont
);
});
// Modify a file in the working copy.
std::fs::write(work_dir.join(A_TXT), "aa").unwrap();
tree.flush_fs_events(cx).await;
deterministic.run_until_parked();
// The worktree detects that the file's git status has changed.
tree.read_with(cx, |tree, _cx| {
let snapshot = tree.snapshot();
assert_eq!(
snapshot.status_for_file(project_path.join(A_TXT)),
Some(GitFileStatus::Modified)
);
});
git_add(Path::new(A_TXT), &repo);
git_add(Path::new(B_TXT), &repo);
// Create a commit in the git repository.
git_add(A_TXT, &repo);
git_add(B_TXT, &repo);
git_commit("Committing modified and added", &repo);
tree.flush_fs_events(cx).await;
deterministic.run_until_parked();
// Check that repo only changes are tracked
// The worktree detects that the files' git status have changed.
tree.read_with(cx, |tree, _cx| {
let snapshot = tree.snapshot();
assert_eq!(
snapshot.status_for_file(project_path.join(F_TXT)),
Some(GitFileStatus::Added)
);
assert_eq!(snapshot.status_for_file(project_path.join(B_TXT)), None);
assert_eq!(snapshot.status_for_file(project_path.join(A_TXT)), None);
});
// Modify files in the working copy and perform git operations on other files.
git_reset(0, &repo);
git_remove_index(Path::new(B_TXT), &repo);
git_stash(&mut repo);

View file

@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
description = "The fast, collaborative code editor."
edition = "2021"
name = "zed"
version = "0.93.0"
version = "0.93.4"
publish = false
[lib]
@ -16,6 +16,7 @@ name = "Zed"
path = "src/main.rs"
[dependencies]
audio = { path = "../audio" }
activity_indicator = { path = "../activity_indicator" }
auto_update = { path = "../auto_update" }
breadcrumbs = { path = "../breadcrumbs" }

View file

@ -1 +1 @@
dev
stable

View file

@ -7,6 +7,7 @@ use rust_embed::RustEmbed;
#[include = "fonts/**/*"]
#[include = "icons/**/*"]
#[include = "themes/**/*"]
#[include = "sounds/**/*"]
#[include = "*.md"]
#[exclude = "*.DS_Store"]
pub struct Assets;

View file

@ -180,6 +180,8 @@ fn main() {
background_actions,
});
cx.set_global(Arc::downgrade(&app_state));
audio::init(Assets, cx);
auto_update::init(http.clone(), client::ZED_SERVER_URL.clone(), cx);
workspace::init(app_state.clone(), cx);

View file

@ -2160,6 +2160,7 @@ mod tests {
state.initialize_workspace = initialize_workspace;
state.build_window_options = build_window_options;
theme::init((), cx);
audio::init((), cx);
call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
workspace::init(app_state.clone(), cx);
Project::init_settings(cx);

View file

@ -1,25 +1,32 @@
import { ColorScheme } from "../common";
import { interactive, toggleable } from "../element";
import { background, foreground } from "../styleTree/components";
import { ColorScheme } from "../common"
import { interactive, toggleable } from "../element"
import { background, foreground } from "../styleTree/components"
export type Margin = {
top: number;
bottom: number;
left: number;
right: number;
top: number
bottom: number
left: number
right: number
}
interface IconButtonOptions {
layer?: ColorScheme['lowest'] | ColorScheme['middle'] | ColorScheme['highest'];
color?: keyof ColorScheme['lowest'];
margin?: Partial<Margin>;
layer?:
| ColorScheme["lowest"]
| ColorScheme["middle"]
| ColorScheme["highest"]
color?: keyof ColorScheme["lowest"]
margin?: Partial<Margin>
}
type ToggleableIconButtonOptions = IconButtonOptions & { active_color?: keyof ColorScheme['lowest'] };
type ToggleableIconButtonOptions = IconButtonOptions & {
active_color?: keyof ColorScheme["lowest"]
}
export function icon_button(theme: ColorScheme, { color, margin, layer }: IconButtonOptions) {
if (!color)
color = "base";
export function icon_button(
theme: ColorScheme,
{ color, margin, layer }: IconButtonOptions
) {
if (!color) color = "base"
const m = {
top: margin?.top ?? 0,
@ -51,25 +58,29 @@ export function icon_button(theme: ColorScheme, { color, margin, layer }: IconBu
hovered: {
background: background(layer ?? theme.lowest, color, "hovered"),
color: foreground(layer ?? theme.lowest, color, "hovered"),
},
clicked: {
background: background(layer ?? theme.lowest, color, "pressed"),
color: foreground(layer ?? theme.lowest, color, "pressed"),
},
},
});
})
}
export function toggleable_icon_button(theme: ColorScheme, { color, active_color, margin }: ToggleableIconButtonOptions) {
if (!color)
color = "base";
export function toggleable_icon_button(
theme: ColorScheme,
{ color, active_color, margin }: ToggleableIconButtonOptions
) {
if (!color) color = "base"
return toggleable({
state: {
inactive: icon_button(theme, { color, margin }),
active: icon_button(theme, { color: active_color ? active_color : color, margin, layer: theme.middle }),
}
active: icon_button(theme, {
color: active_color ? active_color : color,
margin,
layer: theme.middle,
}),
},
})
}

View file

@ -1,25 +1,37 @@
import { ColorScheme } from "../common";
import { interactive, toggleable } from "../element";
import { TextProperties, background, foreground, text } from "../styleTree/components";
import { Margin } from "./icon_button";
import { ColorScheme } from "../common"
import { interactive, toggleable } from "../element"
import {
TextProperties,
background,
foreground,
text,
} from "../styleTree/components"
import { Margin } from "./icon_button"
interface TextButtonOptions {
layer?: ColorScheme['lowest'] | ColorScheme['middle'] | ColorScheme['highest'];
color?: keyof ColorScheme['lowest'];
margin?: Partial<Margin>;
text_properties?: TextProperties;
layer?:
| ColorScheme["lowest"]
| ColorScheme["middle"]
| ColorScheme["highest"]
color?: keyof ColorScheme["lowest"]
margin?: Partial<Margin>
text_properties?: TextProperties
}
type ToggleableTextButtonOptions = TextButtonOptions & { active_color?: keyof ColorScheme['lowest'] };
type ToggleableTextButtonOptions = TextButtonOptions & {
active_color?: keyof ColorScheme["lowest"]
}
export function text_button(theme: ColorScheme, { color, layer, margin, text_properties }: TextButtonOptions) {
if (!color)
color = "base";
export function text_button(
theme: ColorScheme,
{ color, layer, margin, text_properties }: TextButtonOptions
) {
if (!color) color = "base"
const text_options: TextProperties = {
size: "xs",
weight: "normal",
...text_properties
...text_properties,
}
const m = {
@ -40,7 +52,7 @@ export function text_button(theme: ColorScheme, { color, layer, margin, text_pro
},
margin: m,
button_height: 22,
...text(layer ?? theme.lowest, "sans", color, text_options)
...text(layer ?? theme.lowest, "sans", color, text_options),
},
state: {
default: {
@ -50,25 +62,29 @@ export function text_button(theme: ColorScheme, { color, layer, margin, text_pro
hovered: {
background: background(layer ?? theme.lowest, color, "hovered"),
color: foreground(layer ?? theme.lowest, color, "hovered"),
},
clicked: {
background: background(layer ?? theme.lowest, color, "pressed"),
color: foreground(layer ?? theme.lowest, color, "pressed"),
},
},
});
})
}
export function toggleable_text_button(theme: ColorScheme, { color, active_color, margin }: ToggleableTextButtonOptions) {
if (!color)
color = "base";
export function toggleable_text_button(
theme: ColorScheme,
{ color, active_color, margin }: ToggleableTextButtonOptions
) {
if (!color) color = "base"
return toggleable({
state: {
inactive: text_button(theme, { color, margin }),
active: text_button(theme, { color: active_color ? active_color : color, margin, layer: theme.middle }),
}
active: text_button(theme, {
color: active_color ? active_color : color,
margin,
layer: theme.middle,
}),
},
})
}

View file

@ -1,7 +1,7 @@
import merge from "ts-deepmerge"
import { DeepPartial } from "utility-types"
type InteractiveState =
export type InteractiveState =
| "default"
| "hovered"
| "clicked"

View file

@ -26,15 +26,15 @@ export default function assistant(colorScheme: ColorScheme) {
},
container: {
padding: { left: 12, right: 8.5 },
}
},
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
color: foreground(layer, "hovered"),
},
},
},
}),
splitButton: interactive({
base: {
@ -48,15 +48,15 @@ export default function assistant(colorScheme: ColorScheme) {
},
container: {
padding: { left: 8.5, right: 8.5 },
}
},
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
color: foreground(layer, "hovered"),
},
},
},
}),
quoteButton: interactive({
base: {
@ -70,15 +70,15 @@ export default function assistant(colorScheme: ColorScheme) {
},
container: {
padding: { left: 8.5, right: 8.5 },
}
},
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
color: foreground(layer, "hovered"),
},
},
},
}),
assistButton: interactive({
base: {
@ -92,15 +92,15 @@ export default function assistant(colorScheme: ColorScheme) {
},
container: {
padding: { left: 8.5, right: 8.5 },
}
},
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
color: foreground(layer, "hovered"),
},
},
},
}),
zoomInButton: interactive({
base: {
@ -114,15 +114,15 @@ export default function assistant(colorScheme: ColorScheme) {
},
container: {
padding: { left: 10, right: 10 },
}
},
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
color: foreground(layer, "hovered"),
},
},
},
}),
zoomOutButton: interactive({
base: {
@ -136,15 +136,15 @@ export default function assistant(colorScheme: ColorScheme) {
},
container: {
padding: { left: 10, right: 10 },
}
},
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
color: foreground(layer, "hovered"),
},
},
},
}),
plusButton: interactive({
base: {
@ -158,29 +158,29 @@ export default function assistant(colorScheme: ColorScheme) {
},
container: {
padding: { left: 10, right: 10 },
}
},
},
state: {
hovered: {
icon: {
color: foreground(layer, "hovered")
}
}
}
color: foreground(layer, "hovered"),
},
},
},
}),
title: {
...text(layer, "sans", "default", { size: "sm" })
...text(layer, "sans", "default", { size: "sm" }),
},
savedConversation: {
container: interactive({
base: {
background: background(layer, "on"),
padding: { top: 4, bottom: 4 }
padding: { top: 4, bottom: 4 },
},
state: {
hovered: {
background: background(layer, "on", "hovered"),
}
},
},
}),
savedAt: {
@ -189,8 +189,11 @@ export default function assistant(colorScheme: ColorScheme) {
},
title: {
margin: { left: 16 },
...text(layer, "sans", "default", { size: "sm", weight: "bold" }),
}
...text(layer, "sans", "default", {
size: "sm",
weight: "bold",
}),
},
},
userSender: {
default: {

View file

@ -93,6 +93,14 @@ interface Text extends Object {
underline?: boolean
}
export interface TextStyle extends Object {
family: keyof typeof fontFamilies
color: string
size: number
weight?: FontWeight
underline?: boolean
}
export interface TextProperties {
size?: keyof typeof fontSizes
weight?: FontWeight

View file

@ -1,73 +1,125 @@
import { ColorScheme } from "../theme/colorScheme"
import { withOpacity } from "../theme/color"
import { background, border, foreground, text } from "./components"
import {
Border,
TextStyle,
background,
border,
foreground,
text,
} from "./components"
import { interactive, toggleable } from "../element"
import merge from "ts-deepmerge"
export default function projectPanel(colorScheme: ColorScheme) {
const { isLight } = colorScheme
let layer = colorScheme.middle
let baseEntry = {
height: 22,
iconColor: foreground(layer, "variant"),
iconSize: 7,
iconSpacing: 5,
type EntryStateProps = {
background?: string
border?: Border
text?: TextStyle
iconColor?: string
}
let status = {
git: {
modified: isLight
? colorScheme.ramps.yellow(0.6).hex()
: colorScheme.ramps.yellow(0.5).hex(),
inserted: isLight
? colorScheme.ramps.green(0.45).hex()
: colorScheme.ramps.green(0.5).hex(),
conflict: isLight
? colorScheme.ramps.red(0.6).hex()
: colorScheme.ramps.red(0.5).hex(),
},
type EntryState = {
default: EntryStateProps
hovered?: EntryStateProps
clicked?: EntryStateProps
}
const default_entry = interactive({
base: {
...baseEntry,
text: text(layer, "mono", "variant", { size: "sm" }),
status,
},
state: {
default: {
background: background(layer),
const entry = (unselected?: EntryState, selected?: EntryState) => {
const git_status = {
git: {
modified: isLight
? colorScheme.ramps.yellow(0.6).hex()
: colorScheme.ramps.yellow(0.5).hex(),
inserted: isLight
? colorScheme.ramps.green(0.45).hex()
: colorScheme.ramps.green(0.5).hex(),
conflict: isLight
? colorScheme.ramps.red(0.6).hex()
: colorScheme.ramps.red(0.5).hex(),
},
hovered: {
background: background(layer, "variant", "hovered"),
},
clicked: {
background: background(layer, "variant", "pressed"),
},
},
})
}
let entry = toggleable({
base: default_entry,
state: {
active: interactive({
base: {
...default_entry,
},
state: {
default: {
background: background(colorScheme.lowest),
const base_properties = {
height: 22,
background: background(layer),
iconColor: foreground(layer, "variant"),
iconSize: 7,
iconSpacing: 5,
text: text(layer, "sans", "variant", { size: "sm" }),
status: {
...git_status,
},
}
const selectedStyle: EntryState | undefined = selected
? selected
: unselected
const unselected_default_style = merge(
base_properties,
unselected?.default ?? {},
{}
)
const unselected_hovered_style = merge(
base_properties,
{ background: background(layer, "hovered") },
unselected?.hovered ?? {},
)
const unselected_clicked_style = merge(
base_properties,
{ background: background(layer, "pressed") },
unselected?.clicked ?? {},
)
const selected_default_style = merge(
base_properties,
{
background: background(colorScheme.lowest),
text: text(colorScheme.lowest, "sans", { size: "sm" }),
},
selectedStyle?.default ?? {},
)
const selected_hovered_style = merge(
base_properties,
{
background: background(colorScheme.lowest, "hovered"),
text: text(colorScheme.lowest, "sans", { size: "sm" }),
},
selectedStyle?.hovered ?? {},
)
const selected_clicked_style = merge(
base_properties,
{
background: background(colorScheme.lowest, "pressed"),
text: text(colorScheme.lowest, "sans", { size: "sm" }),
},
selectedStyle?.clicked ?? {},
)
return toggleable({
state: {
inactive: interactive({
state: {
default: unselected_default_style,
hovered: unselected_hovered_style,
clicked: unselected_clicked_style,
},
hovered: {
background: background(colorScheme.lowest, "hovered"),
}),
active: interactive({
state: {
default: selected_default_style,
hovered: selected_hovered_style,
clicked: selected_clicked_style,
},
clicked: {
background: background(colorScheme.lowest, "pressed"),
},
},
}),
},
})
}),
},
})
}
const defaultEntry = entry()
return {
openProjectButton: interactive({
@ -104,38 +156,41 @@ export default function projectPanel(colorScheme: ColorScheme) {
background: background(layer),
padding: { left: 6, right: 6, top: 0, bottom: 6 },
indentWidth: 12,
entry,
entry: defaultEntry,
draggedEntry: {
...baseEntry,
text: text(layer, "mono", "on", { size: "sm" }),
...defaultEntry.inactive.default,
text: text(layer, "sans", "on", { size: "sm" }),
background: withOpacity(background(layer, "on"), 0.9),
border: border(layer),
status,
},
ignoredEntry: {
...entry,
iconColor: foreground(layer, "disabled"),
text: text(layer, "mono", "disabled"),
active: {
...entry.active,
iconColor: foreground(layer, "variant"),
},
},
cutEntry: {
...entry,
text: text(layer, "mono", "disabled"),
active: {
...entry.active,
ignoredEntry: entry(
{
default: {
...entry.active.default,
background: background(layer, "active"),
text: text(layer, "mono", "disabled", { size: "sm" }),
text: text(layer, "sans", "disabled"),
},
},
},
{
default: {
iconColor: foreground(layer, "variant"),
},
}
),
cutEntry: entry(
{
default: {
text: text(layer, "sans", "disabled"),
},
},
{
default: {
background: background(layer, "active"),
text: text(layer, "sans", "disabled", { size: "sm" }),
},
}
),
filenameEditor: {
background: background(layer, "on"),
text: text(layer, "mono", "on", { size: "sm" }),
text: text(layer, "sans", "on", { size: "sm" }),
selection: colorScheme.players[0],
},
}

View file

@ -1,9 +1,9 @@
import { ColorScheme } from "../common";
import { ColorScheme } from "../common"
import { icon_button, toggleable_icon_button } from "../component/icon_button"
import { toggleable_text_button } from "../component/text_button"
import { interactive, toggleable } from "../element"
import { withOpacity } from "../theme/color";
import { background, border, foreground, text } from "./components";
import { withOpacity } from "../theme/color"
import { background, border, foreground, text } from "./components"
const ITEM_SPACING = 8
const TITLEBAR_HEIGHT = 32
@ -25,7 +25,7 @@ function build_spacing(
function call_controls(theme: ColorScheme) {
const button_height = 18
const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING);
const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING)
const marginY = {
top: space.marginY,
bottom: space.marginY,
@ -38,14 +38,14 @@ function call_controls(theme: ColorScheme) {
left: space.group,
right: space.half_item,
},
active_color: 'negative'
active_color: "negative",
}),
toggle_speakers_button: toggleable_icon_button(theme, {
margin: {
...marginY,
left: space.half_item,
right: space.half_item
right: space.half_item,
},
}),
@ -53,9 +53,9 @@ function call_controls(theme: ColorScheme) {
margin: {
...marginY,
left: space.half_item,
right: space.group
right: space.group,
},
active_color: 'accent'
active_color: "accent",
}),
muted: foreground(theme.lowest, "negative"),
@ -64,15 +64,15 @@ function call_controls(theme: ColorScheme) {
}
/**
* Opens the User Menu when toggled
*
* When logged in shows the user's avatar and a chevron,
* When logged out only shows a chevron.
*/
* Opens the User Menu when toggled
*
* When logged in shows the user's avatar and a chevron,
* When logged out only shows a chevron.
*/
function user_menu(theme: ColorScheme) {
const button_height = 18
const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING);
const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING)
const build_button = ({ online }: { online: boolean }) => {
const button = toggleable({
@ -124,8 +124,8 @@ function user_menu(theme: ColorScheme) {
background: background(theme.middle, "pressed"),
},
},
}
});
},
})
return {
user_menu: button,
@ -134,7 +134,7 @@ function user_menu(theme: ColorScheme) {
icon_height: 16,
corner_radius: 4,
outer_width: 16,
outer_corner_radius: 16
outer_corner_radius: 16,
},
icon: {
margin: {
@ -145,8 +145,8 @@ function user_menu(theme: ColorScheme) {
},
width: 11,
height: 11,
color: foreground(theme.lowest)
}
color: foreground(theme.lowest),
},
}
}
return {
@ -240,7 +240,7 @@ export function titlebar(theme: ColorScheme) {
leave_call_button: icon_button(theme, {
margin: {
left: ITEM_SPACING / 2,
right: ITEM_SPACING
right: ITEM_SPACING,
},
}),
@ -261,6 +261,6 @@ export function titlebar(theme: ColorScheme) {
background: foreground(theme.lowest, "accent"),
},
shareButton: toggleable_text_button(theme, {}),
user_menu: user_menu(theme)
user_menu: user_menu(theme),
}
}

View file

@ -1,39 +1,39 @@
import { ThemeSyntax } from "../../common";
import { ThemeSyntax } from "../../common"
export const color = {
default: {
base: '#191724',
surface: '#1f1d2e',
overlay: '#26233a',
muted: '#6e6a86',
subtle: '#908caa',
text: '#e0def4',
love: '#eb6f92',
gold: '#f6c177',
rose: '#ebbcba',
pine: '#31748f',
foam: '#9ccfd8',
iris: '#c4a7e7',
highlightLow: '#21202e',
highlightMed: '#403d52',
highlightHigh: '#524f67',
base: "#191724",
surface: "#1f1d2e",
overlay: "#26233a",
muted: "#6e6a86",
subtle: "#908caa",
text: "#e0def4",
love: "#eb6f92",
gold: "#f6c177",
rose: "#ebbcba",
pine: "#31748f",
foam: "#9ccfd8",
iris: "#c4a7e7",
highlightLow: "#21202e",
highlightMed: "#403d52",
highlightHigh: "#524f67",
},
moon: {
base: '#232136',
surface: '#2a273f',
overlay: '#393552',
muted: '#6e6a86',
subtle: '#908caa',
text: '#e0def4',
love: '#eb6f92',
gold: '#f6c177',
rose: '#ea9a97',
pine: '#3e8fb0',
foam: '#9ccfd8',
iris: '#c4a7e7',
highlightLow: '#2a283e',
highlightMed: '#44415a',
highlightHigh: '#56526e',
base: "#232136",
surface: "#2a273f",
overlay: "#393552",
muted: "#6e6a86",
subtle: "#908caa",
text: "#e0def4",
love: "#eb6f92",
gold: "#f6c177",
rose: "#ea9a97",
pine: "#3e8fb0",
foam: "#9ccfd8",
iris: "#c4a7e7",
highlightLow: "#2a283e",
highlightMed: "#44415a",
highlightHigh: "#56526e",
},
dawn: {
base: "#faf4ed",
@ -51,8 +51,8 @@ export const color = {
highlightLow: "#f4ede8",
highlightMed: "#dfdad9",
highlightHigh: "#cecacd",
}
};
},
}
export const syntax = (c: typeof color.default): Partial<ThemeSyntax> => {
return {

View file

@ -6,12 +6,12 @@ import {
ThemeConfig,
} from "../../common"
import { color as c, syntax } from "./common";
import { color as c, syntax } from "./common"
const color = c.dawn
const green = chroma.mix(color.foam, "#10b981", 0.6, 'lab');
const magenta = chroma.mix(color.love, color.pine, 0.5, 'lab');
const green = chroma.mix(color.foam, "#10b981", 0.6, "lab")
const magenta = chroma.mix(color.love, color.pine, 0.5, "lab")
export const theme: ThemeConfig = {
name: "Rosé Pine Dawn",
@ -21,7 +21,19 @@ export const theme: ThemeConfig = {
licenseUrl: "https://github.com/edunfelt/base16-rose-pine-scheme",
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
neutral: chroma.scale([color.base, color.surface, color.highlightHigh, color.overlay, color.muted, color.subtle, color.text].reverse()).domain([0, 0.35, 0.45, 0.65, 0.7, 0.8, 0.9, 1]),
neutral: chroma
.scale(
[
color.base,
color.surface,
color.highlightHigh,
color.overlay,
color.muted,
color.subtle,
color.text,
].reverse()
)
.domain([0, 0.35, 0.45, 0.65, 0.7, 0.8, 0.9, 1]),
red: colorRamp(chroma(color.love)),
orange: colorRamp(chroma(color.iris)),
yellow: colorRamp(chroma(color.gold)),
@ -32,6 +44,6 @@ export const theme: ThemeConfig = {
magenta: colorRamp(chroma(magenta)),
},
override: {
syntax: syntax(color)
}
syntax: syntax(color),
},
}

View file

@ -6,12 +6,12 @@ import {
ThemeConfig,
} from "../../common"
import { color as c, syntax } from "./common";
import { color as c, syntax } from "./common"
const color = c.moon
const green = chroma.mix(color.foam, "#10b981", 0.6, 'lab');
const magenta = chroma.mix(color.love, color.pine, 0.5, 'lab');
const green = chroma.mix(color.foam, "#10b981", 0.6, "lab")
const magenta = chroma.mix(color.love, color.pine, 0.5, "lab")
export const theme: ThemeConfig = {
name: "Rosé Pine Moon",
@ -21,7 +21,17 @@ export const theme: ThemeConfig = {
licenseUrl: "https://github.com/edunfelt/base16-rose-pine-scheme",
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
neutral: chroma.scale([color.base, color.surface, color.highlightHigh, color.overlay, color.muted, color.subtle, color.text]).domain([0, 0.3, 0.55, 1]),
neutral: chroma
.scale([
color.base,
color.surface,
color.highlightHigh,
color.overlay,
color.muted,
color.subtle,
color.text,
])
.domain([0, 0.3, 0.55, 1]),
red: colorRamp(chroma(color.love)),
orange: colorRamp(chroma(color.iris)),
yellow: colorRamp(chroma(color.gold)),
@ -32,6 +42,6 @@ export const theme: ThemeConfig = {
magenta: colorRamp(chroma(magenta)),
},
override: {
syntax: syntax(color)
}
syntax: syntax(color),
},
}

View file

@ -5,12 +5,12 @@ import {
ThemeLicenseType,
ThemeConfig,
} from "../../common"
import { color as c, syntax } from "./common";
import { color as c, syntax } from "./common"
const color = c.default
const green = chroma.mix(color.foam, "#10b981", 0.6, 'lab');
const magenta = chroma.mix(color.love, color.pine, 0.5, 'lab');
const green = chroma.mix(color.foam, "#10b981", 0.6, "lab")
const magenta = chroma.mix(color.love, color.pine, 0.5, "lab")
export const theme: ThemeConfig = {
name: "Rosé Pine",
@ -20,7 +20,15 @@ export const theme: ThemeConfig = {
licenseUrl: "https://github.com/edunfelt/base16-rose-pine-scheme",
licenseFile: `${__dirname}/LICENSE`,
inputColor: {
neutral: chroma.scale([color.base, color.surface, color.highlightHigh, color.overlay, color.muted, color.subtle, color.text]),
neutral: chroma.scale([
color.base,
color.surface,
color.highlightHigh,
color.overlay,
color.muted,
color.subtle,
color.text,
]),
red: colorRamp(chroma(color.love)),
orange: colorRamp(chroma(color.iris)),
yellow: colorRamp(chroma(color.gold)),
@ -31,6 +39,6 @@ export const theme: ThemeConfig = {
magenta: colorRamp(chroma(magenta)),
},
override: {
syntax: syntax(color)
}
syntax: syntax(color),
},
}