diff --git a/.config/hakari.toml b/.config/hakari.toml
index 9cc9c1e4eea246b3bbf13d09e7206ee44c31468b..1e8386a14115be2e36b287ace0d47d464df9e620 100644
--- a/.config/hakari.toml
+++ b/.config/hakari.toml
@@ -24,7 +24,7 @@ workspace-members = [
third-party = [
{ name = "reqwest", version = "0.11.27" },
# build of remote_server should not include scap / its x11 dependency
- { name = "scap", git = "https://github.com/zed-industries/scap", rev = "808aa5c45b41e8f44729d02e38fd00a2fe2722e7" },
+ { name = "zed-scap", git = "https://github.com/zed-industries/scap", rev = "4afea48c3b002197176fb19cd0f9b180dd36eaac", version = "0.0.8-zed" },
# build of remote_server should not need to include on libalsa through rodio
{ name = "rodio", git = "https://github.com/RustAudio/rodio" },
]
@@ -37,8 +37,6 @@ workspace-members = [
"zed_glsl",
"zed_html",
"zed_proto",
- "zed_ruff",
"slash_commands_example",
- "zed_snippets",
"zed_test_extension",
]
diff --git a/.github/actions/run_tests/action.yml b/.github/actions/run_tests/action.yml
index e46bc26945e4b5f94ad9f98a882aaa51fc6189af..faf94017976f4b06fdaaa80a5db8083405a7950a 100644
--- a/.github/actions/run_tests/action.yml
+++ b/.github/actions/run_tests/action.yml
@@ -20,4 +20,4 @@ runs:
- name: Run tests
shell: bash -euxo pipefail {0}
- run: cargo nextest run --workspace --no-fail-fast
+ run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
diff --git a/.github/actions/run_tests_windows/action.yml b/.github/actions/run_tests_windows/action.yml
index 8392ca1d375856c7f649e73d2445ce4f873924b1..d85d47cb969e22ca3c73c9ab8caca279a9b5ba88 100644
--- a/.github/actions/run_tests_windows/action.yml
+++ b/.github/actions/run_tests_windows/action.yml
@@ -24,4 +24,4 @@ runs:
shell: powershell
working-directory: ${{ inputs.working-directory }}
run: |
- cargo nextest run --workspace --no-fail-fast
+ cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 34ef3c20cc3497d27ece10b1b8f1619b23f95f97..1d2500e2f794ca06bdbc6c06b21ce39001b57973 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -826,8 +826,9 @@ jobs:
timeout-minutes: 120
name: Create a Windows installer
runs-on: [self-32vcpu-windows-2022]
- if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
- # if: (startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling'))
+ if: |
+ ( startsWith(github.ref, 'refs/tags/v')
+ || contains(github.event.pull_request.labels.*.name, 'run-bundling') )
needs: [windows_tests]
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
@@ -870,8 +871,7 @@ jobs:
- name: Upload Artifacts to release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
- # Re-enable when we are ready to publish windows preview releases
- if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) && env.RELEASE_CHANNEL == 'preview' }} # upload only preview
+ if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
diff --git a/.github/workflows/community_release_actions.yml b/.github/workflows/community_release_actions.yml
index 0f7a73649e9e1180c78a66ddf54055bf66f243f9..4a042a5e06b499b1ca278f152798c171971129ee 100644
--- a/.github/workflows/community_release_actions.yml
+++ b/.github/workflows/community_release_actions.yml
@@ -39,7 +39,7 @@ jobs:
content: ${{ steps.get-content.outputs.string }}
send_release_notes_email:
- if: github.repository_owner == 'zed-industries' && !github.event.release.prerelease
+ if: false && github.repository_owner == 'zed-industries' && !github.event.release.prerelease
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
diff --git a/.github/workflows/issue_response.yml b/.github/workflows/issue_response.yml
deleted file mode 100644
index f084b9ba9dfc4aba46a766986bafcffa3e9fd0ce..0000000000000000000000000000000000000000
--- a/.github/workflows/issue_response.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-name: Issue Response
-
-on:
- schedule:
- - cron: "0 12 * * 2"
- workflow_dispatch:
-
-jobs:
- issue-response:
- if: github.repository_owner == 'zed-industries'
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-
- - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- with:
- version: 9
-
- - name: Setup Node
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
- with:
- node-version: "20"
- cache: "pnpm"
- cache-dependency-path: "script/issue_response/pnpm-lock.yaml"
-
- - run: pnpm install --dir script/issue_response
-
- - name: Run Issue Response
- run: pnpm run --dir script/issue_response start
- env:
- ISSUE_RESPONSE_GITHUB_TOKEN: ${{ secrets.ISSUE_RESPONSE_GITHUB_TOKEN }}
- SLACK_ISSUE_RESPONSE_WEBHOOK_URL: ${{ secrets.SLACK_ISSUE_RESPONSE_WEBHOOK_URL }}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8615ba2c9ad324357792772392c49c7899ffa410..9cbac4af2b57f0350fa9f5665e110e0d6e7f6341 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -63,6 +63,7 @@ Although there are few hard and fast rules, typically we don't merge:
- New file icons. Zed's default icon theme consists of icons that are hand-designed to fit together in a cohesive manner, please don't submit PRs with off-the-shelf SVGs.
- Giant refactorings.
- Non-trivial changes with no tests.
+- Stylistic code changes that do not alter any app logic. Reducing allocations, removing `.unwrap()`s, fixing typos is great; making code "more readable" — maybe not so much.
- Features where (in our subjective opinion) the extra complexity isn't worth it for the number of people who will benefit.
- Anything that seems completely AI generated.
diff --git a/Cargo.lock b/Cargo.lock
index ea528b0cc670fd9d846ec4787f904f4ffa8f74eb..7b7ffde83d8072556207195c0d86a3e3e093c9c7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11,7 +11,6 @@ dependencies = [
"agent_settings",
"anyhow",
"buffer_diff",
- "collections",
"editor",
"env_logger 0.11.8",
"file_icons",
@@ -36,10 +35,11 @@ dependencies = [
"terminal",
"ui",
"url",
- "util",
"uuid",
"watch",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -47,7 +47,6 @@ name = "acp_tools"
version = "0.1.0"
dependencies = [
"agent-client-protocol",
- "collections",
"gpui",
"language",
"markdown",
@@ -57,9 +56,10 @@ dependencies = [
"settings",
"theme",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -69,7 +69,6 @@ dependencies = [
"anyhow",
"buffer_diff",
"clock",
- "collections",
"ctor",
"futures 0.3.31",
"gpui",
@@ -82,9 +81,10 @@ dependencies = [
"serde_json",
"settings",
"text",
- "util",
"watch",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zlog",
]
@@ -104,9 +104,9 @@ dependencies = [
"release_channel",
"smallvec",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -149,7 +149,6 @@ dependencies = [
"chrono",
"client",
"cloud_llm_client",
- "collections",
"component",
"context_server",
"convert_case 0.8.0",
@@ -158,7 +157,6 @@ dependencies = [
"git",
"gpui",
"heed",
- "http_client",
"icons",
"indoc",
"itertools 0.14.0",
@@ -185,10 +183,12 @@ dependencies = [
"theme",
"thiserror 2.0.12",
"time",
- "util",
"uuid",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zed_env_vars",
"zstd",
]
@@ -228,7 +228,6 @@ dependencies = [
"client",
"clock",
"cloud_llm_client",
- "collections",
"context_server",
"ctor",
"db",
@@ -241,7 +240,6 @@ dependencies = [
"gpui_tokio",
"handlebars 4.5.0",
"html_to_markdown",
- "http_client",
"indoc",
"itertools 0.14.0",
"language",
@@ -273,12 +271,14 @@ dependencies = [
"tree-sitter-rust",
"ui",
"unindent",
- "util",
"uuid",
"watch",
"web_search",
"workspace-hack",
"worktree",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zed_env_vars",
"zlog",
"zstd",
@@ -296,13 +296,11 @@ dependencies = [
"anyhow",
"async-trait",
"client",
- "collections",
"env_logger 0.11.8",
"fs",
"futures 0.3.31",
"gpui",
"gpui_tokio",
- "http_client",
"indoc",
"language",
"language_model",
@@ -318,11 +316,15 @@ dependencies = [
"smol",
"task",
"tempfile",
+ "terminal",
"thiserror 2.0.12",
"ui",
- "util",
+ "uuid",
"watch",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -331,7 +333,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"cloud_llm_client",
- "collections",
"convert_case 0.8.0",
"fs",
"gpui",
@@ -343,8 +344,9 @@ dependencies = [
"serde_json",
"serde_json_lenient",
"settings",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -371,7 +373,6 @@ dependencies = [
"chrono",
"client",
"cloud_llm_client",
- "collections",
"command_palette_hooks",
"component",
"context_server",
@@ -386,7 +387,6 @@ dependencies = [
"fuzzy",
"gpui",
"html_to_markdown",
- "http_client",
"indoc",
"itertools 0.14.0",
"jsonschema",
@@ -419,7 +419,6 @@ dependencies = [
"serde_json",
"serde_json_lenient",
"settings",
- "shlex",
"smol",
"streaming_diff",
"task",
@@ -437,10 +436,12 @@ dependencies = [
"unindent",
"url",
"urlencoding",
- "util",
"watch",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zed_actions",
]
@@ -516,7 +517,7 @@ dependencies = [
"rustix-openpty",
"serde",
"signal-hook",
- "unicode-width 0.2.0",
+ "unicode-width",
"vte",
"windows-sys 0.59.0",
]
@@ -658,7 +659,6 @@ dependencies = [
"anyhow",
"chrono",
"futures 0.3.31",
- "http_client",
"schemars 1.0.1",
"serde",
"serde_json",
@@ -666,6 +666,7 @@ dependencies = [
"strum 0.27.1",
"thiserror 2.0.12",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -805,13 +806,14 @@ dependencies = [
"anyhow",
"futures 0.3.31",
"gpui",
+ "log",
"net",
"proto",
"smol",
"tempfile",
- "util",
"windows 0.61.1",
"workspace-hack",
+ "zed-util",
"zeroize",
]
@@ -837,7 +839,6 @@ dependencies = [
"client",
"clock",
"cloud_llm_client",
- "collections",
"context_server",
"fs",
"futures 0.3.31",
@@ -866,10 +867,11 @@ dependencies = [
"text",
"ui",
"unindent",
- "util",
"uuid",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_env_vars",
]
@@ -879,7 +881,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
- "collections",
"derive_more",
"extension",
"futures 0.3.31",
@@ -891,9 +892,10 @@ dependencies = [
"serde",
"serde_json",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -903,7 +905,6 @@ dependencies = [
"anyhow",
"assistant_slash_command",
"chrono",
- "collections",
"context_server",
"editor",
"feature_flags",
@@ -913,7 +914,6 @@ dependencies = [
"globset",
"gpui",
"html_to_markdown",
- "http_client",
"language",
"pretty_assertions",
"project",
@@ -925,10 +925,12 @@ dependencies = [
"smol",
"text",
"ui",
- "util",
"workspace",
"workspace-hack",
"worktree",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zlog",
]
@@ -940,7 +942,6 @@ dependencies = [
"anyhow",
"buffer_diff",
"clock",
- "collections",
"ctor",
"derive_more",
"gpui",
@@ -958,9 +959,10 @@ dependencies = [
"serde_json",
"settings",
"text",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zlog",
]
@@ -977,7 +979,6 @@ dependencies = [
"client",
"clock",
"cloud_llm_client",
- "collections",
"component",
"derive_more",
"diffy",
@@ -989,7 +990,6 @@ dependencies = [
"gpui_tokio",
"handlebars 4.5.0",
"html_to_markdown",
- "http_client",
"indoc",
"itertools 0.14.0",
"language",
@@ -1024,11 +1024,13 @@ dependencies = [
"tree-sitter-rust",
"ui",
"unindent",
- "util",
"watch",
"web_search",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zlog",
]
@@ -1403,7 +1405,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-tar",
- "collections",
"crossbeam",
"denoise",
"gpui",
@@ -1415,8 +1416,9 @@ dependencies = [
"settings",
"smol",
"thiserror 2.0.12",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -1439,7 +1441,6 @@ dependencies = [
"client",
"db",
"gpui",
- "http_client",
"log",
"paths",
"release_channel",
@@ -1451,6 +1452,7 @@ dependencies = [
"which 6.0.3",
"workspace",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -1474,15 +1476,15 @@ dependencies = [
"client",
"editor",
"gpui",
- "http_client",
"markdown_preview",
"release_channel",
"serde",
"serde_json",
"smol",
- "util",
"workspace",
"workspace-hack",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -2009,8 +2011,8 @@ version = "0.1.0"
dependencies = [
"aws-smithy-runtime-api",
"aws-smithy-types",
- "http_client",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -2307,14 +2309,15 @@ dependencies = [
[[package]]
name = "blade-graphics"
-version = "0.6.0"
-source = "git+https://github.com/kvark/blade?rev=bfa594ea697d4b6326ea29f747525c85ecf933b9#bfa594ea697d4b6326ea29f747525c85ecf933b9"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4deb8f595ce7f00dee3543ebf6fd9a20ea86fc421ab79600dac30876250bdae"
dependencies = [
"ash",
"ash-window",
"bitflags 2.9.0",
"bytemuck",
- "codespan-reporting 0.11.1",
+ "codespan-reporting",
"glow",
"gpu-alloc",
"gpu-alloc-ash",
@@ -2332,6 +2335,7 @@ dependencies = [
"objc2-metal",
"objc2-quartz-core",
"objc2-ui-kit",
+ "once_cell",
"raw-window-handle",
"slab",
"wasm-bindgen",
@@ -2341,7 +2345,8 @@ dependencies = [
[[package]]
name = "blade-macros"
version = "0.3.0"
-source = "git+https://github.com/kvark/blade?rev=bfa594ea697d4b6326ea29f747525c85ecf933b9#bfa594ea697d4b6326ea29f747525c85ecf933b9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27142319e2f4c264581067eaccb9f80acccdde60d8b4bf57cc50cd3152f109ca"
dependencies = [
"proc-macro2",
"quote",
@@ -2350,8 +2355,9 @@ dependencies = [
[[package]]
name = "blade-util"
-version = "0.2.0"
-source = "git+https://github.com/kvark/blade?rev=bfa594ea697d4b6326ea29f747525c85ecf933b9#bfa594ea697d4b6326ea29f747525c85ecf933b9"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a6be3a82c001ba7a17b6f8e413ede5d1004e6047213f8efaf0ffc15b5c4904c"
dependencies = [
"blade-graphics",
"bytemuck",
@@ -2476,11 +2482,11 @@ dependencies = [
"rand 0.9.1",
"rope",
"serde_json",
- "sum_tree",
"text",
"unindent",
- "util",
"workspace-hack",
+ "zed-sum-tree",
+ "zed-util",
"zlog",
]
@@ -2618,13 +2624,11 @@ dependencies = [
"anyhow",
"audio",
"client",
- "collections",
"feature_flags",
"fs",
"futures 0.3.31",
"gpui",
"gpui_tokio",
- "http_client",
"language",
"livekit_client",
"log",
@@ -2633,8 +2637,10 @@ dependencies = [
"serde",
"settings",
"telemetry",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -2933,10 +2939,8 @@ dependencies = [
"anyhow",
"client",
"clock",
- "collections",
"futures 0.3.31",
"gpui",
- "http_client",
"language",
"log",
"postage",
@@ -2945,8 +2949,10 @@ dependencies = [
"settings",
"text",
"time",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -3080,8 +3086,8 @@ name = "cli"
version = "0.1.0"
dependencies = [
"anyhow",
+ "askpass",
"clap",
- "collections",
"core-foundation 0.10.0",
"core-services",
"exec",
@@ -3093,9 +3099,10 @@ dependencies = [
"release_channel",
"serde",
"tempfile",
- "util",
"windows 0.61.1",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -3109,7 +3116,6 @@ dependencies = [
"clock",
"cloud_api_client",
"cloud_llm_client",
- "collections",
"credentials_provider",
"derive_more",
"feature_flags",
@@ -3117,7 +3123,6 @@ dependencies = [
"futures 0.3.31",
"gpui",
"gpui_tokio",
- "http_client",
"http_client_tls",
"httparse",
"log",
@@ -3147,10 +3152,12 @@ dependencies = [
"tokio-rustls 0.26.2",
"tokio-socks",
"url",
- "util",
"windows 0.61.1",
"workspace-hack",
"worktree",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -3172,11 +3179,11 @@ dependencies = [
"futures 0.3.31",
"gpui",
"gpui_tokio",
- "http_client",
"parking_lot",
"serde_json",
"workspace-hack",
"yawc",
+ "zed-http-client",
]
[[package]]
@@ -3216,6 +3223,7 @@ dependencies = [
"indoc",
"ordered-float 2.10.1",
"rustc-hash 2.1.1",
+ "serde",
"strum 0.27.1",
"workspace-hack",
]
@@ -3295,16 +3303,6 @@ dependencies = [
"objc",
]
-[[package]]
-name = "codespan-reporting"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
-dependencies = [
- "termcolor",
- "unicode-width 0.1.14",
-]
-
[[package]]
name = "codespan-reporting"
version = "0.12.0"
@@ -3313,7 +3311,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
dependencies = [
"serde",
"termcolor",
- "unicode-width 0.2.0",
+ "unicode-width",
]
[[package]]
@@ -3340,7 +3338,6 @@ dependencies = [
"client",
"clock",
"collab_ui",
- "collections",
"command_palette_hooks",
"context_server",
"ctor",
@@ -3361,7 +3358,6 @@ dependencies = [
"gpui",
"gpui_tokio",
"hex",
- "http_client",
"hyper 0.14.32",
"indoc",
"language",
@@ -3389,10 +3385,8 @@ dependencies = [
"reqwest 0.11.27",
"reqwest_client",
"rpc",
- "rustc-demangle",
"scrypt",
"sea-orm",
- "semantic_version",
"semver",
"serde",
"serde_json",
@@ -3416,11 +3410,14 @@ dependencies = [
"tracing",
"tracing-subscriber",
"unindent",
- "util",
"uuid",
"workspace",
"workspace-hack",
"worktree",
+ "zed-collections",
+ "zed-http-client",
+ "zed-semantic-version",
+ "zed-util",
"zlog",
]
@@ -3433,13 +3430,11 @@ dependencies = [
"channel",
"chrono",
"client",
- "collections",
"db",
"editor",
"futures 0.3.31",
"fuzzy",
"gpui",
- "http_client",
"log",
"menu",
"notifications",
@@ -3460,18 +3455,11 @@ dependencies = [
"title_bar",
"tree-sitter-md",
"ui",
- "util",
"workspace",
"workspace-hack",
-]
-
-[[package]]
-name = "collections"
-version = "0.1.0"
-dependencies = [
- "indexmap 2.9.0",
- "rustc-hash 2.1.1",
- "workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -3512,7 +3500,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"client",
- "collections",
"command_palette_hooks",
"ctor",
"db",
@@ -3534,9 +3521,10 @@ dependencies = [
"theme",
"time",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_actions",
]
@@ -3544,17 +3532,16 @@ dependencies = [
name = "command_palette_hooks"
version = "0.1.0"
dependencies = [
- "collections",
"derive_more",
"gpui",
"workspace-hack",
+ "zed-collections",
]
[[package]]
name = "component"
version = "0.1.0"
dependencies = [
- "collections",
"documented",
"gpui",
"inventory",
@@ -3562,6 +3549,7 @@ dependencies = [
"strum 0.27.1",
"theme",
"workspace-hack",
+ "zed-collections",
]
[[package]]
@@ -3582,7 +3570,7 @@ dependencies = [
"encode_unicode",
"libc",
"once_cell",
- "unicode-width 0.2.0",
+ "unicode-width",
"windows-sys 0.59.0",
]
@@ -3624,7 +3612,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
- "collections",
"futures 0.3.31",
"gpui",
"log",
@@ -3638,8 +3625,9 @@ dependencies = [
"smol",
"tempfile",
"url",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -3675,7 +3663,6 @@ dependencies = [
"chrono",
"client",
"clock",
- "collections",
"command_palette_hooks",
"ctor",
"dirs 4.0.0",
@@ -3684,7 +3671,6 @@ dependencies = [
"fs",
"futures 0.3.31",
"gpui",
- "http_client",
"indoc",
"itertools 0.14.0",
"language",
@@ -3696,16 +3682,19 @@ dependencies = [
"paths",
"project",
"rpc",
+ "semver",
"serde",
"serde_json",
"settings",
- "sum_tree",
"task",
"theme",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-sum-tree",
+ "zed-util",
"zlog",
]
@@ -4110,9 +4099,9 @@ dependencies = [
[[package]]
name = "crc"
-version = "3.2.1"
+version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
+checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675"
dependencies = [
"crc-catalog",
]
@@ -4371,7 +4360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b4400e26ea4b99417e4263b1ce2d8452404d750ba0809a7bd043072593d430d"
dependencies = [
"cc",
- "codespan-reporting 0.12.0",
+ "codespan-reporting",
"proc-macro2",
"quote",
"scratch",
@@ -4385,7 +4374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31860c98f69fc14da5742c5deaf78983e846c7b27804ca8c8319e32eef421bde"
dependencies = [
"clap",
- "codespan-reporting 0.12.0",
+ "codespan-reporting",
"proc-macro2",
"quote",
"syn 2.0.101",
@@ -4419,12 +4408,10 @@ dependencies = [
"async-tar",
"async-trait",
"client",
- "collections",
"dap-types",
"fs",
"futures 0.3.31",
"gpui",
- "http_client",
"language",
"libc",
"log",
@@ -4442,8 +4429,10 @@ dependencies = [
"telemetry",
"tree-sitter",
"tree-sitter-go",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zlog",
]
@@ -4463,7 +4452,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
- "collections",
"dap",
"dotenvy",
"fs",
@@ -4478,8 +4466,9 @@ dependencies = [
"shlex",
"smol",
"task",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -4576,8 +4565,8 @@ dependencies = [
"sqlez",
"sqlez_macros",
"tempfile",
- "util",
"workspace-hack",
+ "zed-util",
"zed_env_vars",
]
@@ -4603,8 +4592,8 @@ dependencies = [
"gpui",
"serde_json",
"task",
- "util",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -4620,9 +4609,9 @@ dependencies = [
"serde_json",
"settings",
"smol",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -4633,7 +4622,6 @@ dependencies = [
"anyhow",
"bitflags 2.9.0",
"client",
- "collections",
"command_palette_hooks",
"dap",
"dap_adapters",
@@ -4676,9 +4664,10 @@ dependencies = [
"tree-sitter-json",
"ui",
"unindent",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_actions",
"zlog",
]
@@ -4698,11 +4687,11 @@ version = "0.1.0"
dependencies = [
"anyhow",
"futures 0.3.31",
- "http_client",
"schemars 1.0.1",
"serde",
"serde_json",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -4780,23 +4769,12 @@ dependencies = [
"syn 2.0.101",
]
-[[package]]
-name = "derive_refineable"
-version = "0.1.0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.101",
- "workspace-hack",
-]
-
[[package]]
name = "diagnostics"
version = "0.1.0"
dependencies = [
"anyhow",
"client",
- "collections",
"component",
"ctor",
"editor",
@@ -4816,9 +4794,10 @@ dependencies = [
"theme",
"ui",
"unindent",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zlog",
]
@@ -4973,9 +4952,9 @@ dependencies = [
"serde",
"serde_json",
"settings",
- "util",
"workspace-hack",
"zed",
+ "zed-util",
"zlog",
]
@@ -5124,7 +5103,6 @@ dependencies = [
"client",
"gpui",
"language",
- "project",
"workspace-hack",
]
@@ -5168,7 +5146,6 @@ dependencies = [
"arrayvec",
"clap",
"cloud_llm_client",
- "collections",
"futures 0.3.31",
"gpui",
"hashbrown 0.15.3",
@@ -5177,6 +5154,7 @@ dependencies = [
"language",
"log",
"ordered-float 2.10.1",
+ "postage",
"pretty_assertions",
"project",
"regex",
@@ -5187,8 +5165,9 @@ dependencies = [
"strum 0.27.1",
"text",
"tree-sitter",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zlog",
]
@@ -5202,7 +5181,6 @@ dependencies = [
"buffer_diff",
"client",
"clock",
- "collections",
"convert_case 0.8.0",
"criterion",
"ctor",
@@ -5216,7 +5194,6 @@ dependencies = [
"fuzzy",
"git",
"gpui",
- "http_client",
"indoc",
"itertools 0.14.0",
"language",
@@ -5242,7 +5219,6 @@ dependencies = [
"smallvec",
"smol",
"snippet",
- "sum_tree",
"task",
"telemetry",
"tempfile",
@@ -5261,11 +5237,14 @@ dependencies = [
"unicode-segmentation",
"unindent",
"url",
- "util",
"uuid",
"vim_mode_setting",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-sum-tree",
+ "zed-util",
"zed_actions",
"zlog",
]
@@ -5547,7 +5526,6 @@ dependencies = [
"clap",
"client",
"cloud_llm_client",
- "collections",
"debug_adapter_extension",
"dirs 4.0.0",
"dotenvy",
@@ -5582,10 +5560,11 @@ dependencies = [
"terminal_view",
"toml 0.8.20",
"unindent",
- "util",
"uuid",
"watch",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -5664,28 +5643,28 @@ dependencies = [
"async-compression",
"async-tar",
"async-trait",
- "collections",
"dap",
"fs",
"futures 0.3.31",
"gpui",
"heck 0.5.0",
- "http_client",
"language",
"log",
"lsp",
"parking_lot",
"pretty_assertions",
- "semantic_version",
"serde",
"serde_json",
"task",
"toml 0.8.20",
"url",
- "util",
"wasm-encoder 0.221.3",
"wasmparser 0.221.3",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-semantic-version",
+ "zed-util",
]
[[package]]
@@ -5721,7 +5700,6 @@ dependencies = [
"async-tar",
"async-trait",
"client",
- "collections",
"criterion",
"ctor",
"dap",
@@ -5729,7 +5707,6 @@ dependencies = [
"fs",
"futures 0.3.31",
"gpui",
- "http_client",
"language",
"language_extension",
"log",
@@ -5743,7 +5720,6 @@ dependencies = [
"release_channel",
"remote",
"reqwest_client",
- "semantic_version",
"serde",
"serde_json",
"serde_json_lenient",
@@ -5755,11 +5731,14 @@ dependencies = [
"theme_extension",
"toml 0.8.20",
"url",
- "util",
"wasmparser 0.221.3",
"wasmtime",
"wasmtime-wasi",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-semantic-version",
+ "zed-util",
"zlog",
]
@@ -5769,7 +5748,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"client",
- "collections",
"db",
"editor",
"extension",
@@ -5783,7 +5761,6 @@ dependencies = [
"picker",
"project",
"release_channel",
- "semantic_version",
"serde",
"settings",
"smallvec",
@@ -5791,10 +5768,12 @@ dependencies = [
"telemetry",
"theme",
"ui",
- "util",
"vim_mode_setting",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-semantic-version",
+ "zed-util",
"zed_actions",
]
@@ -5887,9 +5866,9 @@ dependencies = [
"system_specs",
"ui",
"urlencoding",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
"zed_actions",
]
@@ -5908,7 +5887,6 @@ name = "file_finder"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"ctor",
"editor",
"file_icons",
@@ -5928,9 +5906,10 @@ dependencies = [
"text",
"theme",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zlog",
]
@@ -5940,10 +5919,9 @@ version = "0.1.0"
dependencies = [
"gpui",
"serde",
- "settings",
"theme",
- "util",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -6050,30 +6028,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
-[[package]]
-name = "font-kit"
-version = "0.14.1"
-source = "git+https://github.com/zed-industries/font-kit?rev=5474cfad4b719a72ec8ed2cb7327b2b01fd10568#5474cfad4b719a72ec8ed2cb7327b2b01fd10568"
-dependencies = [
- "bitflags 2.9.0",
- "byteorder",
- "core-foundation 0.10.0",
- "core-graphics 0.24.0",
- "core-text",
- "dirs 5.0.1",
- "dwrote",
- "float-ord",
- "freetype-sys",
- "lazy_static",
- "libc",
- "log",
- "pathfinder_geometry",
- "pathfinder_simd",
- "walkdir",
- "winapi",
- "yeslogic-fontconfig-sys",
-]
-
[[package]]
name = "font-types"
version = "0.8.4"
@@ -6210,7 +6164,6 @@ dependencies = [
"async-tar",
"async-trait",
"cocoa 0.26.0",
- "collections",
"fsevent",
"futures 0.3.31",
"git",
@@ -6230,9 +6183,10 @@ dependencies = [
"tempfile",
"text",
"time",
- "util",
"windows 0.61.1",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -6269,6 +6223,7 @@ dependencies = [
"bitflags 2.9.0",
"core-foundation 0.10.0",
"fsevent-sys 3.1.0",
+ "log",
"parking_lot",
"tempfile",
"workspace-hack",
@@ -6450,8 +6405,8 @@ version = "0.1.0"
dependencies = [
"gpui",
"log",
- "util",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -6789,12 +6744,10 @@ dependencies = [
"anyhow",
"askpass",
"async-trait",
- "collections",
"derive_more",
"futures 0.3.31",
"git2",
"gpui",
- "http_client",
"log",
"parking_lot",
"pretty_assertions",
@@ -6805,16 +6758,18 @@ dependencies = [
"serde",
"serde_json",
"smol",
- "sum_tree",
"tempfile",
"text",
"thiserror 2.0.12",
"time",
"unindent",
"url",
- "util",
"uuid",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-sum-tree",
+ "zed-util",
]
[[package]]
@@ -6839,7 +6794,6 @@ dependencies = [
"futures 0.3.31",
"git",
"gpui",
- "http_client",
"indoc",
"pretty_assertions",
"regex",
@@ -6847,8 +6801,9 @@ dependencies = [
"serde_json",
"settings",
"url",
- "util",
"workspace-hack",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -6862,7 +6817,6 @@ dependencies = [
"call",
"chrono",
"cloud_llm_client",
- "collections",
"command_palette_hooks",
"component",
"ctor",
@@ -6898,11 +6852,12 @@ dependencies = [
"time_format",
"ui",
"unindent",
- "util",
"watch",
"windows 0.61.1",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_actions",
"zeroize",
"zlog",
@@ -6941,9 +6896,9 @@ dependencies = [
[[package]]
name = "glow"
-version = "0.14.2"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483"
+checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08"
dependencies = [
"js-sys",
"slotmap",
@@ -6970,9 +6925,9 @@ dependencies = [
"tree-sitter-rust",
"tree-sitter-typescript",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -6992,13 +6947,13 @@ version = "0.1.0"
dependencies = [
"anyhow",
"futures 0.3.31",
- "http_client",
"schemars 1.0.1",
"serde",
"serde_json",
"settings",
"strum 0.27.1",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -7050,7 +7005,6 @@ dependencies = [
"calloop-wayland-source",
"cbindgen",
"cocoa 0.26.0",
- "collections",
"core-foundation 0.10.0",
"core-foundation-sys",
"core-graphics 0.24.0",
@@ -7064,18 +7018,15 @@ dependencies = [
"etagere",
"filedescriptor",
"flume",
- "font-kit",
"foreign-types 0.5.0",
"futures 0.3.31",
- "gpui_macros",
- "http_client",
+ "gpui-macros",
"image",
"inventory",
"itertools 0.14.0",
"libc",
"log",
"lyon",
- "media",
"metal",
"naga",
"num_cpus",
@@ -7092,13 +7043,10 @@ dependencies = [
"profiling",
"rand 0.9.1",
"raw-window-handle",
- "refineable",
"reqwest_client",
"resvg",
- "scap",
"schemars 1.0.1",
"seahash",
- "semantic_version",
"serde",
"serde_json",
"slotmap",
@@ -7106,13 +7054,10 @@ dependencies = [
"smol",
"stacksafe",
"strum 0.27.1",
- "sum_tree",
"taffy",
"thiserror 2.0.12",
"unicode-segmentation",
"usvg",
- "util",
- "util_macros",
"uuid",
"waker-fn",
"wayland-backend",
@@ -7127,12 +7072,22 @@ dependencies = [
"workspace-hack",
"x11-clipboard",
"x11rb",
- "xim",
"xkbcommon",
+ "zed-collections",
+ "zed-font-kit",
+ "zed-http-client",
+ "zed-media",
+ "zed-refineable",
+ "zed-scap",
+ "zed-semantic-version",
+ "zed-sum-tree",
+ "zed-util",
+ "zed-util-macros",
+ "zed-xim",
]
[[package]]
-name = "gpui_macros"
+name = "gpui-macros"
version = "0.1.0"
dependencies = [
"gpui",
@@ -7150,8 +7105,8 @@ dependencies = [
"anyhow",
"gpui",
"tokio",
- "util",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -7556,25 +7511,6 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f"
-[[package]]
-name = "http_client"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "bytes 1.10.1",
- "derive_more",
- "futures 0.3.31",
- "http 1.3.1",
- "http-body 1.0.1",
- "log",
- "parking_lot",
- "reqwest 0.12.15 (git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415)",
- "serde",
- "serde_json",
- "url",
- "workspace-hack",
-]
-
[[package]]
name = "http_client_tls"
version = "0.1.0"
@@ -7968,9 +7904,9 @@ dependencies = [
"settings",
"theme",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -8089,10 +8025,10 @@ dependencies = [
"serde_json_lenient",
"theme",
"ui",
- "util",
- "util_macros",
"workspace",
"workspace-hack",
+ "zed-util",
+ "zed-util-macros",
"zed_actions",
]
@@ -8105,9 +8041,9 @@ dependencies = [
"gpui",
"release_channel",
"smol",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -8389,6 +8325,28 @@ dependencies = [
"thiserror 1.0.69",
]
+[[package]]
+name = "json_schema_store"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "dap",
+ "extension",
+ "gpui",
+ "language",
+ "paths",
+ "project",
+ "schemars 1.0.1",
+ "serde",
+ "serde_json",
+ "settings",
+ "snippet_provider",
+ "task",
+ "theme",
+ "workspace-hack",
+ "zed-util",
+]
+
[[package]]
name = "jsonschema"
version = "0.30.0"
@@ -8410,7 +8368,7 @@ dependencies = [
"referencing",
"regex",
"regex-syntax",
- "reqwest 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "reqwest 0.12.15",
"serde",
"serde_json",
"uuid-simd",
@@ -8467,7 +8425,6 @@ name = "keymap_editor"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"command_palette",
"component",
"db",
@@ -8476,6 +8433,7 @@ dependencies = [
"fuzzy",
"gpui",
"itertools 0.14.0",
+ "json_schema_store",
"language",
"log",
"menu",
@@ -8493,10 +8451,11 @@ dependencies = [
"tree-sitter-rust",
"ui",
"ui_input",
- "util",
"vim",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_actions",
]
@@ -8556,7 +8515,6 @@ dependencies = [
"anyhow",
"async-trait",
"clock",
- "collections",
"ctor",
"diffy",
"ec4rs",
@@ -8565,7 +8523,6 @@ dependencies = [
"fuzzy",
"globset",
"gpui",
- "http_client",
"imara-diff",
"indoc",
"itertools 0.14.0",
@@ -8586,7 +8543,6 @@ dependencies = [
"smol",
"streaming-iterator",
"strsim",
- "sum_tree",
"task",
"text",
"theme",
@@ -8604,9 +8560,12 @@ dependencies = [
"tree-sitter-typescript",
"unicase",
"unindent",
- "util",
"watch",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-sum-tree",
+ "zed-util",
"zlog",
]
@@ -8616,7 +8575,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
- "collections",
"extension",
"fs",
"futures 0.3.31",
@@ -8627,8 +8585,9 @@ dependencies = [
"project",
"serde",
"serde_json",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -8641,10 +8600,8 @@ dependencies = [
"client",
"cloud_api_types",
"cloud_llm_client",
- "collections",
"futures 0.3.31",
"gpui",
- "http_client",
"icons",
"image",
"log",
@@ -8658,8 +8615,10 @@ dependencies = [
"smol",
"telemetry_events",
"thiserror 2.0.12",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -8676,7 +8635,6 @@ dependencies = [
"chrono",
"client",
"cloud_llm_client",
- "collections",
"component",
"convert_case 0.8.0",
"copilot",
@@ -8688,7 +8646,6 @@ dependencies = [
"google_ai",
"gpui",
"gpui_tokio",
- "http_client",
"language",
"language_model",
"lmstudio",
@@ -8712,10 +8669,12 @@ dependencies = [
"tokio",
"ui",
"ui_input",
- "util",
"vercel",
"workspace-hack",
"x_ai",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zed_env_vars",
]
@@ -8747,9 +8706,9 @@ dependencies = [
"project",
"settings",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -8758,7 +8717,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"client",
- "collections",
"command_palette_hooks",
"copilot",
"editor",
@@ -8775,9 +8733,10 @@ dependencies = [
"theme",
"tree-sitter",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_actions",
"zlog",
]
@@ -8792,18 +8751,15 @@ dependencies = [
"async-tar",
"async-trait",
"chrono",
- "collections",
- "dap",
"futures 0.3.31",
"gpui",
- "http_client",
"itertools 0.14.0",
+ "json_schema_store",
"language",
"log",
"lsp",
"node_runtime",
"parking_lot",
- "paths",
"pet",
"pet-conda",
"pet-core",
@@ -8816,17 +8772,13 @@ dependencies = [
"regex",
"rope",
"rust-embed",
- "schemars 1.0.1",
"serde",
"serde_json",
"serde_json_lenient",
"settings",
- "sha2",
"shlex",
"smol",
- "snippet_provider",
"task",
- "tempfile",
"text",
"theme",
"toml 0.8.20",
@@ -8850,9 +8802,11 @@ dependencies = [
"tree-sitter-yaml",
"unindent",
"url",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -9034,9 +8988,9 @@ dependencies = [
"picker",
"project",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -9159,9 +9113,9 @@ dependencies = [
"prost 0.9.0",
"prost-build 0.9.0",
"prost-types 0.9.0",
- "reqwest 0.12.15 (git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415)",
"serde",
"workspace-hack",
+ "zed-reqwest",
]
[[package]]
@@ -9171,7 +9125,6 @@ dependencies = [
"anyhow",
"async-trait",
"audio",
- "collections",
"core-foundation 0.10.0",
"core-video",
"coreaudio-rs 0.12.1",
@@ -9190,7 +9143,6 @@ dependencies = [
"parking_lot",
"postage",
"rodio",
- "scap",
"serde",
"serde_json",
"serde_urlencoded",
@@ -9200,8 +9152,10 @@ dependencies = [
"smallvec",
"tokio-tungstenite 0.26.2",
"ui",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-scap",
+ "zed-util",
]
[[package]]
@@ -9221,11 +9175,11 @@ version = "0.1.0"
dependencies = [
"anyhow",
"futures 0.3.31",
- "http_client",
"schemars 1.0.1",
"serde",
"serde_json",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -9285,7 +9239,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-pipe",
- "collections",
"ctor",
"futures 0.3.31",
"gpui",
@@ -9298,8 +9251,9 @@ dependencies = [
"serde",
"serde_json",
"smol",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zlog",
]
@@ -9422,7 +9376,6 @@ version = "0.1.0"
dependencies = [
"assets",
"base64 0.22.1",
- "collections",
"env_logger 0.11.8",
"fs",
"futures 0.3.31",
@@ -9434,11 +9387,12 @@ dependencies = [
"node_runtime",
"pulldown-cmark 0.12.2",
"settings",
- "sum_tree",
"theme",
"ui",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-sum-tree",
+ "zed-util",
]
[[package]]
@@ -9447,7 +9401,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-recursion",
- "collections",
"editor",
"fs",
"gpui",
@@ -9461,9 +9414,10 @@ dependencies = [
"settings",
"theme",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -9591,21 +9545,6 @@ dependencies = [
"warp",
]
-[[package]]
-name = "media"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "bindgen 0.71.1",
- "core-foundation 0.10.0",
- "core-video",
- "ctor",
- "foreign-types 0.5.0",
- "metal",
- "objc",
- "workspace-hack",
-]
-
[[package]]
name = "memchr"
version = "2.7.4"
@@ -9668,14 +9607,18 @@ name = "migrator"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"convert_case 0.8.0",
"log",
"pretty_assertions",
+ "serde_json",
+ "serde_json_lenient",
+ "settings",
"streaming-iterator",
"tree-sitter",
"tree-sitter-json",
+ "unindent",
"workspace-hack",
+ "zed-collections",
]
[[package]]
@@ -9821,12 +9764,12 @@ version = "0.1.0"
dependencies = [
"anyhow",
"futures 0.3.31",
- "http_client",
"schemars 1.0.1",
"serde",
"serde_json",
"strum 0.27.1",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -9864,7 +9807,6 @@ dependencies = [
"anyhow",
"buffer_diff",
"clock",
- "collections",
"ctor",
"gpui",
"indoc",
@@ -9880,12 +9822,13 @@ dependencies = [
"settings",
"smallvec",
"smol",
- "sum_tree",
"text",
"theme",
"tree-sitter",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-sum-tree",
+ "zed-util",
"zlog",
]
@@ -9905,7 +9848,7 @@ dependencies = [
"bit-set 0.8.0",
"bitflags 2.9.0",
"cfg_aliases 0.2.1",
- "codespan-reporting 0.12.0",
+ "codespan-reporting",
"half",
"hashbrown 0.15.3",
"hexf-parse",
@@ -10074,17 +10017,17 @@ dependencies = [
"async-tar",
"async-trait",
"futures 0.3.31",
- "http_client",
"log",
"paths",
"semver",
"serde",
"serde_json",
"smol",
- "util",
"watch",
"which 6.0.3",
"workspace-hack",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -10128,18 +10071,18 @@ dependencies = [
"anyhow",
"channel",
"client",
- "collections",
"component",
"db",
"gpui",
"rpc",
"settings",
- "sum_tree",
"time",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-sum-tree",
+ "zed-util",
"zed_actions",
]
@@ -10578,12 +10521,12 @@ version = "0.1.0"
dependencies = [
"anyhow",
"futures 0.3.31",
- "http_client",
"schemars 1.0.1",
"serde",
"serde_json",
"settings",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -10614,10 +10557,11 @@ dependencies = [
"telemetry",
"theme",
"ui",
- "util",
+ "ui_input",
"vim_mode_setting",
"workspace",
"workspace-hack",
+ "zed-util",
"zed_actions",
"zlog",
]
@@ -10686,7 +10630,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"futures 0.3.31",
- "http_client",
"log",
"schemars 1.0.1",
"serde",
@@ -10694,6 +10637,7 @@ dependencies = [
"settings",
"strum 0.27.1",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -10702,7 +10646,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"futures 0.3.31",
- "http_client",
"schemars 1.0.1",
"serde",
"serde_json",
@@ -10710,6 +10653,7 @@ dependencies = [
"strum 0.27.1",
"thiserror 2.0.12",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -10858,9 +10802,9 @@ dependencies = [
"tree-sitter-rust",
"tree-sitter-typescript",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
"zed_actions",
]
@@ -10869,7 +10813,6 @@ name = "outline_panel"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"db",
"editor",
"file_icons",
@@ -10890,10 +10833,11 @@ dependencies = [
"smol",
"theme",
"ui",
- "util",
"workspace",
"workspace-hack",
"worktree",
+ "zed-collections",
+ "zed-util",
"zed_actions",
]
@@ -11063,8 +11007,8 @@ version = "0.1.0"
dependencies = [
"dirs 4.0.0",
"ignore",
- "util",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -11157,16 +11101,6 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
-[[package]]
-name = "perf"
-version = "0.1.0"
-dependencies = [
- "collections",
- "serde",
- "serde_json",
- "workspace-hack",
-]
-
[[package]]
name = "pest"
version = "2.8.0"
@@ -11932,7 +11866,6 @@ name = "prettier"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"fs",
"gpui",
"language",
@@ -11943,8 +11876,9 @@ dependencies = [
"paths",
"serde",
"serde_json",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -12071,12 +12005,12 @@ dependencies = [
"circular-buffer",
"client",
"clock",
- "collections",
"context_server",
"dap",
"dap_adapters",
"extension",
"fancy-regex 0.14.0",
+ "feature_flags",
"fs",
"futures 0.3.31",
"fuzzy",
@@ -12085,7 +12019,6 @@ dependencies = [
"git_hosting_providers",
"globset",
"gpui",
- "http_client",
"image",
"indexmap 2.9.0",
"itertools 0.14.0",
@@ -12116,7 +12049,6 @@ dependencies = [
"smol",
"snippet",
"snippet_provider",
- "sum_tree",
"task",
"tempfile",
"terminal",
@@ -12124,11 +12056,14 @@ dependencies = [
"toml 0.8.20",
"unindent",
"url",
- "util",
"watch",
"which 6.0.3",
"workspace-hack",
"worktree",
+ "zed-collections",
+ "zed-http-client",
+ "zed-sum-tree",
+ "zed-util",
"zeroize",
"zlog",
]
@@ -12139,8 +12074,8 @@ version = "0.1.0"
dependencies = [
"anyhow",
"client",
- "collections",
"command_palette_hooks",
+ "criterion",
"db",
"editor",
"file_icons",
@@ -12151,6 +12086,7 @@ dependencies = [
"menu",
"pretty_assertions",
"project",
+ "rayon",
"schemars 1.0.1",
"search",
"serde",
@@ -12160,10 +12096,11 @@ dependencies = [
"telemetry",
"theme",
"ui",
- "util",
"workspace",
"workspace-hack",
"worktree",
+ "zed-collections",
+ "zed-util",
"zed_actions",
]
@@ -12185,9 +12122,9 @@ dependencies = [
"serde_json",
"settings",
"theme",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -12212,7 +12149,6 @@ dependencies = [
"anyhow",
"assets",
"chrono",
- "collections",
"fs",
"futures 0.3.31",
"fuzzy",
@@ -12227,9 +12163,10 @@ dependencies = [
"serde",
"serde_json",
"text",
- "util",
"uuid",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -12343,12 +12280,12 @@ name = "proto"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"prost 0.9.0",
"prost-build 0.9.0",
"serde",
"typed-path",
"workspace-hack",
+ "zed-collections",
]
[[package]]
@@ -12832,10 +12769,10 @@ dependencies = [
"telemetry",
"theme",
"ui",
- "util",
"windows-registry 0.6.0",
"workspace",
"workspace-hack",
+ "zed-util",
"zed_actions",
]
@@ -12913,14 +12850,6 @@ dependencies = [
"serde_json",
]
-[[package]]
-name = "refineable"
-version = "0.1.0"
-dependencies = [
- "derive_refineable",
- "workspace-hack",
-]
-
[[package]]
name = "regalloc2"
version = "0.11.2"
@@ -12986,11 +12915,9 @@ dependencies = [
"anyhow",
"askpass",
"async-trait",
- "collections",
"fs",
"futures 0.3.31",
"gpui",
- "itertools 0.14.0",
"log",
"parking_lot",
"paths",
@@ -13005,9 +12932,10 @@ dependencies = [
"tempfile",
"thiserror 2.0.12",
"urlencoding",
- "util",
"which 6.0.3",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -13023,7 +12951,6 @@ dependencies = [
"clap",
"client",
"clock",
- "collections",
"crash-handler",
"crashes",
"dap",
@@ -13041,7 +12968,7 @@ dependencies = [
"git_hosting_providers",
"gpui",
"gpui_tokio",
- "http_client",
+ "json_schema_store",
"language",
"language_extension",
"language_model",
@@ -13069,10 +12996,12 @@ dependencies = [
"thiserror 2.0.12",
"toml 0.8.20",
"unindent",
- "util",
"watch",
"workspace",
"worktree",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zlog",
]
@@ -13095,7 +13024,6 @@ dependencies = [
"async-tungstenite",
"base64 0.22.1",
"client",
- "collections",
"command_palette_hooks",
"editor",
"env_logger 0.11.8",
@@ -13103,7 +13031,6 @@ dependencies = [
"file_icons",
"futures 0.3.31",
"gpui",
- "http_client",
"image",
"indoc",
"jupyter-protocol",
@@ -13130,10 +13057,12 @@ dependencies = [
"tree-sitter-python",
"tree-sitter-typescript",
"ui",
- "util",
"uuid",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -13217,55 +13146,6 @@ dependencies = [
"windows-registry 0.4.0",
]
-[[package]]
-name = "reqwest"
-version = "0.12.15"
-source = "git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415#951c770a32f1998d6e999cef3e59e0013e6c4415"
-dependencies = [
- "base64 0.22.1",
- "bytes 1.10.1",
- "encoding_rs",
- "futures-core",
- "futures-util",
- "h2 0.4.9",
- "http 1.3.1",
- "http-body 1.0.1",
- "http-body-util",
- "hyper 1.6.0",
- "hyper-rustls 0.27.5",
- "hyper-util",
- "ipnet",
- "js-sys",
- "log",
- "mime",
- "mime_guess",
- "once_cell",
- "percent-encoding",
- "pin-project-lite",
- "quinn",
- "rustls 0.23.26",
- "rustls-native-certs 0.8.1",
- "rustls-pemfile 2.2.0",
- "rustls-pki-types",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "sync_wrapper 1.0.2",
- "system-configuration 0.6.1",
- "tokio",
- "tokio-rustls 0.26.2",
- "tokio-socks",
- "tokio-util",
- "tower 0.5.2",
- "tower-service",
- "url",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "wasm-streams",
- "web-sys",
- "windows-registry 0.4.0",
-]
-
[[package]]
name = "reqwest_client"
version = "0.1.0"
@@ -13274,14 +13154,14 @@ dependencies = [
"bytes 1.10.1",
"futures 0.3.31",
"gpui",
- "http_client",
"http_client_tls",
"log",
"regex",
- "reqwest 0.12.15 (git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415)",
"serde",
"tokio",
"workspace-hack",
+ "zed-http-client",
+ "zed-reqwest",
]
[[package]]
@@ -13329,8 +13209,8 @@ dependencies = [
"pulldown-cmark 0.12.2",
"theme",
"ui",
- "util",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -13423,10 +13303,10 @@ dependencies = [
"rand 0.9.1",
"rayon",
"smallvec",
- "sum_tree",
"unicode-segmentation",
- "util",
"workspace-hack",
+ "zed-sum-tree",
+ "zed-util",
"zlog",
]
@@ -13444,7 +13324,6 @@ dependencies = [
"async-tungstenite",
"base64 0.22.1",
"chrono",
- "collections",
"futures 0.3.31",
"gpui",
"parking_lot",
@@ -13456,8 +13335,9 @@ dependencies = [
"sha2",
"strum 0.27.1",
"tracing",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zlog",
"zstd",
]
@@ -13493,7 +13373,6 @@ name = "rules_library"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"editor",
"gpui",
"language",
@@ -13509,9 +13388,10 @@ dependencies = [
"theme",
"title_bar",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_actions",
]
@@ -13577,9 +13457,9 @@ dependencies = [
[[package]]
name = "rust_decimal"
-version = "1.37.1"
+version = "1.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50"
+checksum = "c8975fc98059f365204d635119cf9c5a60ae67b841ed49b5422a9a7e56cdfac0"
dependencies = [
"arrayvec",
"borsh",
@@ -13883,27 +13763,6 @@ dependencies = [
"winapi-util",
]
-[[package]]
-name = "scap"
-version = "0.0.8"
-source = "git+https://github.com/zed-industries/scap?rev=808aa5c45b41e8f44729d02e38fd00a2fe2722e7#808aa5c45b41e8f44729d02e38fd00a2fe2722e7"
-dependencies = [
- "anyhow",
- "cocoa 0.25.0",
- "core-graphics-helmer-fork",
- "log",
- "objc",
- "rand 0.8.5",
- "screencapturekit",
- "screencapturekit-sys",
- "sysinfo",
- "tao-core-video-sys",
- "windows 0.61.1",
- "windows-capture",
- "x11",
- "xcb",
-]
-
[[package]]
name = "schannel"
version = "0.1.27"
@@ -14163,7 +14022,6 @@ dependencies = [
"anyhow",
"bitflags 2.9.0",
"client",
- "collections",
"editor",
"futures 0.3.31",
"gpui",
@@ -14178,9 +14036,10 @@ dependencies = [
"theme",
"ui",
"unindent",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_actions",
]
@@ -14240,15 +14099,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
-[[package]]
-name = "semantic_version"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "serde",
- "workspace-hack",
-]
-
[[package]]
name = "semver"
version = "1.0.26"
@@ -14431,9 +14281,9 @@ dependencies = [
"db",
"gpui",
"serde_json",
- "util",
"uuid",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -14441,7 +14291,7 @@ name = "settings"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
+ "derive_more",
"ec4rs",
"fs",
"futures 0.3.31",
@@ -14466,8 +14316,9 @@ dependencies = [
"tree-sitter",
"tree-sitter-json",
"unindent",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zlog",
]
@@ -14514,11 +14365,13 @@ dependencies = [
"feature_flags",
"fs",
"futures 0.3.31",
+ "fuzzy",
"gpui",
"language",
"menu",
"node_runtime",
"paths",
+ "pretty_assertions",
"project",
"serde",
"session",
@@ -14526,9 +14379,11 @@ dependencies = [
"strum 0.27.1",
"theme",
"ui",
- "util",
+ "ui_input",
"workspace",
"workspace-hack",
+ "zed-util",
+ "zed_actions",
"zlog",
]
@@ -14551,9 +14406,9 @@ checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
[[package]]
name = "sha2"
-version = "0.10.8"
+version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -14799,7 +14654,6 @@ name = "snippet_provider"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"extension",
"fs",
"futures 0.3.31",
@@ -14809,10 +14663,12 @@ dependencies = [
"paths",
"schemars 1.0.1",
"serde",
+ "serde_json",
"serde_json_lenient",
"snippet",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -14828,9 +14684,9 @@ dependencies = [
"picker",
"settings",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -14901,7 +14757,6 @@ name = "sqlez"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"futures 0.3.31",
"indoc",
"libsqlite3-sys",
@@ -14910,9 +14765,10 @@ dependencies = [
"smol",
"sqlformat",
"thread_local",
- "util",
"uuid",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -15245,8 +15101,8 @@ dependencies = [
"ordered-float 2.10.1",
"rand 0.9.1",
"rope",
- "util",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -15356,32 +15212,17 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
-[[package]]
-name = "sum_tree"
-version = "0.1.0"
-dependencies = [
- "arrayvec",
- "ctor",
- "log",
- "rand 0.9.1",
- "rayon",
- "workspace-hack",
- "zlog",
-]
-
[[package]]
name = "supermaven"
version = "0.1.0"
dependencies = [
"anyhow",
"client",
- "collections",
"edit_prediction",
"editor",
"env_logger 0.11.8",
"futures 0.3.31",
"gpui",
- "http_client",
"language",
"log",
"postage",
@@ -15395,8 +15236,10 @@ dependencies = [
"theme",
"ui",
"unicode-segmentation",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -15405,13 +15248,13 @@ version = "0.1.0"
dependencies = [
"anyhow",
"futures 0.3.31",
- "http_client",
"paths",
"serde",
"serde_json",
"smol",
- "util",
"workspace-hack",
+ "zed-http-client",
+ "zed-util",
]
[[package]]
@@ -15878,7 +15721,6 @@ name = "tab_switcher"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"ctor",
"editor",
"fuzzy",
@@ -15894,9 +15736,10 @@ dependencies = [
"smol",
"theme",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zlog",
]
@@ -15959,7 +15802,6 @@ name = "task"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"futures 0.3.31",
"gpui",
"hex",
@@ -15973,8 +15815,9 @@ dependencies = [
"serde_json_lenient",
"sha2",
"shellexpand 2.1.2",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_actions",
]
@@ -15983,7 +15826,6 @@ name = "tasks_ui"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"editor",
"file_icons",
"fuzzy",
@@ -15999,9 +15841,10 @@ dependencies = [
"tree-sitter-rust",
"tree-sitter-typescript",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_actions",
]
@@ -16020,10 +15863,10 @@ dependencies = [
name = "telemetry_events"
version = "0.1.0"
dependencies = [
- "semantic_version",
"serde",
"serde_json",
"workspace-hack",
+ "zed-semantic-version",
]
[[package]]
@@ -16065,7 +15908,6 @@ version = "0.1.0"
dependencies = [
"alacritty_terminal",
"anyhow",
- "collections",
"futures 0.3.31",
"gpui",
"itertools 0.14.0",
@@ -16084,9 +15926,10 @@ dependencies = [
"thiserror 2.0.12",
"url",
"urlencoding",
- "util",
"windows 0.61.1",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
]
[[package]]
@@ -16108,7 +15951,6 @@ dependencies = [
"async-recursion",
"breadcrumbs",
"client",
- "collections",
"db",
"dirs 4.0.0",
"editor",
@@ -16132,9 +15974,10 @@ dependencies = [
"terminal",
"theme",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zed_actions",
]
@@ -16144,10 +15987,8 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clock",
- "collections",
"ctor",
"gpui",
- "http_client",
"log",
"parking_lot",
"postage",
@@ -16155,9 +15996,11 @@ dependencies = [
"regex",
"rope",
"smallvec",
- "sum_tree",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-sum-tree",
+ "zed-util",
"zlog",
]
@@ -16166,7 +16009,6 @@ name = "theme"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"derive_more",
"fs",
"futures 0.3.31",
@@ -16174,7 +16016,6 @@ dependencies = [
"log",
"palette",
"parking_lot",
- "refineable",
"schemars 1.0.1",
"serde",
"serde_json",
@@ -16182,9 +16023,11 @@ dependencies = [
"settings",
"strum 0.27.1",
"thiserror 2.0.12",
- "util",
"uuid",
"workspace-hack",
+ "zed-collections",
+ "zed-refineable",
+ "zed-util",
]
[[package]]
@@ -16205,7 +16048,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clap",
- "collections",
"gpui",
"indexmap 2.9.0",
"log",
@@ -16218,6 +16060,7 @@ dependencies = [
"theme",
"vscode_theme",
"workspace-hack",
+ "zed-collections",
]
[[package]]
@@ -16234,9 +16077,9 @@ dependencies = [
"telemetry",
"theme",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
"zed_actions",
]
@@ -16452,10 +16295,8 @@ dependencies = [
"chrono",
"client",
"cloud_llm_client",
- "collections",
"db",
"gpui",
- "http_client",
"notifications",
"pretty_assertions",
"project",
@@ -16470,10 +16311,12 @@ dependencies = [
"theme",
"tree-sitter-md",
"ui",
- "util",
"windows 0.61.1",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zed_actions",
]
@@ -16690,9 +16533,9 @@ dependencies = [
"picker",
"project",
"ui",
- "util",
"workspace",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -16879,9 +16722,9 @@ dependencies = [
[[package]]
name = "tree-sitter"
-version = "0.25.6"
+version = "0.25.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7cf18d43cbf0bfca51f657132cc616a5097edc4424d538bae6fa60142eaf9f0"
+checksum = "78f873475d258561b06f1c595d93308a7ed124d9977cb26b148c2084a4a3cc87"
dependencies = [
"cc",
"regex",
@@ -17054,8 +16897,9 @@ dependencies = [
[[package]]
name = "tree-sitter-python"
-version = "0.23.6"
-source = "git+https://github.com/zed-industries/tree-sitter-python?rev=218fcbf3fda3d029225f3dec005cb497d111b35e#218fcbf3fda3d029225f3dec005cb497d111b35e"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bf85fd39652e740bf60f46f4cda9492c3a9ad75880575bf14960f775cb74a1c"
dependencies = [
"cc",
"tree-sitter-language",
@@ -17267,7 +17111,7 @@ dependencies = [
"component",
"documented",
"gpui",
- "gpui_macros",
+ "gpui-macros",
"icons",
"itertools 0.14.0",
"menu",
@@ -17279,9 +17123,9 @@ dependencies = [
"strum 0.27.1",
"theme",
"ui_macros",
- "util",
"windows 0.61.1",
"workspace-hack",
+ "zed-util",
]
[[package]]
@@ -17290,7 +17134,10 @@ version = "0.1.0"
dependencies = [
"component",
"editor",
+ "fuzzy",
"gpui",
+ "menu",
+ "picker",
"settings",
"theme",
"ui",
@@ -17403,12 +17250,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
-[[package]]
-name = "unicode-width"
-version = "0.1.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
-
[[package]]
name = "unicode-width"
version = "0.2.0"
@@ -17508,55 +17349,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
-[[package]]
-name = "util"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "async-fs",
- "async_zip",
- "collections",
- "command-fds",
- "dirs 4.0.0",
- "dunce",
- "futures 0.3.31",
- "futures-lite 1.13.0",
- "git2",
- "globset",
- "indoc",
- "itertools 0.14.0",
- "libc",
- "log",
- "nix 0.29.0",
- "pretty_assertions",
- "rand 0.9.1",
- "regex",
- "rust-embed",
- "schemars 1.0.1",
- "serde",
- "serde_json",
- "serde_json_lenient",
- "shlex",
- "smol",
- "take-until",
- "tempfile",
- "tendril",
- "unicase",
- "util_macros",
- "walkdir",
- "workspace-hack",
-]
-
-[[package]]
-name = "util_macros"
-version = "0.1.0"
-dependencies = [
- "perf",
- "quote",
- "syn 2.0.101",
- "workspace-hack",
-]
-
[[package]]
name = "uuid"
version = "1.16.0"
@@ -17669,7 +17461,6 @@ dependencies = [
"assets",
"async-compat",
"async-trait",
- "collections",
"command_palette",
"command_palette_hooks",
"db",
@@ -17683,10 +17474,10 @@ dependencies = [
"language",
"log",
"lsp",
+ "menu",
"multi_buffer",
"nvim-rs",
"parking_lot",
- "perf",
"picker",
"project",
"project_panel",
@@ -17702,11 +17493,13 @@ dependencies = [
"theme",
"tokio",
"ui",
- "util",
- "util_macros",
"vim_mode_setting",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-perf",
+ "zed-util",
+ "zed-util-macros",
"zed_actions",
]
@@ -18449,10 +18242,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"cloud_llm_client",
- "collections",
"gpui",
"serde",
"workspace-hack",
+ "zed-collections",
]
[[package]]
@@ -18464,12 +18257,12 @@ dependencies = [
"cloud_llm_client",
"futures 0.3.31",
"gpui",
- "http_client",
"language_model",
"serde",
"serde_json",
"web_search",
"workspace-hack",
+ "zed-http-client",
]
[[package]]
@@ -19585,14 +19378,12 @@ dependencies = [
"call",
"client",
"clock",
- "collections",
"component",
"dap",
"db",
"fs",
"futures 0.3.31",
"gpui",
- "http_client",
"itertools 0.14.0",
"language",
"log",
@@ -19616,10 +19407,12 @@ dependencies = [
"tempfile",
"theme",
"ui",
- "util",
"uuid",
"windows 0.61.1",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zed_actions",
"zlog",
]
@@ -19660,12 +19453,11 @@ dependencies = [
"cipher",
"clap",
"clap_builder",
- "codespan-reporting 0.12.0",
+ "codespan-reporting",
"concurrent-queue",
"core-foundation 0.9.4",
"core-foundation-sys",
"cranelift-codegen",
- "crc32fast",
"crossbeam-channel",
"crossbeam-epoch",
"crossbeam-utils",
@@ -19823,14 +19615,12 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clock",
- "collections",
"fs",
"futures 0.3.31",
"fuzzy",
"git",
"git2",
"gpui",
- "http_client",
"ignore",
"language",
"log",
@@ -19845,10 +19635,12 @@ dependencies = [
"settings",
"smallvec",
"smol",
- "sum_tree",
"text",
- "util",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-sum-tree",
+ "zed-util",
"zlog",
]
@@ -19950,23 +19742,10 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61"
-[[package]]
-name = "xim"
-version = "0.4.0"
-source = "git+https://github.com/zed-industries/xim-rs?rev=c0a70c1bd2ce197364216e5e818a2cb3adb99a8d#c0a70c1bd2ce197364216e5e818a2cb3adb99a8d"
-dependencies = [
- "ahash 0.8.11",
- "hashbrown 0.14.5",
- "log",
- "x11rb",
- "xim-ctext",
- "xim-parser",
-]
-
[[package]]
name = "xim-ctext"
version = "0.3.0"
-source = "git+https://github.com/zed-industries/xim-rs?rev=c0a70c1bd2ce197364216e5e818a2cb3adb99a8d#c0a70c1bd2ce197364216e5e818a2cb3adb99a8d"
+source = "git+https://github.com/zed-industries/xim-rs.git?rev=16f35a2c881b815a2b6cdfd6687988e84f8447d8#16f35a2c881b815a2b6cdfd6687988e84f8447d8"
dependencies = [
"encoding_rs",
]
@@ -19974,7 +19753,7 @@ dependencies = [
[[package]]
name = "xim-parser"
version = "0.2.1"
-source = "git+https://github.com/zed-industries/xim-rs?rev=c0a70c1bd2ce197364216e5e818a2cb3adb99a8d#c0a70c1bd2ce197364216e5e818a2cb3adb99a8d"
+source = "git+https://github.com/zed-industries/xim-rs.git?rev=16f35a2c881b815a2b6cdfd6687988e84f8447d8#16f35a2c881b815a2b6cdfd6687988e84f8447d8"
dependencies = [
"bitflags 2.9.0",
]
@@ -20028,6 +19807,9 @@ dependencies = [
"cargo_metadata",
"cargo_toml",
"clap",
+ "indoc",
+ "toml 0.8.20",
+ "toml_edit",
"workspace-hack",
]
@@ -20180,7 +19962,7 @@ dependencies = [
[[package]]
name = "zed"
-version = "0.207.0"
+version = "0.208.0"
dependencies = [
"acp_tools",
"activity_indicator",
@@ -20204,7 +19986,6 @@ dependencies = [
"cli",
"client",
"collab_ui",
- "collections",
"command_palette",
"component",
"copilot",
@@ -20233,12 +20014,12 @@ dependencies = [
"go_to_line",
"gpui",
"gpui_tokio",
- "http_client",
"image_viewer",
"inspector_ui",
"install_cli",
"itertools 0.14.0",
"journal",
+ "json_schema_store",
"keymap_editor",
"language",
"language_extension",
@@ -20276,7 +20057,6 @@ dependencies = [
"release_channel",
"remote",
"repl",
- "reqwest 0.12.15 (git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415)",
"reqwest_client",
"rope",
"search",
@@ -20313,7 +20093,6 @@ dependencies = [
"ui_prompt",
"url",
"urlencoding",
- "util",
"uuid",
"vim",
"vim_mode_setting",
@@ -20324,6 +20103,10 @@ dependencies = [
"winresource",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-http-client",
+ "zed-reqwest",
+ "zed-util",
"zed_actions",
"zed_env_vars",
"zeta",
@@ -20333,6 +20116,262 @@ dependencies = [
"zlog_settings",
]
+[[package]]
+name = "zed-collections"
+version = "0.1.0"
+dependencies = [
+ "indexmap 2.9.0",
+ "rustc-hash 2.1.1",
+ "workspace-hack",
+]
+
+[[package]]
+name = "zed-derive-refineable"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "workspace-hack",
+]
+
+[[package]]
+name = "zed-font-kit"
+version = "0.14.1-zed"
+source = "git+https://github.com/zed-industries/font-kit?rev=110523127440aefb11ce0cf280ae7c5071337ec5#110523127440aefb11ce0cf280ae7c5071337ec5"
+dependencies = [
+ "bitflags 2.9.0",
+ "byteorder",
+ "core-foundation 0.10.0",
+ "core-graphics 0.24.0",
+ "core-text",
+ "dirs 5.0.1",
+ "dwrote",
+ "float-ord",
+ "freetype-sys",
+ "lazy_static",
+ "libc",
+ "log",
+ "pathfinder_geometry",
+ "pathfinder_simd",
+ "walkdir",
+ "winapi",
+ "yeslogic-fontconfig-sys",
+]
+
+[[package]]
+name = "zed-http-client"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-compression",
+ "async-fs",
+ "async-tar",
+ "bytes 1.10.1",
+ "derive_more",
+ "futures 0.3.31",
+ "http 1.3.1",
+ "http-body 1.0.1",
+ "log",
+ "parking_lot",
+ "serde",
+ "serde_json",
+ "sha2",
+ "tempfile",
+ "url",
+ "workspace-hack",
+ "zed-reqwest",
+ "zed-util",
+]
+
+[[package]]
+name = "zed-media"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bindgen 0.71.1",
+ "core-foundation 0.10.0",
+ "core-video",
+ "ctor",
+ "foreign-types 0.5.0",
+ "metal",
+ "objc",
+ "workspace-hack",
+]
+
+[[package]]
+name = "zed-perf"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "serde_json",
+ "workspace-hack",
+ "zed-collections",
+]
+
+[[package]]
+name = "zed-refineable"
+version = "0.1.0"
+dependencies = [
+ "workspace-hack",
+ "zed-derive-refineable",
+]
+
+[[package]]
+name = "zed-reqwest"
+version = "0.12.15-zed"
+source = "git+https://github.com/zed-industries/reqwest.git?rev=c15662463bda39148ba154100dd44d3fba5873a4#c15662463bda39148ba154100dd44d3fba5873a4"
+dependencies = [
+ "base64 0.22.1",
+ "bytes 1.10.1",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2 0.4.9",
+ "http 1.3.1",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.6.0",
+ "hyper-rustls 0.27.5",
+ "hyper-util",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "mime_guess",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls 0.23.26",
+ "rustls-native-certs 0.8.1",
+ "rustls-pemfile 2.2.0",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper 1.0.2",
+ "system-configuration 0.6.1",
+ "tokio",
+ "tokio-rustls 0.26.2",
+ "tokio-socks",
+ "tokio-util",
+ "tower 0.5.2",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
+ "windows-registry 0.4.0",
+]
+
+[[package]]
+name = "zed-scap"
+version = "0.0.8-zed"
+source = "git+https://github.com/zed-industries/scap?rev=4afea48c3b002197176fb19cd0f9b180dd36eaac#4afea48c3b002197176fb19cd0f9b180dd36eaac"
+dependencies = [
+ "anyhow",
+ "cocoa 0.25.0",
+ "core-graphics-helmer-fork",
+ "log",
+ "objc",
+ "rand 0.8.5",
+ "screencapturekit",
+ "screencapturekit-sys",
+ "sysinfo",
+ "tao-core-video-sys",
+ "windows 0.61.1",
+ "windows-capture",
+ "x11",
+ "xcb",
+]
+
+[[package]]
+name = "zed-semantic-version"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "serde",
+ "workspace-hack",
+]
+
+[[package]]
+name = "zed-sum-tree"
+version = "0.1.0"
+dependencies = [
+ "arrayvec",
+ "ctor",
+ "log",
+ "rand 0.9.1",
+ "rayon",
+ "workspace-hack",
+ "zlog",
+]
+
+[[package]]
+name = "zed-util"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-fs",
+ "async_zip",
+ "command-fds",
+ "dirs 4.0.0",
+ "dunce",
+ "futures 0.3.31",
+ "futures-lite 1.13.0",
+ "git2",
+ "globset",
+ "indoc",
+ "itertools 0.14.0",
+ "libc",
+ "log",
+ "nix 0.29.0",
+ "pretty_assertions",
+ "rand 0.9.1",
+ "regex",
+ "rust-embed",
+ "schemars 1.0.1",
+ "serde",
+ "serde_json",
+ "serde_json_lenient",
+ "shlex",
+ "smol",
+ "take-until",
+ "tempfile",
+ "tendril",
+ "unicase",
+ "walkdir",
+ "which 6.0.3",
+ "workspace-hack",
+ "zed-collections",
+ "zed-util-macros",
+]
+
+[[package]]
+name = "zed-util-macros"
+version = "0.1.0"
+dependencies = [
+ "quote",
+ "syn 2.0.101",
+ "workspace-hack",
+ "zed-perf",
+]
+
+[[package]]
+name = "zed-xim"
+version = "0.4.0-zed"
+source = "git+https://github.com/zed-industries/xim-rs.git?rev=16f35a2c881b815a2b6cdfd6687988e84f8447d8#16f35a2c881b815a2b6cdfd6687988e84f8447d8"
+dependencies = [
+ "ahash 0.8.11",
+ "hashbrown 0.14.5",
+ "log",
+ "x11rb",
+ "xim-ctext",
+ "xim-parser",
+]
+
[[package]]
name = "zed_actions"
version = "0.1.0"
@@ -20372,6 +20411,17 @@ dependencies = [
"wit-bindgen 0.41.0",
]
+[[package]]
+name = "zed_extension_api"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0729d50b4ca0a7e28e590bbe32e3ca0194d97ef654961451a424c661a366fca0"
+dependencies = [
+ "serde",
+ "serde_json",
+ "wit-bindgen 0.41.0",
+]
+
[[package]]
name = "zed_glsl"
version = "0.1.0"
@@ -20381,9 +20431,9 @@ dependencies = [
[[package]]
name = "zed_html"
-version = "0.2.2"
+version = "0.2.3"
dependencies = [
- "zed_extension_api 0.1.0",
+ "zed_extension_api 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -20393,21 +20443,6 @@ dependencies = [
"zed_extension_api 0.1.0",
]
-[[package]]
-name = "zed_ruff"
-version = "0.1.1"
-dependencies = [
- "zed_extension_api 0.1.0",
-]
-
-[[package]]
-name = "zed_snippets"
-version = "0.0.6"
-dependencies = [
- "serde_json",
- "zed_extension_api 0.1.0",
-]
-
[[package]]
name = "zed_test_extension"
version = "0.1.0"
@@ -20560,7 +20595,6 @@ dependencies = [
"clock",
"cloud_api_types",
"cloud_llm_client",
- "collections",
"command_palette_hooks",
"copilot",
"ctor",
@@ -20571,7 +20605,6 @@ dependencies = [
"fs",
"futures 0.3.31",
"gpui",
- "http_client",
"indoc",
"itertools 0.14.0",
"language",
@@ -20597,11 +20630,13 @@ dependencies = [
"tree-sitter-go",
"tree-sitter-rust",
"ui",
- "util",
"uuid",
"workspace",
"workspace-hack",
"worktree",
+ "zed-collections",
+ "zed-http-client",
+ "zed-util",
"zed_actions",
"zlog",
]
@@ -20632,11 +20667,11 @@ dependencies = [
"serde_json",
"settings",
"thiserror 2.0.12",
- "util",
"uuid",
"workspace",
"workspace-hack",
"worktree",
+ "zed-util",
]
[[package]]
@@ -20647,7 +20682,6 @@ dependencies = [
"clap",
"client",
"cloud_llm_client",
- "collections",
"edit_prediction_context",
"editor",
"futures 0.3.31",
@@ -20663,9 +20697,10 @@ dependencies = [
"text",
"ui",
"ui_input",
- "util",
"workspace",
"workspace-hack",
+ "zed-collections",
+ "zed-util",
"zeta2",
"zlog",
]
@@ -20691,7 +20726,9 @@ dependencies = [
"language_model",
"language_models",
"languages",
+ "log",
"node_runtime",
+ "ordered-float 2.10.1",
"paths",
"project",
"prompt_store",
@@ -20703,11 +20740,12 @@ dependencies = [
"shellexpand 2.1.2",
"smol",
"terminal_view",
- "util",
"watch",
"workspace-hack",
+ "zed-util",
"zeta",
"zeta2",
+ "zlog",
]
[[package]]
@@ -20751,20 +20789,20 @@ version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
- "collections",
"log",
"tempfile",
"workspace-hack",
+ "zed-collections",
]
[[package]]
name = "zlog_settings"
version = "0.1.0"
dependencies = [
- "collections",
"gpui",
"settings",
"workspace-hack",
+ "zed-collections",
"zlog",
]
diff --git a/Cargo.toml b/Cargo.toml
index be6699edb6d19efac2cb981cbd75efd64580629e..8bcde70b55f4bbc99fdafaddc057f571efa5abce 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -91,6 +91,7 @@ members = [
"crates/inspector_ui",
"crates/install_cli",
"crates/journal",
+ "crates/json_schema_store",
"crates/keymap_editor",
"crates/language",
"crates/language_extension",
@@ -211,9 +212,7 @@ members = [
"extensions/glsl",
"extensions/html",
"extensions/proto",
- "extensions/ruff",
"extensions/slash-commands-example",
- "extensions/snippets",
"extensions/test-extension",
#
@@ -274,7 +273,7 @@ cloud_llm_client = { path = "crates/cloud_llm_client" }
cloud_zeta2_prompt = { path = "crates/cloud_zeta2_prompt" }
collab = { path = "crates/collab" }
collab_ui = { path = "crates/collab_ui" }
-collections = { path = "crates/collections" }
+collections = { path = "crates/collections", package = "zed-collections", version = "0.1.0" }
command_palette = { path = "crates/command_palette" }
command_palette_hooks = { path = "crates/command_palette_hooks" }
component = { path = "crates/component" }
@@ -290,6 +289,7 @@ debug_adapter_extension = { path = "crates/debug_adapter_extension" }
debugger_tools = { path = "crates/debugger_tools" }
debugger_ui = { path = "crates/debugger_ui" }
deepseek = { path = "crates/deepseek" }
+derive_refineable = { path = "crates/refineable/derive_refineable", package = "zed-derive-refineable", version = "0.1.0" }
diagnostics = { path = "crates/diagnostics" }
editor = { path = "crates/editor" }
extension = { path = "crates/extension" }
@@ -308,10 +308,10 @@ git_ui = { path = "crates/git_ui" }
go_to_line = { path = "crates/go_to_line" }
google_ai = { path = "crates/google_ai" }
gpui = { path = "crates/gpui", default-features = false }
-gpui_macros = { path = "crates/gpui_macros" }
+gpui_macros = { path = "crates/gpui_macros", package = "gpui-macros", version = "0.1.0" }
gpui_tokio = { path = "crates/gpui_tokio" }
html_to_markdown = { path = "crates/html_to_markdown" }
-http_client = { path = "crates/http_client" }
+http_client = { path = "crates/http_client", package = "zed-http-client", version = "0.1.0" }
http_client_tls = { path = "crates/http_client_tls" }
icons = { path = "crates/icons" }
image_viewer = { path = "crates/image_viewer" }
@@ -322,6 +322,7 @@ zeta2_tools = { path = "crates/zeta2_tools" }
inspector_ui = { path = "crates/inspector_ui" }
install_cli = { path = "crates/install_cli" }
journal = { path = "crates/journal" }
+json_schema_store = { path = "crates/json_schema_store" }
keymap_editor = { path = "crates/keymap_editor" }
language = { path = "crates/language" }
language_extension = { path = "crates/language_extension" }
@@ -339,7 +340,7 @@ lsp = { path = "crates/lsp" }
markdown = { path = "crates/markdown" }
markdown_preview = { path = "crates/markdown_preview" }
svg_preview = { path = "crates/svg_preview" }
-media = { path = "crates/media" }
+media = { path = "crates/media", package = "zed-media", version = "0.1.0" }
menu = { path = "crates/menu" }
migrator = { path = "crates/migrator" }
mistral = { path = "crates/mistral" }
@@ -356,7 +357,7 @@ outline = { path = "crates/outline" }
outline_panel = { path = "crates/outline_panel" }
panel = { path = "crates/panel" }
paths = { path = "crates/paths" }
-perf = { path = "tooling/perf" }
+perf = { path = "tooling/perf", package = "zed-perf", version = "0.1.0" }
picker = { path = "crates/picker" }
plugin = { path = "crates/plugin" }
plugin_macros = { path = "crates/plugin_macros" }
@@ -368,7 +369,7 @@ project_symbols = { path = "crates/project_symbols" }
prompt_store = { path = "crates/prompt_store" }
proto = { path = "crates/proto" }
recent_projects = { path = "crates/recent_projects" }
-refineable = { path = "crates/refineable" }
+refineable = { path = "crates/refineable", package = "zed-refineable", version = "0.1.0" }
release_channel = { path = "crates/release_channel" }
scheduler = { path = "crates/scheduler" }
remote = { path = "crates/remote" }
@@ -381,9 +382,10 @@ rope = { path = "crates/rope" }
rpc = { path = "crates/rpc" }
rules_library = { path = "crates/rules_library" }
search = { path = "crates/search" }
-semantic_version = { path = "crates/semantic_version" }
+semantic_version = { path = "crates/semantic_version", package = "zed-semantic-version", version = "0.1.0" }
session = { path = "crates/session" }
settings = { path = "crates/settings" }
+settings_macros = { path = "crates/settings_macros" }
settings_ui = { path = "crates/settings_ui" }
snippet = { path = "crates/snippet" }
snippet_provider = { path = "crates/snippet_provider" }
@@ -393,7 +395,7 @@ sqlez_macros = { path = "crates/sqlez_macros" }
story = { path = "crates/story" }
storybook = { path = "crates/storybook" }
streaming_diff = { path = "crates/streaming_diff" }
-sum_tree = { path = "crates/sum_tree" }
+sum_tree = { path = "crates/sum_tree", package = "zed-sum-tree", version = "0.1.0" }
supermaven = { path = "crates/supermaven" }
supermaven_api = { path = "crates/supermaven_api" }
system_specs = { path = "crates/system_specs" }
@@ -416,8 +418,8 @@ ui = { path = "crates/ui" }
ui_input = { path = "crates/ui_input" }
ui_macros = { path = "crates/ui_macros" }
ui_prompt = { path = "crates/ui_prompt" }
-util = { path = "crates/util" }
-util_macros = { path = "crates/util_macros" }
+util = { path = "crates/util", package = "zed-util", version = "0.1.0" }
+util_macros = { path = "crates/util_macros", package = "zed-util-macros", version = "0.1.0" }
vercel = { path = "crates/vercel" }
vim = { path = "crates/vim" }
vim_mode_setting = { path = "crates/vim_mode_setting" }
@@ -471,9 +473,9 @@ backtrace = "0.3"
base64 = "0.22"
bincode = "1.2.1"
bitflags = "2.6.0"
-blade-graphics = { git = "https://github.com/kvark/blade", rev = "bfa594ea697d4b6326ea29f747525c85ecf933b9" }
-blade-macros = { git = "https://github.com/kvark/blade", rev = "bfa594ea697d4b6326ea29f747525c85ecf933b9" }
-blade-util = { git = "https://github.com/kvark/blade", rev = "bfa594ea697d4b6326ea29f747525c85ecf933b9" }
+blade-graphics = { version = "0.7.0" }
+blade-macros = { version = "0.3.0" }
+blade-util = { version = "0.3.0" }
blake3 = "1.5.3"
bytes = "1.0"
cargo_metadata = "0.19"
@@ -547,6 +549,7 @@ nanoid = "0.4"
nbformat = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
nix = "0.29"
num-format = "0.4.4"
+num-traits = "0.2"
objc = "0.2"
objc2-foundation = { version = "0.3", default-features = false, features = [
"NSArray",
@@ -603,7 +606,8 @@ rand = "0.9"
rayon = "1.8"
ref-cast = "1.0.24"
regex = "1.5"
-reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "951c770a32f1998d6e999cef3e59e0013e6c4415", default-features = false, features = [
+# WARNING: If you change this, you must also publish a new version of zed-reqwest to crates.io
+reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "c15662463bda39148ba154100dd44d3fba5873a4", default-features = false, features = [
"charset",
"http2",
"macos-system-configuration",
@@ -611,17 +615,17 @@ reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "951c77
"rustls-tls-native-roots",
"socks",
"stream",
-] }
+], package = "zed-reqwest", version = "0.12.15-zed" }
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"
rustc-hash = "2.1.0"
rustls = { version = "0.23.26" }
rustls-platform-verifier = "0.5.0"
-scap = { git = "https://github.com/zed-industries/scap", rev = "808aa5c45b41e8f44729d02e38fd00a2fe2722e7", default-features = false }
+# WARNING: If you change this, you must also publish a new version of zed-scap to crates.io
+scap = { git = "https://github.com/zed-industries/scap", rev = "4afea48c3b002197176fb19cd0f9b180dd36eaac", default-features = false, package = "zed-scap", version = "0.0.8-zed" }
schemars = { version = "1.0", features = ["indexmap2"] }
semver = "1.0"
serde = { version = "1.0.221", features = ["derive", "rc"] }
@@ -647,7 +651,7 @@ 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 = "2.0.101", features = ["full", "extra-traits", "visit-mut"] }
sys-locale = "0.3.1"
sysinfo = "0.31.0"
take-until = "0.2.0"
@@ -665,8 +669,9 @@ tiny_http = "0.8"
tokio = { version = "1" }
tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] }
toml = "0.8"
+toml_edit = { version = "0.22", default-features = false, features = ["display", "parse", "serde"] }
tower-http = "0.4.4"
-tree-sitter = { version = "0.25.6", features = ["wasm"] }
+tree-sitter = { version = "0.25.10", features = ["wasm"] }
tree-sitter-bash = "0.25.0"
tree-sitter-c = "0.23"
tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev = "5cb9b693cfd7bfacab1d9ff4acac1a4150700609" }
@@ -683,7 +688,7 @@ tree-sitter-html = "0.23"
tree-sitter-jsdoc = "0.23"
tree-sitter-json = "0.24"
tree-sitter-md = { git = "https://github.com/tree-sitter-grammars/tree-sitter-markdown", rev = "9a23c1a96c0513d8fc6520972beedd419a973539" }
-tree-sitter-python = { git = "https://github.com/zed-industries/tree-sitter-python", rev = "218fcbf3fda3d029225f3dec005cb497d111b35e" }
+tree-sitter-python = "0.25"
tree-sitter-regex = "0.24"
tree-sitter-ruby = "0.23"
tree-sitter-rust = "0.24"
@@ -799,7 +804,7 @@ wasmtime = { opt-level = 3 }
activity_indicator = { codegen-units = 1 }
assets = { codegen-units = 1 }
breadcrumbs = { codegen-units = 1 }
-collections = { codegen-units = 1 }
+zed-collections = { codegen-units = 1 }
command_palette = { codegen-units = 1 }
command_palette_hooks = { codegen-units = 1 }
extension_cli = { codegen-units = 1 }
@@ -810,6 +815,7 @@ image_viewer = { codegen-units = 1 }
edit_prediction_button = { codegen-units = 1 }
install_cli = { codegen-units = 1 }
journal = { codegen-units = 1 }
+json_schema_store = { codegen-units = 1 }
lmstudio = { codegen-units = 1 }
menu = { codegen-units = 1 }
notifications = { codegen-units = 1 }
@@ -818,11 +824,11 @@ outline = { codegen-units = 1 }
paths = { codegen-units = 1 }
prettier = { codegen-units = 1 }
project_symbols = { codegen-units = 1 }
-refineable = { codegen-units = 1 }
+zed-refineable = { codegen-units = 1 }
release_channel = { codegen-units = 1 }
reqwest_client = { codegen-units = 1 }
rich_text = { codegen-units = 1 }
-semantic_version = { codegen-units = 1 }
+zed-semantic-version = { codegen-units = 1 }
session = { codegen-units = 1 }
snippet = { codegen-units = 1 }
snippets_ui = { codegen-units = 1 }
@@ -861,6 +867,7 @@ todo = "deny"
declare_interior_mutable_const = "deny"
redundant_clone = "deny"
+disallowed_methods = "deny"
# We currently do not restrict any style rules
# as it slows down shipping code to Zed.
diff --git a/GEMINI.md b/GEMINI.md
new file mode 120000
index 0000000000000000000000000000000000000000..8a63b64bdb0afcda986ba715cb39849ac574e096
--- /dev/null
+++ b/GEMINI.md
@@ -0,0 +1 @@
+.rules
\ No newline at end of file
diff --git a/assets/icons/paperclip.svg b/assets/icons/paperclip.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7a864103c013823096b523f3e0f56db2d7e76009
--- /dev/null
+++ b/assets/icons/paperclip.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json
index ceb25c1835172068a809c6e5492d000cde7da5fe..b59a823816ffd81fa78deb1c8fac84f8c654d169 100644
--- a/assets/keymaps/default-linux.json
+++ b/assets/keymaps/default-linux.json
@@ -30,7 +30,8 @@
"ctrl-+": ["zed::IncreaseBufferFontSize", { "persist": false }],
"ctrl--": ["zed::DecreaseBufferFontSize", { "persist": false }],
"ctrl-0": ["zed::ResetBufferFontSize", { "persist": false }],
- "ctrl-,": "zed::OpenSettings",
+ "ctrl-,": "zed::OpenSettingsEditor",
+ "ctrl-alt-,": "zed::OpenSettings",
"ctrl-q": "zed::Quit",
"f4": "debugger::Start",
"shift-f5": "debugger::Stop",
@@ -250,7 +251,7 @@
"alt-enter": "agent::ContinueWithBurnMode",
"ctrl-y": "agent::AllowOnce",
"ctrl-alt-y": "agent::AllowAlways",
- "ctrl-d": "agent::RejectOnce"
+ "ctrl-alt-z": "agent::RejectOnce"
}
},
{
@@ -369,7 +370,15 @@
"bindings": {
"new": "rules_library::NewRule",
"ctrl-n": "rules_library::NewRule",
- "ctrl-shift-s": "rules_library::ToggleDefaultRule"
+ "ctrl-shift-s": "rules_library::ToggleDefaultRule",
+ "ctrl-w": "workspace::CloseWindow"
+ }
+ },
+ {
+ "context": "SettingsWindow",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-w": "workspace::CloseWindow"
}
},
{
diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json
index 4cf25c3b71047b7eb19791cee91f062d5720fe61..dc4d74f84d040576c1c02378472618c105f7aac8 100644
--- a/assets/keymaps/default-macos.json
+++ b/assets/keymaps/default-macos.json
@@ -39,7 +39,8 @@
"cmd-+": ["zed::IncreaseBufferFontSize", { "persist": false }],
"cmd--": ["zed::DecreaseBufferFontSize", { "persist": false }],
"cmd-0": ["zed::ResetBufferFontSize", { "persist": false }],
- "cmd-,": "zed::OpenSettings",
+ "cmd-,": "zed::OpenSettingsEditor",
+ "cmd-alt-,": "zed::OpenSettings",
"cmd-q": "zed::Quit",
"cmd-h": "zed::Hide",
"alt-cmd-h": "zed::HideOthers",
@@ -289,7 +290,7 @@
"alt-enter": "agent::ContinueWithBurnMode",
"cmd-y": "agent::AllowOnce",
"cmd-alt-y": "agent::AllowAlways",
- "cmd-d": "agent::RejectOnce"
+ "cmd-alt-z": "agent::RejectOnce"
}
},
{
@@ -430,6 +431,13 @@
"cmd-w": "workspace::CloseWindow"
}
},
+ {
+ "context": "SettingsWindow",
+ "use_key_equivalents": true,
+ "bindings": {
+ "cmd-w": "workspace::CloseWindow"
+ }
+ },
{
"context": "BufferSearchBar",
"use_key_equivalents": true,
diff --git a/assets/keymaps/default-windows.json b/assets/keymaps/default-windows.json
index fe9efe0f720e7c76bd25878426b9601e6910099b..3e513d8603f611b92abaff654ee5676d0be85ad8 100644
--- a/assets/keymaps/default-windows.json
+++ b/assets/keymaps/default-windows.json
@@ -29,7 +29,8 @@
"ctrl-shift-=": ["zed::IncreaseBufferFontSize", { "persist": false }],
"ctrl--": ["zed::DecreaseBufferFontSize", { "persist": false }],
"ctrl-0": ["zed::ResetBufferFontSize", { "persist": false }],
- "ctrl-,": "zed::OpenSettings",
+ "ctrl-,": "zed::OpenSettingsEditor",
+ "ctrl-alt-,": "zed::OpenSettings",
"ctrl-q": "zed::Quit",
"f4": "debugger::Start",
"shift-f5": "debugger::Stop",
@@ -251,7 +252,7 @@
"alt-enter": "agent::ContinueWithBurnMode",
"ctrl-y": "agent::AllowOnce",
"ctrl-alt-y": "agent::AllowAlways",
- "ctrl-d": "agent::RejectOnce"
+ "ctrl-alt-z": "agent::RejectOnce"
}
},
{
@@ -345,7 +346,7 @@
}
},
{
- "context": "AcpThread > Editor",
+ "context": "AcpThread > Editor && !use_modifier_to_send",
"use_key_equivalents": true,
"bindings": {
"enter": "agent::Chat",
@@ -355,6 +356,17 @@
"shift-tab": "agent::CycleModeSelector"
}
},
+ {
+ "context": "AcpThread > Editor && use_modifier_to_send",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-enter": "agent::Chat",
+ "ctrl-shift-r": "agent::OpenAgentDiff",
+ "ctrl-shift-y": "agent::KeepAll",
+ "ctrl-shift-n": "agent::RejectAll",
+ "shift-tab": "agent::CycleModeSelector"
+ }
+ },
{
"context": "ThreadHistory",
"use_key_equivalents": true,
@@ -367,7 +379,15 @@
"use_key_equivalents": true,
"bindings": {
"ctrl-n": "rules_library::NewRule",
- "ctrl-shift-s": "rules_library::ToggleDefaultRule"
+ "ctrl-shift-s": "rules_library::ToggleDefaultRule",
+ "ctrl-w": "workspace::CloseWindow"
+ }
+ },
+ {
+ "context": "SettingsWindow",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-w": "workspace::CloseWindow"
}
},
{
diff --git a/assets/keymaps/macos/emacs.json b/assets/keymaps/macos/emacs.json
index 0f936ba2f968abe0759e4bb294271a5e5f501848..78e2235965335ac2914355fc0c51abe38d390897 100755
--- a/assets/keymaps/macos/emacs.json
+++ b/assets/keymaps/macos/emacs.json
@@ -4,6 +4,7 @@
// from the command palette.
[
{
+ "context": "!GitPanel",
"bindings": {
"ctrl-g": "menu::Cancel"
}
diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json
index 5d98fd2a1c833f3003edf623b997cb321656fdbe..4d296667ff572a644d4f6b37e1704c0b250a652c 100644
--- a/assets/keymaps/vim.json
+++ b/assets/keymaps/vim.json
@@ -240,6 +240,7 @@
"delete": "vim::DeleteRight",
"g shift-j": "vim::JoinLinesNoWhitespace",
"y": "vim::PushYank",
+ "shift-y": "vim::YankLine",
"x": "vim::DeleteRight",
"shift-x": "vim::DeleteLeft",
"ctrl-a": "vim::Increment",
@@ -883,10 +884,12 @@
"/": "project_panel::NewSearchInDirectory",
"d": "project_panel::NewDirectory",
"enter": "project_panel::OpenPermanent",
- "escape": "project_panel::ToggleFocus",
+ "escape": "vim::ToggleProjectPanelFocus",
"h": "project_panel::CollapseSelectedEntry",
- "j": "menu::SelectNext",
- "k": "menu::SelectPrevious",
+ "j": "vim::MenuSelectNext",
+ "k": "vim::MenuSelectPrevious",
+ "down": "vim::MenuSelectNext",
+ "up": "vim::MenuSelectPrevious",
"l": "project_panel::ExpandSelectedEntry",
"shift-d": "project_panel::Delete",
"shift-r": "project_panel::Rename",
@@ -905,7 +908,22 @@
"{": "project_panel::SelectPrevDirectory",
"shift-g": "menu::SelectLast",
"g g": "menu::SelectFirst",
- "-": "project_panel::SelectParent"
+ "-": "project_panel::SelectParent",
+ "ctrl-u": "project_panel::ScrollUp",
+ "ctrl-d": "project_panel::ScrollDown",
+ "z t": "project_panel::ScrollCursorTop",
+ "z z": "project_panel::ScrollCursorCenter",
+ "z b": "project_panel::ScrollCursorBottom",
+ "0": ["vim::Number", 0],
+ "1": ["vim::Number", 1],
+ "2": ["vim::Number", 2],
+ "3": ["vim::Number", 3],
+ "4": ["vim::Number", 4],
+ "5": ["vim::Number", 5],
+ "6": ["vim::Number", 6],
+ "7": ["vim::Number", 7],
+ "8": ["vim::Number", 8],
+ "9": ["vim::Number", 9]
}
},
{
diff --git a/assets/prompts/content_prompt.hbs b/assets/prompts/content_prompt.hbs
index e601e6dc63376af54e6e1a9bfa53cdbd57190e22..6db53ff48ff251b90f9120398852a9160ca41755 100644
--- a/assets/prompts/content_prompt.hbs
+++ b/assets/prompts/content_prompt.hbs
@@ -29,7 +29,9 @@ Generate {{content_type}} based on the following prompt:
Match the indentation in the original file in the inserted {{content_type}}, don't include any indentation on blank lines.
-Immediately start with the following format with no remarks:
+Return ONLY the {{content_type}} to insert. Do NOT include any XML tags like , , or any surrounding markup from the input.
+
+Respond with a code block containing the {{content_type}} to insert. Replace \{{INSERTED_CODE}} with your actual {{content_type}}:
```
\{{INSERTED_CODE}}
@@ -66,7 +68,9 @@ Only make changes that are necessary to fulfill the prompt, leave everything els
Start at the indentation level in the original file in the rewritten {{content_type}}. Don't stop until you've rewritten the entire section, even if you have no more changes to make, always write out the whole section with no unnecessary elisions.
-Immediately start with the following format with no remarks:
+Return ONLY the rewritten {{content_type}}. Do NOT include any XML tags like , , or any surrounding markup from the input.
+
+Respond with a code block containing the rewritten {{content_type}}. Replace \{{REWRITTEN_CODE}} with your actual rewritten {{content_type}}:
```
\{{REWRITTEN_CODE}}
diff --git a/assets/settings/default.json b/assets/settings/default.json
index 298bce824569c85416e7557231357ab016473a22..a77ce82f00b746adf9efa3886474f4d0fe53847c 100644
--- a/assets/settings/default.json
+++ b/assets/settings/default.json
@@ -1,5 +1,7 @@
{
- "project_name": null,
+ /// The displayed name of this project. If not set or empty, the root directory name
+ /// will be displayed.
+ "project_name": "",
// The name of the Zed theme to use for the UI.
//
// `mode` is one of:
@@ -72,8 +74,10 @@
"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. Falls back to the UI font size if unset.
- "agent_font_size": null,
+ // The default font size for agent responses in the agent panel. Falls back to the UI font size if unset.
+ "agent_ui_font_size": null,
+ // The default font size for user messages in the agent panel. Falls back to the buffer font size if unset.
+ "agent_buffer_font_size": 12,
// How much to fade out unused code.
"unnecessary_code_fade": 0.3,
// Active pane styling settings.
@@ -1227,6 +1231,10 @@
// 2. Hide the gutter
// "git_gutter": "hide"
"git_gutter": "tracked_files",
+ /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter.
+ ///
+ /// Default: null
+ "gutter_debounce": null,
// Control whether the git blame information is shown inline,
// in the currently focused line.
"inline_blame": {
@@ -1242,6 +1250,9 @@
// The minimum column number to show the inline blame information at
"min_column": 0
},
+ "blame": {
+ "show_avatar": true
+ },
// Control which information is shown in the branch picker.
"branch_picker": {
"show_author_name": true
@@ -1322,6 +1333,8 @@
},
// Status bar-related settings.
"status_bar": {
+ // Whether to show the status bar.
+ "experimental.show": true,
// Whether to show the active language button in the status bar.
"active_language_button": true,
// Whether to show the cursor position button in the status bar.
@@ -1557,6 +1570,14 @@
"auto_install_extensions": {
"html": true
},
+ // The capabilities granted to extensions.
+ //
+ // This list can be customized to restrict what extensions are able to do.
+ "granted_extension_capabilities": [
+ { "kind": "process:exec", "command": "*", "args": ["**"] },
+ { "kind": "download_file", "host": "*", "path": ["**"] },
+ { "kind": "npm:install", "package": "*" }
+ ],
// Controls how completions are processed for this language.
"completions": {
// Controls how words are completed.
@@ -1855,21 +1876,19 @@
// Allows to enable/disable formatting with Prettier
// and configure default Prettier, used when no project-level Prettier installation is found.
"prettier": {
- // // Whether to consider prettier formatter or not when attempting to format a file.
- "allowed": false
- //
- // // Use regular Prettier json configuration.
- // // If Prettier is allowed, Zed will use this for its Prettier instance for any applicable file, if
- // // the project has no other Prettier installed.
- // "plugins": [],
- //
- // // Use regular Prettier json configuration.
- // // If Prettier is allowed, Zed will use this for its Prettier instance for any applicable file, if
- // // the project has no other Prettier installed.
+ // Enables or disables formatting with Prettier for any given language.
+ "allowed": false,
+ // Forces Prettier integration to use a specific parser name when formatting files with the language.
+ "plugins": [],
+ // Default Prettier options, in the format as in package.json section for Prettier.
+ // If project installs Prettier via its package.json, these options will be ignored.
// "trailingComma": "es5",
// "tabWidth": 4,
// "semi": false,
// "singleQuote": true
+ // Forces Prettier integration to use a specific parser name when formatting files with the language
+ // when set to a non-empty string.
+ "parser": ""
},
// Settings for auto-closing of JSX tags.
"jsx_tag_auto_close": {
@@ -2019,7 +2038,7 @@
// Examples:
// "profiles": {
// "Presenting": {
- // "agent_font_size": 20.0,
+ // "agent_ui_font_size": 20.0,
// "buffer_font_size": 20.0,
// "theme": "One Light",
// "ui_font_size": 20.0
diff --git a/assets/sounds/guest_joined_call.wav b/assets/sounds/guest_joined_call.wav
new file mode 100644
index 0000000000000000000000000000000000000000..336a6ca754b09c408f63c411193157a20c14bb78
Binary files /dev/null and b/assets/sounds/guest_joined_call.wav differ
diff --git a/assets/themes/ayu/ayu.json b/assets/themes/ayu/ayu.json
index f71048caafba7156b53fd8637ca35c715ad300f2..7c84c603bda7fd7590067ec9f566f3582ba6aefd 100644
--- a/assets/themes/ayu/ayu.json
+++ b/assets/themes/ayu/ayu.json
@@ -192,7 +192,7 @@
"font_weight": null
},
"comment": {
- "color": "#abb5be8c",
+ "color": "#5c6773ff",
"font_style": null,
"font_weight": null
},
@@ -239,7 +239,7 @@
"hint": {
"color": "#628b80ff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#ff8f3fff",
@@ -583,7 +583,7 @@
"font_weight": null
},
"comment": {
- "color": "#787b8099",
+ "color": "#abb0b6ff",
"font_style": null,
"font_weight": null
},
@@ -630,7 +630,7 @@
"hint": {
"color": "#8ca7c2ff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#fa8d3eff",
@@ -974,7 +974,7 @@
"font_weight": null
},
"comment": {
- "color": "#b8cfe680",
+ "color": "#5c6773ff",
"font_style": null,
"font_weight": null
},
@@ -1021,7 +1021,7 @@
"hint": {
"color": "#7399a3ff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#ffad65ff",
diff --git a/assets/themes/gruvbox/gruvbox.json b/assets/themes/gruvbox/gruvbox.json
index fc11cac55f638349778c88869dcb217c89111022..4e6f8334b269e3e5090b0f91d995834906c09083 100644
--- a/assets/themes/gruvbox/gruvbox.json
+++ b/assets/themes/gruvbox/gruvbox.json
@@ -248,7 +248,7 @@
"hint": {
"color": "#8c957dff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#fb4833ff",
@@ -653,7 +653,7 @@
"hint": {
"color": "#8c957dff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#fb4833ff",
@@ -1058,7 +1058,7 @@
"hint": {
"color": "#8c957dff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#fb4833ff",
@@ -1463,7 +1463,7 @@
"hint": {
"color": "#677562ff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#9d0006ff",
@@ -1868,7 +1868,7 @@
"hint": {
"color": "#677562ff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#9d0006ff",
@@ -2273,7 +2273,7 @@
"hint": {
"color": "#677562ff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#9d0006ff",
diff --git a/assets/themes/one/one.json b/assets/themes/one/one.json
index 7cc8c96a23f32aab69596722188e3c5ec87aba08..6849cd05dc70752216789ae04e81fad232f7b14b 100644
--- a/assets/themes/one/one.json
+++ b/assets/themes/one/one.json
@@ -244,7 +244,7 @@
"hint": {
"color": "#788ca6ff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#b477cfff",
@@ -643,7 +643,7 @@
"hint": {
"color": "#7274a7ff",
"font_style": null,
- "font_weight": 700
+ "font_weight": null
},
"keyword": {
"color": "#a449abff",
diff --git a/clippy.toml b/clippy.toml
index e606ad4c79b5cfe289b1f8460b1f46715103fe1b..57f6f59385a4885730015c3d09f040a5f340d379 100644
--- a/clippy.toml
+++ b/clippy.toml
@@ -5,3 +5,14 @@ ignore-interior-mutability = [
# and Hash impls do not use fields with interior mutability.
"agent::context::AgentContextKey"
]
+disallowed-methods = [
+ { path = "std::process::Command::spawn", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::spawn" },
+ { path = "std::process::Command::output", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::output" },
+ { path = "std::process::Command::status", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::status" },
+]
+disallowed-types = [
+ # { path = "std::collections::HashMap", replacement = "collections::HashMap" },
+ # { path = "std::collections::HashSet", replacement = "collections::HashSet" },
+ # { path = "indexmap::IndexSet", replacement = "collections::IndexSet" },
+ # { path = "indexmap::IndexMap", replacement = "collections::IndexMap" },
+]
diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs
index 185eed3cd7a89c8c0dad17979e34c7ffc5684e08..61486a475c4601a9d9201d3a6920c63566a1ba36 100644
--- a/crates/acp_thread/src/acp_thread.rs
+++ b/crates/acp_thread/src/acp_thread.rs
@@ -3,6 +3,7 @@ mod diff;
mod mention;
mod terminal;
+use ::terminal::terminal_settings::TerminalSettings;
use agent_settings::AgentSettings;
use collections::HashSet;
pub use connection::*;
@@ -11,7 +12,7 @@ use language::language_settings::FormatOnSave;
pub use mention::*;
use project::lsp_store::{FormatTrigger, LspFormatTarget};
use serde::{Deserialize, Serialize};
-use settings::Settings as _;
+use settings::{Settings as _, SettingsLocation};
use task::{Shell, ShellBuilder};
pub use terminal::*;
@@ -34,7 +35,7 @@ use std::rc::Rc;
use std::time::{Duration, Instant};
use std::{fmt::Display, mem, path::PathBuf, sync::Arc};
use ui::App;
-use util::{ResultExt, get_default_system_shell};
+use util::{ResultExt, get_default_system_shell_preferring_bash};
use uuid::Uuid;
#[derive(Debug)]
@@ -573,7 +574,7 @@ impl ToolCallContent {
))),
acp::ToolCallContent::Diff { diff } => Ok(Self::Diff(cx.new(|cx| {
Diff::finalized(
- diff.path.to_string_lossy().to_string(),
+ diff.path.to_string_lossy().into_owned(),
diff.old_text,
diff.new_text,
language_registry,
@@ -787,6 +788,8 @@ pub struct AcpThread {
prompt_capabilities: acp::PromptCapabilities,
_observe_prompt_capabilities: Task>,
terminals: HashMap>,
+ pending_terminal_output: HashMap>>,
+ pending_terminal_exit: HashMap,
}
#[derive(Debug)]
@@ -809,6 +812,126 @@ pub enum AcpThreadEvent {
impl EventEmitter for AcpThread {}
+#[derive(Debug, Clone)]
+pub enum TerminalProviderEvent {
+ Created {
+ terminal_id: acp::TerminalId,
+ label: String,
+ cwd: Option,
+ output_byte_limit: Option,
+ terminal: Entity<::terminal::Terminal>,
+ },
+ Output {
+ terminal_id: acp::TerminalId,
+ data: Vec,
+ },
+ TitleChanged {
+ terminal_id: acp::TerminalId,
+ title: String,
+ },
+ Exit {
+ terminal_id: acp::TerminalId,
+ status: acp::TerminalExitStatus,
+ },
+}
+
+#[derive(Debug, Clone)]
+pub enum TerminalProviderCommand {
+ WriteInput {
+ terminal_id: acp::TerminalId,
+ bytes: Vec,
+ },
+ Resize {
+ terminal_id: acp::TerminalId,
+ cols: u16,
+ rows: u16,
+ },
+ Close {
+ terminal_id: acp::TerminalId,
+ },
+}
+
+impl AcpThread {
+ pub fn on_terminal_provider_event(
+ &mut self,
+ event: TerminalProviderEvent,
+ cx: &mut Context,
+ ) {
+ match event {
+ TerminalProviderEvent::Created {
+ terminal_id,
+ label,
+ cwd,
+ output_byte_limit,
+ terminal,
+ } => {
+ let entity = self.register_terminal_created(
+ terminal_id.clone(),
+ label,
+ cwd,
+ output_byte_limit,
+ terminal,
+ cx,
+ );
+
+ if let Some(mut chunks) = self.pending_terminal_output.remove(&terminal_id) {
+ for data in chunks.drain(..) {
+ entity.update(cx, |term, cx| {
+ term.inner().update(cx, |inner, cx| {
+ inner.write_output(&data, cx);
+ })
+ });
+ }
+ }
+
+ if let Some(_status) = self.pending_terminal_exit.remove(&terminal_id) {
+ entity.update(cx, |_term, cx| {
+ cx.notify();
+ });
+ }
+
+ cx.notify();
+ }
+ TerminalProviderEvent::Output { terminal_id, data } => {
+ if let Some(entity) = self.terminals.get(&terminal_id) {
+ entity.update(cx, |term, cx| {
+ term.inner().update(cx, |inner, cx| {
+ inner.write_output(&data, cx);
+ })
+ });
+ } else {
+ self.pending_terminal_output
+ .entry(terminal_id)
+ .or_default()
+ .push(data);
+ }
+ }
+ TerminalProviderEvent::TitleChanged { terminal_id, title } => {
+ if let Some(entity) = self.terminals.get(&terminal_id) {
+ entity.update(cx, |term, cx| {
+ term.inner().update(cx, |inner, cx| {
+ inner.breadcrumb_text = title;
+ cx.emit(::terminal::Event::BreadcrumbsChanged);
+ })
+ });
+ }
+ }
+ TerminalProviderEvent::Exit {
+ terminal_id,
+ status,
+ } => {
+ if let Some(entity) = self.terminals.get(&terminal_id) {
+ entity.update(cx, |_term, cx| {
+ cx.notify();
+ });
+ } else {
+ self.pending_terminal_exit.insert(terminal_id, status);
+ }
+ }
+ }
+ }
+}
+
#[derive(PartialEq, Eq, Debug)]
pub enum ThreadStatus {
Idle,
@@ -886,6 +1009,8 @@ impl AcpThread {
prompt_capabilities,
_observe_prompt_capabilities: task,
terminals: HashMap::default(),
+ pending_terminal_output: HashMap::default(),
+ pending_terminal_exit: HashMap::default(),
}
}
@@ -1961,16 +2086,24 @@ impl AcpThread {
) -> Task>> {
let env = match &cwd {
Some(dir) => self.project.update(cx, |project, cx| {
- project.directory_environment(dir.as_path().into(), cx)
+ let worktree = project.find_worktree(dir.as_path(), cx);
+ let shell = TerminalSettings::get(
+ worktree.as_ref().map(|(worktree, path)| SettingsLocation {
+ worktree_id: worktree.read(cx).id(),
+ path: &path,
+ }),
+ cx,
+ )
+ .shell
+ .clone();
+ project.directory_environment(&shell, dir.as_path().into(), cx)
}),
None => Task::ready(None).shared(),
};
-
let env = cx.spawn(async move |_, _| {
let mut env = env.await.unwrap_or_default();
- if cfg!(unix) {
- env.insert("PAGER".into(), "cat".into());
- }
+ // Disables paging for `git` and hopefully other commands
+ env.insert("PAGER".into(), "".into());
for var in extra_env {
env.insert(var.name, var.value);
}
@@ -1985,24 +2118,22 @@ impl AcpThread {
let terminal_id = terminal_id.clone();
async move |_this, cx| {
let env = env.await;
- let (command, args) = ShellBuilder::new(
- project
- .update(cx, |project, cx| {
- project
- .remote_client()
- .and_then(|r| r.read(cx).default_system_shell())
- })?
- .as_deref(),
- &Shell::Program(get_default_system_shell()),
- )
- .redirect_stdin_to_dev_null()
- .build(Some(command), &args);
+ let shell = project
+ .update(cx, |project, cx| {
+ project
+ .remote_client()
+ .and_then(|r| r.read(cx).default_system_shell())
+ })?
+ .unwrap_or_else(|| get_default_system_shell_preferring_bash());
+ let (task_command, task_args) = ShellBuilder::new(&Shell::Program(shell))
+ .redirect_stdin_to_dev_null()
+ .build(Some(command.clone()), &args);
let terminal = project
.update(cx, |project, cx| {
project.create_terminal_task(
task::SpawnInTerminal {
- command: Some(command.clone()),
- args: args.clone(),
+ command: Some(task_command),
+ args: task_args,
cwd: cwd.clone(),
env,
..Default::default()
@@ -2079,6 +2210,32 @@ impl AcpThread {
pub fn emit_load_error(&mut self, error: LoadError, cx: &mut Context) {
cx.emit(AcpThreadEvent::LoadError(error));
}
+
+ pub fn register_terminal_created(
+ &mut self,
+ terminal_id: acp::TerminalId,
+ command_label: String,
+ working_dir: Option,
+ output_byte_limit: Option,
+ terminal: Entity<::terminal::Terminal>,
+ cx: &mut Context,
+ ) -> Entity {
+ let language_registry = self.project.read(cx).languages().clone();
+
+ let entity = cx.new(|cx| {
+ Terminal::new(
+ terminal_id.clone(),
+ &command_label,
+ working_dir.clone(),
+ output_byte_limit.map(|l| l as usize),
+ terminal,
+ language_registry,
+ cx,
+ )
+ });
+ self.terminals.insert(terminal_id.clone(), entity.clone());
+ entity
+ }
}
fn markdown_for_raw_output(
@@ -2155,6 +2312,145 @@ mod tests {
});
}
+ #[gpui::test]
+ async fn test_terminal_output_buffered_before_created_renders(cx: &mut gpui::TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ let project = Project::test(fs, [], cx).await;
+ let connection = Rc::new(FakeAgentConnection::new());
+ let thread = cx
+ .update(|cx| connection.new_thread(project, std::path::Path::new(path!("/test")), cx))
+ .await
+ .unwrap();
+
+ let terminal_id = acp::TerminalId(uuid::Uuid::new_v4().to_string().into());
+
+ // Send Output BEFORE Created - should be buffered by acp_thread
+ thread.update(cx, |thread, cx| {
+ thread.on_terminal_provider_event(
+ TerminalProviderEvent::Output {
+ terminal_id: terminal_id.clone(),
+ data: b"hello buffered".to_vec(),
+ },
+ cx,
+ );
+ });
+
+ // Create a display-only terminal and then send Created
+ let lower = cx.new(|cx| {
+ let builder = ::terminal::TerminalBuilder::new_display_only(
+ ::terminal::terminal_settings::CursorShape::default(),
+ ::terminal::terminal_settings::AlternateScroll::On,
+ None,
+ 0,
+ )
+ .unwrap();
+ builder.subscribe(cx)
+ });
+
+ thread.update(cx, |thread, cx| {
+ thread.on_terminal_provider_event(
+ TerminalProviderEvent::Created {
+ terminal_id: terminal_id.clone(),
+ label: "Buffered Test".to_string(),
+ cwd: None,
+ output_byte_limit: None,
+ terminal: lower.clone(),
+ },
+ cx,
+ );
+ });
+
+ // After Created, buffered Output should have been flushed into the renderer
+ let content = thread.read_with(cx, |thread, cx| {
+ let term = thread.terminal(terminal_id.clone()).unwrap();
+ term.read_with(cx, |t, cx| t.inner().read(cx).get_content())
+ });
+
+ assert!(
+ content.contains("hello buffered"),
+ "expected buffered output to render, got: {content}"
+ );
+ }
+
+ #[gpui::test]
+ async fn test_terminal_output_and_exit_buffered_before_created(cx: &mut gpui::TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ let project = Project::test(fs, [], cx).await;
+ let connection = Rc::new(FakeAgentConnection::new());
+ let thread = cx
+ .update(|cx| connection.new_thread(project, std::path::Path::new(path!("/test")), cx))
+ .await
+ .unwrap();
+
+ let terminal_id = acp::TerminalId(uuid::Uuid::new_v4().to_string().into());
+
+ // Send Output BEFORE Created
+ thread.update(cx, |thread, cx| {
+ thread.on_terminal_provider_event(
+ TerminalProviderEvent::Output {
+ terminal_id: terminal_id.clone(),
+ data: b"pre-exit data".to_vec(),
+ },
+ cx,
+ );
+ });
+
+ // Send Exit BEFORE Created
+ thread.update(cx, |thread, cx| {
+ thread.on_terminal_provider_event(
+ TerminalProviderEvent::Exit {
+ terminal_id: terminal_id.clone(),
+ status: acp::TerminalExitStatus {
+ exit_code: Some(0),
+ signal: None,
+ meta: None,
+ },
+ },
+ cx,
+ );
+ });
+
+ // Now create a display-only lower-level terminal and send Created
+ let lower = cx.new(|cx| {
+ let builder = ::terminal::TerminalBuilder::new_display_only(
+ ::terminal::terminal_settings::CursorShape::default(),
+ ::terminal::terminal_settings::AlternateScroll::On,
+ None,
+ 0,
+ )
+ .unwrap();
+ builder.subscribe(cx)
+ });
+
+ thread.update(cx, |thread, cx| {
+ thread.on_terminal_provider_event(
+ TerminalProviderEvent::Created {
+ terminal_id: terminal_id.clone(),
+ label: "Buffered Exit Test".to_string(),
+ cwd: None,
+ output_byte_limit: None,
+ terminal: lower.clone(),
+ },
+ cx,
+ );
+ });
+
+ // Output should be present after Created (flushed from buffer)
+ let content = thread.read_with(cx, |thread, cx| {
+ let term = thread.terminal(terminal_id.clone()).unwrap();
+ term.read_with(cx, |t, cx| t.inner().read(cx).get_content())
+ });
+
+ assert!(
+ content.contains("pre-exit data"),
+ "expected pre-exit data to render, got: {content}"
+ );
+ }
+
#[gpui::test]
async fn test_push_user_content_block(cx: &mut gpui::TestAppContext) {
init_test(cx);
diff --git a/crates/acp_thread/src/diff.rs b/crates/acp_thread/src/diff.rs
index 753f157af934d6c92fcd6766aa645ec45c6b22f7..15de12af27fe233bad4ad8ebb2893ffa5fbdd598 100644
--- a/crates/acp_thread/src/diff.rs
+++ b/crates/acp_thread/src/diff.rs
@@ -31,7 +31,7 @@ impl Diff {
let buffer = new_buffer.clone();
async move |_, cx| {
let language = language_registry
- .language_for_file_path(Path::new(&path))
+ .load_language_for_file_path(Path::new(&path))
.await
.log_err();
diff --git a/crates/agent/src/context.rs b/crates/agent/src/context.rs
index 80062b1e46f01d1740359e6832c73a2aec255031..3b2922087a94c497c07f1df67a8d4d9adf759909 100644
--- a/crates/agent/src/context.rs
+++ b/crates/agent/src/context.rs
@@ -187,7 +187,7 @@ impl FileContextHandle {
log::error!("file context missing path");
return Task::ready(None);
};
- let full_path = file.full_path(cx).to_string_lossy().to_string();
+ let full_path = file.full_path(cx).to_string_lossy().into_owned();
let rope = buffer_ref.as_rope().clone();
let buffer = self.buffer.clone();
@@ -283,7 +283,7 @@ impl DirectoryContextHandle {
let descendants_future = future::join_all(file_paths.into_iter().map(|path| {
let worktree_ref = worktree.read(cx);
let worktree_id = worktree_ref.id();
- let full_path = worktree_ref.full_path(&path).to_string_lossy().to_string();
+ let full_path = worktree_ref.full_path(&path).to_string_lossy().into_owned();
let rel_path = path
.strip_prefix(&directory_path)
@@ -403,7 +403,7 @@ impl SymbolContextHandle {
log::error!("symbol context's file has no path");
return Task::ready(None);
};
- let full_path = file.full_path(cx).to_string_lossy().to_string();
+ let full_path = file.full_path(cx).to_string_lossy().into_owned();
let line_range = self.enclosing_range.to_point(&buffer_ref.snapshot());
let text = self.text(cx);
let buffer = self.buffer.clone();
@@ -476,7 +476,7 @@ impl SelectionContextHandle {
let text = self.text(cx);
let buffer = self.buffer.clone();
let context = AgentContext::Selection(SelectionContext {
- full_path: full_path.to_string_lossy().to_string(),
+ full_path: full_path.to_string_lossy().into_owned(),
line_range: self.line_range(cx),
text,
handle: self,
diff --git a/crates/agent/src/context_store.rs b/crates/agent/src/context_store.rs
index c4aa0abc25544e943e0e91c3a95c8dd8fafad8ef..cf35840cc4215695a966931701257c838c00af18 100644
--- a/crates/agent/src/context_store.rs
+++ b/crates/agent/src/context_store.rs
@@ -312,7 +312,7 @@ impl ContextStore {
let item = image_item.read(cx);
this.insert_image(
Some(item.project_path(cx)),
- Some(item.file.full_path(cx).to_string_lossy().to_string()),
+ Some(item.file.full_path(cx).to_string_lossy().into_owned()),
item.image.clone(),
remove_if_exists,
cx,
diff --git a/crates/agent/src/history_store.rs b/crates/agent/src/history_store.rs
index 285ce7c04b7268aea24e0a746737f65d7c3608df..4b1795047b7444dc74f8a41097c0c66aa54ecfd9 100644
--- a/crates/agent/src/history_store.rs
+++ b/crates/agent/src/history_store.rs
@@ -155,7 +155,7 @@ impl HistoryStore {
.iter()
.filter_map(|entry| match entry {
HistoryEntryId::Context(path) => path.file_name().map(|file| {
- SerializedRecentOpen::ContextName(file.to_string_lossy().to_string())
+ SerializedRecentOpen::ContextName(file.to_string_lossy().into_owned())
}),
HistoryEntryId::Thread(id) => Some(SerializedRecentOpen::Thread(id.to_string())),
})
diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs
index 0790e8b2894dc5495cd46585a315696118afd33a..ba737cc1cfb708da101fdac1595eeef867f3a4da 100644
--- a/crates/agent/src/thread.rs
+++ b/crates/agent/src/thread.rs
@@ -2875,7 +2875,7 @@ impl Thread {
// Get worktree path and snapshot
let worktree_info = cx.update(|app_cx| {
let worktree = worktree.read(app_cx);
- let path = worktree.abs_path().to_string_lossy().to_string();
+ let path = worktree.abs_path().to_string_lossy().into_owned();
let snapshot = worktree.snapshot();
(path, snapshot)
});
@@ -3276,7 +3276,6 @@ mod tests {
use settings::{LanguageModelParameters, Settings, SettingsStore};
use std::sync::Arc;
use std::time::Duration;
- use theme::ThemeSettings;
use util::path;
use workspace::Workspace;
@@ -5337,7 +5336,7 @@ fn main() {{
thread_store::init(fs.clone(), cx);
workspace::init_settings(cx);
language_model::init_settings(cx);
- ThemeSettings::register(cx);
+ theme::init(theme::LoadThemes::JustBase, cx);
ToolRegistry::default_global(cx);
assistant_tool::init(cx);
diff --git a/crates/agent2/src/history_store.rs b/crates/agent2/src/history_store.rs
index c656456e01780505c355c878c26d2405286e56b2..ff6caacc78e5dba4ee38f160fa6ded7fcb45a845 100644
--- a/crates/agent2/src/history_store.rs
+++ b/crates/agent2/src/history_store.rs
@@ -262,7 +262,7 @@ impl HistoryStore {
.iter()
.filter_map(|entry| match entry {
HistoryEntryId::TextThread(path) => path.file_name().map(|file| {
- SerializedRecentOpen::TextThread(file.to_string_lossy().to_string())
+ SerializedRecentOpen::TextThread(file.to_string_lossy().into_owned())
}),
HistoryEntryId::AcpThread(id) => {
Some(SerializedRecentOpen::AcpThread(id.to_string()))
diff --git a/crates/agent2/src/thread.rs b/crates/agent2/src/thread.rs
index 630b4f906904df2410716f35ef7d15aa0f706e8b..7a991422838305e089dea453c294825e0f1c6e5b 100644
--- a/crates/agent2/src/thread.rs
+++ b/crates/agent2/src/thread.rs
@@ -898,7 +898,7 @@ impl Thread {
// Get worktree path and snapshot
let worktree_info = cx.update(|app_cx| {
let worktree = worktree.read(app_cx);
- let path = worktree.abs_path().to_string_lossy().to_string();
+ let path = worktree.abs_path().to_string_lossy().into_owned();
let snapshot = worktree.snapshot();
(path, snapshot)
});
diff --git a/crates/agent2/src/tools/edit_file_tool.rs b/crates/agent2/src/tools/edit_file_tool.rs
index 3b1bf6f408e88ed0e6bf80b0b55815d122ab1854..7c51df0fae274e2d5906aa73e70c30105b1a2353 100644
--- a/crates/agent2/src/tools/edit_file_tool.rs
+++ b/crates/agent2/src/tools/edit_file_tool.rs
@@ -218,7 +218,7 @@ impl AgentTool for EditFileTool {
.read(cx)
.short_full_path_for_project_path(&project_path, cx)
})
- .unwrap_or(input.path.to_string_lossy().to_string())
+ .unwrap_or(input.path.to_string_lossy().into_owned())
.into(),
Err(raw_input) => {
if let Some(input) =
@@ -476,7 +476,7 @@ impl AgentTool for EditFileTool {
) -> Result<()> {
event_stream.update_diff(cx.new(|cx| {
Diff::finalized(
- output.input_path.to_string_lossy().to_string(),
+ output.input_path.to_string_lossy().into_owned(),
Some(output.old_text.to_string()),
output.new_text,
self.language_registry.clone(),
diff --git a/crates/agent2/src/tools/open_tool.rs b/crates/agent2/src/tools/open_tool.rs
index 595a9f380b752635f97ef5d1819a1140c1db8be0..b98ae9af3bd98cd44bc9348e72519ceea53c6292 100644
--- a/crates/agent2/src/tools/open_tool.rs
+++ b/crates/agent2/src/tools/open_tool.rs
@@ -104,7 +104,7 @@ mod tests {
async fn test_to_absolute_path(cx: &mut TestAppContext) {
init_test(cx);
let temp_dir = TempDir::new().expect("Failed to create temp directory");
- let temp_path = temp_dir.path().to_string_lossy().to_string();
+ let temp_path = temp_dir.path().to_string_lossy().into_owned();
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
diff --git a/crates/agent_servers/Cargo.toml b/crates/agent_servers/Cargo.toml
index ca6db6c663ddb2132c05d716e5b935c5855bccdb..bdf1b72fdc0c2c71d5e445633d1d4a8ce32a6ba4 100644
--- a/crates/agent_servers/Cargo.toml
+++ b/crates/agent_servers/Cargo.toml
@@ -47,6 +47,8 @@ task.workspace = true
tempfile.workspace = true
thiserror.workspace = true
ui.workspace = true
+terminal.workspace = true
+uuid.workspace = true
util.workspace = true
watch.workspace = true
workspace-hack.workspace = true
diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs
index b14c0467c58d3f41e32e602996560e2cc672d76a..57ddfcf9dc9d635e33f252e6b6f35f015581cfc4 100644
--- a/crates/agent_servers/src/acp.rs
+++ b/crates/agent_servers/src/acp.rs
@@ -9,6 +9,7 @@ use futures::io::BufReader;
use project::Project;
use project::agent_server_store::AgentServerCommand;
use serde::Deserialize;
+use task::Shell;
use util::ResultExt as _;
use std::path::PathBuf;
@@ -19,7 +20,9 @@ use thiserror::Error;
use anyhow::{Context as _, Result};
use gpui::{App, AppContext as _, AsyncApp, Entity, SharedString, Task, WeakEntity};
-use acp_thread::{AcpThread, AuthRequired, LoadError};
+use acp_thread::{AcpThread, AuthRequired, LoadError, TerminalProviderEvent};
+use terminal::TerminalBuilder;
+use terminal::terminal_settings::{AlternateScroll, CursorShape};
#[derive(Debug, Error)]
#[error("Unsupported version")]
@@ -79,7 +82,7 @@ impl AcpConnection {
is_remote: bool,
cx: &mut AsyncApp,
) -> Result {
- let mut child = util::command::new_smol_command(command.path);
+ let mut child = util::command::new_smol_command(&command.path);
child
.args(command.args.iter().map(|arg| arg.as_str()))
.envs(command.env.iter().flatten())
@@ -94,6 +97,11 @@ impl AcpConnection {
let stdout = child.stdout.take().context("Failed to take stdout")?;
let stdin = child.stdin.take().context("Failed to take stdin")?;
let stderr = child.stderr.take().context("Failed to take stderr")?;
+ log::info!(
+ "Spawning external agent server: {:?}, {:?}",
+ command.path,
+ command.args
+ );
log::trace!("Spawned (pid: {})", child.id());
let sessions = Rc::new(RefCell::new(HashMap::default()));
@@ -380,6 +388,10 @@ impl AgentConnection for AcpConnection {
match result {
Ok(response) => Ok(response),
Err(err) => {
+ if err.code == acp::ErrorCode::AUTH_REQUIRED.code {
+ return Err(anyhow!(acp::Error::auth_required()));
+ }
+
if err.code != ErrorCode::INTERNAL_ERROR.code {
anyhow::bail!(err)
}
@@ -696,10 +708,100 @@ impl acp::Client for ClientDelegate {
}
}
+ // Clone so we can inspect meta both before and after handing off to the thread
+ let update_clone = notification.update.clone();
+
+ // Pre-handle: if a ToolCall carries terminal_info, create/register a display-only terminal.
+ if let acp::SessionUpdate::ToolCall(tc) = &update_clone {
+ if let Some(meta) = &tc.meta {
+ if let Some(terminal_info) = meta.get("terminal_info") {
+ if let Some(id_str) = terminal_info.get("terminal_id").and_then(|v| v.as_str())
+ {
+ let terminal_id = acp::TerminalId(id_str.into());
+ let cwd = terminal_info
+ .get("cwd")
+ .and_then(|v| v.as_str().map(PathBuf::from));
+
+ // Create a minimal display-only lower-level terminal and register it.
+ let _ = session.thread.update(&mut self.cx.clone(), |thread, cx| {
+ let builder = TerminalBuilder::new_display_only(
+ CursorShape::default(),
+ AlternateScroll::On,
+ None,
+ 0,
+ )?;
+ let lower = cx.new(|cx| builder.subscribe(cx));
+ thread.on_terminal_provider_event(
+ TerminalProviderEvent::Created {
+ terminal_id: terminal_id.clone(),
+ label: tc.title.clone(),
+ cwd,
+ output_byte_limit: None,
+ terminal: lower,
+ },
+ cx,
+ );
+ anyhow::Ok(())
+ });
+ }
+ }
+ }
+ }
+
+ // Forward the update to the acp_thread as usual.
session.thread.update(&mut self.cx.clone(), |thread, cx| {
- thread.handle_session_update(notification.update, cx)
+ thread.handle_session_update(notification.update.clone(), cx)
})??;
+ // Post-handle: stream terminal output/exit if present on ToolCallUpdate meta.
+ if let acp::SessionUpdate::ToolCallUpdate(tcu) = &update_clone {
+ if let Some(meta) = &tcu.meta {
+ if let Some(term_out) = meta.get("terminal_output") {
+ if let Some(id_str) = term_out.get("terminal_id").and_then(|v| v.as_str()) {
+ let terminal_id = acp::TerminalId(id_str.into());
+ if let Some(s) = term_out.get("data").and_then(|v| v.as_str()) {
+ let data = s.as_bytes().to_vec();
+ let _ = session.thread.update(&mut self.cx.clone(), |thread, cx| {
+ thread.on_terminal_provider_event(
+ TerminalProviderEvent::Output {
+ terminal_id: terminal_id.clone(),
+ data,
+ },
+ cx,
+ );
+ });
+ }
+ }
+ }
+
+ // terminal_exit
+ if let Some(term_exit) = meta.get("terminal_exit") {
+ if let Some(id_str) = term_exit.get("terminal_id").and_then(|v| v.as_str()) {
+ let terminal_id = acp::TerminalId(id_str.into());
+ let status = acp::TerminalExitStatus {
+ exit_code: term_exit
+ .get("exit_code")
+ .and_then(|v| v.as_u64())
+ .map(|i| i as u32),
+ signal: term_exit
+ .get("signal")
+ .and_then(|v| v.as_str().map(|s| s.to_string())),
+ meta: None,
+ };
+ let _ = session.thread.update(&mut self.cx.clone(), |thread, cx| {
+ thread.on_terminal_provider_event(
+ TerminalProviderEvent::Exit {
+ terminal_id: terminal_id.clone(),
+ status,
+ },
+ cx,
+ );
+ });
+ }
+ }
+ }
+ }
+
Ok(())
}
@@ -707,25 +809,68 @@ impl acp::Client for ClientDelegate {
&self,
args: acp::CreateTerminalRequest,
) -> Result {
- let terminal = self
- .session_thread(&args.session_id)?
- .update(&mut self.cx.clone(), |thread, cx| {
- thread.create_terminal(
- args.command,
- args.args,
- args.env,
- args.cwd,
- args.output_byte_limit,
+ let thread = self.session_thread(&args.session_id)?;
+ let project = thread.read_with(&self.cx, |thread, _cx| thread.project().clone())?;
+
+ let mut env = if let Some(dir) = &args.cwd {
+ project
+ .update(&mut self.cx.clone(), |project, cx| {
+ project.directory_environment(&task::Shell::System, dir.clone().into(), cx)
+ })?
+ .await
+ .unwrap_or_default()
+ } else {
+ Default::default()
+ };
+ for var in args.env {
+ env.insert(var.name, var.value);
+ }
+
+ // Use remote shell or default system shell, as appropriate
+ let shell = project
+ .update(&mut self.cx.clone(), |project, cx| {
+ project
+ .remote_client()
+ .and_then(|r| r.read(cx).default_system_shell())
+ .map(Shell::Program)
+ })?
+ .unwrap_or(task::Shell::System);
+ let (task_command, task_args) = task::ShellBuilder::new(&shell)
+ .redirect_stdin_to_dev_null()
+ .build(Some(args.command.clone()), &args.args);
+
+ let terminal_entity = project
+ .update(&mut self.cx.clone(), |project, cx| {
+ project.create_terminal_task(
+ task::SpawnInTerminal {
+ command: Some(task_command),
+ args: task_args,
+ cwd: args.cwd.clone(),
+ env,
+ ..Default::default()
+ },
cx,
)
})?
.await?;
- Ok(
- terminal.read_with(&self.cx, |terminal, _| acp::CreateTerminalResponse {
- terminal_id: terminal.id().clone(),
- meta: None,
- })?,
- )
+
+ // Register with renderer
+ let terminal_entity = thread.update(&mut self.cx.clone(), |thread, cx| {
+ thread.register_terminal_created(
+ acp::TerminalId(uuid::Uuid::new_v4().to_string().into()),
+ format!("{} {}", args.command, args.args.join(" ")),
+ args.cwd.clone(),
+ args.output_byte_limit,
+ terminal_entity,
+ cx,
+ )
+ })?;
+ let terminal_id =
+ terminal_entity.read_with(&self.cx, |terminal, _| terminal.id().clone())?;
+ Ok(acp::CreateTerminalResponse {
+ terminal_id,
+ meta: None,
+ })
}
async fn kill_terminal_command(
diff --git a/crates/agent_servers/src/agent_servers.rs b/crates/agent_servers/src/agent_servers.rs
index b9751d7f63053bf073bcc8181f0cc2f8211d5c9f..b44c2123fb5052e2487464d813936cd1edf9821a 100644
--- a/crates/agent_servers/src/agent_servers.rs
+++ b/crates/agent_servers/src/agent_servers.rs
@@ -1,5 +1,6 @@
mod acp;
mod claude;
+mod codex;
mod custom;
mod gemini;
@@ -8,6 +9,7 @@ pub mod e2e_tests;
pub use claude::*;
use client::ProxySettings;
+pub use codex::*;
use collections::HashMap;
pub use custom::*;
use fs::Fs;
@@ -99,6 +101,9 @@ pub fn load_proxy_env(cx: &mut App) -> HashMap {
if let Some(no_proxy) = read_no_proxy_from_env() {
env.insert("NO_PROXY".to_owned(), no_proxy);
+ } else if proxy_url.is_some() {
+ // We sometimes need local MCP servers that we don't want to proxy
+ env.insert("NO_PROXY".to_owned(), "localhost,127.0.0.1".to_owned());
}
env
diff --git a/crates/agent_servers/src/claude.rs b/crates/agent_servers/src/claude.rs
index 4646b2e8259fa2cd63c0daa67b47f66b5e78af05..b84a386679cee825be22d895634a6971b537fa89 100644
--- a/crates/agent_servers/src/claude.rs
+++ b/crates/agent_servers/src/claude.rs
@@ -62,7 +62,7 @@ impl AgentServer for ClaudeCode {
cx: &mut App,
) -> Task, Option)>> {
let name = self.name();
- let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().to_string());
+ let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
let is_remote = delegate.project.read(cx).is_via_remote_server();
let store = delegate.store.downgrade();
let extra_env = load_proxy_env(cx);
diff --git a/crates/agent_servers/src/codex.rs b/crates/agent_servers/src/codex.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0a19cfd03214972e9c7cd62aee713f3689d525df
--- /dev/null
+++ b/crates/agent_servers/src/codex.rs
@@ -0,0 +1,80 @@
+use std::rc::Rc;
+use std::{any::Any, path::Path};
+
+use crate::{AgentServer, AgentServerDelegate, load_proxy_env};
+use acp_thread::AgentConnection;
+use anyhow::{Context as _, Result};
+use gpui::{App, SharedString, Task};
+use project::agent_server_store::CODEX_NAME;
+
+#[derive(Clone)]
+pub struct Codex;
+
+#[cfg(test)]
+pub(crate) mod tests {
+ use super::*;
+
+ crate::common_e2e_tests!(async |_, _, _| Codex, allow_option_id = "proceed_once");
+}
+
+impl AgentServer for Codex {
+ fn telemetry_id(&self) -> &'static str {
+ "codex"
+ }
+
+ fn name(&self) -> SharedString {
+ "Codex".into()
+ }
+
+ fn logo(&self) -> ui::IconName {
+ ui::IconName::AiOpenAi
+ }
+
+ fn connect(
+ &self,
+ root_dir: Option<&Path>,
+ delegate: AgentServerDelegate,
+ cx: &mut App,
+ ) -> Task, Option)>> {
+ let name = self.name();
+ let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
+ let is_remote = delegate.project.read(cx).is_via_remote_server();
+ let store = delegate.store.downgrade();
+ let extra_env = load_proxy_env(cx);
+ let default_mode = self.default_mode(cx);
+
+ cx.spawn(async move |cx| {
+ let (command, root_dir, login) = store
+ .update(cx, |store, cx| {
+ let agent = store
+ .get_external_agent(&CODEX_NAME.into())
+ .context("Codex is not registered")?;
+ anyhow::Ok(agent.get_command(
+ root_dir.as_deref(),
+ extra_env,
+ delegate.status_tx,
+ // For now, report that there are no updates.
+ // (A future PR will use the GitHub Releases API to fetch them.)
+ delegate.new_version_available,
+ &mut cx.to_async(),
+ ))
+ })??
+ .await?;
+
+ let connection = crate::acp::connect(
+ name,
+ command,
+ root_dir.as_ref(),
+ default_mode,
+ is_remote,
+ cx,
+ )
+ .await?;
+ Ok((connection, login))
+ })
+ }
+
+ fn into_any(self: Rc) -> Rc {
+ self
+ }
+}
diff --git a/crates/agent_servers/src/custom.rs b/crates/agent_servers/src/custom.rs
index cb9a6dba3c6376fa5030c21523c86853c9b6d761..406a18965111a44bc4e78469b20aaf199cbda037 100644
--- a/crates/agent_servers/src/custom.rs
+++ b/crates/agent_servers/src/custom.rs
@@ -67,7 +67,7 @@ impl crate::AgentServer for CustomAgentServer {
cx: &mut App,
) -> Task, Option)>> {
let name = self.name();
- let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().to_string());
+ let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
let is_remote = delegate.project.read(cx).is_via_remote_server();
let default_mode = self.default_mode(cx);
let store = delegate.store.downgrade();
diff --git a/crates/agent_servers/src/e2e_tests.rs b/crates/agent_servers/src/e2e_tests.rs
index 1ee2e099f0ae355267b5f0a5aaddb3371f427240..60480caa541ba1c39dba62ed709c157fd67fede0 100644
--- a/crates/agent_servers/src/e2e_tests.rs
+++ b/crates/agent_servers/src/e2e_tests.rs
@@ -483,6 +483,13 @@ pub async fn init_test(cx: &mut TestAppContext) -> Arc {
default_mode: None,
}),
gemini: Some(crate::gemini::tests::local_command().into()),
+ codex: Some(BuiltinAgentServerSettings {
+ path: Some("codex-acp".into()),
+ args: None,
+ env: None,
+ ignore_system_version: None,
+ default_mode: None,
+ }),
custom: collections::HashMap::default(),
},
cx,
diff --git a/crates/agent_servers/src/gemini.rs b/crates/agent_servers/src/gemini.rs
index 9407a42e68d34e38e78f2103b29f980f874fb3db..8004f5caec4a7bd2e3e6b1d9a885f4943fa21147 100644
--- a/crates/agent_servers/src/gemini.rs
+++ b/crates/agent_servers/src/gemini.rs
@@ -31,7 +31,7 @@ impl AgentServer for Gemini {
cx: &mut App,
) -> Task, Option)>> {
let name = self.name();
- let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().to_string());
+ let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
let is_remote = delegate.project.read(cx).is_via_remote_server();
let store = delegate.store.downgrade();
let mut extra_env = load_proxy_env(cx);
diff --git a/crates/agent_settings/src/agent_settings.rs b/crates/agent_settings/src/agent_settings.rs
index d862cacee18ea53f81cdc91981b22f5531f2d75e..ec05c95672fa29b6e4813207e3e592fff9d3be15 100644
--- a/crates/agent_settings/src/agent_settings.rs
+++ b/crates/agent_settings/src/agent_settings.rs
@@ -151,7 +151,7 @@ impl Default for AgentProfileId {
}
impl Settings for AgentSettings {
- fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
+ fn from_settings(content: &settings::SettingsContent) -> Self {
let agent = content.agent.clone().unwrap();
Self {
enabled: agent.enabled.unwrap(),
diff --git a/crates/agent_ui/Cargo.toml b/crates/agent_ui/Cargo.toml
index 028db95c10a8c7a319bb05927dcabd0564a14683..47d9f6d6a27a2ad5102e831094912208e66a9b43 100644
--- a/crates/agent_ui/Cargo.toml
+++ b/crates/agent_ui/Cargo.toml
@@ -80,7 +80,6 @@ serde.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
settings.workspace = true
-shlex.workspace = true
smol.workspace = true
streaming_diff.workspace = true
task.workspace = true
diff --git a/crates/agent_ui/src/acp/entry_view_state.rs b/crates/agent_ui/src/acp/entry_view_state.rs
index 0d4dfb0c206a78b2af932d5f3ef7d57c9bfbfc16..ee506b98810ba51d0fb933a2ca21e650d0cacc0b 100644
--- a/crates/agent_ui/src/acp/entry_view_state.rs
+++ b/crates/agent_ui/src/acp/entry_view_state.rs
@@ -203,7 +203,7 @@ impl EntryViewState {
self.entries.drain(range);
}
- pub fn agent_font_size_changed(&mut self, cx: &mut App) {
+ pub fn agent_ui_font_size_changed(&mut self, cx: &mut App) {
for entry in self.entries.iter() {
match entry {
Entry::UserMessage { .. } | Entry::AssistantMessage { .. } => {}
@@ -387,7 +387,7 @@ fn diff_editor_text_style_refinement(cx: &mut App) -> TextStyleRefinement {
font_size: Some(
TextSize::Small
.rems(cx)
- .to_pixels(ThemeSettings::get_global(cx).agent_font_size(cx))
+ .to_pixels(ThemeSettings::get_global(cx).agent_ui_font_size(cx))
.into(),
),
..Default::default()
@@ -414,7 +414,6 @@ mod tests {
use project::Project;
use serde_json::json;
use settings::{Settings as _, SettingsStore};
- use theme::ThemeSettings;
use util::path;
use workspace::Workspace;
@@ -544,7 +543,7 @@ mod tests {
Project::init_settings(cx);
AgentSettings::register(cx);
workspace::init_settings(cx);
- ThemeSettings::register(cx);
+ theme::init(theme::LoadThemes::JustBase, cx);
release_channel::init(SemanticVersion::default(), cx);
EditorSettings::register(cx);
});
diff --git a/crates/agent_ui/src/acp/message_editor.rs b/crates/agent_ui/src/acp/message_editor.rs
index dec9b0beb23d9d7d49116a30404f244b5bb1f8e8..b7521517575a9831635f5e08a954c0e2bd493a80 100644
--- a/crates/agent_ui/src/acp/message_editor.rs
+++ b/crates/agent_ui/src/acp/message_editor.rs
@@ -76,7 +76,7 @@ pub enum MessageEditorEvent {
impl EventEmitter for MessageEditor {}
-const COMMAND_HINT_INLAY_ID: usize = 0;
+const COMMAND_HINT_INLAY_ID: u32 = 0;
impl MessageEditor {
pub fn new(
@@ -290,18 +290,18 @@ impl MessageEditor {
let snapshot = self
.editor
.update(cx, |editor, cx| editor.snapshot(window, cx));
- let Some((excerpt_id, _, _)) = snapshot.buffer_snapshot.as_singleton() else {
+ let Some((excerpt_id, _, _)) = snapshot.buffer_snapshot().as_singleton() else {
return Task::ready(());
};
let Some(start_anchor) = snapshot
- .buffer_snapshot
+ .buffer_snapshot()
.anchor_in_excerpt(*excerpt_id, start)
else {
return Task::ready(());
};
let end_anchor = snapshot
- .buffer_snapshot
- .anchor_before(start_anchor.to_offset(&snapshot.buffer_snapshot) + content_len + 1);
+ .buffer_snapshot()
+ .anchor_before(start_anchor.to_offset(&snapshot.buffer_snapshot()) + content_len + 1);
let crease = if let MentionUri::File { abs_path } = &mention_uri
&& let Some(extension) = abs_path.extension()
@@ -718,7 +718,7 @@ impl MessageEditor {
continue;
};
- let crease_range = crease.range().to_offset(&snapshot.buffer_snapshot);
+ let crease_range = crease.range().to_offset(&snapshot.buffer_snapshot());
if crease_range.start > ix {
//todo(): Custom slash command ContentBlock?
// let chunk = if prevent_slash_commands
@@ -865,11 +865,11 @@ impl MessageEditor {
self.editor.update(cx, |message_editor, cx| {
let snapshot = message_editor.snapshot(window, cx);
let (excerpt_id, _, buffer_snapshot) =
- snapshot.buffer_snapshot.as_singleton().unwrap();
+ snapshot.buffer_snapshot().as_singleton().unwrap();
let text_anchor = buffer_snapshot.anchor_before(buffer_snapshot.len());
let multibuffer_anchor = snapshot
- .buffer_snapshot
+ .buffer_snapshot()
.anchor_in_excerpt(*excerpt_id, text_anchor);
message_editor.edit(
[(
@@ -1299,7 +1299,7 @@ impl Render for MessageEditor {
font_family: settings.buffer_font.family.clone(),
font_fallbacks: settings.buffer_font.fallbacks.clone(),
font_features: settings.buffer_font.features.clone(),
- font_size: settings.buffer_font_size(cx).into(),
+ font_size: settings.agent_buffer_font_size(cx).into(),
line_height: relative(settings.buffer_line_height.value()),
..Default::default()
};
@@ -1550,7 +1550,7 @@ impl MentionSet {
fn remove_invalid(&mut self, snapshot: EditorSnapshot) {
for (crease_id, crease) in snapshot.crease_snapshot.creases() {
- if !crease.range().start.is_valid(&snapshot.buffer_snapshot) {
+ if !crease.range().start.is_valid(&snapshot.buffer_snapshot()) {
self.mentions.remove(&crease_id);
}
}
diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs
index 104b06049d44c2b9b08262b545ebe3a499f3155b..8d698265b9cb9aa92db7b791503cf9f379aa1099 100644
--- a/crates/agent_ui/src/acp/thread_view.rs
+++ b/crates/agent_ui/src/acp/thread_view.rs
@@ -9,7 +9,7 @@ use agent_client_protocol::{self as acp, PromptCapabilities};
use agent_servers::{AgentServer, AgentServerDelegate};
use agent_settings::{AgentProfileId, AgentSettings, CompletionMode};
use agent2::{DbThreadMetadata, HistoryEntry, HistoryEntryId, HistoryStore, NativeAgentServer};
-use anyhow::{Context as _, Result, anyhow, bail};
+use anyhow::{Result, anyhow, bail};
use arrayvec::ArrayVec;
use audio::{Audio, Sound};
use buffer_diff::BufferDiff;
@@ -26,7 +26,7 @@ use gpui::{
CursorStyle, EdgesRefinement, ElementId, Empty, Entity, FocusHandle, Focusable, Hsla, Length,
ListOffset, ListState, PlatformDisplay, SharedString, StyleRefinement, Subscription, Task,
TextStyle, TextStyleRefinement, UnderlineStyle, WeakEntity, Window, WindowHandle, div,
- ease_in_out, linear_color_stop, linear_gradient, list, point, prelude::*, pulsating_between,
+ ease_in_out, linear_color_stop, linear_gradient, list, point, pulsating_between,
};
use language::Buffer;
@@ -289,8 +289,9 @@ pub struct AcpThreadView {
available_commands: Rc>>,
is_loading_contents: bool,
new_server_version_available: Option,
+ resume_thread_metadata: Option,
_cancel_task: Option>,
- _subscriptions: [Subscription; 4],
+ _subscriptions: [Subscription; 5],
}
enum ThreadState {
@@ -380,11 +381,17 @@ impl AcpThreadView {
)
});
+ let agent_server_store = project.read(cx).agent_server_store().clone();
let subscriptions = [
- cx.observe_global_in::(window, Self::agent_font_size_changed),
- cx.observe_global_in::(window, Self::agent_font_size_changed),
+ cx.observe_global_in::(window, Self::agent_ui_font_size_changed),
+ cx.observe_global_in::(window, Self::agent_ui_font_size_changed),
cx.subscribe_in(&message_editor, window, Self::handle_message_editor_event),
cx.subscribe_in(&entry_view_state, window, Self::handle_entry_view_event),
+ cx.subscribe_in(
+ &agent_server_store,
+ window,
+ Self::handle_agent_servers_updated,
+ ),
];
Self {
@@ -392,7 +399,14 @@ impl AcpThreadView {
workspace: workspace.clone(),
project: project.clone(),
entry_view_state,
- thread_state: Self::initial_state(agent, resume_thread, workspace, project, window, cx),
+ thread_state: Self::initial_state(
+ agent.clone(),
+ resume_thread.clone(),
+ workspace.clone(),
+ project.clone(),
+ window,
+ cx,
+ ),
login: None,
message_editor,
model_selector: None,
@@ -421,13 +435,14 @@ impl AcpThreadView {
_cancel_task: None,
focus_handle: cx.focus_handle(),
new_server_version_available: None,
+ resume_thread_metadata: resume_thread,
}
}
fn reset(&mut self, window: &mut Window, cx: &mut Context) {
self.thread_state = Self::initial_state(
self.agent.clone(),
- None,
+ self.resume_thread_metadata.clone(),
self.workspace.clone(),
self.project.clone(),
window,
@@ -775,6 +790,25 @@ impl AcpThreadView {
cx.notify();
}
+ fn handle_agent_servers_updated(
+ &mut self,
+ _agent_server_store: &Entity,
+ _event: &project::AgentServersUpdated,
+ window: &mut Window,
+ cx: &mut Context,
+ ) {
+ // If we're in a LoadError state OR have a thread_error set (which can happen
+ // when agent.connect() fails during loading), retry loading the thread.
+ // This handles the case where a thread is restored before authentication completes.
+ let should_retry =
+ matches!(&self.thread_state, ThreadState::LoadError(_)) || self.thread_error.is_some();
+
+ if should_retry {
+ self.thread_error = None;
+ self.reset(window, cx);
+ }
+ }
+
pub fn workspace(&self) -> &WeakEntity {
&self.workspace
}
@@ -1012,11 +1046,13 @@ impl AcpThreadView {
};
let connection = thread.read(cx).connection().clone();
- if !connection
- .auth_methods()
- .iter()
- .any(|method| method.id.0.as_ref() == "claude-login")
- {
+ let auth_methods = connection.auth_methods();
+ let has_supported_auth = auth_methods.iter().any(|method| {
+ let id = method.id.0.as_ref();
+ id == "claude-login" || id == "spawn-gemini-cli"
+ });
+ let can_login = has_supported_auth || auth_methods.is_empty() || self.login.is_some();
+ if !can_login {
return;
};
let this = cx.weak_entity();
@@ -1579,31 +1615,20 @@ impl AcpThreadView {
return Task::ready(Ok(()));
};
let project = workspace.read(cx).project().clone();
- let cwd = project.read(cx).first_project_directory(cx);
- let shell = project.read(cx).terminal_settings(&cwd, cx).shell.clone();
window.spawn(cx, async move |cx| {
let mut task = login.clone();
- task.command = task
- .command
- .map(|command| anyhow::Ok(shlex::try_quote(&command)?.to_string()))
- .transpose()?;
- task.args = task
- .args
- .iter()
- .map(|arg| {
- Ok(shlex::try_quote(arg)
- .context("Failed to quote argument")?
- .to_string())
- })
- .collect::>>()?;
+ task.shell = task::Shell::WithArguments {
+ program: task.command.take().expect("login command should be set"),
+ args: std::mem::take(&mut task.args),
+ title_override: None
+ };
task.full_label = task.label.clone();
task.id = task::TaskId(format!("external-agent-{}-login", task.label));
task.command_label = task.label.clone();
task.use_new_terminal = true;
task.allow_concurrent_runs = true;
task.hide = task::HideStrategy::Always;
- task.shell = shell;
let terminal = terminal_panel.update_in(cx, |terminal_panel, window, cx| {
terminal_panel.spawn_task(&task, window, cx)
@@ -2725,7 +2750,7 @@ impl AcpThreadView {
let working_dir = working_dir
.as_ref()
- .map(|path| format!("{}", path.display()))
+ .map(|path| path.display().to_string())
.unwrap_or_else(|| "current directory".to_string());
let is_expanded = self.expanded_tool_calls.contains(&tool_call.id);
@@ -3363,6 +3388,12 @@ impl AcpThreadView {
.into_any_element()
}
+ fn activity_bar_bg(&self, cx: &Context) -> Hsla {
+ let editor_bg_color = cx.theme().colors().editor_background;
+ let active_color = cx.theme().colors().element_selected;
+ editor_bg_color.blend(active_color.opacity(0.3))
+ }
+
fn render_activity_bar(
&self,
thread_entity: &Entity,
@@ -3378,10 +3409,6 @@ impl AcpThreadView {
return None;
}
- let editor_bg_color = cx.theme().colors().editor_background;
- let active_color = cx.theme().colors().element_selected;
- let bg_edit_files_disclosure = editor_bg_color.blend(active_color.opacity(0.3));
-
// Temporarily always enable ACP edit controls. This is temporary, to lessen the
// impact of a nasty bug that causes them to sometimes be disabled when they shouldn't
// be, which blocks you from being able to accept or reject edits. This switches the
@@ -3392,7 +3419,7 @@ impl AcpThreadView {
v_flex()
.mt_1()
.mx_2()
- .bg(bg_edit_files_disclosure)
+ .bg(self.activity_bar_bg(cx))
.border_1()
.border_b_0()
.border_color(cx.theme().colors().border)
@@ -3433,27 +3460,33 @@ impl AcpThreadView {
.into()
}
- fn render_plan_summary(&self, plan: &Plan, window: &mut Window, cx: &Context) -> Div {
+ fn render_plan_summary(
+ &self,
+ plan: &Plan,
+ window: &mut Window,
+ cx: &Context,
+ ) -> impl IntoElement {
let stats = plan.stats();
let title = if let Some(entry) = stats.in_progress_entry
&& !self.plan_expanded
{
h_flex()
- .w_full()
.cursor_default()
+ .relative()
+ .w_full()
.gap_1()
- .text_xs()
- .text_color(cx.theme().colors().text_muted)
- .justify_between()
+ .truncate()
.child(
- h_flex()
- .gap_1()
- .child(
- Label::new("Current:")
- .size(LabelSize::Small)
- .color(Color::Muted),
- )
+ Label::new("Current:")
+ .size(LabelSize::Small)
+ .color(Color::Muted),
+ )
+ .child(
+ div()
+ .text_xs()
+ .text_color(cx.theme().colors().text_muted)
+ .line_clamp(1)
.child(MarkdownElement::new(
entry.content.clone(),
plan_label_markdown_style(&entry.status, window, cx),
@@ -3461,10 +3494,23 @@ impl AcpThreadView {
)
.when(stats.pending > 0, |this| {
this.child(
- Label::new(format!("{} left", stats.pending))
- .size(LabelSize::Small)
- .color(Color::Muted)
- .mr_1(),
+ h_flex()
+ .absolute()
+ .top_0()
+ .right_0()
+ .h_full()
+ .child(div().min_w_8().h_full().bg(linear_gradient(
+ 90.,
+ linear_color_stop(self.activity_bar_bg(cx), 1.),
+ linear_color_stop(self.activity_bar_bg(cx).opacity(0.2), 0.),
+ )))
+ .child(
+ div().pr_0p5().bg(self.activity_bar_bg(cx)).child(
+ Label::new(format!("{} left", stats.pending))
+ .size(LabelSize::Small)
+ .color(Color::Muted),
+ ),
+ ),
)
})
} else {
@@ -3494,23 +3540,19 @@ impl AcpThreadView {
};
h_flex()
+ .id("plan_summary")
.p_1()
- .justify_between()
+ .w_full()
+ .gap_1()
.when(self.plan_expanded, |this| {
this.border_b_1().border_color(cx.theme().colors().border)
})
- .child(
- h_flex()
- .id("plan_summary")
- .w_full()
- .gap_1()
- .child(Disclosure::new("plan_disclosure", self.plan_expanded))
- .child(title)
- .on_click(cx.listener(|this, _, _, cx| {
- this.plan_expanded = !this.plan_expanded;
- cx.notify();
- })),
- )
+ .child(Disclosure::new("plan_disclosure", self.plan_expanded))
+ .child(title)
+ .on_click(cx.listener(|this, _, _, cx| {
+ this.plan_expanded = !this.plan_expanded;
+ cx.notify();
+ }))
}
fn render_plan_entries(&self, plan: &Plan, window: &mut Window, cx: &Context) -> Div {
@@ -3712,13 +3754,10 @@ impl AcpThreadView {
None
} else {
Some(
- Label::new(format!(
- "{separator}{}{separator}",
- parent.display(path_style)
- ))
- .color(Color::Muted)
- .size(LabelSize::XSmall)
- .buffer_font(cx),
+ Label::new(format!("{}{separator}", parent.display(path_style)))
+ .color(Color::Muted)
+ .size(LabelSize::XSmall)
+ .buffer_font(cx),
)
}
});
@@ -3762,7 +3801,7 @@ impl AcpThreadView {
.id(("file-name", index))
.pr_8()
.gap_1p5()
- .max_w_full()
+ .w_full()
.overflow_x_scroll()
.child(file_icon)
.child(h_flex().gap_0p5().children(file_name).children(file_path))
@@ -4914,9 +4953,9 @@ impl AcpThreadView {
)
}
- fn agent_font_size_changed(&mut self, _window: &mut Window, cx: &mut Context) {
+ fn agent_ui_font_size_changed(&mut self, _window: &mut Window, cx: &mut Context) {
self.entry_view_state.update(cx, |entry_view_state, cx| {
- entry_view_state.agent_font_size_changed(cx);
+ entry_view_state.agent_ui_font_size_changed(cx);
});
}
@@ -5546,23 +5585,23 @@ fn default_markdown_style(
}),
code_block: StyleRefinement {
padding: EdgesRefinement {
- top: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))),
- left: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))),
- right: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))),
- bottom: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))),
+ top: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(px(8.)))),
+ left: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(px(8.)))),
+ right: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(px(8.)))),
+ bottom: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(px(8.)))),
},
margin: EdgesRefinement {
- top: Some(Length::Definite(Pixels(8.).into())),
- left: Some(Length::Definite(Pixels(0.).into())),
- right: Some(Length::Definite(Pixels(0.).into())),
- bottom: Some(Length::Definite(Pixels(12.).into())),
+ top: Some(Length::Definite(px(8.).into())),
+ left: Some(Length::Definite(px(0.).into())),
+ right: Some(Length::Definite(px(0.).into())),
+ bottom: Some(Length::Definite(px(12.).into())),
},
border_style: Some(BorderStyle::Solid),
border_widths: EdgesRefinement {
- top: Some(AbsoluteLength::Pixels(Pixels(1.))),
- left: Some(AbsoluteLength::Pixels(Pixels(1.))),
- right: Some(AbsoluteLength::Pixels(Pixels(1.))),
- bottom: Some(AbsoluteLength::Pixels(Pixels(1.))),
+ top: Some(AbsoluteLength::Pixels(px(1.))),
+ left: Some(AbsoluteLength::Pixels(px(1.))),
+ right: Some(AbsoluteLength::Pixels(px(1.))),
+ bottom: Some(AbsoluteLength::Pixels(px(1.))),
},
border_color: Some(colors.border_variant),
background: Some(colors.editor_background.into()),
@@ -6047,7 +6086,7 @@ pub(crate) mod tests {
Project::init_settings(cx);
AgentSettings::register(cx);
workspace::init_settings(cx);
- ThemeSettings::register(cx);
+ theme::init(theme::LoadThemes::JustBase, cx);
release_channel::init(SemanticVersion::default(), cx);
EditorSettings::register(cx);
prompt_store::init(cx)
diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs
index 3fd78c44ec5a249c6acf4ddd9ac548988a51612c..3581baf4ec62b746a27bd78bade2d9e85ade069a 100644
--- a/crates/agent_ui/src/agent_configuration.rs
+++ b/crates/agent_ui/src/agent_configuration.rs
@@ -15,6 +15,7 @@ use context_server::ContextServerId;
use editor::{Editor, SelectionEffects, scroll::Autoscroll};
use extension::ExtensionManifest;
use extension_host::ExtensionStore;
+use feature_flags::{CodexAcpFeatureFlag, FeatureFlagAppExt as _};
use fs::Fs;
use gpui::{
Action, AnyView, App, AsyncWindowContext, Corner, Entity, EventEmitter, FocusHandle, Focusable,
@@ -26,7 +27,7 @@ use language_model::{
};
use notifications::status_toast::{StatusToast, ToastIcon};
use project::{
- agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, GEMINI_NAME},
+ agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, CODEX_NAME, GEMINI_NAME},
context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},
};
use settings::{Settings, SettingsStore, update_settings_file};
@@ -1014,7 +1015,9 @@ impl AgentConfiguration {
.agent_server_store
.read(cx)
.external_agents()
- .filter(|name| name.0 != GEMINI_NAME && name.0 != CLAUDE_CODE_NAME)
+ .filter(|name| {
+ name.0 != GEMINI_NAME && name.0 != CLAUDE_CODE_NAME && name.0 != CODEX_NAME
+ })
.cloned()
.collect::>();
@@ -1078,13 +1081,21 @@ impl AgentConfiguration {
),
)
.child(self.render_agent_server(
- IconName::AiGemini,
- "Gemini CLI",
+ IconName::AiClaude,
+ "Claude Code",
))
.child(Divider::horizontal().color(DividerColor::BorderFaded))
+ .when(cx.has_flag::(), |this| {
+ this
+ .child(self.render_agent_server(
+ IconName::AiOpenAi,
+ "Codex",
+ ))
+ .child(Divider::horizontal().color(DividerColor::BorderFaded))
+ })
.child(self.render_agent_server(
- IconName::AiClaude,
- "Claude Code",
+ IconName::AiGemini,
+ "Gemini CLI",
))
.map(|mut parent| {
for agent in user_defined_agents {
diff --git a/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs b/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs
index cb9aabab9250b2f6175912c0a73242bc7f331dc6..9a7f0ed602a52d3b27dde565383453f2c5c325fb 100644
--- a/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs
+++ b/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs
@@ -317,6 +317,8 @@ impl ManageProfilesModal {
window: &mut Window,
cx: &mut Context,
) -> impl IntoElement + use<> {
+ let is_focused = profile.navigation.focus_handle.contains_focused(window, cx);
+
div()
.id(SharedString::from(format!("profile-{}", profile.id)))
.track_focus(&profile.navigation.focus_handle)
@@ -328,25 +330,27 @@ impl ManageProfilesModal {
})
.child(
ListItem::new(SharedString::from(format!("profile-{}", profile.id)))
- .toggle_state(profile.navigation.focus_handle.contains_focused(window, cx))
+ .toggle_state(is_focused)
.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,
- )),
- )
+ .when(is_focused, |this| {
+ this.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| {
diff --git a/crates/agent_ui/src/agent_diff.rs b/crates/agent_ui/src/agent_diff.rs
index 5ae43398d626fd5b5208e5e73d2c034644a29d95..67014e3c3a4c8bd9b43f34d9cad3c23832efdc13 100644
--- a/crates/agent_ui/src/agent_diff.rs
+++ b/crates/agent_ui/src/agent_diff.rs
@@ -562,10 +562,6 @@ impl Item for AgentDiffPane {
self.editor.for_each_project_item(cx, f)
}
- fn is_singleton(&self, _: &App) -> bool {
- false
- }
-
fn set_nav_history(
&mut self,
nav_history: ItemNavHistory,
@@ -850,7 +846,7 @@ fn render_diff_hunk_controls(
editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(window, cx);
let position =
- hunk_range.end.to_point(&snapshot.buffer_snapshot);
+ hunk_range.end.to_point(&snapshot.buffer_snapshot());
editor.go_to_hunk_before_or_after_position(
&snapshot,
position,
@@ -886,7 +882,7 @@ fn render_diff_hunk_controls(
editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(window, cx);
let point =
- hunk_range.start.to_point(&snapshot.buffer_snapshot);
+ hunk_range.start.to_point(&snapshot.buffer_snapshot());
editor.go_to_hunk_before_or_after_position(
&snapshot,
point,
@@ -1818,7 +1814,6 @@ mod tests {
use serde_json::json;
use settings::{Settings, SettingsStore};
use std::{path::Path, rc::Rc};
- use theme::ThemeSettings;
use util::path;
#[gpui::test]
@@ -1831,7 +1826,7 @@ mod tests {
AgentSettings::register(cx);
prompt_store::init(cx);
workspace::init_settings(cx);
- ThemeSettings::register(cx);
+ theme::init(theme::LoadThemes::JustBase, cx);
EditorSettings::register(cx);
language_model::init_settings(cx);
});
@@ -1983,7 +1978,7 @@ mod tests {
AgentSettings::register(cx);
prompt_store::init(cx);
workspace::init_settings(cx);
- ThemeSettings::register(cx);
+ theme::init(theme::LoadThemes::JustBase, cx);
EditorSettings::register(cx);
language_model::init_settings(cx);
workspace::register_project_item::(cx);
diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs
index ca6a5fb2f6c216e7886394da069c93e5029a5ed0..5b641dad41a4165fd0eaa0fd0502b982e58816c7 100644
--- a/crates/agent_ui/src/agent_panel.rs
+++ b/crates/agent_ui/src/agent_panel.rs
@@ -7,7 +7,7 @@ use acp_thread::AcpThread;
use agent2::{DbThreadMetadata, HistoryEntry};
use db::kvp::{Dismissable, KEY_VALUE_STORE};
use project::agent_server_store::{
- AgentServerCommand, AllAgentServersSettings, CLAUDE_CODE_NAME, GEMINI_NAME,
+ AgentServerCommand, AllAgentServersSettings, CLAUDE_CODE_NAME, CODEX_NAME, GEMINI_NAME,
};
use serde::{Deserialize, Serialize};
use settings::{
@@ -48,8 +48,8 @@ use editor::{Anchor, AnchorRangeExt as _, Editor, EditorEvent, MultiBuffer};
use fs::Fs;
use gpui::{
Action, AnyElement, App, AsyncWindowContext, Corner, DismissEvent, Entity, EventEmitter,
- ExternalPaths, FocusHandle, Focusable, KeyContext, Pixels, Subscription, Task, UpdateGlobal,
- WeakEntity, prelude::*,
+ ExternalPaths, FocusHandle, Focusable, KeyContext, Pixels, ReadGlobal as _, Subscription, Task,
+ UpdateGlobal, WeakEntity, prelude::*,
};
use language::LanguageRegistry;
use language_model::{ConfigurationError, LanguageModelRegistry};
@@ -75,6 +75,7 @@ use zed_actions::{
assistant::{OpenRulesLibrary, ToggleFocus},
};
+use feature_flags::{CodexAcpFeatureFlag, FeatureFlagAppExt as _};
const AGENT_PANEL_KEY: &str = "agent_panel";
#[derive(Serialize, Deserialize, Debug)]
@@ -216,6 +217,7 @@ pub enum AgentType {
TextThread,
Gemini,
ClaudeCode,
+ Codex,
NativeAgent,
Custom {
name: SharedString,
@@ -230,6 +232,7 @@ impl AgentType {
Self::NativeAgent => "Agent 2".into(),
Self::Gemini => "Gemini CLI".into(),
Self::ClaudeCode => "Claude Code".into(),
+ Self::Codex => "Codex".into(),
Self::Custom { name, .. } => name.into(),
}
}
@@ -239,6 +242,7 @@ impl AgentType {
Self::Zed | Self::NativeAgent | Self::TextThread => None,
Self::Gemini => Some(IconName::AiGemini),
Self::ClaudeCode => Some(IconName::AiClaude),
+ Self::Codex => Some(IconName::AiOpenAi),
Self::Custom { .. } => Some(IconName::Terminal),
}
}
@@ -249,6 +253,7 @@ impl From for AgentType {
match value {
ExternalAgent::Gemini => Self::Gemini,
ExternalAgent::ClaudeCode => Self::ClaudeCode,
+ ExternalAgent::Codex => Self::Codex,
ExternalAgent::Custom { name, command } => Self::Custom { name, command },
ExternalAgent::NativeAgent => Self::NativeAgent,
}
@@ -514,6 +519,14 @@ impl AgentPanel {
cx,
)
});
+
+ if SettingsStore::global(cx)
+ .get::(None)
+ .disable_ai
+ {
+ return panel;
+ }
+
panel.as_mut(cx).loading = true;
if let Some(serialized_panel) = serialized_panel {
panel.update(cx, |panel, cx| {
@@ -1103,15 +1116,15 @@ impl AgentPanel {
WhichFontSize::AgentFont => {
if persist {
update_settings_file(self.fs.clone(), cx, move |settings, cx| {
- let agent_font_size =
- ThemeSettings::get_global(cx).agent_font_size(cx) + delta;
+ let agent_ui_font_size =
+ ThemeSettings::get_global(cx).agent_ui_font_size(cx) + delta;
let _ = settings
.theme
- .agent_font_size
- .insert(theme::clamp_font_size(agent_font_size).into());
+ .agent_ui_font_size
+ .insert(theme::clamp_font_size(agent_ui_font_size).into());
});
} else {
- theme::adjust_agent_font_size(cx, |size| size + delta);
+ theme::adjust_agent_ui_font_size(cx, |size| size + delta);
}
}
WhichFontSize::BufferFont => {
@@ -1131,10 +1144,10 @@ impl AgentPanel {
) {
if action.persist {
update_settings_file(self.fs.clone(), cx, move |settings, _| {
- settings.theme.agent_font_size = None;
+ settings.theme.agent_ui_font_size = None;
});
} else {
- theme::reset_agent_font_size(cx);
+ theme::reset_agent_ui_font_size(cx);
}
}
@@ -1427,6 +1440,11 @@ impl AgentPanel {
cx,
)
}
+ AgentType::Codex => {
+ self.selected_agent = AgentType::Codex;
+ self.serialize(cx);
+ self.external_thread(Some(crate::ExternalAgent::Codex), None, None, window, cx)
+ }
AgentType::Custom { name, command } => self.external_thread(
Some(crate::ExternalAgent::Custom { name, command }),
None,
@@ -1940,10 +1958,10 @@ impl AgentPanel {
.separator()
.header("External Agents")
.item(
- ContextMenuEntry::new("New Gemini CLI Thread")
- .icon(IconName::AiGemini)
- .icon_color(Color::Muted)
+ ContextMenuEntry::new("New Claude Code Thread")
+ .icon(IconName::AiClaude)
.disabled(is_via_collab)
+ .icon_color(Color::Muted)
.handler({
let workspace = workspace.clone();
move |window, cx| {
@@ -1954,7 +1972,7 @@ impl AgentPanel {
{
panel.update(cx, |panel, cx| {
panel.new_agent_thread(
- AgentType::Gemini,
+ AgentType::ClaudeCode,
window,
cx,
);
@@ -1965,11 +1983,39 @@ impl AgentPanel {
}
}),
)
+ .when(cx.has_flag::(), |this| {
+ this.item(
+ ContextMenuEntry::new("New Codex Thread")
+ .icon(IconName::AiOpenAi)
+ .disabled(is_via_collab)
+ .icon_color(Color::Muted)
+ .handler({
+ let workspace = workspace.clone();
+ move |window, cx| {
+ if let Some(workspace) = workspace.upgrade() {
+ workspace.update(cx, |workspace, cx| {
+ if let Some(panel) =
+ workspace.panel::(cx)
+ {
+ panel.update(cx, |panel, cx| {
+ panel.new_agent_thread(
+ AgentType::Codex,
+ window,
+ cx,
+ );
+ });
+ }
+ });
+ }
+ }
+ }),
+ )
+ })
.item(
- ContextMenuEntry::new("New Claude Code Thread")
- .icon(IconName::AiClaude)
- .disabled(is_via_collab)
+ ContextMenuEntry::new("New Gemini CLI Thread")
+ .icon(IconName::AiGemini)
.icon_color(Color::Muted)
+ .disabled(is_via_collab)
.handler({
let workspace = workspace.clone();
move |window, cx| {
@@ -1980,7 +2026,7 @@ impl AgentPanel {
{
panel.update(cx, |panel, cx| {
panel.new_agent_thread(
- AgentType::ClaudeCode,
+ AgentType::Gemini,
window,
cx,
);
@@ -1996,7 +2042,7 @@ impl AgentPanel {
.read(cx)
.external_agents()
.filter(|name| {
- name.0 != GEMINI_NAME && name.0 != CLAUDE_CODE_NAME
+ name.0 != GEMINI_NAME && name.0 != CLAUDE_CODE_NAME && name.0 != CODEX_NAME
})
.cloned()
.collect::>();
@@ -2532,7 +2578,7 @@ impl Render for AgentPanel {
match self.active_view.which_font_size_used() {
WhichFontSize::AgentFont => {
- WithRemSize::new(ThemeSettings::get_global(cx).agent_font_size(cx))
+ WithRemSize::new(ThemeSettings::get_global(cx).agent_ui_font_size(cx))
.size_full()
.child(content)
.into_any()
diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs
index b64334f403bed8cfcf86e80e4fe4589ba920b06d..2c439a725456976f090ddc4cb754664c4953d626 100644
--- a/crates/agent_ui/src/agent_ui.rs
+++ b/crates/agent_ui/src/agent_ui.rs
@@ -167,6 +167,7 @@ enum ExternalAgent {
#[default]
Gemini,
ClaudeCode,
+ Codex,
NativeAgent,
Custom {
name: SharedString,
@@ -188,6 +189,7 @@ impl ExternalAgent {
Self::NativeAgent => "zed",
Self::Gemini => "gemini-cli",
Self::ClaudeCode => "claude-code",
+ Self::Codex => "codex",
Self::Custom { .. } => "custom",
}
}
@@ -200,6 +202,7 @@ impl ExternalAgent {
match self {
Self::Gemini => Rc::new(agent_servers::Gemini),
Self::ClaudeCode => Rc::new(agent_servers::ClaudeCode),
+ Self::Codex => Rc::new(agent_servers::Codex),
Self::NativeAgent => Rc::new(agent2::NativeAgentServer::new(fs, history)),
Self::Custom { name, command: _ } => {
Rc::new(agent_servers::CustomAgentServer::new(name.clone()))
diff --git a/crates/agent_ui/src/inline_assistant.rs b/crates/agent_ui/src/inline_assistant.rs
index 98e7276dc4fd3f94b01df82219f116a07cafa304..d24dc4ab781585e9ebabc7f19016e4da2457a873 100644
--- a/crates/agent_ui/src/inline_assistant.rs
+++ b/crates/agent_ui/src/inline_assistant.rs
@@ -18,7 +18,9 @@ use agent_settings::AgentSettings;
use anyhow::{Context as _, Result};
use client::telemetry::Telemetry;
use collections::{HashMap, HashSet, VecDeque, hash_map};
+use editor::RowExt;
use editor::SelectionEffects;
+use editor::scroll::ScrollOffset;
use editor::{
Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorEvent, ExcerptId, ExcerptRange,
MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint,
@@ -380,7 +382,7 @@ impl InlineAssistant {
if let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) {
for assist_id in &editor_assists.assist_ids {
let assist = &self.assists[assist_id];
- let range = assist.range.to_point(&snapshot.buffer_snapshot);
+ let range = assist.range.to_point(&snapshot.buffer_snapshot());
if range.start.row <= newest_selection.start.row
&& newest_selection.end.row <= range.end.row
{
@@ -400,16 +402,16 @@ impl InlineAssistant {
selection.end.row -= 1;
}
selection.end.column = snapshot
- .buffer_snapshot
+ .buffer_snapshot()
.line_len(MultiBufferRow(selection.end.row));
} else if let Some(fold) =
snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row))
{
selection.start = fold.range().start;
selection.end = fold.range().end;
- if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
+ if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot().max_row() {
let chars = snapshot
- .buffer_snapshot
+ .buffer_snapshot()
.chars_at(Point::new(selection.end.row + 1, 0));
for c in chars {
@@ -425,7 +427,7 @@ impl InlineAssistant {
{
selection.end.row += 1;
selection.end.column = snapshot
- .buffer_snapshot
+ .buffer_snapshot()
.line_len(MultiBufferRow(selection.end.row));
}
}
@@ -445,7 +447,7 @@ impl InlineAssistant {
}
selections.push(selection);
}
- let snapshot = &snapshot.buffer_snapshot;
+ let snapshot = &snapshot.buffer_snapshot();
let newest_selection = newest_selection.unwrap();
let mut codegen_ranges = Vec::new();
@@ -744,7 +746,7 @@ impl InlineAssistant {
let scroll_bottom = scroll_top + editor.visible_line_count().unwrap_or(0.);
editor_assists.scroll_lock = editor
.row_for_block(decorations.prompt_block_id, cx)
- .map(|row| row.0 as f32)
+ .map(|row| row.as_f64())
.filter(|prompt_row| (scroll_top..scroll_bottom).contains(&prompt_row))
.map(|prompt_row| InlineAssistScrollLock {
assist_id,
@@ -910,7 +912,9 @@ impl InlineAssistant {
editor.update(cx, |editor, cx| {
let scroll_position = editor.scroll_position(cx);
- let target_scroll_top = editor.row_for_block(decorations.prompt_block_id, cx)?.0 as f32
+ let target_scroll_top = editor
+ .row_for_block(decorations.prompt_block_id, cx)?
+ .as_f64()
- scroll_lock.distance_from_top;
if target_scroll_top != scroll_position.y {
editor.set_scroll_position(point(scroll_position.x, target_scroll_top), window, cx);
@@ -959,8 +963,9 @@ impl InlineAssistant {
if let Some(decorations) = assist.decorations.as_ref() {
let distance_from_top = editor.update(cx, |editor, cx| {
let scroll_top = editor.scroll_position(cx).y;
- let prompt_row =
- editor.row_for_block(decorations.prompt_block_id, cx)?.0 as f32;
+ let prompt_row = editor
+ .row_for_block(decorations.prompt_block_id, cx)?
+ .0 as ScrollOffset;
Some(prompt_row - scroll_top)
});
@@ -1192,8 +1197,8 @@ impl InlineAssistant {
let mut scroll_target_range = None;
if let Some(decorations) = assist.decorations.as_ref() {
scroll_target_range = maybe!({
- let top = editor.row_for_block(decorations.prompt_block_id, cx)?.0 as f32;
- let bottom = editor.row_for_block(decorations.end_block_id, cx)?.0 as f32;
+ let top = editor.row_for_block(decorations.prompt_block_id, cx)?.0 as f64;
+ let bottom = editor.row_for_block(decorations.end_block_id, cx)?.0 as f64;
Some((top, bottom))
});
if scroll_target_range.is_none() {
@@ -1207,15 +1212,15 @@ impl InlineAssistant {
.start
.to_display_point(&snapshot.display_snapshot)
.row();
- let top = start_row.0 as f32;
+ let top = start_row.0 as ScrollOffset;
let bottom = top + 1.0;
(top, bottom)
});
let mut scroll_target_top = scroll_target_range.0;
let mut scroll_target_bottom = scroll_target_range.1;
- scroll_target_top -= editor.vertical_scroll_margin() as f32;
- scroll_target_bottom += editor.vertical_scroll_margin() as f32;
+ scroll_target_top -= editor.vertical_scroll_margin() as ScrollOffset;
+ scroll_target_bottom += editor.vertical_scroll_margin() as ScrollOffset;
let height_in_lines = editor.visible_line_count().unwrap_or(0.);
let scroll_top = editor.scroll_position(cx).y;
@@ -1543,7 +1548,7 @@ struct EditorInlineAssists {
struct InlineAssistScrollLock {
assist_id: InlineAssistId,
- distance_from_top: f32,
+ distance_from_top: ScrollOffset,
}
impl EditorInlineAssists {
diff --git a/crates/agent_ui/src/profile_selector.rs b/crates/agent_ui/src/profile_selector.rs
index af2354f7a854fd84483889f18e0f51e1c294d8a2..d52b2436d68c4b43cd7e7ec73b27007e34a43153 100644
--- a/crates/agent_ui/src/profile_selector.rs
+++ b/crates/agent_ui/src/profile_selector.rs
@@ -3,12 +3,20 @@ use agent_settings::{
AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles, builtin_profiles,
};
use fs::Fs;
-use gpui::{Action, Entity, FocusHandle, Subscription, prelude::*};
-use settings::{DockPosition, Settings as _, SettingsStore, update_settings_file};
-use std::sync::Arc;
+use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
+use gpui::{
+ Action, AnyElement, App, BackgroundExecutor, Context, DismissEvent, Entity, FocusHandle,
+ Focusable, SharedString, Subscription, Task, Window,
+};
+use picker::{Picker, PickerDelegate, popover_menu::PickerPopoverMenu};
+use settings::{Settings as _, SettingsStore, update_settings_file};
+use std::{
+ sync::atomic::Ordering,
+ sync::{Arc, atomic::AtomicBool},
+};
use ui::{
- ContextMenu, ContextMenuEntry, DocumentationEdge, DocumentationSide, PopoverMenu,
- PopoverMenuHandle, TintColor, Tooltip, prelude::*,
+ DocumentationAside, DocumentationEdge, DocumentationSide, HighlightedLabel, LabelSize,
+ ListItem, ListItemSpacing, PopoverMenuHandle, TintColor, Tooltip, prelude::*,
};
/// Trait for types that can provide and manage agent profiles
@@ -25,9 +33,11 @@ pub trait ProfileProvider {
pub struct ProfileSelector {
profiles: AvailableProfiles,
+ pending_refresh: bool,
fs: Arc,
provider: Arc,
- menu_handle: PopoverMenuHandle,
+ picker: Option>>,
+ picker_handle: PopoverMenuHandle>,
focus_handle: FocusHandle,
_subscriptions: Vec,
}
@@ -40,188 +50,686 @@ impl ProfileSelector {
cx: &mut Context,
) -> Self {
let settings_subscription = cx.observe_global::(move |this, cx| {
- this.refresh_profiles(cx);
+ this.pending_refresh = true;
+ cx.notify();
});
Self {
profiles: AgentProfile::available_profiles(cx),
+ pending_refresh: false,
fs,
provider,
- menu_handle: PopoverMenuHandle::default(),
+ picker: None,
+ picker_handle: PopoverMenuHandle::default(),
focus_handle,
_subscriptions: vec![settings_subscription],
}
}
- pub fn menu_handle(&self) -> PopoverMenuHandle {
- self.menu_handle.clone()
- }
-
- fn refresh_profiles(&mut self, cx: &mut Context) {
- self.profiles = AgentProfile::available_profiles(cx);
+ pub fn menu_handle(&self) -> PopoverMenuHandle> {
+ self.picker_handle.clone()
}
- fn build_context_menu(
- &self,
+ fn ensure_picker(
+ &mut self,
window: &mut Window,
cx: &mut Context,
- ) -> Entity {
- ContextMenu::build(window, cx, |mut menu, _window, cx| {
- let settings = AgentSettings::get_global(cx);
-
- let mut found_non_builtin = false;
- for (profile_id, profile_name) in self.profiles.iter() {
- if !builtin_profiles::is_builtin(profile_id) {
- found_non_builtin = true;
- continue;
- }
- menu = menu.item(self.menu_entry_for_profile(
- profile_id.clone(),
- profile_name,
- settings,
- cx,
- ));
- }
+ ) -> Entity> {
+ if self.picker.is_none() {
+ let delegate = ProfilePickerDelegate::new(
+ self.fs.clone(),
+ self.provider.clone(),
+ self.profiles.clone(),
+ cx.background_executor().clone(),
+ cx,
+ );
- if found_non_builtin {
- menu = menu.separator().header("Custom Profiles");
- for (profile_id, profile_name) in self.profiles.iter() {
- if builtin_profiles::is_builtin(profile_id) {
- continue;
- }
- menu = menu.item(self.menu_entry_for_profile(
- profile_id.clone(),
- profile_name,
- settings,
- cx,
- ));
- }
+ let picker = cx.new(|cx| {
+ Picker::list(delegate, window, cx)
+ .show_scrollbar(true)
+ .width(rems(18.))
+ .max_height(Some(rems(20.).into()))
+ });
+
+ self.picker = Some(picker);
+ }
+
+ if self.pending_refresh {
+ if let Some(picker) = &self.picker {
+ let profiles = AgentProfile::available_profiles(cx);
+ self.profiles = profiles.clone();
+ picker.update(cx, |picker, cx| {
+ let query = picker.query(cx);
+ picker
+ .delegate
+ .refresh_profiles(profiles.clone(), query, cx);
+ });
}
+ self.pending_refresh = false;
+ }
- menu = menu.separator();
- menu = menu.item(ContextMenuEntry::new("Configure Profiles…").handler(
- move |window, cx| {
- window.dispatch_action(ManageProfiles::default().boxed_clone(), cx);
- },
- ));
+ self.picker.as_ref().unwrap().clone()
+ }
+}
- menu
- })
+impl Focusable for ProfileSelector {
+ fn focus_handle(&self, cx: &App) -> FocusHandle {
+ if let Some(picker) = &self.picker {
+ picker.focus_handle(cx)
+ } else {
+ self.focus_handle.clone()
+ }
}
+}
- fn menu_entry_for_profile(
- &self,
- profile_id: AgentProfileId,
- profile_name: &SharedString,
- settings: &AgentSettings,
- cx: &App,
- ) -> ContextMenuEntry {
- let documentation = match profile_name.to_lowercase().as_str() {
+impl Render for ProfileSelector {
+ fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement {
+ if !self.provider.profiles_supported(cx) {
+ return Button::new("tools-not-supported-button", "Tools Unsupported")
+ .disabled(true)
+ .label_size(LabelSize::Small)
+ .color(Color::Muted)
+ .tooltip(Tooltip::text("This model does not support tools."))
+ .into_any_element();
+ }
+
+ let picker = self.ensure_picker(window, cx);
+
+ let settings = AgentSettings::get_global(cx);
+ let profile_id = self.provider.profile_id(cx);
+ let profile = settings.profiles.get(&profile_id);
+
+ let selected_profile = profile
+ .map(|profile| profile.name.clone())
+ .unwrap_or_else(|| "Unknown".into());
+ let focus_handle = self.focus_handle.clone();
+
+ let trigger_button = Button::new("profile-selector", selected_profile)
+ .label_size(LabelSize::Small)
+ .color(Color::Muted)
+ .icon(IconName::ChevronDown)
+ .icon_size(IconSize::XSmall)
+ .icon_position(IconPosition::End)
+ .icon_color(Color::Muted)
+ .selected_style(ButtonStyle::Tinted(TintColor::Accent));
+
+ PickerPopoverMenu::new(
+ picker,
+ trigger_button,
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "Toggle Profile Menu",
+ &ToggleProfileSelector,
+ &focus_handle,
+ window,
+ cx,
+ )
+ },
+ gpui::Corner::BottomRight,
+ cx,
+ )
+ .with_handle(self.picker_handle.clone())
+ .render(window, cx)
+ .into_any_element()
+ }
+}
+
+#[derive(Clone)]
+struct ProfileCandidate {
+ id: AgentProfileId,
+ name: SharedString,
+ is_builtin: bool,
+}
+
+#[derive(Clone)]
+struct ProfileMatchEntry {
+ candidate_index: usize,
+ positions: Vec,
+}
+
+enum ProfilePickerEntry {
+ Header(SharedString),
+ Profile(ProfileMatchEntry),
+}
+
+pub(crate) struct ProfilePickerDelegate {
+ fs: Arc,
+ provider: Arc,
+ background: BackgroundExecutor,
+ candidates: Vec,
+ string_candidates: Arc>,
+ filtered_entries: Vec,
+ selected_index: usize,
+ query: String,
+ cancel: Option>,
+}
+
+impl ProfilePickerDelegate {
+ fn new(
+ fs: Arc,
+ provider: Arc,
+ profiles: AvailableProfiles,
+ background: BackgroundExecutor,
+ cx: &mut Context,
+ ) -> Self {
+ let candidates = Self::candidates_from(profiles);
+ let string_candidates = Arc::new(Self::string_candidates(&candidates));
+ let filtered_entries = Self::entries_from_candidates(&candidates);
+
+ let mut this = Self {
+ fs,
+ provider,
+ background,
+ candidates,
+ string_candidates,
+ filtered_entries,
+ selected_index: 0,
+ query: String::new(),
+ cancel: None,
+ };
+
+ this.selected_index = this
+ .index_of_profile(&this.provider.profile_id(cx))
+ .unwrap_or_else(|| this.first_selectable_index().unwrap_or(0));
+
+ this
+ }
+
+ fn refresh_profiles(
+ &mut self,
+ profiles: AvailableProfiles,
+ query: String,
+ cx: &mut Context>,
+ ) {
+ self.candidates = Self::candidates_from(profiles);
+ self.string_candidates = Arc::new(Self::string_candidates(&self.candidates));
+ self.query = query;
+
+ if self.query.is_empty() {
+ self.filtered_entries = Self::entries_from_candidates(&self.candidates);
+ } else {
+ let matches = self.search_blocking(&self.query);
+ self.filtered_entries = self.entries_from_matches(matches);
+ }
+
+ self.selected_index = self
+ .index_of_profile(&self.provider.profile_id(cx))
+ .unwrap_or_else(|| self.first_selectable_index().unwrap_or(0));
+ cx.notify();
+ }
+
+ fn candidates_from(profiles: AvailableProfiles) -> Vec {
+ profiles
+ .into_iter()
+ .map(|(id, name)| ProfileCandidate {
+ is_builtin: builtin_profiles::is_builtin(&id),
+ id,
+ name,
+ })
+ .collect()
+ }
+
+ fn string_candidates(candidates: &[ProfileCandidate]) -> Vec {
+ candidates
+ .iter()
+ .enumerate()
+ .map(|(index, candidate)| StringMatchCandidate::new(index, candidate.name.as_ref()))
+ .collect()
+ }
+
+ fn documentation(candidate: &ProfileCandidate) -> Option<&'static str> {
+ match candidate.id.as_str() {
builtin_profiles::WRITE => Some("Get help to write anything."),
builtin_profiles::ASK => Some("Chat about your codebase."),
builtin_profiles::MINIMAL => Some("Chat about anything with no tools."),
_ => None,
+ }
+ }
+
+ fn entries_from_candidates(candidates: &[ProfileCandidate]) -> Vec {
+ let mut entries = Vec::new();
+ let mut inserted_custom_header = false;
+
+ for (idx, candidate) in candidates.iter().enumerate() {
+ if !candidate.is_builtin && !inserted_custom_header {
+ if !entries.is_empty() {
+ entries.push(ProfilePickerEntry::Header("Custom Profiles".into()));
+ }
+ inserted_custom_header = true;
+ }
+
+ entries.push(ProfilePickerEntry::Profile(ProfileMatchEntry {
+ candidate_index: idx,
+ positions: Vec::new(),
+ }));
+ }
+
+ entries
+ }
+
+ fn entries_from_matches(&self, matches: Vec) -> Vec {
+ let mut entries = Vec::new();
+ for mat in matches {
+ if self.candidates.get(mat.candidate_id).is_some() {
+ entries.push(ProfilePickerEntry::Profile(ProfileMatchEntry {
+ candidate_index: mat.candidate_id,
+ positions: mat.positions,
+ }));
+ }
+ }
+ entries
+ }
+
+ fn first_selectable_index(&self) -> Option {
+ self.filtered_entries
+ .iter()
+ .position(|entry| matches!(entry, ProfilePickerEntry::Profile(_)))
+ }
+
+ fn index_of_profile(&self, profile_id: &AgentProfileId) -> Option {
+ self.filtered_entries.iter().position(|entry| {
+ matches!(entry, ProfilePickerEntry::Profile(profile) if self
+ .candidates
+ .get(profile.candidate_index)
+ .map(|candidate| &candidate.id == profile_id)
+ .unwrap_or(false))
+ })
+ }
+
+ fn search_blocking(&self, query: &str) -> Vec {
+ if query.is_empty() {
+ return self
+ .string_candidates
+ .iter()
+ .map(|candidate| StringMatch {
+ candidate_id: candidate.id,
+ score: 0.0,
+ positions: Vec::new(),
+ string: candidate.string.clone(),
+ })
+ .collect();
+ }
+
+ let cancel_flag = AtomicBool::new(false);
+
+ self.background.block(match_strings(
+ self.string_candidates.as_ref(),
+ query,
+ false,
+ true,
+ 100,
+ &cancel_flag,
+ self.background.clone(),
+ ))
+ }
+}
+
+impl PickerDelegate for ProfilePickerDelegate {
+ type ListItem = AnyElement;
+
+ fn placeholder_text(&self, _: &mut Window, _: &mut App) -> Arc {
+ "Search profiles…".into()
+ }
+
+ fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> Option {
+ let text = if self.candidates.is_empty() {
+ "No profiles.".into()
+ } else {
+ "No profiles match your search.".into()
};
- let thread_profile_id = self.provider.profile_id(cx);
+ Some(text)
+ }
+
+ fn match_count(&self) -> usize {
+ self.filtered_entries.len()
+ }
- let entry = ContextMenuEntry::new(profile_name.clone())
- .toggleable(IconPosition::End, profile_id == thread_profile_id);
+ fn selected_index(&self) -> usize {
+ self.selected_index
+ }
+
+ fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context>) {
+ self.selected_index = ix.min(self.filtered_entries.len().saturating_sub(1));
+ cx.notify();
+ }
- let entry = if let Some(doc_text) = documentation {
- entry.documentation_aside(
- documentation_side(settings.dock),
- DocumentationEdge::Top,
- move |_| Label::new(doc_text).into_any_element(),
+ fn can_select(
+ &mut self,
+ ix: usize,
+ _window: &mut Window,
+ _cx: &mut Context>,
+ ) -> bool {
+ match self.filtered_entries.get(ix) {
+ Some(ProfilePickerEntry::Profile(_)) => true,
+ Some(ProfilePickerEntry::Header(_)) | None => false,
+ }
+ }
+
+ fn update_matches(
+ &mut self,
+ query: String,
+ window: &mut Window,
+ cx: &mut Context>,
+ ) -> Task<()> {
+ if query.is_empty() {
+ self.query.clear();
+ self.filtered_entries = Self::entries_from_candidates(&self.candidates);
+ self.selected_index = self
+ .index_of_profile(&self.provider.profile_id(cx))
+ .unwrap_or_else(|| self.first_selectable_index().unwrap_or(0));
+ cx.notify();
+ return Task::ready(());
+ }
+
+ if let Some(prev) = &self.cancel {
+ prev.store(true, Ordering::Relaxed);
+ }
+ let cancel = Arc::new(AtomicBool::new(false));
+ self.cancel = Some(cancel.clone());
+
+ let string_candidates = self.string_candidates.clone();
+ let background = self.background.clone();
+ let provider = self.provider.clone();
+ self.query = query.clone();
+
+ let cancel_for_future = cancel;
+
+ cx.spawn_in(window, async move |this, cx| {
+ let matches = match_strings(
+ string_candidates.as_ref(),
+ &query,
+ false,
+ true,
+ 100,
+ cancel_for_future.as_ref(),
+ background,
)
- } else {
- entry
+ .await;
+
+ this.update_in(cx, |this, _, cx| {
+ if this.delegate.query != query {
+ return;
+ }
+
+ this.delegate.filtered_entries = this.delegate.entries_from_matches(matches);
+ this.delegate.selected_index = this
+ .delegate
+ .index_of_profile(&provider.profile_id(cx))
+ .unwrap_or_else(|| this.delegate.first_selectable_index().unwrap_or(0));
+ cx.notify();
+ })
+ .ok();
+ })
+ }
+
+ fn confirm(&mut self, _: bool, _window: &mut Window, cx: &mut Context>) {
+ match self.filtered_entries.get(self.selected_index) {
+ Some(ProfilePickerEntry::Profile(entry)) => {
+ if let Some(candidate) = self.candidates.get(entry.candidate_index) {
+ let profile_id = candidate.id.clone();
+ let fs = self.fs.clone();
+ let provider = self.provider.clone();
+
+ update_settings_file(fs, cx, {
+ let profile_id = profile_id.clone();
+ move |settings, _cx| {
+ settings
+ .agent
+ .get_or_insert_default()
+ .set_profile(profile_id.0);
+ }
+ });
+
+ provider.set_profile(profile_id.clone(), cx);
+
+ telemetry::event!(
+ "agent_profile_switched",
+ profile_id = profile_id.as_str(),
+ source = "picker"
+ );
+ }
+
+ cx.emit(DismissEvent);
+ }
+ _ => {}
+ }
+ }
+
+ fn dismissed(&mut self, window: &mut Window, cx: &mut Context>) {
+ cx.defer_in(window, |picker, window, cx| {
+ picker.set_query("", window, cx);
+ });
+ cx.emit(DismissEvent);
+ }
+
+ fn render_match(
+ &self,
+ ix: usize,
+ selected: bool,
+ _: &mut Window,
+ cx: &mut Context>,
+ ) -> Option {
+ match self.filtered_entries.get(ix)? {
+ ProfilePickerEntry::Header(label) => Some(
+ div()
+ .px_2p5()
+ .pb_0p5()
+ .when(ix > 0, |this| {
+ this.mt_1p5()
+ .pt_2()
+ .border_t_1()
+ .border_color(cx.theme().colors().border_variant)
+ })
+ .child(
+ Label::new(label.clone())
+ .size(LabelSize::XSmall)
+ .color(Color::Muted),
+ )
+ .into_any_element(),
+ ),
+ ProfilePickerEntry::Profile(entry) => {
+ let candidate = self.candidates.get(entry.candidate_index)?;
+ let active_id = self.provider.profile_id(cx);
+ let is_active = active_id == candidate.id;
+
+ Some(
+ ListItem::new(SharedString::from(candidate.id.0.clone()))
+ .inset(true)
+ .spacing(ListItemSpacing::Sparse)
+ .toggle_state(selected)
+ .child(HighlightedLabel::new(
+ candidate.name.clone(),
+ entry.positions.clone(),
+ ))
+ .when(is_active, |this| {
+ this.end_slot(
+ div()
+ .pr_2()
+ .child(Icon::new(IconName::Check).color(Color::Accent)),
+ )
+ })
+ .into_any_element(),
+ )
+ }
+ }
+ }
+
+ fn documentation_aside(
+ &self,
+ _window: &mut Window,
+ cx: &mut Context>,
+ ) -> Option {
+ use std::rc::Rc;
+
+ let entry = match self.filtered_entries.get(self.selected_index)? {
+ ProfilePickerEntry::Profile(entry) => entry,
+ ProfilePickerEntry::Header(_) => return None,
};
- entry.handler({
- let fs = self.fs.clone();
- let provider = self.provider.clone();
- move |_window, cx| {
- update_settings_file(fs.clone(), cx, {
- let profile_id = profile_id.clone();
- move |settings, _cx| {
- settings
- .agent
- .get_or_insert_default()
- .set_profile(profile_id.0);
- }
- });
+ let candidate = self.candidates.get(entry.candidate_index)?;
+ let docs_aside = Self::documentation(candidate)?.to_string();
- provider.set_profile(profile_id.clone(), cx);
+ let settings = AgentSettings::get_global(cx);
+ let side = match settings.dock {
+ settings::DockPosition::Left => DocumentationSide::Right,
+ settings::DockPosition::Bottom | settings::DockPosition::Right => {
+ DocumentationSide::Left
}
+ };
+
+ Some(DocumentationAside {
+ side,
+ edge: DocumentationEdge::Top,
+ render: Rc::new(move |_| Label::new(docs_aside.clone()).into_any_element()),
})
}
+
+ fn render_footer(
+ &self,
+ _: &mut Window,
+ cx: &mut Context>,
+ ) -> Option {
+ Some(
+ h_flex()
+ .w_full()
+ .border_t_1()
+ .border_color(cx.theme().colors().border_variant)
+ .p_1()
+ .gap_4()
+ .justify_between()
+ .child(
+ Button::new("configure", "Configure")
+ .icon(IconName::Settings)
+ .icon_size(IconSize::Small)
+ .icon_color(Color::Muted)
+ .icon_position(IconPosition::Start)
+ .on_click(|_, window, cx| {
+ window.dispatch_action(ManageProfiles::default().boxed_clone(), cx);
+ }),
+ )
+ .into_any(),
+ )
+ }
}
-impl Render for ProfileSelector {
- fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement {
- let settings = AgentSettings::get_global(cx);
- let profile_id = self.provider.profile_id(cx);
- let profile = settings.profiles.get(&profile_id);
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use fs::FakeFs;
+ use gpui::TestAppContext;
- let selected_profile = profile
- .map(|profile| profile.name.clone())
- .unwrap_or_else(|| "Unknown".into());
+ #[gpui::test]
+ fn entries_include_custom_profiles(_cx: &mut TestAppContext) {
+ let candidates = vec![
+ ProfileCandidate {
+ id: AgentProfileId("write".into()),
+ name: SharedString::from("Write"),
+ is_builtin: true,
+ },
+ ProfileCandidate {
+ id: AgentProfileId("my-custom".into()),
+ name: SharedString::from("My Custom"),
+ is_builtin: false,
+ },
+ ];
- if self.provider.profiles_supported(cx) {
- let this = cx.entity();
- let focus_handle = self.focus_handle.clone();
- let trigger_button = Button::new("profile-selector-model", selected_profile)
- .label_size(LabelSize::Small)
- .color(Color::Muted)
- .icon(IconName::ChevronDown)
- .icon_size(IconSize::XSmall)
- .icon_position(IconPosition::End)
- .icon_color(Color::Muted)
- .selected_style(ButtonStyle::Tinted(TintColor::Accent));
-
- PopoverMenu::new("profile-selector")
- .trigger_with_tooltip(trigger_button, {
- move |window, cx| {
- Tooltip::for_action_in(
- "Toggle Profile Menu",
- &ToggleProfileSelector,
- &focus_handle,
- window,
- cx,
- )
- }
- })
- .anchor(
- if documentation_side(settings.dock) == DocumentationSide::Left {
- gpui::Corner::BottomRight
- } else {
- gpui::Corner::BottomLeft
- },
- )
- .with_handle(self.menu_handle.clone())
- .menu(move |window, cx| {
- Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
- })
- .offset(gpui::Point {
- x: px(0.0),
- y: px(-2.0),
- })
- .into_any_element()
- } else {
- Button::new("tools-not-supported-button", "Tools Unsupported")
- .disabled(true)
- .label_size(LabelSize::Small)
- .color(Color::Muted)
- .tooltip(Tooltip::text("This model does not support tools."))
- .into_any_element()
+ let entries = ProfilePickerDelegate::entries_from_candidates(&candidates);
+
+ assert!(entries.iter().any(|entry| matches!(
+ entry,
+ ProfilePickerEntry::Profile(profile)
+ if candidates[profile.candidate_index].id.as_str() == "my-custom"
+ )));
+ assert!(entries.iter().any(|entry| matches!(
+ entry,
+ ProfilePickerEntry::Header(label) if label.as_ref() == "Custom Profiles"
+ )));
+ }
+
+ #[gpui::test]
+ fn fuzzy_filter_returns_no_results_and_keeps_configure(cx: &mut TestAppContext) {
+ let candidates = vec![ProfileCandidate {
+ id: AgentProfileId("write".into()),
+ name: SharedString::from("Write"),
+ is_builtin: true,
+ }];
+
+ let delegate = ProfilePickerDelegate {
+ fs: FakeFs::new(cx.executor()),
+ provider: Arc::new(TestProfileProvider::new(AgentProfileId("write".into()))),
+ background: cx.executor(),
+ candidates,
+ string_candidates: Arc::new(Vec::new()),
+ filtered_entries: Vec::new(),
+ selected_index: 0,
+ query: String::new(),
+ cancel: None,
+ };
+
+ let matches = Vec::new(); // No matches
+ let _entries = delegate.entries_from_matches(matches);
+ }
+
+ #[gpui::test]
+ fn active_profile_selection_logic_works(cx: &mut TestAppContext) {
+ let candidates = vec![
+ ProfileCandidate {
+ id: AgentProfileId("write".into()),
+ name: SharedString::from("Write"),
+ is_builtin: true,
+ },
+ ProfileCandidate {
+ id: AgentProfileId("ask".into()),
+ name: SharedString::from("Ask"),
+ is_builtin: true,
+ },
+ ];
+
+ let delegate = ProfilePickerDelegate {
+ fs: FakeFs::new(cx.executor()),
+ provider: Arc::new(TestProfileProvider::new(AgentProfileId("write".into()))),
+ background: cx.executor(),
+ candidates,
+ string_candidates: Arc::new(Vec::new()),
+ filtered_entries: vec![
+ ProfilePickerEntry::Profile(ProfileMatchEntry {
+ candidate_index: 0,
+ positions: Vec::new(),
+ }),
+ ProfilePickerEntry::Profile(ProfileMatchEntry {
+ candidate_index: 1,
+ positions: Vec::new(),
+ }),
+ ],
+ selected_index: 0,
+ query: String::new(),
+ cancel: None,
+ };
+
+ // Active profile should be found at index 0
+ let active_index = delegate.index_of_profile(&AgentProfileId("write".into()));
+ assert_eq!(active_index, Some(0));
+ }
+
+ struct TestProfileProvider {
+ profile_id: AgentProfileId,
+ }
+
+ impl TestProfileProvider {
+ fn new(profile_id: AgentProfileId) -> Self {
+ Self { profile_id }
}
}
-}
-fn documentation_side(position: DockPosition) -> DocumentationSide {
- match position {
- DockPosition::Left => DocumentationSide::Right,
- DockPosition::Bottom => DocumentationSide::Left,
- DockPosition::Right => DocumentationSide::Left,
+ impl ProfileProvider for TestProfileProvider {
+ fn profile_id(&self, _cx: &App) -> AgentProfileId {
+ self.profile_id.clone()
+ }
+
+ fn set_profile(&self, _profile_id: AgentProfileId, _cx: &mut App) {}
+
+ fn profiles_supported(&self, _cx: &App) -> bool {
+ true
+ }
}
}
diff --git a/crates/agent_ui/src/terminal_inline_assistant.rs b/crates/agent_ui/src/terminal_inline_assistant.rs
index e7070c0d7fc4878c1f73a6d5f874607422ae53d6..4385d2420511c8a148b2a7a58fa8845bd2c19a07 100644
--- a/crates/agent_ui/src/terminal_inline_assistant.rs
+++ b/crates/agent_ui/src/terminal_inline_assistant.rs
@@ -238,7 +238,7 @@ impl TerminalInlineAssistant {
let latest_output = terminal.last_n_non_empty_lines(DEFAULT_CONTEXT_LINES);
let working_directory = terminal
.working_directory()
- .map(|path| path.to_string_lossy().to_string());
+ .map(|path| path.to_string_lossy().into_owned());
(latest_output, working_directory)
})
.ok()
diff --git a/crates/agent_ui/src/text_thread_editor.rs b/crates/agent_ui/src/text_thread_editor.rs
index e01bfc1d0d0745ed612429bc2dae71967fde36d5..e1265e923ef06b0bad945d3751dfbf24e0f1ee00 100644
--- a/crates/agent_ui/src/text_thread_editor.rs
+++ b/crates/agent_ui/src/text_thread_editor.rs
@@ -17,6 +17,7 @@ use editor::{
BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata, CustomBlockId, FoldId,
RenderBlock, ToDisplayPoint,
},
+ scroll::ScrollOffset,
};
use editor::{FoldPlaceholder, display_map::CreaseId};
use fs::Fs;
@@ -108,7 +109,7 @@ pub enum InsertDraggedFiles {
#[derive(Copy, Clone, Debug, PartialEq)]
struct ScrollPosition {
- offset_before_cursor: gpui::Point,
+ offset_before_cursor: gpui::Point,
cursor: Anchor,
}
@@ -631,7 +632,7 @@ impl TextThreadEditor {
let snapshot = editor.snapshot(window, cx);
let cursor_point = scroll_position.cursor.to_display_point(&snapshot);
let scroll_top =
- cursor_point.row().as_f32() - scroll_position.offset_before_cursor.y;
+ cursor_point.row().as_f64() - scroll_position.offset_before_cursor.y;
editor.set_scroll_position(
point(scroll_position.offset_before_cursor.x, scroll_top),
window,
@@ -979,7 +980,7 @@ impl TextThreadEditor {
let cursor_row = cursor
.to_display_point(&snapshot.display_snapshot)
.row()
- .as_f32();
+ .as_f64();
let scroll_position = editor
.scroll_manager
.anchor()
diff --git a/crates/agent_ui/src/ui/burn_mode_tooltip.rs b/crates/agent_ui/src/ui/burn_mode_tooltip.rs
index 72faaa614d0d531365fef9ba5ff0e62a6fbcf145..f95dc1250e36bba388452ce11e6ec783e44248e1 100644
--- a/crates/agent_ui/src/ui/burn_mode_tooltip.rs
+++ b/crates/agent_ui/src/ui/burn_mode_tooltip.rs
@@ -48,7 +48,7 @@ impl Render for BurnModeTooltip {
let keybinding = KeyBinding::for_action(&ToggleBurnMode, window, cx)
.map(|kb| kb.size(rems_from_px(12.)));
- tooltip_container(window, cx, |this, _, _| {
+ tooltip_container(cx, |this, _| {
this
.child(
h_flex()
diff --git a/crates/agent_ui/src/ui/context_pill.rs b/crates/agent_ui/src/ui/context_pill.rs
index c42d33c6d346cf869e7b27dc04c63202b3502068..f85a06455439d8e52a7b4272bc7f8069f36548ac 100644
--- a/crates/agent_ui/src/ui/context_pill.rs
+++ b/crates/agent_ui/src/ui/context_pill.rs
@@ -704,7 +704,7 @@ impl ContextPillHover {
impl Render for ContextPillHover {
fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement {
- tooltip_container(window, cx, move |this, window, cx| {
+ tooltip_container(cx, move |this, cx| {
this.occlude()
.on_mouse_move(|_, _, cx| cx.stop_propagation())
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
diff --git a/crates/agent_ui/src/ui/onboarding_modal.rs b/crates/agent_ui/src/ui/onboarding_modal.rs
index b8b038bdfca334ba048aed0a59cecd0f3da285b0..ad404afa784974631f914e6fece2de6b6c7d6a46 100644
--- a/crates/agent_ui/src/ui/onboarding_modal.rs
+++ b/crates/agent_ui/src/ui/onboarding_modal.rs
@@ -40,7 +40,7 @@ impl AgentOnboardingModal {
}
fn view_blog(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) {
- cx.open_url("http://zed.dev/blog/fastest-ai-code-editor");
+ cx.open_url("https://zed.dev/blog/fastest-ai-code-editor");
cx.notify();
agent_onboarding_event!("Blog Link Clicked");
diff --git a/crates/agent_ui/src/ui/unavailable_editing_tooltip.rs b/crates/agent_ui/src/ui/unavailable_editing_tooltip.rs
index 78d4c64e0acc7bff86516657f76007e78a54d304..2993fb89a989619ecfe3d79b06d82a2a6f71fc31 100644
--- a/crates/agent_ui/src/ui/unavailable_editing_tooltip.rs
+++ b/crates/agent_ui/src/ui/unavailable_editing_tooltip.rs
@@ -12,8 +12,8 @@ impl UnavailableEditingTooltip {
}
impl Render for UnavailableEditingTooltip {
- fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement {
- tooltip_container(window, cx, |this, _, _| {
+ fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement {
+ tooltip_container(cx, |this, _| {
this.child(Label::new("Unavailable Editing")).child(
div().max_w_64().child(
Label::new(format!(
diff --git a/crates/anthropic/src/anthropic.rs b/crates/anthropic/src/anthropic.rs
index a9a4fbb6340b59cafe3bbc555efb6ad5c9e11806..93334fd950f27309000ff175aeafb18767bd2867 100644
--- a/crates/anthropic/src/anthropic.rs
+++ b/crates/anthropic/src/anthropic.rs
@@ -67,7 +67,6 @@ pub enum Model {
alias = "claude-opus-4-1-thinking-latest"
)]
ClaudeOpus4_1Thinking,
- #[default]
#[serde(rename = "claude-sonnet-4", alias = "claude-sonnet-4-latest")]
ClaudeSonnet4,
#[serde(
@@ -75,6 +74,14 @@ pub enum Model {
alias = "claude-sonnet-4-thinking-latest"
)]
ClaudeSonnet4Thinking,
+ #[default]
+ #[serde(rename = "claude-sonnet-4-5", alias = "claude-sonnet-4-5-latest")]
+ ClaudeSonnet4_5,
+ #[serde(
+ rename = "claude-sonnet-4-5-thinking",
+ alias = "claude-sonnet-4-5-thinking-latest"
+ )]
+ ClaudeSonnet4_5Thinking,
#[serde(rename = "claude-3-7-sonnet", alias = "claude-3-7-sonnet-latest")]
Claude3_7Sonnet,
#[serde(
@@ -133,6 +140,14 @@ impl Model {
return Ok(Self::ClaudeOpus4);
}
+ if id.starts_with("claude-sonnet-4-5-thinking") {
+ return Ok(Self::ClaudeSonnet4_5Thinking);
+ }
+
+ if id.starts_with("claude-sonnet-4-5") {
+ return Ok(Self::ClaudeSonnet4_5);
+ }
+
if id.starts_with("claude-sonnet-4-thinking") {
return Ok(Self::ClaudeSonnet4Thinking);
}
@@ -180,6 +195,8 @@ impl Model {
Self::ClaudeOpus4_1Thinking => "claude-opus-4-1-thinking-latest",
Self::ClaudeSonnet4 => "claude-sonnet-4-latest",
Self::ClaudeSonnet4Thinking => "claude-sonnet-4-thinking-latest",
+ Self::ClaudeSonnet4_5 => "claude-sonnet-4-5-latest",
+ Self::ClaudeSonnet4_5Thinking => "claude-sonnet-4-5-thinking-latest",
Self::Claude3_5Sonnet => "claude-3-5-sonnet-latest",
Self::Claude3_7Sonnet => "claude-3-7-sonnet-latest",
Self::Claude3_7SonnetThinking => "claude-3-7-sonnet-thinking-latest",
@@ -197,6 +214,7 @@ impl Model {
Self::ClaudeOpus4 | Self::ClaudeOpus4Thinking => "claude-opus-4-20250514",
Self::ClaudeOpus4_1 | Self::ClaudeOpus4_1Thinking => "claude-opus-4-1-20250805",
Self::ClaudeSonnet4 | Self::ClaudeSonnet4Thinking => "claude-sonnet-4-20250514",
+ Self::ClaudeSonnet4_5 | Self::ClaudeSonnet4_5Thinking => "claude-sonnet-4-5-20250929",
Self::Claude3_5Sonnet => "claude-3-5-sonnet-latest",
Self::Claude3_7Sonnet | Self::Claude3_7SonnetThinking => "claude-3-7-sonnet-latest",
Self::Claude3_5Haiku => "claude-3-5-haiku-latest",
@@ -215,6 +233,8 @@ impl Model {
Self::ClaudeOpus4_1Thinking => "Claude Opus 4.1 Thinking",
Self::ClaudeSonnet4 => "Claude Sonnet 4",
Self::ClaudeSonnet4Thinking => "Claude Sonnet 4 Thinking",
+ Self::ClaudeSonnet4_5 => "Claude Sonnet 4.5",
+ Self::ClaudeSonnet4_5Thinking => "Claude Sonnet 4.5 Thinking",
Self::Claude3_7Sonnet => "Claude 3.7 Sonnet",
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
Self::Claude3_7SonnetThinking => "Claude 3.7 Sonnet Thinking",
@@ -236,6 +256,8 @@ impl Model {
| Self::ClaudeOpus4_1Thinking
| Self::ClaudeSonnet4
| Self::ClaudeSonnet4Thinking
+ | Self::ClaudeSonnet4_5
+ | Self::ClaudeSonnet4_5Thinking
| Self::Claude3_5Sonnet
| Self::Claude3_5Haiku
| Self::Claude3_7Sonnet
@@ -261,6 +283,8 @@ impl Model {
| Self::ClaudeOpus4_1Thinking
| Self::ClaudeSonnet4
| Self::ClaudeSonnet4Thinking
+ | Self::ClaudeSonnet4_5
+ | Self::ClaudeSonnet4_5Thinking
| Self::Claude3_5Sonnet
| Self::Claude3_5Haiku
| Self::Claude3_7Sonnet
@@ -280,6 +304,8 @@ impl Model {
| Self::ClaudeOpus4_1Thinking
| Self::ClaudeSonnet4
| Self::ClaudeSonnet4Thinking
+ | Self::ClaudeSonnet4_5
+ | Self::ClaudeSonnet4_5Thinking
| Self::Claude3_5Sonnet
| Self::Claude3_7Sonnet
| Self::Claude3_7SonnetThinking
@@ -299,6 +325,8 @@ impl Model {
| Self::ClaudeOpus4_1Thinking
| Self::ClaudeSonnet4
| Self::ClaudeSonnet4Thinking
+ | Self::ClaudeSonnet4_5
+ | Self::ClaudeSonnet4_5Thinking
| Self::Claude3_5Sonnet
| Self::Claude3_7Sonnet
| Self::Claude3_7SonnetThinking
@@ -318,6 +346,7 @@ impl Model {
Self::ClaudeOpus4
| Self::ClaudeOpus4_1
| Self::ClaudeSonnet4
+ | Self::ClaudeSonnet4_5
| Self::Claude3_5Sonnet
| Self::Claude3_7Sonnet
| Self::Claude3_5Haiku
@@ -327,6 +356,7 @@ impl Model {
Self::ClaudeOpus4Thinking
| Self::ClaudeOpus4_1Thinking
| Self::ClaudeSonnet4Thinking
+ | Self::ClaudeSonnet4_5Thinking
| Self::Claude3_7SonnetThinking => AnthropicModelMode::Thinking {
budget_tokens: Some(4_096),
},
diff --git a/crates/askpass/Cargo.toml b/crates/askpass/Cargo.toml
index 297139545caeb1c23386cb9f5d8f97b84791dc10..5c7367b9dbf0fe72fb97b0904bd6570f93270299 100644
--- a/crates/askpass/Cargo.toml
+++ b/crates/askpass/Cargo.toml
@@ -18,6 +18,7 @@ gpui.workspace = true
net.workspace = true
proto.workspace = true
smol.workspace = true
+log.workspace = true
tempfile.workspace = true
util.workspace = true
workspace-hack.workspace = true
@@ -25,3 +26,6 @@ zeroize.workspace = true
[target.'cfg(target_os = "windows")'.dependencies]
windows.workspace = true
+
+[package.metadata.cargo-machete]
+ignored = ["log"]
diff --git a/crates/askpass/src/askpass.rs b/crates/askpass/src/askpass.rs
index 6405be5966ba512d68a9ab638d1216d678200968..2dfdd792d94b67d2b6e39305b804fcbf1957f41e 100644
--- a/crates/askpass/src/askpass.rs
+++ b/crates/askpass/src/askpass.rs
@@ -1,8 +1,8 @@
mod encrypted_password;
pub use encrypted_password::{EncryptedPassword, ProcessExt};
+use util::paths::PathExt;
-#[cfg(target_os = "windows")]
use std::sync::OnceLock;
use std::{ffi::OsStr, time::Duration};
@@ -14,10 +14,16 @@ use futures::{
};
use gpui::{AsyncApp, BackgroundExecutor, Task};
use smol::fs;
-use util::ResultExt as _;
+use util::{ResultExt as _, debug_panic};
use crate::encrypted_password::decrypt;
+/// Path to the program used for askpass
+///
+/// On Unix and remote servers, this defaults to the current executable
+/// On Windows, this is set to the CLI variant of zed
+static ASKPASS_PROGRAM: OnceLock = OnceLock::new();
+
#[derive(PartialEq, Eq)]
pub enum AskPassResult {
CancelledByUser,
@@ -46,10 +52,10 @@ impl AskPassDelegate {
Self { tx, _task: task }
}
- pub async fn ask_password(&mut self, prompt: String) -> Result {
+ pub async fn ask_password(&mut self, prompt: String) -> Option {
let (tx, rx) = oneshot::channel();
- self.tx.send((prompt, tx)).await?;
- Ok(rx.await?)
+ self.tx.send((prompt, tx)).await.ok()?;
+ rx.await.ok()
}
}
@@ -85,11 +91,15 @@ impl AskPassSession {
let askpass_script_path = temp_dir.path().join(ASKPASS_SCRIPT_NAME);
let (askpass_opened_tx, askpass_opened_rx) = oneshot::channel::<()>();
let listener = UnixListener::bind(&askpass_socket).context("creating askpass socket")?;
- #[cfg(not(target_os = "windows"))]
- let zed_path = util::get_shell_safe_zed_path()?;
- #[cfg(target_os = "windows")]
- let zed_path = std::env::current_exe()
- .context("finding current executable path for use in askpass")?;
+
+ let current_exec =
+ std::env::current_exe().context("Failed to determine current zed executable path.")?;
+
+ let askpass_program = ASKPASS_PROGRAM
+ .get_or_init(|| current_exec)
+ .try_shell_safe()
+ .context("Failed to shell-escape Askpass program path.")?
+ .to_string();
let (askpass_kill_master_tx, askpass_kill_master_rx) = oneshot::channel::<()>();
let mut kill_tx = Some(askpass_kill_master_tx);
@@ -109,12 +119,7 @@ impl AskPassSession {
buffer.clear();
}
let prompt = String::from_utf8_lossy(&buffer);
- if let Some(password) = delegate
- .ask_password(prompt.to_string())
- .await
- .context("getting askpass password")
- .log_err()
- {
+ if let Some(password) = delegate.ask_password(prompt.into_owned()).await {
#[cfg(target_os = "windows")]
{
askpass_secret.get_or_init(|| password.clone());
@@ -137,7 +142,7 @@ impl AskPassSession {
});
// Create an askpass script that communicates back to this process.
- let askpass_script = generate_askpass_script(&zed_path, &askpass_socket);
+ let askpass_script = generate_askpass_script(&askpass_program, &askpass_socket);
fs::write(&askpass_script_path, askpass_script)
.await
.with_context(|| format!("creating askpass script at {askpass_script_path:?}"))?;
@@ -252,12 +257,17 @@ pub fn main(socket: &str) {
}
}
+pub fn set_askpass_program(path: std::path::PathBuf) {
+ if ASKPASS_PROGRAM.set(path).is_err() {
+ debug_panic!("askpass program has already been set");
+ }
+}
+
#[inline]
#[cfg(not(target_os = "windows"))]
-fn generate_askpass_script(zed_path: &str, askpass_socket: &std::path::Path) -> String {
+fn generate_askpass_script(askpass_program: &str, askpass_socket: &std::path::Path) -> String {
format!(
- "{shebang}\n{print_args} | {zed_exe} --askpass={askpass_socket} 2> /dev/null \n",
- zed_exe = zed_path,
+ "{shebang}\n{print_args} | {askpass_program} --askpass={askpass_socket} 2> /dev/null \n",
askpass_socket = askpass_socket.display(),
print_args = "printf '%s\\0' \"$@\"",
shebang = "#!/bin/sh",
@@ -266,13 +276,12 @@ fn generate_askpass_script(zed_path: &str, askpass_socket: &std::path::Path) ->
#[inline]
#[cfg(target_os = "windows")]
-fn generate_askpass_script(zed_path: &std::path::Path, askpass_socket: &std::path::Path) -> String {
+fn generate_askpass_script(askpass_program: &str, askpass_socket: &std::path::Path) -> String {
format!(
r#"
$ErrorActionPreference = 'Stop';
- ($args -join [char]0) | & "{zed_exe}" --askpass={askpass_socket} 2> $null
+ ($args -join [char]0) | & "{askpass_program}" --askpass={askpass_socket} 2> $null
"#,
- zed_exe = zed_path.display(),
askpass_socket = askpass_socket.display(),
)
}
diff --git a/crates/askpass/src/encrypted_password.rs b/crates/askpass/src/encrypted_password.rs
index 2684f76e8992fa5f33bbeb07996e834fccbf04fa..b5495df6e43cab8efe7a3e505602df11d03a1092 100644
--- a/crates/askpass/src/encrypted_password.rs
+++ b/crates/askpass/src/encrypted_password.rs
@@ -67,7 +67,7 @@ impl TryFrom<&str> for EncryptedPassword {
unsafe {
CryptProtectMemory(
value.as_mut_ptr() as _,
- len,
+ padded_length,
CRYPTPROTECTMEMORY_SAME_PROCESS,
)?;
}
@@ -97,7 +97,7 @@ pub(crate) fn decrypt(mut password: EncryptedPassword) -> Result {
unsafe {
CryptUnprotectMemory(
password.0.as_mut_ptr() as _,
- password.1,
+ password.0.len().try_into()?,
CRYPTPROTECTMEMORY_SAME_PROCESS,
)
.context("while decrypting a SSH password")?
diff --git a/crates/assistant_slash_command/src/extension_slash_command.rs b/crates/assistant_slash_command/src/extension_slash_command.rs
index 301cf65cb45895ff2475b74510264260d827da61..6dd2c05f192358f9fcd843add21df94e301dc6b7 100644
--- a/crates/assistant_slash_command/src/extension_slash_command.rs
+++ b/crates/assistant_slash_command/src/extension_slash_command.rs
@@ -50,7 +50,7 @@ impl WorktreeDelegate for WorktreeDelegateAdapter {
}
fn root_path(&self) -> String {
- self.0.worktree_root_path().to_string_lossy().to_string()
+ self.0.worktree_root_path().to_string_lossy().into_owned()
}
async fn read_text_file(&self, path: &RelPath) -> Result {
@@ -61,7 +61,7 @@ impl WorktreeDelegate for WorktreeDelegateAdapter {
self.0
.which(binary_name.as_ref())
.await
- .map(|path| path.to_string_lossy().to_string())
+ .map(|path| path.to_string_lossy().into_owned())
}
async fn shell_env(&self) -> Vec<(String, String)> {
diff --git a/crates/assistant_slash_commands/src/diagnostics_command.rs b/crates/assistant_slash_commands/src/diagnostics_command.rs
index 77048b1108577689ade4a5e149e54e9784ba0d33..3a9c33061575d385652b685dcca70ee87c6cac35 100644
--- a/crates/assistant_slash_commands/src/diagnostics_command.rs
+++ b/crates/assistant_slash_commands/src/diagnostics_command.rs
@@ -6,7 +6,7 @@ use assistant_slash_command::{
use fuzzy::{PathMatch, StringMatchCandidate};
use gpui::{App, Entity, Task, WeakEntity};
use language::{
- Anchor, BufferSnapshot, DiagnosticEntry, DiagnosticSeverity, LspAdapterDelegate,
+ Anchor, BufferSnapshot, DiagnosticEntryRef, DiagnosticSeverity, LspAdapterDelegate,
OffsetRangeExt, ToOffset,
};
use project::{DiagnosticSummary, PathMatchCandidateSet, Project};
@@ -367,7 +367,7 @@ pub fn collect_buffer_diagnostics(
fn collect_diagnostic(
output: &mut SlashCommandOutput,
- entry: &DiagnosticEntry,
+ entry: &DiagnosticEntryRef<'_, Anchor>,
snapshot: &BufferSnapshot,
include_warnings: bool,
) {
diff --git a/crates/assistant_slash_commands/src/selection_command.rs b/crates/assistant_slash_commands/src/selection_command.rs
index 068a66339b9a3c8619e1050feb4d70b32f97f657..c8692dec718a03af777753f35ae646f245878ed9 100644
--- a/crates/assistant_slash_commands/src/selection_command.rs
+++ b/crates/assistant_slash_commands/src/selection_command.rs
@@ -139,7 +139,7 @@ pub fn selections_creases(
let language_name = language_name.as_deref().unwrap_or("");
let filename = snapshot
.file_at(range.start)
- .map(|file| file.full_path(cx).to_string_lossy().to_string());
+ .map(|file| file.full_path(cx).to_string_lossy().into_owned());
let text = if language_name == "markdown" {
selected_text
.lines()
diff --git a/crates/assistant_tools/src/edit_file_tool.rs b/crates/assistant_tools/src/edit_file_tool.rs
index c8b7add0b8f17f70d2157681b3ae115535a7616e..f88978650a32cdc6922a1ff864b0ee898721df80 100644
--- a/crates/assistant_tools/src/edit_file_tool.rs
+++ b/crates/assistant_tools/src/edit_file_tool.rs
@@ -17,7 +17,7 @@ use editor::{
use futures::StreamExt;
use gpui::{
Animation, AnimationExt, AnyWindowHandle, App, AppContext, AsyncApp, Entity, Task,
- TextStyleRefinement, WeakEntity, pulsating_between, px,
+ TextStyleRefinement, WeakEntity, pulsating_between,
};
use indoc::formatdoc;
use language::{
@@ -1003,7 +1003,7 @@ impl ToolCard for EditFileToolCard {
font_size: Some(
TextSize::Small
.rems(cx)
- .to_pixels(ThemeSettings::get_global(cx).agent_font_size(cx))
+ .to_pixels(ThemeSettings::get_global(cx).agent_ui_font_size(cx))
.into(),
),
..TextStyleRefinement::default()
@@ -1102,7 +1102,7 @@ impl ToolCard for EditFileToolCard {
.relative()
.h_full()
.when(!self.full_height_expanded, |editor_container| {
- editor_container.max_h(px(COLLAPSED_LINES as f32 * editor_line_height.0))
+ editor_container.max_h(COLLAPSED_LINES as f32 * editor_line_height)
})
.overflow_hidden()
.border_t_1()
@@ -1161,7 +1161,7 @@ async fn build_buffer(
LineEnding::normalize(&mut text);
let text = Rope::from(text);
let language = cx
- .update(|_cx| language_registry.language_for_file_path(&path))?
+ .update(|_cx| language_registry.load_language_for_file_path(&path))?
.await
.ok();
let buffer = cx.new(|cx| {
diff --git a/crates/assistant_tools/src/find_path_tool.rs b/crates/assistant_tools/src/find_path_tool.rs
index 53da3106d2f9fa5fd7928b4291cc8e80daa3bfdb..0bc478251cb5d3d558dda4fb41df02e85eaafde2 100644
--- a/crates/assistant_tools/src/find_path_tool.rs
+++ b/crates/assistant_tools/src/find_path_tool.rs
@@ -262,7 +262,7 @@ impl ToolCard for FindPathToolCard {
.children(self.paths.iter().enumerate().map(|(index, path)| {
let path_clone = path.clone();
let workspace_clone = workspace.clone();
- let button_label = path.to_string_lossy().to_string();
+ let button_label = path.to_string_lossy().into_owned();
Button::new(("path", index), button_label)
.icon(IconName::ArrowUpRight)
diff --git a/crates/assistant_tools/src/open_tool.rs b/crates/assistant_tools/src/open_tool.rs
index 6dbf66749b932804df6e00fed726360f0492c7f3..a1aafad041364b0ffca01cc1890c2cc10b3d7b01 100644
--- a/crates/assistant_tools/src/open_tool.rs
+++ b/crates/assistant_tools/src/open_tool.rs
@@ -104,7 +104,7 @@ mod tests {
async fn test_to_absolute_path(cx: &mut TestAppContext) {
init_test(cx);
let temp_dir = TempDir::new().expect("Failed to create temp directory");
- let temp_path = temp_dir.path().to_string_lossy().to_string();
+ let temp_path = temp_dir.path().to_string_lossy().into_owned();
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
diff --git a/crates/assistant_tools/src/terminal_tool.rs b/crates/assistant_tools/src/terminal_tool.rs
index 8014a39e23137ad71b91e5c24d5d79699b530e5d..bc6f5f2a612bf17468577624e34d49119f3813c8 100644
--- a/crates/assistant_tools/src/terminal_tool.rs
+++ b/crates/assistant_tools/src/terminal_tool.rs
@@ -18,7 +18,7 @@ use portable_pty::{CommandBuilder, PtySize, native_pty_system};
use project::Project;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
-use settings::Settings;
+use settings::{Settings, SettingsLocation};
use std::{
env,
path::{Path, PathBuf},
@@ -27,12 +27,13 @@ use std::{
time::{Duration, Instant},
};
use task::{Shell, ShellBuilder};
+use terminal::terminal_settings::TerminalSettings;
use terminal_view::TerminalView;
use theme::ThemeSettings;
use ui::{CommonAnimationExt, Disclosure, Tooltip, prelude::*};
use util::{
- ResultExt, get_default_system_shell, markdown::MarkdownInlineCode, size::format_file_size,
- time::duration_alt_display,
+ ResultExt, get_default_system_shell_preferring_bash, markdown::MarkdownInlineCode,
+ size::format_file_size, time::duration_alt_display,
};
use workspace::Workspace;
@@ -119,17 +120,29 @@ impl Tool for TerminalTool {
};
let cwd = working_dir.clone();
- let env = match &working_dir {
+ let env = match &cwd {
Some(dir) => project.update(cx, |project, cx| {
- project.directory_environment(dir.as_path().into(), cx)
+ let worktree = project.find_worktree(dir.as_path(), cx);
+ let shell = TerminalSettings::get(
+ worktree.as_ref().map(|(worktree, path)| SettingsLocation {
+ worktree_id: worktree.read(cx).id(),
+ path: &path,
+ }),
+ cx,
+ )
+ .shell
+ .clone();
+ project.directory_environment(&shell, dir.as_path().into(), cx)
}),
None => Task::ready(None).shared(),
};
- let remote_shell = project.update(cx, |project, cx| {
- project
- .remote_client()
- .and_then(|r| r.read(cx).default_system_shell())
- });
+ let shell = project
+ .update(cx, |project, cx| {
+ project
+ .remote_client()
+ .and_then(|r| r.read(cx).default_system_shell())
+ })
+ .unwrap_or_else(|| get_default_system_shell_preferring_bash());
let env = cx.spawn(async move |_| {
let mut env = env.await.unwrap_or_default();
@@ -139,18 +152,22 @@ impl Tool for TerminalTool {
env
});
+ let build_cmd = {
+ let input_command = input.command.clone();
+ move || {
+ ShellBuilder::new(&Shell::Program(shell))
+ .redirect_stdin_to_dev_null()
+ .build(Some(input_command), &[])
+ }
+ };
+
let Some(window) = window else {
// Headless setup, a test or eval. Our terminal subsystem requires a workspace,
// so bypass it and provide a convincing imitation using a pty.
let task = cx.background_spawn(async move {
let env = env.await;
let pty_system = native_pty_system();
- let (command, args) = ShellBuilder::new(
- remote_shell.as_deref(),
- &Shell::Program(get_default_system_shell()),
- )
- .redirect_stdin_to_dev_null()
- .build(Some(input.command.clone()), &[]);
+ let (command, args) = build_cmd();
let mut cmd = CommandBuilder::new(command);
cmd.args(args);
for (k, v) in env {
@@ -187,16 +204,10 @@ impl Tool for TerminalTool {
};
};
- let command = input.command.clone();
let terminal = cx.spawn({
let project = project.downgrade();
async move |cx| {
- let (command, args) = ShellBuilder::new(
- remote_shell.as_deref(),
- &Shell::Program(get_default_system_shell()),
- )
- .redirect_stdin_to_dev_null()
- .build(Some(input.command), &[]);
+ let (command, args) = build_cmd();
let env = env.await;
project
.update(cx, |project, cx| {
@@ -215,18 +226,18 @@ impl Tool for TerminalTool {
}
});
- let command_markdown =
- cx.new(|cx| Markdown::new(format!("```bash\n{}\n```", command).into(), None, None, cx));
-
- let card = cx.new(|cx| {
- TerminalToolCard::new(
- command_markdown.clone(),
- working_dir.clone(),
- cx.entity_id(),
+ let command_markdown = cx.new(|cx| {
+ Markdown::new(
+ format!("```bash\n{}\n```", input.command).into(),
+ None,
+ None,
cx,
)
});
+ let card =
+ cx.new(|cx| TerminalToolCard::new(command_markdown, working_dir, cx.entity_id(), cx));
+
let output = cx.spawn({
let card = card.clone();
async move |cx| {
@@ -267,7 +278,7 @@ impl Tool for TerminalTool {
let previous_len = content.len();
let (processed_content, finished_with_empty_output) = process_content(
&content,
- &command,
+ &input.command,
exit_status.map(portable_pty::ExitStatus::from),
);
@@ -475,7 +486,7 @@ impl ToolCard for TerminalToolCard {
.as_ref()
.cloned()
.or_else(|| env::current_dir().ok())
- .map(|path| format!("{}", path.display()))
+ .map(|path| path.display().to_string())
.unwrap_or_else(|| "current directory".to_string());
let header = h_flex()
@@ -693,7 +704,6 @@ mod tests {
use serde_json::json;
use settings::{Settings, SettingsStore};
use terminal::terminal_settings::TerminalSettings;
- use theme::ThemeSettings;
use util::{ResultExt as _, test::TempTree};
use super::*;
@@ -708,7 +718,7 @@ mod tests {
language::init(cx);
Project::init_settings(cx);
workspace::init_settings(cx);
- ThemeSettings::register(cx);
+ theme::init(theme::LoadThemes::JustBase, cx);
TerminalSettings::register(cx);
EditorSettings::register(cx);
});
diff --git a/crates/audio/src/audio.rs b/crates/audio/src/audio.rs
index 379af34751f91cafadd8c4849102624a3aebdcad..955713b7f0105775bb8a6e4d0d315a3fb52fdd07 100644
--- a/crates/audio/src/audio.rs
+++ b/crates/audio/src/audio.rs
@@ -55,6 +55,7 @@ pub fn init(cx: &mut App) {
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub enum Sound {
Joined,
+ GuestJoined,
Leave,
Mute,
Unmute,
@@ -67,6 +68,7 @@ impl Sound {
fn file(&self) -> &'static str {
match self {
Self::Joined => "joined_call",
+ Self::GuestJoined => "guest_joined_call",
Self::Leave => "leave_call",
Self::Mute => "mute",
Self::Unmute => "unmute",
diff --git a/crates/audio/src/audio_settings.rs b/crates/audio/src/audio_settings.rs
index cba7d45c31f4674be6a69c10ab34f00e0b8cbbd1..61a993c3358e5e2bf39b626a0764833508bee742 100644
--- a/crates/audio/src/audio_settings.rs
+++ b/crates/audio/src/audio_settings.rs
@@ -42,7 +42,7 @@ pub struct AudioSettings {
/// Configuration of audio in Zed
impl Settings for AudioSettings {
- fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
+ fn from_settings(content: &settings::SettingsContent) -> Self {
let audio = &content.audio.as_ref().unwrap();
AudioSettings {
rodio_audio: audio.rodio_audio.unwrap(),
diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs
index 31121aa6f6d68984e1b9a81a4e500d6a3e9c21d1..2d1ea7269e2ab102962faeae848d94a8c491d8f2 100644
--- a/crates/auto_update/src/auto_update.rs
+++ b/crates/auto_update/src/auto_update.rs
@@ -127,7 +127,7 @@ struct AutoUpdateSetting(bool);
///
/// Default: true
impl Settings for AutoUpdateSetting {
- fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
+ fn from_settings(content: &settings::SettingsContent) -> Self {
Self(content.auto_update.unwrap())
}
}
@@ -310,10 +310,10 @@ impl AutoUpdater {
// the app after an update, we use `set_restart_path` to run the auto
// update helper instead of the app, so that it can overwrite the app
// and then spawn the new binary.
- let quit_subscription = Some(cx.on_app_quit(|_, _| async move {
- #[cfg(target_os = "windows")]
- finalize_auto_update_on_quit();
- }));
+ #[cfg(target_os = "windows")]
+ let quit_subscription = Some(cx.on_app_quit(|_, _| finalize_auto_update_on_quit()));
+ #[cfg(not(target_os = "windows"))]
+ let quit_subscription = None;
cx.on_app_restart(|this, _| {
this.quit_subscription.take();
@@ -942,11 +942,12 @@ async fn install_release_windows(downloaded_installer: PathBuf) -> Result