Compare commits

...

730 Commits

Author SHA1 Message Date
Antonio Scandurra
587ed1e314 WIP 2025-07-01 12:52:08 +02:00
Conrad Irwin
1cf7a0f97b Rename WIPity WIP
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
2025-06-30 17:48:49 -06:00
Conrad Irwin
f9b43cbd1f Re-merge ZedAgent and Thread
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
2025-06-30 17:43:53 -06:00
Conrad Irwin
dab7ca4a84 WIP
Co-authored-by: Nathan Sobo <nathan@zed.dev>
2025-06-30 17:01:05 -06:00
Agus Zubiaga
e061fbefae Replace edit_message + delete_messages with new truncate method 2025-06-30 15:46:59 -03:00
Agus Zubiaga
64d19c44e4 Remove Message is_hidden 2025-06-30 15:05:44 -03:00
Agus Zubiaga
e51a0852e1 Replace insert_invisible_continue_message with send_continue_message 2025-06-30 14:39:42 -03:00
Agus Zubiaga
2ea1488aca Replace insert_user_message fully 2025-06-30 13:22:47 -03:00
Agus Zubiaga
c76361d213 Test new retry 2025-06-30 13:16:39 -03:00
Agus Zubiaga
9d7c94a16e Rename send_to_model2 to send_message and fix is_generating 2025-06-30 11:37:48 -03:00
Agus Zubiaga
2af70370e9 Trigger summary generation from send_to_model2 2025-06-30 11:14:51 -03:00
Agus Zubiaga
7725b95571 Replace more insert_user_message usages 2025-06-30 10:58:08 -03:00
Ben Brandt
be3a295ae4 Refactor tool use deserialization
Extract the tool use deserialization logic from `ZedAgent::new` into a
new `DeserializedToolUse` helper struct, so we don't have to clone
messages
2025-06-30 12:09:12 +02:00
Ben Brandt
269f73ab7c Report tool output in finished status 2025-06-30 11:53:24 +02:00
Ben Brandt
90899465a2 Cleanup from merge and clippy warnings 2025-06-30 11:31:56 +02:00
Ben Brandt
34a2d23134 Merge branch 'main' into split-agent-from-thread 2025-06-30 11:11:29 +02:00
Bennet Bo Fenner
d63909c598 agent: Use standardized MCP configuration format in settings (#33539)
Changes our MCP settings from:

```json
{
  "context_servers": {
    "some-mcp-server": {
      "source": "custom",
      "command": {
        "path": "npx",
        "args": [
          "-y",
          "@supabase/mcp-server-supabase@latest",
          "--read-only",
          "--project-ref=<project-ref>",
        ],
        "env": {
          "SUPABASE_ACCESS_TOKEN": "<personal-access-token>",
        },
      },
    },
  },
}

```

to:
```json
{
  "context_servers": {
    "some-mcp-server": {
      "source": "custom",
      "command": "npx",
      "args": [
        "-y",
        "@supabase/mcp-server-supabase@latest",
        "--read-only",
        "--project-ref=<project-ref>",
      ],
      "env": {
        "SUPABASE_ACCESS_TOKEN": "<personal-access-token>",
      },
    },
  },
}
```


Which seems to be somewhat of a standard now (VSCode, Cursor, Windsurf,
...)

Release Notes:

- agent: Use standardised format for configuring MCP Servers
2025-06-30 08:05:52 +00:00
Danilo Leal
c3d0230f89 docs: Adjust heading sizes (#33628)
Just fine-tuning some heading sizes that were off, particularly h4s and
h5s.

Release Notes:

- N/A
2025-06-29 20:49:28 -03:00
Piotr Osiewicz
bc5927d5af debugger: Fix spec violation with threads request being issued before debug session is initialized (#33627)
Follow-up to #32852. This time we'll check if the debug session is
initialized before querying threads.

Release Notes:

- Fix Zed's debugger issuing threads request before it is allowed to do
so per DAP specification.
2025-06-29 23:38:16 +00:00
Piotr Osiewicz
d2cf995e27 debugger: Tweak layout of debug landing page in vertical dock position (#33625)
Release Notes:

- Reorganized layout of a debug panel without any sessions for a
vertical dock position.
- Moved parent directories of source breakpoints into a tooltip.
2025-06-30 00:48:14 +02:00
Michael Sloan
86161aa427 Use refs to deduplicate settings JSON schema (~1.7mb to ~0.26mb) (#33618)
Release Notes:

- N/A
2025-06-29 18:33:05 +00:00
Peter Tripp
a602b4b305 Improve R documentation (#33594)
Release Notes:

- N/A
2025-06-29 12:21:10 -04:00
Kirill Bulatov
047d515abf Rework color indicators visual representation (#33605)
Use a div-based rendering code instead of using a text

Closes https://github.com/zed-industries/zed/discussions/33507

Before:
<img width="410" alt="before_dark"
src="https://github.com/user-attachments/assets/66ad63ae-7836-4dc7-8176-a2ff5a38bcd4"
/>
After:
<img width="407" alt="after_dark"
src="https://github.com/user-attachments/assets/0b627da8-461b-4f19-b236-4a69bf5952a0"
/>


Before:
<img width="409" alt="before_light"
src="https://github.com/user-attachments/assets/ebcfabec-fcda-4b63-aee6-c702888f0db4"
/>
After:
<img width="410" alt="after_light"
src="https://github.com/user-attachments/assets/c0da42a1-d6b3-4e08-a56c-9966c07e442d"
/>

The border is not that contrast as in VSCode examples in the issue, but
I'm supposed to use the right thing in

1e11de48ee/crates/editor/src/display_map/inlay_map.rs (L357)

based on 


41583fb066/crates/theme/src/styles/colors.rs (L16-L17)

Another oddity is that the border starts to shrink on `cmd-=`
(`zed::IncreaseBufferFontSize`):

<img width="1244" alt="image"
src="https://github.com/user-attachments/assets/f424edc0-ca0c-4b02-96d4-6da7bf70449a"
/>

but that needs a different part of code to be adjusted hence skipped.

Tailwind CSS example:

<img width="1108" alt="image"
src="https://github.com/user-attachments/assets/10ada4dc-ea8c-46d3-b285-d895bbd6a619"
/>


Release Notes:

- Reworked color indicators visual representation
2025-06-29 09:43:56 +00:00
Piotr Osiewicz
e5bcd720e1 debugger: Add UI for tweaking breakpoint properties directly from breakpoint list (#33097)
Release Notes:

- debugger: Breakpoint properties (log/hit condition/condition) can now
be set directly from breakpoint list.
2025-06-28 23:41:44 +02:00
Kirill Bulatov
41583fb066 Fix document colors issues with other inlays and multi buffers (#33598)
Closes https://github.com/zed-industries/zed/issues/33575

* Fixes inlay colors spoiled after document color displayed
* Optimizes the query pattern for large multi buffers

Release Notes:

- Fixed document colors issues with other inlays and multi buffers
2025-06-28 21:10:49 +00:00
Cole Miller
521a223681 Add editor::Rewrap binding to Emacs keymaps (#33588)
`M-q` is `fill-paragraph` which is like `editor::Rewrap`.

Release Notes:

- emacs: Bound `alt-q` to `editor::Rewrap` (like `M-q` or `M-x
fill-paragraph`)
2025-06-28 15:35:59 -04:00
Conrad Irwin
c8c6468f9c vim: Non-interactive shell (#33568)
Closes #33144

Release Notes:

- vim: Run r! in a non-interactive shell
2025-06-28 10:23:57 -06:00
Umesh Yadav
3f4098e87b open_ai: Make OpenAI error message generic (#33383)
Context: In this PR: https://github.com/zed-industries/zed/pull/33362,
we started to use underlying open_ai crate for making api calls for
vercel as well. Now whenever we get the error we get something like the
below. Where on part of the error mentions OpenAI but the rest of the
error returns the actual error from provider. This PR tries to make the
error generic for now so that people don't get confused seeing OpenAI in
their v0 integration.

```
Error interacting with language model
Failed to connect to OpenAI API: 403 Forbidden {"success":false,"error":"Premium or Team plan required to access the v0 API: https://v0.dev/chat/settings/billing"}
```

Release Notes:

- N/A
2025-06-28 14:38:27 +02:00
alphaArgon
1d684c8890 Add shadow back for blurred/transparent window on macOS (#27403)
Closes #15383
Closes #10993

`NSVisualEffectView` is an official API for implementing blur effects
and, by traversing the layers, we **can remove the background color**
that comes with the view. This avoids using private APIs and aligns
better with macOS’s native design.

Currently, `GPUIView` serves as the content view of the window. To add
the blurred view, `GPUIView` is downgraded to a subview of the content
view, placed at the same level as the blurred view.

Release Notes:

- Fixed the missing shadow for blurred-background windows on macOS.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-28 09:50:54 +03:00
Rift
97c5c5a6e7 vim: Respect count for paragraphs (#33489)
Closes #32462 

Release Notes:

- vim: Paragraph objects now support counts (`d2ap`, `v2ap`, etc.)

---------

Co-authored-by: Rift <no@e.mail>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-27 22:05:47 -06:00
5brian
ba4fc1bcfc vim: Add debug panel ex command (#33560)
Added :Debug to open debug panel, also added
[:display](https://neovim.io/doc/user/change.html#%3Adisplay), alias to
:reg

Release Notes:

- N/A
2025-06-27 21:32:40 -06:00
Smit Barmase
bbf16bda75 editor: Improve rewrap to respect indent and prefix boundaries (#33566)
1. Fixes bug where this would not rewrap:

```rs
// This is the first long comment block to be wrapped.
fn my_func(a: u32);
// This is the second long comment block to be wrapped.
```
2. Comment prefix boundaries (Notice now they don't merge between
different comment prefix):

Initial text:
```rs
// A regular long long comment to be wrapped.
// A second regular long comment to be wrapped.
/// A documentation long comment to be wrapped.
```
Upon rewrap:
```rs
// A regular long long comment to be
// wrapped. A second regular long
// comment to be wrapped.
/// A documentation long comment to be
/// wrapped.
```
3. Indent boundaries (Notice now they don't merge between different
indentation):

Initial text:
```rs
fn foo() {
      // This is a long comment at the base indent.
      // This is a long comment at the base indent.
                 // This is a long comment at the next indent.
                 // This is a long comment at the next indent.
      // This is a long comment at the base indent.
}
```
Upon rewrap:
```rs
fn foo() {
      // This is a long comment at the base
      // indent. This is a long comment at the
      // base indent.
                 // This is a long comment at the 
                 // next indent. This is a long 
                 // comment at the next indent.
      // This is a long comment at the base
      // indent.
}
```

Release Notes:

- Fixed an issue where rewrap would not work with selection when two
comment blocks are separated with line of code.
- Improved rewrap to respect changes in indentation or comment prefix
(e.g. `//` vs `///`) as boundaries so that it doesn't merge them into
one mangled text.
2025-06-28 05:38:18 +05:30
ddoemonn
c56b8904cc Prevent branch name overflow in git panel selection (#33529)
Closes #33527

Release Notes:

- Fixed long branch names overflowing to multiple lines in git panel
branch selector

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-06-27 20:50:53 -03:00
Agus Zubiaga
3e2bcb05fb Start using send_to_model2 in message editor
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-27 19:35:53 -03:00
Artem Loenko
695118d110 agent: Show provider icon in model selectors (#30595)
I often switch between models, and I believe many people do. Currently,
it is difficult to determine which provider offers the selected model
because the same models are available from different providers. I
propose a simple change to the selector so that users can distinguish
between providers from the model they have chosen.

As a side note, I would actually prefer to have a text label with the
provider’s name next to the model name in the selector. However, I
understand that this is too opinionated and takes up too much space.

| Before | After |
| ------ | ------ |
|
![before_inline_assist](https://github.com/user-attachments/assets/35617ba5-e8d4-4dab-a997-f7286f73f2bf)
|
![after_inline_assist](https://github.com/user-attachments/assets/c37c81b4-73e4-49e2-955d-b8543b2855ad)
|
|
![before_text_thread](https://github.com/user-attachments/assets/af90303b-12d6-402c-90a5-8b36cd97396e)
|
![after_text_thread](https://github.com/user-attachments/assets/bca5b423-f12b-4eaf-a82e-424d09b7f447)
|
|
![before_thread](https://github.com/user-attachments/assets/0946775f-1d52-437b-a217-9708ee2e789a)
|
![after_thread](https://github.com/user-attachments/assets/f5e53968-9020-446f-9d5e-653ae9fdea3e)
|

Release Notes:

- The model selector has been improved with a provider icon, making it
easier to distinguish between providers.

---------

Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-06-27 19:33:58 -03:00
Agus Zubiaga
f32af6ab52 Checkpoint: Rendering tool uses
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-27 19:09:34 -03:00
Agus Zubiaga
eef7c07061 Remove MessageSegment::RedactedThinking
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-27 18:37:38 -03:00
Agus Zubiaga
b1a7812232 BASE_RETRY_DELAY_SECS -> BASE_RETRY_DELAY
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-27 18:18:21 -03:00
Agus Zubiaga
2f8fa209bc Test send_to_model2
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-27 18:07:23 -03:00
Conrad Irwin
a675ca7a1e Remove into SelectionEffects from .change_selections (#33554)
In #32656 I generalized the argument to change selections to allow
controling both the scroll and the nav history (and the completion
trigger).

To avoid conflicting with ongoing debugger cherry-picks I left the
argument as an `impl Into<>`, but I think it's clearer to make callers
specify what they want here.

I converted a lot of `None` arguments to `SelectionEffects::no_scroll()`
to be exactly compatible; but I think many people used none as an "i
don't care" value in which case Default::default() might be more
appropraite

Closes #ISSUE

Release Notes:

- N/A
2025-06-27 14:31:31 -06:00
Conrad Irwin
6e762d9c05 Revert "Remove into SelectionEffects from .change_selections"
This reverts commit 28380d714d.
2025-06-27 14:06:17 -06:00
Conrad Irwin
28380d714d Remove into SelectionEffects from .change_selections
In #32656 I generalized the argument to change selections to allow
controling both the scroll and the nav history (and the completion
trigger).

To avoid conflicting with ongoing debugger cherry-picks I left the
argument as an `impl Into<>`, but I think it's clearer to make callers
specify what they want here.

I converted a lot of `None` arguments to `SelectionEffects::no_scroll()`
to be exactly compatible; but I think many people used none as an "i
don't care" value in which case Default::default() might be more
appropraite
2025-06-27 14:03:45 -06:00
Max Brunsfeld
5e0f3e0ead Start writing assistant messages + tool calls to thread in ZedAgent
Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-27 13:00:19 -07:00
Alejandro Fernández Gómez
f338c46bf7 Fix line indices when using a selection in the context (#33549)
Closes #33152.

The label for a file selection context had an off-by-one error on line
indices. This PR adjusts that.

Before:

<img width="1072" alt="Screenshot 2025-06-27 at 20 55 28"
src="https://github.com/user-attachments/assets/da0be503-056b-442b-9a79-38cf504d8a44"
/>

After:

<img width="1090" alt="Screenshot 2025-06-27 at 20 49 35"
src="https://github.com/user-attachments/assets/f98df57a-8f07-4ea5-a06b-a4b6fb8c2e4e"
/>


Release Notes:

- Fixed a off-by-one error on the line indices when using a selection as
context for the agent,
2025-06-27 13:41:17 -06:00
Umesh Yadav
5fbb7b0d40 copilot: Only set Copilot-Vision-Request header for vision requests (#33552)
Closes #31951

The fix is copied and translated from copilot chat actual implementation
code:
ad7cbcae9a/src/platform/openai/node/fetch.ts (L493C1-L495C3)

Release Notes:

- Fix copilot failing due to missing `Copilot-Vision-Request` from
request.
2025-06-27 21:37:26 +02:00
Cole Miller
f12b0dddf4 Touch up extension DAP schemas fix (#33548)
Updates #33546 

Release Notes:

- N/A

Co-authored-by: Piotr <piotr@zed.dev>
2025-06-27 15:34:21 -04:00
Conrad Irwin
14bb10d783 Don't panic on vintage files (#33543)
Release Notes:

- remoting: Fix a crash on the remote side when encountering files from
before 1970.
2025-06-27 13:15:50 -06:00
Cole Miller
c9ce4aec91 Fix debug adapters from extensions not being picked up (#33546)
Copy the debug adapter schemas so that they're available to the
extension host, like we do for other extension assets.

Release Notes:

- N/A
2025-06-27 14:31:58 -04:00
Kirill Bulatov
01dfb6fa82 Respect server capabilities on queries (#33538)
Closes https://github.com/zed-industries/zed/issues/33522

Turns out a bunch of Zed requests were not checking their capabilities
correctly, due to odd copy-paste and due to default that assumed that
the capabilities are met.

Adjust the code, which includes the document colors, add the test on the
colors case.

Release Notes:

- Fixed excessive document colors requests for unrelated files
2025-06-27 16:31:40 +00:00
5brian
f9987a1141 vim: Grep in visual line (#33414)
From
https://github.com/zed-industries/zed/pull/10831#issuecomment-2078523272

> I agree with not prefilling the search bar with a multiline query.

Not sure if it's a bug that a one-line visual line selection does not
get pre filled, this PR corrects the query to use the visual line
selection instead of the 'normal' selection

Release Notes:

- N/A
2025-06-27 10:18:26 -06:00
Agus Zubiaga
8776548b02 Use build_request in tests 2025-06-27 12:46:15 -03:00
Ben Kunkle
7432e947bc Add element_selection_background highlight to theme (#32388)
Closes #32354

The issue is that we render selections over the text in the agent panel,
but under the text in editor, so themes that have no alpha for the
selection background color (defaults to 0xff) will just occlude the
selected region. Making the selection render under the text in markdown
would be a significant (and complicated) refactor, as selections can
cross element boundaries (i.e. spanning code block and a header after
the code block).

The solution is to add a new highlight to themes
`element_selection_background` that defaults to the local players
selection background with an alpha of 0.25 (roughly equal to 0x3D which
is the alpha we use for selection backgrounds in default themes) if the
alpha of the local players selection is 1.0. The idea here is to give
theme authors more control over how the selections look outside of
editor, as in the agent panel specifically, the background color is
different, so while an alpha of 0.25 looks acceptable, a different color
would likely be better.

CC: @iamnbutler. Would appreciate your thoughts on this. 

> Note: Before and after using Everforest theme

| Before | After | 
|-------| -----|
| <img width="618" alt="Screenshot 2025-06-09 at 5 23 10 PM"
src="https://github.com/user-attachments/assets/41c7aa02-5b3f-45c6-981c-646ab9e2a1f3"
/> | <img width="618" alt="Screenshot 2025-06-09 at 5 25 03 PM"
src="https://github.com/user-attachments/assets/dfb13ffc-1559-4f01-98f1-a7aea68079b7"
/> |

Clearly, the selection in the after doesn't look _that_ great, but it is
better than the before, and this PR makes the color of the selection
configurable by the theme so that this theme author could make it a
lighter color for better contrast.




Release Notes:

- agent panel: Fixed an issue with some themes where selections inside
the agent panel would occlude the selected text completely

Co-authored-by: Antonio <me@as-cii.com>
2025-06-27 15:46:04 +00:00
Agus Zubiaga
82b243e4ea Add user messages to agent request 2025-06-27 12:42:35 -03:00
Conrad Irwin
157199b65b Replace newlines in search bar (#33504)
Release Notes:

- search: Pasted newlines are now rendered as "\n" (with an underline),
instead of line-wrapping. This should make it much clearer what you're
searching for.
 
<img width="675" alt="Screenshot 2025-06-27 at 00 34 52"
src="https://github.com/user-attachments/assets/67275bc6-bec1-463f-b351-6b9ed0a6df81"
/>
2025-06-27 09:39:38 -06:00
Conrad Irwin
d74f3f4ea6 Fix crash in git checkout (#33499)
Closes #33438

Release Notes:

- git: Use git cli to perform checkouts (to avoid a crash seen in
libgit2)
2025-06-27 09:16:15 -06:00
Agus Zubiaga
b2434e7fef Checkpoint: Handle all retryable errors 2025-06-27 12:05:24 -03:00
Smit Barmase
9e2023bffc editor: Fix editor tests from changing on format on save (#33532)
Use placeholder to prevent format-on-save from removing whitespace in
editor tests, which leads to unnecessary git diff and failing tests.

cc: https://github.com/zed-industries/zed/pull/32340

Release Notes:

- N/A
2025-06-27 20:14:01 +05:30
Umesh Yadav
3ab4ad6de8 language_models: Use JsonSchemaSubset for Gemini models in OpenRouter (#33477)
Closes #33466

Release Notes:

- N/A
2025-06-27 16:36:16 +02:00
Antonio Scandurra
6036c09c1a Checkpoint
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-06-27 16:00:08 +02:00
Peter Tripp
e3ce0618a3 collab: Lookup avatars by GitHub ID instead of username (#33523)
Closes: https://github.com/zed-industries/zed/issues/19207

This will correctly show Avatars for recently renamed/deleted users and
for enterprise users where the username avatar url triggers a redirect
to an auth prompt. Also saves a request (302 redirect) per avatar.

Tested locally and avatars loaded as expected.

Release Notes:

- N/A
2025-06-27 09:40:50 -04:00
Kirill Bulatov
865dd4c5fc Rework LSP tool keyboard story (#33525)
https://github.com/user-attachments/assets/81da68fe-bbc5-4b23-8182-923c752a8bd2

* Removes all extra elements: headers, buttons, to simplify the menu
navigation approach and save space.
Implements the keyboard navigation and panel toggling.

* Keeps the status icon and the server name, and their ordering approach
(current buffer/other) in the menu.
The status icon can still be hovered, but that is not yet possible to
trigger from the keyboard: future ideas would be make a similar side
display instead of hover, as Zeta menu does:


![image](https://github.com/user-attachments/assets/c844bc39-00ed-4fe3-96d5-1c9d323a21cc)

* Allows to start (if all are stopped) and stop (if some are not
stopped) all servers at once now with the button at the bottom

Release Notes:

- N/A
2025-06-27 16:25:56 +03:00
Antonio Scandurra
865970d42b WIP
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-06-27 15:11:50 +02:00
Antonio Scandurra
b9c4f2c7a8 WIP
Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-27 14:26:15 +02:00
Umesh Yadav
2178f66af6 agent_ui: Rename MaxModeTooltip to BurnModeTooltip (#33521)
Closes #ISSUE

Release Notes:

- N/A
2025-06-27 13:56:46 +02:00
Antonio Scandurra
e458ba2293 WIP
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-27 13:12:45 +02:00
Antonio Scandurra
04c842a7c2 WIP: actually run tools
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-27 13:02:40 +02:00
Antonio Scandurra
7a055b4865 WIP: start reworking tool use
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-27 12:34:27 +02:00
ddoemonn
338a7395a7 Fix blend alpha colors with editor background in inline preview (#33513)
Closes #33505

## Before

<img width="434" alt="Screenshot 2025-06-27 at 12 22 57"
src="https://github.com/user-attachments/assets/ac215a39-b3fe-4c9e-bd7d-0d7568d5fd1f"
/>

## After

<img width="441" alt="Screenshot 2025-06-27 at 12 22 47"
src="https://github.com/user-attachments/assets/28218ed6-c1aa-4d3f-a268-def2fa9f0340"
/>

Release Notes:

- Fixed inline color previews not correctly blending alpha/transparency
values with the editor background
2025-06-27 12:37:05 +03:00
Finn Evers
4c2415b338 editor: Use em_advance everywhere for horizontal scroll position computations (#33514)
Closes #33472

This PR fixes some regressions that were introduced in
https://github.com/zed-industries/zed/pull/32558, which updated the
editor scrolling to use `em_advance` instead of `em_width` for the
horizontal scroll position calculation.
However, not all occurrences were updated, which caused issues with wrap
guides and some small stuttering with horizontal autoscroll whilst
typing/navigating with the keyboard.

Release Notes:

- Fixed an issue where horizontal autoscrolling would stutter and indent
guides would drift when scrolling horizontally.
2025-06-27 09:32:50 +00:00
Ron Harel
e6bc1308af Add SVG preview (#32694)
Closes #10454

Implements SVG file preview capability similar to the existing markdown
preview.
- Adds `svg_preview` crate with preview view and live reloading upon
file save.
- Integrates SVG preview button in quick action bar.
- File preview shortcuts (`ctrl/cmd+k v` and `ctrl/cmd+shift+v`) are
extension-aware.

Release Notes:

- Added SVG file preview, accessible via the quick action bar button or
keyboard shortcuts (`ctrl/cmd+k v` and `ctrl/cmd+shift+v`) when editing
SVG files.
2025-06-27 09:08:05 +00:00
Antonio Scandurra
9eff1c32af Merge remote-tracking branch 'origin/main' into split-agent-from-thread 2025-06-27 10:40:24 +02:00
Ben Brandt
6c46e1129d Cleanup remaining references to max mode (#33509)
Release Notes:

- N/A
2025-06-27 08:32:13 +00:00
Ben Brandt
88b1345595 variable cleanup 2025-06-27 10:09:31 +02:00
fantacell
fbb5628ec6 Reset selection goal after helix motion (#33184)
Closes #33060

Motions like `NextWordStart` don't reset the selection goal in vim mode
`helix_normal` unlike in `normal` which can lead to the cursor jumping
back to the previous horizontal position after going up or down.

Release Notes:

- N/A
2025-06-26 22:32:35 -06:00
Conrad Irwin
8c9116daa5 Fix panic in ctrl-g (#33474)
Release Notes:

- vim: Fixed a crash with ctrl-g
2025-06-26 21:26:58 -06:00
Conrad Irwin
20a3e613b8 vim: Better jump list support (#33495)
Closes #23527
Closes #30183
Closes some Discord chats

Release Notes:

- vim: Motions now push to the jump list using the same logic as vim
(i.e.
`G`/`g g`/`g d` always do, but `j`/`k` always don't). Most non-vim
actions
(including clicking with the mouse) continue to push to the jump list
only
  when they move the cursor by 10 or more lines.
2025-06-26 21:25:07 -06:00
Ben Kunkle
ba1c05abf2 keymap: Add ability to update user keymaps (#33487)
Closes #ISSUE

The ability to update user keybindings in their keymap is required for
#32436. This PR adds the ability to do so, reusing much of the existing
infrastructure for updating settings JSON files.

However, the existing JSON update functionality was intended to work
only with objects, therefore, this PR simply wraps the object updating
code with non-general keymap-specific array updating logic, that only
works for top-level arrays and can only append or update entries in said
top-level arrays. This limited API is reflected in the limited
operations that the new `update_keymap` method on `KeymapFile` can take
as arguments.

Additionally, this PR pulls out the existing JSON updating code into its
own module (where array updating code has been added) and adds a
significant number of tests (hence the high line count in the diff)

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-26 21:52:26 -04:00
Max Brunsfeld
a02a0b9c0a Remove some methods that delegate from ZedAgent to Thread
Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-06-26 17:56:13 -07:00
Max Brunsfeld
f35fbbb78f Move ActionLog from ZedAgent to Thread
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-06-26 17:37:22 -07:00
Max Brunsfeld
bdeaddc59d Move checkpoints from agent to thread 2025-06-26 16:39:43 -07:00
Conrad Irwin
d5aa609bee Split thread/agent in ActiveThread
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-06-26 16:53:57 -06:00
Conrad Irwin
1f0512cd2f Move summary() into ThreadData. Split thread/agent in tests
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-06-26 16:32:51 -06:00
Danilo Leal
2823771c06 Add design improvements to the LSP popover (#33485)
Not the ideal design just yet as that will probably require a different
approach altogether, but am pushing here just some reasonably small UI
adjustments that will make this feel slightly nicer!

Release Notes:

- N/A
2025-06-26 22:27:21 +00:00
Conrad Irwin
438acc98d6 Move messages -> ThreadData
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-06-26 16:09:43 -06:00
Conrad Irwin
5cc016291d Factor id -> ThreadData
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-06-26 15:34:35 -06:00
Conrad Irwin
61ab3bcd8e Rename Thread -> Agent
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-06-26 15:31:53 -06:00
Cole Miller
343f155ab9 Update docs for Swift debugging (#33483)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-26 21:25:03 +00:00
Julia Ryan
2dece13d83 nix: Update to access new rust toolchain version (#33476)
Needed due to #33439

Release Notes:

- N/A
2025-06-26 20:31:24 +00:00
Max Brunsfeld
03478d5715 Inline ToolUseState
Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-26 12:30:52 -07:00
Piotr Osiewicz
985dcf7523 chore: Bump Rust version to 1.88 (#33439)
Goodies in this version:
- if-let chains 🎉
- Better compiler perf for Zed
(https://github.com/rust-lang/rust/pull/138522)

For more, see: https://releases.rs/docs/1.88.0/

Release Notes:

- N/A

---------

Co-authored-by: Junkui Zhang <364772080@qq.com>
2025-06-26 20:54:19 +02:00
Conrad Irwin
b079871428 Fix subtraction with overflow (#33471)
Release Notes:

- N/A
2025-06-26 12:38:54 -06:00
fantacell
4983b01c89 helix: Change word motions (#33408)
When starting on the newline character at the end of a line the helix
word motions select that character, unlike in helix itself. This makes
it easy to accidentaly join two lines together.
Also, word motions that go backwards should stop at the start of a line.
I added that.

Release Notes:

- helix: Fix edge-cases with word motions and newlines
2025-06-26 12:25:47 -06:00
Cole Miller
35863c4302 debugger: Fix treatment of node-terminal scenarios (#33432)
- Normalize `node-terminal` to `pwa-node` before sending to DAP
- Split `command` into `program` and `args`
- Run in external console

Release Notes:

- debugger: Fixed debugging JavaScript tasks that used `"type":
"node-terminal"`.
2025-06-26 14:02:09 -04:00
Kirill Bulatov
a0bd25f218 Feature gate the LSP button (#33463)
Follow-up of https://github.com/zed-industries/zed/pull/32490

The tool still looks like designed by professional developers, and still
may change its UX based on the internal feedback.

Release Notes:

- N/A
2025-06-26 15:41:42 +00:00
Agus Zubiaga
8a1e795746 agent: Move ActiveThread and MessageEditor into ActiveView (#33462)
`ActiveThread` and `MessageEditor` only make sense when `active_view` is
`Thread`, so we moved them in there. This will make it easier to work on
new agent threads.

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-06-26 15:36:23 +00:00
Peter Tripp
f4818b648e linux: Add agent::ToggleBurnMode shortcut (super-ctrl-b) (#33458)
Follow-up to: 
- https://github.com/zed-industries/zed/pull/33452
- https://github.com/zed-industries/zed/pull/33190
- https://github.com/zed-industries/zed/pull/31630

Release Notes:

- N/A
2025-06-26 15:07:46 +00:00
Peter Tripp
7031ed8b87 ci: Fix duplicated/failed eval jobs (#33453)
I think this should fix the CI issues with Eval jobs:

1. Duplicated workflows because `synchronize` / `opened` were triggering
distinct runs. This caused failed job entries because the duplicated
workflows had a shared concurrency group and so one would pre-empt the
other.

3. Removes the no-op job, introduced as an attempted workaround in
   https://github.com/zed-industries/zed/pull/29420.

These should correctly show as "Skipped" now:

| Before | After |
| - | - |
| <img width="359" alt="Screenshot 2025-06-26 at 9 57 04"
src="https://github.com/user-attachments/assets/6ddd4f46-27c7-4d82-98ba-0f1166fc55e7"
/> | <img width="355" alt="Screenshot 2025-06-26 at 10 09 54"
src="https://github.com/user-attachments/assets/5faade2c-f17c-447a-9af9-6396f9e53016"
/> |

Release Notes:

- N/A
2025-06-26 11:01:16 -04:00
Richard Feldman
6073d2c93c Automatically retry when API is Overloaded or 500s (#33275)
<img width="484" alt="Screenshot 2025-06-25 at 2 26 16 PM"
src="https://github.com/user-attachments/assets/340f15d7-b115-4895-bae8-b12a915bfda1"
/>

<img width="460" alt="Screenshot 2025-06-25 at 2 26 08 PM"
src="https://github.com/user-attachments/assets/6e587a38-d542-405f-809f-402e87520538"
/>

Now we:
* Automatically retry up to 3 times on upstream Overloaded or 500 errors
(currently for Anthropic only; will add others in future PRs)
* Also automatically retry on rate limit errors (using the provided
duration to wait, if we were given one)
* Give you a notification if you don't have Zed open and we stopped the
thread because of an error

Still todo in future PRs:
* Update collab to report Overloaded and 500 errors differently if
collab itself is passing through an upstream error vs not (currently we
report these as "Zed's API is overloaded" when actually it's the
upstream one!)
* Updating providers other than Anthropic to categorize their errors so
that they benefit from this
* Expanding graceful error handling/retry to other things besides
Overloaded and 500 errors (e.g. connection reset)

Release Notes:

- Automatically retry in Agent Panel instead of erroring out when an
upstream AI API is overloaded or 500s
- Show a notification when an Agent thread errors out and Zed is not the
active window
2025-06-26 10:53:33 -04:00
Danilo Leal
00499aadd4 Add back default keybindings to Burn Mode and branch picker toggles (#33452)
Follow up to https://github.com/zed-industries/zed/pull/33190, as they
were removed because of conflict with VS Code's usage of those bindings
to toggle the right dock. `cmd-ctrl-b` seems like a safe alternative.
Note that this PR is macOS only, though. I couldn't find yet any good
options for Linux as they were all mostly conflicting with something
else.

Release Notes:

- N/A
2025-06-26 11:35:02 -03:00
Peter Tripp
d1eb69c6cd ci: Improve check_docs skipping (#33455)
Follow-up to: https://github.com/zed-industries/zed/pull/33398

Logic there was incomplete. Caused [this
failure](https://github.com/zed-industries/zed/actions/runs/15904169712/job/44854558276).

Release Notes:

- N/A
2025-06-26 14:34:20 +00:00
Marshall Bowers
40cbfb7eb2 docs: Add note about extension submodules needing to use HTTPS URLS (#33454)
This PR adds a note to the extension publishing docs about extension
submodules needing to use HTTPS URLs.

Release Notes:

- N/A
2025-06-26 14:18:56 +00:00
Peter Tripp
5d0f02d356 Add cmd-alt-b (workspace::ToggleRightDock) on macOS (#33450)
Release Notes:

- N/A
2025-06-26 13:54:21 +00:00
Renze Post
ca8e213151 Suggest reST extension for .rst files (#33413)
Suggest the reST extension when opening a .rst file for the first time.

Release Notes:

- N/A
2025-06-26 08:59:04 -04:00
Michael Sloan
90c893747c gpui: Prevent the same action name from being registered multiple times (#33359)
Also removes duplicate `editor::RevertFile` and `vim::HelixDelete`
actions

Release Notes:

- N/A
2025-06-26 06:24:14 +00:00
Smit Barmase
d09c7eb317 language: Add context-aware decrease indent for Python (#33370)
Closes #33238, follow-up to
https://github.com/zed-industries/zed/pull/29625.

Changes:

- Removed `significant_indentation`, which was the way to introduce
indentation scoping in languages like Python. However, it turned out to
be unnecessarily complicated to define and maintain.
- Introduced `decrease_indent_patterns`, which takes a `pattern` keyword
to automatically outdent and `valid_after` keywords to treat as valid
code points to snap to. The outdent happens to the most recent
`valid_after` keyword that also has less or equal indentation than the
currently typed keyword.

Fixes:

1. In Python, typing `except`, `finally`, `else`, and so on now
automatically indents intelligently based on the context in which it
appears. For instance:

```py
try:
    if a == 1:
        try:
             b = 2
             ^  # <-- typing "except:" here would indent it to inner try block
```

but,

```py
try:
    if a == 1:
        try:
             b = 2
    ^  # <-- typing "except:" here would indent it to outer try block
```

2. Fixes comments not maintaining indent.

Release Notes:

- Improved auto outdent for Python while typing keywords like `except`,
`else`, `finally`, etc.
- Fixed the issue where comments in Python would not maintain their
indentation.
2025-06-26 11:11:03 +05:30
Smit Barmase
1753432406 Fix tree sitter python try statement to accept missing else/except/finally (#33431)
We have fork now:
218fcbf3fd

Release Notes:

- N/A
2025-06-26 09:48:44 +05:30
Cole Miller
d9218b10ea Bump livekit-rust-sdks for candidate webrtc-sys build fix (#33387)
Incorporates https://github.com/zed-industries/livekit-rust-sdks/pull/5

Don't merge yet, waiting until after new releases are cut in case of
unexpected breakage.

Release Notes:

- N/A
2025-06-25 21:00:33 -04:00
Michael Sloan
dfdeb1bf51 linux: Don't insert characters if modifiers other than shift are held (#33424)
Closes #32219 #29666

Release Notes:

- Linux: Now skips insertion of characters when modifiers are held. Before, characters were inserted if there's no match in the keymap.
2025-06-25 17:11:59 -06:00
Max Brunsfeld
b9f81c7ce7 Restore missing initialization of text thread actions (#33422)
Fixes a regression introduced in
https://github.com/zed-industries/zed/pull/33289

Release Notes:

- Fixed a bug where some text thread actions were accidentally removed.
2025-06-25 22:48:40 +00:00
Michael Sloan
b1450b6d71 Remove git_panel::GenerateCommitMessage in favor of git::GenerateCommitMessage (#33421)
`git_panel::GenerateCommitMessage` has no handler,
`git::GenerateCommitMessage` should be preferred. Could add a
`#[action(deprecated_aliases = ["git_panel::GenerateCommitMessage"])]`,
but decided not to because that action didn't work. So instead uses of
it will show up as keymap errors.

Closes #32667

Release Notes:

- N/A
2025-06-25 22:29:30 +00:00
David Barsky
1af9f98c1d lsp-log: Avoid trimming leading space in language server logs (#33418)
Not sure what the full intention/right fix for this is, but
https://github.com/zed-industries/zed/pull/32659 re-introduced trimming
of leading spaces. rust-analyzer has [a custom tracing
formatter](317542c1e4/crates/rust-analyzer/src/tracing/hprof.rs)
that is _super_ useful for profiling what the heck rust-analyzer is
doing. It makes prodigious use of whitespace to delineate to create a
tree-shaped structure. This change reintroduces the leading whitespace.

I made a previous change similar to this that removed a `stderr:` in
https://github.com/zed-industries/zed/pull/27213/. If this is a
direction y'all are happy to go with, I'd be happy to add a test for
this!

<details>
<summary>A screenshot of the before</summary>

<img width="1624" alt="Screenshot 2025-06-25 at 2 12 45 PM"
src="https://github.com/user-attachments/assets/a714d973-9377-41ca-8087-3b0e82b41620"
/>

</details>

<details>
<summary>A screenshot of the after</summary>

<img width="1136" alt="Screenshot 2025-06-25 at 2 40 07 PM"
src="https://github.com/user-attachments/assets/b798ca13-11fc-4f97-9602-55e782068a5a"
/>

</details>

cc: @mgsloan.

Release Notes:

- Fixed the removal of leading whitespace in a language server's stderr
logs.
2025-06-25 16:24:51 -06:00
Danilo Leal
1330cb7a1f docs: Update instructions to use Vercel's v0 model (#33415)
To make sure this reflects the current reality as of today's
preview/stable version.

Release Notes:

- N/A
2025-06-25 17:52:23 -03:00
Peter Tripp
dae4e84bc5 Explicitly associate files as JSONC (#33410)
Fixes an issue when the zed repo was checked out to folder other than
`zed` (e.g. `zed2`) files were incorrectly identified as JSON instead of
JSONC.

Release Notes:

- N/A
2025-06-25 16:29:44 -04:00
morgankrey
6fb5500ef2 collab: Save Customer name and billing address to Customer on checkout (#33385)
We are collecting billing address and name on checkout now (for tax) but
we're not saving it back to the Customer level. Updating the Checkout
Session code to make`customer_update.address` equal to `auto`, instead
of the default `never`, as well as the same for `customer_update.name`.

Release Notes:

- N/A
2025-06-25 16:06:14 -04:00
vipex
8f9817173d pane: Update pinned tab count when it exceeds actual tab count (#33405)
## Summary

This PR improves the workaround introduced in #33335 that handles cases
where the pinned tab count exceeds the actual tab count during workspace
deserialization.

## Problem

The original workaround in #33335 successfully prevented the panic but
had two issues:
1. **Console spam**: The warning message was logged repeatedly because
`self.pinned_tab_count` wasn't updated to match the actual tab count
2. **Auto-pinning behavior**: New tabs up until you exceed the old safe
tab count were automatically pinned after the workaround was triggered.

## Solution

Updates the defensive code to set `self.pinned_tab_count = tab_count`
when the mismatch is detected, ensuring:
- The warning is only logged once when encountered.
- New tabs behave normally (aren't auto-pinned)
- The workspace remains in a consistent state

This is an immediate fix for the workaround. I'll attempt to open up a
follow-up PR when i get the chance that will address the root cause by
implementing serialization for empty untitled tabs, as discussed in
#33342.

Release Notes:

- N/A
2025-06-25 19:52:15 +00:00
Michael Sloan
aae4778b4e gpui: Add more flushing of x11 requests (#33407)
Flushes should happen after sending messages to X11 when effects should
be applied quickly. This is not needed for requests that return replies
since it automatically flushes in that case.

Release Notes:

- N/A
2025-06-25 19:46:15 +00:00
Matin Aniss
e5c812fbcb gpui: Dynamic element arena (#32079)
Implements a chunking strategy for the element arena that allows it to
grow dynamically based on allocations, it is initialised with a single
chunk of a total size of 1 mebibyte. On allocation of data with a size
greater than the remaining space of the current chunk a new chunk is
created.

This reduces the memory allocation from the static 32 mebibytes, this
especially helps GPUI applications that don't need such a large element
arena and even Zed in most cases. This also prevents the panic when
allocations ever exceed the element arena.

Release Notes:

- N/A

---------

Co-authored-by: Michael Sloan <michael@zed.dev>
2025-06-25 13:29:13 -06:00
Peter Tripp
294147f473 ci: Skip build_docs more often (#33398)
Don't run `build_docs` when the only change is:
`.github/{workflows,ISSUE_TEMPLATE}/**`.
Example [extra
run](https://github.com/zed-industries/zed/actions/runs/15883155767).

Release Notes:

- N/A
2025-06-25 14:24:47 -04:00
Richard Feldman
4516b099e7 Reduce segment cloning when rendering messages (#33340)
While working on retries, I discovered some opportunities to reduce
cloning of message segments. These segments have full `String`s (not
`SharedString`s), so cloning them means copying cloning all the bytes of
all the strings in the message, which would be nice to avoid!

Release Notes:

- N/A
2025-06-25 14:10:48 -04:00
Peter Tripp
8e831ced5b ci: Remove community_delete_comments (#33396)
This was a temporary mitigation against a spam campaign, I don't think
this is required any longer. We can easily revert if it's still active.

See:
- https://github.com/zed-industries/zed/pull/16886

Release Notes:

- N/A
2025-06-25 13:44:43 -04:00
Joseph T. Lyons
3740eec5bf Do not show update "View Release Notes" notification in nightly builds (#33394)
These are useless in nightly, as the link within the notification simply
directs us to a commit view on GitHub. We update frequently on nightly;
dismissing this after every update is annoying.

Release Notes:

- N/A
2025-06-25 17:28:06 +00:00
Peter Tripp
2a5a1814cd text_thread: Improve roles after assistant::Split (shift-enter) (#33215)
Default to `You` when triggering `assistant::Split` at the end of a thread

Release Notes:

- agent_thread: Improved roles when triggering `assistant::Split`
(`shift-enter`)
2025-06-25 13:26:24 -04:00
Umesh Yadav
cc62125244 agent: Add GEMINI.md as a supported rules file name (#33381)
Gemini cli creates GEMINI.md file. This PR adds support for it.

Release Notes:

- agent: Add GEMINI.md as a supported rules file name
2025-06-25 14:14:49 -03:00
Bennet Bo Fenner
224de2ec6c settings: Remove version fields (#33372)
This cleans up our settings to not include any `version` fields, as we
have an actual settings migrator now.

This PR removes `language_models > anthropic > version`,
`language_models > openai > version` and `agent > version`.

We had migration paths in the code for a long time, so in practice
almost everyone should be using the latest version of these settings.


Release Notes:

- Remove `version` fields in settings for `agent`, `language_models >
anthropic`, `language_models > openai`. Your settings will automatically
be migrated. If you're running into issues with this open an issue
[here](https://github.com/zed-industries/zed/issues)
2025-06-25 19:05:29 +02:00
Kirill Bulatov
c0acd8e8b1 Add language server control tool into the status bar (#32490)
Release Notes:

- Added the language server control tool into the status bar

---------

Co-authored-by: Nate Butler <iamnbutler@gmail.com>
2025-06-25 19:57:28 +03:00
Cole Miller
91c9281cea Default to cargo-zigbuild for ZED_BUILD_REMOTE_SERVER (#33391)
Follow-up to #31467. `cargo-zigbuild` will be installed if it's not
there already, but you have to install Zig yourself. Pass
`ZED_BUILD_REMOTE_SERVER=cross` to use the old way.

Release Notes:

- N/A
2025-06-25 16:49:37 +00:00
Peter Tripp
84494ab26b Make ctrl-alt-b / cmd-alt-b toggle right dock (#33190)
Closes: https://github.com/zed-industries/zed/issues/33147

In VSCode ctrl-alt-b / cmd-alt-b toggles the right dock. Zed should
follow this behavior.

See also:
- https://github.com/zed-industries/zed/pull/31630

Release Notes:

- N/A
2025-06-25 12:48:46 -04:00
Peter Tripp
93d670af13 Fix empty code actions menu trapping cursor (#33386)
Closes: https://github.com/zed-industries/zed/issues/33382
Follow-up to: https://github.com/zed-industries/zed/pull/32579

CC: @ConradIrwin @Anthony-Eid 

Release Notes:

- Fixed an issue with empty code actions menu locking the cursor
(Preview Only)
2025-06-25 12:48:15 -04:00
Peter Tripp
7d087ea5d2 docs: Improve visual-customization.md docs for Zed prompts (#33254)
Release Notes:

- N/A
2025-06-25 12:48:03 -04:00
ddoemonn
19c9fb3118 Allow multiple Markdown preview tabs (#32859)
Closes #32791


https://github.com/user-attachments/assets/8cb90e3d-ef7b-407f-b78b-7ba4ff6d8df2

Release Notes:
- Allowed multiple Markdown preview tabs
2025-06-25 16:43:00 +00:00
Oleksiy Syvokon
b0bab0bf9a agent: Prevent use of disabled tools (#33392)
The agent now checks if a tool is enabled in the current profile before
calling it. Previously, the agent could still call disabled tools, which
commonly happened after switching profiles in the middle of a thread.

Release Notes:

- Fixed a bug where the agent could use disabled tools sometimes
2025-06-25 16:30:22 +00:00
CharlesChen0823
630a326a07 file_finder: Fix create wrong file in multiple worktree (#33139)
When open multiple worktree, using `file_finder` to create a new file
shoud respect current focused worktree.
test case:
```
project:
   worktree A
        file1
   worktree B
        file2 <-  focused
```
when focused `file2`, `ctrl-p` toggle `file_finder` to create `file3`
should exists in worktreeB.


I try add test case for `CreateNew` in file_finder, but found not
worked, if you help me, I can try add this test case.

Release Notes:

- Fixed file finder selecting wrong worktree when creating a file
2025-06-25 19:17:41 +03:00
Joseph T. Lyons
6848073c38 Bump Zed to v0.194 (#33390)
Release Notes:

-N/A
2025-06-25 16:10:11 +00:00
Smit Barmase
eb51041154 debugger_ui: Fix variable completion accept in console appends the whole word (#33378)
Closes #32959

Release Notes:

- Fixed the issue where accepting variable completion in the Debugger
would append the entire variable name instead of the remaining part.
2025-06-25 19:54:08 +05:30
Sarmad Gulzar
308debe47f terminal: Fix trailing single quote included when opening link from terminal (#33376)
Closes #33210 

Release Notes:

- Fixed an issue where a trailing single quote was included when opening
a link from the terminal.
2025-06-25 19:51:33 +05:30
Vladimir Kuznichenkov
0905255fd1 bedrock: Add prompt caching support (#33194)
Closes https://github.com/zed-industries/zed/issues/33221

Bedrock has similar to anthropic caching api, if we want to cache
messages up to a certain point, we should add a special block into that
message.

Additionally, we can cache tools definition by adding cache point block
after tools spec.

See: [Bedrock User Guide: Prompt
Caching](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html#prompt-caching-models)

Release Notes:

- bedrock: Added prompt caching support

---------

Co-authored-by: Oleksiy Syvokon <oleksiy@zed.dev>
2025-06-25 17:15:13 +03:00
Bennet Bo Fenner
59aeede50d vercel: Use proper model identifiers and add image support (#33377)
Follow up to previous PRs:
- Return `true` in `supports_images` - v0 supports images already
- Rename model id to match the exact version of the model `v0-1.5-md`
(For now we do not expose `sm`/`lg` variants since they seem not to be
available via the API)
- Provide autocompletion in settings for using `vercel` as a `provider`

Release Notes:

- N/A
2025-06-25 13:26:41 +00:00
Bennet Bo Fenner
18f1221a44 vercel: Reuse existing OpenAI code (#33362)
Follow up to #33292

Since Vercel's API is OpenAI compatible, we can reuse a bunch of code.

Release Notes:

- N/A
2025-06-25 15:04:43 +02:00
Rodrigo Freire
c979452c2d Implement indent conversion editor commands (#32340)
## Description of Feature or Change

Zed currently lacks a built-in way to convert a file’s indentation style
on the fly. While it's possible to change indentation behavior via
global or language-specific settings, these changes are persistent and
broad in scope as they apply to all files or all files of a given
language. We believe this could be improved for quick one-off
adjustments to specific files.

This PR introduces two new editor commands:
`Editor::convert_indentation_to_spaces` and
`Editor::convert_indentation_to_tabs`. These commands allow users to
convert the indentation of either the entire buffer or a selection of
lines, to spaces or tabs. Indentation levels are preserved, and any
mixed whitespace lines are properly normalized.

This feature is inspired by VS Code’s "Convert Indentation to
Tabs/Spaces" commands, but offers faster execution and supports
selection-based conversion, making it more flexible for quick formatting
changes.

## Implementation Details

To enable selection-based indentation conversion, we initially
considered reusing the existing `Editor::manipulate_lines` function,
which handles selections for line-based manipulations. However, this
method was designed specifically for operations like sorting or
reversing lines, and does not allow modifications to the line contents
themselves.

To address this limitation, we refactored the method into a more
flexible version: `Editor::manipulate_generic_lines`. This new method
passes a reference to the selected text directly into a callback, giving
the callback full control over how to process and construct the
resulting lines. The callback returns a `String` containing the modified
text, as well as the number of lines before and after the
transformation. These counts are computed using `.len()` on the line
vectors during manipulation, which is more efficient than calculating
them after the fact.


```rust
fn manipulate_generic_lines<M>(
  &mut self,
  window: &mut Window,
  cx: &mut Context<Self>,
  mut manipulate: M,
) where
   M: FnMut(&str) -> (String, usize, usize),
 {
   // ... Get text from buffer.text_for_range() ...
   let (new_text, lines_before, lines_after) = manipulate(&text);
   // ...
``` 

We now introduce two specialized methods:
`Editor::manipulate_mutable_lines` and
`Editor::manipulate_immutable_lines`. Each editor command selects the
appropriate method based on whether it needs to modify line contents or
simply reorder them. This distinction is important for performance: when
line contents remain unchanged, working with an immutable reference as
`&mut Vec<&str>` is both faster and more memory-efficient than using an
owned `&mut Vec<String>`.

## Demonstration


https://github.com/user-attachments/assets/e50b37ea-a128-4c2a-b252-46c3c4530d97



Release Notes:

- Added `editor::ConvertIndentationToSpaces` and
`editor::ConvertIndentationToTabs` actions to change editor indents

---------

Co-authored-by: Pedro Silveira <pedroruanosilveira@tecnico.ulisboa.pt>
2025-06-25 12:02:42 +00:00
Shardul Vaidya
4396ac9dd6 bedrock: DeepSeek does not support receiving Reasoning Blocks (#33326)
Closes #32341

Release Notes:

- Fixed DeepSeek R1 errors for reasoning blocks being sent back to the model.
2025-06-25 14:51:25 +03:00
Vladimir Kuznichenkov
c6ff58675f bedrock: Fix empty tool input on project diagnostic in bedrock (#33369)
Bedrock [do not accept][1] `null` as a JSON value input for the tool
call when called back.

Instead of passing null, we will pass back an empty object, which is
accepted by API

Closes #33204

Release Notes:

- Fixed project diagnostic tool call for bedrock

[1]:
https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolUseBlock.html
2025-06-25 14:28:36 +03:00
Bennet Bo Fenner
1c6b4712a3 agent: Fix issue where unconfigured MCP extensions would not start server (#33365)
Release Notes:

- agent: Fix an issue where MCP servers that were provided by extensions
would sometimes not start up
2025-06-25 11:48:38 +02:00
Umesh Yadav
108162423d language_models: Emit UsageUpdate events for token usage in DeepSeek and OpenAI (#33242)
Closes #ISSUE

Release Notes:

- N/A
2025-06-25 09:42:30 +02:00
Vladimir Kuznichenkov
098896146e bedrock: Fix subsequent bedrock tool calls fail (#33174)
Closes #30714

Bedrock converse api expect to see tool options if at least one tool was
used in conversation in the past messages.

Right now if `LanguageModelToolChoice::None` isn't supported edit agent
[remove][1] tools from request. That point breaks Converse API of
Bedrock. As was proposed in [the issue][2] we won't drop tool choose but
instead will deny any of them if model will respond with a tool choose.

[1]:
fceba6c795/crates/assistant_tools/src/edit_agent.rs (L703)
[2]:
https://github.com/zed-industries/zed/issues/30714#issuecomment-2886422716

Release Notes:

- Fixed bedrock tool calls in edit mode
2025-06-25 10:37:07 +03:00
Michael Sloan
96409965e4 Cleanup handling of surrounding word logic, fixing crash in editor::SelectAllMatches (#33353)
This reduces code complexity and avoids unnecessary roundtripping
through `DisplayPoint`. Hopefully this doesn't cause behavior changes,
but has one known behavior improvement:

`clip_at_line_ends` logic caused `is_inside_word` to return false when
on a word at the end of the line. In vim mode, this caused
`select_all_matches` to not select words at the end of lines, and in
some cases crashes due to not finding any selections.

Closes #29823

Release Notes:

- N/A
2025-06-24 23:18:35 -06:00
marton csutora
014f93008a Make remote mkdir shell-independent for compatibility (#32997)
- Closes: #30962 

Nushell does not support mkdir -p
So invoke sh -c "mkdir -p" instead which will also work under nushell.

Release Notes:

- Fixed ssh remotes running Nushell (and possibly other non
posix-compliant shells)

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-25 03:21:59 +00:00
Cole Miller
17774b17fb debugger: Add a tooltip to the session picker with the session ID (#33331)
This helps correlate sessions in the picker with entries in the debug
adapter logs view.

Release Notes:

- N/A
2025-06-25 02:20:31 +00:00
Remco Smits
cf086544e3 debugger: Add support for completion triggers in debug console (#33211)
Release Notes:

- Debugger: Add support for completion triggers in debug console
2025-06-24 22:19:00 -04:00
Michael Sloan
aa330fcf2c Use background task for settings migrations + notify about errors (#30009)
Release Notes:

- N/A
2025-06-24 23:54:03 +00:00
vipex
be95716e94 helix: Prevent cursor move on entering insert mode (#33201)
Closes #33061


https://github.com/user-attachments/assets/3b3e146e-7c12-412e-b4dd-c70411891b9e

Release Notes:

- Fixed cursor unexpectedly moving when entering/exiting insert mode in
Helix mode, making the behavior consistent with the Helix editor.
2025-06-24 15:20:14 -06:00
waffle
f738fbd4f8 gpui: Disable rounding in the layout engine (#31836)
Rounding broke (among other things, probably) pixel-perfect image
rendering with non-power-of-two scaling factor.

An example which reproduces the problem can be found
[here](https://github.com/WaffleLapkin/gpui_taffy_rounding_whyyyyy).

How it looks with `gpui` from `main`:
![2025-05-31
11:34:25+CEST](https://github.com/user-attachments/assets/2cb19312-6ba6-4e80-8072-f89ddedff77b)

How it looks with this patch:
![2025-05-31
11:35:28+CEST](https://github.com/user-attachments/assets/114b52a9-58c0-4600-871c-a20eceb7179e)

Both screenshots are made on kde+wayland with magnification using kde's
built-in magnification (`Meta`+`+`, `Meta`+`-`). Note that screenshot
apps have a high chance of lying 🙃

The image itself is 400 by 300 pixels of red/green checkerboard pattern
made specifically to exaggerate scaling issues.

Release Notes:

- N/A
2025-06-24 20:28:57 +00:00
Joseph T. Lyons
0c78a115de Patch panic around pinned tab count (#33335)
After much investigation, I have not been able to track down what is
causing [this
panic](https://github.com/zed-industries/zed/issues/33342). I'm clamping
the value for now, because a bug is better than a crash. Hopefully
someone finds reproduction steps, and I will implement a proper fix.

Release Notes:

- N/A
2025-06-24 20:24:06 +00:00
Michael Sloan
9427526a41 gpui: Clear the element arena after presenting the frame (#33338)
This is an easy way to shave some microseconds off the critical path for
frame rendering. On my machine this reduces typical frame rendering
latency by ~100 microseconds, probably quite a bit more on slower
machines.

Here is how long it typically takes to drop elements from the arena,
from a fairly brief run:


![image](https://github.com/user-attachments/assets/65cfd911-eccf-4393-887d-8cad2cd27148)

Release Notes:

- N/A
2025-06-24 19:43:33 +00:00
Kirill Bulatov
eec26c9a41 Add initial docs for editor diagnostics (#33325)
Release Notes:

- N/A
2025-06-24 22:21:27 +03:00
Anthony Eid
3c0475d182 debugger: Reorder step icons to be consistent with other editors (#33330)
Closes #33303

Release Notes:

- debugger: Swap step in/out icon positions in debug panel to be
consistent with other editors
2025-06-24 19:15:34 +00:00
Anthony Eid
fc1fc264ec debugger: Generate inline values based on debugger.scm file (#33081)
## Context

To support inline values a language will have to implement their own
provider trait that walks through tree sitter nodes. This is overly
complicated, hard to accurately implement for each language, and lacks
proper extension support.

This PR switches to a singular inline provider that uses a language's
`debugger.scm` query field to capture variables and scopes. The inline
provider is able to use this information to generate inlays that take
scope into account and work with any language that defines a debugger
query file.

### Todos
- [x] Implement a utility test function to easily test inline values
- [x] Generate inline values based on captures
- [x] Reimplement Python, Rust, and Go support
- [x] Take scope into account when iterating through variable captures
- [x] Add tests for Go inline values
- [x] Remove old inline provider code and trait implementations

Release Notes:

- debugger: Generate inline values based on a language debugger.scm file
2025-06-24 18:24:43 +00:00
Peter Tripp
800b925fd7 Improve Atom keymap (#33329)
Closes: https://github.com/zed-industries/zed/issues/33256

Move some Editor keymap entries into `Editor && mode == full`

Release Notes:

- N/A
2025-06-24 18:02:07 +00:00
fantacell
95cf153ad7 Simulate helix line wrapping (#32763)
In helix the `f`, `F`, `t`, `T`, left and right motions wrap lines. I
added that by default.

Release Notes:

- vim: The `use_multiline_find` setting is replaced by binding to the
correct action in the keymap:
    ```
"f": ["vim::PushFindForward", { "before": false, "multiline": true }],
"t": ["vim::PushFindForward", { "before": true, "multiline": true }],
"shift-f": ["vim::PushFindBackward", { "after": false, "multiline": true
}],
"shift-t": ["vim::PushFindBackward", { "after": true, "multiline": true
}],
    ```
- helix: `f`/`t`/`shift-f`/`shift-t`/`h`/`l`/`left`/`right` are now
multiline by default (like helix)
2025-06-24 10:51:41 -06:00
Bennet Bo Fenner
7be57baef0 agent: Fix issue with Anthropic thinking models (#33317)
cc @osyvokon 

We were seeing a bunch of errors in our backend when people were using
Claude models with thinking enabled.

In the logs we would see
> an error occurred while interacting with the Anthropic API:
invalid_request_error: messages.x.content.0.type: Expected `thinking` or
`redacted_thinking`, but found `text`. When `thinking` is enabled, a
final `assistant` message must start with a thinking block (preceeding
the lastmost set of `tool_use` and `tool_result` blocks). We recommend
you include thinking blocks from previous turns. To avoid this
requirement, disable `thinking`. Please consult our documentation at
https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking

However, this issue did not occur frequently and was not easily
reproducible. Turns out it was triggered by us not correctly handling
[Redacted Thinking
Blocks](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#thinking-redaction).

I could constantly reproduce this issue by including this magic string:
`ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB
` in the request, which forces `claude-3-7-sonnet` to emit redacted
thinking blocks (confusingly the magic string does not seem to be
working for `claude-sonnet-4`). As soon as we hit a tool call Anthropic
would return an error.

Thanks to @osyvokon for pointing me in the right direction 😄!


Release Notes:

- agent: Fixed an issue where Anthropic models would sometimes return an
error when thinking was enabled
2025-06-24 16:23:59 +00:00
Kirill Bulatov
39dc4b9040 Fix being unable to input a whitespace character in collab channels filter (#33318)
Before, `space` was always causing a channel join.
Now it's less fluent, one has to press `ESC` to get the focus out of the
filter editor and then `space` starts joining the channel.

Release Notes:

- Fixed being unable to input a whitespace character in collab channels
filter
2025-06-24 16:11:25 +00:00
Ben Kunkle
deb2564b31 gpui: Add keybind metadata API (#33316)
Closes #ISSUE

Adds a very simple API to track metadata about keybindings in GPUI,
namely the source of the binding. The motivation for this is displaying
the source of keybindings in the [keymap
UI](https://github.com/zed-industries/zed/pull/32436).

The API is designed to be as simple and flexible as possible, storing
only a `Option<u32>` on the bindings themselves to keep the struct
small. It is intended to be used as an index or key into a table/map
created and managed by the consumer of the API to map from indices to
arbitrary meta-data. I.e. the consumer is responsible for both
generating these indices and giving them meaning.

The current usage in Zed is stateless, just a mapping between constants
and User, Default, Base, and Vim keymap sources, however, this can be
extended in the future to also track _which_ base keymap is being used.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-24 16:07:45 +00:00
morgankrey
13f134448d collab: Require billing address in all Stripe checkouts (#32980)
Summary

I've successfully implemented the required billing address collection
feature for Stripe Checkout sessions. Here's what was done:

### 1. **Added New Data Structures** (`stripe_client.rs`):
- Added `StripeBillingAddressCollection` enum with `Auto` and `Required`
variants
- Added `billing_address_collection` field to
`StripeCreateCheckoutSessionParams`

### 2. **Updated Stripe Client Implementation**
(`real_stripe_client.rs`):
- Added conversion from `StripeBillingAddressCollection` to Stripe's
`CheckoutSessionBillingAddressCollection`
- Updated the `TryFrom` implementation to map the billing address
collection field when creating checkout sessions
- Added the necessary import

### 3. **Updated Billing Service** (`stripe_billing.rs`):
- Set `billing_address_collection` to `Required` in both
`checkout_with_zed_pro()` and `checkout_with_zed_pro_trial()` methods
- Added the necessary import

### 4. **Updated Test Infrastructure** (`fake_stripe_client.rs`):
- Added `billing_address_collection` field to
`StripeCreateCheckoutSessionCall`
- Updated the `create_checkout_session` implementation to capture the
new field
- Added the necessary import

### 5. **Updated Tests** (`stripe_billing_tests.rs`):
- Added assertions to verify that `billing_address_collection` is set to
`Required` in all three test cases:
  - `test_checkout_with_zed_pro`
  - `test_checkout_with_zed_pro_trial` (regular trial)
  - `test_checkout_with_zed_pro_trial` (extended trial)
- Added the necessary import

The implementation follows the pattern established in the codebase and
ensures that whenever a Stripe Checkout session is created for Zed Pro
subscriptions (both regular and trial), the billing address will be
required from customers. This aligns with the Stripe documentation you
provided, which shows that setting `billing_address_collection=required`
will ensure the billing address is always collected during checkout.

Release Notes:

- N/A

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-06-24 14:06:00 +00:00
Danilo Leal
94735aef69 Add support for Vercel as a language model provider (#33292)
Vercel v0 is an OpenAI-compatible model, so this is mostly a dupe of the
OpenAI provider files with some adaptations for v0, including going
ahead and using the custom endpoint for the API URL field.

Release Notes:

- Added support for Vercel as a language model provider.
2025-06-24 11:02:06 -03:00
Danilo Leal
0d70bcb88c agent: Allow to force uninstall extension if it provides more than the MCP server (#33279) 2025-06-24 09:11:03 -03:00
Ben Brandt
360d73c763 Implement save functionality for diff view (#33298)
Add `can_save` and `save` methods to `DiffView`, enabling users to save
changes made within the diff view.

Release Notes:

- Allow saving changes in the `zed --diff` view
2025-06-24 09:17:53 +00:00
Stanislav Alekseev
668d5eef3b Add horizontal scroll to REPL outputs (#33247)
Release Notes:

- Made the horizontal outputs scrollable in REPL
2025-06-24 10:15:20 +03:00
Max Brunsfeld
4cd4d28531 Move UI code from assistant_context_editor -> agent_ui (#33289)
This breaks a transitive dependency of `agent` on UI crates. I've also
found and eliminated some dead code in assistant_context_editor.

Release Notes:

- N/A
2025-06-23 22:22:01 -07:00
Michael Sloan
786e724684 Fetch and wait for channels when opening channel notes via URL (#33291)
Release Notes:

* Collaboration: Now fetches and waits for channels when opening channel
notes via URL.
2025-06-24 05:14:15 +00:00
Michael Sloan
24c94d474e gpui: Simplify Action macros + support doc comments in actions! (#33263)
Instead of a menagerie of macros for implementing `Action`, now there
are just two:

* `actions!(editor, [MoveLeft, MoveRight])`
* `#[derive(..., Action)]` with `#[action(namespace = editor)]`

In both contexts, `///` doc comments can be provided and will be used in
`JsonSchema`.

In both contexts, parameters can provided in `#[action(...)]`:

- `namespace = some_namespace` sets the namespace. In Zed this is
required.

- `name = "ActionName"` overrides the action's name. This must not
contain "::".

- `no_json` causes the `build` method to always error and
`action_json_schema` to return `None`
and allows actions not implement `serde::Serialize` and
`schemars::JsonSchema`.

- `no_register` skips registering the action. This is useful for
implementing the `Action` trait
while not supporting invocation by name or JSON deserialization.

- `deprecated_aliases = ["editor::SomeAction"]` specifies deprecated old
names for the action.
These action names should *not* correspond to any actions that are
registered. These old names
can then still be used to refer to invoke this action. In Zed, the
keymap JSON schema will
accept these old names and provide warnings.

- `deprecated = "Message about why this action is deprecation"`
specifies a deprecation message.
In Zed, the keymap JSON schema will cause this to be displayed as a
warning. This is a new feature.

Also makes the following changes since this seems like a good time to
make breaking changes:

* In `zed.rs` tests adds a test with an explicit list of namespaces. The
rationale for this is that there is otherwise no checking of `namespace
= ...` attributes.

* `Action::debug_name` renamed to `name_for_type`, since its only
difference with `name` was that it

* `Action::name` now returns `&'static str` instead of `&str` to match
the return of `name_for_type`. This makes the action trait more limited,
but the code was already assuming that `name_for_type` is the same as
`name`, and it requires `&'static`. So really this just makes the trait
harder to misuse.

* Various action reflection methods now use `&'static str` instead of
`SharedString`.

Release Notes:

- N/A
2025-06-24 04:34:51 +00:00
Kirill Bulatov
21f985a018 Stop showing diagnostics in the diff-related editors (#33285)
<img width="1728" alt="image"
src="https://github.com/user-attachments/assets/a63925a7-8e13-4d48-bd31-33f434209ea6"
/>

Diagnostics UI elements (underlines, popovers, hovers) are quite noisy
by themselves and get even more so with the git background colors.

Release Notes:

- Stopped showing diagnostics in the diff-related editors
2025-06-24 01:35:08 +00:00
Max Brunsfeld
2283ec5de2 Extract an agent_ui crate from agent (#33284)
This PR moves the UI-dependent logic in the `agent` crate into its own
crate, `agent_ui`. The remaining `agent` crate no longer depends on
`editor`, `picker`, `ui`, `workspace`, etc.

This has compile time benefits, but the main motivation is to isolate
our core agentic logic, so that we can make agents more
pluggable/configurable.

Release Notes:

- N/A
2025-06-23 18:00:28 -07:00
okhai
371b7355d3 Add icon for Cairo files (#33268)
Discussion: https://github.com/zed-industries/zed/discussions/33270

Release Notes:

- Add file icon for the Cairo programming language.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-06-23 20:53:55 -03:00
Carl Sverre
95f10fd187 Add ability to clone item when using workspace::MoveItemToPane (#32895)
This PR adds an optional `clone: bool` argument to
`workspace::MoveItemToPane` and `workspace::MoveItemToPaneInDirection`
which causes the item to be cloned into the destination pane rather than
moved. It provides similar functionality to
`workbench.action.splitEditorToRightGroup` in vscode.

This PR supercedes #25030.

Closes #24889

Release Notes:

- Add optional `clone: bool` (default: `false`) to
`workspace::MoveItemToPane` and `workspace::MoveItemToPaneInDirection`
which causes the item to be cloned into the destination pane rather than
moved.
2025-06-23 23:41:46 +00:00
Danilo Leal
324cbecb74 agent: Improve MCP with no config editor empty state (#33282)
Removed an additional label from the modal empty state as I figured we
don't need it, given we already have a version of the description in
place for when the extension itself doesn't return any. So, ultimately,
having both the label and the description was redundant.

Release Notes:

- N/A
2025-06-23 23:33:19 +00:00
Danilo Leal
32df9256c3 notification: Add built-in dismiss button in the Status Toast component (#33278)
There may be cases where we're needing to pass a button just so it is
dismissible, so I figured this out help! It also helps when you want to
have two buttons, one to perform an action and another to dismiss and
cancel.

Release Notes:

- N/A
2025-06-23 19:55:21 -03:00
Maxim
36eebb7ba8 Fix race condition between auto-indent and on-type-formatting (#32005)
This PR addresses to fix (#31308) a race condition where auto-indent (in
buffer.cs) and on-type-formatting (in lsp_store.rs) concurrently
calculate indentation using the same buffer snapshot.

Previous Solution (Abandoned): 
https://github.com/zed-industries/zed/pull/31340

Final Solution:
Delay applying on-type-formatting until auto-indent is complete.

Issue:

If AutoindentMode finishes first, formatting works correctly. If
"Formatting on typing" starts before AutoindentMode completes, it
results in double indentation.

Closes #31308

Release Notes:

- Fixed a race condition resulting in incorrect buffer contents when combining auto-indent and on-type-formatting
2025-06-23 17:59:06 -04:00
Anthony Eid
d34d4f2ef1 debugger: Kill debug sessions on app quit (#33273)
Before this PR force quitting Zed would leave hanging debug adapter
processes and not allow debug adapters to clean up their sessions
properly.

This PR fixes this problem by sending a disconnect/terminate to all
debug adapters and force shutting down their processes after they
respond.

Co-authored-by: Cole Miller \<cole@zed.dev\>

Release Notes:

- debugger: Shutdown and clean up debug processes when force quitting
Zed

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Remco Smits <djsmits12@gmail.com>
2025-06-23 20:41:53 +00:00
Richard Feldman
c610ebfb03 Thread Anthropic errors into LanguageModelKnownError (#33261)
This PR is in preparation for doing automatic retries for certain
errors, e.g. Overloaded. It doesn't change behavior yet (aside from some
granularity of error messages shown to the user), but rather mostly
changes some error handling to be exhaustive enum matches instead of
`anyhow` downcasts, and leaves some comments for where the behavior
change will be in a future PR.

Release Notes:

- N/A
2025-06-23 18:48:26 +00:00
Cole Miller
aabfea4c10 debugger: Document workaround for debugging Swift (#33269)
Release Notes:

- N/A
2025-06-23 18:29:20 +00:00
Danilo Leal
51059b6f50 agent: Rename the delete menu item in the MCP section (#33265)
From "Delete" to "Uninstall" for clarity.

Release Notes:

- N/A
2025-06-23 18:20:12 +00:00
Cole Miller
6b0325b059 debugger: Document some troubleshooting tools (#33047)
Release Notes:

- N/A
2025-06-23 17:11:39 +00:00
Cole Miller
c9bd409732 debugger: Support passing custom arguments to debug adapters (#33251)
Custom arguments replace any arguments that we normally pass to the DAP.
For interpreted languages, they are passed to the interpreter after the
DAP path or module. They can be combined with a custom binary, or you
can omit `dap.binary` and just customize the arguments to the DAPs we
download.

This doesn't take care of updating the extension API to support custom
arguments.

Release Notes:

- debugger: Implemented support for passing custom arguments to a debug
adapter binary using the `dap.args` setting.
- debugger: Fixed not being able to use the `dap` setting in
`.zed/settings.json`.
2025-06-23 17:06:48 +00:00
Matin Aniss
a067c16c82 windows: Use drop target helper (#33203)
It now utilises the
[`IDropTargetHelper`](https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-idroptargethelper)
in drag and drop events to render the proper item drop cursor icon which
includes the thumbnail when available and action text. Also swaps the
drop effect from `DROPEFFECT_LINK` to `DROPEFFECT_COPY` to match other
Windows application behaviour.

Example of drop icon

![example_drop](https://github.com/user-attachments/assets/4f8ea86c-929a-4813-9f8e-b3553ecf4d6e)

Release Notes:

- N/A

---------

Co-authored-by: 张小白 <364772080@qq.com>
2025-06-23 13:30:21 +00:00
Matin Aniss
1a6c1b2159 windows: Fix window close animation (#33228)
Implements a workaround which removes the `WS_EX_LAYERED` style from the
window right before closing it which seems to fix the window close
animation not playing.

Release Notes:

- N/A
2025-06-23 21:13:53 +08:00
Danilo Leal
8718019b52 file finder: Ensure filter options keybinding is displayed (#33244)
Follow up to https://github.com/zed-industries/zed/pull/31777. I
could've sworn the filter options keybinding was being displayed in the
icon button tooltip, but just realized it actually wasn't. So, this PR
fixes that!

Release Notes:

- N/A
2025-06-23 09:19:14 -03:00
Umesh Yadav
a32505fcc2 agent: Fix token limit callout to show burn mode only for zed provider (#33096)
The token limit reached callout was shown for all the providers and it
included the burn mode toggle and description. I have made that
conditional to only show for zed provider.

Before this changes
<img width="413" alt="image"
src="https://github.com/user-attachments/assets/b7e177fc-d2e6-4942-8934-39ac8c844649"
/>

After this change:
<img width="404" alt="image"
src="https://github.com/user-attachments/assets/0e091b99-c8e3-4960-8b4d-47170883d224"
/>


Release Notes:

- agent: Fix token limit callout to show burn mode only for zed provider

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-06-23 11:36:44 +00:00
Peter Tripp
980917bb7c agent: Preserve chat box text on 'New From Summary' (#33220)
CC: @danilo-leal Do you have thoughts on this? I found myself typing
chat messages after a long thread and then deciding I would be better
served by restarting from a summary -- and then "poof" the contents of
my chat box was lost.

Release Notes:

- agent: "New From Summary" now preserves any unsent content in the chat
box.
2025-06-23 08:35:25 -03:00
Peter Tripp
bd8471bb40 agent: Add soft-wrap to long terminal command JSON (#33188)
Closes: https://github.com/zed-industries/zed/issues/33179

| Before | After | 
| - | - |
| <img width="341" alt="Screenshot 2025-06-21 at 23 06 01"
src="https://github.com/user-attachments/assets/963a3663-b24f-4946-8b48-2c1222cdef2e"
/> | <img width="424" alt="Screenshot 2025-06-21 at 23 08 13"
src="https://github.com/user-attachments/assets/51051221-fda2-48af-ae42-a04989bb4913"
/> |

Release Notes:

- N/A
2025-06-23 08:35:16 -03:00
Danilo Leal
16f1da1b7e agent: Fix long previous user message double scroll (#33056)
Previously, if editing a long previous user message in the thread, you'd
have a double scroll situation because the editor used in that case had
its max number of lines capped. To solve that, I made the `max_lines` in
the editor `AutoHeight` mode optional, allowing me to not pass any
arbitrary number to the previous user message editor, and ultimately,
solving the double scroll problem by not having any scroll at all.

Release Notes:

- agent: Fixed double scroll that happened when editing a long previous
user message.

@ConradIrwin adding you as a reviewer as I'm touching editor code
here... want to be careful. :)
2025-06-23 08:32:05 -03:00
张小白
272fc672af windows: Dialog QoL improvements (#33241)
Just like in the previous PR #33230, we need to properly set up modal
windows to make them work as expected.

Before this PR, when you opened an "Open File" or "Save File" dialog,
clicking the main window would steal focus from the modal, even though
the main window wasn’t actually interactive.

With this PR, clicking the main window while a modal is open does
nothing — as it should — until the modal is closed.

#### Before



https://github.com/user-attachments/assets/9c6bdff0-1c46-49c1-a5ff-751c52c7d613

#### After



https://github.com/user-attachments/assets/8776bd28-85ff-4f32-8390-bcf5b4eec1fe





Release Notes:

- N/A
2025-06-23 11:02:00 +00:00
Bennet Bo Fenner
e68b95c61b agent: Ensure tool names are unique (#33237)
Closes #31903

Release Notes:

- agent: Fix an issue where an error would occur when MCP servers
specified tools with the same name

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-23 12:15:08 +02:00
张小白
28f56ad7ae windows: Avoid setting the mouse cursor while the window is disabled (#33230)
Don’t set the mouse cursor when the window is disabled, it causes issues
with modal dialogs.

Release Notes:

- N/A
2025-06-23 05:59:10 +00:00
Michael Sloan
7f44e4b89c Fix logic for updating insert_range on completion resolution (#32523)
I don't have a concrete misbehavior from it, but this update of
`insert_range` doesn't make sense for two reasons:

* If the resolved completion doesn't have a new `text_edit` it would
clear out the `insert_range`.

* It doesn't update the completion if it has already been resolved,
except this update of `insert_range` happened before that.

Guessing it was written this way because this field needed to only be
mutated within the `CompletionSource::Lsp` case and this was a
convenient match below.

Release Notes:

- N/A

Co-authored-by: Smit <smit@zed.dev>
2025-06-23 01:18:47 +00:00
Bennet Bo Fenner
e5ad2c2518 Add crates/assistant_tools/src/edit_agent/evals/fixtures to file_scan_exclusions (#33224)
Follow up to #32211

Release Notes:

- N/A
2025-06-23 01:06:30 +00:00
Artem Zhurikhin
67ac80bd19 linux: Fix KeePassXC integration via org.freedesktop.Secrets (#33026)
Closes #29956

Unlike GNOME Keyring, KeePassXC locks individual secrets in addition to
the entire database when configured to ask for confirmation for access
requests by DBus clients. As such, before the secret is read it should
be unlocked by the client.

Tested against both KeePassXC and GNOME Keyring, and with this patch Zed
successfully logs in and fetches the API keys from the Secret Service.

Release Notes:

- Fixed KeePassXC integration via org.freedesktop.Secrets
2025-06-22 18:57:45 -06:00
Hiroki Tagato
ac30a8b0df Improve FreeBSD support (#33162)
This PR contains a set of changes for improving FreeBSD support (#15309,
#29550) and is a kind of follow up to the PR #20480 which added an
initial support for FreeBSD.

A summary of changes is as follows:
- Add some more freebsd conditionals which seem missing in the previous
PR.
- Implement `anonymous_fd()` and `current_path()` functions for FreeBSD.
- Improve detection of FreeBSD in telemetry and GPU detection.
- Temporarily disable LiveKit/WebRTC support to make build succeed.
- Remove support for flatpak since it is Linux-only packaging format.

Adding `RUSTFLAGS="-C link-dead-code"` does not seem necessary anymore.
It builds fine without the flag.

Known issues:
- Integrated terminal is painfully laggy and virtually unusable in my
environment. This might be specific to my setup.
- I cannot input Japanese using IME. When I type characters, they appear
on the screen. But when I hit return key, they disappears. Seems the
same issue as #15409.

My environment is MATE desktop on X11 on FreeBSD 14.2 on Intel Core
i5-7260U integrated graphics.

P.S. For those who might be interested, a work-in-progress FreeBSD port
and binary packages are available at
https://github.com/tagattie/FreeBSD-Zed

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-22 16:23:17 -04:00
Willem
6b4c607331 bedrock: Support Claude 3.7 in APAC (#33068)
In ap-northeast-1 we have access to 3.7 and 4.0

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-22 20:08:50 +00:00
Peter Tripp
595f61f0d6 bedrock: Use Claude 3.0 Haiku where Haiku 3.5 is not available (#33214)
Closes: https://github.com/zed-industries/zed/issues/33183

@kuzaxak Can you confirm this works for you?

Release Notes:

- bedrock: Use Anthropic Haiku 3.0 in AWS regions where Haiku 3.5 is
unavailable
2025-06-22 15:15:20 -04:00
Vladimir Kuznichenkov
1047d8adec bedrock: Add Sonnet 4 to cross-region model list (eu/apac) (#33192)
Closes #31946

Sonnet 4 is [now
available](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html)
via Bedrock in EU aws regions.

Release Notes:

- bedrock: Add cross-region usage of Sonnet 4 in EU/APAC AWS regions
2025-06-22 15:15:05 -04:00
Peter Tripp
21fd5c24bf emacs: Fix ctrl-p/ctrl-n navigating popover menus (#33218)
Closes https://github.com/zed-industries/zed/issues/33200

Release Notes:

- emacs: Fixed ctrl-p/ctrl-n keyboard navigation of autocomplete/code
actions menus
2025-06-22 15:02:25 -04:00
Danilo Leal
336d2c41fa docs: Update the AI configuration page (#33208)
- Improve the Vercel v0 model section
- Replace "assistant panel" to "agent panel"
- Replace "Zed assistant" to "Zed agent"
- Fix "open configuration" action name
- Break sentences where useful

Release Notes:

- N/A
2025-06-22 16:13:54 +00:00
张小白
5244085bd0 windows: Fix wrong glyph index being reported (#33193)
Closes #32424

This PR fixes two bugs:

* In cases like `fi ~~something~~`, the `fi` gets rendered as a
ligature, meaning the two characters are combined into a single glyph.
The final glyph index didn’t account for this change, which caused
issues.
* On Windows, some emojis are composed of multiple glyphs. These
composite emojis can now be rendered correctly as well.

![屏幕截图 2025-06-22
161900](https://github.com/user-attachments/assets/e125426b-a15e-41d1-a6e6-403a16924ada)

![屏幕截图 2025-06-22
162005](https://github.com/user-attachments/assets/f5f01022-2404-4e73-89e5-1aaddf7419d9)


Release Notes:

- N/A
2025-06-22 11:14:58 +00:00
张小白
af8f26dd34 Add a Copy button for About Zed (#33197)
Closes #33160

Since `TaskDialog` doesn’t allow users to copy its contents directly,
VSCode added a `Copy` button so users can easily copy the message.




https://github.com/user-attachments/assets/04090753-226f-44d9-992c-8cc8cb8d7ecb




Release Notes:

- N/A
2025-06-22 19:12:12 +08:00
yoshi-taka
3b9f504d75 Remove unused dependencies (#33189)
I verified it after running `cargo shear`.

https://crates.io/crates/cargo-shear

Release Notes:

- N/A
2025-06-22 07:43:37 +00:00
Emanuele Stoppa
0579bf73b0 docs: Document language_ids in extension.toml (#33035)
Document `language-servers.*.language_ids` property in extension.toml.

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-22 07:12:09 +00:00
Peter Tripp
534475d7aa Add reference to commit_message_model in git docs. (#33186)
Release Notes:

- N/A
2025-06-22 03:04:55 +00:00
Peter Tripp
8d05f6fdd3 Add visual customization section to documentation (#33133)
Release Notes:

- Improved settings documentation for [visual customization](https://zed.dev/docs/visual-customization/)
2025-06-21 22:55:53 -04:00
Jason Lee
3cb4342a76 editor: Disable tab to indent in single line mode (#33031)
Release Notes:

- Disable indent on `tab` in single line editors.

----


https://github.com/user-attachments/assets/64207777-52c9-4425-be66-88acaf81f0b8
2025-06-22 00:55:19 +03:00
Alvaro Parker
6fb1081b61 Disable next/previous hunk menu items when there are no hunks (#32907)
Closes #32887

Release Notes:

- Button menu hunk is disabled if there are no changes
2025-06-22 00:43:00 +03:00
Arseny Kapoulkine
0ed6b4ef1a git_ui: Add collapse_untracked_diff settings to improve usability for untracked files (#32591)
In repositories with untracked files that are not intended to be added,
showing the expanded diffs in the panel is confusing, as it places the
changes side by side with changes in tracked files.

This change adds a setting, collapse_untracked_diff, that can be enabled
to collapse untracked diffs by default when the panel is opened.

See
https://github.com/zed-industries/zed/pull/31855#issuecomment-2957547018
(and previous comment for examples of why this is useful).

Example before this change, or with the setting in its default state:


![image](https://github.com/user-attachments/assets/f974c849-7ebf-424e-9397-442a6cc2513b)

Example after this change with the setting set to `true`:


![image](https://github.com/user-attachments/assets/bd8934f5-bd9a-4f5a-b723-cd4b798d2c2c)

Release Notes:

- Git: Added `collapse_untracked_diff` setting to auto-collapse
untracked diffs
2025-06-22 00:38:24 +03:00
Dallin Huff
d75e210e73 docs: Fix path to keymap.json config file for macOS (#32728)
Update key-bindings doc page to correctly reference
`~/.config/zed/keymap.json` instead of the old `~/.zed/keymap.json`

Closes #ISSUE

Release Notes:

- N/A
2025-06-22 00:31:01 +03:00
Peter Tripp
2b3e453d2f Avoid using tmpdir when writing Zed settings.json on macOS (#32976)
Closes: https://github.com/zed-industries/zed/issues/23907

Release Notes:

- macOS: Fixed an issue with writing Zed settings.json if user's home
directory is on a non-root volume.
2025-06-21 13:27:06 -04:00
Peter Tripp
cb50f07d23 One Light Theme: Change constant color (#33166)
Closes: https://github.com/zed-industries/zed/issues/4617
Closes: https://github.com/zed-industries/zed/issues/25709

Changes the One Light syntax highlight for `constant` to be a distinct
from `comment`:

| Before | After |
| - | - |
| <img width="424" alt="Screenshot 2025-06-21 at 11 59 22"
src="https://github.com/user-attachments/assets/8780918f-7701-4780-809a-f95951970c90"
/> | <img width="430" alt="Screenshot 2025-06-21 at 11 59 17"
src="https://github.com/user-attachments/assets/d7efaa16-f95a-4076-a0b2-b1170e2ffe68"
/> |

I picked a color which was present in the palette from the original One
Light theme from Atom.

@failable: Sorry this took so long, does this look ok to you?

Release Notes:

- N/A
2025-06-21 12:43:02 -04:00
Peter Tripp
a713c66a9d Redact command environment variables from log output (#32985)
Before/After (linebreaks added for readability)
```log 
# before
INFO  [project::context_server_store::extension]
loaded command for context server mcp-server-github:
Command { 
  command: "/Users/peter/Library/Application Support/Zed/extensions/work/mcp-server-github/github-mcp-server-v0.5.0/github-mcp-server", 
  args: ["stdio"], 
  env: [("GITHUB_PERSONAL_ACCESS_TOKEN", "gho_WOOOOOOOOOOOOOOO")] 
}

#after
INFO  [project::context_server_store::extension]
loaded command for context server mcp-server-github:
Command {
  command: "/Users/peter/Library/Application Support/Zed/extensions/work/mcp-server-github/github-mcp-server-v0.5.0/github-mcp-server",
  args: ["stdio"],
  env: [("GITHUB_PERSONAL_ACCESS_TOKEN", "[REDACTED]")]
}
```

Release Notes:

- Redact sensitive environment variables from MCP logs
2025-06-21 11:19:23 -04:00
Smit Barmase
76e3136369 editor: Utilize filter_text from language server for filter_range (#33155)
Closes #33106

Instead of directly using `filter_text` in `StringMatchCandidate`, which
yields better results for fuzzy matching but messes up with highlighting
letters in bold, as `positions` list generated by fuzzy crate are now of
`filter_text` and not `label` shown to the user.

This PR fixes it by keeping use of `filter_range` in
`StringMatchCandidate`, which is range w.r.t to `label` shown to user.
And actually generating this `filter_range` at source by using
`filter_range` if exists.

- [x] Tests

Release Notes:

- Fixed issue where incorrect letters are marked as bold in completions.
2025-06-21 19:47:16 +05:30
Bennet Bo Fenner
834cdc1271 agent: Store if context server should be enabled/disabled in the settings (#32994)
- [x] Show disabled MCP servers in the list so you can enable them again
- [x] If MCP is not present in the settings, add it

Closes #ISSUE

Release Notes:

- agent: Allow to enable/disable context servers permanently in the
agent configuration view

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-06-21 14:46:36 +02:00
Umesh Yadav
dfdd2b9558 language_models: Add thinking support to OpenRouter provider (#32541)
Did some bit cleanup of code for loading models for settings as that is
not required as we are fetching all the models from openrouter so it's
better to maintain one source of truth

Release Notes:

- Add thinking support to OpenRouter provider
2025-06-21 08:03:50 +02:00
Danilo Leal
b9838efaaa agent: Add button to scroll to top of the thread (#33130)
Release Notes:

- agent: Added a button to scroll to top of the thread.
2025-06-20 20:28:07 -03:00
June
db8acfa9ff Update pathfinder_simd for nightly arm simd fixes (#33131)
`pathfinder_simd` doesn't compile on nightly aarch64 right now, but that
was fixed by https://github.com/servo/pathfinder/pull/575 and updated on
crates.io by https://github.com/servo/pathfinder/pull/577. This PR
simply updates the `pathfinder_simd` dependency to the version that
contains these fixes.

I verified that this compiles with nightly on my machine.
2025-06-20 23:13:37 +00:00
Michael Sloan
5c1dc8b2cc Cargo.lock updates (#33129)
Should have been in #33125

Release Notes:

- N/A
2025-06-20 22:42:57 +00:00
Michael Sloan
7e801dccb0 agent: Fix issues with usage display sometimes showing initially fetched usage (#33125)
Having `Thread::last_usage` as an override of the initially fetched
usage could cause the initial usage to be displayed when the current
thread is empty or in text threads. Fix is to just store last usage info
in `UserStore` and not have these overrides

Release Notes:

- Agent: Fixed request usage display to always include the most recently
known usage - there were some cases where it would show the initially
requested usage.
2025-06-20 21:28:48 +00:00
Joseph T. Lyons
e0c0b6f95d Remove duplicate issues in top-ranking issues script (#33127)
Release Notes:

- N/A
2025-06-20 17:13:26 -04:00
Remco Smits
ad76db7244 debugger: Add variable watchers (#32743)
### This PR introduces support for adding watchers to specific
expressions (such as variable names or evaluated expressions).

This feature is useful in scenarios where many variables are in scope,
but only a few are of interest—especially when tracking variables that
change frequently. By allowing users to add watchers, it becomes easier
to monitor the values of selected expressions across stack frames
without having to sift through a large list of variables.


https://github.com/user-attachments/assets/c49b470a-d912-4182-8419-7406ba4c8f1e

------

**TODO**:
- [x] make render variable code reusable for render watch method
- [x] use SharedString for watches because of a lot of cloning
- [x] add tests
  - [x] basic test
  - [x] test step debugging

Release Notes:

- Debugger Beta: Add support for variable watchers

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
2025-06-20 20:45:55 +00:00
Joseph T. Lyons
9f2c541ab0 Fetch 100 results per page (#33124)
Release Notes:

- N/A
2025-06-20 16:44:39 -04:00
Joseph T. Lyons
9597c73f2b Account for issue types in top-ranking issues script (#33118)
Release Notes:

- N/A
2025-06-20 14:19:29 -04:00
Smit Barmase
7812985d3c linux: Fix blurry rendering on Wayland when using fractional scaling (#33087)
Closes #25195

In Wayland, To create buffer size (`renderer.update_drawable_size`), we
convert logical pixels to device pixels by taking the scale factor into
account. Later, we also let the compositor know the logical pixels we
want to use for our app (`viewport.set_destination`). Then, the
compositor takes our buffer and tries to scale it to fit the viewport
size we provided. If this is accurate, we see perfect rendering. If our
buffer size is not accurate (off by 1px in this case), the compositor
scales our buffer to fit the viewport size. This causes blur.

To make sure we set correct buffer size for renderer as same as what
compositor is going to use, we needs to use rounding instead of truncate
when converting logical pixels to device pixels. It's not super clear
from docs, what exact algorithm it uses but it says it uses rounding and
seems to fix issue for me if we follow that for our buffer.

From https://wayland.app/protocols/fractional-scale-v1:
> If a surface has a surface-local size of 100 px by 50 px and wishes to
submit buffers with a scale of 1.5, then a buffer of 150px by 75 px
should be used and the wp_viewport destination rectangle should be 100
px by 50 px.
>
> For toplevel surfaces, the size is **rounded halfway away from zero**.
The rounding algorithm for subsurface position and size is not defined.

Tested on:
- [x] Gnome
- [x] KDE
- [ ] ~Sway~ (Need to investigate this more for Sway)

Release Notes:

- Fixed blurry rendering on Wayland when using fractional scaling for
Gnome and KDE.

Co-authored-by: Julia Ryan p1n3appl3@users.noreply.github.com
Co-authored-by: Antonio Scandurra me@as-cii.com
2025-06-20 21:22:08 +05:30
Oleksiy Syvokon
b0f192ec2e agent: Do not send stale files notifications (#32974)
Removing it for two reasons:

1. We need a better implementation that doesn't hurt caching and doesn't
distracts the agent too much (see
https://github.com/zed-industries/zed/pull/32876 for more context)

2. Current insertion point of notifications doesn't play well with
Claude Thinking models (see
https://github.com/zed-industries/zed/issues/33000#issuecomment-2991709484)

I think we should get this code back in a form of a tool. But for now,
I'm dropping it to resolve recent issues.

Closes #33000

Release Notes:

- N/A
2025-06-20 16:56:49 +03:00
Piotr Osiewicz
c9e5ff21a1 docs: Update manifest keys in debugger extension docs (#33085)
This is silly and caused at least one of our users a lot of confusion.

Closes #33040

Release Notes:

- N/A
2025-06-20 15:25:53 +02:00
张小白
c02e249ecb windows: Simplify the logic of handle_get_min_max_info_msg (#33102)
Release Notes:

- N/A
2025-06-20 12:52:37 +00:00
Max Brunsfeld
a7bbbc0552 Fix handling of --diff flag (#33094)
* Restore the ability to combine --diff with other path arguments
* Restore combining --diff with --wait

There is still one defect in the current handling of `--diff`: when Zed
is already open, we'll open the diff view in your current active zed
window. It would be better to search all of the open zed windows for any
window containing the diffed paths, but implementing that is a bit
complex. Currently, the logic for *picking* an existing zed window is
coupled to the logic for opening buffers in that window. I'd like to
decouple it, but I wanted to keep this change small, so that we hotfix
it to stable without too much risk.

Release Notes:

- Fixed a bug where the `--diff` CLI flag did not work with `--wait`

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-20 13:02:28 +01:00
张小白
f8a0eb5a8c Fix capslock on windows (#33093)
The new feature doesn't work well on windows

Release Notes:

- N/A
2025-06-20 10:57:00 +00:00
Anthony Eid
d97a58b20e debugger: Refresh variable list on set variable value response (#33078)
Variable list wasn't notified when a set variable value request was
successfully. This caused the variable list and inline values to show
stale data in some cases, which this PR fixes.

Release Notes:

- debugger: Fix bug where setting a variable's value wouldn't update the
variable list or inline values
2025-06-20 07:53:32 +00:00
Anthony Eid
10f0aabec6 docs: Update development debugger guide to include Zed's debugger (#33080)
Closes #33069 

Release Notes:

- N/A
2025-06-20 07:42:42 +00:00
Danilo Leal
2624950472 agent: Fix text wrapping in the provider set up list items (#33063)
Release Notes:

- agent: Fixed text wrapping in the provider set up list items in the
settings view.
2025-06-19 18:17:56 -03:00
Finn Evers
ca3f1d624a Ensure compiled extensions work with older Zed versions (#33051)
Closes #33039

This PR fixes a bug which causes the newest versions of the Biome and
Tombi extensions to not work with older Zed versions.

The bug occurs because in #32822, the type of the debug adapter and
debug locators was changed from a Vec to a BTreeMap. However, these
fields were already introduced much earlier in Zed, which now causes the
de-serialization of the `extension.toml` to fail for older Zed versions.
Any extension compiled with the newest extension CLI bumped in
https://github.com/zed-industries/extensions/pull/2866 will not work
with older Zed versions prior to v0.191.

By adding this change and bumping the extension CLI again, this could be
prevented. On de-serialization, we would just fallback to either a Vec
for versions prior to v0.190 or a BTreeMap after. Feel free to let me
know what you think here.

Release Notes:

- N/A
2025-06-19 22:35:19 +02:00
Piotr Osiewicz
00fe195416 debugger: Move breakpoint management to the pane strip (#33062)
Closes #ISSUE

Release Notes:

- debugger: Moved "remove breakpoint" button to the top of a breakpoint
list"
2025-06-19 22:29:15 +02:00
Kirill Bulatov
bca1a9145c Make pull diagnostics remote test more robust (#33057)
Follow-up of https://github.com/zed-industries/zed/pull/33028

Due to request races, we cannot predict the amount of result_id s that
we'll get. Ensure their amount grows monotonically each time.

This time, iterations=5000 did not fail for me.

Release Notes:

- N/A
2025-06-19 19:27:47 +00:00
Danilo Leal
b89ddf3a6e agent: Improve layout shift on previous message editor (#33055)
Follow up to https://github.com/zed-industries/zed/pull/32765.

This PR creates a slot for the `message_editor::create_editor` to allow
using different values for min and max lines. In practice, the panel's
main editor now has a minimum of 4 lines, whereas the previous message
editor has just one. This makes the layout shift when clicking on a
previous message to edit it much smaller.

Release Notes:

- agent: Improved layout shift when clicking to edit a previous sent
message.
2025-06-19 15:23:33 -03:00
Danilo Leal
5c7e90d494 agent: Move focus to the panel after sending a user message edit (#33049)
Follow up to https://github.com/zed-industries/zed/pull/31611.
Closes https://github.com/zed-industries/zed/issues/33005.

Release Notes:

- agent: Fixed a bug where, after confirming editing a previous user
message while zoomed in, the focus went to the buffer and the panel got
closed.
2025-06-19 14:38:37 -03:00
Danilo Leal
61abfd5930 agent: Avoid layout shift due to the "waiting for confirmation" label (#33046)
Just a tiny, one-line change to avoid the "Waiting for Confirmation"
animated label pushing the "allow" buttons to the side.

Release Notes:

- agent: Fixed layout shift in "waiting for confirmation" state in the
terminal card.
2025-06-19 16:27:18 +00:00
Danilo Leal
ddaa8b3d02 agent: Expand disclosure click area in setting view's provider section (#33041)
Previously, you could only expand the provider item in the agent panel
settings view by clicking on the little chevron icon button. Now, you
can click on the whole title area (minus the button, when present) to do
that. Just that little bit more convenient to interact with it.

Release Notes:

- N/A
2025-06-19 12:29:54 -03:00
Oleksiy Syvokon
3b31db1b1f open_router: Avoid redundant model list downloads (#33033)
Previously, the OpenRouter models list (~412kb) was being downloaded
around 10 times during startup -- even when OpenRouter was not
configured.

This update addresses the issue by:

1. Fetching the models list only when OpenRouter settings change.
2. Skipping API calls if OpenRouter is not configured.


Release Notes:

- Avoid unnecessary requests to OpenRouter
2025-06-19 14:41:36 +00:00
Umesh Yadav
1f736ed693 copilot: Remove PromptTokensDetails from Usage struct (#33029)
Closes #33024

Release Notes:

- Removed `PromptTokensDetails` from `Usage` as Gemini no longer
supplies cached token data for copilot.
2025-06-19 17:37:58 +03:00
Cole Miller
0b228ad12c debugger: Fix issues with debugging scripts from package.json (#32995)
- [x] Pass in cwd
- [x] Use the appropriate package manager
- [x] Don't mix up package.json and composer.json

Release Notes:

- debugger: Fixed wrong arguments being passed to the DAP when debugging
scripts from package.json.
2025-06-19 14:33:24 +00:00
Umesh Yadav
e914d84f00 copilot: Fix config dir logic to support Flatpak environments (#32901)
Closes #30784

In github copilot we were not handling the config path correctly for
FLATPAK.

* Only tested on mac don't have access to other platform. But this
should work on other platform as well. It follows the similar pattern
seen in zed config path resolution.
- [x] Macos
- [ ] Linux
- [ ] Linux with Flatpak
- [ ] Windows

Release Notes:

- Fix copilot config detection for flatpack
2025-06-19 16:32:02 +02:00
Danilo Leal
ec0f2fa79a agent: Fix button ids for resetting keys in OpenAI settings (#33032)
These "Reset API Key" and "Reset API URL" button had the same ids, so
therefore, they weren't working.

Release Notes:

- N/A
2025-06-19 14:09:53 +00:00
Smit Barmase
1bd49a77e0 linux: Add title bar for rules library (#33025)
Closes #30513

- Abstract away common wrapper component to `platform_title_bar`.
- Use it in both zed and rules library.
- For rules library, keep traffic like only style for macOS, and add
custom title bar for Linux and Windows.

Release Notes:

- Added way to minimize, maximize, and close the rules library window
for Linux.
2025-06-19 18:23:09 +05:30
Kirill Bulatov
c8d49408d3 Fix pull diagnostics on the remote clients (#33028)
Also add a test.

Release Notes:

- Fixed pull diagnostics on the remote clients
2025-06-19 12:48:03 +00:00
Evan Simkowitz
e202981f0c editor: Scale minimap width to editor width (#32317) 2025-06-19 14:24:06 +03:00
张小白
dec7baeb97 Revert "client: Fix an issue where non-IP proxy URLs didn’t resolve c… (#33013)
This reverts commit bc68455320.

More bugs...

Closes #32838

Release Notes:

- N/A
2025-06-19 08:56:00 +00:00
Kirill Bulatov
2839c2e492 Fix document colors not showing on file reopen (#33009)
Closes https://github.com/zed-industries/zed/issues/32989

Release Notes:

- Fixed document colors not showing on file reopen
2025-06-19 07:02:49 +00:00
Danilo Leal
0e94ca2a1a debugger: Refine session modal design (#33004)
This PR makes all footer elements in the debugger session modal more
consistent, as well as fixes some weird UI quirks with leaking borders
and whatnot. Took the opportunity to do some light style clean up and
use `prelude::*` for UI imports.

Release Notes:

- N/A
2025-06-19 02:27:34 -03:00
Danilo Leal
804b91aa8c agent: Improve the UX around interacting with MCP servers (#32622)
Still a work in progress! Todos before merging:

- [x] Allow to delete (not just turn off) an MCP server from the panel's
settings view
- [x] Also uninstall the extension upon deleting the server (check if
the extension just provides MCPs)
- [x] Resolve repository URL again
- [x] Add a button to open the configuration modal from the panel's
settings view
- [x] Improve modal UX to install and configure a non-extension MCP

Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-18 22:52:40 +00:00
Jeff Bonhag
526faf287d docs: Ruby debug configuration should be an array (#32991)
Closes #ISSUE

Small correction for something I noticed while setting up the debugger
today.

Release Notes:

- N/A
2025-06-18 22:48:47 +00:00
Kirill Bulatov
6e64628858 Silence another backtrace-full log (#32990)
Release Notes:

- N/A
2025-06-18 22:47:48 +00:00
Danilo Leal
522d92fbe2 docs: Add instructions for how to use v0 via OpenAI (#32983)
Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <53836821+bennetbo@users.noreply.github.com>
2025-06-18 19:37:33 -03:00
Kirill Bulatov
cec19aec7b Omit outlines from the outline panel, not related to the buffer's main language (#32987)
Closes https://github.com/zed-industries/zed/issues/15122

Release Notes:

- Fixed outline panel showing extra languages' outlines
2025-06-18 22:36:00 +00:00
Bennet Bo Fenner
c34b24b5fb open_ai: Fix issues with OpenAI compatible APIs (#32982)
Ran into this while adding support for Vercel v0s models:
- The timestamp seems to be returned in Milliseconds instead of seconds
so it breaks the bounds of `created: u32`. We did not use this field
anywhere so just decided to remove it
- Sometimes the `choices` field can be empty when the last chunk comes
in because it only contains `usage`

Release Notes:

- N/A
2025-06-18 21:51:51 +00:00
Kirill Bulatov
05f944b83a Add up and down global bindings for menu::SelectNext and menu::SelectPrevious (#32981)
Closes https://github.com/zed-industries/zed/discussions/32587

Release Notes:

- Fixed Linux keybindings not having up/down keys bound for menu
navigation
2025-06-18 21:50:10 +00:00
Danilo Leal
629bd42276 agent: Add ability to change the API base URL for OpenAI via the UI (#32979)
The `api_url` setting is one that most providers already support and can
be changed via the `settings.json`. We're adding the ability to change
it via the UI for OpenAI specifically so it can be more easily connected
to v0.

Release Notes:

- agent: Added ability to change the API base URL for OpenAI via the UI

---------

Co-authored-by: Bennet Bo Fenner <53836821+bennetbo@users.noreply.github.com>
2025-06-18 18:47:43 -03:00
Sanjeev Shrestha
ab189b898d vim: Fix escape key switching back to default mode instead of normal mode (#31843)
Closes #31728

This PR introduced new setting `"helix_mode"`. Enabling which will
enable the `vim_mode` along with `helix` behavior.

This solves issue where `vim`'s `default_mode` was being used to switch
between mode instead of opening in `default_mode`.

When `helix_mode` is enabled switcing to `Normal mode` will now switch
to `HelixNormal`


Release Notes:

- Fixed - escape key not switching to normal mode when default_mode is
insert
- Added - `helix_mode` setting to enable/disable helix key bindings

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-18 21:26:12 +00:00
Bennet Bo Fenner
d2ca68bd5d copilot chat: Remove invalid assertions (#32977)
Related to #32888, but will not fix the issue. 
Turns out these assertions are wrong (Not sure if they were correct at
some point).
I tested with this code:
```
        request = LanguageModelRequest {
            messages: vec![
                LanguageModelRequestMessage {
                    role: Role::User,
                    content: vec![MessageContent::Text("Give me 10 jokes".to_string())],
                    cache: false,
                },
                LanguageModelRequestMessage {
                    role: Role::Assistant,
                    content: vec![MessageContent::Text("Sure, here are 10 jokes:".to_string())],
                    cache: false,
                },
            ],
            ..request
        };
```
The API happily accepted this and Claude proceeded to tell me 10 jokes.

Release Notes:

- N/A
2025-06-18 22:17:31 +02:00
Mikayla Maki
72a3292f8d Add a small script to make debugging the CLI easier (#32971)
Release Notes:

- N/A
2025-06-18 19:24:16 +00:00
Artem Zhurikhin
ccb4644365 docs: Fix lsp.rust-analyzer.binary config (#32967)
Document the `lsp.rust-analyzer.binary.arguments` setting (currently
incorrectly referred to as `args`)

Verify:
99215f7660/crates/extension_api/wit/since_v0.1.0/settings.rs (L24-L29)

Question: can such inconsistencies be avoided by automatically
documenting the config using a preprocessor?

Release Notes:

- N/A
2025-06-18 19:08:50 +00:00
Michael Sloan
fdd307cf7a collab: Add automatic install of minio deb or rpm to script/bootstrap (#32968)
Release Notes:

- N/A
2025-06-18 19:07:57 +00:00
Mikayla Maki
c1d0d72db9 Fix a bug where --diff wouldn't open the diff (#32962)
Release Notes:

- Fixed a bug where `zed --diff A B` wouldn't open a diff
2025-06-18 14:57:42 -04:00
Piotr Osiewicz
db99d7131e debugger: Add onboarding modal (#32961)
- **debugger: Add debugger onboarding modal (wip)**
- **woops**

Release Notes:

- debugger: Added the onboarding modal.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Julia Ryan <p1n3appl3@users.noreply.github.com>
2025-06-18 18:44:02 +00:00
Piotr Osiewicz
99215f7660 debugger: Run debug scenarios from package.json (#32958)
Release Notes:

- New session modal for a debugger will now show tasks from package.json
as debuggable scenarios

---------

Co-authored-by: Remco Smits <djsmits12@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-06-18 20:04:25 +02:00
Danilo Leal
73fee01c85 docs: Remove beta tag from Debugger (#32950)
Release Notes:

- N/A
2025-06-18 14:48:09 -03:00
Conrad Irwin
74aa227c09 Wait for source maps when setting TypeScript breakpoints (#32954)
Closes #ISSUE

Release Notes:

- debugger: Fix setting breakpoints in typescript code when debugging
compiled javascript
2025-06-18 11:12:16 -06:00
morgankrey
d0e909e58d Add Anthropic ZDR to AI improvement documentation (#32955)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-18 12:01:19 -05:00
Smit Barmase
aa9dacad28 editor: Log error instead of panic on index out of bounds for line layouts (#32953)
Closes #30191

`line_ix` should never exceed the bounds of `line_layouts`, but a panic
happens on out-of-bounds for this, which seems weird. I couldn’t
reproduce this panic at all. Since this is for displaying inline blame,
we now log an error if this occurs instead of panicking.

Release Notes:

- N/A
2025-06-18 22:16:07 +05:30
Joseph T. Lyons
48491fa487 Bump Zed to v0.193 (#32947)
Release Notes:

-N/A
2025-06-18 15:14:36 +00:00
Conrad Irwin
45b5b2e60d Diff view (#32922)
Todo:

* [x] Open diffed files as regular buffers
* [x] Update diff when buffers change
* [x] Show diffed filenames in the tab title
* [x] Investigate why syntax highlighting isn't reliably handled for old
text
* [x] remove unstage/restore buttons

Release Notes:

- Adds `zed --diff A B` to show the diff between the two files

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-06-18 14:43:23 +00:00
Cole Miller
2f52e2d285 debugger: Fix a few issues with JS debugging (#32918)
- Don't assume all located tasks come from our test runnables
- Run tests from the right working directory
- Scope forking behavior customization for jest and vitest more tightly,
to just our test runnables
- Standardize on `$PACKAGE_MANAGER exec -- $TEST_LIBRARY ...` to fix
runnables not working with npm

Release Notes:

- Debugger Beta: Fixed issues with debugging tasks from package.json and
test runnables.
2025-06-18 10:37:09 -04:00
Marshall Bowers
3e8a07f496 zed_extension_api: Release v0.6.0 (#32945)
This PR releases v0.6.0 of the Zed extension API.

Support for this version of the extension API will land in Zed v0.192.x.

Release Notes:

- N/A
2025-06-18 14:05:29 +00:00
Piotr Osiewicz
8e4031815d debugger: Show child sessions as indented and ensure they're next to the parent session (#32939)
Closes #ISSUE

Release Notes:

- debugger: Tweaked how child sessions are shown in the session list.
2025-06-18 12:50:39 +02:00
Smit Barmase
131f2857a5 editor: Improve code completion filtering to provide fewer and more accurate suggestions (#32928)
Closes #32756

- Uses `filter_text` from LSP source to filter items in completion list.
This fixes noisy lists like on typing `await` in Rust, it would suggest
`await.or`, `await.and`, etc., which are bad suggestions. Fallbacks to
label.
- Add `penalize_length` flag to fuzzy matcher, which was the default
behavior across. Now, this flag is set to `false` just for code
completion fuzzy matching. This fixes the case where if the query is
`unreac` and the completion items are `unreachable` and
`unreachable!()`, the item with a shorter length would have a larger
score than the other one, which is not right in the case of
auto-complete context. Now these two items will have the same fuzzy
score, and LSP `sort_text` will take over in finalizing its ranking.
- Updated test to be more utility based rather than example based. This
will help to iterate/verify logic faster on what's going on.

Before/After:

await: 
<img width="600" alt="before-await"
src="https://github.com/user-attachments/assets/384138dd-a90d-4942-a430-6ae15df37268"
/>
<img width="600" alt="after-await"
src="https://github.com/user-attachments/assets/d05a10fa-bae5-49bd-9fe7-9933ff215f29"
/>

iter:
<img width="600" alt="before-iter"
src="https://github.com/user-attachments/assets/6e57ffe9-007d-4b17-9cc2-d48fc0176c8e"
/>
<img width="600" alt="after-iter"
src="https://github.com/user-attachments/assets/a8577a9f-dcc8-4fd6-9ba0-b7590584ec31"
/>

opt:
<img width="600" alt="opt-before"
src="https://github.com/user-attachments/assets/d45b6c52-c9ee-4bf3-8552-d5e3fdbecbff"
/>
<img width="600" alt="opt-after"
src="https://github.com/user-attachments/assets/daac11a8-9699-48f8-b441-19fe9803848d"
/>

Release Notes:

- Improved code completion filtering to provide fewer and more accurate
suggestions.
2025-06-18 16:01:28 +05:30
Piotr Osiewicz
65067dad9e debugger: Add breakpoint list to the empty state of debug panel (#32930)
![image](https://github.com/user-attachments/assets/3c80855a-3046-42b6-a1a7-409b03cd735d)

Release Notes:

- Debugger: Added breakpoint list to the empty debug panel
2025-06-18 11:20:09 +02:00
Michael Sloan
d8eb341f9b Fix bug where prior LSP completions can be displayed after trigger char (#32927)
Bug in #31872

Closes #32774

Release Notes:

- Fixed a bug in LSP completions caching where prior completions may be
used when they should not, after typing a trigger char like `.`
2025-06-18 09:01:47 +00:00
Danilo Leal
70aab39e4f docs: Add light formatting changes to the Debugger page (#32919)
Just some tiny little formatting improvement opportunities I stumbled
upon while working on the marketing stuff for the debugger.

Release Notes:

- N/A
2025-06-18 01:02:25 -03:00
Cole Miller
bfffc293a3 debugger: Parse and highlight text with ANSI escape sequences (#32915)
Relanding #32817 with an improved approach, bugs fixed, and a test.

Release Notes:

- N/A
2025-06-17 23:39:31 -04:00
张小白
4da58188fb windows: Fix client area is treated as non-client area when window is fullscreen (#32916)
Closes #32909

Release Notes:

- N/A
2025-06-18 02:19:36 +00:00
Michael Sloan
9bdfd1e98a gpui: Fix pending keys dispatch path panic (#32891)
For me this is a panic that started occurring today in my use of Zed.
The repro is to type `ctrl-x` to start a pending key sequence and then
close the collab side panel with the mouse. The issue is that
dispatching the action based on pending keystrokes uses the same
`DispatchNodeId` as when the 1 second timer was started.
`DispatchNodeId` is not stable across frames. This also means that the
wrong `DispatchNodeId` can be used in the non-panicing case, potentially
causing the action to not occur.

The mystery here is why did this only start happening now in my use of
Zed, and why isn't it showing up in the panics dashboard / issue
reports.

Panic looks like

```
{
  "thread": "main",
  "payload": "index out of bounds: the len is 467 but the index is 1861",
  "location_data": {
    "file": "crates/gpui/src/key_dispatch.rs",
    "line": 519
  },
  "backtrace": [
    "zed::reliability::init_panic_hook::{{closure}}::he1d8257b19b16eec+155265758",
    "std::panicking::rust_panic_with_hook::h33b18b24045abff4+128544307",
    "std::panicking::begin_panic_handler::{{closure}}::hf8313cc2fd0126bc+128543530",
    "std::sys::backtrace::__rust_end_short_backtrace::h57fe07c8aea5c98a+128537145",
    "__rustc[95feac21a9532783]::rust_begin_unwind+128542669",
    "core::panicking::panic_fmt::hd54fb667be51beea+9456688",
    "core::panicking::panic_bounds_check::h1a9bf3d94de0fc80+9457170",
    "gpui::key_dispatch::DispatchTree::dispatch_path::hce77d277881569bf+73992023",
    "gpui::app::App::spawn::{{closure}}::hb1e79bbbdead3012+73687056",
    "async_task::raw::RawTask<F,T,S,M>::run::hd13f66f99bb24bbd+70694231",
    "<gpui::platform::linux::x11::client::X11Client as gpui::platform::linux::platform::LinuxClient>::run::h5a92ddaaf9a06dd1+74465138",
    "gpui::platform::linux::platform::<impl gpui::platform::Platform for P>::run::hd19ac52b2d94268e+74064525",
    "gpui::app::Application::run::hee83110c717a5af0+151862692",
    "zed::main::hca7e2265584c4139+153307630",
    "std::sys::backtrace::__rust_begin_short_backtrace::h2e04f4034c2d82c5+153146899",
    "std::rt::lang_start::{{closure}}::h91cf1ca0eeae23ae+154454121",
    "std::rt::lang_start_internal::h418648f91f5be3a1+128467809",
    "main+153326748",
    "__libc_start_call_main+25056432783818",
    "__libc_start_main_impl+25056432784011",
    "_start+12389486"
  ],
  "app_version": "0.190.6",
  "app_commit_sha": "9a2dcbbe244407fed51d61f38e4a4a59ec1cccc6",
  "release_channel": "stable",
  "target": "x86_64-unknown-linux-gnu",
  "os_name": "Linux X11",
  "os_version": "ubuntu 24.04",
  "architecture": "x86_64",
  "panicked_on": 1750185799233,
  "system_id": "abae7201-61fb-442b-922b-202071ae81c0",
  "installation_id": "69a0fb9a-11a2-4065-ad8c-b281e68525ad",
  "session_id": "bc5b5f2f-e4c3-44a8-948e-c0550a2e2ef2"
}
```

Release Notes:

- Fixed a rare panic / potential incorrect action dispatch when a
pending keysequence is applied after the 1 second timer elapsing.
2025-06-18 01:40:59 +00:00
Michael Sloan
aa1b2d74ee x11: Improve error handling (#32913)
Continuing this work from a while back in #21079, now greatly aided by
agent + sonnet 4. With this change, there are now only a few spots that
explicitly panic, though errors during initialization will panic.

Motivation was this recent user panic in `handle_event`, figured fixing
all this use of unwrap was a great use of the agent.

> called `Result::unwrap()` on an `Err` value: X11 GetProperty for
_NET_WM_STATE failed.

Release Notes:

- N/A
2025-06-18 01:40:17 +00:00
Maxim Zaks
90aa99bb14 Add Caps Lock support (#30470)
Closes #21700

Release Notes:

- Added caps lock support and show a warning if the user is entering an
SSH password with Caps Lock enabled

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: 张小白 <364772080@qq.com>
2025-06-18 00:43:33 +00:00
Julia Ryan
e47c48fd3b debugger: Add comment-preserving debug.json editing (#32896)
Release Notes:

- Re-added "Save to `debug.json`" for custom debug tasks

---------

Co-authored-by: Cole Miller <cole@zed.dev>
2025-06-17 15:51:05 -07:00
Cole Miller
2f1d25d7f3 Revert "debugger: Process ANSI color escape codes in console" (#32906)
Reverts zed-industries/zed#32817

Release Notes:
- N/A
2025-06-17 22:13:12 +00:00
Ben Kunkle
0cda28f786 Fix release notes appearing in project search (#32898)
Closes #28829

Release Notes:

- Fixed an issue where release notes would appear in project search
results when opened locally
2025-06-17 20:56:41 +00:00
Michael Sloan
a422345224 Add (flatpak) and (snap) suffixes to Zed version in system info (#32903)
Release Notes:

- N/A
2025-06-17 20:39:35 +00:00
Joseph T. Lyons
051fa06c7c Add docs for cloning extensions repository (#32897)
Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-17 16:31:39 -04:00
Ben Brandt
0191f16ebc Update Gemini Models (#32902)
Updates google_ai to use latest model information from the respective
model cards: https://ai.google.dev/gemini-api/docs/models

Release Notes:

- google: Update to latest Gemini 2.5 models
2025-06-17 20:26:27 +00:00
Conrad Irwin
3c9fe363d5 debugger: Remove feature flag (#32877)
Release Notes:

- debugger: Now available for everyone!
2025-06-17 13:56:19 -06:00
Nate Butler
8883885ecb debugger: Improve debugger panel empty state (#32889)
Before:

![CleanShot 2025-06-17 at 13 48
58@2x](https://github.com/user-attachments/assets/16ecebfa-871e-4a2d-b6a3-2178de70aaef)

After:

![CleanShot 2025-06-17 at 13 49
24@2x](https://github.com/user-attachments/assets/2d8a0444-6088-45f1-a880-0bdd0aef968e)


Release Notes:

- N/A (Beta: Improved the debugger panel when there are no currently
active sessions)
2025-06-17 20:50:46 +02:00
Alejandro Fernández Gómez
dbc4ccd95a vim: Implement [ e and ] e from vim-unimpaired (#32851)
From [this
discussion](https://github.com/zed-industries/zed/discussions/30757).

The default vim keymap already implements some of [vim-unimpaired
keymaps](https://github.com/tpope/vim-unimpaired). I thought I could add
this one as well to move lines up and down.

Since the keymaps are in a plugin and not by default in vim, this might
be out of the scope. If you feel like this is the case, just close the
PR :)


Release Notes:

- vim: Added `[ e` and `] e` key bindings to move lines up and down.
2025-06-17 12:27:27 -06:00
Bennet Bo Fenner
6223d04282 docs: Add note about enabling Copilot models in GitHub settings (#32885)
Seen this in a bunch of issues now, so hopefully this will help.

Release Notes:

- N/A
2025-06-17 17:07:48 +00:00
Kirill Bulatov
c7dad1cb19 Use more conservative settings for the minimap display (#32878)
Follow-up of https://github.com/zed-industries/zed/pull/31390

Release Notes:

- N/A
2025-06-17 16:21:45 +00:00
Kirill Bulatov
d5472bc0ad Document zed CLI (#32879)
Closes https://github.com/zed-industries/zed/issues/32646

Release Notes:

- N/A
2025-06-17 15:53:29 +00:00
Conrad Irwin
b1e59b1371 Revert "debugger: Remove feature flag"
This reverts commit 82dfa82ba7.
2025-06-17 09:28:35 -06:00
Conrad Irwin
82dfa82ba7 debugger: Remove feature flag 2025-06-17 09:28:04 -06:00
Richard Feldman
5405c2c2d3 Standardize on u64 for token counts (#32869)
Previously we were using a mix of `u32` and `usize`, e.g. `max_tokens:
usize, max_output_tokens: Option<u32>` in the same `struct`.

Although [tiktoken](https://github.com/openai/tiktoken) uses `usize`,
token counts should be consistent across targets (e.g. the same model
doesn't suddenly get a smaller context window if you're compiling for
wasm32), and these token counts could end up getting serialized using a
binary protocol, so `usize` is not the right choice for token counts.

I chose to standardize on `u64` over `u32` because we don't store many
of them (so the extra size should be insignificant) and future models
may exceed `u32::MAX` tokens.

Release Notes:

- N/A
2025-06-17 10:43:07 -04:00
Finn Evers
a391d67366 supermaven_api: Ensure downloaded Supermaven binary has executable permissions set (#32576)
Closes #32068
Closes #15653

Not entirely sure that it fixes the latter issue, but I am fairly
certain given the comments in #32068 and the available logs in the
issue.

This PR fixes an issue where the Supermaven provider would not leave the
"Initializing" stage. This happened due to the downloaded binary missing
executable permissions. The change here ensures that freshly downloaded
binaries as well as existing binaries downloaded by Zed have executable
permissions set. I decided on also adding this for the latter since
existing downloads would continue to be broken and Supermaven does not
seem to change versions often given the logs provided by users.


While I was at it, I also added a `make_file_executable` to the util
crate mirroring the method of the `zed_extensions_api` and refactored
existing usages where possible to use that method instead. This makes
the code slightly more readable in my opinion, yet adds a method to
non-unix systems that practically does nothing. I can revert this should
that be preferred.


Release Notes:

- Fixed an issue where the Supermaven completion provider would not
leave the "Initializing" stage.

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-06-17 14:39:45 +00:00
morgankrey
dd850dcf13 Add note on Opus prompt consumption (#32872)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-17 09:21:02 -05:00
feeiyu
c766f52f88 Fix diff indicators not restored when reopening remote project (#31384)
Closes #30917

Release Notes:

- Fix diff indicators not restored when reopening remote project

---------

Co-authored-by: Cole Miller <cole@zed.dev>
2025-06-17 10:07:51 -04:00
Gilles De Mey
b686fb2917 docs: Fix typo in debugger.md (#32867)
A small silly typo :)
2025-06-17 14:01:32 +00:00
Cole Miller
6c7bcfe752 Revert "Bail and signal error when the cwd of a resolved task doesn't exist" (#32866)
Reverts zed-industries/zed#32777
2025-06-17 14:01:16 +00:00
CharlesChen0823
b9dc5f9061 gpui: Bump blade (#32803)
in #30347 bump blade version, recently I found in my windows platform,
after using zed some hours, whole system UI become hang, must reboot.

So I try bump blade and then recompile, I found this problem disappear,
I really don't known why.

Release Notes:

- N/A
2025-06-17 16:56:08 +03:00
qvalentin
3eca9ef3b4 docs: Add values.yaml files to filetypes for Helm (#32369)
Release Notes:

- N/A
2025-06-17 16:51:51 +03:00
Kirill Bulatov
f46957584f Show inline previews for LSP document colors (#32816)
https://github.com/user-attachments/assets/ad0fa304-e4fb-4598-877d-c02141f35d6f

Closes https://github.com/zed-industries/zed/issues/4678

Also adds the code to support `textDocument/colorPresentation`
counterpart that serves as a resolve mechanism for the document colors.
The resolve itself is not run though, and the editor does not
accommodate color presentations in the editor yet — until a well
described use case is provided.

Use `lsp_document_colors` editor settings to alter the presentation and
turn the feature off.

Release Notes:

- Start showing inline previews for LSP document colors
2025-06-17 13:46:21 +00:00
Ben Brandt
acb0210d26 Add epoch interruption to WASM engine for cooperative yielding (#32806)
Prevent extensions from blocking async threads by enabling epoch
interruption with 100ms intervals. Extensions will yield control back to
the executor regularly during Future::poll operations.

Addresses the
[discussion](https://github.com/zed-industries/zed/discussions/24515)
that goes into depth on why this is important when enabling async
support with Wasmtime.

Release Notes:

- N/A
2025-06-17 15:43:17 +02:00
Alvaro Parker
4bbb7b5c2f Add setting for minimap on active editor only (#31390)
Release Notes:

- Add a setting to show the minimap only on the current active editor
(file)
- This can be configured on `settings.json`: 
```json
{
  "minimap": {
    "display_in": "active_editor", //  defaults to "all_editors"
  }
}

```

- The minimap won't hide if you go from an editor pane to the terminal,
the project panel, the search bar, etc. It will only hide if you go from
one editor pane to another.

Preview:


![image](https://github.com/user-attachments/assets/87b476a2-148b-497e-9e97-ea390c545c87)

Only the active editor (left) displays the minimap.
2025-06-17 16:39:49 +03:00
Piotr Osiewicz
a69ebf038a debugger: Prevent port collision when attaching to existing node debugger (#32862)
We were translating port configuration incorrectly, using it for both
attach target and debugger port.
This however meant that we were spawning a 2nd process that'd listen on
the same port as the existing debugger.

Closes #32836

Release Notes:

- debugger: Fixed issues with auto-translated Visual Studio Code debug
configs for attaching to existing node debugger instances.
2025-06-17 13:22:32 +00:00
Piotr Osiewicz
336c49b10d debuggers: Mark processId as optional field in Delve Attach configurations (#32856)
Closes #32849

Release Notes:

- Fixed overly strict validation of Go debugging configurations.
2025-06-17 11:18:37 +00:00
Umesh Yadav
ed4b29f80c language_models: Improve token counting for providers (#32853)
We push the usage data whenever we receive it from the provider to make
sure the counting is correct after the turn has ended.

- [x] Ollama 
- [x] Copilot 
- [x] Mistral 
- [x] OpenRouter 
- [x] LMStudio

Put all the changes into a single PR open to move these to separate PR
if that makes the review and testing easier.

Release Notes:

- N/A
2025-06-17 10:46:29 +00:00
Piotr Osiewicz
d4c9522da7 debugger: Do not query threads when session is still building (#32852)
This should silence a noisy log we see whenever a debug session is
started:
`2025-06-17T12:06:12+02:00 ERROR [project] no adapter running to send
request: ThreadsCommand`

Closes #ISSUE

Release Notes:

- Fixed debugger logs getting clobbered with internal logs about Threads
Command whenever a new debug session is created.
2025-06-17 10:36:46 +00:00
Umesh Yadav
4b88090cca language_models: Add images support to LMStudio provider (#32741)
Tested with gemma3:4b
LMStudio: beta version 0.3.17

Release Notes:

- Add images support to LMStudio provider
2025-06-17 12:14:44 +02:00
Piotr Osiewicz
6ad9a66cf9 extensions: Add "Debug Adapters" category to the extension store (#32845)
Closes #ISSUE

Release Notes:

- N/A
2025-06-17 12:09:08 +02:00
Umesh Yadav
b13144eb1f copilot: Allow enterprise to sign in and use copilot (#32296)
This addresses:
https://github.com/zed-industries/zed/pull/32248#issuecomment-2952060834.

This PR address two main things one allowing enterprise users to use
copilot chat and completion while also introducing the new way to handle
copilot url specific their subscription. Simplifying the UX around the
github copilot and removes the burden of users figuring out what url to
use for their subscription.

- [x] Pass enterprise_uri to copilot lsp so that it can redirect users
to their enterprise server. Ref:
https://github.com/github/copilot-language-server-release#configuration-management
- [x] Remove the old ui and config language_models.copilot which allowed
users to specify their copilot_chat specific endpoint. We now derive
that automatically using token endpoint for copilot so that we can send
the requests to specific copilot endpoint for depending upon the url
returned by copilot server.
- [x] Tested this for checking the both enterprise and non-enterprise
flow work. Thanks to @theherk for the help to debug and test it.
- [ ] Udpdate the zed.dev/docs to refelect how to setup enterprise
copilot.

What this doesn't do at the moment:

* Currently zed doesn't allow to have two seperate accounts as the token
used in chat is same as the one generated by lsp. After this changes
also this behaviour remains same and users can't have both enterprise
and personal copilot installed.

P.S: Might need to do some bit of code cleanup and other things but
overall I felt this PR was ready for atleast first pass of review to
gather feedback around the implementation and code itself.


Release Notes:

- Add enterprise support for GitHub copilot

---------

Signed-off-by: Umesh Yadav <git@umesh.dev>
2025-06-17 11:36:53 +02:00
Ben Brandt
c4355d2905 Fix MCP settings migration continually adding the same key (#32848)
Release Notes:

- N/A
2025-06-17 09:32:08 +00:00
Michael Sloan
2f3acb6185 Fix panic when editor::OpenSelectionsInMultibuffer only has pending selection (#32842)
On the panics dashboard, saw this panic of `There must be at least one
selection` in `open_locations_in_multibuffer`. Only seems to have
happened once in the past month.

Fix is to include the pending selection. Since `selections.all()` cannot
provide anchor selections, added `selections.all_anchors()` which only
really does any work if there is a pending selection.

Also fixes a corner case in jump-to-definitions where if the definition
is `HoverLink::InlayHint` and the `compute_target_location` fails for
all definitions it could potentially also trigger this case (and return
`Navigated::Yes` instead of `Navigated::No`

Release Notes:

- N/A
2025-06-17 08:35:14 +00:00
Piotr Osiewicz
0e794fa0ac extensions: Yet another PR for debugger touchups (#32822)
We'll now clean up DAP locators for unloaded extensions and load schemas
proper

I can now load a custom Ruby extensions with all bells and whistles and
use it as my debugger.

Release Notes:

- N/A
2025-06-17 07:34:55 +00:00
Michael Sloan
d92d52b508 Attempt to log error instead of crash in bracket highlighting (#32837)
Crashes look like:

```
Panic `offset 632 is greater than the snapshot.len() 631` on thread 0 (com.apple.main-thread)

<multi_buffer::MultiBufferSnapshot>::innermost_enclosing_bracket_ranges::<usize>
editor::highlight_matching_bracket::refresh_matching_bracket_highlights
<gpui::app::App>::update_window_id::<bool, <gpui::app::context::Context<editor::Editor>>::subscribe_in<multi_buffer::MultiBuffer, multi_buffer::Event, <editor::Editor>::on_buffer_event>::{closure#0}::{closure#0}>::{closure#0}
<gpui::app::context::Context<editor::Editor>>::subscribe_in::<multi_buffer::MultiBuffer, multi_buffer::Event, <editor::Editor>::on_buffer_event>::{closure#0}
<gpui::app::App>::flush_effects
<project::lsp_store::LocalLspStore>::format_buffer_locally::{closure#0}
<project::lsp_store::LspStore>::format::{closure#1}::{closure#0}::<i32>
```

Though `format_buffer_locally` is not always present. Both issue reports
mention usage of the agent. I suspect this is somehow a result of agent
format-on-save combined with the user's cursor being at the end of the
buffer as it's getting edited by the agent.

The offsets are always off-by-one in the error, so at first I thought
the issue was the condition `head < snapshot.buffer_snapshot.len()`
before setting `tail` to be `head + 1`, but an offset equal to len is
valid. Seems like to get a `to_offset` crash, `head` must be greater
than `len`. Which is quite weird, a selection's offset should never be
out of bounds.

Since this code is just about highlighting brackets, this PR logs an
error instead of crashing in the `head > len` case.

Closes #32732, #32171

Release Notes:

- N/A
2025-06-17 07:26:58 +00:00
Conrad Irwin
109651e6e9 debugger: Fix connections over SSH (#32834)
Before this change, we would see "connection reset" when sending the
initialize
request over SSH in the case that the debug adapter was slow to boot.

(Although we'd have successfully created a connection to the local SSH
port,
trying to read/write from it would not work until the remote end of the
connection had been established)

Fixes  #32575

Release Notes:

- debugger: Fix connecting to a Python debugger over SSH
2025-06-17 06:48:17 +00:00
Joseph T. Lyons
baf4abe101 Correct variable name in project type detection (#32835)
Release Notes:

- N/A
2025-06-17 06:29:44 +00:00
Michael Sloan
2539d57ac7 wayland: Avoid reloading cursor theme on every cursor style change (#32832)
Release Notes:

- N/A
2025-06-17 04:27:02 +00:00
Michael Sloan
c95e2a2f1d linux: Add mouse cursor icon name synonyms (#32820)
Most of the default icon sets on Ubuntu do not use the names that were
there. To fix, using the icon synonyms from the chromium source. This
will probably fix some of the linux mouse cursor issues tracked in
#26141

Also adds a note in the load failure logs mentioning that misconfigured
`XCURSOR_PATH` may be the issue. I ran into this because [the alacritty
snap incorrectly sets
XCURSOR_PATH](https://github.com/snapcrafters/alacritty/issues/21).

On X11 also adds:

* Caching of load errors to log once for missing cursor icons.

* Fallback on default cursor icon. This way if there was a transition
from a non-default icon to a missing icon it doesn't get stuck showing
the non-default icon.

Leaving release notes blank as I have other mouse cursor fixes and would
prefer to just have one entry in the release notes.

Release Notes:

- N/A
2025-06-17 03:39:10 +00:00
Michael Sloan
dfa7ed55be collab: Update Stripe customer email before checkout (#32669)
Release Notes:

- N/A
2025-06-16 19:55:27 -06:00
Conrad Irwin
783412fa1d debugger: Don't spawn unnecessary process (#32827)
Before this change, when spawning a child session we'd launch an extra
node process that would immediately die because it couldn't listen on
the debugger port

Release Notes:

- N/A
2025-06-16 19:08:56 -06:00
Kirill Bulatov
6d96f8be8e Use a proper snapshot version when resolving for utf16 points (#32815)
Release Notes:

- Fixed a panic when merging pull and (newer) push diagnostics

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2025-06-17 01:57:30 +03:00
Danilo Leal
69e84c0c48 agent: Scroll to bottom after submitting a new message (#32819)
This is a follow up to my original attempt
https://github.com/zed-industries/zed/pull/30878 and to the PR that
eventually reverted parts of it because it broke stuff
https://github.com/zed-industries/zed/pull/31295. This new approach
attaches the `scroll_to_bottom` feature to the `chat` function, which is
triggered when the `Chat` action is dispatched by the "send" icon
buttons. With that, and from my testing, the thread doesn't forcefully
scroll as new messages are added, which was the regression I had
introduced.

Release Notes:

- agent: The panel nows scrolls to the bottom after submitting a new
message, allowing to see it more easily.
2025-06-16 19:07:29 -03:00
Cole Miller
ffc6218349 debugger: Process ANSI color escape codes in console (#32817)
- [x] foreground highlights
- [x] background highlights
- [x] advertise support in DAP capabilities

Closes #31372

Release Notes:

- Debugger Beta: added basic support for highlighting in the console
based on ANSI escape codes.
2025-06-16 17:39:53 -04:00
Peter Tripp
1f457169ba Windows tests on self-hosted runners (#29764)
Windows self-hosted runners

Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Junkui Zhang <364772080@qq.com>
2025-06-16 17:29:36 -04:00
Joseph T. Lyons
701fa4daa8 Reduce allocations on project type detection (#32818)
Release Notes:

- N/A
2025-06-16 21:06:16 +00:00
Cole Miller
7fb8ae0024 debugger: Make the remove button easier to click for breakpoint list entries (#32772)
Closes #31574 

Move this button a bit to the left so it doesn't get blocked by the
hitbox of the scrollbar.

Also makes the list entries a bit thicker vertically so that the button
can be `XSmall` instead of `Indicator`-sized again.

Release Notes:

- Debugger Beta: fixed a layout issue that made it hard to click the
remove (`X`) button for entries in the breakpoint list.
2025-06-16 17:06:09 -04:00
Cole Miller
22a2ff4f12 Bail and signal error when the cwd of a resolved task doesn't exist (#32777)
Closes #32688

Release Notes:

- Fixed tasks (including build tasks for debug configurations) silently
using `/` as a working directory when the specified `cwd` didn't exist.
2025-06-16 16:59:49 -04:00
Piotr Osiewicz
0f0ff40c6d extension: Another batch of updates for DAP extension API (#32809)
Closes #ISSUE

Release Notes:

- N/A
2025-06-16 21:34:05 +02:00
Umesh Yadav
4383fee3c1 assistant_tools: Enable diff-fenced edit parser for all Gemini models (#32812)
I saw recently we added diff-fenced edit parser which improves the
overall edit performance of gemini models in this PR: #32737. The idea
is to enable it to all the models which has gemini as their id as this
will help copilot and openrouter provider as they seem to aggregate all
these models under one umbrella. I thought about adding a new method in
LanguageModel as vendor_name() which returns the underlying actual model
provider name but felt like a too early abstraction for a method to be
used at one place.

Release Notes:

- N/A
2025-06-16 22:01:55 +03:00
Richard Feldman
cfbc2d0972 Don't spawn Anthropic telemetry event when API key is missing (#32813)
Minor refactor that I'm extracting from a branch because it can stand
alone.

- Now we no longer spawn an executor for `report_anthropic_event` if
it's just going to immediately fail due to API key being missing
- `report_anthropic_event` now takes a `String` API key instead of
`Option<String>` and the error reporting if the key is missing has been
moved to the caller.
- `report_anthropic_event` is longer coupled to `AnthropicError`,
because all it ever did was generate an `AnthropicEvent::Other`, which
in turn was then only used for `log_err` - so, can just be an
`anyhow::Result`.

Release Notes:

- N/A
2025-06-16 14:58:37 -04:00
Bennet Bo Fenner
6e04b9ef65 inline assistant: Do not dismiss while generating when hitting enter (#32810)
Closes #32798

Release Notes:

- Fixed an issue where the inline assistant would be dismissed when
hitting enter while generating code
2025-06-16 17:12:55 +00:00
Michael Sloan
baad66c740 wayland: Hopefully fix a panic recenty added in #32784 (#32808)
Release Notes:

- N/A
2025-06-16 16:57:42 +00:00
Smit Barmase
c7de817bf9 editor: Fix code action not visible until mouse move or buffer interaction (#32804)
Closes #32796

Regressed since https://github.com/zed-industries/zed/pull/32408. Fixed
in same way as other related PRs
https://github.com/zed-industries/zed/pull/32683,
https://github.com/zed-industries/zed/pull/32692,
https://github.com/zed-industries/zed/pull/32795.

Release Notes:

- Fixed issue where code actions are not visible until the mouse is
moved when the `cursor_blink` setting is `false`.
2025-06-16 22:25:25 +05:30
Joseph T. Lyons
29cdef1ec8 Ensure we scan worktrees again for project types when none were previously found (#32805)
This PR doesn't change behavior, but simply adds a case to a test to
make sure we continue to scan any given worktree for project types, if
one was not found prior.

Also updates `detect_project_types`'s return type to an `Option` so we
can differentiate in tests between the case where we skip a worktree
that previously had project type events sent and the case where we
simply found no project types to report.

Release Notes:

- N/A
2025-06-16 16:20:46 +00:00
Oleksiy Syvokon
6df4c537b9 agent: Less disruptive changed file notification (#31693)
When the user edits one of the tracked files, we used to notify the
agent by inserting a user message at the end of the thread. This was
causing a few problems:
- The agent would stop doing its work and start reading changed files
- The agent would write something like, "Thank you for letting me know
about these changed files."

This fix contains two parts:
1. Changing the prompt to indicate this is a service message
2. Moving the message higher in the conversation thread

This works, but it slightly hurts caching.

We may consider making these notification messages stick in history,
trading context tokens count for the cache.

This might be related to #30906

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-06-16 18:45:24 +03:00
Conrad Irwin
92addb005a Pass project environment to runInTerminal requests (#32720)
Closes #ISSUE

Release Notes:

- debugger: Pass environment to run in terminal requests
2025-06-16 09:34:50 -06:00
Bennet Bo Fenner
d7db4d4e0a agent: Rework context server settings (#32793)
This changes the way context servers are organised. We now store a
`source` which indicates if the MCP server is configured manually or
managed by an extension.

Release Notes:

- N/A

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-16 15:31:31 +00:00
Danilo Leal
c35f22dde0 agent: Enable accepting and rejecting individual file changes from message editor (#32801)
Previously, you could only accept and reject all changes from a specific
file by checking that file out on a tab. Now, you can do that via the
message editor changes summary bar. The buttons appear as you hover over
the file item in the accordion.

Here's what it looks like:

<img
src="https://github.com/user-attachments/assets/8c0843d3-9bf1-4588-8b42-4cd8d0798a68"
width="500" />

Release Notes:

- agent: Enable accepting and rejecting individual file changes from
message editor.
2025-06-16 11:28:37 -03:00
Oleksiy Syvokon
fceba6c795 edit_file: Add diff-fenced output format (#32737)
This format is enabled for Google models as they seem to prefer it.
A relevant unit eval's pass rate has increased from 0.77 to 0.98.

Diff-fenced format looks like this (markdown fences and a line hint are
optional):

```diff
<<<<<<< SEARCH line=42
...
=======
...
>>>>>>> REPLACE
```

Release Notes:

- Agent: Gemini models now use the diff-fenced format when making edits
2025-06-16 14:28:18 +00:00
Piotr Osiewicz
8df6ce2aac extension: Update DAP extension API (#32448)
- DAP schemas will be stored in `debug_adapters_schemas` subdirectory in
extension work dir.
- Added Debug Config integration and such.

Release Notes:

- N/A
2025-06-16 16:25:32 +02:00
Oleksiy Syvokon
41e9f3148c gemini: Send thought signatures back to API (#32064)
This is a follow-up to:
- #31925 
- #31902

Release Notes:

- Support Gemini thought signatures
2025-06-16 14:24:44 +00:00
Finn Evers
b749d9302f editor: Ensure mouse cursor is shown again on mouse move (#32795)
Closes #32787
Follow-up to #27519 and #32408 

This PR fixes an issue where the mouse cursor would stay hidden after
typing in the editor.

Before #32408, we would rerender the editor on every mouse move. Now, we
(correctly) only do this if a rerender is actually required. This caused
a small regression for hiding the mouse cursor though: Due to the view
now being cached, we do not neccessarily update the mouse cursor style
so it is shown again. The boolean is updated but the view is not,
resulting in the cursor style being kept until another action is
performed. This is an issue with both Stable and Preview (due to some
other changes, the issue is slightly worse on Preview though, see
https://github.com/zed-industries/zed/pull/32596#issuecomment-2969258800
and
https://github.com/zed-industries/zed/pull/32596#issuecomment-2969357248
for some more context).

This PR ensures that the cursor is shown again by scheduling a redraw of
the editor whenever the boolean is updated.

The change should not cause any performance regressions: In most cases
where we want to hide the mouse, the editor is about to be rerendered
anyway, hence this would not change anything. For cases where we want to
show the cursor again, this ensures that we actually end up doing so by
rerendering the editor once.

Release Notes:

- Fixed an issue where the mouse cursor would sometimes stay hidden
after typing in editors with the `hide_mouse` setting enabled.
2025-06-16 19:49:08 +05:30
Danilo Leal
d29e94b11c Fix the component preview page scroll (#32797)
Plus some other tiny visual adjustments. I've been using the Component
Preview a lot this past week and vertical scroll wasn't working, which
was a big bummer!

Release Notes:

- N/A
2025-06-16 10:18:03 -03:00
Joseph T. Lyons
c72cdfd843 Do not report same project type multiple times for same worktree (#32785)
Follow-up to: https://github.com/zed-industries/zed/pull/32769

Now that the project type identification telemetry can look for multiple
files in order to identify the project type, we need to make sure we
still only send a single event for a given worktree.

Also, simplifies project detection telemetry code

Release Notes:

- N/A
2025-06-16 06:09:43 +00:00
Michael Sloan
4733f188da linux: Only call on_keyboard_layout_change when layout name changes (#32784)
Release Notes:

- N/A
2025-06-16 05:35:19 +00:00
Joseph T. Lyons
1660438a2a Add tests for project discovery telemetry (#32782)
Release Notes:

- N/A
2025-06-16 05:17:22 +00:00
Smit Barmase
ef61ebe049 editor: Support both cursor and mouse based columnar selection (#32779)
Closes #32584

In https://github.com/zed-industries/zed/pull/31888, we changed the
default `opt + shift` behavior to start columnar selection from the
mouse position (Sublime-like behavior) instead of from the existing
selection head (VSCode-like behavior).

It turns out there is a use case for creating columnar selection from an
existing selection head as well, such as creating a consecutive
multi-cursor from existing selection head with just a click instead of
dragging.

This PR brings back columnar selection from the selection head via `opt
+ shift`, while retaining columnar selection from the mouse position,
which is now mapped to the new `cmd + shift` binding.

Note: If you like to swap the binding, you can use [existing multi
cursor modifier
setting](https://zed.dev/docs/configuring-zed?highlight=multi_cursor_modifier#multi-cursor-modifier).

Release Notes:

- Added `cmd + shift` to start columnar selection from the mouse
position.
- Restored `opt + shift` to create columnar selection (or consecutive
multi-cursor on click) from the selection head.
2025-06-16 10:13:25 +05:30
Michael Sloan
6150c26bd2 X11: Fix handling of key remapping (#32780)
Closes #27384

I wrote #32771 before seeing #27384, and hoped that change would fix it.
It didn't because `XkbSelectNotify` wants a mask of which types of
`XkbMapNotify` to deliver, and otherwise won't send them.

I noticed quite a few events are sent just for remapping a single
keycode, so I updated the event loop to deduplicate these events (since
the handler does not attempt to apply them and instead just re-queries
keyboard info).

Also adds a missing call of `keyboard_layout_change` on `XkbMapNotify`
and `XkbNewKeyboardNotify`.

Release Notes:

- x11: Fixed handling of key remapping occurring while Zed is running
(e.g. xmodmap)
2025-06-16 04:31:48 +00:00
Michael Sloan
3bed5b767f x11: Halt periodic refresh for windows that aren't visible (#32775)
This adds handling of UnmapNotify / MapNotify / VisibilityNotify to
track whether windows are visible. When hidden, the refresh loop is
halted until visible again. Often these refreshes were just checking if
the window is dirty, but I believe it sometimes did a full re-render for
things that change without user interaction (cursor blink, animations).

This also changes handling of Expose events to set a flag indicating the
next refresh should have `require_presentation: true`.

Release Notes:

- x11: No longer refreshes windows that aren't visible.
2025-06-16 02:09:43 +00:00
Michael Sloan
3595dbb155 x11: Fix keymap reload to happen on XkbMapNotify not MapNotify (#32771)
Keyboard hot reloading was added in #15059, but also reloaded this on
MapNotify instead of XkbMapNotify, so it wasn't handling keymap change
events and was instead reloading when windows are mapped (typically when
they go from a minimized / other workspace state to being visible).

Release Notes:

- N/A
2025-06-15 23:33:44 +00:00
Joseph T. Lyons
61771e7e4a Improve code for unsaved tab titles (#32770)
Just fixing a couple of minor things that bugged when revisiting this
code.

Release Notes:

- N/A
2025-06-15 21:18:09 +00:00
Joseph T. Lyons
fd7a133d00 Include .NET project identification in telemetry (#32769)
With Windows support on the horizon this year, we'll want to know how
much .NET dev happens in Zed, so we can know how to prioritize bug fixes
or enhancements to the dev experience in this framework.

Release Notes:

- N/A
2025-06-15 17:00:34 -04:00
Michael Sloan
3810227759 Misc nitpicks, changes too small / unrelated to be in other PRs (#32768)
Release Notes:

- N/A
2025-06-15 19:51:04 +00:00
Jason Garber
02da4669f3 terminal: Fix file paths links with URL escapes not being clickable (#31830)
For #31827

# URL Decoding Fix for Terminal File Path Clicking


## Discussion

This change does not allow for paths that literally have `%XX` inside of
them. If any such paths exist, they will fail to ctrl+click. A larger
change would be needed to handle that.

## Problem

In the terminal, you could ctrl+click file paths to open them in the
editor, but this didn't work when the paths contained URL-encoded
characters (percent-encoded sequences like `%CE%BB` for Greek letter λ).

### Example Issue
- This worked: `dashboardλ.mts:3:8`
- This didn't work: `dashboard%CE%BB.mts:3:8`

The URL-encoded form `%CE%BB` represents the Greek letter λ (lambda),
but the terminal wasn't decoding these sequences before trying to open
the files.

## Solution

Added URL decoding functionality to the terminal path detection system:

1. **Added urlencoding dependency** to `crates/terminal/Cargo.toml`
2. **Created decode_file_path function** in
`crates/terminal/src/terminal.rs` that:
   - Attempts to decode URL-encoded paths using `urlencoding::decode()`
   - Falls back to the original string if decoding fails
   - Handles malformed encodings gracefully
3. **Applied decoding to PathLikeTarget creation** for both:
   - Regular file paths detected by word regex
   - File:// URLs that are treated as paths


## Code Changes

### New Function
```rust
/// Decodes URL-encoded file paths to handle cases where terminal output contains
/// percent-encoded characters (e.g., %CE%BB for λ).
/// Falls back to the original string if decoding fails.
fn decode_file_path(path: &str) -> String {
    urlencoding::decode(path)
        .map(|decoded| decoded.into_owned())
        .unwrap_or_else(|_| path.to_string())
}
```

### Modified PathLikeTarget Creation
The function is now called when creating `PathLikeTarget` instances:
- For file:// URLs: `decode_file_path(path)`
- For regular paths: `decode_file_path(&maybe_url_or_path)`

## Testing

Added comprehensive test coverage in `test_decode_file_path()` that
verifies:
- Normal paths remain unchanged
- URL-encoded characters are properly decoded (λ, spaces, slashes)
- Paths with line numbers work correctly
- Invalid encodings fall back gracefully
- Mixed encoding scenarios work

## Impact

This fix enables ctrl+click functionality for file paths containing
non-ASCII characters that appear URL-encoded in terminal output, making
the feature work consistently with tools that output percent-encoded
file paths.

The change is backward compatible - all existing functionality continues
to work unchanged, and the fix only activates when URL-encoded sequences
are detected.


Release Notes:

* File paths printed in the terminal that have `%XX` escape sequences
will now be properly decoded so that ctrl+click will open them
2025-06-15 19:20:01 +00:00
Michael Sloan
c0717bc613 Fix block cursor using placeholder text even when it's not displayed (#32766)
The condition for displaying the first char of the placeholder text in
the block cursor was `cursor_column == 0`. This meant that it was
displayed on the first column even when the placeholder text is not
being displayed. Instead this now shows it only when
`snapshot.is_empty()` - the same condition used to determine whether to
show placeholder text.

In the case of vim mode + agent panel message editor, this meant that if
you did `shift-enter` to make a newline and then `escape` to enter
normal mode, the block cursor would show `M` inside it as that's the
first character of the placeholder text "Message the agent - @ to
include context"

Release Notes:

- N/A
2025-06-15 19:09:18 +00:00
Ozan Ozbeker
f052a9e28c Fixed typo in SQL language documentation (#32764)
Fixed typo in bullet 2 (line 17) referring to `shfmt` instead of
`sql-formatter`
2025-06-15 19:05:52 +00:00
Michael Sloan
681c88d4e7 Fix clicking in to agent message editor and tighten up vertical spacing (#32765)
* Adds `min_lines` to `EditorMode::AutoHeight` and use `min_lines: 4` in
agent message editor. This makes it so that clicks in the blank space
below the first line of the editor also focus it, instead of needing to
click the very first line.

* Removes the div wrapping the editor, as it was only there to set
`min_h_16()`. This also tightens up the min space given to the editor -
before it was not evenly dividing the number of lines.

* Further tightens up vertical spacing by using `gap_1` instead of
`gap_4` between editor and controls below

At 4 line min height (after on the left, before on the right):


![image](https://github.com/user-attachments/assets/e8eefb1b-9ea3-4f98-ad55-25f95760d61f)

At 5 lines, one more than min height (after on the left, before on the
right):


![image](https://github.com/user-attachments/assets/a6ba737c-6a56-4343-a55a-d264f2a06377)

Release Notes:

- Agent: Fixed clicking to focus the message editor to also work for
clicks below the last line.
2025-06-15 18:45:44 +00:00
Max Brunsfeld
a994666888 Include full abs paths of worktrees in system prompt (#32725)
Some MCP servers expose tools that take absolute paths as arguments. To
interact with these, the agent needs to know the absolute path to the
project directories, not just their names. This PR changes the system
prompt to include the full path to each worktree, and updates some tool
descriptions to reflect this.

Todo:

* [x] Run evals, make sure assistant still understand how to specify
paths for tools, now that we include abs paths in the system prompt.

Release Notes:

- Improved the agent's ability to use MPC tools that require absolute
paths to files and directories in the project.

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-15 15:45:26 +02:00
Piotr Osiewicz
0433b8859d debugger: Fix module list getting queried when not shown (#32761)
Closes #ISSUE

Release Notes:

- N/A
2025-06-15 13:25:33 +02:00
Michael Sloan
a5ceef35fa Improve logic for finding VSCode / Cursor settings files (#32721)
* Fixes a bug where for Cursor, `config_dir()` (Zed's config dir) was
being used instead of `dirs::config_dir` (`~/.config` /
`$XDG_CONFIG_HOME`).

* Adds support for windows, before it was using the user profile folder
+ `/.config` which is incorrect.

* Now looks using a variety of product names - `["Code", "Code - OSS",
"Code Dev", "Code - OSS Dev", "code-oss-dev", "VSCodium"]`.

* Now shows settings path that was read before confirming import.

Including this path in the confirmation modal is a bit ugly (making it
link-styled and clickable would be nice), but I think it's better to
include it now that it is selecting the first match of a list of
candidate paths:


![image](https://github.com/user-attachments/assets/ceada4c2-96a6-4a84-a188-a1d93521ab26)

Release Notes:

- Added more settings file locations to check for VS Code / Cursor
settings import.
2025-06-14 21:39:54 -06:00
Behrang Saeedzadeh
afa70034d5 docs: Fix a typo in text threads docs (#32417) 2025-06-14 10:43:28 +00:00
Oleksiy Syvokon
5d293ae8ac edit_file: Let agent specify locations of edit chunks (#32628)
These changes help the agent edit files when `<old_text>` matches more
than one location.

First, the agent can specify an optional `<old_text line=XX>` parameter.
When this is provided and multiple matches exist, we use this hint to
identify the best match.

Second, when there is ambiguity in matches, we now return the agent a
more helpful message listing the line numbers of all possible matches.

Together, these changes should reduce the number of misplaced edits and
agent confusion.

I have ensured the LLM Worker works with these prompt changes.


Release Notes:

- Agent: Improved locating edits
2025-06-14 09:59:30 +03:00
Anthony Eid
e8d495806f debugger: Select first stack frame with valid path (#32724)
This PR addresses an issue where we could get a stack frame list and
automatically select a stack frame that didn't have a valid path.
Causing a failure on Zed's end to select/update the active debug line.
The fix for this is selecting the first non-subtle stack frame that has
the optional path parameter.

We also made subtle stack frames move into their own collapsable list as
well.

Release Notes:

- debugger: Fix edge case where hitting a breakpoint wouldn't take you
to the active debug line

Co-authored-by: Remco Smits <djsmits12@gmail.com>
2025-06-13 22:35:07 +00:00
Kirill Bulatov
baefec3849 Move r-a status into the activity indicator (#32726)
Deals with the noisy pop-ups by moving r-a **status messages** into the
activity indicator, where the rest of the LSP statuses is displayed.


https://github.com/user-attachments/assets/e16fb374-d34d-4d03-b5f1-41f71f61c7c7


https://github.com/user-attachments/assets/67c611aa-8b73-4adb-a76d-b0c8ce3e2f94

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-13 22:33:02 +00:00
Umesh Yadav
1edaeebae5 languages: Bump ESLint LSP server to version 3.0.10 (#32717)
Testing project: https://github.com/imumesh18/zed-testing

I have attached the logs from the eslint lsp server. Confirming that we
are indeed calling `textDocument/diagnostic` and we are receiving the
error response as well from lsp. Please check the attached log for it.

<details>
<summary>Server Log</summary>

```

// Send:
{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"":{"validate":"on","rulesCustomizations":[],"run":"onType","nodePath":null,"workingDirectory":{"mode":"auto"},"workspaceFolder":{"uri":"/Users/umesh/code/zed-testing","name":"zed-testing"},"problems":{},"codeActionOnSave":{"enable":true},"codeAction":{"disableRuleComment":{"enable":true,"location":"separateLine"},"showDocumentation":{"enable":true}},"useFlatConfig":true}}}}

// Receive:
{"jsonrpc":"2.0","id":6,"method":"workspace/diagnostic/refresh"}

// Send:
{"jsonrpc":"2.0","id":6,"result":null}

// Send:
{"jsonrpc":"2.0","id":8,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"},"range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}},"context":{"diagnostics":[],"only":["quickfix","source.fixAll.eslint"]}}}

// Receive:
{"jsonrpc":"2.0","id":7,"method":"workspace/configuration","params":{"items":[{"scopeUri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","section":""}]}}

// Send:
{"jsonrpc":"2.0","id":7,"result":[{"validate":"on","rulesCustomizations":[],"run":"onType","nodePath":null,"workingDirectory":{"mode":"auto"},"workspaceFolder":{"uri":"/Users/umesh/code/zed-testing","name":"zed-testing"},"problems":{},"codeActionOnSave":{"enable":true},"codeAction":{"disableRuleComment":{"enable":true,"location":"separateLine"},"showDocumentation":{"enable":true}},"useFlatConfig":true}]}

// Receive:
{"jsonrpc":"2.0","id":8,"result":[]}

// Send:
{"jsonrpc":"2.0","id":9,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"},"range":{"start":{"line":12,"character":20},"end":{"line":12,"character":20}},"context":{"diagnostics":[],"only":["quickfix","source.fixAll.eslint"]}}}

// Receive:
{"jsonrpc":"2.0","id":9,"result":[]}

// Send:
{"jsonrpc":"2.0","id":10,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"},"range":{"start":{"line":11,"character":37},"end":{"line":11,"character":37}},"context":{"diagnostics":[],"only":["quickfix","source.fixAll.eslint"]}}}

// Receive:
{"jsonrpc":"2.0","id":10,"result":[]}

// Send:
{"jsonrpc":"2.0","method":"textDocument/didSave","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"}}}

// Send:
{"jsonrpc":"2.0","id":11,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"},"range":{"start":{"line":3,"character":0},"end":{"line":14,"character":0}},"context":{"diagnostics":[{"range":{"start":{"line":4,"character":8},"end":{"line":4,"character":22}},"severity":1,"code":"@typescript-eslint/no-unused-vars","source":"eslint","message":"'unusedVariable' is assigned a value but never used."},{"range":{"start":{"line":4,"character":8},"end":{"line":4,"character":22}},"severity":4,"code":6133,"source":"ts","message":"'unusedVariable' is declared but its value is never read."},{"range":{"start":{"line":7,"character":6},"end":{"line":7,"character":14}},"severity":1,"code":2367,"source":"ts","message":"This comparison appears to be unintentional because the types 'number' and 'string' have no overlap."},{"range":{"start":{"line":13,"character":2},"end":{"line":13,"character":52}},"severity":1,"code":"no-var","source":"eslint","message":"Unexpected var, use let or const instead."},{"range":{"start":{"line":13,"character":6},"end":{"line":13,"character":22}},"severity":1,"code":"@typescript-eslint/no-unused-vars","source":"eslint","message":"'oldStyleVariable' is assigned a value but never used."},{"range":{"start":{"line":13,"character":6},"end":{"line":13,"character":22}},"severity":4,"code":6133,"source":"ts","message":"'oldStyleVariable' is declared but its value is never read."}],"only":["quickfix","source.fixAll.eslint"]}}}

// Receive:
{"jsonrpc":"2.0","id":11,"result":[{"title":"Disable @typescript-eslint/no-unused-vars for this line","command":{"title":"Disable @typescript-eslint/no-unused-vars for this line","command":"eslint.applyDisableLine","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":0,"ruleId":"@typescript-eslint/no-unused-vars"}]},"kind":"quickfix"},{"title":"Disable @typescript-eslint/no-unused-vars for the entire file","command":{"title":"Disable @typescript-eslint/no-unused-vars for the entire file","command":"eslint.applyDisableFile","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":0,"ruleId":"@typescript-eslint/no-unused-vars"}]},"kind":"quickfix"},{"title":"Show documentation for @typescript-eslint/no-unused-vars","command":{"title":"Show documentation for @typescript-eslint/no-unused-vars","command":"eslint.openRuleDoc","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":0,"ruleId":"@typescript-eslint/no-unused-vars"}]},"kind":"quickfix"},{"title":"Fix this no-var problem","command":{"title":"Fix this no-var problem","command":"eslint.applySingleFix","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":0,"ruleId":"no-var"}]},"kind":"quickfix","diagnostics":[{"message":"Unexpected var, use let or const instead.","severity":1,"source":"eslint","range":{"start":{"line":13,"character":2},"end":{"line":13,"character":52}},"code":"no-var","codeDescription":{"href":"https://eslint.org/docs/latest/rules/no-var"}}],"isPreferred":true},{"title":"Disable no-var for this line","command":{"title":"Disable no-var for this line","command":"eslint.applyDisableLine","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":0,"ruleId":"no-var"}]},"kind":"quickfix"},{"title":"Disable no-var for the entire file","command":{"title":"Disable no-var for the entire file","command":"eslint.applyDisableFile","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":0,"ruleId":"no-var"}]},"kind":"quickfix"},{"title":"Show documentation for no-var","command":{"title":"Show documentation for no-var","command":"eslint.openRuleDoc","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":0,"ruleId":"no-var"}]},"kind":"quickfix"},{"title":"Fix all auto-fixable problems","command":{"title":"Fix all auto-fixable problems","command":"eslint.applyAllFixes","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":0}]},"kind":"quickfix"}]}

// Send:
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":1},"contentChanges":[{"range":{"start":{"line":3,"character":2},"end":{"line":3,"character":2}},"text":"// "},{"range":{"start":{"line":4,"character":2},"end":{"line":4,"character":2}},"text":"// "},{"range":{"start":{"line":6,"character":2},"end":{"line":6,"character":2}},"text":"// "},{"range":{"start":{"line":7,"character":2},"end":{"line":7,"character":2}},"text":"// "},{"range":{"start":{"line":8,"character":2},"end":{"line":8,"character":2}},"text":"// "},{"range":{"start":{"line":9,"character":2},"end":{"line":9,"character":2}},"text":"// "},{"range":{"start":{"line":11,"character":2},"end":{"line":11,"character":2}},"text":"// "},{"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":2}},"text":"// "},{"range":{"start":{"line":13,"character":2},"end":{"line":13,"character":2}},"text":"// "}]}}

// Send:
{"jsonrpc":"2.0","id":12,"method":"textDocument/diagnostic","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"},"identifier":"eslint","previousResultId":null}}

// Receive:
{"jsonrpc":"2.0","method":"eslint/status","params":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","state":1,"validationTime":18}}

// Receive:
{"jsonrpc":"2.0","id":12,"result":{"kind":"full","items":[]}}

// Send:
{"jsonrpc":"2.0","id":13,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"},"range":{"start":{"line":3,"character":0},"end":{"line":14,"character":0}},"context":{"diagnostics":[{"range":{"start":{"line":4,"character":11},"end":{"line":4,"character":25}},"severity":4,"code":6133,"source":"ts","message":"'unusedVariable' is declared but its value is never read."},{"range":{"start":{"line":7,"character":9},"end":{"line":7,"character":17}},"severity":1,"code":2367,"source":"ts","message":"This comparison appears to be unintentional because the types 'number' and 'string' have no overlap."},{"range":{"start":{"line":13,"character":9},"end":{"line":13,"character":25}},"severity":4,"code":6133,"source":"ts","message":"'oldStyleVariable' is declared but its value is never read."}],"only":["quickfix","source.fixAll.eslint"]}}}

// Receive:
{"jsonrpc":"2.0","id":13,"result":[]}

// Send:
{"jsonrpc":"2.0","method":"textDocument/didSave","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"}}}

// Send:
{"jsonrpc":"2.0","id":14,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"},"range":{"start":{"line":13,"character":7},"end":{"line":13,"character":7}},"context":{"diagnostics":[],"only":["quickfix","source.fixAll.eslint"]}}}

// Receive:
{"jsonrpc":"2.0","id":14,"result":[]}

// Send:
{"jsonrpc":"2.0","id":15,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"},"range":{"start":{"line":3,"character":0},"end":{"line":14,"character":0}},"context":{"diagnostics":[],"only":["quickfix","source.fixAll.eslint"]}}}

// Receive:
{"jsonrpc":"2.0","id":15,"result":[]}

// Send:
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":2},"contentChanges":[{"range":{"start":{"line":3,"character":2},"end":{"line":3,"character":5}},"text":""},{"range":{"start":{"line":4,"character":2},"end":{"line":4,"character":5}},"text":""},{"range":{"start":{"line":6,"character":2},"end":{"line":6,"character":5}},"text":""},{"range":{"start":{"line":7,"character":2},"end":{"line":7,"character":5}},"text":""},{"range":{"start":{"line":8,"character":2},"end":{"line":8,"character":5}},"text":""},{"range":{"start":{"line":9,"character":2},"end":{"line":9,"character":5}},"text":""},{"range":{"start":{"line":11,"character":2},"end":{"line":11,"character":5}},"text":""},{"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":5}},"text":""},{"range":{"start":{"line":13,"character":2},"end":{"line":13,"character":5}},"text":""}]}}

// Send:
{"jsonrpc":"2.0","id":16,"method":"textDocument/diagnostic","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"},"identifier":"eslint","previousResultId":null}}

// Receive:
{"jsonrpc":"2.0","method":"eslint/status","params":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","state":1,"validationTime":15}}

// Receive:
{"jsonrpc":"2.0","id":16,"result":{"kind":"full","items":[{"message":"'unusedVariable' is assigned a value but never used.","severity":1,"source":"eslint","range":{"start":{"line":4,"character":8},"end":{"line":4,"character":22}},"code":"@typescript-eslint/no-unused-vars","codeDescription":{"href":"https://typescript-eslint.io/rules/no-unused-vars"}},{"message":"Unexpected var, use let or const instead.","severity":1,"source":"eslint","range":{"start":{"line":13,"character":2},"end":{"line":13,"character":52}},"code":"no-var","codeDescription":{"href":"https://eslint.org/docs/latest/rules/no-var"}},{"message":"'oldStyleVariable' is assigned a value but never used.","severity":1,"source":"eslint","range":{"start":{"line":13,"character":6},"end":{"line":13,"character":22}},"code":"@typescript-eslint/no-unused-vars","codeDescription":{"href":"https://typescript-eslint.io/rules/no-unused-vars"}}]}}

// Send:
{"jsonrpc":"2.0","id":17,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"},"range":{"start":{"line":3,"character":0},"end":{"line":14,"character":0}},"context":{"diagnostics":[{"range":{"start":{"line":4,"character":8},"end":{"line":4,"character":22}},"severity":1,"code":"@typescript-eslint/no-unused-vars","source":"eslint","message":"'unusedVariable' is assigned a value but never used."},{"range":{"start":{"line":13,"character":2},"end":{"line":13,"character":52}},"severity":1,"code":"no-var","source":"eslint","message":"Unexpected var, use let or const instead."},{"range":{"start":{"line":13,"character":6},"end":{"line":13,"character":22}},"severity":1,"code":"@typescript-eslint/no-unused-vars","source":"eslint","message":"'oldStyleVariable' is assigned a value but never used."}],"only":["quickfix","source.fixAll.eslint"]}}}

// Receive:
{"jsonrpc":"2.0","id":17,"result":[{"title":"Disable @typescript-eslint/no-unused-vars for this line","command":{"title":"Disable @typescript-eslint/no-unused-vars for this line","command":"eslint.applyDisableLine","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":2,"ruleId":"@typescript-eslint/no-unused-vars"}]},"kind":"quickfix"},{"title":"Disable @typescript-eslint/no-unused-vars for the entire file","command":{"title":"Disable @typescript-eslint/no-unused-vars for the entire file","command":"eslint.applyDisableFile","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":2,"ruleId":"@typescript-eslint/no-unused-vars"}]},"kind":"quickfix"},{"title":"Show documentation for @typescript-eslint/no-unused-vars","command":{"title":"Show documentation for @typescript-eslint/no-unused-vars","command":"eslint.openRuleDoc","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":2,"ruleId":"@typescript-eslint/no-unused-vars"}]},"kind":"quickfix"},{"title":"Fix this no-var problem","command":{"title":"Fix this no-var problem","command":"eslint.applySingleFix","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":2,"ruleId":"no-var"}]},"kind":"quickfix","diagnostics":[{"message":"Unexpected var, use let or const instead.","severity":1,"source":"eslint","range":{"start":{"line":13,"character":2},"end":{"line":13,"character":52}},"code":"no-var","codeDescription":{"href":"https://eslint.org/docs/latest/rules/no-var"}}],"isPreferred":true},{"title":"Disable no-var for this line","command":{"title":"Disable no-var for this line","command":"eslint.applyDisableLine","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":2,"ruleId":"no-var"}]},"kind":"quickfix"},{"title":"Disable no-var for the entire file","command":{"title":"Disable no-var for the entire file","command":"eslint.applyDisableFile","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":2,"ruleId":"no-var"}]},"kind":"quickfix"},{"title":"Show documentation for no-var","command":{"title":"Show documentation for no-var","command":"eslint.openRuleDoc","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":2,"ruleId":"no-var"}]},"kind":"quickfix"},{"title":"Fix all auto-fixable problems","command":{"title":"Fix all auto-fixable problems","command":"eslint.applyAllFixes","arguments":[{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx","version":2}]},"kind":"quickfix"}]}

// Send:
{"jsonrpc":"2.0","method":"textDocument/didSave","params":{"textDocument":{"uri":"file:///Users/umesh/code/zed-testing/src/app/page.tsx"}}}
```

</details>

Release Notes:

- Bump ESLint LSP server to version 3.0.10
2025-06-14 01:20:38 +03:00
Cole Miller
dc475dd292 debugger: Use the right adapter for type: node-terminal (#32723)
Closes #32690 

Release Notes:

- Debugger Beta: fixed `node-terminal` JavaScript configurations from
launch.json not working.
2025-06-13 22:01:08 +00:00
Anthony Eid
feef68bec7 debugger: Add support for label presentation hints for stack frames (#32719)
Release Notes:

- debugger: Add support for `Label` stack frame kinds

Co-authored-by: Remco Smits <djsmits12@gmail.com>
2025-06-13 21:37:03 +00:00
Anthony Eid
6650be8e0f debugger: Improve logging of debug sessions (#32718)
This PR fixes a common issue where a debug session won't start up and
user's weren't able to get any logs from the debug session. We now do
these three things

1. We know store a history of debug sessions
2. We added a new option to only look at the initialization sequence 
3. We default to selecting a session in dap log view in stead of none

Release Notes:

- debugger: Add history to debug session logging

---------

Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Remco Smits <djsmits12@gmail.com>
2025-06-13 20:56:23 +00:00
Kirill Bulatov
4425d58d72 Revert "Hide the notifications panel by default (#32705)" (#32707)
This reverts commit aabce921e3.

Release Notes:

- N/A
2025-06-13 19:00:01 +00:00
Kirill Bulatov
29fa6d1a4d Regroup result_ids (#32710)
Do not cleanup the result_id data on buffer drop, as this data is meant
to be stored between buffer reopens.
Use `LanguageServerId` as keys as this way it's simpler to access the
data.

Follow-up of https://github.com/zed-industries/zed/pull/32403

Release Notes:

- N/A
2025-06-13 18:56:10 +00:00
Kirill Bulatov
aabce921e3 Hide the notifications panel by default (#32705)
Release Notes:

- The notifications panel is hidden by default now

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-13 18:17:04 +00:00
Kyle Kelley
2948e18e0c Fix shell environment loading for Zed installations with spaces in path (#32702)
Follow-up to: https://github.com/zed-industries/zed/pull/32637

When Zed is installed in a path containing spaces (e.g.,
`/Applications/Zed Nightly.app/Contents/MacOS/zed`), environment
variable loading fails and leaves this in the Zed log:

```
login shell exited with exit status: 127. stdout: "", stderr: "Nightly.app/Contents/MacOS/zed --printenv >&0: /Applications/Zed: No such file or directory"
```

This was not part a release (only broke in nightly), but fixes it the
issue in any case when the path to the Zed.app bundle has a space (e.g.
"Zed Nightly.app")

Release Notes:

- N/A
2025-06-13 18:12:31 +00:00
Michael Sloan
1c135f99ef Update documentation about account email addresses (#32703)
Release Notes:

- N/A
2025-06-13 17:51:26 +00:00
Piotr Osiewicz
4370628e30 debugger: Focus child sessions if parent has never stopped (#32693)
Closes #ISSUE

Release Notes:

- When debugging JavaScript, Zed will now preselect child sessions by
default.
2025-06-13 19:17:51 +02:00
Piotr Osiewicz
e59fb2e16a copilot: Remove an unwrap in URI parsing code (#32698)
Closes #32630

Release Notes:

- Fixed a potential crash when opening active modules in a debugger
session (with Copilot enabled).
2025-06-13 19:17:35 +02:00
Peter Tripp
cf129aa19d Silence failed auto update checks (#32696)
Don't immediately show "auto-update failed" errors in the status bar
when launching zed offline or when a periodic auto-update check is
triggered when you are offline. Manual checks (via menu or action) or
errors after the initial version check succeeds (download/extraction
failure) are unchanged.

Supersedes: https://github.com/zed-industries/zed/pull/32643

Release Notes:

- N/A

Co-authored-by: Joseph T. Lyons <JosephTLyons@gmail.com>
2025-06-13 13:11:02 -04:00
Yaroslav Pietukhov
628f91dd96 Disallow running CLI with root privileges (#32583)
In #31331, I made a change that prevents Zed from running with root
privileges, but I forgot about the CLI.
So if you run the CLI without the `--foreground` flag, it just freezes
without any messages. This PR fixes that.

Release Notes:

- N/A
2025-06-13 13:09:32 -04:00
Peter Tripp
3fb28f695f ci: Require check_docs (#32470)
Previously, broken `check_docs` would not prevent merge/automerge.
Introduced in:
- https://github.com/zed-industries/zed/pull/31073

Release Notes:

- N/A
2025-06-13 13:06:42 -04:00
Smit Barmase
2aa79a022e editor: Fix diff hunk controls not shown until buffer interaction (#32692)
Similar to https://github.com/zed-industries/zed/pull/32683, checking
mouse hovered in `mouse_move` instead of `prepaint` for diff hunk
controls.

Release Notes:

- Fixed issue where diff hunk controls were not visible on mouse hover
when `cursor_blink` is `false`.
2025-06-13 22:29:49 +05:30
Peter Tripp
71dbe88459 Replace environment variable parser with zed --printenv outputting JSON (#32637)
Closes: https://github.com/zed-industries/zed/issues/32445
Follow-up to: https://github.com/zed-industries/zed/pull/31799

Release Notes:

- Improved handling of environment variables

---------

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2025-06-13 11:49:15 -04:00
Danilo Leal
d280c95d91 agent: Suggest turning burn mode on when close to the context window limit (#32691)
Previously, upon getting close to reaching the context window, we'd just
suggest creating a new thread using the summary of the current one. Now,
we also suggest turning burn mode on as an alternative action to solve
the context window problem.

Release Notes:

- agent: Added a suggestion to turn burn mode on when getting close to
the context window limit.
2025-06-13 11:41:17 -03:00
Marshall Bowers
fcf5042007 anthropic: Reorder Model variants in descending order (#32689)
This PR reorders the `Model` variants in the `anthropic` crate in
descending order.

Newer/more powerful models at the top -> older/less powerful models at
the bottom.

Release Notes:

- N/A
2025-06-13 14:01:32 +00:00
Marshall Bowers
cb9beb86bf anthropic: Refactor a bit (#32685)
This PR applies some refactorings made in our other repos to this
version of the `anthropic` crate.

Release Notes:

- N/A
2025-06-13 13:34:23 +00:00
Danilo Leal
29f3e62850 ui: Refactor the Callout component (#32684)
What motivated me to refactor this component was the fact that I wanted
a new variant to allow having _two CTAs_ instead of just one. This
variant should work with either a single or multiline description. But,
given we were using a `Callout::single_line` and `Callout::multi_line`
API, I'd then need to have both `Callout::single_line_one_button` and
`Callout::single_line_two_buttons` type of variants, which just points
to a combinatorial problem.

With this refactor, the Callout now follows the same structure of the
Banner component, where it's all `Callout::new` and every method is
passed as if they were props in a React component, allowing for a more
flexible design where you can customize button styles. Also made it
slightly more robust for wrapping and removed the top border as that
should be defined by the place it is being used in.

Release Notes:

- N/A
2025-06-13 10:03:32 -03:00
Smit Barmase
aa1cb9c1e1 editor: Fix inline blame show/hide not working until buffer interaction (#32683)
We recently fixed the issue of `cx.notify` on every mouse move event
https://github.com/zed-industries/zed/pull/32408. As this perf bug was
there for a long time, we made some not-optimal choices for checking
things like if the mouse is hovering over an element in the prepaint
phase rather than the `mouse_move` listener.

After the mentioned fix, it regressed these code paths as prepaint is
not being called for every other frame, and hence the mouse hovering
logic never triggers. This bug is directly noticeable when the
"cursor_blink" setting is turned off, which notifies the editor on every
second.

This PR fixes that for git inline blame popover by moving logic to
show/hide in `mouse_move` instead of prepaint phase. `cx.notify` is only
get called only when popover is shown or hidden.

Release Notes:

- Fixed git inline blame not correctly showing in Editor on hover when
`cursor_blink` is `false`.
2025-06-13 17:53:13 +05:30
Piotr Osiewicz
d5b8c21a75 debugger: Mark DapLocator::create_scenario as an async function (#32680)
Paves way for locators in extensions.

Release Notes:

- N/A
2025-06-13 13:19:03 +02:00
Piotr Osiewicz
2c491d3a66 debugger: Fix regression in rendering of stack frame list (#32682)
Closes #ISSUE

Release Notes:

- N/A
2025-06-13 11:17:03 +00:00
Ben Brandt
9427833fdf Distinguish between missing models and registries in error messages (#32678)
Consolidates configuration error handling by moving the error type and
logic from assistant_context_editor to language_model::registry.

The registry now provides a single method to check for configuration
errors, making the error handling more consistent across the agent panel
and context editor.

This also now checks if the issue is that we don't have any providers,
or if we just can't find the model.

Previously, an incorrect model name showed up as having no providers,
which is very confusing.

Release Notes:

- N/A
2025-06-13 10:31:52 +00:00
张小白
fc7c106b2a chore: Use workspace tiny_http (#32672)
Release Notes:

- N/A
2025-06-13 08:56:10 +00:00
Michael Sloan
83cd1d2545 Improve logging of prettier errors (#32665)
In particular, seems like the error message and the message sent to
prettier were mixed up before

Release Notes:

- N/A
2025-06-13 07:26:06 +00:00
张小白
bc68455320 client: Fix an issue where non-IP proxy URLs didn’t resolve correctly (#32664)
If the proxy URL is in the form of `example.com` instead of a raw IP
address, and `example.com` isn't a well-known domain, then the default
URL resolution can fail.

The test setup:

A Linux machine runs a CoreDNS server with a custom entry: `10.254.7.38
example.com`. On a Windows machine, if the proxy URL is set to
`example.com`, the resolved address does **not** end up being
`10.254.7.38`.

Using `hickory_resolver` for more advanced DNS resolution fixes this
issue.


Release Notes:

- Fixed proxy URL resolution when using custom DNS entries.
2025-06-13 15:17:49 +08:00
Michael Sloan
20793fc251 Autoformat prettier_server.js (#32661)
Formatted via format-on-save in Zed with prettier version 3.5.3

Release Notes:

- N/A
2025-06-13 06:58:57 +00:00
Smit Barmase
cb573172a3 project_panel: Allow collapse all from workspace context (#32660)
Closes #4385

Allow action `project_panel::CollapseAllEntries` to trigger from
workspace context without focusing the project panel.

Release Notes:

- Added a way to collapse all entries in the Project Panel without
having to focus it. This can be done by using the
`project_panel::CollapseAllEntries` action.
2025-06-13 12:26:29 +05:30
Michael Sloan
9cc82212b5 Remove separator! macro and make path! handle relative paths (#32527)
Release Notes:

- N/A
2025-06-13 06:32:29 +00:00
Michael Sloan
babf846ef9 Fix newlines in language server logs when switching log types + misc (#32659)
Mistake in #31863 where the stored log entries no longer had a format
that could simply have `\n` added after each entry.

Also fixes a potential crash in the long line folding logic if unicode
was in the logs - introduced in #22996.

Also updates the log line truncation logic to never exceed the
pre-allocated capacity

Release Notes:

- N/A
2025-06-13 06:22:07 +00:00
Dino
9a6e8a19b5 vim: Add horizontal scrolling support in vim mode (#32558)
Release Notes:

- Added initial support for both `z l` and `z h` in vim mode

These changes relate to #17219 but don't yet close the issue, as this
Pull Request is simply adding support for horizontal scrolling in vim
mode and actually moving the cursor to the correct column in the current
row will be handled in a different Pull Request.

Some notes on these changes:

- 2 new default keybindings added to vim's keymap
    - `z l` which triggers the new `vim::ColumnRight` action
    - `z h` which triggers the new `vim::ColumnLeft` action
- Introduced a new `ScrollAmount` variant, `ScrollAmount::Column(f32)`
to represent horizontal scrolling
- Replaced usage of `em_width` with `em_advance` to actually scroll by
the width of the cursor, instead of the width of the character

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-13 00:17:15 -06:00
Conrad Irwin
f63ae4388d debugger: Show errors loading stack (#32658)
- **TEMP**
- **Show errors loading stack frames**
- **Stop cloning every DAP response unnecessarily**

Closes #ISSUE

Release Notes:

- debugger: Show errors loading stack frames.

<img width="1840" alt="Screenshot 2025-06-12 at 23 53 42"
src="https://github.com/user-attachments/assets/310d3046-f34c-4964-acef-f9742441c9db"
/>
2025-06-13 00:05:57 -06:00
Cole Miller
bcd79331b9 debugger: Fix running JS tests when worktree root and package root do not coincide (#32644)
- construct the correct path to the test library based on the location
of package.json
- run scripts from the package root where they were defined
- run tests in the directory of the defining file

Release Notes:

- Debugger Beta: fixed running JS tests when the worktree root is above
the location of package.json.

---------

Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-13 05:03:07 +00:00
Conrad Irwin
9166e66519 Disable nav history in vim scrolls (#32656)
Reland of #30345 to fix merge conflicts with the new skip-completions
option

Fixes #29431
Fixes #17592

Release Notes:

- vim: Scrolls are no longer added to the jumplist
2025-06-12 22:18:22 -06:00
Conrad Irwin
0fe35f440d vim: Exit temporary normal after scroll (#32653)
Closes #ISSUE

Release Notes:

- vim: Exit temporary normal after scrolling
2025-06-12 22:07:32 -06:00
Cole Miller
f227c2ff0c debugger: Add an action to copy debuggee info and initialization args (#32647)
Release Notes:

- Debugger Beta: added the `dev: copy debug adapter arguments` action to
help troubleshoot debug configurations.
2025-06-12 21:38:25 -04:00
Michael Sloan
1078f929aa Update names of collab auth functions to clarify behavior (#32648)
Release Notes:

- N/A
2025-06-13 00:35:18 +00:00
Kirill Bulatov
cef0c415f6 Don't autosave unmodified buffers (#32626)
Closes https://github.com/zed-industries/zed/issues/12091

Proper redo of https://github.com/zed-industries/zed/pull/32603

Release Notes:

- Fixed formatting effects not triggered when saving unmodified
singleton buffers

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Cole Miller <m@cole-miller.net>
2025-06-12 22:12:14 +00:00
Piotr Osiewicz
cd018da1ad docs: Fix headings in debugger docs (#32641)
Reported by calebmeyer on Discord.
Closes #ISSUE

Release Notes:

- N/A
2025-06-12 23:42:30 +02:00
Anthony Eid
d725371c42 debugger: Pass --nocapture to cargo tests when building debug tasks with locator (#32633)
Release Notes:

- Add --nocapture as a default argument when debugging rust tests

Co-authored-by: Cole Miller <m@cole-miller.net>
2025-06-12 17:30:36 -04:00
Michael Sloan
7d708c14e4 Use git config --global user.email for email address in automatic Co-authored-by (#32624)
Release Notes:

- Automatic population of `Co-authored-by` now uses `git config --global
user.email`

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Conrad <conrad@zed.dev>
2025-06-12 19:39:08 +00:00
Kirill Bulatov
e56a027bea Store result_ids per language server (#32631)
Follow-up of https://github.com/zed-industries/zed/pull/32403


Release Notes:

- N/A
2025-06-12 19:36:08 +00:00
Piotr Osiewicz
1e244f4aff debugger: Do not swallow port property when converting launch.json (#32621)
with JavaScript scenarios.

Closes #32187

Release Notes:

- Fixed `port` property not being respected in debug scenarios converted
from VSC's launch.json

Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
2025-06-12 18:05:48 +00:00
Gabe Shahbazian
c13be165cd Add git: open modified files action (#32347)
Ported over a vscode/cursor command that I like using : )

Release Notes:

- Added "open modified files" command
2025-06-12 13:56:10 -04:00
Kirill Bulatov
0ee6a90912 Update invisibles' default.json docs (#32601)
Follow-up of https://github.com/zed-industries/zed/pull/32329

Release Notes:

- N/A
2025-06-12 17:38:39 +00:00
Jason Lee
4236c9ed0e gpui: Fix data_table example overflow subtracting crash error (#32617)
Release Notes:

- N/A

Just make a simple change to avoid crash.

```
thread 'main' panicked at library\std\src\time.rs:436:33:
overflow when subtracting duration from instant
stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library\std\src\panicking.rs:697
   1: core::panicking::panic_fmt
             at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library\core\src\panicking.rs:75
   2: core::panicking::panic_display
             at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library\core\src\panicking.rs:261
   3: core::option::expect_failed
             at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library\core\src\option.rs:2024
   4: core::option::Option::expect
             at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library\core\src\option.rs:933
   5: std::time::impl$3::sub
             at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library\std\src\time.rs:436
   6: data_table::Quote::random
             at .\crates\gpui\examples\data_table.rs:54
```
2025-06-12 19:52:37 +03:00
Piotr Osiewicz
5923ba4992 debugger: Allow use of externally-managed Delve for Go debugging (#32613)
Closes #ISSUE

Release Notes:

- Go debug scenarios can now use an externally-managed Delve instance.
Use `tcp_connection` in your debug scenario definition to provide
adapter's address.
2025-06-12 15:27:44 +00:00
Jason Lee
bb5a763ef7 title_bar: Use theme colors for window controls on Windows (#32400)
Release Notes:

- N/A

----

Fix Windows title bar window button color by use theme colors.

The `ghost_element_hover` and `ghost_element_active` is same color as
the Buttons in title bar.

## Before


https://github.com/user-attachments/assets/e38a4f9c-7e5c-4d50-b578-608baebaf03c

## After


https://github.com/user-attachments/assets/a32e4d88-1e64-407e-a601-716ca7584111
2025-06-12 11:09:05 -04:00
Smit Barmase
f54129461f editor: Improve completions sort order for Tailwind classes (#32612)
Closes #32532

Before:
<img width="479" alt="Image"
src="https://github.com/user-attachments/assets/5eeee2b8-7f0f-43c1-bbde-65db8ae0dce1"
/>

After:
<img width="580" alt="image"
src="https://github.com/user-attachments/assets/6c6f30ea-e92e-41f7-ba5e-b1616652d367"
/>


Release Notes:

- Improved auto-complete suggestions for Tailwind classes.
2025-06-12 20:23:16 +05:30
Bennet Bo Fenner
dc8eb55b00 agent: Scroll to first diff hunk when clicking on edit tool card header (#32611)
Release Notes:

- agent: Clicking on header of an edit file card now takes you to the
first modified hunk
2025-06-12 14:15:52 +00:00
Danilo Leal
f14a923952 agent: Allow to see the review button while generating (#32610)
I think we mistakenly added an early return for the review multibuffer
button if `has_pending_edit_tool_uses` is true. It is totally fine _to
access_ the review multibuffer while that's happening. It's another
thing to _accept and reject_ changes while they're still ongoing!

Release Notes:

- agent: Fixed access to the review multibuffer from the agent panel.
2025-06-12 11:04:15 -03:00
Vladimir Varankin
47af72bfe1 project_panel: Don't add extra margin-left to file name labels (#32602)
In this PR I want to improve the UI of the project panel's files tree.
Currently, the project panel renders an extra gap between file icons and
the file name, making it visually unpleasant. The changes in the PR
remove the gap, bringing the labels closer to their icon:

_Before/After_

<img width="647" alt="zed-before-after"
src="https://github.com/user-attachments/assets/d815c075-f1f8-4a77-a3b3-d1275988a5dc"
/>

Also, this extra gap between the icon and the label seems inconsistent
with how other similar components, which are based on the `ListItem`,
are used.

Release Notes:

- Fixed an extra gap between the file icon and the file name label in
the project panel.
2025-06-12 09:38:15 -04:00
Ben Brandt
c7ee489c07 agent: Don't stop following after edits (#32606)
This is reverting a change from #32071 which caused agent following to
stop after the file was edited.

This will reintroduce the behavior that the keyboard shortcuts don't
work until the model is done generating, but we will revisit that
afterwards.

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>

Release Notes:

- agent: Fix a regression in agent following behavior after file edits

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-06-12 13:00:00 +00:00
Kirill Bulatov
5e07d0f6de Trigger formatting effects when saving unmodified singleton buffers (#32603)
Closes https://github.com/zed-industries/zed/issues/12091

Use `"save_non_dirty_buffers": false` editor settings to disable this
behavior.

Release Notes:

- Fixed formatting effects not triggered when saving unmodified
singleton buffers
2025-06-12 12:25:32 +00:00
Finn Evers
7ecad2bef9 gpui: Fix window cursor style flickering (#32596)
Closes #32592
Follow-up to #31965 

This PR fixes the cursor style flickering on Linux systems. The issue
arose since the window cursor style was not reused anymore for
subsequent frames after the changes in #31965. This works on MacOS for
hiding cursors, since they are hidden until the next mouse movement
occurs, which is not the case for other systems.

This PR re-adds this whilst keeping the fixes applied in #31965. We now
determine the first cursor style that is hovered and continue searching
for a cursor style that should be applied globally. If one to apply for
the whole window is found, we return that cursor style early instead.

Alternatively, we could store window cursor style request in a vector
similar to normal cursor styles. That would require more memory in
exchange for fewer checks which cursor style to apply. I preferred the
approach here, though, but can change this should the other method be
preferred.

CC @smitbarmase since you assigned yourself that issue.

Release Notes:

- Fixed an issue where the cursor would flicker whilst typing.
2025-06-12 15:24:44 +05:30
Ben Brandt
2d4e427b45 OpenAI cleanups (#32597)
Release Notes:

- openai: Remove support for deprecated o1-preview and o1-mini models 
- openai: Support streaming for o1 model
2025-06-12 08:55:48 +00:00
Anthony Eid
4e4856f2c1 debugger: Handle session restart failures instead of hanging (#32595)
I also enabled the `Restart` action even for sessions that don't support
restarting because we have a restart fallback now.

Closes #31408

Release Notes:

- Fix bug where a debugger session would never be shutdown on a failed
restart attempt
2025-06-12 04:29:34 -04:00
vipex
d1ca6db756 pane: Apply max_tabs change immediately (#32447)
Closes #32217

Follow up of https://github.com/zed-industries/zed/pull/32301, sorry
about the messy rebase in the previous PR.

Release Notes: 
- Fixed `max_tabs` setting not applying immediately when changed
 
TODO: 
- [x] Fix the off-by-one bug (currently closing one more tab than the
max_tabs setting) while perserving "+1 Tab Allowance" feature.
- [x] Investigate Double Invocation of `settings_changed`
- [x] Write test that:
  - Sets max_tabs to `n`
  - Opens `n` buffers
  - Changes max_tabs to `n-1`
  - Asserts we have exactly `n-1` buffers remaining

---------

Co-authored-by: Joseph T. Lyons <JosephTLyons@gmail.com>
2025-06-12 04:21:00 -04:00
Anthony Eid
c7ee635853 Deploy code runner menu from correct display row (#32594)
This fixes a bug introduced in #32579 where the code runner menu would
be deployed from the most recent cursor position instead of the row that
the runner icon was rendered on.

Release Notes:

- N/A
2025-06-12 07:22:58 +00:00
vipex
106b98fbcb workspace: Persist centered layout across project switches (#32299)
Closes #32297

Changes:

- Added restoration of `workspace.centered_layout` from
`serialized.centered_layout`
- Ensures the centered layout state persists across project switches

Release Notes:

- Fixed centered layout not persisting when switching between projects
2025-06-12 05:51:25 +00:00
fantacell
1a321b51df Rewrite documentation comment for right movement (#32547)
I don't think the documentation comment is saying the right thing. This
version is more similar to the comment of the left movement function.

Release Notes:

- N/A
2025-06-12 08:39:17 +03:00
Max Mynter
242af863f5 Use ch-width (0) instead of em-width (m) for gutter width calculation (#32548)
Closes #21860

Release Notes:

- Added `ch_width` and `ch_advance` function alongside their `em_*`
counterparts
- Use `ch_*` version to calculate gutter layouts
- Update a stale comment from changes in #31959

The ch units refer to the width of the number `0` whereas em is the
width of `m` and the actual font size (e.g. 16px means 16 px width of
`m`).

This change has no effect for monospaced fonts but can be drastic for
proportional ones as seen below for "Zed Plex Sans" with a
`"min_line_number_width" = 4`.

<img width="726" alt="Screenshot 2025-06-11 at 15 47 35"
src="https://github.com/user-attachments/assets/aa73f4d4-32bc-42cf-a9f6-7e25fee68c9a"
/>
2025-06-12 08:28:04 +03:00
Julia Ryan
f428d54b74 task: Don't show VSCode worktree tasks when Zed ones exist (#32590)
Fixes #23110

Similar to #32589, we may eventually want to merge instead of making
these lists mutually exclusive.

Release Notes:

- N/A
2025-06-12 01:24:19 +00:00
Julia Ryan
3850da6bee debugger: Don't show VSCode worktree tasks when Zed ones exist (#32589)
Fixes #31699

Eventually we might want to merge the lists and deduplicate based on the
command and args that it's running. For now we'll just use the presence
of _any_ worktree local zed debug tasks to disable all VSCode ones.

Release Notes:

- N/A
2025-06-11 18:12:15 -07:00
Smit Barmase
13ee78c0b4 editor: Add delay for selection drag to prevent accidental drag over attempt for new selection (#32586)
- Add `300ms` delay for it to consider it as selection drag instead of
an attempt to make a new selection.
- Add cursor icon while dragging the selection.

This is same as what chromium does:
https://chromium.googlesource.com/chromium/blink/+/master/Source/core/input/EventHandler.cpp#142

Release Notes:

- Fixed issue where you accidentally end up dragging the selection where
intent was to make a new one instead. To drag selection now, you need to
hold just a little longer before dragging.
2025-06-12 06:07:20 +05:30
Piotr Osiewicz
04223f304b debugger: Fix DebugAdapterDelegate::worktree_root always using the first visible worktree (#32585)
Closes #32577

Release Notes:

- Fixed debugger malfunctioning when using ZED_WORKTREE_ROOT env
variable in multi-worktree workspaces.
2025-06-11 23:40:41 +00:00
Cole Miller
1083c0ac53 debugger: Special-case npm et al. as program field for JS debug definitions (#32549)
Send `runtimeExecutable` and `runtimeArgs` instead of `program` and
`args` to avoid the DAP implicitly wrapping the command in `node`.

This means that putting `pnpm vitest <file>` as the command in the
launch modal will work, as will this in debug.json:

```
[
  {
    "adapter": "JavaScript",
    "type": "pwa-node",
    "label": "Label",
    "request": "launch",
    "program": "pnpm",
    "args": ["vitest", "<file>"],
    "cwd": "/Users/name/project"
  }
]
```


Release Notes:

- Debugger Beta: made it possible to use commands like `pnpm
<subcommand> <args>` in the launch modal and debug.json
2025-06-11 23:28:45 +00:00
Conrad Irwin
2a63c5f951 Fix code actions run confusion (#32579)
Now if you click the triangle you get runnables, if you click the
lightning bolt you get code actions, if you trigger the code actions
menu with the mouse/keyboard you still get both.

Release Notes:

- Fixed the run/code actions menu to not duplicate content when opened
from the respective icons.

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-06-11 16:51:46 -06:00
Kirill Bulatov
9032ea9849 Use buffer's main language when fetching language tasks (#32580)
Closes https://github.com/zed-industries/zed/issues/32465

Release Notes:

- Fixed language tasks fetched incorrectly for certain selections
2025-06-11 21:14:21 +00:00
Ben Brandt
8cc5b04045 open_ai: Remove redundant serde aliases and add model limits (#32572)
Remove unnecessary alias attributes from Model enum variants and add
max_output_tokens limits for all OpenAI models. Also fix
supports_system_messages to explicitly handle all model variants.

Release Notes:

- N/A
2025-06-11 22:51:41 +02:00
Cole Miller
c4277681d1 debugger: Fix issues with launch.json handling (#32563)
After this PR we can run all the in-tree launch.json examples from [this
repo](https://github.com/microsoft/vscode-recipes).

Things done:

- Fill in default cwd at a lower level for all adapters
- Update launch.json parsing for DebugScenario changes
- Imitate how VS Code normalizes the `type` field for JS debug tasks
- Make version field optional
- Extend the variable replacer a bit

Release Notes:

- Debugger Beta: fixed issues preventing loading and running of debug
tasks from VS Code's launch.json.

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-11 16:48:44 -04:00
Conrad Irwin
0e0ac9b846 Hush breakpoint deserialization logs (#32430)
Release Notes:

- debugger: Remove "Deserializing N breakpoints" from the Zed log
2025-06-11 14:22:35 -06:00
Kirill Bulatov
87f77db6d2 Use upstream cpal (#32571)
Release Notes:

- N/A
2025-06-11 22:44:20 +03:00
Bennet Bo Fenner
717bf35484 agent: Remove context server settings when uninstalling MCP extension (#32560)
Release Notes:

- agent: Automatically remove context server settings when uninstalling
MCP extension
2025-06-11 19:30:03 +00:00
Conrad Irwin
e8ba8bb1eb Rerun debug scenario now uses latest definition from JSON (#32569)
Co-authored-by: Piotr Osiewicz <piotr@zed.dev>

Closes #ISSUE

Release Notes:

- debugger: Re-running a debug scenario that has been edited on disk now
uses the latest version

Co-authored-by: Piotr Osiewicz <peterosiewicz@gmail.com>
2025-06-11 12:39:37 -06:00
Alexander
83eb6ffe1e Apply TypeScript test improvements to tsx files (#32477)
relates-to: #32467
relates-to: #31499

Release Notes:

- N/A
2025-06-11 20:23:16 +02:00
Michael Sloan
027ce6889c Fix parsing of direnv export json to support unset of environment variables + better logging (#32559)
Release Notes:

- Fixed parsing of `direnv export json` output to support unset of
environment variables.
2025-06-11 17:57:30 +00:00
Smit Barmase
65a1d09d24 editor: Use fuzzy crate in code completions tests instead of hard coded values (#32565)
This PR makes it a lot cleaner to write code completion tests. It
doesn't contain any logical changes, just refactoring.

Before, we used to depend on hard-coded values of fuzzy score and its
positions for tests. Now we don't need them, as fuzzy crate will handle
that for us. This is possible because fuzzy match score isn't dependent
on relative candidates or the number of candidates; rather, it's just a
one-to-one mapping for each candidate and its score.

This also makes it test robust for future purposes if there are changes
in fuzzy score logic.

Before:
```rs
  SortableMatch {
            string_match: StringMatch {  // -> whole struct provided by fuzzy crate
                candidate_id: 1115,
                score: 1.0,
                positions: vec![],
                string: "Item".to_string(),
            },
            is_snippet: false,  // -> changed to snippet kind
            sort_text: Some("16"),
            sort_kind: 3, // -> changed to function, constant, variable kind
            sort_label: "Item",
        },
```

After:
```rs
  CompletionBuilder::function("Item", "16")
```

Release Notes:

- N/A
2025-06-11 23:26:19 +05:30
Piotr Osiewicz
7f150f7e0f debugger: Fix preselection of debug adapters to not pick CodeLLDB by default (#32557)
Closes #ISSUE

Release Notes:

- debugger: Fix preselection of debug adapters to not pick CodeLLDB by
default
2025-06-11 16:37:26 +00:00
Joseph T. Lyons
ebd745cf2d Bump Zed to v0.192 (#32552)
Release Notes:

-N/A
2025-06-11 15:41:41 +00:00
Cole Miller
06f7d791b7 debugger: Fix a couple of issues with vitest (#32543)
- Pass the right test name filter
- Limit the number of forks used by the testing pool in the spirit of
#32473

Release Notes:

- Debugger Beta: switched to running vitest tests serially when
debugging.
2025-06-11 10:36:23 -04:00
Ben Brandt
2ecc24eb26 eval: Add jitter to retry attempts (#32542)
Adds some jitter to avoid the issue that all requests will retry at
roughly the same time in eval where we have a lot of concurrent
requests.

Release Notes:

- N/A
2025-06-11 12:56:23 +00:00
Piotr Osiewicz
6c4728f00f debugger: Mark DebugAdapterBinary::program as optional (#32534)
This allows us to support debugging with a debug adapter not managed by
Zed. Note that this is not a user facing change, as DebugAdapterBinary
is used to determine how to spawn a debugger. Thus, this should not
break any configs or anything like that.

Closes #ISSUE

Release Notes:

- N/A
2025-06-11 12:38:12 +02:00
张小白
a3cc063107 windows: Show error messages when zed failed to lanuch (#32537)
Now, if either `WindowsPlatform` or `BladeRenderer` fails to initialize,
a window will pop up to notify the user.


![image](https://github.com/user-attachments/assets/40fe7f1d-5218-4ee2-b4ec-0945fed2b743)


Release Notes:

- N/A
2025-06-11 18:37:34 +08:00
Max Mynter
7d5a5d0984 Make minimum width for line numbers in gutter configurable (#31959)
Closes #7334

# Changes
This PR makes the minimum width allocated for line numbers in the side
gutter configurable in units of character width via the
`"line_number_base_width"` attribute in `gutter` settings. Set the
previously hard coded value of `4` as default.

Together with other settings (`"folds"`, `"breakpoints"`,...) this gives
the user control over the gutter width.

If the number of lines exceedes the base width, the number of digits in
the largest line number is chosen instead. This is consistent with
previous behaviour.

Screenshot for reference:
<img width="1104" alt="Screenshot 2025-06-03 at 12 15 29"
src="https://github.com/user-attachments/assets/77c869ad-164b-4b74-8e39-8be43d740ad4"
/>


P.S.: This is my first time contributing to zed (yay!🎉). Let me know if
i'm missing something.

Release Notes:

- Make minimum line number width in gutter configurable
2025-06-11 10:00:50 +00:00
张小白
4c3ada5753 windows: Add back hide_title_bar checks (#32427)
These `if` condition checks were removed in #30828, and this PR adds
them back. This is especially important in the handling of
`WM_NCHITTEST`, where all the calculations are based on the assumption
that `hide_title_bar = true`.


Release Notes:

- N/A
2025-06-11 09:46:16 +00:00
Ben Brandt
b3a8816c0e agent: Add completion cancellation when editing messages (#32533)
When editing a message, cancel any in-progress completion before
starting a new request to prevent overlapping model responses.

Release Notes:

- agent: Fixed previous completion not cancelling when editing a
previous message
2025-06-11 09:36:21 +00:00
Smit Barmase
6d9bcdb2af editor: Fix certain unwanted pre-emptive keys been shown in buffer (#32528)
Closes #32456

https://github.com/zed-industries/zed/pull/32007 added showing
pre-emptive keys for multi-key bindings. But for certain keys like
"control", "backspace", "escape", "shift", "f1", etc., shouldn't be
shown as these keys would not end up in buffer after pending input
delay. This PR changes it to use just `key_char`, as it represents
actual text that will end up in buffer and is `None` for all mentioned
keys.


fad4c17c97/crates/gpui/src/platform/keystroke.rs (L14-L21)

cc @ConradIrwin 

Release Notes:

- Fixed issue where triggering multi-key binding like "shift",
"control", etc. would write them to the buffer for a short time.
2025-06-11 14:16:21 +05:30
Umesh Yadav
0852912fd6 language_models: Add image support to OpenRouter models (#32012)
- [x] Manual Testing(Tested this with Qwen2.5 VL 32B Instruct (free) and
Llama 4 Scout (free), Llama 4 Maverick (free). Llama models have some
issues in write profile due to one of the in built tools schema, so I
tested it with minimal profile.

Closes #ISSUE

Release Notes:

- Add image support to OpenRouter models

---------

Signed-off-by: Umesh Yadav <umesh4257@gmail.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-11 08:01:29 +00:00
Julia Ryan
47ac01842b ci: Fix cachix secrets (#32259) 2025-06-10 23:38:44 -07:00
Michael Sloan
5b22994d9f Log error instead of panics in InlineAssistant::scroll_to_assist (#32519)
Leaving release notes blank as it's not very actionable to know that a
rare crash might be fixed.

Release Notes:

- N/A
2025-06-11 06:22:12 +00:00
Cole Miller
6c0ea88f5b debugger: Make sure debuggees are killed when quitting Zed (#32186)
Closes #31373 

We kill the DAP process in our `on_app_quit` handler, but the debuggee
might not be killed. Try to make this more reliable by making the DAP
process its own process group leader, and killing that entire process
group when quitting Zed.

I also considered going through the normal DAP shutdown sequence here,
but that seems dicey in a quit handler. There's also the DAP
`ProcessEvent` but it seems we can't rely on that as e.g. the JS DAP
doesn't send it.

Release Notes:

- Debugger Beta: Fixed debuggee processes not getting cleaned up when
quitting Zed.
2025-06-11 05:23:38 +00:00
Smit Barmase
fc4ca346be editor: Adjust scope for prefer label for snippet workaround (#32515)
Closes #32159

This PR refines the scope to match just the function name with **the
type argument** instead of the whole call expression.

Matching to whole call expression prevented methods from expanding
inside the function argument. For example, `const foo =
bar(someMethod(2)^);` instead of `const foo = bar(someMethod^)`;

Follow-up for https://github.com/zed-industries/zed/pull/30312,
https://github.com/zed-industries/zed/pull/30351. Mistakenly regressed
since https://github.com/zed-industries/zed/pull/31872 when we stopped
receiving `insert_range` for this particular case and fallback to
`replace_range`.

Release Notes:

- Fixed issue where code completion in TypeScript function arguments
sometimes omitted the dot separator, for example resulting in
`NumberparseInt` instead of `Number.parseInt(string)`.

---------

Co-authored-by: Michael Sloan <michael@zed.dev>
Co-authored-by: Michael Sloan <mgsloan@gmail.com>
2025-06-11 10:38:39 +05:30
Conrad Irwin
e9570eefbf Fix go stop on panic (#32512)
Release Notes:

- debugger: Fix stopping on a panic
2025-06-10 22:24:59 -06:00
Max Brunsfeld
72de3143c8 Add a test demonstrating ERB language loading bug (#32278)
Fixes https://github.com/zed-industries/zed/issues/12174

Release Notes:

- Fixed a bug where ERB files were not parsed correctly when the
languages were initially loaded.
2025-06-11 04:03:42 +00:00
Conrad Irwin
ad206a6a97 Recenter current stack frame on click (#32508)
Release Notes:

- debugger: Recenter current stack frame on click
2025-06-10 22:00:20 -06:00
Conrad Irwin
1e1bc7c373 Fix detach (#32506)
Release Notes:

- debugger: Fix detach to not terminate debuggee (and only be available
when detaching makes sense)
2025-06-10 20:20:28 -06:00
Stanislav Alekseev
84eca53319 Add ANSI C quoting to export env parsing (#32404)
Follow up to #31799 to support ansi-c quoting. This is used by
nix/direnv

Release Notes:

- N/A
2025-06-10 20:15:35 -06:00
fantacell
b4e558ce3d Add more keymaps from helix (#32453)
I added three additional keymaps to simulate helix behavior.

Release Notes:

- N/A
2025-06-11 02:10:43 +00:00
Conrad Irwin
00a8101016 Add a run menu (#32505)
As part of this I refactored the logic that enabled/disabled actions in
the debugger to happen at action registration time instead of using
command palette filters. This allows the menu to grey out actions correctly.

Release Notes:

- Add a "Run" menu to contain tasks and debugger
2025-06-10 19:57:46 -06:00
Anthony Eid
444f797827 debugger beta: Improve resolve debug scenario error message (#32504)
When no locator or valid config is found we expose the invalid config
error message to the user now.

Closes #32067 

Release Notes:

- debugger beta: Improve error message when starting a debugger session
with an invalid configuration
2025-06-11 01:13:27 +00:00
Anthony Eid
7a14987c02 debugger beta: Fix inline value provider panic (#32502)
Closes #32143

Release Notes:

- debugger beta: Fix panic that could occur when generating inline
values
2025-06-11 01:01:30 +00:00
Anthony Eid
5eb68f0ea4 debugger: Fix panic when handling invalid RunInTerminal request (#32500)
The new dap-types version has a default to cwd for the
RunInTerminalRequest

Closes #31695

Release Notes:

- debugger beta: Fix panic that occurred when a debug adapter sent an
invalid `RunInTerminal` request
2025-06-11 00:44:32 +00:00
Kirill Bulatov
9c513223c4 Add initial package.json scripts task autodetection (#32497)
Now, every JS/TS-related file will get their package.json script
contents added as tasks:

<img width="1020" alt="image"
src="https://github.com/user-attachments/assets/5bf80f80-fd72-4ba8-8ccf-418872895a25"
/>

To achieve that, `fn associated_tasks` from the `ContextProvider` was
made asynchronous and the related code adjusted.

Release Notes:

- Added initial `package.json` scripts task autodetection

---------

Co-authored-by: Piotr Osiewicz <piotr@zed.dev>
2025-06-10 22:16:27 +00:00
Cole Miller
0c0933d1c0 debugger: Ungate locator for JS tasks (#32495)
Closes #ISSUE

Release Notes:

- N/A

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-06-10 18:16:07 -04:00
Piotr Osiewicz
a4c5a2d4d3 debugger: Add 'open docs' button in the panel and mention onboarding in the docs (#32496)
Closes #ISSUE

Release Notes:

- N/A
2025-06-10 21:56:29 +00:00
Cole Miller
311e136e30 debugger: Reuse parent's debug terminal for child sessions (#32493)
Closes #ISSUE

Release Notes:

- Debugger Beta: fixed an issue where the terminal pane of the debug
panel would be empty when debugging JavaScript.

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-06-10 17:13:58 -04:00
Michael Sloan
4f5433a180 Filter language server completions even when is_incomplete: true (#32491)
In #31872 I changed the behavior of completions to not filter instead of
requerying completions when `is_incomplete: false`. Unfortunately this
also stopped filtering completions when `is_incomplete: true` - we still
want to filter the incomplete completions so that the menu updates
quickly even when completions are slow. This does mean that the
completions menu will display partial results, hopefully only briefly
while waiting for fresh completions.

Thanks to @mikayla-maki for noticing the regression. Thankfully just in
time to fix it before this makes it into a stable release. Leaving off
release notes since I will cherry-pick this to the current preview
version, 190.x, and there probably won't be a preview release before the
next stable.

Release Notes:

- N/A
2025-06-10 21:01:59 +00:00
Piotr Osiewicz
295db79c47 debugger: Fix phantom JavaScript frames (#32469)
JavaScript debugger is using a phantom stack frame to delineate await
points; that frame reuses a frame ID of 0, which collides with other
frames returned from that adapter.

934075df8c/src/adapter/stackTrace.ts (L287)

The bug has since been fixed in
https://github.com/microsoft/vscode-js-debug/issues/2234, but we'll need
to wait for a new release of node debugger for that to make a
difference. Until then..

Release Notes:

- Fixed a bug with JavaScript debugging which led to stack trace list
containing excessive amount of `await` entries.

---------

Co-authored-by: Conrad Irwin <conrad@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-10 14:48:07 -06:00
Cole Miller
71d5c57119 debugger: Specify runtimeExecutable in output of node locator (#32464)
This appears to fix some cases where we fail to launch JS tests under
the debugger.

Release Notes:

- N/A (node locator is still gated)

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-10 20:42:55 +00:00
Julia Ryan
dd17fd3d5a debug: Launch custom commands from start modal (#32484)
Release Notes:

- Add custom command launching from the `debug: start` modal

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-06-10 16:29:11 -04:00
Cole Miller
e4f8c4fb4c debugger: Don't spin forever when adapter disconnects unexpectedly (#32489)
Closes #ISSUE

Release Notes:

- Debugger Beta: made the debug panel UI more helpful when an invalid
configuration is sent to the debug adapter.

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-10 16:26:43 -04:00
Andy Waite
e62e9facf0 docs: Condense Ruby test framework docs (#32472)
Since `tldr` and `quickdraw` use the same kind of task syntax as RSpec,
I don't think it's necessary to have separate examples.

cc @joeldrapper @vitallium 

Release Notes:

- N/A
2025-06-10 22:58:57 +03:00
Andy Waite
3f419b32f8 docs: Update Ruby docs about args syntax in tasks (#32471)
Due to https://github.com/zed-industries/zed/pull/32345

cc @vitallium 

Release Notes:

- N/A
2025-06-10 22:58:38 +03:00
Joseph T. Lyons
5270844b42 Revert "Preserve selection direction when running editor: open selections in multibuffer" (#32483)
Reverts zed-industries/zed#31399

I found that in some cases, Zed will panic when using `editor: open
selections in multibuffer` if the selection is reversed. It doesn't
happen in most cases that I've tested, but in some strange edge cases
(that I dont fully understand ATM), it does. I'm reverting for now, as
the previous behavior is better than a panic, but will re-implement this
fix to preserving selection directions in a new PR with comprehensive
testing

Release Notes:

- N/A
2025-06-10 15:31:38 -04:00
Ben Kunkle
f567bb52ff gpui: Simplify uniform list API by removing entity param (#32480)
This PR also introduces `Context::processor`, a sibling of
`Context::listener` that takes a strong pointer to entity and allows for
a return result.

Release Notes:

- N/A

Co-authored-by: Mikayla <mikayla@zed.dev>
2025-06-10 18:50:57 +00:00
Cole Miller
c55630889a debugger: Run jest tests serially (#32473)
Pass `--runInBand` to jest when debugging. This prevents jest from
creating a bunch of child processes that clutter the session list.

It might be a bit more natural to add this argument in the test
templates themselves, but I don't think we want to give up parallelism
when running via `task: spawn`.

Release Notes:

- N/A (JS locator is still gated)
2025-06-10 14:25:07 -04:00
Cole Miller
e0ca4270b4 debugger: Use JS adapter's suggested names for child sessions (#32474)
Also introduces an extension point for other adapters to do this if it
turns out they also send this information.

Release Notes:

- N/A (JS locator is still gated)
2025-06-10 14:24:43 -04:00
Peter Tripp
02dfaf7799 ci: Suppress evals on forks (#32479)
Be kind to those with Zed forks.

Example [action run on
fork](https://github.com/G36maid/freebsd-ports-zed/actions/runs/15525942275)
where [this
job](https://github.com/G36maid/freebsd-ports-zed/actions/runs/15549650437/job/43777665341)
will wait forever. Sorry @G36maid

Release Notes:

- N/A
2025-06-10 18:20:03 +00:00
Ben Kunkle
c9972ca532 docs: Consolidate and improve organization of Linux GPU issue documentation (#32468)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-10 12:18:57 -04:00
Alexander
9334e152b4 Allow identifiers in TypeScript/JavaScript test names (#32467)
Current behavior (not detected as runnable):

<img width="1105" alt="image"
src="https://github.com/user-attachments/assets/7d3b7936-43d8-4645-bbbb-e81ed5f9b35a"
/>

New behavior:



https://github.com/user-attachments/assets/524e2a56-cb30-4dc0-98ec-b34b510015e0

Release Notes:

- Improved detection of runnable TypeScript/JavaScript test cases when
they contain identifier
2025-06-10 18:00:42 +02:00
Peter Tripp
9c47c52de5 ci: Restore lychee link check. Only validate internal links (#32463)
Follow-up to: https://github.com/zed-industries/zed/pull/32460
Follow-up to: https://github.com/zed-industries/zed/pull/30844

Release Notes:

- N/A
2025-06-10 11:20:07 -04:00
Umesh Yadav
286b97c0de agent: Fix agent panel model selector layout pushing send button off screen (#32251)
| Before | After |
|--------|-------|
| <video
src="https://github.com/user-attachments/assets/db4dcc91-9a32-4621-be78-87fe9d80b801"
controls width="400"></video> | <video
src="https://github.com/user-attachments/assets/8ee31d6d-5150-4239-a4af-eeca112d56d5"
controls width="400"></video> |

While working on something else I found this weird behaviour in message
editor of agent panel. When model names are too long, the model selector
would expand and push the send button outside the visible area. This
change fixes the flex layout to ensure the send button always remains
accessible while properly truncating long model names.

Closes #ISSUE

Release Notes:

- Fix agent panel model selector layout pushing send button off screen

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
2025-06-10 14:59:42 +00:00
Danilo Leal
415d482395 agent: Only show the MCP configuration modal in the active window (#32450)
We were previously displaying this modal in all open Zed windows if
triggered. That was a bit annoying because I had to go to each window
individually to close it, which meant doing it multiple times. 😅

Release Notes:

- agent: Fixed the MCP configuration modal to show only in the active
window.
2025-06-10 14:34:21 +00:00
Danilo Leal
a9d0eee2a9 docs: Add link to MCP extensions in the overview page (#32458)
Follow up to https://github.com/zed-industries/zed/pull/32422. Missed
this one in this latest round of MCP-related docs changes.

Release Notes:

- N/A

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-10 11:16:06 -03:00
Smit Barmase
e4e3409952 extension_host: Fix SSH reconnect breaks language server (#32457)
Closes #29032

This PR fixes an issue where reconnecting to SSH Remote would result in
a broken language server.

This was caused by SSH clients not registering because the `ssh_clients`
map would still contain an entry from a previously killed SSH server.
For fix, now we also check if its value has been dropped.

Release Notes:

- Fixed issue where reconnecting to SSH Remote would result in broken
code completions and diagnostics.
2025-06-10 19:45:57 +05:30
Peter Tripp
46f98b6001 ci: Move lychee link check to script/check-links (#32460)
Follow-up to: https://github.com/zed-industries/zed/pull/30844

Transient link failure was blocking PR tests passing. [Example
run](https://github.com/zed-industries/zed/actions/runs/15560960788/job/43812878693?pr=32458).

Release Notes:

- N/A
2025-06-10 10:11:24 -04:00
Kirill Bulatov
c1a4a24bce Ensure pull diagnostics do not happen for non-full mode editors (#32449)
Follow-up of https://github.com/zed-industries/zed/pull/19230

Release Notes:

- N/A
2025-06-10 12:05:45 +00:00
CharlesChen0823
eb5f59577d editor: Dismiss drag selection when dropped outside editor (#32382)
This PR fixes two issues:

1. On macOS, using Alt to copy the selection instead of cutting it.
2. Dropping the drag selection outside the editor dismisses it.  


https://github.com/user-attachments/assets/341e21c3-3eca-4e58-9bcc-8ec1de18e999


Release Notes:

- N/A

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-06-10 15:41:59 +05:30
张小白
2dad48d8d9 windows: Fix panic when deleting the last pre-edit char using Pinyin IME (#32442)
Release Notes:

- N/A
2025-06-10 09:51:12 +00:00
Burak Varlı
16853acbb1 Enable cross-region inference for Claude 4 family models on Amazon Bedrock provider (#32235)
These models require cross-region inference, and it currently fails if
you try to use them:
```
Invocation of model ID anthropic.claude-sonnet-4-20250514-v1:0 with on-demand throughput isn’t supported. 
```

Release Notes:

- Enable cross-region inference for Claude 4 family models on Amazon
Bedrock provider

Signed-off-by: Burak Varlı <burakvar@amazon.co.uk>
2025-06-09 23:38:39 -07:00
Michael Sloan
64d649245c Add missing #[track_caller] meant to be in #32433 (#32434)
Release Notes:

- N/A
2025-06-10 04:52:43 +00:00
Michael Sloan
08210b512d Don't push to selection history if selections are empty (#32433)
I got a panic during undo but haven't been able to repro it. Potentially
a consequence of my changes in #31731

> Thread "main" panicked with "There must be at least one selection" at
crates/editor/src/selections_collection.rs

Leaving release notes blank as I'm not sure this actually fixes the
panic

Release Notes:

- N/A
2025-06-10 04:29:45 +00:00
Michael Sloan
6070aea6c0 Skip adding initial 1:1 cursor position to selection history (#32432)
Also improves some minor corner cases in `undo_selection` and
`redo_selection` related to the use of `end_selection`. If the pending
selection was ended, this would separately get pushed to the redo or
undo stack and redundantly run all the other effects of selection
change.

Release Notes:

- N/A
2025-06-10 04:22:46 +00:00
Conrad Irwin
16b44d53f9 debugger: Use Delve to build Go binaries (#32221)
Release Notes:

- debugger: Use delve to build go debug executables, and pass arguments
through.

---------

Co-authored-by: sysradium <sysradium@users.noreply.github.com>
Co-authored-by: Zed AI <ai@zed.dev>
2025-06-09 21:49:04 -06:00
Haru Kim
3bed830a1f Use unix pipe to capture environment variables (#32136)
The use of `NamedTempFile` in #31799 was not secure and could
potentially cause write permission issues (see [this
comment](https://github.com/zed-industries/zed/issues/29528#issuecomment-2939672433)).
Therefore, it has been replaced with a Unix pipe.

Release Notes:
- N/A
2025-06-09 20:37:43 -06:00
Conrad Irwin
ee2a329981 Slow down reconnects on collab (#32418)
We believe that collab deploys currently cause outages because:

* All clients try to reconnect simultaneously
* This causes very high CPU usage on collab (and to some extent, the
database)
* This means that collab is slow to respond to clients
* So clients timeout and retry, over and over again.

We hope by letting clients in in buckets of 512, we can accept some
minor slowness to avoid
complete downtime, while we rewrite the system.

Release Notes:

- N/A
2025-06-09 19:59:04 -06:00
Joseph T. Lyons
6d64058fc6 Add pane: unpin all tabs (#32423)
After integrating pinned tabs into my workflow, I've come to the
conclusion that it is painfully slow to unpin all tabs by hand.


https://github.com/user-attachments/assets/ad087b8e-4595-4c4d-827f-188e36170c25

Release Notes:

- Added a `pane: unpin all tabs` action
2025-06-09 20:25:22 -04:00
Danilo Leal
7c2822a020 docs: Improve MCP-related pages (#32422)
While creating a new MCP extension this weekend, I visited these pages
and it felt like they could be improved a little bit. I'm renaming the
MCP-related page under the /extension directory to use the "MCP"
acronym, instead of "context servers".

Release Notes:

- N/A
2025-06-09 21:00:10 -03:00
Danilo Leal
3db00384f4 agent: Improve generating dots display (#32421)
Previously, upon hitting the "Continue" button to restart an interrupted
thread due to consecutive tool calls reaching its limit, you wouldn't
see the loading dots and the UI would be a weird state. This PR improves
when these loading dots actually show up, including in their conditional
a check for `message.is_hidden`.

Also took advantage of the opportunity to quickly organize some of these
variables. The `render_message` function could potentially be chopped up
in more smaller pieces. Lots of things going on here.

Release Notes:

- N/A
2025-06-09 20:52:50 -03:00
Conrad Irwin
3dfbd9e57c Fix ruby debugger (#32407)
Closes #ISSUE

Release Notes:

- debugger: Fix Ruby (was broken by #30833)

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Piotr Osiewicz <peterosiewicz@gmail.com>
Co-authored-by: Cole Miller <m@cole-miller.net>
2025-06-09 16:11:24 -06:00
Agus Zubiaga
b103d7621b Improve handling of large output in embedded terminals (#32416)
#31922 made embedded terminals automatically grow to fit the content. We
since found some issues with large output which this PR addresses by:

- Only shaping / laying out lines that are visible in the viewport
(based on `window.content_mask`)
- Falling back to embedded scrolling after 1K lines. The perf fix above
actually makes it possible to handle a lot of lines, but:
- Alacrity uses a `u16` for rows internally, so we needed a limit to
prevent overflow.
- Scrolling through thousands of lines to get to the other side of a
terminal tool call isn't great UX, so we might as well set the limit
low.
- We can consider raising the limit when we make card headers sticky.

Release Notes:

- Agent: Improve handling of large terminal output
2025-06-09 18:11:31 -03:00
Danilo Leal
ab70e524c8 agent: Improve MCP status indicator tooltip and loading state (#32414)
Mostly a small tweak making sure that the indicator tooltip hit area is
bigger and the loading state is clearer (not using an indicator
anymore). Way more little improvement opportunities in this component to
do, though.

Release Notes:

- N/A
2025-06-09 18:04:48 -03:00
JonasKaplan
f0ce62ead8 editor: Add trailing whitespace rendering (#32329)
Closes #5237

- Adds "trailing" option for "show_whitespaces" in settings.json
- Supports importing this setting from vscode

The option in question will render only whitespace characters that
appear after every non-whitespace character in a given line.

Release Notes:

- Added trailing whitespace rendering
2025-06-09 20:48:49 +00:00
Cole Miller
f0345df479 debugger: Undo conversion of stack frames list to uniform list (#32413)
Partially reverts #30682

A uniform list is desirable for the scrolling behavior, but this breaks
badly when there are collapsed entries or entries without paths, both of
which seem common with the JS adapter.

It would be nice to go back to a uniform list if we can come up with a
set of design tweaks that allow all entries to be the same height.

Release Notes:

- Debugger Beta: fixed an issue that caused entries in the stack frame
list to overlap in some situations.
2025-06-09 20:45:01 +00:00
Michael Sloan
bbd2262a93 Fix buffer rendering on every mouse move (#32408)
Closes #32210

This notify was added in #13433. Solution is to only notify when the
breakpoint indicator state has changed.

Also improves the logic for enqueuing a task to delay showing - now only
does this if it isn't already visible, and that delay task now only
notifies if still hovering.

Release Notes:

- Fixed a bug where buffers render on every mouse move.
2025-06-09 14:10:03 -06:00
Antonio Scandurra
c4fd9e1a6b Switch to using weak transactions for queries happening on connection (#32411)
Release Notes:

- N/A

Co-authored-by: Conrad <conrad@zed.dev>
2025-06-09 21:37:48 +02:00
Nate Butler
0b7583bae5 Refine styling of merge conflicts (#31012)
- Improved colors
- Blank out diff hunk gutter highlights in conflict regions
- Paint conflict marker highlights all the way to the gutter

Release Notes:

- Improved the highlighting of merge conflict markers in editors.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
Co-authored-by: Cole Miller <cole@zed.dev>
2025-06-09 19:03:19 +00:00
Ben Brandt
e4bd115a63 More resilient eval (#32257)
Bubbles up rate limit information so that we can retry after a certain
duration if needed higher up in the stack.

Also caps the number of concurrent evals running at once to also help.

Release Notes:

- N/A
2025-06-09 18:07:22 +00:00
Kirill Bulatov
fa54fa80d0 Store pulled diagnostics' result_ids more persistently (#32403)
Follow-up of https://github.com/zed-industries/zed/pull/19230

`BufferId` can change between file reopens: e.g. open the buffer, close
it, go back in history to reopen it — the 2nd one will have a different
`BufferId`, but the same `result_ids` semantically.

Release Notes:

- N/A
2025-06-09 17:05:33 +00:00
Antonio Scandurra
de16f2bbe6 Bypass account age check when feature flag is set (#32393)
Release Notes:

- N/A
2025-06-09 18:44:48 +02:00
Jason Lee
e3b13b54c9 title_bar: Merge Linux only code into platform_linux (#32401)
Release Notes:

- N/A
2025-06-09 19:19:24 +03:00
Tommy D. Rossi
2c5d2a58d8 Do not skip punctuation characters with alt-arrow if next character is \n (#32368)
Closes #32356

Release Notes:

- N/A
2025-06-09 09:25:32 -06:00
Peter Tripp
3485b7704b Update GitHub Issue Templates (June 2025) (#32399)
- Remove git/edit predictions templates
- Rename Agent to AI related (include edit predictions, copilot, etc)
- Other minor adjustments

Release Notes:

- N/A
2025-06-09 15:25:17 +00:00
Bennet Bo Fenner
6801b9137f context_server: Make notifications type safe (#32396)
Follow up to #32254 

Release Notes:

- N/A
2025-06-09 15:11:01 +00:00
Umesh Yadav
3853e83da7 git_ui: Improve error handling in commit message generation (#29005)
After and Before video of the issue and solution.


https://github.com/user-attachments/assets/40508f20-5549-4b3d-9331-85b8192a5b5a



Closes #27319

Release Notes:

- Provide Feedback on commit message generation error

---------

Signed-off-by: Umesh Yadav <umesh4257@gmail.com>
Co-authored-by: Cole Miller <cole@zed.dev>
2025-06-09 14:20:19 +00:00
Ben Hamment
047a7f5d29 Decrease the size of the branch picker icon (#32387)
<img width="323" alt="image"
src="https://github.com/user-attachments/assets/0060eaf3-35f9-4f0f-b9b6-e26ffad853c2"
/>

<img width="323" alt="image"
src="https://github.com/user-attachments/assets/57b66dae-2a74-401f-82c1-8fc730a98fb0
" />

Release Notes:

- Adjusted size of the icon inside the title bar's branch picker when
that's turned on.
2025-06-09 11:08:53 -03:00
Evan Simkowitz
8332e60ca9 language: Don't add final newline on format for an empty buffer (#32320)
Closes #32313

Release Notes:

- Fixed newline getting added on format to empty files
2025-06-09 09:00:17 -04:00
Bennet Bo Fenner
afab4b522e agent: Add tests for thread serialization code (#32383)
This adds some unit tests to ensure that the `update(...)`/migration
path to the latest versions works correctly

Release Notes:

- N/A
2025-06-09 12:20:19 +00:00
vipex
0cb7dd2972 git_panel: Persist dock size (#32111)
Closes #32054

The dock size for the git panel wasn't being persisted across Zed
restarts. This was because the git panel lacked the serialization
pattern used by other panels.

Please let me know if you have any sort of feedback or anything, as i'm
still trying to learn :]

Release Notes:

- Fixed Git Panel dock size not being remembered across Zed restarts

## TODO
- [x] Update/fix tests that may be broken by the GitPanel constructor
changes
2025-06-09 16:51:36 +05:30
Angelk90
387281fa5b project_panel: Add hide_root when only one folder in the project (#25289)
Closes #24188

Todo:
- [x] Hide root when only one worktree
- [x] Basic tests
- [x] Docs
- [x] Fix `select_first` + tests
- [x] Fix auto collapse dir + tests
- [x] Fix file / dir creation + tests
- [x] Fix root rename case

| Show root | Hide root |
|--------|--------|
| <img width="272" alt="Screenshot 2025-02-20 alle 22 35 55"
src="https://github.com/user-attachments/assets/361d93c7-e1ad-4419-a5f4-be62c9632807"
/> | <img width="269" alt="Screenshot 2025-02-20 alle 22 36 11"
src="https://github.com/user-attachments/assets/62011f76-a24b-4297-9734-f5c3b9f75760"
/> |
| <img width="275" alt="Screenshot 2025-02-20 alle 22 56 33"
src="https://github.com/user-attachments/assets/77e7e6e6-3dfe-4e88-b4b0-b620cb809d2b"
/> | <img width="267" alt="Screenshot 2025-02-20 alle 22 55 53"
src="https://github.com/user-attachments/assets/fa1099c8-7ed0-45ef-a7cf-aeb54b8283b1"
/> |


Release Notes:

- Added support to hide the root entry of the Project Panel when there’s
only one folder in the project. This can be enabled by setting
`hide_root` to `true` in the `project_panel` config.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-06-09 16:46:31 +05:30
Piotr Osiewicz
72bcb0beb7 chore: Fix warnings for Rust 1.89 (#32378)
Closes #ISSUE

Release Notes:

- N/A
2025-06-09 13:11:57 +02:00
Bennet Bo Fenner
4ff41ba62e context_server: Update types to reflect latest protocol version (2025-03-26) (#32377)
This updates the `types.rs` file to reflect the latest version of the
MCP spec. Next up is making use of some of these new capabilities. Would
also be great to add support for [Streamable HTTP
Transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http)

Release Notes:

- N/A
2025-06-09 13:03:47 +02:00
Bennet Bo Fenner
16e901fb8f docs: Remove reference to outdated Gemini models (#32379)
Release Notes:

- N/A
2025-06-09 10:50:30 +00:00
smaster
54b4587f9a Add bound checks for resizing right dock (#32246)
Closes #30293


[Before](https://github.com/user-attachments/assets/0b95e317-391a-4d90-ba78-ed3d4f10871d)
|
[After](https://github.com/user-attachments/assets/23002a73-103c-4a4f-a7a1-70950372c9d9)

Release Notes:

- Fixed right panel expanding in backwards, when dragged out of its
intended bounds, by adding a bounds check to ensure its size never gets
to high.
2025-06-09 10:39:53 +00:00
Clauses Kim
1fe10117b7 Add GitHub token environment variable support for Copilot (#31392)
Add support for environment variables as authentication alternatives to
OAuth flow for Copilot. Closes #31172

We can include the token in HTTPS request headers to hopefully resolve
the rate limiting issue in #9483. This change will be part of a separate
PR.

Release Notes:

- Added support for manually providing an OAuth token for GitHub Copilot
Chat by assigning the GH_COPILOT_TOKEN environment variable

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-06-09 12:39:44 +02:00
Bennet Bo Fenner
78fd2685d5 gemini: Fix edge case when transforming MCP tool schema (#32373)
Closes #31766

Release Notes:

- Fixed an issue where some MCP tools would not work when using Gemini
2025-06-09 10:27:21 +00:00
Vitaly Slobodin
da9e958b15 ruby: Update documentation (#32345)
Hi! This pull request updates the Ruby extension documentation for [the
upcoming v0.9.0
upgrade](https://github.com/zed-extensions/ruby/pull/106):

- Added documentation for two newly added language servers: `sorbet` and
`steep`.
- Updated documentation on using the `ZED_CUSTOM_RUBY_TEST_NAME` symbol
for tasks.

Thanks!

Release Notes:

- N/A
2025-06-09 13:19:43 +03:00
dannybunschoten
3908ca9744 docs: Add JavaScript configuration to the example setup of Deno (#32104)
When using Deno with the example configuration as described here,
duplicate lsp information is displayed in Javascript files.

This pull request solves that issue by adding Javascript to the
configuration.

Release Notes:

- Improve LSP support when using Deno with Javascript using the default
configuration.
2025-06-09 13:18:06 +03:00
Alexander
6fe58a2c4e Allow to run dynamic TypeScript and JavaScript tests (#31499)
First of all thank you for such a fast editor!

I realized that the existing support for detecting runnable test cases
for typescript/javascript is not full. Meanwhile I can run most of test
by pressing "run button":

<img width="713" alt="image"
src="https://github.com/user-attachments/assets/e8bb1cb1-f0a5-4eb1-b9a6-7188a9fa47ae"
/>

I can't run dynamic tests:

<img width="703" alt="image"
src="https://github.com/user-attachments/assets/d7eef1bc-e99a-4f05-9d62-ec49b8194959"
/>

I was curious whether I can improve it on my own and created this pr. I
edited schemas and added minor changes in `TaskTemplate` to allow to
replace '%s' with regexp pattern, so it can match test cases:

<img width="772" alt="image"
src="https://github.com/user-attachments/assets/db3a6fe0-ad90-4853-8e98-4215e41dfe88"
/>

Release Notes:

- Allow to run dynamic TypeScript/JavaScript tests

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-06-09 12:13:25 +02:00
Dino
79e7ccc1fe vim: Handle case sensitive search editor setting (#32276)
Update the `vim::normal::search::Vim.search` method in order to
correctly set the search bar's case sensitive search option if the
`search.case_sensitive` setting is enabled.

Closes #32172 

Release Notes:

- vim: Fixed a bug where the `search.case_sensitive` setting was not respected when activating search with <kbd>/</kbd> (`vim::Search`)
2025-06-09 06:12:23 -04:00
Umesh Yadav
0bc9478b46 language_models: Add support for images to Mistral models (#32154)
Tested with following models. Hallucinates with whites outline images
like white lined zed logo but works fine with zed black outlined logo:

Pixtral 12B (pixtral-12b-latest)
Pixtral Large (pixtral-large-latest)
Mistral Medium (mistral-medium-latest)
Mistral Small (mistral-small-latest)

After this PR, almost all of the zed's llm provider who support images
are now supported. Only remaining one is LMStudio. Hopefully we will get
that one as well soon.

Release Notes:

- Add support for images to mistral models

---------

Signed-off-by: Umesh Yadav <git@umesh.dev>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2025-06-09 10:00:02 +00:00
Umesh Yadav
4ac7935589 language_models: Add thinking support to LM Studio provider (#32337)
It works similar to how deepseek works where the thinking is returned as
reasoning_content and we don't have to send the reasoning_content back
in the request.

This is a experiment feature which can be enabled from settings like
this:
<img width="1381" alt="Screenshot 2025-06-08 at 4 26 06 PM"
src="https://github.com/user-attachments/assets/d2f60f3c-0f93-45fc-bae2-4ded42981820"
/>

Here is how it looks to use(tested with
`deepseek/deepseek-r1-0528-qwen3-8b`

<img width="528" alt="Screenshot 2025-06-08 at 5 12 33 PM"
src="https://github.com/user-attachments/assets/f7716f52-5417-4f14-82b8-e853de054f63"
/>


Release Notes:

- Add thinking support to LM Studio provider
2025-06-09 11:55:34 +02:00
Umesh Yadav
c75ad2fd11 language_models: Add thinking support to DeepSeek provider (#32338)
For DeepSeek provider thinking is returned as reasoning_content and we
don't have to send the reasoning_content back in the request.

Release Notes:

- Add thinking support to DeepSeek provider
2025-06-09 11:10:55 +02:00
andrewkolda
365997d79d docs: Remove duplicate Clang-Format link (#32359)
Release Notes:

- N/A
2025-06-09 06:42:31 +00:00
Smit Barmase
c57a6263aa editor: Fix select when click on existing selection (#32365)
Follow-up for https://github.com/zed-industries/zed/pull/30671

Now, when clicking on an existing selection, the cursor will change on
`mouse_up` when `drag_and_drop_selection` is `true`. When
`drag_and_drop_selection` is `false`, it will change on `mouse_down`
(previous default).

Release Notes:

- N/A
2025-06-09 11:58:06 +05:30
Joseph T. Lyons
ebea734515 Coalesce consecutive spaces in new buffer tab titles (#32363)
VS Code has a behavior where it coalesces consecutive spaces in new
buffer tab titles, which I quite like. This presents the content better
and allows more meaningful content to be displayed, as consecutive
spaces don't count towards the 40 character limit.

VS Code

<img width="1013" alt="SCR-20250608-uelt"
src="https://github.com/user-attachments/assets/71a1fd4b-a506-4eab-b6a4-66096a12f1ad"
/>

Zed

<img width="1136" alt="SCR-20250608-ueif"
src="https://github.com/user-attachments/assets/f40fc3c9-0f0f-471d-93ed-be9568fbe778"
/>


Release Notes:

- N/A
2025-06-09 02:01:32 -04:00
CharlesChen0823
4fe05530b0 editor: Add support for drag_and_drop_selection (#30671)
Closes #4958 

Release Notes:

- Added support for drag and drop text selection. It can be disabled by
setting `drag_and_drop_selection` to `false`.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-06-09 10:21:18 +05:30
Joseph T. Lyons
b15aef4310 Introduce dynamic tab titles for unsaved files based on buffer content (#32353)
https://github.com/user-attachments/assets/0bb08784-251c-4221-890a-2d6b3fb94e0f

For new, unsaved files:

- If a buffer has no content, or contains only whitespace, use
`untitled`
- If a buffer has content, take the first 40 chars of the first line

| Sublime | VS Code | Zed |
|---------|---------|-----|
| <img width="227" alt="SCR-20250608-ouux"
src="https://github.com/user-attachments/assets/d02b1e50-5775-4252-86e6-6c9d3f6c72fb"
/> | <img width="230" alt="SCR-20250608-ousn"
src="https://github.com/user-attachments/assets/7c9c016b-642f-4a80-9bc1-8c9bdc7bbd32"
/> | <img width="242" alt="SCR-20250608-ovbg"
src="https://github.com/user-attachments/assets/c7f4be5c-5bba-4a2a-b477-1392ca938cd5"
/> |

Note that this implementation also trims all leading whitespace, so that
if the buffer has any non-whitespace content, we use it. VS Code and
Sublime do not do this.

| Sublime | VS Code | Zed |
|---------|---------|-----|
| <img width="233" alt="SCR-20250608-oviq"
src="https://github.com/user-attachments/assets/ccffecc6-0f46-4d1b-8739-740240bc067b"
/> | <img width="198" alt="SCR-20250608-ovkq"
src="https://github.com/user-attachments/assets/35c20149-f898-417b-aff3-dda22b8cc1f3"
/> | <img width="233" alt="SCR-20250608-ovns"
src="https://github.com/user-attachments/assets/2509e8f6-254b-4fcb-a0ea-e18e95bb685b"
/> |

Release Notes:

- Introduced dynamic tab titles for unsaved files based on buffer
content
2025-06-08 17:30:33 -04:00
Michael Sloan
23adff6ff2 Add CI check that cmd- is not in linux keymaps + check other mods (#32334)
Motivation for the `cmd-` check is that there were a couple keybindings
using `cmd-` in the linux keymap and so these were bound to super /
windows

Release Notes:

- N/A
2025-06-08 09:34:07 +00:00
Michael Sloan
866fe427b3 Cleanup comments in linux keymaps (#32333)
Release Notes:

- N/A
2025-06-08 09:02:52 +00:00
Michael Sloan
f7b2faf64f Fix a few linux keybindings that use cmd- instead of ctrl- (#32332)
Release Notes:

- N/A
2025-06-08 08:50:35 +00:00
Kirill Bulatov
5187954711 Remove previous multi buffer hardcode from the outline panel (#32321)
Closes https://github.com/zed-industries/zed/issues/32316

Multi buffer design was changed so that control buttons are not
occupying extra lines, the hardcoded logic for that is obsolete thus
removed.

Release Notes:

- Fixed incorrect offsets during outline panel navigation in singleton
buffers
2025-06-07 23:54:47 +00:00
Michael Sloan
cabd22f36b No longer instantiate recently opened agent threads on startup (#32285)
This was causing a lot of work on startup, particularly due to
instantiating edit tool cards. The minor downside is that now these
threads don't open quite as fast.

Includes a few other improvements:

* On text thread rename, now immediately updates the metadata for
display in the UI instead of waiting for reload.

* On text thread rename, first renames the file before writing. Before
if the file removal failed you'd end up with a duplicate.

* Now only stores text thread file names instead of full paths. This is
more concise and allows for the app data dir changing location.

* Renames `ThreadStore::unordered_threads` to
`ThreadStore::reverse_chronological_threads` (and removes the old one
that sorted), since the recent change to use a SQL database queries them
in that order.

* Removes `ContextStore::reverse_chronological_contexts` since it was
only used in one location where it does sorting anyway - no need to sort
twice.

* `SavedContextMetadata::title` is now `SharedString` instead of
`String`.

Release Notes:

- Fixed regression in startup performance by not deserializing and
instantiating recently opened agent threads.
2025-06-07 14:53:36 -06:00
Tommy D. Rossi
1552198b55 Cursor keymap: Add cmd-enter to submit inline assistant (#32295)
Closes https://github.com/zed-industries/zed/discussions/29035

Release Notes:

- N/A
2025-06-07 16:37:45 -04:00
Richard Feldman
0da97b0c8b editor: Respect multi_cursor_modifier setting when making columnar selections using mouse (#32273)
Closes https://github.com/zed-industries/zed/issues/31181

Release Notes:

- Added the `multi_cursor_modifier` setting to be respected when making
columnar selections using the mouse drag.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-06-08 01:51:13 +05:30
Joseph T. Lyons
037df8cec5 Simplify logic updating pinned tab count (#32310)
Just a tiny improvement to clean up the logic

Release Notes:

- N/A
2025-06-07 19:15:30 +00:00
Peter Tripp
05ac9f1f84 docs: Missing . from .sql-formatter.json (#32312)
See:
https://github.com/zed-industries/zed/issues/9537#issuecomment-2952784074

Release Notes:

- N/A
2025-06-07 19:11:19 +00:00
morgankrey
9ffb3c5176 Add Opus to Model Docs (#32302)
Release Notes:

- N/A
2025-06-07 14:38:10 -03:00
Joseph T. Lyons
72d787b3ae Fix panic dragging tabs multiple positions to the right (#32305)
Closes https://github.com/zed-industries/zed/issues/32303

Release Notes:

- Fixed a panic that occurred when dragging tabs multiple positions to
the right (preview only)
2025-06-07 17:15:31 +00:00
Umesh Yadav
104f601413 language_models: Fix Copilot models not loading (#32288)
Recently in this PR: https://github.com/zed-industries/zed/pull/32248
github copilot settings was introduced. This had missing settings update
which was leading to github copilot models not getting fetched. This had
missing subscription to update the settings inside the copilot language
model provider. Which caused it not show models at all.

cc @osiewicz 

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-06-07 09:32:01 +00:00
Peter Tripp
46773ebbd8 docs: Fix typo in SUMMARY.md (#32275)
- Follow-up to: https://github.com/zed-industries/zed/pull/30981
- See also: https://github.com/zed-industries/zed/pull/30844
- See also: https://github.com/zed-industries/zed/pull/31073

Changes made to docs tests meant that failure to build docs did not
prevent an automerge.
This resulted in breaking main in
65a93a0036 [action run
link](https://github.com/zed-industries/zed/actions/runs/15501437863).

CC: @probably-neb 

Release Notes:

- N/A
2025-06-06 19:26:59 -04:00
G36maid
65a93a0036 Add initial FreeBSD script & installation doc (#30981)
This PR adds initial FreeBSD support for building Zed:

*  Adds `script/freebsd` to install required dependencies on FreeBSD
*  Adds `docs/freebsd.md` with build instructions and notes
* ⚠️ Mentions that `webrtc` is still **work-in-progress** on FreeBSD.

Related to : #15309 
I’m currently working at discussions :
[Discussions](https://github.com/zed-industries/zed/discussions/29550)

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-06 23:14:25 +00:00
Kirill Bulatov
dc63138089 Use proper paths when determining file finder icons for external files (#32274)
Before:
<img width="576" alt="before"
src="https://github.com/user-attachments/assets/8469e27f-c878-4b89-a6f0-577a502fc625"
/>

After:
<img width="558" alt="after"
src="https://github.com/user-attachments/assets/b8361b88-c6c6-40a5-890d-047fff8e909e"
/>


Release Notes:

- N/A
2025-06-06 23:04:49 +00:00
Kirill Bulatov
fa02bd71c3 Select applicable positions for lsp_ext methods more leniently (#32272)
Closes https://github.com/zed-industries/zed/issues/27238

Release Notes:

- Fixed `editor::SwitchSourceHeader` and
`editor::ExpandMacroRecursively` not working with text selections
2025-06-06 22:47:20 +00:00
Peter Tripp
6d95fd9167 Make assistant::QuoteSelection shortcuts work in agent threads (#32270)
Closes: https://github.com/zed-industries/zed/discussions/30626

Release Notes:

- Fixed `assistant::QuoteSelection` default shortcuts (`cmd->` and
`ctrl->`) so they work in Agent threads too (in addition to text threads
and in the Editor pane).
2025-06-06 17:50:55 -04:00
Peter Tripp
899153d9a4 Stop formatting SQL by default with prettier (#32268)
- Closes: https://github.com/zed-industries/zed/discussions/32261
- Follow-up to: https://github.com/zed-extensions/sql/pull/19
- Follow-up to: https://github.com/zed-industries/zed/pull/32003

The underlying `sql-formatter` used by `prettier-plugin-sql` needs to
have the SQL dialect (mysql, postgresql) passed as the prettier language
name, while Zed passes `sql`, which default will corrupt sql files with
vendor specific extensions (postgresql jsonb operators, etc).

I improved the [Zed SQL Language
Docs](https://zed.dev/docs/languages/sql) in
https://github.com/zed-industries/zed/pull/32003 to show how to use
`sql-formatter` directly as an external formatter.

Release Notes:

- SQL: Disable `format_on_save` using `prettier-plugin-sql` by default.
Please see the [Zed SQL Language
Docs](https://zed.dev/docs/languages/sql) for settings to use
`sql-formatter` directly instead.
2025-06-06 17:21:46 -04:00
Kirill Bulatov
77ead25f8c Implement the rest of the worktree pulls (#32269)
Follow-up of https://github.com/zed-industries/zed/pull/19230

Implements the workspace diagnostics pulling, and replaces "pull
diagnostics every open editors' buffer" strategy with "pull changed
buffer's diagnostics" + "schedule workspace diagnostics pull" for the
rest of the diagnostics.

This means that if the server does not support the workspace diagnostics
and does not return more in linked files, only the currently edited
buffer has its diagnostics updated.

This is better than the existing implementation that causes a lot of
diagnostics pulls to be done instead, and we can add more heuristics on
top later for querying more diagnostics.

Release Notes:

- N/A
2025-06-06 21:19:46 +00:00
chbk
9e5f89dc26 Improve CSS syntax highlighting (#25326)
Release Notes:

  - Improved CSS syntax highlighting

| Zed 0.174.6 | With this PR |
| --- | --- |
| ![css_0 174
6](https://github.com/user-attachments/assets/d069f20e-5f1f-4d03-a010-81ba4b61b3a0)
|
![css_pr](https://github.com/user-attachments/assets/36463ef1-2ead-421d-9825-bd359e7677ab)
|

- `|`: `operator`
- `and`, `or`, `not`, `only`: `operator` -> `keyword.operator`, as
defined in other languages
- `id_name`, `class_name`: `property`/`attribute` -> `selector`, not a
property name. [CSS
reference](https://www.w3.org/TR/selectors-3/#class-html)
- `namespace_name`: `property` -> `namespace`, not a property name
- `property_name`: `constant` -> `property`, like `feature_name` already
defined
- `(keyword_query)`: `property`, similar to `feature_name`. [CSS
reference](https://www.w3.org/TR/mediaqueries-3/#media1)
- `keyword_query`: `constant.builtin`, [CSS
reference](https://www.w3.org/TR/mediaqueries-3/#media0)
- `plain_value`, `keyframes_name`: `constant.builtin`, [CSS
reference](https://www.w3.org/TR/css-values-3/#value-defs)
- `unit`: `type` -> `type.unit`,
[Atom](9e4afce058/grammars/tree-sitter-css.cson (L73))
and [VS
Code](336801752d/extensions/css/syntaxes/css.tmLanguage.json (L1393))
also have a `unit` scope for this. [CSS
reference](https://www.w3.org/TR/css3-values/#dimensions)


```css
@media (keyword_query) and keyword_query {}
@supports (feature_name: plain_value) {}
@namespace namespace_name url("string");
namespace_name|tag_name {}
@keyframes keyframes_name {
  to {
    top: 200unit;
    color: #c01045;
  }
}
tag_name::before,
#id_name:nth-child(even),
.class_name[attribute_name=plain_value] {
  property_name: 2em 1.2em;
  --variable: rgb(250, 0, 0);
  color: var(--variable);
  animation: keyframes_name 5s plain_value;
}
```
2025-06-06 17:14:32 -04:00
CharlesChen0823
cf5e76b1b9 git: Add PushTo to select which remote to push (#31482)
mostly, I using `git checkout -b branch_name upstream/main` to create
new branch which reference remote upstream not my fork.
When using `Push` will always failed with not permission. So we need
ability to select which remote to push.

Current branch is based on my previous pr #26897 

Release Notes:

- Add `PushTo` to select which remote to push.

---------

Co-authored-by: Cole Miller <cole@zed.dev>
2025-06-06 21:07:40 +00:00
tidely
9775747ba9 gpui: Pre-allocate paths in open file dialog (#32106)
Pre-allocates the required memory for storing paths returned by open
file dialog on windows

Release Notes:

- N/A
2025-06-06 17:07:24 -04:00
not a cow
b7c2d4876c Fix syntax highlighting conflicts with certain glsl types (#32022)
added first line pattern for glsl because some extensions conflict with
others like fragment shaders (.fs) would be highlighted by f#

<details>

| no f# extension no fix | f# extension no fix | f# extension with fix |
|--------|--------|--------|
|
![zed_TW1mkwXSMS](https://github.com/user-attachments/assets/d3d64b44-ced5-41a8-86b1-36cafc92f7e4)
|
![zed_T5ewceKmHo](https://github.com/user-attachments/assets/08ced11d-c2c6-4b73-964d-768e8ba763da)
|
![zed_9Rhkc5flQZ](https://github.com/user-attachments/assets/9e949d00-4e69-4687-9863-e0ab38b8b3df)
|

</details>

Release Notes:

- glsl: Added a workaround for an issue where fragment shaders with the `.fs` file extension would be misidentified as F#. Now, adding `#version {version}` to the first line of your fragment shader will ensure it is always recognized as glsl
2025-06-06 20:58:18 +00:00
Finn Evers
2fe1293fba Improve cursor style behavior for some draggable elements (#31965)
Follow-up to #24797

This PR ensures some cursor styles do not change for draggable elements
during dragging. The linked PR covered this on the higher level for
draggable divs. However, e.g. the pane divider inbetween two editors is
not a draggable div and thus still has the issue that the cursor style
changes during dragging. This PR fixes this issue by setting the hitbox
to `None` in cases where the element is currently being dragged, which
ensures the cursor style is applied to the cursor no matter what during
dragging.

Namely, this change fixes this for
- non-div pane dividers
- minimap slider and the
- editor scrollbars

and implements it for the UI scrollbars (Notably, UI scrollbars do
already have `cursor_default` on their parent container but would not
keep this during dragging. I opted out on removing this from the parent
containers until #30194 or a similar PR is merged).


https://github.com/user-attachments/assets/f97859dd-5f1d-4449-ab92-c27f2d933c4a

Release Notes:

- N/A
2025-06-06 16:56:27 -04:00
Michael Sloan
e0057ccd0f Fix anchor biases for completion replacement ranges (esp slash commands) (#32262)
Closes #32205

The issue was that in some places the end of the replacement range used
anchors with `Bias::Left` instead of `Bias::Right`. Before #31872
completions were recomputed on every change and so the anchor bias
didn't matter. After that change, the end anchor didn't move as the
user's typing. Changing it to `Bias::Right` to "stick" to the character
to the right of the cursor fixes this.

Release Notes:

- Fixes incorrect auto-completion of `/files` in text threads (Preview
Only)
2025-06-06 20:54:00 +00:00
Michael Sloan
51585e770d Contextualize errors from extensions with extension name and version (#32202)
Before this I'd get log lines like

> ERROR [project] missing `database_url` setting

Now it's:

> ERROR [project] from extension "Postgres Context Server" version
0.0.3: missing `database_url` setting

Release Notes:

- N/A
2025-06-06 14:47:46 -06:00
Elijah McMorris
52fa7ababb lmstudio: Fill max_tokens using the response from /models (#25606)
The info for `max_tokens` for the model is included in
`{api_url}/models`
I don't think this needs to be `.clamp` like in
`crates/ollama/src/ollama.rs` `get_max_tokens`, but it might need to be

## Before:
Every model shows 2k

![image](https://github.com/user-attachments/assets/676075c8-0ceb-44b1-ae27-72ed6a6d783c)

## After:

![image](https://github.com/user-attachments/assets/8291535b-976e-4601-b617-1a508bf44e12)

### Json from `{api_url}/models` with model not loaded
```json
  {
      "id": "qwen2.5-coder-1.5b-instruct-mlx",
      "object": "model",
      "type": "llm",
      "publisher": "lmstudio-community",
      "arch": "qwen2",
      "compatibility_type": "mlx",
      "quantization": "4bit",
      "state": "not-loaded",
      "max_context_length": 32768
    },
```

## Notes
The response from `{api_url}/models` seems to return the `max_tokens`
for the model, not the currently configured context length, but I think
showing the `max_tokens` for the model is better than setting 2k for
everything

`loaded_context_length` exists, but only if the model is loaded at the
startup of zed, which usually isn't the case

maybe `fetch_models` should be rerun when swapping lmstudio models

### Currently configured context
this isn't shown in `{api_url}/models`

![image](https://github.com/user-attachments/assets/8511cb9d-914b-4065-9eba-c0b086ad253b)

### Json from `{api_url}/models` with model loaded
```json
  {
     "id": "qwen2.5-coder-1.5b-instruct-mlx",
      "object": "model",
      "type": "llm",
      "publisher": "lmstudio-community",
      "arch": "qwen2",
      "compatibility_type": "mlx",
      "quantization": "4bit",
      "state": "loaded",
      "max_context_length": 32768,
      "loaded_context_length": 4096
    },
```

Release Notes:

- lmstudio: Fixed showing `max_tokens` in the assistant panel

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-06 20:21:23 +00:00
Conrad Irwin
5ad51ca48e vim: Show 'j' from jk pre-emptively (#32007)
Fixes: #29812
Fixes: #22538

Co-Authored-By: <corentinhenry@gmail.com>

Release Notes:

- vim: Multi-key bindings in insert mode will now show the pending
keystroke in the buffer. For example if you have `jk` mapped to escape,
pressing `j` will immediately show a `j`.
2025-06-06 14:11:51 -06:00
Diógenes Castro
35a119d573 Add Go debugging example to debugger documentation (#31798)
This pull request updates the documentation for the debugger to include
Go-specific examples alongside existing Python examples.

Documentation update:

*
[`docs/src/debugger.md`](diffhunk://#diff-aa14715cca56f3ad6a32c669b0c317250dab212b8108136b7ca79217465f39b8R69-R80):
Added a new "Go examples" section with a JSON snippet demonstrating how
to configure the debugger for Go using Delve.

Release Notes:

- debugger: Add Go debugging example to debugger documentation

---------

Co-authored-by: Cole Miller <cole@zed.dev>
2025-06-06 19:51:09 +00:00
Joseph T. Lyons
bbf3b20fc3 Fix panic when dragging pinned item left in pinned region (#32263)
This was a regression with my recent fixes to pinned tabs. Dragging a
pinned tab left in the pinned region would still update the pinned tab
count, which would later cause an out-of-bounds later when it used that
value to index into a vec.

https://zed-industries.slack.com/archives/C04S6T1T7TQ/p1749220447796559

Release Notes:

- Fixed a panic caused by dragging a pinned item to the left in the
pinned region
2025-06-06 15:35:18 -04:00
Peter Tripp
c1b997002a ci: Auto-release release prefix hotfixes again (#32265)
Undo:
- https://github.com/zed-industries/zed/pull/24894

CC: @JosephTLyons

Release Notes:

- N/A
2025-06-06 15:29:33 -04:00
Mikayla Maki
69e99b9f2f Remove unescessary unimplemented (#32264)
Release Notes:

- N/A
2025-06-06 11:25:21 -07:00
Peter Tripp
0fc85a020a Fix script/build-linux with newer GCC (#32029)
If you follow the [workaround advice in the
Docs](https://zed.dev/docs/development/linux#installing-a-development-build)
for building with a newer GCC, it will error:


79b1dd7db8/script/bundle-linux (L88-L91)

[Reported on
Discord](https://discord.com/channels/869392257814519848/1379093394105696288)

Release Notes:

- Fixed `script/build-linux` for non-musl builds.
2025-06-06 13:58:13 -04:00
Pavle Sokic
974f724151 vim: Enable window shortcuts in Agent panel (#31000)
Release Notes:

- Enabled vim window commands (ctrl-w X) when agent panel is focused

Co-authored-by: Cole Miller <cole@zed.dev>
2025-06-06 17:20:04 +00:00
Matin Aniss
ca3f46588a gpui: Implement dynamic window control elements (#30828)
Allows setting element as window control elements which consist of
`Drag`, `Close`, `Max`, or `Min`. This allows you to implement
dynamically sized elements that control the platform window, this is
used for areas such as the title bar. Currently only implemented for
Windows.

Release Notes:

- N/A
2025-06-06 10:11:24 -07:00
Jason Lee
d9efa2860f gpui: Fix scroll area to support two-layer scrolling in different directions (#31062)
Release Notes:

- N/A

---

This change is used to solve the problem of not being able to respond
correctly in two-layer scrolling (in different directions). This is a
common practical requirement.

As in the example, in actual use, there may be a scene with a horizontal
scroll in a vertical scroll. Before the modification, if we scroll up
and down in the area that can scroll horizontally, it will not respond
(because it is blocked by the horizontal scroll layer).

## Before


https://github.com/user-attachments/assets/e8ea0118-52a5-44d8-b419-639d4b6c0793

## After


https://github.com/user-attachments/assets/aa14ddd7-5596-4dc5-9c6e-278aabdfef8e

----

This change may cause many side effects, causing some scrolling details
to be different from before, and more testing and analysis are needed.

I have tested some existing scenarios of Zed (such as opening the Branch
panel on the Editor and scrolling) and it seems to be correct (but it is
possible that I don’t know some interaction details). Here, the person
who added this line of code before needs to evaluate the original
purpose.
2025-06-06 10:06:09 -07:00
Floyd Wang
ac806d982b gpui: Introduce dash array support for PathBuilder (#31678)
A simple way to draw dashed lines.


https://github.com/user-attachments/assets/2105d7b2-42d0-4d73-bb29-83a4a6bd7029

Release Notes:

- N/A
2025-06-06 09:54:21 -07:00
Piotr Osiewicz
73cd6ef92c Add UI for configuring the API Url directly (#32248)
Closes #22901 

Release Notes:

- Copilot Chat endpoint URLs can now be configured via `settings.json`
or Configuration View.
2025-06-06 18:05:40 +02:00
Antonio Scandurra
019a14bcde Replace async-watch with a custom watch (#32245)
The `async-watch` crate doesn't seem to be maintained and we noticed
several panics coming from it, such as:

```
[bug] failed to observe change after notificaton.
zed::reliability::init_panic_hook::{{closure}}::hea8cdcb6299fad6b+154543526
std::panicking::rust_panic_with_hook::h33b18b24045abff4+127578547
std::panicking::begin_panic_handler::{{closure}}::hf8313cc2fd0126bc+127577770
std::sys::backtrace::__rust_end_short_backtrace::h57fe07c8aea5c98a+127571385
__rustc[95feac21a9532783]::rust_begin_unwind+127576909
core::panicking::panic_fmt::hd54fb667be51beea+9433328
core::option::expect_failed::h8456634a3dada3e4+9433291
assistant_tools::edit_agent::EditAgent::apply_edit_chunks::{{closure}}::habe2e1a32b267fd4+26921553
gpui::app::async_context::AsyncApp::spawn::{{closure}}::h12f5f25757f572ea+25923441
async_task::raw::RawTask<F,T,S,M>::run::h3cca0d402690ccba+25186815
<gpui::platform::linux::x11::client::X11Client as gpui::platform::linux::platform::LinuxClient>::run::h26264aefbcfbc14b+73961666
gpui::platform::linux::platform::<impl gpui::platform::Platform for P>::run::hb12dcd4abad715b5+73562509
gpui::app::Application::run::h0f936a5f855a3f9f+150676820
zed::main::ha17f9a25fe257d35+154788471
std::sys::backtrace::__rust_begin_short_backtrace::h1edd02429370b2bd+154624579
std::rt::lang_start::{{closure}}::h3d2e300f10059b0a+154264777
std::rt::lang_start_internal::h418648f91f5be3a1+127502049
main+154806636
__libc_start_main+46051972301573
_start+12358494
```

I didn't find an executor-agnostic watch crate that was well maintained
(we already tried postage and async-watch), so decided to implement it
our own version.

Release Notes:

- Fixed a panic that could sometimes occur when the agent performed
edits.
2025-06-06 16:00:09 +00:00
Bennet Bo Fenner
95d78ff8d5 context server: Make requests type safe (#32254)
This changes the context server crate so that the input/output for a
request are encoded at the type level, similar to how it is done for LSP
requests.

This also makes it easier to write tests that mock context servers, e.g.
you can write something like this now when using the `test-support`
feature of the `context-server` crate:

```rust
create_fake_transport("mcp-1", cx.background_executor())
    .on_request::<context_server::types::request::PromptsList>(|_params| {
        PromptsListResponse {
            prompts: vec![/* some prompts */],
            ..
        }
    })
```

Release Notes:

- N/A
2025-06-06 17:47:21 +02:00
Peter Tripp
454adfacae freebsd: Improve nightly builds of zed-remote-server (#32255)
Follow-up to: https://github.com/zed-industries/zed/pull/29561

Don't create a release
Don't upload artifact to workflow.
Add freebsd support to upload-nightly

Release Notes:

- N/A
2025-06-06 15:30:03 +00:00
CharlesChen0823
edd40566b7 git: Pick which remote to fetch (#26897)
I don't want to fetch `--all` branch, we should can picker which remote
to fetch.

Release Notes:

- Added the `git::FetchFrom` action to fetch from a single remote.

---------

Co-authored-by: Cole Miller <cole@zed.dev>
2025-06-06 11:28:07 -04:00
Ben Kunkle
a40ee74a1f Improve handling of injection.combined injections in SyntaxSnapshot::layers_for_range (#32145)
Closes #27596

The problem in this case was incorrect identification of which language
(layer) contains the selection.

Language layer selection incorrectly assumed that the deepest
`SyntaxLayer` containing a range was the most specific. This worked for
Markdown (base document + injected subtrees) but failed for PHP, where
`injection.combined` injections are used to make HTML logically function
as the base layer, despite being at a greater depth in the layer stack.
This caused HTML to be incorrectly identified as the most specific
language for PHP ranges.

The solution is to track included sub-ranges for syntax layers and
filter out layers that don't contain a sub-range covering the desired
range. The top-level layer is never filtered to ensure gaps between
sibling nodes always have a fallback language, as the top-level layer is
likely more correct than the default language settings.

Release Notes:

- Fixed an issue in PHP where PHP language settings would be
occasionally overridden by HTML language settings
2025-06-06 10:47:28 -04:00
Richard Feldman
2e883be4b5 Add regression test for #11671 (#32250)
I can reproduce #11671 on current Nightly but not on `main`; it looks
like https://github.com/zed-industries/zed/pull/32204 fixed it. So I'm
adding a regression test and closing that issue.

Closes #11671

Release Notes:

- N/A
2025-06-06 14:29:59 +00:00
Jonathan LEI
6ea4d2b30d agent: Fix MCP server handler subscription race condition (#32133)
Closes #32132

Release Notes:

- Fixed MCP server handler subscription race condition causing tools to
not load.

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-06-06 15:32:06 +02:00
Kirill Bulatov
380d8c5662 Pull diagnostics fixes (#32242)
Follow-up of https://github.com/zed-industries/zed/pull/19230

* starts to send `result_id` in pull requests to allow servers to reply
with non-full results
* fixes a bug where disk-based diagnostics were offset after pulling the
diagnostics
* fixes a bug due to which pull diagnostics could not be disabled
* uses better names and comments for the workspace pull diagnostics part

Release Notes:

- N/A
2025-06-06 16:18:05 +03:00
Ben Kunkle
508b604b67 project: Try to make git tests less flaky (#32234)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-06 13:01:42 +00:00
chbk
3da1de2a48 Add JSDoc scope (#29476)
This is a small PR that adds a `.jsdoc` scope to JSDoc tokens, just like
[JSX](3fdbc3090d/crates/languages/src/javascript/highlights.scm (L239))
has a specific scope.
This effectively allows differentiating between JavaScript keywords and
JSDoc tags in comments.

Release Notes:

  - Add scope for JSDoc
2025-06-06 08:31:59 -04:00
Dave Waggoner
8837e5564d Add new terminal hyperlink tests (#28525)
Part of #28238

This PR refactors `FindHyperlink` handling and associated code in
`terminal.rs` into its own file for improved testability, and adds
tests.

Release Notes:

- N/A
2025-06-06 08:08:20 -04:00
Ben Brandt
709523bf36 Store profile per thread (#31907)
This allows storing the profile per thread, as well as moving the logic
of which tools are enabled or not to the profile itself.

This makes it much easier to switch between profiles, means there is
less global state being changed on every profile change.

Release Notes:

- agent panel: allow saving the profile per thread

---------

Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
2025-06-06 12:05:27 +00:00
Piotr Osiewicz
7afee64119 multi_buffer: Refactor diff_transforms field into a separate struct (#32237)
A minor refactor ~needed to unblock #22546; it's pretty hard to add an
extra field to `diff_transforms` dimension, as it is a 2-tuple (which
uses a blanket impl)

Release Notes:

- N/A
2025-06-06 11:06:42 +00:00
shenjack
cd0ef4b982 docs: Add more troubleshooting steps for Windows (#31500)
Release Notes:

- N/A

---------

Co-authored-by: 张小白 <364772080@qq.com>
2025-06-06 18:57:40 +08:00
Roland Crosby
be6f29cc28 terminal: Use conventional XTerm indexed color values (#32200)
Fixes rendering of colors in the terminal to use XTerm's idiosyncratic standard steps instead of the range that was previously in use. Matches the behavior of Alacritty, Ghostty, iTerm2, and every other terminal emulator I've looked at.

Release Notes:

- Fixed rendering of terminal colors for the XTerm 256-color indexed color palette.
2025-06-06 13:02:07 +03:00
Thomas Zahner
38b8e6549f ci: Check for broken links (#30844)
This PR fixes some broken links that where found using
[lychee](https://github.com/lycheeverse/lychee/) as discussed today with
@JosephTLyons and @nathansobo at the RustNL hackathon. Using
[lychee-action](https://github.com/lycheeverse/lychee-action/) we can
scan for broken links daily to prevent issues in the future.
There are still 6 broken links that I didn't know how to fix myself.
See https://github.com/thomas-zahner/zed/actions/runs/15075808232 for
details.

## Missing images

```
Errors in ./docs/src/channels.md

    [ERROR] file:///home/runner/work/zed/zed/docs/.gitbook/assets/channels-3.png | Cannot find file
    [ERROR] file:///home/runner/work/zed/zed/docs/.gitbook/assets/channels-1.png | Cannot find file
    [ERROR] file:///home/runner/work/zed/zed/docs/.gitbook/assets/channels-2.png | Cannot find file
```

These errors are showing up as missing images on
https://zed.dev/docs/channels
I tried to search the git history to see when or why they were deleted
but didn't find anything.

## ./crates/assistant_tools/src/edit_agent/evals/fixtures/zode/prompt.md

There are three errors in that file. I don't fully understand how these
issues were caused historically. Technically it would be possible to
ignore the files but of course if possible we should address the issues.
 
Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-06-06 09:39:35 +00:00
Umesh Yadav
b8c1b54f9e language_models: Fix Mistral tool->user message sequence handling (#31736)
Closes #31491

### Problem
Mistral API enforces strict conversation flow requirements that other
providers don't. Specifically, after a `tool` message, the next message
**must** be from the `assistant` role, not `user`. This causes the
error:
```
"Unexpected role 'user' after role 'tool'"
```
This can also occur in normal conversation flow where mistral doesn't
return the assistant message but that is something which can't be
reproduce reliably.

### Root Cause
When users interrupt an ongoing tool call sequence by sending a new
message, we insert a `user` message directly after a `tool` message,
violating Mistral's protocol.

**Expected Mistral flow:**
```
user → assistant (with tool_calls) → tool (results) → assistant (processes results) → user (next input)
```

**What we were doing:**
```
user → assistant (with tool_calls) → tool (results) → user (interruption) 
```

### Solution
Insert an empty `assistant` message between any `tool` → `user` sequence
in the Mistral provider's request construction. This satisfies Mistral's
API requirements without affecting other providers or requiring UX
changes.

### Testing
To reproduce the original error:
1. Start agent chat with `codestral-latest`
2. Send: "Describe this project using tool call only"
3. Once tool calls begin, send: "stop this"
4. Main branch: API error
5. This fix: Works correctly

Release Notes:

- Fixed Mistral tool calling in some cases
2025-06-06 12:35:22 +03:00
Jakub Sygnowski
c304e964fe Display the first keystroke instead of an error for multi-keystroke binding (#31456)
Ideally we would show multi-keystroke binding, but I'd say this improves
over the status quo.

A partial solution to #27334

Release Notes:

- Fixed spurious warning for lack of edit prediction on multi-keystroke
binding

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-06-06 09:30:57 +00:00
Joseph T. Lyons
53abad5979 Fixed more bugs around moving pinned tabs (#32228)
Closes https://github.com/zed-industries/zed/issues/32199
https://github.com/zed-industries/zed/issues/32229
https://github.com/zed-industries/zed/issues/32230
https://github.com/zed-industries/zed/issues/32232

Release Notes:

- Fixed a bug where if the last tab was a pinned tab and it was dragged
to the right, resulting in a no-op, it would become unpinned
- Fixed a bug where a pinned tab dragged just to the right of the end of
the pinned tab region would become unpinned
- Fixed a bug where dragging a pinned tab from one pane to another
pane's pinned region could result in an existing pinned tab becoming
unpinned when `max_tabs` was reached
- Fixed a bug where moving an unpinned tab to the left, just to the end
of the pinned region, would cause the pinned tabs to become unpinned.
2025-06-06 05:09:48 -04:00
张小白
54e64b2407 windows: Refactor the current ime implementation (#32224)
Release Notes:

- N/A
2025-06-06 07:31:45 +00:00
Michael Sloan
ce8854007f Add crates/assistant_tools/src/evals/fixtures to file_scan_exclusions (#32211)
Particularly got tired of `disable_cursor_blinking/before.rs` (an old
copy of `editor.rs`) showing up in tons of searches

Release Notes:

- N/A
2025-06-06 06:56:41 +00:00
Michael Sloan
3e8565ac25 Initialize zlog default filters on init rather than waiting for settings load (#32209)
Now immediately initializes the zlog filter even when there isn't an env
config. Before this change the default filters were applied after
settings load - I was seeing some `zbus` logs on init.

Also defaults to allowing warnings and errors from the suppressed log
sources. If these turn out to be chatty (they don't seem to be so far),
can bring back more suppression.

Release Notes:

- N/A
2025-06-06 00:49:30 -06:00
Michael Sloan
d801b7b12e Fix bindings_for_action handling of shadowed key bindings (#32220)
Fixes two things:

* ~3 months ago [in PR
#26420](https://github.com/zed-industries/zed/pull/26420/files#diff-33b58aa2da03d791c2c4761af6012851b7400e348922d64babe5fd48ac2a8e60)
`bindings_for_action` was changed to return bindings even when they are
shadowed (when the keystrokes would actually do something else).

* For edit prediction keybindings there was some odd behavior where
bindings for `edit_prediction_conflict` were taking precedence over
bindings for `edit_prediction` even when the `edit_prediction_conflict`
predicate didn't match. The workaround for this was #24812. The way it
worked was:

    - List all bindings for the action

- For each binding, get the highest precedence binding with the same
input sequence

- If the highest precedence binding has the same action, include this
binding. This was the bug - this meant that if a binding in the keymap
has the same keystrokes and action it can incorrectly take display
precedence even if its context predicate does not pass.

- Fix is to check that the highest precedence binding is a full match.
To do this efficiently, it's based on an index within the keymap
bindings.

Also adds `highest_precedence_binding_*` variants which avoid the
inefficiency of building lists of bindings just to use the last.

Release Notes:

- Fixed display of keybindings to skip bindings that are shadowed by a
binding that uses the same keystrokes.
- Fixed display of `editor::AcceptEditPrediction` bindings to use the
normal precedence that prioritizes user bindings.
2025-06-06 06:24:59 +00:00
张小白
37fa42d5cc windows: Fix a typo in function name (#32223)
Release Notes:

- N/A
2025-06-06 06:24:05 +00:00
Michael Sloan
5c9b8e8321 Move workspace::toast_layer::RunAction to zed_actions::toast::RunAction (#32222)
Cleaner to have references to this be `toast::RunAction` matching how it
appears in the keymap, instead of `workspace::RunAction`.

Release Notes:

- N/A
2025-06-06 06:23:09 +00:00
Lucas
96609151c6 Fix typo in assistant_tool.rs (#32207)
Release Notes:

- N/A
2025-06-06 05:23:25 +00:00
Joseph T. Lyons
e37c78bde7 Refactor some logic in handle_tab_drop (#32213)
Tiny little clean up PR after #32184 

Release Notes:

- N/A
2025-06-06 04:49:36 +00:00
Michael Sloan
920ca688a7 Display subtle-mode prediction preview when partial accept modifiers held (#32212)
Closes #27567

Release notes covered by the notes for #32193

Release Notes:

- N/A
2025-06-06 02:52:16 +00:00
Julia Ryan
f62d76159b Fix matching braces in jsx/tsx tags (#32196)
Closes #27998

Also fixed an issue where jumping back from closing to opening tags
didn't work in javascript due to missing brackets in our tree-sitter
query.

Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-05 18:10:22 -07:00
Smit Barmase
6a8fdbfd62 editor: Add multi cursor support for AddSelectionAbove/AddSelectionBelow (#32204)
Closes #31648

This PR adds support for:
- Expanding multiple cursors above/below
- Expanding multiple selections above/below
- Adding new cursors/selections when expansion has already been done.
Existing expansions preserve their state and expand/shrink according to
the action, while new cursors/selections act like freshly created ones.

Tests for both cursor and selections:
- below/above cases
- undo/redo cases
- adding new cursors/selections with existing expansion

Before/After:


https://github.com/user-attachments/assets/d2fd556b-8972-4719-bd86-e633d42a1aa3


Release Notes:

- Improved `AddSelectionAbove` and `AddSelectionBelow` to extend
multiple cursors/selections.
2025-06-06 06:20:12 +05:30
Michael Sloan
711a9e5753 x11: Remove logs for mac-os specific set_edited and show_character_palette (#32203)
Release Notes:

- N/A
2025-06-06 00:14:20 +00:00
Michael Sloan
6de5d29bff Fix caching of Node.js runtime paths and improve error messages (#32198)
* `state.last_options` was never being updated, so the caching wasn't
working.

* If node on the PATH was too old it was logging errors on every
invocation even though managed node is being used.

Release Notes:

- Fixed caching of Node.js runtime paths and improved error messages.
2025-06-05 23:35:43 +00:00
Joseph T. Lyons
d7015e5b8f Fix bugs around tab state loss when moving pinned tabs across panes (#32184)
Closes https://github.com/zed-industries/zed/issues/32181,
https://github.com/zed-industries/zed/issues/32179

In the screenshots: left = nightly, right = dev

Fix 1: A pinned tab dragged into a new split should remain pinned


https://github.com/user-attachments/assets/608a7e10-4ccb-4219-ba81-624298c960b0

Fix 2: Moving a pinned tab from one pane to another should not cause
other pinned tabs to be unpinned


https://github.com/user-attachments/assets/ccc05913-591d-4a43-85bb-3a7164a4d6a8

I also added tests for moving both pinned tabs and unpinned tabs into
existing panes, both into the "pinned" region and the "unpinned" region.

Release Notes:

- Fixed a bug where dragging a pinned tab into a new split would lose
its pinned tab state.
- Fixed a bug where pinned tabs in one pane could be lost when moving
one of the pinned tabs to another pane.
2025-06-05 22:31:30 +00:00
Ben Brandt
ddf70b3bb8 Add mismatched tag threshold parameter to eval function (#32190)
Replace hardcoded 0.10 threshold with configurable parameter and set
0.05 default for most tests, with 0.2 for from_pixels_constructor
eval that produces more mismatched tags.

Release Notes:

- N/A
2025-06-05 21:30:05 +00:00
Michael Sloan
8bd8435887 Fix default keybindings for AcceptPartialEditPrediction to work in subtle mode (#32193)
Closes #27567

Release Notes:

- Fixed default keybindings for `editor::AcceptPartialEditPrediction` to
work with subtle mode.

Co-authored-by: Richard <richard@zed.dev>
2025-06-05 21:21:06 +00:00
Conrad Irwin
4b297a9967 Fix innermost brackets panic (#32120)
Release Notes:

- Fixed a few rare panics that could happen when a multibuffer excerpt
started with expanded deleted content.
2025-06-05 20:24:56 +00:00
Vitaly Slobodin
7aa70a4858 lsp: Implement support for the textDocument/diagnostic command (#19230)
Closes [#13107](https://github.com/zed-industries/zed/issues/13107)

Enabled pull diagnostics by default, for the language servers that
declare support in the corresponding capabilities.

```
"diagnostics": {
    "lsp_pull_diagnostics_debounce_ms": null
}
```
settings can be used to disable the pulling.

Release Notes:

- Added support for the LSP `textDocument/diagnostic` command.

# Brief

This is draft PR that implements the LSP `textDocument/diagnostic`
command. The goal is to receive your feedback and establish further
steps towards fully implementing this command. I tried to re-use
existing method and structures to ensure:

1. The existing functionality works as before
2. There is no interference between the diagnostics sent by a server and
the diagnostics requested by a client.

The current implementation is done via a new LSP command
`GetDocumentDiagnostics` that is sent when a buffer is saved and when a
buffer is edited. There is a new method called `pull_diagnostic` that is
called for such events. It has debounce to ensure we don't spam a server
with commands every time the buffer is edited. Probably, we don't need
the debounce when the buffer is saved.

All in all, the goal is basically to get your feedback and ensure I am
on the right track. Thanks!


## References

1.
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_pullDiagnostics

## In action

You can clone any Ruby repo since the `ruby-lsp` supports the pull
diagnostics only.

Steps to reproduce:

1. Clone this repo https://github.com/vitallium/stimulus-lsp-error-zed
2. Install Ruby (via `asdf` or `mise).
4. Install Ruby gems via `bundle install`
5. Install Ruby LSP with `gem install ruby-lsp`
6. Check out this PR and build Zed
7. Open any file and start editing to see diagnostics in realtime.



https://github.com/user-attachments/assets/0ef6ec41-e4fa-4539-8f2c-6be0d8be4129

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-06-05 19:42:52 +00:00
Oleksiy Syvokon
04cd3fcd23 google: Add latest versions of Gemini 2.5 Pro and Flash Preview (#32183)
Release Notes:

- Added the latest versions of Gemini 2.5 Pro and Flash Preview
2025-06-05 19:30:34 +00:00
Michael Sloan
d15d85830a snippets: Fix tabstop completion choices (#31955)
I'm not sure when snippet tabstop choices broke, I checked the parent of
#31872 and they also don't work before that.

Release Notes:

- N/A
2025-06-05 19:17:41 +00:00
VladKopylets
ccc173ebb1 Fix "j" key latency in vim mode with "j k" keymap (#31163)
Problem:
Initial keymap has "j k" keymap, which if uncommented will add +-1s
delay to every "j" key press
This workaround was taken from
https://github.com/zed-industries/zed/discussions/6661

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Michael Sloan <michael@zed.dev>
2025-06-05 13:16:59 -06:00
Michael Sloan
03a030fd00 Add default method for CompletionProvider::resolve_completions (#32045)
Release Notes:

- N/A
2025-06-05 19:15:06 +00:00
Richard Feldman
894f3b9d15 Make a test no longer pub (#32177)
I spotted this while working on something else. Very quick fix!

Release Notes:

- N/A
2025-06-05 17:27:45 +00:00
Cole Miller
f36143a461 debugger: Run locators on LSP tasks for the new process modal (#32097)
- [x] pass LSP tasks into list_debug_scenarios
- [x] load LSP tasks only once for both modals
- [x] align ordering
- [x] improve appearance of LSP debug task icons
- [ ] reconsider how `add_current_language_tasks` works
- [ ] add a test

Release Notes:

- Debugger Beta: Added debuggable LSP tasks to the "Debug" tab of the
new process modal.

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-06-05 13:25:51 -04:00
Oleksiy Syvokon
8730d317a8 themes: Swap ANSI white with ANSI black (#32175)
Here’s how it looks after the fix:
![colors](https://github.com/user-attachments/assets/11c78ad6-da50-4aad-b133-9be5e3844878).
White is white and black is black, as intended.

In some cases, dimmed colors were poorly defined, so I took
`text.dimmed` values.

Note that white is defined exactly as the background color for light
themes. Similarly, black is the exact background color for dark themes.
I didn’t change this, but many themes intentionally make white and black
slightly different from the background color. This prevents issues where
programs assume, say, a dark background and set the foreground to white,
making text invisible. I'm not sure if we want to adjust these themes to
address this; just noting it here.


Closes #29379 

Release Notes:

- Fixed ANSI black and ANSI white colors in built-in themes
2025-06-05 17:14:39 +00:00
Cole Miller
783b33b5c9 git: Rewrap commit messages just before committing instead of interactively (#32114)
Closes #27508 

Release Notes:

- Fixed unintuitive wrapping behavior when editing Git commit messages.
2025-06-05 17:12:17 +00:00
Bennet Bo Fenner
28da99cc06 anthropic: Fix error when attaching multiple images (#32092)
Closes #31438

Release Notes:

- agent: Fixed an edge case were the request would fail when using
Claude and multiple images were attached

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2025-06-05 16:29:49 +00:00
Cole Miller
d2c265c71f debugger: Change some text in the launch tab (#32170)
I think using `Debugger Program` and `~/bin/debugger` here makes it seem
like that field is for specifying the path to the debugger itself, and
not the program being debugged.

Release Notes:

- N/A
2025-06-05 12:29:18 -04:00
Peter Tripp
bbd431ae8c Build zed-remote-server on FreeBSD (#29561)
Builds freebsd-remote-server under qemu working on linux runners.

Release Notes:

- Initial support for ssh remotes running FreeBSD x86_64
2025-06-05 11:59:10 -04:00
张小白
5b9d3ea097 windows: Only call TranslateMessage when we can't handle the event (#32166)
This PR improves key handling on Windows by moving the
`TranslateMessage` call from the message loop to after we handled
`WM_KEYDOWN`. This brings Windows behavior more in line with macOS and
gives us finer control over key events. As a result, Vim keybindings now
work properly even when an IME is active. The trade-off is that it might
introduce a slight delay in text input.


Release Notes:

- N/A
2025-06-05 23:57:47 +08:00
tidely
738cfdff84 gpui: Simplify u8 to u32 conversion (#32099)
Removes an allocation when converting four u8 into a u32.
Makes some functions const compatible.

Release Notes:

- N/A
2025-06-05 23:57:27 +08:00
Cole Miller
32d5a2cca0 debugger: Fix wrong variant of new process modal deployed (#32168)
I think this was added back when the `Launch` variant meant what we now
call `Debug`

Release Notes:

- N/A
2025-06-05 15:51:13 +00:00
Ben Brandt
dda614091a eval: Add eval unit tests as a CI job (#32152)
We run the unit evals once a day in the middle of the night, and trigger
a Slack post if it fails.


Release Notes:

- N/A

---------

Co-authored-by: Oleksiy Syvokon <oleksiy.syvokon@gmail.com>
2025-06-05 13:16:27 +00:00
Hans
fa9da6ad5b Fix typo (#32160)
Release Notes:

- N/A
2025-06-05 12:59:22 +00:00
Piotr Osiewicz
d082cfdbec lsp: Fix language servers not starting up on save (#32156)
Closes #24349

Release Notes:

- Fixed language servers not starting up when a buffer is saved.

---------

Co-authored-by: 张小白 <364772080@qq.com>
2025-06-05 14:22:34 +02:00
张小白
c71791d64e windows: Fix Japanese IME (#32153)
Fixed an issue where pressing `Escape` wouldn’t clear all pre-edit text
when using Japanese IME.


Release Notes:

- N/A
2025-06-05 12:13:09 +00:00
InfyniteHeap
244d8517f1 Fix Unexpected Console Window When Running Zed Release Build (#32144)
The commit #31073 had introduced `zed-main.rs`, which replaced the
previous `main.rs` to be the "true" entry of the whole program. But as
the macro `#![cfg_attr(not(debug_assertions), windows_subsystem =
"windows")]` only works in the "true" entry, the release build will also
arise the console window if this macro doesn't move to the new entry
(the `zed-main.rs` here).


Release Notes:

- N/A
2025-06-05 19:38:19 +08:00
Oleksiy Syvokon
3884de937b assistant: Partial fix for HTML entities in tools params (#32148)
This problem seems to be specific to Opus 4. Eval shows improvement from
89% to 97%.

Closes: https://github.com/zed-industries/zed/issues/32060

Release Notes:

- N/A

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-05 10:36:55 +00:00
Richard Feldman
8af984ae70 Have tools respect private and excluded file settings (#32036)
Based on a Slack conversation with @notpeter - this prevents secrets in
private/excluded files from being sent by the agent to third parties for
tools that don't require confirmation.

Of course, the agent can still use the terminal tool or MCP to access
these, but those require confirmation before they run (unlike these
tools).

This change doesn't seem to cause any trouble for evals:

<img width="730" alt="Screenshot 2025-06-03 at 8 48 33 PM"
src="https://github.com/user-attachments/assets/d90221be-f946-4af2-b57b-4aa047e86853"
/>


Release Notes:

- N/A
2025-06-05 10:02:11 +02:00
Kirill Bulatov
9d533f9d30 Allow to reuse windows in open remote projects dialogue (#32138)
Closes https://github.com/zed-industries/zed/issues/26276

Same as other "open window" actions like "open recent", add a
`"create_new_window": false` (default `false`) argument into the
`projects::OpenRemote` action.

Make all menus to use this default; allow users to change this in the
keybindings.
Same as with other actions, `cmd`/`ctrl` inverts the parameter value.

<img width="554" alt="default"
src="https://github.com/user-attachments/assets/156d50f0-6511-47b3-b650-7a5133ae9541"
/>

<img width="552" alt="override"
src="https://github.com/user-attachments/assets/cf7d963b-86a3-4925-afec-fdb5414418e1"
/>

Release Notes:

- Allowed to reuse windows in open remote projects dialogue
2025-06-05 07:09:09 +00:00
Ben Swift
274a40b7e0 docs: Fix missing comma in MCP code snippet (#32126)
the docs now contain valid json

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-05 00:00:51 -03:00
Cole Miller
9c7b1d19ce Fix a panic in merge conflict parsing (#32119)
Release Notes:

- Fixed a panic that could occur when editing files containing merge
conflicts.
2025-06-04 20:05:26 -04:00
Michael Sloan
3d9881121f Reapply support for pasting images on x11 (#32121)
This brings back [linux(x11): Add support for pasting images from
clipboard · Pull Request
#29387](https://github.com/zed-industries/zed/pull/29387) while fixing
#30523 (which caused it to be reverted).

Commit message from that PR:

> Closes:
https://github.com/zed-industries/zed/pull/29177#issuecomment-2823359242
>
> Removes dependency on
[quininer/x11-clipboard](https://github.com/quininer/x11-clipboard) as
it is in [maintenance
mode](https://github.com/quininer/x11-clipboard/issues/19).
>
> X11 clipboard functionality is now built-in to GPUI which was
accomplished by stripping the non-x11-related code/abstractions from
[1Password/arboard](https://github.com/1Password/arboard) and extending
it to support all image formats already supported by GPUI on wayland and
macos.
>
> A benefit of switching over to the `arboard` implementation, is that
we now make an attempt to have an X11 "clipboard manager" (if available
- something the user has to setup themselves) save the contents of
clipboard (if the last copy operation was within Zed) so that the copied
contents can still be pasted once Zed has completely stopped.

Before the fix for reapply, it was iterating through the formats and
requesting conversion to each. Some clipboard providers just respond
with a different format rather than saying the format is unsupported.
The fix is to use this response if it matches a supported format. It
also now typically avoids this iteration by requesting the `TARGETS` and
taking the highest precedence supported target.

Closes #30523

Release Notes:

- Linux (X11): Restored the ability to paste images.

---------

Co-authored-by: Ben <ben@zed.dev>
2025-06-05 00:05:11 +00:00
Conrad Irwin
a2e98e9f0e Fix potential race-condition in DisplayLink::drop on macOS (#32116)
Fix a segfault in CVDisplayLink

We see 1-2 crashes a day on macOS on the `CVDisplayLink` thread.

```
Segmentation fault: 11 on thread 9325960 (CVDisplayLink)
CoreVideo	CVHWTime::reset()
CoreVideo	CVXTime::reset()
CoreVideo	CVDisplayLink::runIOThread()
libsystem_pthread.dylib	_pthread_start
libsystem_pthread.dylib	thread_start
```

With the help of the Zed AI, I dove into the crash report, which looks
like this:

```
Crashed Thread:        49  CVDisplayLink

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x00000000000001f6
Exception Codes:       0x0000000000000001, 0x00000000000001f6

Thread 49 Crashed:: CVDisplayLink
0   CoreVideo                     	       0x18c1ed994 CVHWTime::reset() + 64
1   CoreVideo                     	       0x18c1ee474 CVXTime::reset() + 52
2   CoreVideo                     	       0x18c1ee198 CVDisplayLink::runIOThread() + 176
3   libsystem_pthread.dylib       	       0x18285ac0c _pthread_start + 136
4   libsystem_pthread.dylib       	       0x182855b80 thread_start + 8

Thread 49 crashed with ARM Thread State (64-bit):
    x0: 0x0000000000000000   x1: 0x000000018c206e08   x2: 0x0000002c00001513   x3: 0x0001d4630002a433
    x4: 0x00000e2100000000   x5: 0x0001d46300000000   x6: 0x000000000000002c   x7: 0x0000000000000000
    x8: 0x000000000000002e   x9: 0x000000004d555458  x10: 0x0000000000000000  x11: 0x0000000000000000
   x12: 0x0000000000000000  x13: 0x0000000000000000  x14: 0x0000000000000000  x15: 0x0000000000000000
   x16: 0x0000000182856a9c  x17: 0x00000001f19bc540  x18: 0x0000000000000000  x19: 0x0000600003c56ed8
   x20: 0x000000000002a433  x21: 0x0000000000000000  x22: 0x0000000000000000  x23: 0x0000000000000000
   x24: 0x0000000000000000  x25: 0x0000000000000000  x26: 0x0000000000000000  x27: 0x0000000000000000
   x28: 0x0000000000000000   fp: 0x000000016b02ade0   lr: 0x000000018c1ed984
    sp: 0x000000016b02adc0   pc: 0x000000018c1ed994 cpsr: 0x80001000
   far: 0x00000000000001f6  esr: 0x92000006 (Data Abort) byte read Translation fault

Binary Images:
       0x1828c9000 -        0x182e07fff com.apple.CoreFoundation (6.9) <df489a59-b4f6-32b8-9bb4-9b832960aa52> /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
```

Using lldb to disassemble `CVHWTime::reset()` (and the AI to interpret
it), the crash is caused by dereferencing the pointer at the start of
the CVHWTime struct + 0x1c8. In this case the pointer has (the clearly
nonsense) value 0x2e (and 0x2e + 0x1c8 = 0x1f6, the failing address).

As to how this could happen...

Looking at the implementation of `CVDisplayLinkRelease`, it calls
straight into `CFRelease` on the main thread; and so it is not safe to
call `CVDisplayLinkRelease` concurrently with other threads that access
the CVDisplayLink. While we already stopped the display link, it turns
out that `CVDisplayLinkStop` just sets a flag on the struct to instruct
the io-thread to exit "soon", and returns immediately. That means we
don't know when the other thread will actually exit, and so we can't
safely call `CVDisplayLinkRelease`.

So, for now, we just leak these objects. They should be created
relatively infrequently (when the app is foregrounded/backgrounded), so
I don't think this is a huge problem.

Release Notes:

- Fix a rare crash on macOS when putting the app in the background.
2025-06-04 17:10:27 -06:00
Smit Barmase
7c64737e00 project_panel: Fix drop highlight is not being removed when esc is pressed (#32115)
Release Notes:

- Fixed the issue where pressing `esc` would cancel the drag-and-drop
operation but wouldn’t clear the drop highlight on directories.
2025-06-05 03:53:59 +05:30
Cole Miller
8191a5339d Make editor::Rewrap respect paragraphs (#32046)
Closes #32021 

Release Notes:

- Changed the behavior of `editor::Rewrap` to not join paragraphs
together.
2025-06-04 22:14:38 +00:00
Ben Kunkle
17c3b741ec Validate actions in docs (#31073)
Adds a validation step to docs preprocessing so that actions referenced
in docs are checked against the list of all registered actions in GPUI.

In order for this to work properly, all of the crates that register
actions had to be importable by the `docs_preprocessor` crate and
actually used (see [this
comment](ec16e70336 (diff-2674caf14ae6d70752ea60c7061232393d84e7f61a52915ace089c30a797a1c3))
for why this is challenging).

In order to accomplish this I have moved the entry point of zed into a
separate stub file named `zed_main.rs` so that `main.rs` is importable
by the `docs_preprocessor` crate, this is kind of gross, but ensures
that all actions that are registered in the application are registered
when checking them in `docs_preprocessor`. An alternative solution
suggested by @mikayla-maki was to separate out all our `::init()`
functions into a lib entry point in the `zed` crate that can be imported
instead, however, this turned out to be a far bigger refactor and is in
my opinion better to do in a follow up PR with significant testing to
ensure no regressions in behavior occur.

Release Notes:

- N/A
2025-06-04 19:18:12 +00:00
Martin Pool
52770cd3ad docs: Fix the database path on Linux (and BSD) (#32072)
Updated to reflect the logic in
https://github.com/zed-industries/zed/blob/main/crates/paths/src/paths.rs.

Release Notes:

- N/A
2025-06-04 21:00:27 +03:00
Antonio Scandurra
4ac67ac5ae Automatically keep edits if they are included in a commit (#32093)
Release Notes:

- Improved the review experience in the agent panel. Now, when you
commit changes (generated by the AI agent) using Git, Zed will
automatically dismiss the agent’s review UI for those changes. This
means you won’t have to manually “keep” or approve changes twice—just
commit, and you’re done.
2025-06-04 19:54:24 +02:00
Aaron Ruan
8c1b549683 workspace: Add setting to make dock resize apply to all panels (#30551)
Re: #19015
Close #12667

When dragging a dock’s resize handle, only the active panel grows or
shrinks. This patch introduces an opt-in behaviour that lets users
resize every panel hosted by that dock at once.

Release Notes:

- Added new `resize_all_panels_in_dock` setting to optionally resize
every panel in a dock together.

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2025-06-04 17:40:35 +00:00
Kirill Bulatov
ff6ac60bad Allow running certain Zed actions when headless (#32095)
Rework of https://github.com/zed-industries/zed/pull/30783

Before:

<img width="483" alt="before_1"
src="https://github.com/user-attachments/assets/c08531ce-0c1c-4a91-8375-4542220fc1b1"
/>

<img width="250" alt="before_2"
src="https://github.com/user-attachments/assets/e6f5404e-4e00-4125-bf2b-59a5bc6c41c1"
/>

<img width="369" alt="before_3"
src="https://github.com/user-attachments/assets/6a17c63d-80f6-4d91-a63b-69a9d8fe533a"
/>

After:

<img width="443" alt="after_1"
src="https://github.com/user-attachments/assets/4f7203c2-0065-41da-b7df-02aeba89ab7b"
/>

<img width="246" alt="after_2"
src="https://github.com/user-attachments/assets/585e2e25-bf06-4cdc-bfa5-930e0405c8d0"
/>

<img width="371" alt="after_3"
src="https://github.com/user-attachments/assets/54585f1a-6a9b-45a3-9d77-b0bb1ace580b"
/>


Release Notes:

- Allowed running certain Zed actions when headless
2025-06-04 17:29:08 +00:00
Max Brunsfeld
f8ab51307a Bump tree-sitter-bash to 0.25 (#32091)
Closes https://github.com/zed-industries/zed/issues/23703

Release Notes:

- Fixed a crash that could occur when editing bash files
2025-06-04 13:22:34 -04:00
Nathan Sobo
0a2186c87b Add channel reordering functionality (#31833)
Release Notes:

- Added channel reordering for administrators (use `cmd-up` and
`cmd-down` on macOS or `ctrl-up` `ctrl-down` on Linux to move channels
up or down within their parent)

## Summary

This PR introduces the ability for channel administrators to reorder
channels within their parent context, providing better organizational
control over channel hierarchies. Users can now move channels up or down
relative to their siblings using keyboard shortcuts.

## Problem

Previously, channels were displayed in alphabetical order with no way to
customize their arrangement. This made it difficult for teams to
organize channels in a logical order that reflected their workflow or
importance, forcing users to prefix channel names with numbers or
special characters as a workaround.

## Solution

The implementation adds a persistent `channel_order` field to channels
that determines their display order within their parent. Channels with
the same parent are sorted by this field rather than alphabetically.

## Implementation Details

### Database Schema

Added a new column and index to support efficient ordering:

```sql
-- crates/collab/migrations/20250530175450_add_channel_order.sql
ALTER TABLE channels ADD COLUMN channel_order INTEGER NOT NULL DEFAULT 1;

CREATE INDEX CONCURRENTLY "index_channels_on_parent_path_and_order" ON "channels" ("parent_path", "channel_order");
```

### RPC Protocol

Extended the channel proto with ordering support:

```proto
// crates/proto/proto/channel.proto
message Channel {
    uint64 id = 1;
    string name = 2;
    ChannelVisibility visibility = 3;
    int32 channel_order = 4;
    repeated uint64 parent_path = 5;
}

message ReorderChannel {
    uint64 channel_id = 1;
    enum Direction {
        Up = 0;
        Down = 1;
    }
    Direction direction = 2;
}
```

### Server-side Logic

The reordering is handled by swapping `channel_order` values between
adjacent channels:

```rust
// crates/collab/src/db/queries/channels.rs
pub async fn reorder_channel(
    &self,
    channel_id: ChannelId,
    direction: proto::reorder_channel::Direction,
    user_id: UserId,
) -> Result<Vec<Channel>> {
    // Find the sibling channel to swap with
    let sibling_channel = match direction {
        proto::reorder_channel::Direction::Up => {
            // Find channel with highest order less than current
            channel::Entity::find()
                .filter(
                    channel::Column::ParentPath
                        .eq(&channel.parent_path)
                        .and(channel::Column::ChannelOrder.lt(channel.channel_order)),
                )
                .order_by_desc(channel::Column::ChannelOrder)
                .one(&*tx)
                .await?
        }
        // Similar logic for Down...
    };
    
    // Swap the channel_order values
    let temp_order = channel.channel_order;
    channel.channel_order = sibling_channel.channel_order;
    sibling_channel.channel_order = temp_order;
}
```

### Client-side Sorting

Optimized the sorting algorithm to avoid O(n²) complexity:

```rust
// crates/collab/src/db/queries/channels.rs
// Pre-compute sort keys for efficient O(n log n) sorting
let mut channels_with_keys: Vec<(Vec<i32>, Channel)> = channels
    .into_iter()
    .map(|channel| {
        let mut sort_key = Vec::with_capacity(channel.parent_path.len() + 1);
        
        // Build sort key from parent path orders
        for parent_id in &channel.parent_path {
            sort_key.push(channel_order_map.get(parent_id).copied().unwrap_or(i32::MAX));
        }
        sort_key.push(channel.channel_order);
        
        (sort_key, channel)
    })
    .collect();

channels_with_keys.sort_by(|a, b| a.0.cmp(&b.0));
```

### User Interface

Added keyboard shortcuts and proper context handling:

```json
// assets/keymaps/default-macos.json
{
  "context": "CollabPanel && not_editing",
  "bindings": {
    "cmd-up": "collab_panel::MoveChannelUp",
    "cmd-down": "collab_panel::MoveChannelDown"
  }
}
```

The CollabPanel now properly sets context to distinguish between editing
and navigation modes:

```rust
// crates/collab_ui/src/collab_panel.rs
fn dispatch_context(&self, window: &Window, cx: &Context<Self>) -> KeyContext {
    let mut dispatch_context = KeyContext::new_with_defaults();
    dispatch_context.add("CollabPanel");
    dispatch_context.add("menu");
    
    let identifier = if self.channel_name_editor.focus_handle(cx).is_focused(window) {
        "editing"
    } else {
        "not_editing"
    };
    
    dispatch_context.add(identifier);
    dispatch_context
}
```

## Testing

Comprehensive tests were added to verify:
- Basic reordering functionality (up/down movement)
- Boundary conditions (first/last channels)
- Permission checks (non-admins cannot reorder)
- Ordering persistence across server restarts
- Correct broadcasting of changes to channel members

## Migration Strategy

Existing channels are assigned initial `channel_order` values based on
their current alphabetical sorting to maintain the familiar order users
expect:

```sql
UPDATE channels
SET channel_order = (
    SELECT ROW_NUMBER() OVER (
        PARTITION BY parent_path
        ORDER BY name, id
    )
    FROM channels c2
    WHERE c2.id = channels.id
);
```

## Future Enhancements

While this PR provides basic reordering functionality, potential future
improvements could include:
- Drag-and-drop reordering in the UI
- Bulk reordering operations
- Custom sorting strategies (by activity, creation date, etc.)

## Checklist

- [x] Database migration included
- [x] Tests added for new functionality
- [x] Keybindings work on macOS and Linux
- [x] Permissions properly enforced
- [x] Error handling implemented throughout
- [x] Manual testing completed
- [x] Documentation updated

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-06-04 16:56:33 +00:00
Danilo Leal
c3653f4cb1 docs: Update "Burn Mode" callout in the models page (#31995)
To be merged tomorrow after the release, which will make the "Burn Mode"
terminology live on stable.

Release Notes:

- N/A
2025-06-04 13:56:13 -03:00
Max Brunsfeld
8b28941c14 Bump Tree-sitter to 0.25.6 (#32090)
Fixes #31810 

Release Notes:

- Fixed a crash that could occur when editing YAML files.
2025-06-04 12:55:10 -04:00
Bennet Bo Fenner
aefb798090 inline assistant: Allow to attach images from clipboard (#32087)
Noticed while working on #31848 that we do not support pasting images as
context in the inline assistant

Release Notes:

- agent: Add support for attaching images as context from clipboard in
the inline assistant
2025-06-04 16:43:52 +00:00
Conrad Irwin
2c5aa5891d Don't show invisibles from inlays (#32088)
Closes #24266

Release Notes:

- Whitespace added by inlay hints is no longer shown when
`"show_whitespaces": "all"` is used.

-
2025-06-04 10:33:22 -06:00
Umesh Yadav
7d54d9f45e agent: Show warning for image context pill if model doesn't support images (#31848)
Closes #31781

Currently we don't any warning or error if the image is not supported by
the current model in selected in the agent panel which leads for users
to think it's supported as there is no visual feedback provided by zed.
This PR adds a warning on image context pill to show warning when the
model doesn't support it.

| Before | After |
|--------|-------|
| <img width="374" alt="image"
src="https://github.com/user-attachments/assets/da659fb6-d5da-4c53-8878-7a1c4553f168"
/> | <img width="442" alt="image"
src="https://github.com/user-attachments/assets/0f23d184-6095-47e2-8f2b-0eac64a0942e"
/> |

Release Notes:

- Show warning for image context pill in agent panel when selected model
doesn't support images.

---------

Signed-off-by: Umesh Yadav <git@umesh.dev>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-06-04 16:20:56 +00:00
Oleksiy Syvokon
cde47e60cd assistant_tools: Disallow extra tool parameters by default (#32081)
This prevents models from hallucinating tool parameters.


Release Notes:

- Prevent models from hallucinating tool parameters
2025-06-04 16:11:40 +00:00
Peter Tripp
79f96a5afe docs: Improve LuaLS formatter example (#32084)
- Closes https://github.com/zed-extensions/lua/issues/4

Release Notes:

- N/A
2025-06-04 11:51:53 -04:00
Tommy D. Rossi
81058ee172 Make alt-left and alt-right skip punctuation like VSCode (#31977)
Closes https://github.com/zed-industries/zed/discussions/25526
Follow up of #29872

Release Notes:

- Make `alt-left` and `alt-right` skip punctuation on Mac OS to respect
the Mac default behaviour. When pressing alt-left and the first
character is a punctuation character like a dot, this character should
be skipped. For example: `hello.|` goes to `|hello.`

This change makes the editor feels much snappier, it now follows the
same behaviour as VSCode and any other Mac OS native application.


@ConradIrwin
2025-06-04 09:48:20 -06:00
Alejandro Fernández Gómez
89743117c6 vim: Add Ctrl-w ] and Ctrl-w Ctrl-] keymaps (#31990)
Closes #31989

Release Notes:

- Added support for `Ctrl-w ]` and `Ctrl-w Ctrl-]` to go to a definition
in a new split
2025-06-04 09:47:42 -06:00
Conrad Irwin
6de37fa57c Don't show squiggles on unnecesarry code (#32082)
Co-Authored-By: @davidhewitt <mail@davidhewitt.dev>

Closes #31747
Closes https://github.com/zed-industries/zed/issues/32080

Release Notes:

- Fixed a recently introduced bug where unnecessary code was underlined
with blue squiggles

Co-authored-by: @davidhewitt <mail@davidhewitt.dev>
2025-06-04 09:46:06 -06:00
Bennet Bo Fenner
beb0d49dc4 agent: Introduce ModelUsageContext (#32076)
This PR is a refactor of the existing `ModelType` in
`agent_model_selector`.

In #31848 we also need to know which context we are operating in, to
check if the configured model has image support.
In order to deduplicate the logic needed, I introduced a new type called
`ModelUsageContext` which can be used throughout the agent crate


Release Notes:

- N/A
2025-06-04 15:35:50 +00:00
Conrad Irwin
c9aadadc4b Add a script to connect to the database. (#32023)
This avoids needing passwords in plaintext on the command line....

Release Notes:

- N/A
2025-06-04 09:23:23 -06:00
Conrad Irwin
bcd182f480 A script to help with PR naggery (#32025)
Release Notes:

- N/A
2025-06-04 09:23:14 -06:00
Joseph T. Lyons
3987b60738 Set upstream tracking when pushing preview branch (#32075)
Release Notes:

- N/A
2025-06-04 10:42:50 -04:00
Joseph T. Lyons
827103908e Bump Zed to v0.191 (#32073)
Release Notes:

-N/A
2025-06-04 14:34:01 +00:00
Vitaly Slobodin
8e9e3ba1a5 ruby: Add sorbet and steep to the list of available language servers (#32008)
Hi, this pull request adds `sorbet` and `steep` to the list of available
language servers for the Ruby language in order to prepare default Ruby
language settings for these LS. Both language servers are disabled by
default. We plan to add both in #104 and #102. Thanks!

Release Notes:

- ruby: Added `sorbet` and `steep` to the list of available language servers.
2025-06-04 10:19:33 -04:00
Danilo Leal
676ed8fb8a agent: Use new has_pending_edit_tool_use state for toolbar review buttons (#32071)
Follow up to https://github.com/zed-industries/zed/pull/31971. Now, the
toolbar review buttons will also appear/be available at the same time as
the panel buttons.

Release Notes:

- N/A
2025-06-04 11:14:34 -03:00
Ben Brandt
4304521655 Remove unused load_model method from LanguageModelProvider (#32070)
Removes the load_model trait method and its implementations in Ollama
and LM Studio providers, along with associated preload_model functions
and unused imports.

Release Notes:

- N/A
2025-06-04 14:07:01 +00:00
Oleksiy Syvokon
04716a0e4a edit_file_tool: Fail when edit location is not unique (#32056)
When `<old_text>` points to more than one location in a file, we used to
edit the first match, confusing the agent along the way. Now we will
return an error, asking to expand `<old_text>` selection.

Closes #ISSUE

Release Notes:

- agent: Fixed incorrect file edits when edit locations are ambiguous
2025-06-04 13:04:01 +03:00
Kirill Bulatov
5e38915d45 Properly register buffers with reused language servers (#32057)
Follow-up of https://github.com/zed-industries/zed/pull/30707

The old code does something odd, re-accessing essentially the same
adapter-server pair for every language server initialized; but that was
done before for "incorrect", non-reused worktree_id hence never resulted
in external worktrees' files registration in this code path.

Release Notes:

- Fixed certain external worktrees' files sometimes not registered with
language servers
2025-06-04 09:59:57 +00:00
Alex
f9257b0efe debugger: Use UUID for Go debug binary names, do not rely on OUT_DIR (#32004)
It seems that there was a regression. `build_config` no longer has an
`OUT_DIR` in it.
On way to mitigate it is to stop relying on it and just use `cwd` as dir
for the test binary to be placed in.

Release Notes:
- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-06-04 11:18:04 +02:00
Wanten
5d0c96872b editor: Stabilize IME candidate box position during pre-edit on Wayland (#28429)
Modify the `replace_and_mark_text_in_range` method in the `Editor` to
keep the cursor at the start of the preedit range during IME
composition. Previously, the cursor would move to the end of the preedit
text with each update, causing the IME candidate box to shift (e.g.,
when typing pinyin with Fcitx5 on Wayland). This change ensures the
cursor and candidate box remain fixed until the composition is
committed, improving the IME experience.

Closes #21004

Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: 张小白 <364772080@qq.com>
2025-06-04 09:14:01 +00:00
Umesh Yadav
071e684be4 bedrock: Fix ci failure due model enum and model name mismatch (#32049)
Release Notes:

- N/A
2025-06-04 10:41:12 +03:00
Shardul Vaidya
2280594408 bedrock: Allow users to pick Thinking vs. Non-Thinking models (#31600)
Release Notes:

- bedrock: Added ability to pick between Thinking and Non-Thinking models
2025-06-04 09:00:41 +03:00
Shardul Vaidya
09a1d51e9a bedrock: Fix Claude 4 output token bug (#31599)
Release Notes:

- Fixed an issue preventing the use of Claude 4 Thinking models with Bedrock
2025-06-04 08:57:31 +03:00
Umesh Yadav
ac15194d11 docs: Add OpenRouter agent support (#32011)
Update few other docs as well. Like recently tool support was added for
deepseek. Also there was recent thinking and images support for ollama
model.

Release Notes:

- N/A
2025-06-04 08:54:00 +03:00
Smit Barmase
988d834c33 project_panel: When initiating a drag the highlight selection should jump to the item you've picked up (#32044)
Closes #14496.

In https://github.com/zed-industries/zed/pull/31976, we modified the
highlighting behavior for entries when certain entries or paths are
being dragged over them. Instead of relying on marked entries for
highlighting, we introduced the `highlight_entry_id` parameter, which
determines which entry and its children should be highlighted when an
item is being dragged over it.

The rationale behind that is that we can now utilize marked entries for
various other functions, such as:

1. When dragging multiple items, we use marked entried to show which
items are being dragged. (This is already covered because to drag
multiple items, you need to use marked entries.)
2. When dragging a single item, set that item to marked entries. (This
PR)


https://github.com/user-attachments/assets/8a03bdd4-b5db-467d-b70f-53d9766fec52

Release Notes:

- Added highlighting to entries being dragged in the Project Panel,
indicating which items are being moved.
2025-06-04 08:30:51 +05:30
Michael Sloan
48eacf3f2a Add #[track_caller] to test utilities that involve marked text (#32043)
Release Notes:

- N/A
2025-06-04 02:37:27 +00:00
Smit Barmase
030d4d2631 project_panel: Holding alt or shift to copy the file should adds a green (+) icon to the mouse cursor (#32040)
Part of https://github.com/zed-industries/zed/issues/14496

Depends on new API https://github.com/zed-industries/zed/pull/32028

Holding `alt` or `shift` to copy the file should add a green (+) icon to
the mouse cursor to indicate this is a copy operation.

1. Press `option` first, then drag:


https://github.com/user-attachments/assets/ae58c441-f1ab-423e-be59-a8ec5cba33b0

2. Drag first, then press `option`:


https://github.com/user-attachments/assets/5136329f-9396-4ab9-a799-07d69cec89e2

Release Notes:

- Added copy-drag cursor when pressing Alt or Shift to copy the file in
Project Panel.
2025-06-04 07:16:56 +05:30
Smit Barmase
10df7b5eb9 gpui: Add API for read and write active drag cursor style (#32028)
Prep for https://github.com/zed-industries/zed/pull/32040

Currently, there’s no way to modify the cursor style of the active drag
state after dragging begins. However, there are scenarios where we might
want to change the cursor style, such as pressing a modifier key while
dragging. This PR introduces an API to update and read the current
active drag state cursor.

Release Notes:

- N/A
2025-06-04 06:53:03 +05:30
Haru Kim
55120c4231 Properly load environment variables from the login shell (#31799)
Fixes #11647
Fixes #13888
Fixes #18771
Fixes #19779
Fixes #22437
Fixes #23649
Fixes #24200
Fixes #27601

Zed’s current method of loading environment variables from the login
shell has two issues:
1. Some shells—​fish in particular—​​write specific escape characters to
`stdout` right before they exit. When this happens, the tail end of the
last environment variable printed by `/usr/bin/env` becomes corrupted.
2. If a multi-line value contains an equals sign, that line is
mis-parsed as a separate name-value pair.

This PR addresses those problems by:
1. Redirecting the shell command's `stdout` directly to a temporary
file, eliminating any side effects caused by the shell itself.
2. Replacing `/usr/bin/env` with `sh -c 'export -p'`, which removes
ambiguity when handling multi-line values.

Additional changes:
- Correctly set the arguments used to launch a login shell under `csh`
or `tcsh`.
- Deduplicate code by sharing the implementation that loads environment
variables on first run with the logic that reloads them for a project.



Release Notes:

- N/A
2025-06-03 19:16:26 -06:00
Piotr Osiewicz
8227c45a11 docs: Document debugger.dock setting (#32038)
Closes #ISSUE

Release Notes:

- N/A
2025-06-03 23:38:41 +00:00
Piotr Osiewicz
d23359e19a debugger: Fix issues with running Zed-installed debugpy + hangs when downloading (#32034)
Closes #32018

Release Notes:

- Fixed issues with launching Python debug adapter downloaded by Zed.
You might need to clear the old install of Debugpy from
`$HOME/.local/share/zed/debug_adapters/Debugpy` (Linux) or
`$HOME/Library/Application Support/Zed/debug_adapters/Debugpy` (Mac).
2025-06-04 01:37:25 +02:00
Kirill Bulatov
936ad0bf10 Use better fallback for empty rerun action (#32031)
When invoking the task rerun action before any task had been run, it
falls back to task selection modal.
Adjust this fall back to use the debugger view, if available:

Before:

![before](https://github.com/user-attachments/assets/737d2dc1-15a4-4eea-a5f9-4aff6c7600cc)


After:

![after](https://github.com/user-attachments/assets/43381b85-5167-44e7-a8b0-865a64eaa6ea)


Release Notes:

- N/A
2025-06-03 22:45:37 +00:00
Kirill Bulatov
faa0bb51c9 Better log canonicalization errors (#32030)
Based on
https://github.com/zed-industries/zed/issues/18673#issuecomment-2933025951

Adds an anyhow error context with the path used for canonicalization
(also, explicitly mention path at the place from the comment).

Release Notes:

- N/A
2025-06-03 22:30:59 +00:00
Joseph T. Lyons
2db2271e3c Do not activate inactive tabs when pinning or unpinning
Closes https://github.com/zed-industries/zed/issues/32024

Release Notes:

- Fixed a bug where inactive tabs would be activated when pinning or
unpinning.
2025-06-03 17:43:06 -04:00
Conrad Irwin
79b1dd7db8 Improve collab cleanup (#32000)
Co-authored-by: Max <max@zed.dev>
Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>

Release Notes:

- N/A
2025-06-03 15:27:28 -06:00
Anthony Eid
81f8e2ed4a Limit BufferSnapshot::surrounding_word search to 256 characters (#32016)
This is the first step to closing #16120. Part of the problem was that
`surrounding_word` would search the whole line for matches with no
limit.

Co-authored-by: Conrad Irwin \<conrad@zed.dev\>
Co-authored-by: Ben Kunkle \<ben@zed.dev\>
Co-authored-by: Cole Miller \<cole@zed.dev\>

Release Notes:

- N/A
2025-06-03 21:08:59 +00:00
Gilles Peiffer
b9256dd469 editor: Apply common_prefix_len refactor suggestion (#31957)
This adds João's nice suggestion from
https://github.com/zed-industries/zed/pull/31818#discussion_r2118582616.

Release Notes:

- N/A

---------

Co-authored-by: João Marcos <marcospb19@hotmail.com>
2025-06-03 15:07:14 -06:00
Bennet Bo Fenner
27d3da678c editor: Fix panic when full width crease is wrapped (#31960)
Closes #31919

Release Notes:

- Fixed a panic that could sometimes occur when the agent panel was too
narrow and contained context included via `@`.

---------

Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-06-03 22:59:27 +02:00
Conrad Irwin
03357f3f7b Fix panic when re-editing old message with creases (#32017)
Co-authored-by: Cole Miller <m@cole-miller.net>

Release Notes:

- agent: Fixed a panic when re-editing old messages

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Cole Miller <cole@zed.dev>
2025-06-03 20:56:18 +00:00
Kirill Bulatov
4aabba6cf6 Improve Zed prompts for file path selection (#32014)
Part of https://github.com/zed-industries/zed/discussions/31653
`"use_system_path_prompts": false` is needed in settings for these to
appear as modals for new file save and file open.

Fixed a very subpar experience of the "save new file" Zed modal,
compared to a similar "open file path" Zed modal by uniting their code.

Before:


https://github.com/user-attachments/assets/c4082b70-6cdc-4598-a416-d491011c8ac4


After:



https://github.com/user-attachments/assets/21ca672a-ae40-426c-b68f-9efee4f93c8c


Also 

* alters both prompts to start in the current worktree directory, with
the fallback to home directory.
* adjusts the code to handle Windows paths better

Release Notes:

- Improved Zed prompts for file path selection

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-06-03 20:35:25 +00:00
Michael Sloan
8c46a4f594 Make completions menu stay open after after it's manually requested (#32015)
Also includes a clarity refactoring to remove
`ignore_completion_provider`.

Closes #15549

Release Notes:

- Fixed completions menu closing on typing after being requested while
`show_completions_on_input: false`.
2025-06-03 20:33:52 +00:00
Luke Naylor
522abe8e59 Change default formatter settings for LaTeX (#28727)
Closes: https://github.com/rzukic/zed-latex/issues/77 

## Default formatter: `latexindent`
Before, formatting was delegated to the language server, which just ran
a `latexindent` executable. There was no benefit to running it through
the language server over running it as an "external" formatter in zed.
In fact this was an issue because there was no way to provide an
explicit path for the executable (causing above extension issue). Having
the default settings configure the formatter directly gives more control
to user and removes the number of indirections making it clearer how to
tweak things like the executable path, or extra CLI args, etc...

## Alternative: `prettier`
Default settings have also been added to allow prettier as the formatter
(by just setting `"formatter": "prettier"` in the "LaTeX" language
settings). This was not possible before because an extra line needed to
be added to the `prettier` crate (similarly to what was done for
https://github.com/zed-industries/zed/issues/19024) to find the plugin
correctly.
> [!NOTE]
> The `prettier-plugin-latex` node module also contained a
`dist/standalone.js` but using that instead of
`dist/prettier-plugin-latex.js` gave an error, and indeed the latter
worked as intended (along with its questionable choices for formatting).

Release Notes:

- LaTeX: added default `latexindent` formatter settings without relying
on `texlab`, as well as allowing `prettier` to be chosen for formatting

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-03 19:51:30 +00:00
Michael Sloan
5ae8c4cf09 Fix word completions clobbering the text after the cursor (#32010)
Release Notes:

- N/A
2025-06-03 19:37:26 +00:00
Smit Barmase
d8195a8fd7 project_panel: Highlight containing folder which would be the target of the drop operation (#31976)
Part of https://github.com/zed-industries/zed/issues/14496

This PR adds highlighting on the containing folder which would be the
target of the drop operation. It only highlights those directories where
actual drop is possible, i.e. same directory where drag started is not
highlighted.

- [x] Tests


https://github.com/user-attachments/assets/46528467-e07a-4574-a8d5-beab25e70162

Release Notes:

- Improved project panel to show a highlight on the containing folder
which would be the target of the drop operation.
2025-06-04 00:34:37 +05:30
Danilo Leal
2645591cd5 agent: Allow to accept and reject all via the panel (#31971)
This PR introduces the "Reject All" and "Accept All" buttons in the
panel's edit bar, which appears as soon as the agent starts editing a
file. I'm also adding here a new method to the thread called
`has_pending_edit_tool_uses`, which is a more specific way of knowing,
in comparison to the `is_generating` method, whether or not the
reject/accept all actions can be triggered.

Previously, without this new method, you'd be waiting for the whole
generation to end (e.g., the agent would be generating markdown with
things like change summary) to be able to click those buttons, when the
edit was already there, ready for you. It always felt like waiting for
the whole thing was unnecessary when you really wanted to just wait for
the _edits_ to be done, as so to avoid any potential conflicting state.

<img
src="https://github.com/user-attachments/assets/0927f3a6-c9ee-46ae-8f7b-97157d39a7b5"
width="500"/>

---

Release Notes:

- agent: Added ability to reject and accept all changes from the agent
panel.

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-06-03 15:20:25 -03:00
Danilo Leal
526a7c0702 agent: Support AGENT.md and AGENTS.md as rules file names (#31998)
These started to be used more recently, so we should also support them.

Release Notes:

- agent: Added support for `AGENT.md` and `AGENTS.md` as rules file
names.
2025-06-03 15:19:39 -03:00
Danilo Leal
e793740168 agent: Refine rules library window design (#31994)
Just polishing up a bit the Rules Library design. I think the most
confusing part here was the icon that was being used to tag a rule as
default; I've heard feedback more than once saying that was confusing,
so I'm now switching to a rather standard star icon, which I'd assume is
well-understood as a "favoriting" affordance.

Release Notes:

- N/A
2025-06-03 14:59:17 -03:00
Danilo Leal
dea0a58727 docs: Update mentions to GitHub to use correct capitalization (#31996)
That type of thing... 😅 "Github" is the incorrect formatting; "GitHub"
is the correct.

Release Notes:

- N/A
2025-06-03 14:55:24 -03:00
Agus Zubiaga
b7abc9d493 agent: Display full terminal output without scrolling (#31922)
The terminal tool card used a fixed height and scrolling, but this meant
that it was too tall for commands that only outputted a few lines, and
the nested scrolling was undesirable.

This PR makes the card be as too as needed to fit the entire output (no
scrolling), and allows the user to collapse it to fewer lines when
applicable. Making it work the same way as the edit tool card. In fact,
both tools now use a shared UI component.


https://github.com/user-attachments/assets/1127e21d-1d41-4a4b-a99f-7cd70fccbb56


Release Notes:

- Agent: Display full terminal output
- Agent: Allow collapsing terminal output
2025-06-03 10:54:25 -07:00
Peter Tripp
01a77bb231 Add sql language docs (#32003)
Closes: https://github.com/zed-industries/zed/issues/9537

Pairs with removing `prettier-plugin-sql` from the sql extension:
- https://github.com/zed-extensions/sql/pull/19

Release Notes:

- N/A
2025-06-03 13:52:42 -04:00
Daniel Zhu
de225fd242 file_finder: Add option to create new file (#31567)
https://github.com/user-attachments/assets/7c8a05a1-8d59-4371-a1d6-a8cb82aa13b9

While implementing this, I noticed that currently when the search panel
displays only one result, the box oscillates a bit up and down like so:


https://github.com/user-attachments/assets/dd1520e2-fa0b-4307-b27a-984e69b0a644

Not sure how to fix this at the moment, maybe that could be another PR?

Release Notes:

- Add option to create new file in project search panel.
2025-06-03 10:44:57 -07:00
Oleksiy Syvokon
1bc052d76b docs: Gemini thinking budget configuration (#32002)
Release Notes:

- N/A
2025-06-03 20:41:42 +03:00
Ben Kunkle
29cb95a3ca Remove support for changing magnification of active pane (#31981)
Closes #4265
Closes #24600

This setting causes many visual defects, and introduces unnecessary
(maintenance) complexity. as seen by #4265 and #24600


CC: @iamnbutler - How do you feel about this? I recommend looking at
https://github.com/zed-industries/zed/pull/24150#issuecomment-2866706506
for more context

Release Notes:

- Removed support

---------

Co-authored-by: Peter <peter@zed.dev>
2025-06-03 13:32:32 -04:00
Cole Miller
1307b81721 Allow configuring custom git hosting providers in project settings (#31929)
Closes #29229

Release Notes:

- Extended the support for configuring custom git hosting providers to
cover project settings in addition to global settings.

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-06-03 12:23:01 -04:00
Piotr Osiewicz
203754d0db docs: Demote rdbg support in docs (#31993)
Closes #ISSUE

Release Notes:

- N/A
2025-06-03 16:12:21 +00:00
Umesh Yadav
c9c603b1d1 Add support for OpenRouter as a language model provider (#29496)
This pull request adds full integration with OpenRouter, allowing users
to access a wide variety of language models through a single API key.

**Implementation Details:**

* **Provider Registration:** Registers OpenRouter as a new language
model provider within the application's model registry. This includes UI
for API key authentication, token counting, streaming completions, and
tool-call handling.
* **Dedicated Crate:** Adds a new `open_router` crate to manage
interactions with the OpenRouter HTTP API, including model discovery and
streaming helpers.
* **UI & Configuration:** Extends workspace manifests, the settings
schema, icons, and default configurations to surface the OpenRouter
provider and its settings within the UI.
* **Readability:** Reformats JSON arrays within the settings files for
improved readability.

**Design Decisions & Discussion Points:**

* **Code Reuse:** I leveraged much of the existing logic from the
`openai` provider integration due to the significant similarities
between the OpenAI and OpenRouter API specifications.
* **Default Model:** I set the default model to `openrouter/auto`. This
model automatically routes user prompts to the most suitable underlying
model on OpenRouter, providing a convenient starting point.
* **Model Population Strategy:**
* <strike>I've implemented dynamic population of available models by
querying the OpenRouter API upon initialization.
* Currently, this involves three separate API calls: one for all models,
one for tool-use models, and one for models good at programming.
* The data from the tool-use API call sets a `tool_use` flag for
relevant models.
* The data from the programming models API call is used to sort the
list, prioritizing coding-focused models in the dropdown.</strike>
* <strike>**Feedback Welcome:** I acknowledge this multi-call approach
is API-intensive. I am open to feedback and alternative implementation
suggestions if the team believes this can be optimized.</strike>
    * **Update: Now this has been simplified to one api call.**
* **UI/UX Considerations:**
* <strike>Authentication Method: Currently, I've implemented the
standard API key input in settings, similar to other providers like
OpenAI/Anthropic. However, OpenRouter also supports OAuth 2.0 with PKCE.
This could offer a potentially smoother, more integrated setup
experience for users (e.g., clicking a button to authorize instead of
copy-pasting a key). Should we prioritize implementing OAuth PKCE now,
or perhaps add it as an alternative option later?</strike>(PKCE is not
straight forward and complicated so skipping this for now. So that we
can add the support and work on this later.)
* <strike>To visually distinguish models better suited for programming,
I've considered adding a marker (e.g., `</>` or `🧠`) next to their
names. Thoughts on this proposal?</strike>. (This will require a changes
and discussion across model provider. This doesn't fall under the scope
of current PR).
* OpenRouter offers 300+ models. The current implementation loads all of
them. **Feedback Needed:** Should we refine this list or implement more
sophisticated filtering/categorization for better usability?

**Motivation:**

This integration directly addresses one of the most highly upvoted
feature requests/discussions within the Zed community. Adding OpenRouter
support significantly expands the range of AI models accessible to
users.

I welcome feedback from the Zed team on this implementation and the
design choices made. I am eager to refine this feature and make it
available to users.

ISSUES: https://github.com/zed-industries/zed/discussions/16576

Release Notes:

- Added support for OpenRouter as a language model provider.

---------

Signed-off-by: Umesh Yadav <umesh4257@gmail.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-06-03 15:59:46 +00:00
Shardul Vaidya
e13b494c9e bedrock: Fix cross-region inference (#30659)
Closes #30535

Release Notes:

- AWS Bedrock: Add support for Meta Llama 4 Scout and Maverick models.
- AWS Bedrock: Fixed cross-region inference for all regions.
- AWS Bedrock: Updated all models available through Cross Region
inference.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-06-03 15:46:35 +00:00
little-dude
c0397727e0 language_models: Sort Ollama models by name (#31620)
Hello,

This is my first contribution so apologies if I'm not following the
proper process (I haven't seen anything special in
https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md). Also,
I have tested my changes manually, but I could not figure out an easy we
to instantiate a `LanguageModelSelector` in the unit tests, so I didn't
write a test. If you can provide some guidance I'd be happy to write a
test.

---

If the user configured the models with custom names via `display_name`,
we want the ollama models to be sorted based on the name that is
actually displayed.

~~The original issue is only about ollama but this change will also
affect the other providers.~~

Closes #30854

Release Notes:

- Ollama: Changed models to be sorted by name.
2025-06-03 15:37:08 +00:00
Marshall Bowers
9c2b90fb8f collab: Return subscription period from GET /billing/subscriptions (#31987)
This PR updates the `GET /billing/subscriptions` endpoint to return the
subscription period on them.

Release Notes:

- N/A
2025-06-03 15:29:08 +00:00
Marshall Bowers
d108e5f53c collab: Fix deserialization of create meter event response (#31982)
This PR fixes the deserialization of the create meter event response
from the Stripe API.

Release Notes:

- N/A
2025-06-03 15:23:38 +00:00
Marshall Bowers
2551bde1d3 collab: Increase number of returned extensions to 1,000 (#31983)
This PR increases the number of returned extensions from the extension
API to 1,000 (up from 500).

We'll need a better solution at some point, but for now we can keep
bumping this number.

Closes https://github.com/zed-industries/zed/issues/31067.

Release Notes:

- N/A
2025-06-03 15:03:32 +00:00
Peter Tripp
e7de80c6ae ci: Improve Danger and ci.yml explicitness (#31979)
Allow colons after issue links and for them to in ul.
Change ci references from [self-hosted, test] to more explicit
[self-hosted, macOS]

Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-06-03 10:54:04 -04:00
Peter Tripp
ae210eced8 Fix aggressive indent in shell scripts (#31973)
Closes: https://github.com/zed-industries/zed/issues/31774

Release Notes:

- N/A

Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
2025-06-03 10:50:58 -04:00
Piotr Osiewicz
a9d99d8347 docs: Improve docs for debugger (around breakpoints and doc structure) (#31962)
Closes #ISSUE

Release Notes:

- N/A
2025-06-03 16:35:35 +02:00
Thiago Pacheco
3e6435eddc Fix Python virtual environment detection (#31934)
# Fix Python Virtual Environment Detection in Zed

## Problem

Zed was not properly detecting Python virtual environments when a
project didn't contain a `pyrightconfig.json` file. This caused Pyright
(the Python language server) to report `reportMissingImports` errors for
packages installed in virtual environments, even though the virtual
environment was correctly set up and worked fine in other editors.

The issue was that while Zed's `PythonToolchainProvider` correctly
detected virtual environments, this information wasn't being
communicated to Pyright in a format it could understand.

## Root Cause

The main issue was in how Zed communicated virtual environment
configuration to Pyright through the Language Server Protocol (LSP).
When Pyright requests workspace configuration, it expects virtual
environment settings (`venvPath` and `venv`) at the root level of the
configuration object - the same format used in `pyrightconfig.json`
files. However, Zed was attempting to place these settings in various
nested locations that Pyright wasn't checking.

## Solution

The fix involves several coordinated changes to ensure Pyright receives
virtual environment configuration in all the ways it might expect:

### 1. Enhanced Workspace Configuration (`workspace_configuration`
method)
- When a virtual environment is detected, Zed now sets `venvPath` and
`venv` at the root level of the configuration object, matching the exact
format of a `pyrightconfig.json` file
- Uses relative path `"."` when the virtual environment is located in
the workspace root
- Also sets `python.pythonPath` and `python.defaultInterpreterPath` for
compatibility with different Pyright versions

### 2. Environment Variables for All Language Server Binaries
- Updated `check_if_user_installed`, `fetch_server_binary`,
`check_if_version_installed`, and `cached_server_binary` methods to
include shell environment variables
- This ensures environment variables like `VIRTUAL_ENV` are available to
Pyright, helping with automatic virtual environment detection

### 3. Initialization Options
- Added minimal initialization options to enable Pyright's automatic
path searching and import completion features
- Sets `autoSearchPaths: true` and `useLibraryCodeForTypes: true` to
improve Pyright's ability to find packages

## Key Changes

The workspace configuration now properly formats virtual environment
configuration:
- Root level: `venvPath` and `venv` (matches pyrightconfig.json format)
- Python section: `pythonPath` and `defaultInterpreterPath` for
interpreter paths

## Impact

- Users no longer need to create a `pyrightconfig.json` file for virtual
environment detection
- Python projects with virtual environments in standard locations
(`.venv`, `venv`, etc.) will work out of the box
- Import resolution for packages installed in virtual environments now
works correctly
- Maintains compatibility with manual `pyrightconfig.json` configuration
for complex setups

## Testing

The changes were tested with Python projects using virtual environments
without `pyrightconfig.json` files. Pyright now correctly resolves
imports from packages installed in the virtual environment, eliminating
the `reportMissingImports` errors.

## Release Notes

- Fixed Python virtual environment detection when no
`pyrightconfig.json` is present
- Pyright now correctly resolves imports from packages installed in
virtual environments (`.venv`, `venv`, etc.)
- Python projects with virtual environments no longer show false
`reportMissingImports` errors
- Improved Python development experience with automatic virtual
environment configuration

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-06-03 16:35:13 +02:00
Danilo Leal
9e75871d48 agent: Make the sound notification play only if Zed is in the background (#31975)
Users were giving feedback about the sound notification being
annoying/unnecessary if Zed is in the foreground, which I agree! So,
this PR changes it so that it only plays if that is not the case.

Release Notes:

- agent: Improved sound notification behavior by making it play only if
Zed is in the background.
2025-06-03 11:14:26 -03:00
853 changed files with 67211 additions and 28357 deletions

View File

@@ -1,4 +1,4 @@
name: Bug Report (AI Related)
name: Bug Report (AI)
description: Zed Agent Panel Bugs
type: "Bug"
labels: ["ai"]
@@ -19,15 +19,14 @@ body:
2.
3.
Actual Behavior:
Expected Behavior:
**Expected Behavior**:
**Actual Behavior**:
### Model Provider Details
- Provider: (Anthropic via ZedPro, Anthropic via API key, Copilot Chat, Mistral, OpenAI, etc)
- Model Name:
- Mode: (Agent Panel, Inline Assistant, Terminal Assistant or Text Threads)
- MCP Servers in-use:
- Other Details:
- Other Details (MCPs, other settings, etc):
validations:
required: true

View File

@@ -1,36 +0,0 @@
name: Bug Report (Edit Predictions)
description: Zed Edit Predictions bugs
type: "Bug"
labels: ["ai", "inline completion", "zeta"]
title: "Edit Predictions: <a short description of the Edit Prediction bug>"
body:
- type: textarea
attributes:
label: Summary
description: Describe the bug with a one line summary, and provide detailed reproduction steps
value: |
<!-- Please insert a one line summary of the issue below -->
SUMMARY_SENTENCE_HERE
### Description
<!-- Describe with sufficient detail to reproduce from a clean Zed install. -->
<!-- Please include the LLM provider and model name you are using -->
Steps to trigger the problem:
1.
2.
3.
Actual Behavior:
Expected Behavior:
validations:
required: true
- type: textarea
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
placeholder: |
Output of "zed: copy system specs into clipboard"
validations:
required: true

View File

@@ -1,35 +0,0 @@
name: Bug Report (Git)
description: Zed Git-Related Bugs
type: "Bug"
labels: ["git"]
title: "Git: <a short description of the Git bug>"
body:
- type: textarea
attributes:
label: Summary
description: Describe the bug with a one line summary, and provide detailed reproduction steps
value: |
<!-- Please insert a one line summary of the issue below -->
SUMMARY_SENTENCE_HERE
### Description
<!-- Describe with sufficient detail to reproduce from a clean Zed install. -->
Steps to trigger the problem:
1.
2.
3.
Actual Behavior:
Expected Behavior:
validations:
required: true
- type: textarea
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
placeholder: |
Output of "zed: copy system specs into clipboard"
validations:
required: true

View File

@@ -19,8 +19,8 @@ body:
2.
3.
Actual Behavior:
Expected Behavior:
**Expected Behavior**:
**Actual Behavior**:
validations:
required: true

View File

@@ -18,14 +18,16 @@ body:
- Issues with insufficient detail may be summarily closed.
-->
DESCRIPTION_HERE
Steps to reproduce:
1.
2.
3.
4.
Expected Behavior:
Actual Behavior:
**Expected Behavior**:
**Actual Behavior**:
<!-- Before Submitting, did you:
1. Include settings.json, keymap.json, .editorconfig if relevant?

32
.github/actions/build_docs/action.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: "Build docs"
description: "Build the docs"
runs:
using: "composite"
steps:
- name: Setup mdBook
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2
with:
mdbook-version: "0.4.37"
- name: Cache dependencies
uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "buildjet"
- name: Install Linux dependencies
shell: bash -euxo pipefail {0}
run: ./script/linux
- name: Check for broken links
uses: lycheeverse/lychee-action@82202e5e9c2f4ef1a55a3d02563e1cb6041e5332 # v2.4.1
with:
args: --no-progress --exclude '^http' './docs/src/**/*'
fail: true
- name: Build book
shell: bash -euxo pipefail {0}
run: |
mkdir -p target/deploy
mdbook build ./docs --dest-dir=../target/deploy/docs/

View File

@@ -10,8 +10,8 @@ inputs:
runs:
using: "composite"
steps:
- name: Install Rust
shell: pwsh
- name: Install test runner
shell: powershell
working-directory: ${{ inputs.working-directory }}
run: cargo install cargo-nextest --locked
@@ -21,6 +21,6 @@ runs:
node-version: "18"
- name: Run tests
shell: pwsh
shell: powershell
working-directory: ${{ inputs.working-directory }}
run: cargo nextest run --workspace --no-fail-fast --config='profile.dev.debug="limited"'
run: cargo nextest run --workspace --no-fail-fast

View File

@@ -29,6 +29,7 @@ jobs:
outputs:
run_tests: ${{ steps.filter.outputs.run_tests }}
run_license: ${{ steps.filter.outputs.run_license }}
run_docs: ${{ steps.filter.outputs.run_docs }}
runs-on:
- ubuntu-latest
steps:
@@ -58,6 +59,11 @@ jobs:
else
echo "run_tests=false" >> $GITHUB_OUTPUT
fi
if [[ $(git diff --name-only $COMPARE_REV ${{ github.sha }} | grep '^docs/') ]]; then
echo "run_docs=true" >> $GITHUB_OUTPUT
else
echo "run_docs=false" >> $GITHUB_OUTPUT
fi
if [[ $(git diff --name-only $COMPARE_REV ${{ github.sha }} | grep '^Cargo.lock') ]]; then
echo "run_license=true" >> $GITHUB_OUTPUT
else
@@ -73,7 +79,7 @@ jobs:
timeout-minutes: 60
runs-on:
- self-hosted
- test
- macOS
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -183,6 +189,9 @@ jobs:
- name: Check for todo! and FIXME comments
run: script/check-todos
- name: Check modifier use in keymaps
run: script/check-keymaps
- name: Run style checks
uses: ./.github/actions/check_style
@@ -191,6 +200,29 @@ jobs:
with:
config: ./typos.toml
check_docs:
timeout-minutes: 60
name: Check docs
needs: [job_spec]
if: |
github.repository_owner == 'zed-industries' &&
(needs.job_spec.outputs.run_tests == 'true' || needs.job_spec.outputs.run_docs == 'true')
runs-on:
- buildjet-8vcpu-ubuntu-2204
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Configure CI
run: |
mkdir -p ./../.cargo
cp ./.cargo/ci-config.toml ./../.cargo/config.toml
- name: Build docs
uses: ./.github/actions/build_docs
macos_tests:
timeout-minutes: 60
name: (macOS) Run Clippy and tests
@@ -200,7 +232,7 @@ jobs:
needs.job_spec.outputs.run_tests == 'true'
runs-on:
- self-hosted
- test
- macOS
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -349,64 +381,6 @@ jobs:
if: always()
run: rm -rf ./../.cargo
windows_clippy:
timeout-minutes: 60
name: (Windows) Run Clippy
needs: [job_spec]
if: |
github.repository_owner == 'zed-industries' &&
needs.job_spec.outputs.run_tests == 'true'
runs-on: windows-2025-16
steps:
# more info here:- https://github.com/rust-lang/cargo/issues/13020
- name: Enable longer pathnames for git
run: git config --system core.longpaths true
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Create Dev Drive using ReFS
run: ./script/setup-dev-driver.ps1
# actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone...
- name: Copy Git Repo to Dev Drive
run: |
Copy-Item -Path "${{ github.workspace }}" -Destination "${{ env.ZED_WORKSPACE }}" -Recurse
- name: Cache dependencies
uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
workspaces: ${{ env.ZED_WORKSPACE }}
cache-provider: "github"
- name: Configure CI
run: |
mkdir -p ${{ env.CARGO_HOME }} -ErrorAction Ignore
cp ./.cargo/ci-config.toml ${{ env.CARGO_HOME }}/config.toml
- name: cargo clippy
working-directory: ${{ env.ZED_WORKSPACE }}
run: ./script/clippy.ps1
- name: Check dev drive space
working-directory: ${{ env.ZED_WORKSPACE }}
# `setup-dev-driver.ps1` creates a 100GB drive, with CI taking up ~45GB of the drive.
run: ./script/exit-ci-if-dev-drive-is-full.ps1 95
# Since the Windows runners are stateful, so we need to remove the config file to prevent potential bug.
- name: Clean CI config file
if: always()
run: |
if (Test-Path "${{ env.CARGO_HOME }}/config.toml") {
Remove-Item -Path "${{ env.CARGO_HOME }}/config.toml" -Force
}
# Windows CI takes twice as long as our other platforms and fast github hosted runners are expensive.
# But we still want to do CI, so let's only run tests on main and come back to this when we're
# ready to self host our Windows CI (e.g. during the push for full Windows support)
windows_tests:
timeout-minutes: 60
name: (Windows) Run Tests
@@ -414,51 +388,45 @@ jobs:
if: |
github.repository_owner == 'zed-industries' &&
needs.job_spec.outputs.run_tests == 'true'
# Use bigger runners for PRs (speed); smaller for async (cost)
runs-on: ${{ github.event_name == 'pull_request' && 'windows-2025-32' || 'windows-2025-16' }}
runs-on: [self-hosted, Windows, X64]
steps:
# more info here:- https://github.com/rust-lang/cargo/issues/13020
- name: Enable longer pathnames for git
run: git config --system core.longpaths true
- name: Environment Setup
run: |
$RunnerDir = Split-Path -Parent $env:RUNNER_WORKSPACE
Write-Output `
"RUSTUP_HOME=$RunnerDir\.rustup" `
"CARGO_HOME=$RunnerDir\.cargo" `
"PATH=$RunnerDir\.cargo\bin;$env:PATH" `
>> $env:GITHUB_ENV
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Create Dev Drive using ReFS
run: ./script/setup-dev-driver.ps1
# actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone...
- name: Copy Git Repo to Dev Drive
run: |
Copy-Item -Path "${{ github.workspace }}" -Destination "${{ env.ZED_WORKSPACE }}" -Recurse
- name: Cache dependencies
uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
workspaces: ${{ env.ZED_WORKSPACE }}
cache-provider: "github"
- name: Configure CI
- name: Setup Cargo and Rustup
run: |
mkdir -p ${{ env.CARGO_HOME }} -ErrorAction Ignore
cp ./.cargo/ci-config.toml ${{ env.CARGO_HOME }}/config.toml
.\script\install-rustup.ps1
- name: cargo clippy
run: |
.\script\clippy.ps1
- name: Run tests
uses: ./.github/actions/run_tests_windows
with:
working-directory: ${{ env.ZED_WORKSPACE }}
- name: Build Zed
working-directory: ${{ env.ZED_WORKSPACE }}
run: cargo build
- name: Check dev drive space
working-directory: ${{ env.ZED_WORKSPACE }}
# `setup-dev-driver.ps1` creates a 100GB drive, with CI taking up ~45GB of the drive.
run: ./script/exit-ci-if-dev-drive-is-full.ps1 95
- name: Limit target directory size
run: ./script/clear-target-dir-if-larger-than.ps1 250
# - name: Check dev drive space
# working-directory: ${{ env.ZED_WORKSPACE }}
# # `setup-dev-driver.ps1` creates a 100GB drive, with CI taking up ~45GB of the drive.
# run: ./script/exit-ci-if-dev-drive-is-full.ps1 95
# Since the Windows runners are stateful, so we need to remove the config file to prevent potential bug.
- name: Clean CI config file
@@ -474,13 +442,13 @@ jobs:
needs:
- job_spec
- style
- check_docs
- migration_checks
# run_tests: If adding required tests, add them here and to script below.
- workspace_hack
- linux_tests
- build_remote_server
- macos_tests
- windows_clippy
- windows_tests
if: |
github.repository_owner == 'zed-industries' &&
@@ -491,15 +459,17 @@ jobs:
# Check dependent jobs...
RET_CODE=0
# Always check style
[[ "${{ needs.style.result }}" != 'success' ]] && { RET_CODE=1; echo "style tests failed"; }
[[ "${{ needs.style.result }}" != 'success' ]] && { RET_CODE=1; echo "style tests failed"; }
if [[ "${{ needs.job_spec.outputs.run_docs }}" == "true" ]]; then
[[ "${{ needs.check_docs.result }}" != 'success' ]] && { RET_CODE=1; echo "docs checks failed"; }
fi
# Only check test jobs if they were supposed to run
if [[ "${{ needs.job_spec.outputs.run_tests }}" == "true" ]]; then
[[ "${{ needs.workspace_hack.result }}" != 'success' ]] && { RET_CODE=1; echo "Workspace Hack failed"; }
[[ "${{ needs.macos_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "macOS tests failed"; }
[[ "${{ needs.linux_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Linux tests failed"; }
[[ "${{ needs.windows_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Windows tests failed"; }
[[ "${{ needs.windows_clippy.result }}" != 'success' ]] && { RET_CODE=1; echo "Windows clippy failed"; }
[[ "${{ needs.build_remote_server.result }}" != 'success' ]] && { RET_CODE=1; echo "Remote server build failed"; }
# This check is intentionally disabled. See: https://github.com/zed-industries/zed/pull/28431
# [[ "${{ needs.migration_checks.result }}" != 'success' ]] && { RET_CODE=1; echo "Migration Checks failed"; }
@@ -715,10 +685,69 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
freebsd:
timeout-minutes: 60
runs-on: github-8vcpu-ubuntu-2404
if: |
startsWith(github.ref, 'refs/tags/v')
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
needs: [linux_tests]
name: Build Zed on FreeBSD
# env:
# MYTOKEN : ${{ secrets.MYTOKEN }}
# MYTOKEN2: "value2"
steps:
- uses: actions/checkout@v4
- name: Build FreeBSD remote-server
id: freebsd-build
uses: vmactions/freebsd-vm@c3ae29a132c8ef1924775414107a97cac042aad5 # v1.2.0
with:
# envs: "MYTOKEN MYTOKEN2"
usesh: true
release: 13.5
copyback: true
prepare: |
pkg install -y \
bash curl jq git \
rustup-init cmake-core llvm-devel-lite pkgconf protobuf # ibx11 alsa-lib rust-bindgen-cli
run: |
freebsd-version
sysctl hw.model
sysctl hw.ncpu
sysctl hw.physmem
sysctl hw.usermem
git config --global --add safe.directory /home/runner/work/zed/zed
rustup-init --profile minimal --default-toolchain none -y
. "$HOME/.cargo/env"
./script/bundle-freebsd
mkdir -p out/
mv "target/zed-remote-server-freebsd-x86_64.gz" out/
rm -rf target/
cargo clean
- name: Upload Artifact to Workflow - zed-remote-server (run-bundling)
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
with:
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-freebsd.gz
path: out/zed-remote-server-freebsd-x86_64.gz
- name: Upload Artifacts to release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
files: |
out/zed-remote-server-freebsd-x86_64.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
nix-build:
name: Build with Nix
uses: ./.github/workflows/nix.yml
if: github.repository_owner == 'zed-industries' && contains(github.event.pull_request.labels.*.name, 'run-nix')
secrets: inherit
with:
flake-output: debug
# excludes the final package to only cache dependencies
@@ -729,12 +758,12 @@ jobs:
if: |
startsWith(github.ref, 'refs/tags/v')
&& endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre')
needs: [bundle-mac, bundle-linux-x86_x64, bundle-linux-aarch64]
needs: [bundle-mac, bundle-linux-x86_x64, bundle-linux-aarch64, freebsd]
runs-on:
- self-hosted
- bundle
steps:
- name: gh release
run: gh release edit $GITHUB_REF_NAME --draft=true
run: gh release edit $GITHUB_REF_NAME --draft=false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,34 +0,0 @@
name: Delete Mediafire Comments
on:
issue_comment:
types: [created]
permissions:
issues: write
jobs:
delete_comment:
if: github.repository_owner == 'zed-industries'
runs-on: ubuntu-latest
steps:
- name: Check for specific strings in comment
id: check_comment
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
with:
script: |
const comment = context.payload.comment.body;
const triggerStrings = ['www.mediafire.com'];
return triggerStrings.some(triggerString => comment.includes(triggerString));
- name: Delete comment if it contains any of the specific strings
if: steps.check_comment.outputs.result == 'true'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
with:
script: |
const commentId = context.payload.comment.id;
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId
});

View File

@@ -9,7 +9,7 @@ jobs:
deploy-docs:
name: Deploy Docs
if: github.repository_owner == 'zed-industries'
runs-on: ubuntu-latest
runs-on: buildjet-16vcpu-ubuntu-2204
steps:
- name: Checkout repo
@@ -17,24 +17,11 @@ jobs:
with:
clean: false
- name: Setup mdBook
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2
with:
mdbook-version: "0.4.37"
- name: Set up default .cargo/config.toml
run: cp ./.cargo/collab-config.toml ./.cargo/config.toml
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install libxkbcommon-dev libxkbcommon-x11-dev
- name: Build book
run: |
set -euo pipefail
mkdir -p target/deploy
mdbook build ./docs --dest-dir=../target/deploy/docs/
- name: Build docs
uses: ./.github/actions/build_docs
- name: Deploy Docs
uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3

View File

@@ -15,7 +15,7 @@ jobs:
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
- macOS
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -33,7 +33,7 @@ jobs:
name: Run tests
runs-on:
- self-hosted
- test
- macOS
needs: style
steps:
- name: Checkout repo

View File

@@ -7,7 +7,7 @@ on:
pull_request:
branches:
- "**"
types: [opened, synchronize, reopened, labeled]
types: [synchronize, reopened, labeled]
workflow_dispatch:
@@ -25,15 +25,6 @@ env:
ZED_EVAL_TELEMETRY: 1
jobs:
# This is a no-op job that we run to prevent GitHub from marking the workflow
# as failed for PRs that don't have the `run-eval` label.
noop:
name: No-op
runs-on: ubuntu-latest
steps:
- name: No-op
run: echo "Nothing to do"
run_eval:
timeout-minutes: 60
name: Run Agent Eval

View File

@@ -20,7 +20,7 @@ jobs:
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
- macOS
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -40,7 +40,7 @@ jobs:
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
- macOS
needs: style
steps:
- name: Checkout repo
@@ -167,9 +167,54 @@ jobs:
- name: Upload Zed Nightly
run: script/upload-nightly linux-targz
freebsd:
timeout-minutes: 60
if: github.repository_owner == 'zed-industries'
runs-on: github-8vcpu-ubuntu-2404
needs: tests
env:
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
name: Build Zed on FreeBSD
# env:
# MYTOKEN : ${{ secrets.MYTOKEN }}
# MYTOKEN2: "value2"
steps:
- uses: actions/checkout@v4
- name: Build FreeBSD remote-server
id: freebsd-build
uses: vmactions/freebsd-vm@c3ae29a132c8ef1924775414107a97cac042aad5 # v1.2.0
with:
# envs: "MYTOKEN MYTOKEN2"
usesh: true
release: 13.5
copyback: true
prepare: |
pkg install -y \
bash curl jq git \
rustup-init cmake-core llvm-devel-lite pkgconf protobuf # ibx11 alsa-lib rust-bindgen-cli
run: |
freebsd-version
sysctl hw.model
sysctl hw.ncpu
sysctl hw.physmem
sysctl hw.usermem
git config --global --add safe.directory /home/runner/work/zed/zed
rustup-init --profile minimal --default-toolchain none -y
. "$HOME/.cargo/env"
./script/bundle-freebsd
mkdir -p out/
mv "target/zed-remote-server-freebsd-x86_64.gz" out/
rm -rf target/
cargo clean
- name: Upload Zed Nightly
run: script/upload-nightly freebsd
bundle-nix:
name: Build and cache Nix package
needs: tests
secrets: inherit
uses: ./.github/workflows/nix.yml
update-nightly-tag:

86
.github/workflows/unit_evals.yml vendored Normal file
View File

@@ -0,0 +1,86 @@
name: Run Unit Evals
on:
schedule:
# GitHub might drop jobs at busy times, so we choose a random time in the middle of the night.
- cron: "47 1 * * *"
workflow_dispatch:
concurrency:
# Allow only one workflow per any non-`main` branch.
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
jobs:
unit_evals:
if: github.repository_owner == 'zed-industries'
timeout-minutes: 60
name: Run unit evals
runs-on:
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Add Rust to the PATH
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "buildjet"
- name: Install Linux dependencies
run: ./script/linux
- name: Configure CI
run: |
mkdir -p ./../.cargo
cp ./.cargo/ci-config.toml ./../.cargo/config.toml
- name: Install Rust
shell: bash -euxo pipefail {0}
run: |
cargo install cargo-nextest --locked
- name: Install Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "18"
- name: Limit target directory size
shell: bash -euxo pipefail {0}
run: script/clear-target-dir-if-larger-than 100
- name: Run unit evals
shell: bash -euxo pipefail {0}
run: cargo nextest run --workspace --no-fail-fast --features eval --no-capture -E 'test(::eval_)'
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Send failure message to Slack channel if needed
if: ${{ failure() }}
uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52
with:
method: chat.postMessage
token: ${{ secrets.SLACK_APP_ZED_UNIT_EVALS_BOT_TOKEN }}
payload: |
channel: C04UDRNNJFQ
text: "Unit Evals Failed: https://github.com/zed-industries/zed/actions/runs/${{ github.run_id }}"
# Even the Linux runner is not stateful, in theory there is no need to do this cleanup.
# But, to avoid potential issues in the future if we choose to use a stateful Linux runner and forget to add code
# to clean up the config file, Ive included the cleanup code here as a precaution.
# While its not strictly necessary at this moment, I believe its better to err on the side of caution.
- name: Clean CI config file
if: always()
run: rm -rf ./../.cargo

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@
.flatpak-builder
.idea
.netrc
*.pyc
.pytest_cache
.swiftpm
.swiftpm/config/registries.json

10
.rules
View File

@@ -5,6 +5,12 @@
* Prefer implementing functionality in existing files unless it is a new logical component. Avoid creating many small files.
* Avoid using functions that panic like `unwrap()`, instead use mechanisms like `?` to propagate errors.
* Be careful with operations like indexing which may panic if the indexes are out of bounds.
* Never silently discard errors with `let _ =` on fallible operations. Always handle errors appropriately:
- Propagate errors with `?` when the calling function should handle them
- Use `.log_err()` or similar when you need to ignore errors but want visibility
- Use explicit error handling with `match` or `if let Err(...)` when you need custom logic
- Example: avoid `let _ = client.request(...).await?;` - use `client.request(...).await?;` instead
* When implementing async operations that may fail, ensure errors propagate to the UI layer so users get meaningful feedback.
* Never create files with `mod.rs` paths - prefer `src/some_module.rs` instead of `src/some_module/mod.rs`.
# GPUI
@@ -94,9 +100,7 @@ Often event handlers will want to update the entity that's in the current `Conte
Actions are dispatched via user keyboard interaction or in code via `window.dispatch_action(SomeAction.boxed_clone(), cx)` or `focus_handle.dispatch_action(&SomeAction, window, cx)`.
Actions which have no data inside are created and registered with the `actions!(some_namespace, [SomeAction, AnotherAction])` macro call.
Actions that do have data must implement `Clone, Default, PartialEq, Deserialize, JsonSchema` and can be registered with an `impl_actions!(some_namespace, [SomeActionWithData])` macro call.
Actions with no data defined with the `actions!(some_namespace, [SomeAction, AnotherAction])` macro call. Otherwise the `Action` derive macro is used. Doc comments on actions are displayed to the user.
Action handlers can be registered on an element via the event handler `.on_action(|action, window, cx| ...)`. Like other event handlers, this is often used with `cx.listener`.

View File

@@ -2,11 +2,23 @@
{
"label": "Debug Zed (CodeLLDB)",
"adapter": "CodeLLDB",
"build": { "label": "Build Zed", "command": "cargo", "args": ["build"] }
"build": {
"label": "Build Zed",
"command": "cargo",
"args": [
"build"
]
}
},
{
"label": "Debug Zed (GDB)",
"adapter": "GDB",
"build": { "label": "Build Zed", "command": "cargo", "args": ["build"] }
}
"build": {
"label": "Build Zed",
"command": "cargo",
"args": [
"build"
]
}
},
]

View File

@@ -40,6 +40,7 @@
},
"file_types": {
"Dockerfile": ["Dockerfile*[!dockerignore]"],
"JSONC": ["assets/**/*.json", "renovate.json"],
"Git Ignore": ["dockerignore"]
},
"hard_tabs": false,
@@ -47,6 +48,7 @@
"remove_trailing_whitespace_on_save": true,
"ensure_final_newline_on_save": true,
"file_scan_exclusions": [
"crates/assistant_tools/src/edit_agent/evals/fixtures",
"crates/eval/worktrees/",
"crates/eval/repos/",
"**/.git",

449
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,13 @@
resolver = "2"
members = [
"crates/activity_indicator",
"crates/agent_ui",
"crates/agent",
"crates/agent_settings",
"crates/anthropic",
"crates/askpass",
"crates/assets",
"crates/assistant_context_editor",
"crates/assistant_context",
"crates/assistant_slash_command",
"crates/assistant_slash_commands",
"crates/assistant_tool",
@@ -65,6 +66,7 @@ members = [
"crates/gpui",
"crates/gpui_macros",
"crates/gpui_tokio",
"crates/html_to_markdown",
"crates/http_client",
"crates/http_client_tls",
@@ -93,6 +95,7 @@ members = [
"crates/markdown_preview",
"crates/media",
"crates/menu",
"crates/svg_preview",
"crates/migrator",
"crates/mistral",
"crates/multi_buffer",
@@ -100,6 +103,7 @@ members = [
"crates/notifications",
"crates/ollama",
"crates/open_ai",
"crates/open_router",
"crates/outline",
"crates/outline_panel",
"crates/panel",
@@ -162,8 +166,10 @@ members = [
"crates/ui_prompt",
"crates/util",
"crates/util_macros",
"crates/vercel",
"crates/vim",
"crates/vim_mode_setting",
"crates/watch",
"crates/web_search",
"crates/web_search_providers",
"crates/welcome",
@@ -211,12 +217,13 @@ edition = "2024"
activity_indicator = { path = "crates/activity_indicator" }
agent = { path = "crates/agent" }
agent_ui = { path = "crates/agent_ui" }
agent_settings = { path = "crates/agent_settings" }
ai = { path = "crates/ai" }
anthropic = { path = "crates/anthropic" }
askpass = { path = "crates/askpass" }
assets = { path = "crates/assets" }
assistant_context_editor = { path = "crates/assistant_context_editor" }
assistant_context = { path = "crates/assistant_context" }
assistant_slash_command = { path = "crates/assistant_slash_command" }
assistant_slash_commands = { path = "crates/assistant_slash_commands" }
assistant_tool = { path = "crates/assistant_tool" }
@@ -298,6 +305,7 @@ lmstudio = { path = "crates/lmstudio" }
lsp = { path = "crates/lsp" }
markdown = { path = "crates/markdown" }
markdown_preview = { path = "crates/markdown_preview" }
svg_preview = { path = "crates/svg_preview" }
media = { path = "crates/media" }
menu = { path = "crates/menu" }
migrator = { path = "crates/migrator" }
@@ -307,6 +315,7 @@ node_runtime = { path = "crates/node_runtime" }
notifications = { path = "crates/notifications" }
ollama = { path = "crates/ollama" }
open_ai = { path = "crates/open_ai" }
open_router = { path = "crates/open_router", features = ["schemars"] }
outline = { path = "crates/outline" }
outline_panel = { path = "crates/outline_panel" }
panel = { path = "crates/panel" }
@@ -369,8 +378,11 @@ ui_macros = { path = "crates/ui_macros" }
ui_prompt = { path = "crates/ui_prompt" }
util = { path = "crates/util" }
util_macros = { path = "crates/util_macros" }
vercel = { path = "crates/vercel" }
vim = { path = "crates/vim" }
vim_mode_setting = { path = "crates/vim_mode_setting" }
watch = { path = "crates/watch" }
web_search = { path = "crates/web_search" }
web_search_providers = { path = "crates/web_search_providers" }
welcome = { path = "crates/welcome" }
@@ -401,7 +413,6 @@ async-recursion = "1.0.0"
async-tar = "0.5.0"
async-trait = "0.1"
async-tungstenite = "0.29.1"
async-watch = "0.3.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
aws-config = { version = "1.6.1", features = ["behavior-version-latest"] }
aws-credential-types = { version = "1.2.2", features = [
@@ -414,9 +425,9 @@ aws-smithy-runtime-api = { version = "1.7.4", features = ["http-1x", "client"] }
aws-smithy-types = { version = "1.3.0", features = ["http-body-1-x"] }
base64 = "0.22"
bitflags = "2.6.0"
blade-graphics = { git = "https://github.com/kvark/blade", rev = "416375211bb0b5826b3584dccdb6a43369e499ad" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "416375211bb0b5826b3584dccdb6a43369e499ad" }
blade-util = { git = "https://github.com/kvark/blade", rev = "416375211bb0b5826b3584dccdb6a43369e499ad" }
blade-graphics = { git = "https://github.com/kvark/blade", rev = "e0ec4e720957edd51b945b64dd85605ea54bcfe5" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "e0ec4e720957edd51b945b64dd85605ea54bcfe5" }
blade-util = { git = "https://github.com/kvark/blade", rev = "e0ec4e720957edd51b945b64dd85605ea54bcfe5" }
blake3 = "1.5.3"
bytes = "1.0"
cargo_metadata = "0.19"
@@ -430,9 +441,10 @@ convert_case = "0.8.0"
core-foundation = "0.10.0"
core-foundation-sys = "0.8.6"
core-video = { version = "0.4.3", features = ["metal"] }
cpal = "0.16"
criterion = { version = "0.5", features = ["html_reports"] }
ctor = "0.4.0"
dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "68516de327fa1be15214133a0a2e52a12982ce75" }
dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "b40956a7f4d1939da67429d941389ee306a3a308" }
dashmap = "6.0"
derive_more = "0.99.17"
dirs = "4.0"
@@ -449,7 +461,6 @@ futures-batch = "0.6.1"
futures-lite = "1.13"
git2 = { version = "0.20.1", default-features = false }
globset = "0.4"
hashbrown = "0.15.3"
handlebars = "4.3"
heck = "0.5"
heed = { version = "0.21.0", features = ["read-txn-no-tls"] }
@@ -477,7 +488,6 @@ log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "c9c189f1c5dd53c624a419ce35bc77ad6a908d18" }
markup5ever_rcdom = "0.3.0"
metal = "0.29"
mlua = { version = "0.10", features = ["lua54", "vendored", "async", "send"] }
moka = { version = "0.12.10", features = ["sync"] }
naga = { version = "25.0", features = ["wgsl-in"] }
nanoid = "0.4"
@@ -512,7 +522,6 @@ rand = "0.8.5"
rayon = "1.8"
ref-cast = "1.0.24"
regex = "1.5"
repair_json = "0.1.0"
reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "951c770a32f1998d6e999cef3e59e0013e6c4415", default-features = false, features = [
"charset",
"http2",
@@ -544,7 +553,6 @@ serde_repr = "0.1"
sha2 = "0.10"
shellexpand = "2.1.0"
shlex = "1.3.0"
signal-hook = "0.3.17"
simplelog = "0.12.2"
smallvec = { version = "1.6", features = ["union"] }
smol = "2.0"
@@ -572,8 +580,8 @@ tokio = { version = "1" }
tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] }
toml = "0.8"
tower-http = "0.4.4"
tree-sitter = { version = "0.25.5", features = ["wasm"] }
tree-sitter-bash = "0.23"
tree-sitter = { version = "0.25.6", features = ["wasm"] }
tree-sitter-bash = "0.25.0"
tree-sitter-c = "0.23"
tree-sitter-cpp = "0.23"
tree-sitter-css = "0.23"
@@ -589,7 +597,7 @@ tree-sitter-html = "0.23"
tree-sitter-jsdoc = "0.23"
tree-sitter-json = "0.24"
tree-sitter-md = { git = "https://github.com/tree-sitter-grammars/tree-sitter-markdown", rev = "9a23c1a96c0513d8fc6520972beedd419a973539" }
tree-sitter-python = "0.23"
tree-sitter-python = { git = "https://github.com/zed-industries/tree-sitter-python", rev = "218fcbf3fda3d029225f3dec005cb497d111b35e" }
tree-sitter-regex = "0.24"
tree-sitter-ruby = "0.23"
tree-sitter-rust = "0.24"
@@ -679,9 +687,7 @@ features = [
"Win32_UI_WindowsAndMessaging",
]
# TODO livekit https://github.com/RustAudio/cpal/pull/891
[patch.crates-io]
cpal = { git = "https://github.com/zed-industries/cpal", rev = "fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50" }
notify = { git = "https://github.com/zed-industries/notify.git", rev = "bbb9ea5ae52b253e095737847e367c30653a2e96" }
notify-types = { git = "https://github.com/zed-industries/notify.git", rev = "bbb9ea5ae52b253e095737847e367c30653a2e96" }
@@ -695,6 +701,8 @@ codegen-units = 16
[profile.dev.package]
taffy = { opt-level = 3 }
cranelift-codegen = { opt-level = 3 }
cranelift-codegen-meta = { opt-level = 3 }
cranelift-codegen-shared = { opt-level = 3 }
resvg = { opt-level = 3 }
rustybuzz = { opt-level = 3 }
ttf-parser = { opt-level = 3 }

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.2
FROM rust:1.87-bookworm as builder
FROM rust:1.88-bookworm as builder
WORKDIR app
COPY . .

View File

@@ -0,0 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor" stroke="currentColor">
<g clip-path="url(#clip0_205_3)">
<path d="M0.094 7.78c0.469 0 2.281 -0.405 3.219 -0.936s0.938 -0.531 2.875 -1.906c2.453 -1.741 4.188 -1.158 7.031 -1.158" stroke-width="2.8125" />
<path d="m15.969 3.797 -4.805 2.774V1.023z" />
<path d="M0 7.781c0.469 0 2.281 0.405 3.219 0.936s0.938 0.531 2.875 1.906C8.547 12.364 10.281 11.781 13.125 11.781" stroke-width="2.8125" />
<path d="m15.875 11.764 -4.805 -2.774v5.548z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 575 B

View File

@@ -0,0 +1,16 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2639_570)">
<g clip-path="url(#clip1_2639_570)">
<path d="M9.85676 4H13.6675C15.2128 4 16.4654 5.25266 16.4654 6.7979V10.4322H14.9002V6.7979C14.9002 6.76067 14.8988 6.7237 14.8959 6.68706L11.0851 10.4316C11.098 10.432 11.1109 10.4322 11.1238 10.4322H14.9002V11.9105H11.1238C9.57856 11.9105 8.29152 10.6456 8.29152 9.10032V5.47569H9.85676V9.10032C9.85676 9.17012 9.86216 9.23908 9.87264 9.30672L13.7673 5.4798C13.7344 5.47708 13.7012 5.47569 13.6675 5.47569H9.85676V4Z" fill="black"/>
<path d="M6.00752 11.6382L0.5 5.47504H2.71573L5.94924 9.09348V5.47504H7.6014V11.0298C7.6014 11.8682 6.56616 12.2634 6.00752 11.6382Z" fill="black"/>
</g>
</g>
<defs>
<clipPath id="clip0_2639_570">
<rect width="16" height="16" fill="white" transform="translate(0.5)"/>
</clipPath>
<clipPath id="clip1_2639_570">
<rect width="16" height="8" fill="white" transform="translate(0.5 4)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1015 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down10-icon lucide-arrow-down-1-0"><path d="m3 16 4 4 4-4"/><path d="M7 20V4"/><path d="M17 10V4h-2"/><path d="M15 10h4"/><rect x="15" y="14" width="4" height="6" ry="2"/></svg>

After

Width:  |  Height:  |  Size: 386 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 3.5L12.5 8M8 3.5L3.5 8M8 3.5V12.5" stroke="black" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-blocks"><rect width="7" height="7" x="14" y="3" rx="1"/><path d="M10 21V8a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H3"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-blocks-icon lucide-blocks"><rect width="7" height="7" x="14" y="3" rx="1"/><path d="M10 21V8a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H3"/></svg>

Before

Width:  |  Height:  |  Size: 368 B

After

Width:  |  Height:  |  Size: 386 B

View File

@@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.75776 5.50003H8.49988C8.70769 5.50003 8.89518 5.62971 8.95455 5.82346C9.04049 6.01876 8.9858 6.23906 8.82956 6.37656L4.82971 9.87643C4.65315 10.0295 4.39488 10.042 4.20614 9.90455C4.01724 9.76705 3.94849 9.51706 4.04052 9.30301L5.24219 6.49999H3.48601C3.2918 6.49999 3.10524 6.37031 3.03197 6.17657C2.9587 5.98126 3.014 5.76096 3.1708 5.62346L7.17018 2.12375C7.34674 1.97001 7.60454 1.95829 7.7936 2.09547C7.98265 2.23275 8.0514 2.48218 7.95922 2.69695L6.75776 5.50003Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 601 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-help-icon lucide-circle-help"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>

After

Width:  |  Height:  |  Size: 348 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.51298 2C5.19374 2 2.5 4.6945 2.5 8.01298C2.5 9.41828 2.99491 10.7205 3.80143 11.7485C4.45214 10.8243 5.15479 9.95214 6.03997 9.23651C6.06594 9.21054 6.13086 9.17159 6.20876 9.11965C6.57307 8.84623 6.8335 8.44297 6.89842 7.98702V7.97403C7.11991 6.50306 7.71869 5.99593 9.3974 5.99593C9.54099 5.99593 9.70978 5.99593 9.86558 6.00891C10.7248 6.04786 11.2189 6.29532 11.2709 6.42515C11.3098 6.49007 11.2968 6.56874 11.2839 6.64664L11.2189 6.63366C10.6851 6.56874 9.87856 6.72454 9.76171 7.31034C9.69679 7.63569 9.77469 8 9.80066 8.32536C9.83961 8.6637 9.86558 9.01502 9.86558 9.35336C9.86558 9.37933 9.83961 9.50916 9.86558 9.52215C8.95443 8.64995 6.84572 9.74364 6.18203 10.2248C6.24695 10.1988 6.31263 10.1729 6.39053 10.1469C7.02826 9.92541 8.94145 9.35336 9.68304 9.99109C10.3078 10.7587 9.74796 12.1777 9.27979 12.8803C9.00636 13.2966 8.66802 13.6739 8.29073 14H8.51222C11.8315 14 14.5252 11.3055 14.5252 7.98702C14.5252 4.66853 11.8452 2 8.51298 2ZM9.20265 5.25356C8.55193 5.25356 8.01808 4.7197 8.01808 4.06899C8.01808 3.41828 8.55193 2.88442 9.20265 2.88442C9.85336 2.88442 10.3872 3.41828 10.3872 4.06899C10.3872 4.7197 9.86634 5.25356 9.20265 5.25356Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-list-todo-icon lucide-list-todo"><rect x="3" y="5" width="6" height="6" rx="1"/><path d="m3 17 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/></svg>

After

Width:  |  Height:  |  Size: 373 B

View File

@@ -0,0 +1,12 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 3L7 4" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 4L10 3" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.002 6V5.51658C5.98992 5.32067 6.03266 5.12502 6.12762 4.94143C6.22259 4.75784 6.36781 4.59012 6.55453 4.44839C6.74125 4.30666 6.9656 4.19386 7.21403 4.1168C7.46246 4.03973 7.72983 4 8 4C8.27017 4 8.53754 4.03973 8.78597 4.1168C9.0344 4.19386 9.25875 4.30666 9.44547 4.44839C9.63219 4.59012 9.77741 4.75784 9.87238 4.94143C9.96734 5.12502 10.0101 5.32067 9.998 5.51658V6" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 13C6.35 13 5 11.5462 5 9.76923V8.15385C5 7.58261 5.21071 7.03477 5.58579 6.63085C5.96086 6.22692 6.46957 6 7 6H9C9.53043 6 10.0391 6.22692 10.4142 6.63085C10.7893 7.03477 11 7.58261 11 8.15385V9.76923C11 11.5462 9.65 13 8 13Z" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 6.16663C3.90652 6.06663 3 5.21663 3 4.16663" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 9H3" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 13C3 11.95 3.89474 11.05 5 11" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13 4C13 5.05 12.0857 5.9 11 6" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13 9H11" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 11C12.1053 11.05 13 11.95 13 13" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.84265 10.7778C4.39206 11.6001 5.17295 12.241 6.08658 12.6194C7.00021 12.9978 8.00555 13.0969 8.97545 12.9039C9.94535 12.711 10.8363 12.2348 11.5355 11.5355C12.2348 10.8363 12.711 9.94535 12.9039 8.97545C13.0969 8.00555 12.9978 7.00021 12.6194 6.08658C12.241 5.17295 11.6001 4.39206 10.7778 3.84265C9.9556 3.29324 8.9889 3 8 3C6.60219 3.00526 5.26054 3.55068 4.25556 4.52222L3 5.77778" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 3V6H6" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 685 B

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 13C10.7614 13 13 10.7614 13 8C13 5.23858 10.7614 3 8 3C5.23858 3 3 5.23858 3 8C3 10.7614 5.23858 13 8 13Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 5L11 11" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 409 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-scroll-text-icon lucide-scroll-text"><path d="M15 12h-5"/><path d="M15 8h-5"/><path d="M19 17V5a2 2 0 0 0-2-2H4"/><path d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"/></svg>

After

Width:  |  Height:  |  Size: 441 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-split-icon lucide-split"><path d="M16 3h5v5"/><path d="M8 3H3v5"/><path d="M12 22v-8.3a4 4 0 0 0-1.172-2.872L3 3"/><path d="m15 9 6-6"/></svg>

After

Width:  |  Height:  |  Size: 345 B

View File

@@ -1 +1,3 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.97942 1.25171L6.9585 1.30199L5.58662 4.60039C5.54342 4.70426 5.44573 4.77523 5.3336 4.78422L1.7727 5.0697L1.71841 5.07405L1.38687 5.10063L1.08608 5.12475C0.820085 5.14607 0.712228 5.47802 0.914889 5.65162L1.14406 5.84793L1.39666 6.06431L1.43802 6.09974L4.15105 8.42374C4.23648 8.49692 4.2738 8.61176 4.24769 8.72118L3.41882 12.196L3.40618 12.249L3.32901 12.5725L3.25899 12.866C3.19708 13.1256 3.47945 13.3308 3.70718 13.1917L3.9647 13.0344L4.24854 12.861L4.29502 12.8326L7.34365 10.9705C7.43965 10.9119 7.5604 10.9119 7.6564 10.9705L10.705 12.8326L10.7515 12.861L11.0354 13.0344L11.2929 13.1917C11.5206 13.3308 11.803 13.1256 11.7411 12.866L11.671 12.5725L11.5939 12.249L11.5812 12.196L10.7524 8.72118C10.7263 8.61176 10.7636 8.49692 10.849 8.42374L13.562 6.09974L13.6034 6.06431L13.856 5.84793L14.0852 5.65162C14.2878 5.47802 14.18 5.14607 13.914 5.12475L13.6132 5.10063L13.2816 5.07405L13.2274 5.0697L9.66645 4.78422C9.55432 4.77523 9.45663 4.70426 9.41343 4.60039L8.04155 1.30199L8.02064 1.25171L7.89291 0.944609L7.77702 0.665992C7.67454 0.419604 7.32551 0.419604 7.22303 0.665992L7.10715 0.944609L6.97942 1.25171ZM7.50003 2.60397L6.50994 4.98442C6.32273 5.43453 5.89944 5.74207 5.41351 5.78103L2.84361 5.98705L4.8016 7.66428C5.17183 7.98142 5.33351 8.47903 5.2204 8.95321L4.62221 11.461L6.8224 10.1171C7.23842 9.86302 7.76164 9.86302 8.17766 10.1171L10.3778 11.461L9.77965 8.95321C9.66654 8.47903 9.82822 7.98142 10.1984 7.66428L12.1564 5.98705L9.58654 5.78103C9.10061 5.74207 8.67732 5.43453 8.49011 4.98442L7.50003 2.60397Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.68323 1.53C7.71245 1.47097 7.75758 1.42129 7.81353 1.38655C7.86949 1.35181 7.93404 1.3334 7.9999 1.3334C8.06576 1.3334 8.13031 1.35181 8.18626 1.38655C8.24222 1.42129 8.28735 1.47097 8.31656 1.53L9.85656 4.64933C9.95802 4.85465 10.1078 5.03227 10.293 5.16697C10.4782 5.30167 10.6933 5.38941 10.9199 5.42267L14.3639 5.92667C14.4292 5.93612 14.4905 5.96365 14.5409 6.00613C14.5913 6.04862 14.6289 6.10437 14.6492 6.16707C14.6696 6.22978 14.6721 6.29694 14.6563 6.36096C14.6405 6.42498 14.6071 6.4833 14.5599 6.52933L12.0692 8.95467C11.905 9.11473 11.7821 9.31232 11.7111 9.53042C11.6402 9.74852 11.6233 9.98059 11.6619 10.2067L12.2499 13.6333C12.2614 13.6986 12.2544 13.7657 12.2296 13.8271C12.2048 13.8885 12.1632 13.9417 12.1096 13.9807C12.056 14.0196 11.9926 14.0427 11.9265 14.0473C11.8604 14.0519 11.7944 14.0378 11.7359 14.0067L8.65723 12.388C8.45438 12.2815 8.22868 12.2258 7.99956 12.2258C7.77044 12.2258 7.54475 12.2815 7.3419 12.388L4.2639 14.0067C4.20545 14.0376 4.1395 14.0515 4.07353 14.0468C4.00757 14.0421 3.94424 14.019 3.89076 13.9801C3.83728 13.9413 3.79579 13.8881 3.771 13.8268C3.74622 13.7655 3.73914 13.6985 3.75056 13.6333L4.3379 10.2073C4.3767 9.98116 4.35989 9.74893 4.28892 9.5307C4.21796 9.31246 4.09497 9.11477 3.93056 8.95467L1.4399 6.53C1.39229 6.48402 1.35856 6.4256 1.34254 6.36138C1.32652 6.29717 1.32886 6.22975 1.34928 6.16679C1.36971 6.10384 1.40741 6.04789 1.45808 6.00532C1.50876 5.96275 1.57037 5.93527 1.6359 5.926L5.07923 5.42267C5.30607 5.38967 5.52149 5.30204 5.70695 5.16733C5.89242 5.03261 6.04237 4.85485 6.1439 4.64933L7.68323 1.53Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1 +1,3 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.22303 0.665992C7.32551 0.419604 7.67454 0.419604 7.77702 0.665992L9.41343 4.60039C9.45663 4.70426 9.55432 4.77523 9.66645 4.78422L13.914 5.12475C14.18 5.14607 14.2878 5.47802 14.0852 5.65162L10.849 8.42374C10.7636 8.49692 10.7263 8.61176 10.7524 8.72118L11.7411 12.866C11.803 13.1256 11.5206 13.3308 11.2929 13.1917L7.6564 10.9705C7.5604 10.9119 7.43965 10.9119 7.34365 10.9705L3.70718 13.1917C3.47945 13.3308 3.19708 13.1256 3.25899 12.866L4.24769 8.72118C4.2738 8.61176 4.23648 8.49692 4.15105 8.42374L0.914889 5.65162C0.712228 5.47802 0.820086 5.14607 1.08608 5.12475L5.3336 4.78422C5.44573 4.77523 5.54342 4.70426 5.58662 4.60039L7.22303 0.665992Z" fill="currentColor"></path></svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.68323 1.53C7.71245 1.47097 7.75758 1.42129 7.81353 1.38655C7.86949 1.35181 7.93404 1.3334 7.9999 1.3334C8.06576 1.3334 8.13031 1.35181 8.18626 1.38655C8.24222 1.42129 8.28735 1.47097 8.31656 1.53L9.85656 4.64933C9.95802 4.85465 10.1078 5.03227 10.293 5.16697C10.4782 5.30167 10.6933 5.38941 10.9199 5.42267L14.3639 5.92667C14.4292 5.93612 14.4905 5.96365 14.5409 6.00613C14.5913 6.04862 14.6289 6.10437 14.6492 6.16707C14.6696 6.22978 14.6721 6.29694 14.6563 6.36096C14.6405 6.42498 14.6071 6.4833 14.5599 6.52933L12.0692 8.95467C11.905 9.11473 11.7821 9.31232 11.7111 9.53042C11.6402 9.74852 11.6233 9.98059 11.6619 10.2067L12.2499 13.6333C12.2614 13.6986 12.2544 13.7657 12.2296 13.8271C12.2048 13.8885 12.1632 13.9417 12.1096 13.9807C12.056 14.0196 11.9926 14.0427 11.9265 14.0473C11.8604 14.0519 11.7944 14.0378 11.7359 14.0067L8.65723 12.388C8.45438 12.2815 8.22868 12.2258 7.99956 12.2258C7.77044 12.2258 7.54475 12.2815 7.3419 12.388L4.2639 14.0067C4.20545 14.0376 4.1395 14.0515 4.07353 14.0468C4.00757 14.0421 3.94424 14.019 3.89076 13.9801C3.83728 13.9413 3.79579 13.8881 3.771 13.8268C3.74622 13.7655 3.73914 13.6985 3.75056 13.6333L4.3379 10.2073C4.3767 9.98116 4.35989 9.74893 4.28892 9.5307C4.21796 9.31246 4.09497 9.11477 3.93056 8.95467L1.4399 6.53C1.39229 6.48402 1.35856 6.4256 1.34254 6.36138C1.32652 6.29717 1.32886 6.22975 1.34928 6.16679C1.36971 6.10384 1.40741 6.04789 1.45808 6.00532C1.50876 5.96275 1.57037 5.93527 1.6359 5.926L5.07923 5.42267C5.30607 5.38967 5.52149 5.30204 5.70695 5.16733C5.89242 5.03261 6.04237 4.85485 6.1439 4.64933L7.68323 1.53Z" fill="black" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 794 B

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect opacity="0.3" x="2" y="2" width="12" height="12" rx="2" stroke="black" stroke-width="1.5"/>
<path d="M9.62109 4.65039C9.80393 4.65039 9.98678 4.70637 10.1279 4.83203C10.2722 4.96055 10.3496 5.14167 10.3496 5.33691C10.3496 5.53215 10.2723 5.71329 10.1279 5.8418C9.98678 5.96744 9.80392 6.02344 9.62109 6.02344H7.1416V7.24902H9.46289C9.6423 7.24902 9.82303 7.30362 9.96387 7.42773C10.1085 7.55531 10.1875 7.73591 10.1875 7.93164C10.1874 8.12674 10.1072 8.30592 9.96484 8.43262C9.82536 8.55656 9.64499 8.61426 9.46289 8.61426H7.1416V9.97656H9.62109C9.80392 9.97656 9.98678 10.0326 10.1279 10.1582C10.2723 10.2867 10.3496 10.4678 10.3496 10.6631C10.3496 10.8583 10.2722 11.0394 10.1279 11.168C9.98678 11.2936 9.80393 11.3496 9.62109 11.3496H6.39648C6.20308 11.3496 6.00965 11.289 5.86328 11.1465C5.7159 11.0028 5.65039 10.8095 5.65039 10.6133V5.38672C5.65039 5.19054 5.7159 4.99723 5.86328 4.85352C6.00965 4.71101 6.20308 4.65039 6.39648 4.65039H9.62109ZM9.70215 10.9941C9.72618 10.9903 9.74882 10.9838 9.77051 10.9766C9.74884 10.9838 9.72616 10.9903 9.70215 10.9941ZM9.78809 10.9688C9.80381 10.9626 9.81877 10.9561 9.83301 10.9482C9.81877 10.9561 9.80381 10.9626 9.78809 10.9688ZM9.85059 10.9375C9.86364 10.9292 9.87617 10.9209 9.8877 10.9111C9.87616 10.9209 9.86365 10.9293 9.85059 10.9375ZM6.1123 10.8994C6.12346 10.9099 6.1358 10.9188 6.14844 10.9277C6.1358 10.9188 6.12346 10.9099 6.1123 10.8994ZM9.90039 10.8984C9.91122 10.8882 9.92149 10.8778 9.93066 10.8662C9.92147 10.8778 9.91123 10.8882 9.90039 10.8984ZM6.07129 10.8516C6.0792 10.8626 6.08754 10.8729 6.09668 10.8828C6.08754 10.8729 6.0792 10.8626 6.07129 10.8516ZM9.94434 10.8477C9.95104 10.8377 9.95638 10.8272 9.96191 10.8164C9.95637 10.8271 9.95106 10.8377 9.94434 10.8477ZM6.03418 10.7842C6.04066 10.7995 6.04735 10.8143 6.05566 10.8281C6.04735 10.8143 6.04066 10.7995 6.03418 10.7842ZM9.97559 10.7891C9.97984 10.7783 9.98321 10.7673 9.98633 10.7559C9.98321 10.7673 9.97984 10.7783 9.97559 10.7891ZM6.01367 10.7236C6.0173 10.7388 6.02121 10.7535 6.02637 10.7676C6.02121 10.7535 6.0173 10.7388 6.01367 10.7236ZM9.62109 10.3262C9.67716 10.3262 9.72942 10.3346 9.77539 10.3506C9.72927 10.3345 9.67733 10.3262 9.62109 10.3262ZM9.56641 8.25098C9.5802 8.24792 9.59348 8.24449 9.60645 8.24023C9.59348 8.2445 9.58021 8.24792 9.56641 8.25098ZM9.625 8.2334C9.64073 8.22735 9.65566 8.2207 9.66992 8.21289C9.65565 8.22072 9.64074 8.22733 9.625 8.2334ZM9.68848 8.20117C9.69832 8.19495 9.70781 8.18872 9.7168 8.18164C9.70781 8.18874 9.69832 8.19493 9.68848 8.20117ZM9.74121 8.16016C9.7511 8.15059 9.76008 8.14059 9.76855 8.12988C9.76009 8.1406 9.75109 8.15057 9.74121 8.16016ZM9.77734 8.11914C9.78753 8.10499 9.79605 8.09004 9.80371 8.07422C9.79606 8.09005 9.78751 8.10498 9.77734 8.11914ZM9.79883 5.6377C9.80686 5.6343 9.81463 5.63082 9.82227 5.62695C9.81463 5.63084 9.80687 5.63429 9.79883 5.6377ZM9.93457 5.53516C9.91124 5.56582 9.88273 5.59214 9.84863 5.61328C9.88277 5.5922 9.91121 5.56576 9.93457 5.53516ZM9.94434 5.52246C9.95363 5.50886 9.96171 5.4946 9.96875 5.47949C9.96172 5.4946 9.9536 5.50885 9.94434 5.52246ZM6.02637 5.23145C6.02118 5.24552 6.01733 5.26024 6.01367 5.27539C6.01734 5.26023 6.02118 5.24552 6.02637 5.23145ZM6.05566 5.17188C6.04766 5.18525 6.04049 5.19914 6.03418 5.21387C6.04049 5.19914 6.04765 5.18525 6.05566 5.17188ZM6.09961 5.11328C6.08938 5.124 6.08003 5.13538 6.07129 5.14746C6.08003 5.13538 6.08938 5.12399 6.09961 5.11328ZM6.14844 5.07129C6.13601 5.08003 6.12428 5.08938 6.11328 5.09961C6.12428 5.08938 6.13601 5.08003 6.14844 5.07129ZM6.23242 5.02637C6.21195 5.03382 6.19287 5.04334 6.1748 5.05371C6.19287 5.04335 6.21195 5.03382 6.23242 5.02637ZM6.1748 10.9453C6.19195 10.9552 6.21017 10.9635 6.22949 10.9707C6.21017 10.9635 6.19195 10.9552 6.1748 10.9453ZM6.30957 5.00684C6.28239 5.01136 6.25651 5.01759 6.23242 5.02637C6.25651 5.01759 6.28239 5.01136 6.30957 5.00684Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,890 @@
<svg width="400" height="92" viewBox="0 0 400 92" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_532_2318)">
<mask id="mask0_532_2318" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="400" height="92">
<path d="M400 0H0V92H400V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_532_2318)">
<g clip-path="url(#clip1_532_2318)">
<path d="M62.1878 1.24874C60.4044 1.12767 59.0444 -0.430533 59.1654 -2.21393L59.2755 -3.8352C59.3144 -4.40851 59.5795 -4.94289 60.0124 -5.32076C60.4453 -5.69863 61.0106 -5.88906 61.5839 -5.85013L63.7456 -5.70338C64.3189 -5.66446 64.8533 -5.39938 65.2312 -4.96647C65.6091 -4.53355 65.7995 -3.96825 65.7606 -3.39494L65.6505 -1.77367C65.5294 0.00972748 63.9712 1.36981 62.1878 1.24874Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M62.1879 1.24869L62.5181 -3.61511" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M57.2873 1.45893C57.3644 0.324045 58.3491 -0.586346 59.4877 -0.563343" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M65.108 -0.181763C66.2393 -0.0506748 67.0919 0.984456 67.0149 2.11934" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip2_532_2318)">
<path d="M87.1305 2.94204C85.3471 2.82097 83.987 1.26277 84.108 -0.52063L84.2181 -2.1419C84.257 -2.71521 84.5221 -3.24959 84.955 -3.62746C85.3879 -4.00534 85.9532 -4.19576 86.5266 -4.15684L88.6883 -4.01008C89.2616 -3.97116 89.7959 -3.70608 90.1738 -3.27317C90.5517 -2.84025 90.7421 -2.27495 90.7032 -1.70164L90.5931 -0.0803691C90.4721 1.70303 88.9139 3.06311 87.1305 2.94204Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M87.1305 2.94199L87.4607 -1.92181" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M82.23 3.15223C82.307 2.01734 83.2918 1.10695 84.4303 1.12996" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M92.7915 -0.474035L90.6298 -0.620789" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M90.0507 1.51154C91.1819 1.64262 92.0346 2.67775 91.9575 3.81264" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip3_532_2318)">
<path d="M112.073 4.63534C110.29 4.51426 108.929 2.95606 109.051 1.17267L109.161 -0.4486C109.2 -1.02192 109.465 -1.55629 109.898 -1.93416C110.33 -2.31204 110.896 -2.50246 111.469 -2.46354L113.631 -2.31678C114.204 -2.27786 114.738 -2.01279 115.116 -1.57987C115.494 -1.14695 115.685 -0.581655 115.646 -0.00833894L115.536 1.61293C115.415 3.39632 113.856 4.75641 112.073 4.63534Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M112.073 4.63529L112.403 -0.228516" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M109.087 0.632227L106.926 0.485474" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M107.172 4.84553C107.25 3.71064 108.234 2.80025 109.373 2.82325" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M117.734 1.21926L115.572 1.07251" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M114.993 3.20483C116.124 3.33592 116.977 4.37105 116.9 5.50594" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip4_532_2318)">
<path d="M135.867 -0.736649L135.903 -1.27707C135.908 -1.49674 135.958 -1.71311 136.049 -1.91312C136.14 -2.11313 136.27 -2.29262 136.433 -2.44078C136.595 -2.58894 136.786 -2.70268 136.993 -2.77515C137.2 -2.84762 137.42 -2.8773 137.64 -2.86242C137.859 -2.84754 138.073 -2.78839 138.269 -2.68855C138.464 -2.58872 138.638 -2.45025 138.779 -2.28153C138.919 -2.1128 139.024 -1.9173 139.087 -1.70683C139.151 -1.49636 139.17 -1.27528 139.146 -1.05694L139.109 -0.516519" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M137.016 6.32869C135.232 6.20762 133.872 4.64942 133.993 2.86603L134.103 1.24476C134.142 0.671444 134.407 0.13707 134.84 -0.240804C135.273 -0.618678 135.838 -0.809099 136.412 -0.770178L138.573 -0.623424C139.147 -0.584503 139.681 -0.319426 140.059 0.113491C140.437 0.546408 140.627 1.1117 140.588 1.68502L140.478 3.30629C140.357 5.08968 138.799 6.44977 137.016 6.32869Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M137.016 6.32865L137.346 1.46484" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M134.463 0.183352C133.427 0.00445886 132.625 -0.972961 132.702 -2.10785" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M134.03 2.32559L131.868 2.17883" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M132.115 6.53889C132.192 5.404 133.177 4.49361 134.315 4.51661" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M142.413 -1.44856C142.336 -0.313668 141.409 0.546349 140.375 0.584726" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M142.677 2.91262L140.515 2.76587" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M139.936 4.89819C141.067 5.02928 141.92 6.06441 141.843 7.1993" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip5_532_2318)">
<path d="M160.457 -1.85242L161.404 -0.767448" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M163.695 -0.611874L164.78 -1.55889" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M160.809 0.956649L160.846 0.416226C160.851 0.196553 160.9 -0.0198148 160.991 -0.219821C161.082 -0.419827 161.213 -0.599325 161.375 -0.747482C161.538 -0.895638 161.728 -1.00938 161.936 -1.08185C162.143 -1.15432 162.363 -1.184 162.582 -1.16912C162.801 -1.15424 163.015 -1.09509 163.211 -0.995255C163.407 -0.895417 163.58 -0.756956 163.721 -0.588227C163.862 -0.419498 163.967 -0.224001 164.03 -0.0135316C164.093 0.196937 164.113 0.418014 164.088 0.636357L164.052 1.17678" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M161.958 8.02199C160.175 7.90092 158.815 6.34272 158.936 4.55933L159.046 2.93806C159.085 2.36474 159.35 1.83037 159.783 1.45249C160.216 1.07462 160.781 0.884199 161.354 0.923121L163.516 1.06987C164.089 1.1088 164.624 1.37387 165.002 1.80679C165.379 2.23971 165.57 2.805 165.531 3.37832L165.421 4.99959C165.3 6.78298 163.742 8.14306 161.958 8.02199Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M161.958 8.02195L162.288 3.15814" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M159.406 1.87665C158.37 1.69776 157.568 0.720337 157.645 -0.414551" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M158.972 4.01888L156.811 3.87213" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M157.058 8.23219C157.135 7.0973 158.12 6.18691 159.258 6.20991" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M167.356 0.244742C167.279 1.37963 166.352 2.23965 165.318 2.27802" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M167.619 4.60592L165.458 4.45917" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M164.878 6.59149C166.01 6.72258 166.862 7.75771 166.785 8.8926" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip6_532_2318)">
<path d="M185.399 -0.159119L186.346 0.92585" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M188.638 1.08142L189.723 0.134404" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M185.752 2.64995L185.788 2.10952C185.793 1.88985 185.843 1.67348 185.934 1.47348C186.025 1.27347 186.156 1.09397 186.318 0.945817C186.48 0.79766 186.671 0.683916 186.878 0.611449C187.086 0.538982 187.306 0.509294 187.525 0.524177C187.744 0.53906 187.958 0.598205 188.154 0.698043C188.349 0.797881 188.523 0.936343 188.664 1.10507C188.804 1.2738 188.91 1.4693 188.973 1.67977C189.036 1.89024 189.056 2.11131 189.031 2.32966L188.994 2.87008" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M186.901 9.71529C185.117 9.59422 183.757 8.03602 183.878 6.25262L183.988 4.63136C184.027 4.05804 184.292 3.52367 184.725 3.14579C185.158 2.76792 185.724 2.5775 186.297 2.61642L188.459 2.76317C189.032 2.80209 189.566 3.06717 189.944 3.50009C190.322 3.933 190.512 4.4983 190.474 5.07162L190.363 6.69289C190.242 8.47628 188.684 9.83636 186.901 9.71529Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M186.901 9.71525L187.231 4.85144" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M184.348 3.56995C183.313 3.39106 182.51 2.41364 182.587 1.27875" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M183.915 5.71218L181.753 5.56543" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M182 9.92549C182.077 8.7906 183.062 7.88021 184.201 7.90321" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M192.299 1.93804C192.222 3.07293 191.295 3.93295 190.26 3.97132" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M192.562 6.29922L190.4 6.15247" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M189.821 8.28479C190.952 8.41588 191.805 9.45101 191.728 10.5859" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip7_532_2318)">
<path d="M210.342 1.53418L211.289 2.61915" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M213.58 2.77472L214.665 1.8277" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M210.694 4.34325L210.731 3.80282C210.736 3.58315 210.786 3.36678 210.877 3.16678C210.968 2.96677 211.098 2.78727 211.26 2.63911C211.423 2.49096 211.613 2.37721 211.821 2.30475C212.028 2.23228 212.248 2.20259 212.467 2.21748C212.687 2.23236 212.9 2.2915 213.096 2.39134C213.292 2.49118 213.465 2.62964 213.606 2.79837C213.747 2.9671 213.852 3.1626 213.915 3.37307C213.978 3.58353 213.998 3.80461 213.973 4.02295L213.937 4.56338" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M211.843 11.4086C210.06 11.2875 208.7 9.72932 208.821 7.94592L208.931 6.32465C208.97 5.75134 209.235 5.21697 209.668 4.83909C210.101 4.46122 210.666 4.2708 211.239 4.30972L213.401 4.45647C213.974 4.49539 214.509 4.76047 214.887 5.19339C215.265 5.6263 215.455 6.1916 215.416 6.76492L215.306 8.38618C215.185 10.1696 213.627 11.5297 211.843 11.4086Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M211.843 11.4085L212.174 6.54474" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M209.291 5.26325C208.255 5.08435 207.453 4.10693 207.53 2.97205" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M208.858 7.40548L206.696 7.25873" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M206.943 11.6188C207.02 10.4839 208.005 9.5735 209.143 9.59651" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M217.241 3.63134C217.164 4.76623 216.237 5.62624 215.203 5.66462" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M217.504 7.99252L215.343 7.84576" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M214.764 9.97809C215.895 10.1092 216.748 11.1443 216.67 12.2792" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip8_532_2318)">
<path d="M235.285 3.22748L236.232 4.31245" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M238.523 4.46802L239.608 3.521" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M235.637 6.03654L235.674 5.49612C235.679 5.27645 235.728 5.06008 235.819 4.86007C235.91 4.66007 236.041 4.48057 236.203 4.33241C236.365 4.18426 236.556 4.07051 236.763 3.99805C236.971 3.92558 237.191 3.89589 237.41 3.91077C237.629 3.92566 237.843 3.9848 238.039 4.08464C238.235 4.18448 238.408 4.32294 238.549 4.49167C238.69 4.6604 238.795 4.85589 238.858 5.06636C238.921 5.27683 238.941 5.49791 238.916 5.71625L238.879 6.25667" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M236.786 13.1019C235.003 12.9808 233.642 11.4226 233.764 9.63922L233.874 8.01795C233.913 7.44464 234.178 6.91026 234.611 6.53239C235.043 6.15452 235.609 5.96409 236.182 6.00302L238.344 6.14977C238.917 6.18869 239.451 6.45377 239.829 6.88668C240.207 7.3196 240.398 7.8849 240.359 8.45821L240.249 10.0795C240.128 11.8629 238.569 13.223 236.786 13.1019Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M236.786 13.1018L237.116 8.23804" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M234.233 6.95655C233.198 6.77765 232.395 5.80023 232.472 4.66534" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M233.8 9.09878L231.639 8.95203" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M231.885 13.3121C231.963 12.1772 232.947 11.2668 234.086 11.2898" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M242.184 5.32464C242.107 6.45953 241.18 7.31954 240.146 7.35792" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M242.447 9.68582L240.285 9.53906" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M239.706 11.6714C240.837 11.8025 241.69 12.8376 241.613 13.9725" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip9_532_2318)">
<path d="M260.227 4.92084L261.174 6.00581" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M263.466 6.16138L264.551 5.21436" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M260.579 7.7299L260.616 7.18948C260.621 6.96981 260.671 6.75344 260.762 6.55343C260.853 6.35343 260.983 6.17393 261.146 6.02577C261.308 5.87762 261.498 5.76387 261.706 5.6914C261.913 5.61894 262.133 5.58925 262.353 5.60413C262.572 5.61902 262.786 5.67816 262.981 5.778C263.177 5.87784 263.351 6.0163 263.491 6.18503C263.632 6.35376 263.737 6.54925 263.8 6.75972C263.864 6.97019 263.883 7.19127 263.859 7.40961L263.822 7.95003" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M261.729 14.7952C259.945 14.6742 258.585 13.116 258.706 11.3326L258.816 9.71131C258.855 9.138 259.12 8.60362 259.553 8.22575C259.986 7.84788 260.551 7.65745 261.125 7.69638L263.286 7.84313C263.86 7.88205 264.394 8.14713 264.772 8.58004C265.15 9.01296 265.34 9.57826 265.301 10.1516L265.191 11.7728C265.07 13.5562 263.512 14.9163 261.729 14.7952Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M261.729 14.7952L262.059 9.9314" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M259.176 8.6499C258.14 8.47101 257.338 7.49359 257.415 6.3587" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M258.743 10.7921L256.581 10.6454" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M256.828 15.0054C256.905 13.8706 257.89 12.9602 259.028 12.9832" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M267.126 7.018C267.049 8.15288 266.122 9.0129 265.088 9.05128" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M267.39 11.3792L265.228 11.2324" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M264.649 13.3647C265.78 13.4958 266.633 14.531 266.556 15.6659" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip10_532_2318)">
<path d="M285.17 6.61414L286.117 7.6991" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M288.408 7.85468L289.493 6.90766" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M285.522 9.4232L285.559 8.88278C285.564 8.66311 285.613 8.44674 285.704 8.24673C285.795 8.04673 285.926 7.86723 286.088 7.71907C286.25 7.57091 286.441 7.45717 286.648 7.3847C286.856 7.31224 287.076 7.28255 287.295 7.29743C287.514 7.31231 287.728 7.37146 287.924 7.4713C288.12 7.57114 288.293 7.7096 288.434 7.87833C288.575 8.04705 288.68 8.24255 288.743 8.45302C288.806 8.66349 288.826 8.88457 288.801 9.10291L288.765 9.64333" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M286.671 16.4885C284.888 16.3675 283.528 14.8093 283.649 13.0259L283.759 11.4046C283.798 10.8313 284.063 10.2969 284.496 9.91905C284.929 9.54117 285.494 9.35075 286.067 9.38967L288.229 9.53643C288.802 9.57535 289.337 9.84042 289.714 10.2733C290.092 10.7063 290.283 11.2716 290.244 11.8449L290.134 13.4661C290.013 15.2495 288.455 16.6096 286.671 16.4885Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M286.671 16.4885L287.001 11.6247" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M284.119 10.3432C283.083 10.1643 282.281 9.18689 282.358 8.052" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M283.685 12.4854L281.524 12.3387" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M281.771 16.6987C281.848 15.5639 282.832 14.6535 283.971 14.6765" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M292.069 8.7113C291.992 9.84618 291.065 10.7062 290.031 10.7446" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M292.332 13.0725L290.17 12.9257" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M289.591 15.058C290.723 15.1891 291.575 16.2243 291.498 17.3592" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip11_532_2318)">
<path d="M310.112 8.30743L311.059 9.3924" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M313.351 9.54798L314.436 8.60096" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M310.465 11.1165L310.501 10.5761C310.506 10.3564 310.556 10.14 310.647 9.94003C310.738 9.74002 310.868 9.56053 311.031 9.41237C311.193 9.26421 311.384 9.15047 311.591 9.078C311.799 9.00553 312.018 8.97585 312.238 8.99073C312.457 9.00561 312.671 9.06476 312.867 9.1646C313.062 9.26443 313.236 9.4029 313.377 9.57162C313.517 9.74035 313.622 9.93585 313.686 10.1463C313.749 10.3568 313.769 10.5779 313.744 10.7962L313.707 11.3366" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M311.614 18.1818C309.83 18.0608 308.47 16.5026 308.591 14.7192L308.701 13.0979C308.74 12.5246 309.005 11.9902 309.438 11.6123C309.871 11.2345 310.437 11.0441 311.01 11.083L313.172 11.2297C313.745 11.2686 314.279 11.5337 314.657 11.9666C315.035 12.3996 315.225 12.9649 315.186 13.5382L315.076 15.1594C314.955 16.9428 313.397 18.3029 311.614 18.1818Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M311.614 18.1818L311.944 13.318" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M309.061 12.0365C308.025 11.8576 307.223 10.8802 307.3 9.7453" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M308.628 14.1787L306.466 14.032" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M306.713 18.392C306.79 17.2572 307.775 16.3468 308.914 16.3698" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M317.012 10.4046C316.935 11.5395 316.008 12.3995 314.973 12.4379" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M317.275 14.7658L315.113 14.619" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M314.534 16.7513C315.665 16.8824 316.518 17.9176 316.441 19.0524" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip12_532_2318)">
<path d="M335.055 10.0007L336.002 11.0857" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M338.293 11.2413L339.378 10.2943" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M335.407 12.8098L335.444 12.2694C335.449 12.0497 335.498 11.8333 335.589 11.6333C335.68 11.4333 335.811 11.2538 335.973 11.1057C336.136 10.9575 336.326 10.8438 336.534 10.7713C336.741 10.6988 336.961 10.6691 337.18 10.684C337.399 10.6989 337.613 10.7581 337.809 10.8579C338.005 10.9577 338.178 11.0962 338.319 11.2649C338.46 11.4337 338.565 11.6291 338.628 11.8396C338.691 12.0501 338.711 12.2712 338.686 12.4895L338.65 13.0299" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M336.556 19.8751C334.773 19.7541 333.413 18.1959 333.534 16.4125L333.644 14.7912C333.683 14.2179 333.948 13.6835 334.381 13.3056C334.814 12.9278 335.379 12.7373 335.952 12.7763L338.114 12.923C338.687 12.9619 339.222 13.227 339.6 13.6599C339.978 14.0929 340.168 14.6582 340.129 15.2315L340.019 16.8527C339.898 18.6361 338.34 19.9962 336.556 19.8751Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M336.556 19.8751L336.886 15.0113" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M334.004 13.7298C332.968 13.5509 332.166 12.5735 332.243 11.4386" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M333.571 15.872L331.409 15.7253" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M331.656 20.0853C331.733 18.9504 332.718 18.0401 333.856 18.0631" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M341.954 12.0979C341.877 13.2328 340.95 14.0928 339.916 14.1312" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M342.217 16.4591L340.056 16.3123" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M339.477 18.4446C340.608 18.5757 341.46 19.6109 341.383 20.7457" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip13_532_2318)">
<path d="M359.998 11.694L360.945 12.779" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M363.236 12.9346L364.321 11.9876" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M360.35 14.5031L360.386 13.9627C360.392 13.743 360.441 13.5266 360.532 13.3266C360.623 13.1266 360.754 12.9471 360.916 12.799C361.078 12.6508 361.269 12.5371 361.476 12.4646C361.684 12.3921 361.904 12.3624 362.123 12.3773C362.342 12.3922 362.556 12.4514 362.752 12.5512C362.947 12.651 363.121 12.7895 363.262 12.9582C363.402 13.1269 363.508 13.3224 363.571 13.5329C363.634 13.7434 363.654 13.9645 363.629 14.1828L363.592 14.7232" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M361.499 21.5684C359.715 21.4474 358.355 19.8892 358.476 18.1058L358.587 16.4845C358.625 15.9112 358.891 15.3768 359.323 14.9989C359.756 14.6211 360.322 14.4306 360.895 14.4696L363.057 14.6163C363.63 14.6552 364.164 14.9203 364.542 15.3532C364.92 15.7862 365.111 16.3515 365.072 16.9248L364.962 18.546C364.84 20.3294 363.282 21.6895 361.499 21.5684Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M361.499 21.5684L361.829 16.7046" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M358.946 15.4231C357.911 15.2442 357.108 14.2668 357.185 13.1319" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M358.513 17.5653L356.351 17.4186" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M356.598 21.7786C356.675 20.6437 357.66 19.7334 358.799 19.7564" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M366.897 13.7912C366.82 14.9261 365.893 15.7861 364.859 15.8245" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M367.16 18.1524L364.998 18.0056" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M364.419 20.1379C365.55 20.269 366.403 21.3042 366.326 22.439" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip14_532_2318)">
<path d="M384.94 13.3874L385.887 14.4724" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M388.179 14.6279L389.264 13.6809" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M385.292 16.1965L385.329 15.656C385.334 15.4364 385.384 15.22 385.475 15.02C385.566 14.82 385.696 14.6405 385.859 14.4923C386.021 14.3442 386.211 14.2304 386.419 14.158C386.626 14.0855 386.846 14.0558 387.065 14.0707C387.285 14.0856 387.499 14.1447 387.694 14.2446C387.89 14.3444 388.064 14.4829 388.204 14.6516C388.345 14.8203 388.45 15.0158 388.513 15.2263C388.576 15.4367 388.596 15.6578 388.572 15.8762L388.535 16.4166" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M386.441 23.2618C384.658 23.1407 383.298 21.5825 383.419 19.7991L383.529 18.1779C383.568 17.6045 383.833 17.0702 384.266 16.6923C384.699 16.3144 385.264 16.124 385.838 16.1629L387.999 16.3097C388.573 16.3486 389.107 16.6137 389.485 17.0466C389.863 17.4795 390.053 18.0448 390.014 18.6181L389.904 20.2394C389.783 22.0228 388.225 23.3829 386.441 23.2618Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M386.441 23.2618L386.772 18.3979" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M383.889 17.1165C382.853 16.9376 382.051 15.9601 382.128 14.8253" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M383.456 19.2587L381.294 19.1119" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M381.541 23.472C381.618 22.3371 382.603 21.4267 383.741 21.4497" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M391.839 15.4845C391.762 16.6194 390.835 17.4795 389.801 17.5178" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M392.103 19.8457L389.941 19.699" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M389.362 21.8313C390.493 21.9624 391.346 22.9975 391.269 24.1324" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip15_532_2318)">
<path d="M59.2642 12.3261L60.2112 13.4111" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M62.5026 13.5667L63.5875 12.6196" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M59.6164 15.1352L59.6531 14.5948C59.6582 14.3751 59.7077 14.1587 59.7987 13.9587C59.8897 13.7587 60.0203 13.5792 60.1825 13.431C60.3448 13.2829 60.5354 13.1691 60.7429 13.0967C60.9503 13.0242 61.1703 12.9945 61.3895 13.0094C61.6087 13.0243 61.8227 13.0834 62.0184 13.1833C62.2141 13.2831 62.3876 13.4216 62.5284 13.5903C62.6691 13.759 62.7742 13.9545 62.8374 14.165C62.9005 14.3755 62.9203 14.5965 62.8957 14.8149L62.859 15.3553" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M60.7655 22.2005C58.9821 22.0794 57.622 20.5212 57.7431 18.7379L57.8531 17.1166C57.892 16.5433 58.1571 16.0089 58.59 15.631C59.023 15.2531 59.5883 15.0627 60.1616 15.1016L62.3233 15.2484C62.8966 15.2873 63.4309 15.5524 63.8088 15.9853C64.1867 16.4182 64.3771 16.9835 64.3382 17.5568L64.2281 19.1781C64.1071 20.9615 62.5489 22.3216 60.7655 22.2005Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M60.7655 22.2005L61.0957 17.3367" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M58.213 16.0552C57.1773 15.8763 56.375 14.8989 56.452 13.764" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M57.7797 18.1974L55.618 18.0507" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M55.865 22.4107C55.942 21.2758 56.9268 20.3654 58.0653 20.3884" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M66.1633 14.4233C66.0863 15.5582 65.1592 16.4182 64.1251 16.4566" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M66.4265 18.7844L64.2648 18.6377" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M63.6857 20.77C64.8169 20.9011 65.6696 21.9362 65.5925 23.0711" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip16_532_2318)">
<path d="M84.2068 14.0194L85.1538 15.1044" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M87.4452 15.26L88.5302 14.3129" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M84.5591 16.8285L84.5958 16.2881C84.6008 16.0684 84.6503 15.852 84.7413 15.652C84.8323 15.452 84.9629 15.2725 85.1252 15.1243C85.2874 14.9762 85.478 14.8624 85.6855 14.79C85.8929 14.7175 86.1129 14.6878 86.3321 14.7027C86.5513 14.7176 86.7653 14.7767 86.961 14.8766C87.1568 14.9764 87.3302 15.1149 87.471 15.2836C87.6118 15.4523 87.7169 15.6478 87.78 15.8583C87.8431 16.0688 87.863 16.2898 87.8383 16.5082L87.8016 17.0486" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M85.7081 23.8938C83.9247 23.7727 82.5646 22.2145 82.6857 20.4312L82.7958 18.8099C82.8347 18.2366 83.0997 17.7022 83.5327 17.3243C83.9656 16.9464 84.5309 16.756 85.1042 16.7949L87.2659 16.9417C87.8392 16.9806 88.3736 17.2457 88.7515 17.6786C89.1293 18.1115 89.3197 18.6768 89.2808 19.2501L89.1708 20.8714C89.0497 22.6548 87.4915 24.0149 85.7081 23.8938Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M85.7081 23.8938L86.0383 19.03" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M83.1556 17.7485C82.1199 17.5696 81.3176 16.5922 81.3947 15.4573" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M82.7224 19.8907L80.5607 19.744" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M80.8076 24.104C80.8846 22.9691 81.8694 22.0587 83.008 22.0817" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M91.106 16.1166C91.0289 17.2515 90.1019 18.1115 89.0677 18.1499" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M91.3691 20.4777L89.2074 20.331" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M88.6283 22.4633C89.7595 22.5944 90.6122 23.6295 90.5351 24.7644" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip17_532_2318)">
<path d="M109.149 15.7127L110.096 16.7977" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M112.388 16.9533L113.473 16.0062" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M109.502 18.5218L109.538 17.9814C109.543 17.7617 109.593 17.5453 109.684 17.3453C109.775 17.1453 109.905 16.9658 110.068 16.8176C110.23 16.6695 110.421 16.5557 110.628 16.4833C110.835 16.4108 111.055 16.3811 111.275 16.396C111.494 16.4109 111.708 16.47 111.904 16.5699C112.099 16.6697 112.273 16.8082 112.414 16.9769C112.554 17.1456 112.659 17.3411 112.723 17.5516C112.786 17.7621 112.805 17.9831 112.781 18.2015L112.744 18.7419" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M110.651 25.5871C108.867 25.466 107.507 23.9078 107.628 22.1245L107.738 20.5032C107.777 19.9299 108.042 19.3955 108.475 19.0176C108.908 18.6397 109.473 18.4493 110.047 18.4882L112.208 18.635C112.782 18.6739 113.316 18.939 113.694 19.3719C114.072 19.8048 114.262 20.3701 114.223 20.9434L114.113 22.5647C113.992 24.3481 112.434 25.7082 110.651 25.5871Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M110.651 25.5871L110.981 20.7233" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M108.098 19.4418C107.062 19.2629 106.26 18.2855 106.337 17.1506" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M107.665 21.584L105.503 21.4373" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M105.75 25.7973C105.827 24.6624 106.812 23.752 107.95 23.775" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M116.048 17.8099C115.971 18.9448 115.044 19.8048 114.01 19.8431" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M116.312 22.171L114.15 22.0243" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M113.571 24.1566C114.702 24.2877 115.555 25.3228 115.478 26.4577" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip18_532_2318)">
<path d="M134.092 17.4061L135.039 18.491" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M137.33 18.6466L138.415 17.6996" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M134.444 20.2151L134.481 19.6747C134.486 19.455 134.535 19.2387 134.626 19.0387C134.717 18.8387 134.848 18.6592 135.01 18.511C135.173 18.3628 135.363 18.2491 135.571 18.1766C135.778 18.1042 135.998 18.0745 136.217 18.0894C136.436 18.1042 136.65 18.1634 136.846 18.2632C137.042 18.3631 137.215 18.5015 137.356 18.6703C137.497 18.839 137.602 19.0345 137.665 19.245C137.728 19.4554 137.748 19.6765 137.723 19.8948L137.687 20.4353" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M135.593 27.2805C133.81 27.1594 132.45 25.6012 132.571 23.8178L132.681 22.1965C132.72 21.6232 132.985 21.0889 133.418 20.711C133.851 20.3331 134.416 20.1427 134.989 20.1816L137.151 20.3284C137.724 20.3673 138.259 20.6324 138.637 21.0653C139.014 21.4982 139.205 22.0635 139.166 22.6368L139.056 24.2581C138.935 26.0415 137.377 27.4015 135.593 27.2805Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M135.593 27.2804L135.923 22.4166" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M133.041 21.1351C132.005 20.9562 131.203 19.9788 131.28 18.8439" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M132.607 23.2774L130.446 23.1306" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M130.693 27.4907C130.77 26.3558 131.755 25.4454 132.893 25.4684" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M140.991 19.5032C140.914 20.6381 139.987 21.4981 138.953 21.5365" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M141.254 23.8644L139.093 23.7177" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M138.513 25.85C139.645 25.9811 140.497 27.0162 140.42 28.1511" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip19_532_2318)">
<path d="M159.035 19.0994L159.982 20.1843" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M162.273 20.3399L163.358 19.3929" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M159.387 21.9084L159.424 21.368C159.429 21.1483 159.478 20.932 159.569 20.732C159.66 20.532 159.791 20.3525 159.953 20.2043C160.115 20.0561 160.306 19.9424 160.513 19.8699C160.721 19.7975 160.941 19.7678 161.16 19.7827C161.379 19.7975 161.593 19.8567 161.789 19.9565C161.985 20.0564 162.158 20.1948 162.299 20.3636C162.44 20.5323 162.545 20.7278 162.608 20.9383C162.671 21.1487 162.691 21.3698 162.666 21.5881L162.629 22.1286" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M160.536 28.9738C158.752 28.8527 157.392 27.2945 157.513 25.5111L157.624 23.8898C157.662 23.3165 157.928 22.7822 158.36 22.4043C158.793 22.0264 159.359 21.836 159.932 21.8749L162.094 22.0217C162.667 22.0606 163.201 22.3257 163.579 22.7586C163.957 23.1915 164.148 23.7568 164.109 24.3301L163.999 25.9514C163.877 27.7348 162.319 29.0948 160.536 28.9738Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M160.536 28.9737L160.866 24.1099" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M157.983 22.8284C156.948 22.6495 156.145 21.6721 156.222 20.5372" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M157.55 24.9707L155.388 24.8239" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M155.635 29.184C155.712 28.0491 156.697 27.1387 157.836 27.1617" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M165.934 21.1965C165.857 22.3314 164.93 23.1914 163.896 23.2298" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M166.197 25.5577L164.035 25.4109" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M163.456 27.5433C164.587 27.6744 165.44 28.7095 165.363 29.8444" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip20_532_2318)">
<path d="M183.977 20.7927L184.924 21.8776" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M187.216 22.0332L188.3 21.0862" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M184.329 23.6017L184.366 23.0613C184.371 22.8416 184.421 22.6253 184.512 22.4253C184.603 22.2253 184.733 22.0458 184.895 21.8976C185.058 21.7494 185.248 21.6357 185.456 21.5632C185.663 21.4908 185.883 21.4611 186.102 21.476C186.322 21.4908 186.536 21.55 186.731 21.6498C186.927 21.7497 187.101 21.8881 187.241 22.0569C187.382 22.2256 187.487 22.4211 187.55 22.6315C187.613 22.842 187.633 23.0631 187.609 23.2814L187.572 23.8219" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M185.478 30.6671C183.695 30.546 182.335 28.9878 182.456 27.2044L182.566 25.5831C182.605 25.0098 182.87 24.4754 183.303 24.0976C183.736 23.7197 184.301 23.5293 184.875 23.5682L187.036 23.715C187.61 23.7539 188.144 24.019 188.522 24.4519C188.9 24.8848 189.09 25.4501 189.051 26.0234L188.941 27.6447C188.82 29.4281 187.262 30.7881 185.478 30.6671Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M185.478 30.667L185.809 25.8032" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M182.926 24.5217C181.89 24.3428 181.088 23.3654 181.165 22.2305" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M182.493 26.664L180.331 26.5172" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M180.578 30.8773C180.655 29.7424 181.64 28.832 182.778 28.855" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M190.876 22.8898C190.799 24.0247 189.872 24.8847 188.838 24.9231" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M191.139 27.251L188.978 27.1042" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M188.399 29.2366C189.53 29.3677 190.383 30.4028 190.306 31.5377" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip21_532_2318)">
<path d="M208.92 22.486L209.867 23.5709" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M212.158 23.7265L213.243 22.7795" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M209.272 25.295L209.309 24.7546C209.314 24.5349 209.363 24.3186 209.454 24.1186C209.545 23.9186 209.676 23.7391 209.838 23.5909C210 23.4427 210.191 23.329 210.398 23.2565C210.606 23.1841 210.826 23.1544 211.045 23.1693C211.264 23.1841 211.478 23.2433 211.674 23.3431C211.87 23.443 212.043 23.5814 212.184 23.7502C212.325 23.9189 212.43 24.1144 212.493 24.3248C212.556 24.5353 212.576 24.7564 212.551 24.9747L212.514 25.5152" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M210.421 32.3604C208.638 32.2393 207.278 30.6811 207.399 28.8977L207.509 27.2764C207.548 26.7031 207.813 26.1687 208.246 25.7909C208.678 25.413 209.244 25.2226 209.817 25.2615L211.979 25.4083C212.552 25.4472 213.086 25.7123 213.464 26.1452C213.842 26.5781 214.033 27.1434 213.994 27.7167L213.884 29.338C213.763 31.1214 212.204 32.4814 210.421 32.3604Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M210.421 32.3603L210.751 27.4965" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M207.868 26.215C206.833 26.0361 206.03 25.0587 206.107 23.9238" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M207.435 28.3573L205.274 28.2105" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M205.52 32.5706C205.598 31.4357 206.582 30.5253 207.721 30.5483" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M215.819 24.5831C215.742 25.718 214.815 26.578 213.781 26.6164" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M216.082 28.9443L213.92 28.7975" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M213.341 30.9299C214.472 31.061 215.325 32.0961 215.248 33.231" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip22_532_2318)">
<path d="M233.862 24.1793L234.809 25.2642" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M237.101 25.4198L238.186 24.4728" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M234.215 26.9883L234.251 26.4479C234.256 26.2282 234.306 26.0119 234.397 25.8119C234.488 25.6118 234.618 25.4324 234.781 25.2842C234.943 25.136 235.134 25.0223 235.341 24.9498C235.548 24.8774 235.768 24.8477 235.988 24.8626C236.207 24.8774 236.421 24.9366 236.616 25.0364C236.812 25.1363 236.986 25.2747 237.126 25.4435C237.267 25.6122 237.372 25.8077 237.435 26.0181C237.499 26.2286 237.518 26.4497 237.494 26.668L237.457 27.2085" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M235.364 34.0537C233.58 33.9326 232.22 32.3744 232.341 30.591L232.451 28.9697C232.49 28.3964 232.755 27.862 233.188 27.4842C233.621 27.1063 234.186 26.9159 234.76 26.9548L236.921 27.1016C237.495 27.1405 238.029 27.4055 238.407 27.8385C238.785 28.2714 238.975 28.8367 238.936 29.41L238.826 31.0313C238.705 32.8147 237.147 34.1747 235.364 34.0537Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M235.364 34.0536L235.694 29.1898" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M232.811 27.9083C231.775 27.7294 230.973 26.752 231.05 25.6171" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M232.378 30.0506L230.216 29.9038" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M230.463 34.2639C230.54 33.129 231.525 32.2186 232.663 32.2416" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M240.762 26.2764C240.684 27.4113 239.757 28.2713 238.723 28.3097" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M241.025 30.6376L238.863 30.4908" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M238.284 32.6232C239.415 32.7543 240.268 33.7894 240.191 34.9243" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip23_532_2318)">
<path d="M258.805 25.8726L259.752 26.9576" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M262.043 27.1132L263.128 26.1661" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M259.157 28.6817L259.194 28.1413C259.199 27.9216 259.248 27.7052 259.339 27.5052C259.43 27.3052 259.561 27.1257 259.723 26.9776C259.885 26.8294 260.076 26.7157 260.284 26.6432C260.491 26.5707 260.711 26.541 260.93 26.5559C261.149 26.5708 261.363 26.6299 261.559 26.7298C261.755 26.8296 261.928 26.9681 262.069 27.1368C262.21 27.3055 262.315 27.501 262.378 27.7115C262.441 27.922 262.461 28.1431 262.436 28.3614L262.4 28.9018" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M260.306 35.747C258.523 35.626 257.163 34.0678 257.284 32.2844L257.394 30.6631C257.433 30.0898 257.698 29.5554 258.131 29.1775C258.564 28.7997 259.129 28.6092 259.702 28.6482L261.864 28.7949C262.437 28.8338 262.972 29.0989 263.35 29.5318C263.727 29.9647 263.918 30.53 263.879 31.1034L263.769 32.7246C263.648 34.508 262.09 35.8681 260.306 35.747Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M260.306 35.747L260.636 30.8832" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M257.754 29.6017C256.718 29.4228 255.916 28.4454 255.993 27.3105" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M257.32 31.7439L255.159 31.5972" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M255.406 35.9572C255.483 34.8223 256.467 33.9119 257.606 33.9349" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M265.704 27.9698C265.627 29.1047 264.7 29.9647 263.666 30.0031" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M265.967 32.331L263.806 32.1842" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M263.226 34.3165C264.358 34.4476 265.21 35.4827 265.133 36.6176" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip24_532_2318)">
<path d="M283.747 27.5659L284.694 28.6509" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M286.986 28.8065L288.071 27.8594" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M284.1 30.375L284.136 29.8346C284.141 29.6149 284.191 29.3985 284.282 29.1985C284.373 28.9985 284.503 28.819 284.666 28.6709C284.828 28.5227 285.019 28.409 285.226 28.3365C285.434 28.264 285.653 28.2343 285.873 28.2492C286.092 28.2641 286.306 28.3232 286.502 28.4231C286.697 28.5229 286.871 28.6614 287.012 28.8301C287.152 28.9988 287.257 29.1943 287.321 29.4048C287.384 29.6153 287.404 29.8363 287.379 30.0547L287.342 30.5951" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M285.249 37.4403C283.465 37.3193 282.105 35.7611 282.226 33.9777L282.336 32.3564C282.375 31.7831 282.64 31.2487 283.073 30.8708C283.506 30.493 284.072 30.3025 284.645 30.3415L286.807 30.4882C287.38 30.5271 287.914 30.7922 288.292 31.2251C288.67 31.658 288.86 32.2233 288.821 32.7967L288.711 34.4179C288.59 36.2013 287.032 37.5614 285.249 37.4403Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M285.249 37.4403L285.579 32.5765" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M282.696 31.295C281.661 31.1161 280.858 30.1387 280.935 29.0038" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M282.263 33.4372L280.101 33.2905" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M280.348 37.6505C280.425 36.5156 281.41 35.6052 282.549 35.6282" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M290.647 29.6631C290.57 30.798 289.643 31.658 288.608 31.6964" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M290.91 34.0243L288.748 33.8775" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M288.169 36.0098C289.3 36.1409 290.153 37.176 290.076 38.3109" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip25_532_2318)">
<path d="M308.69 29.2592L309.637 30.3442" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M311.928 30.4998L313.013 29.5527" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M309.042 32.0683L309.079 31.5279C309.084 31.3082 309.134 31.0918 309.225 30.8918C309.316 30.6918 309.446 30.5123 309.608 30.3642C309.771 30.216 309.961 30.1023 310.169 30.0298C310.376 29.9573 310.596 29.9276 310.815 29.9425C311.035 29.9574 311.248 30.0165 311.444 30.1164C311.64 30.2162 311.813 30.3547 311.954 30.5234C312.095 30.6921 312.2 30.8876 312.263 31.0981C312.326 31.3086 312.346 31.5296 312.322 31.748L312.285 32.2884" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M310.191 39.1336C308.408 39.0126 307.048 37.4544 307.169 35.671L307.279 34.0497C307.318 33.4764 307.583 32.942 308.016 32.5641C308.449 32.1863 309.014 31.9958 309.587 32.0348L311.749 32.1815C312.322 32.2204 312.857 32.4855 313.235 32.9184C313.613 33.3513 313.803 33.9166 313.764 34.49L313.654 36.1112C313.533 37.8946 311.975 39.2547 310.191 39.1336Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M310.191 39.1336L310.522 34.2698" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M307.639 32.9883C306.603 32.8094 305.801 31.832 305.878 30.6971" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M307.206 35.1305L305.044 34.9838" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M305.291 39.3438C305.368 38.2089 306.353 37.2985 307.491 37.3215" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M315.589 31.3564C315.512 32.4913 314.585 33.3513 313.551 33.3897" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M315.852 35.7176L313.691 35.5708" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M313.112 37.7031C314.243 37.8342 315.096 38.8693 315.018 40.0042" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip26_532_2318)">
<path d="M333.633 30.9525L334.58 32.0375" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M336.871 32.1931L337.956 31.246" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M333.985 33.7616L334.022 33.2212C334.027 33.0015 334.076 32.7851 334.167 32.5851C334.258 32.3851 334.389 32.2056 334.551 32.0574C334.713 31.9093 334.904 31.7955 335.111 31.7231C335.319 31.6506 335.539 31.6209 335.758 31.6358C335.977 31.6507 336.191 31.7098 336.387 31.8097C336.583 31.9095 336.756 32.048 336.897 32.2167C337.038 32.3854 337.143 32.5809 337.206 32.7914C337.269 33.0019 337.289 33.2229 337.264 33.4413L337.227 33.9817" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M335.134 40.8269C333.351 40.7059 331.99 39.1477 332.112 37.3643L332.222 35.743C332.261 35.1697 332.526 34.6353 332.959 34.2574C333.391 33.8796 333.957 33.6891 334.53 33.7281L336.692 33.8748C337.265 33.9137 337.799 34.1788 338.177 34.6117C338.555 35.0446 338.746 35.6099 338.707 36.1833L338.597 37.8045C338.476 39.5879 336.917 40.948 335.134 40.8269Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M335.134 40.8269L335.464 35.9631" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M332.581 34.6816C331.546 34.5027 330.743 33.5253 330.82 32.3904" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M332.148 36.8238L329.987 36.6771" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M330.233 41.0371C330.31 39.9022 331.295 38.9918 332.434 39.0148" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M340.532 33.0497C340.455 34.1846 339.528 35.0446 338.494 35.083" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M340.795 37.4109L338.633 37.2641" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M338.054 39.3964C339.185 39.5275 340.038 40.5626 339.961 41.6975" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip27_532_2318)">
<path d="M358.575 32.6458L359.522 33.7308" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M361.814 33.8864L362.899 32.9393" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M358.927 35.4549L358.964 34.9145C358.969 34.6948 359.019 34.4784 359.11 34.2784C359.201 34.0784 359.331 33.8989 359.494 33.7507C359.656 33.6026 359.846 33.4888 360.054 33.4164C360.261 33.3439 360.481 33.3142 360.7 33.3291C360.92 33.344 361.134 33.4031 361.329 33.503C361.525 33.6028 361.699 33.7413 361.839 33.91C361.98 34.0787 362.085 34.2742 362.148 34.4847C362.211 34.6952 362.231 34.9162 362.207 35.1346L362.17 35.675" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M360.077 42.5202C358.293 42.3992 356.933 40.841 357.054 39.0576L357.164 37.4363C357.203 36.863 357.468 36.3286 357.901 35.9507C358.334 35.5729 358.899 35.3824 359.473 35.4214L361.634 35.5681C362.208 35.607 362.742 35.8721 363.12 36.305C363.498 36.7379 363.688 37.3032 363.649 37.8765L363.539 39.4978C363.418 41.2812 361.86 42.6413 360.077 42.5202Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M360.076 42.5202L360.407 37.6564" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M357.524 36.3749C356.488 36.196 355.686 35.2186 355.763 34.0837" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M357.091 38.5171L354.929 38.3704" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M355.176 42.7304C355.253 41.5955 356.238 40.6851 357.376 40.7081" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M365.474 34.743C365.397 35.8779 364.47 36.7379 363.436 36.7763" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M365.738 39.1042L363.576 38.9574" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M362.997 41.0897C364.128 41.2208 364.981 42.2559 364.904 43.3908" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip28_532_2318)">
<path d="M383.518 34.3392L384.465 35.4241" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M386.756 35.5797L387.841 34.6327" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M383.87 37.1482L383.907 36.6078C383.912 36.3881 383.961 36.1718 384.052 35.9718C384.143 35.7718 384.274 35.5923 384.436 35.4441C384.598 35.296 384.789 35.1822 384.996 35.1097C385.204 35.0373 385.424 35.0076 385.643 35.0225C385.862 35.0374 386.076 35.0965 386.272 35.1963C386.468 35.2962 386.641 35.4346 386.782 35.6034C386.923 35.7721 387.028 35.9676 387.091 36.1781C387.154 36.3885 387.174 36.6096 387.149 36.8279L387.113 37.3684" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M385.019 44.2136C383.236 44.0925 381.876 42.5343 381.997 40.7509L382.107 39.1296C382.146 38.5563 382.411 38.022 382.844 37.6441C383.277 37.2662 383.842 37.0758 384.415 37.1147L386.577 37.2615C387.15 37.3004 387.685 37.5655 388.062 37.9984C388.44 38.4313 388.631 38.9966 388.592 39.5699L388.482 41.1912C388.361 42.9746 386.803 44.3347 385.019 44.2136Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M385.019 44.2135L385.349 39.3497" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M382.467 38.0682C381.431 37.8893 380.629 36.9119 380.706 35.777" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M382.033 40.2105L379.872 40.0637" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M380.119 44.4238C380.196 43.2889 381.18 42.3785 382.319 42.4015" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M390.417 36.4363C390.34 37.5712 389.413 38.4312 388.379 38.4696" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M390.68 40.7975L388.518 40.6508" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M387.939 42.7831C389.071 42.9142 389.923 43.9493 389.846 45.0842" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip29_532_2318)">
<path d="M57.8418 33.2779L58.7888 34.3629" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M61.0802 34.5184L62.1652 33.5714" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M58.1941 36.087L58.2308 35.5465C58.2358 35.3269 58.2854 35.1105 58.3763 34.9105C58.4673 34.7105 58.5979 34.531 58.7602 34.3828C58.9225 34.2347 59.1131 34.1209 59.3205 34.0485C59.5279 33.976 59.7479 33.9463 59.9671 33.9612C60.1864 33.9761 60.4003 34.0352 60.596 34.1351C60.7918 34.2349 60.9653 34.3734 61.106 34.5421C61.2468 34.7108 61.3519 34.9063 61.415 35.1168C61.4781 35.3272 61.498 35.5483 61.4733 35.7667L61.4366 36.3071" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M59.3431 43.1523C57.5597 43.0312 56.1996 41.473 56.3207 39.6896L56.4308 38.0684C56.4697 37.4951 56.7348 36.9607 57.1677 36.5828C57.6006 36.2049 58.1659 36.0145 58.7392 36.0534L60.9009 36.2002C61.4742 36.2391 62.0086 36.5042 62.3865 36.9371C62.7643 37.37 62.9548 37.9353 62.9158 38.5086L62.8058 40.1299C62.6847 41.9133 61.1265 43.2734 59.3431 43.1523Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M59.3431 43.1523L59.6733 38.2885" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M56.7906 37.007C55.7549 36.8281 54.9526 35.8506 55.0297 34.7158" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M56.3574 39.1492L54.1957 39.0024" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M54.4426 43.3625C54.5196 42.2276 55.5044 41.3172 56.643 41.3402" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M64.741 35.3751C64.6639 36.5099 63.7369 37.37 62.7027 37.4083" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M65.0041 39.7362L62.8424 39.5895" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M62.2633 41.7218C63.3945 41.8529 64.2472 42.888 64.1702 44.0229" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip30_532_2318)">
<path d="M82.7844 34.9712L83.7314 36.0562" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M86.0228 36.2117L87.1078 35.2647" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M83.1367 37.7803L83.1734 37.2398C83.1785 37.0202 83.228 36.8038 83.319 36.6038C83.41 36.4038 83.5405 36.2243 83.7028 36.0761C83.8651 35.928 84.0557 35.8142 84.2631 35.7418C84.4706 35.6693 84.6905 35.6396 84.9098 35.6545C85.129 35.6694 85.3429 35.7285 85.5387 35.8284C85.7344 35.9282 85.9079 36.0667 86.0486 36.2354C86.1894 36.4041 86.2945 36.5996 86.3576 36.8101C86.4208 37.0205 86.4406 37.2416 86.4159 37.46L86.3792 38.0004" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M84.2857 44.8456C82.5023 44.7245 81.1423 43.1663 81.2633 41.3829L81.3734 39.7617C81.4123 39.1884 81.6774 38.654 82.1103 38.2761C82.5432 37.8982 83.1085 37.7078 83.6818 37.7467L85.8435 37.8935C86.4168 37.9324 86.9512 38.1975 87.3291 38.6304C87.707 39.0633 87.8974 39.6286 87.8585 40.2019L87.7484 41.8232C87.6273 43.6066 86.0691 44.9667 84.2857 44.8456Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M84.2858 44.8456L84.616 39.9818" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M81.7332 38.7003C80.6976 38.5214 79.8952 37.5439 79.9723 36.4091" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M81.3 40.8425L79.1383 40.6957" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M79.3852 45.0558C79.4623 43.9209 80.447 43.0105 81.5856 43.0335" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M89.6836 37.0684C89.6065 38.2032 88.6795 39.0633 87.6454 39.1016" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M89.9467 41.4295L87.785 41.2828" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M87.2059 43.4151C88.3372 43.5462 89.1898 44.5813 89.1128 45.7162" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip31_532_2318)">
<path d="M107.727 36.6645L108.674 37.7495" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M110.965 37.905L112.05 36.958" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M108.079 39.4736L108.116 38.9331C108.121 38.7135 108.17 38.4971 108.261 38.2971C108.352 38.0971 108.483 37.9176 108.645 37.7694C108.808 37.6213 108.998 37.5075 109.206 37.4351C109.413 37.3626 109.633 37.3329 109.852 37.3478C110.071 37.3627 110.285 37.4218 110.481 37.5217C110.677 37.6215 110.85 37.76 110.991 37.9287C111.132 38.0974 111.237 38.2929 111.3 38.5034C111.363 38.7138 111.383 38.9349 111.358 39.1533L111.322 39.6937" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M109.228 46.5389C107.445 46.4178 106.085 44.8596 106.206 43.0762L106.316 41.455C106.355 40.8816 106.62 40.3473 107.053 39.9694C107.486 39.5915 108.051 39.4011 108.624 39.44L110.786 39.5868C111.359 39.6257 111.894 39.8908 112.272 40.3237C112.649 40.7566 112.84 41.3219 112.801 41.8952L112.691 43.5165C112.57 45.2999 111.012 46.66 109.228 46.5389Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M109.228 46.5389L109.558 41.675" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M106.676 40.3936C105.64 40.2147 104.838 39.2372 104.915 38.1024" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M106.243 42.5358L104.081 42.389" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M104.328 46.7491C104.405 45.6142 105.39 44.7038 106.528 44.7268" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M114.626 38.7616C114.549 39.8965 113.622 40.7566 112.588 40.7949" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M114.889 43.1228L112.728 42.9761" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M112.148 45.1084C113.28 45.2395 114.132 46.2746 114.055 47.4095" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip32_532_2318)">
<path d="M132.67 38.3578L133.617 39.4428" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M135.908 39.5984L136.993 38.6514" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M133.022 41.1669L133.059 40.6265C133.064 40.4068 133.113 40.1905 133.204 39.9904C133.295 39.7904 133.426 39.6109 133.588 39.4628C133.75 39.3146 133.941 39.2009 134.148 39.1284C134.356 39.0559 134.576 39.0263 134.795 39.0411C135.014 39.056 135.228 39.1152 135.424 39.215C135.62 39.3148 135.793 39.4533 135.934 39.622C136.075 39.7908 136.18 39.9863 136.243 40.1967C136.306 40.4072 136.326 40.6283 136.301 40.8466L136.264 41.387" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M134.171 48.2323C132.387 48.1112 131.027 46.553 131.148 44.7696L131.259 43.1483C131.297 42.575 131.563 42.0406 131.995 41.6628C132.428 41.2849 132.994 41.0945 133.567 41.1334L135.729 41.2801C136.302 41.3191 136.836 41.5841 137.214 42.0171C137.592 42.45 137.783 43.0153 137.744 43.5886L137.634 45.2099C137.512 46.9932 135.954 48.3533 134.171 48.2323Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M134.171 48.2322L134.501 43.3684" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M131.618 42.0869C130.583 41.908 129.78 40.9306 129.857 39.7957" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M131.185 44.2292L129.023 44.0824" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M129.27 48.4425C129.347 47.3076 130.332 46.3972 131.471 46.4202" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M139.569 40.455C139.492 41.5899 138.565 42.4499 137.53 42.4883" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M139.832 44.8162L137.67 44.6694" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M137.091 46.8018C138.222 46.9328 139.075 47.968 138.998 49.1029" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip33_532_2318)">
<path d="M157.612 40.0511L158.559 41.1361" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M160.851 41.2917L161.936 40.3447" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M157.964 42.8602L158.001 42.3198C158.006 42.1001 158.056 41.8837 158.147 41.6837C158.238 41.4837 158.368 41.3042 158.531 41.1561C158.693 41.0079 158.883 40.8942 159.091 40.8217C159.298 40.7492 159.518 40.7196 159.738 40.7344C159.957 40.7493 160.171 40.8085 160.366 40.9083C160.562 41.0081 160.736 41.1466 160.876 41.3153C161.017 41.4841 161.122 41.6796 161.185 41.89C161.249 42.1005 161.268 42.3216 161.244 42.5399L161.207 43.0803" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M159.113 49.9256C157.33 49.8045 155.97 48.2463 156.091 46.4629L156.201 44.8416C156.24 44.2683 156.505 43.7339 156.938 43.3561C157.371 42.9782 157.936 42.7878 158.51 42.8267L160.671 42.9734C161.245 43.0124 161.779 43.2774 162.157 43.7104C162.535 44.1433 162.725 44.7086 162.686 45.2819L162.576 46.9032C162.455 48.6865 160.897 50.0466 159.113 49.9256Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M159.114 49.9255L159.444 45.0617" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M156.561 43.7802C155.525 43.6013 154.723 42.6239 154.8 41.489" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M156.128 45.9224L153.966 45.7757" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M154.213 50.1358C154.29 49.0009 155.275 48.0905 156.413 48.1135" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M164.511 42.1483C164.434 43.2832 163.507 44.1432 162.473 44.1816" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M164.775 46.5095L162.613 46.3627" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M162.034 48.4951C163.165 48.6261 164.018 49.6613 163.941 50.7962" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip34_532_2318)">
<path d="M182.555 41.7444L183.502 42.8294" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M185.793 42.985L186.878 42.038" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M182.907 44.5535L182.944 44.0131C182.949 43.7934 182.998 43.577 183.089 43.377C183.18 43.177 183.311 42.9975 183.473 42.8494C183.635 42.7012 183.826 42.5875 184.033 42.515C184.241 42.4425 184.461 42.4129 184.68 42.4277C184.899 42.4426 185.113 42.5018 185.309 42.6016C185.505 42.7014 185.678 42.8399 185.819 43.0086C185.96 43.1774 186.065 43.3729 186.128 43.5833C186.191 43.7938 186.211 44.0149 186.186 44.2332L186.149 44.7736" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M184.056 51.6189C182.273 51.4978 180.913 49.9396 181.034 48.1562L181.144 46.5349C181.183 45.9616 181.448 45.4272 181.881 45.0494C182.314 44.6715 182.879 44.4811 183.452 44.52L185.614 44.6667C186.187 44.7057 186.722 44.9707 187.099 45.4037C187.477 45.8366 187.668 46.4019 187.629 46.9752L187.519 48.5964C187.398 50.3798 185.839 51.7399 184.056 51.6189Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M184.056 51.6188L184.386 46.755" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M181.503 45.4735C180.468 45.2946 179.666 44.3172 179.743 43.1823" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M181.07 47.6157L178.909 47.469" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M179.156 51.829C179.233 50.6942 180.217 49.7838 181.356 49.8068" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M189.454 43.8416C189.377 44.9765 188.45 45.8365 187.416 45.8749" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M189.717 48.2028L187.555 48.056" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M186.976 50.1884C188.108 50.3194 188.96 51.3546 188.883 52.4895" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip35_532_2318)">
<path d="M207.497 43.4377L208.444 44.5227" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M210.736 44.6783L211.821 43.7313" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M207.85 46.2468L207.886 45.7064C207.891 45.4867 207.941 45.2703 208.032 45.0703C208.123 44.8703 208.253 44.6908 208.416 44.5427C208.578 44.3945 208.769 44.2808 208.976 44.2083C209.183 44.1358 209.403 44.1062 209.623 44.121C209.842 44.1359 210.056 44.1951 210.251 44.2949C210.447 44.3947 210.621 44.5332 210.761 44.7019C210.902 44.8707 211.007 45.0662 211.07 45.2766C211.134 45.4871 211.153 45.7082 211.129 45.9265L211.092 46.4669" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M208.999 53.3122C207.215 53.1911 205.855 51.6329 205.976 49.8495L206.086 48.2282C206.125 47.6549 206.39 47.1205 206.823 46.7427C207.256 46.3648 207.821 46.1744 208.395 46.2133L210.556 46.36C211.13 46.399 211.664 46.664 212.042 47.097C212.42 47.5299 212.61 48.0952 212.571 48.6685L212.461 50.2897C212.34 52.0731 210.782 53.4332 208.999 53.3122Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M208.999 53.3121L209.329 48.4483" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M206.446 47.1668C205.41 46.9879 204.608 46.0105 204.685 44.8756" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M206.013 49.309L203.851 49.1623" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M204.098 53.5223C204.175 52.3875 205.16 51.4771 206.298 51.5001" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M214.397 45.5349C214.32 46.6698 213.392 47.5298 212.358 47.5682" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M214.66 49.8961L212.498 49.7493" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M211.919 51.8817C213.05 52.0127 213.903 53.0479 213.826 54.1828" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip36_532_2318)">
<path d="M232.44 45.131L233.387 46.216" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M235.678 46.3716L236.763 45.4246" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M232.792 47.9401L232.829 47.3997C232.834 47.18 232.883 46.9636 232.974 46.7636C233.065 46.5636 233.196 46.3841 233.358 46.236C233.521 46.0878 233.711 45.9741 233.919 45.9016C234.126 45.8291 234.346 45.7995 234.565 45.8143C234.784 45.8292 234.998 45.8884 235.194 45.9882C235.39 46.088 235.563 46.2265 235.704 46.3952C235.845 46.564 235.95 46.7595 236.013 46.9699C236.076 47.1804 236.096 47.4015 236.071 47.6198L236.035 48.1602" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M233.941 55.0054C232.158 54.8844 230.798 53.3262 230.919 51.5428L231.029 49.9215C231.068 49.3482 231.333 48.8138 231.766 48.4359C232.199 48.058 232.764 47.8676 233.337 47.9065L235.499 48.0533C236.072 48.0922 236.607 48.3573 236.985 48.7902C237.362 49.2231 237.553 49.7884 237.514 50.3617L237.404 51.983C237.283 53.7664 235.725 55.1265 233.941 55.0054Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M233.941 55.0054L234.271 50.1416" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M231.389 48.8601C230.353 48.6812 229.551 47.7038 229.628 46.5689" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M230.956 51.0023L228.794 50.8556" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M229.041 55.2156C229.118 54.0808 230.103 53.1704 231.241 53.1934" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M239.339 47.2282C239.262 48.3631 238.335 49.2231 237.301 49.2615" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M239.602 51.5894L237.441 51.4426" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M236.862 53.575C237.993 53.706 238.845 54.7412 238.768 55.8761" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip37_532_2318)">
<path d="M257.383 46.8244L258.33 47.9094" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M260.621 48.0649L261.706 47.1179" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M257.735 49.6335L257.771 49.093C257.776 48.8734 257.826 48.657 257.917 48.457C258.008 48.257 258.139 48.0775 258.301 47.9293C258.463 47.7812 258.654 47.6674 258.861 47.595C259.069 47.5225 259.289 47.4928 259.508 47.5077C259.727 47.5226 259.941 47.5817 260.137 47.6816C260.332 47.7814 260.506 47.9199 260.647 48.0886C260.787 48.2573 260.893 48.4528 260.956 48.6633C261.019 48.8738 261.039 49.0948 261.014 49.3132L260.977 49.8536" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M258.884 56.6988C257.1 56.5777 255.74 55.0195 255.861 53.2361L255.971 51.6148C256.01 51.0415 256.275 50.5072 256.708 50.1293C257.141 49.7514 257.707 49.561 258.28 49.5999L260.442 49.7467C261.015 49.7856 261.549 50.0507 261.927 50.4836C262.305 50.9165 262.495 51.4818 262.457 52.0551L262.346 53.6764C262.225 55.4598 260.667 56.8199 258.884 56.6988Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M258.884 56.6988L259.214 51.835" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M256.331 50.5534C255.296 50.3745 254.493 49.3971 254.57 48.2622" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M255.898 52.6957L253.736 52.549" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M253.983 56.909C254.06 55.7741 255.045 54.8637 256.184 54.8867" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M264.282 48.9215C264.205 50.0564 263.278 50.9164 262.244 50.9548" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M264.545 53.2827L262.383 53.136" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M261.804 55.2683C262.935 55.3994 263.788 56.4345 263.711 57.5694" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip38_532_2318)">
<path d="M282.325 48.5177L283.272 49.6027" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M285.563 49.7582L286.648 48.8112" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M282.677 51.3268L282.714 50.7863C282.719 50.5667 282.769 50.3503 282.86 50.1503C282.951 49.9503 283.081 49.7708 283.243 49.6226C283.406 49.4745 283.596 49.3607 283.804 49.2883C284.011 49.2158 284.231 49.1861 284.45 49.201C284.67 49.2159 284.884 49.275 285.079 49.3749C285.275 49.4747 285.448 49.6132 285.589 49.7819C285.73 49.9506 285.835 50.1461 285.898 50.3566C285.961 50.5671 285.981 50.7881 285.957 51.0065L285.92 51.5469" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M283.826 58.3921C282.043 58.271 280.683 56.7128 280.804 54.9294L280.914 53.3081C280.953 52.7348 281.218 52.2005 281.651 51.8226C282.084 51.4447 282.649 51.2543 283.222 51.2932L285.384 51.44C285.957 51.4789 286.492 51.744 286.87 52.1769C287.248 52.6098 287.438 53.1751 287.399 53.7484L287.289 55.3697C287.168 57.1531 285.61 58.5132 283.826 58.3921Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M283.826 58.3921L284.157 53.5283" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M281.274 52.2467C280.238 52.0678 279.436 51.0904 279.513 49.9555" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M280.841 54.389L278.679 54.2422" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M278.926 58.6023C279.003 57.4674 279.988 56.557 281.126 56.58" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M289.224 50.6148C289.147 51.7497 288.22 52.6097 287.186 52.6481" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M289.487 54.976L287.326 54.8293" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M286.747 56.9616C287.878 57.0927 288.731 58.1278 288.653 59.2627" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip39_532_2318)">
<path d="M307.268 50.211L308.215 51.296" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M310.506 51.4515L311.591 50.5045" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M307.62 53.0201L307.657 52.4796C307.662 52.26 307.711 52.0436 307.802 51.8436C307.893 51.6436 308.024 51.4641 308.186 51.3159C308.348 51.1678 308.539 51.054 308.746 50.9816C308.954 50.9091 309.174 50.8794 309.393 50.8943C309.612 50.9092 309.826 50.9683 310.022 51.0682C310.218 51.168 310.391 51.3065 310.532 51.4752C310.673 51.6439 310.778 51.8394 310.841 52.0499C310.904 52.2604 310.924 52.4814 310.899 52.6998L310.862 53.2402" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M308.769 60.0854C306.986 59.9643 305.626 58.4061 305.747 56.6227L305.857 55.0014C305.896 54.4281 306.161 53.8938 306.594 53.5159C307.026 53.138 307.592 52.9476 308.165 52.9865L310.327 53.1333C310.9 53.1722 311.434 53.4373 311.812 53.8702C312.19 54.3031 312.381 54.8684 312.342 55.4417L312.232 57.063C312.111 58.8464 310.552 60.2064 308.769 60.0854Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M308.769 60.0854L309.099 55.2216" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M306.216 53.94C305.181 53.7611 304.378 52.7837 304.456 51.6488" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M305.783 56.0823L303.622 55.9355" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M303.869 60.2956C303.946 59.1607 304.93 58.2503 306.069 58.2733" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M314.167 52.3081C314.09 53.443 313.163 54.303 312.129 54.3414" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M314.43 56.6693L312.268 56.5226" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M311.689 58.6549C312.82 58.786 313.673 59.8211 313.596 60.956" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip40_532_2318)">
<path d="M332.21 51.9043L333.157 52.9893" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M335.449 53.1448L336.534 52.1978" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M332.562 54.7134L332.599 54.1729C332.604 53.9533 332.654 53.7369 332.745 53.5369C332.836 53.3369 332.966 53.1574 333.129 53.0092C333.291 52.8611 333.481 52.7473 333.689 52.6749C333.896 52.6024 334.116 52.5727 334.336 52.5876C334.555 52.6025 334.769 52.6616 334.964 52.7615C335.16 52.8613 335.334 52.9998 335.474 53.1685C335.615 53.3372 335.72 53.5327 335.783 53.7432C335.847 53.9537 335.866 54.1747 335.842 54.3931L335.805 54.9335" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M333.712 61.7787C331.928 61.6576 330.568 60.0994 330.689 58.316L330.799 56.6947C330.838 56.1214 331.103 55.5871 331.536 55.2092C331.969 54.8313 332.534 54.6409 333.108 54.6798L335.269 54.8266C335.843 54.8655 336.377 55.1306 336.755 55.5635C337.133 55.9964 337.323 56.5617 337.284 57.135L337.174 58.7563C337.053 60.5397 335.495 61.8997 333.712 61.7787Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M333.712 61.7787L334.042 56.9149" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M331.159 55.6333C330.123 55.4544 329.321 54.477 329.398 53.3421" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M330.726 57.7756L328.564 57.6288" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M328.811 61.9889C328.888 60.854 329.873 59.9436 331.011 59.9666" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M339.109 54.0014C339.032 55.1363 338.105 55.9963 337.071 56.0347" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M339.373 58.3626L337.211 58.2159" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M336.632 60.3482C337.763 60.4793 338.616 61.5144 338.539 62.6493" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip41_532_2318)">
<path d="M357.153 53.5977L358.1 54.6826" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M360.391 54.8382L361.476 53.8911" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M357.505 56.4067L357.542 55.8663C357.547 55.6466 357.596 55.4302 357.687 55.2302C357.778 55.0302 357.909 54.8507 358.071 54.7026C358.233 54.5544 358.424 54.4407 358.631 54.3682C358.839 54.2957 359.059 54.266 359.278 54.2809C359.497 54.2958 359.711 54.3549 359.907 54.4548C360.103 54.5546 360.276 54.6931 360.417 54.8618C360.558 55.0305 360.663 55.226 360.726 55.4365C360.789 55.647 360.809 55.8681 360.784 56.0864L360.748 56.6268" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M358.654 63.472C356.871 63.3509 355.511 61.7927 355.632 60.0093L355.742 58.3881C355.781 57.8148 356.046 57.2804 356.479 56.9025C356.912 56.5246 357.477 56.3342 358.05 56.3731L360.212 56.5199C360.785 56.5588 361.32 56.8239 361.697 57.2568C362.075 57.6897 362.266 58.255 362.227 58.8283L362.117 60.4496C361.996 62.233 360.438 63.5931 358.654 63.472Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M358.654 63.472L358.984 58.6082" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M356.102 57.3267C355.066 57.1478 354.264 56.1703 354.341 55.0355" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M355.668 59.4689L353.507 59.3222" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M353.754 63.6822C353.831 62.5473 354.815 61.637 355.954 61.66" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M364.052 55.6948C363.975 56.8296 363.048 57.6897 362.014 57.728" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M364.315 60.0559L362.154 59.9092" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M361.574 62.0415C362.706 62.1726 363.558 63.2078 363.481 64.3426" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip42_532_2318)">
<path d="M382.095 55.291L383.042 56.3759" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M385.334 56.5315L386.419 55.5844" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M382.448 58.1L382.484 57.5596C382.489 57.3399 382.539 57.1235 382.63 56.9235C382.721 56.7235 382.852 56.544 383.014 56.3959C383.176 56.2477 383.367 56.134 383.574 56.0615C383.782 55.989 384.002 55.9593 384.221 55.9742C384.44 55.9891 384.654 56.0482 384.85 56.1481C385.045 56.2479 385.219 56.3864 385.36 56.5551C385.5 56.7238 385.605 56.9193 385.669 57.1298C385.732 57.3403 385.752 57.5614 385.727 57.7797L385.69 58.3201" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M383.597 65.1653C381.813 65.0442 380.453 63.486 380.574 61.7026L380.684 60.0814C380.723 59.5081 380.988 58.9737 381.421 58.5958C381.854 58.2179 382.42 58.0275 382.993 58.0664L385.155 58.2132C385.728 58.2521 386.262 58.5172 386.64 58.9501C387.018 59.383 387.208 59.9483 387.169 60.5216L387.059 62.1429C386.938 63.9263 385.38 65.2864 383.597 65.1653Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M383.597 65.1653L383.927 60.3015" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M381.044 59.02C380.009 58.8411 379.206 57.8636 379.283 56.7288" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M380.611 61.1622L378.449 61.0155" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M378.696 65.3755C378.773 64.2406 379.758 63.3302 380.897 63.3533" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M388.995 57.3881C388.918 58.5229 387.991 59.383 386.956 59.4213" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M389.258 61.7492L387.096 61.6025" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M386.517 63.7348C387.648 63.8659 388.501 64.9011 388.424 66.0359" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip43_532_2318)">
<path d="M56.4194 54.2297L57.3665 55.3146" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M59.6578 55.4702L60.7428 54.5232" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M56.7717 57.0387L56.8084 56.4983C56.8135 56.2786 56.863 56.0622 56.954 55.8622C57.045 55.6622 57.1755 55.4827 57.3378 55.3346C57.5001 55.1864 57.6907 55.0727 57.8981 55.0002C58.1056 54.9277 58.3255 54.8981 58.5448 54.9129C58.764 54.9278 58.9779 54.987 59.1737 55.0868C59.3694 55.1866 59.5429 55.3251 59.6837 55.4938C59.8244 55.6626 59.9295 55.8581 59.9926 56.0685C60.0558 56.279 60.0756 56.5001 60.0509 56.7184L60.0142 57.2588" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M57.9207 64.104C56.1373 63.983 54.7773 62.4248 54.8983 60.6414L55.0084 59.0201C55.0473 58.4468 55.3124 57.9124 55.7453 57.5345C56.1782 57.1567 56.7435 56.9662 57.3168 57.0052L59.4785 57.1519C60.0518 57.1908 60.5862 57.4559 60.9641 57.8888C61.342 58.3217 61.5324 58.887 61.4935 59.4604L61.3834 61.0816C61.2623 62.865 59.7041 64.2251 57.9207 64.104Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M57.9208 64.104L58.251 59.2402" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M55.3682 57.9587C54.3326 57.7798 53.5303 56.8024 53.6073 55.6675" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M54.935 60.1009L52.7733 59.9542" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M53.0202 64.3142C53.0973 63.1794 54.082 62.269 55.2206 62.292" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M63.3186 56.3268C63.2416 57.4617 62.3145 58.3217 61.2804 58.3601" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M63.5817 60.688L61.42 60.5412" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M60.8409 62.6736C61.9722 62.8046 62.8248 63.8398 62.7478 64.9747" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip44_532_2318)">
<path d="M81.3621 55.923L82.3091 57.0079" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M84.6005 57.1635L85.6854 56.2165" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M81.7143 58.732L81.751 58.1916C81.7561 57.9719 81.8056 57.7555 81.8966 57.5555C81.9876 57.3555 82.1182 57.176 82.2804 57.0279C82.4427 56.8797 82.6333 56.766 82.8408 56.6935C83.0482 56.621 83.2682 56.5914 83.4874 56.6062C83.7066 56.6211 83.9206 56.6803 84.1163 56.7801C84.312 56.8799 84.4855 57.0184 84.6263 57.1871C84.767 57.3559 84.8721 57.5514 84.9353 57.7618C84.9984 57.9723 85.0182 58.1934 84.9936 58.4117L84.9569 58.9521" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M82.8634 65.7973C81.08 65.6763 79.7199 64.1181 79.841 62.3347L79.951 60.7134C79.9899 60.1401 80.255 59.6057 80.6879 59.2278C81.1209 58.85 81.6862 58.6595 82.2595 58.6985L84.4212 58.8452C84.9945 58.8841 85.5288 59.1492 85.9067 59.5821C86.2846 60.015 86.475 60.5803 86.4361 61.1536L86.326 62.7749C86.205 64.5583 84.6468 65.9184 82.8634 65.7973Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M82.8634 65.7973L83.1936 60.9335" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M80.3109 59.652C79.2752 59.4731 78.4729 58.4957 78.5499 57.3608" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M79.8776 61.7942L77.7159 61.6475" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M77.9629 66.0075C78.0399 64.8727 79.0247 63.9623 80.1632 63.9853" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M88.2612 58.0201C88.1842 59.155 87.2571 60.015 86.223 60.0534" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M88.5244 62.3813L86.3627 62.2345" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M85.7836 64.3669C86.9148 64.4979 87.7675 65.5331 87.6904 66.668" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip45_532_2318)">
<path d="M106.305 57.6163L107.252 58.7013" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M109.543 58.8568L110.628 57.9098" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M106.657 60.4253L106.694 59.8849C106.699 59.6652 106.748 59.4489 106.839 59.2489C106.93 59.0489 107.061 58.8694 107.223 58.7212C107.385 58.5731 107.576 58.4593 107.783 58.3868C107.991 58.3144 108.211 58.2847 108.43 58.2996C108.649 58.3145 108.863 58.3736 109.059 58.4734C109.255 58.5733 109.428 58.7117 109.569 58.8805C109.71 59.0492 109.815 59.2447 109.878 59.4552C109.941 59.6656 109.961 59.8867 109.936 60.105L109.899 60.6455" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M107.806 67.4907C106.022 67.3696 104.662 65.8114 104.783 64.028L104.894 62.4067C104.932 61.8334 105.198 61.299 105.63 60.9212C106.063 60.5433 106.629 60.3529 107.202 60.3918L109.364 60.5385C109.937 60.5775 110.471 60.8425 110.849 61.2754C111.227 61.7084 111.418 62.2737 111.379 62.847L111.269 64.4682C111.147 66.2516 109.589 67.6117 107.806 67.4907Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M107.806 67.4907L108.136 62.6269" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M105.253 61.3453C104.218 61.1664 103.415 60.189 103.492 59.0541" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M104.82 63.4876L102.658 63.3408" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M102.905 67.7009C102.982 66.566 103.967 65.6556 105.106 65.6786" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M113.204 59.7134C113.127 60.8483 112.2 61.7083 111.166 61.7467" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M113.467 64.0746L111.305 63.9278" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M110.726 66.0602C111.857 66.1913 112.71 67.2264 112.633 68.3613" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip46_532_2318)">
<path d="M131.247 59.3096L132.194 60.3946" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M134.486 60.5501L135.571 59.6031" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M131.599 62.1186L131.636 61.5782C131.641 61.3585 131.691 61.1422 131.782 60.9422C131.873 60.7422 132.003 60.5627 132.166 60.4145C132.328 60.2663 132.518 60.1526 132.726 60.0801C132.933 60.0077 133.153 59.978 133.373 59.9929C133.592 60.0077 133.806 60.0669 134.001 60.1667C134.197 60.2666 134.371 60.405 134.511 60.5738C134.652 60.7425 134.757 60.938 134.82 61.1485C134.884 61.3589 134.903 61.58 134.879 61.7983L134.842 62.3388" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M132.748 69.1839C130.965 69.0629 129.605 67.5047 129.726 65.7213L129.836 64.1C129.875 63.5267 130.14 62.9923 130.573 62.6145C131.006 62.2366 131.571 62.0462 132.145 62.0851L134.306 62.2318C134.88 62.2708 135.414 62.5358 135.792 62.9687C136.17 63.4017 136.36 63.967 136.321 64.5403L136.211 66.1615C136.09 67.9449 134.532 69.305 132.748 69.1839Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M132.749 69.184L133.079 64.3202" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M130.196 63.0386C129.16 62.8597 128.358 61.8823 128.435 60.7474" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M129.763 65.1809L127.601 65.0341" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M127.848 69.3942C127.925 68.2593 128.91 67.3489 130.048 67.3719" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M138.146 61.4067C138.069 62.5416 137.142 63.4016 136.108 63.44" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M138.409 65.7679L136.248 65.6211" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M135.669 67.7535C136.8 67.8846 137.653 68.9197 137.576 70.0546" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip47_532_2318)">
<path d="M156.19 61.0029L157.137 62.0879" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M159.428 62.2434L160.513 61.2964" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M156.542 63.812L156.579 63.2715C156.584 63.0519 156.633 62.8355 156.724 62.6355C156.815 62.4355 156.946 62.256 157.108 62.1078C157.27 61.9597 157.461 61.8459 157.669 61.7735C157.876 61.701 158.096 61.6713 158.315 61.6862C158.534 61.7011 158.748 61.7602 158.944 61.8601C159.14 61.9599 159.313 62.0984 159.454 62.2671C159.595 62.4358 159.7 62.6313 159.763 62.8418C159.826 63.0523 159.846 63.2733 159.821 63.4917L159.785 64.0321" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M157.691 70.8773C155.908 70.7562 154.548 69.198 154.669 67.4146L154.779 65.7933C154.818 65.22 155.083 64.6857 155.516 64.3078C155.949 63.9299 156.514 63.7395 157.087 63.7784L159.249 63.9252C159.822 63.9641 160.357 64.2292 160.734 64.6621C161.112 65.095 161.303 65.6603 161.264 66.2336L161.154 67.8549C161.033 69.6383 159.475 70.9984 157.691 70.8773Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M157.691 70.8773L158.021 66.0135" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M155.139 64.7319C154.103 64.553 153.301 63.5756 153.378 62.4407" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M154.705 66.8742L152.544 66.7274" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M152.791 71.0875C152.868 69.9526 153.852 69.0422 154.991 69.0652" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M163.089 63.1C163.012 64.2349 162.085 65.0949 161.051 65.1333" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M163.352 67.4612L161.19 67.3145" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M160.611 69.4468C161.743 69.5779 162.595 70.613 162.518 71.7479" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip48_532_2318)">
<path d="M181.132 62.6962L182.079 63.7812" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M184.371 63.9367L185.456 62.9897" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M181.485 65.5053L181.521 64.9648C181.526 64.7452 181.576 64.5288 181.667 64.3288C181.758 64.1288 181.888 63.9493 182.051 63.8011C182.213 63.653 182.404 63.5392 182.611 63.4668C182.818 63.3943 183.038 63.3646 183.258 63.3795C183.477 63.3944 183.691 63.4535 183.887 63.5534C184.082 63.6532 184.256 63.7917 184.397 63.9604C184.537 64.1291 184.642 64.3246 184.706 64.5351C184.769 64.7456 184.788 64.9666 184.764 65.185L184.727 65.7254" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M182.634 72.5706C180.85 72.4495 179.49 70.8913 179.611 69.1079L179.721 67.4866C179.76 66.9133 180.025 66.379 180.458 66.0011C180.891 65.6232 181.456 65.4328 182.03 65.4717L184.191 65.6185C184.765 65.6574 185.299 65.9225 185.677 66.3554C186.055 66.7883 186.245 67.3536 186.206 67.9269L186.096 69.5482C185.975 71.3316 184.417 72.6916 182.634 72.5706Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M182.634 72.5706L182.964 67.7068" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M180.081 66.4252C179.045 66.2463 178.243 65.2689 178.32 64.134" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M179.648 68.5675L177.486 68.4207" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M177.733 72.7808C177.81 71.6459 178.795 70.7355 179.934 70.7585" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M188.032 64.7933C187.955 65.9282 187.028 66.7882 185.993 66.8266" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M188.295 69.1545L186.133 69.0078" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M185.554 71.1401C186.685 71.2712 187.538 72.3063 187.461 73.4412" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip49_532_2318)">
<path d="M206.075 64.3895L207.022 65.4745" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M209.313 65.63L210.398 64.683" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M206.427 67.1986L206.464 66.6581C206.469 66.4385 206.518 66.2221 206.609 66.0221C206.7 65.8221 206.831 65.6426 206.993 65.4944C207.156 65.3463 207.346 65.2325 207.554 65.1601C207.761 65.0876 207.981 65.0579 208.2 65.0728C208.419 65.0877 208.633 65.1468 208.829 65.2467C209.025 65.3465 209.198 65.485 209.339 65.6537C209.48 65.8224 209.585 66.0179 209.648 66.2284C209.711 66.4389 209.731 66.6599 209.706 66.8783L209.67 67.4187" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M207.576 74.2639C205.793 74.1428 204.433 72.5846 204.554 70.8012L204.664 69.1799C204.703 68.6066 204.968 68.0723 205.401 67.6944C205.834 67.3165 206.399 67.1261 206.972 67.165L209.134 67.3118C209.707 67.3507 210.242 67.6158 210.62 68.0487C210.997 68.4816 211.188 69.0469 211.149 69.6202L211.039 71.2415C210.918 73.0249 209.36 74.3849 207.576 74.2639Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M207.576 74.2639L207.906 69.4001" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M205.024 68.1185C203.988 67.9396 203.186 66.9622 203.263 65.8273" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M204.591 70.2608L202.429 70.114" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M202.676 74.4741C202.753 73.3392 203.738 72.4288 204.876 72.4518" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M212.974 66.4866C212.897 67.6215 211.97 68.4815 210.936 68.5199" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M213.237 70.8478L211.076 70.701" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M210.497 72.8334C211.628 72.9645 212.48 73.9996 212.403 75.1345" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip50_532_2318)">
<path d="M231.018 66.0829L231.965 67.1678" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M234.256 67.3234L235.341 66.3763" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M231.37 68.8919L231.406 68.3515C231.412 68.1318 231.461 67.9154 231.552 67.7154C231.643 67.5154 231.774 67.3359 231.936 67.1878C232.098 67.0396 232.289 66.9259 232.496 66.8534C232.704 66.7809 232.924 66.7512 233.143 66.7661C233.362 66.781 233.576 66.8401 233.772 66.94C233.967 67.0398 234.141 67.1783 234.282 67.347C234.422 67.5157 234.528 67.7112 234.591 67.9217C234.654 68.1322 234.674 68.3533 234.649 68.5716L234.612 69.112" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M232.519 75.9572C230.735 75.8361 229.375 74.2779 229.496 72.4945L229.607 70.8733C229.645 70.3 229.911 69.7656 230.343 69.3877C230.776 69.0098 231.342 68.8194 231.915 68.8583L234.077 69.0051C234.65 69.044 235.184 69.3091 235.562 69.742C235.94 70.1749 236.131 70.7402 236.092 71.3135L235.982 72.9348C235.86 74.7182 234.302 76.0783 232.519 75.9572Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M232.519 75.9572L232.849 71.0934" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M229.966 69.8119C228.931 69.633 228.128 68.6555 228.205 67.5207" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M229.533 71.9541L227.371 71.8074" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M227.618 76.1674C227.695 75.0325 228.68 74.1221 229.819 74.1452" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M237.917 68.18C237.84 69.3148 236.913 70.1749 235.879 70.2132" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M238.18 72.5411L236.018 72.3944" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M235.439 74.5267C236.57 74.6578 237.423 75.693 237.346 76.8278" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip51_532_2318)">
<path d="M255.96 67.7762L256.907 68.8611" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M259.199 69.0167L260.284 68.0696" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M256.312 70.5852L256.349 70.0448C256.354 69.8251 256.404 69.6087 256.495 69.4087C256.586 69.2087 256.716 69.0292 256.878 68.8811C257.041 68.7329 257.231 68.6192 257.439 68.5467C257.646 68.4742 257.866 68.4445 258.085 68.4594C258.305 68.4743 258.519 68.5334 258.714 68.6333C258.91 68.7331 259.084 68.8716 259.224 69.0403C259.365 69.209 259.47 69.4045 259.533 69.615C259.596 69.8255 259.616 70.0466 259.592 70.2649L259.555 70.8053" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M257.461 77.6505C255.678 77.5294 254.318 75.9712 254.439 74.1878L254.549 72.5666C254.588 71.9933 254.853 71.4589 255.286 71.081C255.719 70.7031 256.284 70.5127 256.858 70.5516L259.019 70.6984C259.593 70.7373 260.127 71.0024 260.505 71.4353C260.883 71.8682 261.073 72.4335 261.034 73.0068L260.924 74.6281C260.803 76.4115 259.245 77.7716 257.461 77.6505Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M257.461 77.6505L257.792 72.7867" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M254.909 71.5052C253.873 71.3263 253.071 70.3488 253.148 69.214" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M254.476 73.6474L252.314 73.5007" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M252.561 77.8607C252.638 76.7258 253.623 75.8154 254.761 75.8385" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M262.859 69.8733C262.782 71.0081 261.855 71.8682 260.821 71.9065" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M263.123 74.2344L260.961 74.0877" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M260.382 76.22C261.513 76.3511 262.366 77.3863 262.289 78.5211" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip52_532_2318)">
<path d="M280.903 69.4695L281.85 70.5545" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M284.141 70.71L285.226 69.763" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M281.255 72.2785L281.292 71.7381C281.297 71.5184 281.346 71.3021 281.437 71.102C281.528 70.902 281.659 70.7225 281.821 70.5744C281.983 70.4262 282.174 70.3125 282.381 70.24C282.589 70.1676 282.809 70.1379 283.028 70.1527C283.247 70.1676 283.461 70.2268 283.657 70.3266C283.853 70.4265 284.026 70.5649 284.167 70.7336C284.308 70.9024 284.413 71.0979 284.476 71.3083C284.539 71.5188 284.559 71.7399 284.534 71.9582L284.497 72.4986" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M282.404 79.3438C280.621 79.2228 279.261 77.6646 279.382 75.8812L279.492 74.2599C279.531 73.6866 279.796 73.1522 280.229 72.7743C280.662 72.3965 281.227 72.206 281.8 72.245L283.962 72.3917C284.535 72.4306 285.069 72.6957 285.447 73.1286C285.825 73.5615 286.016 74.1268 285.977 74.7002L285.867 76.3214C285.746 78.1048 284.187 79.4649 282.404 79.3438Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M282.404 79.3438L282.734 74.48" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M279.851 73.1985C278.816 73.0196 278.013 72.0422 278.091 70.9073" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M279.418 75.3408L277.257 75.194" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M277.504 79.5541C277.581 78.4192 278.565 77.5088 279.704 77.5318" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M287.802 71.5666C287.725 72.7015 286.798 73.5615 285.764 73.5999" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M288.065 75.9278L285.903 75.781" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M285.324 77.9134C286.455 78.0444 287.308 79.0796 287.231 80.2145" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip53_532_2318)">
<path d="M305.845 71.1628L306.792 72.2477" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M309.084 72.4033L310.169 71.4563" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M306.198 73.9718L306.234 73.4314C306.239 73.2117 306.289 72.9954 306.38 72.7953C306.471 72.5953 306.601 72.4158 306.764 72.2677C306.926 72.1195 307.117 72.0058 307.324 71.9333C307.531 71.8609 307.751 71.8312 307.971 71.846C308.19 71.8609 308.404 71.9201 308.6 72.0199C308.795 72.1198 308.969 72.2582 309.109 72.4269C309.25 72.5957 309.355 72.7912 309.418 73.0016C309.482 73.2121 309.501 73.4332 309.477 73.6515L309.44 74.1919" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M307.347 81.0371C305.563 80.9161 304.203 79.3579 304.324 77.5745L304.434 75.9532C304.473 75.3799 304.738 74.8455 305.171 74.4676C305.604 74.0898 306.169 73.8993 306.743 73.9383L308.904 74.085C309.478 74.1239 310.012 74.389 310.39 74.8219C310.768 75.2548 310.958 75.8201 310.919 76.3935L310.809 78.0147C310.688 79.7981 309.13 81.1582 307.347 81.0371Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M307.347 81.0371L307.677 76.1733" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M304.794 74.8918C303.758 74.7129 302.956 73.7355 303.033 72.6006" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M304.361 77.0341L302.199 76.8873" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M302.446 81.2474C302.523 80.1125 303.508 79.2021 304.647 79.2251" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M312.745 73.2599C312.668 74.3948 311.74 75.2548 310.706 75.2932" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M313.008 77.6211L310.846 77.4743" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M310.267 79.6067C311.398 79.7377 312.251 80.7729 312.174 81.9078" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip54_532_2318)">
<path d="M330.788 72.8561L331.735 73.941" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M334.026 74.0966L335.111 73.1496" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M331.14 75.6651L331.177 75.1247C331.182 74.905 331.231 74.6887 331.322 74.4886C331.413 74.2886 331.544 74.1091 331.706 73.961C331.868 73.8128 332.059 73.6991 332.267 73.6266C332.474 73.5541 332.694 73.5245 332.913 73.5393C333.132 73.5542 333.346 73.6134 333.542 73.7132C333.738 73.813 333.911 73.9515 334.052 74.1202C334.193 74.289 334.298 74.4845 334.361 74.6949C334.424 74.9054 334.444 75.1265 334.419 75.3448L334.383 75.8852" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M332.289 82.7304C330.506 82.6094 329.146 81.0512 329.267 79.2678L329.377 77.6465C329.416 77.0732 329.681 76.5388 330.114 76.1609C330.547 75.7831 331.112 75.5926 331.685 75.6316L333.847 75.7783C334.42 75.8172 334.955 76.0823 335.333 76.5152C335.71 76.9481 335.901 77.5134 335.862 78.0868L335.752 79.708C335.631 81.4914 334.073 82.8515 332.289 82.7304Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M332.289 82.7304L332.619 77.8666" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M329.737 76.5851C328.701 76.4062 327.899 75.4288 327.976 74.2939" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M329.303 78.7274L327.142 78.5806" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M327.389 82.9407C327.466 81.8058 328.451 80.8954 329.589 80.9184" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M337.687 74.9532C337.61 76.0881 336.683 76.9481 335.649 76.9865" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M337.95 79.3144L335.789 79.1676" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M335.209 81.3C336.341 81.431 337.193 82.4662 337.116 83.6011" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip55_532_2318)">
<path d="M355.73 74.5494L356.677 75.6344" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M358.969 75.7899L360.054 74.8429" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M356.083 77.3584L356.119 76.818C356.124 76.5983 356.174 76.382 356.265 76.182C356.356 75.982 356.487 75.8025 356.649 75.6543C356.811 75.5062 357.002 75.3924 357.209 75.3199C357.417 75.2475 357.637 75.2178 357.856 75.2327C358.075 75.2476 358.289 75.3067 358.485 75.4065C358.68 75.5064 358.854 75.6448 358.995 75.8136C359.135 75.9823 359.24 76.1778 359.304 76.3883C359.367 76.5987 359.387 76.8198 359.362 77.0382L359.325 77.5786" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M357.232 84.4238C355.448 84.3027 354.088 82.7445 354.209 80.9611L354.319 79.3398C354.358 78.7665 354.623 78.2321 355.056 77.8543C355.489 77.4764 356.055 77.286 356.628 77.3249L358.79 77.4716C359.363 77.5106 359.897 77.7756 360.275 78.2086C360.653 78.6415 360.843 79.2068 360.805 79.7801L360.694 81.4014C360.573 83.1847 359.015 84.5448 357.232 84.4238Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M357.232 84.4238L357.562 79.56" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M354.679 78.2784C353.644 78.0995 352.841 77.1221 352.918 75.9872" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M354.246 80.4207L352.084 80.2739" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M352.331 84.634C352.408 83.4991 353.393 82.5887 354.532 82.6117" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M362.63 76.6465C362.553 77.7814 361.626 78.6414 360.591 78.6798" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M362.893 81.0077L360.731 80.8609" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M360.152 82.9933C361.283 83.1244 362.136 84.1595 362.059 85.2944" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g opacity="0.6" clip-path="url(#clip56_532_2318)">
<path d="M380.673 76.2427L381.62 77.3277" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M383.911 77.4832L384.996 76.5362" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M381.025 79.0517L381.062 78.5113C381.067 78.2916 381.117 78.0753 381.208 77.8753C381.299 77.6753 381.429 77.4958 381.591 77.3476C381.754 77.1995 381.944 77.0857 382.152 77.0132C382.359 76.9408 382.579 76.9111 382.798 76.926C383.018 76.9409 383.232 77 383.427 77.0998C383.623 77.1997 383.796 77.3381 383.937 77.5069C384.078 77.6756 384.183 77.8711 384.246 78.0816C384.309 78.292 384.329 78.5131 384.305 78.7314L384.268 79.2719" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M382.174 86.1171C380.391 85.996 379.031 84.4378 379.152 82.6544L379.262 81.0331C379.301 80.4598 379.566 79.9254 379.999 79.5476C380.432 79.1697 380.997 78.9793 381.571 79.0182L383.732 79.1649C384.306 79.2039 384.84 79.4689 385.218 79.9019C385.596 80.3348 385.786 80.9001 385.747 81.4734L385.637 83.0946C385.516 84.878 383.958 86.2381 382.174 86.1171Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M382.174 86.1171L382.505 81.2533" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M379.622 79.9717C378.586 79.7928 377.784 78.8154 377.861 77.6805" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M379.189 82.114L377.027 81.9672" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M377.274 86.3273C377.351 85.1924 378.336 84.282 379.474 84.305" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M387.572 78.3398C387.495 79.4747 386.568 80.3347 385.534 80.3731" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M387.835 82.701L385.674 82.5542" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M385.095 84.6866C386.226 84.8177 387.079 85.8528 387.002 86.9877" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
</g>
<defs>
<clipPath id="clip0_532_2318">
<rect width="400" height="92" fill="white"/>
</clipPath>
<clipPath id="clip1_532_2318">
<rect width="13" height="13" fill="white" transform="translate(56.4365 -10) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip2_532_2318">
<rect width="13" height="13" fill="white" transform="translate(81.3792 -8.3067) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip3_532_2318">
<rect width="13" height="13" fill="white" transform="translate(106.322 -6.6134) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip4_532_2318">
<rect width="13" height="13" fill="white" transform="translate(131.264 -4.92004) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip5_532_2318">
<rect width="13" height="13" fill="white" transform="translate(156.207 -3.22675) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip6_532_2318">
<rect width="13" height="13" fill="white" transform="translate(181.149 -1.53345) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip7_532_2318">
<rect width="13" height="13" fill="white" transform="translate(206.092 0.159851) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip8_532_2318">
<rect width="13" height="13" fill="white" transform="translate(231.035 1.85315) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip9_532_2318">
<rect width="13" height="13" fill="white" transform="translate(255.977 3.54651) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip10_532_2318">
<rect width="13" height="13" fill="white" transform="translate(280.92 5.23981) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip11_532_2318">
<rect width="13" height="13" fill="white" transform="translate(305.862 6.93311) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip12_532_2318">
<rect width="13" height="13" fill="white" transform="translate(330.805 8.6264) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip13_532_2318">
<rect width="13" height="13" fill="white" transform="translate(355.748 10.3197) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip14_532_2318">
<rect width="13" height="13" fill="white" transform="translate(380.69 12.0131) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip15_532_2318">
<rect width="13" height="13" fill="white" transform="translate(55.0142 10.9518) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip16_532_2318">
<rect width="13" height="13" fill="white" transform="translate(79.9568 12.6451) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip17_532_2318">
<rect width="13" height="13" fill="white" transform="translate(104.899 14.3384) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip18_532_2318">
<rect width="13" height="13" fill="white" transform="translate(129.842 16.0317) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip19_532_2318">
<rect width="13" height="13" fill="white" transform="translate(154.785 17.725) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip20_532_2318">
<rect width="13" height="13" fill="white" transform="translate(179.727 19.4183) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip21_532_2318">
<rect width="13" height="13" fill="white" transform="translate(204.67 21.1116) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip22_532_2318">
<rect width="13" height="13" fill="white" transform="translate(229.612 22.8049) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip23_532_2318">
<rect width="13" height="13" fill="white" transform="translate(254.555 24.4983) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip24_532_2318">
<rect width="13" height="13" fill="white" transform="translate(279.497 26.1916) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip25_532_2318">
<rect width="13" height="13" fill="white" transform="translate(304.44 27.8849) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip26_532_2318">
<rect width="13" height="13" fill="white" transform="translate(329.383 29.5782) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip27_532_2318">
<rect width="13" height="13" fill="white" transform="translate(354.325 31.2715) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip28_532_2318">
<rect width="13" height="13" fill="white" transform="translate(379.268 32.9648) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip29_532_2318">
<rect width="13" height="13" fill="white" transform="translate(53.5918 31.9036) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip30_532_2318">
<rect width="13" height="13" fill="white" transform="translate(78.5344 33.5969) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip31_532_2318">
<rect width="13" height="13" fill="white" transform="translate(103.477 35.2902) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip32_532_2318">
<rect width="13" height="13" fill="white" transform="translate(128.42 36.9835) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip33_532_2318">
<rect width="13" height="13" fill="white" transform="translate(153.362 38.6768) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip34_532_2318">
<rect width="13" height="13" fill="white" transform="translate(178.305 40.3701) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip35_532_2318">
<rect width="13" height="13" fill="white" transform="translate(203.247 42.0634) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip36_532_2318">
<rect width="13" height="13" fill="white" transform="translate(228.19 43.7567) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip37_532_2318">
<rect width="13" height="13" fill="white" transform="translate(253.133 45.4501) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip38_532_2318">
<rect width="13" height="13" fill="white" transform="translate(278.075 47.1434) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip39_532_2318">
<rect width="13" height="13" fill="white" transform="translate(303.018 48.8367) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip40_532_2318">
<rect width="13" height="13" fill="white" transform="translate(327.96 50.53) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip41_532_2318">
<rect width="13" height="13" fill="white" transform="translate(352.903 52.2233) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip42_532_2318">
<rect width="13" height="13" fill="white" transform="translate(377.845 53.9166) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip43_532_2318">
<rect width="13" height="13" fill="white" transform="translate(52.1694 52.8553) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip44_532_2318">
<rect width="13" height="13" fill="white" transform="translate(77.1121 54.5486) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip45_532_2318">
<rect width="13" height="13" fill="white" transform="translate(102.055 56.2419) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip46_532_2318">
<rect width="13" height="13" fill="white" transform="translate(126.997 57.9352) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip47_532_2318">
<rect width="13" height="13" fill="white" transform="translate(151.94 59.6286) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip48_532_2318">
<rect width="13" height="13" fill="white" transform="translate(176.882 61.3219) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip49_532_2318">
<rect width="13" height="13" fill="white" transform="translate(201.825 63.0152) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip50_532_2318">
<rect width="13" height="13" fill="white" transform="translate(226.768 64.7085) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip51_532_2318">
<rect width="13" height="13" fill="white" transform="translate(251.71 66.4018) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip52_532_2318">
<rect width="13" height="13" fill="white" transform="translate(276.653 68.0951) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip53_532_2318">
<rect width="13" height="13" fill="white" transform="translate(301.595 69.7884) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip54_532_2318">
<rect width="13" height="13" fill="white" transform="translate(326.538 71.4817) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip55_532_2318">
<rect width="13" height="13" fill="white" transform="translate(351.48 73.175) rotate(3.88375)"/>
</clipPath>
<clipPath id="clip56_532_2318">
<rect width="13" height="13" fill="white" transform="translate(376.423 74.8683) rotate(3.88375)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 129 KiB

View File

@@ -10,8 +10,10 @@
"pagedown": "menu::SelectLast",
"ctrl-n": "menu::SelectNext",
"tab": "menu::SelectNext",
"down": "menu::SelectNext",
"ctrl-p": "menu::SelectPrevious",
"shift-tab": "menu::SelectPrevious",
"up": "menu::SelectPrevious",
"enter": "menu::Confirm",
"ctrl-enter": "menu::SecondaryConfirm",
"ctrl-escape": "menu::Cancel",
@@ -35,11 +37,12 @@
"ctrl-shift-f5": "debugger::Restart",
"f6": "debugger::Pause",
"f7": "debugger::StepOver",
"cmd-f11": "debugger::StepInto",
"ctrl-f11": "debugger::StepInto",
"shift-f11": "debugger::StepOut",
"f11": "zed::ToggleFullScreen",
"ctrl-alt-z": "edit_prediction::RateCompletions",
"ctrl-shift-i": "edit_prediction::ToggleMenu"
"ctrl-shift-i": "edit_prediction::ToggleMenu",
"ctrl-alt-l": "lsp_tool::ToggleMenu"
}
},
{
@@ -59,7 +62,6 @@
"tab": "editor::Tab",
"shift-tab": "editor::Backtab",
"ctrl-k": "editor::CutToEndOfLine",
// "ctrl-t": "editor::Transpose",
"ctrl-k ctrl-q": "editor::Rewrap",
"ctrl-k q": "editor::Rewrap",
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
@@ -100,27 +102,23 @@
"shift-down": "editor::SelectDown",
"shift-left": "editor::SelectLeft",
"shift-right": "editor::SelectRight",
"ctrl-shift-left": "editor::SelectToPreviousWordStart", // cursorWordLeftSelect
"ctrl-shift-right": "editor::SelectToNextWordEnd", // cursorWordRightSelect
"ctrl-shift-left": "editor::SelectToPreviousWordStart",
"ctrl-shift-right": "editor::SelectToNextWordEnd",
"ctrl-shift-home": "editor::SelectToBeginning",
"ctrl-shift-end": "editor::SelectToEnd",
"ctrl-a": "editor::SelectAll",
"ctrl-l": "editor::SelectLine",
"ctrl-shift-i": "editor::Format",
"alt-shift-o": "editor::OrganizeImports",
// "cmd-shift-left": ["editor::SelectToBeginningOfLine", {"stop_at_soft_wraps": true, "stop_at_indent": true }],
// "ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
// "cmd-shift-right": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
// "ctrl-shift-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
// "alt-v": ["editor::MovePageUp", { "center_cursor": true }],
"ctrl-alt-space": "editor::ShowCharacterPalette",
"ctrl-;": "editor::ToggleLineNumbers",
"ctrl-'": "editor::ToggleSelectedDiffHunks",
"ctrl-\"": "editor::ExpandAllDiffHunks",
"ctrl-i": "editor::ShowSignatureHelp",
"alt-g b": "editor::ToggleGitBlame",
"alt-g b": "git::Blame",
"alt-g m": "git::OpenModifiedFiles",
"menu": "editor::OpenContextMenu",
"shift-f10": "editor::OpenContextMenu",
"ctrl-shift-e": "editor::ToggleEditPrediction",
@@ -140,7 +138,6 @@
"find": "buffer_search::Deploy",
"ctrl-f": "buffer_search::Deploy",
"ctrl-h": "buffer_search::DeployReplace",
// "cmd-e": ["buffer_search::Deploy", { "focus": false }],
"ctrl->": "assistant::QuoteSelection",
"ctrl-<": "assistant::InsertIntoEditor",
"ctrl-alt-e": "editor::SelectEnclosingSymbol",
@@ -153,8 +150,7 @@
"context": "Editor && mode == full && edit_prediction",
"bindings": {
"alt-]": "editor::NextEditPrediction",
"alt-[": "editor::PreviousEditPrediction",
"alt-right": "editor::AcceptPartialEditPrediction"
"alt-[": "editor::PreviousEditPrediction"
}
},
{
@@ -219,7 +215,6 @@
"ctrl-enter": "assistant::Assist",
"ctrl-s": "workspace::Save",
"save": "workspace::Save",
"ctrl->": "assistant::QuoteSelection",
"ctrl-<": "assistant::InsertIntoEditor",
"shift-enter": "assistant::Split",
"ctrl-r": "assistant::CycleMessageRole",
@@ -245,11 +240,12 @@
"ctrl-shift-j": "agent::ToggleNavigationMenu",
"ctrl-shift-i": "agent::ToggleOptionsMenu",
"shift-alt-escape": "agent::ExpandMessageEditor",
"ctrl->": "assistant::QuoteSelection",
"ctrl-alt-e": "agent::RemoveAllContext",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-shift-enter": "agent::ContinueThread",
"alt-enter": "agent::ContinueWithBurnMode",
"ctrl-alt-b": "agent::ToggleBurnMode"
"super-ctrl-b": "agent::ToggleBurnMode",
"alt-enter": "agent::ContinueWithBurnMode"
}
},
{
@@ -268,8 +264,8 @@
{
"context": "AgentPanel && prompt_editor",
"bindings": {
"cmd-n": "agent::NewTextThread",
"cmd-alt-t": "agent::NewThread"
"ctrl-n": "agent::NewTextThread",
"ctrl-alt-t": "agent::NewThread"
}
},
{
@@ -278,7 +274,9 @@
"enter": "agent::Chat",
"ctrl-enter": "agent::ChatWithFollow",
"ctrl-i": "agent::ToggleProfileSelector",
"shift-ctrl-r": "agent::OpenAgentDiff"
"shift-ctrl-r": "agent::OpenAgentDiff",
"ctrl-shift-y": "agent::KeepAll",
"ctrl-shift-n": "agent::RejectAll"
}
},
{
@@ -493,13 +491,27 @@
"ctrl-k r": "editor::RevealInFileManager",
"ctrl-k p": "editor::CopyPath",
"ctrl-\\": "pane::SplitRight",
"ctrl-k v": "markdown::OpenPreviewToTheSide",
"ctrl-shift-v": "markdown::OpenPreview",
"ctrl-alt-shift-c": "editor::DisplayCursorNames",
"alt-.": "editor::GoToHunk",
"alt-,": "editor::GoToPreviousHunk"
}
},
{
"context": "Editor && extension == md",
"use_key_equivalents": true,
"bindings": {
"ctrl-k v": "markdown::OpenPreviewToTheSide",
"ctrl-shift-v": "markdown::OpenPreview"
}
},
{
"context": "Editor && extension == svg",
"use_key_equivalents": true,
"bindings": {
"ctrl-k v": "svg::OpenPreviewToTheSide",
"ctrl-shift-v": "svg::OpenPreview"
}
},
{
"context": "Editor && mode == full",
"bindings": {
@@ -510,14 +522,14 @@
{
"context": "Workspace",
"bindings": {
"alt-open": ["projects::OpenRecent", { "create_new_window": false }],
// Change the default action on `menu::Confirm` by setting the parameter
// "alt-ctrl-o": ["projects::OpenRecent", { "create_new_window": true }],
"alt-open": "projects::OpenRecent",
"alt-ctrl-o": "projects::OpenRecent",
"alt-shift-open": "projects::OpenRemote",
"alt-ctrl-shift-o": "projects::OpenRemote",
"alt-ctrl-o": ["projects::OpenRecent", { "create_new_window": false }],
"alt-shift-open": ["projects::OpenRemote", { "from_existing_connection": false, "create_new_window": false }],
// Change to open path modal for existing remote connection by setting the parameter
// "alt-ctrl-shift-o": "["projects::OpenRemote", { "from_existing_connection": true }]",
"alt-ctrl-shift-o": ["projects::OpenRemote", { "from_existing_connection": false, "create_new_window": false }],
"alt-ctrl-shift-b": "branches::OpenRecent",
"alt-shift-enter": "toast::RunAction",
"ctrl-~": "workspace::NewTerminal",
@@ -660,14 +672,16 @@
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"alt-l": "editor::AcceptEditPrediction",
"tab": "editor::AcceptEditPrediction"
"tab": "editor::AcceptEditPrediction",
"alt-right": "editor::AcceptPartialEditPrediction"
}
},
{
"context": "Editor && edit_prediction_conflict",
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"alt-l": "editor::AcceptEditPrediction"
"alt-l": "editor::AcceptEditPrediction",
"alt-right": "editor::AcceptPartialEditPrediction"
}
},
{
@@ -895,21 +909,28 @@
"right": "variable_list::ExpandSelectedEntry",
"enter": "variable_list::EditVariable",
"ctrl-c": "variable_list::CopyVariableValue",
"ctrl-alt-c": "variable_list::CopyVariableName"
"ctrl-alt-c": "variable_list::CopyVariableName",
"delete": "variable_list::RemoveWatch",
"backspace": "variable_list::RemoveWatch",
"alt-enter": "variable_list::AddWatch"
}
},
{
"context": "BreakpointList",
"bindings": {
"space": "debugger::ToggleEnableBreakpoint",
"backspace": "debugger::UnsetBreakpoint"
"backspace": "debugger::UnsetBreakpoint",
"left": "debugger::PreviousBreakpointProperty",
"right": "debugger::NextBreakpointProperty"
}
},
{
"context": "CollabPanel && not_editing",
"bindings": {
"ctrl-backspace": "collab_panel::Remove",
"space": "menu::Confirm"
"space": "menu::Confirm",
"ctrl-up": "collab_panel::MoveChannelUp",
"ctrl-down": "collab_panel::MoveChannelDown"
}
},
{
@@ -941,7 +962,7 @@
}
},
{
"context": "FileFinder",
"context": "FileFinder || (FileFinder > Picker > Editor)",
"bindings": {
"ctrl-shift-a": "file_finder::ToggleSplitMenu",
"ctrl-shift-i": "file_finder::ToggleFilterMenu"
@@ -1036,7 +1057,8 @@
"context": "DebugConsole > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "menu::Confirm"
"enter": "menu::Confirm",
"alt-enter": "console::WatchExpression"
}
},
{

View File

@@ -47,7 +47,8 @@
"fn-f": "zed::ToggleFullScreen",
"ctrl-cmd-f": "zed::ToggleFullScreen",
"ctrl-cmd-z": "edit_prediction::RateCompletions",
"ctrl-cmd-i": "edit_prediction::ToggleMenu"
"ctrl-cmd-i": "edit_prediction::ToggleMenu",
"ctrl-cmd-l": "lsp_tool::ToggleMenu"
}
},
{
@@ -138,7 +139,8 @@
"cmd-;": "editor::ToggleLineNumbers",
"cmd-'": "editor::ToggleSelectedDiffHunks",
"cmd-\"": "editor::ExpandAllDiffHunks",
"cmd-alt-g b": "editor::ToggleGitBlame",
"cmd-alt-g b": "git::Blame",
"cmd-alt-g m": "git::OpenModifiedFiles",
"cmd-i": "editor::ShowSignatureHelp",
"f9": "editor::ToggleBreakpoint",
"shift-f9": "editor::EditLogBreakpoint",
@@ -181,8 +183,7 @@
"use_key_equivalents": true,
"bindings": {
"alt-tab": "editor::NextEditPrediction",
"alt-shift-tab": "editor::PreviousEditPrediction",
"ctrl-cmd-right": "editor::AcceptPartialEditPrediction"
"alt-shift-tab": "editor::PreviousEditPrediction"
}
},
{
@@ -253,7 +254,6 @@
"bindings": {
"cmd-enter": "assistant::Assist",
"cmd-s": "workspace::Save",
"cmd->": "assistant::QuoteSelection",
"cmd-<": "assistant::InsertIntoEditor",
"shift-enter": "assistant::Split",
"ctrl-r": "assistant::CycleMessageRole",
@@ -280,11 +280,12 @@
"cmd-shift-j": "agent::ToggleNavigationMenu",
"cmd-shift-i": "agent::ToggleOptionsMenu",
"shift-alt-escape": "agent::ExpandMessageEditor",
"cmd->": "assistant::QuoteSelection",
"cmd-alt-e": "agent::RemoveAllContext",
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-ctrl-b": "agent::ToggleBurnMode",
"cmd-shift-enter": "agent::ContinueThread",
"alt-enter": "agent::ContinueWithBurnMode",
"cmd-alt-b": "agent::ToggleBurnMode"
"alt-enter": "agent::ContinueWithBurnMode"
}
},
{
@@ -315,7 +316,9 @@
"enter": "agent::Chat",
"cmd-enter": "agent::ChatWithFollow",
"cmd-i": "agent::ToggleProfileSelector",
"shift-ctrl-r": "agent::OpenAgentDiff"
"shift-ctrl-r": "agent::OpenAgentDiff",
"cmd-shift-y": "agent::KeepAll",
"cmd-shift-n": "agent::RejectAll"
}
},
{
@@ -542,11 +545,25 @@
"cmd-k r": "editor::RevealInFileManager",
"cmd-k p": "editor::CopyPath",
"cmd-\\": "pane::SplitRight",
"cmd-k v": "markdown::OpenPreviewToTheSide",
"cmd-shift-v": "markdown::OpenPreview",
"ctrl-cmd-c": "editor::DisplayCursorNames"
}
},
{
"context": "Editor && extension == md",
"use_key_equivalents": true,
"bindings": {
"cmd-k v": "markdown::OpenPreviewToTheSide",
"cmd-shift-v": "markdown::OpenPreview"
}
},
{
"context": "Editor && extension == svg",
"use_key_equivalents": true,
"bindings": {
"cmd-k v": "svg::OpenPreviewToTheSide",
"cmd-shift-v": "svg::OpenPreview"
}
},
{
"context": "Editor && mode == full",
"use_key_equivalents": true,
@@ -582,10 +599,10 @@
"bindings": {
// Change the default action on `menu::Confirm` by setting the parameter
// "alt-cmd-o": ["projects::OpenRecent", {"create_new_window": true }],
"alt-cmd-o": "projects::OpenRecent",
"ctrl-cmd-o": "projects::OpenRemote",
"ctrl-cmd-shift-o": ["projects::OpenRemote", { "from_existing_connection": true }],
"alt-cmd-b": "branches::OpenRecent",
"alt-cmd-o": ["projects::OpenRecent", { "create_new_window": false }],
"ctrl-cmd-o": ["projects::OpenRemote", { "from_existing_connection": false, "create_new_window": false }],
"ctrl-cmd-shift-o": ["projects::OpenRemote", { "from_existing_connection": true, "create_new_window": false }],
"cmd-ctrl-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal",
"cmd-s": "workspace::Save",
"cmd-k s": "workspace::SaveWithoutFormat",
@@ -603,6 +620,7 @@
"cmd-8": ["workspace::ActivatePane", 7],
"cmd-9": ["workspace::ActivatePane", 8],
"cmd-b": "workspace::ToggleLeftDock",
"cmd-alt-b": "workspace::ToggleRightDock",
"cmd-r": "workspace::ToggleRightDock",
"cmd-j": "workspace::ToggleBottomDock",
"alt-cmd-y": "workspace::CloseAllDocks",
@@ -717,14 +735,16 @@
"context": "Editor && edit_prediction",
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"tab": "editor::AcceptEditPrediction"
"tab": "editor::AcceptEditPrediction",
"ctrl-cmd-right": "editor::AcceptPartialEditPrediction"
}
},
{
"context": "Editor && edit_prediction_conflict",
"use_key_equivalents": true,
"bindings": {
"alt-tab": "editor::AcceptEditPrediction"
"alt-tab": "editor::AcceptEditPrediction",
"ctrl-cmd-right": "editor::AcceptPartialEditPrediction"
}
},
{
@@ -860,7 +880,10 @@
"right": "variable_list::ExpandSelectedEntry",
"enter": "variable_list::EditVariable",
"cmd-c": "variable_list::CopyVariableValue",
"cmd-alt-c": "variable_list::CopyVariableName"
"cmd-alt-c": "variable_list::CopyVariableName",
"delete": "variable_list::RemoveWatch",
"backspace": "variable_list::RemoveWatch",
"alt-enter": "variable_list::AddWatch"
}
},
{
@@ -957,7 +980,9 @@
"context": "BreakpointList",
"bindings": {
"space": "debugger::ToggleEnableBreakpoint",
"backspace": "debugger::UnsetBreakpoint"
"backspace": "debugger::UnsetBreakpoint",
"left": "debugger::PreviousBreakpointProperty",
"right": "debugger::NextBreakpointProperty"
}
},
{
@@ -965,7 +990,9 @@
"use_key_equivalents": true,
"bindings": {
"ctrl-backspace": "collab_panel::Remove",
"space": "menu::Confirm"
"space": "menu::Confirm",
"cmd-up": "collab_panel::MoveChannelUp",
"cmd-down": "collab_panel::MoveChannelDown"
}
},
{
@@ -1002,7 +1029,7 @@
}
},
{
"context": "FileFinder",
"context": "FileFinder || (FileFinder > Picker > Editor)",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-a": "file_finder::ToggleSplitMenu",
@@ -1129,7 +1156,8 @@
"context": "DebugConsole > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "menu::Confirm"
"enter": "menu::Confirm",
"alt-enter": "console::WatchExpression"
}
},
{

View File

@@ -13,9 +13,9 @@
}
},
{
"context": "Editor",
"context": "Editor && vim_mode == insert && !menu",
"bindings": {
// "j k": ["workspace::SendKeystrokes", "escape"]
// "j k": "vim::SwitchToNormalMode"
}
}
]

View File

@@ -9,6 +9,13 @@
},
{
"context": "Editor",
"bindings": {
"ctrl-k ctrl-u": "editor::ConvertToUpperCase", // editor:upper-case
"ctrl-k ctrl-l": "editor::ConvertToLowerCase" // editor:lower-case
}
},
{
"context": "Editor && mode == full",
"bindings": {
"ctrl-shift-l": "language_selector::Toggle", // grammar-selector:show
"ctrl-|": "pane::RevealInProjectPanel", // tree-view:reveal-active-file
@@ -19,25 +26,20 @@
"shift-f3": ["editor::SelectPrevious", { "replace_newest": true }], //find-and-replace:find-previous
"alt-shift-down": "editor::AddSelectionBelow", // editor:add-selection-below
"alt-shift-up": "editor::AddSelectionAbove", // editor:add-selection-above
"ctrl-k ctrl-u": "editor::ConvertToUpperCase", // editor:upper-case
"ctrl-k ctrl-l": "editor::ConvertToLowerCase", // editor:lower-case
"ctrl-j": "editor::JoinLines", // editor:join-lines
"ctrl-shift-d": "editor::DuplicateLineDown", // editor:duplicate-lines
"ctrl-up": "editor::MoveLineUp", // editor:move-line-up
"ctrl-down": "editor::MoveLineDown", // editor:move-line-down
"ctrl-\\": "workspace::ToggleLeftDock", // tree-view:toggle
"ctrl-shift-m": "markdown::OpenPreviewToTheSide" // markdown-preview:toggle
}
},
{
"context": "Editor && mode == full",
"bindings": {
"ctrl-shift-m": "markdown::OpenPreviewToTheSide", // markdown-preview:toggle
"ctrl-r": "outline::Toggle" // symbols-view:toggle-project-symbols
}
},
{
"context": "BufferSearchBar",
"bindings": {
"f3": ["editor::SelectNext", { "replace_newest": true }], // find-and-replace:find-next
"shift-f3": ["editor::SelectPrevious", { "replace_newest": true }], //find-and-replace:find-previous
"ctrl-f3": "search::SelectNextMatch", // find-and-replace:find-next-selected
"ctrl-shift-f3": "search::SelectPreviousMatch" // find-and-replace:find-previous-selected
}

View File

@@ -8,7 +8,6 @@
"ctrl-shift-i": "agent::ToggleFocus",
"ctrl-l": "agent::ToggleFocus",
"ctrl-shift-l": "agent::ToggleFocus",
"ctrl-alt-b": "agent::ToggleFocus",
"ctrl-shift-j": "agent::OpenConfiguration"
}
},
@@ -42,7 +41,6 @@
"ctrl-shift-i": "workspace::ToggleRightDock",
"ctrl-l": "workspace::ToggleRightDock",
"ctrl-shift-l": "workspace::ToggleRightDock",
"ctrl-alt-b": "workspace::ToggleRightDock",
"ctrl-w": "workspace::ToggleRightDock", // technically should close chat
"ctrl-.": "agent::ToggleProfileSelector",
"ctrl-/": "agent::ToggleModelSelector",

View File

@@ -59,7 +59,8 @@
"alt->": "editor::MoveToEnd", // end-of-buffer
"ctrl-l": "editor::ScrollCursorCenterTopBottom", // recenter-top-bottom
"ctrl-s": "buffer_search::Deploy", // isearch-forward
"alt-^": "editor::JoinLines" // join-line
"alt-^": "editor::JoinLines", // join-line
"alt-q": "editor::Rewrap" // fill-paragraph
}
},
{
@@ -90,6 +91,13 @@
"ctrl-g": "editor::Cancel"
}
},
{
"context": "Editor && (showing_code_actions || showing_completions)",
"bindings": {
"ctrl-p": "editor::ContextMenuPrevious",
"ctrl-n": "editor::ContextMenuNext"
}
},
{
"context": "Workspace",
"bindings": {

View File

@@ -38,7 +38,7 @@
"ctrl-shift-d": "editor::DuplicateSelection",
"alt-f3": "editor::SelectAllMatches", // find_all_under
// "ctrl-f3": "", // find_under (cancels any selections)
// "cmd-alt-shift-g": "" // find_under_prev (cancels any selections)
// "ctrl-alt-shift-g": "" // find_under_prev (cancels any selections)
"f9": "editor::SortLinesCaseSensitive",
"ctrl-f9": "editor::SortLinesCaseInsensitive",
"f12": "editor::GoToDefinition",

View File

@@ -9,6 +9,14 @@
},
{
"context": "Editor",
"bindings": {
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine",
"cmd-k cmd-u": "editor::ConvertToUpperCase",
"cmd-k cmd-l": "editor::ConvertToLowerCase"
}
},
{
"context": "Editor && mode == full",
"bindings": {
"ctrl-shift-l": "language_selector::Toggle",
"cmd-|": "pane::RevealInProjectPanel",
@@ -19,26 +27,20 @@
"cmd-shift-g": ["editor::SelectPrevious", { "replace_newest": true }],
"ctrl-shift-down": "editor::AddSelectionBelow",
"ctrl-shift-up": "editor::AddSelectionAbove",
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine",
"cmd-k cmd-u": "editor::ConvertToUpperCase",
"cmd-k cmd-l": "editor::ConvertToLowerCase",
"alt-enter": "editor::Newline",
"cmd-shift-d": "editor::DuplicateLineDown",
"ctrl-cmd-up": "editor::MoveLineUp",
"ctrl-cmd-down": "editor::MoveLineDown",
"cmd-\\": "workspace::ToggleLeftDock",
"ctrl-shift-m": "markdown::OpenPreviewToTheSide"
}
},
{
"context": "Editor && mode == full",
"bindings": {
"ctrl-shift-m": "markdown::OpenPreviewToTheSide",
"cmd-r": "outline::Toggle"
}
},
{
"context": "BufferSearchBar",
"bindings": {
"cmd-g": ["editor::SelectNext", { "replace_newest": true }],
"cmd-shift-g": ["editor::SelectPrevious", { "replace_newest": true }],
"cmd-f3": "search::SelectNextMatch",
"cmd-shift-f3": "search::SelectPreviousMatch"
}

View File

@@ -8,7 +8,6 @@
"cmd-shift-i": "agent::ToggleFocus",
"cmd-l": "agent::ToggleFocus",
"cmd-shift-l": "agent::ToggleFocus",
"cmd-alt-b": "agent::ToggleFocus",
"cmd-shift-j": "agent::OpenConfiguration"
}
},
@@ -28,7 +27,8 @@
"context": "InlineAssistEditor",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-backspace": "editor::Cancel"
"cmd-shift-backspace": "editor::Cancel",
"cmd-enter": "menu::Confirm"
// "alt-enter": // Quick Question
// "cmd-shift-enter": // Full File Context
// "cmd-shift-k": // Toggle input focus (editor <> inline assist)
@@ -42,7 +42,6 @@
"cmd-shift-i": "workspace::ToggleRightDock",
"cmd-l": "workspace::ToggleRightDock",
"cmd-shift-l": "workspace::ToggleRightDock",
"cmd-alt-b": "workspace::ToggleRightDock",
"cmd-w": "workspace::ToggleRightDock", // technically should close chat
"cmd-.": "agent::ToggleProfileSelector",
"cmd-/": "agent::ToggleModelSelector",

View File

@@ -59,7 +59,8 @@
"alt->": "editor::MoveToEnd", // end-of-buffer
"ctrl-l": "editor::ScrollCursorCenterTopBottom", // recenter-top-bottom
"ctrl-s": "buffer_search::Deploy", // isearch-forward
"alt-^": "editor::JoinLines" // join-line
"alt-^": "editor::JoinLines", // join-line
"alt-q": "editor::Rewrap" // fill-paragraph
}
},
{
@@ -90,6 +91,13 @@
"ctrl-g": "editor::Cancel"
}
},
{
"context": "Editor && (showing_code_actions || showing_completions)",
"bindings": {
"ctrl-p": "editor::ContextMenuPrevious",
"ctrl-n": "editor::ContextMenuNext"
}
},
{
"context": "Workspace",
"bindings": {

View File

@@ -56,6 +56,9 @@
"[ shift-b": ["pane::ActivateItem", 0],
"] space": "vim::InsertEmptyLineBelow",
"[ space": "vim::InsertEmptyLineAbove",
"[ e": "editor::MoveLineUp",
"] e": "editor::MoveLineDown",
// Word motions
"w": "vim::NextWordStart",
"e": "vim::NextWordEnd",
@@ -82,10 +85,10 @@
"[ {": ["vim::UnmatchedBackward", { "char": "{" }],
"] )": ["vim::UnmatchedForward", { "char": ")" }],
"[ (": ["vim::UnmatchedBackward", { "char": "(" }],
"f": ["vim::PushFindForward", { "before": false }],
"t": ["vim::PushFindForward", { "before": true }],
"shift-f": ["vim::PushFindBackward", { "after": false }],
"shift-t": ["vim::PushFindBackward", { "after": true }],
"f": ["vim::PushFindForward", { "before": false, "multiline": false }],
"t": ["vim::PushFindForward", { "before": true, "multiline": false }],
"shift-f": ["vim::PushFindBackward", { "after": false, "multiline": false }],
"shift-t": ["vim::PushFindBackward", { "after": true, "multiline": false }],
"m": "vim::PushMark",
"'": ["vim::PushJump", { "line": true }],
"`": ["vim::PushJump", { "line": false }],
@@ -184,6 +187,8 @@
"z f": "editor::FoldSelectedRanges",
"z shift-m": "editor::FoldAll",
"z shift-r": "editor::UnfoldAll",
"z l": "vim::ColumnRight",
"z h": "vim::ColumnLeft",
"shift-z shift-q": ["pane::CloseActiveItem", { "save_intent": "skip" }],
"shift-z shift-z": ["pane::CloseActiveItem", { "save_intent": "save_all" }],
// Count support
@@ -198,6 +203,8 @@
"9": ["vim::Number", 9],
"ctrl-w d": "editor::GoToDefinitionSplit",
"ctrl-w g d": "editor::GoToDefinitionSplit",
"ctrl-w ]": "editor::GoToDefinitionSplit",
"ctrl-w ctrl-]": "editor::GoToDefinitionSplit",
"ctrl-w shift-d": "editor::GoToTypeDefinitionSplit",
"ctrl-w g shift-d": "editor::GoToTypeDefinitionSplit",
"ctrl-w space": "editor::OpenExcerptsSplit",
@@ -361,6 +368,10 @@
"escape": "editor::Cancel",
"ctrl-[": "editor::Cancel",
":": "command_palette::Toggle",
"left": "vim::WrappingLeft",
"right": "vim::WrappingRight",
"h": "vim::WrappingLeft",
"l": "vim::WrappingRight",
"shift-d": "vim::DeleteToEndOfLine",
"shift-j": "vim::JoinLines",
"y": "editor::Copy",
@@ -378,6 +389,10 @@
"shift-p": ["vim::Paste", { "before": true }],
"u": "vim::Undo",
"ctrl-r": "vim::Redo",
"f": ["vim::PushFindForward", { "before": false, "multiline": true }],
"t": ["vim::PushFindForward", { "before": true, "multiline": true }],
"shift-f": ["vim::PushFindBackward", { "after": false, "multiline": true }],
"shift-t": ["vim::PushFindBackward", { "after": true, "multiline": true }],
"r": "vim::PushReplace",
"s": "vim::Substitute",
"shift-s": "vim::SubstituteLine",
@@ -393,6 +408,8 @@
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePreviousItem",
"insert": "vim::InsertBefore",
".": "vim::Repeat",
"alt-.": "vim::RepeatFind",
// tree-sitter related commands
"[ x": "editor::SelectLargerSyntaxNode",
"] x": "editor::SelectSmallerSyntaxNode",
@@ -419,6 +436,7 @@
"x": "editor::SelectLine",
"shift-x": "editor::SelectLine",
"%": "editor::SelectAll",
// Window mode
"space w h": "workspace::ActivatePaneLeft",
"space w l": "workspace::ActivatePaneRight",
@@ -448,7 +466,8 @@
"ctrl-c": "editor::ToggleComments",
"d": "vim::HelixDelete",
"c": "vim::Substitute",
"shift-c": "editor::AddSelectionBelow"
"shift-c": "editor::AddSelectionBelow",
"alt-shift-c": "editor::AddSelectionAbove"
}
},
{
@@ -709,7 +728,7 @@
}
},
{
"context": "GitPanel || ProjectPanel || CollabPanel || OutlinePanel || ChatPanel || VimControl || EmptyPane || SharedScreen || MarkdownPreview || KeyContextView || DebugPanel",
"context": "AgentPanel || GitPanel || ProjectPanel || CollabPanel || OutlinePanel || ChatPanel || VimControl || EmptyPane || SharedScreen || MarkdownPreview || KeyContextView || DebugPanel",
"bindings": {
// window related commands (ctrl-w X)
"ctrl-w": null,

View File

@@ -17,28 +17,27 @@ You are a highly skilled software engineer with extensive knowledge in many prog
4. Use only the tools that are currently available.
5. DO NOT use a tool that is not available just because it appears in the conversation. This means the user turned it off.
6. NEVER run commands that don't terminate on their own such as web servers (like `npm run start`, `npm run dev`, `python -m http.server`, etc) or file watchers.
7. Avoid HTML entity escaping - use plain characters instead.
## Searching and Reading
If you are unsure how to fulfill the user's request, gather more information with tool calls and/or clarifying questions.
{{! TODO: If there are files, we should mention it but otherwise omit that fact }}
{{#if has_tools}}
If appropriate, use tool calls to explore the current project, which contains the following root directories:
{{#each worktrees}}
- `{{root_name}}`
- `{{abs_path}}`
{{/each}}
- Bias towards not asking the user for help if you can find the answer yourself.
- When providing paths to tools, the path should always begin with a path that starts with a project root directory listed above.
- When providing paths to tools, the path should always start with the name of a project root directory listed above.
- Before you read or edit a file, you must first find the full path. DO NOT ever guess a file path!
{{# if (has_tool 'grep') }}
- When looking for symbols in the project, prefer the `grep` tool.
- As you learn about the structure of the project, use that information to scope `grep` searches to targeted subtrees of the project.
- The user might specify a partial file path. If you don't know the full path, use `find_path` (not `grep`) before you read the file.
{{/if}}
{{/if}}
{{else}}
You are being tasked with providing a response, but you have no ability to use tools or to read or write any aspect of the user's system (other than any context the user might have provided to you).

View File

@@ -73,9 +73,6 @@
"unnecessary_code_fade": 0.3,
// Active pane styling settings.
"active_pane_modifiers": {
// The factor to grow the active pane by. Defaults to 1.0
// which gives the same size as all other panes.
"magnification": 1.0,
// Inset border size of the active pane, in pixels.
"border_size": 0.0,
// Opacity of the inactive panes. 0 means transparent, 1 means opaque.
@@ -83,6 +80,7 @@
"inactive_opacity": 1.0
},
// Layout mode of the bottom dock. Defaults to "contained"
// choices: contained, full, left_aligned, right_aligned
"bottom_dock_layout": "contained",
// The direction that you want to split panes horizontally. Defaults to "up"
"pane_split_direction_horizontal": "up",
@@ -97,19 +95,22 @@
// workspace when the centered layout is used.
"right_padding": 0.2
},
// All settings related to the image viewer.
// Image viewer settings
"image_viewer": {
// The unit for image file sizes.
// By default we're setting it to binary.
// The second option is decimal.
// The unit for image file sizes: "binary" (KiB, MiB) or decimal (KB, MB)
"unit": "binary"
},
// The key to use for adding multiple cursors
// Currently "alt" or "cmd_or_ctrl" (also aliased as
// "cmd" and "ctrl") are supported.
// Determines the modifier to be used to add multiple cursors with the mouse. The open hover link mouse gestures will adapt such that it do not conflict with the multicursor modifier.
//
// 1. Maps to `Alt` on Linux and Windows and to `Option` on MacOS:
// "alt"
// 2. Maps `Control` on Linux and Windows and to `Command` on MacOS:
// "cmd_or_ctrl" (alias: "cmd", "ctrl")
"multi_cursor_modifier": "alt",
// Whether to enable vim modes and key bindings.
"vim_mode": false,
// Whether to enable helix mode and key bindings.
"helix_mode": false,
// Whether to show the informational hover box when moving the mouse
// over symbols in the editor.
"hover_popover_enabled": true,
@@ -117,7 +118,14 @@
"hover_popover_delay": 300,
// Whether to confirm before quitting Zed.
"confirm_quit": false,
// Whether to restore last closed project when fresh Zed instance is opened.
// Whether to restore last closed project when fresh Zed instance is opened
// May take 3 values:
// 1. All workspaces open during last session
// "restore_on_startup": "last_session"
// 2. The workspace opened
// "restore_on_startup": "last_workspace",
// 3. Do not restore previous workspaces
// "restore_on_startup": "none",
"restore_on_startup": "last_session",
// Whether to attempt to restore previous file's state when opening it again.
// The state is stored per pane.
@@ -130,7 +138,9 @@
"restore_on_file_reopen": true,
// Whether to automatically close files that have been deleted on disk.
"close_on_file_delete": false,
// Size of the drop target in the editor.
// Relative size of the drop target in the editor that will open dropped file as a split pane (0-0.5)
// E.g. 0.25 == If you drop onto the top/bottom quarter of the pane a new vertical split will be used
// If you drop onto the left/right quarter of the pane a new horizontal split will be used
"drop_target_size": 0.2,
// Whether the window should be closed when using 'close active item' on a window with no tabs.
// May take 3 values:
@@ -217,6 +227,8 @@
"show_signature_help_after_edits": false,
// Whether to show code action button at start of buffer line.
"inline_code_actions": true,
// Whether to allow drag and drop text selection in buffer.
"drag_and_drop_selection": true,
// What to do when go to definition yields no results.
//
// 1. Do nothing: `none`
@@ -305,6 +317,8 @@
// "all"
// 4. Draw whitespaces at boundaries only:
// "boundary"
// 5. Draw whitespaces only after non-whitespace characters:
// "trailing"
// For a whitespace to be on a boundary, any of the following conditions need to be met:
// - It is a tab
// - It is adjacent to an edge (start or end)
@@ -396,6 +410,13 @@
// 3. Never show the minimap:
// "never" (default)
"show": "never",
// Where to show the minimap in the editor.
// This setting can take two values:
// 1. Show the minimap on the focused editor only:
// "active_editor" (default)
// 2. Show the minimap on all open editors:
// "all_editors"
"display_in": "active_editor",
// When to show the minimap thumb.
// This setting can take two values:
// 1. Show the minimap thumb if the mouse is over the minimap:
@@ -422,7 +443,9 @@
// 1. `null` to inherit the editor `current_line_highlight` setting (default)
// 2. "line" or "all" to highlight the current line in the minimap.
// 3. "gutter" or "none" to not highlight the current line in the minimap.
"current_line_highlight": null
"current_line_highlight": null,
// Maximum number of columns to display in the minimap.
"max_width_columns": 80
},
// Enable middle-click paste on Linux.
"middle_click_paste": true,
@@ -443,7 +466,9 @@
// Whether to show breakpoints in the gutter.
"breakpoints": true,
// Whether to show fold buttons in the gutter.
"folds": true
"folds": true,
// Minimum number of characters to reserve space for in the gutter.
"min_line_number_digits": 4
},
"indent_guides": {
// Whether to show indent guides in the editor.
@@ -468,7 +493,7 @@
},
// Whether the editor will scroll beyond the last line.
"scroll_beyond_last_line": "one_page",
// The number of lines to keep above/below the cursor when scrolling.
// The number of lines to keep above/below the cursor when scrolling with the keyboard
"vertical_scroll_margin": 3,
// Whether to scroll when clicking near the edge of the visible text area.
"autoscroll_on_clicks": false,
@@ -536,6 +561,9 @@
"function": false
}
},
// Whether to resize all the panels in a dock when resizing the dock.
// Can be a combination of "left", "right" and "bottom".
"resize_all_panels_in_dock": ["left"],
"project_panel": {
// Whether to show the project panel button in the status bar
"button": true,
@@ -599,7 +627,9 @@
// 2. Never show indent guides:
// "never"
"show": "always"
}
},
// Whether to hide the root entry when only one folder is open in the window.
"hide_root": false
},
"outline_panel": {
// Whether to show the outline panel button in the status bar
@@ -679,23 +709,27 @@
"default_width": 360,
// Style of the git status indicator in the panel.
//
// Choices: label_color, icon
// Default: icon
"status_style": "icon",
// What branch name to use if init.defaultBranch
// is not set
// What branch name to use if `init.defaultBranch` is not set
//
// Default: main
"fallback_branch_name": "main",
// Whether to sort entries in the panel by path
// or by status (the default).
// Whether to sort entries in the panel by path or by status (the default).
//
// Default: false
"sort_by_path": false,
// Whether to collapse untracked files in the diff panel.
//
// Default: false
"collapse_untracked_diff": false,
"scrollbar": {
// When to show the scrollbar in the git panel.
//
// Choices: always, auto, never, system
// Default: inherits editor scrollbar settings
"show": null
// "show": null
}
},
"message_editor": {
@@ -771,7 +805,6 @@
"tools": {
"copy_path": true,
"create_directory": true,
"create_file": true,
"delete_path": true,
"diagnostics": true,
"edit_file": true,
@@ -974,8 +1007,7 @@
// Removes any lines containing only whitespace at the end of the file and
// ensures just one newline at the end.
"ensure_final_newline_on_save": true,
// Whether or not to perform a buffer format before saving
//
// Whether or not to perform a buffer format before saving: [on, off, prettier, language_server]
// Keep in mind, if the autosave with delay is enabled, format_on_save will be ignored
"format_on_save": "on",
// How to perform a buffer format. This setting can take 4 values:
@@ -1028,12 +1060,33 @@
// Automatically update Zed. This setting may be ignored on Linux if
// installed through a package manager.
"auto_update": true,
// How to render LSP `textDocument/documentColor` colors in the editor.
//
// Possible values:
//
// 1. Do not query and render document colors.
// "lsp_document_colors": "none",
// 2. Render document colors as inlay hints near the color text (default).
// "lsp_document_colors": "inlay",
// 3. Draw a border around the color text.
// "lsp_document_colors": "border",
// 4. Draw a background behind the color text..
// "lsp_document_colors": "background",
"lsp_document_colors": "inlay",
// Diagnostics configuration.
"diagnostics": {
// Whether to show the project diagnostics button in the status bar.
"button": true,
// Whether to show warnings or not by default.
"include_warnings": true,
// Settings for using LSP pull diagnostics mechanism in Zed.
"lsp_pull_diagnostics": {
// Whether to pull for diagnostics or not.
"enabled": true,
// Minimum time to wait before pulling diagnostics from the language server(s).
// 0 turns the debounce off.
"debounce_ms": 50
},
// Settings for inline diagnostics
"inline": {
// Whether to show diagnostics inline or not
@@ -1135,6 +1188,12 @@
// 2. Display predictions inline only when holding a modifier key (alt by default).
// "mode": "subtle"
"mode": "eager",
// Copilot-specific settings
// "copilot": {
// "enterprise_uri": "",
// "proxy": "",
// "proxy_no_verify": false
// },
// Whether edit predictions are enabled when editing text threads.
// This setting has no effect if globally disabled.
"enabled_in_text_threads": true
@@ -1300,6 +1359,8 @@
// the terminal will default to matching the buffer's font fallbacks.
// This will be merged with the platform's default font fallbacks
// "font_fallbacks": ["FiraCode Nerd Fonts"],
// The weight of the editor font in standard CSS units from 100 to 900.
// "font_weight": 400
// Sets the maximum number of lines in the terminal's scrollback buffer.
// Default: 10_000, maximum: 100_000 (all bigger values set will be treated as 100_000), 0 disables the scrolling.
// Existing terminals will not pick up this change until they are recreated.
@@ -1457,12 +1518,15 @@
"language_servers": ["erlang-ls", "!elp", "..."]
},
"Git Commit": {
"allow_rewrap": "anywhere"
"allow_rewrap": "anywhere",
"soft_wrap": "editor_width",
"preferred_line_length": 72
},
"Go": {
"code_actions_on_format": {
"source.organizeImports": true
}
},
"debuggers": ["Delve"]
},
"GraphQL": {
"prettier": {
@@ -1500,11 +1564,11 @@
}
},
"LaTeX": {
"format_on_save": "on",
"formatter": "language_server",
"language_servers": ["texlab", "..."],
"prettier": {
"allowed": false
"allowed": true,
"plugins": ["prettier-plugin-latex"]
}
},
"Markdown": {
@@ -1527,20 +1591,20 @@
"Plain Text": {
"allow_rewrap": "anywhere"
},
"Python": {
"debuggers": ["Debugpy"]
},
"Ruby": {
"language_servers": ["solargraph", "!ruby-lsp", "!rubocop", "..."]
"language_servers": ["solargraph", "!ruby-lsp", "!rubocop", "!sorbet", "!steep", "..."]
},
"Rust": {
"debuggers": ["CodeLLDB"]
},
"SCSS": {
"prettier": {
"allowed": true
}
},
"SQL": {
"prettier": {
"allowed": true,
"plugins": ["prettier-plugin-sql"]
}
},
"Starlark": {
"language_servers": ["starpls", "!buck2-lsp", "..."]
},
@@ -1605,6 +1669,9 @@
"version": "1",
"api_url": "https://api.openai.com/v1"
},
"open_router": {
"api_url": "https://openrouter.ai/api/v1"
},
"lmstudio": {
"api_url": "http://localhost:1234/api/v0"
},
@@ -1653,6 +1720,11 @@
// }
// }
},
// Common language server settings.
"global_lsp_settings": {
// Whether to show the LSP servers button in the status bar.
"button": true
},
// Jupyter settings
"jupyter": {
"enabled": true
@@ -1667,7 +1739,6 @@
"default_mode": "normal",
"toggle_relative_line_numbers": false,
"use_system_clipboard": "always",
"use_multiline_find": false,
"use_smartcase_find": false,
"highlight_on_yank_duration": 200,
"custom_digraphs": {},
@@ -1750,6 +1821,7 @@
"debugger": {
"stepping_granularity": "line",
"save_breakpoints": true,
"dock": "bottom",
"button": true
}
}

View File

@@ -1,4 +1,4 @@
// Static tasks configuration.
// Project tasks configuration. See https://zed.dev/docs/tasks for documentation.
//
// Example:
[

View File

@@ -261,6 +261,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#bfbdb6ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#d2a6ffff",
"font_style": null,
@@ -316,6 +321,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#d2a6ffff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#5ac1feff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#a9d94bff",
"font_style": null,
@@ -442,9 +457,9 @@
"terminal.foreground": "#5c6166ff",
"terminal.bright_foreground": "#5c6166ff",
"terminal.dim_foreground": "#fcfcfcff",
"terminal.ansi.black": "#fcfcfcff",
"terminal.ansi.bright_black": "#bcbec0ff",
"terminal.ansi.dim_black": "#5c6166ff",
"terminal.ansi.black": "#5c6166ff",
"terminal.ansi.bright_black": "#3b9ee5ff",
"terminal.ansi.dim_black": "#9c9fa2ff",
"terminal.ansi.red": "#ef7271ff",
"terminal.ansi.bright_red": "#febab6ff",
"terminal.ansi.dim_red": "#833538ff",
@@ -463,9 +478,9 @@
"terminal.ansi.cyan": "#4dbf99ff",
"terminal.ansi.bright_cyan": "#ace0cbff",
"terminal.ansi.dim_cyan": "#2a5f4aff",
"terminal.ansi.white": "#5c6166ff",
"terminal.ansi.bright_white": "#5c6166ff",
"terminal.ansi.dim_white": "#9c9fa2ff",
"terminal.ansi.white": "#fcfcfcff",
"terminal.ansi.bright_white": "#fcfcfcff",
"terminal.ansi.dim_white": "#bcbec0ff",
"link_text.hover": "#3b9ee5ff",
"conflict": "#f1ad49ff",
"conflict.background": "#ffeedaff",
@@ -632,6 +647,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#5c6166ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#a37accff",
"font_style": null,
@@ -687,6 +707,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#a37accff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#3b9ee5ff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#86b300ff",
"font_style": null,
@@ -1003,6 +1033,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#cccac2ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#dfbfffff",
"font_style": null,
@@ -1058,6 +1093,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#dfbfffff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#72cffeff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#d4fe7fff",
"font_style": null,

View File

@@ -270,6 +270,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#d3869bff",
"font_style": null,
@@ -325,6 +330,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#fabd2eff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#b8bb25ff",
"font_style": null,
@@ -655,6 +670,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#d3869bff",
"font_style": null,
@@ -710,6 +730,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#fabd2eff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#b8bb25ff",
"font_style": null,
@@ -1040,6 +1070,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#d3869bff",
"font_style": null,
@@ -1095,6 +1130,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#fabd2eff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#b8bb25ff",
"font_style": null,
@@ -1227,9 +1272,9 @@
"terminal.foreground": "#282828ff",
"terminal.bright_foreground": "#282828ff",
"terminal.dim_foreground": "#fbf1c7ff",
"terminal.ansi.black": "#fbf1c7ff",
"terminal.ansi.bright_black": "#b0a189ff",
"terminal.ansi.dim_black": "#282828ff",
"terminal.ansi.black": "#282828ff",
"terminal.ansi.bright_black": "#0b6678ff",
"terminal.ansi.dim_black": "#5f5650ff",
"terminal.ansi.red": "#9d0308ff",
"terminal.ansi.bright_red": "#db8b7aff",
"terminal.ansi.dim_red": "#4e1207ff",
@@ -1248,9 +1293,9 @@
"terminal.ansi.cyan": "#437b59ff",
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
"terminal.ansi.white": "#282828ff",
"terminal.ansi.bright_white": "#282828ff",
"terminal.ansi.dim_white": "#73675eff",
"terminal.ansi.white": "#fbf1c7ff",
"terminal.ansi.bright_white": "#fbf1c7ff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#0b6678ff",
"version_control.added": "#797410ff",
"version_control.modified": "#b57615ff",
@@ -1425,6 +1470,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#066578ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#8f3e71ff",
"font_style": null,
@@ -1480,6 +1530,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#b57613ff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#0b6678ff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#79740eff",
"font_style": null,
@@ -1612,9 +1672,9 @@
"terminal.foreground": "#282828ff",
"terminal.bright_foreground": "#282828ff",
"terminal.dim_foreground": "#f9f5d7ff",
"terminal.ansi.black": "#f9f5d7ff",
"terminal.ansi.bright_black": "#b0a189ff",
"terminal.ansi.dim_black": "#282828ff",
"terminal.ansi.black": "#282828ff",
"terminal.ansi.bright_black": "#73675eff",
"terminal.ansi.dim_black": "#f9f5d7ff",
"terminal.ansi.red": "#9d0308ff",
"terminal.ansi.bright_red": "#db8b7aff",
"terminal.ansi.dim_red": "#4e1207ff",
@@ -1633,9 +1693,9 @@
"terminal.ansi.cyan": "#437b59ff",
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
"terminal.ansi.white": "#282828ff",
"terminal.ansi.bright_white": "#282828ff",
"terminal.ansi.dim_white": "#73675eff",
"terminal.ansi.white": "#f9f5d7ff",
"terminal.ansi.bright_white": "#f9f5d7ff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#0b6678ff",
"version_control.added": "#797410ff",
"version_control.modified": "#b57615ff",
@@ -1810,6 +1870,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#066578ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#8f3e71ff",
"font_style": null,
@@ -1865,6 +1930,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#b57613ff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#0b6678ff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#79740eff",
"font_style": null,
@@ -1997,9 +2072,9 @@
"terminal.foreground": "#282828ff",
"terminal.bright_foreground": "#282828ff",
"terminal.dim_foreground": "#f2e5bcff",
"terminal.ansi.black": "#f2e5bcff",
"terminal.ansi.bright_black": "#b0a189ff",
"terminal.ansi.dim_black": "#282828ff",
"terminal.ansi.black": "#282828ff",
"terminal.ansi.bright_black": "#73675eff",
"terminal.ansi.dim_black": "#f2e5bcff",
"terminal.ansi.red": "#9d0308ff",
"terminal.ansi.bright_red": "#db8b7aff",
"terminal.ansi.dim_red": "#4e1207ff",
@@ -2018,9 +2093,9 @@
"terminal.ansi.cyan": "#437b59ff",
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
"terminal.ansi.white": "#282828ff",
"terminal.ansi.bright_white": "#282828ff",
"terminal.ansi.dim_white": "#73675eff",
"terminal.ansi.white": "#f2e5bcff",
"terminal.ansi.bright_white": "#f2e5bcff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#0b6678ff",
"version_control.added": "#797410ff",
"version_control.modified": "#b57615ff",
@@ -2195,6 +2270,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#066578ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#8f3e71ff",
"font_style": null,
@@ -2250,6 +2330,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#b57613ff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#0b6678ff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#79740eff",
"font_style": null,

View File

@@ -99,6 +99,8 @@
"version_control.added": "#27a657ff",
"version_control.modified": "#d3b020ff",
"version_control.deleted": "#e06c76ff",
"version_control.conflict_marker.ours": "#a1c1811a",
"version_control.conflict_marker.theirs": "#74ade81a",
"conflict": "#dec184ff",
"conflict.background": "#dec1841a",
"conflict.border": "#5d4c2fff",
@@ -264,6 +266,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#dce0e5ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#bf956aff",
"font_style": null,
@@ -319,6 +326,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#dfc184ff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#74ade8ff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#a1c181ff",
"font_style": null,
@@ -450,9 +467,9 @@
"terminal.foreground": "#242529ff",
"terminal.bright_foreground": "#242529ff",
"terminal.dim_foreground": "#fafafaff",
"terminal.ansi.black": "#fafafaff",
"terminal.ansi.bright_black": "#aaaaaaff",
"terminal.ansi.dim_black": "#242529ff",
"terminal.ansi.black": "#242529ff",
"terminal.ansi.bright_black": "#242529ff",
"terminal.ansi.dim_black": "#97979aff",
"terminal.ansi.red": "#d36151ff",
"terminal.ansi.bright_red": "#f0b0a4ff",
"terminal.ansi.dim_red": "#6f312aff",
@@ -471,9 +488,9 @@
"terminal.ansi.cyan": "#3a82b7ff",
"terminal.ansi.bright_cyan": "#a3bedaff",
"terminal.ansi.dim_cyan": "#254058ff",
"terminal.ansi.white": "#242529ff",
"terminal.ansi.bright_white": "#242529ff",
"terminal.ansi.dim_white": "#97979aff",
"terminal.ansi.white": "#fafafaff",
"terminal.ansi.bright_white": "#fafafaff",
"terminal.ansi.dim_white": "#aaaaaaff",
"link_text.hover": "#5c78e2ff",
"version_control.added": "#27a657ff",
"version_control.modified": "#d3b020ff",
@@ -584,7 +601,7 @@
"font_weight": null
},
"constant": {
"color": "#669f59ff",
"color": "#c18401ff",
"font_style": null,
"font_weight": null
},
@@ -643,6 +660,11 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#242529ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#ad6e25ff",
"font_style": null,
@@ -698,6 +720,16 @@
"font_style": null,
"font_weight": null
},
"selector": {
"color": "#669f59ff",
"font_style": null,
"font_weight": null
},
"selector.pseudo": {
"color": "#5c78e2ff",
"font_style": null,
"font_weight": null
},
"string": {
"color": "#649f57ff",
"font_style": null,

View File

@@ -21,6 +21,7 @@ futures.workspace = true
gpui.workspace = true
language.workspace = true
project.workspace = true
proto.workspace = true
smallvec.workspace = true
ui.workspace = true
util.workspace = true

View File

@@ -7,7 +7,10 @@ use gpui::{
InteractiveElement as _, ParentElement as _, Render, SharedString, StatefulInteractiveElement,
Styled, Transformation, Window, actions, percentage,
};
use language::{BinaryStatus, LanguageRegistry, LanguageServerId};
use language::{
BinaryStatus, LanguageRegistry, LanguageServerId, LanguageServerName,
LanguageServerStatusUpdate, ServerHealth,
};
use project::{
EnvironmentErrorMessage, LanguageServerProgress, LspStoreEvent, Project,
ProjectEnvironmentEvent,
@@ -16,6 +19,7 @@ use project::{
use smallvec::SmallVec;
use std::{
cmp::Reverse,
collections::HashSet,
fmt::Write,
path::Path,
sync::Arc,
@@ -30,9 +34,9 @@ const GIT_OPERATION_DELAY: Duration = Duration::from_millis(0);
actions!(activity_indicator, [ShowErrorMessage]);
pub enum Event {
ShowError {
server_name: SharedString,
error: String,
ShowStatus {
server_name: LanguageServerName,
status: SharedString,
},
}
@@ -45,8 +49,8 @@ pub struct ActivityIndicator {
#[derive(Debug)]
struct ServerStatus {
name: SharedString,
status: BinaryStatus,
name: LanguageServerName,
status: LanguageServerStatusUpdate,
}
struct PendingWork<'a> {
@@ -76,10 +80,13 @@ impl ActivityIndicator {
let this = cx.new(|cx| {
let mut status_events = languages.language_server_binary_statuses();
cx.spawn(async move |this, cx| {
while let Some((name, status)) = status_events.next().await {
while let Some((name, binary_status)) = status_events.next().await {
this.update(cx, |this: &mut ActivityIndicator, cx| {
this.statuses.retain(|s| s.name != name);
this.statuses.push(ServerStatus { name, status });
this.statuses.push(ServerStatus {
name,
status: LanguageServerStatusUpdate::Binary(binary_status),
});
cx.notify();
})?;
}
@@ -108,8 +115,76 @@ impl ActivityIndicator {
cx.subscribe(
&project.read(cx).lsp_store(),
|_, _, event, cx| match event {
LspStoreEvent::LanguageServerUpdate { .. } => cx.notify(),
|activity_indicator, _, event, cx| match event {
LspStoreEvent::LanguageServerUpdate { name, message, .. } => {
if let proto::update_language_server::Variant::StatusUpdate(status_update) =
message
{
let Some(name) = name.clone() else {
return;
};
let status = match &status_update.status {
Some(proto::status_update::Status::Binary(binary_status)) => {
if let Some(binary_status) =
proto::ServerBinaryStatus::from_i32(*binary_status)
{
let binary_status = match binary_status {
proto::ServerBinaryStatus::None => BinaryStatus::None,
proto::ServerBinaryStatus::CheckingForUpdate => {
BinaryStatus::CheckingForUpdate
}
proto::ServerBinaryStatus::Downloading => {
BinaryStatus::Downloading
}
proto::ServerBinaryStatus::Starting => {
BinaryStatus::Starting
}
proto::ServerBinaryStatus::Stopping => {
BinaryStatus::Stopping
}
proto::ServerBinaryStatus::Stopped => {
BinaryStatus::Stopped
}
proto::ServerBinaryStatus::Failed => {
let Some(error) = status_update.message.clone()
else {
return;
};
BinaryStatus::Failed { error }
}
};
LanguageServerStatusUpdate::Binary(binary_status)
} else {
return;
}
}
Some(proto::status_update::Status::Health(health_status)) => {
if let Some(health) =
proto::ServerHealth::from_i32(*health_status)
{
let health = match health {
proto::ServerHealth::Ok => ServerHealth::Ok,
proto::ServerHealth::Warning => ServerHealth::Warning,
proto::ServerHealth::Error => ServerHealth::Error,
};
LanguageServerStatusUpdate::Health(
health,
status_update.message.clone().map(SharedString::from),
)
} else {
return;
}
}
None => return,
};
activity_indicator.statuses.retain(|s| s.name != name);
activity_indicator
.statuses
.push(ServerStatus { name, status });
}
cx.notify()
}
_ => {}
},
)
@@ -145,19 +220,19 @@ impl ActivityIndicator {
});
cx.subscribe_in(&this, window, move |_, _, event, window, cx| match event {
Event::ShowError { server_name, error } => {
Event::ShowStatus {
server_name,
status,
} => {
let create_buffer = project.update(cx, |project, cx| project.create_buffer(cx));
let project = project.clone();
let error = error.clone();
let status = status.clone();
let server_name = server_name.clone();
cx.spawn_in(window, async move |workspace, cx| {
let buffer = create_buffer.await?;
buffer.update(cx, |buffer, cx| {
buffer.edit(
[(
0..0,
format!("Language server error: {}\n\n{}", server_name, error),
)],
[(0..0, format!("Language server {server_name}:\n\n{status}"))],
None,
cx,
);
@@ -166,7 +241,10 @@ impl ActivityIndicator {
workspace.update_in(cx, |workspace, window, cx| {
workspace.add_item_to_active_pane(
Box::new(cx.new(|cx| {
Editor::for_buffer(buffer, Some(project.clone()), window, cx)
let mut editor =
Editor::for_buffer(buffer, Some(project.clone()), window, cx);
editor.set_read_only(true);
editor
})),
None,
true,
@@ -185,19 +263,34 @@ impl ActivityIndicator {
}
fn show_error_message(&mut self, _: &ShowErrorMessage, _: &mut Window, cx: &mut Context<Self>) {
self.statuses.retain(|status| {
if let BinaryStatus::Failed { error } = &status.status {
cx.emit(Event::ShowError {
let mut status_message_shown = false;
self.statuses.retain(|status| match &status.status {
LanguageServerStatusUpdate::Binary(BinaryStatus::Failed { error })
if !status_message_shown =>
{
cx.emit(Event::ShowStatus {
server_name: status.name.clone(),
error: error.clone(),
status: SharedString::from(error),
});
status_message_shown = true;
false
} else {
true
}
LanguageServerStatusUpdate::Health(
ServerHealth::Error | ServerHealth::Warning,
status_string,
) if !status_message_shown => match status_string {
Some(error) => {
cx.emit(Event::ShowStatus {
server_name: status.name.clone(),
status: error.clone(),
});
status_message_shown = true;
false
}
None => false,
},
_ => true,
});
cx.notify();
}
fn dismiss_error_message(
@@ -206,9 +299,23 @@ impl ActivityIndicator {
_: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(updater) = &self.auto_updater {
updater.update(cx, |updater, cx| updater.dismiss_error(cx));
let error_dismissed = if let Some(updater) = &self.auto_updater {
updater.update(cx, |updater, cx| updater.dismiss_error(cx))
} else {
false
};
if error_dismissed {
return;
}
self.project.update(cx, |project, cx| {
if project.last_formatting_failure(cx).is_some() {
project.reset_last_formatting_failure(cx);
true
} else {
false
}
});
}
fn pending_language_server_work<'a>(
@@ -267,48 +374,52 @@ impl ActivityIndicator {
});
}
// Show any language server has pending activity.
let mut pending_work = self.pending_language_server_work(cx);
if let Some(PendingWork {
progress_token,
progress,
..
}) = pending_work.next()
{
let mut message = progress
.title
.as_deref()
.unwrap_or(progress_token)
.to_string();
let mut pending_work = self.pending_language_server_work(cx);
if let Some(PendingWork {
progress_token,
progress,
..
}) = pending_work.next()
{
let mut message = progress
.title
.as_deref()
.unwrap_or(progress_token)
.to_string();
if let Some(percentage) = progress.percentage {
write!(&mut message, " ({}%)", percentage).unwrap();
if let Some(percentage) = progress.percentage {
write!(&mut message, " ({}%)", percentage).unwrap();
}
if let Some(progress_message) = progress.message.as_ref() {
message.push_str(": ");
message.push_str(progress_message);
}
let additional_work_count = pending_work.count();
if additional_work_count > 0 {
write!(&mut message, " + {} more", additional_work_count).unwrap();
}
return Some(Content {
icon: Some(
Icon::new(IconName::ArrowCircle)
.size(IconSize::Small)
.with_animation(
"arrow-circle",
Animation::new(Duration::from_secs(2)).repeat(),
|icon, delta| {
icon.transform(Transformation::rotate(percentage(delta)))
},
)
.into_any_element(),
),
message,
on_click: Some(Arc::new(Self::toggle_language_server_work_context_menu)),
tooltip_message: None,
});
}
if let Some(progress_message) = progress.message.as_ref() {
message.push_str(": ");
message.push_str(progress_message);
}
let additional_work_count = pending_work.count();
if additional_work_count > 0 {
write!(&mut message, " + {} more", additional_work_count).unwrap();
}
return Some(Content {
icon: Some(
Icon::new(IconName::ArrowCircle)
.size(IconSize::Small)
.with_animation(
"arrow-circle",
Animation::new(Duration::from_secs(2)).repeat(),
|icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
)
.into_any_element(),
),
message,
on_click: Some(Arc::new(Self::toggle_language_server_work_context_menu)),
tooltip_message: None,
});
}
if let Some(session) = self
@@ -369,14 +480,44 @@ impl ActivityIndicator {
let mut downloading = SmallVec::<[_; 3]>::new();
let mut checking_for_update = SmallVec::<[_; 3]>::new();
let mut failed = SmallVec::<[_; 3]>::new();
let mut health_messages = SmallVec::<[_; 3]>::new();
let mut servers_to_clear_statuses = HashSet::<LanguageServerName>::default();
for status in &self.statuses {
match status.status {
BinaryStatus::CheckingForUpdate => checking_for_update.push(status.name.clone()),
BinaryStatus::Downloading => downloading.push(status.name.clone()),
BinaryStatus::Failed { .. } => failed.push(status.name.clone()),
BinaryStatus::None => {}
match &status.status {
LanguageServerStatusUpdate::Binary(
BinaryStatus::Starting | BinaryStatus::Stopping,
) => {}
LanguageServerStatusUpdate::Binary(BinaryStatus::Stopped) => {
servers_to_clear_statuses.insert(status.name.clone());
}
LanguageServerStatusUpdate::Binary(BinaryStatus::CheckingForUpdate) => {
checking_for_update.push(status.name.clone());
}
LanguageServerStatusUpdate::Binary(BinaryStatus::Downloading) => {
downloading.push(status.name.clone());
}
LanguageServerStatusUpdate::Binary(BinaryStatus::Failed { .. }) => {
failed.push(status.name.clone());
}
LanguageServerStatusUpdate::Binary(BinaryStatus::None) => {}
LanguageServerStatusUpdate::Health(health, server_status) => match server_status {
Some(server_status) => {
health_messages.push((status.name.clone(), *health, server_status.clone()));
}
None => {
servers_to_clear_statuses.insert(status.name.clone());
}
},
}
}
self.statuses
.retain(|status| !servers_to_clear_statuses.contains(&status.name));
health_messages.sort_by_key(|(_, health, _)| match health {
ServerHealth::Error => 2,
ServerHealth::Warning => 1,
ServerHealth::Ok => 0,
});
if !downloading.is_empty() {
return Some(Content {
@@ -457,7 +598,7 @@ impl ActivityIndicator {
}),
),
on_click: Some(Arc::new(|this, window, cx| {
this.show_error_message(&Default::default(), window, cx)
this.show_error_message(&ShowErrorMessage, window, cx)
})),
tooltip_message: None,
});
@@ -471,7 +612,7 @@ impl ActivityIndicator {
.size(IconSize::Small)
.into_any_element(),
),
message: format!("Formatting failed: {}. Click to see logs.", failure),
message: format!("Formatting failed: {failure}. Click to see logs."),
on_click: Some(Arc::new(|indicator, window, cx| {
indicator.project.update(cx, |project, cx| {
project.reset_last_formatting_failure(cx);
@@ -482,6 +623,56 @@ impl ActivityIndicator {
});
}
// Show any health messages for the language servers
if let Some((server_name, health, message)) = health_messages.pop() {
let health_str = match health {
ServerHealth::Ok => format!("({server_name}) "),
ServerHealth::Warning => format!("({server_name}) Warning: "),
ServerHealth::Error => format!("({server_name}) Error: "),
};
let single_line_message = message
.lines()
.filter_map(|line| {
let line = line.trim();
if line.is_empty() { None } else { Some(line) }
})
.collect::<Vec<_>>()
.join(" ");
let mut altered_message = single_line_message != message;
let truncated_message = truncate_and_trailoff(
&single_line_message,
MAX_MESSAGE_LEN.saturating_sub(health_str.len()),
);
altered_message |= truncated_message != single_line_message;
let final_message = format!("{health_str}{truncated_message}");
let tooltip_message = if altered_message {
Some(format!("{health_str}{message}"))
} else {
None
};
return Some(Content {
icon: Some(
Icon::new(IconName::Warning)
.size(IconSize::Small)
.into_any_element(),
),
message: final_message,
tooltip_message,
on_click: Some(Arc::new(move |activity_indicator, window, cx| {
if altered_message {
activity_indicator.show_error_message(&ShowErrorMessage, window, cx)
} else {
activity_indicator
.statuses
.retain(|status| status.name != server_name);
cx.notify();
}
})),
});
}
// Show any application auto-update info.
if let Some(updater) = &self.auto_updater {
return match &updater.read(cx).status() {

View File

@@ -21,92 +21,60 @@ test-support = [
[dependencies]
agent_settings.workspace = true
anyhow.workspace = true
assistant_context_editor.workspace = true
assistant_slash_command.workspace = true
assistant_slash_commands.workspace = true
assistant_context.workspace = true
assistant_tool.workspace = true
async-watch.workspace = true
audio.workspace = true
buffer_diff.workspace = true
chrono.workspace = true
client.workspace = true
collections.workspace = true
component.workspace = true
context_server.workspace = true
convert_case.workspace = true
db.workspace = true
editor.workspace = true
extension.workspace = true
feature_flags.workspace = true
file_icons.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
git.workspace = true
gpui.workspace = true
heed.workspace = true
html_to_markdown.workspace = true
icons.workspace = true
indoc.workspace = true
http_client.workspace = true
indexed_docs.workspace = true
inventory.workspace = true
itertools.workspace = true
jsonschema.workspace = true
language.workspace = true
language_model.workspace = true
log.workspace = true
lsp.workspace = true
markdown.workspace = true
menu.workspace = true
multi_buffer.workspace = true
notifications.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
postage.workspace = true
project.workspace = true
prompt_store.workspace = true
proto.workspace = true
ref-cast.workspace = true
release_channel.workspace = true
rope.workspace = true
rules_library.workspace = true
schemars.workspace = true
search.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
settings.workspace = true
smol.workspace = true
sqlez.workspace = true
streaming_diff.workspace = true
telemetry.workspace = true
telemetry_events.workspace = true
terminal.workspace = true
terminal_view.workspace = true
text.workspace = true
theme.workspace = true
thiserror.workspace = true
time.workspace = true
time_format.workspace = true
ui.workspace = true
ui_input.workspace = true
urlencoding.workspace = true
util.workspace = true
uuid.workspace = true
workspace-hack.workspace = true
workspace.workspace = true
zed_actions.workspace = true
zed_llm_client.workspace = true
zstd.workspace = true
[dev-dependencies]
buffer_diff = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
assistant_tools.workspace = true
assistant_tool = { workspace = true, "features" = ["test-support"] }
gpui = { workspace = true, "features" = ["test-support"] }
indoc.workspace = true
language = { workspace = true, "features" = ["test-support"] }
language_model = { workspace = true, "features" = ["test-support"] }
parking_lot.workspace = true
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }
rand.workspace = true

View File

@@ -1,269 +1,19 @@
mod active_thread;
mod agent_configuration;
mod agent_diff;
mod agent_model_selector;
mod agent_panel;
mod buffer_codegen;
mod context;
mod context_picker;
mod context_server_configuration;
mod context_server_tool;
mod context_store;
mod context_strip;
mod debug;
mod history_store;
mod inline_assistant;
mod inline_prompt_editor;
mod message_editor;
mod profile_selector;
mod slash_command_settings;
mod terminal_codegen;
mod terminal_inline_assistant;
mod thread;
mod thread_history;
mod thread_store;
mod tool_compatibility;
mod tool_use;
mod ui;
pub mod agent_profile;
pub mod context;
pub mod context_server_tool;
pub mod context_store;
pub mod history_store;
pub mod thread;
pub mod thread_store;
use std::sync::Arc;
use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
use assistant_slash_command::SlashCommandRegistry;
use client::Client;
use feature_flags::FeatureFlagAppExt as _;
use fs::Fs;
use gpui::{App, actions, impl_actions};
use language::LanguageRegistry;
use language_model::{LanguageModelId, LanguageModelProviderId, LanguageModelRegistry};
use prompt_store::PromptBuilder;
use schemars::JsonSchema;
use serde::Deserialize;
use settings::{Settings as _, SettingsStore};
use thread::ThreadId;
pub use crate::active_thread::ActiveThread;
use crate::agent_configuration::{AddContextServerModal, ManageProfilesModal};
pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate};
pub use crate::context::{ContextLoadResult, LoadedContext};
pub use crate::inline_assistant::InlineAssistant;
use crate::slash_command_settings::SlashCommandSettings;
pub use crate::thread::{Message, MessageSegment, Thread, ThreadEvent};
pub use crate::thread_store::{SerializedThread, TextThreadStore, ThreadStore};
pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
pub use context::{AgentContext, ContextId, ContextLoadResult};
pub use context_store::ContextStore;
pub use ui::preview::{all_agent_previews, get_agent_preview};
pub use thread::{
LastRestoreCheckpoint, Message, MessageCrease, MessageId, MessageSegment, ThreadError,
ThreadEvent, ThreadFeedback, ThreadId, ThreadSummary, TokenUsageRatio, ZedAgentThread,
};
pub use thread_store::{SerializedThread, TextThreadStore, ThreadStore};
actions!(
agent,
[
NewTextThread,
ToggleContextPicker,
ToggleNavigationMenu,
ToggleOptionsMenu,
DeleteRecentlyOpenThread,
ToggleProfileSelector,
RemoveAllContext,
ExpandMessageEditor,
OpenHistory,
AddContextServer,
RemoveSelectedThread,
Chat,
ChatWithFollow,
CycleNextInlineAssist,
CyclePreviousInlineAssist,
FocusUp,
FocusDown,
FocusLeft,
FocusRight,
RemoveFocusedContext,
AcceptSuggestedContext,
OpenActiveThreadAsMarkdown,
OpenAgentDiff,
Keep,
Reject,
RejectAll,
KeepAll,
Follow,
ResetTrialUpsell,
ResetTrialEndUpsell,
ContinueThread,
ContinueWithBurnMode,
ToggleBurnMode,
]
);
#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema)]
pub struct NewThread {
#[serde(default)]
from_thread_id: Option<ThreadId>,
}
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema)]
pub struct ManageProfiles {
#[serde(default)]
pub customize_tools: Option<AgentProfileId>,
}
impl ManageProfiles {
pub fn customize_tools(profile_id: AgentProfileId) -> Self {
Self {
customize_tools: Some(profile_id),
}
}
}
impl_actions!(agent, [NewThread, ManageProfiles]);
/// Initializes the `agent` crate.
pub fn init(
fs: Arc<dyn Fs>,
client: Arc<Client>,
prompt_builder: Arc<PromptBuilder>,
language_registry: Arc<LanguageRegistry>,
is_eval: bool,
cx: &mut App,
) {
AgentSettings::register(cx);
SlashCommandSettings::register(cx);
assistant_context_editor::init(client.clone(), cx);
rules_library::init(cx);
if !is_eval {
// Initializing the language model from the user settings messes with the eval, so we only initialize them when
// we're not running inside of the eval.
init_language_model_settings(cx);
}
assistant_slash_command::init(cx);
pub fn init(cx: &mut gpui::App) {
thread_store::init(cx);
agent_panel::init(cx);
context_server_configuration::init(language_registry, cx);
register_slash_commands(cx);
inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
terminal_inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
indexed_docs::init(cx);
cx.observe_new(AddContextServerModal::register).detach();
cx.observe_new(ManageProfilesModal::register).detach();
}
fn init_language_model_settings(cx: &mut App) {
update_active_language_model_from_settings(cx);
cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
.detach();
cx.subscribe(
&LanguageModelRegistry::global(cx),
|_, event: &language_model::Event, cx| match event {
language_model::Event::ProviderStateChanged
| language_model::Event::AddedProvider(_)
| language_model::Event::RemovedProvider(_) => {
update_active_language_model_from_settings(cx);
}
_ => {}
},
)
.detach();
}
fn update_active_language_model_from_settings(cx: &mut App) {
let settings = AgentSettings::get_global(cx);
fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel {
language_model::SelectedModel {
provider: LanguageModelProviderId::from(selection.provider.0.clone()),
model: LanguageModelId::from(selection.model.clone()),
}
}
let default = to_selected_model(&settings.default_model);
let inline_assistant = settings
.inline_assistant_model
.as_ref()
.map(to_selected_model);
let commit_message = settings
.commit_message_model
.as_ref()
.map(to_selected_model);
let thread_summary = settings
.thread_summary_model
.as_ref()
.map(to_selected_model);
let inline_alternatives = settings
.inline_alternatives
.iter()
.map(to_selected_model)
.collect::<Vec<_>>();
LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
registry.select_default_model(Some(&default), cx);
registry.select_inline_assistant_model(inline_assistant.as_ref(), cx);
registry.select_commit_message_model(commit_message.as_ref(), cx);
registry.select_thread_summary_model(thread_summary.as_ref(), cx);
registry.select_inline_alternative_models(inline_alternatives, cx);
});
}
fn register_slash_commands(cx: &mut App) {
let slash_command_registry = SlashCommandRegistry::global(cx);
slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
slash_command_registry
.register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
slash_command_registry
.register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
if is_enabled {
slash_command_registry.register_command(
assistant_slash_commands::StreamingExampleSlashCommand,
false,
);
}
}
})
.detach();
update_slash_commands_from_settings(cx);
cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
.detach();
}
fn update_slash_commands_from_settings(cx: &mut App) {
let slash_command_registry = SlashCommandRegistry::global(cx);
let settings = SlashCommandSettings::get_global(cx);
if settings.docs.enabled {
slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
} else {
slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
}
if settings.cargo_workspace.enabled {
slash_command_registry
.register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
} else {
slash_command_registry
.unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
}
}

View File

@@ -1,197 +0,0 @@
use context_server::ContextServerCommand;
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, WeakEntity, prelude::*};
use project::project_settings::{ContextServerConfiguration, ProjectSettings};
use serde_json::json;
use settings::update_settings_file;
use ui::{KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*};
use ui_input::SingleLineInput;
use workspace::{ModalView, Workspace};
use crate::AddContextServer;
pub struct AddContextServerModal {
workspace: WeakEntity<Workspace>,
name_editor: Entity<SingleLineInput>,
command_editor: Entity<SingleLineInput>,
}
impl AddContextServerModal {
pub fn register(
workspace: &mut Workspace,
_window: Option<&mut Window>,
_cx: &mut Context<Workspace>,
) {
workspace.register_action(|workspace, _: &AddContextServer, window, cx| {
let workspace_handle = cx.entity().downgrade();
workspace.toggle_modal(window, cx, |window, cx| {
Self::new(workspace_handle, window, cx)
})
});
}
pub fn new(
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let name_editor =
cx.new(|cx| SingleLineInput::new(window, cx, "my-custom-server").label("Name"));
let command_editor = cx.new(|cx| {
SingleLineInput::new(window, cx, "Command").label("Command to run the MCP server")
});
Self {
name_editor,
command_editor,
workspace,
}
}
fn confirm(&mut self, _: &menu::Confirm, cx: &mut Context<Self>) {
let name = self
.name_editor
.read(cx)
.editor()
.read(cx)
.text(cx)
.trim()
.to_string();
let command = self
.command_editor
.read(cx)
.editor()
.read(cx)
.text(cx)
.trim()
.to_string();
if name.is_empty() || command.is_empty() {
return;
}
let mut command_parts = command.split(' ').map(|part| part.trim().to_string());
let Some(path) = command_parts.next() else {
return;
};
let args = command_parts.collect::<Vec<_>>();
if let Some(workspace) = self.workspace.upgrade() {
workspace.update(cx, |workspace, cx| {
let fs = workspace.app_state().fs.clone();
update_settings_file::<ProjectSettings>(fs.clone(), cx, |settings, _| {
settings.context_servers.insert(
name.into(),
ContextServerConfiguration {
command: Some(ContextServerCommand {
path,
args,
env: None,
}),
settings: Some(json!({})),
},
);
});
});
}
cx.emit(DismissEvent);
}
fn cancel(&mut self, _: &menu::Cancel, cx: &mut Context<Self>) {
cx.emit(DismissEvent);
}
}
impl ModalView for AddContextServerModal {}
impl Focusable for AddContextServerModal {
fn focus_handle(&self, cx: &App) -> FocusHandle {
self.name_editor.focus_handle(cx).clone()
}
}
impl EventEmitter<DismissEvent> for AddContextServerModal {}
impl Render for AddContextServerModal {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let is_name_empty = self.name_editor.read(cx).is_empty(cx);
let is_command_empty = self.command_editor.read(cx).is_empty(cx);
let focus_handle = self.focus_handle(cx);
div()
.elevation_3(cx)
.w(rems(34.))
.key_context("AddContextServerModal")
.on_action(
cx.listener(|this, _: &menu::Cancel, _window, cx| this.cancel(&menu::Cancel, cx)),
)
.on_action(
cx.listener(|this, _: &menu::Confirm, _window, cx| {
this.confirm(&menu::Confirm, cx)
}),
)
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
this.focus_handle(cx).focus(window);
}))
.on_mouse_down_out(cx.listener(|_this, _, _, cx| cx.emit(DismissEvent)))
.child(
Modal::new("add-context-server", None)
.header(ModalHeader::new().headline("Add MCP Server"))
.section(
Section::new().child(
v_flex()
.gap_2()
.child(self.name_editor.clone())
.child(self.command_editor.clone()),
),
)
.footer(
ModalFooter::new().end_slot(
h_flex()
.gap_2()
.child(
Button::new("cancel", "Cancel")
.key_binding(
KeyBinding::for_action_in(
&menu::Cancel,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(cx.listener(|this, _event, _window, cx| {
this.cancel(&menu::Cancel, cx)
})),
)
.child(
Button::new("add-server", "Add Server")
.disabled(is_name_empty || is_command_empty)
.key_binding(
KeyBinding::for_action_in(
&menu::Confirm,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
)
.map(|button| {
if is_name_empty {
button.tooltip(Tooltip::text("Name is required"))
} else if is_command_empty {
button.tooltip(Tooltip::text("Command is required"))
} else {
button
}
})
.on_click(cx.listener(|this, _event, _window, cx| {
this.confirm(&menu::Confirm, cx)
})),
),
),
),
)
}
}

View File

@@ -1,553 +0,0 @@
use std::{
sync::{Arc, Mutex},
time::Duration,
};
use anyhow::Context as _;
use context_server::ContextServerId;
use editor::{Editor, EditorElement, EditorStyle};
use gpui::{
Animation, AnimationExt, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task,
TextStyle, TextStyleRefinement, Transformation, UnderlineStyle, WeakEntity, percentage,
};
use language::{Language, LanguageRegistry};
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use notifications::status_toast::{StatusToast, ToastIcon};
use project::{
context_server_store::{ContextServerStatus, ContextServerStore},
project_settings::{ContextServerConfiguration, ProjectSettings},
};
use settings::{Settings as _, update_settings_file};
use theme::ThemeSettings;
use ui::{KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*};
use util::ResultExt;
use workspace::{ModalView, Workspace};
pub(crate) struct ConfigureContextServerModal {
workspace: WeakEntity<Workspace>,
focus_handle: FocusHandle,
context_servers_to_setup: Vec<ContextServerSetup>,
context_server_store: Entity<ContextServerStore>,
}
enum Configuration {
NotAvailable,
Required(ConfigurationRequiredState),
}
struct ConfigurationRequiredState {
installation_instructions: Entity<markdown::Markdown>,
settings_validator: Option<jsonschema::Validator>,
settings_editor: Entity<Editor>,
last_error: Option<SharedString>,
waiting_for_context_server: bool,
}
struct ContextServerSetup {
id: ContextServerId,
repository_url: Option<SharedString>,
configuration: Configuration,
}
impl ConfigureContextServerModal {
pub fn new(
configurations: impl Iterator<Item = crate::context_server_configuration::Configuration>,
context_server_store: Entity<ContextServerStore>,
jsonc_language: Option<Arc<Language>>,
language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let context_servers_to_setup = configurations
.map(|config| match config {
crate::context_server_configuration::Configuration::NotAvailable(
context_server_id,
repository_url,
) => ContextServerSetup {
id: context_server_id,
repository_url,
configuration: Configuration::NotAvailable,
},
crate::context_server_configuration::Configuration::Required(
context_server_id,
repository_url,
config,
) => {
let jsonc_language = jsonc_language.clone();
let settings_validator = jsonschema::validator_for(&config.settings_schema)
.context("Failed to load JSON schema for context server settings")
.log_err();
let state = ConfigurationRequiredState {
installation_instructions: cx.new(|cx| {
Markdown::new(
config.installation_instructions.clone().into(),
Some(language_registry.clone()),
None,
cx,
)
}),
settings_validator,
settings_editor: cx.new(|cx| {
let mut editor = Editor::auto_height(16, window, cx);
editor.set_text(config.default_settings.trim(), window, cx);
editor.set_show_gutter(false, cx);
editor.set_soft_wrap_mode(
language::language_settings::SoftWrap::None,
cx,
);
if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
buffer.update(cx, |buffer, cx| {
buffer.set_language(jsonc_language, cx)
})
}
editor
}),
waiting_for_context_server: false,
last_error: None,
};
ContextServerSetup {
id: context_server_id,
repository_url,
configuration: Configuration::Required(state),
}
}
})
.collect::<Vec<_>>();
Self {
workspace,
focus_handle: cx.focus_handle(),
context_servers_to_setup,
context_server_store,
}
}
}
impl ConfigureContextServerModal {
pub fn confirm(&mut self, cx: &mut Context<Self>) {
if self.context_servers_to_setup.is_empty() {
self.dismiss(cx);
return;
}
let Some(workspace) = self.workspace.upgrade() else {
return;
};
let id = self.context_servers_to_setup[0].id.clone();
let configuration = match &mut self.context_servers_to_setup[0].configuration {
Configuration::NotAvailable => {
self.context_servers_to_setup.remove(0);
if self.context_servers_to_setup.is_empty() {
self.dismiss(cx);
}
return;
}
Configuration::Required(state) => state,
};
configuration.last_error.take();
if configuration.waiting_for_context_server {
return;
}
let settings_value = match serde_json_lenient::from_str::<serde_json::Value>(
&configuration.settings_editor.read(cx).text(cx),
) {
Ok(value) => value,
Err(error) => {
configuration.last_error = Some(error.to_string().into());
cx.notify();
return;
}
};
if let Some(validator) = configuration.settings_validator.as_ref() {
if let Err(error) = validator.validate(&settings_value) {
configuration.last_error = Some(error.to_string().into());
cx.notify();
return;
}
}
let id = id.clone();
let settings_changed = ProjectSettings::get_global(cx)
.context_servers
.get(&id.0)
.map_or(true, |config| {
config.settings.as_ref() != Some(&settings_value)
});
let is_running = self.context_server_store.read(cx).status_for_server(&id)
== Some(ContextServerStatus::Running);
if !settings_changed && is_running {
self.complete_setup(id, cx);
return;
}
configuration.waiting_for_context_server = true;
let task = wait_for_context_server(&self.context_server_store, id.clone(), cx);
cx.spawn({
let id = id.clone();
async move |this, cx| {
let result = task.await;
this.update(cx, |this, cx| match result {
Ok(_) => {
this.complete_setup(id, cx);
}
Err(err) => {
if let Some(setup) = this.context_servers_to_setup.get_mut(0) {
match &mut setup.configuration {
Configuration::NotAvailable => {}
Configuration::Required(state) => {
state.last_error = Some(err.into());
state.waiting_for_context_server = false;
}
}
} else {
this.dismiss(cx);
}
cx.notify();
}
})
}
})
.detach();
// When we write the settings to the file, the context server will be restarted.
update_settings_file::<ProjectSettings>(workspace.read(cx).app_state().fs.clone(), cx, {
let id = id.clone();
|settings, _| {
if let Some(server_config) = settings.context_servers.get_mut(&id.0) {
server_config.settings = Some(settings_value);
} else {
settings.context_servers.insert(
id.0,
ContextServerConfiguration {
settings: Some(settings_value),
..Default::default()
},
);
}
}
});
}
fn complete_setup(&mut self, id: ContextServerId, cx: &mut Context<Self>) {
self.context_servers_to_setup.remove(0);
cx.notify();
if !self.context_servers_to_setup.is_empty() {
return;
}
self.workspace
.update(cx, {
|workspace, cx| {
let status_toast = StatusToast::new(
format!("{} configured successfully.", id),
cx,
|this, _cx| {
this.icon(ToastIcon::new(IconName::Hammer).color(Color::Muted))
.action("Dismiss", |_, _| {})
},
);
workspace.toggle_status_toast(status_toast, cx);
}
})
.log_err();
self.dismiss(cx);
}
fn dismiss(&self, cx: &mut Context<Self>) {
cx.emit(DismissEvent);
}
}
fn wait_for_context_server(
context_server_store: &Entity<ContextServerStore>,
context_server_id: ContextServerId,
cx: &mut App,
) -> Task<Result<(), Arc<str>>> {
let (tx, rx) = futures::channel::oneshot::channel();
let tx = Arc::new(Mutex::new(Some(tx)));
let subscription = cx.subscribe(context_server_store, move |_, event, _cx| match event {
project::context_server_store::Event::ServerStatusChanged { server_id, status } => {
match status {
ContextServerStatus::Running => {
if server_id == &context_server_id {
if let Some(tx) = tx.lock().unwrap().take() {
let _ = tx.send(Ok(()));
}
}
}
ContextServerStatus::Stopped => {
if server_id == &context_server_id {
if let Some(tx) = tx.lock().unwrap().take() {
let _ = tx.send(Err("Context server stopped running".into()));
}
}
}
ContextServerStatus::Error(error) => {
if server_id == &context_server_id {
if let Some(tx) = tx.lock().unwrap().take() {
let _ = tx.send(Err(error.clone()));
}
}
}
_ => {}
}
}
});
cx.spawn(async move |_cx| {
let result = rx.await.unwrap();
drop(subscription);
result
})
}
impl Render for ConfigureContextServerModal {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let Some(setup) = self.context_servers_to_setup.first() else {
return div().into_any_element();
};
let focus_handle = self.focus_handle(cx);
div()
.elevation_3(cx)
.w(rems(42.))
.key_context("ConfigureContextServerModal")
.track_focus(&focus_handle)
.on_action(cx.listener(|this, _: &menu::Confirm, _window, cx| this.confirm(cx)))
.on_action(cx.listener(|this, _: &menu::Cancel, _window, cx| this.dismiss(cx)))
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
this.focus_handle(cx).focus(window);
}))
.child(
Modal::new("configure-context-server", None)
.header(ModalHeader::new().headline(format!("Configure {}", setup.id)))
.section(match &setup.configuration {
Configuration::NotAvailable => Section::new().child(
Label::new(
"No configuration options available for this context server. Visit the Repository for any further instructions.",
)
.color(Color::Muted),
),
Configuration::Required(configuration) => Section::new()
.child(div().pb_2().text_sm().child(MarkdownElement::new(
configuration.installation_instructions.clone(),
default_markdown_style(window, cx),
)))
.child(
div()
.p_2()
.rounded_md()
.border_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().editor_background)
.gap_1()
.child({
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: cx.theme().colors().text,
font_family: settings.buffer_font.family.clone(),
font_fallbacks: settings.buffer_font.fallbacks.clone(),
font_size: settings.buffer_font_size(cx).into(),
font_weight: settings.buffer_font.weight,
line_height: relative(
settings.buffer_line_height.value(),
),
..Default::default()
};
EditorElement::new(
&configuration.settings_editor,
EditorStyle {
background: cx.theme().colors().editor_background,
local_player: cx.theme().players().local(),
text: text_style,
syntax: cx.theme().syntax().clone(),
..Default::default()
},
)
})
.when_some(configuration.last_error.clone(), |this, error| {
this.child(
h_flex()
.gap_2()
.px_2()
.py_1()
.child(
Icon::new(IconName::Warning)
.size(IconSize::XSmall)
.color(Color::Warning),
)
.child(
div().w_full().child(
Label::new(error)
.size(LabelSize::Small)
.color(Color::Muted),
),
),
)
}),
)
.when(configuration.waiting_for_context_server, |this| {
this.child(
h_flex()
.gap_1p5()
.child(
Icon::new(IconName::ArrowCircle)
.size(IconSize::XSmall)
.color(Color::Info)
.with_animation(
"arrow-circle",
Animation::new(Duration::from_secs(2)).repeat(),
|icon, delta| {
icon.transform(Transformation::rotate(
percentage(delta),
))
},
)
.into_any_element(),
)
.child(
Label::new("Waiting for Context Server")
.size(LabelSize::Small)
.color(Color::Muted),
),
)
}),
})
.footer(
ModalFooter::new()
.when_some(setup.repository_url.clone(), |this, repository_url| {
this.start_slot(
h_flex().w_full().child(
Button::new("open-repository", "Open Repository")
.icon(IconName::ArrowUpRight)
.icon_color(Color::Muted)
.icon_size(IconSize::XSmall)
.tooltip({
let repository_url = repository_url.clone();
move |window, cx| {
Tooltip::with_meta(
"Open Repository",
None,
repository_url.clone(),
window,
cx,
)
}
})
.on_click(move |_, _, cx| cx.open_url(&repository_url)),
),
)
})
.end_slot(match &setup.configuration {
Configuration::NotAvailable => Button::new("dismiss", "Dismiss")
.key_binding(
KeyBinding::for_action_in(
&menu::Cancel,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(
cx.listener(|this, _event, _window, cx| this.dismiss(cx)),
)
.into_any_element(),
Configuration::Required(state) => h_flex()
.gap_2()
.child(
Button::new("cancel", "Cancel")
.key_binding(
KeyBinding::for_action_in(
&menu::Cancel,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(cx.listener(|this, _event, _window, cx| {
this.dismiss(cx)
})),
)
.child(
Button::new("configure-server", "Configure MCP")
.disabled(state.waiting_for_context_server)
.key_binding(
KeyBinding::for_action_in(
&menu::Confirm,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(cx.listener(|this, _event, _window, cx| {
this.confirm(cx)
})),
)
.into_any_element(),
}),
),
).into_any_element()
}
}
pub(crate) fn default_markdown_style(window: &Window, cx: &App) -> MarkdownStyle {
let theme_settings = ThemeSettings::get_global(cx);
let colors = cx.theme().colors();
let mut text_style = window.text_style();
text_style.refine(&TextStyleRefinement {
font_family: Some(theme_settings.ui_font.family.clone()),
font_fallbacks: theme_settings.ui_font.fallbacks.clone(),
font_features: Some(theme_settings.ui_font.features.clone()),
font_size: Some(TextSize::XSmall.rems(cx).into()),
color: Some(colors.text_muted),
..Default::default()
});
MarkdownStyle {
base_text_style: text_style.clone(),
selection_background_color: cx.theme().players().local().selection,
link: TextStyleRefinement {
background_color: Some(colors.editor_foreground.opacity(0.025)),
underline: Some(UnderlineStyle {
color: Some(colors.text_accent.opacity(0.5)),
thickness: px(1.),
..Default::default()
}),
..Default::default()
},
..Default::default()
}
}
impl ModalView for ConfigureContextServerModal {}
impl EventEmitter<DismissEvent> for ConfigureContextServerModal {}
impl Focusable for ConfigureContextServerModal {
fn focus_handle(&self, cx: &App) -> FocusHandle {
if let Some(current) = self.context_servers_to_setup.first() {
match &current.configuration {
Configuration::NotAvailable => self.focus_handle.clone(),
Configuration::Required(configuration) => {
configuration.settings_editor.read(cx).focus_handle(cx)
}
}
} else {
self.focus_handle.clone()
}
}
}

View File

@@ -0,0 +1,341 @@
use std::sync::Arc;
use agent_settings::{AgentProfileId, AgentProfileSettings, AgentSettings};
use assistant_tool::{Tool, ToolSource, ToolWorkingSet};
use collections::IndexMap;
use convert_case::{Case, Casing};
use fs::Fs;
use gpui::{App, Entity, SharedString};
use settings::{Settings, update_settings_file};
use util::ResultExt;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AgentProfile {
id: AgentProfileId,
tool_set: Entity<ToolWorkingSet>,
}
pub type AvailableProfiles = IndexMap<AgentProfileId, SharedString>;
impl AgentProfile {
pub fn new(id: AgentProfileId, tool_set: Entity<ToolWorkingSet>) -> Self {
Self { id, tool_set }
}
/// Saves a new profile to the settings.
pub fn create(
name: String,
base_profile_id: Option<AgentProfileId>,
fs: Arc<dyn Fs>,
cx: &App,
) -> AgentProfileId {
let id = AgentProfileId(name.to_case(Case::Kebab).into());
let base_profile =
base_profile_id.and_then(|id| AgentSettings::get_global(cx).profiles.get(&id).cloned());
let profile_settings = AgentProfileSettings {
name: name.into(),
tools: base_profile
.as_ref()
.map(|profile| profile.tools.clone())
.unwrap_or_default(),
enable_all_context_servers: base_profile
.as_ref()
.map(|profile| profile.enable_all_context_servers)
.unwrap_or_default(),
context_servers: base_profile
.map(|profile| profile.context_servers)
.unwrap_or_default(),
};
update_settings_file::<AgentSettings>(fs, cx, {
let id = id.clone();
move |settings, _cx| {
settings.create_profile(id, profile_settings).log_err();
}
});
id
}
/// Returns a map of AgentProfileIds to their names
pub fn available_profiles(cx: &App) -> AvailableProfiles {
let mut profiles = AvailableProfiles::default();
for (id, profile) in AgentSettings::get_global(cx).profiles.iter() {
profiles.insert(id.clone(), profile.name.clone());
}
profiles
}
pub fn id(&self) -> &AgentProfileId {
&self.id
}
pub fn enabled_tools(&self, cx: &App) -> Vec<Arc<dyn Tool>> {
let Some(settings) = AgentSettings::get_global(cx).profiles.get(&self.id) else {
return Vec::new();
};
self.tool_set
.read(cx)
.tools(cx)
.into_iter()
.filter(|tool| Self::is_enabled(settings, tool.source(), tool.name()))
.collect()
}
pub fn is_tool_enabled(&self, source: ToolSource, tool_name: String, cx: &App) -> bool {
let Some(settings) = AgentSettings::get_global(cx).profiles.get(&self.id) else {
return false;
};
return Self::is_enabled(settings, source, tool_name);
}
fn is_enabled(settings: &AgentProfileSettings, source: ToolSource, name: String) -> bool {
match source {
ToolSource::Native => *settings.tools.get(name.as_str()).unwrap_or(&false),
ToolSource::ContextServer { id } => {
if settings.enable_all_context_servers {
return true;
}
let Some(preset) = settings.context_servers.get(id.as_ref()) else {
return false;
};
*preset.tools.get(name.as_str()).unwrap_or(&false)
}
}
}
}
#[cfg(test)]
mod tests {
use agent_settings::ContextServerPreset;
use assistant_tool::ToolRegistry;
use collections::IndexMap;
use gpui::SharedString;
use gpui::{AppContext, TestAppContext};
use http_client::FakeHttpClient;
use project::Project;
use settings::{Settings, SettingsStore};
use super::*;
#[gpui::test]
async fn test_enabled_built_in_tools_for_profile(cx: &mut TestAppContext) {
init_test_settings(cx);
let id = AgentProfileId::default();
let profile_settings = cx.read(|cx| {
AgentSettings::get_global(cx)
.profiles
.get(&id)
.unwrap()
.clone()
});
let tool_set = default_tool_set(cx);
let profile = AgentProfile::new(id.clone(), tool_set);
let mut enabled_tools = cx
.read(|cx| profile.enabled_tools(cx))
.into_iter()
.map(|tool| tool.name())
.collect::<Vec<_>>();
enabled_tools.sort();
let mut expected_tools = profile_settings
.tools
.into_iter()
.filter_map(|(tool, enabled)| enabled.then_some(tool.to_string()))
// Provider dependent
.filter(|tool| tool != "web_search")
.collect::<Vec<_>>();
// Plus all registered MCP tools
expected_tools.extend(["enabled_mcp_tool".into(), "disabled_mcp_tool".into()]);
expected_tools.sort();
assert_eq!(enabled_tools, expected_tools);
}
#[gpui::test]
async fn test_custom_mcp_settings(cx: &mut TestAppContext) {
init_test_settings(cx);
let id = AgentProfileId("custom_mcp".into());
let profile_settings = cx.read(|cx| {
AgentSettings::get_global(cx)
.profiles
.get(&id)
.unwrap()
.clone()
});
let tool_set = default_tool_set(cx);
let profile = AgentProfile::new(id.clone(), tool_set);
let mut enabled_tools = cx
.read(|cx| profile.enabled_tools(cx))
.into_iter()
.map(|tool| tool.name())
.collect::<Vec<_>>();
enabled_tools.sort();
let mut expected_tools = profile_settings.context_servers["mcp"]
.tools
.iter()
.filter_map(|(key, enabled)| enabled.then(|| key.to_string()))
.collect::<Vec<_>>();
expected_tools.sort();
assert_eq!(enabled_tools, expected_tools);
}
#[gpui::test]
async fn test_only_built_in(cx: &mut TestAppContext) {
init_test_settings(cx);
let id = AgentProfileId("write_minus_mcp".into());
let profile_settings = cx.read(|cx| {
AgentSettings::get_global(cx)
.profiles
.get(&id)
.unwrap()
.clone()
});
let tool_set = default_tool_set(cx);
let profile = AgentProfile::new(id.clone(), tool_set);
let mut enabled_tools = cx
.read(|cx| profile.enabled_tools(cx))
.into_iter()
.map(|tool| tool.name())
.collect::<Vec<_>>();
enabled_tools.sort();
let mut expected_tools = profile_settings
.tools
.into_iter()
.filter_map(|(tool, enabled)| enabled.then_some(tool.to_string()))
// Provider dependent
.filter(|tool| tool != "web_search")
.collect::<Vec<_>>();
expected_tools.sort();
assert_eq!(enabled_tools, expected_tools);
}
fn init_test_settings(cx: &mut TestAppContext) {
cx.update(|cx| {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
Project::init_settings(cx);
AgentSettings::register(cx);
language_model::init_settings(cx);
ToolRegistry::default_global(cx);
assistant_tools::init(FakeHttpClient::with_404_response(), cx);
});
cx.update(|cx| {
let mut agent_settings = AgentSettings::get_global(cx).clone();
agent_settings.profiles.insert(
AgentProfileId("write_minus_mcp".into()),
AgentProfileSettings {
name: "write_minus_mcp".into(),
enable_all_context_servers: false,
..agent_settings.profiles[&AgentProfileId::default()].clone()
},
);
agent_settings.profiles.insert(
AgentProfileId("custom_mcp".into()),
AgentProfileSettings {
name: "mcp".into(),
tools: IndexMap::default(),
enable_all_context_servers: false,
context_servers: IndexMap::from_iter([("mcp".into(), context_server_preset())]),
},
);
AgentSettings::override_global(agent_settings, cx);
})
}
fn context_server_preset() -> ContextServerPreset {
ContextServerPreset {
tools: IndexMap::from_iter([
("enabled_mcp_tool".into(), true),
("disabled_mcp_tool".into(), false),
]),
}
}
fn default_tool_set(cx: &mut TestAppContext) -> Entity<ToolWorkingSet> {
cx.new(|_| {
let mut tool_set = ToolWorkingSet::default();
tool_set.insert(Arc::new(FakeTool::new("enabled_mcp_tool", "mcp")));
tool_set.insert(Arc::new(FakeTool::new("disabled_mcp_tool", "mcp")));
tool_set
})
}
struct FakeTool {
name: String,
source: SharedString,
}
impl FakeTool {
fn new(name: impl Into<String>, source: impl Into<SharedString>) -> Self {
Self {
name: name.into(),
source: source.into(),
}
}
}
impl Tool for FakeTool {
fn name(&self) -> String {
self.name.clone()
}
fn source(&self) -> ToolSource {
ToolSource::ContextServer {
id: self.source.clone(),
}
}
fn description(&self) -> String {
unimplemented!()
}
fn icon(&self) -> icons::IconName {
unimplemented!()
}
fn needs_confirmation(&self, _input: &serde_json::Value, _cx: &App) -> bool {
unimplemented!()
}
fn ui_text(&self, _input: &serde_json::Value) -> String {
unimplemented!()
}
fn run(
self: Arc<Self>,
_input: serde_json::Value,
_request: Arc<language_model::LanguageModelRequest>,
_project: Entity<Project>,
_action_log: Entity<assistant_tool::ActionLog>,
_model: Arc<dyn language_model::LanguageModel>,
_window: Option<gpui::AnyWindowHandle>,
_cx: &mut App,
) -> assistant_tool::ToolResult {
unimplemented!()
}
fn may_perform_edits(&self) -> bool {
unimplemented!()
}
}
}

View File

@@ -1,30 +1,25 @@
use std::fmt::{self, Display, Formatter, Write as _};
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::{ops::Range, path::Path, sync::Arc};
use assistant_context_editor::AssistantContext;
use crate::thread::ZedAgentThread;
use assistant_context::AssistantContext;
use assistant_tool::outline;
use collections::{HashMap, HashSet};
use editor::display_map::CreaseId;
use editor::{Addon, Editor};
use collections::HashSet;
use futures::future;
use futures::{FutureExt, future::Shared};
use gpui::{App, AppContext as _, Entity, SharedString, Subscription, Task};
use gpui::{App, AppContext as _, ElementId, Entity, SharedString, Task};
use icons::IconName;
use language::{Buffer, ParseStatus};
use language_model::{LanguageModelImage, LanguageModelRequestMessage, MessageContent};
use project::{Project, ProjectEntryId, ProjectPath, Worktree};
use prompt_store::{PromptStore, UserPromptId};
use ref_cast::RefCast;
use rope::Point;
use std::fmt::{self, Display, Formatter, Write as _};
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::{ops::Range, path::Path, sync::Arc};
use text::{Anchor, OffsetRangeExt as _};
use ui::{Context, ElementId, IconName};
use util::markdown::MarkdownCodeBlock;
use util::{ResultExt as _, post_inc};
use crate::context_store::{ContextStore, ContextStoreEvent};
use crate::thread::Thread;
pub const RULES_ICON: IconName = IconName::Context;
pub enum ContextKind {
@@ -565,7 +560,7 @@ impl Display for FetchedUrlContext {
#[derive(Debug, Clone)]
pub struct ThreadContextHandle {
pub thread: Entity<Thread>,
pub agent: Entity<ZedAgentThread>,
pub context_id: ContextId,
}
@@ -578,23 +573,23 @@ pub struct ThreadContext {
impl ThreadContextHandle {
pub fn eq_for_key(&self, other: &Self) -> bool {
self.thread == other.thread
self.agent == other.agent
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.thread.hash(state)
self.agent.hash(state)
}
pub fn title(&self, cx: &App) -> SharedString {
self.thread.read(cx).summary().or_default()
self.agent.read(cx).summary().or_default()
}
fn load(self, cx: &App) -> Task<Option<(AgentContext, Vec<Entity<Buffer>>)>> {
cx.spawn(async move |cx| {
let text = Thread::wait_for_detailed_summary_or_text(&self.thread, cx).await?;
let text = ZedAgentThread::wait_for_detailed_summary_or_text(&self.agent, cx).await?;
let title = self
.thread
.read_with(cx, |thread, _cx| thread.summary().or_default())
.agent
.read_with(cx, |thread, _| thread.summary().or_default())
.ok()?;
let context = AgentContext::Thread(ThreadContext {
title,
@@ -745,6 +740,7 @@ pub struct ImageContext {
pub enum ImageStatus {
Loading,
Error,
Warning,
Ready,
}
@@ -761,11 +757,17 @@ impl ImageContext {
self.image_task.clone().now_or_never().flatten()
}
pub fn status(&self) -> ImageStatus {
pub fn status(&self, model: Option<&Arc<dyn language_model::LanguageModel>>) -> ImageStatus {
match self.image_task.clone().now_or_never() {
None => ImageStatus::Loading,
Some(None) => ImageStatus::Error,
Some(Some(_)) => ImageStatus::Ready,
Some(Some(_)) => {
if model.is_some_and(|model| !model.supports_images()) {
ImageStatus::Warning
} else {
ImageStatus::Ready
}
}
}
}
@@ -1110,69 +1112,6 @@ impl Hash for AgentContextKey {
}
}
#[derive(Default)]
pub struct ContextCreasesAddon {
creases: HashMap<AgentContextKey, Vec<(CreaseId, SharedString)>>,
_subscription: Option<Subscription>,
}
impl Addon for ContextCreasesAddon {
fn to_any(&self) -> &dyn std::any::Any {
self
}
fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
Some(self)
}
}
impl ContextCreasesAddon {
pub fn new() -> Self {
Self {
creases: HashMap::default(),
_subscription: None,
}
}
pub fn add_creases(
&mut self,
context_store: &Entity<ContextStore>,
key: AgentContextKey,
creases: impl IntoIterator<Item = (CreaseId, SharedString)>,
cx: &mut Context<Editor>,
) {
self.creases.entry(key).or_default().extend(creases);
self._subscription = Some(cx.subscribe(
&context_store,
|editor, _, event, cx| match event {
ContextStoreEvent::ContextRemoved(key) => {
let Some(this) = editor.addon_mut::<Self>() else {
return;
};
let (crease_ids, replacement_texts): (Vec<_>, Vec<_>) = this
.creases
.remove(key)
.unwrap_or_default()
.into_iter()
.unzip();
let ranges = editor
.remove_creases(crease_ids, cx)
.into_iter()
.map(|(_, range)| range)
.collect::<Vec<_>>();
editor.unfold_ranges(&ranges, false, false, cx);
editor.edit(ranges.into_iter().zip(replacement_texts), cx);
cx.notify();
}
},
))
}
pub fn into_inner(self) -> HashMap<AgentContextKey, Vec<(CreaseId, SharedString)>> {
self.creases
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -1,140 +0,0 @@
use std::sync::Arc;
use anyhow::Context as _;
use context_server::ContextServerId;
use extension::{ContextServerConfiguration, ExtensionManifest};
use gpui::Task;
use language::LanguageRegistry;
use project::context_server_store::registry::ContextServerDescriptorRegistry;
use ui::prelude::*;
use util::ResultExt;
use workspace::Workspace;
use crate::agent_configuration::ConfigureContextServerModal;
pub(crate) fn init(language_registry: Arc<LanguageRegistry>, cx: &mut App) {
cx.observe_new(move |_: &mut Workspace, window, cx| {
let Some(window) = window else {
return;
};
if let Some(extension_events) = extension::ExtensionEvents::try_global(cx).as_ref() {
cx.subscribe_in(extension_events, window, {
let language_registry = language_registry.clone();
move |workspace, _, event, window, cx| match event {
extension::Event::ExtensionInstalled(manifest) => {
show_configure_mcp_modal(
language_registry.clone(),
manifest,
workspace,
window,
cx,
);
}
extension::Event::ConfigureExtensionRequested(manifest) => {
if !manifest.context_servers.is_empty() {
show_configure_mcp_modal(
language_registry.clone(),
manifest,
workspace,
window,
cx,
);
}
}
_ => {}
}
})
.detach();
} else {
log::info!(
"No extension events global found. Skipping context server configuration wizard"
);
}
})
.detach();
}
pub enum Configuration {
NotAvailable(ContextServerId, Option<SharedString>),
Required(
ContextServerId,
Option<SharedString>,
ContextServerConfiguration,
),
}
fn show_configure_mcp_modal(
language_registry: Arc<LanguageRegistry>,
manifest: &Arc<ExtensionManifest>,
workspace: &mut Workspace,
window: &mut Window,
cx: &mut Context<'_, Workspace>,
) {
let context_server_store = workspace.project().read(cx).context_server_store();
let repository: Option<SharedString> = manifest.repository.as_ref().map(|s| s.clone().into());
let registry = ContextServerDescriptorRegistry::default_global(cx).read(cx);
let worktree_store = workspace.project().read(cx).worktree_store();
let configuration_tasks = manifest
.context_servers
.keys()
.cloned()
.map({
|key| {
let Some(descriptor) = registry.context_server_descriptor(&key) else {
return Task::ready(Configuration::NotAvailable(
ContextServerId(key),
repository.clone(),
));
};
cx.spawn({
let repository_url = repository.clone();
let worktree_store = worktree_store.clone();
async move |_, cx| {
let configuration = descriptor
.configuration(worktree_store.clone(), &cx)
.await
.context("Failed to resolve context server configuration")
.log_err()
.flatten();
match configuration {
Some(config) => Configuration::Required(
ContextServerId(key),
repository_url,
config,
),
None => {
Configuration::NotAvailable(ContextServerId(key), repository_url)
}
}
}
})
}
})
.collect::<Vec<_>>();
let jsonc_language = language_registry.language_for_name("jsonc");
cx.spawn_in(window, async move |this, cx| {
let configurations = futures::future::join_all(configuration_tasks).await;
let jsonc_language = jsonc_language.await.ok();
this.update_in(cx, |this, window, cx| {
let workspace = cx.entity().downgrade();
this.toggle_modal(window, cx, |window, cx| {
ConfigureContextServerModal::new(
configurations.into_iter(),
context_server_store,
jsonc_language,
language_registry,
workspace,
window,
cx,
)
});
})
})
.detach();
}

View File

@@ -4,9 +4,9 @@ use anyhow::{Result, anyhow, bail};
use assistant_tool::{ActionLog, Tool, ToolResult, ToolSource};
use context_server::{ContextServerId, types};
use gpui::{AnyWindowHandle, App, Entity, Task};
use icons::IconName;
use language_model::{LanguageModel, LanguageModelRequest, LanguageModelToolSchemaFormat};
use project::{Project, context_server_store::ContextServerStore};
use ui::IconName;
pub struct ContextServerTool {
store: Entity<ContextServerStore>,
@@ -51,6 +51,10 @@ impl Tool for ContextServerTool {
true
}
fn may_perform_edits(&self) -> bool {
true
}
fn input_schema(&self, format: LanguageModelToolSchemaFormat) -> Result<serde_json::Value> {
let mut schema = self.tool.input_schema.clone();
assistant_tool::adapt_schema_to_format(&mut schema, format)?;
@@ -100,7 +104,15 @@ impl Tool for ContextServerTool {
tool_name,
arguments
);
let response = protocol.run_tool(tool_name, arguments).await?;
let response = protocol
.request::<context_server::types::requests::CallTool>(
context_server::types::CallToolParams {
name: tool_name,
arguments,
meta: None,
},
)
.await?;
let mut result = String::new();
for content in response.content {
@@ -111,6 +123,9 @@ impl Tool for ContextServerTool {
types::ToolResponseContent::Image { .. } => {
log::warn!("Ignoring image content from tool response");
}
types::ToolResponseContent::Audio { .. } => {
log::warn!("Ignoring audio content from tool response");
}
types::ToolResponseContent::Resource { .. } => {
log::warn!("Ignoring resource content from tool response");
}

View File

@@ -1,28 +1,28 @@
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::{
context::{
AgentContextHandle, AgentContextKey, ContextId, ContextKind, DirectoryContextHandle,
FetchedUrlContext, FileContextHandle, ImageContext, RulesContextHandle,
SelectionContextHandle, SymbolContextHandle, TextThreadContextHandle, ThreadContextHandle,
},
thread::{MessageId, ThreadId, ZedAgentThread},
thread_store::ThreadStore,
};
use anyhow::{Context as _, Result, anyhow};
use assistant_context_editor::AssistantContext;
use assistant_context::AssistantContext;
use collections::{HashSet, IndexSet};
use futures::{self, FutureExt};
use gpui::{App, Context, Entity, EventEmitter, Image, SharedString, Task, WeakEntity};
use language::{Buffer, File as _};
use language_model::LanguageModelImage;
use project::image_store::is_image_file;
use project::{Project, ProjectItem, ProjectPath, Symbol};
use project::{Project, ProjectItem, ProjectPath, Symbol, image_store::is_image_file};
use prompt_store::UserPromptId;
use ref_cast::RefCast as _;
use text::{Anchor, OffsetRangeExt};
use crate::ThreadStore;
use crate::context::{
AgentContextHandle, AgentContextKey, ContextId, DirectoryContextHandle, FetchedUrlContext,
FileContextHandle, ImageContext, RulesContextHandle, SelectionContextHandle,
SymbolContextHandle, TextThreadContextHandle, ThreadContextHandle,
use std::{
ops::Range,
path::{Path, PathBuf},
sync::Arc,
};
use crate::context_strip::SuggestedContext;
use crate::thread::{MessageId, Thread, ThreadId};
use text::{Anchor, OffsetRangeExt};
pub struct ContextStore {
project: WeakEntity<Project>,
@@ -66,8 +66,9 @@ impl ContextStore {
pub fn new_context_for_thread(
&self,
thread: &Thread,
thread: &ZedAgentThread,
exclude_messages_from_id: Option<MessageId>,
_cx: &App,
) -> Vec<AgentContextHandle> {
let existing_context = thread
.messages()
@@ -206,12 +207,15 @@ impl ContextStore {
pub fn add_thread(
&mut self,
thread: Entity<Thread>,
thread: Entity<ZedAgentThread>,
remove_if_exists: bool,
cx: &mut Context<Self>,
) -> Option<AgentContextHandle> {
let context_id = self.next_context_id.post_inc();
let context = AgentContextHandle::Thread(ThreadContextHandle { thread, context_id });
let context = AgentContextHandle::Thread(ThreadContextHandle {
agent: thread,
context_id,
});
if let Some(existing) = self.context_set.get(AgentContextKey::ref_cast(&context)) {
if remove_if_exists {
@@ -387,7 +391,10 @@ impl ContextStore {
if let Some(thread) = thread.upgrade() {
let context_id = self.next_context_id.post_inc();
self.insert_context(
AgentContextHandle::Thread(ThreadContextHandle { thread, context_id }),
AgentContextHandle::Thread(ThreadContextHandle {
agent: thread,
context_id,
}),
cx,
);
}
@@ -411,11 +418,11 @@ impl ContextStore {
match &context {
AgentContextHandle::Thread(thread_context) => {
if let Some(thread_store) = self.thread_store.clone() {
thread_context.thread.update(cx, |thread, cx| {
thread_context.agent.update(cx, |thread, cx| {
thread.start_generating_detailed_summary_if_needed(thread_store, cx);
});
self.context_thread_ids
.insert(thread_context.thread.read(cx).id().clone());
.insert(thread_context.agent.read(cx).id().clone());
} else {
return false;
}
@@ -441,7 +448,7 @@ impl ContextStore {
match context {
AgentContextHandle::Thread(thread_context) => {
self.context_thread_ids
.remove(thread_context.thread.read(cx).id());
.remove(thread_context.agent.read(cx).id());
}
AgentContextHandle::TextThread(text_thread_context) => {
if let Some(path) = text_thread_context.context.read(cx).path() {
@@ -561,6 +568,49 @@ impl ContextStore {
}
}
#[derive(Clone)]
pub enum SuggestedContext {
File {
name: SharedString,
icon_path: Option<SharedString>,
buffer: WeakEntity<Buffer>,
},
Thread {
name: SharedString,
thread: WeakEntity<ZedAgentThread>,
},
TextThread {
name: SharedString,
context: WeakEntity<AssistantContext>,
},
}
impl SuggestedContext {
pub fn name(&self) -> &SharedString {
match self {
Self::File { name, .. } => name,
Self::Thread { name, .. } => name,
Self::TextThread { name, .. } => name,
}
}
pub fn icon_path(&self) -> Option<SharedString> {
match self {
Self::File { icon_path, .. } => icon_path.clone(),
Self::Thread { .. } => None,
Self::TextThread { .. } => None,
}
}
pub fn kind(&self) -> ContextKind {
match self {
Self::File { .. } => ContextKind::File,
Self::Thread { .. } => ContextKind::Thread,
Self::TextThread { .. } => ContextKind::TextThread,
}
}
}
pub enum FileInclusion {
Direct,
InDirectory { full_path: PathBuf },

View File

@@ -1,21 +1,16 @@
use std::{collections::VecDeque, path::Path, sync::Arc};
use anyhow::Context as _;
use assistant_context_editor::{AssistantContext, SavedContextMetadata};
use chrono::{DateTime, Utc};
use futures::future::{TryFutureExt as _, join_all};
use gpui::{Entity, Task, prelude::*};
use serde::{Deserialize, Serialize};
use smol::future::FutureExt;
use std::time::Duration;
use ui::{App, SharedString, Window};
use util::ResultExt as _;
use crate::{
Thread,
thread::ThreadId,
ThreadId,
thread_store::{SerializedThreadMetadata, ThreadStore},
};
use anyhow::{Context as _, Result};
use assistant_context::SavedContextMetadata;
use chrono::{DateTime, Utc};
use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::*};
use itertools::Itertools;
use paths::contexts_dir;
use serde::{Deserialize, Serialize};
use std::{collections::VecDeque, path::Path, sync::Arc, time::Duration};
use util::ResultExt as _;
const MAX_RECENTLY_OPENED_ENTRIES: usize = 6;
const NAVIGATION_HISTORY_PATH: &str = "agent-navigation-history.json";
@@ -41,52 +36,34 @@ impl HistoryEntry {
HistoryEntry::Context(context) => HistoryEntryId::Context(context.path.clone()),
}
}
pub fn title(&self) -> &SharedString {
match self {
HistoryEntry::Thread(thread) => &thread.summary,
HistoryEntry::Context(context) => &context.title,
}
}
}
/// Generic identifier for a history entry.
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum HistoryEntryId {
Thread(ThreadId),
Context(Arc<Path>),
}
#[derive(Clone, Debug)]
pub(crate) enum RecentEntry {
Thread(ThreadId, Entity<Thread>),
Context(Entity<AssistantContext>),
}
impl PartialEq for RecentEntry {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Thread(l0, _), Self::Thread(r0, _)) => l0 == r0,
(Self::Context(l0), Self::Context(r0)) => l0 == r0,
_ => false,
}
}
}
impl Eq for RecentEntry {}
impl RecentEntry {
pub(crate) fn summary(&self, cx: &App) -> SharedString {
match self {
RecentEntry::Thread(_, thread) => thread.read(cx).summary().or_default(),
RecentEntry::Context(context) => context.read(cx).summary().or_default(),
}
}
}
#[derive(Serialize, Deserialize)]
enum SerializedRecentEntry {
enum SerializedRecentOpen {
Thread(String),
ContextName(String),
/// Old format which stores the full path
Context(String),
}
pub struct HistoryStore {
thread_store: Entity<ThreadStore>,
context_store: Entity<assistant_context_editor::ContextStore>,
recently_opened_entries: VecDeque<RecentEntry>,
context_store: Entity<assistant_context::ContextStore>,
recently_opened_entries: VecDeque<HistoryEntryId>,
_subscriptions: Vec<gpui::Subscription>,
_save_recently_opened_entries_task: Task<()>,
}
@@ -94,9 +71,8 @@ pub struct HistoryStore {
impl HistoryStore {
pub fn new(
thread_store: Entity<ThreadStore>,
context_store: Entity<assistant_context_editor::ContextStore>,
initial_recent_entries: impl IntoIterator<Item = RecentEntry>,
window: &mut Window,
context_store: Entity<assistant_context::ContextStore>,
initial_recent_entries: impl IntoIterator<Item = HistoryEntryId>,
cx: &mut Context<Self>,
) -> Self {
let subscriptions = vec![
@@ -104,68 +80,20 @@ impl HistoryStore {
cx.observe(&context_store, |_, _, cx| cx.notify()),
];
window
.spawn(cx, {
let thread_store = thread_store.downgrade();
let context_store = context_store.downgrade();
let this = cx.weak_entity();
async move |cx| {
let path = paths::data_dir().join(NAVIGATION_HISTORY_PATH);
let contents = cx
.background_spawn(async move { std::fs::read_to_string(path) })
.await
.ok()?;
let entries = serde_json::from_str::<Vec<SerializedRecentEntry>>(&contents)
.context("deserializing persisted agent panel navigation history")
.log_err()?
.into_iter()
.take(MAX_RECENTLY_OPENED_ENTRIES)
.map(|serialized| match serialized {
SerializedRecentEntry::Thread(id) => thread_store
.update_in(cx, |thread_store, window, cx| {
let thread_id = ThreadId::from(id.as_str());
thread_store
.open_thread(&thread_id, window, cx)
.map_ok(|thread| RecentEntry::Thread(thread_id, thread))
.boxed()
})
.unwrap_or_else(|_| {
async {
anyhow::bail!("no thread store");
}
.boxed()
}),
SerializedRecentEntry::Context(id) => context_store
.update(cx, |context_store, cx| {
context_store
.open_local_context(Path::new(&id).into(), cx)
.map_ok(RecentEntry::Context)
.boxed()
})
.unwrap_or_else(|_| {
async {
anyhow::bail!("no context store");
}
.boxed()
}),
});
let entries = join_all(entries)
.await
.into_iter()
.filter_map(|result| result.log_with_level(log::Level::Debug))
.collect::<VecDeque<_>>();
this.update(cx, |this, _| {
this.recently_opened_entries.extend(entries);
this.recently_opened_entries
.truncate(MAX_RECENTLY_OPENED_ENTRIES);
})
.ok();
Some(())
}
cx.spawn(async move |this, cx| {
let entries = Self::load_recently_opened_entries(cx).await.log_err()?;
this.update(cx, |this, _| {
this.recently_opened_entries
.extend(
entries.into_iter().take(
MAX_RECENTLY_OPENED_ENTRIES
.saturating_sub(this.recently_opened_entries.len()),
),
);
})
.detach();
.ok()
})
.detach();
Self {
thread_store,
@@ -184,19 +112,20 @@ impl HistoryStore {
return history_entries;
}
for thread in self
.thread_store
.update(cx, |this, _cx| this.reverse_chronological_threads())
{
history_entries.push(HistoryEntry::Thread(thread));
}
for context in self
.context_store
.update(cx, |this, _cx| this.reverse_chronological_contexts())
{
history_entries.push(HistoryEntry::Context(context));
}
history_entries.extend(
self.thread_store
.read(cx)
.reverse_chronological_threads()
.cloned()
.map(HistoryEntry::Thread),
);
history_entries.extend(
self.context_store
.read(cx)
.unordered_contexts()
.cloned()
.map(HistoryEntry::Context),
);
history_entries.sort_unstable_by_key(|entry| std::cmp::Reverse(entry.updated_at()));
history_entries
@@ -206,15 +135,62 @@ impl HistoryStore {
self.entries(cx).into_iter().take(limit).collect()
}
pub fn recently_opened_entries(&self, cx: &App) -> Vec<HistoryEntry> {
#[cfg(debug_assertions)]
if std::env::var("ZED_SIMULATE_NO_THREAD_HISTORY").is_ok() {
return Vec::new();
}
let thread_entries = self
.thread_store
.read(cx)
.reverse_chronological_threads()
.flat_map(|thread| {
self.recently_opened_entries
.iter()
.enumerate()
.flat_map(|(index, entry)| match entry {
HistoryEntryId::Thread(id) if &thread.id == id => {
Some((index, HistoryEntry::Thread(thread.clone())))
}
_ => None,
})
});
let context_entries =
self.context_store
.read(cx)
.unordered_contexts()
.flat_map(|context| {
self.recently_opened_entries
.iter()
.enumerate()
.flat_map(|(index, entry)| match entry {
HistoryEntryId::Context(path) if &context.path == path => {
Some((index, HistoryEntry::Context(context.clone())))
}
_ => None,
})
});
thread_entries
.chain(context_entries)
// optimization to halt iteration early
.take(self.recently_opened_entries.len())
.sorted_unstable_by_key(|(index, _)| *index)
.map(|(_, entry)| entry)
.collect()
}
fn save_recently_opened_entries(&mut self, cx: &mut Context<Self>) {
let serialized_entries = self
.recently_opened_entries
.iter()
.filter_map(|entry| match entry {
RecentEntry::Context(context) => Some(SerializedRecentEntry::Context(
context.read(cx).path()?.to_str()?.to_owned(),
)),
RecentEntry::Thread(id, _) => Some(SerializedRecentEntry::Thread(id.to_string())),
HistoryEntryId::Context(path) => path.file_name().map(|file| {
SerializedRecentOpen::ContextName(file.to_string_lossy().to_string())
}),
HistoryEntryId::Thread(id) => Some(SerializedRecentOpen::Thread(id.to_string())),
})
.collect::<Vec<_>>();
@@ -233,7 +209,33 @@ impl HistoryStore {
});
}
pub fn push_recently_opened_entry(&mut self, entry: RecentEntry, cx: &mut Context<Self>) {
fn load_recently_opened_entries(cx: &AsyncApp) -> Task<Result<Vec<HistoryEntryId>>> {
cx.background_spawn(async move {
let path = paths::data_dir().join(NAVIGATION_HISTORY_PATH);
let contents = smol::fs::read_to_string(path).await?;
let entries = serde_json::from_str::<Vec<SerializedRecentOpen>>(&contents)
.context("deserializing persisted agent panel navigation history")?
.into_iter()
.take(MAX_RECENTLY_OPENED_ENTRIES)
.flat_map(|entry| match entry {
SerializedRecentOpen::Thread(id) => {
Some(HistoryEntryId::Thread(id.as_str().into()))
}
SerializedRecentOpen::ContextName(file_name) => Some(HistoryEntryId::Context(
contexts_dir().join(file_name).into(),
)),
SerializedRecentOpen::Context(path) => {
Path::new(&path).file_name().map(|file_name| {
HistoryEntryId::Context(contexts_dir().join(file_name).into())
})
}
})
.collect::<Vec<_>>();
Ok(entries)
})
}
pub fn push_recently_opened_entry(&mut self, entry: HistoryEntryId, cx: &mut Context<Self>) {
self.recently_opened_entries
.retain(|old_entry| old_entry != &entry);
self.recently_opened_entries.push_front(entry);
@@ -244,24 +246,33 @@ impl HistoryStore {
pub fn remove_recently_opened_thread(&mut self, id: ThreadId, cx: &mut Context<Self>) {
self.recently_opened_entries.retain(|entry| match entry {
RecentEntry::Thread(thread_id, _) if thread_id == &id => false,
HistoryEntryId::Thread(thread_id) if thread_id == &id => false,
_ => true,
});
self.save_recently_opened_entries(cx);
}
pub fn remove_recently_opened_entry(&mut self, entry: &RecentEntry, cx: &mut Context<Self>) {
pub fn replace_recently_opened_text_thread(
&mut self,
old_path: &Path,
new_path: &Arc<Path>,
cx: &mut Context<Self>,
) {
for entry in &mut self.recently_opened_entries {
match entry {
HistoryEntryId::Context(path) if path.as_ref() == old_path => {
*entry = HistoryEntryId::Context(new_path.clone());
break;
}
_ => {}
}
}
self.save_recently_opened_entries(cx);
}
pub fn remove_recently_opened_entry(&mut self, entry: &HistoryEntryId, cx: &mut Context<Self>) {
self.recently_opened_entries
.retain(|old_entry| old_entry != entry);
self.save_recently_opened_entries(cx);
}
pub fn recently_opened_entries(&self, _cx: &mut Context<Self>) -> VecDeque<RecentEntry> {
#[cfg(debug_assertions)]
if std::env::var("ZED_SIMULATE_NO_THREAD_HISTORY").is_ok() {
return VecDeque::new();
}
self.recently_opened_entries.clone()
}
}

View File

@@ -1 +0,0 @@
These files changed since last read:

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,25 @@
use std::cell::{Ref, RefCell};
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use agent_settings::{AgentProfile, AgentProfileId, AgentSettings, CompletionMode};
use crate::{
context_server_tool::ContextServerTool,
thread::{
DetailedSummaryState, ExceededWindowError, MessageId, ProjectSnapshot, ThreadId, ZedAgentThread,
},
};
use agent_settings::{AgentProfileId, CompletionMode};
use anyhow::{Context as _, Result, anyhow};
use assistant_tool::{ToolId, ToolSource, ToolWorkingSet};
use assistant_tool::{ToolId, ToolWorkingSet};
use chrono::{DateTime, Utc};
use collections::HashMap;
use context_server::ContextServerId;
use futures::channel::{mpsc, oneshot};
use futures::future::{self, BoxFuture, Shared};
use futures::{FutureExt as _, StreamExt as _};
use futures::{
FutureExt as _, StreamExt as _,
channel::{mpsc, oneshot},
future::{self, BoxFuture, Shared},
};
use gpui::{
App, BackgroundExecutor, Context, Entity, EventEmitter, Global, ReadGlobal, SharedString,
Subscription, Task, prelude::*,
Subscription, Task, Window, prelude::*,
};
use indoc::indoc;
use language_model::{LanguageModelToolResultContent, LanguageModelToolUseId, Role, TokenUsage};
use project::context_server_store::{ContextServerStatus, ContextServerStore};
use project::{Project, ProjectItem, ProjectPath, Worktree};
@@ -25,20 +28,18 @@ use prompt_store::{
UserRulesContext, WorktreeContext,
};
use serde::{Deserialize, Serialize};
use settings::{Settings as _, SettingsStore};
use ui::Window;
use util::ResultExt as _;
use crate::context_server_tool::ContextServerTool;
use crate::thread::{
DetailedSummaryState, ExceededWindowError, MessageId, ProjectSnapshot, Thread, ThreadId,
};
use indoc::indoc;
use sqlez::{
bindable::{Bind, Column},
connection::Connection,
statement::Statement,
};
use std::{
cell::{Ref, RefCell},
path::{Path, PathBuf},
rc::Rc,
sync::{Arc, Mutex},
};
use util::ResultExt as _;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DataType {
@@ -70,13 +71,16 @@ impl Column for DataType {
}
}
const RULES_FILE_NAMES: [&'static str; 6] = [
const RULES_FILE_NAMES: [&'static str; 9] = [
".rules",
".cursorrules",
".windsurfrules",
".clinerules",
".github/copilot-instructions.md",
"CLAUDE.md",
"AGENT.md",
"AGENTS.md",
"GEMINI.md",
];
pub fn init(cx: &mut App) {
@@ -88,12 +92,12 @@ pub fn init(cx: &mut App) {
pub struct SharedProjectContext(Rc<RefCell<Option<ProjectContext>>>);
impl SharedProjectContext {
pub fn borrow(&self) -> Ref<Option<ProjectContext>> {
pub fn borrow(&self) -> Ref<'_, Option<ProjectContext>> {
self.0.borrow()
}
}
pub type TextThreadStore = assistant_context_editor::ContextStore;
pub type TextThreadStore = assistant_context::ContextStore;
pub struct ThreadStore {
project: Entity<Project>,
@@ -145,12 +149,7 @@ impl ThreadStore {
prompt_store: Option<Entity<PromptStore>>,
cx: &mut Context<Self>,
) -> (Self, oneshot::Receiver<()>) {
let mut subscriptions = vec![
cx.observe_global::<SettingsStore>(move |this: &mut Self, cx| {
this.load_default_profile(cx);
}),
cx.subscribe(&project, Self::handle_project_event),
];
let mut subscriptions = vec![cx.subscribe(&project, Self::handle_project_event)];
if let Some(prompt_store) = prompt_store.as_ref() {
subscriptions.push(cx.subscribe(
@@ -198,7 +197,6 @@ impl ThreadStore {
_reload_system_prompt_task: reload_system_prompt_task,
_subscriptions: subscriptions,
};
this.load_default_profile(cx);
this.register_context_server_handlers(cx);
this.reload(cx).detach_and_log_err(cx);
(this, ready_rx)
@@ -310,17 +308,19 @@ impl ThreadStore {
project: Entity<Project>,
cx: &mut App,
) -> Task<(WorktreeContext, Option<RulesLoadingError>)> {
let root_name = worktree.read(cx).root_name().into();
let tree = worktree.read(cx);
let root_name = tree.root_name().into();
let abs_path = tree.abs_path();
let mut context = WorktreeContext {
root_name,
abs_path,
rules_file: None,
};
let rules_task = Self::load_worktree_rules_file(worktree, project, cx);
let Some(rules_task) = rules_task else {
return Task::ready((
WorktreeContext {
root_name,
rules_file: None,
},
None,
));
return Task::ready((context, None));
};
cx.spawn(async move |_| {
@@ -333,11 +333,8 @@ impl ThreadStore {
}),
),
};
let worktree_info = WorktreeContext {
root_name,
rules_file,
};
(worktree_info, rules_file_error)
context.rules_file = rules_file;
(context, rules_file_error)
})
}
@@ -346,12 +343,12 @@ impl ThreadStore {
project: Entity<Project>,
cx: &mut App,
) -> Option<Task<Result<RulesFileContext>>> {
let worktree_ref = worktree.read(cx);
let worktree_id = worktree_ref.id();
let worktree = worktree.read(cx);
let worktree_id = worktree.id();
let selected_rules_file = RULES_FILE_NAMES
.into_iter()
.filter_map(|name| {
worktree_ref
worktree
.entry_for_path(name)
.filter(|entry| entry.is_file())
.map(|entry| entry.path.clone())
@@ -398,19 +395,14 @@ impl ThreadStore {
self.threads.len()
}
pub fn unordered_threads(&self) -> impl Iterator<Item = &SerializedThreadMetadata> {
pub fn reverse_chronological_threads(&self) -> impl Iterator<Item = &SerializedThreadMetadata> {
// ordering is from "ORDER BY" in `list_threads`
self.threads.iter()
}
pub fn reverse_chronological_threads(&self) -> Vec<SerializedThreadMetadata> {
let mut threads = self.threads.iter().cloned().collect::<Vec<_>>();
threads.sort_unstable_by_key(|thread| std::cmp::Reverse(thread.updated_at));
threads
}
pub fn create_thread(&mut self, cx: &mut Context<Self>) -> Entity<Thread> {
pub fn create_thread(&mut self, cx: &mut Context<Self>) -> Entity<ZedAgentThread> {
cx.new(|cx| {
Thread::new(
ZedAgentThread::new(
self.project.clone(),
self.tools.clone(),
self.prompt_builder.clone(),
@@ -424,9 +416,9 @@ impl ThreadStore {
&mut self,
serialized: SerializedThread,
cx: &mut Context<Self>,
) -> Entity<Thread> {
) -> Entity<ZedAgentThread> {
cx.new(|cx| {
Thread::deserialize(
ZedAgentThread::deserialize(
ThreadId::new(),
serialized,
self.project.clone(),
@@ -444,7 +436,7 @@ impl ThreadStore {
id: &ThreadId,
window: &mut Window,
cx: &mut Context<Self>,
) -> Task<Result<Entity<Thread>>> {
) -> Task<Result<Entity<ZedAgentThread>>> {
let id = id.clone();
let database_future = ThreadsDatabase::global_future(cx);
let this = cx.weak_entity();
@@ -457,7 +449,7 @@ impl ThreadStore {
let thread = this.update_in(cx, |this, window, cx| {
cx.new(|cx| {
Thread::deserialize(
ZedAgentThread::deserialize(
id.clone(),
thread,
this.project.clone(),
@@ -474,9 +466,14 @@ impl ThreadStore {
})
}
pub fn save_thread(&self, thread: &Entity<Thread>, cx: &mut Context<Self>) -> Task<Result<()>> {
let (metadata, serialized_thread) =
thread.update(cx, |thread, cx| (thread.id().clone(), thread.serialize(cx)));
pub fn save_thread(
&self,
thread: &Entity<ZedAgentThread>,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
let (metadata, serialized_thread) = thread.update(cx, |thread, cx| {
(thread.id().clone(), thread.serialize(cx))
});
let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(async move |this, cx| {
@@ -518,92 +515,15 @@ impl ThreadStore {
})
}
fn load_default_profile(&self, cx: &mut Context<Self>) {
let assistant_settings = AgentSettings::get_global(cx);
self.load_profile_by_id(assistant_settings.default_profile.clone(), cx);
}
pub fn load_profile_by_id(&self, profile_id: AgentProfileId, cx: &mut Context<Self>) {
let assistant_settings = AgentSettings::get_global(cx);
if let Some(profile) = assistant_settings.profiles.get(&profile_id) {
self.load_profile(profile.clone(), cx);
}
}
pub fn load_profile(&self, profile: AgentProfile, cx: &mut Context<Self>) {
self.tools.update(cx, |tools, cx| {
tools.disable_all_tools(cx);
tools.enable(
ToolSource::Native,
&profile
.tools
.into_iter()
.filter_map(|(tool, enabled)| enabled.then(|| tool))
.collect::<Vec<_>>(),
cx,
);
});
if profile.enable_all_context_servers {
for context_server_id in self
.project
.read(cx)
.context_server_store()
.read(cx)
.all_server_ids()
{
self.tools.update(cx, |tools, cx| {
tools.enable_source(
ToolSource::ContextServer {
id: context_server_id.0.into(),
},
cx,
);
});
}
// Enable all the tools from all context servers, but disable the ones that are explicitly disabled
for (context_server_id, preset) in profile.context_servers {
self.tools.update(cx, |tools, cx| {
tools.disable(
ToolSource::ContextServer {
id: context_server_id.into(),
},
&preset
.tools
.into_iter()
.filter_map(|(tool, enabled)| (!enabled).then(|| tool))
.collect::<Vec<_>>(),
cx,
)
})
}
} else {
for (context_server_id, preset) in profile.context_servers {
self.tools.update(cx, |tools, cx| {
tools.enable(
ToolSource::ContextServer {
id: context_server_id.into(),
},
&preset
.tools
.into_iter()
.filter_map(|(tool, enabled)| enabled.then(|| tool))
.collect::<Vec<_>>(),
cx,
)
})
}
}
}
fn register_context_server_handlers(&self, cx: &mut Context<Self>) {
cx.subscribe(
&self.project.read(cx).context_server_store(),
Self::handle_context_server_event,
)
.detach();
let context_server_store = self.project.read(cx).context_server_store();
cx.subscribe(&context_server_store, Self::handle_context_server_event)
.detach();
// Check for any servers that were already running before the handler was registered
for server in context_server_store.read(cx).running_servers() {
self.load_context_server_tools(server.id(), context_server_store.clone(), cx);
}
}
fn handle_context_server_event(
@@ -616,71 +536,71 @@ impl ThreadStore {
match event {
project::context_server_store::Event::ServerStatusChanged { server_id, status } => {
match status {
ContextServerStatus::Starting => {}
ContextServerStatus::Running => {
if let Some(server) =
context_server_store.read(cx).get_running_server(server_id)
{
let context_server_manager = context_server_store.clone();
cx.spawn({
let server = server.clone();
let server_id = server_id.clone();
async move |this, cx| {
let Some(protocol) = server.client() else {
return;
};
if protocol.capable(context_server::protocol::ServerCapability::Tools) {
if let Some(tools) = protocol.list_tools().await.log_err() {
let tool_ids = tool_working_set
.update(cx, |tool_working_set, _| {
tools
.tools
.into_iter()
.map(|tool| {
log::info!(
"registering context server tool: {:?}",
tool.name
);
tool_working_set.insert(Arc::new(
ContextServerTool::new(
context_server_manager.clone(),
server.id(),
tool,
),
))
})
.collect::<Vec<_>>()
})
.log_err();
if let Some(tool_ids) = tool_ids {
this.update(cx, |this, cx| {
this.context_server_tool_ids
.insert(server_id, tool_ids);
this.load_default_profile(cx);
})
.log_err();
}
}
}
}
})
.detach();
}
self.load_context_server_tools(server_id.clone(), context_server_store, cx);
}
ContextServerStatus::Stopped | ContextServerStatus::Error(_) => {
if let Some(tool_ids) = self.context_server_tool_ids.remove(server_id) {
tool_working_set.update(cx, |tool_working_set, _| {
tool_working_set.remove(&tool_ids);
});
self.load_default_profile(cx);
}
}
_ => {}
}
}
}
}
fn load_context_server_tools(
&self,
server_id: ContextServerId,
context_server_store: Entity<ContextServerStore>,
cx: &mut Context<Self>,
) {
let Some(server) = context_server_store.read(cx).get_running_server(&server_id) else {
return;
};
let tool_working_set = self.tools.clone();
cx.spawn(async move |this, cx| {
let Some(protocol) = server.client() else {
return;
};
if protocol.capable(context_server::protocol::ServerCapability::Tools) {
if let Some(response) = protocol
.request::<context_server::types::requests::ListTools>(())
.await
.log_err()
{
let tool_ids = tool_working_set
.update(cx, |tool_working_set, _| {
response
.tools
.into_iter()
.map(|tool| {
log::info!("registering context server tool: {:?}", tool.name);
tool_working_set.insert(Arc::new(ContextServerTool::new(
context_server_store.clone(),
server.id(),
tool,
)))
})
.collect::<Vec<_>>()
})
.log_err();
if let Some(tool_ids) = tool_ids {
this.update(cx, |this, _| {
this.context_server_tool_ids.insert(server_id, tool_ids);
})
.log_err();
}
}
}
})
.detach();
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -690,7 +610,7 @@ pub struct SerializedThreadMetadata {
pub updated_at: DateTime<Utc>,
}
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct SerializedThread {
pub version: String,
pub summary: SharedString,
@@ -712,9 +632,11 @@ pub struct SerializedThread {
pub completion_mode: Option<CompletionMode>,
#[serde(default)]
pub tool_use_limit_reached: bool,
#[serde(default)]
pub profile: Option<AgentProfileId>,
}
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct SerializedLanguageModel {
pub provider: String,
pub model: String,
@@ -775,11 +697,15 @@ impl SerializedThreadV0_1_0 {
messages.push(message);
}
SerializedThread { messages, ..self.0 }
SerializedThread {
messages,
version: SerializedThread::VERSION.to_string(),
..self.0
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct SerializedMessage {
pub id: MessageId,
pub role: Role,
@@ -793,11 +719,9 @@ pub struct SerializedMessage {
pub context: String,
#[serde(default)]
pub creases: Vec<SerializedCrease>,
#[serde(default)]
pub is_hidden: bool,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[serde(tag = "type")]
pub enum SerializedMessageSegment {
#[serde(rename = "text")]
@@ -811,18 +735,18 @@ pub enum SerializedMessageSegment {
signature: Option<String>,
},
RedactedThinking {
data: Vec<u8>,
data: String,
},
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct SerializedToolUse {
pub id: LanguageModelToolUseId,
pub name: SharedString,
pub input: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct SerializedToolResult {
pub tool_use_id: LanguageModelToolUseId,
pub is_error: bool,
@@ -854,6 +778,7 @@ impl LegacySerializedThread {
model: None,
completion_mode: None,
tool_use_limit_reached: false,
profile: None,
}
}
}
@@ -879,12 +804,11 @@ impl LegacySerializedMessage {
tool_results: self.tool_results,
context: String::new(),
creases: Vec::new(),
is_hidden: false,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct SerializedCrease {
pub start: usize,
pub end: usize,
@@ -1003,7 +927,7 @@ impl ThreadsDatabase {
fn bytes_encode(
item: &Self::EItem,
) -> Result<std::borrow::Cow<[u8]>, heed::BoxedError> {
) -> Result<std::borrow::Cow<'_, [u8]>, heed::BoxedError> {
serde_json::to_vec(&item.0)
.map(std::borrow::Cow::Owned)
.map_err(Into::into)
@@ -1141,3 +1065,175 @@ impl ThreadsDatabase {
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::thread::{DetailedSummaryState, MessageId};
use chrono::Utc;
use language_model::{Role, TokenUsage};
use pretty_assertions::assert_eq;
#[test]
fn test_legacy_serialized_thread_upgrade() {
let updated_at = Utc::now();
let legacy_thread = LegacySerializedThread {
summary: "Test conversation".into(),
updated_at,
messages: vec![LegacySerializedMessage {
id: MessageId(1),
role: Role::User,
text: "Hello, world!".to_string(),
tool_uses: vec![],
tool_results: vec![],
}],
initial_project_snapshot: None,
};
let upgraded = legacy_thread.upgrade();
assert_eq!(
upgraded,
SerializedThread {
summary: "Test conversation".into(),
updated_at,
messages: vec![SerializedMessage {
id: MessageId(1),
role: Role::User,
segments: vec![SerializedMessageSegment::Text {
text: "Hello, world!".to_string()
}],
tool_uses: vec![],
tool_results: vec![],
context: "".to_string(),
creases: vec![],
}],
version: SerializedThread::VERSION.to_string(),
initial_project_snapshot: None,
cumulative_token_usage: TokenUsage::default(),
request_token_usage: vec![],
detailed_summary_state: DetailedSummaryState::default(),
exceeded_window_error: None,
model: None,
completion_mode: None,
tool_use_limit_reached: false,
profile: None
}
)
}
#[test]
fn test_serialized_threadv0_1_0_upgrade() {
let updated_at = Utc::now();
let thread_v0_1_0 = SerializedThreadV0_1_0(SerializedThread {
summary: "Test conversation".into(),
updated_at,
messages: vec![
SerializedMessage {
id: MessageId(1),
role: Role::User,
segments: vec![SerializedMessageSegment::Text {
text: "Use tool_1".to_string(),
}],
tool_uses: vec![],
tool_results: vec![],
context: "".to_string(),
creases: vec![],
},
SerializedMessage {
id: MessageId(2),
role: Role::Assistant,
segments: vec![SerializedMessageSegment::Text {
text: "I want to use a tool".to_string(),
}],
tool_uses: vec![SerializedToolUse {
id: "abc".into(),
name: "tool_1".into(),
input: serde_json::Value::Null,
}],
tool_results: vec![],
context: "".to_string(),
creases: vec![],
},
SerializedMessage {
id: MessageId(1),
role: Role::User,
segments: vec![SerializedMessageSegment::Text {
text: "Here is the tool result".to_string(),
}],
tool_uses: vec![],
tool_results: vec![SerializedToolResult {
tool_use_id: "abc".into(),
is_error: false,
content: LanguageModelToolResultContent::Text("abcdef".into()),
output: Some(serde_json::Value::Null),
}],
context: "".to_string(),
creases: vec![],
},
],
version: SerializedThreadV0_1_0::VERSION.to_string(),
initial_project_snapshot: None,
cumulative_token_usage: TokenUsage::default(),
request_token_usage: vec![],
detailed_summary_state: DetailedSummaryState::default(),
exceeded_window_error: None,
model: None,
completion_mode: None,
tool_use_limit_reached: false,
profile: None,
});
let upgraded = thread_v0_1_0.upgrade();
assert_eq!(
upgraded,
SerializedThread {
summary: "Test conversation".into(),
updated_at,
messages: vec![
SerializedMessage {
id: MessageId(1),
role: Role::User,
segments: vec![SerializedMessageSegment::Text {
text: "Use tool_1".to_string()
}],
tool_uses: vec![],
tool_results: vec![],
context: "".to_string(),
creases: vec![],
},
SerializedMessage {
id: MessageId(2),
role: Role::Assistant,
segments: vec![SerializedMessageSegment::Text {
text: "I want to use a tool".to_string(),
}],
tool_uses: vec![SerializedToolUse {
id: "abc".into(),
name: "tool_1".into(),
input: serde_json::Value::Null,
}],
tool_results: vec![SerializedToolResult {
tool_use_id: "abc".into(),
is_error: false,
content: LanguageModelToolResultContent::Text("abcdef".into()),
output: Some(serde_json::Value::Null),
}],
context: "".to_string(),
creases: vec![],
},
],
version: SerializedThread::VERSION.to_string(),
initial_project_snapshot: None,
cumulative_token_usage: TokenUsage::default(),
request_token_usage: vec![],
detailed_summary_state: DetailedSummaryState::default(),
exceeded_window_error: None,
model: None,
completion_mode: None,
tool_use_limit_reached: false,
profile: None
}
)
}
}

View File

@@ -1,560 +0,0 @@
use std::sync::Arc;
use anyhow::Result;
use assistant_tool::{
AnyToolCard, Tool, ToolResultContent, ToolResultOutput, ToolUseStatus, ToolWorkingSet,
};
use collections::HashMap;
use futures::FutureExt as _;
use futures::future::Shared;
use gpui::{App, Entity, SharedString, Task};
use language_model::{
ConfiguredModel, LanguageModel, LanguageModelRequest, LanguageModelToolResult,
LanguageModelToolResultContent, LanguageModelToolUse, LanguageModelToolUseId, Role,
};
use project::Project;
use ui::{IconName, Window};
use util::truncate_lines_to_byte_limit;
use crate::thread::{MessageId, PromptId, ThreadId};
use crate::thread_store::SerializedMessage;
#[derive(Debug)]
pub struct ToolUse {
pub id: LanguageModelToolUseId,
pub name: SharedString,
pub ui_text: SharedString,
pub status: ToolUseStatus,
pub input: serde_json::Value,
pub icon: ui::IconName,
pub needs_confirmation: bool,
}
pub struct ToolUseState {
tools: Entity<ToolWorkingSet>,
tool_uses_by_assistant_message: HashMap<MessageId, Vec<LanguageModelToolUse>>,
tool_results: HashMap<LanguageModelToolUseId, LanguageModelToolResult>,
pending_tool_uses_by_id: HashMap<LanguageModelToolUseId, PendingToolUse>,
tool_result_cards: HashMap<LanguageModelToolUseId, AnyToolCard>,
tool_use_metadata_by_id: HashMap<LanguageModelToolUseId, ToolUseMetadata>,
}
impl ToolUseState {
pub fn new(tools: Entity<ToolWorkingSet>) -> Self {
Self {
tools,
tool_uses_by_assistant_message: HashMap::default(),
tool_results: HashMap::default(),
pending_tool_uses_by_id: HashMap::default(),
tool_result_cards: HashMap::default(),
tool_use_metadata_by_id: HashMap::default(),
}
}
/// Constructs a [`ToolUseState`] from the given list of [`SerializedMessage`]s.
///
/// Accepts a function to filter the tools that should be used to populate the state.
///
/// If `window` is `None` (e.g., when in headless mode or when running evals),
/// tool cards won't be deserialized
pub fn from_serialized_messages(
tools: Entity<ToolWorkingSet>,
messages: &[SerializedMessage],
project: Entity<Project>,
window: Option<&mut Window>, // None in headless mode
cx: &mut App,
) -> Self {
let mut this = Self::new(tools);
let mut tool_names_by_id = HashMap::default();
let mut window = window;
for message in messages {
match message.role {
Role::Assistant => {
if !message.tool_uses.is_empty() {
let tool_uses = message
.tool_uses
.iter()
.map(|tool_use| LanguageModelToolUse {
id: tool_use.id.clone(),
name: tool_use.name.clone().into(),
raw_input: tool_use.input.to_string(),
input: tool_use.input.clone(),
is_input_complete: true,
})
.collect::<Vec<_>>();
tool_names_by_id.extend(
tool_uses
.iter()
.map(|tool_use| (tool_use.id.clone(), tool_use.name.clone())),
);
this.tool_uses_by_assistant_message
.insert(message.id, tool_uses);
for tool_result in &message.tool_results {
let tool_use_id = tool_result.tool_use_id.clone();
let Some(tool_use) = tool_names_by_id.get(&tool_use_id) else {
log::warn!("no tool name found for tool use: {tool_use_id:?}");
continue;
};
this.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name: tool_use.clone(),
is_error: tool_result.is_error,
content: tool_result.content.clone(),
output: tool_result.output.clone(),
},
);
if let Some(window) = &mut window {
if let Some(tool) = this.tools.read(cx).tool(tool_use, cx) {
if let Some(output) = tool_result.output.clone() {
if let Some(card) = tool.deserialize_card(
output,
project.clone(),
window,
cx,
) {
this.tool_result_cards.insert(tool_use_id, card);
}
}
}
}
}
}
}
Role::System | Role::User => {}
}
}
this
}
pub fn cancel_pending(&mut self) -> Vec<PendingToolUse> {
let mut cancelled_tool_uses = Vec::new();
self.pending_tool_uses_by_id
.retain(|tool_use_id, tool_use| {
if matches!(tool_use.status, PendingToolUseStatus::Error { .. }) {
return true;
}
let content = "Tool canceled by user".into();
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name: tool_use.name.clone(),
content,
output: None,
is_error: true,
},
);
cancelled_tool_uses.push(tool_use.clone());
false
});
cancelled_tool_uses
}
pub fn pending_tool_uses(&self) -> Vec<&PendingToolUse> {
self.pending_tool_uses_by_id.values().collect()
}
pub fn tool_uses_for_message(&self, id: MessageId, cx: &App) -> Vec<ToolUse> {
let Some(tool_uses_for_message) = &self.tool_uses_by_assistant_message.get(&id) else {
return Vec::new();
};
let mut tool_uses = Vec::new();
for tool_use in tool_uses_for_message.iter() {
let tool_result = self.tool_results.get(&tool_use.id);
let status = (|| {
if let Some(tool_result) = tool_result {
let content = tool_result
.content
.to_str()
.map(|str| str.to_owned().into())
.unwrap_or_default();
return if tool_result.is_error {
ToolUseStatus::Error(content)
} else {
ToolUseStatus::Finished(content)
};
}
if let Some(pending_tool_use) = self.pending_tool_uses_by_id.get(&tool_use.id) {
match pending_tool_use.status {
PendingToolUseStatus::Idle => ToolUseStatus::Pending,
PendingToolUseStatus::NeedsConfirmation { .. } => {
ToolUseStatus::NeedsConfirmation
}
PendingToolUseStatus::Running { .. } => ToolUseStatus::Running,
PendingToolUseStatus::Error(ref err) => {
ToolUseStatus::Error(err.clone().into())
}
PendingToolUseStatus::InputStillStreaming => {
ToolUseStatus::InputStillStreaming
}
}
} else {
ToolUseStatus::Pending
}
})();
let (icon, needs_confirmation) =
if let Some(tool) = self.tools.read(cx).tool(&tool_use.name, cx) {
(tool.icon(), tool.needs_confirmation(&tool_use.input, cx))
} else {
(IconName::Cog, false)
};
tool_uses.push(ToolUse {
id: tool_use.id.clone(),
name: tool_use.name.clone().into(),
ui_text: self.tool_ui_label(
&tool_use.name,
&tool_use.input,
tool_use.is_input_complete,
cx,
),
input: tool_use.input.clone(),
status,
icon,
needs_confirmation,
})
}
tool_uses
}
pub fn tool_ui_label(
&self,
tool_name: &str,
input: &serde_json::Value,
is_input_complete: bool,
cx: &App,
) -> SharedString {
if let Some(tool) = self.tools.read(cx).tool(tool_name, cx) {
if is_input_complete {
tool.ui_text(input).into()
} else {
tool.still_streaming_ui_text(input).into()
}
} else {
format!("Unknown tool {tool_name:?}").into()
}
}
pub fn tool_results_for_message(
&self,
assistant_message_id: MessageId,
) -> Vec<&LanguageModelToolResult> {
let Some(tool_uses) = self
.tool_uses_by_assistant_message
.get(&assistant_message_id)
else {
return Vec::new();
};
tool_uses
.iter()
.filter_map(|tool_use| self.tool_results.get(&tool_use.id))
.collect()
}
pub fn message_has_tool_results(&self, assistant_message_id: MessageId) -> bool {
self.tool_uses_by_assistant_message
.get(&assistant_message_id)
.map_or(false, |results| !results.is_empty())
}
pub fn tool_result(
&self,
tool_use_id: &LanguageModelToolUseId,
) -> Option<&LanguageModelToolResult> {
self.tool_results.get(tool_use_id)
}
pub fn tool_result_card(&self, tool_use_id: &LanguageModelToolUseId) -> Option<&AnyToolCard> {
self.tool_result_cards.get(tool_use_id)
}
pub fn insert_tool_result_card(
&mut self,
tool_use_id: LanguageModelToolUseId,
card: AnyToolCard,
) {
self.tool_result_cards.insert(tool_use_id, card);
}
pub fn request_tool_use(
&mut self,
assistant_message_id: MessageId,
tool_use: LanguageModelToolUse,
metadata: ToolUseMetadata,
cx: &App,
) -> Arc<str> {
let tool_uses = self
.tool_uses_by_assistant_message
.entry(assistant_message_id)
.or_default();
let mut existing_tool_use_found = false;
for existing_tool_use in tool_uses.iter_mut() {
if existing_tool_use.id == tool_use.id {
*existing_tool_use = tool_use.clone();
existing_tool_use_found = true;
}
}
if !existing_tool_use_found {
tool_uses.push(tool_use.clone());
}
let status = if tool_use.is_input_complete {
self.tool_use_metadata_by_id
.insert(tool_use.id.clone(), metadata);
PendingToolUseStatus::Idle
} else {
PendingToolUseStatus::InputStillStreaming
};
let ui_text: Arc<str> = self
.tool_ui_label(
&tool_use.name,
&tool_use.input,
tool_use.is_input_complete,
cx,
)
.into();
self.pending_tool_uses_by_id.insert(
tool_use.id.clone(),
PendingToolUse {
assistant_message_id,
id: tool_use.id,
name: tool_use.name.clone(),
ui_text: ui_text.clone(),
input: tool_use.input,
status,
},
);
ui_text
}
pub fn run_pending_tool(
&mut self,
tool_use_id: LanguageModelToolUseId,
ui_text: SharedString,
task: Task<()>,
) {
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
tool_use.ui_text = ui_text.into();
tool_use.status = PendingToolUseStatus::Running {
_task: task.shared(),
};
}
}
pub fn confirm_tool_use(
&mut self,
tool_use_id: LanguageModelToolUseId,
ui_text: impl Into<Arc<str>>,
input: serde_json::Value,
request: Arc<LanguageModelRequest>,
tool: Arc<dyn Tool>,
) {
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
let ui_text = ui_text.into();
tool_use.ui_text = ui_text.clone();
let confirmation = Confirmation {
tool_use_id,
input,
request,
tool,
ui_text,
};
tool_use.status = PendingToolUseStatus::NeedsConfirmation(Arc::new(confirmation));
}
}
pub fn insert_tool_output(
&mut self,
tool_use_id: LanguageModelToolUseId,
tool_name: Arc<str>,
output: Result<ToolResultOutput>,
configured_model: Option<&ConfiguredModel>,
) -> Option<PendingToolUse> {
let metadata = self.tool_use_metadata_by_id.remove(&tool_use_id);
telemetry::event!(
"Agent Tool Finished",
model = metadata
.as_ref()
.map(|metadata| metadata.model.telemetry_id()),
model_provider = metadata
.as_ref()
.map(|metadata| metadata.model.provider_id().to_string()),
thread_id = metadata.as_ref().map(|metadata| metadata.thread_id.clone()),
prompt_id = metadata.as_ref().map(|metadata| metadata.prompt_id.clone()),
tool_name,
success = output.is_ok()
);
match output {
Ok(output) => {
let tool_result = output.content;
const BYTES_PER_TOKEN_ESTIMATE: usize = 3;
let old_use = self.pending_tool_uses_by_id.remove(&tool_use_id);
// Protect from overly large output
let tool_output_limit = configured_model
.map(|model| model.model.max_token_count() * BYTES_PER_TOKEN_ESTIMATE)
.unwrap_or(usize::MAX);
let content = match tool_result {
ToolResultContent::Text(text) => {
let text = if text.len() < tool_output_limit {
text
} else {
let truncated = truncate_lines_to_byte_limit(&text, tool_output_limit);
format!(
"Tool result too long. The first {} bytes:\n\n{}",
truncated.len(),
truncated
)
};
LanguageModelToolResultContent::Text(text.into())
}
ToolResultContent::Image(language_model_image) => {
if language_model_image.estimate_tokens() < tool_output_limit {
LanguageModelToolResultContent::Image(language_model_image)
} else {
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name,
content: "Tool responded with an image that would exceeded the remaining tokens".into(),
is_error: true,
output: None,
},
);
return old_use;
}
}
};
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name,
content,
is_error: false,
output: output.output,
},
);
old_use
}
Err(err) => {
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name,
content: LanguageModelToolResultContent::Text(err.to_string().into()),
is_error: true,
output: None,
},
);
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
tool_use.status = PendingToolUseStatus::Error(err.to_string().into());
}
self.pending_tool_uses_by_id.get(&tool_use_id).cloned()
}
}
}
pub fn has_tool_results(&self, assistant_message_id: MessageId) -> bool {
self.tool_uses_by_assistant_message
.contains_key(&assistant_message_id)
}
pub fn tool_results(
&self,
assistant_message_id: MessageId,
) -> impl Iterator<Item = (&LanguageModelToolUse, Option<&LanguageModelToolResult>)> {
self.tool_uses_by_assistant_message
.get(&assistant_message_id)
.into_iter()
.flatten()
.map(|tool_use| (tool_use, self.tool_results.get(&tool_use.id)))
}
}
#[derive(Debug, Clone)]
pub struct PendingToolUse {
pub id: LanguageModelToolUseId,
/// The ID of the Assistant message in which the tool use was requested.
#[allow(unused)]
pub assistant_message_id: MessageId,
pub name: Arc<str>,
pub ui_text: Arc<str>,
pub input: serde_json::Value,
pub status: PendingToolUseStatus,
}
#[derive(Debug, Clone)]
pub struct Confirmation {
pub tool_use_id: LanguageModelToolUseId,
pub input: serde_json::Value,
pub ui_text: Arc<str>,
pub request: Arc<LanguageModelRequest>,
pub tool: Arc<dyn Tool>,
}
#[derive(Debug, Clone)]
pub enum PendingToolUseStatus {
InputStillStreaming,
Idle,
NeedsConfirmation(Arc<Confirmation>),
Running { _task: Shared<Task<()>> },
Error(#[allow(unused)] Arc<str>),
}
impl PendingToolUseStatus {
pub fn is_idle(&self) -> bool {
matches!(self, PendingToolUseStatus::Idle)
}
pub fn is_error(&self) -> bool {
matches!(self, PendingToolUseStatus::Error(_))
}
pub fn needs_confirmation(&self) -> bool {
matches!(self, PendingToolUseStatus::NeedsConfirmation { .. })
}
}
#[derive(Clone)]
pub struct ToolUseMetadata {
pub model: Arc<dyn LanguageModel>,
pub thread_id: ThreadId,
pub prompt_id: PromptId,
}

View File

@@ -12,18 +12,10 @@ workspace = true
path = "src/agent_settings.rs"
[dependencies]
anthropic = { workspace = true, features = ["schemars"] }
anyhow.workspace = true
collections.workspace = true
gpui.workspace = true
indexmap.workspace = true
language_model.workspace = true
lmstudio = { workspace = true, features = ["schemars"] }
log.workspace = true
ollama = { workspace = true, features = ["schemars"] }
open_ai = { workspace = true, features = ["schemars"] }
deepseek = { workspace = true, features = ["schemars"] }
mistral = { workspace = true, features = ["schemars"] }
schemars.workspace = true
serde.workspace = true
settings.workspace = true

View File

@@ -17,29 +17,6 @@ pub mod builtin_profiles {
}
}
#[derive(Default)]
pub struct GroupedAgentProfiles {
pub builtin: IndexMap<AgentProfileId, AgentProfile>,
pub custom: IndexMap<AgentProfileId, AgentProfile>,
}
impl GroupedAgentProfiles {
pub fn from_settings(settings: &crate::AgentSettings) -> Self {
let mut builtin = IndexMap::default();
let mut custom = IndexMap::default();
for (profile_id, profile) in settings.profiles.clone() {
if builtin_profiles::is_builtin(&profile_id) {
builtin.insert(profile_id, profile);
} else {
custom.insert(profile_id, profile);
}
}
Self { builtin, custom }
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AgentProfileId(pub Arc<str>);
@@ -63,7 +40,7 @@ impl Default for AgentProfileId {
/// A profile for the Zed Agent that controls its behavior.
#[derive(Debug, Clone)]
pub struct AgentProfile {
pub struct AgentProfileSettings {
/// The name of the profile.
pub name: SharedString,
pub tools: IndexMap<Arc<str>, bool>,

View File

@@ -2,16 +2,10 @@ mod agent_profile;
use std::sync::Arc;
use ::open_ai::Model as OpenAiModel;
use anthropic::Model as AnthropicModel;
use anyhow::{Result, bail};
use collections::IndexMap;
use deepseek::Model as DeepseekModel;
use gpui::{App, Pixels, SharedString};
use language_model::LanguageModel;
use lmstudio::Model as LmStudioModel;
use mistral::Model as MistralModel;
use ollama::Model as OllamaModel;
use schemars::{JsonSchema, schema::Schema};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
@@ -48,45 +42,6 @@ pub enum NotifyWhenAgentWaiting {
Never,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
#[serde(tag = "name", rename_all = "snake_case")]
#[schemars(deny_unknown_fields)]
pub enum AgentProviderContentV1 {
#[serde(rename = "zed.dev")]
ZedDotDev { default_model: Option<String> },
#[serde(rename = "openai")]
OpenAi {
default_model: Option<OpenAiModel>,
api_url: Option<String>,
available_models: Option<Vec<OpenAiModel>>,
},
#[serde(rename = "anthropic")]
Anthropic {
default_model: Option<AnthropicModel>,
api_url: Option<String>,
},
#[serde(rename = "ollama")]
Ollama {
default_model: Option<OllamaModel>,
api_url: Option<String>,
},
#[serde(rename = "lmstudio")]
LmStudio {
default_model: Option<LmStudioModel>,
api_url: Option<String>,
},
#[serde(rename = "deepseek")]
DeepSeek {
default_model: Option<DeepseekModel>,
api_url: Option<String>,
},
#[serde(rename = "mistral")]
Mistral {
default_model: Option<MistralModel>,
api_url: Option<String>,
},
}
#[derive(Default, Clone, Debug)]
pub struct AgentSettings {
pub enabled: bool,
@@ -102,7 +57,7 @@ pub struct AgentSettings {
pub using_outdated_settings_version: bool,
pub default_profile: AgentProfileId,
pub default_view: DefaultView,
pub profiles: IndexMap<AgentProfileId, AgentProfile>,
pub profiles: IndexMap<AgentProfileId, AgentProfileSettings>,
pub always_allow_tool_actions: bool,
pub notify_when_agent_waiting: NotifyWhenAgentWaiting,
pub play_sound_when_agent_done: bool,
@@ -168,444 +123,96 @@ impl LanguageModelParameters {
}
}
/// Agent panel settings
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
pub struct AgentSettingsContent {
#[serde(flatten)]
pub inner: Option<AgentSettingsContentInner>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum AgentSettingsContentInner {
Versioned(Box<VersionedAgentSettingsContent>),
Legacy(LegacyAgentSettingsContent),
}
impl AgentSettingsContentInner {
fn for_v2(content: AgentSettingsContentV2) -> Self {
AgentSettingsContentInner::Versioned(Box::new(VersionedAgentSettingsContent::V2(content)))
}
}
impl JsonSchema for AgentSettingsContent {
fn schema_name() -> String {
VersionedAgentSettingsContent::schema_name()
}
fn json_schema(r#gen: &mut schemars::r#gen::SchemaGenerator) -> Schema {
VersionedAgentSettingsContent::json_schema(r#gen)
}
fn is_referenceable() -> bool {
VersionedAgentSettingsContent::is_referenceable()
}
}
impl AgentSettingsContent {
pub fn is_version_outdated(&self) -> bool {
match &self.inner {
Some(AgentSettingsContentInner::Versioned(settings)) => match **settings {
VersionedAgentSettingsContent::V1(_) => true,
VersionedAgentSettingsContent::V2(_) => false,
},
Some(AgentSettingsContentInner::Legacy(_)) => true,
None => false,
}
}
fn upgrade(&self) -> AgentSettingsContentV2 {
match &self.inner {
Some(AgentSettingsContentInner::Versioned(settings)) => match **settings {
VersionedAgentSettingsContent::V1(ref settings) => AgentSettingsContentV2 {
enabled: settings.enabled,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_width,
default_model: settings
.provider
.clone()
.and_then(|provider| match provider {
AgentProviderContentV1::ZedDotDev { default_model } => default_model
.map(|model| LanguageModelSelection {
provider: "zed.dev".into(),
model,
}),
AgentProviderContentV1::OpenAi { default_model, .. } => default_model
.map(|model| LanguageModelSelection {
provider: "openai".into(),
model: model.id().to_string(),
}),
AgentProviderContentV1::Anthropic { default_model, .. } => {
default_model.map(|model| LanguageModelSelection {
provider: "anthropic".into(),
model: model.id().to_string(),
})
}
AgentProviderContentV1::Ollama { default_model, .. } => default_model
.map(|model| LanguageModelSelection {
provider: "ollama".into(),
model: model.id().to_string(),
}),
AgentProviderContentV1::LmStudio { default_model, .. } => default_model
.map(|model| LanguageModelSelection {
provider: "lmstudio".into(),
model: model.id().to_string(),
}),
AgentProviderContentV1::DeepSeek { default_model, .. } => default_model
.map(|model| LanguageModelSelection {
provider: "deepseek".into(),
model: model.id().to_string(),
}),
AgentProviderContentV1::Mistral { default_model, .. } => default_model
.map(|model| LanguageModelSelection {
provider: "mistral".into(),
model: model.id().to_string(),
}),
}),
inline_assistant_model: None,
commit_message_model: None,
thread_summary_model: None,
inline_alternatives: None,
default_profile: None,
default_view: None,
profiles: None,
always_allow_tool_actions: None,
notify_when_agent_waiting: None,
stream_edits: None,
single_file_review: None,
model_parameters: Vec::new(),
preferred_completion_mode: None,
enable_feedback: None,
play_sound_when_agent_done: None,
},
VersionedAgentSettingsContent::V2(ref settings) => settings.clone(),
},
Some(AgentSettingsContentInner::Legacy(settings)) => AgentSettingsContentV2 {
enabled: None,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_height,
default_model: Some(LanguageModelSelection {
provider: "openai".into(),
model: settings
.default_open_ai_model
.clone()
.unwrap_or_default()
.id()
.to_string(),
}),
inline_assistant_model: None,
commit_message_model: None,
thread_summary_model: None,
inline_alternatives: None,
default_profile: None,
default_view: None,
profiles: None,
always_allow_tool_actions: None,
notify_when_agent_waiting: None,
stream_edits: None,
single_file_review: None,
model_parameters: Vec::new(),
preferred_completion_mode: None,
enable_feedback: None,
play_sound_when_agent_done: None,
},
None => AgentSettingsContentV2::default(),
}
}
pub fn set_dock(&mut self, dock: AgentDockPosition) {
match &mut self.inner {
Some(AgentSettingsContentInner::Versioned(settings)) => match **settings {
VersionedAgentSettingsContent::V1(ref mut settings) => {
settings.dock = Some(dock);
}
VersionedAgentSettingsContent::V2(ref mut settings) => {
settings.dock = Some(dock);
}
},
Some(AgentSettingsContentInner::Legacy(settings)) => {
settings.dock = Some(dock);
}
None => {
self.inner = Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
dock: Some(dock),
..Default::default()
}))
}
}
self.dock = Some(dock);
}
pub fn set_model(&mut self, language_model: Arc<dyn LanguageModel>) {
let model = language_model.id().0.to_string();
let provider = language_model.provider_id().0.to_string();
match &mut self.inner {
Some(AgentSettingsContentInner::Versioned(settings)) => match **settings {
VersionedAgentSettingsContent::V1(ref mut settings) => match provider.as_ref() {
"zed.dev" => {
log::warn!("attempted to set zed.dev model on outdated settings");
}
"anthropic" => {
let api_url = match &settings.provider {
Some(AgentProviderContentV1::Anthropic { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AgentProviderContentV1::Anthropic {
default_model: AnthropicModel::from_id(&model).ok(),
api_url,
});
}
"ollama" => {
let api_url = match &settings.provider {
Some(AgentProviderContentV1::Ollama { api_url, .. }) => api_url.clone(),
_ => None,
};
settings.provider = Some(AgentProviderContentV1::Ollama {
default_model: Some(ollama::Model::new(
&model,
None,
None,
Some(language_model.supports_tools()),
Some(language_model.supports_images()),
None,
)),
api_url,
});
}
"lmstudio" => {
let api_url = match &settings.provider {
Some(AgentProviderContentV1::LmStudio { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AgentProviderContentV1::LmStudio {
default_model: Some(lmstudio::Model::new(&model, None, None, false)),
api_url,
});
}
"openai" => {
let (api_url, available_models) = match &settings.provider {
Some(AgentProviderContentV1::OpenAi {
api_url,
available_models,
..
}) => (api_url.clone(), available_models.clone()),
_ => (None, None),
};
settings.provider = Some(AgentProviderContentV1::OpenAi {
default_model: OpenAiModel::from_id(&model).ok(),
api_url,
available_models,
});
}
"deepseek" => {
let api_url = match &settings.provider {
Some(AgentProviderContentV1::DeepSeek { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AgentProviderContentV1::DeepSeek {
default_model: DeepseekModel::from_id(&model).ok(),
api_url,
});
}
_ => {}
},
VersionedAgentSettingsContent::V2(ref mut settings) => {
settings.default_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
},
Some(AgentSettingsContentInner::Legacy(settings)) => {
if let Ok(model) = OpenAiModel::from_id(&language_model.id().0) {
settings.default_open_ai_model = Some(model);
}
}
None => {
self.inner = Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
default_model: Some(LanguageModelSelection {
provider: provider.into(),
model,
}),
..Default::default()
}));
}
}
self.default_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
self.v2_setting(|setting| {
setting.inline_assistant_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
Ok(())
})
.ok();
self.inline_assistant_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_commit_message_model(&mut self, provider: String, model: String) {
self.v2_setting(|setting| {
setting.commit_message_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
Ok(())
})
.ok();
}
pub fn v2_setting(
&mut self,
f: impl FnOnce(&mut AgentSettingsContentV2) -> anyhow::Result<()>,
) -> anyhow::Result<()> {
match self.inner.get_or_insert_with(|| {
AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
..Default::default()
})
}) {
AgentSettingsContentInner::Versioned(boxed) => {
if let VersionedAgentSettingsContent::V2(ref mut settings) = **boxed {
f(settings)
} else {
Ok(())
}
}
_ => Ok(()),
}
self.commit_message_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_thread_summary_model(&mut self, provider: String, model: String) {
self.v2_setting(|setting| {
setting.thread_summary_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
Ok(())
})
.ok();
self.thread_summary_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_always_allow_tool_actions(&mut self, allow: bool) {
self.v2_setting(|setting| {
setting.always_allow_tool_actions = Some(allow);
Ok(())
})
.ok();
self.always_allow_tool_actions = Some(allow);
}
pub fn set_play_sound_when_agent_done(&mut self, allow: bool) {
self.v2_setting(|setting| {
setting.play_sound_when_agent_done = Some(allow);
Ok(())
})
.ok();
self.play_sound_when_agent_done = Some(allow);
}
pub fn set_single_file_review(&mut self, allow: bool) {
self.v2_setting(|setting| {
setting.single_file_review = Some(allow);
Ok(())
})
.ok();
self.single_file_review = Some(allow);
}
pub fn set_profile(&mut self, profile_id: AgentProfileId) {
self.v2_setting(|setting| {
setting.default_profile = Some(profile_id);
Ok(())
})
.ok();
self.default_profile = Some(profile_id);
}
pub fn create_profile(
&mut self,
profile_id: AgentProfileId,
profile: AgentProfile,
profile_settings: AgentProfileSettings,
) -> Result<()> {
self.v2_setting(|settings| {
let profiles = settings.profiles.get_or_insert_default();
if profiles.contains_key(&profile_id) {
bail!("profile with ID '{profile_id}' already exists");
}
let profiles = self.profiles.get_or_insert_default();
if profiles.contains_key(&profile_id) {
bail!("profile with ID '{profile_id}' already exists");
}
profiles.insert(
profile_id,
AgentProfileContent {
name: profile.name.into(),
tools: profile.tools,
enable_all_context_servers: Some(profile.enable_all_context_servers),
context_servers: profile
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
},
);
profiles.insert(
profile_id,
AgentProfileContent {
name: profile_settings.name.into(),
tools: profile_settings.tools,
enable_all_context_servers: Some(profile_settings.enable_all_context_servers),
context_servers: profile_settings
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
},
);
Ok(())
})
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
#[serde(tag = "version")]
#[schemars(deny_unknown_fields)]
pub enum VersionedAgentSettingsContent {
#[serde(rename = "1")]
V1(AgentSettingsContentV1),
#[serde(rename = "2")]
V2(AgentSettingsContentV2),
}
impl Default for VersionedAgentSettingsContent {
fn default() -> Self {
Self::V2(AgentSettingsContentV2 {
enabled: None,
button: None,
dock: None,
default_width: None,
default_height: None,
default_model: None,
inline_assistant_model: None,
commit_message_model: None,
thread_summary_model: None,
inline_alternatives: None,
default_profile: None,
default_view: None,
profiles: None,
always_allow_tool_actions: None,
notify_when_agent_waiting: None,
stream_edits: None,
single_file_review: None,
model_parameters: Vec::new(),
preferred_completion_mode: None,
enable_feedback: None,
play_sound_when_agent_done: None,
})
Ok(())
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, Default)]
#[schemars(deny_unknown_fields)]
pub struct AgentSettingsContentV2 {
pub struct AgentSettingsContent {
/// Whether the Agent is enabled.
///
/// Default: true
@@ -730,7 +337,9 @@ impl JsonSchema for LanguageModelProviderSetting {
"zed.dev".into(),
"copilot_chat".into(),
"deepseek".into(),
"openrouter".into(),
"mistral".into(),
"vercel".into(),
]),
..Default::default()
}
@@ -775,65 +384,6 @@ pub struct ContextServerPresetContent {
pub tools: IndexMap<Arc<str>, bool>,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
#[schemars(deny_unknown_fields)]
pub struct AgentSettingsContentV1 {
/// Whether the Agent is enabled.
///
/// Default: true
enabled: Option<bool>,
/// Whether to show the Agent panel button in the status bar.
///
/// Default: true
button: Option<bool>,
/// Where to dock the Agent.
///
/// Default: right
dock: Option<AgentDockPosition>,
/// Default width in pixels when the Agent is docked to the left or right.
///
/// Default: 640
default_width: Option<f32>,
/// Default height in pixels when the Agent is docked to the bottom.
///
/// Default: 320
default_height: Option<f32>,
/// The provider of the Agent service.
///
/// This can be "openai", "anthropic", "ollama", "lmstudio", "deepseek", "zed.dev"
/// each with their respective default models and configurations.
provider: Option<AgentProviderContentV1>,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
#[schemars(deny_unknown_fields)]
pub struct LegacyAgentSettingsContent {
/// Whether to show the Agent panel button in the status bar.
///
/// Default: true
pub button: Option<bool>,
/// Where to dock the Agent.
///
/// Default: right
pub dock: Option<AgentDockPosition>,
/// Default width in pixels when the Agent is docked to the left or right.
///
/// Default: 640
pub default_width: Option<f32>,
/// Default height in pixels when the Agent is docked to the bottom.
///
/// Default: 320
pub default_height: Option<f32>,
/// The default OpenAI model to use when creating new chats.
///
/// Default: gpt-4-1106-preview
pub default_open_ai_model: Option<OpenAiModel>,
/// OpenAI API base URL to use when creating new chats.
///
/// Default: <https://api.openai.com/v1>
pub openai_api_url: Option<String>,
}
impl Settings for AgentSettings {
const KEY: Option<&'static str> = Some("agent");
@@ -850,11 +400,6 @@ impl Settings for AgentSettings {
let mut settings = AgentSettings::default();
for value in sources.defaults_and_customizations() {
if value.is_version_outdated() {
settings.using_outdated_settings_version = true;
}
let value = value.upgrade();
merge(&mut settings.enabled, value.enabled);
merge(&mut settings.button, value.button);
merge(&mut settings.dock, value.dock);
@@ -866,17 +411,23 @@ impl Settings for AgentSettings {
&mut settings.default_height,
value.default_height.map(Into::into),
);
merge(&mut settings.default_model, value.default_model);
merge(&mut settings.default_model, value.default_model.clone());
settings.inline_assistant_model = value
.inline_assistant_model
.clone()
.or(settings.inline_assistant_model.take());
settings.commit_message_model = value
.clone()
.commit_message_model
.or(settings.commit_message_model.take());
settings.thread_summary_model = value
.clone()
.thread_summary_model
.or(settings.thread_summary_model.take());
merge(&mut settings.inline_alternatives, value.inline_alternatives);
merge(
&mut settings.inline_alternatives,
value.inline_alternatives.clone(),
);
merge(
&mut settings.always_allow_tool_actions,
value.always_allow_tool_actions,
@@ -891,7 +442,7 @@ impl Settings for AgentSettings {
);
merge(&mut settings.stream_edits, value.stream_edits);
merge(&mut settings.single_file_review, value.single_file_review);
merge(&mut settings.default_profile, value.default_profile);
merge(&mut settings.default_profile, value.default_profile.clone());
merge(&mut settings.default_view, value.default_view);
merge(
&mut settings.preferred_completion_mode,
@@ -903,24 +454,24 @@ impl Settings for AgentSettings {
.model_parameters
.extend_from_slice(&value.model_parameters);
if let Some(profiles) = value.profiles {
if let Some(profiles) = value.profiles.as_ref() {
settings
.profiles
.extend(profiles.into_iter().map(|(id, profile)| {
(
id,
AgentProfile {
name: profile.name.into(),
tools: profile.tools,
id.clone(),
AgentProfileSettings {
name: profile.name.clone().into(),
tools: profile.tools.clone(),
enable_all_context_servers: profile
.enable_all_context_servers
.unwrap_or_default(),
context_servers: profile
.context_servers
.into_iter()
.iter()
.map(|(context_server_id, preset)| {
(
context_server_id,
context_server_id.clone(),
ContextServerPreset {
tools: preset.tools.clone(),
},
@@ -941,28 +492,8 @@ impl Settings for AgentSettings {
.read_value("chat.agent.enabled")
.and_then(|b| b.as_bool())
{
match &mut current.inner {
Some(AgentSettingsContentInner::Versioned(versioned)) => match versioned.as_mut() {
VersionedAgentSettingsContent::V1(setting) => {
setting.enabled = Some(b);
setting.button = Some(b);
}
VersionedAgentSettingsContent::V2(setting) => {
setting.enabled = Some(b);
setting.button = Some(b);
}
},
Some(AgentSettingsContentInner::Legacy(setting)) => setting.button = Some(b),
None => {
current.inner =
Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
enabled: Some(b),
button: Some(b),
..Default::default()
}));
}
}
current.enabled = Some(b);
current.button = Some(b);
}
}
}
@@ -972,149 +503,3 @@ fn merge<T>(target: &mut T, value: Option<T>) {
*target = value;
}
}
#[cfg(test)]
mod tests {
use fs::Fs;
use gpui::{ReadGlobal, TestAppContext};
use settings::SettingsStore;
use super::*;
#[gpui::test]
async fn test_deserialize_agent_settings_with_version(cx: &mut TestAppContext) {
let fs = fs::FakeFs::new(cx.executor().clone());
fs.create_dir(paths::settings_file().parent().unwrap())
.await
.unwrap();
cx.update(|cx| {
let test_settings = settings::SettingsStore::test(cx);
cx.set_global(test_settings);
AgentSettings::register(cx);
});
cx.update(|cx| {
assert!(!AgentSettings::get_global(cx).using_outdated_settings_version);
assert_eq!(
AgentSettings::get_global(cx).default_model,
LanguageModelSelection {
provider: "zed.dev".into(),
model: "claude-sonnet-4".into(),
}
);
});
cx.update(|cx| {
settings::SettingsStore::global(cx).update_settings_file::<AgentSettings>(
fs.clone(),
|settings, _| {
*settings = AgentSettingsContent {
inner: Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
default_model: Some(LanguageModelSelection {
provider: "test-provider".into(),
model: "gpt-99".into(),
}),
inline_assistant_model: None,
commit_message_model: None,
thread_summary_model: None,
inline_alternatives: None,
enabled: None,
button: None,
dock: None,
default_width: None,
default_height: None,
default_profile: None,
default_view: None,
profiles: None,
always_allow_tool_actions: None,
play_sound_when_agent_done: None,
notify_when_agent_waiting: None,
stream_edits: None,
single_file_review: None,
enable_feedback: None,
model_parameters: Vec::new(),
preferred_completion_mode: None,
})),
}
},
);
});
cx.run_until_parked();
let raw_settings_value = fs.load(paths::settings_file()).await.unwrap();
assert!(raw_settings_value.contains(r#""version": "2""#));
#[derive(Debug, Deserialize)]
struct AgentSettingsTest {
agent: AgentSettingsContent,
}
let agent_settings: AgentSettingsTest =
serde_json_lenient::from_str(&raw_settings_value).unwrap();
assert!(!agent_settings.agent.is_version_outdated());
}
#[gpui::test]
async fn test_load_settings_from_old_key(cx: &mut TestAppContext) {
let fs = fs::FakeFs::new(cx.executor().clone());
fs.create_dir(paths::settings_file().parent().unwrap())
.await
.unwrap();
cx.update(|cx| {
let mut test_settings = settings::SettingsStore::test(cx);
let user_settings_content = r#"{
"assistant": {
"enabled": true,
"version": "2",
"default_model": {
"provider": "zed.dev",
"model": "gpt-99"
},
}}"#;
test_settings
.set_user_settings(user_settings_content, cx)
.unwrap();
cx.set_global(test_settings);
AgentSettings::register(cx);
});
cx.run_until_parked();
let agent_settings = cx.update(|cx| AgentSettings::get_global(cx).clone());
assert!(agent_settings.enabled);
assert!(!agent_settings.using_outdated_settings_version);
assert_eq!(agent_settings.default_model.model, "gpt-99");
cx.update_global::<SettingsStore, _>(|settings_store, cx| {
settings_store.update_user_settings::<AgentSettings>(cx, |settings| {
*settings = AgentSettingsContent {
inner: Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
enabled: Some(false),
default_model: Some(LanguageModelSelection {
provider: "xai".to_owned().into(),
model: "grok".to_owned(),
}),
..Default::default()
})),
};
});
});
cx.run_until_parked();
let settings = cx.update(|cx| SettingsStore::global(cx).raw_user_settings().clone());
#[derive(Debug, Deserialize)]
struct AgentSettingsTest {
assistant: AgentSettingsContent,
agent: Option<serde_json_lenient::Value>,
}
let agent_settings: AgentSettingsTest = serde_json::from_value(settings).unwrap();
assert!(agent_settings.agent.is_none());
}
}

111
crates/agent_ui/Cargo.toml Normal file
View File

@@ -0,0 +1,111 @@
[package]
name = "agent_ui"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/agent_ui.rs"
doctest = false
[features]
test-support = [
"gpui/test-support",
"language/test-support",
]
[dependencies]
agent.workspace = true
agent_settings.workspace = true
anyhow.workspace = true
assistant_context.workspace = true
assistant_slash_command.workspace = true
assistant_slash_commands.workspace = true
assistant_tool.workspace = true
audio.workspace = true
buffer_diff.workspace = true
chrono.workspace = true
client.workspace = true
collections.workspace = true
component.workspace = true
context_server.workspace = true
db.workspace = true
editor.workspace = true
extension.workspace = true
extension_host.workspace = true
feature_flags.workspace = true
file_icons.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
html_to_markdown.workspace = true
indoc.workspace = true
http_client.workspace = true
indexed_docs.workspace = true
inventory.workspace = true
itertools.workspace = true
jsonschema.workspace = true
language.workspace = true
language_model.workspace = true
log.workspace = true
lsp.workspace = true
markdown.workspace = true
menu.workspace = true
multi_buffer.workspace = true
notifications.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
project.workspace = true
prompt_store.workspace = true
proto.workspace = true
release_channel.workspace = true
rope.workspace = true
rules_library.workspace = true
schemars.workspace = true
search.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
settings.workspace = true
smol.workspace = true
streaming_diff.workspace = true
telemetry.workspace = true
telemetry_events.workspace = true
terminal.workspace = true
terminal_view.workspace = true
text.workspace = true
theme.workspace = true
time.workspace = true
time_format.workspace = true
ui.workspace = true
urlencoding.workspace = true
util.workspace = true
uuid.workspace = true
watch.workspace = true
workspace-hack.workspace = true
workspace.workspace = true
zed_actions.workspace = true
zed_llm_client.workspace = true
[dev-dependencies]
assistant_tools.workspace = true
assistant_tool = { workspace = true, "features" = ["test-support"] }
buffer_diff = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, "features" = ["test-support"] }
indoc.workspace = true
language = { workspace = true, "features" = ["test-support"] }
languages = { workspace = true, features = ["test-support"] }
language_model = { workspace = true, "features" = ["test-support"] }
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
rand.workspace = true
tree-sitter-md.workspace = true
unindent.workspace = true

View File

@@ -1,4 +1,3 @@
mod add_context_server_modal;
mod configure_context_server_modal;
mod manage_profiles_modal;
mod tool_picker;
@@ -9,22 +8,29 @@ use agent_settings::AgentSettings;
use assistant_tool::{ToolSource, ToolWorkingSet};
use collections::HashMap;
use context_server::ContextServerId;
use extension::ExtensionManifest;
use extension_host::ExtensionStore;
use fs::Fs;
use gpui::{
Action, Animation, AnimationExt as _, AnyView, App, Entity, EventEmitter, FocusHandle,
Focusable, ScrollHandle, Subscription, pulsating_between,
Action, Animation, AnimationExt as _, AnyView, App, Corner, Entity, EventEmitter, FocusHandle,
Focusable, ScrollHandle, Subscription, Task, Transformation, WeakEntity, percentage,
};
use language::LanguageRegistry;
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
use project::context_server_store::{ContextServerStatus, ContextServerStore};
use notifications::status_toast::{StatusToast, ToastIcon};
use project::{
context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},
project_settings::{ContextServerSettings, ProjectSettings},
};
use settings::{Settings, update_settings_file};
use ui::{
Disclosure, ElevationIndex, Indicator, Scrollbar, ScrollbarState, Switch, SwitchColor, Tooltip,
prelude::*,
ContextMenu, Disclosure, ElevationIndex, Indicator, PopoverMenu, Scrollbar, ScrollbarState,
Switch, SwitchColor, Tooltip, prelude::*,
};
use util::ResultExt as _;
use workspace::Workspace;
use zed_actions::ExtensionCategoryFilter;
pub(crate) use add_context_server_modal::AddContextServerModal;
pub(crate) use configure_context_server_modal::ConfigureContextServerModal;
pub(crate) use manage_profiles_modal::ManageProfilesModal;
@@ -32,6 +38,8 @@ use crate::AddContextServer;
pub struct AgentConfiguration {
fs: Arc<dyn Fs>,
language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>,
focus_handle: FocusHandle,
configuration_views_by_provider: HashMap<LanguageModelProviderId, AnyView>,
context_server_store: Entity<ContextServerStore>,
@@ -48,6 +56,8 @@ impl AgentConfiguration {
fs: Arc<dyn Fs>,
context_server_store: Entity<ContextServerStore>,
tools: Entity<ToolWorkingSet>,
language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@@ -70,11 +80,16 @@ impl AgentConfiguration {
},
);
cx.subscribe(&context_server_store, |_, _, _, cx| cx.notify())
.detach();
let scroll_handle = ScrollHandle::new();
let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
let mut this = Self {
fs,
language_registry,
workspace,
focus_handle,
configuration_views_by_provider: HashMap::default(),
context_server_store,
@@ -133,6 +148,8 @@ impl AgentConfiguration {
) -> impl IntoElement + use<> {
let provider_id = provider.id().0.clone();
let provider_name = provider.name().0.clone();
let provider_id_string = SharedString::from(format!("provider-disclosure-{provider_id}"));
let configuration_view = self
.configuration_views_by_provider
.get(&provider.id())
@@ -145,72 +162,80 @@ impl AgentConfiguration {
.unwrap_or(false);
v_flex()
.pt_3()
.py_2()
.gap_1p5()
.border_t_1()
.border_color(cx.theme().colors().border.opacity(0.6))
.child(
h_flex()
.w_full()
.gap_1()
.justify_between()
.child(
h_flex()
.gap_2()
.id(provider_id_string.clone())
.cursor_pointer()
.py_0p5()
.w_full()
.justify_between()
.rounded_sm()
.hover(|hover| hover.bg(cx.theme().colors().element_hover))
.child(
Icon::new(provider.icon())
.size(IconSize::Small)
.color(Color::Muted),
)
.child(Label::new(provider_name.clone()).size(LabelSize::Large))
.when(provider.is_authenticated(cx) && !is_expanded, |parent| {
parent.child(Icon::new(IconName::Check).color(Color::Success))
}),
)
.child(
h_flex()
.gap_1()
.when(provider.is_authenticated(cx), |parent| {
parent.child(
Button::new(
SharedString::from(format!("new-thread-{provider_id}")),
"Start New Thread",
h_flex()
.gap_2()
.child(
Icon::new(provider.icon())
.size(IconSize::Small)
.color(Color::Muted),
)
.icon_position(IconPosition::Start)
.icon(IconName::Plus)
.icon_size(IconSize::Small)
.layer(ElevationIndex::ModalSurface)
.label_size(LabelSize::Small)
.on_click(cx.listener({
let provider = provider.clone();
move |_this, _event, _window, cx| {
cx.emit(AssistantConfigurationEvent::NewThread(
provider.clone(),
))
}
})),
)
})
.child(Label::new(provider_name.clone()).size(LabelSize::Large))
.when(
provider.is_authenticated(cx) && !is_expanded,
|parent| {
parent.child(
Icon::new(IconName::Check).color(Color::Success),
)
},
),
)
.child(
Disclosure::new(
SharedString::from(format!(
"provider-disclosure-{provider_id}"
)),
is_expanded,
)
.opened_icon(IconName::ChevronUp)
.closed_icon(IconName::ChevronDown)
.on_click(cx.listener({
let provider_id = provider.id().clone();
move |this, _event, _window, _cx| {
let is_expanded = this
.expanded_provider_configurations
.entry(provider_id.clone())
.or_insert(false);
Disclosure::new(provider_id_string, is_expanded)
.opened_icon(IconName::ChevronUp)
.closed_icon(IconName::ChevronDown),
)
.on_click(cx.listener({
let provider_id = provider.id().clone();
move |this, _event, _window, _cx| {
let is_expanded = this
.expanded_provider_configurations
.entry(provider_id.clone())
.or_insert(false);
*is_expanded = !*is_expanded;
}
})),
),
),
*is_expanded = !*is_expanded;
}
})),
)
.when(provider.is_authenticated(cx), |parent| {
parent.child(
Button::new(
SharedString::from(format!("new-thread-{provider_id}")),
"Start New Thread",
)
.icon_position(IconPosition::Start)
.icon(IconName::Plus)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.label_size(LabelSize::Small)
.on_click(cx.listener({
let provider = provider.clone();
move |_this, _event, _window, cx| {
cx.emit(AssistantConfigurationEvent::NewThread(
provider.clone(),
))
}
})),
)
}),
)
.when(is_expanded, |parent| match configuration_view {
Some(configuration_view) => parent.child(configuration_view),
@@ -229,11 +254,11 @@ impl AgentConfiguration {
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.pr(DynamicSpacing::Base20.rems(cx))
.gap_4()
.border_b_1()
.border_color(cx.theme().colors().border)
.child(
v_flex()
.mb_2p5()
.gap_0p5()
.child(Headline::new("LLM Providers"))
.child(
@@ -460,9 +485,22 @@ impl AgentConfiguration {
.read(cx)
.status_for_server(&context_server_id)
.unwrap_or(ContextServerStatus::Stopped);
let server_configuration = self
.context_server_store
.read(cx)
.configuration_for_server(&context_server_id);
let is_running = matches!(server_status, ContextServerStatus::Running);
let item_id = SharedString::from(context_server_id.0.clone());
let is_from_extension = server_configuration
.as_ref()
.map(|config| {
matches!(
config.as_ref(),
ContextServerConfiguration::Extension { .. }
)
})
.unwrap_or(false);
let error = if let ContextServerStatus::Error(error) = server_status.clone() {
Some(error)
@@ -475,7 +513,6 @@ impl AgentConfiguration {
.get(&context_server_id)
.copied()
.unwrap_or_default();
let tools = tools_by_source
.get(&ToolSource::ContextServer {
id: context_server_id.0.clone().into(),
@@ -484,25 +521,35 @@ impl AgentConfiguration {
let tool_count = tools.len();
let border_color = cx.theme().colors().border.opacity(0.6);
let success_color = Color::Success.color(cx);
let (source_icon, source_tooltip) = if is_from_extension {
(
IconName::ZedMcpExtension,
"This MCP server was installed from an extension.",
)
} else {
(
IconName::ZedMcpCustom,
"This custom MCP server was installed directly.",
)
};
let (status_indicator, tooltip_text) = match server_status {
ContextServerStatus::Starting => (
Indicator::dot()
.color(Color::Success)
Icon::new(IconName::LoadCircle)
.size(IconSize::XSmall)
.color(Color::Accent)
.with_animation(
SharedString::from(format!("{}-starting", context_server_id.0.clone(),)),
Animation::new(Duration::from_secs(2))
.repeat()
.with_easing(pulsating_between(0.4, 1.)),
move |this, delta| this.color(success_color.alpha(delta).into()),
Animation::new(Duration::from_secs(3)).repeat(),
|icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
)
.into_any_element(),
"Server is starting.",
),
ContextServerStatus::Running => (
Indicator::dot().color(Color::Success).into_any_element(),
"Server is running.",
"Server is active.",
),
ContextServerStatus::Error(_) => (
Indicator::dot().color(Color::Error).into_any_element(),
@@ -514,6 +561,105 @@ impl AgentConfiguration {
),
};
let context_server_configuration_menu = PopoverMenu::new("context-server-config-menu")
.trigger_with_tooltip(
IconButton::new("context-server-config-menu", IconName::Settings)
.icon_color(Color::Muted)
.icon_size(IconSize::Small),
Tooltip::text("Open MCP server options"),
)
.anchor(Corner::TopRight)
.menu({
let fs = self.fs.clone();
let context_server_id = context_server_id.clone();
let language_registry = self.language_registry.clone();
let context_server_store = self.context_server_store.clone();
let workspace = self.workspace.clone();
move |window, cx| {
Some(ContextMenu::build(window, cx, |menu, _window, _cx| {
menu.entry("Configure Server", None, {
let context_server_id = context_server_id.clone();
let language_registry = language_registry.clone();
let workspace = workspace.clone();
move |window, cx| {
ConfigureContextServerModal::show_modal_for_existing_server(
context_server_id.clone(),
language_registry.clone(),
workspace.clone(),
window,
cx,
)
.detach_and_log_err(cx);
}
})
.separator()
.entry("Uninstall", None, {
let fs = fs.clone();
let context_server_id = context_server_id.clone();
let context_server_store = context_server_store.clone();
let workspace = workspace.clone();
move |_, cx| {
let is_provided_by_extension = context_server_store
.read(cx)
.configuration_for_server(&context_server_id)
.as_ref()
.map(|config| {
matches!(
config.as_ref(),
ContextServerConfiguration::Extension { .. }
)
})
.unwrap_or(false);
let uninstall_extension_task = match (
is_provided_by_extension,
resolve_extension_for_context_server(&context_server_id, cx),
) {
(true, Some((id, manifest))) => {
if extension_only_provides_context_server(manifest.as_ref())
{
ExtensionStore::global(cx).update(cx, |store, cx| {
store.uninstall_extension(id, cx)
})
} else {
workspace.update(cx, |workspace, cx| {
show_unable_to_uninstall_extension_with_context_server(workspace, context_server_id.clone(), cx);
}).log_err();
Task::ready(Ok(()))
}
}
_ => Task::ready(Ok(())),
};
cx.spawn({
let fs = fs.clone();
let context_server_id = context_server_id.clone();
async move |cx| {
uninstall_extension_task.await?;
cx.update(|cx| {
update_settings_file::<ProjectSettings>(
fs.clone(),
cx,
{
let context_server_id =
context_server_id.clone();
move |settings, _| {
settings
.context_servers
.remove(&context_server_id.0);
}
},
)
})
}
})
.detach_and_log_err(cx);
}
})
}))
}
});
v_flex()
.id(item_id.clone())
.border_1()
@@ -526,12 +672,11 @@ impl AgentConfiguration {
.p_1()
.justify_between()
.when(
error.is_some() || are_tools_expanded && tool_count > 1,
error.is_some() || are_tools_expanded && tool_count >= 1,
|element| element.border_b_1().border_color(border_color),
)
.child(
h_flex()
.gap_1p5()
.child(
Disclosure::new(
"tool-list-disclosure",
@@ -551,12 +696,28 @@ impl AgentConfiguration {
})),
)
.child(
div()
.id(item_id.clone())
h_flex()
.id(SharedString::from(format!("tooltip-{}", item_id)))
.h_full()
.w_3()
.mx_1()
.justify_center()
.tooltip(Tooltip::text(tooltip_text))
.child(status_indicator),
)
.child(Label::new(context_server_id.0.clone()).ml_0p5())
.child(Label::new(item_id).ml_0p5())
.child(
div()
.id("extension-source")
.mt_0p5()
.mx_1()
.tooltip(Tooltip::text(source_tooltip))
.child(
Icon::new(source_icon)
.size(IconSize::Small)
.color(Color::Muted),
),
)
.when(is_running, |this| {
this.child(
Label::new(if tool_count == 1 {
@@ -570,28 +731,72 @@ impl AgentConfiguration {
}),
)
.child(
Switch::new("context-server-switch", is_running.into())
.color(SwitchColor::Accent)
.on_click({
let context_server_manager = self.context_server_store.clone();
let context_server_id = context_server_id.clone();
move |state, _window, cx| match state {
ToggleState::Unselected | ToggleState::Indeterminate => {
context_server_manager.update(cx, |this, cx| {
this.stop_server(&context_server_id, cx).log_err();
});
}
ToggleState::Selected => {
context_server_manager.update(cx, |this, cx| {
if let Some(server) =
this.get_server(&context_server_id)
{
this.start_server(server, cx).log_err();
}
})
}
}
}),
h_flex()
.gap_1()
.child(context_server_configuration_menu)
.child(
Switch::new("context-server-switch", is_running.into())
.color(SwitchColor::Accent)
.on_click({
let context_server_manager =
self.context_server_store.clone();
let context_server_id = context_server_id.clone();
let fs = self.fs.clone();
move |state, _window, cx| {
let is_enabled = match state {
ToggleState::Unselected
| ToggleState::Indeterminate => {
context_server_manager.update(
cx,
|this, cx| {
this.stop_server(
&context_server_id,
cx,
)
.log_err();
},
);
false
}
ToggleState::Selected => {
context_server_manager.update(
cx,
|this, cx| {
if let Some(server) =
this.get_server(&context_server_id)
{
this.start_server(server, cx);
}
},
);
true
}
};
update_settings_file::<ProjectSettings>(
fs.clone(),
cx,
{
let context_server_id =
context_server_id.clone();
move |settings, _| {
settings
.context_servers
.entry(context_server_id.0)
.or_insert_with(|| {
ContextServerSettings::Extension {
enabled: is_enabled,
settings: serde_json::json!({}),
}
})
.set_enabled(is_enabled);
}
},
);
}
}),
),
),
)
.map(|parent| {
@@ -701,3 +906,92 @@ impl Render for AgentConfiguration {
)
}
}
fn extension_only_provides_context_server(manifest: &ExtensionManifest) -> bool {
manifest.context_servers.len() == 1
&& manifest.themes.is_empty()
&& manifest.icon_themes.is_empty()
&& manifest.languages.is_empty()
&& manifest.grammars.is_empty()
&& manifest.language_servers.is_empty()
&& manifest.slash_commands.is_empty()
&& manifest.indexed_docs_providers.is_empty()
&& manifest.snippets.is_none()
&& manifest.debug_locators.is_empty()
}
pub(crate) fn resolve_extension_for_context_server(
id: &ContextServerId,
cx: &App,
) -> Option<(Arc<str>, Arc<ExtensionManifest>)> {
ExtensionStore::global(cx)
.read(cx)
.installed_extensions()
.iter()
.find(|(_, entry)| entry.manifest.context_servers.contains_key(&id.0))
.map(|(id, entry)| (id.clone(), entry.manifest.clone()))
}
// This notification appears when trying to delete
// an MCP server extension that not only provides
// the server, but other things, too, like language servers and more.
fn show_unable_to_uninstall_extension_with_context_server(
workspace: &mut Workspace,
id: ContextServerId,
cx: &mut App,
) {
let workspace_handle = workspace.weak_handle();
let context_server_id = id.clone();
let status_toast = StatusToast::new(
format!(
"The {} extension provides more than just the MCP server. Proceed to uninstall anyway?",
id.0
),
cx,
move |this, _cx| {
let workspace_handle = workspace_handle.clone();
let context_server_id = context_server_id.clone();
this.icon(ToastIcon::new(IconName::Warning).color(Color::Warning))
.dismiss_button(true)
.action("Uninstall", move |_, _cx| {
if let Some((extension_id, _)) =
resolve_extension_for_context_server(&context_server_id, _cx)
{
ExtensionStore::global(_cx).update(_cx, |store, cx| {
store
.uninstall_extension(extension_id, cx)
.detach_and_log_err(cx);
});
workspace_handle
.update(_cx, |workspace, cx| {
let fs = workspace.app_state().fs.clone();
cx.spawn({
let context_server_id = context_server_id.clone();
async move |_workspace_handle, cx| {
cx.update(|cx| {
update_settings_file::<ProjectSettings>(
fs,
cx,
move |settings, _| {
settings
.context_servers
.remove(&context_server_id.0);
},
);
})?;
anyhow::Ok(())
}
})
.detach_and_log_err(cx);
})
.log_err();
}
})
},
);
workspace.toggle_status_toast(status_toast, cx);
}

View File

@@ -0,0 +1,760 @@
use std::{
sync::{Arc, Mutex},
time::Duration,
};
use anyhow::{Context as _, Result};
use context_server::{ContextServerCommand, ContextServerId};
use editor::{Editor, EditorElement, EditorStyle};
use gpui::{
Animation, AnimationExt as _, AsyncWindowContext, DismissEvent, Entity, EventEmitter,
FocusHandle, Focusable, Task, TextStyle, TextStyleRefinement, Transformation, UnderlineStyle,
WeakEntity, percentage, prelude::*,
};
use language::{Language, LanguageRegistry};
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use notifications::status_toast::{StatusToast, ToastIcon};
use project::{
context_server_store::{
ContextServerStatus, ContextServerStore, registry::ContextServerDescriptorRegistry,
},
project_settings::{ContextServerSettings, ProjectSettings},
worktree_store::WorktreeStore,
};
use settings::{Settings as _, update_settings_file};
use theme::ThemeSettings;
use ui::{KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*};
use util::ResultExt as _;
use workspace::{ModalView, Workspace};
use crate::AddContextServer;
enum ConfigurationTarget {
New,
Existing {
id: ContextServerId,
command: ContextServerCommand,
},
Extension {
id: ContextServerId,
repository_url: Option<SharedString>,
installation: Option<extension::ContextServerConfiguration>,
},
}
enum ConfigurationSource {
New {
editor: Entity<Editor>,
},
Existing {
editor: Entity<Editor>,
},
Extension {
id: ContextServerId,
editor: Option<Entity<Editor>>,
repository_url: Option<SharedString>,
installation_instructions: Option<Entity<markdown::Markdown>>,
settings_validator: Option<jsonschema::Validator>,
},
}
impl ConfigurationSource {
fn has_configuration_options(&self) -> bool {
!matches!(self, ConfigurationSource::Extension { editor: None, .. })
}
fn is_new(&self) -> bool {
matches!(self, ConfigurationSource::New { .. })
}
fn from_target(
target: ConfigurationTarget,
language_registry: Arc<LanguageRegistry>,
jsonc_language: Option<Arc<Language>>,
window: &mut Window,
cx: &mut App,
) -> Self {
fn create_editor(
json: String,
jsonc_language: Option<Arc<Language>>,
window: &mut Window,
cx: &mut App,
) -> Entity<Editor> {
cx.new(|cx| {
let mut editor = Editor::auto_height(4, 16, window, cx);
editor.set_text(json, window, cx);
editor.set_show_gutter(false, cx);
editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx);
if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
buffer.update(cx, |buffer, cx| buffer.set_language(jsonc_language, cx))
}
editor
})
}
match target {
ConfigurationTarget::New => ConfigurationSource::New {
editor: create_editor(context_server_input(None), jsonc_language, window, cx),
},
ConfigurationTarget::Existing { id, command } => ConfigurationSource::Existing {
editor: create_editor(
context_server_input(Some((id, command))),
jsonc_language,
window,
cx,
),
},
ConfigurationTarget::Extension {
id,
repository_url,
installation,
} => {
let settings_validator = installation.as_ref().and_then(|installation| {
jsonschema::validator_for(&installation.settings_schema)
.context("Failed to load JSON schema for context server settings")
.log_err()
});
let installation_instructions = installation.as_ref().map(|installation| {
cx.new(|cx| {
Markdown::new(
installation.installation_instructions.clone().into(),
Some(language_registry.clone()),
None,
cx,
)
})
});
ConfigurationSource::Extension {
id,
repository_url,
installation_instructions,
settings_validator,
editor: installation.map(|installation| {
create_editor(installation.default_settings, jsonc_language, window, cx)
}),
}
}
}
}
fn output(&self, cx: &mut App) -> Result<(ContextServerId, ContextServerSettings)> {
match self {
ConfigurationSource::New { editor } | ConfigurationSource::Existing { editor } => {
parse_input(&editor.read(cx).text(cx)).map(|(id, command)| {
(
id,
ContextServerSettings::Custom {
enabled: true,
command,
},
)
})
}
ConfigurationSource::Extension {
id,
editor,
settings_validator,
..
} => {
let text = editor
.as_ref()
.context("No output available")?
.read(cx)
.text(cx);
let settings = serde_json_lenient::from_str::<serde_json::Value>(&text)?;
if let Some(settings_validator) = settings_validator {
if let Err(error) = settings_validator.validate(&settings) {
return Err(anyhow::anyhow!(error.to_string()));
}
}
Ok((
id.clone(),
ContextServerSettings::Extension {
enabled: true,
settings,
},
))
}
}
}
}
fn context_server_input(existing: Option<(ContextServerId, ContextServerCommand)>) -> String {
let (name, command, args, env) = match existing {
Some((id, cmd)) => {
let args = serde_json::to_string(&cmd.args).unwrap();
let env = serde_json::to_string(&cmd.env.unwrap_or_default()).unwrap();
(id.0.to_string(), cmd.path, args, env)
}
None => (
"some-mcp-server".to_string(),
"".to_string(),
"[]".to_string(),
"{}".to_string(),
),
};
format!(
r#"{{
/// The name of your MCP server
"{name}": {{
/// The command which runs the MCP server
"command": "{command}",
/// The arguments to pass to the MCP server
"args": {args},
/// The environment variables to set
"env": {env}
}}
}}"#
)
}
fn resolve_context_server_extension(
id: ContextServerId,
worktree_store: Entity<WorktreeStore>,
cx: &mut App,
) -> Task<Option<ConfigurationTarget>> {
let registry = ContextServerDescriptorRegistry::default_global(cx).read(cx);
let Some(descriptor) = registry.context_server_descriptor(&id.0) else {
return Task::ready(None);
};
let extension = crate::agent_configuration::resolve_extension_for_context_server(&id, cx);
cx.spawn(async move |cx| {
let installation = descriptor
.configuration(worktree_store, cx)
.await
.context("Failed to resolve context server configuration")
.log_err()
.flatten();
Some(ConfigurationTarget::Extension {
id,
repository_url: extension
.and_then(|(_, manifest)| manifest.repository.clone().map(SharedString::from)),
installation,
})
})
}
enum State {
Idle,
Waiting,
Error(SharedString),
}
pub struct ConfigureContextServerModal {
context_server_store: Entity<ContextServerStore>,
workspace: WeakEntity<Workspace>,
source: ConfigurationSource,
state: State,
}
impl ConfigureContextServerModal {
pub fn register(
workspace: &mut Workspace,
language_registry: Arc<LanguageRegistry>,
_window: Option<&mut Window>,
_cx: &mut Context<Workspace>,
) {
workspace.register_action({
let language_registry = language_registry.clone();
move |_workspace, _: &AddContextServer, window, cx| {
let workspace_handle = cx.weak_entity();
let language_registry = language_registry.clone();
window
.spawn(cx, async move |cx| {
Self::show_modal(
ConfigurationTarget::New,
language_registry,
workspace_handle,
cx,
)
.await
})
.detach_and_log_err(cx);
}
});
}
pub fn show_modal_for_existing_server(
server_id: ContextServerId,
language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut App,
) -> Task<Result<()>> {
let Some(settings) = ProjectSettings::get_global(cx)
.context_servers
.get(&server_id.0)
.cloned()
.or_else(|| {
ContextServerDescriptorRegistry::default_global(cx)
.read(cx)
.context_server_descriptor(&server_id.0)
.map(|_| ContextServerSettings::default_extension())
})
else {
return Task::ready(Err(anyhow::anyhow!("Context server not found")));
};
window.spawn(cx, async move |cx| {
let target = match settings {
ContextServerSettings::Custom {
enabled: _,
command,
} => Some(ConfigurationTarget::Existing {
id: server_id,
command,
}),
ContextServerSettings::Extension { .. } => {
match workspace
.update(cx, |workspace, cx| {
resolve_context_server_extension(
server_id,
workspace.project().read(cx).worktree_store(),
cx,
)
})
.ok()
{
Some(task) => task.await,
None => None,
}
}
};
match target {
Some(target) => Self::show_modal(target, language_registry, workspace, cx).await,
None => Err(anyhow::anyhow!("Failed to resolve context server")),
}
})
}
fn show_modal(
target: ConfigurationTarget,
language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>,
cx: &mut AsyncWindowContext,
) -> Task<Result<()>> {
cx.spawn(async move |cx| {
let jsonc_language = language_registry.language_for_name("jsonc").await.ok();
workspace.update_in(cx, |workspace, window, cx| {
let workspace_handle = cx.weak_entity();
let context_server_store = workspace.project().read(cx).context_server_store();
workspace.toggle_modal(window, cx, |window, cx| Self {
context_server_store,
workspace: workspace_handle,
state: State::Idle,
source: ConfigurationSource::from_target(
target,
language_registry,
jsonc_language,
window,
cx,
),
})
})
})
}
fn set_error(&mut self, err: impl Into<SharedString>, cx: &mut Context<Self>) {
self.state = State::Error(err.into());
cx.notify();
}
fn confirm(&mut self, _: &menu::Confirm, cx: &mut Context<Self>) {
self.state = State::Idle;
let Some(workspace) = self.workspace.upgrade() else {
return;
};
let (id, settings) = match self.source.output(cx) {
Ok(val) => val,
Err(error) => {
self.set_error(error.to_string(), cx);
return;
}
};
self.state = State::Waiting;
let wait_for_context_server_task =
wait_for_context_server(&self.context_server_store, id.clone(), cx);
cx.spawn({
let id = id.clone();
async move |this, cx| {
let result = wait_for_context_server_task.await;
this.update(cx, |this, cx| match result {
Ok(_) => {
this.state = State::Idle;
this.show_configured_context_server_toast(id, cx);
cx.emit(DismissEvent);
}
Err(err) => {
this.set_error(err, cx);
}
})
}
})
.detach();
// When we write the settings to the file, the context server will be restarted.
workspace.update(cx, |workspace, cx| {
let fs = workspace.app_state().fs.clone();
update_settings_file::<ProjectSettings>(fs.clone(), cx, |project_settings, _| {
project_settings.context_servers.insert(id.0, settings);
});
});
}
fn cancel(&mut self, _: &menu::Cancel, cx: &mut Context<Self>) {
cx.emit(DismissEvent);
}
fn show_configured_context_server_toast(&self, id: ContextServerId, cx: &mut App) {
self.workspace
.update(cx, {
|workspace, cx| {
let status_toast = StatusToast::new(
format!("{} configured successfully.", id.0),
cx,
|this, _cx| {
this.icon(ToastIcon::new(IconName::Hammer).color(Color::Muted))
.action("Dismiss", |_, _| {})
},
);
workspace.toggle_status_toast(status_toast, cx);
}
})
.log_err();
}
}
fn parse_input(text: &str) -> Result<(ContextServerId, ContextServerCommand)> {
let value: serde_json::Value = serde_json_lenient::from_str(text)?;
let object = value.as_object().context("Expected object")?;
anyhow::ensure!(object.len() == 1, "Expected exactly one key-value pair");
let (context_server_name, value) = object.into_iter().next().unwrap();
let command: ContextServerCommand = serde_json::from_value(value.clone())?;
Ok((ContextServerId(context_server_name.clone().into()), command))
}
impl ModalView for ConfigureContextServerModal {}
impl Focusable for ConfigureContextServerModal {
fn focus_handle(&self, cx: &App) -> FocusHandle {
match &self.source {
ConfigurationSource::New { editor } => editor.focus_handle(cx),
ConfigurationSource::Existing { editor, .. } => editor.focus_handle(cx),
ConfigurationSource::Extension { editor, .. } => editor
.as_ref()
.map(|editor| editor.focus_handle(cx))
.unwrap_or_else(|| cx.focus_handle()),
}
}
}
impl EventEmitter<DismissEvent> for ConfigureContextServerModal {}
impl ConfigureContextServerModal {
fn render_modal_header(&self) -> ModalHeader {
let text: SharedString = match &self.source {
ConfigurationSource::New { .. } => "Add MCP Server".into(),
ConfigurationSource::Existing { .. } => "Configure MCP Server".into(),
ConfigurationSource::Extension { id, .. } => format!("Configure {}", id.0).into(),
};
ModalHeader::new().headline(text)
}
fn render_modal_description(&self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
const MODAL_DESCRIPTION: &'static str = "Visit the MCP server configuration docs to find all necessary arguments and environment variables.";
if let ConfigurationSource::Extension {
installation_instructions: Some(installation_instructions),
..
} = &self.source
{
div()
.pb_2()
.text_sm()
.child(MarkdownElement::new(
installation_instructions.clone(),
default_markdown_style(window, cx),
))
.into_any_element()
} else {
Label::new(MODAL_DESCRIPTION)
.color(Color::Muted)
.into_any_element()
}
}
fn render_modal_content(&self, cx: &App) -> AnyElement {
let editor = match &self.source {
ConfigurationSource::New { editor } => editor,
ConfigurationSource::Existing { editor } => editor,
ConfigurationSource::Extension { editor, .. } => {
let Some(editor) = editor else {
return div().into_any_element();
};
editor
}
};
div()
.p_2()
.rounded_md()
.border_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().editor_background)
.child({
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: cx.theme().colors().text,
font_family: settings.buffer_font.family.clone(),
font_fallbacks: settings.buffer_font.fallbacks.clone(),
font_size: settings.buffer_font_size(cx).into(),
font_weight: settings.buffer_font.weight,
line_height: relative(settings.buffer_line_height.value()),
..Default::default()
};
EditorElement::new(
editor,
EditorStyle {
background: cx.theme().colors().editor_background,
local_player: cx.theme().players().local(),
text: text_style,
syntax: cx.theme().syntax().clone(),
..Default::default()
},
)
})
.into_any_element()
}
fn render_modal_footer(&self, window: &mut Window, cx: &mut Context<Self>) -> ModalFooter {
let focus_handle = self.focus_handle(cx);
let is_connecting = matches!(self.state, State::Waiting);
ModalFooter::new()
.start_slot::<Button>(
if let ConfigurationSource::Extension {
repository_url: Some(repository_url),
..
} = &self.source
{
Some(
Button::new("open-repository", "Open Repository")
.icon(IconName::ArrowUpRight)
.icon_color(Color::Muted)
.icon_size(IconSize::XSmall)
.tooltip({
let repository_url = repository_url.clone();
move |window, cx| {
Tooltip::with_meta(
"Open Repository",
None,
repository_url.clone(),
window,
cx,
)
}
})
.on_click({
let repository_url = repository_url.clone();
move |_, _, cx| cx.open_url(&repository_url)
}),
)
} else {
None
},
)
.end_slot(
h_flex()
.gap_2()
.child(
Button::new(
"cancel",
if self.source.has_configuration_options() {
"Cancel"
} else {
"Dismiss"
},
)
.key_binding(
KeyBinding::for_action_in(&menu::Cancel, &focus_handle, window, cx)
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(
cx.listener(|this, _event, _window, cx| this.cancel(&menu::Cancel, cx)),
),
)
.children(self.source.has_configuration_options().then(|| {
Button::new(
"add-server",
if self.source.is_new() {
"Add Server"
} else {
"Configure Server"
},
)
.disabled(is_connecting)
.key_binding(
KeyBinding::for_action_in(&menu::Confirm, &focus_handle, window, cx)
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(
cx.listener(|this, _event, _window, cx| {
this.confirm(&menu::Confirm, cx)
}),
)
})),
)
}
fn render_waiting_for_context_server() -> Div {
h_flex()
.gap_2()
.child(
Icon::new(IconName::ArrowCircle)
.size(IconSize::XSmall)
.color(Color::Info)
.with_animation(
"arrow-circle",
Animation::new(Duration::from_secs(2)).repeat(),
|icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
)
.into_any_element(),
)
.child(
Label::new("Waiting for Context Server")
.size(LabelSize::Small)
.color(Color::Muted),
)
}
fn render_modal_error(error: SharedString) -> Div {
h_flex()
.gap_2()
.child(
Icon::new(IconName::Warning)
.size(IconSize::XSmall)
.color(Color::Warning),
)
.child(
div()
.w_full()
.child(Label::new(error).size(LabelSize::Small).color(Color::Muted)),
)
}
}
impl Render for ConfigureContextServerModal {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.elevation_3(cx)
.w(rems(34.))
.key_context("ConfigureContextServerModal")
.on_action(
cx.listener(|this, _: &menu::Cancel, _window, cx| this.cancel(&menu::Cancel, cx)),
)
.on_action(
cx.listener(|this, _: &menu::Confirm, _window, cx| {
this.confirm(&menu::Confirm, cx)
}),
)
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
this.focus_handle(cx).focus(window);
}))
.child(
Modal::new("configure-context-server", None)
.header(self.render_modal_header())
.section(
Section::new()
.child(self.render_modal_description(window, cx))
.child(self.render_modal_content(cx))
.child(match &self.state {
State::Idle => div(),
State::Waiting => Self::render_waiting_for_context_server(),
State::Error(error) => Self::render_modal_error(error.clone()),
}),
)
.footer(self.render_modal_footer(window, cx)),
)
}
}
fn wait_for_context_server(
context_server_store: &Entity<ContextServerStore>,
context_server_id: ContextServerId,
cx: &mut App,
) -> Task<Result<(), Arc<str>>> {
let (tx, rx) = futures::channel::oneshot::channel();
let tx = Arc::new(Mutex::new(Some(tx)));
let subscription = cx.subscribe(context_server_store, move |_, event, _cx| match event {
project::context_server_store::Event::ServerStatusChanged { server_id, status } => {
match status {
ContextServerStatus::Running => {
if server_id == &context_server_id {
if let Some(tx) = tx.lock().unwrap().take() {
let _ = tx.send(Ok(()));
}
}
}
ContextServerStatus::Stopped => {
if server_id == &context_server_id {
if let Some(tx) = tx.lock().unwrap().take() {
let _ = tx.send(Err("Context server stopped running".into()));
}
}
}
ContextServerStatus::Error(error) => {
if server_id == &context_server_id {
if let Some(tx) = tx.lock().unwrap().take() {
let _ = tx.send(Err(error.clone()));
}
}
}
_ => {}
}
}
});
cx.spawn(async move |_cx| {
let result = rx.await.unwrap();
drop(subscription);
result
})
}
pub(crate) fn default_markdown_style(window: &Window, cx: &App) -> MarkdownStyle {
let theme_settings = ThemeSettings::get_global(cx);
let colors = cx.theme().colors();
let mut text_style = window.text_style();
text_style.refine(&TextStyleRefinement {
font_family: Some(theme_settings.ui_font.family.clone()),
font_fallbacks: theme_settings.ui_font.fallbacks.clone(),
font_features: Some(theme_settings.ui_font.features.clone()),
font_size: Some(TextSize::XSmall.rems(cx).into()),
color: Some(colors.text_muted),
..Default::default()
});
MarkdownStyle {
base_text_style: text_style.clone(),
selection_background_color: colors.element_selection_background,
link: TextStyleRefinement {
background_color: Some(colors.editor_foreground.opacity(0.025)),
underline: Some(UnderlineStyle {
color: Some(colors.text_accent.opacity(0.5)),
thickness: px(1.),
..Default::default()
}),
..Default::default()
},
..Default::default()
}
}

View File

@@ -2,25 +2,21 @@ mod profile_modal_header;
use std::sync::Arc;
use agent_settings::{AgentProfile, AgentProfileId, AgentSettings, builtin_profiles};
use agent_settings::{AgentProfileId, AgentSettings, builtin_profiles};
use assistant_tool::ToolWorkingSet;
use convert_case::{Case, Casing as _};
use editor::Editor;
use fs::Fs;
use gpui::{
DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription, WeakEntity,
prelude::*,
};
use settings::{Settings as _, update_settings_file};
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription, prelude::*};
use settings::Settings as _;
use ui::{
KeyBinding, ListItem, ListItemSpacing, ListSeparator, Navigable, NavigableEntry, prelude::*,
};
use util::ResultExt as _;
use workspace::{ModalView, Workspace};
use crate::agent_configuration::manage_profiles_modal::profile_modal_header::ProfileModalHeader;
use crate::agent_configuration::tool_picker::{ToolPicker, ToolPickerDelegate};
use crate::{AgentPanel, ManageProfiles, ThreadStore};
use crate::{AgentPanel, ManageProfiles};
use agent::agent_profile::AgentProfile;
use super::tool_picker::ToolPickerMode;
@@ -103,7 +99,6 @@ pub struct NewProfileMode {
pub struct ManageProfilesModal {
fs: Arc<dyn Fs>,
tools: Entity<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
focus_handle: FocusHandle,
mode: Mode,
}
@@ -119,9 +114,8 @@ impl ManageProfilesModal {
let fs = workspace.app_state().fs.clone();
let thread_store = panel.read(cx).thread_store();
let tools = thread_store.read(cx).tools();
let thread_store = thread_store.downgrade();
workspace.toggle_modal(window, cx, |window, cx| {
let mut this = Self::new(fs, tools, thread_store, window, cx);
let mut this = Self::new(fs, tools, window, cx);
if let Some(profile_id) = action.customize_tools.clone() {
this.configure_builtin_tools(profile_id, window, cx);
@@ -136,7 +130,6 @@ impl ManageProfilesModal {
pub fn new(
fs: Arc<dyn Fs>,
tools: Entity<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@@ -145,7 +138,6 @@ impl ManageProfilesModal {
Self {
fs,
tools,
thread_store,
focus_handle,
mode: Mode::choose_profile(window, cx),
}
@@ -206,7 +198,6 @@ impl ManageProfilesModal {
ToolPickerMode::McpTools,
self.fs.clone(),
self.tools.clone(),
self.thread_store.clone(),
profile_id.clone(),
profile,
cx,
@@ -244,7 +235,6 @@ impl ManageProfilesModal {
ToolPickerMode::BuiltinTools,
self.fs.clone(),
self.tools.clone(),
self.thread_store.clone(),
profile_id.clone(),
profile,
cx,
@@ -270,32 +260,10 @@ impl ManageProfilesModal {
match &self.mode {
Mode::ChooseProfile { .. } => {}
Mode::NewProfile(mode) => {
let settings = AgentSettings::get_global(cx);
let base_profile = mode
.base_profile_id
.as_ref()
.and_then(|profile_id| settings.profiles.get(profile_id).cloned());
let name = mode.name_editor.read(cx).text(cx);
let profile_id = AgentProfileId(name.to_case(Case::Kebab).into());
let profile = AgentProfile {
name: name.into(),
tools: base_profile
.as_ref()
.map(|profile| profile.tools.clone())
.unwrap_or_default(),
enable_all_context_servers: base_profile
.as_ref()
.map(|profile| profile.enable_all_context_servers)
.unwrap_or_default(),
context_servers: base_profile
.map(|profile| profile.context_servers)
.unwrap_or_default(),
};
self.create_profile(profile_id.clone(), profile, cx);
let profile_id =
AgentProfile::create(name, mode.base_profile_id.clone(), self.fs.clone(), cx);
self.view_profile(profile_id, window, cx);
}
Mode::ViewProfile(_) => {}
@@ -325,19 +293,6 @@ impl ManageProfilesModal {
}
}
}
fn create_profile(
&self,
profile_id: AgentProfileId,
profile: AgentProfile,
cx: &mut Context<Self>,
) {
update_settings_file::<AgentSettings>(self.fs.clone(), cx, {
move |settings, _cx| {
settings.create_profile(profile_id, profile).log_err();
}
});
}
}
impl ModalView for ManageProfilesModal {}
@@ -520,14 +475,13 @@ impl ManageProfilesModal {
) -> impl IntoElement {
let settings = AgentSettings::get_global(cx);
let profile_id = &settings.default_profile;
let profile_name = settings
.profiles
.get(&mode.profile_id)
.map(|profile| profile.name.clone())
.unwrap_or_else(|| "Unknown".into());
let icon = match profile_id.as_str() {
let icon = match mode.profile_id.as_str() {
"write" => IconName::Pencil,
"ask" => IconName::MessageBubbles,
_ => IconName::UserRoundPen,

View File

@@ -1,19 +1,17 @@
use std::{collections::BTreeMap, sync::Arc};
use agent_settings::{
AgentProfile, AgentProfileContent, AgentProfileId, AgentSettings, AgentSettingsContent,
AgentProfileContent, AgentProfileId, AgentProfileSettings, AgentSettings, AgentSettingsContent,
ContextServerPresetContent,
};
use assistant_tool::{ToolSource, ToolWorkingSet};
use fs::Fs;
use gpui::{App, Context, DismissEvent, Entity, EventEmitter, Focusable, Task, WeakEntity, Window};
use picker::{Picker, PickerDelegate};
use settings::{Settings as _, update_settings_file};
use settings::update_settings_file;
use ui::{ListItem, ListItemSpacing, prelude::*};
use util::ResultExt as _;
use crate::ThreadStore;
pub struct ToolPicker {
picker: Entity<Picker<ToolPickerDelegate>>,
}
@@ -71,11 +69,10 @@ pub enum PickerItem {
pub struct ToolPickerDelegate {
tool_picker: WeakEntity<ToolPicker>,
thread_store: WeakEntity<ThreadStore>,
fs: Arc<dyn Fs>,
items: Arc<Vec<PickerItem>>,
profile_id: AgentProfileId,
profile: AgentProfile,
profile_settings: AgentProfileSettings,
filtered_items: Vec<PickerItem>,
selected_index: usize,
mode: ToolPickerMode,
@@ -86,20 +83,18 @@ impl ToolPickerDelegate {
mode: ToolPickerMode,
fs: Arc<dyn Fs>,
tool_set: Entity<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
profile_id: AgentProfileId,
profile: AgentProfile,
profile_settings: AgentProfileSettings,
cx: &mut Context<ToolPicker>,
) -> Self {
let items = Arc::new(Self::resolve_items(mode, &tool_set, cx));
Self {
tool_picker: cx.entity().downgrade(),
thread_store,
fs,
items,
profile_id,
profile,
profile_settings,
filtered_items: Vec::new(),
selected_index: 0,
mode,
@@ -249,67 +244,63 @@ impl PickerDelegate for ToolPickerDelegate {
};
let is_currently_enabled = if let Some(server_id) = server_id.clone() {
let preset = self.profile.context_servers.entry(server_id).or_default();
let preset = self
.profile_settings
.context_servers
.entry(server_id)
.or_default();
let is_enabled = *preset.tools.entry(tool_name.clone()).or_default();
*preset.tools.entry(tool_name.clone()).or_default() = !is_enabled;
is_enabled
} else {
let is_enabled = *self.profile.tools.entry(tool_name.clone()).or_default();
*self.profile.tools.entry(tool_name.clone()).or_default() = !is_enabled;
let is_enabled = *self
.profile_settings
.tools
.entry(tool_name.clone())
.or_default();
*self
.profile_settings
.tools
.entry(tool_name.clone())
.or_default() = !is_enabled;
is_enabled
};
let active_profile_id = &AgentSettings::get_global(cx).default_profile;
if active_profile_id == &self.profile_id {
self.thread_store
.update(cx, |this, cx| {
this.load_profile(self.profile.clone(), cx);
})
.log_err();
}
update_settings_file::<AgentSettings>(self.fs.clone(), cx, {
let profile_id = self.profile_id.clone();
let default_profile = self.profile.clone();
let default_profile = self.profile_settings.clone();
let server_id = server_id.clone();
let tool_name = tool_name.clone();
move |settings: &mut AgentSettingsContent, _cx| {
settings
.v2_setting(|v2_settings| {
let profiles = v2_settings.profiles.get_or_insert_default();
let profile =
profiles
.entry(profile_id)
.or_insert_with(|| AgentProfileContent {
name: default_profile.name.into(),
tools: default_profile.tools,
enable_all_context_servers: Some(
default_profile.enable_all_context_servers,
),
context_servers: default_profile
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
});
let profiles = settings.profiles.get_or_insert_default();
let profile = profiles
.entry(profile_id)
.or_insert_with(|| AgentProfileContent {
name: default_profile.name.into(),
tools: default_profile.tools,
enable_all_context_servers: Some(
default_profile.enable_all_context_servers,
),
context_servers: default_profile
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
});
if let Some(server_id) = server_id {
let preset = profile.context_servers.entry(server_id).or_default();
*preset.tools.entry(tool_name).or_default() = !is_currently_enabled;
} else {
*profile.tools.entry(tool_name).or_default() = !is_currently_enabled;
}
Ok(())
})
.ok();
if let Some(server_id) = server_id {
let preset = profile.context_servers.entry(server_id).or_default();
*preset.tools.entry(tool_name).or_default() = !is_currently_enabled;
} else {
*profile.tools.entry(tool_name).or_default() = !is_currently_enabled;
}
}
});
}
@@ -348,14 +339,18 @@ impl PickerDelegate for ToolPickerDelegate {
),
PickerItem::Tool { name, server_id } => {
let is_enabled = if let Some(server_id) = server_id {
self.profile
self.profile_settings
.context_servers
.get(server_id.as_ref())
.and_then(|preset| preset.tools.get(name))
.copied()
.unwrap_or(self.profile.enable_all_context_servers)
.unwrap_or(self.profile_settings.enable_all_context_servers)
} else {
self.profile.tools.get(name).copied().unwrap_or(false)
self.profile_settings
.tools
.get(name)
.copied()
.unwrap_or(false)
};
Some(

View File

@@ -1,10 +1,13 @@
use crate::{Keep, KeepAll, OpenAgentDiff, Reject, RejectAll, Thread, ThreadEvent};
use crate::{Keep, KeepAll, OpenAgentDiff, Reject, RejectAll};
use agent::{ThreadEvent, ZedAgentThread};
use agent_settings::AgentSettings;
use anyhow::Result;
use assistant_tool::ActionLog;
use buffer_diff::DiffHunkStatus;
use collections::{HashMap, HashSet};
use editor::{
Direction, Editor, EditorEvent, EditorSettings, MultiBuffer, MultiBufferSnapshot, ToPoint,
Direction, Editor, EditorEvent, EditorSettings, MultiBuffer, MultiBufferSnapshot,
SelectionEffects, ToPoint,
actions::{GoToHunk, GoToPreviousHunk},
scroll::Autoscroll,
};
@@ -31,7 +34,7 @@ use util::ResultExt;
use workspace::{
Item, ItemHandle, ItemNavHistory, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
Workspace,
item::{BreadcrumbText, ItemEvent, TabContentParams},
item::{BreadcrumbText, ItemEvent, SaveOptions, TabContentParams},
searchable::SearchableItemHandle,
};
use zed_actions::assistant::ToggleFocus;
@@ -39,7 +42,8 @@ use zed_actions::assistant::ToggleFocus;
pub struct AgentDiffPane {
multibuffer: Entity<MultiBuffer>,
editor: Entity<Editor>,
thread: Entity<Thread>,
agent: Entity<ZedAgentThread>,
action_log: Entity<ActionLog>,
focus_handle: FocusHandle,
workspace: WeakEntity<Workspace>,
title: SharedString,
@@ -48,70 +52,71 @@ pub struct AgentDiffPane {
impl AgentDiffPane {
pub fn deploy(
thread: Entity<Thread>,
agent: Entity<ZedAgentThread>,
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut App,
) -> Result<Entity<Self>> {
workspace.update(cx, |workspace, cx| {
Self::deploy_in_workspace(thread, workspace, window, cx)
Self::deploy_in_workspace(agent, workspace, window, cx)
})
}
pub fn deploy_in_workspace(
thread: Entity<Thread>,
agent: Entity<ZedAgentThread>,
workspace: &mut Workspace,
window: &mut Window,
cx: &mut Context<Workspace>,
) -> Entity<Self> {
let existing_diff = workspace
.items_of_type::<AgentDiffPane>(cx)
.find(|diff| diff.read(cx).thread == thread);
.find(|diff| diff.read(cx).agent == agent);
if let Some(existing_diff) = existing_diff {
workspace.activate_item(&existing_diff, true, true, window, cx);
existing_diff
} else {
let agent_diff = cx
.new(|cx| AgentDiffPane::new(thread.clone(), workspace.weak_handle(), window, cx));
let agent_diff =
cx.new(|cx| AgentDiffPane::new(agent.clone(), workspace.weak_handle(), window, cx));
workspace.add_item_to_center(Box::new(agent_diff.clone()), window, cx);
agent_diff
}
}
pub fn new(
thread: Entity<Thread>,
agent: Entity<ZedAgentThread>,
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let focus_handle = cx.focus_handle();
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
let action_log = agent.read(cx).action_log();
let project = agent.read(cx).project().clone();
let project = thread.read(cx).project().clone();
let editor = cx.new(|cx| {
let mut editor =
Editor::for_multibuffer(multibuffer.clone(), Some(project.clone()), window, cx);
editor.disable_inline_diagnostics();
editor.set_expand_all_diff_hunks(cx);
editor.set_render_diff_hunk_controls(diff_hunk_controls(&thread), cx);
editor.set_render_diff_hunk_controls(diff_hunk_controls(&action_log), cx);
editor.register_addon(AgentDiffAddon);
editor
});
let action_log = thread.read(cx).action_log().clone();
let mut this = Self {
_subscriptions: vec![
cx.observe_in(&action_log, window, |this, _action_log, window, cx| {
this.update_excerpts(window, cx)
}),
cx.subscribe(&thread, |this, _thread, event, cx| {
cx.subscribe(&agent, |this, _thread, event, cx| {
this.handle_thread_event(event, cx)
}),
],
title: SharedString::default(),
action_log,
multibuffer,
editor,
thread,
agent,
focus_handle,
workspace,
};
@@ -121,8 +126,8 @@ impl AgentDiffPane {
}
fn update_excerpts(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let thread = self.thread.read(cx);
let changed_buffers = thread.action_log().read(cx).changed_buffers(cx);
let agent = self.agent.read(cx);
let changed_buffers = agent.action_log().read(cx).changed_buffers(cx);
let mut paths_to_delete = self.multibuffer.read(cx).paths().collect::<HashSet<_>>();
for (buffer, diff_handle) in changed_buffers {
@@ -170,15 +175,9 @@ impl AgentDiffPane {
if let Some(first_hunk) = first_hunk {
let first_hunk_start = first_hunk.multi_buffer_range().start;
editor.change_selections(
Some(Autoscroll::fit()),
window,
cx,
|selections| {
selections
.select_anchor_ranges([first_hunk_start..first_hunk_start]);
},
)
editor.change_selections(Default::default(), window, cx, |selections| {
selections.select_anchor_ranges([first_hunk_start..first_hunk_start]);
})
}
}
@@ -215,7 +214,7 @@ impl AgentDiffPane {
}
fn update_title(&mut self, cx: &mut Context<Self>) {
let new_title = self.thread.read(cx).summary().unwrap_or("Agent Changes");
let new_title = self.agent.read(cx).summary().unwrap_or("Agent Changes");
if new_title != self.title {
self.title = new_title;
cx.emit(EditorEvent::TitleChanged);
@@ -241,7 +240,7 @@ impl AgentDiffPane {
if let Some(first_hunk) = first_hunk {
let first_hunk_start = first_hunk.multi_buffer_range().start;
editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
editor.change_selections(Default::default(), window, cx, |selections| {
selections.select_anchor_ranges([first_hunk_start..first_hunk_start]);
})
}
@@ -252,14 +251,14 @@ impl AgentDiffPane {
fn keep(&mut self, _: &Keep, window: &mut Window, cx: &mut Context<Self>) {
self.editor.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
keep_edits_in_selection(editor, &snapshot, &self.thread, window, cx);
keep_edits_in_selection(editor, &snapshot, &self.action_log, window, cx);
});
}
fn reject(&mut self, _: &Reject, window: &mut Window, cx: &mut Context<Self>) {
self.editor.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
reject_edits_in_selection(editor, &snapshot, &self.thread, window, cx);
reject_edits_in_selection(editor, &snapshot, &self.action_log, window, cx);
});
}
@@ -269,7 +268,7 @@ impl AgentDiffPane {
reject_edits_in_ranges(
editor,
&snapshot,
&self.thread,
&self.action_log,
vec![editor::Anchor::min()..editor::Anchor::max()],
window,
cx,
@@ -278,15 +277,15 @@ impl AgentDiffPane {
}
fn keep_all(&mut self, _: &KeepAll, _window: &mut Window, cx: &mut Context<Self>) {
self.thread
.update(cx, |thread, cx| thread.keep_all_edits(cx));
self.action_log
.update(cx, |action_log, cx| action_log.keep_all_edits(cx));
}
}
fn keep_edits_in_selection(
editor: &mut Editor,
buffer_snapshot: &MultiBufferSnapshot,
thread: &Entity<Thread>,
action_log: &Entity<ActionLog>,
window: &mut Window,
cx: &mut Context<Editor>,
) {
@@ -295,13 +294,13 @@ fn keep_edits_in_selection(
.disjoint_anchor_ranges()
.collect::<Vec<_>>();
keep_edits_in_ranges(editor, buffer_snapshot, &thread, ranges, window, cx)
keep_edits_in_ranges(editor, buffer_snapshot, &action_log, ranges, window, cx)
}
fn reject_edits_in_selection(
editor: &mut Editor,
buffer_snapshot: &MultiBufferSnapshot,
thread: &Entity<Thread>,
action_log: &Entity<ActionLog>,
window: &mut Window,
cx: &mut Context<Editor>,
) {
@@ -309,13 +308,13 @@ fn reject_edits_in_selection(
.selections
.disjoint_anchor_ranges()
.collect::<Vec<_>>();
reject_edits_in_ranges(editor, buffer_snapshot, &thread, ranges, window, cx)
reject_edits_in_ranges(editor, buffer_snapshot, &action_log, ranges, window, cx)
}
fn keep_edits_in_ranges(
editor: &mut Editor,
buffer_snapshot: &MultiBufferSnapshot,
thread: &Entity<Thread>,
action_log: &Entity<ActionLog>,
ranges: Vec<Range<editor::Anchor>>,
window: &mut Window,
cx: &mut Context<Editor>,
@@ -330,8 +329,8 @@ fn keep_edits_in_ranges(
for hunk in &diff_hunks_in_ranges {
let buffer = multibuffer.read(cx).buffer(hunk.buffer_id);
if let Some(buffer) = buffer {
thread.update(cx, |thread, cx| {
thread.keep_edits_in_range(buffer, hunk.buffer_range.clone(), cx)
action_log.update(cx, |action_log, cx| {
action_log.keep_edits_in_range(buffer, hunk.buffer_range.clone(), cx)
});
}
}
@@ -340,7 +339,7 @@ fn keep_edits_in_ranges(
fn reject_edits_in_ranges(
editor: &mut Editor,
buffer_snapshot: &MultiBufferSnapshot,
thread: &Entity<Thread>,
action_log: &Entity<ActionLog>,
ranges: Vec<Range<editor::Anchor>>,
window: &mut Window,
cx: &mut Context<Editor>,
@@ -365,9 +364,9 @@ fn reject_edits_in_ranges(
}
for (buffer, ranges) in ranges_by_buffer {
thread
.update(cx, |thread, cx| {
thread.reject_edits_in_ranges(buffer, ranges, cx)
action_log
.update(cx, |action_log, cx| {
action_log.reject_edits_in_ranges(buffer, ranges, cx)
})
.detach_and_log_err(cx);
}
@@ -415,7 +414,7 @@ fn update_editor_selection(
};
if let Some(target_hunk) = target_hunk {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
editor.change_selections(Default::default(), window, cx, |selections| {
let next_hunk_start = target_hunk.multi_buffer_range().start;
selections.select_anchor_ranges([next_hunk_start..next_hunk_start]);
})
@@ -465,7 +464,7 @@ impl Item for AgentDiffPane {
}
fn tab_content(&self, params: TabContentParams, _window: &Window, cx: &App) -> AnyElement {
let summary = self.thread.read(cx).summary().unwrap_or("Agent Changes");
let summary = self.agent.read(cx).summary().or_default();
Label::new(format!("Review: {}", summary))
.color(if params.selected {
Color::Default
@@ -515,7 +514,7 @@ impl Item for AgentDiffPane {
where
Self: Sized,
{
Some(cx.new(|cx| Self::new(self.thread.clone(), self.workspace.clone(), window, cx)))
Some(cx.new(|cx| Self::new(self.agent.clone(), self.workspace.clone(), window, cx)))
}
fn is_dirty(&self, cx: &App) -> bool {
@@ -532,12 +531,12 @@ impl Item for AgentDiffPane {
fn save(
&mut self,
format: bool,
options: SaveOptions,
project: Entity<Project>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
self.editor.save(format, project, window, cx)
self.editor.save(options, project, window, cx)
}
fn save_as(
@@ -645,8 +644,8 @@ impl Render for AgentDiffPane {
}
}
fn diff_hunk_controls(thread: &Entity<Thread>) -> editor::RenderDiffHunkControlsFn {
let thread = thread.clone();
fn diff_hunk_controls(action_log: &Entity<ActionLog>) -> editor::RenderDiffHunkControlsFn {
let action_log = action_log.clone();
Arc::new(
move |row,
@@ -664,7 +663,7 @@ fn diff_hunk_controls(thread: &Entity<Thread>) -> editor::RenderDiffHunkControls
hunk_range,
is_created_file,
line_height,
&thread,
&action_log,
editor,
window,
cx,
@@ -680,7 +679,7 @@ fn render_diff_hunk_controls(
hunk_range: Range<editor::Anchor>,
is_created_file: bool,
line_height: Pixels,
thread: &Entity<Thread>,
action_log: &Entity<ActionLog>,
editor: &Entity<Editor>,
window: &mut Window,
cx: &mut App,
@@ -715,14 +714,14 @@ fn render_diff_hunk_controls(
)
.on_click({
let editor = editor.clone();
let thread = thread.clone();
let action_log = action_log.clone();
move |_event, window, cx| {
editor.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
reject_edits_in_ranges(
editor,
&snapshot,
&thread,
&action_log,
vec![hunk_range.start..hunk_range.start],
window,
cx,
@@ -737,14 +736,14 @@ fn render_diff_hunk_controls(
)
.on_click({
let editor = editor.clone();
let thread = thread.clone();
let action_log = action_log.clone();
move |_event, window, cx| {
editor.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
keep_edits_in_ranges(
editor,
&snapshot,
&thread,
&action_log,
vec![hunk_range.start..hunk_range.start],
window,
cx,
@@ -1086,7 +1085,7 @@ impl Render for AgentDiffToolbar {
.child(vertical_divider())
.when_some(editor.read(cx).workspace(), |this, _workspace| {
this.child(
IconButton::new("review", IconName::ListCollapse)
IconButton::new("review", IconName::ListTodo)
.icon_size(IconSize::Small)
.tooltip(Tooltip::for_action_title_in(
"Review All Files",
@@ -1116,8 +1115,13 @@ impl Render for AgentDiffToolbar {
return Empty.into_any();
};
let is_generating = agent_diff.read(cx).thread.read(cx).is_generating();
if is_generating {
let has_pending_edit_tool_use = agent_diff
.read(cx)
.agent
.read(cx)
.has_pending_edit_tool_uses();
if has_pending_edit_tool_use {
return div().px_2().child(spinner_icon).into_any();
}
@@ -1186,7 +1190,7 @@ pub enum EditorState {
}
struct WorkspaceThread {
thread: WeakEntity<Thread>,
agent: WeakEntity<ZedAgentThread>,
_thread_subscriptions: [Subscription; 2],
singleton_editors: HashMap<WeakEntity<Buffer>, HashMap<WeakEntity<Editor>, Subscription>>,
_settings_subscription: Subscription,
@@ -1211,7 +1215,7 @@ impl AgentDiff {
pub fn set_active_thread(
workspace: &WeakEntity<Workspace>,
thread: &Entity<Thread>,
thread: &Entity<ZedAgentThread>,
window: &mut Window,
cx: &mut App,
) {
@@ -1223,11 +1227,11 @@ impl AgentDiff {
fn register_active_thread_impl(
&mut self,
workspace: &WeakEntity<Workspace>,
thread: &Entity<Thread>,
agent: &Entity<ZedAgentThread>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let action_log = thread.read(cx).action_log().clone();
let action_log = agent.read(cx).action_log().clone();
let action_log_subscription = cx.observe_in(&action_log, window, {
let workspace = workspace.clone();
@@ -1236,7 +1240,7 @@ impl AgentDiff {
}
});
let thread_subscription = cx.subscribe_in(&thread, window, {
let thread_subscription = cx.subscribe_in(&agent, window, {
let workspace = workspace.clone();
move |this, _thread, event, window, cx| {
this.handle_thread_event(&workspace, event, window, cx)
@@ -1245,7 +1249,7 @@ impl AgentDiff {
if let Some(workspace_thread) = self.workspace_threads.get_mut(&workspace) {
// replace thread and action log subscription, but keep editors
workspace_thread.thread = thread.downgrade();
workspace_thread.agent = agent.downgrade();
workspace_thread._thread_subscriptions = [action_log_subscription, thread_subscription];
self.update_reviewing_editors(&workspace, window, cx);
return;
@@ -1270,7 +1274,7 @@ impl AgentDiff {
self.workspace_threads.insert(
workspace.clone(),
WorkspaceThread {
thread: thread.downgrade(),
agent: agent.downgrade(),
_thread_subscriptions: [action_log_subscription, thread_subscription],
singleton_editors: HashMap::default(),
_settings_subscription: settings_subscription,
@@ -1318,7 +1322,7 @@ impl AgentDiff {
fn register_review_action<T: Action>(
workspace: &mut Workspace,
review: impl Fn(&Entity<Editor>, &Entity<Thread>, &mut Window, &mut App) -> PostReviewState
review: impl Fn(&Entity<Editor>, &Entity<ZedAgentThread>, &mut Window, &mut App) -> PostReviewState
+ 'static,
this: &Entity<AgentDiff>,
) {
@@ -1361,6 +1365,7 @@ impl AgentDiff {
| ThreadEvent::StreamedAssistantText(_, _)
| ThreadEvent::StreamedAssistantThinking(_, _)
| ThreadEvent::StreamedToolUse { .. }
| ThreadEvent::StreamedToolUse2 { .. }
| ThreadEvent::InvalidToolInput { .. }
| ThreadEvent::MissingToolUse { .. }
| ThreadEvent::MessageAdded(_)
@@ -1373,7 +1378,9 @@ impl AgentDiff {
| ThreadEvent::CheckpointChanged
| ThreadEvent::ToolConfirmationNeeded
| ThreadEvent::ToolUseLimitReached
| ThreadEvent::CancelEditing => {}
| ThreadEvent::CancelEditing
| ThreadEvent::RetriesFailed { .. }
| ThreadEvent::ProfileChanged => {}
}
}
@@ -1478,11 +1485,11 @@ impl AgentDiff {
return;
};
let Some(thread) = workspace_thread.thread.upgrade() else {
let Some(agent) = workspace_thread.agent.upgrade() else {
return;
};
let action_log = thread.read(cx).action_log();
let action_log = agent.read(cx).action_log();
let changed_buffers = action_log.read(cx).changed_buffers(cx);
let mut unaffected = self.reviewing_editors.clone();
@@ -1507,7 +1514,7 @@ impl AgentDiff {
multibuffer.add_diff(diff_handle.clone(), cx);
});
let new_state = if thread.read(cx).is_generating() {
let new_state = if agent.read(cx).is_generating() {
EditorState::Generating
} else {
EditorState::Reviewing
@@ -1520,7 +1527,7 @@ impl AgentDiff {
if previous_state.is_none() {
editor.update(cx, |editor, cx| {
editor.start_temporary_diff_override();
editor.set_render_diff_hunk_controls(diff_hunk_controls(&thread), cx);
editor.set_render_diff_hunk_controls(diff_hunk_controls(&action_log), cx);
editor.set_expand_all_diff_hunks(cx);
editor.register_addon(EditorAgentDiffAddon);
});
@@ -1536,7 +1543,7 @@ impl AgentDiff {
let first_hunk_start = first_hunk.multi_buffer_range().start;
editor.change_selections(
Some(Autoscroll::center()),
SelectionEffects::scroll(Autoscroll::center()),
window,
cx,
|selections| {
@@ -1588,22 +1595,22 @@ impl AgentDiff {
return;
};
let Some(WorkspaceThread { thread, .. }) =
let Some(WorkspaceThread { agent, .. }) =
self.workspace_threads.get(&workspace.downgrade())
else {
return;
};
let Some(thread) = thread.upgrade() else {
let Some(agent) = agent.upgrade() else {
return;
};
AgentDiffPane::deploy(thread, workspace.downgrade(), window, cx).log_err();
AgentDiffPane::deploy(agent, workspace.downgrade(), window, cx).log_err();
}
fn keep_all(
editor: &Entity<Editor>,
thread: &Entity<Thread>,
agent: &Entity<ZedAgentThread>,
window: &mut Window,
cx: &mut App,
) -> PostReviewState {
@@ -1612,7 +1619,7 @@ impl AgentDiff {
keep_edits_in_ranges(
editor,
&snapshot,
thread,
&agent.read(cx).action_log(),
vec![editor::Anchor::min()..editor::Anchor::max()],
window,
cx,
@@ -1623,7 +1630,7 @@ impl AgentDiff {
fn reject_all(
editor: &Entity<Editor>,
thread: &Entity<Thread>,
thread: &Entity<ZedAgentThread>,
window: &mut Window,
cx: &mut App,
) -> PostReviewState {
@@ -1632,7 +1639,7 @@ impl AgentDiff {
reject_edits_in_ranges(
editor,
&snapshot,
thread,
&thread.read(cx).action_log(),
vec![editor::Anchor::min()..editor::Anchor::max()],
window,
cx,
@@ -1643,26 +1650,26 @@ impl AgentDiff {
fn keep(
editor: &Entity<Editor>,
thread: &Entity<Thread>,
agent: &Entity<ZedAgentThread>,
window: &mut Window,
cx: &mut App,
) -> PostReviewState {
editor.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
keep_edits_in_selection(editor, &snapshot, thread, window, cx);
keep_edits_in_selection(editor, &snapshot, &agent.read(cx).action_log(), window, cx);
Self::post_review_state(&snapshot)
})
}
fn reject(
editor: &Entity<Editor>,
thread: &Entity<Thread>,
agent: &Entity<ZedAgentThread>,
window: &mut Window,
cx: &mut App,
) -> PostReviewState {
editor.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
reject_edits_in_selection(editor, &snapshot, thread, window, cx);
reject_edits_in_selection(editor, &snapshot, &agent.read(cx).action_log(), window, cx);
Self::post_review_state(&snapshot)
})
}
@@ -1679,7 +1686,7 @@ impl AgentDiff {
fn review_in_active_editor(
&mut self,
workspace: &mut Workspace,
review: impl Fn(&Entity<Editor>, &Entity<Thread>, &mut Window, &mut App) -> PostReviewState,
review: impl Fn(&Entity<Editor>, &Entity<ZedAgentThread>, &mut Window, &mut App) -> PostReviewState,
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<Task<Result<()>>> {
@@ -1693,14 +1700,13 @@ impl AgentDiff {
return None;
}
let WorkspaceThread { thread, .. } =
self.workspace_threads.get(&workspace.weak_handle())?;
let WorkspaceThread { agent, .. } = self.workspace_threads.get(&workspace.weak_handle())?;
let thread = thread.upgrade()?;
let agent = agent.upgrade()?;
if let PostReviewState::AllReviewed = review(&editor, &thread, window, cx) {
if let PostReviewState::AllReviewed = review(&editor, &agent, window, cx) {
if let Some(curr_buffer) = editor.read(cx).buffer().read(cx).as_singleton() {
let changed_buffers = thread.read(cx).action_log().read(cx).changed_buffers(cx);
let changed_buffers = agent.read(cx).action_log().read(cx).changed_buffers(cx);
let mut keys = changed_buffers.keys().cycle();
keys.find(|k| *k == &curr_buffer);
@@ -1742,7 +1748,8 @@ impl editor::Addon for EditorAgentDiffAddon {
#[cfg(test)]
mod tests {
use super::*;
use crate::{Keep, ThreadStore, thread_store};
use crate::Keep;
use agent::thread_store::{self, ThreadStore};
use agent_settings::AgentSettings;
use assistant_tool::ToolWorkingSet;
use editor::EditorSettings;
@@ -1797,13 +1804,13 @@ mod tests {
})
.await
.unwrap();
let thread = thread_store.update(cx, |store, cx| store.create_thread(cx));
let action_log = thread.read_with(cx, |thread, _| thread.action_log().clone());
let agent = thread_store.update(cx, |store, cx| store.create_thread(cx));
let action_log = agent.read_with(cx, |agent, _| agent.action_log().clone());
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
let agent_diff = cx.new_window_entity(|window, cx| {
AgentDiffPane::new(thread.clone(), workspace.downgrade(), window, cx)
AgentDiffPane::new(agent.clone(), workspace.downgrade(), window, cx)
});
let editor = agent_diff.read_with(cx, |diff, _cx| diff.editor.clone());
@@ -1859,7 +1866,7 @@ mod tests {
// Rejecting a hunk also moves the cursor to the next hunk, possibly cycling if it's at the end.
editor.update_in(cx, |editor, window, cx| {
editor.change_selections(None, window, cx, |selections| {
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
selections.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
});
});
@@ -1891,7 +1898,7 @@ mod tests {
keep_edits_in_ranges(
editor,
&snapshot,
&thread,
&agent.read(cx).action_log(),
vec![position..position],
window,
cx,
@@ -1962,8 +1969,8 @@ mod tests {
})
.await
.unwrap();
let thread = thread_store.update(cx, |store, cx| store.create_thread(cx));
let action_log = thread.read_with(cx, |thread, _| thread.action_log().clone());
let agent = thread_store.update(cx, |store, cx| store.create_thread(cx));
let action_log = agent.read_with(cx, |agent, _| agent.action_log().clone());
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
@@ -1985,7 +1992,7 @@ mod tests {
// Set the active thread
cx.update(|window, cx| {
AgentDiff::set_active_thread(&workspace.downgrade(), &thread, window, cx)
AgentDiff::set_active_thread(&workspace.downgrade(), &agent, window, cx)
});
let buffer1 = project
@@ -2115,7 +2122,7 @@ mod tests {
// Rejecting a hunk also moves the cursor to the next hunk, possibly cycling if it's at the end.
editor1.update_in(cx, |editor, window, cx| {
editor.change_selections(None, window, cx, |selections| {
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
selections.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
});
});
@@ -2142,7 +2149,7 @@ mod tests {
keep_edits_in_ranges(
editor,
&snapshot,
&thread,
&agent.read(cx).action_log(),
vec![position..position],
window,
cx,

View File

@@ -1,22 +1,17 @@
use crate::{
ModelUsageContext,
language_model_selector::{
LanguageModelSelector, ToggleModelSelector, language_model_selector,
},
};
use agent_settings::AgentSettings;
use fs::Fs;
use gpui::{Entity, FocusHandle, SharedString};
use picker::popover_menu::PickerPopoverMenu;
use crate::Thread;
use assistant_context_editor::language_model_selector::{
LanguageModelSelector, ToggleModelSelector, language_model_selector,
};
use language_model::{ConfiguredModel, LanguageModelRegistry};
use picker::popover_menu::PickerPopoverMenu;
use settings::update_settings_file;
use std::sync::Arc;
use ui::{PopoverMenuHandle, Tooltip, prelude::*};
#[derive(Clone)]
pub enum ModelType {
Default(Entity<Thread>),
InlineAssistant,
}
use ui::{ButtonLike, PopoverMenuHandle, Tooltip, prelude::*};
pub struct AgentModelSelector {
selector: Entity<LanguageModelSelector>,
@@ -29,7 +24,7 @@ impl AgentModelSelector {
fs: Arc<dyn Fs>,
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
focus_handle: FocusHandle,
model_type: ModelType,
model_usage_context: ModelUsageContext,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@@ -38,19 +33,14 @@ impl AgentModelSelector {
let fs = fs.clone();
language_model_selector(
{
let model_type = model_type.clone();
move |cx| match &model_type {
ModelType::Default(thread) => thread.read(cx).configured_model(),
ModelType::InlineAssistant => {
LanguageModelRegistry::read_global(cx).inline_assistant_model()
}
}
let model_context = model_usage_context.clone();
move |cx| model_context.configured_model(cx)
},
move |model, cx| {
let provider = model.provider_id().0.to_string();
let model_id = model.id().0.to_string();
match &model_type {
ModelType::Default(thread) => {
match &model_usage_context {
ModelUsageContext::Thread(thread) => {
thread.update(cx, |thread, cx| {
let registry = LanguageModelRegistry::read_global(cx);
if let Some(provider) = registry.provider(&model.provider_id())
@@ -72,7 +62,7 @@ impl AgentModelSelector {
},
);
}
ModelType::InlineAssistant => {
ModelUsageContext::InlineAssistant => {
update_settings_file::<AgentSettings>(
fs.clone(),
cx,
@@ -102,21 +92,37 @@ impl AgentModelSelector {
impl Render for AgentModelSelector {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let focus_handle = self.focus_handle.clone();
let model = self.selector.read(cx).delegate.active_model(cx);
let model_name = model
.as_ref()
.map(|model| model.model.name().0)
.unwrap_or_else(|| SharedString::from("No model selected"));
let provider_icon = model
.as_ref()
.map(|model| model.provider.icon())
.unwrap_or_else(|| IconName::Ai);
let focus_handle = self.focus_handle.clone();
PickerPopoverMenu::new(
self.selector.clone(),
Button::new("active-model", model_name)
.label_size(LabelSize::Small)
.color(Color::Muted)
.icon(IconName::ChevronDown)
.icon_size(IconSize::XSmall)
.icon_position(IconPosition::End)
.icon_color(Color::Muted),
ButtonLike::new("active-model")
.child(
Icon::new(provider_icon)
.color(Color::Muted)
.size(IconSize::XSmall),
)
.child(
Label::new(model_name)
.color(Color::Muted)
.size(LabelSize::Small)
.ml_0p5(),
)
.child(
Icon::new(IconName::ChevronDown)
.color(Color::Muted)
.size(IconSize::XSmall),
),
move |window, cx| {
Tooltip::for_action_in(
"Change Model",

View File

@@ -0,0 +1,292 @@
mod active_thread;
mod agent_configuration;
mod agent_diff;
mod agent_model_selector;
mod agent_panel;
mod buffer_codegen;
mod burn_mode_tooltip;
mod context_picker;
mod context_server_configuration;
mod context_strip;
mod debug;
mod inline_assistant;
mod inline_prompt_editor;
mod language_model_selector;
mod message_editor;
mod profile_selector;
mod slash_command;
mod slash_command_picker;
mod slash_command_settings;
mod terminal_codegen;
mod terminal_inline_assistant;
mod text_thread_editor;
mod thread_history;
mod tool_compatibility;
mod ui;
use std::sync::Arc;
use agent::{ThreadId, ZedAgentThread};
use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
use assistant_slash_command::SlashCommandRegistry;
use client::Client;
use feature_flags::FeatureFlagAppExt as _;
use fs::Fs;
use gpui::{Action, App, Entity, actions};
use language::LanguageRegistry;
use language_model::{
ConfiguredModel, LanguageModel, LanguageModelId, LanguageModelProviderId, LanguageModelRegistry,
};
use prompt_store::PromptBuilder;
use schemars::JsonSchema;
use serde::Deserialize;
use settings::{Settings as _, SettingsStore};
pub use crate::active_thread::ActiveThread;
use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};
pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate};
pub use crate::inline_assistant::InlineAssistant;
use crate::slash_command_settings::SlashCommandSettings;
pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
pub use text_thread_editor::{AgentPanelDelegate, TextThreadEditor};
pub use ui::preview::{all_agent_previews, get_agent_preview};
actions!(
agent,
[
NewTextThread,
ToggleContextPicker,
ToggleNavigationMenu,
ToggleOptionsMenu,
DeleteRecentlyOpenThread,
ToggleProfileSelector,
RemoveAllContext,
ExpandMessageEditor,
OpenHistory,
AddContextServer,
RemoveSelectedThread,
Chat,
ChatWithFollow,
CycleNextInlineAssist,
CyclePreviousInlineAssist,
FocusUp,
FocusDown,
FocusLeft,
FocusRight,
RemoveFocusedContext,
AcceptSuggestedContext,
OpenActiveThreadAsMarkdown,
OpenAgentDiff,
Keep,
Reject,
RejectAll,
KeepAll,
Follow,
ResetTrialUpsell,
ResetTrialEndUpsell,
ContinueThread,
ContinueWithBurnMode,
ToggleBurnMode,
]
);
#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema, Action)]
#[action(namespace = agent)]
pub struct NewThread {
#[serde(default)]
from_thread_id: Option<ThreadId>,
}
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema, Action)]
#[action(namespace = agent)]
pub struct ManageProfiles {
#[serde(default)]
pub customize_tools: Option<AgentProfileId>,
}
impl ManageProfiles {
pub fn customize_tools(profile_id: AgentProfileId) -> Self {
Self {
customize_tools: Some(profile_id),
}
}
}
#[derive(Clone)]
pub(crate) enum ModelUsageContext {
Thread(Entity<ZedAgentThread>),
InlineAssistant,
}
impl ModelUsageContext {
pub fn configured_model(&self, cx: &App) -> Option<ConfiguredModel> {
match self {
Self::Thread(thread) => thread.read(cx).configured_model(),
Self::InlineAssistant => {
LanguageModelRegistry::read_global(cx).inline_assistant_model()
}
}
}
pub fn language_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> {
self.configured_model(cx)
.map(|configured_model| configured_model.model)
}
}
/// Initializes the `agent` crate.
pub fn init(
fs: Arc<dyn Fs>,
client: Arc<Client>,
prompt_builder: Arc<PromptBuilder>,
language_registry: Arc<LanguageRegistry>,
is_eval: bool,
cx: &mut App,
) {
AgentSettings::register(cx);
SlashCommandSettings::register(cx);
assistant_context::init(client.clone(), cx);
rules_library::init(cx);
if !is_eval {
// Initializing the language model from the user settings messes with the eval, so we only initialize them when
// we're not running inside of the eval.
init_language_model_settings(cx);
}
assistant_slash_command::init(cx);
agent::init(cx);
agent_panel::init(cx);
context_server_configuration::init(language_registry.clone(), fs.clone(), cx);
TextThreadEditor::init(cx);
register_slash_commands(cx);
inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
terminal_inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
indexed_docs::init(cx);
cx.observe_new(move |workspace, window, cx| {
ConfigureContextServerModal::register(workspace, language_registry.clone(), window, cx)
})
.detach();
cx.observe_new(ManageProfilesModal::register).detach();
}
fn init_language_model_settings(cx: &mut App) {
update_active_language_model_from_settings(cx);
cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
.detach();
cx.subscribe(
&LanguageModelRegistry::global(cx),
|_, event: &language_model::Event, cx| match event {
language_model::Event::ProviderStateChanged
| language_model::Event::AddedProvider(_)
| language_model::Event::RemovedProvider(_) => {
update_active_language_model_from_settings(cx);
}
_ => {}
},
)
.detach();
}
fn update_active_language_model_from_settings(cx: &mut App) {
let settings = AgentSettings::get_global(cx);
fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel {
language_model::SelectedModel {
provider: LanguageModelProviderId::from(selection.provider.0.clone()),
model: LanguageModelId::from(selection.model.clone()),
}
}
let default = to_selected_model(&settings.default_model);
let inline_assistant = settings
.inline_assistant_model
.as_ref()
.map(to_selected_model);
let commit_message = settings
.commit_message_model
.as_ref()
.map(to_selected_model);
let thread_summary = settings
.thread_summary_model
.as_ref()
.map(to_selected_model);
let inline_alternatives = settings
.inline_alternatives
.iter()
.map(to_selected_model)
.collect::<Vec<_>>();
LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
registry.select_default_model(Some(&default), cx);
registry.select_inline_assistant_model(inline_assistant.as_ref(), cx);
registry.select_commit_message_model(commit_message.as_ref(), cx);
registry.select_thread_summary_model(thread_summary.as_ref(), cx);
registry.select_inline_alternative_models(inline_alternatives, cx);
});
}
fn register_slash_commands(cx: &mut App) {
let slash_command_registry = SlashCommandRegistry::global(cx);
slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
slash_command_registry
.register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
slash_command_registry
.register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
if is_enabled {
slash_command_registry.register_command(
assistant_slash_commands::StreamingExampleSlashCommand,
false,
);
}
}
})
.detach();
update_slash_commands_from_settings(cx);
cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
.detach();
}
fn update_slash_commands_from_settings(cx: &mut App) {
let slash_command_registry = SlashCommandRegistry::global(cx);
let settings = SlashCommandSettings::get_global(cx);
if settings.docs.enabled {
slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
} else {
slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
}
if settings.cargo_workspace.enabled {
slash_command_registry
.register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
} else {
slash_command_registry
.unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
}
}

View File

@@ -1,6 +1,8 @@
use crate::context::ContextLoadResult;
use crate::inline_prompt_editor::CodegenStatus;
use crate::{context::load_context, context_store::ContextStore};
use agent::{
ContextStore,
context::{ContextLoadResult, load_context},
};
use agent_settings::AgentSettings;
use anyhow::{Context as _, Result};
use client::telemetry::Telemetry;
@@ -18,8 +20,7 @@ use language_model::{
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
use project::Project;
use prompt_store::PromptBuilder;
use prompt_store::PromptStore;
use prompt_store::{PromptBuilder, PromptStore};
use rope::Rope;
use smol::future::FutureExt;
use std::{
@@ -386,8 +387,10 @@ impl CodegenAlternative {
async { Ok(LanguageModelTextStream::default()) }.boxed_local()
} else {
let request = self.build_request(&model, user_prompt, cx)?;
cx.spawn(async move |_, cx| model.stream_completion_text(request.await, &cx).await)
.boxed_local()
cx.spawn(async move |_, cx| {
Ok(model.stream_completion_text(request.await, &cx).await?)
})
.boxed_local()
};
self.handle_stream(telemetry_id, provider_id.to_string(), api_key, stream, cx);
Ok(())
@@ -1091,15 +1094,9 @@ mod tests {
};
use language_model::{LanguageModelRegistry, TokenUsage};
use rand::prelude::*;
use serde::Serialize;
use settings::SettingsStore;
use std::{future, sync::Arc};
#[derive(Serialize)]
pub struct DummyCompletionRequest {
pub name: String,
}
#[gpui::test(iterations = 10)]
async fn test_transform_autoindent(cx: &mut TestAppContext, mut rng: StdRng) {
init_test(cx);

View File

@@ -1,11 +1,11 @@
use gpui::{Context, FontWeight, IntoElement, Render, Window};
use ui::{prelude::*, tooltip_container};
pub struct MaxModeTooltip {
pub struct BurnModeTooltip {
selected: bool,
}
impl MaxModeTooltip {
impl BurnModeTooltip {
pub fn new() -> Self {
Self { selected: false }
}
@@ -16,7 +16,7 @@ impl MaxModeTooltip {
}
}
impl Render for MaxModeTooltip {
impl Render for BurnModeTooltip {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let (icon, color) = if self.selected {
(IconName::ZedBurnModeOn, Color::Error)

View File

@@ -37,10 +37,12 @@ use uuid::Uuid;
use workspace::{Workspace, notifications::NotifyResultExt};
use crate::AgentPanel;
use crate::context::RULES_ICON;
use crate::context_store::ContextStore;
use crate::thread::ThreadId;
use crate::thread_store::{TextThreadStore, ThreadStore};
use agent::{
ThreadId,
context::RULES_ICON,
context_store::ContextStore,
thread_store::{TextThreadStore, ThreadStore},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ContextPickerEntry {
@@ -659,7 +661,7 @@ fn recent_context_picker_entries(
let active_thread_id = workspace
.panel::<AgentPanel>(cx)
.and_then(|panel| Some(panel.read(cx).active_thread()?.read(cx).id()));
.and_then(|panel| Some(panel.read(cx).active_thread(cx)?.read(cx).id()));
if let Some((thread_store, text_thread_store)) = thread_store
.and_then(|store| store.upgrade())
@@ -928,8 +930,8 @@ impl MentionLink {
format!(
"[@{} ({}-{})]({}:{}:{}-{})",
file_name,
line_range.start,
line_range.end,
line_range.start + 1,
line_range.end + 1,
Self::SELECTION,
full_path,
line_range.start,

View File

@@ -1,10 +1,9 @@
use std::cell::RefCell;
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use agent::context_store::ContextStore;
use anyhow::Result;
use editor::{CompletionProvider, Editor, ExcerptId, ToOffset as _};
use file_icons::FileIcons;
@@ -22,10 +21,11 @@ use ui::prelude::*;
use util::ResultExt as _;
use workspace::Workspace;
use crate::Thread;
use crate::context::{AgentContextHandle, AgentContextKey, ContextCreasesAddon, RULES_ICON};
use crate::context_store::ContextStore;
use crate::thread_store::{TextThreadStore, ThreadStore};
use agent::{
ZedAgentThread,
context::{AgentContextHandle, AgentContextKey, RULES_ICON},
thread_store::{TextThreadStore, ThreadStore},
};
use super::fetch_context_picker::fetch_url_content;
use super::file_context_picker::{FileMatch, search_files};
@@ -37,6 +37,7 @@ use super::{
ContextPickerAction, ContextPickerEntry, ContextPickerMode, MentionLink, RecentEntry,
available_context_picker_entries, recent_context_picker_entries, selection_ranges,
};
use crate::message_editor::ContextCreasesAddon;
pub(crate) enum Match {
File(FileMatch),
@@ -72,7 +73,7 @@ fn search(
recent_entries: Vec<RecentEntry>,
prompt_store: Option<Entity<PromptStore>>,
thread_store: Option<WeakEntity<ThreadStore>>,
text_thread_context_store: Option<WeakEntity<assistant_context_editor::ContextStore>>,
text_thread_context_store: Option<WeakEntity<assistant_context::ContextStore>>,
workspace: Entity<Workspace>,
cx: &mut App,
) -> Task<Vec<Match>> {
@@ -216,6 +217,7 @@ fn search(
&entry_candidates,
&query,
false,
true,
100,
&Arc::new(AtomicBool::default()),
executor,
@@ -447,7 +449,7 @@ impl ContextPickerCompletionProvider {
let context_store = context_store.clone();
let thread_store = thread_store.clone();
window.spawn::<_, Option<_>>(cx, async move |cx| {
let thread: Entity<Thread> = thread_store
let thread: Entity<ZedAgentThread> = thread_store
.update_in(cx, |thread_store, window, cx| {
thread_store.open_thread(&thread_id, window, cx)
})
@@ -767,7 +769,7 @@ impl CompletionProvider for ContextPickerCompletionProvider {
let snapshot = buffer.read(cx).snapshot();
let source_range = snapshot.anchor_before(state.source_range.start)
..snapshot.anchor_before(state.source_range.end);
..snapshot.anchor_after(state.source_range.end);
let thread_store = self.thread_store.clone();
let text_thread_store = self.text_thread_store.clone();
@@ -912,22 +914,13 @@ impl CompletionProvider for ContextPickerCompletionProvider {
})
}
fn resolve_completions(
&self,
_buffer: Entity<Buffer>,
_completion_indices: Vec<usize>,
_completions: Rc<RefCell<Box<[Completion]>>>,
_cx: &mut Context<Editor>,
) -> Task<Result<bool>> {
Task::ready(Ok(true))
}
fn is_completion_trigger(
&self,
buffer: &Entity<language::Buffer>,
position: language::Anchor,
_: &str,
_: bool,
_text: &str,
_trigger_in_words: bool,
_menu_is_open: bool,
cx: &mut Context<Editor>,
) -> bool {
let buffer = buffer.read(cx);
@@ -1076,8 +1069,8 @@ mod tests {
use project::{Project, ProjectPath};
use serde_json::json;
use settings::SettingsStore;
use std::ops::Deref;
use util::{path, separator};
use std::{ops::Deref, rc::Rc};
use util::path;
use workspace::{AppState, Item};
#[test]
@@ -1228,14 +1221,14 @@ mod tests {
let mut cx = VisualTestContext::from_window(*window.deref(), cx);
let paths = vec![
separator!("a/one.txt"),
separator!("a/two.txt"),
separator!("a/three.txt"),
separator!("a/four.txt"),
separator!("b/five.txt"),
separator!("b/six.txt"),
separator!("b/seven.txt"),
separator!("b/eight.txt"),
path!("a/one.txt"),
path!("a/two.txt"),
path!("a/three.txt"),
path!("a/four.txt"),
path!("b/five.txt"),
path!("b/six.txt"),
path!("b/seven.txt"),
path!("b/eight.txt"),
];
let mut opened_editors = Vec::new();

View File

@@ -2,6 +2,7 @@ use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use agent::context_store::ContextStore;
use anyhow::{Context as _, Result, bail};
use futures::AsyncReadExt as _;
use gpui::{App, DismissEvent, Entity, FocusHandle, Focusable, Task, WeakEntity};
@@ -12,7 +13,6 @@ use ui::{Context, ListItem, Window, prelude::*};
use workspace::Workspace;
use crate::context_picker::ContextPicker;
use crate::context_store::ContextStore;
pub struct FetchContextPicker {
picker: Entity<Picker<FetchContextPickerDelegate>>,

View File

@@ -14,7 +14,7 @@ use util::ResultExt as _;
use workspace::Workspace;
use crate::context_picker::ContextPicker;
use crate::context_store::{ContextStore, FileInclusion};
use agent::context_store::{ContextStore, FileInclusion};
pub struct FileContextPicker {
picker: Entity<Picker<FileContextPickerDelegate>>,

View File

@@ -7,9 +7,9 @@ use prompt_store::{PromptId, PromptStore, UserPromptId};
use ui::{ListItem, prelude::*};
use util::ResultExt as _;
use crate::context::RULES_ICON;
use crate::context_picker::ContextPicker;
use crate::context_store::{self, ContextStore};
use agent::context::RULES_ICON;
use agent::context_store::{self, ContextStore};
pub struct RulesContextPicker {
picker: Entity<Picker<RulesContextPickerDelegate>>,

View File

@@ -14,9 +14,9 @@ use ui::{ListItem, prelude::*};
use util::ResultExt as _;
use workspace::Workspace;
use crate::context::AgentContextHandle;
use crate::context_picker::ContextPicker;
use crate::context_store::ContextStore;
use agent::context::AgentContextHandle;
use agent::context_store::ContextStore;
pub struct SymbolContextPicker {
picker: Entity<Picker<SymbolContextPickerDelegate>>,
@@ -307,6 +307,7 @@ pub(crate) fn search_symbols(
&visible_match_candidates,
&query,
false,
true,
MAX_MATCHES,
&cancellation_flag,
cx.background_executor().clone(),
@@ -315,6 +316,7 @@ pub(crate) fn search_symbols(
&external_match_candidates,
&query,
false,
true,
MAX_MATCHES - visible_matches.len().min(MAX_MATCHES),
&cancellation_flag,
cx.background_executor().clone(),

View File

@@ -9,9 +9,11 @@ use picker::{Picker, PickerDelegate};
use ui::{ListItem, prelude::*};
use crate::context_picker::ContextPicker;
use crate::context_store::{self, ContextStore};
use crate::thread::ThreadId;
use crate::thread_store::{TextThreadStore, ThreadStore};
use agent::{
ThreadId,
context_store::{self, ContextStore},
thread_store::{TextThreadStore, ThreadStore},
};
pub struct ThreadContextPicker {
picker: Entity<Picker<ThreadContextPickerDelegate>>,
@@ -282,15 +284,18 @@ pub fn unordered_thread_entries(
text_thread_store: Entity<TextThreadStore>,
cx: &App,
) -> impl Iterator<Item = (DateTime<Utc>, ThreadContextEntry)> {
let threads = thread_store.read(cx).unordered_threads().map(|thread| {
(
thread.updated_at,
ThreadContextEntry::Thread {
id: thread.id.clone(),
title: thread.summary.clone(),
},
)
});
let threads = thread_store
.read(cx)
.reverse_chronological_threads()
.map(|thread| {
(
thread.updated_at,
ThreadContextEntry::Thread {
id: thread.id.clone(),
title: thread.summary.clone(),
},
)
});
let text_threads = text_thread_store
.read(cx)
@@ -300,7 +305,7 @@ pub fn unordered_thread_entries(
context.mtime.to_utc(),
ThreadContextEntry::Context {
path: context.path.clone(),
title: context.title.clone().into(),
title: context.title.clone(),
},
)
});
@@ -339,6 +344,7 @@ pub(crate) fn search_threads(
&candidates,
&query,
false,
true,
100,
&cancellation_flag,
executor,

View File

@@ -0,0 +1,116 @@
use std::sync::Arc;
use context_server::ContextServerId;
use extension::ExtensionManifest;
use fs::Fs;
use gpui::WeakEntity;
use language::LanguageRegistry;
use project::project_settings::ProjectSettings;
use settings::update_settings_file;
use ui::prelude::*;
use util::ResultExt;
use workspace::Workspace;
use crate::agent_configuration::ConfigureContextServerModal;
pub(crate) fn init(language_registry: Arc<LanguageRegistry>, fs: Arc<dyn Fs>, cx: &mut App) {
cx.observe_new(move |_: &mut Workspace, window, cx| {
let Some(window) = window else {
return;
};
if let Some(extension_events) = extension::ExtensionEvents::try_global(cx).as_ref() {
cx.subscribe_in(extension_events, window, {
let language_registry = language_registry.clone();
let fs = fs.clone();
move |_, _, event, window, cx| match event {
extension::Event::ExtensionInstalled(manifest) => {
show_configure_mcp_modal(
language_registry.clone(),
manifest,
cx.weak_entity(),
window,
cx,
);
}
extension::Event::ExtensionUninstalled(manifest) => {
remove_context_server_settings(
manifest.context_servers.keys().cloned().collect(),
fs.clone(),
cx,
);
}
extension::Event::ConfigureExtensionRequested(manifest) => {
if !manifest.context_servers.is_empty() {
show_configure_mcp_modal(
language_registry.clone(),
manifest,
cx.weak_entity(),
window,
cx,
);
}
}
_ => {}
}
})
.detach();
} else {
log::info!(
"No extension events global found. Skipping context server configuration wizard"
);
}
})
.detach();
}
fn remove_context_server_settings(
context_server_ids: Vec<Arc<str>>,
fs: Arc<dyn Fs>,
cx: &mut App,
) {
update_settings_file::<ProjectSettings>(fs, cx, move |settings, _| {
settings
.context_servers
.retain(|server_id, _| !context_server_ids.contains(server_id));
});
}
fn show_configure_mcp_modal(
language_registry: Arc<LanguageRegistry>,
manifest: &Arc<ExtensionManifest>,
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<'_, Workspace>,
) {
if !window.is_window_active() {
return;
}
let ids = manifest.context_servers.keys().cloned().collect::<Vec<_>>();
if ids.is_empty() {
return;
}
window
.spawn(cx, async move |cx| {
for id in ids {
let Some(task) = cx
.update(|window, cx| {
ConfigureContextServerModal::show_modal_for_existing_server(
ContextServerId(id.clone()),
language_registry.clone(),
workspace.clone(),
window,
cx,
)
})
.ok()
else {
continue;
};
task.await.log_err();
}
})
.detach();
}

View File

@@ -1,7 +1,15 @@
use std::path::Path;
use std::rc::Rc;
use assistant_context_editor::AssistantContext;
use crate::{
AcceptSuggestedContext, AgentPanel, FocusDown, FocusLeft, FocusRight, FocusUp,
ModelUsageContext, RemoveAllContext, RemoveFocusedContext, ToggleContextPicker,
context_picker::ContextPicker,
ui::{AddedContext, ContextPill},
};
use agent::context_store::SuggestedContext;
use agent::{
context::AgentContextHandle,
context_store::ContextStore,
thread_store::{TextThreadStore, ThreadStore},
};
use collections::HashSet;
use editor::Editor;
use file_icons::FileIcons;
@@ -10,22 +18,11 @@ use gpui::{
Subscription, WeakEntity,
};
use itertools::Itertools;
use language::Buffer;
use project::ProjectItem;
use std::{path::Path, rc::Rc};
use ui::{PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
use workspace::Workspace;
use crate::context::{AgentContextHandle, ContextKind};
use crate::context_picker::ContextPicker;
use crate::context_store::ContextStore;
use crate::thread::Thread;
use crate::thread_store::{TextThreadStore, ThreadStore};
use crate::ui::{AddedContext, ContextPill};
use crate::{
AcceptSuggestedContext, AgentPanel, FocusDown, FocusLeft, FocusRight, FocusUp,
RemoveAllContext, RemoveFocusedContext, ToggleContextPicker,
};
pub struct ContextStrip {
context_store: Entity<ContextStore>,
context_picker: Entity<ContextPicker>,
@@ -37,6 +34,7 @@ pub struct ContextStrip {
_subscriptions: Vec<Subscription>,
focused_index: Option<usize>,
children_bounds: Option<Vec<Bounds<Pixels>>>,
model_usage_context: ModelUsageContext,
}
impl ContextStrip {
@@ -47,6 +45,7 @@ impl ContextStrip {
text_thread_store: Option<WeakEntity<TextThreadStore>>,
context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
suggest_context_kind: SuggestContextKind,
model_usage_context: ModelUsageContext,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@@ -81,6 +80,7 @@ impl ContextStrip {
_subscriptions: subscriptions,
focused_index: None,
children_bounds: None,
model_usage_context,
}
}
@@ -98,11 +98,20 @@ impl ContextStrip {
.as_ref()
.and_then(|thread_store| thread_store.upgrade())
.and_then(|thread_store| thread_store.read(cx).prompt_store().as_ref());
let current_model = self.model_usage_context.language_model(cx);
self.context_store
.read(cx)
.context()
.flat_map(|context| {
AddedContext::new_pending(context.clone(), prompt_store, project, cx)
AddedContext::new_pending(
context.clone(),
prompt_store,
project,
current_model.as_ref(),
cx,
)
})
.collect::<Vec<_>>()
} else {
@@ -152,7 +161,7 @@ impl ContextStrip {
let workspace = self.workspace.upgrade()?;
let panel = workspace.read(cx).panel::<AgentPanel>(cx)?.read(cx);
if let Some(active_thread) = panel.active_thread() {
if let Some(active_thread) = panel.active_thread(cx) {
let weak_active_thread = active_thread.downgrade();
let active_thread = active_thread.read(cx);
@@ -563,46 +572,3 @@ pub enum SuggestContextKind {
File,
Thread,
}
#[derive(Clone)]
pub enum SuggestedContext {
File {
name: SharedString,
icon_path: Option<SharedString>,
buffer: WeakEntity<Buffer>,
},
Thread {
name: SharedString,
thread: WeakEntity<Thread>,
},
TextThread {
name: SharedString,
context: WeakEntity<AssistantContext>,
},
}
impl SuggestedContext {
pub fn name(&self) -> &SharedString {
match self {
Self::File { name, .. } => name,
Self::Thread { name, .. } => name,
Self::TextThread { name, .. } => name,
}
}
pub fn icon_path(&self) -> Option<SharedString> {
match self {
Self::File { icon_path, .. } => icon_path.clone(),
Self::Thread { .. } => None,
Self::TextThread { .. } => None,
}
}
pub fn kind(&self) -> ContextKind {
match self {
Self::File { .. } => ContextKind::File,
Self::Thread { .. } => ContextKind::Thread,
Self::TextThread { .. } => ContextKind::TextThread,
}
}
}

View File

@@ -1,7 +1,7 @@
#![allow(unused, dead_code)]
use client::{ModelRequestUsage, RequestUsage};
use gpui::Global;
use language_model::RequestUsage;
use std::ops::{Deref, DerefMut};
use ui::prelude::*;
use zed_llm_client::{Plan, UsageLimit};
@@ -17,7 +17,7 @@ pub struct DebugAccountState {
pub enabled: bool,
pub trial_expired: bool,
pub plan: Plan,
pub custom_prompt_usage: RequestUsage,
pub custom_prompt_usage: ModelRequestUsage,
pub usage_based_billing_enabled: bool,
pub monthly_spending_cap: i32,
pub custom_edit_prediction_usage: UsageLimit,
@@ -43,7 +43,7 @@ impl DebugAccountState {
self
}
pub fn set_custom_prompt_usage(&mut self, custom_prompt_usage: RequestUsage) -> &mut Self {
pub fn set_custom_prompt_usage(&mut self, custom_prompt_usage: ModelRequestUsage) -> &mut Self {
self.custom_prompt_usage = custom_prompt_usage;
self
}
@@ -76,10 +76,10 @@ impl Default for DebugAccountState {
enabled: false,
trial_expired: false,
plan: Plan::ZedFree,
custom_prompt_usage: RequestUsage {
custom_prompt_usage: ModelRequestUsage(RequestUsage {
limit: UsageLimit::Unlimited,
amount: 0,
},
}),
usage_based_billing_enabled: false,
// $50.00
monthly_spending_cap: 5000,

Some files were not shown because too many files have changed in this diff Show More