Fixes ZED-5K1
Release Notes:
- Fixed a panic on windows when a monitor disappears from windows
monitor enumeration
---------
Co-authored-by: John Tur <john-tur@outlook.com>
Adds 4 (technically 5) new tools to the zed agent, corresponding to LSP
actions:
- `find_references`
- `goto_definition`
- `rename_symbol`
- `get_code_actions` and `apply_code_actions`
Notes:
- `rename_symbol` skips doing a `prepare_rename`. If there is nothing to
rename at the position, it will forward the error to the agent
- The code action tools are stateful. The state is stored in the
`get_code_actions` tool itself as a `PendingCodeActions`. It is not
passed into/out of subagents. Calling `apply_code_actions` without
calling `get_code_actions` first is an error, but I've never seen an
agent do this
Symbols are identified by:
- file name
- line number
- symbol
If there is no substring match on that line for the symbol text, it is
an error. If there are multiple, it chooses the first. This may not be
great if you have a line like: `fn convert(x: foo::Something) ->
bar::Something` - the second `Something` is a different symbol, but is
inacessible to these tools. Probably fine for now, but we can look into
improving
Release Notes:
- Added: New tools for the Zed Agent for interacting with language
servers
---------
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
Instead of manually handing hiding the cursor on keyboard input at the
editor level, GPUI will now take care of it.
This makes it significantly easier to handle the edge cases, and allows
delegating the cursor restoration to the platform itself in the macOS
case. On Linux and Windows, we still have to restore the cursor on
movement ourselves, but this now happens at the platform-specific level.
Bugs fixed by this change:
- No cursor when "Unsaved edits" prompt appears
- Cursor disappears when clicking a panel button if it contains a search
bar (e.g. collab panel)
### Setting rename
The `hide_mouse` setting value `"on_typing_and_movement"` has been
renamed to `"on_typing_and_action"` to better reflect what it actually
does — it hides the cursor when a keystroke resolves to an action (e.g.
cursor movement, deletion). Existing settings are migrated
automatically.
### Tested platforms
- [x] macOS
- [x] Wayland
- [x] X11
- [x] Windows
- [x] Web
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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Renamed the `hide_mouse` setting value `on_typing_and_movement` to
`on_typing_and_action` to better describe its behavior (existing
settings are auto-migrated)
- Fixed a few situations where the mouse cursor would be incorrectly
hidden
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 #ISSUE
Release Notes:
- N/A
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
Closes#53202
`Anchored::prepaint` computes the bounding box of its children to
determine the size used for fitting the anchored element in the window.
Previously, this calculation manually tracked the minimum origin and
maximum bottom-right point, initializing the maximum point to `(0, 0)`.
If child bounds were in negative coordinates, the maximum point could be
clamped to `(0, 0)`, inflating the computed size.
This replaces the manual min/max accumulation with `Bounds::union`,
starting from the actual child bounds instead of sentinel values. This
computes the child bounding box correctly regardless of coordinate sign.
---
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
Release Notes:
- Fixed a bug where the context menu in the agent panel (and other
scrollable surfaces) would appear at the wrong location
Apple's text rendering stack dilates glyph outlines for text rendered
with a light foreground color. Zed doesn't consider this nuance today;
we populate our atlas using glyphs rendered with a dark foreground
color. This means that, particularly in dark themes, text in Zed looks
thin and blurry, and doesn't match the look of native macOS
applications.
This pull request replicates the native behavior of Core Graphics. Some
reverse-engineering revealed that CG computes the foreground color
luminance using the Rec. 709 formula ($Y=0.2126R + 0.7152B + 0.0722G$)
and quantizes it into five levels (0, 0.25, 0.5, 0.75, and 1). Each
level uses a different dilation factor.
With this patch, we calculate this same luminance bucket and supply it
as the foreground color during rasterization. The correct dilation will
be applied, and we'll store this glyph in the atlas keyed by this
luminance bucket. So, we'll generate and use up to 5 different bitmaps
for each glyph based on its foreground color.
I've confirmed that text rendered by Zed now exactly matches native
applications like Safari, TextEdit, etc.
Release Notes:
- Improved text rendering clarity on macOS, particularly in dark themes.
---------
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
#48029 introduced `set_document_path` which is frequently (we are also
working on a PR to make it less frequent) called as tabs and panes
update. Apparently, the AppKit function it uses
(`NSWindow::setRepresentedFilename`) can cause the current cursor style
to be reset, producing flicker as the cursor would change to `Arrow`
temporarily until the right cursor style is set again in the next frame.
This PR reworks how we set the cursor to use AppKit's
`resetCursorRects`, giving us a chance to re-set the cursor when the OS
decides to invalidate it.
Finally, it fixes a separate bug introduced in #50827 where moving the
mouse while typing in an editor would cause to the cursor to flicker
between `None` (hidden) -> `Arrow` -> `IBeam`. This was caused by
incorrectly resetting the cursor to `Arrow` when the last input came
from the keyboard.
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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
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
https://github.com/zed-industries/zed/issues/54981#issuecomment-4333425722
Release Notes:
- N/A or Added/Fixed/Improved ...
This allows us to move entities between windows without breaking all the
callbacks.
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
Release Notes:
- N/A
This fixes an issue where due to the scrollbar appearing, the reported
content size would shift, causing issues in the process. We now actually
always reserve space for the scrollbar appropriately as described in
https://github.com/zed-industries/zed/pull/33636 initially.
Release Notes:
- Fixed an issue where the scrollbar could cause a layout shift in the
terminal.
---------
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Clamp the cached `frame_index` of an `img` element when the underlying
image changes so a stale index from a previous, longer animation cannot
index out of bounds on the new image.
Release Notes:
- Fixed a panic when a GIF was replaced with one that had fewer frames
in Markdown Preview.
This reduces the size of Sharedstring from 32 bytes to 24 while also
allowing for small-string optimization, meaning strings with length < 23
bytes will not actually allocate.
Release Notes:
- N/A or Added/Fixed/Improved ...
Painting primitives at non-integer pixel coordinates produces blurry
output. Pixel snapping converts layout coordinates into integer
device-pixel coordinates so painted edges land exactly on physical pixel
boundaries.
Non-integer coordinates can arise for several reasons, including:
- flex distribution, percentages, centering, and text measurement can
produce fractional element sizes and positions;
- at fractional scale factors (for example 125% or 150%), integer
logical-pixel values can map to non-integer device-pixel values.
We pixel-snap by rounding in device-pixel space, after multiplying by
`scale_factor`, so that snapping targets physical pixels. Bounds are
divided by `scale_factor` before being returned to GPUI.
Midpoints are rounded toward zero. This is a stylistic choice: a
1-logical-pixel line at 150% scale should render as 1 dp rather than 2
dp.
Pixel snapping is done in two phases:
1. Pre-layout metric snapping. Before Taffy computes layout, all
authored absolute lengths are rounded in `to_taffy`. This includes
borders, padding, gaps, and explicit sizes. Custom-measured leaf nodes
have their measured sizes rounded up to integer device-pixel lengths.
2. Post-layout edge snapping. After Taffy resolves the tree, layout
relationships such as flex shares, grid tracks, percentages, and
centering can produce new fractional edge positions. Boxes now have
edges in absolute coordinates, and snapping must decide where those
edges land on the device-pixel grid.
Ideally, post-layout snapping would satisfy:
- Edge closure. Two raw layout edges at the same absolute position
should snap to the same pixel column.
- Translation stability. A component's internal geometry should not
change when it moves to a new absolute position.
These goals are in tension because rounding is not associative. The
simple local schemes make different tradeoffs:
- Absolute edge rounding gives each window coordinate one answer, so
coincident edges always close globally. But a span's snapped length is
`round(far) - round(near)`, which may change by 1 dp as its absolute
origin moves.
- Parent-relative edge rounding rounds each child inside its parent's
coordinate space. This guarantees translation stability, but a shared
edge reached through different parents can accumulate different
rounding, causing non-closure between cousins.
- Length rounding rounds each width, height, and thickness independently
and then places boxes from those rounded lengths. Sizes stay stable
under translation, but neighboring boxes derive their shared boundary
from different sources, so closure is not guaranteed.
We apply absolute edge rounding for each element's outer box in
post-layout rounding to preserve closure. Border and padding widths are
not touched by post-layout rounding; they keep their pre-layout rounded
value so that they remain stable under translation.
This gives both closure and translation stability in the case that all
local metrics are integer device-pixel lengths. Pre-layout rounding
covers that in most cases. The exception is metrics resolved by layout
relationships, such as percentages. Outer box edges will still close
globally, and painted border widths are still snapped independently, but
the raw content-box origin can carry a 1 dp residual into descendants.
---
Fixes https://github.com/zed-industries/zed/issues/46360
Fixes https://github.com/zed-industries/zed/issues/44528
Fixes https://github.com/zed-industries/zed/issues/40282
Fixes https://github.com/zed-industries/zed/issues/42257
---
Release Notes:
- Fixed potentially blurry appearance of UI elements when using
fractional display scaling.
This sets the accessibility document property (AXDocument) of the
window, which other apps can use to understand what file the current
window represents. Document-based apps on macOS are generally expected
to set this property.
The document path is set via `Window::set_document_path()` which calls
`setRepresentedFilename:` on the underlying NSWindow. The workspace
updates this path in `update_window_title()` whenever the active item or
project structure changes. For tests, instead of trying to somehow
assemble a proper NSWindow, we store the document path on the window
mock used for testing and check its value.
Motivation: I am the developer of Timing, an automatic time tracking app
for Mac. Timing uses the `AXDocument` property (a standard property of
most document-based app windows on macOS) to understand what document
the user is working on. With this change, Timing is able to understand
which directory the user is working in. Without this, Timing would only
record the window title, i.e. the filename without information about the
containing directory. I've had several users ask for better support for
Zed.
Here's a screenshot of the macOS Accessibility Inspector showing the
`AXDocument` property with this change. The UI of Zed itself does not
change. However, in my dev build of Zed, the traffic lights are a bit
too large and misaligned. However, this happens even when building
`main`, so I assume it’s unrelated to my changes.
<img width="1370" height="1162" alt="Screenshot 2026-01-30 at 16 18 25"
src="https://github.com/user-attachments/assets/dc260252-91fb-41e1-97a9-e6fb843c6a70"
/>
Release Notes:
- Set the represented filename property of windows on macOS
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
No, sadly, the title is not a typo. See
https://www.githubstatus.com/incidents/zsg1lk7w13cf for the context.
I'll read with joy and popcorn through that root cause analysis.
It makes literally zero sense what happened here, but for some completly
bonkers reason GitHub completely messed up the merge queue with
https://github.com/zed-industries/zed/pull/54632.
I have no idea how it happened. It makes literally zero sense. A PR
going into the merge queue should have the same LoC when getting out of
it. GitHub obviously does not check this. GitHub causes extra work with
a feature that is supposed to save time.
Thanks, I guess.
Release Notes:
- N/A
---------
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Needs https://github.com/zed-industries/zed/pull/54635 for the profile
overrides added into default settings json to work.
Part of https://github.com/zed-industries/zed/issues/48968
Another part of the fix related seems to be
https://github.com/zed-industries/zed/pull/45669 ?
Using the steps from the issue and profiling on macOs had shown that Zed
has 2 memory "leaks" in play when a certain file is being rewritten a
lot of times.
* First, the thread profiler registers a lot of tasks' data and fills
its buffer to the limit:
<img width="3456" height="2158" alt="image"
src="https://github.com/user-attachments/assets/f183312d-4389-4072-8915-d54e60419b08"
/>
* Second, if the buffer gets open, the undo history fragments start to
creep up infinitely:
<img width="3456" height="2158" alt="image"
src="https://github.com/user-attachments/assets/61a2b66b-81fd-4973-9c3c-c339f886d9b2"
/>
The PR aims to solve the first issue by disabling the profiling by
default, yet leaving the way to turn in on quickly with settings.
The memory usage profiling shows that the memory usage is now
dynamically affected by the new setting:
<img width="2032" height="1136" alt="image"
src="https://github.com/user-attachments/assets/8a6c76b9-6fb7-44bc-ac1d-3c34afe7c575"
/>
While the test directory being thrashed with the script from the issue,
* first, Zed starts with the profiling disabled
* then gets the profiling enabled which results in the memory growth
close to 1 minute mark of the screenshot
* last, the profiling gets disabled again, releasing all the memory
accumulated
Release Notes:
- Improved Zed's default memory usage
Fixes an inconsistency with the way scrolling interacts with the
`follow_tail` GPUI list feature:
- Scrolling normally sets `follow_state` to `FollowState::Tail {
is_following: false }`, so later scrolling to the bottom re-engages
`follow_tail`
- Scrolling by dragging the scrollbar previously set `follow_state` to
`FollowState::Normal`, preventing `follow_tail` from re-engaging after
scrolling to the bottom
As far as I can tell this behavior was unintentional, despite there
being a test that exercised this exact behavior that `follow_tail`
doesn't re-engage after dragging the scrollbar — my best guess is that
that test was derived from the bugged scrollbar handler logic, and not
based on any actual intent for scrollbar drags to prevent `follow_tail`
from re-engaging.
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
Release Notes:
- Fixed agent panel auto-scroll not re-engaging after interacting with
the scrollbar
---------
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This PR brings back the button to filter remote branches when accessing
the title bar's branch picker with the mouse. It was unintentionally
removed when we introduced the new worktree picker.
Release Notes:
- N/A
Build split text directly from Arc<str> slices instead of allocating
intermediate Strings. Reuse the original SharedString for full-line
splits, return a static empty string for empty sides, and add a
regression test covering boundary splits.
Release Notes:
- N/A
---------
Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
## Summary
- Enable VSCode-like side-by-side file comparison in FileDiffView using
SplittableEditor
- Leverages existing SplittableEditor infrastructure that ProjectDiff
already uses
- Controlled by the user's `diff_view_style` setting (split/unified)
## Changes
- `crates/git_ui/src/file_diff_view.rs`: Refactored to use
SplittableEditor instead of plain Editor (+66/-30 lines)
## Test plan
- [ ] Open a file with git changes and verify the diff view renders
correctly
- [ ] Toggle between split and unified diff view styles via settings
- [ ] Verify side-by-side comparison shows old/new content correctly
Release Notes:
- Enabled side by side diffs in the file diff view
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
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#46942
## Problem
The terminal's monospace grid forcing logic in `LineLayoutCache` breaks
rendering of combining marks in complex scripts. Thai, Lao, Arabic,
Hebrew, Myanmar, Khmer, and other scripts use combining characters
(vowels, tone marks, diacritics) that should render on top of or below
their base character — not in a separate cell.
The root cause is in `apply_force_width` within `line_layout.rs`. After
HarfBuzz correctly shapes the text (positioning combining marks at the
same x coordinate as their base character), the force-width loop blindly
increments the cell position counter for **every** glyph — including
zero-advance combining marks. This displaces them into the next cell,
breaking the visual output.
For example, Thai `สวัสดี` renders as `ส ว ั ส ด ี` with vowels and tone
marks scattered across separate cells.
#39526 previously fixed Latin combining characters (like NFD-normalized
é) by handling them at the terminal cell level via
`append_zero_width_chars`. That fix works when alacritty's cell model
already identifies the characters as zero-width. However, many complex
script combining marks still get mispositioned at the shaping layer —
which is what this PR addresses.
## Solution
Extracted `apply_force_width_to_layout()` in `line_layout.rs` that
detects combining marks during the force-width pass by checking whether
a glyph's shaped x position has advanced by at least half a cell width
beyond the previous base character. Combining marks (which HarfBuzz
places at the same x as their base) are kept at their shaped position
relative to the forced base, rather than being pushed to the next cell.
This approach is **script-agnostic** — it works for all complex scripts
(Thai, Arabic, Devanagari, Myanmar, Khmer, etc.) without hardcoding
Unicode ranges or disabling the monospace grid. The fix lives at the
text shaping layer in GPUI, so it benefits any consumer of
`LineLayoutCache` that uses `force_width`.
The two duplicate force-width loops (in `layout_line` and
`layout_line_by_hash`) are consolidated into the single shared helper.
## Testing
**Unit tests** cover the following scenarios:
- Latin text positions remain unchanged
- A single combining mark stays at its base character's cell
- A base character following combining marks gets the correct next cell
- Multiple stacked combining marks all stay anchored to cell 0
- Drifted base positions respect the existing 1px tolerance
**Manual testing** with the following scripts in the integrated
terminal:
| Script | Test text | What to look for |
|--------|-----------|-----------------|
| Thai | `สวัสดีครับ ภาษาไทย ก้อนหิน น้ำตก` | Vowel signs (ั ี) and tone
marks (้) render above/below consonants, not in separate cells |
| Lao | `ສະບາຍດີ ພາສາລາວ` | Vowel signs stay attached to consonants |
| Arabic | `بِسْمِ اللَّهِ الرَّحْمَٰنِ` | Diacritics (harakat) render
on their base letters |
| Hebrew | `בְּרֵאשִׁית בָּרָא אֱלֹהִים` | Vowel points (nikud) stay
under/over their letters |
| Hindi | `नमस्ते हिन्दी भाषा` | Vowel matras render correctly on
consonants |
| Bengali | `বাংলা ভাষা নমস্কার` | Combining marks stay attached |
| Tamil | `தமிழ் மொழி வணக்கம்` | Vowel signs render correctly |
| Myanmar | `မြန်မာဘာသာ မင်္ဂလာပါ` | Combining marks and medial
consonants stay in place |
| Khmer | `ភាសាខ្មែរ សួស្តី` | Subscript consonants and vowel signs
render correctly |
<details>
<summary>Script to reproduce</summary>
```sh
echo "=== Thai ==="
echo "สวัสดีครับ ภาษาไทย ก้อนหิน น้ำตก"
echo ""
echo "=== Lao ==="
echo "ສະບາຍດີ ພາສາລາວ"
echo ""
echo "=== Arabic ==="
echo "بِسْمِ اللَّهِ الرَّحْمَٰنِ"
echo ""
echo "=== Hebrew ==="
echo "בְּרֵאשִׁית בָּרָא אֱלֹהִים"
echo ""
echo "=== Hindi (Devanagari) ==="
echo "नमस्ते हिन्दी भाषा"
echo ""
echo "=== Bengali ==="
echo "বাংলা ভাষা নমস্কার"
echo ""
echo "=== Tamil ==="
echo "தமிழ் மொழி வணக்கம்"
echo ""
echo "=== Myanmar ==="
echo "မြန်မာဘာသာ မင်္ဂလာပါ"
echo ""
echo "=== Khmer ==="
echo "ភាសាខ្មែរ សួស្តី"
```
</details>
### Before
<img width="1144" height="740" alt="Screenshot 2569-04-05 at 10 43 00"
src="https://github.com/user-attachments/assets/bce2075a-9e19-40dd-81ea-0e29a451b781"
/>
### After
<img width="1144" height="747" alt="Screenshot 2569-04-05 at 11 22 55"
src="https://github.com/user-attachments/assets/e0613bf6-8452-4c65-b893-f9931a471b2e"
/>
### Tests passing
<img width="1144" height="740" alt="Screenshot 2569-04-05 at 11 20 22"
src="https://github.com/user-attachments/assets/9100bd77-4ea6-4f77-ae31-5129484552fd"
/>
Release Notes:
- Fixed terminal rendering of combining marks in complex scripts (Thai,
Arabic, Hebrew, Devanagari, Myanmar, Khmer, and others) where vowel
signs and tone marks were incorrectly displaced to adjacent cells
instead of rendering on their base characters.
---------
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
When soft-wrapping text, `LineWrapper::is_word_char` determines which
characters stick to the preceding word at wrap boundaries. The list
already includes trailing punctuation like `,`, `.`, and `:`, but was
missing `;`. As a result, a message like "we won't pass it on; no big
deal" could wrap with `;` isolated at the start of the next line (`on` /
`; no`).
This was most visible in the agent panel message editor but affects any
surface that uses soft-wrapping.
Also adds `assert_word("on;")` to `test_is_word_char` to guard against
regressions.
Release Notes:
- Fixed trailing `;` being wrapped to a new line instead of staying
attached to the preceding word.
Reduces memory usage on Windows by using a newer heap implementation.
Memory usage after opening into the Welcome screen:
- Before: 124 MB
- After: 91 MB
Release Notes:
- N/A
This PR adds support for rendering **Netpbm** image formats (`.pbm`,
`.ppm`, `.pgm`) within Zed's built-in image viewer.
These formats are particularly useful for projects that want minimal
external dependencies, a common scenario in academic environments and
low-level graphics programming.
Since the underlying `image` crate and `GPUI` already provide support
for these codecs, this change explicitly exposes the `Pnm` variant
within `gpui::ImageFormat` by mapping it to `image::ImageFormat::Pnm`.
## Screenshots/Examples
Below is an example of `.pbm`, `.ppm`, and `.pgm` files being rendered
correctly in the image preview (images taken from
https://filesamples.com):
<img width="1917" height="1012" alt="pnm_example"
src="https://github.com/user-attachments/assets/0056133f-908c-4c91-ba9d-53aef0657b05"
/>
Release Notes:
- Added support for PNM image previews (`.pbm`, `.ppm`, `.pgm`).
Adds basic bookmark functionality to the editor, allowing users to mark
lines and later navigate between them. This is an MVP and will later be
expanded with a picker, vim marks integration and syntax tree based
bookmark positions. In this MVP bookmarks shift under external edits.
# UI
## Adding/Removing bookmarks
To add a bookmark:
- run the toggle bookmark action
- hold secondary and click in the gutter
- open the context menu by right clicking in the gutter and select add
bookmark To remove a bookmark:
- run the toggle bookmark action
- click on the bookmarks icon in the gutter
- open the context menu by right clicking in the gutter and select
remove bookmark
remove all bookmarks with `workspace: clear bookmarks`
# Implementation
This mirrors the implementation of breakpoints. The rendering of the
gutter was refactored to make place for bookmark icons and buttons:
- Code was extracted to a `Gutter` struct
- Runnables, breakpoints and bookmarks are now collected ahead of
layouting. Just before layouting we remove the items that collide and do
not have priority.
- The `phantom_breakpoint` is replaced by a `gutter_hover_button`
## In depth phantom breakpoint discussion:
This was phantom_breakpoint. It worked as follows:
- A fake breakpoint was added to the list of breakpoints.
- While rendering the breakpoints it a breakpoint turned out to be fake
it would get a different description and look.
- The breakpoint list was edited run_indicators ("play buttons")
rendering to removes the fake breakpoint if it collided.
This would not scale to more functionality. Now we only render
breakpoints, bookmarks and run indicators. Then we render a button if
there is not breakpoint, bookmark or run indicator already present. We
can do so since the rendering of such "gutter indicators" has been
refactored into two phases:
- collect the items.
- render them if no higher priority item collides.
This is far easier and more readable which enabled me to easily take the
phantom_breakpoint system and use it for placing bookmarks as well :)
Note: this was previously merged but it needed a better squashed commit
message. For the actual PR see: 51404. This reverts commit
7e523a2d2b.
Release Notes:
- Added Bookmarks
Co-authored-by: Austin Cummings <me@austincummings.com>
Closes#4526
Adds basic bookmark functionality to the editor, allowing users to mark
lines and later navigate between them.
### What's new
**Toggling bookmarks**
Users can toggle a bookmark on the current line(s) via the `editor:
toggle bookmark` action. A bookmark icon appears in the gutter for each
bookmarked line.
**Navigation**
Two new actions, `editor: go to next bookmark` and `editor: go to
previous bookmark`, navigate between bookmarks in the current buffer,
wrapping around at the ends of the buffer.
**Viewing all bookmarks**
`editor: view bookmarks` opens all bookmarks across the project in a
multibuffer, similar to how references and diagnostics are surfaced.
**Clearing bookmarks**
`workspace: clear bookmarks` removes all bookmarks in the current
project.
**Persistence**
Bookmarks are persisted to the workspace database and restored when the
workspace is reopened. They are stored as `(path, row)` pairs and
resolved back to text anchors. Out of range or unresolvable bookmarks
are skipped with a logged warning.
**Gutter rendering**
Bookmark icons are rendered in the gutter using the existing gutter
button layout system, consistent with breakpoints. They are suppressed
on lines that already show a breakpoint or phantom breakpoint indicator.
A new `gutter.bookmarks` setting (defaulting to `true`) controls their
visibility.
### What's left
- [x] Lazily load buffers that have bookmarks
- [x] Clean up test boilerplate
- [ ] Assign default keybindings
- [ ] Compare line of saved bookmarks with current buffer (gray out the
"stale" bookmarks)
### What's next (and nice to haves)
- [ ] Resilience against external edits
- [ ] Save column position with the bookmark
- [ ] Bookmarks attached to syntactic structures?
- [ ] Labeled bookmarks?
---
Release Notes:
- Added bookmarks: toggle bookmarks on lines with `editor: toggle
bookmark`, navigate with `editor: go to next bookmark` / `editor: go to
previous bookmark`, view all bookmarks with `editor: view bookmarks`,
and clear with `workspace: clear bookmarks`. Bookmarks are shown in the
gutter and persisted across sessions.
---------
Co-authored-by: Yara <git@yara.blue>
Adds instrumentation to track input-to-frame latency in GPUI windows,
helping diagnose input responsiveness issues.
## What this does
- Records the time between when an input event is dispatched and when
the resulting frame is presented, capturing worst-case latency when
multiple events are coalesced into a single frame.
- Tracks how many input events get coalesced per rendered frame.
- Both metrics are stored in
[HdrHistogram](https://docs.rs/hdrhistogram) instances with 3
significant digits of precision.
- Latency is only recorded when the input event actually causes a redraw
(i.e. marks the window dirty), so idle mouse moves and other no-op
events don't skew the data.
- Adds a `Dump Input Latency Histogram` command that opens a buffer with
a formatted report including percentile breakdowns and visual
distribution bars.
## Example output
The report shows percentile latencies, a bucketed distribution with bar
charts, and a per-frame event coalescing breakdown.
Release Notes:
- N/A
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Cole Miller <cole@zed.dev>
### Summary
`#[gpui::property_test]` was non-deterministic because it always used a
random seed with proptest. So it generated different test cases and
scheduler seeds on every run, causing flaky failures. `#[gpui::test]`
doesn't have this problem because it defaults to fixed seeds.
This changes `#[gpui::property_test]` to match: proptest's RNG seed now
defaults to `0` instead of random, so the same cases run every time.
The`$SEED` env var overrides both the scheduler seed and case generation
seed.
Self-Review Checklist:
- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A or Added/Fixed/Improved ...
---------
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
Closes#53426
Fixes a crash when previewing markdown with GIFs, and enables GIF
animation in the preview panel.
Two issues: a partially-decoded GIF could crash the preview, and GIFs in
markdown previews never animated.
The fix for GIFs with empty comment extensions (`21 fe 00`) was
implemented upstream in the `image-gif` crate (image-rs/image-gif#228)
and released as `gif 0.14.2`. This PR bumps the dependency so those GIFs
now render correctly in the markdown preview without any further changes
to Zed itself.
## Screenshot
Left=VS Code
Right=Zed
https://github.com/user-attachments/assets/7950abbc-1a79-4f01-a425-9595aa688325
Release Notes:
- Fixed a crash in certain scenarios when opening Markdown Preview with
GIFs.
- Added GIF animation support for Markdown Preview.
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This PR adds the `cli_default_open_behavior` setting and a first-run TUI
prompt
that appears when `zed <path>` is invoked without flags while existing
windows are
open and the setting hasn't been configured yet.
## What it does
### Setting and prompt
- Adds a new `cli_default_open_behavior` workspace setting with two
values:
`existing_window` (default) and `new_window`.
- When the user runs `zed <path>` for the first time with existing Zed
windows
open, a `dialoguer::Select` prompt in the CLI asks them to choose their
preferred behavior. The choice is persisted to `settings.json`.
- The prompt is skipped when:
- An explicit flag (`-n`, `-e`, `-a`) is given
- No existing Zed windows are open
- The setting is already configured in `settings.json`
- The paths being opened are already contained in an existing workspace
### IPC transport abstraction
- Introduces a `CliResponseSink` trait in the `cli` crate that abstracts
`IpcSender<CliResponse>`, with an implementation for the real IPC
sender.
- Replaces `IpcSender<CliResponse>` with `Box<dyn CliResponseSink>` /
`&dyn CliResponseSink` across all signatures in `open_listener.rs`:
`OpenRequestKind::CliConnection`, `handle_cli_connection`,
`maybe_prompt_open_behavior`, `open_workspaces`, `open_local_workspace`.
- Extracts the inline CLI response loop from `main.rs` into a testable
`cli::run_cli_response_loop` function.
- Switches the request channel from bounded `mpsc::channel(16)` to
`mpsc::unbounded()`, eliminating `smol::block_on` in the bridge thread.
### End-to-end tests
Seven new tests exercise both the CLI-side response loop and the
Zed-side
handler connected through in-memory channels, using `allow_parking()` so
the
real `cli::run_cli_response_loop` runs on an OS thread while the GPUI
executor
drives the Zed handler:
- No flags, no windows → no prompt, opens new window
- No flags, existing windows, user picks "existing window" → prompt,
setting persisted
- No flags, existing windows, user picks "new window" → prompt, setting
persisted
- Setting already configured → no prompt
- Paths already in existing workspace → no prompt
- Explicit `-e` flag → no prompt
- Explicit `-n` flag → no prompt
Existing tests that previously used `ipc::channel()` now use a
`DiscardResponseSink`, removing OS-level IPC from all tests.
Release Notes:
- Added a first-run prompt when using `zed <path>` to choose between
opening
in an existing window or a new window. The choice is saved to settings
and
can be changed later via the `cli_default_open_behavior` setting.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
This should reduce energy consumption when having agents running in
background windows, as the spinner that is being animated won't refresh
at the display framerate anymore.
Release Notes:
- N/A or Added/Fixed/Improved ...
Follow up to https://github.com/zed-industries/zed/pull/53349 as I was
still seeing cases where content would overlap with buttons.
<img width="500" height="266" alt="Screenshot 2026-04-08 at 6 53@2x"
src="https://github.com/user-attachments/assets/e58742d4-a736-4509-9a29-35c135ddb980"
/>
I believe this PR makes the whole message editor more solid against
these cases because we're relying on `flex_basis` now to set the
max-width, which runs before we do any editor mode calculations. Tested
this a bunch and it seems to be pretty stable.
Release Notes:
- N/A
Commit eaf14d028a changed SvgRenderer::new() to eagerly deep-clone the
system font database and enrich it with bundled fonts at construction
time. Since every #[gpui::test] creates a TestAppContext →
App::new_app() → SvgRenderer::new(), and nextest runs each test in its
own process, this added ~2-3s of overhead to every GPUI-based test (~132
minutes total across the full suite).
Move the expensive work (deep-clone + load_bundled_fonts +
fix_generic_font_families) into a OnceLock inside the font resolver
closure, so it only executes on the first actual SVG render. Tests that
never render SVGs thus do not need to load the fonts which in itself can
be fairly expensive.
This also bumps the opt-level for crane lift and some other wasmtime
crates, as only wasmtime isn't really sufficient
Release Notes:
- N/A or Added/Fixed/Improved ...
- `language_model` no longer depends on provider-specific crates such as
`anthropic` and `open_ai` (inverted dependency)
- `language_model_core` was extracted from `language_model` which
contains the types for the provider-specific crates to convert to/from.
- `gpui::SharedString` has been extracted into its own crate (still
exposed by `gpui`), so `language_model_core` and provider API crates
don't have to depend on `gpui`.
- Removes some unnecessary `&'static str` | `SharedString` -> `String`
-> `SharedString` conversions across the codebase.
- Extracts the core logic of the cloud `LanguageModelProvider` into its
own crate with simpler dependencies.
Release Notes:
- N/A
---------
Co-authored-by: John Tur <john-tur@outlook.com>
Closes#51466
Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [ ] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
Release Notes:
- Fixed mermaid diagrams not showing text in markdown preview by
bundling fallback fonts and fixing generic font family resolution in the
SVG renderer.
Follow-up to https://github.com/zed-industries/zed/pull/53101
In the process of fixing the thread view's scroll experience, we for got
to turn off the follow state tail for functions that power the scroll
buttons in the agent panel.
Release Notes:
- N/A