Compare commits

..

79 Commits

Author SHA1 Message Date
Joseph T. Lyons
12a274bd3c zed 0.184.9 2025-05-02 11:53:20 -04:00
gcp-cherry-pick-bot[bot]
6bc494c248 Properly score fuzzy match queries with multiple chars in lower case (cherry-pick #29794) (#29797)
Cherry-picked Properly score fuzzy match queries with multiple chars in
lower case (#29794)

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

Release Notes:

- Fixed file finder crashing for certain file names with multiple chars
in lowercase form

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-05-02 18:25:14 +03:00
Kirill Bulatov
3c0a8da425 Fix tasks not being stopped on reruns (#29786)
Follow-up of https://github.com/zed-industries/zed/pull/28993

* Tone down tasks' cancellation logging
* Fix task terminals' leak, disallowing to fully cancel the task by
dropping the terminal off the pane:

f619d5f02a/crates/terminal_view/src/terminal_panel.rs (L1464-L1471)

Release Notes:

- Fixed tasks not being stopped on reruns
2025-05-02 16:44:19 +03:00
Joseph T. Lyons
b64161d45f v0.184.x stable 2025-04-30 11:33:20 -04:00
Smit Barmase
49bc7a70b1 editor: Improve code completions by prioritizing prefix matching (#29456)
- Use common prefix length-based matching as primary criteria.
- Test added for multiple cases.

Before:
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/8c653225-cac2-41bd-95f0-0fb8724284c9"
/>

After:
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/a3d59399-cff2-435d-9b56-69a530f35da4"
/>

Release Notes:

- Fixed issues with code completions where they wouldn't show
completions with matched prefix at top.
2025-04-30 10:59:36 -04:00
Piotr Osiewicz
0ca114e21f python: Do not look up venv path from source file path (#29676)
Closes #ISSUE

Release Notes:

- N/A
2025-04-30 10:54:25 -04:00
Zed Bot
dee476b6b4 Bump to 0.184.8 for @osiewicz 2025-04-30 11:01:58 +00:00
gcp-cherry-pick-bot[bot]
be439e9be8 Revert "python: Enable subroot detection for pylsp and pyright (#27364)" (cherry-pick #29658) (#29661)
Cherry-picked Revert "python: Enable subroot detection for pylsp and
pyright (#27364)" (#29658)

This reverts commit e661a0afd6.

Closes #ISSUE

Release Notes:

- Reverted changes to Python subroot detection which could have caused
multiple python processes to be spawned when working in projects with
multiple `pyproject.toml` files.

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-04-30 13:00:00 +02:00
Conrad Irwin
7609894046 Maybe fix panic (#29352)
Since around the time we shipped block diagnostics, we've been seeing an
out of range panic in the editor.

Although the code is heavily inlined, so the stacktrace is missing, this
seems like a likely place that indexing may have gone wrong.

Release Notes:

- Fixed a rare panic in the editor
2025-04-29 19:51:13 -06:00
Joseph T. Lyons
92d6ee1ce7 zed 0.184.7 2025-04-29 14:46:24 -04:00
Joseph T. Lyons
6b221fbb2f Derive Default for TaskId and SpawnInTerminal
These were made Default in #29208, but rather than pull that entire commit in to fix this, I'm resolving manually.
2025-04-29 14:44:16 -04:00
Danilo Leal
640610530a agent: Add icon for Zed's max mode (#29610)
Release Notes:

- N/A
2025-04-29 14:32:50 -04:00
Agus Zubiaga
9bd7dcbbb2 agent: Enrich grep tool output with syntax information (#29601)
The `grep` tool used to include 4 lines of context around the match, but
the lines included would often be unhelpful. This PR improves this
behavior by using the range of the parent syntax node that contains the
full line(s) matched.

The match headers will also now include symbol breadcrumbs so that the
model can already gather code structure before/without reading files.

````md
### impl GitRepository for RealGitRepository › fn compare_checkpoints › L1278-1284
```rust
                let result = git
                    .run(&[
                        "diff-tree",
                        "--quiet",
                        &left.commit_sha.to_string(),
                        &right.commit_sha.to_string(),
                    ])
```
````

This positively impacts the `add_arg_to_trait_method` eval example with
better diff output, fewer tool failures, and reduced total turns.

Note: We have some plans to use a an "elision" approach where we would
combine all matches for a given file, skipping lines between them while
keeping symbol declaration lines. The theory is that this would be map
more closely to the expected input for edits. For now, this PR is a
significant improvement.

Release Notes:

- Agent: Enrich `grep` tool output with syntax information
2025-04-29 13:44:36 -04:00
Shardul Vaidya
597eb8f567 bedrock: Preserve thinking blocks for Bedrock (#29602)
Fixes a regression from #29055, resolves #29290

Release Notes:

- agent: Fixed a regression that rendered Claude 3.7 Thinking unusable
on Bedrock.
2025-04-29 13:44:28 -04:00
João Marcos
c14313e158 agent: Create TerminalToolCard and display shell output while it's running (#29546)
Also, don't require a worktree to run the terminal tool.

Release Notes:

- N/A
2025-04-29 13:44:14 -04:00
Marshall Bowers
1bc14b7298 language_models: Change default fast model for Zed provider (#29600)
This PR changes the default fast model for the Zed provider from Claude
3.5 Haiku to Claude 3.5 Sonnet.

We don't offer Claude 3.5 Haiku to users.

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

Release Notes:

- agent: Changed the default fast model for the Zed provider to Claude
3.5 Sonnet.
2025-04-29 13:44:08 -04:00
Richard Feldman
a108c68922 Re-add code block formatting instructions (#29574)
Re-enabled instructions about code block formatting.

In practice, the model doesn't seem to use these very often, but there's
no negative effect on evals. In a future PR, I'll experiment with adding
more evals around the model actually using the code blocks.

2 runs before: (`--repetitions=8`)
```
=================================================================
                            AGGREGATE
=================================================================


4 examples failed to run!
Average programmatic score: 37%
Average diff score: 66%
Average thread score: 93%


-----------------------------------------------------------------
                     CUMULATIVE TOOL METRICS
-----------------------------------------------------------------

┌──────────────────────────────┬──────────┬──────────┬──────────┐
│             Tool             │   Uses   │ Failures │   Rate   │
├──────────────────────────────┼──────────┼──────────┼──────────┤
│edit_file                     │   398    │    53    │   13%    │
│terminal                      │    11    │    1     │    9%    │
│create_file                   │    40    │    2     │    5%    │
│read_file                     │   245    │    8     │    3%    │
│find_path                     │    48    │    0     │    0%    │
│list_directory                │    13    │    0     │    0%    │
│grep                          │   133    │    0     │    0%    │
│thinking                      │    18    │    0     │    0%    │
│diagnostics                   │   130    │    0     │    0%    │
```

```
=================================================================
                            AGGREGATE
=================================================================


1 examples failed to run!
Average programmatic score: 41%
Average diff score: 68%
Average thread score: 96%


-----------------------------------------------------------------
                     CUMULATIVE TOOL METRICS
-----------------------------------------------------------------

┌──────────────────────────────┬──────────┬──────────┬──────────┐
│             Tool             │   Uses   │ Failures │   Rate   │
├──────────────────────────────┼──────────┼──────────┼──────────┤
│fetch                         │    1     │    1     │   100%   │
│edit_file                     │   553    │    63    │   11%    │
│read_file                     │   349    │    3     │    1%    │
│diagnostics                   │   158    │    0     │    0%    │
│find_path                     │    70    │    0     │    0%    │
│list_directory                │    10    │    0     │    0%    │
│thinking                      │    45    │    0     │    0%    │
│grep                          │   213    │    0     │    0%    │
│create_file                   │    24    │    0     │    0%    │
│terminal                      │    17    │    0     │    0%    │
└──────────────────────────────┴──────────┴──────────┴──────────┘
```

1 run after this change:

```
=================================================================
                            AGGREGATE
=================================================================

Average programmatic score: 42%
Average diff score: 74%
Average thread score: 100%


-----------------------------------------------------------------
                     CUMULATIVE TOOL METRICS
-----------------------------------------------------------------

┌──────────────────────────────┬──────────┬──────────┬──────────┐
│             Tool             │   Uses   │ Failures │   Rate   │
├──────────────────────────────┼──────────┼──────────┼──────────┤
│edit_file                     │   534    │    92    │   17%    │
│read_file                     │   325    │    6     │    2%    │
│list_directory                │    6     │    0     │    0%    │
│thinking                      │    12    │    0     │    0%    │
│create_file                   │    16    │    0     │    0%    │
│diagnostics                   │    49    │    0     │    0%    │
│grep                          │   234    │    0     │    0%    │
│find_path                     │    65    │    0     │    0%    │
│terminal                      │    38    │    0     │    0%    │
└──────────────────────────────┴──────────┴──────────┴──────────┘
```


Release Notes:

- N/A
2025-04-29 13:43:45 -04:00
Bennet Bo Fenner
a21157143a agent: Support images via @file and the file context picker (#29596)
Release Notes:

- agent: Add support for @mentioning images
- agent: Add support for including images via file context picker

---------

Co-authored-by: Oleksiy Syvokon <oleksiy.syvokon@gmail.com>
2025-04-29 13:43:36 -04:00
Joseph T. Lyons
747471c1a5 zed 0.184.6 2025-04-29 08:01:55 -04:00
Michael Sloan
8acd8a273d Fix gemini model token limits (#29584)
Release Notes:

- N/A
2025-04-29 08:00:20 -04:00
Bennet Bo Fenner
13854b47a0 agent: Improve MCP tools compatibility with Gemini models (#29411)
Release Notes:

- agent: Improve MCP tools compatibility with Gemini models
2025-04-29 07:59:11 -04:00
Conrad Irwin
f50dc72240 Fix context_stack race in KeyContextView (#29324)
cc @notpeter

Before this change we used our own copy of `cx.key_context()` when
matching.
This led to races where the context queried could be either before (or
after) the
context used in dispatching.

To avoid the race, gpui now passes out the context stack actually used
instead.

Release Notes:

- Fixed a bug where the Key Context View could show the incorrect
context,
  causing confusing results.
2025-04-28 21:52:23 -04:00
Peter Tripp
9e03eacda5 ollama: Add Qwen3 and Gemma3 (default to 16K context) (#29580)
If you have the VRAM you can increase the context by adding this to your
settings.json:

```json
  "language_models": {
    "ollama": {
      "available_models": [
        { "max_tokens": 65536, "name": "qwen3", "display_name": "Qwen3-64k" }
      ]
    }
  },
```

Release Notes:

- ollama: Add support for Qwen3. Defaults to 16K token context. See:
[Assistant Configuration
Docs](https://zed.dev/docs/assistant/configuration#ollama-context) to
increase.
2025-04-28 21:48:29 -04:00
shenjack
34bb705fdb ollama: Add DeepSeek v3 max token length (#29156)
Add deepseek-v3 max token length for ollama

Release Notes:

- N/A
2025-04-28 21:48:27 -04:00
Michael Sloan
b2d49fb398 Refactor markdown formatting utilities to avoid building intermediate strings (#29511)
These were nearly always used when using `format!` / `write!` etc, so it
makes sense to not have an intermediate `String`.

Release Notes:

- N/A
2025-04-28 16:20:23 -04:00
Michael Sloan
5721089b86 Fix agent rules files for remote project by loading via buffer (#29440)
When using the agent with a project shared by a collaborator, rules file
loading didn't work as it was trying to read from the client's
filesystem

Release Notes:

- Fixed rules file loading when using the agent with a project shared by
a collaborator.
2025-04-28 16:20:15 -04:00
Marshall Bowers
39483b34a7 title_bar: Put plan behind new-billing feature flag (#29540)
This PR puts the displaying of the plan in the user menu behind the
`new-billing` feature flag instead of the old `zed-pro` feature flag.

Release Notes:

- N/A
2025-04-28 16:16:58 -04:00
Marshall Bowers
aff5ac5ca3 feature_flags: Add FeatureFlag suffix to feature flag types (#29392)
This PR adds the `FeatureFlag` suffix to the feature flag types that
were missing them.

This makes the names easier to search in the codebase.

Release Notes:

- N/A
2025-04-28 16:15:52 -04:00
Marshall Bowers
bf915e90e8 feature_flags: Remove remoting feature flag (#29390)
This PR removes the `remoting` feature flag.

The feature is shipped, and we aren't referencing the flag anywhere
anymore.

Release Notes:

- N/A
2025-04-28 16:15:47 -04:00
Mani Rash Ahmadi
cc8fe25736 Agent: Include partial output if terminal tool fails (#29115)
This PR addresses the behavior of the agent's terminal tool when the
executed command is interrupted or fails after producing some output.
Currently, if the command doesn't finish successfully, any partial
output captured before the interruption/failure is discarded, and only
an error message (or a generic cancellation message) is returned to the
LLM.

This change modifies the `run_command_limited` function in the terminal
tool to catch errors when awaiting the command's status (which includes
interruptions). In the case of such an error, it now includes any
partial stdout/stderr captured up to that point within the error message
returned to the `ToolUseState`. This ensures the LLM receives the
partial context even when the command doesn't complete cleanly, framed
appropriately as part of an error/interruption message.

Closes #29101

Release Notes:

- N/A
2025-04-28 16:13:28 -04:00
Marshall Bowers
6476700c97 language_models: Count Google AI tokens through LLM service (#29319)
This PR wires the counting of Google AI tokens back up.

It now goes through the LLM service instead of collab's RPC.

Still only available for Zed staff.

Release Notes:

- N/A
2025-04-28 16:11:14 -04:00
Marshall Bowers
0a656c276e language_models: Pass up mode for completion requests through Zed (#29294)
This PR makes it so we pass up the `mode` for completion requests
through the Zed provider.

Release Notes:

- N/A
2025-04-28 16:11:00 -04:00
Marshall Bowers
93cf95f6d5 collab: Remove CountLanguageModelTokens RPC message (#29314)
This PR removes the `CountLanguageModelTokens` RPC message from collab.

We were only using this for Google AI models through the Zed provider
(which is only available to Zed staff).

For now we're returning `0`, but will bring back soon.

Release Notes:

- N/A
2025-04-28 16:10:27 -04:00
Marshall Bowers
8a68fad53c collab: Remove code for embeddings (#29310)
This PR removes the embeddings-related code from collab and the
protocol, as we weren't using it anywhere.

Release Notes:

- N/A
2025-04-28 16:10:15 -04:00
Oleksiy Syvokon
b39f9e9472 agent: Include grep-related instructions in the prompt only if the tool is available (#29536)
This change updates the system prompt to conditionally include
`grep`-related instructions based on whether the `grep` tool is enabled.

Implementation details:
1. Add a `has_tool` handlebars helper.
2. Pass the `model` to all locations where the prompt is built.
3. Use `{{#if has_tool "grep"}}` in the system prompt to gate
`grep`-specific instructions.

Testing:
- Unit tests for the `hasTool` helper.
- Unit tests to verify that `grep`-related instructions are included /
omitted from the prompt as appropriate.
- Manual agent evaluation:
- Setup: Asked the Agent "List all impls of MyTrait in the project"
using a custom "No tools" profile (all tools disabled).
- Before the change: The Agent attempted to call `grep`, encountered an
error, then realized the tool was unavailable.
- After the change: The Agent immediately asked to enable a search tool.

Note: in principle, `grep`/`read_file` tool descriptions alone might be
enough, but to confirm this we need more evaluation. If it turns out to
be true, we'll be able to remove grep-specific instructions from the
system prompt and undo this change.

Release Notes:

- N/A
2025-04-28 16:01:38 -04:00
Marshall Bowers
7eeed546e1 agent: Add "max mode" toggle (#29549)
This PR adds a "max mode" toggle to the Agent panel, for models that
support it.

Only visible to folks in the `new-billing` feature flag.

Icon is just a placeholder.

Release Notes:

- N/A
2025-04-28 16:01:31 -04:00
Michael Sloan
af292ed6d1 agent: Improve attached context display and show hovers for symbol / selection / rules / thread (#29551)
* Brings back hover popover of selection context.

* Adds hover popover for symbol, rules, and thread context.

* Makes context attached to messages display the names / content at
attachment time.

* Adds the file name as the displayed parent of symbol context.

* Brings back `impl Component for AddedContext`

Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2025-04-28 15:59:04 -04:00
Michael Sloan
b1aa15f442 Use u64 instead of usize in ElementId (#29493)
Truncation to a 32 bit `usize` could cause two distinct IDs to be
considered the same element.

Release Notes:

- N/A
2025-04-28 15:58:58 -04:00
Joseph T. Lyons
2bfac793cd zed 0.184.5 2025-04-28 12:53:16 -04:00
gcp-cherry-pick-bot[bot]
a62a6c46a8 askpass: Shell escape Zed path in askpass script (cherry-pick #29447) (#29449)
Cherry-picked askpass: Shell escape Zed path in askpass script (#29447)

Closes #29439

Add shell escaping as well as additional sanity check for Zed path when
used in askpass. This caused issues on preview and nightly as the
standard paths for those releases contain spaces which were not escaped
appropriately leading to erroneous "Permission denied" errors from SSH
when the askpass script failed

Release Notes:

- Fixed a missing shell-escape in askpass resulting in erroneous
"Permission denied" errors when trying to connect to a remote server
over ssh (effecting preview release v0.184.1 and nightly only)

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-04-28 11:20:08 -04:00
Danilo Leal
d68df740f7 agent: Simplify elements of the thread design (#29533)
Namely, this PR removes the layout shift when you click on a user
message to edit it and displays the feedback disclaimer only upon
hovering the thumbs up/down button container.

Release Notes:

- N/A
2025-04-28 11:02:02 -04:00
Danilo Leal
17e2746c16 agent: Bring title editing back to text threads (#29425)
This also fixes a little UI bug where the text thread title would push
the buttons away from the UI when there was still space.

Release Notes:

- agent: Made text thread titles editable again.

---------

Co-authored-by: Michael Sloan <mgsloan@gmail.com>
2025-04-28 10:29:10 -04:00
Oleksiy Syvokon
45ebaac2a4 agent tools: Make read_file.end_line inclusive (#29524)
One motivation is that the outlines returned by `read_file` for large
files list line numbers assuming an inclusive `end_line`. As a result,
when the agent uses these outlines for `read_line` calls, it would
otherwise miss the last line.

Release Notes:

- N/A
2025-04-28 10:29:01 -04:00
Antonio Scandurra
0f051dd36a Fix error when deserializing Gemini streams (#29470)
Sometimes Gemini would report `Content` without a `parts` field.

Release Notes:

- Fixed a bug that would sometimes cause Gemini models to fail streaming
their response.
2025-04-28 10:27:21 -04:00
Joseph T. Lyons
18c213709b zed 0.184.4 2025-04-25 18:14:55 -04:00
Smit Barmase
d92de5ee61 editor: Improve fuzzy match bucket logic for code completions (#29442)
Add new test and improve fuzzy match bucket logic which results into far
better balance between LSP and fuzzy search.

Before:
<img width="500" alt="before"
src="https://github.com/user-attachments/assets/3e8900a6-c0ff-4f37-b88e-b0e3783b7e9a"
/>

After:
<img width="500" alt="after"
src="https://github.com/user-attachments/assets/738c074c-d446-4697-aac6-9814362e88db"
/>

Release Notes:

- N/A
2025-04-25 18:13:30 -04:00
Smit Barmase
0078f673c3 editor: Add setting for snippet sorting behavior for code completion (#29429)
Added `snippet_sort_order`, which determines how snippets are sorted
relative to other completion items. It can have the values `top`,
`bottom`, or `inline`, with `inline` being the default.

This mimics VS Code’s setting:
https://code.visualstudio.com/docs/editing/intellisense#_snippets-in-suggestions

Release Notes:

- Added support for `snippet_sort_order` to control snippet sorting
behavior in code completion menus.
2025-04-25 18:13:17 -04:00
Danilo Leal
77f1609822 ui: Add inline_code method to label (#29306)
This makes it easy to have a label that looks like Markdown inline code
via the `inline_code(cx)` method.

Release Notes:

- N/A
2025-04-25 17:55:18 -04:00
Michael Sloan
93302ab78c Fix inclusion of message when counting tokens from message editor (#29443)
Accidentally omitted this in #29233

Release Notes:

- N/A
2025-04-25 17:38:58 -04:00
Danilo Leal
7c93c8e572 agent: Render path search results with ToolCard (#28894)
Implementing the `ToolCard` for the path_search tool. It also adds the
"jump to file" functionality if you expand the results.

Release Notes:

- N/A

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-04-25 17:38:42 -04:00
Danilo Leal
01d206626d agent: Make markdown code blocks uncollapsed by default (#29424)
Seeing the markdown code stream in is actually more helpful than hiding
parts of it; parts that you may be interested in.

Release Notes:

- N/A
2025-04-25 17:38:42 -04:00
Bennet Bo Fenner
dfea962e1d agent: Allow to explictly disable tools when using enable_all_context_servers (#29414)
Previously, all MCP tools would be completed regardless if they were
disabled/enabled for the profile. This meant that the "Write" profile
was always using all MCP tools, even if you disabled them in the
settings.

Now, when `enable_all_context_servers` is set to `true`, we will enable
all tools from all MCP servers by default but disable the ones that are
explicitly disabled for the profile.

Also fixes an issue where the tools would not show up as enabled when
using `enable_all_context_servers: true`

Release Notes:

- agent: Fix an issue where MCP tools could not be enabled/disabled
2025-04-25 13:30:26 -04:00
Zed Bot
0f3c56f8d9 Bump to 0.184.3 for @bennetbo 2025-04-25 13:21:10 +00:00
gcp-cherry-pick-bot[bot]
1cbdce4607 assistant: Fix issue when using inline assistant with Gemini models (cherry-pick #29407) (#29409)
Cherry-picked assistant: Fix issue when using inline assistant with
Gemini models (#29407)

Closes #29020

Release Notes:

- assistant: Fix issue when using inline assistant with Gemini models

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2025-04-25 14:47:17 +02:00
Marshall Bowers
745e7d60c4 language_models: Use POST /completions endpoint for Zed provider (#29389)
This PR updates the Zed provider to use the `POST /completions`
endpoint.

There is no functional difference from `POST /completion`, but the
pluralized version reads better.

Release Notes:

- N/A
2025-04-25 08:22:43 -04:00
Michael Sloan
19de45c638 Bring back reload of agent context before sending message (#29385)
Realized after merging #29233 that this behavior is desired

Release Notes:

- N/A
2025-04-25 08:22:17 -04:00
Max Brunsfeld
f8c3a01523 Remove unnecessary fields from the tool schemas (#29381)
This PR removes two fields from JSON schemas (`$schema` and `title`),
which are not expected by any model provider, but were spuriously
included by our JSON schema library, `schemars`.

These added noise to requests and cost wasted input tokens.

### Old

```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "FetchToolInput",
  "type": "object",
  "required": [
    "url"
  ],
  "properties": {
    "url": {
      "description": "The URL to fetch.",
      "type": "string"
    }
  }
}
```

### New:

```json
{
  "properties": {
    "url": {
      "description": "The URL to fetch.",
      "type": "string"
    }
  },
  "required": [
    "url"
  ],
  "type": "object"
}
```

- N/A
2025-04-25 08:22:09 -04:00
Michael Sloan
08cb7034af Restructure agent context (#29233)
Simplifies the data structures involved in agent context by removing
caching and limiting the use of ContextId:

* `AssistantContext` enum is now like an ID / handle to context that
does not need to be updated. `ContextId` still exists but is only used
for generating unique `ElementId`.
* `ContextStore` has a `IndexMap<ContextSetEntry>`. Only need to keep a
`HashSet<ThreadId>` consistent with it. `ContextSetEntry` is a newtype
wrapper around `AssistantContext` which implements eq / hash on a subset
of fields.
* Thread `Message` directly stores its context.

Fixes the following bugs:

* If a context entry is removed from the strip and added again, it was
reincluded in the next message.
* Clicking file context in the thread that has been removed from the
context strip didn't jump to the file.
* Refresh of directory context didn't reflect added / removed files.
* Deleted directories would remain in the message editor context strip.
* Token counting requests didn't include image context.
* File, directory, and symbol context deduplication relied on
`ProjectPath` for identity, and so didn't handle renames.
* Symbol context line numbers didn't update when shifted

Known bugs (not fixed):

* Deleting a directory causes it to disappear from messages in threads.
Fixing this in a nice way is tricky. One easy fix is to store the
original path and show that on deletion. It's weird that deletion would
cause the name to "revert", though. Another possibility would be to
snapshot context metadata on add (ala `AddedContext`), and keep that
around despite deletion.

Release Notes:

- N/A
2025-04-24 21:04:33 -04:00
Richard Feldman
43a04dc46f Treat invalid JSON in tool calls as failed tool calls (#29375)
Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-04-24 17:00:14 -04:00
Danilo Leal
00e92e37fd Rename "Prompt Library" to "Rules Library" (#29349)
There's probably more to do to fully make the transition, and we'll
still debate a bit internally whether this is the name, but just opening
this PR up now for visibility.

Release Notes:

- N/A
2025-04-24 17:00:14 -04:00
Bennet Bo Fenner
9e703accdb gemini: Fix issue when deserializing tool call (#29363)
Fixes a regression introduced in #29322

Release Notes:

- N/A

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-04-24 14:26:27 -04:00
Nathan Sobo
44d5976849 Introduce LanguageModelToolUse::raw_input (#29322)
This is to enable alternative streaming solutions at the application
layer. I'm not sure we really should have performed parsing of the input
at this layer. Either way I want to experiment with streaming approaches
in a separate crate on a branch, and this will help.

/cc @maxdeviant @bennetbo @rtfeldman

Closes #ISSUE

Release Notes:

- N/A
2025-04-24 14:26:16 -04:00
Agus Zubiaga
44fc0045dd agent: Do not reuse assistant message across generations (#29360)
#29354 introduced a bug where we would append tool uses to the last
assistant message even if it was from a previous request.

Release Notes:

- N/A

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-24 14:25:19 -04:00
Joseph T. Lyons
c60b731f2f zed 0.184.2 2025-04-24 13:40:32 -04:00
Agus Zubiaga
b85bfbad67 agent: Do not create user messages for tool results in thread (#29354)
We used to insert empty user messages into the `Thread::messages` `Vec`
when tools finished running and then we would attach the results when
creating the request. This approach was very easy to mess up during
state handling, leading to empty user messages displayed in the
conversation and API failures.

Instead, we will no longer insert actual user messages for tool results
to the `Thread`, and will only do this on the fly when creating the
model request. This simplifies a lot of code and show fix the mentioned
errors.

Release Notes:

- agent: Improve reliability of LLM requests when including tool results

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Oleksiy Syvokon <oleksiy.syvokon@gmail.com>
2025-04-24 12:46:52 -04:00
Danilo Leal
f743cdc41a agent: Simplify user message design more (#29326)
Follow-up to https://github.com/zed-industries/zed/pull/29165 where the
user message design is simplified even more. The edit button is not
visible anymore, and you can click on the whole message block to edit a
message.

Release Notes:

- N/A
2025-04-24 11:21:08 -04:00
Peter Tripp
808d9c9361 Fix ctrl-enter opening inline-assistant in assistant text threads (#29313)
Closes: https://github.com/zed-industries/zed/issues/24501

This has been broken for a while on linux (at least since Feb 8th!) for Assistant1.
It is also broken for Text Threads in Assitant2 (on macos and linux).

This should fix both.

Potentially related:
- https://github.com/zed-industries/zed/pull/29107

Release Notes:

- Fix for `ctrl-enter` shortcut in Assistant text threads incorrectly
opening inline assist instead of triggering Send.

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2025-04-24 11:21:00 -04:00
Agus Zubiaga
a53e561d5e agent: Improve initial file search quality (#29317)
This PR significantly improves the quality of the initial file search
that occurs when the model doesn't yet know the full path to a file it
needs to read/edit.

Previously, the assertions in file_search often failed on main as the
model attempted to guess full file paths. On this branch, it reliably
calls `find_path` (previously `path_search`) before reading files.

After getting the model to find paths first, I noticed it would try
using `grep` instead of `path_search`. This motivated renaming
`path_search` to `find_path` (continuing the analogy to unix commands)
and adding system prompt instructions about proper tool selection.

Note: I know the command is just called `find`, but that seemed too
general.

In my eval runs, the `file_search` example improved from 40% ± 10% to
98% ± 2%. The only assertion I'm seeing occasionally fail is "glob
starts with `**` or project". We can probably add some instructions in
that regard.

Release Notes:

- N/A
2025-04-24 09:07:37 -04:00
Agus Zubiaga
80bac1ca92 eval: New add_arg_to_trait_method example (#29297)
Release Notes:

- N/A

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2025-04-24 09:07:21 -04:00
Agus Zubiaga
4171bbbe9e agent: Encourage model to include displayed fields first (#29308)
Instructs the model to include the fields that we display first in the
input object, so that e.g the user can see the path of a file while the
model generates the content.

Release Notes:

- N/A
2025-04-24 09:05:57 -04:00
Marshall Bowers
2a88867aa0 agent: Read the user's plan from the UserStore (#29305)
This PR updates the Agent panel to read the user's plan from the
`UserStore` instead of hard-coding it.

Release Notes:

- N/A
2025-04-24 09:05:41 -04:00
Oleksiy Syvokon
9d420ae8f1 agent: Add "copy to clipboard" button to error message popups (#29299)
This change makes agent errors copy-able to clipboard:


![image](https://github.com/user-attachments/assets/bd34a3f2-ecd4-4092-9b3b-960953ed1879)



Release Notes:

- N/A
2025-04-24 09:05:29 -04:00
Finn Evers
b431886188 editor: Fix broken mouse scrolling on main (#29307)
This PR is a quick follow-up to #29234 , which unfortunately broke
scrolling with the mouse in editors on main.

The linked PR introduced the possiblilty to completely disable scrolling
for editors. Unfortunately, it also disabled scrolling for editors by
default. This PR fixes this by re-enabling it by default.

This change also needs to be backported to v0.184.x. Otherwise, mouse
scrolling in the next preview release will not work!

Release Notes:

- N/A
2025-04-23 15:38:09 -07:00
Danilo Leal
43c99ad066 agent: Improve feedback text and buttons wrapping (#29302)
Just a little UI improvement here.

Release Notes:

- N/A
2025-04-23 16:32:43 -04:00
Danilo Leal
f802b56299 agent: Render diffs for the edit file tool (#29234)
This PR implements the `ToolCard` for the edit file tool, which allow us
to display an editor with a diff in the thread view with the changes
performed by the model.

- [x] Fix buffer sometimes displaying empty
- [x] Stop buffer from scrolling together with the thread
- [x] Fix multibuffer header sometimes appearing
- [x] Fix buffer height issue
- [x] Implement "full height" expand button
- [x] Add "Jump To File" functionality
- [x] Polish and refine styles

Release Notes:

- agent: Added diff preview cards in the thread view for edits performed
by the agent.

---------

Co-authored-by: João Marcos <marcospb19@hotmail.com>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-23 16:32:43 -04:00
Joseph T. Lyons
0c292b6cc6 zed 0.184.1 2025-04-23 14:31:23 -04:00
Richard Feldman
8446005112 More graceful invalid JSON handling (#29295)
Now we're more tolerant of invalid JSON coming back from the model
(possibly because it was incomplete and we're streaming), plus if we do
end up with invalid JSON once it has all streamed back, we report what
the malformed JSON actually was:

<img width="444" alt="Screenshot 2025-04-23 at 1 49 14 PM"
src="https://github.com/user-attachments/assets/480f5da7-869b-49f3-9ffd-8f08ccddb33d"
/>

Release Notes:

- N/A
2025-04-23 14:30:05 -04:00
Kirill Bulatov
5b028ac2d0 Fix relative paths not properly resolved in the terminal during cmd-click (#29289)
Closes https://github.com/zed-industries/zed/pull/28342
Closes https://github.com/zed-industries/zed/issues/28339
Fixes
https://github.com/zed-industries/zed/pull/29274#issuecomment-2824794396

Release Notes:

- Fixed relative paths not properly resolved in the terminal during
cmd-click
2025-04-23 19:37:15 +03:00
Joseph T. Lyons
57d0276180 v0.184.x preview 2025-04-23 11:57:41 -04:00
1306 changed files with 55229 additions and 150975 deletions

View File

@@ -13,6 +13,12 @@ rustflags = ["-C", "link-arg=-fuse-ld=mold"]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-all_load"]
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-all_load"]
[target.'cfg(target_os = "windows")']
rustflags = [
"--cfg",

View File

@@ -30,7 +30,3 @@ ffdda588b41f7d9d270ffe76cab116f828ad545e
# 2024-07-05 Improved formatting of default keymaps (single line per bind)
# https://github.com/zed-industries/zed/pull/13887
813cc3f5e537372fc86720b5e71b6e1c815440ab
# 2024-07-24 docs: Format docs
# https://github.com/zed-industries/zed/pull/15352
3a44a59f8ec114ac1ba22f7da1652717ef7e4e5c

View File

@@ -1,8 +1,8 @@
name: Bug Report (Debugger)
description: Zed Debugger-Related Bugs
name: Bug Report (Agent Panel)
description: Zed Agent Panel Bugs
type: "Bug"
labels: ["debugger"]
title: "Debugger: <a short description of the Debugger bug>"
labels: ["agent", "ai"]
title: "Agent Panel: <a short description of the Agent Panel bug>"
body:
- type: textarea
attributes:
@@ -14,6 +14,7 @@ body:
### 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.
@@ -21,15 +22,15 @@ body:
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"'
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
placeholder: |
Output of "zed: copy system specs into clipboard"
Output of "zed: Copy System Specs Into Clipboard"
validations:
required: true

View File

@@ -1,42 +0,0 @@
name: Bug Report (AI Related)
description: Zed Agent Panel Bugs
type: "Bug"
labels: ["ai"]
title: "AI: <a short description of the AI Related 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:
### 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:
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

@@ -29,8 +29,8 @@ body:
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
placeholder: |
Output of "zed: copy system specs into clipboard"
Output of "zed: Copy System Specs Into Clipboard"
validations:
required: true

View File

@@ -28,8 +28,8 @@ body:
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
placeholder: |
Output of "zed: copy system specs into clipboard"
Output of "zed: Copy System Specs Into Clipboard"
validations:
required: true

View File

@@ -49,8 +49,8 @@ body:
attributes:
label: Zed Version and System Specs
description: |
Open Zed, from the command palette select "zed: copy system specs into clipboard"
Open Zed, from the command palette select "zed: Copy System Specs Into Clipboard"
placeholder: |
Output of "zed: copy system specs into clipboard"
Output of "zed: Copy System Specs Into Clipboard"
validations:
required: true

View File

@@ -26,9 +26,9 @@ body:
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
placeholder: |
Output of "zed: copy system specs into clipboard"
Output of "zed: Copy System Specs Into Clipboard"
validations:
required: true
- type: textarea

View File

@@ -1,32 +0,0 @@
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 './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

@@ -73,7 +73,7 @@ jobs:
timeout-minutes: 60
runs-on:
- self-hosted
- macOS
- test
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -162,23 +162,13 @@ jobs:
working-directory: ./docs
run: |
pnpm dlx prettier@${PRETTIER_VERSION} . --check || {
echo "To fix, run from the root of the Zed repo:"
echo "To fix, run from the root of the zed repo:"
echo " cd docs && pnpm dlx prettier@${PRETTIER_VERSION} . --write && cd .."
false
}
env:
PRETTIER_VERSION: 3.5.0
- name: Prettier Check on default.json
run: |
pnpm dlx prettier@${PRETTIER_VERSION} assets/settings/default.json --check || {
echo "To fix, run from the root of the Zed repo:"
echo " pnpm dlx prettier@${PRETTIER_VERSION} assets/settings/default.json --write"
false
}
env:
PRETTIER_VERSION: 3.5.0
# To support writing comments that they will certainly be revisited.
- name: Check for todo! and FIXME comments
run: script/check-todos
@@ -191,27 +181,6 @@ jobs:
with:
config: ./typos.toml
check_docs:
timeout-minutes: 60
name: Check docs
needs: [job_spec]
if: github.repository_owner == 'zed-industries'
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
@@ -221,7 +190,7 @@ jobs:
needs.job_spec.outputs.run_tests == 'true'
runs-on:
- self-hosted
- macOS
- test
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -503,9 +472,7 @@ jobs:
- macos_tests
- windows_clippy
- windows_tests
if: |
github.repository_owner == 'zed-industries' &&
always()
if: always()
steps:
- name: Check all tests passed
run: |
@@ -547,6 +514,7 @@ jobs:
APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
steps:
@@ -633,6 +601,7 @@ jobs:
needs: [linux_tests]
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
steps:
@@ -683,13 +652,14 @@ jobs:
timeout-minutes: 60
name: Linux arm64 release bundle
runs-on:
- hosted-linux-arm-1
- buildjet-16vcpu-ubuntu-2204-arm
if: |
startsWith(github.ref, 'refs/tags/v')
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
needs: [linux_tests]
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
steps:
@@ -736,84 +706,62 @@ 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
timeout-minutes: 60
name: Nix Build
continue-on-error: true
if: github.repository_owner == 'zed-industries' && contains(github.event.pull_request.labels.*.name, 'run-nix')
with:
flake-output: debug
# excludes the final package to only cache dependencies
cachix-filter: "-zed-editor-[0-9.]*-nightly"
strategy:
fail-fast: false
matrix:
system:
- os: x86 Linux
runner: buildjet-16vcpu-ubuntu-2204
install_nix: true
- os: arm Mac
runner: [macOS, ARM64, test]
install_nix: false
runs-on: ${{ matrix.system.runner }}
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
GIT_LFS_SKIP_SMUDGE: 1 # breaks the livekit rust sdk examples which we don't actually depend on
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Set path
if: ${{ ! matrix.system.install_nix }}
run: |
echo "/nix/var/nix/profiles/default/bin" >> $GITHUB_PATH
echo "/Users/administrator/.nix-profile/bin" >> $GITHUB_PATH
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
if: ${{ matrix.system.install_nix }}
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
name: zed-industries
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
skipPush: true
- run: nix build .#debug
- name: Limit /nix/store to 50GB
run: "[ $(du -sm /nix/store | cut -f1) -gt 50000 ] && nix-collect-garbage -d"
auto-release-preview:
name: Auto release preview
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, freebsd]
needs: [bundle-mac, bundle-linux-x86_x64, bundle-linux-aarch64]
runs-on:
- self-hosted
- bundle
steps:
- name: gh release
run: gh release edit $GITHUB_REF_NAME --draft=false
run: gh release edit $GITHUB_REF_NAME --draft=true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -9,7 +9,7 @@ jobs:
deploy-docs:
name: Deploy Docs
if: github.repository_owner == 'zed-industries'
runs-on: buildjet-16vcpu-ubuntu-2204
runs-on: ubuntu-latest
steps:
- name: Checkout repo
@@ -17,11 +17,24 @@ 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: Build docs
uses: ./.github/actions/build_docs
- 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: 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
- macOS
- test
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -33,7 +33,7 @@ jobs:
name: Run tests
runs-on:
- self-hosted
- macOS
- test
needs: style
steps:
- name: Checkout repo

View File

@@ -2,7 +2,7 @@ name: Run Agent Eval
on:
schedule:
- cron: "0 0 * * *"
- cron: "0 * * * *"
pull_request:
branches:
@@ -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
@@ -69,7 +60,7 @@ jobs:
run: cargo build --package=eval
- name: Run eval
run: cargo run --package=eval -- --repetitions=8 --concurrency=1
run: cargo run --package=eval -- --repetitions=3 --concurrency=1
# 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

View File

@@ -1,66 +0,0 @@
name: "Nix build"
on:
workflow_call:
inputs:
flake-output:
type: string
default: "default"
cachix-filter:
type: string
default: ""
jobs:
nix-build:
timeout-minutes: 60
name: (${{ matrix.system.os }}) Nix Build
continue-on-error: true # TODO: remove when we want this to start blocking CI
strategy:
fail-fast: false
matrix:
system:
- os: x86 Linux
runner: buildjet-16vcpu-ubuntu-2204
install_nix: true
- os: arm Mac
runner: [macOS, ARM64, test]
install_nix: false
if: github.repository_owner == 'zed-industries'
runs-on: ${{ matrix.system.runner }}
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
GIT_LFS_SKIP_SMUDGE: 1 # breaks the livekit rust sdk examples which we don't actually depend on
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
# on our macs we manually install nix. for some reason the cachix action is running
# under a non-login /bin/bash shell which doesn't source the proper script to add the
# nix profile to PATH, so we manually add them here
- name: Set path
if: ${{ ! matrix.system.install_nix }}
run: |
echo "/nix/var/nix/profiles/default/bin" >> $GITHUB_PATH
echo "/Users/administrator/.nix-profile/bin" >> $GITHUB_PATH
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
if: ${{ matrix.system.install_nix }}
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
name: zed
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
pushFilter: "${{ inputs.cachix-filter }}"
cachixArgs: '-v'
- run: nix build .#${{ inputs.flake-output }} -L --accept-flake-config
- name: Limit /nix/store to 50GB on macs
if: ${{ ! matrix.system.install_nix }}
run: |
[ $(du -sm /nix/store | cut -f1) -gt 50000 ] && nix-collect-garbage -d || :

View File

@@ -20,7 +20,7 @@ jobs:
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- macOS
- test
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -40,7 +40,7 @@ jobs:
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- macOS
- test
needs: style
steps:
- name: Checkout repo
@@ -68,6 +68,7 @@ jobs:
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Install Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
@@ -103,6 +104,7 @@ jobs:
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -142,6 +144,7 @@ jobs:
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -167,54 +170,54 @@ jobs:
- name: Upload Zed Nightly
run: script/upload-nightly linux-targz
freebsd:
bundle-nix:
timeout-minutes: 60
name: (${{ matrix.system.os }}) Nix Build
continue-on-error: true
strategy:
fail-fast: false
matrix:
system:
- os: x86 Linux
runner: buildjet-16vcpu-ubuntu-2204
install_nix: true
- os: arm Mac
runner: [macOS, ARM64, test]
install_nix: false
if: github.repository_owner == 'zed-industries'
runs-on: github-8vcpu-ubuntu-2404
runs-on: ${{ matrix.system.runner }}
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"
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
GIT_LFS_SKIP_SMUDGE: 1 # breaks the livekit rust sdk examples which we don't actually depend on
steps:
- uses: actions/checkout@v4
- name: Build FreeBSD remote-server
id: freebsd-build
uses: vmactions/freebsd-vm@c3ae29a132c8ef1924775414107a97cac042aad5 # v1.2.0
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
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
clean: false
- name: Upload Zed Nightly
run: script/upload-nightly freebsd
# on our macs we manually install nix. for some reason the cachix action is running
# under a non-login /bin/bash shell which doesn't source the proper script to add the
# nix profile to PATH, so we manually add them here
- name: Set path
if: ${{ ! matrix.system.install_nix }}
run: |
echo "/nix/var/nix/profiles/default/bin" >> $GITHUB_PATH
echo "/Users/administrator/.nix-profile/bin" >> $GITHUB_PATH
bundle-nix:
name: Build and cache Nix package
needs: tests
uses: ./.github/workflows/nix.yml
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
if: ${{ matrix.system.install_nix }}
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
name: zed-industries
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- run: nix build
- name: Limit /nix/store to 50GB
run: '[ $(du -sm /nix/store | cut -f1) -gt 50000 ] && nix-collect-garbage -d'
update-nightly-tag:
name: Update nightly tag

View File

@@ -1,85 +0,0 @@
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:
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_)' --test-threads 1
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

3
.gitignore vendored
View File

@@ -2,7 +2,6 @@
**/cargo-target
**/target
**/venv
**/.direnv
*.wasm
*.xcodeproj
.DS_Store
@@ -24,7 +23,6 @@
/crates/theme/schemas/theme.json
/crates/zed/resources/flatpak/flatpak-cargo-sources.json
/dev.zed.Zed*.json
/node_modules/
/plugins/bin
/script/node_modules
/snap
@@ -34,5 +32,4 @@ Packages
xcuserdata/
# Don't commit any secrets to the repo.
.env
.env.secret.toml

View File

@@ -19,8 +19,6 @@ amtoaer <amtoaer@gmail.com>
amtoaer <amtoaer@gmail.com> <amtoaer@outlook.com>
Andrei Zvonimir Crnković <andrei@0x7f.dev>
Andrei Zvonimir Crnković <andrei@0x7f.dev> <andreicek@0x7f.dev>
Angelk90 <angelo.k90@hotmail.it>
Angelk90 <angelo.k90@hotmail.it> <20476002+Angelk90@users.noreply.github.com>
Antonio Scandurra <me@as-cii.com>
Antonio Scandurra <me@as-cii.com> <antonio@zed.dev>
Ben Kunkle <ben@zed.dev>
@@ -40,8 +38,6 @@ Dairon Medina <dairon.medina@gmail.com>
Danilo Leal <danilo@zed.dev>
Danilo Leal <danilo@zed.dev> <67129314+danilo-leal@users.noreply.github.com>
Edwin Aronsson <75266237+4teapo@users.noreply.github.com>
Elvis Pranskevichus <elvis@geldata.com>
Elvis Pranskevichus <elvis@geldata.com> <elvis@magic.io>
Evren Sen <nervenes@icloud.com>
Evren Sen <nervenes@icloud.com> <146845123+evrensen467@users.noreply.github.com>
Evren Sen <nervenes@icloud.com> <146845123+evrsen@users.noreply.github.com>
@@ -73,8 +69,6 @@ Lilith Iris <itslirissama@gmail.com> <83819417+Irilith@users.noreply.github.com>
LoganDark <contact@logandark.mozmail.com>
LoganDark <contact@logandark.mozmail.com> <git@logandark.mozmail.com>
LoganDark <contact@logandark.mozmail.com> <github@logandark.mozmail.com>
Marko Kungla <marko.kungla@gmail.com>
Marko Kungla <marko.kungla@gmail.com> <marko@mkungla.dev>
Marshall Bowers <git@maxdeviant.com>
Marshall Bowers <git@maxdeviant.com> <elliott.codes@gmail.com>
Marshall Bowers <git@maxdeviant.com> <marshall@zed.dev>
@@ -90,7 +84,6 @@ Michael Sloan <michael@zed.dev> <mgsloan@google.com>
Mikayla Maki <mikayla@zed.dev>
Mikayla Maki <mikayla@zed.dev> <mikayla.c.maki@gmail.com>
Mikayla Maki <mikayla@zed.dev> <mikayla.c.maki@icloud.com>
Morgan Krey <morgan@zed.dev>
Muhammad Talal Anwar <mail@talal.io>
Muhammad Talal Anwar <mail@talal.io> <talalanwar@outlook.com>
Nate Butler <iamnbutler@gmail.com>
@@ -123,18 +116,11 @@ Shish <webmaster@shishnet.org>
Shish <webmaster@shishnet.org> <shish@shishnet.org>
Smit Barmase <0xtimsb@gmail.com>
Smit Barmase <0xtimsb@gmail.com> <smit@zed.dev>
Thomas <github.thomaub@gmail.com>
Thomas <github.thomaub@gmail.com> <thomas.aubry94@gmail.com>
Thomas <github.thomaub@gmail.com> <thomas.aubry@paylead.fr>
Thomas Heartman <thomasheartman+github@gmail.com>
Thomas Heartman <thomasheartman+github@gmail.com> <thomas@getunleash.io>
Thomas Mickley-Doyle <tmickleydoyle@gmail.com>
Thomas Mickley-Doyle <tmickleydoyle@gmail.com> <thomas@zed.dev>
Thorben Kröger <dev@thorben.net>
Thorben Kröger <dev@thorben.net> <thorben.kroeger@hexagon.com>
Thorsten Ball <mrnugget@gmail.com>
Thorsten Ball <mrnugget@gmail.com> <me@thorstenball.com>
Thorsten Ball <mrnugget@gmail.com> <thorsten@zed.dev>
Thorsten Ball <thorsten@zed.dev>
Thorsten Ball <thorsten@zed.dev> <me@thorstenball.com>
Thorsten Ball <thorsten@zed.dev> <mrnugget@gmail.com>
Tristan Hume <tris.hume@gmail.com>
Tristan Hume <tris.hume@gmail.com> <tristan@anthropic.com>
Uladzislau Kaminski <i@uladkaminski.com>

View File

@@ -1,3 +0,0 @@
{
"printWidth": 120
}

13
.rules
View File

@@ -5,12 +5,6 @@
* 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
@@ -121,12 +115,7 @@ Other entities can then register a callback to handle these events by doing `cx.
GPUI has had some changes to its APIs. Always write code using the new APIs:
* `spawn` methods now take async closures (`AsyncFn`), and so should be called like `cx.spawn(async move |cx| ...)`.
* Use `Entity<T>`. This replaces `Model<T>` and `View<T>` which no longer exist and should NEVER be used.
* Use `Entity<T>`. This replaces `Model<T>` and `View<T>` which longer exists and should NEVER be used.
* Use `App` references. This replaces `AppContext` which no longer exists and should NEVER be used.
* Use `Context<T>` references. This replaces `ModelContext<T>` which no longer exists and should NEVER be used.
* `Window` is now passed around explicitly. The new interface adds a `Window` reference parameter to some methods, and adds some new "*_in" methods for plumbing `Window`. The old types `WindowContext` and `ViewContext<T>` should NEVER be used.
## General guidelines
- Use `./script/clippy` instead of `cargo clippy`

View File

@@ -2,11 +2,18 @@
{
"label": "Debug Zed (CodeLLDB)",
"adapter": "CodeLLDB",
"build": { "label": "Build Zed", "command": "cargo", "args": ["build"] }
"program": "$ZED_WORKTREE_ROOT/target/debug/zed",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT"
},
{
"label": "Debug Zed (GDB)",
"adapter": "GDB",
"build": { "label": "Build Zed", "command": "cargo", "args": ["build"] }
"program": "$ZED_WORKTREE_ROOT/target/debug/zed",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT",
"initialize_args": {
"stopAtBeginningOfMainSubprogram": true
}
}
]

View File

@@ -46,18 +46,5 @@
"formatter": "auto",
"remove_trailing_whitespace_on_save": true,
"ensure_final_newline_on_save": true,
"file_scan_exclusions": [
"crates/assistant_tools/src/evals/fixtures",
"crates/eval/worktrees/",
"crates/eval/repos/",
"**/.git",
"**/.svn",
"**/.hg",
"**/.jj",
"**/CVS",
"**/.DS_Store",
"**/Thumbs.db",
"**/.classpath",
"**/.settings"
]
"file_scan_exclusions": ["crates/eval/worktrees/", "crates/eval/repos/"]
}

2576
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,11 +3,12 @@ resolver = "2"
members = [
"crates/activity_indicator",
"crates/agent",
"crates/agent_settings",
"crates/anthropic",
"crates/askpass",
"crates/assets",
"crates/assistant",
"crates/assistant_context_editor",
"crates/assistant_settings",
"crates/assistant_slash_command",
"crates/assistant_slash_commands",
"crates/assistant_tool",
@@ -31,15 +32,16 @@ members = [
"crates/command_palette",
"crates/command_palette_hooks",
"crates/component",
"crates/component_preview",
"crates/context_server",
"crates/context_server_settings",
"crates/copilot",
"crates/credentials_provider",
"crates/dap",
"crates/dap_adapters",
"crates/db",
"crates/debug_adapter_extension",
"crates/debugger_tools",
"crates/debugger_ui",
"crates/db",
"crates/deepseek",
"crates/diagnostics",
"crates/docs_preprocessor",
@@ -73,14 +75,12 @@ members = [
"crates/indexed_docs",
"crates/inline_completion",
"crates/inline_completion_button",
"crates/inspector_ui",
"crates/install_cli",
"crates/jj",
"crates/jj_ui",
"crates/journal",
"crates/language",
"crates/language_extension",
"crates/language_model",
"crates/language_model_selector",
"crates/language_models",
"crates/language_selector",
"crates/language_tools",
@@ -100,7 +100,6 @@ members = [
"crates/notifications",
"crates/ollama",
"crates/open_ai",
"crates/open_router",
"crates/outline",
"crates/outline_panel",
"crates/panel",
@@ -110,6 +109,7 @@ members = [
"crates/project",
"crates/project_panel",
"crates/project_symbols",
"crates/rules_library",
"crates/prompt_store",
"crates/proto",
"crates/recent_projects",
@@ -123,7 +123,6 @@ members = [
"crates/rich_text",
"crates/rope",
"crates/rpc",
"crates/rules_library",
"crates/schema_generator",
"crates/search",
"crates/semantic_index",
@@ -165,7 +164,6 @@ members = [
"crates/util_macros",
"crates/vim",
"crates/vim_mode_setting",
"crates/watch",
"crates/web_search",
"crates/web_search_providers",
"crates/welcome",
@@ -213,12 +211,13 @@ edition = "2024"
activity_indicator = { path = "crates/activity_indicator" }
agent = { path = "crates/agent" }
agent_settings = { path = "crates/agent_settings" }
ai = { path = "crates/ai" }
anthropic = { path = "crates/anthropic" }
askpass = { path = "crates/askpass" }
assets = { path = "crates/assets" }
assistant = { path = "crates/assistant" }
assistant_context_editor = { path = "crates/assistant_context_editor" }
assistant_settings = { path = "crates/assistant_settings" }
assistant_slash_command = { path = "crates/assistant_slash_command" }
assistant_slash_commands = { path = "crates/assistant_slash_commands" }
assistant_tool = { path = "crates/assistant_tool" }
@@ -230,7 +229,6 @@ auto_update_ui = { path = "crates/auto_update_ui" }
aws_http_client = { path = "crates/aws_http_client" }
bedrock = { path = "crates/bedrock" }
breadcrumbs = { path = "crates/breadcrumbs" }
buffer_diff = { path = "crates/buffer_diff" }
call = { path = "crates/call" }
channel = { path = "crates/channel" }
cli = { path = "crates/cli" }
@@ -242,17 +240,19 @@ collections = { path = "crates/collections" }
command_palette = { path = "crates/command_palette" }
command_palette_hooks = { path = "crates/command_palette_hooks" }
component = { path = "crates/component" }
component_preview = { path = "crates/component_preview" }
context_server = { path = "crates/context_server" }
context_server_settings = { path = "crates/context_server_settings" }
copilot = { path = "crates/copilot" }
credentials_provider = { path = "crates/credentials_provider" }
dap = { path = "crates/dap" }
dap_adapters = { path = "crates/dap_adapters" }
db = { path = "crates/db" }
debug_adapter_extension = { path = "crates/debug_adapter_extension" }
debugger_tools = { path = "crates/debugger_tools" }
debugger_ui = { path = "crates/debugger_ui" }
debugger_tools = { path = "crates/debugger_tools" }
deepseek = { path = "crates/deepseek" }
diagnostics = { path = "crates/diagnostics" }
buffer_diff = { path = "crates/buffer_diff" }
editor = { path = "crates/editor" }
extension = { path = "crates/extension" }
extension_host = { path = "crates/extension_host" }
@@ -282,14 +282,12 @@ image_viewer = { path = "crates/image_viewer" }
indexed_docs = { path = "crates/indexed_docs" }
inline_completion = { path = "crates/inline_completion" }
inline_completion_button = { path = "crates/inline_completion_button" }
inspector_ui = { path = "crates/inspector_ui" }
install_cli = { path = "crates/install_cli" }
jj = { path = "crates/jj" }
jj_ui = { path = "crates/jj_ui" }
journal = { path = "crates/journal" }
language = { path = "crates/language" }
language_extension = { path = "crates/language_extension" }
language_model = { path = "crates/language_model" }
language_model_selector = { path = "crates/language_model_selector" }
language_models = { path = "crates/language_models" }
language_selector = { path = "crates/language_selector" }
language_tools = { path = "crates/language_tools" }
@@ -309,11 +307,10 @@ 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" }
paths = { path = "crates/paths" }
panel = { path = "crates/panel" }
picker = { path = "crates/picker" }
plugin = { path = "crates/plugin" }
plugin_macros = { path = "crates/plugin_macros" }
@@ -321,6 +318,7 @@ prettier = { path = "crates/prettier" }
project = { path = "crates/project" }
project_panel = { path = "crates/project_panel" }
project_symbols = { path = "crates/project_symbols" }
rules_library = { path = "crates/rules_library" }
prompt_store = { path = "crates/prompt_store" }
proto = { path = "crates/proto" }
recent_projects = { path = "crates/recent_projects" }
@@ -333,7 +331,6 @@ reqwest_client = { path = "crates/reqwest_client" }
rich_text = { path = "crates/rich_text" }
rope = { path = "crates/rope" }
rpc = { path = "crates/rpc" }
rules_library = { path = "crates/rules_library" }
search = { path = "crates/search" }
semantic_index = { path = "crates/semantic_index" }
semantic_version = { path = "crates/semantic_version" }
@@ -374,7 +371,6 @@ util = { path = "crates/util" }
util_macros = { path = "crates/util_macros" }
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" }
@@ -405,6 +401,7 @@ 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 = [
@@ -417,9 +414,10 @@ 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 = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
blade-util = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
naga = { version = "23.1.0", features = ["wgsl-in"] }
blake3 = "1.5.3"
bytes = "1.0"
cargo_metadata = "0.19"
@@ -429,18 +427,15 @@ circular-buffer = "1.0"
clap = { version = "4.4", features = ["derive"] }
cocoa = "0.26"
cocoa-foundation = "0.2.0"
core-video = { version = "0.4.3", features = ["metal"] }
convert_case = "0.8.0"
core-foundation = "0.10.0"
core-foundation-sys = "0.8.6"
core-video = { version = "0.4.3", features = ["metal"] }
criterion = { version = "0.5", features = ["html_reports"] }
ctor = "0.4.0"
dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "68516de327fa1be15214133a0a2e52a12982ce75" }
dashmap = "6.0"
dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "be69a016ba710191b9fdded28c8b042af4b617f7" }
derive_more = "0.99.17"
dirs = "4.0"
documented = "0.9.1"
dotenv = "0.15.0"
ec4rs = "1.1"
emojis = "0.6.1"
env_logger = "0.11"
@@ -452,14 +447,13 @@ 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"] }
hex = "0.4.3"
html5ever = "0.27.0"
http = "1.1"
hyper = "0.14"
http = "1.1"
ignore = "0.4.22"
image = "0.25.1"
imara-diff = "0.1.8"
@@ -467,41 +461,34 @@ indexmap = { version = "2.7.0", features = ["serde"] }
indoc = "2"
inventory = "0.3.19"
itertools = "0.14.0"
jj-lib = { git = "https://github.com/jj-vcs/jj", rev = "e18eb8e05efaa153fad5ef46576af145bba1807f" }
json_dotpath = "1.1"
jsonschema = "0.30.0"
jsonwebtoken = "9.3"
jupyter-protocol = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
jupyter-websocket-client = { git = "https://github.com/ConradIrwin/runtimed" ,rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
libc = "0.2"
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
linkify = "0.10.0"
linkme = "0.3.31"
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"
nbformat = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
nix = "0.29"
num-format = "0.4.4"
objc = "0.2"
open = "5.0.0"
num-format = "0.4.4"
ordered-float = "2.1.1"
palette = { version = "0.7.5", default-features = false, features = ["std"] }
parking_lot = "0.12.1"
partial-json-fixer = "0.5.3"
pathdiff = "0.2"
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
portable-pty = "0.9.0"
postage = { version = "0.5", features = ["futures-traits"] }
pretty_assertions = { version = "1.3.0", features = ["unstable"] }
proc-macro2 = "1.0.93"
@@ -512,11 +499,11 @@ prost-types = "0.9"
pulldown-cmark = { version = "0.12.0", default-features = false }
quote = "1.0.9"
rand = "0.8.5"
rayon = "1.8"
ref-cast = "1.0.24"
rayon = "1.8"
regex = "1.5"
repair_json = "0.1.0"
reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "951c770a32f1998d6e999cef3e59e0013e6c4415", default-features = false, features = [
reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "fd110f6998da16bbca97b6dddda9be7827c50e29", default-features = false, features = [
"charset",
"http2",
"macos-system-configuration",
@@ -528,8 +515,8 @@ rsa = "0.9.6"
runtimelib = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734", default-features = false, features = [
"async-dispatcher-runtime",
] }
rust-embed = { version = "8.4", features = ["include-exclude"] }
rustc-demangle = "0.1.23"
rust-embed = { version = "8.4", features = ["include-exclude"] }
rustc-hash = "2.1.0"
rustls = { version = "0.23.26" }
rustls-platform-verifier = "0.5.0"
@@ -556,13 +543,13 @@ streaming-iterator = "0.1"
strsim = "0.11"
strum = { version = "0.27.0", features = ["derive"] }
subtle = "2.5.0"
syn = { version = "2.0.101", features = ["full", "extra-traits"] }
syn = { version = "1.0.72", features = ["full", "extra-traits"] }
sys-locale = "0.3.1"
sysinfo = "0.31.0"
take-until = "0.2.0"
tempfile = "3.20.0"
tempfile = "3.9.0"
thiserror = "2.0.12"
tiktoken-rs = "0.7.0"
tiktoken-rs = "0.6.0"
time = { version = "0.3", features = [
"macros",
"parsing",
@@ -571,16 +558,15 @@ time = { version = "0.3", features = [
"formatting",
] }
tiny_http = "0.8"
toml = "0.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.6", features = ["wasm"] }
tree-sitter-bash = "0.25.0"
tree-sitter = { version = "0.25.3", features = ["wasm"] }
tree-sitter-bash = "0.23"
tree-sitter-c = "0.23"
tree-sitter-cpp = "0.23"
tree-sitter-css = "0.23"
tree-sitter-diff = "0.1.0"
tree-sitter-elixir = "0.3"
tree-sitter-embedded-template = "0.23.0"
tree-sitter-gitcommit = { git = "https://github.com/zed-industries/tree-sitter-git-commit", rev = "88309716a69dd13ab83443721ba6e0b491d37ee9" }
@@ -588,6 +574,7 @@ tree-sitter-go = "0.23"
tree-sitter-go-mod = { git = "https://github.com/camdencheek/tree-sitter-go-mod", rev = "6efb59652d30e0e9cd5f3b3a669afd6f1a926d3c", package = "tree-sitter-gomod" }
tree-sitter-gowork = { git = "https://github.com/zed-industries/tree-sitter-go-work", rev = "acb0617bf7f4fda02c6217676cc64acb89536dc7" }
tree-sitter-heex = { git = "https://github.com/zed-industries/tree-sitter-heex", rev = "1dd45142fbb05562e35b2040c6129c9bca346592" }
tree-sitter-diff = "0.1.0"
tree-sitter-html = "0.23"
tree-sitter-jsdoc = "0.23"
tree-sitter-json = "0.24"
@@ -599,29 +586,29 @@ tree-sitter-rust = "0.24"
tree-sitter-typescript = "0.23"
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "baff0b51c64ef6a1fb1f8390f3ad6015b83ec13a" }
unicase = "2.6"
unicode-script = "0.5.7"
unicode-segmentation = "1.10"
unindent = "0.2.0"
unicode-segmentation = "1.10"
unicode-script = "0.5.7"
url = "2.2"
urlencoding = "2.1.2"
uuid = { version = "1.1.2", features = ["v4", "v5", "v7", "serde"] }
walkdir = "2.5"
wasm-encoder = "0.221"
walkdir = "2.3"
wasmparser = "0.221"
wasm-encoder = "0.221"
wasmtime = { version = "29", default-features = false, features = [
"async",
"demangle",
"runtime",
"cranelift",
"component-model",
"incremental-cache",
"parallel-compilation",
] }
wasmtime-wasi = "29"
which = "6.0.0"
wit-component = "0.221"
workspace-hack = "0.1.0"
zed_llm_client = "0.8.4"
zed_llm_client = "0.7.1"
zstd = "0.11"
metal = "0.29"
[workspace.dependencies.async-stripe]
git = "https://github.com/zed-industries/async-stripe"
@@ -800,9 +787,6 @@ let_underscore_future = "allow"
# running afoul of the borrow checker.
too_many_arguments = "allow"
# We often have large enum variants yet we rarely actually bother with splitting them up.
large_enum_variant = "allow"
[workspace.metadata.cargo-machete]
ignored = [
"bindgen",
@@ -810,6 +794,6 @@ ignored = [
"prost_build",
"serde",
"component",
"documented",
"linkme",
"workspace-hack",
]

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.2
FROM rust:1.87-bookworm as builder
FROM rust:1.86-bookworm as builder
WORKDIR app
COPY . .

View File

@@ -8,6 +8,10 @@ Welcome to Zed, a high-performance, multiplayer code editor from the creators of
### Installation
<a href="https://repology.org/project/zed-editor/versions">
<img src="https://repology.org/badge/vertical-allrepos/zed-editor.svg?minversion=0.143.5" alt="Packaging status" align="right">
</a>
On macOS and Linux you can [download Zed directly](https://zed.dev/download) or [install Zed via your local package manager](https://zed.dev/docs/linux#installing-via-a-package-manager).
Other platforms are not yet available:

View File

@@ -1,8 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 575 B

View File

@@ -1,3 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.3 1.75L3 7.35H5.8L4.7 12.25L11 6.65H8.2L9.3 1.75Z" stroke="black" stroke-width="1.25" stroke-linejoin="round"/>
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.76019 3.50003H6.50231C6.71012 3.50003 6.89761 3.62971 6.95698 3.82346C7.04292 4.01876 6.98823 4.23906 6.83199 4.37656L2.83214 7.87643C2.65558 8.02954 2.39731 8.04204 2.20857 7.90455C2.01967 7.76705 1.95092 7.51706 2.04295 7.30301L3.24462 4.49999H1.48844C1.29423 4.49999 1.10767 4.37031 1.0344 4.17657C0.961132 3.98126 1.01643 3.76096 1.17323 3.62346L5.17261 0.123753C5.34917 -0.0299914 5.60697 -0.0417097 5.79603 0.0954726C5.98508 0.232749 6.05383 0.482177 5.96165 0.69695L4.76013 3.49981L4.76019 3.50003Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 227 B

After

Width:  |  Height:  |  Size: 633 B

View File

@@ -1,3 +0,0 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.76019 3.50003H6.50231C6.71012 3.50003 6.89761 3.62971 6.95698 3.82346C7.04292 4.01876 6.98823 4.23906 6.83199 4.37656L2.83214 7.87643C2.65558 8.02954 2.39731 8.04204 2.20857 7.90455C2.01967 7.76705 1.95092 7.51706 2.04295 7.30301L3.24462 4.49999H1.48844C1.29423 4.49999 1.10767 4.37031 1.0344 4.17657C0.961132 3.98126 1.01643 3.76096 1.17323 3.62346L5.17261 0.123753C5.34917 -0.0299914 5.60697 -0.0417097 5.79603 0.0954726C5.98508 0.232749 6.05383 0.482177 5.96165 0.69695L4.76013 3.49981L4.76019 3.50003Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 633 B

View File

@@ -1,7 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 12C9.76142 12 12 9.76142 12 7C12 4.23858 9.76142 2 7 2C4.23858 2 2 4.23858 2 7C2 9.76142 4.23858 12 7 12Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.5 7H9" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.5 7H2" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 4.5V2" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 11.5V9" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 720 B

View File

@@ -1,4 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 13H10.4C9.76346 13 9.15302 12.7893 8.70296 12.4142C8.25284 12.0391 8 11.5304 8 11V5C8 4.46957 8.25284 3.96086 8.70296 3.58579C9.15302 3.21071 9.76346 3 10.4 3H11" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 13H5.6C6.23654 13 6.84698 12.7893 7.29704 12.4142C7.74716 12.0391 8 11.5304 8 11V5C8 4.46957 7.74716 3.96086 7.29704 3.58579C6.84698 3.21071 6.23654 3 5.6 3H5" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 20H16C14.9391 20 13.9217 19.6629 13.1716 19.0627C12.4214 18.4626 12 17.6487 12 16.8V7.2C12 6.35131 12.4214 5.53737 13.1716 4.93726C13.9217 4.33714 14.9391 4 16 4H17" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 20H8C9.06087 20 10.0783 19.5786 10.8284 18.8284C11.5786 18.0783 12 17.0609 12 16V15" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 4H8C9.06087 4 10.0783 4.42143 10.8284 5.17157C11.5786 5.92172 12 6.93913 12 8V9" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 617 B

After

Width:  |  Height:  |  Size: 715 B

View File

Before

Width:  |  Height:  |  Size: 474 B

After

Width:  |  Height:  |  Size: 474 B

View File

@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
<path fill-rule="evenodd" d="M4.5 2A1.5 1.5 0 0 0 3 3.5v13A1.5 1.5 0 0 0 4.5 18h11a1.5 1.5 0 0 0 1.5-1.5V7.621a1.5 1.5 0 0 0-.44-1.06l-4.12-4.122A1.5 1.5 0 0 0 11.378 2H4.5Zm2.25 8.5a.75.75 0 0 0 0 1.5h6.5a.75.75 0 0 0 0-1.5h-6.5Zm0 3a.75.75 0 0 0 0 1.5h6.5a.75.75 0 0 0 0-1.5h-6.5Z" clip-rule="evenodd" />
</svg>

Before

Width:  |  Height:  |  Size: 412 B

View File

@@ -1 +0,0 @@
<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-hammer-icon lucide-hammer"><path d="m15 12-8.373 8.373a1 1 0 1 1-3-3L12 9"/><path d="m18 15 4-4"/><path d="m21.5 11.5-1.914-1.914A2 2 0 0 1 19 8.172V7l-2.26-2.26a6 6 0 0 0-4.202-1.756L9 2.96l.92.82A6.18 6.18 0 0 1 12 8.4V10l2 2h1.172a2 2 0 0 1 1.414.586L18.5 14.5"/></svg>

Before

Width:  |  Height:  |  Size: 475 B

View File

@@ -1 +0,0 @@
<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-collapse-icon lucide-list-collapse"><path d="m3 10 2.5-2.5L3 5"/><path d="m3 19 2.5-2.5L3 14"/><path d="M10 6h11"/><path d="M10 12h11"/><path d="M10 18h11"/></svg>

Before

Width:  |  Height:  |  Size: 371 B

View File

@@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 373 B

View File

@@ -1 +0,0 @@
<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-loader-circle-icon lucide-loader-circle"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>

Before

Width:  |  Height:  |  Size: 289 B

View File

@@ -1,5 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 12H16" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 6H20" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 18H12" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 402 B

View File

@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 3L13 8L4 13V3Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 214 B

View File

@@ -1,8 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 12C2.35977 11.85 1 10.575 1 9" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1.00875 15.2C1.00875 13.625 0.683456 12.275 4.00001 12.2" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 9C7 10.575 5.62857 11.85 4 12" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 12.2C6.98117 12.2 7 13.625 7 15.2" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="2.5" y="9" width="3" height="6" rx="1.5" fill="black"/>
<path d="M9 10L13 8L4 3V7.5" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 813 B

View File

@@ -1 +0,0 @@
<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-scissors-icon lucide-scissors"><circle cx="6" cy="6" r="3"/><path d="M8.12 8.12 12 12"/><path d="M20 4 8.12 15.88"/><circle cx="6" cy="18" r="3"/><path d="M14.8 14.8 20 20"/></svg>

Before

Width:  |  Height:  |  Size: 383 B

View File

@@ -1,8 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 5H4" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M8 5L14 5" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M12 11L14 11" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M2 11H8" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<circle cx="6" cy="5" r="2" fill="black" fill-opacity="0.1" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<circle cx="10" cy="11" r="2" fill="black" fill-opacity="0.1" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.36667 3.79167C5.53364 3.79167 4.85833 4.46697 4.85833 5.3C4.85833 6.13303 5.53364 6.80833 6.36667 6.80833C7.1997 6.80833 7.875 6.13303 7.875 5.3C7.875 4.46697 7.1997 3.79167 6.36667 3.79167ZM2.1 5.925H3.67944C3.9626 7.14732 5.05824 8.05833 6.36667 8.05833C7.67509 8.05833 8.77073 7.14732 9.05389 5.925H14.9C15.2452 5.925 15.525 5.64518 15.525 5.3C15.525 4.95482 15.2452 4.675 14.9 4.675H9.05389C8.77073 3.45268 7.67509 2.54167 6.36667 2.54167C5.05824 2.54167 3.9626 3.45268 3.67944 4.675H2.1C1.75482 4.675 1.475 4.95482 1.475 5.3C1.475 5.64518 1.75482 5.925 2.1 5.925ZM13.3206 12.325C13.0374 13.5473 11.9418 14.4583 10.6333 14.4583C9.32491 14.4583 8.22927 13.5473 7.94611 12.325H2.1C1.75482 12.325 1.475 12.0452 1.475 11.7C1.475 11.3548 1.75482 11.075 2.1 11.075H7.94611C8.22927 9.85268 9.32491 8.94167 10.6333 8.94167C11.9418 8.94167 13.0374 9.85268 13.3206 11.075H14.9C15.2452 11.075 15.525 11.3548 15.525 11.7C15.525 12.0452 15.2452 12.325 14.9 12.325H13.3206ZM9.125 11.7C9.125 10.867 9.8003 10.1917 10.6333 10.1917C11.4664 10.1917 12.1417 10.867 12.1417 11.7C12.1417 12.533 11.4664 13.2083 10.6333 13.2083C9.8003 13.2083 9.125 12.533 9.125 11.7Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 657 B

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,3 +1 @@
<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>
<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>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,3 +1 @@
<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>
<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>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 794 B

View File

@@ -1,3 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
<path d="M18.905 12.75a1.25 1.25 0 1 1-2.5 0v-7.5a1.25 1.25 0 0 1 2.5 0v7.5ZM8.905 17v1.3c0 .268-.14.526-.395.607A2 2 0 0 1 5.905 17c0-.995.182-1.948.514-2.826.204-.54-.166-1.174-.744-1.174h-2.52c-1.243 0-2.261-1.01-2.146-2.247.193-2.08.651-4.082 1.341-5.974C2.752 3.678 3.833 3 5.005 3h3.192a3 3 0 0 1 1.341.317l2.734 1.366A3 3 0 0 0 13.613 5h1.292v7h-.963c-.685 0-1.258.482-1.612 1.068a4.01 4.01 0 0 1-2.166 1.73c-.432.143-.853.386-1.011.814-.16.432-.248.9-.248 1.388Z" />
</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-thumbs-down"><path d="M17 14V2"/><path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z"/></svg>

Before

Width:  |  Height:  |  Size: 580 B

After

Width:  |  Height:  |  Size: 405 B

View File

@@ -1,3 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
<path d="M1 8.25a1.25 1.25 0 1 1 2.5 0v7.5a1.25 1.25 0 1 1-2.5 0v-7.5ZM11 3V1.7c0-.268.14-.526.395-.607A2 2 0 0 1 14 3c0 .995-.182 1.948-.514 2.826-.204.54.166 1.174.744 1.174h2.52c1.243 0 2.261 1.01 2.146 2.247a23.864 23.864 0 0 1-1.341 5.974C17.153 16.323 16.072 17 14.9 17h-3.192a3 3 0 0 1-1.341-.317l-2.734-1.366A3 3 0 0 0 6.292 15H5V8h.963c.685 0 1.258-.483 1.612-1.068a4.011 4.011 0 0 1 2.166-1.73c.432-.143.853-.386 1.011-.814.16-.432.248-.9.248-1.388Z" />
</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-thumbs-up"><path d="M7 10v12"/><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"/></svg>

Before

Width:  |  Height:  |  Size: 569 B

After

Width:  |  Height:  |  Size: 404 B

View File

@@ -1 +0,0 @@
<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-user-round-check-icon lucide-user-round-check"><path d="M2 21a8 8 0 0 1 13.292-6"/><circle cx="10" cy="8" r="5"/><path d="m16 19 2 2 4-4"/></svg>

Before

Width:  |  Height:  |  Size: 348 B

View File

@@ -1,5 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 2L6.72534 5.87534C6.6601 6.07367 6.5492 6.25392 6.40155 6.40155C6.25392 6.5492 6.07367 6.6601 5.87534 6.72534L2 8L5.87534 9.27466C6.07367 9.3399 6.25392 9.4508 6.40155 9.59845C6.5492 9.74608 6.6601 9.92633 6.72534 10.1247L8 14L9.27466 10.1247C9.3399 9.92633 9.4508 9.74608 9.59845 9.59845C9.74608 9.4508 9.92633 9.3399 10.1247 9.27466L14 8L10.1247 6.72534C9.92633 6.6601 9.74608 6.5492 9.59845 6.40155C9.4508 6.25392 9.3399 6.07367 9.27466 5.87534L8 2Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.33334 2V4.66666M2 3.33334H4.66666" stroke="black" stroke-opacity="0.75" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.6665 11.3333V14M11.3333 12.6666H13.9999" stroke="black" stroke-opacity="0.75" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 1.75L5.88467 5.14092C5.82759 5.31446 5.73055 5.47218 5.60136 5.60136C5.47218 5.73055 5.31446 5.82759 5.14092 5.88467L1.75 7L5.14092 8.11533C5.31446 8.17241 5.47218 8.26945 5.60136 8.39864C5.73055 8.52782 5.82759 8.68554 5.88467 8.85908L7 12.25L8.11533 8.85908C8.17241 8.68554 8.26945 8.52782 8.39864 8.39864C8.52782 8.26945 8.68554 8.17241 8.85908 8.11533L12.25 7L8.85908 5.88467C8.68554 5.82759 8.52782 5.73055 8.39864 5.60136C8.26945 5.47218 8.17241 5.31446 8.11533 5.14092L7 1.75Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.91667 1.75V4.08333M1.75 2.91667H4.08333" stroke="black" stroke-opacity="0.75" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.0833 9.91667V12.25M9.91667 11.0833H12.25" stroke="black" stroke-opacity="0.75" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 998 B

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,3 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.99207 8.14741C5.37246 8.14741 5.73726 7.9963 6.00623 7.72733C6.27521 7.45836 6.42631 7.09355 6.42631 6.71317C6.42631 5.92147 6.13946 5.56578 5.85262 4.99208C5.23761 3.76265 5.72411 2.66631 7.00001 1.5499C7.28686 2.98414 8.1474 4.36101 9.2948 5.27893C10.4422 6.19684 11.0159 7.28687 11.0159 8.43426C11.0159 8.96163 10.912 9.48384 10.7102 9.97107C10.5084 10.4583 10.2126 10.901 9.83967 11.2739C9.46676 11.6468 9.02405 11.9426 8.53682 12.1444C8.04959 12.3463 7.52738 12.4501 7.00001 12.4501C6.47264 12.4501 5.95043 12.3463 5.4632 12.1444C4.97597 11.9426 4.53326 11.6468 4.16035 11.2739C3.78745 10.901 3.49164 10.4583 3.28982 9.97107C3.088 9.48384 2.98413 8.96163 2.98413 8.43426C2.98413 7.77279 3.23254 7.1182 3.55783 6.71317C3.55783 7.09355 3.70894 7.45836 3.97791 7.72733C4.24688 7.9963 4.61169 8.14741 4.99207 8.14741Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1018 B

View File

@@ -1,13 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2595_5640)">
<path d="M4.99207 8.14741C5.37246 8.14741 5.73726 7.9963 6.00623 7.72733C6.27521 7.45836 6.42631 7.09355 6.42631 6.71317C6.42631 5.92147 6.13946 5.56578 5.85262 4.99208C5.23761 3.76265 5.72411 2.66631 7.00001 1.5499C7.28686 2.98414 8.1474 4.36101 9.2948 5.27893C10.4422 6.19684 11.0159 7.28687 11.0159 8.43426C11.0159 8.96163 10.912 9.48384 10.7102 9.97107C10.5084 10.4583 10.2126 10.901 9.83967 11.2739C9.46676 11.6468 9.02405 11.9426 8.53682 12.1444C8.04959 12.3463 7.52738 12.4501 7.00001 12.4501C6.47264 12.4501 5.95043 12.3463 5.4632 12.1444C4.97597 11.9426 4.53326 11.6468 4.16035 11.2739C3.78745 10.901 3.49164 10.4583 3.28982 9.97107C3.088 9.48384 2.98413 8.96163 2.98413 8.43426C2.98413 7.77279 3.23254 7.1182 3.55783 6.71317C3.55783 7.09355 3.70894 7.45836 3.97791 7.72733C4.24688 7.9963 4.61169 8.14741 4.99207 8.14741Z" fill="black" fill-opacity="0.5" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 4C2.55228 4 3 3.55228 3 3C3 2.44772 2.55228 2 2 2C1.44772 2 1 2.44772 1 3C1 3.55228 1.44772 4 2 4Z" fill="black"/>
<path d="M10 2C10.5523 2 11 1.55228 11 1C11 0.44772 10.5523 0 10 0C9.44772 0 9 0.44772 9 1C9 1.55228 9.44772 2 10 2Z" fill="black"/>
<path d="M13 5C13.5522 5 14 4.55228 14 4C14 3.44772 13.5522 3 13 3C12.4478 3 12 3.44772 12 4C12 4.55228 12.4478 5 13 5Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_2595_5640">
<rect width="14" height="14" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,14 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2489_484)">
<path d="M11 8.9V11C8.51716 11 7.48284 11 5 11V10.4L11 5.6V5H5V7.1" stroke="black" stroke-width="1.5"/>
<path d="M1.5 5.5V1.5H5" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M14.5 5.5V1.5H11" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M1.5 10.5V14.5H5" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M14.5 10.5V14.5H11" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
<defs>
<clipPath id="clip0_2489_484">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@@ -1,334 +0,0 @@
<svg width="400" height="92" viewBox="0 0 400 92" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2501_1466)">
<path d="M73.6743 -4.41071L75.5416 -1.32632L73.4126 1.58358" stroke="black" stroke-width="1.5"/>
<path d="M71.6108 -2.99939L72.5445 -1.4572L71.48 -0.00224495" stroke="black" stroke-width="1.5"/>
<path d="M69.0085 -0.710689L68.9169 1.38731C66.8498 1.29706 65.9887 1.25947 63.9216 1.16922L63.9478 0.569787L69.1524 -4.00755L69.1786 -4.60698L64.1833 -4.82507L64.0917 -2.72707" stroke="black" stroke-width="1.5"/>
<path d="M91.1332 -5.23706C90.8807 -5.51263 90.529 -5.67673 90.1555 -5.69303C89.7821 -5.70934 89.4174 -5.57672 89.1418 -5.3242C88.8661 -5.07169 88.7021 -4.72001 88.6858 -4.34656C88.6694 -3.97312 88.8021 -3.60849 89.0546 -3.33281L95.4319 3.62501C95.5424 3.74604 95.681 3.83803 95.8356 3.89288L97.9807 4.64767C98.0234 4.66247 98.0692 4.66544 98.1133 4.65629C98.1576 4.64713 98.1984 4.62618 98.2317 4.59567C98.2649 4.56516 98.2894 4.52621 98.3023 4.48296C98.3152 4.43971 98.3161 4.39377 98.305 4.35003L97.7405 2.14679C97.6998 1.98827 97.6207 1.84218 97.5104 1.72135L91.1332 -5.23706Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M88.9944 0.874369L87.9954 0.83075" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M91.8171 5.0014L91.8608 4.00236" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M89.9062 2.91609L88.8635 3.87152" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<g clip-path="url(#clip1_2501_1466)">
<mask id="mask0_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="109" y="-8" width="17" height="17">
<path d="M125.843 -7.13772L109.858 -7.83563L109.16 8.14914L125.145 8.84705L125.843 -7.13772Z" fill="white"/>
</mask>
<g mask="url(#mask0_2501_1466)">
<path d="M120.459 1.53575L120.368 3.63375C117.887 3.52545 116.854 3.48034 114.374 3.37204L114.4 2.77261L120.603 -1.76111L120.63 -2.36054L114.635 -2.62225L114.544 -0.524252" stroke="black" stroke-width="1.5"/>
<path d="M110.899 2.71985L110.724 6.71604L114.221 6.86871" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M123.886 3.28688L123.712 7.28308L120.215 7.13041" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<path d="M141.74 -4.44171L140.298 -0.625665C140.224 -0.430368 140.105 -0.255126 139.951 -0.114073C139.798 0.026993 139.613 0.129928 139.412 0.186449L135.484 1.29086L139.3 2.73335C139.496 2.80717 139.671 2.92583 139.812 3.07978C139.953 3.23371 140.056 3.41863 140.113 3.61962L141.217 7.54686L142.659 3.73082C142.733 3.53552 142.852 3.36028 143.006 3.21922C143.16 3.07816 143.345 2.97522 143.546 2.9187L147.473 1.81429L143.657 0.371802C143.462 0.297979 143.286 0.179319 143.145 0.0253739C143.004 -0.12856 142.901 -0.313477 142.845 -0.514465L141.74 -4.44171Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M145.995 5.08642L145.879 7.75054M144.605 6.36028L147.269 6.4766" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M169.583 -0.223267L171.45 2.86112L169.321 5.77102" stroke="black" stroke-width="1.5"/>
<path d="M167.52 1.18805L168.453 2.73024L167.389 4.18519" stroke="black" stroke-width="1.5"/>
<path d="M164.917 3.47675L164.826 5.57475C162.759 5.4845 161.897 5.4469 159.83 5.35666L159.856 4.75723L165.061 0.179892L165.087 -0.419537L160.092 -0.637634L160 1.46037" stroke="black" stroke-width="1.5"/>
<g clip-path="url(#clip2_2501_1466)">
<mask id="mask1_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="181" y="-5" width="17" height="17">
<path d="M197.774 -3.99716L181.79 -4.69507L181.092 11.2897L197.076 11.9876L197.774 -3.99716Z" fill="white"/>
</mask>
<g mask="url(#mask1_2501_1466)">
<path d="M192.391 4.67631L192.299 6.77432C189.819 6.66602 188.785 6.6209 186.305 6.5126L186.331 5.91317L192.535 1.37946L192.561 0.780027L186.567 0.518311L186.475 2.61631" stroke="black" stroke-width="1.5"/>
<path d="M183.048 0.86515L183.223 -3.13104L186.719 -2.97837" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M196.036 1.43219L196.21 -2.56401L192.714 -2.71667" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M182.83 5.86041L182.656 9.85661L186.152 10.0093" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M195.818 6.42745L195.643 10.4236L192.147 10.271" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<path d="M211.019 -0.00268162C210.766 -0.278258 210.415 -0.442354 210.041 -0.458659C209.668 -0.474965 209.303 -0.342345 209.028 -0.0898278C208.752 0.162689 208.588 0.514369 208.571 0.887811C208.555 1.26125 208.688 1.62589 208.94 1.90157L215.318 8.85938C215.428 8.98042 215.567 9.0724 215.721 9.12725L217.866 9.88204C217.909 9.89685 217.955 9.89982 217.999 9.89067C218.043 9.88151 218.084 9.86056 218.117 9.83004C218.151 9.79953 218.175 9.76058 218.188 9.71733C218.201 9.67408 218.202 9.62815 218.191 9.5844L217.626 7.38117C217.586 7.22265 217.506 7.07656 217.396 6.95572L211.019 -0.00268162Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M209.501 2.52063L211.587 0.609772" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M208.88 6.10874L207.881 6.06512" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M218.174 3.32983L219.173 3.37345" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M211.703 10.2358L211.747 9.23673" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M215.351 -0.797205L215.307 0.201843" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M209.792 8.15047L208.749 9.1059" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M217.262 1.28815L218.305 0.332718" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M241.514 2.91736L243.382 6.00174L241.253 8.91165" stroke="black" stroke-width="1.5"/>
<path d="M239.451 4.32867L240.385 5.87087L239.32 7.32582" stroke="black" stroke-width="1.5"/>
<path d="M236.849 6.61738L236.757 8.71538C234.69 8.62513 233.829 8.58753 231.762 8.49728L231.788 7.89785L236.993 3.32052L237.019 2.72109L232.023 2.50299L231.932 4.60099" stroke="black" stroke-width="1.5"/>
<path d="M258.973 2.09101C258.721 1.81543 258.369 1.65134 257.996 1.63503C257.622 1.61872 257.258 1.75134 256.982 2.00386C256.706 2.25638 256.542 2.60806 256.526 2.9815C256.509 3.35494 256.642 3.71958 256.895 3.99525L263.272 10.9531C263.382 11.0741 263.521 11.1661 263.676 11.2209L265.821 11.9757C265.863 11.9905 265.909 11.9935 265.953 11.9844C265.998 11.9752 266.039 11.9542 266.072 11.9237C266.105 11.8932 266.129 11.8543 266.142 11.811C266.155 11.7678 266.156 11.7218 266.145 11.6781L265.581 9.47486C265.54 9.31634 265.461 9.17025 265.35 9.04941L258.973 2.09101Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M257.456 4.61432L259.541 2.70346" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M256.834 8.20243L255.835 8.15881" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M266.128 5.42352L267.127 5.46714" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M259.657 12.3295L259.701 11.3304" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M263.305 1.29648L263.262 2.29553" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M257.746 10.2442L256.704 11.1996" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M265.216 3.38184L266.259 2.42641" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<g clip-path="url(#clip3_2501_1466)">
<mask id="mask2_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="277" y="-1" width="17" height="18">
<path d="M293.683 0.190342L277.698 -0.507568L277 15.4772L292.985 16.1751L293.683 0.190342Z" fill="white"/>
</mask>
<g mask="url(#mask2_2501_1466)">
<path d="M288.3 8.86381L288.208 10.9618C285.727 10.8535 284.694 10.8084 282.214 10.7001L282.24 10.1007L288.443 5.56696L288.47 4.96753L282.475 4.70581L282.384 6.80381" stroke="black" stroke-width="1.5"/>
<path d="M278.957 5.05265L279.131 1.05646L282.628 1.20913" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M291.945 5.61969L292.119 1.62349L288.622 1.47083" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M278.739 10.0479L278.564 14.0441L282.061 14.1968" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M291.726 10.6149L291.552 14.6111L288.055 14.4585" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<g clip-path="url(#clip4_2501_1466)">
<path d="M309.58 2.88629L308.138 6.70234C308.064 6.89763 307.945 7.07288 307.792 7.21393C307.638 7.355 307.453 7.45793 307.252 7.51445L303.324 8.61886L307.141 10.0614C307.336 10.1352 307.511 10.2538 307.652 10.4078C307.793 10.5617 307.896 10.7466 307.953 10.9476L309.057 14.8749L310.5 11.0588C310.573 10.8635 310.692 10.6883 310.846 10.5472C311 10.4062 311.185 10.3032 311.386 10.2467L315.313 9.14229L311.497 7.6998C311.302 7.62598 311.126 7.50732 310.985 7.35338C310.844 7.19944 310.741 7.01453 310.685 6.81354L309.58 2.88629Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M304.918 2.68273L304.802 5.34686M303.528 3.95664L306.192 4.07296" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M313.836 12.4144L313.719 15.0785M312.446 13.6882L315.11 13.8045" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<path d="M337.423 7.1048L339.29 10.1892L337.161 13.0991" stroke="black" stroke-width="1.5"/>
<path d="M335.36 8.51611L336.293 10.0583L335.229 11.5133" stroke="black" stroke-width="1.5"/>
<path d="M332.757 10.8048L332.666 12.9028C330.599 12.8126 329.737 12.775 327.67 12.6847L327.697 12.0853L332.901 7.50796L332.927 6.90853L327.932 6.69043L327.841 8.78843" stroke="black" stroke-width="1.5"/>
<g clip-path="url(#clip5_2501_1466)">
<mask id="mask3_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="348" y="2" width="18" height="18">
<path d="M365.614 3.33091L349.63 2.633L348.932 18.6178L364.917 19.3157L365.614 3.33091Z" fill="white"/>
</mask>
<g mask="url(#mask3_2501_1466)">
<path d="M360.231 12.0044L360.139 14.1024C357.659 13.9941 356.625 13.949 354.145 13.8407L354.171 13.2412L360.375 8.70752L360.401 8.10809L354.407 7.84637L354.315 9.94438" stroke="black" stroke-width="1.5"/>
<path d="M350.888 8.19321L351.063 4.19702L354.559 4.34969" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M363.876 8.76025L364.05 4.76406L360.554 4.61139" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M350.67 13.1885L350.496 17.1847L353.992 17.3373" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M363.658 13.7555L363.483 17.7517L359.987 17.599" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<path d="M378.859 7.32532C378.606 7.04974 378.255 6.88565 377.881 6.86934C377.508 6.85304 377.143 6.98566 376.868 7.23818C376.592 7.49069 376.428 7.84237 376.412 8.21581C376.395 8.58926 376.528 8.95389 376.78 9.22957L383.158 16.1874C383.268 16.3084 383.407 16.4004 383.561 16.4553L385.707 17.21C385.749 17.2249 385.795 17.2278 385.839 17.2187C385.883 17.2095 385.924 17.1886 385.958 17.158C385.991 17.1275 386.015 17.0886 386.028 17.0453C386.041 17.0021 386.042 16.9562 386.031 16.9124L385.466 14.7092C385.426 14.5507 385.347 14.4046 385.236 14.2837L378.859 7.32532Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M377.341 9.84863L379.427 7.93778" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M376.72 13.4367L375.721 13.3931" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M386.014 10.6578L387.013 10.7015" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M379.543 17.5638L379.587 16.5647" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M383.191 6.5308L383.147 7.52985" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M377.632 15.4785L376.589 16.4339" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M385.102 8.61615L386.145 7.66072" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<g clip-path="url(#clip6_2501_1466)">
<mask id="mask4_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="58" y="46" width="18" height="17">
<path d="M75.446 46.7152L59.4612 46.0173L58.7633 62.0021L74.748 62.7L75.446 46.7152Z" fill="white"/>
</mask>
<g mask="url(#mask4_2501_1466)">
<path d="M70.0625 55.3887L69.9709 57.4867C67.4904 57.3784 66.457 57.3333 63.9766 57.225L64.0027 56.6256L70.2064 52.0919L70.2326 51.4924L64.2383 51.2307L64.1467 53.3287" stroke="black" stroke-width="1.5"/>
<path d="M60.7198 51.5776L60.8943 47.5814L64.391 47.734" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M73.7074 52.1446L73.8819 48.1484L70.3853 47.9957" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M60.5017 56.5728L60.3272 60.569L63.8239 60.7217" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M73.4893 57.1399L73.3149 61.136L69.8182 60.9834" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<path d="M91.3434 49.4113L89.9009 53.2273C89.8271 53.4226 89.7084 53.5978 89.5545 53.7389C89.4006 53.88 89.2156 53.9829 89.0147 54.0394L85.0874 55.1438L88.9035 56.5863C89.0988 56.6601 89.274 56.7788 89.415 56.9327C89.5561 57.0867 89.659 57.2716 89.7156 57.4726L90.82 61.3998L92.2625 57.5838C92.3363 57.3885 92.4549 57.2132 92.6089 57.0722C92.7628 56.9311 92.9477 56.8282 93.1487 56.7717L97.076 55.6673L93.2599 54.2248C93.0646 54.1509 92.8894 54.0323 92.7483 53.8783C92.6073 53.7244 92.5043 53.5395 92.4478 53.3385L91.3434 49.4113Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M86.6812 49.2077L86.5649 51.8718M85.291 50.4816L87.9551 50.5979" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M95.5984 58.9394L95.4821 61.6035M94.2082 60.2132L96.8723 60.3296" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M112.668 51.7565C112.415 51.481 112.063 51.3169 111.69 51.3006C111.316 51.2843 110.952 51.4169 110.676 51.6694C110.401 51.9219 110.237 52.2736 110.22 52.647C110.204 53.0205 110.337 53.3851 110.589 53.6608L116.966 60.6186C117.077 60.7396 117.215 60.8316 117.37 60.8865L119.515 61.6413C119.558 61.6561 119.604 61.659 119.648 61.6499C119.692 61.6407 119.733 61.6198 119.766 61.5893C119.799 61.5587 119.824 61.5198 119.837 61.4765C119.85 61.4333 119.85 61.3874 119.839 61.3436L119.275 59.1404C119.234 58.9819 119.155 58.8358 119.045 58.7149L112.668 51.7565Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M111.15 54.2798L113.235 52.369" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M110.529 57.868L109.53 57.8243" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M119.822 55.0891L120.821 55.1327" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M113.352 61.995L113.395 60.9959" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M117 50.962L116.956 51.9611" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M111.441 59.9097L110.398 60.8651" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M118.911 53.0474L119.953 52.0919" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M143.163 54.6766L145.03 57.761L142.901 60.6709" stroke="black" stroke-width="1.5"/>
<path d="M141.1 56.088L142.033 57.6301L140.969 59.0851" stroke="black" stroke-width="1.5"/>
<path d="M138.497 58.3767L138.406 60.4747C136.339 60.3844 135.477 60.3468 133.41 60.2566L133.437 59.6571L138.641 55.0798L138.667 54.4804L133.672 54.2623L133.581 56.3603" stroke="black" stroke-width="1.5"/>
<g clip-path="url(#clip7_2501_1466)">
<mask id="mask5_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="154" y="50" width="18" height="17">
<path d="M171.355 50.9027L155.37 50.2048L154.672 66.1895L170.657 66.8875L171.355 50.9027Z" fill="white"/>
</mask>
<g mask="url(#mask5_2501_1466)">
<path d="M165.971 59.5762L165.88 61.6742C163.399 61.5659 162.366 61.5207 159.885 61.4124L159.911 60.813L166.115 56.2793L166.141 55.6799L160.147 55.4182L160.055 57.5162" stroke="black" stroke-width="1.5"/>
<path d="M156.629 55.765L156.803 51.7688L160.3 51.9215" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M169.616 56.332L169.791 52.3358L166.294 52.1832" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M156.41 60.7603L156.236 64.7564L159.733 64.9091" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M169.398 61.3273L169.224 65.3235L165.727 65.1708" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<path d="M187.252 53.5987L185.81 57.4147C185.736 57.61 185.617 57.7853 185.463 57.9263C185.309 58.0674 185.124 58.1703 184.923 58.2269L180.996 59.3313L184.812 60.7738C185.007 60.8476 185.183 60.9662 185.324 61.1202C185.465 61.2741 185.568 61.459 185.624 61.66L186.729 65.5873L188.171 61.7712C188.245 61.5759 188.364 61.4007 188.518 61.2596C188.672 61.1186 188.856 61.0156 189.057 60.9591L192.985 59.8547L189.169 58.4122C188.973 58.3384 188.798 58.2197 188.657 58.0658C188.516 57.9118 188.413 57.7269 188.357 57.5259L187.252 53.5987Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M182.59 53.3951L182.474 56.0593M181.2 54.669L183.864 54.7854" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M191.507 63.1268L191.391 65.7909M190.117 64.4007L192.781 64.517" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M208.576 55.944C208.324 55.6685 207.972 55.5044 207.599 55.4881C207.225 55.4718 206.861 55.6044 206.585 55.8569C206.309 56.1094 206.145 56.4611 206.129 56.8345C206.113 57.208 206.245 57.5726 206.498 57.8483L212.875 64.8061C212.986 64.9271 213.124 65.0191 213.279 65.074L215.424 65.8288C215.466 65.8436 215.512 65.8465 215.556 65.8374C215.601 65.8282 215.642 65.8073 215.675 65.7768C215.708 65.7462 215.732 65.7073 215.745 65.664C215.758 65.6208 215.759 65.5749 215.748 65.5311L215.184 63.3279C215.143 63.1694 215.064 63.0233 214.953 62.9024L208.576 55.944Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M207.059 58.4673L209.144 56.5565" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M206.438 62.0555L205.438 62.0118" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M215.731 59.2766L216.73 59.3202" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M209.26 66.1825L209.304 65.1834" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M212.908 55.1495L212.865 56.1486" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M207.349 64.0972L206.307 65.0526" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M214.819 57.2349L215.862 56.2794" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M239.072 58.8641L240.939 61.9485L238.81 64.8584" stroke="black" stroke-width="1.5"/>
<path d="M237.008 60.2754L237.942 61.8176L236.877 63.2725" stroke="black" stroke-width="1.5"/>
<path d="M234.406 62.5641L234.314 64.6621C232.247 64.5718 231.386 64.5342 229.319 64.444L229.345 63.8446L234.55 59.2672L234.576 58.6678L229.581 58.4497L229.489 60.5477" stroke="black" stroke-width="1.5"/>
<g clip-path="url(#clip8_2501_1466)">
<mask id="mask6_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="250" y="54" width="18" height="18">
<path d="M267.263 55.0902L251.278 54.3923L250.58 70.377L266.565 71.075L267.263 55.0902Z" fill="white"/>
</mask>
<g mask="url(#mask6_2501_1466)">
<path d="M261.88 63.7637L261.788 65.8617C259.308 65.7534 258.274 65.7082 255.794 65.5999L255.82 65.0005L262.024 60.4668L262.05 59.8674L256.055 59.6057L255.964 61.7037" stroke="black" stroke-width="1.5"/>
<path d="M252.537 59.9525L252.711 55.9563L256.208 56.109" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M265.525 60.5195L265.699 56.5233L262.202 56.3707" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M252.319 64.9478L252.144 68.9439L255.641 69.0966" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M265.306 65.5148L265.132 69.511L261.635 69.3583" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<g clip-path="url(#clip9_2501_1466)">
<path d="M283.161 57.7862L281.718 61.6022C281.644 61.7975 281.526 61.9728 281.372 62.1138C281.218 62.2549 281.033 62.3578 280.832 62.4144L276.905 63.5188L280.721 64.9613C280.916 65.0351 281.091 65.1537 281.232 65.3077C281.373 65.4616 281.476 65.6465 281.533 65.8475L282.637 69.7748L284.08 65.9587C284.154 65.7634 284.272 65.5882 284.426 65.4471C284.58 65.3061 284.765 65.2031 284.966 65.1466L288.893 64.0422L285.077 62.5997C284.882 62.5259 284.707 62.4072 284.566 62.2533C284.425 62.0993 284.322 61.9144 284.265 61.7134L283.161 57.7862Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M278.499 57.5826L278.382 60.2468M277.108 58.8565L279.772 58.9729" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M287.416 67.3143L287.3 69.9784M286.026 68.5881L288.69 68.7044" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<path d="M304.485 60.1315C304.232 59.8559 303.881 59.6918 303.507 59.6755C303.134 59.6592 302.769 59.7918 302.494 60.0443C302.218 60.2968 302.054 60.6485 302.038 61.022C302.021 61.3954 302.154 61.76 302.406 62.0357L308.784 68.9935C308.894 69.1146 309.033 69.2066 309.187 69.2614L311.332 70.0162C311.375 70.031 311.421 70.034 311.465 70.0248C311.509 70.0157 311.55 69.9947 311.583 69.9642C311.617 69.9337 311.641 69.8947 311.654 69.8515C311.667 69.8082 311.668 69.7623 311.657 69.7186L311.092 67.5153C311.052 67.3568 310.973 67.2107 310.862 67.0899L304.485 60.1315Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M302.967 62.6548L305.053 60.7439" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M302.346 66.2429L301.347 66.1993" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M311.64 63.464L312.639 63.5076" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M305.169 70.3699L305.213 69.3709" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M308.817 59.337L308.773 60.336" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M303.258 68.2846L302.215 69.2401" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M310.728 61.4223L311.771 60.4669" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<g clip-path="url(#clip10_2501_1466)">
<path d="M331.115 59.8799L329.673 63.6959C329.599 63.8912 329.48 64.0665 329.326 64.2075C329.172 64.3486 328.987 64.4515 328.786 64.508L324.859 65.6125L328.675 67.0549C328.87 67.1288 329.046 67.2474 329.187 67.4014C329.328 67.5553 329.431 67.7402 329.487 67.9412L330.592 71.8685L332.034 68.0524C332.108 67.8571 332.227 67.6819 332.381 67.5408C332.535 67.3998 332.719 67.2968 332.92 67.2403L336.848 66.1359L333.032 64.6934C332.836 64.6196 332.661 64.5009 332.52 64.347C332.379 64.193 332.276 64.0081 332.22 63.8071L331.115 59.8799Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M326.453 59.6763L326.337 62.3404M325.063 60.9502L327.727 61.0666" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M335.37 69.4079L335.254 72.0721M333.98 70.6818L336.644 70.7981" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<path d="M358.958 64.0984L360.825 67.1828L358.696 70.0927" stroke="black" stroke-width="1.5"/>
<path d="M356.894 65.5097L357.828 67.0519L356.763 68.5068" stroke="black" stroke-width="1.5"/>
<path d="M354.292 67.7984L354.2 69.8964C352.133 69.8062 351.272 69.7686 349.205 69.6783L349.231 69.0789L354.436 64.5015L354.462 63.9021L349.467 63.684L349.375 65.782" stroke="black" stroke-width="1.5"/>
<g clip-path="url(#clip11_2501_1466)">
<mask id="mask7_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="370" y="59" width="18" height="18">
<path d="M387.149 60.3245L371.164 59.6266L370.466 75.6114L386.451 76.3093L387.149 60.3245Z" fill="white"/>
</mask>
<g mask="url(#mask7_2501_1466)">
<path d="M381.766 68.998L381.674 71.096C379.194 70.9877 378.16 70.9426 375.68 70.8343L375.706 70.2348L381.91 65.7011L381.936 65.1017L375.941 64.84L375.85 66.938" stroke="black" stroke-width="1.5"/>
<path d="M372.423 65.1868L372.597 61.1906L376.094 61.3433" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M385.411 65.7538L385.585 61.7576L382.088 61.605" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M372.205 70.1821L372.03 74.1783L375.527 74.3309" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M385.192 70.7491L385.018 74.7453L381.521 74.5926" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<path d="M402.246 69.8922L402.154 71.9902C400.087 71.8999 399.226 71.8623 397.159 71.7721L397.185 71.1726L402.39 66.5953L402.416 65.9959L397.421 65.7778L397.329 67.8758" stroke="black" stroke-width="1.5"/>
<path d="M53.9459 21.166C53.6934 20.8904 53.3417 20.7263 52.9683 20.71C52.5948 20.6937 52.2302 20.8263 51.9545 21.0788C51.6789 21.3313 51.5149 21.683 51.4985 22.0565C51.4821 22.4299 51.6148 22.7945 51.8673 23.0702L58.2446 30.028C58.3551 30.1491 58.4938 30.241 58.6483 30.2959L60.7934 31.0507C60.8361 31.0655 60.8819 31.0685 60.9261 31.0593C60.9703 31.0501 61.0112 31.0292 61.0444 30.9987C61.0777 30.9682 61.1021 30.9292 61.115 30.886C61.1279 30.8427 61.1288 30.7968 61.1177 30.753L60.5533 28.5498C60.5126 28.3913 60.4335 28.2452 60.3231 28.1244L53.9459 21.166Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M52.4282 23.6893L54.5136 21.7784" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M51.8072 27.2774L50.8081 27.2338" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M61.1006 24.4985L62.0996 24.5421" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M54.6299 31.4044L54.6735 30.4054" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M58.278 20.3714L58.2344 21.3705" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M52.7189 29.3191L51.6763 30.2745" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M60.189 22.4568L61.2316 21.5014" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M84.4414 24.0861L86.3086 27.1704L84.1797 30.0803" stroke="black" stroke-width="1.5"/>
<path d="M82.3779 25.4974L83.3115 27.0396L82.2471 28.4945" stroke="black" stroke-width="1.5"/>
<path d="M79.7756 27.7861L79.684 29.8841C77.6169 29.7938 76.7558 29.7562 74.6887 29.666L74.7149 29.0666L79.9195 24.4892L79.9457 23.8898L74.9504 23.6717L74.8588 25.7697" stroke="black" stroke-width="1.5"/>
<path d="M104.553 21.9613L103.111 25.7773C103.037 25.9726 102.918 26.1479 102.764 26.2889C102.611 26.43 102.426 26.5329 102.225 26.5895L98.2974 27.6939L102.113 29.1364C102.309 29.2102 102.484 29.3288 102.625 29.4828C102.766 29.6367 102.869 29.8216 102.926 30.0226L104.03 33.9499L105.472 30.1338C105.546 29.9385 105.665 29.7633 105.819 29.6222C105.973 29.4812 106.158 29.3782 106.359 29.3217L110.286 28.2173L106.47 26.7748C106.275 26.701 106.099 26.5823 105.958 26.4284C105.817 26.2745 105.714 26.0895 105.658 25.8885L104.553 21.9613Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M99.8912 21.7577L99.7748 24.4219M98.5009 23.0317L101.165 23.148" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M108.808 31.4894L108.692 34.1536M107.418 32.7633L110.082 32.8796" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<g clip-path="url(#clip12_2501_1466)">
<mask id="mask8_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="119" y="20" width="18" height="18">
<path d="M136.61 21.359L120.625 20.6611L119.927 36.6459L135.912 37.3438L136.61 21.359Z" fill="white"/>
</mask>
<g mask="url(#mask8_2501_1466)">
<path d="M131.227 30.0325L131.135 32.1305C128.654 32.0222 127.621 31.9771 125.141 31.8688L125.167 31.2694L131.37 26.7357L131.397 26.1362L125.402 25.8745L125.311 27.9725" stroke="black" stroke-width="1.5"/>
<path d="M121.884 26.2214L122.058 22.2252L125.555 22.3778" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M134.872 26.7884L135.046 22.7922L131.549 22.6395" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M121.666 31.2166L121.491 35.2128L124.988 35.3655" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M134.653 31.7836L134.479 35.7798L130.982 35.6272" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<path d="M149.855 25.3535C149.602 25.0779 149.25 24.9138 148.877 24.8975C148.504 24.8812 148.139 25.0138 147.863 25.2663C147.588 25.5188 147.424 25.8705 147.407 26.244C147.391 26.6174 147.524 26.982 147.776 27.2577L154.153 34.2155C154.264 34.3366 154.402 34.4285 154.557 34.4834L156.702 35.2382C156.745 35.253 156.791 35.256 156.835 35.2468C156.879 35.2376 156.92 35.2167 156.953 35.1862C156.986 35.1557 157.011 35.1167 157.024 35.0735C157.037 35.0302 157.038 34.9843 157.026 34.9405L156.462 32.7373C156.421 32.5788 156.342 32.4327 156.232 32.3119L149.855 25.3535Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M148.337 27.8768L150.422 25.9659" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M147.716 31.4649L146.717 31.4213" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M157.009 28.686L158.008 28.7296" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M150.539 35.5919L150.582 34.5929" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M154.187 24.5589L154.143 25.558" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M148.628 33.5066L147.585 34.462" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M156.098 26.6443L157.14 25.6889" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M180.35 28.2735L182.217 31.3579L180.088 34.2678" stroke="black" stroke-width="1.5"/>
<path d="M178.287 29.6848L179.22 31.227L178.156 32.682" stroke="black" stroke-width="1.5"/>
<path d="M175.684 31.9735L175.593 34.0715C173.526 33.9813 172.664 33.9437 170.597 33.8534L170.624 33.254L175.828 28.6767L175.854 28.0772L170.859 27.8591L170.768 29.9571" stroke="black" stroke-width="1.5"/>
<path d="M200.462 26.1487L199.02 29.9648C198.946 30.1601 198.827 30.3353 198.673 30.4764C198.519 30.6174 198.334 30.7204 198.133 30.7769L194.206 31.8813L198.022 33.3238C198.217 33.3976 198.393 33.5163 198.534 33.6702C198.675 33.8242 198.778 34.0091 198.834 34.2101L199.939 38.1373L201.381 34.3213C201.455 34.126 201.574 33.9507 201.728 33.8097C201.881 33.6686 202.066 33.5657 202.267 33.5092L206.195 32.4047L202.379 30.9623C202.183 30.8884 202.008 30.7698 201.867 30.6158C201.726 30.4619 201.623 30.277 201.566 30.076L200.462 26.1487Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M195.8 25.9452L195.684 28.6093M194.41 27.2191L197.074 27.3354" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M204.717 35.6769L204.601 38.341M203.327 36.9507L205.991 37.0671" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<g clip-path="url(#clip13_2501_1466)">
<mask id="mask9_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="215" y="24" width="18" height="18">
<path d="M232.519 25.5465L216.534 24.8486L215.836 40.8333L231.821 41.5313L232.519 25.5465Z" fill="white"/>
</mask>
<g mask="url(#mask9_2501_1466)">
<path d="M227.135 34.22L227.044 36.318C224.563 36.2097 223.53 36.1645 221.049 36.0562L221.075 35.4568L227.279 30.9231L227.305 30.3237L221.311 30.062L221.219 32.16" stroke="black" stroke-width="1.5"/>
<path d="M217.793 30.4088L217.967 26.4126L221.464 26.5653" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M230.78 30.9758L230.955 26.9796L227.458 26.827" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M217.574 35.4041L217.4 39.4002L220.897 39.5529" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M230.562 35.9711L230.388 39.9673L226.891 39.8146" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<path d="M245.763 29.5409C245.511 29.2653 245.159 29.1012 244.786 29.0849C244.412 29.0686 244.048 29.2012 243.772 29.4538C243.496 29.7063 243.332 30.0579 243.316 30.4314C243.3 30.8048 243.432 31.1695 243.685 31.4451L250.062 38.403C250.173 38.524 250.311 38.616 250.466 38.6708L252.611 39.4256C252.654 39.4404 252.699 39.4434 252.743 39.4342C252.788 39.4251 252.829 39.4041 252.862 39.3736C252.895 39.3431 252.919 39.3042 252.932 39.2609C252.945 39.2177 252.946 39.1717 252.935 39.128L252.371 36.9247C252.33 36.7662 252.251 36.6201 252.141 36.4993L245.763 29.5409Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M244.246 32.0642L246.331 30.1534" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M243.625 35.6523L242.625 35.6087" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M252.918 32.8734L253.917 32.917" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M246.447 39.7794L246.491 38.7803" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M250.095 28.7464L250.052 29.7454" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M244.536 37.694L243.494 38.6495" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M252.006 30.8317L253.049 29.8763" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M276.259 32.461L278.126 35.5454L275.997 38.4553" stroke="black" stroke-width="1.5"/>
<path d="M274.195 33.8723L275.129 35.4145L274.064 36.8695" stroke="black" stroke-width="1.5"/>
<path d="M271.593 36.161L271.501 38.259C269.434 38.1688 268.573 38.1312 266.506 38.0409L266.532 37.4415L271.737 32.8642L271.763 32.2647L266.768 32.0466L266.676 34.1446" stroke="black" stroke-width="1.5"/>
<g clip-path="url(#clip14_2501_1466)">
<mask id="mask10_2501_1466" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="287" y="27" width="18" height="18">
<path d="M304.45 28.687L288.465 27.9891L287.767 43.9739L303.752 44.6718L304.45 28.687Z" fill="white"/>
</mask>
<g mask="url(#mask10_2501_1466)">
<path d="M299.067 37.3605L298.975 39.4585C296.495 39.3502 295.461 39.3051 292.981 39.1968L293.007 38.5974L299.211 34.0637L299.237 33.4642L293.242 33.2025L293.151 35.3005" stroke="black" stroke-width="1.5"/>
<path d="M289.724 33.5494L289.898 29.5532L293.395 29.7058" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M302.712 34.1164L302.886 30.1202L299.389 29.9675" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M289.506 38.5446L289.331 42.5408L292.828 42.6935" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
<path d="M302.493 39.1117L302.319 43.1078L298.822 42.9552" stroke="black" stroke-opacity="0.5" stroke-width="1.5"/>
</g>
</g>
<path d="M317.695 32.6815C317.442 32.4059 317.091 32.2419 316.717 32.2255C316.344 32.2092 315.979 32.3419 315.703 32.5944C315.428 32.8469 315.264 33.1986 315.247 33.572C315.231 33.9455 315.364 34.3101 315.616 34.5858L321.993 41.5436C322.104 41.6646 322.243 41.7566 322.397 41.8115L324.542 42.5662C324.585 42.5811 324.631 42.584 324.675 42.5749C324.719 42.5657 324.76 42.5448 324.793 42.5142C324.826 42.4837 324.851 42.4448 324.864 42.4015C324.877 42.3583 324.878 42.3124 324.866 42.2686L324.302 40.0654C324.261 39.9069 324.182 39.7608 324.072 39.6399L317.695 32.6815Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M316.177 35.2048L318.262 33.294" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M315.556 38.7929L314.557 38.7493" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M324.849 36.014L325.848 36.0577" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M318.379 42.92L318.422 41.9209" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M322.027 31.887L321.983 32.886" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M316.468 40.8347L315.425 41.7901" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M323.938 33.9724L324.98 33.0169" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M344.325 32.4299L342.882 36.246C342.809 36.4413 342.69 36.6165 342.536 36.7576C342.382 36.8986 342.197 37.0016 341.996 37.0581L338.069 38.1625L341.885 39.605C342.08 39.6788 342.255 39.7975 342.396 39.9514C342.538 40.1054 342.64 40.2903 342.697 40.4913L343.801 44.4185L345.244 40.6025C345.318 40.4072 345.436 40.2319 345.59 40.0909C345.744 39.9498 345.929 39.8469 346.13 39.7903L350.057 38.6859L346.241 37.2434C346.046 37.1696 345.871 37.051 345.73 36.897C345.589 36.7431 345.486 36.5582 345.429 36.3572L344.325 32.4299Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M339.663 32.2264L339.546 34.8905M338.272 33.5003L340.937 33.6166" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M348.58 41.9579L348.464 44.6221M347.19 43.2318L349.854 43.3481" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M372.167 36.6484L374.035 39.7328L371.906 42.6427" stroke="black" stroke-width="1.5"/>
<path d="M370.104 38.0598L371.038 39.6019L369.973 41.0569" stroke="black" stroke-width="1.5"/>
<path d="M367.502 40.3485L367.41 42.4465C365.343 42.3562 364.482 42.3186 362.415 42.2284L362.441 41.6289L367.646 37.0516L367.672 36.4522L362.677 36.2341L362.585 38.3321" stroke="black" stroke-width="1.5"/>
<path d="M392.279 34.5237L390.837 38.3397C390.763 38.535 390.644 38.7103 390.49 38.8513C390.336 38.9924 390.151 39.0953 389.95 39.1518L386.023 40.2563L389.839 41.6987C390.035 41.7726 390.21 41.8912 390.351 42.0452C390.492 42.1991 390.595 42.384 390.651 42.585L391.756 46.5123L393.198 42.6962C393.272 42.5009 393.391 42.3257 393.545 42.1846C393.699 42.0435 393.884 41.9406 394.085 41.8841L398.012 40.7797L394.196 39.3372C394 39.2634 393.825 39.1447 393.684 38.9908C393.543 38.8368 393.44 38.6519 393.384 38.4509L392.279 34.5237Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M387.617 34.3201L387.501 36.9842M386.227 35.594L388.891 35.7103" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M396.534 44.0517L396.418 46.7159M395.144 45.3256L397.808 45.4419" stroke="black" stroke-opacity="0.75" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_2501_1466">
<rect width="400" height="92" fill="white"/>
</clipPath>
<clipPath id="clip1_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(109.858 -7.83563) rotate(2.5)"/>
</clipPath>
<clipPath id="clip2_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(181.79 -4.69507) rotate(2.5)"/>
</clipPath>
<clipPath id="clip3_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(277.698 -0.507568) rotate(2.5)"/>
</clipPath>
<clipPath id="clip4_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(301.675 0.539246) rotate(2.5)"/>
</clipPath>
<clipPath id="clip5_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(349.63 2.633) rotate(2.5)"/>
</clipPath>
<clipPath id="clip6_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(59.4612 46.0173) rotate(2.5)"/>
</clipPath>
<clipPath id="clip7_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(155.37 50.2048) rotate(2.5)"/>
</clipPath>
<clipPath id="clip8_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(251.278 54.3923) rotate(2.5)"/>
</clipPath>
<clipPath id="clip9_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(275.256 55.4391) rotate(2.5)"/>
</clipPath>
<clipPath id="clip10_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(323.21 57.5328) rotate(2.5)"/>
</clipPath>
<clipPath id="clip11_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(371.164 59.6266) rotate(2.5)"/>
</clipPath>
<clipPath id="clip12_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(120.625 20.6611) rotate(2.5)"/>
</clipPath>
<clipPath id="clip13_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(216.534 24.8486) rotate(2.5)"/>
</clipPath>
<clipPath id="clip14_2501_1466">
<rect width="16" height="16" fill="white" transform="translate(288.465 27.9891) rotate(2.5)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -1,153 +0,0 @@
<svg width="441" height="167" viewBox="0 0 441 167" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2500_1167)">
<path d="M440 3H441V98H440V3Z" fill="black"/>
<path d="M429 3H430V98H429V3Z" fill="black"/>
<path d="M418 3H419V98H418V3Z" fill="black"/>
<path d="M407 3H408V98H407V3Z" fill="black"/>
<path d="M396 3H397V98H396V3Z" fill="black"/>
<path d="M385 3H386V98H385V3Z" fill="black"/>
<path d="M374 0H375V95H374V0Z" fill="black"/>
<path d="M308 0H309V95H308V0Z" fill="black"/>
<path d="M242 0H243V95H242V0Z" fill="black"/>
<path d="M429 -2H430V93H429V-2Z" fill="black"/>
<path d="M429 -2H430V93H429V-2Z" fill="black"/>
<path d="M441 11V12L202 12V11L441 11Z" fill="black"/>
<path d="M441 22V23L202 23V22L441 22Z" fill="black"/>
<path d="M441 33V34L202 34V33L441 33Z" fill="black"/>
<path d="M441 44V45L202 45V44L441 44Z" fill="black"/>
<path d="M441 55V56L202 56V55L441 55Z" fill="black"/>
<path d="M440 66V67L201 67V66L440 66Z" fill="black"/>
<path d="M440 77V78L201 78V77L440 77Z" fill="black"/>
<path d="M363 0H364V95H363V0Z" fill="black"/>
<path d="M297 0H298V95H297V0Z" fill="black"/>
<path d="M231 0H232V95H231V0Z" fill="black"/>
<path d="M418 -2H419V93H418V-2Z" fill="black"/>
<path d="M352 0H353V95H352V0Z" fill="black"/>
<path d="M286 0H287V95H286V0Z" fill="black"/>
<path d="M220 0H221V95H220V0Z" fill="black"/>
<path d="M407 -2H408V93H407V-2Z" fill="black"/>
<path d="M341 0H342V95H341V0Z" fill="black"/>
<path d="M275 0H276V95H275V0Z" fill="black"/>
<path d="M396 -2H397V93H396V-2Z" fill="black"/>
<path d="M330 0H331V95H330V0Z" fill="black"/>
<path d="M264 0H265V95H264V0Z" fill="black"/>
<path d="M198 0H199V95H198V0Z" fill="black"/>
<path d="M385 -2H386V93H385V-2Z" fill="black"/>
<path d="M319 0H320V95H319V0Z" fill="black"/>
<path d="M253 0H254V95H253V0Z" fill="black"/>
<path d="M187 0H188V95H187V0Z" fill="black"/>
<path d="M231 3H232V98H231V3Z" fill="black"/>
<path d="M220 3H221V98H220V3Z" fill="black"/>
<path d="M209 3H210V98H209V3Z" fill="black"/>
<path d="M198 3H199V98H198V3Z" fill="black"/>
<path d="M187 3H188V98H187V3Z" fill="black"/>
<path d="M176 3H177V98H176V3Z" fill="black"/>
<path d="M165 0H166V95H165V0Z" fill="black"/>
<path d="M99 0H100V95H99V0Z" fill="black"/>
<path d="M33 0H34V95H33V0Z" fill="black"/>
<path d="M220 -2H221V93H220V-2Z" fill="black"/>
<path d="M220 -2H221V93H220V-2Z" fill="black"/>
<path d="M232 11V12L-7 12V11L232 11Z" fill="black"/>
<path d="M232 22V23L-7 23V22L232 22Z" fill="black"/>
<path d="M232 33V34L-7 34V33L232 33Z" fill="black"/>
<path d="M232 44V45L-7 45V44L232 44Z" fill="black"/>
<path d="M232 55V56L-7 56V55L232 55Z" fill="black"/>
<path d="M231 66V67L-8 67V66L231 66Z" fill="black"/>
<path d="M231 77V78L-8 78V77L231 77Z" fill="black"/>
<path d="M154 0H155V95H154V0Z" fill="black"/>
<path d="M88 0H89V95H88V0Z" fill="black"/>
<path d="M22 0H23V95H22V0Z" fill="black"/>
<path d="M209 -2H210V93H209V-2Z" fill="black"/>
<path d="M143 0H144V95H143V0Z" fill="black"/>
<path d="M77 0H78V95H77V0Z" fill="black"/>
<path d="M11 0H12V95H11V0Z" fill="black"/>
<path d="M198 -2H199V93H198V-2Z" fill="black"/>
<path d="M132 0H133V95H132V0Z" fill="black"/>
<path d="M66 0H67V95H66V0Z" fill="black"/>
<path d="M187 -2H188V93H187V-2Z" fill="black"/>
<path d="M121 0H122V95H121V0Z" fill="black"/>
<path d="M55 0H56V95H55V0Z" fill="black"/>
<path d="M-11 0H-10V95H-11V0Z" fill="black"/>
<path d="M176 -2H177V93H176V-2Z" fill="black"/>
<path d="M110 0H111V95H110V0Z" fill="black"/>
<path d="M44 0H45V95H44V0Z" fill="black"/>
<path d="M-22 0H-21V95H-22V0Z" fill="black"/>
<path d="M440 81H441V176H440V81Z" fill="black"/>
<path d="M429 81H430V176H429V81Z" fill="black"/>
<path d="M418 81H419V176H418V81Z" fill="black"/>
<path d="M407 81H408V176H407V81Z" fill="black"/>
<path d="M396 81H397V176H396V81Z" fill="black"/>
<path d="M385 81H386V176H385V81Z" fill="black"/>
<path d="M374 78H375V173H374V78Z" fill="black"/>
<path d="M308 78H309V173H308V78Z" fill="black"/>
<path d="M242 78H243V173H242V78Z" fill="black"/>
<path d="M429 76H430V171H429V76Z" fill="black"/>
<path d="M429 76H430V171H429V76Z" fill="black"/>
<path d="M441 89V90L202 90V89L441 89Z" fill="black"/>
<path d="M441 100V101L202 101V100L441 100Z" fill="black"/>
<path d="M441 111V112L202 112V111L441 111Z" fill="black"/>
<path d="M441 122V123L202 123V122L441 122Z" fill="black"/>
<path d="M441 133V134L202 134V133L441 133Z" fill="black"/>
<path d="M440 144V145L201 145V144L440 144Z" fill="black"/>
<path d="M440 155V156L201 156V155L440 155Z" fill="black"/>
<path d="M363 78H364V173H363V78Z" fill="black"/>
<path d="M297 78H298V173H297V78Z" fill="black"/>
<path d="M231 78L232 78V173H231V78Z" fill="black"/>
<path d="M418 76H419V171H418V76Z" fill="black"/>
<path d="M352 78H353V173H352V78Z" fill="black"/>
<path d="M286 78H287V173H286V78Z" fill="black"/>
<path d="M220 78H221V173H220V78Z" fill="black"/>
<path d="M407 76H408V171H407V76Z" fill="black"/>
<path d="M341 78H342V173H341V78Z" fill="black"/>
<path d="M275 78H276V173H275V78Z" fill="black"/>
<path d="M396 76H397V171H396V76Z" fill="black"/>
<path d="M330 78H331V173H330V78Z" fill="black"/>
<path d="M264 78H265V173H264V78Z" fill="black"/>
<path d="M198 78H199V173H198V78Z" fill="black"/>
<path d="M385 76H386V171H385V76Z" fill="black"/>
<path d="M319 78H320V173H319V78Z" fill="black"/>
<path d="M253 78H254V173H253V78Z" fill="black"/>
<path d="M187 78H188V173H187V78Z" fill="black"/>
<path d="M231 81H232V176H231V81Z" fill="black"/>
<path d="M220 81H221V176H220V81Z" fill="black"/>
<path d="M209 81H210V176H209V81Z" fill="black"/>
<path d="M198 81H199V176H198V81Z" fill="black"/>
<path d="M187 81H188V176H187V81Z" fill="black"/>
<path d="M176 81H177V176H176V81Z" fill="black"/>
<path d="M165 78H166V173H165V78Z" fill="black"/>
<path d="M99 78H100V173H99V78Z" fill="black"/>
<path d="M33 78H34V173H33V78Z" fill="black"/>
<path d="M220 76H221V171H220V76Z" fill="black"/>
<path d="M220 76H221V171H220V76Z" fill="black"/>
<path d="M232 89V90L-7 90V89L232 89Z" fill="black"/>
<path d="M232 100V101L-7 101V100L232 100Z" fill="black"/>
<path d="M232 111V112L-7 112V111L232 111Z" fill="black"/>
<path d="M232 122V123L-7 123V122L232 122Z" fill="black"/>
<path d="M232 133V134L-7 134V133L232 133Z" fill="black"/>
<path d="M231 144V145L-8 145V144L231 144Z" fill="black"/>
<path d="M231 155V156L-8 156V155L231 155Z" fill="black"/>
<path d="M154 78H155V173H154V78Z" fill="black"/>
<path d="M88 78H89V173H88V78Z" fill="black"/>
<path d="M22 78H23V173H22V78Z" fill="black"/>
<path d="M209 76H210V171H209V76Z" fill="black"/>
<path d="M143 78H144V173H143V78Z" fill="black"/>
<path d="M77 78H78V173H77V78Z" fill="black"/>
<path d="M11 78H12V173H11V78Z" fill="black"/>
<path d="M198 76H199V171H198V76Z" fill="black"/>
<path d="M132 78H133V173H132V78Z" fill="black"/>
<path d="M66 78H67V173H66V78Z" fill="black"/>
<path d="M187 76H188V171H187V76Z" fill="black"/>
<path d="M121 78H122V173H121V78Z" fill="black"/>
<path d="M55 78H56V173H55V78Z" fill="black"/>
<path d="M-11 78H-10V173H-11V78Z" fill="black"/>
<path d="M176 76H177V171H176V76Z" fill="black"/>
<path d="M110 78H111V173H110V78Z" fill="black"/>
<path d="M44 78H45V173H44V78Z" fill="black"/>
<path d="M-22 78H-21V173H-22V78Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_2500_1167">
<rect width="441" height="167" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -31,8 +31,8 @@
"ctrl-,": "zed::OpenSettings",
"ctrl-q": "zed::Quit",
"f4": "debugger::Start",
"f5": "debugger::Continue",
"shift-f5": "debugger::Stop",
"ctrl-shift-f5": "debugger::Restart",
"f6": "debugger::Pause",
"f7": "debugger::StepOver",
"cmd-f11": "debugger::StepInto",
@@ -120,12 +120,14 @@
"ctrl-'": "editor::ToggleSelectedDiffHunks",
"ctrl-\"": "editor::ExpandAllDiffHunks",
"ctrl-i": "editor::ShowSignatureHelp",
"alt-g b": "git::Blame",
"alt-g b": "editor::ToggleGitBlame",
"menu": "editor::OpenContextMenu",
"shift-f10": "editor::OpenContextMenu",
"ctrl-shift-e": "editor::ToggleEditPrediction",
"f9": "editor::ToggleBreakpoint",
"shift-f9": "editor::EditLogBreakpoint"
"shift-f9": "editor::EditLogBreakpoint",
"ctrl-shift-backspace": "editor::GoToPreviousChange",
"ctrl-shift-alt-backspace": "editor::GoToNextChange"
}
},
{
@@ -144,8 +146,6 @@
"ctrl->": "assistant::QuoteSelection",
"ctrl-<": "assistant::InsertIntoEditor",
"ctrl-alt-e": "editor::SelectEnclosingSymbol",
"ctrl-shift-backspace": "editor::GoToPreviousChange",
"ctrl-shift-alt-backspace": "editor::GoToNextChange",
"alt-enter": "editor::OpenSelectionsInMultibuffer"
}
},
@@ -153,7 +153,8 @@
"context": "Editor && mode == full && edit_prediction",
"bindings": {
"alt-]": "editor::NextEditPrediction",
"alt-[": "editor::PreviousEditPrediction"
"alt-[": "editor::PreviousEditPrediction",
"alt-right": "editor::AcceptPartialEditPrediction"
}
},
{
@@ -193,16 +194,6 @@
"alt-shift-y": "git::UnstageAndNext"
}
},
{
"context": "Editor && editor_agent_diff",
"bindings": {
"ctrl-y": "agent::Keep",
"ctrl-n": "agent::Reject",
"ctrl-shift-y": "agent::KeepAll",
"ctrl-shift-n": "agent::RejectAll",
"shift-ctrl-r": "agent::OpenAgentDiff"
}
},
{
"context": "AgentDiff",
"bindings": {
@@ -212,21 +203,35 @@
"ctrl-shift-n": "agent::RejectAll"
}
},
{
"context": "AssistantPanel",
"bindings": {
"ctrl-k c": "assistant::CopyCode",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-g": "search::SelectNextMatch",
"ctrl-shift-g": "search::SelectPreviousMatch",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"ctrl-k h": "assistant::DeployHistory",
"ctrl-k l": "assistant::OpenRulesLibrary",
"new": "assistant::NewChat",
"ctrl-t": "assistant::NewChat",
"ctrl-n": "assistant::NewChat"
}
},
{
"context": "ContextEditor > Editor",
"bindings": {
"ctrl-enter": "assistant::Assist",
"ctrl-shift-enter": "assistant::Edit",
"ctrl-s": "workspace::Save",
"save": "workspace::Save",
"ctrl->": "assistant::QuoteSelection",
"ctrl-<": "assistant::InsertIntoEditor",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"shift-enter": "assistant::Split",
"ctrl-r": "assistant::CycleMessageRole",
"enter": "assistant::ConfirmCommand",
"alt-enter": "editor::Newline",
"ctrl-k c": "assistant::CopyCode",
"ctrl-g": "search::SelectNextMatch",
"ctrl-shift-g": "search::SelectPreviousMatch",
"ctrl-k l": "agent::OpenRulesLibrary"
"alt-enter": "editor::Newline"
}
},
{
@@ -236,25 +241,13 @@
"ctrl-alt-n": "agent::NewTextThread",
"ctrl-shift-h": "agent::OpenHistory",
"ctrl-alt-c": "agent::OpenConfiguration",
"ctrl-alt-p": "agent::OpenRulesLibrary",
"ctrl-alt-p": "assistant::OpenRulesLibrary",
"ctrl-i": "agent::ToggleProfileSelector",
"ctrl-alt-/": "agent::ToggleModelSelector",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"ctrl-shift-a": "agent::ToggleContextPicker",
"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"
}
},
{
"context": "AgentPanel > NavigationMenu",
"bindings": {
"shift-backspace": "agent::DeleteRecentlyOpenThread"
"shift-escape": "agent::ExpandMessageEditor",
"ctrl-e": "agent::ChatMode",
"ctrl-alt-e": "agent::RemoveAllContext"
}
},
{
@@ -275,11 +268,8 @@
"context": "MessageEditor > Editor",
"bindings": {
"enter": "agent::Chat",
"ctrl-enter": "agent::ChatWithFollow",
"ctrl-i": "agent::ToggleProfileSelector",
"shift-ctrl-r": "agent::OpenAgentDiff",
"ctrl-shift-y": "agent::KeepAll",
"ctrl-shift-n": "agent::RejectAll"
"shift-ctrl-r": "agent::OpenAgentDiff"
}
},
{
@@ -511,14 +501,12 @@
{
"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-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-open": "projects::OpenRecent",
"alt-ctrl-o": "projects::OpenRecent",
"alt-shift-open": "projects::OpenRemote",
"alt-ctrl-shift-o": "projects::OpenRemote",
"alt-ctrl-shift-b": "branches::OpenRecent",
"alt-shift-enter": "toast::RunAction",
"ctrl-~": "workspace::NewTerminal",
@@ -532,7 +520,6 @@
"shift-new": "workspace::NewWindow",
"ctrl-shift-n": "workspace::NewWindow",
"ctrl-`": "terminal_panel::ToggleFocus",
"f10": ["app_menu::OpenApplicationMenu", "Zed"],
"alt-1": ["workspace::ActivatePane", 0],
"alt-2": ["workspace::ActivatePane", 1],
"alt-3": ["workspace::ActivatePane", 2],
@@ -563,8 +550,7 @@
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-shift-b": "outline_panel::ToggleFocus",
"ctrl-shift-g": "git_panel::ToggleFocus",
"ctrl-shift-d": "debug_panel::ToggleFocus",
"ctrl-?": "agent::ToggleFocus",
"ctrl-?": "assistant::ToggleFocus",
"alt-save": "workspace::SaveAll",
"ctrl-alt-s": "workspace::SaveAll",
"ctrl-k m": "language_selector::Toggle",
@@ -582,30 +568,16 @@
"ctrl-alt-r": "task::Rerun",
"alt-t": "task::Rerun",
"alt-shift-t": "task::Spawn",
"alt-shift-r": ["task::Spawn", { "reveal_target": "center" }],
"alt-shift-r": ["task::Spawn", { "reveal_target": "center" }]
// also possible to spawn tasks by name:
// "foo-bar": ["task::Spawn", { "task_name": "MyTask", "reveal_target": "dock" }]
// or by tag:
// "foo-bar": ["task::Spawn", { "task_tag": "MyTag" }],
"f5": "debugger::RerunLastSession"
}
},
{
"context": "Workspace && debugger_running",
"bindings": {
"f5": "zed::NoAction"
}
},
{
"context": "Workspace && debugger_stopped",
"bindings": {
"f5": "debugger::Continue"
}
},
{
"context": "ApplicationMenu",
"bindings": {
"f10": "menu::Cancel",
"left": "app_menu::ActivateMenuLeft",
"right": "app_menu::ActivateMenuRight"
}
@@ -614,6 +586,7 @@
{
"context": "Editor",
"bindings": {
"ctrl-shift-d": "editor::DuplicateLineDown",
"ctrl-shift-j": "editor::JoinLines",
"ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",
"ctrl-alt-h": "editor::DeleteToPreviousSubwordStart",
@@ -661,16 +634,14 @@
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"alt-l": "editor::AcceptEditPrediction",
"tab": "editor::AcceptEditPrediction",
"alt-right": "editor::AcceptPartialEditPrediction"
"tab": "editor::AcceptEditPrediction"
}
},
{
"context": "Editor && edit_prediction_conflict",
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"alt-l": "editor::AcceptEditPrediction",
"alt-right": "editor::AcceptPartialEditPrediction"
"alt-l": "editor::AcceptEditPrediction"
}
},
{
@@ -694,8 +665,7 @@
{
"bindings": {
"ctrl-alt-shift-f": "workspace::FollowNextCollaborator",
// Only available in debug builds: opens an element inspector for development.
"ctrl-alt-i": "dev::ToggleInspector"
"ctrl-alt-i": "zed::DebugElements"
}
},
{
@@ -720,8 +690,8 @@
{
"context": "PromptEditor",
"bindings": {
"ctrl-[": "agent::CyclePreviousInlineAssist",
"ctrl-]": "agent::CycleNextInlineAssist",
"ctrl-[": "assistant::CyclePreviousInlineAssist",
"ctrl-]": "assistant::CycleNextInlineAssist",
"ctrl-alt-e": "agent::RemoveAllContext"
}
},
@@ -789,7 +759,7 @@
"alt-ctrl-r": "project_panel::RevealInFileManager",
"ctrl-shift-enter": "project_panel::OpenWithSystem",
"shift-find": "project_panel::NewSearchInDirectory",
"ctrl-alt-shift-f": "project_panel::NewSearchInDirectory",
"ctrl-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrevious",
"escape": "menu::Cancel"
@@ -883,38 +853,11 @@
"alt-l": "git::GenerateCommitMessage"
}
},
{
"context": "DebugPanel",
"bindings": {
"ctrl-t": "debugger::ToggleThreadPicker",
"ctrl-i": "debugger::ToggleSessionPicker",
"shift-alt-escape": "debugger::ToggleExpandItem"
}
},
{
"context": "VariableList",
"bindings": {
"left": "variable_list::CollapseSelectedEntry",
"right": "variable_list::ExpandSelectedEntry",
"enter": "variable_list::EditVariable",
"ctrl-c": "variable_list::CopyVariableValue",
"ctrl-alt-c": "variable_list::CopyVariableName"
}
},
{
"context": "BreakpointList",
"bindings": {
"space": "debugger::ToggleEnableBreakpoint",
"backspace": "debugger::UnsetBreakpoint"
}
},
{
"context": "CollabPanel && not_editing",
"bindings": {
"ctrl-backspace": "collab_panel::Remove",
"space": "menu::Confirm",
"ctrl-up": "collab_panel::MoveChannelUp",
"ctrl-down": "collab_panel::MoveChannelDown"
"space": "menu::Confirm"
}
},
{
@@ -945,13 +888,6 @@
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "FileFinder",
"bindings": {
"ctrl-shift-a": "file_finder::ToggleSplitMenu",
"ctrl-shift-i": "file_finder::ToggleFilterMenu"
}
},
{
"context": "FileFinder || (FileFinder > Picker > Editor) || (FileFinder > Picker > menu)",
"bindings": {
@@ -985,14 +921,12 @@
"alt-b": ["terminal::SendText", "\u001bb"],
"alt-f": ["terminal::SendText", "\u001bf"],
"alt-.": ["terminal::SendText", "\u001b."],
"ctrl-delete": ["terminal::SendText", "\u001bd"],
// Overrides for conflicting keybindings
"ctrl-b": ["terminal::SendKeystroke", "ctrl-b"],
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
"ctrl-e": ["terminal::SendKeystroke", "ctrl-e"],
"ctrl-o": ["terminal::SendKeystroke", "ctrl-o"],
"ctrl-w": ["terminal::SendKeystroke", "ctrl-w"],
"ctrl-backspace": ["terminal::SendKeystroke", "ctrl-w"],
"ctrl-shift-a": "editor::SelectAll",
"find": "buffer_search::Deploy",
"ctrl-shift-f": "buffer_search::Deploy",
@@ -1010,10 +944,7 @@
"shift-down": "terminal::ScrollLineDown",
"shift-home": "terminal::ScrollToTop",
"shift-end": "terminal::ScrollToBottom",
"ctrl-shift-space": "terminal::ToggleViMode",
"ctrl-shift-r": "terminal::RerunTask",
"ctrl-alt-r": "terminal::RerunTask",
"alt-t": "terminal::RerunTask"
"ctrl-shift-space": "terminal::ToggleViMode"
}
},
{
@@ -1021,34 +952,5 @@
"bindings": {
"escape": "menu::Cancel"
}
},
{
"context": "ConfigureContextServerModal > Editor",
"bindings": {
"escape": "menu::Cancel",
"enter": "editor::Newline",
"ctrl-enter": "menu::Confirm"
}
},
{
"context": "Diagnostics",
"use_key_equivalents": true,
"bindings": {
"ctrl-r": "diagnostics::ToggleDiagnosticsRefresh"
}
},
{
"context": "DebugConsole > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "menu::Confirm"
}
},
{
"context": "RunModal",
"bindings": {
"ctrl-tab": "pane::ActivateNextItem",
"ctrl-shift-tab": "pane::ActivatePreviousItem"
}
}
]

View File

@@ -1,11 +1,22 @@
[
// Moved before Standard macOS bindings so that `cmd-w` is not the last binding for
// `workspace::CloseWindow` and displayed/intercepted by macOS
{
"context": "PromptLibrary",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "rules_library::NewRule",
"cmd-shift-s": "rules_library::ToggleDefaultRule",
"cmd-w": "workspace::CloseWindow"
}
},
// Standard macOS bindings
{
"use_key_equivalents": true,
"bindings": {
"f4": "debugger::Start",
"f5": "debugger::Continue",
"shift-f5": "debugger::Stop",
"shift-cmd-f5": "debugger::Restart",
"f6": "debugger::Pause",
"f7": "debugger::StepOver",
"f11": "debugger::StepInto",
@@ -138,7 +149,7 @@
"cmd-;": "editor::ToggleLineNumbers",
"cmd-'": "editor::ToggleSelectedDiffHunks",
"cmd-\"": "editor::ExpandAllDiffHunks",
"cmd-alt-g b": "git::Blame",
"cmd-alt-g b": "editor::ToggleGitBlame",
"cmd-i": "editor::ShowSignatureHelp",
"f9": "editor::ToggleBreakpoint",
"shift-f9": "editor::EditLogBreakpoint",
@@ -181,7 +192,8 @@
"use_key_equivalents": true,
"bindings": {
"alt-tab": "editor::NextEditPrediction",
"alt-shift-tab": "editor::PreviousEditPrediction"
"alt-shift-tab": "editor::PreviousEditPrediction",
"ctrl-cmd-right": "editor::AcceptPartialEditPrediction"
}
},
{
@@ -236,14 +248,18 @@
}
},
{
"context": "Editor && editor_agent_diff",
"context": "AssistantPanel",
"use_key_equivalents": true,
"bindings": {
"cmd-y": "agent::Keep",
"cmd-n": "agent::Reject",
"cmd-shift-y": "agent::KeepAll",
"cmd-shift-n": "agent::RejectAll",
"shift-ctrl-r": "agent::OpenAgentDiff"
"cmd-k c": "assistant::CopyCode",
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-g": "search::SelectNextMatch",
"cmd-shift-g": "search::SelectPreviousMatch",
"cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-k h": "assistant::DeployHistory",
"cmd-k l": "assistant::OpenRulesLibrary",
"cmd-t": "assistant::NewChat",
"cmd-n": "assistant::NewChat"
}
},
{
@@ -251,16 +267,15 @@
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "assistant::Assist",
"cmd-shift-enter": "assistant::Edit",
"cmd-s": "workspace::Save",
"cmd->": "assistant::QuoteSelection",
"cmd-<": "assistant::InsertIntoEditor",
"cmd-alt-/": "assistant::ToggleModelSelector",
"shift-enter": "assistant::Split",
"ctrl-r": "assistant::CycleMessageRole",
"enter": "assistant::ConfirmCommand",
"alt-enter": "editor::Newline",
"cmd-k c": "assistant::CopyCode",
"cmd-g": "search::SelectNextMatch",
"cmd-shift-g": "search::SelectPreviousMatch",
"cmd-k l": "agent::OpenRulesLibrary"
"alt-enter": "editor::Newline"
}
},
{
@@ -271,25 +286,13 @@
"cmd-alt-n": "agent::NewTextThread",
"cmd-shift-h": "agent::OpenHistory",
"cmd-alt-c": "agent::OpenConfiguration",
"cmd-alt-p": "agent::OpenRulesLibrary",
"cmd-alt-p": "assistant::OpenRulesLibrary",
"cmd-i": "agent::ToggleProfileSelector",
"cmd-alt-/": "agent::ToggleModelSelector",
"cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-shift-a": "agent::ToggleContextPicker",
"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-shift-enter": "agent::ContinueThread",
"alt-enter": "agent::ContinueWithBurnMode",
"cmd-alt-b": "agent::ToggleBurnMode"
}
},
{
"context": "AgentPanel > NavigationMenu",
"bindings": {
"shift-backspace": "agent::DeleteRecentlyOpenThread"
"shift-escape": "agent::ExpandMessageEditor",
"cmd-e": "agent::ChatMode",
"cmd-alt-e": "agent::RemoveAllContext"
}
},
{
@@ -312,11 +315,8 @@
"use_key_equivalents": true,
"bindings": {
"enter": "agent::Chat",
"cmd-enter": "agent::ChatWithFollow",
"cmd-i": "agent::ToggleProfileSelector",
"shift-ctrl-r": "agent::OpenAgentDiff",
"cmd-shift-y": "agent::KeepAll",
"cmd-shift-n": "agent::RejectAll"
"shift-ctrl-r": "agent::OpenAgentDiff"
}
},
{
@@ -362,18 +362,15 @@
}
},
{
"context": "ThreadHistory > Editor",
"context": "ThreadHistory",
"bindings": {
"shift-backspace": "agent::RemoveSelectedThread"
"ctrl--": "pane::GoBack"
}
},
{
"context": "PromptLibrary",
"use_key_equivalents": true,
"context": "ThreadHistory > Editor",
"bindings": {
"cmd-n": "rules_library::NewRule",
"cmd-shift-s": "rules_library::ToggleDefaultRule",
"cmd-w": "workspace::CloseWindow"
"shift-backspace": "agent::RemoveSelectedThread"
}
},
{
@@ -545,7 +542,9 @@
"cmd-\\": "pane::SplitRight",
"cmd-k v": "markdown::OpenPreviewToTheSide",
"cmd-shift-v": "markdown::OpenPreview",
"ctrl-cmd-c": "editor::DisplayCursorNames"
"ctrl-cmd-c": "editor::DisplayCursorNames",
"cmd-shift-backspace": "editor::GoToPreviousChange",
"cmd-shift-alt-backspace": "editor::GoToNextChange"
}
},
{
@@ -553,9 +552,7 @@
"use_key_equivalents": true,
"bindings": {
"cmd-shift-o": "outline::Toggle",
"ctrl-g": "go_to_line::Toggle",
"cmd-shift-backspace": "editor::GoToPreviousChange",
"cmd-shift-alt-backspace": "editor::GoToNextChange"
"ctrl-g": "go_to_line::Toggle"
}
},
{
@@ -583,9 +580,8 @@
"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", { "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 }],
"alt-cmd-o": "projects::OpenRecent",
"ctrl-cmd-o": "projects::OpenRemote",
"alt-cmd-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal",
"cmd-s": "workspace::Save",
@@ -621,8 +617,7 @@
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-shift-b": "outline_panel::ToggleFocus",
"ctrl-shift-g": "git_panel::ToggleFocus",
"cmd-shift-d": "debug_panel::ToggleFocus",
"cmd-?": "agent::ToggleFocus",
"cmd-?": "assistant::ToggleFocus",
"cmd-alt-s": "workspace::SaveAll",
"cmd-k m": "language_selector::Toggle",
"escape": "workspace::Unfollow",
@@ -634,8 +629,7 @@
"cmd-k shift-right": "workspace::SwapPaneRight",
"cmd-k shift-up": "workspace::SwapPaneUp",
"cmd-k shift-down": "workspace::SwapPaneDown",
"cmd-shift-x": "zed::Extensions",
"f5": "debugger::RerunLastSession"
"cmd-shift-x": "zed::Extensions"
}
},
{
@@ -652,20 +646,6 @@
// "foo-bar": ["task::Spawn", { "task_tag": "MyTag" }],
}
},
{
"context": "Workspace && debugger_running",
"use_key_equivalents": true,
"bindings": {
"f5": "zed::NoAction"
}
},
{
"context": "Workspace && debugger_stopped",
"use_key_equivalents": true,
"bindings": {
"f5": "debugger::Continue"
}
},
// Bindings from Sublime Text
{
"context": "Editor",
@@ -718,16 +698,14 @@
"context": "Editor && edit_prediction",
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"tab": "editor::AcceptEditPrediction",
"ctrl-cmd-right": "editor::AcceptPartialEditPrediction"
"tab": "editor::AcceptEditPrediction"
}
},
{
"context": "Editor && edit_prediction_conflict",
"use_key_equivalents": true,
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"ctrl-cmd-right": "editor::AcceptPartialEditPrediction"
"alt-tab": "editor::AcceptEditPrediction"
}
},
{
@@ -756,8 +734,7 @@
"ctrl-alt-cmd-f": "workspace::FollowNextCollaborator",
// TODO: Move this to a dock open action
"cmd-shift-c": "collab_panel::ToggleFocus",
// Only available in debug builds: opens an element inspector for development.
"cmd-alt-i": "dev::ToggleInspector"
"cmd-alt-i": "zed::DebugElements"
}
},
{
@@ -779,10 +756,10 @@
"use_key_equivalents": true,
"bindings": {
"cmd-shift-a": "agent::ToggleContextPicker",
"cmd-alt-/": "agent::ToggleModelSelector",
"cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-alt-e": "agent::RemoveAllContext",
"ctrl-[": "agent::CyclePreviousInlineAssist",
"ctrl-]": "agent::CycleNextInlineAssist"
"ctrl-[": "assistant::CyclePreviousInlineAssist",
"ctrl-]": "assistant::CycleNextInlineAssist"
}
},
{
@@ -842,7 +819,7 @@
"alt-cmd-r": "project_panel::RevealInFileManager",
"ctrl-shift-enter": "project_panel::OpenWithSystem",
"cmd-alt-backspace": ["project_panel::Delete", { "skip_prompt": false }],
"cmd-alt-shift-f": "project_panel::NewSearchInDirectory",
"cmd-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrevious",
"escape": "menu::Cancel"
@@ -860,10 +837,7 @@
"use_key_equivalents": true,
"bindings": {
"left": "variable_list::CollapseSelectedEntry",
"right": "variable_list::ExpandSelectedEntry",
"enter": "variable_list::EditVariable",
"cmd-c": "variable_list::CopyVariableValue",
"cmd-alt-c": "variable_list::CopyVariableName"
"right": "variable_list::ExpandSelectedEntry"
}
},
{
@@ -948,29 +922,12 @@
"alt-tab": "git::GenerateCommitMessage"
}
},
{
"context": "DebugPanel",
"bindings": {
"cmd-t": "debugger::ToggleThreadPicker",
"cmd-i": "debugger::ToggleSessionPicker",
"shift-alt-escape": "debugger::ToggleExpandItem"
}
},
{
"context": "BreakpointList",
"bindings": {
"space": "debugger::ToggleEnableBreakpoint",
"backspace": "debugger::UnsetBreakpoint"
}
},
{
"context": "CollabPanel && not_editing",
"use_key_equivalents": true,
"bindings": {
"ctrl-backspace": "collab_panel::Remove",
"space": "menu::Confirm",
"cmd-up": "collab_panel::MoveChannelUp",
"cmd-down": "collab_panel::MoveChannelDown"
"space": "menu::Confirm"
}
},
{
@@ -1006,14 +963,6 @@
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "FileFinder",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-a": "file_finder::ToggleSplitMenu",
"cmd-shift-i": "file_finder::ToggleFilterMenu"
}
},
{
"context": "FileFinder || (FileFinder > Picker > Editor) || (FileFinder > Picker > menu)",
"use_key_equivalents": true,
@@ -1056,7 +1005,7 @@
"alt-right": ["terminal::SendText", "\u001bf"],
"alt-b": ["terminal::SendText", "\u001bb"],
"alt-f": ["terminal::SendText", "\u001bf"],
"ctrl-delete": ["terminal::SendText", "\u001bd"],
"alt-.": ["terminal::SendText", "\u001b."],
// There are conflicting bindings for these keys in the global context.
// these bindings override them, remove at your own risk:
"up": ["terminal::SendKeystroke", "up"],
@@ -1066,7 +1015,6 @@
"escape": ["terminal::SendKeystroke", "escape"],
"enter": ["terminal::SendKeystroke", "enter"],
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
"ctrl-backspace": ["terminal::SendKeystroke", "ctrl-w"],
"shift-pageup": "terminal::ScrollPageUp",
"cmd-up": "terminal::ScrollPageUp",
"shift-pagedown": "terminal::ScrollPageDown",
@@ -1083,8 +1031,7 @@
"ctrl-alt-up": "pane::SplitUp",
"ctrl-alt-down": "pane::SplitDown",
"ctrl-alt-left": "pane::SplitLeft",
"ctrl-alt-right": "pane::SplitRight",
"cmd-alt-r": "terminal::RerunTask"
"ctrl-alt-right": "pane::SplitRight"
}
},
{
@@ -1113,36 +1060,5 @@
"bindings": {
"escape": "menu::Cancel"
}
},
{
"context": "ConfigureContextServerModal > Editor",
"use_key_equivalents": true,
"bindings": {
"escape": "menu::Cancel",
"enter": "editor::Newline",
"cmd-enter": "menu::Confirm"
}
},
{
"context": "Diagnostics",
"use_key_equivalents": true,
"bindings": {
"ctrl-r": "diagnostics::ToggleDiagnosticsRefresh"
}
},
{
"context": "DebugConsole > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "menu::Confirm"
}
},
{
"context": "RunModal",
"use_key_equivalents": true,
"bindings": {
"ctrl-tab": "pane::ActivateNextItem",
"ctrl-shift-tab": "pane::ActivatePreviousItem"
}
}
]

View File

@@ -13,9 +13,9 @@
}
},
{
"context": "Editor && vim_mode == insert && !menu",
"context": "Editor",
"bindings": {
// "j k": "vim::SwitchToNormalMode"
// "j k": ["workspace::SendKeystrokes", "escape"]
}
}
]

View File

@@ -1,85 +0,0 @@
[
// Cursor for MacOS. See: https://docs.cursor.com/kbd
{
"context": "Workspace",
"use_key_equivalents": true,
"bindings": {
"ctrl-i": "agent::ToggleFocus",
"ctrl-shift-i": "agent::ToggleFocus",
"ctrl-l": "agent::ToggleFocus",
"ctrl-shift-l": "agent::ToggleFocus",
"ctrl-alt-b": "agent::ToggleFocus",
"ctrl-shift-j": "agent::OpenConfiguration"
}
},
{
"context": "Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"ctrl-i": "agent::ToggleFocus",
"ctrl-shift-i": "agent::ToggleFocus",
"ctrl-shift-l": "assistant::QuoteSelection", // In cursor uses "Ask" mode
"ctrl-l": "assistant::QuoteSelection", // In cursor uses "Agent" mode
"ctrl-k": "assistant::InlineAssist",
"ctrl-shift-k": "assistant::InsertIntoEditor"
}
},
{
"context": "InlineAssistEditor",
"use_key_equivalents": true,
"bindings": {
"ctrl-shift-backspace": "editor::Cancel"
// "alt-enter": // Quick Question
// "ctrl-shift-enter": // Full File Context
// "ctrl-shift-k": // Toggle input focus (editor <> inline assist)
}
},
{
"context": "AgentPanel || ContextEditor || (MessageEditor > Editor)",
"use_key_equivalents": true,
"bindings": {
"ctrl-i": "workspace::ToggleRightDock",
"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",
"ctrl-shift-backspace": "editor::Cancel",
"ctrl-r": "agent::NewThread",
"ctrl-shift-v": "editor::Paste",
"ctrl-shift-k": "assistant::InsertIntoEditor"
// "escape": "agent::ToggleFocus"
///// Enable when Zed supports multiple thread tabs
// "ctrl-t": // new thread tab
// "ctrl-[": // next thread tab
// "ctrl-]": // next thread tab
///// Enable if Zed adds support for keyboard navigation of thread elements
// "tab": // cycle to next message
// "shift-tab": // cycle to previous message
}
},
{
"context": "Editor && editor_agent_diff",
"use_key_equivalents": true,
"bindings": {
"ctrl-enter": "agent::KeepAll",
"ctrl-backspace": "agent::RejectAll"
}
},
{
"context": "Editor && mode == full && edit_prediction",
"use_key_equivalents": true,
"bindings": {
"ctrl-right": "editor::AcceptPartialEditPrediction"
}
},
{
"context": "Terminal",
"use_key_equivalents": true,
"bindings": {
"ctrl-k": "assistant::InlineAssist"
}
}
]

View File

@@ -72,9 +72,7 @@
"alt-left": "editor::SelectToPreviousWordStart",
"alt-right": "editor::SelectToNextWordEnd",
"pagedown": "editor::SelectPageDown",
"ctrl-v": "editor::SelectPageDown",
"pageup": "editor::SelectPageUp",
"alt-v": "editor::SelectPageUp",
"ctrl-f": "editor::SelectRight",
"ctrl-b": "editor::SelectLeft",
"ctrl-n": "editor::SelectDown",

View File

@@ -52,10 +52,8 @@
"shift-alt-m": "markdown::OpenPreviewToTheSide",
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
"ctrl-delete": "editor::DeleteToNextWordEnd",
"alt-right": "editor::MoveToNextSubwordEnd",
"alt-left": "editor::MoveToPreviousSubwordStart",
"alt-shift-right": "editor::SelectToNextSubwordEnd",
"alt-shift-left": "editor::SelectToPreviousSubwordStart"
"f3": "editor::FindNextMatch",
"shift-f3": "editor::FindPreviousMatch"
}
},
{

View File

@@ -1,85 +0,0 @@
[
// Cursor for MacOS. See: https://docs.cursor.com/kbd
{
"context": "Workspace",
"use_key_equivalents": true,
"bindings": {
"cmd-i": "agent::ToggleFocus",
"cmd-shift-i": "agent::ToggleFocus",
"cmd-l": "agent::ToggleFocus",
"cmd-shift-l": "agent::ToggleFocus",
"cmd-alt-b": "agent::ToggleFocus",
"cmd-shift-j": "agent::OpenConfiguration"
}
},
{
"context": "Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"cmd-i": "agent::ToggleFocus",
"cmd-shift-i": "agent::ToggleFocus",
"cmd-shift-l": "assistant::QuoteSelection", // In cursor uses "Ask" mode
"cmd-l": "assistant::QuoteSelection", // In cursor uses "Agent" mode
"cmd-k": "assistant::InlineAssist",
"cmd-shift-k": "assistant::InsertIntoEditor"
}
},
{
"context": "InlineAssistEditor",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-backspace": "editor::Cancel"
// "alt-enter": // Quick Question
// "cmd-shift-enter": // Full File Context
// "cmd-shift-k": // Toggle input focus (editor <> inline assist)
}
},
{
"context": "AgentPanel || ContextEditor || (MessageEditor > Editor)",
"use_key_equivalents": true,
"bindings": {
"cmd-i": "workspace::ToggleRightDock",
"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",
"cmd-shift-backspace": "editor::Cancel",
"cmd-r": "agent::NewThread",
"cmd-shift-v": "editor::Paste",
"cmd-shift-k": "assistant::InsertIntoEditor"
// "escape": "agent::ToggleFocus"
///// Enable when Zed supports multiple thread tabs
// "cmd-t": // new thread tab
// "cmd-[": // next thread tab
// "cmd-]": // next thread tab
///// Enable if Zed adds support for keyboard navigation of thread elements
// "tab": // cycle to next message
// "shift-tab": // cycle to previous message
}
},
{
"context": "Editor && editor_agent_diff",
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "agent::KeepAll",
"cmd-backspace": "agent::RejectAll"
}
},
{
"context": "Editor && mode == full && edit_prediction",
"use_key_equivalents": true,
"bindings": {
"cmd-right": "editor::AcceptPartialEditPrediction"
}
},
{
"context": "Terminal",
"use_key_equivalents": true,
"bindings": {
"cmd-k": "assistant::InlineAssist"
}
}
]

View File

@@ -72,9 +72,7 @@
"alt-left": "editor::SelectToPreviousWordStart",
"alt-right": "editor::SelectToNextWordEnd",
"pagedown": "editor::SelectPageDown",
"ctrl-v": "editor::SelectPageDown",
"pageup": "editor::SelectPageUp",
"alt-v": "editor::SelectPageUp",
"ctrl-f": "editor::SelectRight",
"ctrl-b": "editor::SelectLeft",
"ctrl-n": "editor::SelectDown",

View File

@@ -54,10 +54,8 @@
"shift-alt-m": "markdown::OpenPreviewToTheSide",
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
"ctrl-delete": "editor::DeleteToNextWordEnd",
"ctrl-right": "editor::MoveToNextSubwordEnd",
"ctrl-left": "editor::MoveToPreviousSubwordStart",
"ctrl-shift-right": "editor::SelectToNextSubwordEnd",
"ctrl-shift-left": "editor::SelectToPreviousSubwordStart"
"cmd-g": "editor::FindNextMatch",
"cmd-shift-g": "editor::FindPreviousMatch"
}
},
{

View File

@@ -50,12 +50,6 @@
"] -": "vim::NextLesserIndent",
"] +": "vim::NextGreaterIndent",
"] =": "vim::NextSameIndent",
"] b": "pane::ActivateNextItem",
"[ b": "pane::ActivatePreviousItem",
"] shift-b": "pane::ActivateLastItem",
"[ shift-b": ["pane::ActivateItem", 0],
"] space": "vim::InsertEmptyLineBelow",
"[ space": "vim::InsertEmptyLineAbove",
// Word motions
"w": "vim::NextWordStart",
"e": "vim::NextWordEnd",
@@ -114,11 +108,7 @@
"ctrl-e": "vim::LineDown",
"ctrl-y": "vim::LineUp",
// "g" commands
"g shift-r": "vim::PushReplaceWithRegister",
"g r n": "editor::Rename",
"g r r": "editor::FindAllReferences",
"g r i": "editor::GoToImplementation",
"g r a": "editor::ToggleCodeActions",
"g r": "vim::PushReplaceWithRegister",
"g g": "vim::StartOfDocument",
"g h": "editor::Hover",
"g t": "pane::ActivateNextItem",
@@ -137,7 +127,6 @@
"g <": ["editor::SelectPrevious", { "replace_newest": true }],
"g a": "editor::SelectAllMatches",
"g s": "outline::Toggle",
"g shift-o": "outline::Toggle",
"g shift-s": "project_symbols::Toggle",
"g .": "editor::ToggleCodeActions", // zed specific
"g shift-a": "editor::FindAllReferences", // zed specific
@@ -152,7 +141,6 @@
"g end": ["vim::EndOfLine", { "display_lines": true }],
"g 0": ["vim::StartOfLine", { "display_lines": true }],
"g home": ["vim::StartOfLine", { "display_lines": true }],
"g shift-m": ["vim::MiddleOfLine", { "display_lines": true }],
"g ^": ["vim::FirstNonWhitespace", { "display_lines": true }],
"g v": "vim::RestoreVisualSelection",
"g ]": "editor::GoToDiagnostic",
@@ -198,8 +186,6 @@
"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",
@@ -319,7 +305,7 @@
"!": "vim::ShellCommand",
"i": ["vim::PushObject", { "around": false }],
"a": ["vim::PushObject", { "around": true }],
"g shift-r": ["vim::Paste", { "preserve_clipboard": true }],
"g r": ["vim::Paste", { "preserve_clipboard": true }],
"g c": "vim::ToggleComments",
"g q": "vim::Rewrap",
"g ?": "vim::ConvertToRot13",
@@ -353,8 +339,7 @@
"ctrl-shift-q": ["vim::PushLiteral", {}],
"ctrl-r": "vim::PushRegister",
"insert": "vim::ToggleReplace",
"ctrl-o": "vim::TemporaryNormal",
"ctrl-s": "editor::ShowSignatureHelp"
"ctrl-o": "vim::TemporaryNormal"
}
},
{
@@ -519,14 +504,12 @@
"'": "vim::Quotes",
"`": "vim::BackQuotes",
"\"": "vim::DoubleQuotes",
// "q": "vim::AnyQuotes",
"q": "vim::MiniQuotes",
"q": "vim::AnyQuotes",
"|": "vim::VerticalBars",
"(": "vim::Parentheses",
")": "vim::Parentheses",
"b": "vim::Parentheses",
// "b": "vim::AnyBrackets",
// "b": "vim::MiniBrackets",
"[": "vim::SquareBrackets",
"]": "vim::SquareBrackets",
"r": "vim::SquareBrackets",
@@ -647,10 +630,9 @@
}
},
{
"context": "vim_operator == gR",
"context": "vim_operator == gr",
"bindings": {
"r": "vim::CurrentLine",
"shift-r": "vim::CurrentLine"
"r": "vim::CurrentLine"
}
},
{
@@ -711,7 +693,7 @@
}
},
{
"context": "AgentPanel || GitPanel || ProjectPanel || CollabPanel || OutlinePanel || ChatPanel || VimControl || EmptyPane || SharedScreen || MarkdownPreview || KeyContextView || DebugPanel",
"context": "GitPanel || ProjectPanel || CollabPanel || OutlinePanel || ChatPanel || VimControl || EmptyPane || SharedScreen || MarkdownPreview || KeyContextView",
"bindings": {
// window related commands (ctrl-w X)
"ctrl-w": null,
@@ -840,19 +822,6 @@
"tab": "editor::AcceptEditPrediction"
}
},
{
"context": "MessageEditor > Editor && VimControl",
"bindings": {
"enter": "agent::Chat",
// TODO: Implement search
"/": null,
"?": null,
"#": null,
"*": null,
"n": null,
"shift-n": null
}
},
{
"context": "os != macos && Editor && edit_prediction_conflict",
"bindings": {
@@ -861,5 +830,13 @@
// and Windows.
"alt-l": "editor::AcceptEditPrediction"
}
},
{
// Fixes https://github.com/zed-industries/zed/issues/29095 by ensuring that
// the last binding for editor::ToggleComments is not ctrl-c.
"context": "hack_to_fix_ctrl-c",
"bindings": {
"g c": "editor::ToggleComments"
}
}
]

View File

@@ -3,12 +3,11 @@ You are a highly skilled software engineer with extensive knowledge in many prog
## Communication
1. Be conversational but professional.
2. Refer to the user in the second person and yourself in the first person.
2. Refer to the USER in the second person and yourself in the first person.
3. Format your responses in markdown. Use backticks to format file, directory, function, and class names.
4. NEVER lie or make things up.
5. Refrain from apologizing all the time when results are unexpected. Instead, just try your best to proceed or explain the circumstances to the user without apologizing.
{{#if has_tools}}
## Tool Use
1. Make sure to adhere to the tools schema.
@@ -16,8 +15,6 @@ You are a highly skilled software engineer with extensive knowledge in many prog
3. DO NOT use tools to access items that are already available in the context section.
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
@@ -38,90 +35,21 @@ If appropriate, use tool calls to explore the current project, which contains th
- 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}}
{{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).
As such, if you need the user to perform any actions for you, you must request them explicitly. Bias towards giving a response to the best of your ability, and then making requests for the user to take action (e.g. to give you more context) only optionally.
The one exception to this is if the user references something you don't know about - for example, the name of a source code file, function, type, or other piece of code that you have no awareness of. In this case, you MUST NOT MAKE SOMETHING UP, or assume you know what that thing is or how it works. Instead, you must ask the user for clarification rather than giving a response.
{{/if}}
## Code Block Formatting
Whenever you mention a code block, you MUST use ONLY use the following format:
Whenever you mention a code block, you MUST use ONLY use the following format when the code in the block comes from a file
in the project:
```path/to/Something.blah#L123-456
(code goes here)
```
The `#L123-456` means the line number range 123 through 456, and the path/to/Something.blah
is a path in the project. (If there is no valid path in the project, then you can use
/dev/null/path.extension for its path.) This is the ONLY valid way to format code blocks, because the Markdown parser
does not understand the more common ```language syntax, or bare ``` blocks. It only
understands this path-based syntax, and if the path is missing, then it will error and you will have to do it over again.
Just to be really clear about this, if you ever find yourself writing three backticks followed by a language name, STOP!
You have made a mistake. You can only ever put paths after triple backticks!
<example>
Based on all the information I've gathered, here's a summary of how this system works:
1. The README file is loaded into the system.
2. The system finds the first two headers, including everything in between. In this case, that would be:
```path/to/README.md#L8-12
# First Header
This is the info under the first header.
## Sub-header
```
3. Then the system finds the last header in the README:
```path/to/README.md#L27-29
## Last Header
This is the last header in the README.
```
4. Finally, it passes this information on to the next process.
</example>
<example>
In Markdown, hash marks signify headings. For example:
```/dev/null/example.md#L1-3
# Level 1 heading
## Level 2 heading
### Level 3 heading
```
</example>
Here are examples of ways you must never render code blocks:
<bad_example_do_not_do_this>
In Markdown, hash marks signify headings. For example:
```
# Level 1 heading
## Level 2 heading
### Level 3 heading
```
</bad_example_do_not_do_this>
This example is unacceptable because it does not include the path.
<bad_example_do_not_do_this>
In Markdown, hash marks signify headings. For example:
```markdown
# Level 1 heading
## Level 2 heading
### Level 3 heading
```
</bad_example_do_not_do_this>
This example is unacceptable because it has the language instead of the path.
<bad_example_do_not_do_this>
In Markdown, hash marks signify headings. For example:
# Level 1 heading
## Level 2 heading
### Level 3 heading
</bad_example_do_not_do_this>
This example is unacceptable because it uses indentation to mark the code block
instead of backticks with a path.
<bad_example_do_not_do_this>
In Markdown, hash marks signify headings. For example:
```markdown
/dev/null/example.md#L1-3
# Level 1 heading
## Level 2 heading
### Level 3 heading
```
</bad_example_do_not_do_this>
This example is unacceptable because the path is in the wrong place. The path must be directly after the opening backticks.
{{#if has_tools}}
The `#L123-456` means the line number range 123 through 456, and the path/to/Something.blah
is a path in the project. (If this code block does not come from a file in the project, then you may instead use
the normal markdown style of three backticks followed by language name. However, you MUST use this format if
the code in the block comes from a file in the project.)
## Fixing Diagnostics
1. Make 1-2 attempts at fixing diagnostics, then defer to the user.
@@ -135,11 +63,10 @@ Otherwise, follow debugging best practices:
2. Add descriptive logging statements and error messages to track variable and code state.
3. Add test functions and statements to isolate the problem.
{{/if}}
## Calling External APIs
1. Unless explicitly requested by the user, use the best suited external APIs and packages to solve the task. There is no need to ask the user for permission.
2. When selecting which version of an API or package to use, choose one that is compatible with the user's dependency management file(s). If no such file exists or if the package is not present, use the latest version that is in your training data.
2. When selecting which version of an API or package to use, choose one that is compatible with the user's dependency management file. If no such file exists or if the package is not present, use the latest version that is in your training data.
3. If an external API requires an API Key, be sure to point this out to the user. Adhere to best security practices (e.g. DO NOT hardcode an API key in a place where it can be exposed)
## System Information
@@ -147,10 +74,10 @@ Otherwise, follow debugging best practices:
Operating System: {{os}}
Default Shell: {{shell}}
{{#if (or has_rules has_user_rules)}}
{{#if (or has_rules has_default_user_rules)}}
## User's Custom Instructions
The following additional instructions are provided by the user, and should be followed to the best of your ability{{#if has_tools}} without interfering with the tool use guidelines{{/if}}.
The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the tool use guidelines.
{{#if has_rules}}
There are project rules that apply to these root directories:

View File

@@ -49,9 +49,10 @@ And here's the section to rewrite based on that prompt again for reference:
</rewrite_this>
{{#if diagnostic_errors}}
{{#each diagnostic_errors}}
Below are the diagnostic errors visible to the user. If the user requests problems to be fixed, use this information, but do not try to fix these errors if the user hasn't asked you to.
{{#each diagnostic_errors}}
<diagnostic_error>
<line_number>{{line_number}}</line_number>
<error_message>{{error_message}}</error_message>

View File

@@ -0,0 +1,206 @@
<task_description>
The user of a code editor wants to make a change to their codebase.
You must describe the change using the following XML structure:
- <patch> - A group of related code changes.
Child tags:
- <title> (required) - A high-level description of the changes. This should be as short
as possible, possibly using common abbreviations.
- <edit> (1 or more) - An edit to make at a particular range within a file.
Includes the following child tags:
- <path> (required) - The path to the file that will be changed.
- <description> (optional) - An arbitrarily-long comment that describes the purpose
of this edit.
- <old_text> (optional) - An excerpt from the file's current contents that uniquely
identifies a range within the file where the edit should occur. Required for all operations
except `create`.
- <new_text> (required) - The new text to insert into the file.
- <operation> (required) - The type of change that should occur at the given range
of the file. Must be one of the following values:
- `update`: Replaces the entire range with the new text.
- `insert_before`: Inserts the new text before the range.
- `insert_after`: Inserts new text after the range.
- `create`: Creates or overwrites a file with the given path and the new text.
- `delete`: Deletes the specified range from the file.
<guidelines>
- Never provide multiple edits whose ranges intersect each other. Instead, merge them into one edit.
- Prefer multiple edits to smaller, disjoint ranges, rather than one edit to a larger range.
- There's no need to escape angle brackets within XML tags.
- Always ensure imports are added if you're referencing symbols that are not in scope.
</guidelines>
Here are some concrete examples.
<example>
<message role="user">
```rs src/shapes.rs
pub mod rectangle;
pub mod circle;
```
```rs src/shapes/rectangle.rs
pub struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
pub fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
}
```
```rs src/shapes/circle.rs
pub struct Circle {
radius: f64,
}
impl Circle {
pub fn new(radius: f64) -> Self {
Circle { radius }
}
}
```
Update all shapes to store their origin as an (x, y) tuple and implement Display.
</message>
<message role="assistant">
We'll need to update both the rectangle and circle modules.
<patch>
<title>Add origins and display impls to shapes</title>
<edit>
<path>src/shapes/rectangle.rs</path>
<description>Add the origin field to Rectangle struct</description>
<operation>insert_after</operation>
<old_text>
pub struct Rectangle {
</old_text>
<new_text>
origin: (f64, f64),
</new_text>
</edit>
<edit>
<path>src/shapes/rectangle.rs</path>
<operation>update</operation>
<old_text>
fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
</old_text>
<new_text>
fn new(origin: (f64, f64), width: f64, height: f64) -> Self {
Rectangle { origin, width, height }
}
</new_text>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<description>Add the origin field to Circle struct</description>
<operation>insert_after</operation>
<old_text>
pub struct Circle {
radius: f64,
</old_text>
<new_text>
origin: (f64, f64),
</new_text>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<operation>update</operation>
<old_text>
fn new(radius: f64) -> Self {
Circle { radius }
}
</old_text>
<new_text>
fn new(origin: (f64, f64), radius: f64) -> Self {
Circle { origin, radius }
}
</new_text>
</edit>
</step>
<edit>
<path>src/shapes/rectangle.rs</path>
<operation>insert_before</operation>
<old_text>
struct Rectangle {
</old_text>
<new_text>
use std::fmt;
</new_text>
</edit>
<edit>
<path>src/shapes/rectangle.rs</path>
<description>
Add a manual Display implementation for Rectangle.
Currently, this is the same as a derived Display implementation.
</description>
<operation>insert_after</operation>
<old_text>
Rectangle { width, height }
}
}
</old_text>
<new_text>
impl fmt::Display for Rectangle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.format_struct(f, "Rectangle")
.field("origin", &self.origin)
.field("width", &self.width)
.field("height", &self.height)
.finish()
}
}
</new_text>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<operation>insert_before</operation>
<old_text>
struct Circle {
</old_text>
<new_text>
use std::fmt;
</new_text>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<operation>insert_after</operation>
<old_text>
Circle { radius }
}
}
</old_text>
<new_text>
impl fmt::Display for Rectangle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.format_struct(f, "Rectangle")
.field("origin", &self.origin)
.field("width", &self.width)
.field("height", &self.height)
.finish()
}
}
</new_text>
</edit>
</patch>
</message>
</example>
</task_description>

View File

@@ -67,12 +67,13 @@
"ui_font_weight": 400,
// The default font size for text in the UI
"ui_font_size": 16,
// The default font size for text in the agent panel
"agent_font_size": 16,
// How much to fade out unused code.
"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.
@@ -110,8 +111,8 @@
// Whether to show the informational hover box when moving the mouse
// over symbols in the editor.
"hover_popover_enabled": true,
// Time to wait in milliseconds before showing the informational hover box.
"hover_popover_delay": 300,
// Time to wait before showing the informational hover box
"hover_popover_delay": 350,
// Whether to confirm before quitting Zed.
"confirm_quit": false,
// Whether to restore last closed project when fresh Zed instance is opened.
@@ -125,8 +126,6 @@
//
// Default: true
"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.
"drop_target_size": 0.2,
// Whether the window should be closed when using 'close active item' on a window with no tabs.
@@ -212,30 +211,11 @@
// Whether to show the signature help after completion or a bracket pair inserted.
// If `auto_signature_help` is enabled, this setting will be treated as enabled also.
"show_signature_help_after_edits": false,
// Whether to show code action button at start of buffer line.
"inline_code_actions": true,
// What to do when go to definition yields no results.
//
// 1. Do nothing: `none`
// 2. Find references for the same symbol: `find_all_references` (default)
"go_to_definition_fallback": "find_all_references",
// Which level to use to filter out diagnostics displayed in the editor.
//
// Affects the editor rendering only, and does not interrupt
// the functionality of diagnostics fetching and project diagnostics editor.
// Which files containing diagnostic errors/warnings to mark in the tabs.
// Diagnostics are only shown when file icons are also active.
// This setting only works when can take the following three values:
//
// Which diagnostic indicators to show in the scrollbar, their level should be more or equal to the specified severity level.
// Possible values:
// - "off" — no diagnostics are allowed
// - "error"
// - "warning"
// - "info"
// - "hint"
// - null — allow all diagnostics (default)
"diagnostics_max_severity": null,
// Whether to show wrap guides (vertical rulers) in the editor.
// Setting this to true will show a guide at the 'preferred_line_length' value
// if 'soft_wrap' is set to 'preferred_line_length', and will show any
@@ -246,7 +226,7 @@
// Hide the values of in variables from visual display in private files
"redact_private_values": false,
// The default number of lines to expand excerpts in the multibuffer by.
"expand_excerpt_lines": 5,
"expand_excerpt_lines": 3,
// Globs to match against file paths to determine if a file is private.
"private_files": ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/secrets.yml"],
// Whether to use additional LSP queries to format (and amend) the code after
@@ -320,27 +300,8 @@
"breadcrumbs": true,
// Whether to show quick action buttons.
"quick_actions": true,
// Whether to show the Selections menu in the editor toolbar.
"selections_menu": true,
// Whether to show agent review buttons in the editor toolbar.
"agent_review": true,
// Whether to show code action buttons in the editor toolbar.
"code_actions": false
},
// Titlebar related settings
"title_bar": {
// Whether to show the branch icon beside branch switcher in the titlebar.
"show_branch_icon": false,
// Whether to show the branch name button in the titlebar.
"show_branch_name": true,
// Whether to show the project host and name in the titlebar.
"show_project_items": true,
// Whether to show onboarding banners in the titlebar.
"show_onboarding_banner": true,
// Whether to show user picture in the titlebar.
"show_user_picture": true,
// Whether to show the sign in button in the titlebar.
"show_sign_in": true
// Whether to show the Selections menu in the editor toolbar
"selections_menu": true
},
// Scrollbar related settings
"scrollbar": {
@@ -382,45 +343,6 @@
"vertical": true
}
},
// Minimap related settings
"minimap": {
// When to show the minimap in the editor.
// This setting can take three values:
// 1. Show the minimap if the editor's scrollbar is visible:
// "auto"
// 2. Always show the minimap:
// "always"
// 3. Never show the minimap:
// "never" (default)
"show": "never",
// When to show the minimap thumb.
// This setting can take two values:
// 1. Show the minimap thumb if the mouse is over the minimap:
// "hover"
// 2. Always show the minimap thumb:
// "always" (default)
"thumb": "always",
// How the minimap thumb border should look.
// This setting can take five values:
// 1. Display a border on all sides of the thumb:
// "thumb_border": "full"
// 2. Display a border on all sides except the left side of the thumb:
// "thumb_border": "left_open" (default)
// 3. Display a border on all sides except the right side of the thumb:
// "thumb_border": "right_open"
// 4. Display a border only on the left side of the thumb:
// "thumb_border": "left_only"
// 5. Display the thumb without any border:
// "thumb_border": "none"
"thumb_border": "left_open",
// How to highlight the current line in the minimap.
// This setting can take the following values:
//
// 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
},
// Enable middle-click paste on Linux.
"middle_click_paste": true,
// What to do when multibuffer is double clicked in some of its excerpts
@@ -435,6 +357,8 @@
"gutter": {
// Whether to show line numbers in the gutter.
"line_numbers": true,
// Whether to show code action buttons in the gutter.
"code_actions": true,
// Whether to show runnables buttons in the gutter.
"runnables": true,
// Whether to show breakpoints in the gutter.
@@ -474,17 +398,11 @@
// Scroll sensitivity multiplier. This multiplier is applied
// to both the horizontal and vertical delta values while scrolling.
"scroll_sensitivity": 1.0,
// Scroll sensitivity multiplier for fast scrolling. This multiplier is applied
// to both the horizontal and vertical delta values while scrolling. Fast scrolling
// happens when a user holds the alt or option key while scrolling.
"fast_scroll_sensitivity": 4.0,
"relative_line_numbers": false,
// If 'search_wrap' is disabled, search result do not wrap around the end of the file.
"search_wrap": true,
// Search options to enable by default when opening new project and buffer searches.
"search": {
// Whether to show the project search button in the status bar.
"button": true,
"whole_word": false,
"case_sensitive": false,
"include_ignored": false,
@@ -533,9 +451,6 @@
"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,
@@ -686,11 +601,6 @@
//
// Default: main
"fallback_branch_name": "main",
// Whether to sort entries in the panel by path
// or by status (the default).
//
// Default: false
"sort_by_path": false,
"scrollbar": {
// When to show the scrollbar in the git panel.
//
@@ -711,81 +621,37 @@
// Default width of the notification panel.
"default_width": 380
},
"agent": {
"assistant": {
// Version of this setting.
"version": "2",
// Whether the agent is enabled.
// Whether the assistant is enabled.
"enabled": true,
/// What completion mode to start new threads in, if available. Can be 'normal' or 'burn'.
"preferred_completion_mode": "normal",
// Whether to show the agent panel button in the status bar.
// Whether to show the assistant panel button in the status bar.
"button": true,
// Where to dock the agent panel. Can be 'left', 'right' or 'bottom'.
// Where to dock the assistant panel. Can be 'left', 'right' or 'bottom'.
"dock": "right",
// Default width when the agent panel is docked to the left or right.
// Default width when the assistant is docked to the left or right.
"default_width": 640,
// Default height when the agent panel is docked to the bottom.
// Default height when the assistant is docked to the bottom.
"default_height": 320,
// The default model to use when creating new threads.
// The default model to use when creating new chats.
"default_model": {
// The provider to use.
"provider": "zed.dev",
// The model to use.
"model": "claude-sonnet-4"
"model": "claude-3-7-sonnet-latest"
},
// The model to use when applying edits from the assistant.
"editor_model": {
// The provider to use.
"provider": "zed.dev",
// The model to use.
"model": "claude-3-7-sonnet-latest"
},
// Additional parameters for language model requests. When making a request to a model, parameters will be taken
// from the last entry in this list that matches the model's provider and name. In each entry, both provider
// and model are optional, so that you can specify parameters for either one.
"model_parameters": [
// To set parameters for all requests to OpenAI models:
// {
// "provider": "openai",
// "temperature": 0.5
// }
//
// To set parameters for all requests in general:
// {
// "temperature": 0
// }
//
// To set parameters for a specific provider and model:
// {
// "provider": "zed.dev",
// "model": "claude-sonnet-4",
// "temperature": 1.0
// }
],
// When enabled, the agent can run potentially destructive actions without asking for your confirmation.
"always_allow_tool_actions": false,
// When enabled, the agent will stream edits.
"stream_edits": false,
// When enabled, agent edits will be displayed in single-file editors for review
"single_file_review": true,
// When enabled, show voting thumbs for feedback on agent edits.
"enable_feedback": true,
"default_profile": "write",
"profiles": {
"write": {
"name": "Write",
"enable_all_context_servers": true,
"tools": {
"copy_path": true,
"create_directory": true,
"delete_path": true,
"diagnostics": true,
"edit_file": true,
"fetch": true,
"list_directory": true,
"move_path": true,
"now": true,
"find_path": true,
"read_file": true,
"grep": true,
"terminal": true,
"thinking": true,
"web_search": true
}
},
"ask": {
"name": "Ask",
// We don't know which of the context server tools are safe for the "Ask" profile, so we don't enable them by default.
@@ -794,34 +660,50 @@
"contents": true,
"diagnostics": true,
"fetch": true,
"list_directory": true,
"list_directory": false,
"now": true,
"find_path": true,
"read_file": true,
"open": true,
"grep": true,
"thinking": true,
"web_search": true
}
},
"minimal": {
"name": "Minimal",
"enable_all_context_servers": false,
"tools": {}
"write": {
"name": "Write",
"enable_all_context_servers": true,
"tools": {
"batch_tool": false,
"code_actions": false,
"code_symbols": false,
"contents": false,
"copy_path": false,
"create_file": true,
"delete_path": false,
"diagnostics": true,
"edit_file": true,
"fetch": true,
"list_directory": true,
"move_path": false,
"now": false,
"find_path": true,
"read_file": true,
"grep": true,
"rename": false,
"symbol_info": false,
"terminal": true,
"thinking": true,
"web_search": true
}
}
},
// Where to show notifications when the agent has either completed
// Where to show notifications when an agent has either completed
// its response, or else needs confirmation before it can run a
// tool action.
// "primary_screen" - Show the notification only on your primary screen (default)
// "all_screens" - Show these notifications on all screens
// "never" - Never show these notifications
"notify_when_agent_waiting": "primary_screen",
// Whether to play a sound when the agent has either completed
// its response, or needs user input.
// Default: false
"play_sound_when_agent_done": false
"notify_when_agent_waiting": "primary_screen"
},
// The settings for slash commands.
"slash_commands": {
@@ -940,30 +822,7 @@
// "modal_max_width": "full"
//
// Default: small
"modal_max_width": "small",
// Determines whether the file finder should skip focus for the active file in search results.
// There are 2 possible values:
//
// 1. true: When searching for files, if the currently active file appears as the first result,
// auto-focus will skip it and focus the second result instead.
// "skip_focus_for_active_in_search": true
//
// 2. false: When searching for files, the first result will always receive focus,
// even if it's the currently active file.
// "skip_focus_for_active_in_search": false
//
// Default: true
"skip_focus_for_active_in_search": true,
// Whether to show the git status in the file finder.
"git_status": true,
// Whether to use gitignored files when searching.
// Only the file Zed had indexed will be used, not necessary all the gitignored files.
//
// Can accept 3 values:
// * `true`: Use all gitignored files
// * `false`: Use only the files Zed had indexed
// * `null`: Be smart and search for ignored when called from a gitignored worktree
"include_ignored": null
"modal_max_width": "small"
},
// Whether or not to remove any trailing whitespace from lines of a buffer
// before saving it.
@@ -1015,8 +874,6 @@
"hard_tabs": false,
// How many columns a tab should occupy.
"tab_size": 4,
// What debuggers are preferred by default for all languages.
"debuggers": [],
// Control what info is collected by Zed.
"telemetry": {
// Send debug info like crash reports.
@@ -1029,18 +886,8 @@
"auto_update": true,
// 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
@@ -1056,13 +903,8 @@
// longer than this value will still push diagnostics further to the right.
"min_column": 0,
// The minimum severity of the diagnostics to show inline.
// Inherits editor's diagnostics' max severity settings when `null`.
// Shows all diagnostics when not specified.
"max_severity": null
},
"cargo": {
// When enabled, Zed disables rust-analyzer's check on save and starts to query
// Cargo diagnostics separately.
"fetch_cargo_diagnostics": false
}
},
// Files or globs of files that will be excluded by Zed entirely. They will be skipped during file
@@ -1142,9 +984,9 @@
// 2. Display predictions inline only when holding a modifier key (alt by default).
// "mode": "subtle"
"mode": "eager",
// Whether edit predictions are enabled when editing text threads.
// Whether edit predictions are enabled in the assistant panel.
// This setting has no effect if globally disabled.
"enabled_in_text_threads": true
"enabled_in_assistant": true
},
// Settings specific to journaling
"journal": {
@@ -1316,17 +1158,7 @@
// Settings related to running tasks.
"tasks": {
"variables": {},
"enabled": true,
// Use LSP tasks over Zed language extension ones.
// If no LSP tasks are returned due to error/timeout or regular execution,
// Zed language extension tasks will be used instead.
//
// Other Zed tasks will still be shown:
// * Zed task from either of the task config file
// * Zed task from history (e.g. one-off task was spawned before)
//
// Default: true
"prefer_lsp": true
"enabled": true
},
// An object whose keys are language names, and whose values
// are arrays of filenames or extensions of files that should
@@ -1344,22 +1176,21 @@
"JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json", "**/Zed/**/*.json", "**/.vscode/**/*.json"],
"Shell Script": [".env.*"]
},
// Settings for which version of Node.js and NPM to use when installing
// language servers and Copilot.
//
// Note: changing this setting currently requires restarting Zed.
"node": {
// By default, Zed will look for `node` and `npm` on your `$PATH`, and use the
// existing executables if their version is recent enough. Set this to `true`
// to prevent this, and force Zed to always download and install its own
// version of Node.
"ignore_system_version": false,
// You can also specify alternative paths to Node and NPM. If you specify
// `path`, but not `npm_path`, Zed will assume that `npm` is located at
// `${path}/../npm`.
"path": null,
"npm_path": null
},
// By default use a recent system version of node, or install our own.
// You can override this to use a version of node that is not in $PATH with:
// {
// "node": {
// "path": "/path/to/node"
// "npm_path": "/path/to/npm" (defaults to node_path/../npm)
// }
// }
// or to ensure Zed always downloads and installs an isolated version of node:
// {
// "node": {
// "ignore_system_version": true,
// }
// NOTE: changing this setting currently requires restarting Zed.
"node": {},
// The extensions that Zed should automatically install on startup.
//
// If you don't want any of these extensions, add this field to your settings
@@ -1457,16 +1288,11 @@
"Elixir": {
"language_servers": ["elixir-ls", "!next-ls", "!lexical", "..."]
},
"Elm": {
"tab_size": 4
},
"Erlang": {
"language_servers": ["erlang-ls", "!elp", "..."]
},
"Git Commit": {
"allow_rewrap": "anywhere",
"soft_wrap": "editor_width",
"preferred_line_length": 72
"allow_rewrap": "anywhere"
},
"Go": {
"code_actions_on_format": {
@@ -1509,11 +1335,11 @@
}
},
"LaTeX": {
"format_on_save": "on",
"formatter": "language_server",
"language_servers": ["texlab", "..."],
"prettier": {
"allowed": true,
"plugins": ["prettier-plugin-latex"]
"allowed": false
}
},
"Markdown": {
@@ -1537,13 +1363,19 @@
"allow_rewrap": "anywhere"
},
"Ruby": {
"language_servers": ["solargraph", "!ruby-lsp", "!rubocop", "!sorbet", "!steep", "..."]
"language_servers": ["solargraph", "!ruby-lsp", "!rubocop", "..."]
},
"SCSS": {
"prettier": {
"allowed": true
}
},
"SQL": {
"prettier": {
"allowed": true,
"plugins": ["prettier-plugin-sql"]
}
},
"Starlark": {
"language_servers": ["starpls", "!buck2-lsp", "..."]
},
@@ -1608,9 +1440,6 @@
"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"
},
@@ -1733,6 +1562,8 @@
// "W": "workspace::Save"
// }
"command_aliases": {},
// Whether to show user picture in titlebar.
"show_user_picture": true,
// ssh_connections is an array of ssh connections.
// You can configure these from `project: Open Remote` in the command palette.
// Zed's ssh support will pull configuration from your ~/.ssh too.
@@ -1749,9 +1580,7 @@
// }
// ]
"ssh_connections": [],
// Whether to read ~/.ssh/config for ssh connection sources.
"read_ssh_config": true,
// Configures context servers for use by the agent.
// Configures context servers for use in the Assistant.
"context_servers": {},
"debugger": {
"stepping_granularity": "line",

View File

@@ -1,34 +1,32 @@
// Some example tasks for common languages.
//
// For more documentation on how to configure debug tasks,
// see: https://zed.dev/docs/debugger
[
{
"label": "Debug active PHP file",
"adapter": "PHP",
"adapter": "php",
"program": "$ZED_FILE",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT"
},
{
"label": "Debug active Python file",
"adapter": "Debugpy",
"adapter": "python",
"program": "$ZED_FILE",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT"
},
{
"label": "Debug active JavaScript file",
"adapter": "JavaScript",
"adapter": "javascript",
"program": "$ZED_FILE",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT"
},
{
"label": "JavaScript debug terminal",
"adapter": "JavaScript",
"adapter": "javascript",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT",
"console": "integratedTerminal"
"initialize_args": {
"console": "integratedTerminal"
}
}
]

View File

@@ -1,5 +0,0 @@
// Project-local debug tasks
//
// For more documentation on how to configure debug tasks,
// see: https://zed.dev/docs/debugger
[]

Binary file not shown.

View File

@@ -261,11 +261,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#bfbdb6ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#d2a6ffff",
"font_style": null,
@@ -321,16 +316,6 @@
"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,
@@ -457,9 +442,9 @@
"terminal.foreground": "#5c6166ff",
"terminal.bright_foreground": "#5c6166ff",
"terminal.dim_foreground": "#fcfcfcff",
"terminal.ansi.black": "#5c6166ff",
"terminal.ansi.bright_black": "#3b9ee5ff",
"terminal.ansi.dim_black": "#9c9fa2ff",
"terminal.ansi.black": "#fcfcfcff",
"terminal.ansi.bright_black": "#bcbec0ff",
"terminal.ansi.dim_black": "#5c6166ff",
"terminal.ansi.red": "#ef7271ff",
"terminal.ansi.bright_red": "#febab6ff",
"terminal.ansi.dim_red": "#833538ff",
@@ -478,9 +463,9 @@
"terminal.ansi.cyan": "#4dbf99ff",
"terminal.ansi.bright_cyan": "#ace0cbff",
"terminal.ansi.dim_cyan": "#2a5f4aff",
"terminal.ansi.white": "#fcfcfcff",
"terminal.ansi.bright_white": "#fcfcfcff",
"terminal.ansi.dim_white": "#bcbec0ff",
"terminal.ansi.white": "#5c6166ff",
"terminal.ansi.bright_white": "#5c6166ff",
"terminal.ansi.dim_white": "#9c9fa2ff",
"link_text.hover": "#3b9ee5ff",
"conflict": "#f1ad49ff",
"conflict.background": "#ffeedaff",
@@ -647,11 +632,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#5c6166ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#a37accff",
"font_style": null,
@@ -707,16 +687,6 @@
"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,
@@ -1033,11 +1003,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#cccac2ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#dfbfffff",
"font_style": null,
@@ -1093,16 +1058,6 @@
"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,11 +270,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#d3869bff",
"font_style": null,
@@ -330,16 +325,6 @@
"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,
@@ -670,11 +655,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#d3869bff",
"font_style": null,
@@ -730,16 +710,6 @@
"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,
@@ -1070,11 +1040,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#d3869bff",
"font_style": null,
@@ -1130,16 +1095,6 @@
"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,
@@ -1272,9 +1227,9 @@
"terminal.foreground": "#282828ff",
"terminal.bright_foreground": "#282828ff",
"terminal.dim_foreground": "#fbf1c7ff",
"terminal.ansi.black": "#282828ff",
"terminal.ansi.bright_black": "#0b6678ff",
"terminal.ansi.dim_black": "#5f5650ff",
"terminal.ansi.black": "#fbf1c7ff",
"terminal.ansi.bright_black": "#b0a189ff",
"terminal.ansi.dim_black": "#282828ff",
"terminal.ansi.red": "#9d0308ff",
"terminal.ansi.bright_red": "#db8b7aff",
"terminal.ansi.dim_red": "#4e1207ff",
@@ -1293,9 +1248,9 @@
"terminal.ansi.cyan": "#437b59ff",
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
"terminal.ansi.white": "#fbf1c7ff",
"terminal.ansi.bright_white": "#fbf1c7ff",
"terminal.ansi.dim_white": "#b0a189ff",
"terminal.ansi.white": "#282828ff",
"terminal.ansi.bright_white": "#282828ff",
"terminal.ansi.dim_white": "#73675eff",
"link_text.hover": "#0b6678ff",
"version_control.added": "#797410ff",
"version_control.modified": "#b57615ff",
@@ -1470,11 +1425,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#066578ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#8f3e71ff",
"font_style": null,
@@ -1530,16 +1480,6 @@
"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,
@@ -1672,9 +1612,9 @@
"terminal.foreground": "#282828ff",
"terminal.bright_foreground": "#282828ff",
"terminal.dim_foreground": "#f9f5d7ff",
"terminal.ansi.black": "#282828ff",
"terminal.ansi.bright_black": "#73675eff",
"terminal.ansi.dim_black": "#f9f5d7ff",
"terminal.ansi.black": "#f9f5d7ff",
"terminal.ansi.bright_black": "#b0a189ff",
"terminal.ansi.dim_black": "#282828ff",
"terminal.ansi.red": "#9d0308ff",
"terminal.ansi.bright_red": "#db8b7aff",
"terminal.ansi.dim_red": "#4e1207ff",
@@ -1693,9 +1633,9 @@
"terminal.ansi.cyan": "#437b59ff",
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
"terminal.ansi.white": "#f9f5d7ff",
"terminal.ansi.bright_white": "#f9f5d7ff",
"terminal.ansi.dim_white": "#b0a189ff",
"terminal.ansi.white": "#282828ff",
"terminal.ansi.bright_white": "#282828ff",
"terminal.ansi.dim_white": "#73675eff",
"link_text.hover": "#0b6678ff",
"version_control.added": "#797410ff",
"version_control.modified": "#b57615ff",
@@ -1870,11 +1810,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#066578ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#8f3e71ff",
"font_style": null,
@@ -1930,16 +1865,6 @@
"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,
@@ -2072,9 +1997,9 @@
"terminal.foreground": "#282828ff",
"terminal.bright_foreground": "#282828ff",
"terminal.dim_foreground": "#f2e5bcff",
"terminal.ansi.black": "#282828ff",
"terminal.ansi.bright_black": "#73675eff",
"terminal.ansi.dim_black": "#f2e5bcff",
"terminal.ansi.black": "#f2e5bcff",
"terminal.ansi.bright_black": "#b0a189ff",
"terminal.ansi.dim_black": "#282828ff",
"terminal.ansi.red": "#9d0308ff",
"terminal.ansi.bright_red": "#db8b7aff",
"terminal.ansi.dim_red": "#4e1207ff",
@@ -2093,9 +2018,9 @@
"terminal.ansi.cyan": "#437b59ff",
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
"terminal.ansi.white": "#f2e5bcff",
"terminal.ansi.bright_white": "#f2e5bcff",
"terminal.ansi.dim_white": "#b0a189ff",
"terminal.ansi.white": "#282828ff",
"terminal.ansi.bright_white": "#282828ff",
"terminal.ansi.dim_white": "#73675eff",
"link_text.hover": "#0b6678ff",
"version_control.added": "#797410ff",
"version_control.modified": "#b57615ff",
@@ -2270,11 +2195,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#066578ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#8f3e71ff",
"font_style": null,
@@ -2330,16 +2250,6 @@
"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

@@ -264,11 +264,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#dce0e5ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#bf956aff",
"font_style": null,
@@ -324,16 +319,6 @@
"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,
@@ -465,9 +450,9 @@
"terminal.foreground": "#242529ff",
"terminal.bright_foreground": "#242529ff",
"terminal.dim_foreground": "#fafafaff",
"terminal.ansi.black": "#242529ff",
"terminal.ansi.bright_black": "#242529ff",
"terminal.ansi.dim_black": "#97979aff",
"terminal.ansi.black": "#fafafaff",
"terminal.ansi.bright_black": "#aaaaaaff",
"terminal.ansi.dim_black": "#242529ff",
"terminal.ansi.red": "#d36151ff",
"terminal.ansi.bright_red": "#f0b0a4ff",
"terminal.ansi.dim_red": "#6f312aff",
@@ -486,9 +471,9 @@
"terminal.ansi.cyan": "#3a82b7ff",
"terminal.ansi.bright_cyan": "#a3bedaff",
"terminal.ansi.dim_cyan": "#254058ff",
"terminal.ansi.white": "#fafafaff",
"terminal.ansi.bright_white": "#fafafaff",
"terminal.ansi.dim_white": "#aaaaaaff",
"terminal.ansi.white": "#242529ff",
"terminal.ansi.bright_white": "#242529ff",
"terminal.ansi.dim_white": "#97979aff",
"link_text.hover": "#5c78e2ff",
"version_control.added": "#27a657ff",
"version_control.modified": "#d3b020ff",
@@ -658,11 +643,6 @@
"font_style": null,
"font_weight": null
},
"namespace": {
"color": "#242529ff",
"font_style": null,
"font_weight": null
},
"number": {
"color": "#ad6e25ff",
"font_style": null,
@@ -718,16 +698,6 @@
"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

@@ -24,9 +24,8 @@ project.workspace = true
smallvec.workspace = true
ui.workspace = true
util.workspace = true
workspace-hack.workspace = true
workspace.workspace = true
workspace-hack.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }
release_channel.workspace = true

View File

@@ -1,4 +1,4 @@
use auto_update::{AutoUpdateStatus, AutoUpdater, DismissErrorMessage, VersionCheckType};
use auto_update::{AutoUpdateStatus, AutoUpdater, DismissErrorMessage};
use editor::Editor;
use extension_host::ExtensionStore;
use futures::StreamExt;
@@ -43,7 +43,6 @@ pub struct ActivityIndicator {
context_menu_handle: PopoverMenuHandle<ContextMenu>,
}
#[derive(Debug)]
struct ServerStatus {
name: SharedString,
status: BinaryStatus,
@@ -60,7 +59,6 @@ struct Content {
message: String,
on_click:
Option<Arc<dyn Fn(&mut ActivityIndicator, &mut Window, &mut Context<ActivityIndicator>)>>,
tooltip_message: Option<String>,
}
impl ActivityIndicator {
@@ -72,7 +70,6 @@ impl ActivityIndicator {
) -> Entity<ActivityIndicator> {
let project = workspace.project().clone();
let auto_updater = AutoUpdater::get(cx);
let workspace_handle = cx.entity();
let this = cx.new(|cx| {
let mut status_events = languages.language_server_binary_statuses();
cx.spawn(async move |this, cx| {
@@ -87,23 +84,17 @@ impl ActivityIndicator {
})
.detach();
cx.subscribe_in(
&workspace_handle,
window,
|activity_indicator, _, event, window, cx| match event {
workspace::Event::ClearActivityIndicator { .. } => {
if activity_indicator.statuses.pop().is_some() {
activity_indicator.dismiss_error_message(
&DismissErrorMessage,
window,
cx,
);
cx.notify();
}
}
_ => {}
},
)
let mut status_events = languages.dap_server_binary_statuses();
cx.spawn(async move |this, cx| {
while let Some((name, status)) = status_events.next().await {
this.update(cx, |this, cx| {
this.statuses.retain(|s| s.name != name);
this.statuses.push(ServerStatus { name, status });
cx.notify();
})?;
}
anyhow::Ok(())
})
.detach();
cx.subscribe(
@@ -137,7 +128,7 @@ impl ActivityIndicator {
}
Self {
statuses: Vec::new(),
statuses: Default::default(),
project: project.clone(),
auto_updater,
context_menu_handle: Default::default(),
@@ -207,8 +198,11 @@ impl ActivityIndicator {
cx: &mut Context<Self>,
) {
if let Some(updater) = &self.auto_updater {
updater.update(cx, |updater, cx| updater.dismiss_error(cx));
updater.update(cx, |updater, cx| {
updater.dismiss_error(cx);
});
}
cx.notify();
}
fn pending_language_server_work<'a>(
@@ -263,7 +257,6 @@ impl ActivityIndicator {
});
window.dispatch_action(Box::new(workspace::OpenLog), cx);
})),
tooltip_message: None,
});
}
// Show any language server has pending activity.
@@ -307,32 +300,6 @@ impl ActivityIndicator {
),
message,
on_click: Some(Arc::new(Self::toggle_language_server_work_context_menu)),
tooltip_message: None,
});
}
if let Some(session) = self
.project
.read(cx)
.dap_store()
.read(cx)
.sessions()
.find(|s| !s.read(cx).is_started())
{
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: format!("Debug: {}", session.read(cx).adapter()),
tooltip_message: Some(session.read(cx).label().to_string()),
on_click: None,
});
}
@@ -360,7 +327,6 @@ impl ActivityIndicator {
),
message: job_info.message.into(),
on_click: None,
tooltip_message: None,
});
}
}
@@ -403,7 +369,6 @@ impl ActivityIndicator {
.retain(|status| !downloading.contains(&status.name));
this.dismiss_error_message(&DismissErrorMessage, window, cx)
})),
tooltip_message: None,
});
}
@@ -432,7 +397,6 @@ impl ActivityIndicator {
.retain(|status| !checking_for_update.contains(&status.name));
this.dismiss_error_message(&DismissErrorMessage, window, cx)
})),
tooltip_message: None,
});
}
@@ -459,7 +423,6 @@ impl ActivityIndicator {
on_click: Some(Arc::new(|this, window, cx| {
this.show_error_message(&Default::default(), window, cx)
})),
tooltip_message: None,
});
}
@@ -478,7 +441,6 @@ impl ActivityIndicator {
});
window.dispatch_action(Box::new(workspace::OpenLog), cx);
})),
tooltip_message: None,
});
}
@@ -495,9 +457,8 @@ impl ActivityIndicator {
on_click: Some(Arc::new(|this, window, cx| {
this.dismiss_error_message(&DismissErrorMessage, window, cx)
})),
tooltip_message: None,
}),
AutoUpdateStatus::Downloading { version } => Some(Content {
AutoUpdateStatus::Downloading => Some(Content {
icon: Some(
Icon::new(IconName::Download)
.size(IconSize::Small)
@@ -507,9 +468,8 @@ impl ActivityIndicator {
on_click: Some(Arc::new(|this, window, cx| {
this.dismiss_error_message(&DismissErrorMessage, window, cx)
})),
tooltip_message: Some(Self::version_tooltip_message(&version)),
}),
AutoUpdateStatus::Installing { version } => Some(Content {
AutoUpdateStatus::Installing => Some(Content {
icon: Some(
Icon::new(IconName::Download)
.size(IconSize::Small)
@@ -519,12 +479,8 @@ impl ActivityIndicator {
on_click: Some(Arc::new(|this, window, cx| {
this.dismiss_error_message(&DismissErrorMessage, window, cx)
})),
tooltip_message: Some(Self::version_tooltip_message(&version)),
}),
AutoUpdateStatus::Updated {
binary_path,
version,
} => Some(Content {
AutoUpdateStatus::Updated { binary_path } => Some(Content {
icon: None,
message: "Click to restart and update Zed".to_string(),
on_click: Some(Arc::new({
@@ -533,7 +489,6 @@ impl ActivityIndicator {
};
move |_, _, cx| workspace::reload(&reload, cx)
})),
tooltip_message: Some(Self::version_tooltip_message(&version)),
}),
AutoUpdateStatus::Errored => Some(Content {
icon: Some(
@@ -545,7 +500,6 @@ impl ActivityIndicator {
on_click: Some(Arc::new(|this, window, cx| {
this.dismiss_error_message(&DismissErrorMessage, window, cx)
})),
tooltip_message: None,
}),
AutoUpdateStatus::Idle => None,
};
@@ -565,7 +519,6 @@ impl ActivityIndicator {
on_click: Some(Arc::new(|this, window, cx| {
this.dismiss_error_message(&DismissErrorMessage, window, cx)
})),
tooltip_message: None,
});
}
}
@@ -573,17 +526,6 @@ impl ActivityIndicator {
None
}
fn version_tooltip_message(version: &VersionCheckType) -> String {
format!("Version: {}", {
match version {
auto_update::VersionCheckType::Sha(sha) => format!("{}", sha.short()),
auto_update::VersionCheckType::Semantic(semantic_version) => {
semantic_version.to_string()
}
}
})
}
fn toggle_language_server_work_context_menu(
&mut self,
window: &mut Window,
@@ -628,14 +570,7 @@ impl Render for ActivityIndicator {
)
.tooltip(Tooltip::text(content.message))
} else {
button
.child(Label::new(content.message).size(LabelSize::Small))
.when_some(
content.tooltip_message,
|this, tooltip_message| {
this.tooltip(Tooltip::text(tooltip_message))
},
)
button.child(Label::new(content.message).size(LabelSize::Small))
}
})
.when_some(content.on_click, |this, handler| {
@@ -715,26 +650,3 @@ impl StatusItemView for ActivityIndicator {
) {
}
}
#[cfg(test)]
mod tests {
use gpui::SemanticVersion;
use release_channel::AppCommitSha;
use super::*;
#[test]
fn test_version_tooltip_message() {
let message = ActivityIndicator::version_tooltip_message(&VersionCheckType::Semantic(
SemanticVersion::new(1, 0, 0),
));
assert_eq!(message, "Version: 1.0.0");
let message = ActivityIndicator::version_tooltip_message(&VersionCheckType::Sha(
AppCommitSha::new("14d9a4189f058d8736339b06ff2340101eaea5af".to_string()),
));
assert_eq!(message, "Version: 14d9a41…");
}
}

View File

@@ -9,7 +9,7 @@ license = "GPL-3.0-or-later"
workspace = true
[lib]
path = "src/agent.rs"
path = "src/assistant.rs"
doctest = false
[features]
@@ -19,23 +19,22 @@ test-support = [
]
[dependencies]
agent_settings.workspace = true
anyhow.workspace = true
assistant_context_editor.workspace = true
assistant_settings.workspace = true
assistant_slash_command.workspace = true
assistant_slash_commands.workspace = true
assistant_tool.workspace = true
audio.workspace = true
async-watch.workspace = true
buffer_diff.workspace = true
chrono.workspace = true
client.workspace = true
collections.workspace = true
command_palette_hooks.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
@@ -45,40 +44,35 @@ git.workspace = true
gpui.workspace = true
heed.workspace = true
html_to_markdown.workspace = true
indoc.workspace = true
http_client.workspace = true
indexed_docs.workspace = true
inventory.workspace = true
indexmap.workspace = true
itertools.workspace = true
jsonschema.workspace = true
language.workspace = true
language_model.workspace = true
language_model_selector.workspace = true
linkme.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
rules_library.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
smallvec.workspace = true
smol.workspace = true
sqlez.workspace = true
streaming_diff.workspace = true
telemetry.workspace = true
telemetry_events.workspace = true
@@ -91,18 +85,14 @@ time.workspace = true
time_format.workspace = true
ui.workspace = true
ui_input.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
zstd.workspace = true
[dev-dependencies]
assistant_tools.workspace = true
buffer_diff = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, "features" = ["test-support"] }

File diff suppressed because it is too large Load Diff

View File

@@ -1,703 +0,0 @@
mod add_context_server_modal;
mod configure_context_server_modal;
mod manage_profiles_modal;
mod tool_picker;
use std::{sync::Arc, time::Duration};
use agent_settings::AgentSettings;
use assistant_tool::{ToolSource, ToolWorkingSet};
use collections::HashMap;
use context_server::ContextServerId;
use fs::Fs;
use gpui::{
Action, Animation, AnimationExt as _, AnyView, App, Entity, EventEmitter, FocusHandle,
Focusable, ScrollHandle, Subscription, pulsating_between,
};
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
use project::context_server_store::{ContextServerStatus, ContextServerStore};
use settings::{Settings, update_settings_file};
use ui::{
Disclosure, ElevationIndex, Indicator, Scrollbar, ScrollbarState, Switch, SwitchColor, Tooltip,
prelude::*,
};
use util::ResultExt as _;
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;
use crate::AddContextServer;
pub struct AgentConfiguration {
fs: Arc<dyn Fs>,
focus_handle: FocusHandle,
configuration_views_by_provider: HashMap<LanguageModelProviderId, AnyView>,
context_server_store: Entity<ContextServerStore>,
expanded_context_server_tools: HashMap<ContextServerId, bool>,
expanded_provider_configurations: HashMap<LanguageModelProviderId, bool>,
tools: Entity<ToolWorkingSet>,
_registry_subscription: Subscription,
scroll_handle: ScrollHandle,
scrollbar_state: ScrollbarState,
}
impl AgentConfiguration {
pub fn new(
fs: Arc<dyn Fs>,
context_server_store: Entity<ContextServerStore>,
tools: Entity<ToolWorkingSet>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let focus_handle = cx.focus_handle();
let registry_subscription = cx.subscribe_in(
&LanguageModelRegistry::global(cx),
window,
|this, _, event: &language_model::Event, window, cx| match event {
language_model::Event::AddedProvider(provider_id) => {
let provider = LanguageModelRegistry::read_global(cx).provider(provider_id);
if let Some(provider) = provider {
this.add_provider_configuration_view(&provider, window, cx);
}
}
language_model::Event::RemovedProvider(provider_id) => {
this.remove_provider_configuration_view(provider_id);
}
_ => {}
},
);
let scroll_handle = ScrollHandle::new();
let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
let mut this = Self {
fs,
focus_handle,
configuration_views_by_provider: HashMap::default(),
context_server_store,
expanded_context_server_tools: HashMap::default(),
expanded_provider_configurations: HashMap::default(),
tools,
_registry_subscription: registry_subscription,
scroll_handle,
scrollbar_state,
};
this.build_provider_configuration_views(window, cx);
this
}
fn build_provider_configuration_views(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let providers = LanguageModelRegistry::read_global(cx).providers();
for provider in providers {
self.add_provider_configuration_view(&provider, window, cx);
}
}
fn remove_provider_configuration_view(&mut self, provider_id: &LanguageModelProviderId) {
self.configuration_views_by_provider.remove(provider_id);
self.expanded_provider_configurations.remove(provider_id);
}
fn add_provider_configuration_view(
&mut self,
provider: &Arc<dyn LanguageModelProvider>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let configuration_view = provider.configuration_view(window, cx);
self.configuration_views_by_provider
.insert(provider.id(), configuration_view);
}
}
impl Focusable for AgentConfiguration {
fn focus_handle(&self, _: &App) -> FocusHandle {
self.focus_handle.clone()
}
}
pub enum AssistantConfigurationEvent {
NewThread(Arc<dyn LanguageModelProvider>),
}
impl EventEmitter<AssistantConfigurationEvent> for AgentConfiguration {}
impl AgentConfiguration {
fn render_provider_configuration_block(
&mut self,
provider: &Arc<dyn LanguageModelProvider>,
cx: &mut Context<Self>,
) -> impl IntoElement + use<> {
let provider_id = provider.id().0.clone();
let provider_name = provider.name().0.clone();
let configuration_view = self
.configuration_views_by_provider
.get(&provider.id())
.cloned();
let is_expanded = self
.expanded_provider_configurations
.get(&provider.id())
.copied()
.unwrap_or(false);
v_flex()
.pt_3()
.gap_1p5()
.border_t_1()
.border_color(cx.theme().colors().border.opacity(0.6))
.child(
h_flex()
.justify_between()
.child(
h_flex()
.gap_2()
.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",
)
.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(
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);
*is_expanded = !*is_expanded;
}
})),
),
),
)
.when(is_expanded, |parent| match configuration_view {
Some(configuration_view) => parent.child(configuration_view),
None => parent.child(Label::new(format!(
"No configuration view for {provider_name}",
))),
})
}
fn render_provider_configuration_section(
&mut self,
cx: &mut Context<Self>,
) -> impl IntoElement {
let providers = LanguageModelRegistry::read_global(cx).providers();
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()
.gap_0p5()
.child(Headline::new("LLM Providers"))
.child(
Label::new("Add at least one provider to use AI-powered features.")
.color(Color::Muted),
),
)
.children(
providers
.into_iter()
.map(|provider| self.render_provider_configuration_block(&provider, cx)),
)
}
fn render_command_permission(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let always_allow_tool_actions = AgentSettings::get_global(cx).always_allow_tool_actions;
h_flex()
.gap_4()
.justify_between()
.flex_wrap()
.child(
v_flex()
.gap_0p5()
.max_w_5_6()
.child(Label::new("Allow running editing tools without asking for confirmation"))
.child(
Label::new(
"The agent can perform potentially destructive actions without asking for your confirmation.",
)
.color(Color::Muted),
),
)
.child(
Switch::new(
"always-allow-tool-actions-switch",
always_allow_tool_actions.into(),
)
.color(SwitchColor::Accent)
.on_click({
let fs = self.fs.clone();
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(
fs.clone(),
cx,
move |settings, _| {
settings.set_always_allow_tool_actions(allow);
},
);
}
}),
)
}
fn render_single_file_review(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let single_file_review = AgentSettings::get_global(cx).single_file_review;
h_flex()
.gap_4()
.justify_between()
.flex_wrap()
.child(
v_flex()
.gap_0p5()
.max_w_5_6()
.child(Label::new("Enable single-file agent reviews"))
.child(
Label::new(
"Agent edits are also displayed in single-file editors for review.",
)
.color(Color::Muted),
),
)
.child(
Switch::new("single-file-review-switch", single_file_review.into())
.color(SwitchColor::Accent)
.on_click({
let fs = self.fs.clone();
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(
fs.clone(),
cx,
move |settings, _| {
settings.set_single_file_review(allow);
},
);
}
}),
)
}
fn render_sound_notification(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let play_sound_when_agent_done = AgentSettings::get_global(cx).play_sound_when_agent_done;
h_flex()
.gap_4()
.justify_between()
.flex_wrap()
.child(
v_flex()
.gap_0p5()
.max_w_5_6()
.child(Label::new("Play sound when finished generating"))
.child(
Label::new(
"Hear a notification sound when the agent is done generating changes or needs your input.",
)
.color(Color::Muted),
),
)
.child(
Switch::new("play-sound-notification-switch", play_sound_when_agent_done.into())
.color(SwitchColor::Accent)
.on_click({
let fs = self.fs.clone();
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(
fs.clone(),
cx,
move |settings, _| {
settings.set_play_sound_when_agent_done(allow);
},
);
}
}),
)
}
fn render_general_settings_section(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.pr(DynamicSpacing::Base20.rems(cx))
.gap_2p5()
.border_b_1()
.border_color(cx.theme().colors().border)
.child(Headline::new("General Settings"))
.child(self.render_command_permission(cx))
.child(self.render_single_file_review(cx))
.child(self.render_sound_notification(cx))
}
fn render_context_servers_section(
&mut self,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
let context_server_ids = self.context_server_store.read(cx).all_server_ids().clone();
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.pr(DynamicSpacing::Base20.rems(cx))
.gap_2()
.border_b_1()
.border_color(cx.theme().colors().border)
.child(
v_flex()
.gap_0p5()
.child(Headline::new("Model Context Protocol (MCP) Servers"))
.child(Label::new("Connect to context servers via the Model Context Protocol either via Zed extensions or directly.").color(Color::Muted)),
)
.children(
context_server_ids.into_iter().map(|context_server_id| {
self.render_context_server(context_server_id, window, cx)
}),
)
.child(
h_flex()
.justify_between()
.gap_2()
.child(
h_flex().w_full().child(
Button::new("add-context-server", "Add Custom Server")
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.full_width()
.icon(IconName::Plus)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.on_click(|_event, window, cx| {
window.dispatch_action(AddContextServer.boxed_clone(), cx)
}),
),
)
.child(
h_flex().w_full().child(
Button::new(
"install-context-server-extensions",
"Install MCP Extensions",
)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.full_width()
.icon(IconName::Hammer)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.on_click(|_event, window, cx| {
window.dispatch_action(
zed_actions::Extensions {
category_filter: Some(
ExtensionCategoryFilter::ContextServers,
),
}
.boxed_clone(),
cx,
)
}),
),
),
)
}
fn render_context_server(
&self,
context_server_id: ContextServerId,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl use<> + IntoElement {
let tools_by_source = self.tools.read(cx).tools_by_source(cx);
let server_status = self
.context_server_store
.read(cx)
.status_for_server(&context_server_id)
.unwrap_or(ContextServerStatus::Stopped);
let is_running = matches!(server_status, ContextServerStatus::Running);
let item_id = SharedString::from(context_server_id.0.clone());
let error = if let ContextServerStatus::Error(error) = server_status.clone() {
Some(error)
} else {
None
};
let are_tools_expanded = self
.expanded_context_server_tools
.get(&context_server_id)
.copied()
.unwrap_or_default();
let tools = tools_by_source
.get(&ToolSource::ContextServer {
id: context_server_id.0.clone().into(),
})
.map_or([].as_slice(), |tools| tools.as_slice());
let tool_count = tools.len();
let border_color = cx.theme().colors().border.opacity(0.6);
let success_color = Color::Success.color(cx);
let (status_indicator, tooltip_text) = match server_status {
ContextServerStatus::Starting => (
Indicator::dot()
.color(Color::Success)
.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()),
)
.into_any_element(),
"Server is starting.",
),
ContextServerStatus::Running => (
Indicator::dot().color(Color::Success).into_any_element(),
"Server is running.",
),
ContextServerStatus::Error(_) => (
Indicator::dot().color(Color::Error).into_any_element(),
"Server has an error.",
),
ContextServerStatus::Stopped => (
Indicator::dot().color(Color::Muted).into_any_element(),
"Server is stopped.",
),
};
v_flex()
.id(item_id.clone())
.border_1()
.rounded_md()
.border_color(border_color)
.bg(cx.theme().colors().background.opacity(0.2))
.overflow_hidden()
.child(
h_flex()
.p_1()
.justify_between()
.when(
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",
are_tools_expanded || error.is_some(),
)
.disabled(tool_count == 0)
.on_click(cx.listener({
let context_server_id = context_server_id.clone();
move |this, _event, _window, _cx| {
let is_open = this
.expanded_context_server_tools
.entry(context_server_id.clone())
.or_insert(false);
*is_open = !*is_open;
}
})),
)
.child(
div()
.id(item_id.clone())
.tooltip(Tooltip::text(tooltip_text))
.child(status_indicator),
)
.child(Label::new(context_server_id.0.clone()).ml_0p5())
.when(is_running, |this| {
this.child(
Label::new(if tool_count == 1 {
SharedString::from("1 tool")
} else {
SharedString::from(format!("{} tools", tool_count))
})
.color(Color::Muted)
.size(LabelSize::Small),
)
}),
)
.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();
}
})
}
}
}),
),
)
.map(|parent| {
if let Some(error) = error {
return parent.child(
h_flex()
.p_2()
.gap_2()
.items_start()
.child(
h_flex()
.flex_none()
.h(window.line_height() / 1.6_f32)
.justify_center()
.child(
Icon::new(IconName::XCircle)
.size(IconSize::XSmall)
.color(Color::Error),
),
)
.child(
div().w_full().child(
Label::new(error)
.buffer_font(cx)
.color(Color::Muted)
.size(LabelSize::Small),
),
),
);
}
if !are_tools_expanded || tools.is_empty() {
return parent;
}
parent.child(v_flex().py_1p5().px_1().gap_1().children(
tools.into_iter().enumerate().map(|(ix, tool)| {
h_flex()
.id(("tool-item", ix))
.px_1()
.gap_2()
.justify_between()
.hover(|style| style.bg(cx.theme().colors().element_hover))
.rounded_sm()
.child(
Label::new(tool.name())
.buffer_font(cx)
.size(LabelSize::Small),
)
.child(
Icon::new(IconName::Info)
.size(IconSize::Small)
.color(Color::Ignored),
)
.tooltip(Tooltip::text(tool.description()))
}),
))
})
}
}
impl Render for AgentConfiguration {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.id("assistant-configuration")
.key_context("AgentConfiguration")
.track_focus(&self.focus_handle(cx))
.relative()
.size_full()
.pb_8()
.bg(cx.theme().colors().panel_background)
.child(
v_flex()
.id("assistant-configuration-content")
.track_scroll(&self.scroll_handle)
.size_full()
.overflow_y_scroll()
.child(self.render_general_settings_section(cx))
.child(self.render_context_servers_section(window, cx))
.child(self.render_provider_configuration_section(cx)),
)
.child(
div()
.id("assistant-configuration-scrollbar")
.occlude()
.absolute()
.right(px(3.))
.top_0()
.bottom_0()
.pb_6()
.w(px(12.))
.cursor_default()
.on_mouse_move(cx.listener(|_, _, _window, cx| {
cx.notify();
cx.stop_propagation()
}))
.on_hover(|_, _window, cx| {
cx.stop_propagation();
})
.on_any_mouse_down(|_, _window, cx| {
cx.stop_propagation();
})
.on_scroll_wheel(cx.listener(|_, _, _window, cx| {
cx.notify();
}))
.children(Scrollbar::vertical(self.scrollbar_state.clone())),
)
}
}

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

@@ -1,42 +0,0 @@
use ui::prelude::*;
#[derive(IntoElement)]
pub struct ProfileModalHeader {
label: SharedString,
icon: Option<IconName>,
}
impl ProfileModalHeader {
pub fn new(label: impl Into<SharedString>, icon: Option<IconName>) -> Self {
Self {
label: label.into(),
icon,
}
}
}
impl RenderOnce for ProfileModalHeader {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let mut container = h_flex()
.w_full()
.px(DynamicSpacing::Base12.rems(cx))
.pt(DynamicSpacing::Base08.rems(cx))
.pb(DynamicSpacing::Base04.rems(cx))
.rounded_t_sm()
.gap_1p5();
if let Some(icon) = self.icon {
container = container.child(Icon::new(icon).size(IconSize::XSmall).color(Color::Muted));
}
container.child(
h_flex().gap_1().overflow_x_hidden().child(
div()
.max_w_96()
.overflow_x_hidden()
.text_ellipsis()
.child(Headline::new(self.label).size(HeadlineSize::XSmall)),
),
)
}
}

View File

@@ -1,379 +0,0 @@
use std::{collections::BTreeMap, sync::Arc};
use agent_settings::{
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::update_settings_file;
use ui::{ListItem, ListItemSpacing, prelude::*};
use util::ResultExt as _;
pub struct ToolPicker {
picker: Entity<Picker<ToolPickerDelegate>>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ToolPickerMode {
BuiltinTools,
McpTools,
}
impl ToolPicker {
pub fn builtin_tools(
delegate: ToolPickerDelegate,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false));
Self { picker }
}
pub fn mcp_tools(
delegate: ToolPickerDelegate,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let picker = cx.new(|cx| Picker::list(delegate, window, cx).modal(false));
Self { picker }
}
}
impl EventEmitter<DismissEvent> for ToolPicker {}
impl Focusable for ToolPicker {
fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
self.picker.focus_handle(cx)
}
}
impl Render for ToolPicker {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
v_flex().w(rems(34.)).child(self.picker.clone())
}
}
#[derive(Debug, Clone)]
pub enum PickerItem {
Tool {
server_id: Option<Arc<str>>,
name: Arc<str>,
},
ContextServer {
server_id: Arc<str>,
},
}
pub struct ToolPickerDelegate {
tool_picker: WeakEntity<ToolPicker>,
fs: Arc<dyn Fs>,
items: Arc<Vec<PickerItem>>,
profile_id: AgentProfileId,
profile_settings: AgentProfileSettings,
filtered_items: Vec<PickerItem>,
selected_index: usize,
mode: ToolPickerMode,
}
impl ToolPickerDelegate {
pub fn new(
mode: ToolPickerMode,
fs: Arc<dyn Fs>,
tool_set: Entity<ToolWorkingSet>,
profile_id: AgentProfileId,
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(),
fs,
items,
profile_id,
profile_settings,
filtered_items: Vec::new(),
selected_index: 0,
mode,
}
}
fn resolve_items(
mode: ToolPickerMode,
tool_set: &Entity<ToolWorkingSet>,
cx: &mut App,
) -> Vec<PickerItem> {
let mut items = Vec::new();
for (source, tools) in tool_set.read(cx).tools_by_source(cx) {
match source {
ToolSource::Native => {
if mode == ToolPickerMode::BuiltinTools {
items.extend(tools.into_iter().map(|tool| PickerItem::Tool {
name: tool.name().into(),
server_id: None,
}));
}
}
ToolSource::ContextServer { id } => {
if mode == ToolPickerMode::McpTools && !tools.is_empty() {
let server_id: Arc<str> = id.clone().into();
items.push(PickerItem::ContextServer {
server_id: server_id.clone(),
});
items.extend(tools.into_iter().map(|tool| PickerItem::Tool {
name: tool.name().into(),
server_id: Some(server_id.clone()),
}));
}
}
}
}
items
}
}
impl PickerDelegate for ToolPickerDelegate {
type ListItem = AnyElement;
fn match_count(&self) -> usize {
self.filtered_items.len()
}
fn selected_index(&self) -> usize {
self.selected_index
}
fn set_selected_index(
&mut self,
ix: usize,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) {
self.selected_index = ix;
}
fn can_select(
&mut self,
ix: usize,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) -> bool {
let item = &self.filtered_items[ix];
match item {
PickerItem::Tool { .. } => true,
PickerItem::ContextServer { .. } => false,
}
}
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
match self.mode {
ToolPickerMode::BuiltinTools => "Search built-in tools…",
ToolPickerMode::McpTools => "Search MCP tools…",
}
.into()
}
fn update_matches(
&mut self,
query: String,
window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let all_items = self.items.clone();
cx.spawn_in(window, async move |this, cx| {
let filtered_items = cx
.background_spawn(async move {
let mut tools_by_provider: BTreeMap<Option<Arc<str>>, Vec<Arc<str>>> =
BTreeMap::default();
for item in all_items.iter() {
if let PickerItem::Tool { server_id, name } = item.clone() {
if name.contains(&query) {
tools_by_provider.entry(server_id).or_default().push(name);
}
}
}
let mut items = Vec::new();
for (server_id, names) in tools_by_provider {
if let Some(server_id) = server_id.clone() {
items.push(PickerItem::ContextServer { server_id });
}
for name in names {
items.push(PickerItem::Tool {
server_id: server_id.clone(),
name,
});
}
}
items
})
.await;
this.update(cx, |this, _cx| {
this.delegate.filtered_items = filtered_items;
this.delegate.selected_index = this
.delegate
.selected_index
.min(this.delegate.filtered_items.len().saturating_sub(1));
})
.log_err();
})
}
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
if self.filtered_items.is_empty() {
self.dismissed(window, cx);
return;
}
let item = &self.filtered_items[self.selected_index];
let PickerItem::Tool {
name: tool_name,
server_id,
} = item
else {
return;
};
let is_currently_enabled = if let Some(server_id) = server_id.clone() {
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_settings
.tools
.entry(tool_name.clone())
.or_default();
*self
.profile_settings
.tools
.entry(tool_name.clone())
.or_default() = !is_enabled;
is_enabled
};
update_settings_file::<AgentSettings>(self.fs.clone(), cx, {
let profile_id = self.profile_id.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(),
});
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();
}
});
}
fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
self.tool_picker
.update(cx, |_this, cx| cx.emit(DismissEvent))
.log_err();
}
fn render_match(
&self,
ix: usize,
selected: bool,
_window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Option<Self::ListItem> {
let item = &self.filtered_items[ix];
match item {
PickerItem::ContextServer { server_id, .. } => Some(
div()
.px_2()
.pb_1()
.when(ix > 1, |this| {
this.mt_1()
.pt_2()
.border_t_1()
.border_color(cx.theme().colors().border_variant)
})
.child(
Label::new(server_id)
.size(LabelSize::XSmall)
.color(Color::Muted),
)
.into_any_element(),
),
PickerItem::Tool { name, server_id } => {
let is_enabled = if let Some(server_id) = server_id {
self.profile_settings
.context_servers
.get(server_id.as_ref())
.and_then(|preset| preset.tools.get(name))
.copied()
.unwrap_or(self.profile_settings.enable_all_context_servers)
} else {
self.profile_settings
.tools
.get(name)
.copied()
.unwrap_or(false)
};
Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.child(Label::new(name.clone()))
.end_slot::<Icon>(is_enabled.then(|| {
Icon::new(IconName::Check)
.size(IconSize::Small)
.color(Color::Success)
}))
.into_any_element(),
)
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,124 +0,0 @@
use agent_settings::AgentSettings;
use fs::Fs;
use gpui::{Entity, FocusHandle, SharedString};
use picker::popover_menu::PickerPopoverMenu;
use crate::ModelUsageContext;
use assistant_context_editor::language_model_selector::{
LanguageModelSelector, ToggleModelSelector, language_model_selector,
};
use language_model::{ConfiguredModel, LanguageModelRegistry};
use settings::update_settings_file;
use std::sync::Arc;
use ui::{PopoverMenuHandle, Tooltip, prelude::*};
pub struct AgentModelSelector {
selector: Entity<LanguageModelSelector>,
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
focus_handle: FocusHandle,
}
impl AgentModelSelector {
pub(crate) fn new(
fs: Arc<dyn Fs>,
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
focus_handle: FocusHandle,
model_usage_context: ModelUsageContext,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
Self {
selector: cx.new(move |cx| {
let fs = fs.clone();
language_model_selector(
{
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_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())
{
thread.set_configured_model(
Some(ConfiguredModel {
provider,
model: model.clone(),
}),
cx,
);
}
});
update_settings_file::<AgentSettings>(
fs.clone(),
cx,
move |settings, _cx| {
settings.set_model(model.clone());
},
);
}
ModelUsageContext::InlineAssistant => {
update_settings_file::<AgentSettings>(
fs.clone(),
cx,
move |settings, _cx| {
settings.set_inline_assistant_model(
provider.clone(),
model_id.clone(),
);
},
);
}
}
},
window,
cx,
)
}),
menu_handle,
focus_handle,
}
}
pub fn toggle(&self, window: &mut Window, cx: &mut Context<Self>) {
self.menu_handle.toggle(window, cx);
}
}
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
.map(|model| model.model.name().0)
.unwrap_or_else(|| SharedString::from("No model selected"));
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),
move |window, cx| {
Tooltip::for_action_in(
"Change Model",
&ToggleModelSelector,
&focus_handle,
window,
cx,
)
},
gpui::Corner::BottomRight,
cx,
)
.with_handle(self.menu_handle.clone())
.render(window, cx)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,334 +0,0 @@
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};
use settings::{Settings, update_settings_file};
use ui::SharedString;
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()
}
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::{AppContext, TestAppContext};
use http_client::FakeHttpClient;
use project::Project;
use settings::{Settings, SettingsStore};
use ui::SharedString;
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) -> ui::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

@@ -0,0 +1,148 @@
mod active_thread;
mod agent_diff;
mod assistant_configuration;
mod assistant_model_selector;
mod assistant_panel;
mod buffer_codegen;
mod context;
mod context_picker;
mod context_store;
mod context_strip;
mod history_store;
mod inline_assistant;
mod inline_prompt_editor;
mod message_editor;
mod profile_selector;
mod terminal_codegen;
mod terminal_inline_assistant;
mod thread;
mod thread_history;
mod thread_store;
mod tool_compatibility;
mod tool_use;
mod ui;
use std::sync::Arc;
use assistant_settings::{AgentProfileId, AssistantSettings};
use client::Client;
use command_palette_hooks::CommandPaletteFilter;
use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt};
use fs::Fs;
use gpui::{App, actions, impl_actions};
use prompt_store::PromptBuilder;
use schemars::JsonSchema;
use serde::Deserialize;
use settings::Settings as _;
use thread::ThreadId;
pub use crate::active_thread::ActiveThread;
use crate::assistant_configuration::{AddContextServerModal, ManageProfilesModal};
pub use crate::assistant_panel::{AssistantPanel, ConcreteAssistantPanelDelegate};
pub use crate::context::{ContextLoadResult, LoadedContext};
pub use crate::inline_assistant::InlineAssistant;
pub use crate::thread::{Message, Thread, ThreadEvent};
pub use crate::thread_store::ThreadStore;
pub use agent_diff::{AgentDiff, AgentDiffToolbar};
actions!(
agent,
[
NewTextThread,
ToggleContextPicker,
ToggleProfileSelector,
RemoveAllContext,
ExpandMessageEditor,
OpenHistory,
AddContextServer,
RemoveSelectedThread,
Chat,
ChatMode,
CycleNextInlineAssist,
CyclePreviousInlineAssist,
FocusUp,
FocusDown,
FocusLeft,
FocusRight,
RemoveFocusedContext,
AcceptSuggestedContext,
OpenActiveThreadAsMarkdown,
OpenAgentDiff,
Keep,
Reject,
RejectAll,
KeepAll
]
);
#[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]);
const NAMESPACE: &str = "agent";
/// Initializes the `agent` crate.
pub fn init(
fs: Arc<dyn Fs>,
client: Arc<Client>,
prompt_builder: Arc<PromptBuilder>,
cx: &mut App,
) {
AssistantSettings::register(cx);
thread_store::init(cx);
assistant_panel::init(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,
);
cx.observe_new(AddContextServerModal::register).detach();
cx.observe_new(ManageProfilesModal::register).detach();
feature_gate_agent_actions(cx);
}
fn feature_gate_agent_actions(cx: &mut App) {
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.hide_namespace(NAMESPACE);
});
cx.observe_flag::<Assistant2FeatureFlag, _>(move |is_enabled, cx| {
if is_enabled {
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.show_namespace(NAMESPACE);
});
} else {
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.hide_namespace(NAMESPACE);
});
}
})
.detach();
}

View File

@@ -0,0 +1,501 @@
mod add_context_server_modal;
mod manage_profiles_modal;
mod tool_picker;
use std::sync::Arc;
use assistant_settings::AssistantSettings;
use assistant_tool::{ToolSource, ToolWorkingSet};
use collections::HashMap;
use context_server::manager::ContextServerManager;
use fs::Fs;
use gpui::{
Action, AnyView, App, Entity, EventEmitter, FocusHandle, Focusable, ScrollHandle, Subscription,
};
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
use settings::{Settings, update_settings_file};
use ui::{
Disclosure, Divider, DividerColor, ElevationIndex, Indicator, Scrollbar, ScrollbarState,
Switch, SwitchColor, Tooltip, prelude::*,
};
use util::ResultExt as _;
use zed_actions::ExtensionCategoryFilter;
pub(crate) use add_context_server_modal::AddContextServerModal;
pub(crate) use manage_profiles_modal::ManageProfilesModal;
use crate::AddContextServer;
pub struct AssistantConfiguration {
fs: Arc<dyn Fs>,
focus_handle: FocusHandle,
configuration_views_by_provider: HashMap<LanguageModelProviderId, AnyView>,
context_server_manager: Entity<ContextServerManager>,
expanded_context_server_tools: HashMap<Arc<str>, bool>,
tools: Entity<ToolWorkingSet>,
_registry_subscription: Subscription,
scroll_handle: ScrollHandle,
scrollbar_state: ScrollbarState,
}
impl AssistantConfiguration {
pub fn new(
fs: Arc<dyn Fs>,
context_server_manager: Entity<ContextServerManager>,
tools: Entity<ToolWorkingSet>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let focus_handle = cx.focus_handle();
let registry_subscription = cx.subscribe_in(
&LanguageModelRegistry::global(cx),
window,
|this, _, event: &language_model::Event, window, cx| match event {
language_model::Event::AddedProvider(provider_id) => {
let provider = LanguageModelRegistry::read_global(cx).provider(provider_id);
if let Some(provider) = provider {
this.add_provider_configuration_view(&provider, window, cx);
}
}
language_model::Event::RemovedProvider(provider_id) => {
this.remove_provider_configuration_view(provider_id);
}
_ => {}
},
);
let scroll_handle = ScrollHandle::new();
let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
let mut this = Self {
fs,
focus_handle,
configuration_views_by_provider: HashMap::default(),
context_server_manager,
expanded_context_server_tools: HashMap::default(),
tools,
_registry_subscription: registry_subscription,
scroll_handle,
scrollbar_state,
};
this.build_provider_configuration_views(window, cx);
this
}
fn build_provider_configuration_views(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let providers = LanguageModelRegistry::read_global(cx).providers();
for provider in providers {
self.add_provider_configuration_view(&provider, window, cx);
}
}
fn remove_provider_configuration_view(&mut self, provider_id: &LanguageModelProviderId) {
self.configuration_views_by_provider.remove(provider_id);
}
fn add_provider_configuration_view(
&mut self,
provider: &Arc<dyn LanguageModelProvider>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let configuration_view = provider.configuration_view(window, cx);
self.configuration_views_by_provider
.insert(provider.id(), configuration_view);
}
}
impl Focusable for AssistantConfiguration {
fn focus_handle(&self, _: &App) -> FocusHandle {
self.focus_handle.clone()
}
}
pub enum AssistantConfigurationEvent {
NewThread(Arc<dyn LanguageModelProvider>),
}
impl EventEmitter<AssistantConfigurationEvent> for AssistantConfiguration {}
impl AssistantConfiguration {
fn render_provider_configuration_block(
&mut self,
provider: &Arc<dyn LanguageModelProvider>,
cx: &mut Context<Self>,
) -> impl IntoElement + use<> {
let provider_id = provider.id().0.clone();
let provider_name = provider.name().0.clone();
let configuration_view = self
.configuration_views_by_provider
.get(&provider.id())
.cloned();
v_flex()
.pt_3()
.pb_1()
.gap_1p5()
.border_t_1()
.border_color(cx.theme().colors().border.opacity(0.6))
.child(
h_flex()
.justify_between()
.child(
h_flex()
.gap_2()
.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), |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)
.style(ButtonStyle::Filled)
.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(),
))
}
})),
)
}),
)
.map(|parent| match configuration_view {
Some(configuration_view) => parent.child(configuration_view),
None => parent.child(div().child(Label::new(format!(
"No configuration view for {provider_name}",
)))),
})
}
fn render_provider_configuration_section(
&mut self,
cx: &mut Context<Self>,
) -> impl IntoElement {
let providers = LanguageModelRegistry::read_global(cx).providers();
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.pr(DynamicSpacing::Base20.rems(cx))
.gap_4()
.flex_1()
.child(
v_flex()
.gap_0p5()
.child(Headline::new("LLM Providers"))
.child(
Label::new("Add at least one provider to use AI-powered features.")
.color(Color::Muted),
),
)
.children(
providers
.into_iter()
.map(|provider| self.render_provider_configuration_block(&provider, cx)),
)
}
fn render_command_permission(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let always_allow_tool_actions = AssistantSettings::get_global(cx).always_allow_tool_actions;
const HEADING: &str = "Allow running editing tools without asking for confirmation";
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.pr(DynamicSpacing::Base20.rems(cx))
.gap_2()
.flex_1()
.child(Headline::new("General Settings"))
.child(
h_flex()
.gap_4()
.justify_between()
.flex_wrap()
.child(
v_flex()
.gap_0p5()
.max_w_5_6()
.child(Label::new(HEADING))
.child(Label::new("When enabled, the agent can perform potentially destructive actions without asking for your confirmation.").color(Color::Muted)),
)
.child(
Switch::new(
"always-allow-tool-actions-switch",
always_allow_tool_actions.into(),
)
.color(SwitchColor::Accent)
.on_click({
let fs = self.fs.clone();
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _| {
settings.set_always_allow_tool_actions(allow);
},
);
}
}),
),
)
}
fn render_context_servers_section(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let context_servers = self.context_server_manager.read(cx).all_servers().clone();
let tools_by_source = self.tools.read(cx).tools_by_source(cx);
let empty = Vec::new();
const SUBHEADING: &str = "Connect to context servers via the Model Context Protocol either via Zed extensions or directly.";
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.pr(DynamicSpacing::Base20.rems(cx))
.gap_2()
.flex_1()
.child(
v_flex()
.gap_0p5()
.child(Headline::new("Model Context Protocol (MCP) Servers"))
.child(Label::new(SUBHEADING).color(Color::Muted)),
)
.children(context_servers.into_iter().map(|context_server| {
let is_running = context_server.client().is_some();
let are_tools_expanded = self
.expanded_context_server_tools
.get(&context_server.id())
.copied()
.unwrap_or_default();
let tools = tools_by_source
.get(&ToolSource::ContextServer {
id: context_server.id().into(),
})
.unwrap_or_else(|| &empty);
let tool_count = tools.len();
v_flex()
.id(SharedString::from(context_server.id()))
.border_1()
.rounded_md()
.border_color(cx.theme().colors().border)
.bg(cx.theme().colors().background.opacity(0.25))
.child(
h_flex()
.p_1()
.justify_between()
.when(are_tools_expanded && tool_count > 1, |element| {
element
.border_b_1()
.border_color(cx.theme().colors().border)
})
.child(
h_flex()
.gap_2()
.child(
Disclosure::new("tool-list-disclosure", are_tools_expanded)
.disabled(tool_count == 0)
.on_click(cx.listener({
let context_server_id = context_server.id();
move |this, _event, _window, _cx| {
let is_open = this
.expanded_context_server_tools
.entry(context_server_id.clone())
.or_insert(false);
*is_open = !*is_open;
}
})),
)
.child(Indicator::dot().color(if is_running {
Color::Success
} else {
Color::Error
}))
.child(Label::new(context_server.id()))
.child(
Label::new(format!("{tool_count} tools"))
.color(Color::Muted)
.size(LabelSize::Small),
),
)
.child(
Switch::new("context-server-switch", is_running.into())
.color(SwitchColor::Accent)
.on_click({
let context_server_manager =
self.context_server_manager.clone();
let context_server = context_server.clone();
move |state, _window, cx| match state {
ToggleState::Unselected
| ToggleState::Indeterminate => {
context_server_manager.update(cx, |this, cx| {
this.stop_server(context_server.clone(), cx)
.log_err();
});
}
ToggleState::Selected => {
cx.spawn({
let context_server_manager =
context_server_manager.clone();
let context_server = context_server.clone();
async move |cx| {
if let Some(start_server_task) =
context_server_manager
.update(cx, |this, cx| {
this.start_server(
context_server,
cx,
)
})
.log_err()
{
start_server_task.await.log_err();
}
}
})
.detach();
}
}
}),
),
)
.map(|parent| {
if !are_tools_expanded {
return parent;
}
parent.child(v_flex().py_1p5().px_1().gap_1().children(
tools.into_iter().enumerate().map(|(ix, tool)| {
h_flex()
.id(("tool-item", ix))
.px_1()
.gap_2()
.justify_between()
.hover(|style| style.bg(cx.theme().colors().element_hover))
.rounded_sm()
.child(
Label::new(tool.name())
.buffer_font(cx)
.size(LabelSize::Small),
)
.child(
Icon::new(IconName::Info)
.size(IconSize::Small)
.color(Color::Ignored),
)
.tooltip(Tooltip::text(tool.description()))
}),
))
})
}))
.child(
h_flex()
.justify_between()
.gap_2()
.child(
h_flex().w_full().child(
Button::new("add-context-server", "Add Custom Server")
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.full_width()
.icon(IconName::Plus)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.on_click(|_event, window, cx| {
window.dispatch_action(AddContextServer.boxed_clone(), cx)
}),
),
)
.child(
h_flex().w_full().child(
Button::new(
"install-context-server-extensions",
"Install MCP Extensions",
)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.full_width()
.icon(IconName::DatabaseZap)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.on_click(|_event, window, cx| {
window.dispatch_action(
zed_actions::Extensions {
category_filter: Some(
ExtensionCategoryFilter::ContextServers,
),
}
.boxed_clone(),
cx,
)
}),
),
),
)
}
}
impl Render for AssistantConfiguration {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.id("assistant-configuration")
.key_context("AgentConfiguration")
.track_focus(&self.focus_handle(cx))
.relative()
.size_full()
.pb_8()
.bg(cx.theme().colors().panel_background)
.child(
v_flex()
.id("assistant-configuration-content")
.track_scroll(&self.scroll_handle)
.size_full()
.overflow_y_scroll()
.child(self.render_command_permission(cx))
.child(Divider::horizontal().color(DividerColor::Border))
.child(self.render_context_servers_section(cx))
.child(Divider::horizontal().color(DividerColor::Border))
.child(self.render_provider_configuration_section(cx)),
)
.child(
div()
.id("assistant-configuration-scrollbar")
.occlude()
.absolute()
.right(px(3.))
.top_0()
.bottom_0()
.pb_6()
.w(px(12.))
.cursor_default()
.on_mouse_move(cx.listener(|_, _, _window, cx| {
cx.notify();
cx.stop_propagation()
}))
.on_hover(|_, _window, cx| {
cx.stop_propagation();
})
.on_any_mouse_down(|_, _window, cx| {
cx.stop_propagation();
})
.on_scroll_wheel(cx.listener(|_, _, _window, cx| {
cx.notify();
}))
.children(Scrollbar::vertical(self.scrollbar_state.clone())),
)
}
}

View File

@@ -1,6 +1,5 @@
use context_server::ContextServerCommand;
use context_server::{ContextServerSettings, ServerCommand, ServerConfig};
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::*};
@@ -78,11 +77,11 @@ impl AddContextServerModal {
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, _| {
update_settings_file::<ContextServerSettings>(fs.clone(), cx, |settings, _| {
settings.context_servers.insert(
name.into(),
ContextServerConfiguration {
command: Some(ContextServerCommand {
ServerConfig {
command: Some(ServerCommand {
path,
args,
env: None,
@@ -147,50 +146,47 @@ impl Render for AddContextServerModal {
),
)
.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.))),
ModalFooter::new()
.start_slot(
Button::new("cancel", "Cancel")
.key_binding(
KeyBinding::for_action_in(
&menu::Cancel,
&focus_handle,
window,
cx,
)
.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(|kb| kb.size(rems_from_px(12.))),
)
.on_click(cx.listener(|this, _event, _window, cx| {
this.cancel(&menu::Cancel, cx)
})),
)
.end_slot(
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(|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)
})),
),
),
.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

@@ -2,23 +2,25 @@ mod profile_modal_header;
use std::sync::Arc;
use agent_settings::{AgentProfileId, AgentSettings, builtin_profiles};
use assistant_settings::{AgentProfile, AgentProfileId, AssistantSettings};
use assistant_tool::ToolWorkingSet;
use convert_case::{Case, Casing as _};
use editor::Editor;
use fs::Fs;
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription, prelude::*};
use settings::Settings as _;
use gpui::{
DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription, WeakEntity,
prelude::*,
};
use settings::{Settings as _, update_settings_file};
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::agent_profile::AgentProfile;
use crate::{AgentPanel, ManageProfiles};
use super::tool_picker::ToolPickerMode;
use crate::assistant_configuration::manage_profiles_modal::profile_modal_header::ProfileModalHeader;
use crate::assistant_configuration::tool_picker::{ToolPicker, ToolPickerDelegate};
use crate::{AssistantPanel, ManageProfiles, ThreadStore};
enum Mode {
ChooseProfile(ChooseProfileMode),
@@ -29,39 +31,26 @@ enum Mode {
tool_picker: Entity<ToolPicker>,
_subscription: Subscription,
},
ConfigureMcps {
profile_id: AgentProfileId,
tool_picker: Entity<ToolPicker>,
_subscription: Subscription,
},
}
impl Mode {
pub fn choose_profile(_window: &mut Window, cx: &mut Context<ManageProfilesModal>) -> Self {
let settings = AgentSettings::get_global(cx);
let settings = AssistantSettings::get_global(cx);
let mut builtin_profiles = Vec::new();
let mut custom_profiles = Vec::new();
let mut profiles = settings.profiles.clone();
profiles.sort_unstable_by(|_, a, _, b| a.name.cmp(&b.name));
for (profile_id, profile) in settings.profiles.iter() {
let entry = ProfileEntry {
id: profile_id.clone(),
name: profile.name.clone(),
let profiles = profiles
.into_iter()
.map(|(id, profile)| ProfileEntry {
id,
name: profile.name,
navigation: NavigableEntry::focusable(cx),
};
if builtin_profiles::is_builtin(profile_id) {
builtin_profiles.push(entry);
} else {
custom_profiles.push(entry);
}
}
builtin_profiles.sort_unstable_by(|a, b| a.name.cmp(&b.name));
custom_profiles.sort_unstable_by(|a, b| a.name.cmp(&b.name));
})
.collect::<Vec<_>>();
Self::ChooseProfile(ChooseProfileMode {
builtin_profiles,
custom_profiles,
profiles,
add_new_profile: NavigableEntry::focusable(cx),
})
}
@@ -76,8 +65,7 @@ struct ProfileEntry {
#[derive(Clone)]
pub struct ChooseProfileMode {
builtin_profiles: Vec<ProfileEntry>,
custom_profiles: Vec<ProfileEntry>,
profiles: Vec<ProfileEntry>,
add_new_profile: NavigableEntry,
}
@@ -86,8 +74,6 @@ pub struct ViewProfileMode {
profile_id: AgentProfileId,
fork_profile: NavigableEntry,
configure_tools: NavigableEntry,
configure_mcps: NavigableEntry,
cancel_item: NavigableEntry,
}
#[derive(Clone)]
@@ -99,6 +85,7 @@ pub struct NewProfileMode {
pub struct ManageProfilesModal {
fs: Arc<dyn Fs>,
tools: Entity<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
focus_handle: FocusHandle,
mode: Mode,
}
@@ -110,15 +97,16 @@ impl ManageProfilesModal {
_cx: &mut Context<Workspace>,
) {
workspace.register_action(|workspace, action: &ManageProfiles, window, cx| {
if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
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, window, cx);
let mut this = Self::new(fs, tools, thread_store, window, cx);
if let Some(profile_id) = action.customize_tools.clone() {
this.configure_builtin_tools(profile_id, window, cx);
this.configure_tools(profile_id, window, cx);
}
this
@@ -130,6 +118,7 @@ impl ManageProfilesModal {
pub fn new(
fs: Arc<dyn Fs>,
tools: Entity<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@@ -138,6 +127,7 @@ impl ManageProfilesModal {
Self {
fs,
tools,
thread_store,
focus_handle,
mode: Mode::choose_profile(window, cx),
}
@@ -176,70 +166,31 @@ impl ManageProfilesModal {
profile_id,
fork_profile: NavigableEntry::focusable(cx),
configure_tools: NavigableEntry::focusable(cx),
configure_mcps: NavigableEntry::focusable(cx),
cancel_item: NavigableEntry::focusable(cx),
});
self.focus_handle(cx).focus(window);
}
fn configure_mcp_tools(
fn configure_tools(
&mut self,
profile_id: AgentProfileId,
window: &mut Window,
cx: &mut Context<Self>,
) {
let settings = AgentSettings::get_global(cx);
let settings = AssistantSettings::get_global(cx);
let Some(profile) = settings.profiles.get(&profile_id).cloned() else {
return;
};
let tool_picker = cx.new(|cx| {
let delegate = ToolPickerDelegate::new(
ToolPickerMode::McpTools,
self.fs.clone(),
self.tools.clone(),
self.thread_store.clone(),
profile_id.clone(),
profile,
cx,
);
ToolPicker::mcp_tools(delegate, window, cx)
});
let dismiss_subscription = cx.subscribe_in(&tool_picker, window, {
let profile_id = profile_id.clone();
move |this, _tool_picker, _: &DismissEvent, window, cx| {
this.view_profile(profile_id.clone(), window, cx);
}
});
self.mode = Mode::ConfigureMcps {
profile_id,
tool_picker,
_subscription: dismiss_subscription,
};
self.focus_handle(cx).focus(window);
}
fn configure_builtin_tools(
&mut self,
profile_id: AgentProfileId,
window: &mut Window,
cx: &mut Context<Self>,
) {
let settings = AgentSettings::get_global(cx);
let Some(profile) = settings.profiles.get(&profile_id).cloned() else {
return;
};
let tool_picker = cx.new(|cx| {
let delegate = ToolPickerDelegate::new(
ToolPickerMode::BuiltinTools,
self.fs.clone(),
self.tools.clone(),
profile_id.clone(),
profile,
cx,
);
ToolPicker::builtin_tools(delegate, window, cx)
ToolPicker::new(delegate, window, cx)
});
let dismiss_subscription = cx.subscribe_in(&tool_picker, window, {
let profile_id = profile_id.clone();
@@ -260,15 +211,36 @@ impl ManageProfilesModal {
match &self.mode {
Mode::ChooseProfile { .. } => {}
Mode::NewProfile(mode) => {
let name = mode.name_editor.read(cx).text(cx);
let settings = AssistantSettings::get_global(cx);
let profile_id =
AgentProfile::create(name, mode.base_profile_id.clone(), self.fs.clone(), 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);
self.view_profile(profile_id, window, cx);
}
Mode::ViewProfile(_) => {}
Mode::ConfigureTools { .. } => {}
Mode::ConfigureMcps { .. } => {}
}
}
@@ -285,14 +257,22 @@ impl ManageProfilesModal {
}
}
Mode::ViewProfile(_) => self.choose_profile(window, cx),
Mode::ConfigureTools { profile_id, .. } => {
self.view_profile(profile_id.clone(), window, cx)
}
Mode::ConfigureMcps { profile_id, .. } => {
self.view_profile(profile_id.clone(), window, cx)
}
Mode::ConfigureTools { .. } => {}
}
}
fn create_profile(
&self,
profile_id: AgentProfileId,
profile: AgentProfile,
cx: &mut Context<Self>,
) {
update_settings_file::<AssistantSettings>(self.fs.clone(), cx, {
move |settings, _cx| {
settings.create_profile(profile_id, profile).log_err();
}
});
}
}
impl ModalView for ManageProfilesModal {}
@@ -304,7 +284,6 @@ impl Focusable for ManageProfilesModal {
Mode::NewProfile(mode) => mode.name_editor.focus_handle(cx),
Mode::ViewProfile(_) => self.focus_handle.clone(),
Mode::ConfigureTools { tool_picker, .. } => tool_picker.focus_handle(cx),
Mode::ConfigureMcps { tool_picker, .. } => tool_picker.focus_handle(cx),
}
}
}
@@ -312,51 +291,6 @@ impl Focusable for ManageProfilesModal {
impl EventEmitter<DismissEvent> for ManageProfilesModal {}
impl ManageProfilesModal {
fn render_profile(
&self,
profile: &ProfileEntry,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement + use<> {
div()
.id(SharedString::from(format!("profile-{}", profile.id)))
.track_focus(&profile.navigation.focus_handle)
.on_action({
let profile_id = profile.id.clone();
cx.listener(move |this, _: &menu::Confirm, window, cx| {
this.view_profile(profile_id.clone(), window, cx);
})
})
.child(
ListItem::new(SharedString::from(format!("profile-{}", profile.id)))
.toggle_state(profile.navigation.focus_handle.contains_focused(window, cx))
.inset(true)
.spacing(ListItemSpacing::Sparse)
.child(Label::new(profile.name.clone()))
.end_slot(
h_flex()
.gap_1()
.child(
Label::new("Customize")
.size(LabelSize::Small)
.color(Color::Muted),
)
.children(KeyBinding::for_action_in(
&menu::Confirm,
&self.focus_handle,
window,
cx,
)),
)
.on_click({
let profile_id = profile.id.clone();
cx.listener(move |this, _, window, cx| {
this.view_profile(profile_id.clone(), window, cx);
})
}),
)
}
fn render_choose_profile(
&mut self,
mode: ChooseProfileMode,
@@ -367,31 +301,57 @@ impl ManageProfilesModal {
div()
.track_focus(&self.focus_handle(cx))
.size_full()
.child(ProfileModalHeader::new("Agent Profiles", None))
.child(ProfileModalHeader::new(
"Agent Profiles",
IconName::ZedAssistant,
))
.child(
v_flex()
.pb_1()
.child(ListSeparator)
.children(
mode.builtin_profiles
.iter()
.map(|profile| self.render_profile(profile, window, cx)),
)
.when(!mode.custom_profiles.is_empty(), |this| {
this.child(ListSeparator)
.children(mode.profiles.iter().map(|profile| {
div()
.id(SharedString::from(format!("profile-{}", profile.id)))
.track_focus(&profile.navigation.focus_handle)
.on_action({
let profile_id = profile.id.clone();
cx.listener(move |this, _: &menu::Confirm, window, cx| {
this.view_profile(profile_id.clone(), window, cx);
})
})
.child(
div().pl_2().pb_1().child(
Label::new("Custom Profiles")
.size(LabelSize::Small)
.color(Color::Muted),
),
ListItem::new(SharedString::from(format!(
"profile-{}",
profile.id
)))
.toggle_state(
profile
.navigation
.focus_handle
.contains_focused(window, cx),
)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.child(Label::new(profile.name.clone()))
.end_slot(
h_flex()
.gap_1()
.child(Label::new("Customize").size(LabelSize::Small))
.children(KeyBinding::for_action_in(
&menu::Confirm,
&self.focus_handle,
window,
cx,
)),
)
.on_click({
let profile_id = profile.id.clone();
cx.listener(move |this, _, window, cx| {
this.view_profile(profile_id.clone(), window, cx);
})
}),
)
.children(
mode.custom_profiles
.iter()
.map(|profile| self.render_profile(profile, window, cx)),
)
})
}))
.child(ListSeparator)
.child(
div()
@@ -422,10 +382,7 @@ impl ManageProfilesModal {
.into_any_element(),
)
.map(|mut navigable| {
for profile in mode.builtin_profiles {
navigable = navigable.entry(profile.navigation);
}
for profile in mode.custom_profiles {
for profile in mode.profiles {
navigable = navigable.entry(profile.navigation);
}
@@ -440,7 +397,7 @@ impl ManageProfilesModal {
_window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
let settings = AgentSettings::get_global(cx);
let settings = AssistantSettings::get_global(cx);
let base_profile_name = mode.base_profile_id.as_ref().map(|base_profile_id| {
settings
@@ -454,14 +411,11 @@ impl ManageProfilesModal {
.id("new-profile")
.track_focus(&self.focus_handle(cx))
.child(ProfileModalHeader::new(
match &base_profile_name {
match base_profile_name {
Some(base_profile) => format!("Fork {base_profile}"),
None => "New Profile".into(),
},
match base_profile_name {
Some(_) => Some(IconName::Scissors),
None => Some(IconName::Plus),
},
IconName::Plus,
))
.child(ListSeparator)
.child(h_flex().p_2().child(mode.name_editor.clone()))
@@ -473,7 +427,7 @@ impl ManageProfilesModal {
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
let settings = AgentSettings::get_global(cx);
let settings = AssistantSettings::get_global(cx);
let profile_name = settings
.profiles
@@ -481,17 +435,14 @@ impl ManageProfilesModal {
.map(|profile| profile.name.clone())
.unwrap_or_else(|| "Unknown".into());
let icon = match mode.profile_id.as_str() {
"write" => IconName::Pencil,
"ask" => IconName::MessageBubbles,
_ => IconName::UserRoundPen,
};
Navigable::new(
div()
.track_focus(&self.focus_handle(cx))
.size_full()
.child(ProfileModalHeader::new(profile_name, Some(icon)))
.child(ProfileModalHeader::new(
profile_name,
IconName::ZedAssistant,
))
.child(
v_flex()
.pb_1()
@@ -515,11 +466,7 @@ impl ManageProfilesModal {
)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.start_slot(
Icon::new(IconName::Scissors)
.size(IconSize::Small)
.color(Color::Muted),
)
.start_slot(Icon::new(IconName::GitBranch))
.child(Label::new("Fork Profile"))
.on_click({
let profile_id = mode.profile_id.clone();
@@ -535,20 +482,16 @@ impl ManageProfilesModal {
)
.child(
div()
.id("configure-builtin-tools")
.id("configure-tools")
.track_focus(&mode.configure_tools.focus_handle)
.on_action({
let profile_id = mode.profile_id.clone();
cx.listener(move |this, _: &menu::Confirm, window, cx| {
this.configure_builtin_tools(
profile_id.clone(),
window,
cx,
);
this.configure_tools(profile_id.clone(), window, cx);
})
})
.child(
ListItem::new("configure-builtin-tools-item")
ListItem::new("configure-tools")
.toggle_state(
mode.configure_tools
.focus_handle
@@ -556,16 +499,12 @@ impl ManageProfilesModal {
)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.start_slot(
Icon::new(IconName::Settings)
.size(IconSize::Small)
.color(Color::Muted),
)
.child(Label::new("Configure Built-in Tools"))
.start_slot(Icon::new(IconName::Cog))
.child(Label::new("Configure Tools"))
.on_click({
let profile_id = mode.profile_id.clone();
cx.listener(move |this, _, window, cx| {
this.configure_builtin_tools(
this.configure_tools(
profile_id.clone(),
window,
cx,
@@ -573,137 +512,18 @@ impl ManageProfilesModal {
})
}),
),
)
.child(
div()
.id("configure-mcps")
.track_focus(&mode.configure_mcps.focus_handle)
.on_action({
let profile_id = mode.profile_id.clone();
cx.listener(move |this, _: &menu::Confirm, window, cx| {
this.configure_mcp_tools(profile_id.clone(), window, cx);
})
})
.child(
ListItem::new("configure-mcp-tools")
.toggle_state(
mode.configure_mcps
.focus_handle
.contains_focused(window, cx),
)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.start_slot(
Icon::new(IconName::Hammer)
.size(IconSize::Small)
.color(Color::Muted),
)
.child(Label::new("Configure MCP Tools"))
.on_click({
let profile_id = mode.profile_id.clone();
cx.listener(move |this, _, window, cx| {
this.configure_mcp_tools(
profile_id.clone(),
window,
cx,
);
})
}),
),
)
.child(ListSeparator)
.child(
div()
.id("cancel-item")
.track_focus(&mode.cancel_item.focus_handle)
.on_action({
cx.listener(move |this, _: &menu::Confirm, window, cx| {
this.cancel(window, cx);
})
})
.child(
ListItem::new("cancel-item")
.toggle_state(
mode.cancel_item
.focus_handle
.contains_focused(window, cx),
)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.start_slot(
Icon::new(IconName::ArrowLeft)
.size(IconSize::Small)
.color(Color::Muted),
)
.child(Label::new("Go Back"))
.end_slot(
div().children(
KeyBinding::for_action_in(
&menu::Cancel,
&self.focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
),
)
.on_click({
cx.listener(move |this, _, window, cx| {
this.cancel(window, cx);
})
}),
),
),
)
.into_any_element(),
)
.entry(mode.fork_profile)
.entry(mode.configure_tools)
.entry(mode.configure_mcps)
.entry(mode.cancel_item)
}
}
impl Render for ManageProfilesModal {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let settings = AgentSettings::get_global(cx);
let go_back_item = div()
.id("cancel-item")
.track_focus(&self.focus_handle)
.on_action({
cx.listener(move |this, _: &menu::Confirm, window, cx| {
this.cancel(window, cx);
})
})
.child(
ListItem::new("cancel-item")
.toggle_state(self.focus_handle.contains_focused(window, cx))
.inset(true)
.spacing(ListItemSpacing::Sparse)
.start_slot(
Icon::new(IconName::ArrowLeft)
.size(IconSize::Small)
.color(Color::Muted),
)
.child(Label::new("Go Back"))
.end_slot(
div().children(
KeyBinding::for_action_in(
&menu::Cancel,
&self.focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
),
)
.on_click({
cx.listener(move |this, _, window, cx| {
this.cancel(window, cx);
})
}),
);
let settings = AssistantSettings::get_global(cx);
div()
.elevation_3(cx)
@@ -736,39 +556,13 @@ impl Render for ManageProfilesModal {
.map(|profile| profile.name.clone())
.unwrap_or_else(|| "Unknown".into());
v_flex()
.pb_1()
div()
.child(ProfileModalHeader::new(
format!("{profile_name} Configure Built-in Tools"),
Some(IconName::Cog),
format!("{profile_name}: Configure Tools"),
IconName::Cog,
))
.child(ListSeparator)
.child(tool_picker.clone())
.child(ListSeparator)
.child(go_back_item)
.into_any_element()
}
Mode::ConfigureMcps {
profile_id,
tool_picker,
..
} => {
let profile_name = settings
.profiles
.get(profile_id)
.map(|profile| profile.name.clone())
.unwrap_or_else(|| "Unknown".into());
v_flex()
.pb_1()
.child(ProfileModalHeader::new(
format!("{profile_name} — Configure MCP Tools"),
Some(IconName::Hammer),
))
.child(ListSeparator)
.child(tool_picker.clone())
.child(ListSeparator)
.child(go_back_item)
.into_any_element()
}
})

View File

@@ -0,0 +1,38 @@
use ui::prelude::*;
#[derive(IntoElement)]
pub struct ProfileModalHeader {
label: SharedString,
icon: IconName,
}
impl ProfileModalHeader {
pub fn new(label: impl Into<SharedString>, icon: IconName) -> Self {
Self {
label: label.into(),
icon,
}
}
}
impl RenderOnce for ProfileModalHeader {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
h_flex()
.w_full()
.px(DynamicSpacing::Base12.rems(cx))
.pt(DynamicSpacing::Base08.rems(cx))
.pb(DynamicSpacing::Base04.rems(cx))
.rounded_t_sm()
.gap_1p5()
.child(Icon::new(self.icon).size(IconSize::XSmall))
.child(
h_flex().gap_1().overflow_x_hidden().child(
div()
.max_w_96()
.overflow_x_hidden()
.text_ellipsis()
.child(Headline::new(self.label).size(HeadlineSize::XSmall)),
),
)
}
}

View File

@@ -0,0 +1,302 @@
use std::sync::Arc;
use assistant_settings::{
AgentProfile, AgentProfileContent, AgentProfileId, AssistantSettings, AssistantSettingsContent,
ContextServerPresetContent, VersionedAssistantSettingsContent,
};
use assistant_tool::{ToolSource, ToolWorkingSet};
use fs::Fs;
use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
use gpui::{App, Context, DismissEvent, Entity, EventEmitter, Focusable, Task, WeakEntity, Window};
use picker::{Picker, PickerDelegate};
use settings::{Settings as _, update_settings_file};
use ui::{HighlightedLabel, ListItem, ListItemSpacing, prelude::*};
use util::ResultExt as _;
use crate::ThreadStore;
pub struct ToolPicker {
picker: Entity<Picker<ToolPickerDelegate>>,
}
impl ToolPicker {
pub fn new(delegate: ToolPickerDelegate, window: &mut Window, cx: &mut Context<Self>) -> Self {
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false));
Self { picker }
}
}
impl EventEmitter<DismissEvent> for ToolPicker {}
impl Focusable for ToolPicker {
fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
self.picker.focus_handle(cx)
}
}
impl Render for ToolPicker {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
v_flex().w(rems(34.)).child(self.picker.clone())
}
}
#[derive(Debug, Clone)]
pub struct ToolEntry {
pub name: Arc<str>,
pub source: ToolSource,
}
pub struct ToolPickerDelegate {
tool_picker: WeakEntity<ToolPicker>,
thread_store: WeakEntity<ThreadStore>,
fs: Arc<dyn Fs>,
tools: Vec<ToolEntry>,
profile_id: AgentProfileId,
profile: AgentProfile,
matches: Vec<StringMatch>,
selected_index: usize,
}
impl ToolPickerDelegate {
pub fn new(
fs: Arc<dyn Fs>,
tool_set: Entity<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
profile_id: AgentProfileId,
profile: AgentProfile,
cx: &mut Context<ToolPicker>,
) -> Self {
let mut tool_entries = Vec::new();
for (source, tools) in tool_set.read(cx).tools_by_source(cx) {
tool_entries.extend(tools.into_iter().map(|tool| ToolEntry {
name: tool.name().into(),
source: source.clone(),
}));
}
Self {
tool_picker: cx.entity().downgrade(),
thread_store,
fs,
tools: tool_entries,
profile_id,
profile,
matches: Vec::new(),
selected_index: 0,
}
}
}
impl PickerDelegate for ToolPickerDelegate {
type ListItem = ListItem;
fn match_count(&self) -> usize {
self.matches.len()
}
fn selected_index(&self) -> usize {
self.selected_index
}
fn set_selected_index(
&mut self,
ix: usize,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) {
self.selected_index = ix;
}
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
"Search tools…".into()
}
fn update_matches(
&mut self,
query: String,
window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let background = cx.background_executor().clone();
let candidates = self
.tools
.iter()
.enumerate()
.map(|(id, profile)| StringMatchCandidate::new(id, profile.name.as_ref()))
.collect::<Vec<_>>();
cx.spawn_in(window, async move |this, cx| {
let matches = if query.is_empty() {
candidates
.into_iter()
.enumerate()
.map(|(index, candidate)| StringMatch {
candidate_id: index,
string: candidate.string,
positions: Vec::new(),
score: 0.,
})
.collect()
} else {
match_strings(
&candidates,
&query,
false,
100,
&Default::default(),
background,
)
.await
};
this.update(cx, |this, _cx| {
this.delegate.matches = matches;
this.delegate.selected_index = this
.delegate
.selected_index
.min(this.delegate.matches.len().saturating_sub(1));
})
.log_err();
})
}
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
if self.matches.is_empty() {
self.dismissed(window, cx);
return;
}
let candidate_id = self.matches[self.selected_index].candidate_id;
let tool = &self.tools[candidate_id];
let is_enabled = match &tool.source {
ToolSource::Native => {
let is_enabled = self.profile.tools.entry(tool.name.clone()).or_default();
*is_enabled = !*is_enabled;
*is_enabled
}
ToolSource::ContextServer { id } => {
let preset = self
.profile
.context_servers
.entry(id.clone().into())
.or_default();
let is_enabled = preset.tools.entry(tool.name.clone()).or_default();
*is_enabled = !*is_enabled;
*is_enabled
}
};
let active_profile_id = &AssistantSettings::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::<AssistantSettings>(self.fs.clone(), cx, {
let profile_id = self.profile_id.clone();
let default_profile = self.profile.clone();
let tool = tool.clone();
move |settings, _cx| match settings {
AssistantSettingsContent::Versioned(boxed) => {
if let VersionedAssistantSettingsContent::V2(ref mut settings) = **boxed {
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(),
});
match tool.source {
ToolSource::Native => {
*profile.tools.entry(tool.name).or_default() = is_enabled;
}
ToolSource::ContextServer { id } => {
let preset = profile
.context_servers
.entry(id.clone().into())
.or_default();
*preset.tools.entry(tool.name.clone()).or_default() = is_enabled;
}
}
}
}
_ => {}
}
});
}
fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
self.tool_picker
.update(cx, |_this, cx| cx.emit(DismissEvent))
.log_err();
}
fn render_match(
&self,
ix: usize,
selected: bool,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) -> Option<Self::ListItem> {
let tool_match = &self.matches[ix];
let tool = &self.tools[tool_match.candidate_id];
let is_enabled = match &tool.source {
ToolSource::Native => self.profile.tools.get(&tool.name).copied().unwrap_or(false),
ToolSource::ContextServer { id } => self
.profile
.context_servers
.get(id.as_ref())
.and_then(|preset| preset.tools.get(&tool.name))
.copied()
.unwrap_or(self.profile.enable_all_context_servers),
};
Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.child(
h_flex()
.gap_2()
.child(HighlightedLabel::new(
tool_match.string.clone(),
tool_match.positions.clone(),
))
.map(|parent| match &tool.source {
ToolSource::Native => parent,
ToolSource::ContextServer { id } => parent
.child(Label::new(id).size(LabelSize::XSmall).color(Color::Muted)),
}),
)
.end_slot::<Icon>(is_enabled.then(|| {
Icon::new(IconName::Check)
.size(IconSize::Small)
.color(Color::Success)
})),
)
}
}

View File

@@ -0,0 +1,123 @@
use assistant_settings::AssistantSettings;
use fs::Fs;
use gpui::{Entity, FocusHandle, SharedString};
use language_model_selector::{
LanguageModelSelector, LanguageModelSelectorPopoverMenu, ToggleModelSelector,
};
use settings::update_settings_file;
use std::sync::Arc;
use ui::{ButtonLike, PopoverMenuHandle, Tooltip, prelude::*};
pub use language_model_selector::ModelType;
pub struct AssistantModelSelector {
selector: Entity<LanguageModelSelector>,
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
focus_handle: FocusHandle,
}
impl AssistantModelSelector {
pub(crate) fn new(
fs: Arc<dyn Fs>,
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
focus_handle: FocusHandle,
model_type: ModelType,
window: &mut Window,
cx: &mut App,
) -> Self {
Self {
selector: cx.new(|cx| {
let fs = fs.clone();
LanguageModelSelector::new(
move |model, cx| {
let provider = model.provider_id().0.to_string();
let model_id = model.id().0.to_string();
match model_type {
ModelType::Default => {
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _cx| {
settings.set_model(model.clone());
},
);
}
ModelType::InlineAssistant => {
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _cx| {
settings.set_inline_assistant_model(
provider.clone(),
model_id.clone(),
);
},
);
}
}
},
model_type,
window,
cx,
)
}),
menu_handle,
focus_handle,
}
}
pub fn toggle(&self, window: &mut Window, cx: &mut Context<Self>) {
self.menu_handle.toggle(window, cx);
}
}
impl Render for AssistantModelSelector {
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).active_model(cx);
let (model_name, model_icon) = match model {
Some(model) => (model.model.name().0, Some(model.provider.icon())),
_ => (SharedString::from("No model selected"), None),
};
LanguageModelSelectorPopoverMenu::new(
self.selector.clone(),
ButtonLike::new("active-model")
.style(ButtonStyle::Subtle)
.child(
h_flex()
.gap_0p5()
.children(
model_icon.map(|icon| {
Icon::new(icon).color(Color::Muted).size(IconSize::Small)
}),
)
.child(
Label::new(model_name)
.size(LabelSize::Small)
.color(Color::Muted)
.ml_1(),
)
.child(
Icon::new(IconName::ChevronDown)
.color(Color::Muted)
.size(IconSize::XSmall),
),
),
move |window, cx| {
Tooltip::for_action_in(
"Change Model",
&ToggleModelSelector,
&focus_handle,
window,
cx,
)
},
gpui::Corner::BottomRight,
)
.with_handle(self.menu_handle.clone())
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,7 @@
use crate::context::ContextLoadResult;
use crate::inline_prompt_editor::CodegenStatus;
use crate::{context::load_context, context_store::ContextStore};
use agent_settings::AgentSettings;
use anyhow::{Context as _, Result};
use anyhow::Result;
use client::telemetry::Telemetry;
use collections::HashSet;
use editor::{Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint};
@@ -34,7 +33,6 @@ use std::{
};
use streaming_diff::{CharOperation, LineDiff, LineOperation, StreamingDiff};
use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase};
use zed_llm_client::CompletionIntent;
pub struct BufferCodegen {
alternatives: Vec<Entity<CodegenAlternative>>,
@@ -385,7 +383,7 @@ impl CodegenAlternative {
if user_prompt.trim().to_lowercase() == "delete" {
async { Ok(LanguageModelTextStream::default()) }.boxed_local()
} else {
let request = self.build_request(&model, user_prompt, cx)?;
let request = self.build_request(user_prompt, cx)?;
cx.spawn(async move |_, cx| model.stream_completion_text(request.await, &cx).await)
.boxed_local()
};
@@ -395,7 +393,6 @@ impl CodegenAlternative {
fn build_request(
&self,
model: &Arc<dyn LanguageModel>,
user_prompt: String,
cx: &mut App,
) -> Result<Task<LanguageModelRequest>> {
@@ -420,16 +417,16 @@ impl CodegenAlternative {
if start_buffer.remote_id() == end_buffer.remote_id() {
(start_buffer.clone(), start_buffer_offset..end_buffer_offset)
} else {
anyhow::bail!("invalid transformation range");
return Err(anyhow::anyhow!("invalid transformation range"));
}
} else {
anyhow::bail!("invalid transformation range");
return Err(anyhow::anyhow!("invalid transformation range"));
};
let prompt = self
.builder
.generate_inline_transformation_prompt(user_prompt, language_name, buffer, range)
.context("generating content prompt")?;
.map_err(|e| anyhow::anyhow!("Failed to generate content prompt: {}", e))?;
let context_task = self.context_store.as_ref().map(|context_store| {
if let Some(project) = self.project.upgrade() {
@@ -444,8 +441,6 @@ impl CodegenAlternative {
}
});
let temperature = AgentSettings::temperature_for_model(&model, cx);
Ok(cx.spawn(async move |_cx| {
let mut request_message = LanguageModelRequestMessage {
role: Role::User,
@@ -465,12 +460,10 @@ impl CodegenAlternative {
LanguageModelRequest {
thread_id: None,
prompt_id: None,
intent: Some(CompletionIntent::InlineAssist),
mode: None,
tools: Vec::new(),
tool_choice: None,
stop: Vec::new(),
temperature,
temperature: None,
messages: vec![request_message],
}
}))
@@ -511,7 +504,7 @@ impl CodegenAlternative {
}
}
let http_client = cx.http_client();
let http_client = cx.http_client().clone();
let telemetry = self.telemetry.clone();
let language_name = {
let multibuffer = self.buffer.read(cx);
@@ -779,7 +772,7 @@ impl CodegenAlternative {
cx: &mut Context<CodegenAlternative>,
) {
let transaction = self.buffer.update(cx, |buffer, cx| {
// Avoid grouping agent edits with user edits.
// Avoid grouping assistant edits with user edits.
buffer.finalize_last_transaction(cx);
buffer.start_transaction(cx);
buffer.edit(edits, None, cx);
@@ -788,7 +781,7 @@ impl CodegenAlternative {
if let Some(transaction) = transaction {
if let Some(first_transaction) = self.transformation_transaction_id {
// Group all agent edits into the first transaction.
// Group all assistant edits into the first transaction.
self.buffer.update(cx, |buffer, cx| {
buffer.merge_transactions(transaction, first_transaction, cx)
});
@@ -1102,7 +1095,9 @@ mod tests {
#[gpui::test(iterations = 10)]
async fn test_transform_autoindent(cx: &mut TestAppContext, mut rng: StdRng) {
init_test(cx);
cx.set_global(cx.update(SettingsStore::test));
cx.update(language_model::LanguageModelRegistry::test);
cx.update(language_settings::init);
let text = indoc! {"
fn main() {
@@ -1172,7 +1167,8 @@ mod tests {
cx: &mut TestAppContext,
mut rng: StdRng,
) {
init_test(cx);
cx.set_global(cx.update(SettingsStore::test));
cx.update(language_settings::init);
let text = indoc! {"
fn main() {
@@ -1241,7 +1237,9 @@ mod tests {
cx: &mut TestAppContext,
mut rng: StdRng,
) {
init_test(cx);
cx.update(LanguageModelRegistry::test);
cx.set_global(cx.update(SettingsStore::test));
cx.update(language_settings::init);
let text = concat!(
"fn main() {\n",
@@ -1307,7 +1305,9 @@ mod tests {
#[gpui::test(iterations = 10)]
async fn test_autoindent_respects_tabs_in_selection(cx: &mut TestAppContext) {
init_test(cx);
cx.update(LanguageModelRegistry::test);
cx.set_global(cx.update(SettingsStore::test));
cx.update(language_settings::init);
let text = indoc! {"
func main() {
@@ -1367,7 +1367,9 @@ mod tests {
#[gpui::test]
async fn test_inactive_codegen_alternative(cx: &mut TestAppContext) {
init_test(cx);
cx.update(LanguageModelRegistry::test);
cx.set_global(cx.update(SettingsStore::test));
cx.update(language_settings::init);
let text = indoc! {"
fn main() {
@@ -1471,13 +1473,6 @@ mod tests {
}
}
fn init_test(cx: &mut TestAppContext) {
cx.update(LanguageModelRegistry::test);
cx.set_global(cx.update(SettingsStore::test));
cx.update(Project::init_settings);
cx.update(language_settings::init);
}
fn simulate_response_stream(
codegen: Entity<CodegenAlternative>,
cx: &mut TestAppContext,

View File

@@ -3,26 +3,21 @@ use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::{ops::Range, path::Path, sync::Arc};
use assistant_context_editor::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 language::{Buffer, ParseStatus};
use gpui::{App, AppContext as _, Entity, SharedString, Task};
use language::Buffer;
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 text::{Anchor, OffsetRangeExt as _};
use ui::{Context, ElementId, IconName};
use ui::{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;
@@ -34,7 +29,6 @@ pub enum ContextKind {
Selection,
FetchedUrl,
Thread,
TextThread,
Rules,
Image,
}
@@ -48,7 +42,6 @@ impl ContextKind {
ContextKind::Selection => IconName::Context,
ContextKind::FetchedUrl => IconName::Globe,
ContextKind::Thread => IconName::MessageBubbles,
ContextKind::TextThread => IconName::MessageBubbles,
ContextKind::Rules => RULES_ICON,
ContextKind::Image => IconName::Image,
}
@@ -68,13 +61,12 @@ pub enum AgentContextHandle {
Selection(SelectionContextHandle),
FetchedUrl(FetchedUrlContext),
Thread(ThreadContextHandle),
TextThread(TextThreadContextHandle),
Rules(RulesContextHandle),
Image(ImageContext),
}
impl AgentContextHandle {
pub fn id(&self) -> ContextId {
fn id(&self) -> ContextId {
match self {
Self::File(context) => context.context_id,
Self::Directory(context) => context.context_id,
@@ -82,7 +74,6 @@ impl AgentContextHandle {
Self::Selection(context) => context.context_id,
Self::FetchedUrl(context) => context.context_id,
Self::Thread(context) => context.context_id,
Self::TextThread(context) => context.context_id,
Self::Rules(context) => context.context_id,
Self::Image(context) => context.context_id,
}
@@ -103,7 +94,6 @@ pub enum AgentContext {
Selection(SelectionContext),
FetchedUrl(FetchedUrlContext),
Thread(ThreadContext),
TextThread(TextThreadContext),
Rules(RulesContext),
Image(ImageContext),
}
@@ -121,9 +111,6 @@ impl AgentContext {
}
AgentContext::FetchedUrl(context) => AgentContextHandle::FetchedUrl(context.clone()),
AgentContext::Thread(context) => AgentContextHandle::Thread(context.handle.clone()),
AgentContext::TextThread(context) => {
AgentContextHandle::TextThread(context.handle.clone())
}
AgentContext::Rules(context) => AgentContextHandle::Rules(context.handle.clone()),
AgentContext::Image(context) => AgentContextHandle::Image(context.clone()),
}
@@ -165,7 +152,6 @@ pub struct FileContext {
pub handle: FileContextHandle,
pub full_path: Arc<Path>,
pub text: SharedString,
pub is_outline: bool,
}
impl FileContextHandle {
@@ -191,51 +177,14 @@ impl FileContextHandle {
log::error!("file context missing path");
return Task::ready(None);
};
let full_path: Arc<Path> = file.full_path(cx).into();
let full_path = file.full_path(cx);
let rope = buffer_ref.as_rope().clone();
let buffer = self.buffer.clone();
cx.spawn(async move |cx| {
// For large files, use outline instead of full content
if rope.len() > outline::AUTO_OUTLINE_SIZE {
// Wait until the buffer has been fully parsed, so we can read its outline
if let Ok(mut parse_status) =
buffer.read_with(cx, |buffer, _| buffer.parse_status())
{
while *parse_status.borrow() != ParseStatus::Idle {
parse_status.changed().await.log_err();
}
if let Ok(snapshot) = buffer.read_with(cx, |buffer, _| buffer.snapshot()) {
if let Some(outline) = snapshot.outline(None) {
let items = outline
.items
.into_iter()
.map(|item| item.to_point(&snapshot));
if let Ok(outline_text) =
outline::render_outline(items, None, 0, usize::MAX).await
{
let context = AgentContext::File(FileContext {
handle: self,
full_path,
text: outline_text.into(),
is_outline: true,
});
return Some((context, vec![buffer]));
}
}
}
}
}
// Fallback to full content if we couldn't build an outline
// (or didn't need to because the file was small enough)
cx.background_spawn(async move {
let context = AgentContext::File(FileContext {
handle: self,
full_path,
full_path: full_path.into(),
text: rope.to_string().into(),
is_outline: false,
});
Some((context, vec![buffer]))
})
@@ -586,23 +535,19 @@ impl ThreadContextHandle {
}
pub fn title(&self, cx: &App) -> SharedString {
self.thread.read(cx).summary().or_default()
self.thread
.read(cx)
.summary()
.unwrap_or_else(|| "New thread".into())
}
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 title = self
.thread
.read_with(cx, |thread, _cx| thread.summary().or_default())
.ok()?;
let context = AgentContext::Thread(ThreadContext {
title,
text,
handle: self,
});
Some((context, vec![]))
})
let context = AgentContext::Thread(ThreadContext {
title: self.title(cx),
text: self.thread.read(cx).latest_detailed_summary_or_text(),
handle: self,
});
Task::ready(Some((context, vec![])))
}
}
@@ -613,54 +558,6 @@ impl Display for ThreadContext {
}
}
#[derive(Debug, Clone)]
pub struct TextThreadContextHandle {
pub context: Entity<AssistantContext>,
pub context_id: ContextId,
}
#[derive(Debug, Clone)]
pub struct TextThreadContext {
pub handle: TextThreadContextHandle,
pub title: SharedString,
pub text: SharedString,
}
impl TextThreadContextHandle {
// pub fn lookup_key() ->
pub fn eq_for_key(&self, other: &Self) -> bool {
self.context == other.context
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.context.hash(state)
}
pub fn title(&self, cx: &App) -> SharedString {
self.context.read(cx).summary().or_default()
}
fn load(self, cx: &App) -> Task<Option<(AgentContext, Vec<Entity<Buffer>>)>> {
let title = self.title(cx);
let text = self.context.read(cx).to_xml(cx);
let context = AgentContext::TextThread(TextThreadContext {
title,
text: text.into(),
handle: self,
});
Task::ready(Some((context, vec![])))
}
}
impl Display for TextThreadContext {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
// TODO: escape title?
write!(f, "<text_thread title=\"{}\">\n", self.title)?;
write!(f, "{}", self.text.trim())?;
write!(f, "\n</text_thread>")
}
}
#[derive(Debug, Clone)]
pub struct RulesContextHandle {
pub prompt_id: UserPromptId,
@@ -734,7 +631,6 @@ impl Display for RulesContext {
#[derive(Debug, Clone)]
pub struct ImageContext {
pub project_path: Option<ProjectPath>,
pub full_path: Option<Arc<Path>>,
pub original_image: Arc<gpui::Image>,
// TODO: handle this elsewhere and remove `ignore-interior-mutability` opt-out in clippy.toml
// needed due to a false positive of `clippy::mutable_key_type`.
@@ -745,34 +641,27 @@ pub struct ImageContext {
pub enum ImageStatus {
Loading,
Error,
Warning,
Ready,
}
impl ImageContext {
pub fn eq_for_key(&self, other: &Self) -> bool {
self.original_image.id() == other.original_image.id()
self.original_image.id == other.original_image.id
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.original_image.id().hash(state);
self.original_image.id.hash(state);
}
pub fn image(&self) -> Option<LanguageModelImage> {
self.image_task.clone().now_or_never().flatten()
}
pub fn status(&self, model: Option<&Arc<dyn language_model::LanguageModel>>) -> ImageStatus {
pub fn status(&self) -> ImageStatus {
match self.image_task.clone().now_or_never() {
None => ImageStatus::Loading,
Some(None) => ImageStatus::Error,
Some(Some(_)) => {
if model.is_some_and(|model| !model.supports_images()) {
ImageStatus::Warning
} else {
ImageStatus::Ready
}
}
Some(Some(_)) => ImageStatus::Ready,
}
}
@@ -833,20 +722,22 @@ pub fn load_context(
prompt_store: &Option<Entity<PromptStore>>,
cx: &mut App,
) -> Task<ContextLoadResult> {
let load_tasks: Vec<_> = contexts
.into_iter()
.map(|context| match context {
AgentContextHandle::File(context) => context.load(cx),
AgentContextHandle::Directory(context) => context.load(project.clone(), cx),
AgentContextHandle::Symbol(context) => context.load(cx),
AgentContextHandle::Selection(context) => context.load(cx),
AgentContextHandle::FetchedUrl(context) => context.load(),
AgentContextHandle::Thread(context) => context.load(cx),
AgentContextHandle::TextThread(context) => context.load(cx),
AgentContextHandle::Rules(context) => context.load(prompt_store, cx),
AgentContextHandle::Image(context) => context.load(cx),
})
.collect();
let mut load_tasks = Vec::new();
for context in contexts.iter().cloned() {
match context {
AgentContextHandle::File(context) => load_tasks.push(context.load(cx)),
AgentContextHandle::Directory(context) => {
load_tasks.push(context.load(project.clone(), cx))
}
AgentContextHandle::Symbol(context) => load_tasks.push(context.load(cx)),
AgentContextHandle::Selection(context) => load_tasks.push(context.load(cx)),
AgentContextHandle::FetchedUrl(context) => load_tasks.push(context.load()),
AgentContextHandle::Thread(context) => load_tasks.push(context.load(cx)),
AgentContextHandle::Rules(context) => load_tasks.push(context.load(prompt_store, cx)),
AgentContextHandle::Image(context) => load_tasks.push(context.load(cx)),
}
}
cx.background_spawn(async move {
let load_results = future::join_all(load_tasks).await;
@@ -868,7 +759,6 @@ pub fn load_context(
let mut selection_context = Vec::new();
let mut fetched_url_context = Vec::new();
let mut thread_context = Vec::new();
let mut text_thread_context = Vec::new();
let mut rules_context = Vec::new();
let mut images = Vec::new();
for context in &contexts {
@@ -879,21 +769,17 @@ pub fn load_context(
AgentContext::Selection(context) => selection_context.push(context),
AgentContext::FetchedUrl(context) => fetched_url_context.push(context),
AgentContext::Thread(context) => thread_context.push(context),
AgentContext::TextThread(context) => text_thread_context.push(context),
AgentContext::Rules(context) => rules_context.push(context),
AgentContext::Image(context) => images.extend(context.image()),
}
}
// Use empty text if there are no contexts that contribute to text (everything but image
// context).
if file_context.is_empty()
&& directory_context.is_empty()
&& symbol_context.is_empty()
&& selection_context.is_empty()
&& fetched_url_context.is_empty()
&& thread_context.is_empty()
&& text_thread_context.is_empty()
&& rules_context.is_empty()
{
return ContextLoadResult {
@@ -909,7 +795,7 @@ pub fn load_context(
text.push_str(
"\n<context>\n\
The following items were attached by the user. \
They are up-to-date and don't need to be re-read.\n\n",
You don't need to use other tools to read them.\n\n",
);
if !file_context.is_empty() {
@@ -966,15 +852,6 @@ pub fn load_context(
text.push_str("</conversation_threads>\n");
}
if !text_thread_context.is_empty() {
text.push_str("<text_threads>");
for context in text_thread_context {
text.push('\n');
let _ = writeln!(text, "{context}");
}
text.push_str("<text_threads>");
}
if !rules_context.is_empty() {
text.push_str(
"<user_rules>\n\
@@ -1091,11 +968,6 @@ impl PartialEq for AgentContextKey {
return context.eq_for_key(other_context);
}
}
AgentContextHandle::TextThread(context) => {
if let AgentContextHandle::TextThread(other_context) = &other.0 {
return context.eq_for_key(other_context);
}
}
}
false
}
@@ -1110,184 +982,8 @@ impl Hash for AgentContextKey {
AgentContextHandle::Selection(context) => context.hash_for_key(state),
AgentContextHandle::FetchedUrl(context) => context.hash_for_key(state),
AgentContextHandle::Thread(context) => context.hash_for_key(state),
AgentContextHandle::TextThread(context) => context.hash_for_key(state),
AgentContextHandle::Rules(context) => context.hash_for_key(state),
AgentContextHandle::Image(context) => context.hash_for_key(state),
}
}
}
#[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::*;
use gpui::TestAppContext;
use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use util::path;
fn init_test_settings(cx: &mut TestAppContext) {
cx.update(|cx| {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
language::init(cx);
Project::init_settings(cx);
});
}
// Helper to create a test project with test files
async fn create_test_project(
cx: &mut TestAppContext,
files: serde_json::Value,
) -> Entity<Project> {
let fs = FakeFs::new(cx.background_executor.clone());
fs.insert_tree(path!("/test"), files).await;
Project::test(fs, [path!("/test").as_ref()], cx).await
}
#[gpui::test]
async fn test_large_file_uses_outline(cx: &mut TestAppContext) {
init_test_settings(cx);
// Create a large file that exceeds AUTO_OUTLINE_SIZE
const LINE: &str = "Line with some text\n";
let large_content = LINE.repeat(2 * (outline::AUTO_OUTLINE_SIZE / LINE.len()));
let content_len = large_content.len();
assert!(content_len > outline::AUTO_OUTLINE_SIZE);
let file_context = file_context_for(large_content, cx).await;
assert!(
file_context.is_outline,
"Large file should use outline format"
);
assert!(
file_context.text.len() < content_len,
"Outline should be smaller than original content"
);
}
#[gpui::test]
async fn test_small_file_uses_full_content(cx: &mut TestAppContext) {
init_test_settings(cx);
let small_content = "This is a small file.\n";
let content_len = small_content.len();
assert!(content_len < outline::AUTO_OUTLINE_SIZE);
let file_context = file_context_for(small_content.to_string(), cx).await;
assert!(
!file_context.is_outline,
"Small files should not get an outline"
);
assert_eq!(file_context.text, small_content);
}
async fn file_context_for(content: String, cx: &mut TestAppContext) -> FileContext {
// Create a test project with the file
let project = create_test_project(
cx,
json!({
"file.txt": content,
}),
)
.await;
// Open the buffer
let buffer_path = project
.read_with(cx, |project, cx| project.find_project_path("file.txt", cx))
.unwrap();
let buffer = project
.update(cx, |project, cx| project.open_buffer(buffer_path, cx))
.await
.unwrap();
let context_handle = AgentContextHandle::File(FileContextHandle {
buffer: buffer.clone(),
context_id: ContextId::zero(),
});
cx.update(|cx| load_context(vec![context_handle], &project, &None, cx))
.await
.loaded_context
.contexts
.into_iter()
.find_map(|ctx| {
if let AgentContext::File(file_ctx) = ctx {
Some(file_ctx)
} else {
None
}
})
.expect("Should have found a file context")
}
}

View File

@@ -6,12 +6,12 @@ mod symbol_context_picker;
mod thread_context_picker;
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::sync::Arc;
use anyhow::{Result, anyhow};
pub use completion_provider::ContextPickerCompletionProvider;
use editor::display_map::{Crease, CreaseId, CreaseMetadata, FoldId};
use editor::display_map::{Crease, FoldId};
use editor::{Anchor, AnchorRangeExt as _, Editor, ExcerptId, FoldPlaceholder, ToOffset};
use fetch_context_picker::FetchContextPicker;
use file_context_picker::FileContextPicker;
@@ -22,25 +22,22 @@ use gpui::{
};
use language::Buffer;
use multi_buffer::MultiBufferRow;
use paths::contexts_dir;
use project::{Entry, ProjectPath};
use prompt_store::{PromptStore, UserPromptId};
use rules_context_picker::{RulesContextEntry, RulesContextPicker};
use symbol_context_picker::SymbolContextPicker;
use thread_context_picker::{
ThreadContextEntry, ThreadContextPicker, render_thread_context_entry, unordered_thread_entries,
};
use thread_context_picker::{ThreadContextEntry, ThreadContextPicker, render_thread_context_entry};
use ui::{
ButtonLike, ContextMenu, ContextMenuEntry, ContextMenuItem, Disclosure, TintColor, prelude::*,
};
use uuid::Uuid;
use workspace::{Workspace, notifications::NotifyResultExt};
use crate::AgentPanel;
use crate::AssistantPanel;
use crate::context::RULES_ICON;
use crate::context_store::ContextStore;
use crate::thread::ThreadId;
use crate::thread_store::{TextThreadStore, ThreadStore};
use crate::thread_store::ThreadStore;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ContextPickerEntry {
@@ -114,7 +111,7 @@ impl TryFrom<&str> for ContextPickerMode {
"symbol" => Ok(Self::Symbol),
"fetch" => Ok(Self::Fetch),
"thread" => Ok(Self::Thread),
"rule" => Ok(Self::Rules),
"rules" => Ok(Self::Rules),
_ => Err(format!("Invalid context picker mode: {}", value)),
}
}
@@ -127,7 +124,7 @@ impl ContextPickerMode {
Self::Symbol => "symbol",
Self::Fetch => "fetch",
Self::Thread => "thread",
Self::Rules => "rule",
Self::Rules => "rules",
}
}
@@ -167,7 +164,6 @@ pub(super) struct ContextPicker {
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
text_thread_store: Option<WeakEntity<TextThreadStore>>,
prompt_store: Option<Entity<PromptStore>>,
_subscriptions: Vec<Subscription>,
}
@@ -176,7 +172,6 @@ impl ContextPicker {
pub fn new(
workspace: WeakEntity<Workspace>,
thread_store: Option<WeakEntity<ThreadStore>>,
text_thread_store: Option<WeakEntity<TextThreadStore>>,
context_store: WeakEntity<ContextStore>,
window: &mut Window,
cx: &mut Context<Self>,
@@ -213,7 +208,6 @@ impl ContextPicker {
workspace,
context_store,
thread_store,
text_thread_store,
prompt_store,
_subscriptions: subscriptions,
}
@@ -273,7 +267,7 @@ impl ContextPicker {
context_picker.update(cx, |this, cx| this.select_entry(entry, window, cx))
})
}))
.keep_open_on_confirm(true)
.keep_open_on_confirm()
});
cx.subscribe(&menu, move |_, _, _: &DismissEvent, cx| {
@@ -346,15 +340,10 @@ impl ContextPicker {
}));
}
ContextPickerMode::Thread => {
if let Some((thread_store, text_thread_store)) = self
.thread_store
.as_ref()
.zip(self.text_thread_store.as_ref())
{
if let Some(thread_store) = self.thread_store.as_ref() {
self.mode = ContextPickerState::Thread(cx.new(|cx| {
ThreadContextPicker::new(
thread_store.clone(),
text_thread_store.clone(),
context_picker.clone(),
self.context_store.clone(),
window,
@@ -381,16 +370,6 @@ impl ContextPicker {
cx.focus_self(window);
}
pub fn select_first(&mut self, window: &mut Window, cx: &mut Context<Self>) {
match &self.mode {
ContextPickerState::Default(entity) => entity.update(cx, |entity, cx| {
entity.select_first(&Default::default(), window, cx)
}),
// Other variants already select their first entry on open automatically
_ => {}
}
}
fn recent_menu_item(
&self,
context_picker: Entity<ContextPicker>,
@@ -435,9 +414,9 @@ impl ContextPicker {
render_thread_context_entry(&view_thread, context_store.clone(), cx)
.into_any()
},
move |window, cx| {
move |_window, cx| {
context_picker.update(cx, |this, cx| {
this.add_recent_thread(thread.clone(), window, cx)
this.add_recent_thread(thread.clone(), cx)
.detach_and_log_err(cx);
})
},
@@ -468,54 +447,30 @@ impl ContextPicker {
fn add_recent_thread(
&self,
entry: ThreadContextEntry,
window: &mut Window,
thread: ThreadContextEntry,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
let Some(context_store) = self.context_store.upgrade() else {
return Task::ready(Err(anyhow!("context store not available")));
};
match entry {
ThreadContextEntry::Thread { id, .. } => {
let Some(thread_store) = self
.thread_store
.as_ref()
.and_then(|thread_store| thread_store.upgrade())
else {
return Task::ready(Err(anyhow!("thread store not available")));
};
let Some(thread_store) = self
.thread_store
.as_ref()
.and_then(|thread_store| thread_store.upgrade())
else {
return Task::ready(Err(anyhow!("thread store not available")));
};
let open_thread_task =
thread_store.update(cx, |this, cx| this.open_thread(&id, window, cx));
cx.spawn(async move |this, cx| {
let thread = open_thread_task.await?;
context_store.update(cx, |context_store, cx| {
context_store.add_thread(thread, true, cx);
})?;
this.update(cx, |_this, cx| cx.notify())
})
}
ThreadContextEntry::Context { path, .. } => {
let Some(text_thread_store) = self
.text_thread_store
.as_ref()
.and_then(|thread_store| thread_store.upgrade())
else {
return Task::ready(Err(anyhow!("text thread store not available")));
};
let open_thread_task = thread_store.update(cx, |this, cx| this.open_thread(&thread.id, cx));
cx.spawn(async move |this, cx| {
let thread = open_thread_task.await?;
context_store.update(cx, |context_store, cx| {
context_store.add_thread(thread, true, cx);
})?;
let task = text_thread_store
.update(cx, |this, cx| this.open_local_context(path.clone(), cx));
cx.spawn(async move |this, cx| {
let thread = task.await?;
context_store.update(cx, |context_store, cx| {
context_store.add_text_thread(thread, true, cx);
})?;
this.update(cx, |_this, cx| cx.notify())
})
}
}
this.update(cx, |_this, cx| cx.notify())
})
}
fn recent_entries(&self, cx: &mut App) -> Vec<RecentEntry> {
@@ -527,14 +482,7 @@ impl ContextPicker {
return vec![];
};
recent_context_picker_entries(
context_store,
self.thread_store.clone(),
self.text_thread_store.clone(),
workspace,
None,
cx,
)
recent_context_picker_entries(context_store, self.thread_store.clone(), workspace, cx)
}
fn notify_current_picker(&mut self, cx: &mut Context<Self>) {
@@ -629,14 +577,12 @@ fn available_context_picker_entries(
fn recent_context_picker_entries(
context_store: Entity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
text_thread_store: Option<WeakEntity<TextThreadStore>>,
workspace: Entity<Workspace>,
exclude_path: Option<ProjectPath>,
cx: &App,
) -> Vec<RecentEntry> {
let mut recent = Vec::with_capacity(6);
let mut current_files = context_store.read(cx).file_paths(cx);
current_files.extend(exclude_path);
let current_files = context_store.read(cx).file_paths(cx);
let workspace = workspace.read(cx);
let project = workspace.project().read(cx);
@@ -658,35 +604,25 @@ fn recent_context_picker_entries(
let current_threads = context_store.read(cx).thread_ids();
let active_thread_id = workspace
.panel::<AgentPanel>(cx)
.and_then(|panel| Some(panel.read(cx).active_thread()?.read(cx).id()));
if let Some((thread_store, text_thread_store)) = thread_store
.and_then(|store| store.upgrade())
.zip(text_thread_store.and_then(|store| store.upgrade()))
{
let mut threads = unordered_thread_entries(thread_store, text_thread_store, cx)
.filter(|(_, thread)| match thread {
ThreadContextEntry::Thread { id, .. } => {
Some(id) != active_thread_id && !current_threads.contains(id)
}
ThreadContextEntry::Context { .. } => true,
})
.collect::<Vec<_>>();
const RECENT_COUNT: usize = 2;
if threads.len() > RECENT_COUNT {
threads.select_nth_unstable_by_key(RECENT_COUNT - 1, |(updated_at, _)| {
std::cmp::Reverse(*updated_at)
});
threads.truncate(RECENT_COUNT);
}
threads.sort_unstable_by_key(|(updated_at, _)| std::cmp::Reverse(*updated_at));
.panel::<AssistantPanel>(cx)
.map(|panel| panel.read(cx).active_thread(cx).read(cx).id());
if let Some(thread_store) = thread_store.and_then(|thread_store| thread_store.upgrade()) {
recent.extend(
threads
thread_store
.read(cx)
.reverse_chronological_threads()
.into_iter()
.map(|(_, thread)| RecentEntry::Thread(thread)),
.filter(|thread| {
Some(&thread.id) != active_thread_id && !current_threads.contains(&thread.id)
})
.take(2)
.map(|thread| {
RecentEntry::Thread(ThreadContextEntry {
id: thread.id,
summary: thread.summary,
})
}),
);
}
@@ -739,20 +675,21 @@ fn selection_ranges(
})
}
pub(crate) fn insert_crease_for_mention(
pub(crate) fn insert_fold_for_mention(
excerpt_id: ExcerptId,
crease_start: text::Anchor,
content_len: usize,
crease_label: SharedString,
crease_icon_path: SharedString,
editor_entity: Entity<Editor>,
window: &mut Window,
cx: &mut App,
) -> Option<CreaseId> {
) {
editor_entity.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let start = snapshot.anchor_in_excerpt(excerpt_id, crease_start)?;
let Some(start) = snapshot.anchor_in_excerpt(excerpt_id, crease_start) else {
return;
};
let start = start.bias_right(&snapshot);
let end = snapshot.anchor_before(start.to_offset(&snapshot) + content_len);
@@ -764,11 +701,10 @@ pub(crate) fn insert_crease_for_mention(
editor_entity.downgrade(),
);
let ids = editor.insert_creases(vec![crease.clone()], cx);
editor.fold_creases(vec![crease], false, window, cx);
Some(ids[0])
})
editor.display_map.update(cx, |display_map, cx| {
display_map.fold(vec![crease], cx);
});
});
}
pub fn crease_for_mention(
@@ -778,20 +714,20 @@ pub fn crease_for_mention(
editor_entity: WeakEntity<Editor>,
) -> Crease<Anchor> {
let placeholder = FoldPlaceholder {
render: render_fold_icon_button(icon_path.clone(), label.clone(), editor_entity),
render: render_fold_icon_button(icon_path, label, editor_entity),
merge_adjacent: false,
..Default::default()
};
let render_trailer = move |_row, _unfold, _window: &mut Window, _cx: &mut App| Empty.into_any();
Crease::inline(
let crease = Crease::inline(
range,
placeholder.clone(),
fold_toggle("mention"),
render_trailer,
)
.with_metadata(CreaseMetadata { icon_path, label })
);
crease
}
fn render_fold_icon_button(
@@ -885,8 +821,7 @@ pub enum MentionLink {
Selection(ProjectPath, Range<usize>),
Fetch(String),
Thread(ThreadId),
TextThread(Arc<Path>),
Rule(UserPromptId),
Rules(UserPromptId),
}
impl MentionLink {
@@ -895,9 +830,7 @@ impl MentionLink {
const SELECTION: &str = "@selection";
const THREAD: &str = "@thread";
const FETCH: &str = "@fetch";
const RULE: &str = "@rule";
const TEXT_THREAD_URL_PREFIX: &str = "text-thread://";
const RULES: &str = "@rules";
const SEPARATOR: &str = ":";
@@ -907,7 +840,7 @@ impl MentionLink {
|| url.starts_with(Self::FETCH)
|| url.starts_with(Self::SELECTION)
|| url.starts_with(Self::THREAD)
|| url.starts_with(Self::RULE)
|| url.starts_with(Self::RULES)
}
pub fn for_file(file_name: &str, full_path: &str) -> String {
@@ -938,30 +871,15 @@ impl MentionLink {
}
pub fn for_thread(thread: &ThreadContextEntry) -> String {
match thread {
ThreadContextEntry::Thread { id, title } => {
format!("[@{}]({}:{})", title, Self::THREAD, id)
}
ThreadContextEntry::Context { path, title } => {
let filename = path.file_name().unwrap_or_default().to_string_lossy();
let escaped_filename = urlencoding::encode(&filename);
format!(
"[@{}]({}:{}{})",
title,
Self::THREAD,
Self::TEXT_THREAD_URL_PREFIX,
escaped_filename
)
}
}
format!("[@{}]({}:{})", thread.summary, Self::THREAD, thread.id)
}
pub fn for_fetch(url: &str) -> String {
format!("[@{}]({}:{})", url, Self::FETCH, url)
}
pub fn for_rule(rule: &RulesContextEntry) -> String {
format!("[@{}]({}:{})", rule.title, Self::RULE, rule.prompt_id.0)
pub fn for_rules(rules: &RulesContextEntry) -> String {
format!("[@{}]({}:{})", rules.title, Self::RULES, rules.prompt_id.0)
}
pub fn try_parse(link: &str, workspace: &Entity<Workspace>, cx: &App) -> Option<Self> {
@@ -1015,20 +933,13 @@ impl MentionLink {
Some(MentionLink::Selection(project_path, line_range))
}
Self::THREAD => {
if let Some(encoded_filename) = argument.strip_prefix(Self::TEXT_THREAD_URL_PREFIX)
{
let filename = urlencoding::decode(encoded_filename).ok()?;
let path = contexts_dir().join(filename.as_ref()).into();
Some(MentionLink::TextThread(path))
} else {
let thread_id = ThreadId::from(argument);
Some(MentionLink::Thread(thread_id))
}
let thread_id = ThreadId::from(argument);
Some(MentionLink::Thread(thread_id))
}
Self::FETCH => Some(MentionLink::Fetch(argument.to_string())),
Self::RULE => {
Self::RULES => {
let prompt_id = UserPromptId(Uuid::try_parse(argument).ok()?);
Some(MentionLink::Rule(prompt_id))
Some(MentionLink::Rules(prompt_id))
}
_ => None,
}

View File

@@ -1,5 +1,7 @@
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;
@@ -12,18 +14,16 @@ use http_client::HttpClientWithUrl;
use itertools::Itertools;
use language::{Buffer, CodeLabel, HighlightId};
use lsp::CompletionContext;
use project::{Completion, CompletionIntent, CompletionResponse, ProjectPath, Symbol, WorktreeId};
use project::{Completion, CompletionIntent, ProjectPath, Symbol, WorktreeId};
use prompt_store::PromptStore;
use rope::Point;
use text::{Anchor, OffsetRangeExt, ToPoint};
use ui::prelude::*;
use util::ResultExt as _;
use workspace::Workspace;
use crate::Thread;
use crate::context::{AgentContextHandle, AgentContextKey, ContextCreasesAddon, RULES_ICON};
use crate::context::RULES_ICON;
use crate::context_store::ContextStore;
use crate::thread_store::{TextThreadStore, ThreadStore};
use crate::thread_store::ThreadStore;
use super::fetch_context_picker::fetch_url_content;
use super::file_context_picker::{FileMatch, search_files};
@@ -70,7 +70,6 @@ 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>>,
workspace: Entity<Workspace>,
cx: &mut App,
) -> Task<Vec<Match>> {
@@ -100,18 +99,9 @@ fn search(
}
Some(ContextPickerMode::Thread) => {
if let Some((thread_store, context_store)) = thread_store
.as_ref()
.and_then(|t| t.upgrade())
.zip(text_thread_context_store.as_ref().and_then(|t| t.upgrade()))
{
let search_threads_task = search_threads(
query.clone(),
cancellation_flag.clone(),
thread_store,
context_store,
cx,
);
if let Some(thread_store) = thread_store.as_ref().and_then(|t| t.upgrade()) {
let search_threads_task =
search_threads(query.clone(), cancellation_flag.clone(), thread_store, cx);
cx.background_spawn(async move {
search_threads_task
.await
@@ -244,9 +234,7 @@ pub struct ContextPickerCompletionProvider {
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
text_thread_store: Option<WeakEntity<TextThreadStore>>,
editor: WeakEntity<Editor>,
excluded_buffer: Option<WeakEntity<Buffer>>,
}
impl ContextPickerCompletionProvider {
@@ -254,17 +242,13 @@ impl ContextPickerCompletionProvider {
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
text_thread_store: Option<WeakEntity<TextThreadStore>>,
editor: WeakEntity<Editor>,
exclude_buffer: Option<WeakEntity<Buffer>>,
) -> Self {
Self {
workspace,
context_store,
thread_store,
text_thread_store,
editor,
excluded_buffer: exclude_buffer,
}
}
@@ -320,16 +304,13 @@ impl ContextPickerCompletionProvider {
})
.collect::<Vec<_>>();
let new_text = format!(
"{} ",
selection_infos.iter().map(|(_, link, _)| link).join(" ")
);
let new_text = selection_infos.iter().map(|(_, link, _)| link).join(" ");
let callback = Arc::new({
let context_store = context_store.clone();
let selections = selections.clone();
let selection_infos = selection_infos.clone();
move |_, window: &mut Window, cx: &mut App| {
move |_, _: &mut Window, cx: &mut App| {
context_store.update(cx, |context_store, cx| {
for (buffer, range) in &selections {
context_store.add_selection(
@@ -342,7 +323,7 @@ impl ContextPickerCompletionProvider {
let editor = editor.clone();
let selection_infos = selection_infos.clone();
window.defer(cx, move |window, cx| {
cx.defer(move |cx| {
let mut current_offset = 0;
for (file_name, link, line_range) in selection_infos.iter() {
let snapshot =
@@ -373,8 +354,9 @@ impl ContextPickerCompletionProvider {
);
editor.update(cx, |editor, cx| {
editor.insert_creases(vec![crease.clone()], cx);
editor.fold_creases(vec![crease], false, window, cx);
editor.display_map.update(cx, |display_map, cx| {
display_map.fold(vec![crease], cx);
});
});
current_offset += text_len + 1;
@@ -414,70 +396,44 @@ impl ContextPickerCompletionProvider {
editor: Entity<Editor>,
context_store: Entity<ContextStore>,
thread_store: Entity<ThreadStore>,
text_thread_store: Entity<TextThreadStore>,
) -> Completion {
let icon_for_completion = if recent {
IconName::HistoryRerun
} else {
IconName::MessageBubbles
};
let new_text = format!("{} ", MentionLink::for_thread(&thread_entry));
let new_text = MentionLink::for_thread(&thread_entry);
let new_text_len = new_text.len();
Completion {
replace_range: source_range.clone(),
new_text,
label: CodeLabel::plain(thread_entry.title().to_string(), None),
label: CodeLabel::plain(thread_entry.summary.to_string(), None),
documentation: None,
insert_text_mode: None,
source: project::CompletionSource::Custom,
icon_path: Some(icon_for_completion.path().into()),
confirm: Some(confirm_completion_callback(
IconName::MessageBubbles.path().into(),
thread_entry.title().clone(),
thread_entry.summary.clone(),
excerpt_id,
source_range.start,
new_text_len - 1,
new_text_len,
editor.clone(),
context_store.clone(),
move |window, cx| match &thread_entry {
ThreadContextEntry::Thread { id, .. } => {
let thread_id = id.clone();
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
.update_in(cx, |thread_store, window, cx| {
thread_store.open_thread(&thread_id, window, cx)
})
.ok()?
.await
.log_err()?;
let context = context_store
.update(cx, |context_store, cx| {
context_store.add_thread(thread, false, cx)
})
.ok()??;
Some(context)
move |cx| {
let thread_id = thread_entry.id.clone();
let context_store = context_store.clone();
let thread_store = thread_store.clone();
cx.spawn(async move |cx| {
let thread = thread_store
.update(cx, |thread_store, cx| {
thread_store.open_thread(&thread_id, cx)
})?
.await?;
context_store.update(cx, |context_store, cx| {
context_store.add_thread(thread, false, cx)
})
}
ThreadContextEntry::Context { path, .. } => {
let path = path.clone();
let context_store = context_store.clone();
let text_thread_store = text_thread_store.clone();
cx.spawn::<_, Option<_>>(async move |cx| {
let thread = text_thread_store
.update(cx, |store, cx| store.open_local_context(path, cx))
.ok()?
.await
.log_err()?;
let context = context_store
.update(cx, |context_store, cx| {
context_store.add_text_thread(thread, false, cx)
})
.ok()??;
Some(context)
})
}
})
.detach_and_log_err(cx);
},
)),
}
@@ -490,7 +446,7 @@ impl ContextPickerCompletionProvider {
editor: Entity<Editor>,
context_store: Entity<ContextStore>,
) -> Completion {
let new_text = format!("{} ", MentionLink::for_rule(&rules));
let new_text = MentionLink::for_rules(&rules);
let new_text_len = new_text.len();
Completion {
replace_range: source_range.clone(),
@@ -505,15 +461,13 @@ impl ContextPickerCompletionProvider {
rules.title.clone(),
excerpt_id,
source_range.start,
new_text_len - 1,
new_text_len,
editor.clone(),
context_store.clone(),
move |_, cx| {
move |cx| {
let user_prompt_id = rules.prompt_id;
let context = context_store.update(cx, |context_store, cx| {
context_store.add_rules(user_prompt_id, false, cx)
context_store.update(cx, |context_store, cx| {
context_store.add_rules(user_prompt_id, false, cx);
});
Task::ready(context)
},
)),
}
@@ -527,7 +481,7 @@ impl ContextPickerCompletionProvider {
context_store: Entity<ContextStore>,
http_client: Arc<HttpClientWithUrl>,
) -> Completion {
let new_text = format!("{} ", MentionLink::for_fetch(&url_to_fetch));
let new_text = MentionLink::for_fetch(&url_to_fetch);
let new_text_len = new_text.len();
Completion {
replace_range: source_range.clone(),
@@ -542,35 +496,29 @@ impl ContextPickerCompletionProvider {
url_to_fetch.clone(),
excerpt_id,
source_range.start,
new_text_len - 1,
new_text_len,
editor.clone(),
context_store.clone(),
move |_, cx| {
move |cx| {
let context_store = context_store.clone();
let http_client = http_client.clone();
let url_to_fetch = url_to_fetch.clone();
cx.spawn(async move |cx| {
if let Some(context) = context_store
.read_with(cx, |context_store, _| {
context_store.get_url_context(url_to_fetch.clone())
})
.ok()?
{
return Some(context);
if context_store.update(cx, |context_store, _| {
context_store.includes_url(&url_to_fetch)
})? {
return Ok(());
}
let content = cx
.background_spawn(fetch_url_content(
http_client,
url_to_fetch.to_string(),
))
.await
.log_err()?;
context_store
.update(cx, |context_store, cx| {
context_store.add_fetched_url(url_to_fetch.to_string(), content, cx)
})
.ok()
.await?;
context_store.update(cx, |context_store, cx| {
context_store.add_fetched_url(url_to_fetch.to_string(), content, cx)
})
})
.detach_and_log_err(cx);
},
)),
}
@@ -612,7 +560,7 @@ impl ContextPickerCompletionProvider {
crease_icon_path.clone()
};
let new_text = format!("{} ", MentionLink::for_file(&file_name, &full_path));
let new_text = MentionLink::for_file(&file_name, &full_path);
let new_text_len = new_text.len();
Completion {
replace_range: source_range.clone(),
@@ -627,25 +575,17 @@ impl ContextPickerCompletionProvider {
file_name,
excerpt_id,
source_range.start,
new_text_len - 1,
new_text_len,
editor,
context_store.clone(),
move |_, cx| {
if is_directory {
Task::ready(
context_store
.update(cx, |context_store, cx| {
context_store.add_directory(&project_path, false, cx)
})
.log_err()
.flatten(),
)
} else {
let result = context_store.update(cx, |context_store, cx| {
move |cx| {
context_store.update(cx, |context_store, cx| {
let task = if is_directory {
Task::ready(context_store.add_directory(&project_path, false, cx))
} else {
context_store.add_file_from_path(project_path.clone(), false, cx)
});
cx.spawn(async move |_| result.await.log_err().flatten())
}
};
task.detach_and_log_err(cx);
})
},
)),
}
@@ -683,7 +623,7 @@ impl ContextPickerCompletionProvider {
label.push_str(" ", None);
label.push_str(&file_name, comment_id);
let new_text = format!("{} ", MentionLink::for_symbol(&symbol.name, &full_path));
let new_text = MentionLink::for_symbol(&symbol.name, &full_path);
let new_text_len = new_text.len();
Some(Completion {
replace_range: source_range.clone(),
@@ -698,21 +638,20 @@ impl ContextPickerCompletionProvider {
symbol.name.clone().into(),
excerpt_id,
source_range.start,
new_text_len - 1,
new_text_len,
editor.clone(),
context_store.clone(),
move |_, cx| {
move |cx| {
let symbol = symbol.clone();
let context_store = context_store.clone();
let workspace = workspace.clone();
let result = super::symbol_context_picker::add_symbol(
super::symbol_context_picker::add_symbol(
symbol.clone(),
false,
workspace.clone(),
context_store.downgrade(),
cx,
);
cx.spawn(async move |_| result.await.log_err()?.0)
)
.detach_and_log_err(cx);
},
)),
})
@@ -744,7 +683,7 @@ impl CompletionProvider for ContextPickerCompletionProvider {
_trigger: CompletionContext,
_window: &mut Window,
cx: &mut Context<Editor>,
) -> Task<Result<Vec<CompletionResponse>>> {
) -> Task<Result<Option<Vec<Completion>>>> {
let state = buffer.update(cx, |buffer, _cx| {
let position = buffer_position.to_point(buffer);
let line_start = Point::new(position.row, 0);
@@ -754,40 +693,30 @@ impl CompletionProvider for ContextPickerCompletionProvider {
MentionCompletion::try_parse(line, offset_to_line)
});
let Some(state) = state else {
return Task::ready(Ok(Vec::new()));
return Task::ready(Ok(None));
};
let Some((workspace, context_store)) =
self.workspace.upgrade().zip(self.context_store.upgrade())
else {
return Task::ready(Ok(Vec::new()));
return Task::ready(Ok(None));
};
let snapshot = buffer.read(cx).snapshot();
let source_range = snapshot.anchor_before(state.source_range.start)
..snapshot.anchor_after(state.source_range.end);
..snapshot.anchor_before(state.source_range.end);
let thread_store = self.thread_store.clone();
let text_thread_store = self.text_thread_store.clone();
let editor = self.editor.clone();
let http_client = workspace.read(cx).client().http_client();
let http_client = workspace.read(cx).client().http_client().clone();
let MentionCompletion { mode, argument, .. } = state;
let query = argument.unwrap_or_else(|| "".to_string());
let excluded_path = self
.excluded_buffer
.as_ref()
.and_then(WeakEntity::upgrade)
.and_then(|b| b.read(cx).file())
.map(|file| ProjectPath::from_file(file.as_ref(), cx));
let recent_entries = recent_context_picker_entries(
context_store.clone(),
thread_store.clone(),
text_thread_store.clone(),
workspace.clone(),
excluded_path.clone(),
cx,
);
@@ -805,7 +734,6 @@ impl CompletionProvider for ContextPickerCompletionProvider {
recent_entries,
prompt_store,
thread_store.clone(),
text_thread_store.clone(),
workspace.clone(),
cx,
);
@@ -813,25 +741,19 @@ impl CompletionProvider for ContextPickerCompletionProvider {
cx.spawn(async move |_, cx| {
let matches = search_task.await;
let Some(editor) = editor.upgrade() else {
return Ok(Vec::new());
return Ok(None);
};
let completions = cx.update(|cx| {
Ok(Some(cx.update(|cx| {
matches
.into_iter()
.filter_map(|mat| match mat {
Match::File(FileMatch { mat, is_recent }) => {
let project_path = ProjectPath {
worktree_id: WorktreeId::from_usize(mat.worktree_id),
path: mat.path.clone(),
};
if excluded_path.as_ref() == Some(&project_path) {
return None;
}
Some(Self::completion_for_path(
project_path,
ProjectPath {
worktree_id: WorktreeId::from_usize(mat.worktree_id),
path: mat.path.clone(),
},
&mat.path_prefix,
is_recent,
mat.is_dir,
@@ -857,8 +779,6 @@ impl CompletionProvider for ContextPickerCompletionProvider {
thread, is_recent, ..
}) => {
let thread_store = thread_store.as_ref().and_then(|t| t.upgrade())?;
let text_thread_store =
text_thread_store.as_ref().and_then(|t| t.upgrade())?;
Some(Self::completion_for_thread(
thread,
excerpt_id,
@@ -867,7 +787,6 @@ impl CompletionProvider for ContextPickerCompletionProvider {
editor.clone(),
context_store.clone(),
thread_store,
text_thread_store,
))
}
@@ -899,24 +818,26 @@ impl CompletionProvider for ContextPickerCompletionProvider {
),
})
.collect()
})?;
Ok(vec![CompletionResponse {
completions,
// Since this does its own filtering (see `filter_completions()` returns false),
// there is no benefit to computing whether this set of completions is incomplete.
is_incomplete: true,
}])
})?))
})
}
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,
_text: &str,
_trigger_in_words: bool,
_menu_is_open: bool,
_: &str,
_: bool,
cx: &mut Context<Editor>,
) -> bool {
let buffer = buffer.read(cx);
@@ -952,47 +873,24 @@ fn confirm_completion_callback(
start: Anchor,
content_len: usize,
editor: Entity<Editor>,
context_store: Entity<ContextStore>,
add_context_fn: impl Fn(&mut Window, &mut App) -> Task<Option<AgentContextHandle>>
+ Send
+ Sync
+ 'static,
add_context_fn: impl Fn(&mut App) -> () + Send + Sync + 'static,
) -> Arc<dyn Fn(CompletionIntent, &mut Window, &mut App) -> bool + Send + Sync> {
Arc::new(move |_, window, cx| {
let context = add_context_fn(window, cx);
Arc::new(move |_, _, cx| {
add_context_fn(cx);
let crease_text = crease_text.clone();
let crease_icon_path = crease_icon_path.clone();
let editor = editor.clone();
let context_store = context_store.clone();
window.defer(cx, move |window, cx| {
let crease_id = crate::context_picker::insert_crease_for_mention(
cx.defer(move |cx| {
crate::context_picker::insert_fold_for_mention(
excerpt_id,
start,
content_len,
crease_text.clone(),
crease_text,
crease_icon_path,
editor.clone(),
window,
editor,
cx,
);
cx.spawn(async move |cx| {
let crease_id = crease_id?;
let context = context.await?;
editor
.update(cx, |editor, cx| {
if let Some(addon) = editor.addon_mut::<ContextCreasesAddon>() {
addon.add_creases(
&context_store,
AgentContextKey(context),
[(crease_id, crease_text)],
cx,
);
}
})
.ok()
})
.detach();
});
false
})
@@ -1065,7 +963,7 @@ mod tests {
use project::{Project, ProjectPath};
use serde_json::json;
use settings::SettingsStore;
use std::{ops::Deref, rc::Rc};
use std::ops::Deref;
use util::{path, separator};
use workspace::{AppState, Item};
@@ -1147,10 +1045,6 @@ mod tests {
fn include_in_nav_history() -> bool {
false
}
fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString {
"Test".into()
}
}
impl EventEmitter<()> for AtMentionEditor {}
@@ -1197,7 +1091,6 @@ mod tests {
"five.txt": "",
"six.txt": "",
"seven.txt": "",
"eight.txt": "",
}
}),
)
@@ -1212,7 +1105,7 @@ mod tests {
assert_eq!(worktrees.len(), 1);
worktrees.pop().unwrap()
});
let worktree_id = worktree.read_with(cx, |worktree, _| worktree.id());
let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
let mut cx = VisualTestContext::from_window(*window.deref(), cx);
@@ -1224,12 +1117,9 @@ mod tests {
separator!("b/five.txt"),
separator!("b/six.txt"),
separator!("b/seven.txt"),
separator!("b/eight.txt"),
];
let mut opened_editors = Vec::new();
for path in paths {
let buffer = workspace
workspace
.update_in(&mut cx, |workspace, window, cx| {
workspace.open_path(
ProjectPath {
@@ -1244,7 +1134,6 @@ mod tests {
})
.await
.unwrap();
opened_editors.push(buffer);
}
let editor = workspace.update_in(&mut cx, |workspace, window, cx| {
@@ -1274,24 +1163,12 @@ mod tests {
let editor_entity = editor.downgrade();
editor.update_in(&mut cx, |editor, window, cx| {
let last_opened_buffer = opened_editors.last().and_then(|editor| {
editor
.downcast::<Editor>()?
.read(cx)
.buffer()
.read(cx)
.as_singleton()
.as_ref()
.map(Entity::downgrade)
});
window.focus(&editor.focus_handle(cx));
editor.set_completion_provider(Some(Rc::new(ContextPickerCompletionProvider::new(
editor.set_completion_provider(Some(Box::new(ContextPickerCompletionProvider::new(
workspace.downgrade(),
context_store.downgrade(),
None,
None,
editor_entity,
last_opened_buffer,
))));
});
@@ -1352,7 +1229,7 @@ mod tests {
});
editor.update(&mut cx, |editor, cx| {
assert_eq!(editor.text(cx), "Lorem [@one.txt](@file:dir/a/one.txt) ");
assert_eq!(editor.text(cx), "Lorem [@one.txt](@file:dir/a/one.txt)",);
assert!(!editor.has_visible_completions_menu());
assert_eq!(
fold_ranges(editor, cx),
@@ -1363,7 +1240,7 @@ mod tests {
cx.simulate_input(" ");
editor.update(&mut cx, |editor, cx| {
assert_eq!(editor.text(cx), "Lorem [@one.txt](@file:dir/a/one.txt) ");
assert_eq!(editor.text(cx), "Lorem [@one.txt](@file:dir/a/one.txt) ",);
assert!(!editor.has_visible_completions_menu());
assert_eq!(
fold_ranges(editor, cx),
@@ -1376,7 +1253,7 @@ mod tests {
editor.update(&mut cx, |editor, cx| {
assert_eq!(
editor.text(cx),
"Lorem [@one.txt](@file:dir/a/one.txt) Ipsum ",
"Lorem [@one.txt](@file:dir/a/one.txt) Ipsum ",
);
assert!(!editor.has_visible_completions_menu());
assert_eq!(
@@ -1390,7 +1267,7 @@ mod tests {
editor.update(&mut cx, |editor, cx| {
assert_eq!(
editor.text(cx),
"Lorem [@one.txt](@file:dir/a/one.txt) Ipsum @file ",
"Lorem [@one.txt](@file:dir/a/one.txt) Ipsum @file ",
);
assert!(editor.has_visible_completions_menu());
assert_eq!(
@@ -1408,14 +1285,14 @@ mod tests {
editor.update(&mut cx, |editor, cx| {
assert_eq!(
editor.text(cx),
"Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt) "
"Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt)"
);
assert!(!editor.has_visible_completions_menu());
assert_eq!(
fold_ranges(editor, cx),
vec![
Point::new(0, 6)..Point::new(0, 37),
Point::new(0, 45)..Point::new(0, 80)
Point::new(0, 44)..Point::new(0, 79)
]
);
});
@@ -1425,14 +1302,14 @@ mod tests {
editor.update(&mut cx, |editor, cx| {
assert_eq!(
editor.text(cx),
"Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt) \n@"
"Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt)\n@"
);
assert!(editor.has_visible_completions_menu());
assert_eq!(
fold_ranges(editor, cx),
vec![
Point::new(0, 6)..Point::new(0, 37),
Point::new(0, 45)..Point::new(0, 80)
Point::new(0, 44)..Point::new(0, 79)
]
);
});
@@ -1446,14 +1323,14 @@ mod tests {
editor.update(&mut cx, |editor, cx| {
assert_eq!(
editor.text(cx),
"Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt) \n[@six.txt](@file:dir/b/six.txt) "
"Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt)\n[@six.txt](@file:dir/b/six.txt)"
);
assert!(!editor.has_visible_completions_menu());
assert_eq!(
fold_ranges(editor, cx),
vec![
Point::new(0, 6)..Point::new(0, 37),
Point::new(0, 45)..Point::new(0, 80),
Point::new(0, 44)..Point::new(0, 79),
Point::new(1, 0)..Point::new(1, 31)
]
);

View File

@@ -193,7 +193,7 @@ impl PickerDelegate for FetchContextPickerDelegate {
return;
};
let http_client = workspace.read(cx).client().http_client();
let http_client = workspace.read(cx).client().http_client().clone();
let url = self.url.clone();
cx.spawn_in(window, async move |this, cx| {
let text = cx

View File

@@ -130,19 +130,21 @@ impl PickerDelegate for FileContextPickerDelegate {
let is_directory = mat.is_dir;
self.context_store
let Some(task) = self
.context_store
.update(cx, |context_store, cx| {
if is_directory {
context_store
.add_directory(&project_path, true, cx)
.log_err();
Task::ready(context_store.add_directory(&project_path, true, cx))
} else {
context_store
.add_file_from_path(project_path.clone(), true, cx)
.detach_and_log_err(cx);
context_store.add_file_from_path(project_path.clone(), true, cx)
}
})
.ok();
.ok()
else {
return;
};
task.detach_and_log_err(cx);
}
fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {

View File

@@ -14,7 +14,6 @@ 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;
@@ -144,7 +143,7 @@ impl PickerDelegate for SymbolContextPickerDelegate {
let selected_index = self.selected_index;
cx.spawn(async move |this, cx| {
let (_, included) = add_symbol_task.await?;
let included = add_symbol_task.await?;
this.update(cx, |this, _| {
if let Some(mat) = this.delegate.matches.get_mut(selected_index) {
mat.is_included = included;
@@ -188,7 +187,7 @@ pub(crate) fn add_symbol(
workspace: Entity<Workspace>,
context_store: WeakEntity<ContextStore>,
cx: &mut App,
) -> Task<Result<(Option<AgentContextHandle>, bool)>> {
) -> Task<Result<bool>> {
let project = workspace.read(cx).project().clone();
let open_buffer_task = project.update(cx, |project, cx| {
project.open_buffer(symbol.path.clone(), cx)

Some files were not shown because too many files have changed in this diff Show More