Compare commits
19 Commits
local-live
...
add-compon
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8103b9ffa | ||
|
|
7719afeafa | ||
|
|
27b25100c9 | ||
|
|
4d16a3a2a8 | ||
|
|
d36230e46e | ||
|
|
8dddbf31bf | ||
|
|
0c8bb20424 | ||
|
|
5edb86511b | ||
|
|
7e75244620 | ||
|
|
175290c43a | ||
|
|
27c7541e5b | ||
|
|
ae9f51d0e0 | ||
|
|
f609429ac4 | ||
|
|
7cbf651cf9 | ||
|
|
81475ef182 | ||
|
|
9ce110f9ad | ||
|
|
f500de9c3b | ||
|
|
9d810a44fd | ||
|
|
3248bb9172 |
@@ -1,6 +1,3 @@
|
||||
[env]
|
||||
LK_CUSTOM_WEBRTC = { value = "../livekit-rust-sdks/webrtc-sys/libwebrtc/linux-x64-release", relative = true }
|
||||
|
||||
[build]
|
||||
# v0 mangling scheme provides more detailed backtraces around closures
|
||||
rustflags = ["-C", "symbol-mangling-version=v0", "--cfg", "tokio_unstable"]
|
||||
|
||||
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -129,9 +129,8 @@ jobs:
|
||||
run: |
|
||||
cargo build --workspace --bins --all-features
|
||||
cargo check -p gpui --features "macos-blade"
|
||||
cargo check -p workspace
|
||||
cargo check -p workspace --features "livekit-cross-platform"
|
||||
cargo build -p remote_server
|
||||
script/check-rust-livekit-macos
|
||||
|
||||
linux_tests:
|
||||
timeout-minutes: 60
|
||||
@@ -163,10 +162,8 @@ jobs:
|
||||
- name: Run tests
|
||||
uses: ./.github/actions/run_tests
|
||||
|
||||
- name: Build other binaries and features
|
||||
run: |
|
||||
cargo build -p zed
|
||||
cargo check -p workspace
|
||||
- name: Build Zed
|
||||
run: cargo build -p zed
|
||||
|
||||
build_remote_server:
|
||||
timeout-minutes: 60
|
||||
|
||||
8
.github/workflows/deploy_cloudflare.yml
vendored
8
.github/workflows/deploy_cloudflare.yml
vendored
@@ -37,28 +37,28 @@ jobs:
|
||||
mdbook build ./docs --dest-dir=../target/deploy/docs/
|
||||
|
||||
- name: Deploy Docs
|
||||
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
|
||||
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy target/deploy --project-name=docs
|
||||
|
||||
- name: Deploy Install
|
||||
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
|
||||
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: r2 object put -f script/install.sh zed-open-source-website-assets/install.sh
|
||||
|
||||
- name: Deploy Docs Workers
|
||||
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
|
||||
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy .cloudflare/docs-proxy/src/worker.js
|
||||
|
||||
- name: Deploy Install Workers
|
||||
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
|
||||
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
|
||||
46
Cargo.lock
generated
46
Cargo.lock
generated
@@ -2862,6 +2862,29 @@ dependencies = [
|
||||
"gpui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "component_preview"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"component_system",
|
||||
"gpui",
|
||||
"strum 0.25.0",
|
||||
"ui",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "component_system"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"collections",
|
||||
"gpui",
|
||||
"lazy_static",
|
||||
"linkme",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
@@ -3480,9 +3503,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.2.9"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
|
||||
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
@@ -3957,7 +3980,6 @@ dependencies = [
|
||||
"unindent",
|
||||
"url",
|
||||
"util",
|
||||
"uuid",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
@@ -7422,7 +7444,6 @@ dependencies = [
|
||||
"settings",
|
||||
"theme",
|
||||
"ui",
|
||||
"util",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
@@ -8579,9 +8600,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.3"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||
checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
|
||||
|
||||
[[package]]
|
||||
name = "pathfinder_geometry"
|
||||
@@ -10695,9 +10716,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.7"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519"
|
||||
checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"digest",
|
||||
@@ -13811,9 +13832,14 @@ name = "ui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"collections",
|
||||
"component_system",
|
||||
"gpui",
|
||||
"itertools 0.13.0",
|
||||
"lazy_static",
|
||||
"linkme",
|
||||
"menu",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"settings",
|
||||
"smallvec",
|
||||
@@ -13839,6 +13865,7 @@ dependencies = [
|
||||
name = "ui_macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"component_system",
|
||||
"convert_case 0.6.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -15622,6 +15649,7 @@ dependencies = [
|
||||
"client",
|
||||
"clock",
|
||||
"collections",
|
||||
"component_system",
|
||||
"db",
|
||||
"derive_more",
|
||||
"env_logger 0.11.5",
|
||||
@@ -16053,6 +16081,7 @@ dependencies = [
|
||||
"collections",
|
||||
"command_palette",
|
||||
"command_palette_hooks",
|
||||
"component_preview",
|
||||
"copilot",
|
||||
"db",
|
||||
"diagnostics",
|
||||
@@ -16085,7 +16114,6 @@ dependencies = [
|
||||
"languages",
|
||||
"libc",
|
||||
"log",
|
||||
"markdown",
|
||||
"markdown_preview",
|
||||
"menu",
|
||||
"mimalloc",
|
||||
|
||||
21
Cargo.toml
21
Cargo.toml
@@ -21,6 +21,8 @@ members = [
|
||||
"crates/collab",
|
||||
"crates/collab_ui",
|
||||
"crates/collections",
|
||||
"crates/component_preview",
|
||||
"crates/component_system",
|
||||
"crates/command_palette",
|
||||
"crates/command_palette_hooks",
|
||||
"crates/context_server",
|
||||
@@ -142,15 +144,6 @@ members = [
|
||||
"crates/zed",
|
||||
"crates/zed_actions",
|
||||
|
||||
"crates/livekit",
|
||||
"crates/livekit-api",
|
||||
"crates/livekit-protocol",
|
||||
"crates/livekit-ffi",
|
||||
"crates/libwebrtc",
|
||||
"crates/soxr-sys",
|
||||
"crates/webrtc-sys",
|
||||
"crates/webrtc-sys/build",
|
||||
|
||||
#
|
||||
# Extensions
|
||||
#
|
||||
@@ -216,6 +209,8 @@ clock = { path = "crates/clock" }
|
||||
collab = { path = "crates/collab" }
|
||||
collab_ui = { path = "crates/collab_ui" }
|
||||
collections = { path = "crates/collections" }
|
||||
component_system = { path = "crates/component_system" }
|
||||
component_preview = { path = "crates/component_preview" }
|
||||
command_palette = { path = "crates/command_palette" }
|
||||
command_palette_hooks = { path = "crates/command_palette_hooks" }
|
||||
context_server = { path = "crates/context_server" }
|
||||
@@ -335,8 +330,6 @@ worktree = { path = "crates/worktree" }
|
||||
zed = { path = "crates/zed" }
|
||||
zed_actions = { path = "crates/zed_actions" }
|
||||
|
||||
livekit = { path = "crates/livekit", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots" ], default-features = false }
|
||||
|
||||
#
|
||||
# External crates
|
||||
#
|
||||
@@ -404,10 +397,12 @@ itertools = "0.13.0"
|
||||
jsonwebtoken = "9.3"
|
||||
jupyter-protocol = { version = "0.5.0" }
|
||||
jupyter-websocket-client = { version = "0.8.0" }
|
||||
lazy_static = "1.5.0"
|
||||
libc = "0.2"
|
||||
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
|
||||
linkify = "0.10.0"
|
||||
# livekit = { git = "https://github.com/zed-industries/rust-sdks", rev="799f10133d93ba2a88642cd480d01ec4da53408c", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots"], default-features = false }
|
||||
linkme = "0.3"
|
||||
livekit = { git = "https://github.com/zed-industries/rust-sdks", rev="799f10133d93ba2a88642cd480d01ec4da53408c", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots"], default-features = false }
|
||||
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
|
||||
markup5ever_rcdom = "0.3.0"
|
||||
nanoid = "0.4"
|
||||
@@ -517,7 +512,7 @@ unindent = "0.1.7"
|
||||
unicode-segmentation = "1.10"
|
||||
unicode-script = "0.5.7"
|
||||
url = "2.2"
|
||||
uuid = { version = "1.1.2", features = ["v4", "v5", "v7", "serde"] }
|
||||
uuid = { version = "1.1.2", features = ["v4", "v5", "serde"] }
|
||||
wasmparser = "0.215"
|
||||
wasm-encoder = "0.215"
|
||||
wasmtime = { version = "24", default-features = false, features = [
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
[
|
||||
// Standard macOS bindings
|
||||
{
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "menu::SelectPrev",
|
||||
"shift-tab": "menu::SelectPrev",
|
||||
@@ -41,7 +40,6 @@
|
||||
},
|
||||
{
|
||||
"context": "Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"escape": "editor::Cancel",
|
||||
"backspace": "editor::Backspace",
|
||||
@@ -133,7 +131,6 @@
|
||||
},
|
||||
{
|
||||
"context": "Editor && mode == full",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "editor::Newline",
|
||||
"shift-enter": "editor::Newline",
|
||||
@@ -151,23 +148,20 @@
|
||||
},
|
||||
{
|
||||
"context": "Editor && mode == full && inline_completion",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"alt-tab": "editor::NextInlineCompletion",
|
||||
"alt-shift-tab": "editor::PreviousInlineCompletion",
|
||||
"alt-]": "editor::NextInlineCompletion",
|
||||
"alt-[": "editor::PreviousInlineCompletion",
|
||||
"ctrl-right": "editor::AcceptPartialInlineCompletion"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && !inline_completion",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"alt-tab": "editor::ShowInlineCompletion"
|
||||
"alt-\\": "editor::ShowInlineCompletion"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && mode == auto_height",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-enter": "editor::Newline",
|
||||
"shift-enter": "editor::Newline",
|
||||
@@ -176,14 +170,12 @@
|
||||
},
|
||||
{
|
||||
"context": "Markdown",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-c": "markdown::Copy"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && jupyter && !ContextEditor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-shift-enter": "repl::Run",
|
||||
"ctrl-alt-enter": "repl::RunInPlace"
|
||||
@@ -191,7 +183,6 @@
|
||||
},
|
||||
{
|
||||
"context": "AssistantPanel",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-k c": "assistant::CopyCode",
|
||||
"cmd-g": "search::SelectNextMatch",
|
||||
@@ -204,7 +195,6 @@
|
||||
},
|
||||
{
|
||||
"context": "ContextEditor > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-enter": "assistant::Assist",
|
||||
"cmd-shift-enter": "assistant::Edit",
|
||||
@@ -219,7 +209,6 @@
|
||||
},
|
||||
{
|
||||
"context": "AssistantPanel2",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-n": "assistant2::NewThread",
|
||||
"cmd-shift-h": "assistant2::OpenHistory"
|
||||
@@ -227,14 +216,12 @@
|
||||
},
|
||||
{
|
||||
"context": "MessageEditor > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-enter": "assistant2::Chat"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "PromptLibrary",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-n": "prompt_library::NewPrompt",
|
||||
"cmd-shift-s": "prompt_library::ToggleDefaultPrompt",
|
||||
@@ -243,7 +230,6 @@
|
||||
},
|
||||
{
|
||||
"context": "BufferSearchBar",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"escape": "buffer_search::Dismiss",
|
||||
"tab": "buffer_search::FocusEditor",
|
||||
@@ -257,7 +243,6 @@
|
||||
},
|
||||
{
|
||||
"context": "BufferSearchBar && in_replace > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "search::ReplaceNext",
|
||||
"cmd-enter": "search::ReplaceAll"
|
||||
@@ -265,7 +250,6 @@
|
||||
},
|
||||
{
|
||||
"context": "BufferSearchBar && !in_replace > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "search::PreviousHistoryQuery",
|
||||
"down": "search::NextHistoryQuery"
|
||||
@@ -273,7 +257,6 @@
|
||||
},
|
||||
{
|
||||
"context": "ProjectSearchBar",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"escape": "project_search::ToggleFocus",
|
||||
"cmd-shift-j": "project_search::ToggleFilters",
|
||||
@@ -285,7 +268,6 @@
|
||||
},
|
||||
{
|
||||
"context": "ProjectSearchBar > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "search::PreviousHistoryQuery",
|
||||
"down": "search::NextHistoryQuery"
|
||||
@@ -293,7 +275,6 @@
|
||||
},
|
||||
{
|
||||
"context": "ProjectSearchBar && in_replace > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "search::ReplaceNext",
|
||||
"cmd-enter": "search::ReplaceAll"
|
||||
@@ -301,7 +282,6 @@
|
||||
},
|
||||
{
|
||||
"context": "ProjectSearchView",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"escape": "project_search::ToggleFocus",
|
||||
"cmd-shift-j": "project_search::ToggleFilters",
|
||||
@@ -312,7 +292,6 @@
|
||||
},
|
||||
{
|
||||
"context": "Pane",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-{": "pane::ActivatePrevItem",
|
||||
"cmd-}": "pane::ActivateNextItem",
|
||||
@@ -341,7 +320,6 @@
|
||||
// Bindings from VS Code
|
||||
{
|
||||
"context": "Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-[": "editor::Outdent",
|
||||
"cmd-]": "editor::Indent",
|
||||
@@ -405,7 +383,6 @@
|
||||
},
|
||||
{
|
||||
"context": "Editor && mode == full",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-shift-o": "outline::Toggle",
|
||||
"ctrl-g": "go_to_line::Toggle"
|
||||
@@ -413,7 +390,6 @@
|
||||
},
|
||||
{
|
||||
"context": "Pane",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-1": ["pane::ActivateItem", 0],
|
||||
"ctrl-2": ["pane::ActivateItem", 1],
|
||||
@@ -433,7 +409,6 @@
|
||||
},
|
||||
{
|
||||
"context": "Workspace",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
// Change the default action on `menu::Confirm` by setting the parameter
|
||||
// "alt-cmd-o": ["projects::OpenRecent", {"create_new_window": true }],
|
||||
@@ -489,7 +464,6 @@
|
||||
},
|
||||
{
|
||||
"context": "Workspace && !Terminal",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-shift-r": "task::Spawn",
|
||||
"cmd-alt-r": "task::Rerun",
|
||||
@@ -500,7 +474,6 @@
|
||||
// Bindings from Sublime Text
|
||||
{
|
||||
"context": "Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-j": "editor::JoinLines",
|
||||
"ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",
|
||||
@@ -520,7 +493,6 @@
|
||||
// Bindings from Atom
|
||||
{
|
||||
"context": "Pane",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-k up": "pane::SplitUp",
|
||||
"cmd-k down": "pane::SplitDown",
|
||||
@@ -531,14 +503,12 @@
|
||||
// Bindings that should be unified with bindings for more general actions
|
||||
{
|
||||
"context": "Editor && renaming",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "editor::ConfirmRename"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && showing_completions",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "editor::ConfirmCompletion",
|
||||
"tab": "editor::ComposeCompletion"
|
||||
@@ -546,21 +516,18 @@
|
||||
},
|
||||
{
|
||||
"context": "Editor && inline_completion && !showing_completions",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"tab": "editor::AcceptInlineCompletion"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && showing_code_actions",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "editor::ConfirmCodeAction"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && (showing_code_actions || showing_completions)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "editor::ContextMenuPrev",
|
||||
"ctrl-p": "editor::ContextMenuPrev",
|
||||
@@ -572,7 +539,6 @@
|
||||
},
|
||||
// Custom bindings
|
||||
{
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-alt-cmd-f": "workspace::FollowNextCollaborator",
|
||||
// TODO: Move this to a dock open action
|
||||
@@ -583,7 +549,6 @@
|
||||
},
|
||||
{
|
||||
"context": "Editor && mode == full",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"alt-enter": "editor::OpenExcerpts",
|
||||
"shift-enter": "editor::ExpandExcerpts",
|
||||
@@ -595,7 +560,6 @@
|
||||
},
|
||||
{
|
||||
"context": "ProposedChangesEditor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-shift-y": "editor::ApplyDiffHunk",
|
||||
"cmd-shift-a": "editor::ApplyAllDiffHunks"
|
||||
@@ -603,7 +567,6 @@
|
||||
},
|
||||
{
|
||||
"context": "PromptEditor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-[": "assistant::CyclePreviousInlineAssist",
|
||||
"ctrl-]": "assistant::CycleNextInlineAssist"
|
||||
@@ -611,14 +574,12 @@
|
||||
},
|
||||
{
|
||||
"context": "ProjectSearchBar && !in_replace",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-enter": "project_search::SearchInNew"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "OutlinePanel && not_editing",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"escape": "menu::Cancel",
|
||||
"left": "outline_panel::CollapseSelectedEntry",
|
||||
@@ -635,7 +596,6 @@
|
||||
},
|
||||
{
|
||||
"context": "ProjectPanel",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"left": "project_panel::CollapseSelectedEntry",
|
||||
"right": "project_panel::ExpandSelectedEntry",
|
||||
@@ -665,14 +625,12 @@
|
||||
},
|
||||
{
|
||||
"context": "ProjectPanel && not_editing",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"space": "project_panel::Open"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "CollabPanel && not_editing",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-backspace": "collab_panel::Remove",
|
||||
"space": "menu::Confirm"
|
||||
@@ -680,21 +638,18 @@
|
||||
},
|
||||
{
|
||||
"context": "(CollabPanel && editing) > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"space": "collab_panel::InsertSpace"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "ChannelModal",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"tab": "channel_modal::ToggleMode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Picker > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"tab": "picker::ConfirmCompletion",
|
||||
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
|
||||
@@ -703,21 +658,18 @@
|
||||
},
|
||||
{
|
||||
"context": "ChannelModal > Picker > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"tab": "channel_modal::ToggleMode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "FileFinder",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd": "file_finder::ToggleMenu"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "FileFinder && !menu_open",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-shift-p": "file_finder::SelectPrev",
|
||||
"cmd-j": "pane::SplitDown",
|
||||
@@ -728,7 +680,6 @@
|
||||
},
|
||||
{
|
||||
"context": "FileFinder && menu_open",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"j": "pane::SplitDown",
|
||||
"k": "pane::SplitUp",
|
||||
@@ -738,7 +689,6 @@
|
||||
},
|
||||
{
|
||||
"context": "TabSwitcher",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-up": "menu::SelectPrev",
|
||||
"ctrl-down": "menu::SelectNext",
|
||||
@@ -748,7 +698,6 @@
|
||||
},
|
||||
{
|
||||
"context": "Terminal",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-cmd-space": "terminal::ShowCharacterPalette",
|
||||
"cmd-c": "terminal::Copy",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[
|
||||
{
|
||||
"context": "VimControl && !menu",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"i": ["vim::PushOperator", { "Object": { "around": false } }],
|
||||
"a": ["vim::PushOperator", { "Object": { "around": true } }],
|
||||
@@ -187,6 +188,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_mode == normal",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"escape": "editor::Cancel",
|
||||
"ctrl-[": "editor::Cancel",
|
||||
@@ -241,6 +243,7 @@
|
||||
},
|
||||
{
|
||||
"context": "VimControl && VimCount",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"0": ["vim::Number", 0],
|
||||
":": "vim::CountCommand"
|
||||
@@ -248,6 +251,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_mode == visual",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
":": "vim::VisualCommand",
|
||||
"u": "vim::ConvertToLowerCase",
|
||||
@@ -297,6 +301,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_mode == insert",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"escape": "vim::NormalBefore",
|
||||
"ctrl-c": "vim::NormalBefore",
|
||||
@@ -326,7 +331,6 @@
|
||||
"bindings": {
|
||||
"i": "vim::InsertBefore",
|
||||
"a": "vim::InsertAfter",
|
||||
"d": "vim::HelixDelete",
|
||||
"w": "vim::NextWordStart",
|
||||
"e": "vim::NextWordEnd",
|
||||
"b": "vim::PreviousWordStart",
|
||||
@@ -340,6 +344,7 @@
|
||||
|
||||
{
|
||||
"context": "vim_mode == insert && !(showing_code_actions || showing_completions)",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"ctrl-p": "editor::ShowCompletions",
|
||||
"ctrl-n": "editor::ShowCompletions"
|
||||
@@ -347,6 +352,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_mode == replace",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"escape": "vim::NormalBefore",
|
||||
"ctrl-c": "vim::NormalBefore",
|
||||
@@ -364,6 +370,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_mode == waiting",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"tab": "vim::Tab",
|
||||
"enter": "vim::Enter",
|
||||
@@ -377,6 +384,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_mode == operator",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"escape": "vim::ClearOperators",
|
||||
"ctrl-c": "vim::ClearOperators",
|
||||
@@ -386,6 +394,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == a || vim_operator == i || vim_operator == cs",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"w": "vim::Word",
|
||||
"shift-w": ["vim::Word", { "ignorePunctuation": true }],
|
||||
@@ -416,6 +425,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == c",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"c": "vim::CurrentLine",
|
||||
"d": "editor::Rename", // zed specific
|
||||
@@ -424,6 +434,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == d",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"d": "vim::CurrentLine",
|
||||
"s": ["vim::PushOperator", "DeleteSurrounds"],
|
||||
@@ -433,6 +444,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == gu",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"g u": "vim::CurrentLine",
|
||||
"u": "vim::CurrentLine"
|
||||
@@ -440,6 +452,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == gU",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"g shift-u": "vim::CurrentLine",
|
||||
"shift-u": "vim::CurrentLine"
|
||||
@@ -447,6 +460,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == g~",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"g ~": "vim::CurrentLine",
|
||||
"~": "vim::CurrentLine"
|
||||
@@ -454,6 +468,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == gq",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"g q": "vim::CurrentLine",
|
||||
"q": "vim::CurrentLine",
|
||||
@@ -463,6 +478,7 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == y",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"y": "vim::CurrentLine",
|
||||
"s": ["vim::PushOperator", { "AddSurrounds": {} }]
|
||||
@@ -470,36 +486,42 @@
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == ys",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"s": "vim::CurrentLine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == >",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
">": "vim::CurrentLine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == <",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"<": "vim::CurrentLine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == eq",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"=": "vim::CurrentLine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "vim_operator == gc",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"c": "vim::CurrentLine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "vim_mode == literal",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"ctrl-@": ["vim::Literal", ["ctrl-@", "\u0000"]],
|
||||
"ctrl-a": ["vim::Literal", ["ctrl-a", "\u0001"]],
|
||||
@@ -543,6 +565,7 @@
|
||||
},
|
||||
{
|
||||
"context": "BufferSearchBar && !in_replace",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"enter": "vim::SearchSubmit",
|
||||
"escape": "buffer_search::Dismiss"
|
||||
@@ -550,6 +573,7 @@
|
||||
},
|
||||
{
|
||||
"context": "ProjectPanel || CollabPanel || OutlinePanel || ChatPanel || VimControl || EmptyPane || SharedScreen || MarkdownPreview || KeyContextView",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
// window related commands (ctrl-w X)
|
||||
"ctrl-w": null,
|
||||
@@ -606,6 +630,7 @@
|
||||
},
|
||||
{
|
||||
"context": "EmptyPane || SharedScreen || MarkdownPreview || KeyContextView || Welcome",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
":": "command_palette::Toggle",
|
||||
"g /": "pane::DeploySearch"
|
||||
@@ -614,6 +639,7 @@
|
||||
{
|
||||
// netrw compatibility
|
||||
"context": "ProjectPanel && not_editing",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
":": "command_palette::Toggle",
|
||||
"%": "project_panel::NewFile",
|
||||
@@ -647,6 +673,7 @@
|
||||
},
|
||||
{
|
||||
"context": "OutlinePanel && not_editing",
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"j": "menu::SelectNext",
|
||||
"k": "menu::SelectPrev",
|
||||
|
||||
@@ -569,8 +569,7 @@
|
||||
// "Neighbour"
|
||||
"activate_on_close": "history",
|
||||
/// Which files containing diagnostic errors/warnings to mark in the tabs.
|
||||
/// Diagnostics are only shown when file icons are also active.
|
||||
/// This setting only works when can take the following three values:
|
||||
/// This setting can take the following three values:
|
||||
///
|
||||
/// 1. Do not mark any files:
|
||||
/// "off"
|
||||
@@ -578,7 +577,7 @@
|
||||
/// "errors"
|
||||
/// 3. Mark files with errors and warnings:
|
||||
/// "all"
|
||||
"show_diagnostics": "off"
|
||||
"show_diagnostics": "all"
|
||||
},
|
||||
// Settings related to preview tabs.
|
||||
"preview_tabs": {
|
||||
|
||||
@@ -21,6 +21,8 @@ test-support = [
|
||||
"project/test-support",
|
||||
"util/test-support"
|
||||
]
|
||||
livekit-macos = ["livekit_client_macos"]
|
||||
livekit-cross-platform = ["livekit_client"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
@@ -40,12 +42,8 @@ serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
settings.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
livekit_client_macos = { workspace = true }
|
||||
|
||||
[target.'cfg(not(target_os = "macos"))'.dependencies]
|
||||
livekit_client = { workspace = true }
|
||||
livekit_client_macos = { workspace = true, optional = true }
|
||||
livekit_client = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
client = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -1,13 +1,41 @@
|
||||
pub mod call_settings;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(any(
|
||||
all(target_os = "macos", feature = "livekit-macos"),
|
||||
all(
|
||||
not(target_os = "macos"),
|
||||
feature = "livekit-macos",
|
||||
not(feature = "livekit-cross-platform")
|
||||
)
|
||||
))]
|
||||
mod macos;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(any(
|
||||
all(target_os = "macos", feature = "livekit-macos"),
|
||||
all(
|
||||
not(target_os = "macos"),
|
||||
feature = "livekit-macos",
|
||||
not(feature = "livekit-cross-platform")
|
||||
)
|
||||
))]
|
||||
pub use macos::*;
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[cfg(any(
|
||||
all(
|
||||
target_os = "macos",
|
||||
feature = "livekit-cross-platform",
|
||||
not(feature = "livekit-macos"),
|
||||
),
|
||||
all(not(target_os = "macos"), feature = "livekit-cross-platform"),
|
||||
))]
|
||||
mod cross_platform;
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[cfg(any(
|
||||
all(
|
||||
target_os = "macos",
|
||||
feature = "livekit-cross-platform",
|
||||
not(feature = "livekit-macos"),
|
||||
),
|
||||
all(not(target_os = "macos"), feature = "livekit-cross-platform"),
|
||||
))]
|
||||
pub use cross_platform::*;
|
||||
|
||||
@@ -9,7 +9,6 @@ use collections::HashSet;
|
||||
use reqwest::StatusCode;
|
||||
use sea_orm::ActiveValue;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use std::{str::FromStr, sync::Arc, time::Duration};
|
||||
use stripe::{
|
||||
BillingPortalSession, CreateBillingPortalSession, CreateBillingPortalSessionFlowData,
|
||||
@@ -20,7 +19,6 @@ use stripe::{
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::api::events::SnowflakeRow;
|
||||
use crate::llm::{DEFAULT_MAX_MONTHLY_SPEND, FREE_TIER_MONTHLY_SPENDING_LIMIT};
|
||||
use crate::rpc::{ResultExt as _, Server};
|
||||
use crate::{
|
||||
@@ -126,20 +124,6 @@ async fn update_billing_preferences(
|
||||
.await?
|
||||
};
|
||||
|
||||
SnowflakeRow::new(
|
||||
"Spend Limit Updated",
|
||||
Some(user.metrics_id),
|
||||
user.admin,
|
||||
None,
|
||||
json!({
|
||||
"user_id": user.id,
|
||||
"max_monthly_llm_usage_spending_in_cents": billing_preferences.max_monthly_llm_usage_spending_in_cents,
|
||||
}),
|
||||
)
|
||||
.write(&app.kinesis_client, &app.config.kinesis_stream)
|
||||
.await
|
||||
.log_err();
|
||||
|
||||
rpc_server.refresh_llm_tokens_for_user(user.id).await;
|
||||
|
||||
Ok(Json(BillingPreferencesResponse {
|
||||
|
||||
22
crates/component_preview/Cargo.toml
Normal file
22
crates/component_preview/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "component_preview"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/component_preview.rs"
|
||||
|
||||
[dependencies]
|
||||
gpui.workspace = true
|
||||
ui.workspace = true
|
||||
component_system.workspace = true
|
||||
strum.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
110
crates/component_preview/src/component_preview.rs
Normal file
110
crates/component_preview/src/component_preview.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
//! # Component Preview
|
||||
//!
|
||||
//! A view for exploring Zed components.
|
||||
|
||||
use component_system::components;
|
||||
use gpui::{prelude::*, AppContext, EventEmitter, FocusHandle, FocusableView};
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
use ui::{prelude::*, TintColor};
|
||||
|
||||
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
cx.observe_new_views(|workspace: &mut Workspace, _cx| {
|
||||
workspace.register_action(|workspace, _: &workspace::ComponentPreview, cx| {
|
||||
let component_preview = cx.new_view(ComponentPreview::new);
|
||||
workspace.add_item_to_active_pane(Box::new(component_preview), None, true, cx)
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
struct ComponentPreview {
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
impl ComponentPreview {
|
||||
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render_sidebar(&self, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
h_flex().children(components().all().iter().map(|component| {
|
||||
Button::new(component.name().clone(), component.name()).on_click(cx.listener(
|
||||
move |_this, _, _cx| {
|
||||
// Handle button click
|
||||
},
|
||||
))
|
||||
}))
|
||||
}
|
||||
|
||||
fn render_preview(&self, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.size_full()
|
||||
.children(components().all_previews().iter().map(|component| {
|
||||
if let Some(preview) = component.preview() {
|
||||
preview(cx)
|
||||
} else {
|
||||
div().into_any_element()
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ComponentPreview {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
h_flex()
|
||||
.id("component-preview")
|
||||
.key_context("ComponentPreview")
|
||||
.items_start()
|
||||
.overflow_hidden()
|
||||
.size_full()
|
||||
.max_h_full()
|
||||
.track_focus(&self.focus_handle)
|
||||
.px_2()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(self.render_sidebar(cx))
|
||||
.child(self.render_preview(cx))
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<ItemEvent> for ComponentPreview {}
|
||||
|
||||
impl FocusableView for ComponentPreview {
|
||||
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for ComponentPreview {
|
||||
type Event = ItemEvent;
|
||||
|
||||
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
|
||||
Some("Component Preview".into())
|
||||
}
|
||||
|
||||
fn telemetry_event_text(&self) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
fn show_toolbar(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: Option<WorkspaceId>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<gpui::View<Self>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Some(cx.new_view(Self::new))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
|
||||
f(*event)
|
||||
}
|
||||
}
|
||||
23
crates/component_system/Cargo.toml
Normal file
23
crates/component_system/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "component_system"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/component_system.rs"
|
||||
|
||||
[dependencies]
|
||||
gpui.workspace = true
|
||||
lazy_static.workspace = true
|
||||
linkme.workspace = true
|
||||
once_cell.workspace = true
|
||||
collections.workspace = true
|
||||
parking_lot.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
142
crates/component_system/src/component_system.rs
Normal file
142
crates/component_system/src/component_system.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use collections::HashMap;
|
||||
use gpui::{AnyElement, SharedString, WindowContext};
|
||||
use linkme::distributed_slice;
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
pub trait Component {
|
||||
fn scope() -> Option<&'static str>;
|
||||
fn name() -> &'static str {
|
||||
std::any::type_name::<Self>()
|
||||
}
|
||||
fn description() -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ComponentPreview: Component {
|
||||
fn preview(_cx: &WindowContext) -> AnyElement;
|
||||
}
|
||||
|
||||
#[distributed_slice]
|
||||
pub static __ALL_COMPONENTS: [fn()] = [..];
|
||||
|
||||
#[distributed_slice]
|
||||
pub static __ALL_PREVIEWS: [fn()] = [..];
|
||||
|
||||
pub static COMPONENT_DATA: Lazy<RwLock<ComponentData>> =
|
||||
Lazy::new(|| RwLock::new(ComponentData::new()));
|
||||
|
||||
pub struct ComponentData {
|
||||
components: Vec<(Option<&'static str>, &'static str, Option<&'static str>)>,
|
||||
previews: HashMap<&'static str, fn(&WindowContext) -> AnyElement>,
|
||||
}
|
||||
|
||||
impl ComponentData {
|
||||
fn new() -> Self {
|
||||
ComponentData {
|
||||
components: Vec::new(),
|
||||
previews: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
let component_fns: Vec<_> = __ALL_COMPONENTS.iter().cloned().collect();
|
||||
let preview_fns: Vec<_> = __ALL_PREVIEWS.iter().cloned().collect();
|
||||
|
||||
for f in component_fns {
|
||||
f();
|
||||
}
|
||||
for f in preview_fns {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_component<T: Component>() {
|
||||
let component_data = (T::scope(), T::name(), T::description());
|
||||
COMPONENT_DATA.write().components.push(component_data);
|
||||
}
|
||||
|
||||
pub fn register_preview<T: ComponentPreview>() {
|
||||
let preview_data = (T::name(), T::preview as fn(&WindowContext) -> AnyElement);
|
||||
COMPONENT_DATA
|
||||
.write()
|
||||
.previews
|
||||
.insert(preview_data.0, preview_data.1);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ComponentId(pub &'static str);
|
||||
|
||||
pub struct ComponentMetadata {
|
||||
name: SharedString,
|
||||
scope: Option<SharedString>,
|
||||
description: Option<SharedString>,
|
||||
preview: Option<fn(&WindowContext) -> AnyElement>,
|
||||
}
|
||||
|
||||
impl ComponentMetadata {
|
||||
pub fn name(&self) -> SharedString {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn scope(&self) -> Option<SharedString> {
|
||||
self.scope.clone()
|
||||
}
|
||||
|
||||
pub fn description(&self) -> Option<SharedString> {
|
||||
self.description.clone()
|
||||
}
|
||||
|
||||
pub fn preview(&self) -> Option<fn(&WindowContext) -> AnyElement> {
|
||||
self.preview
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AllComponents(pub HashMap<ComponentId, ComponentMetadata>);
|
||||
|
||||
impl AllComponents {
|
||||
pub fn new() -> Self {
|
||||
AllComponents(HashMap::default())
|
||||
}
|
||||
|
||||
pub fn add(&mut self, id: ComponentId, metadata: ComponentMetadata) {
|
||||
self.0.insert(id, metadata);
|
||||
}
|
||||
|
||||
pub fn get(&self, id: &ComponentId) -> Option<&ComponentMetadata> {
|
||||
self.0.get(id)
|
||||
}
|
||||
|
||||
/// Returns all components with previews
|
||||
pub fn all_previews(&self) -> Vec<&ComponentMetadata> {
|
||||
self.0.values().filter(|c| c.preview.is_some()).collect()
|
||||
}
|
||||
|
||||
/// Returns all components
|
||||
pub fn all(&self) -> Vec<&ComponentMetadata> {
|
||||
self.0.values().collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn components() -> AllComponents {
|
||||
let data = COMPONENT_DATA.read();
|
||||
let mut all_components = AllComponents::new();
|
||||
|
||||
for &(scope, name, description) in &data.components {
|
||||
let scope = scope.map(Into::into);
|
||||
let preview = data.previews.get(name).cloned();
|
||||
all_components.add(
|
||||
ComponentId(name),
|
||||
ComponentMetadata {
|
||||
name: name.into(),
|
||||
scope,
|
||||
description: description.map(Into::into),
|
||||
preview,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
all_components
|
||||
}
|
||||
@@ -85,7 +85,6 @@ unindent = { workspace = true, optional = true }
|
||||
ui.workspace = true
|
||||
url.workspace = true
|
||||
util.workspace = true
|
||||
uuid.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -105,7 +105,6 @@ pub struct MoveDownByLines {
|
||||
#[serde(default)]
|
||||
pub(super) lines: u32,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct SelectUpByLines {
|
||||
#[serde(default)]
|
||||
@@ -167,13 +166,6 @@ pub struct SpawnNearestTask {
|
||||
pub reveal: task::RevealStrategy,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Default)]
|
||||
pub enum UuidVersion {
|
||||
#[default]
|
||||
V4,
|
||||
V7,
|
||||
}
|
||||
|
||||
impl_actions!(
|
||||
editor,
|
||||
[
|
||||
@@ -279,8 +271,6 @@ gpui::actions!(
|
||||
HalfPageUp,
|
||||
Hover,
|
||||
Indent,
|
||||
InsertUuidV4,
|
||||
InsertUuidV7,
|
||||
JoinLines,
|
||||
KillRingCut,
|
||||
KillRingYank,
|
||||
|
||||
@@ -12004,33 +12004,6 @@ impl Editor {
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn insert_uuid_v4(&mut self, _: &InsertUuidV4, cx: &mut ViewContext<Self>) {
|
||||
self.insert_uuid(UuidVersion::V4, cx);
|
||||
}
|
||||
|
||||
pub fn insert_uuid_v7(&mut self, _: &InsertUuidV7, cx: &mut ViewContext<Self>) {
|
||||
self.insert_uuid(UuidVersion::V7, cx);
|
||||
}
|
||||
|
||||
fn insert_uuid(&mut self, version: UuidVersion, cx: &mut ViewContext<Self>) {
|
||||
self.transact(cx, |this, cx| {
|
||||
let edits = this
|
||||
.selections
|
||||
.all::<Point>(cx)
|
||||
.into_iter()
|
||||
.map(|selection| {
|
||||
let uuid = match version {
|
||||
UuidVersion::V4 => uuid::Uuid::new_v4(),
|
||||
UuidVersion::V7 => uuid::Uuid::now_v7(),
|
||||
};
|
||||
|
||||
(selection.range(), uuid.to_string())
|
||||
});
|
||||
this.edit(edits, cx);
|
||||
this.refresh_inline_completion(true, false, cx);
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds a row highlight for the given range. If a row has multiple highlights, the
|
||||
/// last highlight added will be used.
|
||||
///
|
||||
|
||||
@@ -456,8 +456,6 @@ impl EditorElement {
|
||||
register_action(view, cx, Editor::open_active_item_in_terminal);
|
||||
register_action(view, cx, Editor::reload_file);
|
||||
register_action(view, cx, Editor::spawn_nearest_task);
|
||||
register_action(view, cx, Editor::insert_uuid_v4);
|
||||
register_action(view, cx, Editor::insert_uuid_v7);
|
||||
}
|
||||
|
||||
fn register_key_listeners(&self, cx: &mut WindowContext, layout: &EditorLayout) {
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context as _};
|
||||
use anyhow::Context as _;
|
||||
use collections::{BTreeMap, HashMap};
|
||||
use feature_flags::FeatureFlagAppExt;
|
||||
use git::{
|
||||
@@ -235,30 +235,22 @@ impl ProjectDiffEditor {
|
||||
>::default();
|
||||
let mut change_sets = Vec::new();
|
||||
for (status, entry_id, entry_path, open_task) in open_tasks {
|
||||
let Some(buffer) = open_task
|
||||
.await
|
||||
.and_then(|(_, opened_model)| {
|
||||
opened_model
|
||||
.downcast::<Buffer>()
|
||||
.map_err(|_| anyhow!("Unexpected non-buffer"))
|
||||
})
|
||||
.with_context(|| {
|
||||
format!("loading {} for git diff", entry_path.path.display())
|
||||
})
|
||||
.log_err()
|
||||
else {
|
||||
continue;
|
||||
let (_, opened_model) = open_task.await.with_context(|| {
|
||||
format!("loading buffer {:?} for git diff", entry_path.path)
|
||||
})?;
|
||||
let buffer = match opened_model.downcast::<Buffer>() {
|
||||
Ok(buffer) => buffer,
|
||||
Err(_model) => anyhow::bail!(
|
||||
"Could not load {:?} as a buffer for git diff",
|
||||
entry_path.path
|
||||
),
|
||||
};
|
||||
|
||||
let Some(change_set) = project
|
||||
let change_set = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.open_unstaged_changes(buffer.clone(), cx)
|
||||
})?
|
||||
.await
|
||||
.log_err()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
.await?;
|
||||
|
||||
cx.update(|cx| {
|
||||
buffers.insert(
|
||||
@@ -275,7 +267,7 @@ impl ProjectDiffEditor {
|
||||
new_entries.push((entry_path, entry_id));
|
||||
}
|
||||
|
||||
anyhow::Ok((buffers, new_entries, change_sets))
|
||||
Ok((buffers, new_entries, change_sets))
|
||||
})
|
||||
.await
|
||||
.log_err()
|
||||
|
||||
@@ -80,7 +80,7 @@ gpui_macros.workspace = true
|
||||
http_client = { optional = true, workspace = true }
|
||||
image = "0.25.1"
|
||||
itertools.workspace = true
|
||||
linkme = "0.3"
|
||||
linkme.workspace = true
|
||||
log.workspace = true
|
||||
num_cpus = "1.13"
|
||||
parking = "2.0.0"
|
||||
|
||||
@@ -8,8 +8,7 @@ use anyhow::{anyhow, Result};
|
||||
|
||||
use futures::{AsyncReadExt, Future};
|
||||
use image::{
|
||||
codecs::{gif::GifDecoder, webp::WebPDecoder},
|
||||
AnimationDecoder, DynamicImage, Frame, ImageBuffer, ImageError, ImageFormat, Rgba,
|
||||
codecs::gif::GifDecoder, AnimationDecoder, Frame, ImageBuffer, ImageError, ImageFormat,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
@@ -543,34 +542,6 @@ impl Asset for ImageAssetLoader {
|
||||
|
||||
frames
|
||||
}
|
||||
ImageFormat::WebP => {
|
||||
let mut decoder = WebPDecoder::new(Cursor::new(&bytes))?;
|
||||
|
||||
if decoder.has_animation() {
|
||||
let _ = decoder.set_background_color(Rgba([0, 0, 0, 0]));
|
||||
let mut frames = SmallVec::new();
|
||||
|
||||
for frame in decoder.into_frames() {
|
||||
let mut frame = frame?;
|
||||
// Convert from RGBA to BGRA.
|
||||
for pixel in frame.buffer_mut().chunks_exact_mut(4) {
|
||||
pixel.swap(0, 2);
|
||||
}
|
||||
frames.push(frame);
|
||||
}
|
||||
|
||||
frames
|
||||
} else {
|
||||
let mut data = DynamicImage::from_decoder(decoder)?.into_rgba8();
|
||||
|
||||
// Convert from RGBA to BGRA.
|
||||
for pixel in data.chunks_exact_mut(4) {
|
||||
pixel.swap(0, 2);
|
||||
}
|
||||
|
||||
SmallVec::from_elem(Frame::new(data), 1)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let mut data =
|
||||
image::load_from_memory_with_format(&bytes, format)?.into_rgba8();
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::fd::{AsFd, AsRawFd, FromRawFd};
|
||||
use std::panic::{AssertUnwindSafe, Location};
|
||||
use std::panic::Location;
|
||||
use std::rc::Weak;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
@@ -18,12 +18,12 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context as _};
|
||||
use anyhow::anyhow;
|
||||
use async_task::Runnable;
|
||||
use calloop::channel::Channel;
|
||||
use calloop::{EventLoop, LoopHandle, LoopSignal};
|
||||
use flume::{Receiver, Sender};
|
||||
use futures::{channel::oneshot, future::FutureExt};
|
||||
use futures::channel::oneshot;
|
||||
use parking_lot::Mutex;
|
||||
use util::ResultExt;
|
||||
|
||||
@@ -382,14 +382,14 @@ impl<P: LinuxClient + 'static> Platform for P {
|
||||
}
|
||||
|
||||
fn open_with_system(&self, path: &Path) {
|
||||
let executor = self.background_executor().clone();
|
||||
let path = path.to_owned();
|
||||
self.background_executor()
|
||||
executor
|
||||
.spawn(async move {
|
||||
let _ = std::process::Command::new("xdg-open")
|
||||
.arg(path)
|
||||
.spawn()
|
||||
.context("invoking xdg-open")
|
||||
.log_err();
|
||||
.expect("Failed to open file with xdg-open");
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
@@ -489,12 +489,7 @@ impl<P: LinuxClient + 'static> Platform for P {
|
||||
let username = attributes
|
||||
.get("username")
|
||||
.ok_or_else(|| anyhow!("Cannot find username in stored credentials"))?;
|
||||
// oo7 panics if the retrieved secret can't be decrypted due to
|
||||
// unexpected padding.
|
||||
let secret = AssertUnwindSafe(item.secret())
|
||||
.catch_unwind()
|
||||
.await
|
||||
.map_err(|_| anyhow!("oo7 panicked while trying to read credentials"))??;
|
||||
let secret = item.secret().await?;
|
||||
|
||||
// we lose the zeroizing capabilities at this boundary,
|
||||
// a current limitation GPUI's credentials api
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
PlatformTextSystem, PlatformWindow, Result, ScreenCaptureSource, SemanticVersion, Task,
|
||||
WindowAppearance, WindowParams,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _};
|
||||
use anyhow::anyhow;
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
appkit::{
|
||||
@@ -57,7 +57,6 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
use strum::IntoEnumIterator;
|
||||
use util::ResultExt;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const NSUTF8StringEncoding: NSUInteger = 4;
|
||||
@@ -780,16 +779,15 @@ impl Platform for MacPlatform {
|
||||
}
|
||||
|
||||
fn open_with_system(&self, path: &Path) {
|
||||
let path = path.to_owned();
|
||||
let path = path.to_path_buf();
|
||||
self.0
|
||||
.lock()
|
||||
.background_executor
|
||||
.spawn(async move {
|
||||
let _ = std::process::Command::new("open")
|
||||
std::process::Command::new("open")
|
||||
.arg(path)
|
||||
.spawn()
|
||||
.context("invoking open command")
|
||||
.log_err();
|
||||
.expect("Failed to open file");
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
@@ -152,6 +152,10 @@ unsafe fn build_classes() {
|
||||
sel!(flagsChanged:),
|
||||
handle_view_event as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(cancelOperation:),
|
||||
cancel_operation as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(makeBackingLayer),
|
||||
@@ -1451,6 +1455,29 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
||||
}
|
||||
}
|
||||
|
||||
// Allows us to receive `cmd-.` (the shortcut for closing a dialog)
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
|
||||
extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
|
||||
let window_state = unsafe { get_window_state(this) };
|
||||
let mut lock = window_state.as_ref().lock();
|
||||
|
||||
let keystroke = Keystroke {
|
||||
modifiers: Default::default(),
|
||||
key: ".".into(),
|
||||
key_char: None,
|
||||
};
|
||||
let event = PlatformInput::KeyDown(KeyDownEvent {
|
||||
keystroke: keystroke.clone(),
|
||||
is_held: false,
|
||||
});
|
||||
|
||||
if let Some(mut callback) = lock.event_callback.take() {
|
||||
drop(lock);
|
||||
callback(event);
|
||||
window_state.lock().event_callback = Some(callback);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn window_did_change_occlusion_state(this: &Object, _: Sel, _: id) {
|
||||
let window_state = unsafe { get_window_state(this) };
|
||||
let lock = &mut *window_state.lock();
|
||||
|
||||
@@ -33,7 +33,7 @@ pub(crate) fn handle_msg(
|
||||
WM_ACTIVATE => handle_activate_msg(handle, wparam, state_ptr),
|
||||
WM_CREATE => handle_create_msg(handle, state_ptr),
|
||||
WM_MOVE => handle_move_msg(handle, lparam, state_ptr),
|
||||
WM_SIZE => handle_size_msg(wparam, lparam, state_ptr),
|
||||
WM_SIZE => handle_size_msg(lparam, state_ptr),
|
||||
WM_ENTERSIZEMOVE | WM_ENTERMENULOOP => handle_size_move_loop(handle),
|
||||
WM_EXITSIZEMOVE | WM_EXITMENULOOP => handle_size_move_loop_exit(handle),
|
||||
WM_TIMER => handle_timer_msg(handle, wparam, state_ptr),
|
||||
@@ -136,15 +136,7 @@ fn handle_move_msg(
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn handle_size_msg(
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
state_ptr: Rc<WindowsWindowStatePtr>,
|
||||
) -> Option<isize> {
|
||||
if wparam.0 == SIZE_MINIMIZED as usize {
|
||||
return Some(0);
|
||||
}
|
||||
|
||||
fn handle_size_msg(lparam: LPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
|
||||
let width = lparam.loword().max(1) as i32;
|
||||
let height = lparam.hiword().max(1) as i32;
|
||||
let mut lock = state_ptr.state.borrow_mut();
|
||||
|
||||
@@ -14,7 +14,7 @@ use settings::WorktreeId;
|
||||
use crate::LanguageName;
|
||||
|
||||
/// Represents a single toolchain.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Toolchain {
|
||||
/// User-facing label
|
||||
pub name: SharedString,
|
||||
@@ -24,18 +24,6 @@ pub struct Toolchain {
|
||||
pub as_json: serde_json::Value,
|
||||
}
|
||||
|
||||
impl PartialEq for Toolchain {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// Do not use as_json for comparisons; it shouldn't impact equality, as it's not user-surfaced.
|
||||
// Thus, there could be multiple entries that look the same in the UI.
|
||||
(&self.name, &self.path, &self.language_name).eq(&(
|
||||
&other.name,
|
||||
&other.path,
|
||||
&other.language_name,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ToolchainLister: Send + Sync {
|
||||
async fn list(
|
||||
|
||||
@@ -104,15 +104,14 @@ impl LanguageSelectorDelegate {
|
||||
let candidates = language_registry
|
||||
.language_names()
|
||||
.into_iter()
|
||||
.filter_map(|name| {
|
||||
.enumerate()
|
||||
.filter_map(|(candidate_id, name)| {
|
||||
language_registry
|
||||
.available_language_for_name(&name)?
|
||||
.hidden()
|
||||
.not()
|
||||
.then_some(name)
|
||||
.then(|| StringMatchCandidate::new(candidate_id, name))
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(candidate_id, name)| StringMatchCandidate::new(candidate_id, name))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Self {
|
||||
|
||||
@@ -8,7 +8,3 @@
|
||||
|
||||
((html_block) @content
|
||||
(#set! "language" "html"))
|
||||
|
||||
((minus_metadata) @content (#set! "language" "yaml"))
|
||||
|
||||
((plus_metadata) @content (#set! "language" "toml"))
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../livekit-rust-sdks/libwebrtc
|
||||
@@ -1 +0,0 @@
|
||||
../../livekit-rust-sdks/livekit/
|
||||
@@ -1 +0,0 @@
|
||||
../../livekit-rust-sdks/livekit-api/
|
||||
@@ -1 +0,0 @@
|
||||
../../livekit-rust-sdks/livekit-ffi
|
||||
@@ -1 +0,0 @@
|
||||
../../livekit-rust-sdks/livekit-protocol
|
||||
@@ -1 +0,0 @@
|
||||
../../livekit-rust-sdks/livekit-runtime
|
||||
@@ -6,8 +6,8 @@
|
||||
"repositoryURL": "https://github.com/livekit/client-sdk-swift.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "8cde9e66ce9b470c3a743f5c72784f57c5a6d0c3",
|
||||
"version": "1.1.6"
|
||||
"revision": "7331b813a5ab8a95cfb81fb2b4ed10519428b9ff",
|
||||
"version": "1.0.12"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -24,8 +24,8 @@
|
||||
"repositoryURL": "https://github.com/webrtc-sdk/Specs.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "4fa8d6d647fc759cdd0265fd413d2f28ea2e0e08",
|
||||
"version": "114.5735.8"
|
||||
"revision": "2f6bab30c8df0fe59ab3e58bc99097f757f85f65",
|
||||
"version": "104.5112.17"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -12,16 +12,16 @@ let package = Package(
|
||||
.library(
|
||||
name: "LiveKitBridge",
|
||||
type: .static,
|
||||
targets: ["LiveKitBridge"])
|
||||
targets: ["LiveKitBridge"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/livekit/client-sdk-swift.git", .exact("1.1.6"))
|
||||
.package(url: "https://github.com/livekit/client-sdk-swift.git", .exact("1.0.12")),
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||
.target(
|
||||
name: "LiveKitBridge",
|
||||
dependencies: [.product(name: "LiveKit", package: "client-sdk-swift")])
|
||||
dependencies: [.product(name: "LiveKit", package: "client-sdk-swift")]),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -638,12 +638,6 @@ impl LanguageServer {
|
||||
snippet_edit_support: Some(true),
|
||||
..WorkspaceEditClientCapabilities::default()
|
||||
}),
|
||||
file_operations: Some(WorkspaceFileOperationsClientCapabilities {
|
||||
dynamic_registration: Some(false),
|
||||
did_rename: Some(true),
|
||||
will_rename: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
text_document: Some(TextDocumentClientCapabilities {
|
||||
|
||||
@@ -28,7 +28,6 @@ pulldown-cmark.workspace = true
|
||||
settings.workspace = true
|
||||
theme.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -7,8 +7,8 @@ use crate::markdown_elements::{
|
||||
use gpui::{
|
||||
div, img, px, rems, AbsoluteLength, AnyElement, ClipboardItem, DefiniteLength, Div, Element,
|
||||
ElementId, HighlightStyle, Hsla, ImageSource, InteractiveText, IntoElement, Keystroke, Length,
|
||||
Modifiers, ParentElement, Render, Resource, SharedString, Styled, StyledText, TextStyle, View,
|
||||
WeakView, WindowContext,
|
||||
Modifiers, ParentElement, Resource, SharedString, Styled, StyledText, TextStyle, WeakView,
|
||||
WindowContext,
|
||||
};
|
||||
use settings::Settings;
|
||||
use std::{
|
||||
@@ -18,10 +18,9 @@ use std::{
|
||||
};
|
||||
use theme::{ActiveTheme, SyntaxTheme, ThemeSettings};
|
||||
use ui::{
|
||||
h_flex, relative, tooltip_container, v_flex, Checkbox, Clickable, Color, FluentBuilder,
|
||||
IconButton, IconName, IconSize, InteractiveElement, Label, LabelCommon, LabelSize, LinkPreview,
|
||||
Selection, StatefulInteractiveElement, StyledExt, StyledImage, ViewContext, VisibleOnHover,
|
||||
VisualContext as _,
|
||||
h_flex, relative, v_flex, Checkbox, Clickable, FluentBuilder, IconButton, IconName, IconSize,
|
||||
InteractiveElement, LinkPreview, Selection, StatefulInteractiveElement, StyledExt, StyledImage,
|
||||
Tooltip, VisibleOnHover,
|
||||
};
|
||||
use workspace::Workspace;
|
||||
|
||||
@@ -207,7 +206,15 @@ fn render_markdown_list_item(
|
||||
)
|
||||
.hover(|s| s.cursor_pointer())
|
||||
.tooltip(|cx| {
|
||||
InteractiveMarkdownElementTooltip::new(None, "toggle checkbox", cx).into()
|
||||
let secondary_modifier = Keystroke {
|
||||
key: "".to_string(),
|
||||
modifiers: Modifiers::secondary_key(),
|
||||
key_char: None,
|
||||
};
|
||||
Tooltip::text(
|
||||
format!("{}-click to toggle the checkbox", secondary_modifier),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.into_any_element(),
|
||||
};
|
||||
@@ -506,7 +513,6 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
|
||||
|
||||
let image_element = div()
|
||||
.id(element_id)
|
||||
.cursor_pointer()
|
||||
.child(img(ImageSource::Resource(image_resource)).with_fallback({
|
||||
let alt_text = image.alt_text.clone();
|
||||
{
|
||||
@@ -515,31 +521,18 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
|
||||
}))
|
||||
.tooltip({
|
||||
let link = image.link.clone();
|
||||
move |cx| {
|
||||
InteractiveMarkdownElementTooltip::new(
|
||||
Some(link.to_string()),
|
||||
"open image",
|
||||
cx,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
move |cx| LinkPreview::new(&link.to_string(), cx)
|
||||
})
|
||||
.on_click({
|
||||
let workspace = workspace_clone.clone();
|
||||
let link = image.link.clone();
|
||||
move |_, cx| {
|
||||
if cx.modifiers().secondary() {
|
||||
match &link {
|
||||
Link::Web { url } => cx.open_url(url),
|
||||
Link::Path { path, .. } => {
|
||||
if let Some(workspace) = &workspace {
|
||||
_ = workspace.update(cx, |workspace, cx| {
|
||||
workspace
|
||||
.open_abs_path(path.clone(), false, cx)
|
||||
.detach();
|
||||
});
|
||||
}
|
||||
}
|
||||
move |_event, window_cx| match &link {
|
||||
Link::Web { url } => window_cx.open_url(url),
|
||||
Link::Path { path, .. } => {
|
||||
if let Some(workspace) = &workspace {
|
||||
_ = workspace.update(window_cx, |workspace, cx| {
|
||||
workspace.open_abs_path(path.clone(), false, cx).detach();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -557,50 +550,3 @@ fn render_markdown_rule(cx: &mut RenderContext) -> AnyElement {
|
||||
let rule = div().w_full().h(px(2.)).bg(cx.border_color);
|
||||
div().pt_3().pb_3().child(rule).into_any()
|
||||
}
|
||||
|
||||
struct InteractiveMarkdownElementTooltip {
|
||||
tooltip_text: Option<SharedString>,
|
||||
action_text: String,
|
||||
}
|
||||
|
||||
impl InteractiveMarkdownElementTooltip {
|
||||
pub fn new(
|
||||
tooltip_text: Option<String>,
|
||||
action_text: &str,
|
||||
cx: &mut WindowContext,
|
||||
) -> View<Self> {
|
||||
let tooltip_text = tooltip_text.map(|t| util::truncate_and_trailoff(&t, 50).into());
|
||||
|
||||
cx.new_view(|_| Self {
|
||||
tooltip_text,
|
||||
action_text: action_text.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for InteractiveMarkdownElementTooltip {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
tooltip_container(cx, |el, _| {
|
||||
let secondary_modifier = Keystroke {
|
||||
modifiers: Modifiers::secondary_key(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
el.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.when_some(self.tooltip_text.clone(), |this, text| {
|
||||
this.child(Label::new(text).size(LabelSize::Small))
|
||||
})
|
||||
.child(
|
||||
Label::new(format!(
|
||||
"{}-click to {}",
|
||||
secondary_modifier, self.action_text
|
||||
))
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ use futures::{
|
||||
stream::FuturesUnordered,
|
||||
AsyncWriteExt, Future, FutureExt, StreamExt,
|
||||
};
|
||||
use globset::{Glob, GlobBuilder, GlobMatcher, GlobSet, GlobSetBuilder};
|
||||
use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||
use gpui::{
|
||||
AppContext, AsyncAppContext, Entity, EventEmitter, Model, ModelContext, PromptLevel, Task,
|
||||
WeakModel,
|
||||
AppContext, AsyncAppContext, Context, Entity, EventEmitter, Model, ModelContext, PromptLevel,
|
||||
Task, WeakModel,
|
||||
};
|
||||
use http_client::HttpClient;
|
||||
use itertools::Itertools as _;
|
||||
@@ -43,13 +43,12 @@ use language::{
|
||||
Unclipped,
|
||||
};
|
||||
use lsp::{
|
||||
notification::DidRenameFiles, CodeActionKind, CompletionContext, DiagnosticSeverity,
|
||||
DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, Edit, FileOperationFilter,
|
||||
FileOperationPatternKind, FileOperationRegistrationOptions, FileRename, FileSystemWatcher,
|
||||
InsertTextFormat, LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions,
|
||||
LanguageServerId, LanguageServerName, LspRequestFuture, MessageActionItem, MessageType, OneOf,
|
||||
RenameFilesParams, ServerHealthStatus, ServerStatus, SymbolKind, TextEdit, Url,
|
||||
WillRenameFiles, WorkDoneProgressCancelParams, WorkspaceFolder,
|
||||
CodeActionKind, CompletionContext, DiagnosticSeverity, DiagnosticTag,
|
||||
DidChangeWatchedFilesRegistrationOptions, Edit, FileSystemWatcher, InsertTextFormat,
|
||||
LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerId,
|
||||
LanguageServerName, LspRequestFuture, MessageActionItem, MessageType, OneOf,
|
||||
ServerHealthStatus, ServerStatus, SymbolKind, TextEdit, Url, WorkDoneProgressCancelParams,
|
||||
WorkspaceFolder,
|
||||
};
|
||||
use node_runtime::read_package_installed_version;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
@@ -140,9 +139,7 @@ pub struct LocalLspStore {
|
||||
pub language_servers: HashMap<LanguageServerId, LanguageServerState>,
|
||||
buffers_being_formatted: HashSet<BufferId>,
|
||||
last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>,
|
||||
language_server_watched_paths: HashMap<LanguageServerId, LanguageServerWatchedPaths>,
|
||||
language_server_paths_watched_for_rename:
|
||||
HashMap<LanguageServerId, RenamePathsWatchedForServer>,
|
||||
language_server_watched_paths: HashMap<LanguageServerId, Model<LanguageServerWatchedPaths>>,
|
||||
language_server_watcher_registrations:
|
||||
HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>,
|
||||
supplementary_language_servers:
|
||||
@@ -902,7 +899,6 @@ impl LspStore {
|
||||
language_servers: Default::default(),
|
||||
last_workspace_edits_by_language_server: Default::default(),
|
||||
language_server_watched_paths: Default::default(),
|
||||
language_server_paths_watched_for_rename: Default::default(),
|
||||
language_server_watcher_registrations: Default::default(),
|
||||
current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(),
|
||||
buffers_being_formatted: Default::default(),
|
||||
@@ -4336,112 +4332,6 @@ impl LspStore {
|
||||
.map(|(key, value)| (*key, value))
|
||||
}
|
||||
|
||||
pub(super) fn did_rename_entry(
|
||||
&self,
|
||||
worktree_id: WorktreeId,
|
||||
old_path: &Path,
|
||||
new_path: &Path,
|
||||
is_dir: bool,
|
||||
) {
|
||||
maybe!({
|
||||
let local_store = self.as_local()?;
|
||||
|
||||
let old_uri = lsp::Url::from_file_path(old_path).ok().map(String::from)?;
|
||||
let new_uri = lsp::Url::from_file_path(new_path).ok().map(String::from)?;
|
||||
|
||||
for language_server in self.language_servers_for_worktree(worktree_id) {
|
||||
let Some(filter) = local_store
|
||||
.language_server_paths_watched_for_rename
|
||||
.get(&language_server.server_id())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if filter.should_send_did_rename(&old_uri, is_dir) {
|
||||
language_server
|
||||
.notify::<DidRenameFiles>(RenameFilesParams {
|
||||
files: vec![FileRename {
|
||||
old_uri: old_uri.clone(),
|
||||
new_uri: new_uri.clone(),
|
||||
}],
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn will_rename_entry(
|
||||
this: WeakModel<Self>,
|
||||
worktree_id: WorktreeId,
|
||||
old_path: &Path,
|
||||
new_path: &Path,
|
||||
is_dir: bool,
|
||||
cx: AsyncAppContext,
|
||||
) -> Task<()> {
|
||||
let old_uri = lsp::Url::from_file_path(old_path).ok().map(String::from);
|
||||
let new_uri = lsp::Url::from_file_path(new_path).ok().map(String::from);
|
||||
cx.spawn(move |mut cx| async move {
|
||||
let mut tasks = vec![];
|
||||
this.update(&mut cx, |this, cx| {
|
||||
let local_store = this.as_local()?;
|
||||
let old_uri = old_uri?;
|
||||
let new_uri = new_uri?;
|
||||
for language_server in this.language_servers_for_worktree(worktree_id) {
|
||||
let Some(filter) = local_store
|
||||
.language_server_paths_watched_for_rename
|
||||
.get(&language_server.server_id())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let Some(adapter) =
|
||||
this.language_server_adapter_for_id(language_server.server_id())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if filter.should_send_will_rename(&old_uri, is_dir) {
|
||||
let apply_edit = cx.spawn({
|
||||
let old_uri = old_uri.clone();
|
||||
let new_uri = new_uri.clone();
|
||||
let language_server = language_server.clone();
|
||||
|this, mut cx| async move {
|
||||
let edit = language_server
|
||||
.request::<WillRenameFiles>(RenameFilesParams {
|
||||
files: vec![FileRename { old_uri, new_uri }],
|
||||
})
|
||||
.log_err()
|
||||
.await
|
||||
.flatten()?;
|
||||
|
||||
Self::deserialize_workspace_edit(
|
||||
this.upgrade()?,
|
||||
edit,
|
||||
false,
|
||||
adapter.clone(),
|
||||
language_server.clone(),
|
||||
&mut cx,
|
||||
)
|
||||
.await
|
||||
.ok();
|
||||
Some(())
|
||||
}
|
||||
});
|
||||
tasks.push(apply_edit);
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
})
|
||||
.ok()
|
||||
.flatten();
|
||||
for task in tasks {
|
||||
// Await on tasks sequentially so that the order of application of edits is deterministic
|
||||
// (at least with regards to the order of registration of language servers)
|
||||
task.await;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn lsp_notify_abs_paths_changed(
|
||||
&mut self,
|
||||
server_id: LanguageServerId,
|
||||
@@ -4479,32 +4369,6 @@ impl LspStore {
|
||||
language_server_id: LanguageServerId,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let Some(watchers) = self.as_local().and_then(|local| {
|
||||
local
|
||||
.language_server_watcher_registrations
|
||||
.get(&language_server_id)
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let watch_builder =
|
||||
self.rebuild_watched_paths_inner(language_server_id, watchers.values().flatten(), cx);
|
||||
let Some(local_lsp_store) = self.as_local_mut() else {
|
||||
return;
|
||||
};
|
||||
let watcher = watch_builder.build(local_lsp_store.fs.clone(), language_server_id, cx);
|
||||
local_lsp_store
|
||||
.language_server_watched_paths
|
||||
.insert(language_server_id, watcher);
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
fn rebuild_watched_paths_inner<'a>(
|
||||
&'a self,
|
||||
language_server_id: LanguageServerId,
|
||||
watchers: impl Iterator<Item = &'a FileSystemWatcher>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> LanguageServerWatchedPathsBuilder {
|
||||
let worktrees = self
|
||||
.worktree_store
|
||||
.read(cx)
|
||||
@@ -4516,6 +4380,15 @@ impl LspStore {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let local_lsp_store = self.as_local_mut().unwrap();
|
||||
|
||||
let Some(watchers) = local_lsp_store
|
||||
.language_server_watcher_registrations
|
||||
.get(&language_server_id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut worktree_globs = HashMap::default();
|
||||
let mut abs_globs = HashMap::default();
|
||||
log::trace!(
|
||||
@@ -4533,7 +4406,7 @@ impl LspStore {
|
||||
pattern: String,
|
||||
},
|
||||
}
|
||||
for watcher in watchers {
|
||||
for watcher in watchers.values().flatten() {
|
||||
let mut found_host = false;
|
||||
for worktree in &worktrees {
|
||||
let glob_is_inside_worktree = worktree.update(cx, |tree, _| {
|
||||
@@ -4672,7 +4545,12 @@ impl LspStore {
|
||||
watch_builder.watch_abs_path(abs_path, globset);
|
||||
}
|
||||
}
|
||||
watch_builder
|
||||
let watcher = watch_builder.build(local_lsp_store.fs.clone(), language_server_id, cx);
|
||||
local_lsp_store
|
||||
.language_server_watched_paths
|
||||
.insert(language_server_id, watcher);
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn language_server_for_id(&self, id: LanguageServerId) -> Option<Arc<LanguageServer>> {
|
||||
@@ -6772,23 +6650,6 @@ impl LspStore {
|
||||
simulate_disk_based_diagnostics_completion: None,
|
||||
},
|
||||
);
|
||||
if let Some(file_ops_caps) = language_server
|
||||
.capabilities()
|
||||
.workspace
|
||||
.as_ref()
|
||||
.and_then(|ws| ws.file_operations.as_ref())
|
||||
{
|
||||
let did_rename_caps = file_ops_caps.did_rename.as_ref();
|
||||
let will_rename_caps = file_ops_caps.will_rename.as_ref();
|
||||
if did_rename_caps.or(will_rename_caps).is_some() {
|
||||
let watcher = RenamePathsWatchedForServer::default()
|
||||
.with_did_rename_patterns(did_rename_caps)
|
||||
.with_will_rename_patterns(will_rename_caps);
|
||||
local
|
||||
.language_server_paths_watched_for_rename
|
||||
.insert(server_id, watcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.language_server_statuses.insert(
|
||||
@@ -7149,7 +7010,7 @@ impl LspStore {
|
||||
if let Some(watched_paths) = local
|
||||
.language_server_watched_paths
|
||||
.get(server_id)
|
||||
.and_then(|paths| paths.worktree_paths.get(&worktree_id))
|
||||
.and_then(|paths| paths.read(cx).worktree_paths.get(&worktree_id))
|
||||
{
|
||||
let params = lsp::DidChangeWatchedFilesParams {
|
||||
changes: changes
|
||||
@@ -7254,7 +7115,7 @@ impl LspStore {
|
||||
Ok(transaction)
|
||||
}
|
||||
|
||||
pub(crate) async fn deserialize_workspace_edit(
|
||||
pub async fn deserialize_workspace_edit(
|
||||
this: Model<Self>,
|
||||
edit: lsp::WorkspaceEdit,
|
||||
push_to_history: bool,
|
||||
@@ -7654,84 +7515,6 @@ pub enum LanguageServerToQuery {
|
||||
Other(LanguageServerId),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RenamePathsWatchedForServer {
|
||||
did_rename: Vec<RenameActionPredicate>,
|
||||
will_rename: Vec<RenameActionPredicate>,
|
||||
}
|
||||
|
||||
impl RenamePathsWatchedForServer {
|
||||
fn with_did_rename_patterns(
|
||||
mut self,
|
||||
did_rename: Option<&FileOperationRegistrationOptions>,
|
||||
) -> Self {
|
||||
if let Some(did_rename) = did_rename {
|
||||
self.did_rename = did_rename
|
||||
.filters
|
||||
.iter()
|
||||
.filter_map(|filter| filter.try_into().log_err())
|
||||
.collect();
|
||||
}
|
||||
self
|
||||
}
|
||||
fn with_will_rename_patterns(
|
||||
mut self,
|
||||
will_rename: Option<&FileOperationRegistrationOptions>,
|
||||
) -> Self {
|
||||
if let Some(will_rename) = will_rename {
|
||||
self.will_rename = will_rename
|
||||
.filters
|
||||
.iter()
|
||||
.filter_map(|filter| filter.try_into().log_err())
|
||||
.collect();
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
fn should_send_did_rename(&self, path: &str, is_dir: bool) -> bool {
|
||||
self.did_rename.iter().any(|pred| pred.eval(path, is_dir))
|
||||
}
|
||||
fn should_send_will_rename(&self, path: &str, is_dir: bool) -> bool {
|
||||
self.will_rename.iter().any(|pred| pred.eval(path, is_dir))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&FileOperationFilter> for RenameActionPredicate {
|
||||
type Error = globset::Error;
|
||||
fn try_from(ops: &FileOperationFilter) -> Result<Self, globset::Error> {
|
||||
Ok(Self {
|
||||
kind: ops.pattern.matches.clone(),
|
||||
glob: GlobBuilder::new(&ops.pattern.glob)
|
||||
.case_insensitive(
|
||||
ops.pattern
|
||||
.options
|
||||
.as_ref()
|
||||
.map_or(false, |ops| ops.ignore_case.unwrap_or(false)),
|
||||
)
|
||||
.build()?
|
||||
.compile_matcher(),
|
||||
})
|
||||
}
|
||||
}
|
||||
struct RenameActionPredicate {
|
||||
glob: GlobMatcher,
|
||||
kind: Option<FileOperationPatternKind>,
|
||||
}
|
||||
|
||||
impl RenameActionPredicate {
|
||||
// Returns true if language server should be notified
|
||||
fn eval(&self, path: &str, is_dir: bool) -> bool {
|
||||
self.kind.as_ref().map_or(true, |kind| {
|
||||
let expected_kind = if is_dir {
|
||||
FileOperationPatternKind::Folder
|
||||
} else {
|
||||
FileOperationPatternKind::File
|
||||
};
|
||||
kind == &expected_kind
|
||||
}) && self.glob.is_match(path)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct LanguageServerWatchedPaths {
|
||||
worktree_paths: HashMap<WorktreeId, GlobSet>,
|
||||
@@ -7756,65 +7539,78 @@ impl LanguageServerWatchedPathsBuilder {
|
||||
fs: Arc<dyn Fs>,
|
||||
language_server_id: LanguageServerId,
|
||||
cx: &mut ModelContext<LspStore>,
|
||||
) -> LanguageServerWatchedPaths {
|
||||
) -> Model<LanguageServerWatchedPaths> {
|
||||
let project = cx.weak_model();
|
||||
|
||||
const LSP_ABS_PATH_OBSERVE: Duration = Duration::from_millis(100);
|
||||
let abs_paths = self
|
||||
.abs_paths
|
||||
.into_iter()
|
||||
.map(|(abs_path, globset)| {
|
||||
let task = cx.spawn({
|
||||
let abs_path = abs_path.clone();
|
||||
let fs = fs.clone();
|
||||
cx.new_model(|cx| {
|
||||
let this_id = cx.entity_id();
|
||||
const LSP_ABS_PATH_OBSERVE: Duration = Duration::from_millis(100);
|
||||
let abs_paths = self
|
||||
.abs_paths
|
||||
.into_iter()
|
||||
.map(|(abs_path, globset)| {
|
||||
let task = cx.spawn({
|
||||
let abs_path = abs_path.clone();
|
||||
let fs = fs.clone();
|
||||
|
||||
let lsp_store = project.clone();
|
||||
|_, mut cx| async move {
|
||||
maybe!(async move {
|
||||
let mut push_updates = fs.watch(&abs_path, LSP_ABS_PATH_OBSERVE).await;
|
||||
while let Some(update) = push_updates.0.next().await {
|
||||
let action = lsp_store
|
||||
.update(&mut cx, |this, _| {
|
||||
let Some(local) = this.as_local() else {
|
||||
return ControlFlow::Break(());
|
||||
};
|
||||
let Some(watcher) = local
|
||||
.language_server_watched_paths
|
||||
.get(&language_server_id)
|
||||
else {
|
||||
return ControlFlow::Break(());
|
||||
};
|
||||
let (globs, _) = watcher.abs_paths.get(&abs_path).expect(
|
||||
"Watched abs path is not registered with a watcher",
|
||||
);
|
||||
let matching_entries = update
|
||||
.into_iter()
|
||||
.filter(|event| globs.is_match(&event.path))
|
||||
.collect::<Vec<_>>();
|
||||
this.lsp_notify_abs_paths_changed(
|
||||
language_server_id,
|
||||
matching_entries,
|
||||
);
|
||||
ControlFlow::Continue(())
|
||||
})
|
||||
.ok()?;
|
||||
let lsp_store = project.clone();
|
||||
|_, mut cx| async move {
|
||||
maybe!(async move {
|
||||
let mut push_updates =
|
||||
fs.watch(&abs_path, LSP_ABS_PATH_OBSERVE).await;
|
||||
while let Some(update) = push_updates.0.next().await {
|
||||
let action = lsp_store
|
||||
.update(&mut cx, |this, cx| {
|
||||
let Some(local) = this.as_local() else {
|
||||
return ControlFlow::Break(());
|
||||
};
|
||||
let Some(watcher) = local
|
||||
.language_server_watched_paths
|
||||
.get(&language_server_id)
|
||||
else {
|
||||
return ControlFlow::Break(());
|
||||
};
|
||||
if watcher.entity_id() != this_id {
|
||||
// This watcher is no longer registered on the project, which means that we should
|
||||
// cease operations.
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
let (globs, _) = watcher
|
||||
.read(cx)
|
||||
.abs_paths
|
||||
.get(&abs_path)
|
||||
.expect(
|
||||
"Watched abs path is not registered with a watcher",
|
||||
);
|
||||
let matching_entries = update
|
||||
.into_iter()
|
||||
.filter(|event| globs.is_match(&event.path))
|
||||
.collect::<Vec<_>>();
|
||||
this.lsp_notify_abs_paths_changed(
|
||||
language_server_id,
|
||||
matching_entries,
|
||||
);
|
||||
ControlFlow::Continue(())
|
||||
})
|
||||
.ok()?;
|
||||
|
||||
if action.is_break() {
|
||||
break;
|
||||
}
|
||||
if action.is_break() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
Some(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
});
|
||||
(abs_path, (globset, task))
|
||||
});
|
||||
(abs_path, (globset, task))
|
||||
})
|
||||
.collect();
|
||||
LanguageServerWatchedPaths {
|
||||
worktree_paths: self.worktree_paths,
|
||||
abs_paths,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
LanguageServerWatchedPaths {
|
||||
worktree_paths: self.worktree_paths,
|
||||
abs_paths,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -583,8 +583,6 @@ impl Project {
|
||||
client.add_model_request_handler(Self::handle_open_new_buffer);
|
||||
client.add_model_message_handler(Self::handle_create_buffer_for_peer);
|
||||
|
||||
client.add_model_request_handler(WorktreeStore::handle_rename_project_entry);
|
||||
|
||||
WorktreeStore::init(&client);
|
||||
BufferStore::init(&client);
|
||||
LspStore::init(&client);
|
||||
@@ -1491,45 +1489,11 @@ impl Project {
|
||||
new_path: impl Into<Arc<Path>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<CreatedEntry>> {
|
||||
let worktree_store = self.worktree_store.read(cx);
|
||||
let new_path = new_path.into();
|
||||
let Some((worktree, old_path, is_dir)) = worktree_store
|
||||
.worktree_and_entry_for_id(entry_id, cx)
|
||||
.map(|(worktree, entry)| (worktree, entry.path.clone(), entry.is_dir()))
|
||||
else {
|
||||
let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
|
||||
return Task::ready(Err(anyhow!(format!("No worktree for entry {entry_id:?}"))));
|
||||
};
|
||||
|
||||
let worktree_id = worktree.read(cx).id();
|
||||
|
||||
let lsp_store = self.lsp_store().downgrade();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let (old_abs_path, new_abs_path) = {
|
||||
let root_path = worktree.update(&mut cx, |this, _| this.abs_path())?;
|
||||
(root_path.join(&old_path), root_path.join(&new_path))
|
||||
};
|
||||
LspStore::will_rename_entry(
|
||||
lsp_store.clone(),
|
||||
worktree_id,
|
||||
&old_abs_path,
|
||||
&new_abs_path,
|
||||
is_dir,
|
||||
cx.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let entry = worktree
|
||||
.update(&mut cx, |worktree, cx| {
|
||||
worktree.rename_entry(entry_id, new_path.clone(), cx)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
lsp_store
|
||||
.update(&mut cx, |this, _| {
|
||||
this.did_rename_entry(worktree_id, &old_abs_path, &new_abs_path, is_dir);
|
||||
})
|
||||
.ok();
|
||||
Ok(entry)
|
||||
worktree.update(cx, |worktree, cx| {
|
||||
worktree.rename_entry(entry_id, new_path, cx)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,16 +9,12 @@ use language::{
|
||||
tree_sitter_rust, tree_sitter_typescript, Diagnostic, DiagnosticSet, DiskState, FakeLspAdapter,
|
||||
LanguageConfig, LanguageMatcher, LanguageName, LineEnding, OffsetRangeExt, Point, ToPoint,
|
||||
};
|
||||
use lsp::{
|
||||
notification::DidRenameFiles, DiagnosticSeverity, DocumentChanges, FileOperationFilter,
|
||||
NumberOrString, TextDocumentEdit, WillRenameFiles,
|
||||
};
|
||||
use lsp::{DiagnosticSeverity, NumberOrString};
|
||||
use parking_lot::Mutex;
|
||||
use pretty_assertions::{assert_eq, assert_matches};
|
||||
use serde_json::json;
|
||||
#[cfg(not(windows))]
|
||||
use std::os;
|
||||
use std::{str::FromStr, sync::OnceLock};
|
||||
|
||||
use std::{mem, num::NonZeroU32, ops::Range, task::Poll};
|
||||
use task::{ResolvedTask, TaskContext};
|
||||
@@ -3919,135 +3915,6 @@ async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_lsp_rename_notifications(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
"one.rs": "const ONE: usize = 1;",
|
||||
"two": {
|
||||
"two.rs": "const TWO: usize = one::ONE + one::ONE;"
|
||||
}
|
||||
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(rust_lang());
|
||||
let watched_paths = lsp::FileOperationRegistrationOptions {
|
||||
filters: vec![
|
||||
FileOperationFilter {
|
||||
scheme: Some("file".to_owned()),
|
||||
pattern: lsp::FileOperationPattern {
|
||||
glob: "**/*.rs".to_owned(),
|
||||
matches: Some(lsp::FileOperationPatternKind::File),
|
||||
options: None,
|
||||
},
|
||||
},
|
||||
FileOperationFilter {
|
||||
scheme: Some("file".to_owned()),
|
||||
pattern: lsp::FileOperationPattern {
|
||||
glob: "**/**".to_owned(),
|
||||
matches: Some(lsp::FileOperationPatternKind::Folder),
|
||||
options: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
let mut fake_servers = language_registry.register_fake_lsp(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
workspace: Some(lsp::WorkspaceServerCapabilities {
|
||||
workspace_folders: None,
|
||||
file_operations: Some(lsp::WorkspaceFileOperationsServerCapabilities {
|
||||
did_rename: Some(watched_paths.clone()),
|
||||
will_rename: Some(watched_paths),
|
||||
..Default::default()
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let _ = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/dir/one.rs", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let fake_server = fake_servers.next().await.unwrap();
|
||||
let response = project.update(cx, |project, cx| {
|
||||
let worktree = project.worktrees(cx).next().unwrap();
|
||||
let entry = worktree.read(cx).entry_for_path("one.rs").unwrap();
|
||||
project.rename_entry(entry.id, "three.rs".as_ref(), cx)
|
||||
});
|
||||
let expected_edit = lsp::WorkspaceEdit {
|
||||
changes: None,
|
||||
document_changes: Some(DocumentChanges::Edits({
|
||||
vec![TextDocumentEdit {
|
||||
edits: vec![lsp::Edit::Plain(lsp::TextEdit {
|
||||
range: lsp::Range {
|
||||
start: lsp::Position {
|
||||
line: 0,
|
||||
character: 1,
|
||||
},
|
||||
end: lsp::Position {
|
||||
line: 0,
|
||||
character: 3,
|
||||
},
|
||||
},
|
||||
new_text: "This is not a drill".to_owned(),
|
||||
})],
|
||||
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
||||
uri: Url::from_str("file:///dir/two/two.rs").unwrap(),
|
||||
version: Some(1337),
|
||||
},
|
||||
}]
|
||||
})),
|
||||
change_annotations: None,
|
||||
};
|
||||
let resolved_workspace_edit = Arc::new(OnceLock::new());
|
||||
fake_server
|
||||
.handle_request::<WillRenameFiles, _, _>({
|
||||
let resolved_workspace_edit = resolved_workspace_edit.clone();
|
||||
let expected_edit = expected_edit.clone();
|
||||
move |params, _| {
|
||||
let resolved_workspace_edit = resolved_workspace_edit.clone();
|
||||
let expected_edit = expected_edit.clone();
|
||||
async move {
|
||||
assert_eq!(params.files.len(), 1);
|
||||
assert_eq!(params.files[0].old_uri, "file:///dir/one.rs");
|
||||
assert_eq!(params.files[0].new_uri, "file:///dir/three.rs");
|
||||
resolved_workspace_edit.set(expected_edit.clone()).unwrap();
|
||||
Ok(Some(expected_edit))
|
||||
}
|
||||
}
|
||||
})
|
||||
.next()
|
||||
.await
|
||||
.unwrap();
|
||||
let _ = response.await.unwrap();
|
||||
fake_server
|
||||
.handle_notification::<DidRenameFiles, _>(|params, _| {
|
||||
assert_eq!(params.files.len(), 1);
|
||||
assert_eq!(params.files[0].old_uri, "file:///dir/one.rs");
|
||||
assert_eq!(params.files[0].new_uri, "file:///dir/three.rs");
|
||||
})
|
||||
.next()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resolved_workspace_edit.get(), Some(&expected_edit));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_rename(cx: &mut gpui::TestAppContext) {
|
||||
// hi
|
||||
|
||||
@@ -26,7 +26,7 @@ use text::ReplicaId;
|
||||
use util::{paths::SanitizedPath, ResultExt};
|
||||
use worktree::{Entry, ProjectEntryId, Worktree, WorktreeId, WorktreeSettings};
|
||||
|
||||
use crate::{search::SearchQuery, LspStore, ProjectPath};
|
||||
use crate::{search::SearchQuery, ProjectPath};
|
||||
|
||||
struct MatchingEntry {
|
||||
worktree_path: Arc<Path>,
|
||||
@@ -69,6 +69,7 @@ impl EventEmitter<WorktreeStoreEvent> for WorktreeStore {}
|
||||
impl WorktreeStore {
|
||||
pub fn init(client: &AnyProtoClient) {
|
||||
client.add_model_request_handler(Self::handle_create_project_entry);
|
||||
client.add_model_request_handler(Self::handle_rename_project_entry);
|
||||
client.add_model_request_handler(Self::handle_copy_project_entry);
|
||||
client.add_model_request_handler(Self::handle_delete_project_entry);
|
||||
client.add_model_request_handler(Self::handle_expand_project_entry);
|
||||
@@ -183,19 +184,6 @@ impl WorktreeStore {
|
||||
.find_map(|worktree| worktree.read(cx).entry_for_id(entry_id))
|
||||
}
|
||||
|
||||
pub fn worktree_and_entry_for_id<'a>(
|
||||
&'a self,
|
||||
entry_id: ProjectEntryId,
|
||||
cx: &'a AppContext,
|
||||
) -> Option<(Model<Worktree>, &'a Entry)> {
|
||||
self.worktrees().find_map(|worktree| {
|
||||
worktree
|
||||
.read(cx)
|
||||
.entry_for_id(entry_id)
|
||||
.map(|e| (worktree.clone(), e))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Entry> {
|
||||
self.worktree_for_id(path.worktree_id, cx)?
|
||||
.read(cx)
|
||||
@@ -1016,56 +1004,16 @@ impl WorktreeStore {
|
||||
}
|
||||
|
||||
pub async fn handle_rename_project_entry(
|
||||
this: Model<super::Project>,
|
||||
this: Model<Self>,
|
||||
envelope: TypedEnvelope<proto::RenameProjectEntry>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<proto::ProjectEntryResponse> {
|
||||
let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
|
||||
let (worktree_id, worktree, old_path, is_dir) = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.worktree_store
|
||||
.read(cx)
|
||||
.worktree_and_entry_for_id(entry_id, cx)
|
||||
.map(|(worktree, entry)| {
|
||||
(
|
||||
worktree.read(cx).id(),
|
||||
worktree,
|
||||
entry.path.clone(),
|
||||
entry.is_dir(),
|
||||
)
|
||||
})
|
||||
})?
|
||||
.ok_or_else(|| anyhow!("worktree not found"))?;
|
||||
let (old_abs_path, new_abs_path) = {
|
||||
let root_path = worktree.update(&mut cx, |this, _| this.abs_path())?;
|
||||
(
|
||||
root_path.join(&old_path),
|
||||
root_path.join(&envelope.payload.new_path),
|
||||
)
|
||||
};
|
||||
let lsp_store = this
|
||||
.update(&mut cx, |this, _| this.lsp_store())?
|
||||
.downgrade();
|
||||
LspStore::will_rename_entry(
|
||||
lsp_store,
|
||||
worktree_id,
|
||||
&old_abs_path,
|
||||
&new_abs_path,
|
||||
is_dir,
|
||||
cx.clone(),
|
||||
)
|
||||
.await;
|
||||
let response = Worktree::handle_rename_entry(worktree, envelope.payload, cx.clone()).await;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.lsp_store().read(cx).did_rename_entry(
|
||||
worktree_id,
|
||||
&old_abs_path,
|
||||
&new_abs_path,
|
||||
is_dir,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
response
|
||||
let worktree = this.update(&mut cx, |this, cx| {
|
||||
this.worktree_for_entry(entry_id, cx)
|
||||
.ok_or_else(|| anyhow!("worktree not found"))
|
||||
})??;
|
||||
Worktree::handle_rename_entry(worktree, envelope.payload, cx).await
|
||||
}
|
||||
|
||||
pub async fn handle_copy_project_entry(
|
||||
|
||||
@@ -881,26 +881,7 @@ pub fn get_key_equivalents(layout: &str) -> Option<HashMap<char, char>> {
|
||||
('}', 'Æ'),
|
||||
('~', '>'),
|
||||
],
|
||||
"com.apple.keylayout.NorwegianExtended" => &[
|
||||
('"', 'ˆ'),
|
||||
('&', '/'),
|
||||
('(', ')'),
|
||||
(')', '='),
|
||||
('*', '('),
|
||||
('/', '´'),
|
||||
(':', 'Å'),
|
||||
(';', 'å'),
|
||||
('<', ';'),
|
||||
('=', '`'),
|
||||
('>', ':'),
|
||||
('@', '"'),
|
||||
('[', 'ø'),
|
||||
('\\', '@'),
|
||||
(']', 'æ'),
|
||||
('`', '<'),
|
||||
('}', 'Æ'),
|
||||
('~', '>'),
|
||||
],
|
||||
"com.apple.keylayout.NorwegianExtended" => &[('^', 'ˆ'), ('~', '˜')],
|
||||
"com.apple.keylayout.NorwegianSami-PC" => &[
|
||||
('"', 'ˆ'),
|
||||
('&', '/'),
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct KeymapBlock {
|
||||
#[serde(default)]
|
||||
context: Option<String>,
|
||||
#[serde(default)]
|
||||
use_key_equivalents: Option<bool>,
|
||||
use_layout_keys: Option<bool>,
|
||||
bindings: BTreeMap<String, KeymapAction>,
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ impl KeymapFile {
|
||||
|
||||
for KeymapBlock {
|
||||
context,
|
||||
use_key_equivalents,
|
||||
use_layout_keys,
|
||||
bindings,
|
||||
} in self.0
|
||||
{
|
||||
@@ -124,10 +124,10 @@ impl KeymapFile {
|
||||
&keystroke,
|
||||
action,
|
||||
context.as_deref(),
|
||||
if use_key_equivalents.unwrap_or_default() {
|
||||
key_equivalents.as_ref()
|
||||
} else {
|
||||
if use_layout_keys.unwrap_or_default() {
|
||||
None
|
||||
} else {
|
||||
key_equivalents.as_ref()
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../livekit-rust-sdks/soxr-sys
|
||||
@@ -214,13 +214,8 @@ async fn deserialize_pane_group(
|
||||
.await;
|
||||
|
||||
let pane = panel
|
||||
.update(cx, |terminal_panel, cx| {
|
||||
new_terminal_pane(
|
||||
workspace.clone(),
|
||||
project.clone(),
|
||||
terminal_panel.active_pane.read(cx).is_zoomed(),
|
||||
cx,
|
||||
)
|
||||
.update(cx, |_, cx| {
|
||||
new_terminal_pane(workspace.clone(), project.clone(), cx)
|
||||
})
|
||||
.log_err()?;
|
||||
let active_item = serialized_pane.active_item;
|
||||
|
||||
@@ -84,10 +84,9 @@ pub struct TerminalPanel {
|
||||
impl TerminalPanel {
|
||||
pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
|
||||
let project = workspace.project();
|
||||
let pane = new_terminal_pane(workspace.weak_handle(), project.clone(), false, cx);
|
||||
let pane = new_terminal_pane(workspace.weak_handle(), project.clone(), cx);
|
||||
let center = PaneGroup::new(pane.clone());
|
||||
let enabled = project.read(cx).supports_terminal(cx);
|
||||
cx.focus_view(&pane);
|
||||
let terminal_panel = Self {
|
||||
center,
|
||||
active_pane: pane,
|
||||
@@ -300,9 +299,6 @@ impl TerminalPanel {
|
||||
let pane_count_before_removal = self.center.panes().len();
|
||||
let _removal_result = self.center.remove(&pane);
|
||||
if pane_count_before_removal == 1 {
|
||||
self.center.first_pane().update(cx, |pane, cx| {
|
||||
pane.set_zoomed(false, cx);
|
||||
});
|
||||
cx.emit(PanelEvent::Close);
|
||||
} else {
|
||||
if let Some(focus_on_pane) =
|
||||
@@ -312,49 +308,27 @@ impl TerminalPanel {
|
||||
}
|
||||
}
|
||||
}
|
||||
pane::Event::ZoomIn => {
|
||||
for pane in self.center.panes() {
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.set_zoomed(true, cx);
|
||||
})
|
||||
}
|
||||
cx.emit(PanelEvent::ZoomIn);
|
||||
cx.notify();
|
||||
}
|
||||
pane::Event::ZoomOut => {
|
||||
for pane in self.center.panes() {
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.set_zoomed(false, cx);
|
||||
})
|
||||
}
|
||||
cx.emit(PanelEvent::ZoomOut);
|
||||
cx.notify();
|
||||
}
|
||||
pane::Event::ZoomIn => cx.emit(PanelEvent::ZoomIn),
|
||||
pane::Event::ZoomOut => cx.emit(PanelEvent::ZoomOut),
|
||||
pane::Event::AddItem { item } => {
|
||||
if let Some(workspace) = self.workspace.upgrade() {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
item.added_to_pane(workspace, pane.clone(), cx)
|
||||
})
|
||||
}
|
||||
self.serialize(cx);
|
||||
}
|
||||
pane::Event::Split(direction) => {
|
||||
let new_pane = self.new_pane_with_cloned_active_terminal(cx);
|
||||
let pane = pane.clone();
|
||||
let direction = *direction;
|
||||
cx.spawn(move |terminal_panel, mut cx| async move {
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let Some(new_pane) = new_pane.await else {
|
||||
return;
|
||||
};
|
||||
terminal_panel
|
||||
.update(&mut cx, |terminal_panel, cx| {
|
||||
terminal_panel
|
||||
.center
|
||||
.split(&pane, &new_pane, direction)
|
||||
.log_err();
|
||||
cx.focus_view(&new_pane);
|
||||
})
|
||||
.ok();
|
||||
this.update(&mut cx, |this, _| {
|
||||
this.center.split(&pane, &new_pane, direction).log_err();
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
@@ -391,7 +365,7 @@ impl TerminalPanel {
|
||||
.or_else(|| default_working_directory(workspace.read(cx), cx));
|
||||
let kind = TerminalKind::Shell(working_directory);
|
||||
let window = cx.window_handle();
|
||||
cx.spawn(move |terminal_panel, mut cx| async move {
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let terminal = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.create_terminal(kind, window, cx)
|
||||
@@ -406,15 +380,10 @@ impl TerminalPanel {
|
||||
})
|
||||
.ok()?,
|
||||
);
|
||||
let pane = terminal_panel
|
||||
.update(&mut cx, |terminal_panel, cx| {
|
||||
let pane = new_terminal_pane(
|
||||
weak_workspace,
|
||||
project,
|
||||
terminal_panel.active_pane.read(cx).is_zoomed(),
|
||||
cx,
|
||||
);
|
||||
terminal_panel.apply_tab_bar_buttons(&pane, cx);
|
||||
let pane = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
let pane = new_terminal_pane(weak_workspace, project, cx);
|
||||
this.apply_tab_bar_buttons(&pane, cx);
|
||||
pane
|
||||
})
|
||||
.ok()?;
|
||||
@@ -423,6 +392,7 @@ impl TerminalPanel {
|
||||
pane.add_item(terminal_view, true, true, None, cx);
|
||||
})
|
||||
.ok()?;
|
||||
cx.focus_view(&pane).ok()?;
|
||||
|
||||
Some(pane)
|
||||
})
|
||||
@@ -844,7 +814,6 @@ impl TerminalPanel {
|
||||
pub fn new_terminal_pane(
|
||||
workspace: WeakView<Workspace>,
|
||||
project: Model<Project>,
|
||||
zoomed: bool,
|
||||
cx: &mut ViewContext<TerminalPanel>,
|
||||
) -> View<Pane> {
|
||||
let is_local = project.read(cx).is_local();
|
||||
@@ -858,11 +827,9 @@ pub fn new_terminal_pane(
|
||||
NewTerminal.boxed_clone(),
|
||||
cx,
|
||||
);
|
||||
pane.set_zoomed(zoomed, cx);
|
||||
pane.set_can_navigate(false, cx);
|
||||
pane.display_nav_history_buttons(None);
|
||||
pane.set_should_display_tab_bar(|_| true);
|
||||
pane.set_zoom_out_on_close(false);
|
||||
|
||||
let terminal_panel_for_split_check = terminal_panel.clone();
|
||||
pane.set_can_split(Some(Arc::new(move |pane, dragged_item, cx| {
|
||||
@@ -912,12 +879,8 @@ pub fn new_terminal_pane(
|
||||
|
||||
let new_pane = pane.drag_split_direction().and_then(|split_direction| {
|
||||
terminal_panel.update(cx, |terminal_panel, cx| {
|
||||
let new_pane = new_terminal_pane(
|
||||
workspace.clone(),
|
||||
project.clone(),
|
||||
terminal_panel.active_pane.read(cx).is_zoomed(),
|
||||
cx,
|
||||
);
|
||||
let new_pane =
|
||||
new_terminal_pane(workspace.clone(), project.clone(), cx);
|
||||
terminal_panel.apply_tab_bar_buttons(&new_pane, cx);
|
||||
terminal_panel
|
||||
.center
|
||||
@@ -1099,21 +1062,14 @@ impl Render for TerminalPanel {
|
||||
cx.focus_view(&pane);
|
||||
} else {
|
||||
let new_pane = terminal_panel.new_pane_with_cloned_active_terminal(cx);
|
||||
cx.spawn(|terminal_panel, mut cx| async move {
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
if let Some(new_pane) = new_pane.await {
|
||||
terminal_panel
|
||||
.update(&mut cx, |terminal_panel, cx| {
|
||||
terminal_panel
|
||||
.center
|
||||
.split(
|
||||
&terminal_panel.active_pane,
|
||||
&new_pane,
|
||||
SplitDirection::Right,
|
||||
)
|
||||
.log_err();
|
||||
cx.focus_view(&new_pane);
|
||||
})
|
||||
.ok();
|
||||
this.update(&mut cx, |this, _| {
|
||||
this.center
|
||||
.split(&this.active_pane, &new_pane, SplitDirection::Right)
|
||||
.log_err();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
@@ -1196,12 +1152,8 @@ impl Panel for TerminalPanel {
|
||||
}
|
||||
|
||||
fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>) {
|
||||
for pane in self.center.panes() {
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.set_zoomed(zoomed, cx);
|
||||
})
|
||||
}
|
||||
cx.notify();
|
||||
self.active_pane
|
||||
.update(cx, |pane, cx| pane.set_zoomed(zoomed, cx));
|
||||
}
|
||||
|
||||
fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {
|
||||
|
||||
@@ -14,8 +14,12 @@ path = "src/ui.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono.workspace = true
|
||||
collections.workspace = true
|
||||
gpui.workspace = true
|
||||
itertools = { workspace = true, optional = true }
|
||||
lazy_static.workspace = true
|
||||
linkme.workspace = true
|
||||
once_cell.workspace = true
|
||||
menu.workspace = true
|
||||
serde.workspace = true
|
||||
settings.workspace = true
|
||||
@@ -24,6 +28,7 @@ story = { workspace = true, optional = true }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
theme.workspace = true
|
||||
ui_macros.workspace = true
|
||||
component_system.workspace = true
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows.workspace = true
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{
|
||||
prelude::*,
|
||||
utils::{component_preview, component_preview_group},
|
||||
Indicator,
|
||||
};
|
||||
|
||||
use component_system::ComponentPreview;
|
||||
use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
|
||||
use ui_macros::IntoComponent;
|
||||
|
||||
/// An element that renders a user avatar with customizable appearance options.
|
||||
///
|
||||
@@ -14,7 +20,7 @@ use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
|
||||
/// .grayscale(true)
|
||||
/// .border_color(gpui::red());
|
||||
/// ```
|
||||
#[derive(IntoElement)]
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
pub struct Avatar {
|
||||
image: Img,
|
||||
size: Option<AbsoluteLength>,
|
||||
@@ -96,3 +102,35 @@ impl RenderOnce for Avatar {
|
||||
.children(self.indicator.map(|indicator| div().child(indicator)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for Avatar {
|
||||
fn preview(cx: &WindowContext) -> AnyElement {
|
||||
let avatar_1 = "https://avatars.githubusercontent.com/u/1789?s=70&v=4";
|
||||
let avatar_2 = "https://avatars.githubusercontent.com/u/482957?s=70&v=4";
|
||||
let avatar_3 = "https://avatars.githubusercontent.com/u/326587?s=70&v=4";
|
||||
|
||||
v_flex()
|
||||
.gap_4()
|
||||
.child(
|
||||
component_preview_group()
|
||||
.child(component_preview("Default").child(Avatar::new(avatar_1)))
|
||||
.child(
|
||||
component_preview("Custom Size").child(Avatar::new(avatar_2).size(px(48.))),
|
||||
)
|
||||
.child(
|
||||
component_preview("Grayscale").child(Avatar::new(avatar_3).grayscale(true)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
component_preview_group()
|
||||
.child(
|
||||
component_preview("With Border")
|
||||
.child(Avatar::new(avatar_1).border_color(cx.theme().colors().border)),
|
||||
)
|
||||
.child(component_preview("With Indicator").child(
|
||||
Avatar::new(avatar_2).indicator(Indicator::dot().color(Color::Success)),
|
||||
)),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![allow(missing_docs)]
|
||||
use gpui::{AnyView, DefiniteLength};
|
||||
use gpui::{AnyElement, AnyView, DefiniteLength};
|
||||
|
||||
use crate::{
|
||||
prelude::*, Color, DynamicSpacing, ElevationIndex, IconPosition, KeyBinding, TintColor,
|
||||
@@ -440,102 +440,87 @@ impl RenderOnce for Button {
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for Button {
|
||||
fn description() -> impl Into<Option<&'static str>> {
|
||||
"A button allows users to take actions, and make choices, with a single tap."
|
||||
}
|
||||
// register_components!(input, [Button]);
|
||||
|
||||
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
|
||||
vec![
|
||||
example_group_with_title(
|
||||
"Styles",
|
||||
vec![
|
||||
single_example("Default", Button::new("default", "Default")),
|
||||
single_example(
|
||||
"Filled",
|
||||
Button::new("filled", "Filled").style(ButtonStyle::Filled),
|
||||
),
|
||||
single_example(
|
||||
"Subtle",
|
||||
Button::new("outline", "Subtle").style(ButtonStyle::Subtle),
|
||||
),
|
||||
single_example(
|
||||
"Transparent",
|
||||
Button::new("transparent", "Transparent").style(ButtonStyle::Transparent),
|
||||
),
|
||||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"Tinted",
|
||||
vec![
|
||||
single_example(
|
||||
"Accent",
|
||||
Button::new("tinted_accent", "Accent")
|
||||
.style(ButtonStyle::Tinted(TintColor::Accent)),
|
||||
),
|
||||
single_example(
|
||||
"Negative",
|
||||
Button::new("tinted_negative", "Negative")
|
||||
.style(ButtonStyle::Tinted(TintColor::Negative)),
|
||||
),
|
||||
single_example(
|
||||
"Warning",
|
||||
Button::new("tinted_warning", "Warning")
|
||||
.style(ButtonStyle::Tinted(TintColor::Warning)),
|
||||
),
|
||||
single_example(
|
||||
"Positive",
|
||||
Button::new("tinted_positive", "Positive")
|
||||
.style(ButtonStyle::Tinted(TintColor::Positive)),
|
||||
),
|
||||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"States",
|
||||
vec![
|
||||
single_example("Default", Button::new("default_state", "Default")),
|
||||
single_example(
|
||||
"Disabled",
|
||||
Button::new("disabled", "Disabled").disabled(true),
|
||||
),
|
||||
single_example(
|
||||
"Selected",
|
||||
Button::new("selected", "Selected").selected(true),
|
||||
),
|
||||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"With Icons",
|
||||
vec![
|
||||
single_example(
|
||||
"Icon Start",
|
||||
Button::new("icon_start", "Icon Start")
|
||||
.icon(IconName::Check)
|
||||
.icon_position(IconPosition::Start),
|
||||
),
|
||||
single_example(
|
||||
"Icon End",
|
||||
Button::new("icon_end", "Icon End")
|
||||
.icon(IconName::Check)
|
||||
.icon_position(IconPosition::End),
|
||||
),
|
||||
single_example(
|
||||
"Icon Color",
|
||||
Button::new("icon_color", "Icon Color")
|
||||
.icon(IconName::Check)
|
||||
.icon_color(Color::Accent),
|
||||
),
|
||||
single_example(
|
||||
"Tinted Icons",
|
||||
Button::new("icon_color", "Delete")
|
||||
.style(ButtonStyle::Tinted(TintColor::Negative))
|
||||
.color(Color::Error)
|
||||
.icon_color(Color::Error)
|
||||
.icon(IconName::Trash)
|
||||
.icon_position(IconPosition::Start),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
// impl ComponentElement for Button {
|
||||
// fn scope() -> &'static str {
|
||||
// "input"
|
||||
// }
|
||||
|
||||
// fn description() -> impl Into<Option<&'static str>> {
|
||||
// "A button allows users to take actions, and make choices, with a single tap."
|
||||
// }
|
||||
|
||||
// fn preview(_cx: &WindowContext) -> Option<AnyElement> {
|
||||
// Some(
|
||||
// v_flex()
|
||||
// .gap_4()
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(Button::new("default", "Default"))
|
||||
// .child(Button::new("filled", "Filled").style(ButtonStyle::Filled))
|
||||
// .child(Button::new("subtle", "Subtle").style(ButtonStyle::Subtle))
|
||||
// .child(
|
||||
// Button::new("transparent", "Transparent")
|
||||
// .style(ButtonStyle::Transparent),
|
||||
// ),
|
||||
// )
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(
|
||||
// Button::new("tinted_accent", "Accent")
|
||||
// .style(ButtonStyle::Tinted(TintColor::Accent)),
|
||||
// )
|
||||
// .child(
|
||||
// Button::new("tinted_negative", "Negative")
|
||||
// .style(ButtonStyle::Tinted(TintColor::Negative)),
|
||||
// )
|
||||
// .child(
|
||||
// Button::new("tinted_warning", "Warning")
|
||||
// .style(ButtonStyle::Tinted(TintColor::Warning)),
|
||||
// )
|
||||
// .child(
|
||||
// Button::new("tinted_positive", "Positive")
|
||||
// .style(ButtonStyle::Tinted(TintColor::Positive)),
|
||||
// ),
|
||||
// )
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(Button::new("default_state", "Default"))
|
||||
// .child(Button::new("disabled", "Disabled").disabled(true))
|
||||
// .child(Button::new("selected", "Selected").selected(true)),
|
||||
// )
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(
|
||||
// Button::new("icon_start", "Icon Start")
|
||||
// .icon(IconName::Check)
|
||||
// .icon_position(IconPosition::Start),
|
||||
// )
|
||||
// .child(
|
||||
// Button::new("icon_end", "Icon End")
|
||||
// .icon(IconName::Check)
|
||||
// .icon_position(IconPosition::End),
|
||||
// )
|
||||
// .child(
|
||||
// Button::new("icon_color", "Icon Color")
|
||||
// .icon(IconName::Check)
|
||||
// .icon_color(Color::Accent),
|
||||
// )
|
||||
// .child(
|
||||
// Button::new("icon_color", "Delete")
|
||||
// .style(ButtonStyle::Tinted(TintColor::Negative))
|
||||
// .color(Color::Error)
|
||||
// .icon_color(Color::Error)
|
||||
// .icon(IconName::Trash)
|
||||
// .icon_position(IconPosition::Start),
|
||||
// ),
|
||||
// )
|
||||
// .into_any_element(),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#![allow(missing_docs)]
|
||||
use crate::{prelude::*, Icon, IconName, IconSize};
|
||||
use gpui::AnyElement;
|
||||
|
||||
use crate::{prelude::*, Icon, IconName, IconSize, TintColor};
|
||||
|
||||
/// An icon that appears within a button.
|
||||
///
|
||||
@@ -99,3 +101,53 @@ impl RenderOnce for ButtonIcon {
|
||||
Icon::new(icon).size(self.size).color(icon_color)
|
||||
}
|
||||
}
|
||||
|
||||
// register_components!(input, [ButtonIcon]);
|
||||
|
||||
// impl ComponentElement for ButtonIcon {
|
||||
// fn scope() -> &'static str {
|
||||
// "input"
|
||||
// }
|
||||
|
||||
// fn description() -> impl Into<Option<&'static str>> {
|
||||
// "A ButtonIcon is an icon that appears within a button, used either alongside a label or as a standalone icon."
|
||||
// }
|
||||
|
||||
// fn preview(_cx: &WindowContext) -> Option<AnyElement> {
|
||||
// Some(
|
||||
// v_flex()
|
||||
// .gap_4()
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(ButtonIcon::new(IconName::Check))
|
||||
// .child(ButtonIcon::new(IconName::Check).color(Color::Accent))
|
||||
// .child(ButtonIcon::new(IconName::Check).size(IconSize::XSmall))
|
||||
// .child(ButtonIcon::new(IconName::Check).size(IconSize::Small)),
|
||||
// )
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(ButtonIcon::new(IconName::Check).selected(true))
|
||||
// .child(ButtonIcon::new(IconName::Check).disabled(true))
|
||||
// .child(ButtonIcon::new(IconName::Check).selected_icon(IconName::X))
|
||||
// .child(ButtonIcon::new(IconName::Check).selected_icon_color(Color::Error)),
|
||||
// )
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(ButtonIcon::new(IconName::Check).selected_style(ButtonStyle::Filled))
|
||||
// .child(ButtonIcon::new(IconName::Check).selected_style(ButtonStyle::Subtle))
|
||||
// .child(
|
||||
// ButtonIcon::new(IconName::Check)
|
||||
// .selected_style(ButtonStyle::Transparent),
|
||||
// )
|
||||
// .child(
|
||||
// ButtonIcon::new(IconName::Check)
|
||||
// .selected_style(ButtonStyle::Tinted(TintColor::Accent)),
|
||||
// ),
|
||||
// )
|
||||
// .into_any_element(),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#![allow(missing_docs)]
|
||||
use gpui::{AnyView, DefiniteLength};
|
||||
use gpui::{AnyElement, AnyView, DefiniteLength};
|
||||
|
||||
use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle};
|
||||
use crate::{prelude::*, ElevationIndex, SelectableButton};
|
||||
use crate::{prelude::*, ElevationIndex, SelectableButton, TintColor};
|
||||
use crate::{IconName, IconSize};
|
||||
|
||||
use super::button_icon::ButtonIcon;
|
||||
@@ -165,3 +165,86 @@ impl RenderOnce for IconButton {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// register_components!(input, [IconButton]);
|
||||
|
||||
// impl ComponentElement for IconButton {
|
||||
// fn scope() -> &'static str {
|
||||
// "input"
|
||||
// }
|
||||
|
||||
// fn description() -> impl Into<Option<&'static str>> {
|
||||
// "An icon button allows users to take actions with a single tap, represented by an icon."
|
||||
// }
|
||||
|
||||
// fn preview(_cx: &WindowContext) -> Option<AnyElement> {
|
||||
// Some(
|
||||
// v_flex()
|
||||
// .gap_4()
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(IconButton::new("default", IconName::Check))
|
||||
// .child(
|
||||
// IconButton::new("filled", IconName::Check).style(ButtonStyle::Filled),
|
||||
// )
|
||||
// .child(
|
||||
// IconButton::new("subtle", IconName::Check).style(ButtonStyle::Subtle),
|
||||
// )
|
||||
// .child(
|
||||
// IconButton::new("transparent", IconName::Check)
|
||||
// .style(ButtonStyle::Transparent),
|
||||
// ),
|
||||
// )
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(
|
||||
// IconButton::new("tinted_accent", IconName::Check)
|
||||
// .style(ButtonStyle::Tinted(TintColor::Accent)),
|
||||
// )
|
||||
// .child(
|
||||
// IconButton::new("tinted_negative", IconName::Check)
|
||||
// .style(ButtonStyle::Tinted(TintColor::Negative)),
|
||||
// )
|
||||
// .child(
|
||||
// IconButton::new("tinted_warning", IconName::Check)
|
||||
// .style(ButtonStyle::Tinted(TintColor::Warning)),
|
||||
// )
|
||||
// .child(
|
||||
// IconButton::new("tinted_positive", IconName::Check)
|
||||
// .style(ButtonStyle::Tinted(TintColor::Positive)),
|
||||
// ),
|
||||
// )
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(IconButton::new("default_state", IconName::Check))
|
||||
// .child(IconButton::new("disabled", IconName::Check).disabled(true))
|
||||
// .child(IconButton::new("selected", IconName::Check).selected(true)),
|
||||
// )
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .gap_4()
|
||||
// .child(
|
||||
// IconButton::new("icon_size_small", IconName::Check)
|
||||
// .icon_size(IconSize::Small),
|
||||
// )
|
||||
// .child(
|
||||
// IconButton::new("icon_size_medium", IconName::Check)
|
||||
// .icon_size(IconSize::Medium),
|
||||
// )
|
||||
// .child(
|
||||
// IconButton::new("icon_color", IconName::Check)
|
||||
// .icon_color(Color::Accent),
|
||||
// )
|
||||
// .child(
|
||||
// IconButton::new("icon_delete", IconName::Trash)
|
||||
// .style(ButtonStyle::Tinted(TintColor::Negative))
|
||||
// .icon_color(Color::Error),
|
||||
// ),
|
||||
// )
|
||||
// .into_any_element(),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use gpui::{div, prelude::*, ElementId, IntoElement, Styled, WindowContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{Color, Icon, IconName, Selection};
|
||||
use gpui::{div, prelude::*, ElementId, IntoElement, Styled, WindowContext};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// # Checkbox
|
||||
///
|
||||
@@ -113,55 +112,38 @@ impl RenderOnce for Checkbox {
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for Checkbox {
|
||||
fn description() -> impl Into<Option<&'static str>> {
|
||||
"A checkbox lets people choose between a pair of opposing states, like enabled and disabled, using a different appearance to indicate each state."
|
||||
}
|
||||
// impl ComponentElement for Checkbox {
|
||||
// fn description() -> impl Into<Option<&'static str>> {
|
||||
// "A checkbox allows people to toggle between two states, typically representing on/off, or a pair of opposites."
|
||||
// }
|
||||
|
||||
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
|
||||
vec![
|
||||
example_group_with_title(
|
||||
"Default",
|
||||
vec![
|
||||
single_example(
|
||||
"Unselected",
|
||||
Checkbox::new("checkbox_unselected", Selection::Unselected),
|
||||
),
|
||||
single_example(
|
||||
"Indeterminate",
|
||||
Checkbox::new("checkbox_indeterminate", Selection::Indeterminate),
|
||||
),
|
||||
single_example(
|
||||
"Selected",
|
||||
Checkbox::new("checkbox_selected", Selection::Selected),
|
||||
),
|
||||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"Disabled",
|
||||
vec![
|
||||
single_example(
|
||||
"Unselected",
|
||||
Checkbox::new("checkbox_disabled_unselected", Selection::Unselected)
|
||||
.disabled(true),
|
||||
),
|
||||
single_example(
|
||||
"Indeterminate",
|
||||
Checkbox::new("checkbox_disabled_indeterminate", Selection::Indeterminate)
|
||||
.disabled(true),
|
||||
),
|
||||
single_example(
|
||||
"Selected",
|
||||
Checkbox::new("checkbox_disabled_selected", Selection::Selected)
|
||||
.disabled(true),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
// fn scope() -> &'static str {
|
||||
// "input"
|
||||
// }
|
||||
|
||||
use std::sync::Arc;
|
||||
// fn preview(_cx: &WindowContext) -> Option<gpui::AnyElement> {
|
||||
// Some(
|
||||
// component_preview_group()
|
||||
// .child(
|
||||
// component_preview("Default")
|
||||
// .child(Checkbox::new("checkbox-1", Selection::Unselected)),
|
||||
// )
|
||||
// .child(
|
||||
// component_preview("Selected")
|
||||
// .child(Checkbox::new("checkbox-2", Selection::Selected)),
|
||||
// )
|
||||
// .child(
|
||||
// component_preview("Indeterminate")
|
||||
// .child(Checkbox::new("checkbox-3", Selection::Indeterminate)),
|
||||
// )
|
||||
// .child(
|
||||
// component_preview("Disabled")
|
||||
// .child(Checkbox::new("checkbox-4", Selection::Selected).disabled(true)),
|
||||
// )
|
||||
// .into_any_element(),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
/// A [`Checkbox`] that has a [`Label`].
|
||||
#[derive(IntoElement)]
|
||||
@@ -209,40 +191,64 @@ impl RenderOnce for CheckboxWithLabel {
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for CheckboxWithLabel {
|
||||
fn description() -> impl Into<Option<&'static str>> {
|
||||
"A checkbox with an associated label, allowing users to select an option while providing a descriptive text."
|
||||
}
|
||||
// impl ComponentElement for CheckboxWithLabel {
|
||||
// fn description() -> impl Into<Option<&'static str>> {
|
||||
// "A checkbox with an associated label."
|
||||
// }
|
||||
|
||||
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
|
||||
vec![example_group(vec![
|
||||
single_example(
|
||||
"Unselected",
|
||||
CheckboxWithLabel::new(
|
||||
"checkbox_with_label_unselected",
|
||||
Label::new("Always save on quit"),
|
||||
Selection::Unselected,
|
||||
|_, _| {},
|
||||
),
|
||||
),
|
||||
single_example(
|
||||
"Indeterminate",
|
||||
CheckboxWithLabel::new(
|
||||
"checkbox_with_label_indeterminate",
|
||||
Label::new("Always save on quit"),
|
||||
Selection::Indeterminate,
|
||||
|_, _| {},
|
||||
),
|
||||
),
|
||||
single_example(
|
||||
"Selected",
|
||||
CheckboxWithLabel::new(
|
||||
"checkbox_with_label_selected",
|
||||
Label::new("Always save on quit"),
|
||||
Selection::Selected,
|
||||
|_, _| {},
|
||||
),
|
||||
),
|
||||
])]
|
||||
}
|
||||
}
|
||||
// fn scope() -> &'static str {
|
||||
// "input"
|
||||
// }
|
||||
|
||||
// fn preview(_cx: &WindowContext) -> Option<gpui::AnyElement> {
|
||||
// Some(
|
||||
// v_flex()
|
||||
// .gap_3()
|
||||
// .child(
|
||||
// component_preview_group()
|
||||
// .child(component_preview("Default").child(CheckboxWithLabel::new(
|
||||
// "checkbox-1",
|
||||
// Label::new("Show Completions"),
|
||||
// Selection::Unselected,
|
||||
// |_, _| {},
|
||||
// )))
|
||||
// .child(component_preview("Selected").child(CheckboxWithLabel::new(
|
||||
// "checkbox-2",
|
||||
// Label::new("Show Completions"),
|
||||
// Selection::Selected,
|
||||
// |_, _| {},
|
||||
// ))),
|
||||
// )
|
||||
// .child(
|
||||
// component_preview_group().child(
|
||||
// component_preview("Indeterminate").child(
|
||||
// v_flex()
|
||||
// .child(CheckboxWithLabel::new(
|
||||
// "checkbox-3",
|
||||
// Label::new("Show Completions"),
|
||||
// Selection::Indeterminate,
|
||||
// |_, _| {},
|
||||
// ))
|
||||
// .child(h_flex().child(div().w_5().h_full()).child(
|
||||
// CheckboxWithLabel::new(
|
||||
// "checkbox-4",
|
||||
// Label::new("Editor"),
|
||||
// Selection::Selected,
|
||||
// |_, _| {},
|
||||
// ),
|
||||
// ))
|
||||
// .child(h_flex().child(div().w_5().h_full()).child(
|
||||
// CheckboxWithLabel::new(
|
||||
// "checkbox-5",
|
||||
// Label::new("Assistant"),
|
||||
// Selection::Unselected,
|
||||
// |_, _| {},
|
||||
// ),
|
||||
// )),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// .into_any_element(),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -26,7 +26,9 @@ pub fn h_group() -> ContentGroup {
|
||||
pub struct ContentGroup {
|
||||
base: Div,
|
||||
border: bool,
|
||||
padding: Pixels,
|
||||
fill: bool,
|
||||
outset_amount: Option<AbsoluteLength>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
@@ -36,7 +38,9 @@ impl ContentGroup {
|
||||
Self {
|
||||
base: div(),
|
||||
border: true,
|
||||
padding: px(8.),
|
||||
fill: true,
|
||||
outset_amount: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
@@ -52,6 +56,25 @@ impl ContentGroup {
|
||||
self.fill = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Outset the [ContentBox] to create a visual "outset" effect.
|
||||
///
|
||||
/// This sets a default outset amount which matches the width of the border and padding,
|
||||
/// useful for lining up content with other elements.
|
||||
pub fn outset(mut self) -> Self {
|
||||
let border_width = if self.border { px(1.) } else { px(0.) };
|
||||
let outset_amount = self.padding + border_width;
|
||||
self.outset_amount = Some(outset_amount.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the amount of outset to apply to the [ContentBox].
|
||||
///
|
||||
/// This will add negative left and right margin to the [ContentBox] to create a visual "outset" effect.
|
||||
pub fn outset_amount(mut self, amount: impl Into<AbsoluteLength>) -> Self {
|
||||
self.outset_amount = Some(amount.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for ContentGroup {
|
||||
@@ -81,55 +104,14 @@ impl RenderOnce for ContentGroup {
|
||||
this.border_1().border_color(cx.theme().colors().border)
|
||||
})
|
||||
.rounded_md()
|
||||
.p_2()
|
||||
.p(self.padding)
|
||||
.when_some(self.outset_amount, |this, amount| {
|
||||
let outset_rems: Rems = match amount {
|
||||
AbsoluteLength::Pixels(p) => rems(p / cx.rem_size()),
|
||||
AbsoluteLength::Rems(r) => r,
|
||||
};
|
||||
this.mx(-outset_rems)
|
||||
})
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for ContentGroup {
|
||||
fn description() -> impl Into<Option<&'static str>> {
|
||||
"A flexible container component that can hold other elements. It can be customized with or without a border and background fill."
|
||||
}
|
||||
|
||||
fn example_label_side() -> ExampleLabelSide {
|
||||
ExampleLabelSide::Bottom
|
||||
}
|
||||
|
||||
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
|
||||
vec![example_group(vec![
|
||||
single_example(
|
||||
"Default",
|
||||
ContentGroup::new()
|
||||
.flex_1()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.h_48()
|
||||
.child(Label::new("Default ContentBox")),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Without Border",
|
||||
ContentGroup::new()
|
||||
.flex_1()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.h_48()
|
||||
.borderless()
|
||||
.child(Label::new("Borderless ContentBox")),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Without Fill",
|
||||
ContentGroup::new()
|
||||
.flex_1()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.h_48()
|
||||
.unfilled()
|
||||
.child(Label::new("Unfilled ContentBox")),
|
||||
)
|
||||
.grow(),
|
||||
])
|
||||
.grow()]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{prelude::*, Avatar};
|
||||
use crate::prelude::*;
|
||||
use gpui::{AnyElement, StyleRefinement};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
@@ -59,61 +59,3 @@ impl RenderOnce for Facepile {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for Facepile {
|
||||
fn description() -> impl Into<Option<&'static str>> {
|
||||
"A facepile is a collection of faces stacked horizontally–\
|
||||
always with the leftmost face on top and descending in z-index.\
|
||||
\n\nFacepiles are used to display a group of people or things,\
|
||||
such as a list of participants in a collaboration session."
|
||||
}
|
||||
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
|
||||
let few_faces: [&'static str; 3] = [
|
||||
"https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
|
||||
"https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
|
||||
"https://avatars.githubusercontent.com/u/482957?s=60&v=4",
|
||||
];
|
||||
|
||||
let many_faces: [&'static str; 6] = [
|
||||
"https://avatars.githubusercontent.com/u/326587?s=60&v=4",
|
||||
"https://avatars.githubusercontent.com/u/2280405?s=60&v=4",
|
||||
"https://avatars.githubusercontent.com/u/1789?s=60&v=4",
|
||||
"https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
|
||||
"https://avatars.githubusercontent.com/u/482957?s=60&v=4",
|
||||
"https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
|
||||
];
|
||||
|
||||
vec![example_group_with_title(
|
||||
"Examples",
|
||||
vec![
|
||||
single_example(
|
||||
"Few Faces",
|
||||
Facepile::new(
|
||||
few_faces
|
||||
.iter()
|
||||
.map(|&url| Avatar::new(url).into_any_element())
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
single_example(
|
||||
"Many Faces",
|
||||
Facepile::new(
|
||||
many_faces
|
||||
.iter()
|
||||
.map(|&url| Avatar::new(url).into_any_element())
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
single_example(
|
||||
"Custom Size",
|
||||
Facepile::new(
|
||||
few_faces
|
||||
.iter()
|
||||
.map(|&url| Avatar::new(url).size(px(24.)).into_any_element())
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
],
|
||||
)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
#![allow(missing_docs)]
|
||||
use gpui::{svg, AnimationElement, Hsla, IntoElement, Point, Rems, Transformation};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{EnumIter, EnumString, IntoEnumIterator, IntoStaticStr};
|
||||
use strum::{EnumIter, EnumString, IntoStaticStr};
|
||||
use ui_macros::DerivePathStr;
|
||||
|
||||
use crate::{
|
||||
prelude::*,
|
||||
traits::component_preview::{ComponentExample, ComponentPreview},
|
||||
Indicator,
|
||||
};
|
||||
use crate::{prelude::*, Indicator};
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub enum AnyIcon {
|
||||
@@ -491,26 +487,6 @@ impl RenderOnce for IconDecoration {
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for IconDecoration {
|
||||
fn examples(cx: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
|
||||
let all_kinds = IconDecorationKind::iter().collect::<Vec<_>>();
|
||||
|
||||
let examples = all_kinds
|
||||
.iter()
|
||||
.map(|kind| {
|
||||
let name = format!("{:?}", kind).to_string();
|
||||
|
||||
single_example(
|
||||
name,
|
||||
IconDecoration::new(*kind, cx.theme().colors().surface_background, cx),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
vec![example_group(examples)]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct DecoratedIcon {
|
||||
icon: Icon,
|
||||
@@ -533,66 +509,6 @@ impl RenderOnce for DecoratedIcon {
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for DecoratedIcon {
|
||||
fn examples(cx: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
|
||||
let icon_1 = Icon::new(IconName::FileDoc);
|
||||
let icon_2 = Icon::new(IconName::FileDoc);
|
||||
let icon_3 = Icon::new(IconName::FileDoc);
|
||||
let icon_4 = Icon::new(IconName::FileDoc);
|
||||
|
||||
let decoration_x = IconDecoration::new(
|
||||
IconDecorationKind::X,
|
||||
cx.theme().colors().surface_background,
|
||||
cx,
|
||||
)
|
||||
.color(cx.theme().status().error)
|
||||
.position(Point {
|
||||
x: px(-2.),
|
||||
y: px(-2.),
|
||||
});
|
||||
|
||||
let decoration_triangle = IconDecoration::new(
|
||||
IconDecorationKind::Triangle,
|
||||
cx.theme().colors().surface_background,
|
||||
cx,
|
||||
)
|
||||
.color(cx.theme().status().error)
|
||||
.position(Point {
|
||||
x: px(-2.),
|
||||
y: px(-2.),
|
||||
});
|
||||
|
||||
let decoration_dot = IconDecoration::new(
|
||||
IconDecorationKind::Dot,
|
||||
cx.theme().colors().surface_background,
|
||||
cx,
|
||||
)
|
||||
.color(cx.theme().status().error)
|
||||
.position(Point {
|
||||
x: px(-2.),
|
||||
y: px(-2.),
|
||||
});
|
||||
|
||||
let examples = vec![
|
||||
single_example("no_decoration", DecoratedIcon::new(icon_1, None)),
|
||||
single_example(
|
||||
"with_decoration",
|
||||
DecoratedIcon::new(icon_2, Some(decoration_x)),
|
||||
),
|
||||
single_example(
|
||||
"with_decoration",
|
||||
DecoratedIcon::new(icon_3, Some(decoration_triangle)),
|
||||
),
|
||||
single_example(
|
||||
"with_decoration",
|
||||
DecoratedIcon::new(icon_4, Some(decoration_dot)),
|
||||
),
|
||||
];
|
||||
|
||||
vec![example_group(examples)]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct IconWithIndicator {
|
||||
icon: Icon,
|
||||
@@ -651,26 +567,3 @@ impl RenderOnce for IconWithIndicator {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for Icon {
|
||||
fn examples(_cx: &WindowContext) -> Vec<ComponentExampleGroup<Icon>> {
|
||||
let arrow_icons = vec![
|
||||
IconName::ArrowDown,
|
||||
IconName::ArrowLeft,
|
||||
IconName::ArrowRight,
|
||||
IconName::ArrowUp,
|
||||
IconName::ArrowCircle,
|
||||
];
|
||||
|
||||
vec![example_group_with_title(
|
||||
"Arrow Icons",
|
||||
arrow_icons
|
||||
.into_iter()
|
||||
.map(|icon| {
|
||||
let name = format!("{:?}", icon).to_string();
|
||||
ComponentExample::new(name, Icon::new(icon))
|
||||
})
|
||||
.collect(),
|
||||
)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,34 +83,3 @@ impl RenderOnce for Indicator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for Indicator {
|
||||
fn description() -> impl Into<Option<&'static str>> {
|
||||
"An indicator visually represents a status or state."
|
||||
}
|
||||
|
||||
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
|
||||
vec![
|
||||
example_group_with_title(
|
||||
"Types",
|
||||
vec![
|
||||
single_example("Dot", Indicator::dot().color(Color::Info)),
|
||||
single_example("Bar", Indicator::bar().color(Color::Player(2))),
|
||||
single_example(
|
||||
"Icon",
|
||||
Indicator::icon(Icon::new(IconName::Check).color(Color::Success)),
|
||||
),
|
||||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"Examples",
|
||||
vec![
|
||||
single_example("Info", Indicator::dot().color(Color::Info)),
|
||||
single_example("Success", Indicator::dot().color(Color::Success)),
|
||||
single_example("Warning", Indicator::dot().color(Color::Warning)),
|
||||
single_example("Error", Indicator::dot().color(Color::Error)),
|
||||
],
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{prelude::*, Indicator};
|
||||
use crate::prelude::*;
|
||||
use gpui::{div, AnyElement, FontWeight, IntoElement, Length};
|
||||
|
||||
/// A table component
|
||||
@@ -150,90 +150,3 @@ where
|
||||
TableCell::String(e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for Table {
|
||||
fn description() -> impl Into<Option<&'static str>> {
|
||||
"Used for showing tabular data. Tables may show both text and elements in their cells."
|
||||
}
|
||||
|
||||
fn example_label_side() -> ExampleLabelSide {
|
||||
ExampleLabelSide::Top
|
||||
}
|
||||
|
||||
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
|
||||
vec![
|
||||
example_group(vec![
|
||||
single_example(
|
||||
"Simple Table",
|
||||
Table::new(vec!["Name", "Age", "City"])
|
||||
.width(px(400.))
|
||||
.row(vec!["Alice", "28", "New York"])
|
||||
.row(vec!["Bob", "32", "San Francisco"])
|
||||
.row(vec!["Charlie", "25", "London"]),
|
||||
),
|
||||
single_example(
|
||||
"Two Column Table",
|
||||
Table::new(vec!["Category", "Value"])
|
||||
.width(px(300.))
|
||||
.row(vec!["Revenue", "$100,000"])
|
||||
.row(vec!["Expenses", "$75,000"])
|
||||
.row(vec!["Profit", "$25,000"]),
|
||||
),
|
||||
]),
|
||||
example_group(vec![single_example(
|
||||
"Striped Table",
|
||||
Table::new(vec!["Product", "Price", "Stock"])
|
||||
.width(px(600.))
|
||||
.striped()
|
||||
.row(vec!["Laptop", "$999", "In Stock"])
|
||||
.row(vec!["Phone", "$599", "Low Stock"])
|
||||
.row(vec!["Tablet", "$399", "Out of Stock"])
|
||||
.row(vec!["Headphones", "$199", "In Stock"]),
|
||||
)]),
|
||||
example_group_with_title(
|
||||
"Mixed Content Table",
|
||||
vec![single_example(
|
||||
"Table with Elements",
|
||||
Table::new(vec!["Status", "Name", "Priority", "Deadline", "Action"])
|
||||
.width(px(840.))
|
||||
.row(vec![
|
||||
element_cell(Indicator::dot().color(Color::Success).into_any_element()),
|
||||
string_cell("Project A"),
|
||||
string_cell("High"),
|
||||
string_cell("2023-12-31"),
|
||||
element_cell(
|
||||
Button::new("view_a", "View")
|
||||
.style(ButtonStyle::Filled)
|
||||
.full_width()
|
||||
.into_any_element(),
|
||||
),
|
||||
])
|
||||
.row(vec![
|
||||
element_cell(Indicator::dot().color(Color::Warning).into_any_element()),
|
||||
string_cell("Project B"),
|
||||
string_cell("Medium"),
|
||||
string_cell("2024-03-15"),
|
||||
element_cell(
|
||||
Button::new("view_b", "View")
|
||||
.style(ButtonStyle::Filled)
|
||||
.full_width()
|
||||
.into_any_element(),
|
||||
),
|
||||
])
|
||||
.row(vec![
|
||||
element_cell(Indicator::dot().color(Color::Error).into_any_element()),
|
||||
string_cell("Project C"),
|
||||
string_cell("Low"),
|
||||
string_cell("2024-06-30"),
|
||||
element_cell(
|
||||
Button::new("view_c", "View")
|
||||
.style(ButtonStyle::Filled)
|
||||
.full_width()
|
||||
.into_any_element(),
|
||||
),
|
||||
]),
|
||||
)],
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ pub use gpui::{
|
||||
|
||||
pub use crate::styles::{rems_from_px, vh, vw, PlatformStyle, StyledTypography, TextSize};
|
||||
pub use crate::traits::clickable::*;
|
||||
pub use crate::traits::component_preview::*;
|
||||
pub use crate::traits::disableable::*;
|
||||
pub use crate::traits::fixed::*;
|
||||
pub use crate::traits::selectable::*;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod clickable;
|
||||
pub mod component_preview;
|
||||
pub mod disableable;
|
||||
pub mod fixed;
|
||||
pub mod selectable;
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
#![allow(missing_docs)]
|
||||
use crate::prelude::*;
|
||||
use gpui::{AnyElement, SharedString};
|
||||
|
||||
/// Which side of the preview to show labels on
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ExampleLabelSide {
|
||||
/// Left side
|
||||
Left,
|
||||
/// Right side
|
||||
Right,
|
||||
#[default]
|
||||
/// Top side
|
||||
Top,
|
||||
/// Bottom side
|
||||
Bottom,
|
||||
}
|
||||
|
||||
/// Implement this trait to enable rich UI previews with metadata in the Theme Preview tool.
|
||||
pub trait ComponentPreview: IntoElement {
|
||||
fn title() -> &'static str {
|
||||
std::any::type_name::<Self>()
|
||||
}
|
||||
|
||||
fn description() -> impl Into<Option<&'static str>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn example_label_side() -> ExampleLabelSide {
|
||||
ExampleLabelSide::default()
|
||||
}
|
||||
|
||||
fn examples(_cx: &WindowContext) -> Vec<ComponentExampleGroup<Self>>;
|
||||
|
||||
fn custom_example(_cx: &WindowContext) -> impl Into<Option<AnyElement>> {
|
||||
None::<AnyElement>
|
||||
}
|
||||
|
||||
fn component_previews(cx: &WindowContext) -> Vec<AnyElement> {
|
||||
Self::examples(cx)
|
||||
.into_iter()
|
||||
.map(|example| Self::render_example_group(example))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn render_component_previews(cx: &WindowContext) -> AnyElement {
|
||||
let title = Self::title();
|
||||
let (source, title) = title
|
||||
.rsplit_once("::")
|
||||
.map_or((None, title), |(s, t)| (Some(s), t));
|
||||
let description = Self::description().into();
|
||||
|
||||
v_flex()
|
||||
.w_full()
|
||||
.gap_6()
|
||||
.p_4()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_md()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(Headline::new(title).size(HeadlineSize::Small))
|
||||
.when_some(source, |this, source| {
|
||||
this.child(Label::new(format!("({})", source)).color(Color::Muted))
|
||||
}),
|
||||
)
|
||||
.when_some(description, |this, description| {
|
||||
this.child(
|
||||
div()
|
||||
.text_ui_sm(cx)
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.max_w(px(600.0))
|
||||
.child(description),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.when_some(Self::custom_example(cx).into(), |this, custom_example| {
|
||||
this.child(custom_example)
|
||||
})
|
||||
.children(Self::component_previews(cx))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_example_group(group: ComponentExampleGroup<Self>) -> AnyElement {
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.when(group.grow, |this| this.w_full().flex_1())
|
||||
.when_some(group.title, |this, title| {
|
||||
this.child(Label::new(title).size(LabelSize::Small))
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.gap_6()
|
||||
.children(group.examples.into_iter().map(Self::render_example))
|
||||
.into_any_element(),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_example(example: ComponentExample<Self>) -> AnyElement {
|
||||
let base = div().flex();
|
||||
|
||||
let base = match Self::example_label_side() {
|
||||
ExampleLabelSide::Right => base.flex_row(),
|
||||
ExampleLabelSide::Left => base.flex_row_reverse(),
|
||||
ExampleLabelSide::Bottom => base.flex_col(),
|
||||
ExampleLabelSide::Top => base.flex_col_reverse(),
|
||||
};
|
||||
|
||||
base.gap_1()
|
||||
.when(example.grow, |this| this.flex_1())
|
||||
.child(example.element)
|
||||
.child(
|
||||
Label::new(example.variant_name)
|
||||
.size(LabelSize::XSmall)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
/// A single example of a component.
|
||||
pub struct ComponentExample<T> {
|
||||
variant_name: SharedString,
|
||||
element: T,
|
||||
grow: bool,
|
||||
}
|
||||
|
||||
impl<T> ComponentExample<T> {
|
||||
/// Create a new example with the given variant name and example value.
|
||||
pub fn new(variant_name: impl Into<SharedString>, example: T) -> Self {
|
||||
Self {
|
||||
variant_name: variant_name.into(),
|
||||
element: example,
|
||||
grow: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the example to grow to fill the available horizontal space.
|
||||
pub fn grow(mut self) -> Self {
|
||||
self.grow = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A group of component examples.
|
||||
pub struct ComponentExampleGroup<T> {
|
||||
pub title: Option<SharedString>,
|
||||
pub examples: Vec<ComponentExample<T>>,
|
||||
pub grow: bool,
|
||||
}
|
||||
|
||||
impl<T> ComponentExampleGroup<T> {
|
||||
/// Create a new group of examples with the given title.
|
||||
pub fn new(examples: Vec<ComponentExample<T>>) -> Self {
|
||||
Self {
|
||||
title: None,
|
||||
examples,
|
||||
grow: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new group of examples with the given title.
|
||||
pub fn with_title(title: impl Into<SharedString>, examples: Vec<ComponentExample<T>>) -> Self {
|
||||
Self {
|
||||
title: Some(title.into()),
|
||||
examples,
|
||||
grow: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the group to grow to fill the available horizontal space.
|
||||
pub fn grow(mut self) -> Self {
|
||||
self.grow = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a single example
|
||||
pub fn single_example<T>(variant_name: impl Into<SharedString>, example: T) -> ComponentExample<T> {
|
||||
ComponentExample::new(variant_name, example)
|
||||
}
|
||||
|
||||
/// Create a group of examples without a title
|
||||
pub fn example_group<T>(examples: Vec<ComponentExample<T>>) -> ComponentExampleGroup<T> {
|
||||
ComponentExampleGroup::new(examples)
|
||||
}
|
||||
|
||||
/// Create a group of examples with a title
|
||||
pub fn example_group_with_title<T>(
|
||||
title: impl Into<SharedString>,
|
||||
examples: Vec<ComponentExample<T>>,
|
||||
) -> ComponentExampleGroup<T> {
|
||||
ComponentExampleGroup::with_title(title, examples)
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
//! UI-related utilities
|
||||
|
||||
mod color_contrast;
|
||||
mod component_preview;
|
||||
mod format_distance;
|
||||
mod search_input;
|
||||
mod with_rem_size;
|
||||
|
||||
pub use color_contrast::*;
|
||||
pub use component_preview::*;
|
||||
pub use format_distance::*;
|
||||
pub use search_input::*;
|
||||
pub use with_rem_size::*;
|
||||
|
||||
119
crates/ui/src/utils/component_preview.rs
Normal file
119
crates/ui/src/utils/component_preview.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use crate::prelude::*;
|
||||
use gpui::{AnyElement, Axis};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// A component preview with a label and children.
|
||||
#[derive(IntoElement)]
|
||||
pub struct ComponentPreview {
|
||||
label: Option<SharedString>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl ComponentPreview {
|
||||
/// Creates a new ComponentPreview with the specified label side.
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
label: Some(label.into()),
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new ComponentPreview with no label.
|
||||
pub fn no_label() -> Self {
|
||||
Self {
|
||||
label: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the label for the ComponentPreview.
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ComponentPreview {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
v_flex()
|
||||
.flex_none()
|
||||
.gap_3()
|
||||
.when_some(self.label, |this, label| {
|
||||
this.child(Label::new(label).color(Color::Muted))
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.flex_none()
|
||||
.children(self.children),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for ComponentPreview {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||
self.children.extend(elements)
|
||||
}
|
||||
}
|
||||
|
||||
/// A group of component previews.
|
||||
#[derive(IntoElement)]
|
||||
pub struct ComponentPreviewGroup {
|
||||
direction: Axis,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl ComponentPreviewGroup {
|
||||
/// Creates a new ComponentPreviewGroup.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
direction: Axis::Horizontal,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lay out the previews horizontally.
|
||||
pub fn horizontal(mut self) -> Self {
|
||||
self.direction = Axis::Horizontal;
|
||||
self
|
||||
}
|
||||
|
||||
/// Lay out the previews vertically.
|
||||
pub fn vertical(mut self) -> Self {
|
||||
self.direction = Axis::Vertical;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for ComponentPreviewGroup {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||
self.children.extend(elements)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ComponentPreviewGroup {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
let group = match self.direction {
|
||||
Axis::Horizontal => h_group(),
|
||||
Axis::Vertical => v_group(),
|
||||
};
|
||||
|
||||
group
|
||||
.size_full()
|
||||
.items_start()
|
||||
.outset()
|
||||
.gap_3()
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [ComponentPreview]
|
||||
pub fn component_preview(label: impl Into<SharedString>) -> ComponentPreview {
|
||||
ComponentPreview::new(label)
|
||||
}
|
||||
|
||||
/// Creates a new [ComponentPreviewGroup]
|
||||
pub fn component_preview_group() -> ComponentPreviewGroup {
|
||||
ComponentPreviewGroup::new()
|
||||
}
|
||||
@@ -17,3 +17,4 @@ proc-macro2 = "1.0.66"
|
||||
quote = "1.0.9"
|
||||
syn = { version = "1.0.72", features = ["full", "extra-traits"] }
|
||||
convert_case.workspace = true
|
||||
component_system.workspace = true
|
||||
|
||||
81
crates/ui_macros/src/derive_into_component.rs
Normal file
81
crates/ui_macros/src/derive_into_component.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput, Lit, Meta, MetaList, MetaNameValue, NestedMeta};
|
||||
|
||||
pub fn derive_into_component(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let mut scope_val = None;
|
||||
let mut description_val = None;
|
||||
|
||||
for attr in &input.attrs {
|
||||
if attr.path.is_ident("component") {
|
||||
if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() {
|
||||
for item in nested {
|
||||
if let NestedMeta::Meta(Meta::NameValue(MetaNameValue {
|
||||
path,
|
||||
lit: Lit::Str(s),
|
||||
..
|
||||
})) = item
|
||||
{
|
||||
let ident = path.get_ident().map(|i| i.to_string()).unwrap_or_default();
|
||||
if ident == "scope" {
|
||||
scope_val = Some(s.value());
|
||||
} else if ident == "description" {
|
||||
description_val = Some(s.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let name = &input.ident;
|
||||
|
||||
let scope_impl = if let Some(s) = scope_val {
|
||||
quote! {
|
||||
fn scope() -> Option<&'static str> {
|
||||
Some(#s)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
fn scope() -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let description_impl = if let Some(desc) = description_val {
|
||||
quote! {
|
||||
fn description() -> Option<&'static str> {
|
||||
Some(#desc)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let expanded = quote! {
|
||||
impl component_system::Component for #name {
|
||||
#scope_impl
|
||||
|
||||
fn name() -> &'static str {
|
||||
stringify!(#name)
|
||||
}
|
||||
|
||||
#description_impl
|
||||
}
|
||||
|
||||
#[linkme::distributed_slice(component_system::__ALL_COMPONENTS)]
|
||||
fn __register_component() {
|
||||
component_system::register_component::<#name>();
|
||||
}
|
||||
|
||||
#[linkme::distributed_slice(component_system::__ALL_PREVIEWS)]
|
||||
fn __register_preview() {
|
||||
component_system::register_preview::<#name>();
|
||||
}
|
||||
};
|
||||
|
||||
expanded.into()
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
mod derive_into_component;
|
||||
mod derive_path_str;
|
||||
mod dynamic_spacing;
|
||||
|
||||
@@ -58,3 +59,27 @@ pub fn path_str(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pub fn derive_dynamic_spacing(input: TokenStream) -> TokenStream {
|
||||
dynamic_spacing::derive_spacing(input)
|
||||
}
|
||||
|
||||
/// Derives the `IntoComponent` trait for a struct.
|
||||
///
|
||||
/// This macro generates implementations for the `Component` trait and associated
|
||||
/// registration functions for the component system.
|
||||
///
|
||||
/// # Attributes
|
||||
///
|
||||
/// - `#[component(scope = "...")]`: Required. Specifies the scope of the component.
|
||||
/// - `#[component(description = "...")]`: Optional. Provides a description for the component.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use ui_macros::IntoComponent;
|
||||
///
|
||||
/// #[derive(IntoComponent)]
|
||||
/// #[component(scope = "my_scope", description = "A sample component")]
|
||||
/// struct MyComponent;
|
||||
/// ```
|
||||
#[proc_macro_derive(IntoComponent, attributes(component))]
|
||||
pub fn derive_into_component(input: TokenStream) -> TokenStream {
|
||||
derive_into_component::derive_into_component(input)
|
||||
}
|
||||
|
||||
@@ -5,11 +5,10 @@ use ui::ViewContext;
|
||||
|
||||
use crate::{motion::Motion, state::Mode, Vim};
|
||||
|
||||
actions!(vim, [HelixNormalAfter, HelixDelete]);
|
||||
actions!(vim, [HelixNormalAfter]);
|
||||
|
||||
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
|
||||
Vim::action(editor, cx, Vim::helix_normal_after);
|
||||
Vim::action(editor, cx, Vim::helix_delete);
|
||||
}
|
||||
|
||||
impl Vim {
|
||||
@@ -227,27 +226,6 @@ impl Vim {
|
||||
_ => self.helix_move_and_collapse(motion, times, cx),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn helix_delete(&mut self, _: &HelixDelete, cx: &mut ViewContext<Self>) {
|
||||
self.store_visual_marks(cx);
|
||||
self.update_editor(cx, |vim, editor, cx| {
|
||||
// Fixup selections so they have helix's semantics.
|
||||
// Specifically:
|
||||
// - Make sure that each cursor acts as a 1 character wide selection
|
||||
editor.transact(cx, |editor, cx| {
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
if selection.is_empty() && !selection.reversed {
|
||||
selection.end = movement::right(map, selection.end);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
vim.copy_selections_content(editor, false, cx);
|
||||
editor.insert("", cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -290,84 +268,4 @@ mod test {
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_delete(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
|
||||
// test delete a selection
|
||||
cx.set_state(
|
||||
indoc! {"
|
||||
The qu«ick ˇ»brown
|
||||
fox jumps over
|
||||
the lazy dog."},
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
|
||||
cx.simulate_keystrokes("d");
|
||||
|
||||
cx.assert_state(
|
||||
indoc! {"
|
||||
The quˇbrown
|
||||
fox jumps over
|
||||
the lazy dog."},
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
|
||||
// test deleting a single character
|
||||
cx.simulate_keystrokes("d");
|
||||
|
||||
cx.assert_state(
|
||||
indoc! {"
|
||||
The quˇrown
|
||||
fox jumps over
|
||||
the lazy dog."},
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_delete_character_end_of_line(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
|
||||
cx.set_state(
|
||||
indoc! {"
|
||||
The quick brownˇ
|
||||
fox jumps over
|
||||
the lazy dog."},
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
|
||||
cx.simulate_keystrokes("d");
|
||||
|
||||
cx.assert_state(
|
||||
indoc! {"
|
||||
The quick brownˇfox jumps over
|
||||
the lazy dog."},
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_delete_character_end_of_buffer(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
|
||||
cx.set_state(
|
||||
indoc! {"
|
||||
The quick brown
|
||||
fox jumps over
|
||||
the lazy dog.ˇ"},
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
|
||||
cx.simulate_keystrokes("d");
|
||||
|
||||
cx.assert_state(
|
||||
indoc! {"
|
||||
The quick brown
|
||||
fox jumps over
|
||||
the lazy dog.ˇ"},
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../livekit-rust-sdks/webrtc-sys
|
||||
@@ -24,16 +24,19 @@ test-support = [
|
||||
"gpui/test-support",
|
||||
"fs/test-support",
|
||||
]
|
||||
livekit-macos = ["call/livekit-macos"]
|
||||
livekit-cross-platform = ["call/livekit-cross-platform"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
any_vec.workspace = true
|
||||
anyhow.workspace = true
|
||||
async-recursion.workspace = true
|
||||
bincode = "1.2.1"
|
||||
call.workspace = true
|
||||
client.workspace = true
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
component_system.workspace = true
|
||||
db.workspace = true
|
||||
derive_more.workspace = true
|
||||
fs.workspace = true
|
||||
@@ -47,7 +50,6 @@ node_runtime.workspace = true
|
||||
parking_lot.workspace = true
|
||||
postage.workspace = true
|
||||
project.workspace = true
|
||||
task.workspace = true
|
||||
remote.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
@@ -56,11 +58,12 @@ session.workspace = true
|
||||
settings.workspace = true
|
||||
smallvec.workspace = true
|
||||
sqlez.workspace = true
|
||||
strum.workspace = true
|
||||
task.workspace = true
|
||||
theme.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
uuid.workspace = true
|
||||
strum.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
call = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -64,9 +64,9 @@ pub enum ClosePosition {
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShowDiagnostics {
|
||||
#[default]
|
||||
Off,
|
||||
Errors,
|
||||
#[default]
|
||||
All,
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ pub struct ItemSettingsContent {
|
||||
/// Which files containing diagnostic errors/warnings to mark in the tabs.
|
||||
/// This setting can take the following three values:
|
||||
///
|
||||
/// Default: off
|
||||
/// Default: all
|
||||
show_diagnostics: Option<ShowDiagnostics>,
|
||||
/// Whether to always show the close button on tabs.
|
||||
///
|
||||
|
||||
@@ -306,7 +306,6 @@ pub struct Pane {
|
||||
pub split_item_context_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||
pinned_tab_count: usize,
|
||||
diagnostics: HashMap<ProjectPath, DiagnosticSeverity>,
|
||||
zoom_out_on_close: bool,
|
||||
}
|
||||
|
||||
pub struct ActivationHistoryEntry {
|
||||
@@ -508,7 +507,6 @@ impl Pane {
|
||||
new_item_context_menu_handle: Default::default(),
|
||||
pinned_tab_count: 0,
|
||||
diagnostics: Default::default(),
|
||||
zoom_out_on_close: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1588,7 +1586,7 @@ impl Pane {
|
||||
.remove(&item.item_id());
|
||||
}
|
||||
|
||||
if self.zoom_out_on_close && self.items.is_empty() && close_pane_if_empty && self.zoomed {
|
||||
if self.items.is_empty() && close_pane_if_empty && self.zoomed {
|
||||
cx.emit(Event::ZoomOut);
|
||||
}
|
||||
|
||||
@@ -2002,8 +2000,12 @@ impl Pane {
|
||||
|
||||
let icon = if decorated_icon.is_none() {
|
||||
match item_diagnostic {
|
||||
Some(&DiagnosticSeverity::ERROR) => None,
|
||||
Some(&DiagnosticSeverity::WARNING) => None,
|
||||
Some(&DiagnosticSeverity::ERROR) => {
|
||||
Some(Icon::new(IconName::X).color(Color::Error))
|
||||
}
|
||||
Some(&DiagnosticSeverity::WARNING) => {
|
||||
Some(Icon::new(IconName::Triangle).color(Color::Warning))
|
||||
}
|
||||
_ => item.tab_icon(cx).map(|icon| icon.color(Color::Muted)),
|
||||
}
|
||||
.map(|icon| icon.size(IconSize::Small))
|
||||
@@ -2140,17 +2142,13 @@ impl Pane {
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.children(
|
||||
std::iter::once(if let Some(decorated_icon) = decorated_icon {
|
||||
Some(div().child(decorated_icon.into_any_element()))
|
||||
} else if let Some(icon) = icon {
|
||||
Some(div().child(icon.into_any_element()))
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.flatten(),
|
||||
)
|
||||
.child(if let Some(decorated_icon) = decorated_icon {
|
||||
div().child(decorated_icon.into_any_element())
|
||||
} else if let Some(icon) = icon {
|
||||
div().mt(px(2.5)).child(icon.into_any_element())
|
||||
} else {
|
||||
div()
|
||||
})
|
||||
.child(label),
|
||||
);
|
||||
|
||||
@@ -2789,10 +2787,6 @@ impl Pane {
|
||||
pub fn drag_split_direction(&self) -> Option<SplitDirection> {
|
||||
self.drag_split_direction
|
||||
}
|
||||
|
||||
pub fn set_zoom_out_on_close(&mut self, zoom_out_on_close: bool) {
|
||||
self.zoom_out_on_close = zoom_out_on_close;
|
||||
}
|
||||
}
|
||||
|
||||
impl FocusableView for Pane {
|
||||
|
||||
@@ -1,11 +1,282 @@
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
#[cfg(any(
|
||||
all(
|
||||
target_os = "macos",
|
||||
feature = "livekit-cross-platform",
|
||||
not(feature = "livekit-macos"),
|
||||
),
|
||||
all(not(target_os = "macos"), feature = "livekit-cross-platform"),
|
||||
))]
|
||||
mod cross_platform {
|
||||
use crate::{
|
||||
item::{Item, ItemEvent},
|
||||
ItemNavHistory, WorkspaceId,
|
||||
};
|
||||
use call::{RemoteVideoTrack, RemoteVideoTrackView};
|
||||
use client::{proto::PeerId, User};
|
||||
use gpui::{
|
||||
div, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
|
||||
ParentElement, Render, SharedString, Styled, View, ViewContext, VisualContext,
|
||||
WindowContext,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use ui::{prelude::*, Icon, IconName};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub use macos::*;
|
||||
pub enum Event {
|
||||
Close,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
mod cross_platform;
|
||||
pub struct SharedScreen {
|
||||
pub peer_id: PeerId,
|
||||
user: Arc<User>,
|
||||
nav_history: Option<ItemNavHistory>,
|
||||
view: View<RemoteVideoTrackView>,
|
||||
focus: FocusHandle,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
impl SharedScreen {
|
||||
pub fn new(
|
||||
track: RemoteVideoTrack,
|
||||
peer_id: PeerId,
|
||||
user: Arc<User>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let view = cx.new_view(|cx| RemoteVideoTrackView::new(track.clone(), cx));
|
||||
cx.subscribe(&view, |_, _, ev, cx| match ev {
|
||||
call::RemoteVideoTrackViewEvent::Close => cx.emit(Event::Close),
|
||||
})
|
||||
.detach();
|
||||
Self {
|
||||
view,
|
||||
peer_id,
|
||||
user,
|
||||
nav_history: Default::default(),
|
||||
focus: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<Event> for SharedScreen {}
|
||||
|
||||
impl FocusableView for SharedScreen {
|
||||
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
||||
self.focus.clone()
|
||||
}
|
||||
}
|
||||
impl Render for SharedScreen {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.track_focus(&self.focus)
|
||||
.key_context("SharedScreen")
|
||||
.size_full()
|
||||
.child(self.view.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for SharedScreen {
|
||||
type Event = Event;
|
||||
|
||||
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
|
||||
Some(format!("{}'s screen", self.user.github_login).into())
|
||||
}
|
||||
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if let Some(nav_history) = self.nav_history.as_mut() {
|
||||
nav_history.push::<()>(None, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
|
||||
Some(Icon::new(IconName::Screen))
|
||||
}
|
||||
|
||||
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
|
||||
Some(format!("{}'s screen", self.user.github_login).into())
|
||||
}
|
||||
|
||||
fn telemetry_event_text(&self) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
|
||||
self.nav_history = Some(history);
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: Option<WorkspaceId>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<View<Self>> {
|
||||
Some(cx.new_view(|cx| Self {
|
||||
view: self.view.update(cx, |view, cx| view.clone(cx)),
|
||||
peer_id: self.peer_id,
|
||||
user: self.user.clone(),
|
||||
nav_history: Default::default(),
|
||||
focus: cx.focus_handle(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
|
||||
match event {
|
||||
Event::Close => f(ItemEvent::CloseItem),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
all(
|
||||
target_os = "macos",
|
||||
feature = "livekit-cross-platform",
|
||||
not(feature = "livekit-macos"),
|
||||
),
|
||||
all(not(target_os = "macos"), feature = "livekit-cross-platform"),
|
||||
))]
|
||||
pub use cross_platform::*;
|
||||
|
||||
#[cfg(any(
|
||||
all(target_os = "macos", feature = "livekit-macos"),
|
||||
all(
|
||||
not(target_os = "macos"),
|
||||
feature = "livekit-macos",
|
||||
not(feature = "livekit-cross-platform")
|
||||
)
|
||||
))]
|
||||
mod macos {
|
||||
use crate::{
|
||||
item::{Item, ItemEvent},
|
||||
ItemNavHistory, WorkspaceId,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use call::participant::{Frame, RemoteVideoTrack};
|
||||
use client::{proto::PeerId, User};
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
div, surface, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
|
||||
ParentElement, Render, SharedString, Styled, Task, View, ViewContext, VisualContext,
|
||||
WindowContext,
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
use ui::{prelude::*, Icon, IconName};
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
}
|
||||
|
||||
pub struct SharedScreen {
|
||||
track: Weak<RemoteVideoTrack>,
|
||||
frame: Option<Frame>,
|
||||
pub peer_id: PeerId,
|
||||
user: Arc<User>,
|
||||
nav_history: Option<ItemNavHistory>,
|
||||
_maintain_frame: Task<Result<()>>,
|
||||
focus: FocusHandle,
|
||||
}
|
||||
|
||||
impl SharedScreen {
|
||||
pub fn new(
|
||||
track: Arc<RemoteVideoTrack>,
|
||||
peer_id: PeerId,
|
||||
user: Arc<User>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
cx.focus_handle();
|
||||
let mut frames = track.frames();
|
||||
Self {
|
||||
track: Arc::downgrade(&track),
|
||||
frame: None,
|
||||
peer_id,
|
||||
user,
|
||||
nav_history: Default::default(),
|
||||
_maintain_frame: cx.spawn(|this, mut cx| async move {
|
||||
while let Some(frame) = frames.next().await {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.frame = Some(frame);
|
||||
cx.notify();
|
||||
})?;
|
||||
}
|
||||
this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
|
||||
Ok(())
|
||||
}),
|
||||
focus: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<Event> for SharedScreen {}
|
||||
|
||||
impl FocusableView for SharedScreen {
|
||||
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
||||
self.focus.clone()
|
||||
}
|
||||
}
|
||||
impl Render for SharedScreen {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.track_focus(&self.focus)
|
||||
.key_context("SharedScreen")
|
||||
.size_full()
|
||||
.children(
|
||||
self.frame
|
||||
.as_ref()
|
||||
.map(|frame| surface(frame.image()).size_full()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for SharedScreen {
|
||||
type Event = Event;
|
||||
|
||||
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
|
||||
Some(format!("{}'s screen", self.user.github_login).into())
|
||||
}
|
||||
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if let Some(nav_history) = self.nav_history.as_mut() {
|
||||
nav_history.push::<()>(None, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
|
||||
Some(Icon::new(IconName::Screen))
|
||||
}
|
||||
|
||||
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
|
||||
Some(format!("{}'s screen", self.user.github_login).into())
|
||||
}
|
||||
|
||||
fn telemetry_event_text(&self) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
|
||||
self.nav_history = Some(history);
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: Option<WorkspaceId>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<View<Self>> {
|
||||
let track = self.track.upgrade()?;
|
||||
Some(cx.new_view(|cx| Self::new(track, self.peer_id, self.user.clone(), cx)))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
|
||||
match event {
|
||||
Event::Close => f(ItemEvent::CloseItem),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
all(target_os = "macos", feature = "livekit-macos"),
|
||||
all(
|
||||
not(target_os = "macos"),
|
||||
feature = "livekit-macos",
|
||||
not(feature = "livekit-cross-platform")
|
||||
)
|
||||
))]
|
||||
pub use macos::*;
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
use crate::{
|
||||
item::{Item, ItemEvent},
|
||||
ItemNavHistory, WorkspaceId,
|
||||
};
|
||||
use call::{RemoteVideoTrack, RemoteVideoTrackView};
|
||||
use client::{proto::PeerId, User};
|
||||
use gpui::{
|
||||
div, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement, ParentElement,
|
||||
Render, SharedString, Styled, View, ViewContext, VisualContext, WindowContext,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use ui::{prelude::*, Icon, IconName};
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
}
|
||||
|
||||
pub struct SharedScreen {
|
||||
pub peer_id: PeerId,
|
||||
user: Arc<User>,
|
||||
nav_history: Option<ItemNavHistory>,
|
||||
view: View<RemoteVideoTrackView>,
|
||||
focus: FocusHandle,
|
||||
}
|
||||
|
||||
impl SharedScreen {
|
||||
pub fn new(
|
||||
track: RemoteVideoTrack,
|
||||
peer_id: PeerId,
|
||||
user: Arc<User>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let view = cx.new_view(|cx| RemoteVideoTrackView::new(track.clone(), cx));
|
||||
cx.subscribe(&view, |_, _, ev, cx| match ev {
|
||||
call::RemoteVideoTrackViewEvent::Close => cx.emit(Event::Close),
|
||||
})
|
||||
.detach();
|
||||
Self {
|
||||
view,
|
||||
peer_id,
|
||||
user,
|
||||
nav_history: Default::default(),
|
||||
focus: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<Event> for SharedScreen {}
|
||||
|
||||
impl FocusableView for SharedScreen {
|
||||
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
||||
self.focus.clone()
|
||||
}
|
||||
}
|
||||
impl Render for SharedScreen {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.track_focus(&self.focus)
|
||||
.key_context("SharedScreen")
|
||||
.size_full()
|
||||
.child(self.view.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for SharedScreen {
|
||||
type Event = Event;
|
||||
|
||||
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
|
||||
Some(format!("{}'s screen", self.user.github_login).into())
|
||||
}
|
||||
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if let Some(nav_history) = self.nav_history.as_mut() {
|
||||
nav_history.push::<()>(None, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
|
||||
Some(Icon::new(IconName::Screen))
|
||||
}
|
||||
|
||||
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
|
||||
Some(format!("{}'s screen", self.user.github_login).into())
|
||||
}
|
||||
|
||||
fn telemetry_event_text(&self) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
|
||||
self.nav_history = Some(history);
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: Option<WorkspaceId>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<View<Self>> {
|
||||
Some(cx.new_view(|cx| Self {
|
||||
view: self.view.update(cx, |view, cx| view.clone(cx)),
|
||||
peer_id: self.peer_id,
|
||||
user: self.user.clone(),
|
||||
nav_history: Default::default(),
|
||||
focus: cx.focus_handle(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
|
||||
match event {
|
||||
Event::Close => f(ItemEvent::CloseItem),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
use crate::{
|
||||
item::{Item, ItemEvent},
|
||||
ItemNavHistory, WorkspaceId,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use call::participant::{Frame, RemoteVideoTrack};
|
||||
use client::{proto::PeerId, User};
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
div, surface, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
|
||||
ParentElement, Render, SharedString, Styled, Task, View, ViewContext, VisualContext,
|
||||
WindowContext,
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
use ui::{prelude::*, Icon, IconName};
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
}
|
||||
|
||||
pub struct SharedScreen {
|
||||
track: Weak<RemoteVideoTrack>,
|
||||
frame: Option<Frame>,
|
||||
pub peer_id: PeerId,
|
||||
user: Arc<User>,
|
||||
nav_history: Option<ItemNavHistory>,
|
||||
_maintain_frame: Task<Result<()>>,
|
||||
focus: FocusHandle,
|
||||
}
|
||||
|
||||
impl SharedScreen {
|
||||
pub fn new(
|
||||
track: Arc<RemoteVideoTrack>,
|
||||
peer_id: PeerId,
|
||||
user: Arc<User>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
cx.focus_handle();
|
||||
let mut frames = track.frames();
|
||||
Self {
|
||||
track: Arc::downgrade(&track),
|
||||
frame: None,
|
||||
peer_id,
|
||||
user,
|
||||
nav_history: Default::default(),
|
||||
_maintain_frame: cx.spawn(|this, mut cx| async move {
|
||||
while let Some(frame) = frames.next().await {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.frame = Some(frame);
|
||||
cx.notify();
|
||||
})?;
|
||||
}
|
||||
this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
|
||||
Ok(())
|
||||
}),
|
||||
focus: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<Event> for SharedScreen {}
|
||||
|
||||
impl FocusableView for SharedScreen {
|
||||
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
||||
self.focus.clone()
|
||||
}
|
||||
}
|
||||
impl Render for SharedScreen {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.track_focus(&self.focus)
|
||||
.key_context("SharedScreen")
|
||||
.size_full()
|
||||
.children(
|
||||
self.frame
|
||||
.as_ref()
|
||||
.map(|frame| surface(frame.image()).size_full()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for SharedScreen {
|
||||
type Event = Event;
|
||||
|
||||
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
|
||||
Some(format!("{}'s screen", self.user.github_login).into())
|
||||
}
|
||||
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if let Some(nav_history) = self.nav_history.as_mut() {
|
||||
nav_history.push::<()>(None, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
|
||||
Some(Icon::new(IconName::Screen))
|
||||
}
|
||||
|
||||
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
|
||||
Some(format!("{}'s screen", self.user.github_login).into())
|
||||
}
|
||||
|
||||
fn telemetry_event_text(&self) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
|
||||
self.nav_history = Some(history);
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: Option<WorkspaceId>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<View<Self>> {
|
||||
let track = self.track.upgrade()?;
|
||||
Some(cx.new_view(|cx| Self::new(track, self.peer_id, self.user.clone(), cx)))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
|
||||
match event {
|
||||
Event::Close => f(ItemEvent::CloseItem),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
#![allow(unused, dead_code)]
|
||||
use gpui::{actions, hsla, AnyElement, AppContext, EventEmitter, FocusHandle, FocusableView, Hsla};
|
||||
use gpui::{actions, AppContext, EventEmitter, FocusHandle, FocusableView, Hsla};
|
||||
use strum::IntoEnumIterator;
|
||||
use theme::all_theme_colors;
|
||||
use ui::{
|
||||
element_cell, prelude::*, string_cell, utils::calculate_contrast_ratio, AudioStatus,
|
||||
Availability, Avatar, AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike,
|
||||
Checkbox, CheckboxWithLabel, ContentGroup, DecoratedIcon, ElevationIndex, Facepile,
|
||||
IconDecoration, Indicator, Table, TintColor, Tooltip,
|
||||
prelude::*, utils::calculate_contrast_ratio, AudioStatus, Availability, Avatar,
|
||||
AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike, ElevationIndex, Facepile,
|
||||
TintColor, Tooltip,
|
||||
};
|
||||
|
||||
use crate::{Item, Workspace};
|
||||
@@ -27,7 +25,6 @@ pub fn init(cx: &mut AppContext) {
|
||||
enum ThemePreviewPage {
|
||||
Overview,
|
||||
Typography,
|
||||
Components,
|
||||
}
|
||||
|
||||
impl ThemePreviewPage {
|
||||
@@ -35,7 +32,6 @@ impl ThemePreviewPage {
|
||||
match self {
|
||||
Self::Overview => "Overview",
|
||||
Self::Typography => "Typography",
|
||||
Self::Components => "Components",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,7 +57,6 @@ impl ThemePreview {
|
||||
match page {
|
||||
ThemePreviewPage::Overview => self.render_overview_page(cx).into_any_element(),
|
||||
ThemePreviewPage::Typography => self.render_typography_page(cx).into_any_element(),
|
||||
ThemePreviewPage::Components => self.render_components_page(cx).into_any_element(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,154 +96,11 @@ impl Item for ThemePreview {
|
||||
}
|
||||
}
|
||||
|
||||
const AVATAR_URL: &str = "https://avatars.githubusercontent.com/u/1714999?v=4";
|
||||
|
||||
impl ThemePreview {
|
||||
fn preview_bg(cx: &WindowContext) -> Hsla {
|
||||
cx.theme().colors().editor_background
|
||||
}
|
||||
|
||||
fn render_avatars(&self, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
Headline::new("Avatars")
|
||||
.size(HeadlineSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.items_start()
|
||||
.gap_4()
|
||||
.child(Avatar::new(AVATAR_URL).size(px(24.)))
|
||||
.child(Avatar::new(AVATAR_URL).size(px(24.)).grayscale(true))
|
||||
.child(
|
||||
Avatar::new(AVATAR_URL)
|
||||
.size(px(24.))
|
||||
.indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)),
|
||||
)
|
||||
.child(
|
||||
Avatar::new(AVATAR_URL)
|
||||
.size(px(24.))
|
||||
.indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)),
|
||||
)
|
||||
.child(
|
||||
Avatar::new(AVATAR_URL)
|
||||
.size(px(24.))
|
||||
.indicator(AvatarAvailabilityIndicator::new(Availability::Free)),
|
||||
)
|
||||
.child(
|
||||
Avatar::new(AVATAR_URL)
|
||||
.size(px(24.))
|
||||
.indicator(AvatarAvailabilityIndicator::new(Availability::Free)),
|
||||
)
|
||||
.child(
|
||||
Facepile::empty()
|
||||
.child(
|
||||
Avatar::new(AVATAR_URL)
|
||||
.border_color(Self::preview_bg(cx))
|
||||
.size(px(22.))
|
||||
.into_any_element(),
|
||||
)
|
||||
.child(
|
||||
Avatar::new(AVATAR_URL)
|
||||
.border_color(Self::preview_bg(cx))
|
||||
.size(px(22.))
|
||||
.into_any_element(),
|
||||
)
|
||||
.child(
|
||||
Avatar::new(AVATAR_URL)
|
||||
.border_color(Self::preview_bg(cx))
|
||||
.size(px(22.))
|
||||
.into_any_element(),
|
||||
)
|
||||
.child(
|
||||
Avatar::new(AVATAR_URL)
|
||||
.border_color(Self::preview_bg(cx))
|
||||
.size(px(22.))
|
||||
.into_any_element(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_buttons(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
Headline::new("Buttons")
|
||||
.size(HeadlineSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.items_start()
|
||||
.gap_px()
|
||||
.child(
|
||||
IconButton::new("icon_button_transparent", IconName::Check)
|
||||
.style(ButtonStyle::Transparent),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("icon_button_subtle", IconName::Check)
|
||||
.style(ButtonStyle::Subtle),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("icon_button_filled", IconName::Check)
|
||||
.style(ButtonStyle::Filled),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("icon_button_selected_accent", IconName::Check)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.selected(true),
|
||||
)
|
||||
.child(IconButton::new("icon_button_selected", IconName::Check).selected(true))
|
||||
.child(
|
||||
IconButton::new("icon_button_positive", IconName::Check)
|
||||
.style(ButtonStyle::Tinted(TintColor::Positive)),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("icon_button_warning", IconName::Check)
|
||||
.style(ButtonStyle::Tinted(TintColor::Warning)),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("icon_button_negative", IconName::Check)
|
||||
.style(ButtonStyle::Tinted(TintColor::Negative)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_px()
|
||||
.child(
|
||||
Button::new("button_transparent", "Transparent")
|
||||
.style(ButtonStyle::Transparent),
|
||||
)
|
||||
.child(Button::new("button_subtle", "Subtle").style(ButtonStyle::Subtle))
|
||||
.child(Button::new("button_filled", "Filled").style(ButtonStyle::Filled))
|
||||
.child(
|
||||
Button::new("button_selected", "Selected")
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.selected(true),
|
||||
)
|
||||
.child(
|
||||
Button::new("button_selected_tinted", "Selected (Tinted)")
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.selected(true),
|
||||
)
|
||||
.child(
|
||||
Button::new("button_positive", "Tint::Positive")
|
||||
.style(ButtonStyle::Tinted(TintColor::Positive)),
|
||||
)
|
||||
.child(
|
||||
Button::new("button_warning", "Tint::Warning")
|
||||
.style(ButtonStyle::Tinted(TintColor::Warning)),
|
||||
)
|
||||
.child(
|
||||
Button::new("button_negative", "Tint::Negative")
|
||||
.style(ButtonStyle::Tinted(TintColor::Negative)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_text(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
let bg = layer.bg(cx);
|
||||
|
||||
@@ -410,8 +262,7 @@ impl ThemePreview {
|
||||
)
|
||||
}
|
||||
|
||||
fn render_colors(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
let bg = layer.bg(cx);
|
||||
fn render_colors(&self, _layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
let all_colors = all_theme_colors(cx);
|
||||
|
||||
v_flex()
|
||||
@@ -502,28 +353,6 @@ impl ThemePreview {
|
||||
)
|
||||
}
|
||||
|
||||
fn render_components_page(&self, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
let layer = ElevationIndex::Surface;
|
||||
|
||||
v_flex()
|
||||
.id("theme-preview-components")
|
||||
.overflow_scroll()
|
||||
.size_full()
|
||||
.gap_2()
|
||||
.child(ContentGroup::render_component_previews(cx))
|
||||
.child(IconDecoration::render_component_previews(cx))
|
||||
.child(DecoratedIcon::render_component_previews(cx))
|
||||
.child(Checkbox::render_component_previews(cx))
|
||||
.child(CheckboxWithLabel::render_component_previews(cx))
|
||||
.child(Facepile::render_component_previews(cx))
|
||||
.child(Button::render_component_previews(cx))
|
||||
.child(Indicator::render_component_previews(cx))
|
||||
.child(Icon::render_component_previews(cx))
|
||||
.child(Table::render_component_previews(cx))
|
||||
.child(self.render_avatars(cx))
|
||||
.child(self.render_buttons(layer, cx))
|
||||
}
|
||||
|
||||
fn render_page_nav(&self, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
h_flex()
|
||||
.id("theme-preview-nav")
|
||||
|
||||
@@ -143,12 +143,13 @@ actions!(
|
||||
FollowNextCollaborator,
|
||||
NewCenterTerminal,
|
||||
NewFile,
|
||||
NewFileSplitVertical,
|
||||
NewFileSplitHorizontal,
|
||||
NewFileSplitVertical,
|
||||
NewSearch,
|
||||
NewTerminal,
|
||||
NewWindow,
|
||||
Open,
|
||||
ComponentPreview,
|
||||
OpenInTerminal,
|
||||
ReloadActiveItem,
|
||||
SaveAs,
|
||||
@@ -323,6 +324,7 @@ pub fn init_settings(cx: &mut AppContext) {
|
||||
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||
init_settings(cx);
|
||||
notifications::init(cx);
|
||||
component_system::init();
|
||||
theme_preview::init(cx);
|
||||
|
||||
cx.on_action(Workspace::close_global);
|
||||
|
||||
@@ -37,6 +37,7 @@ collab_ui.workspace = true
|
||||
collections.workspace = true
|
||||
command_palette.workspace = true
|
||||
command_palette_hooks.workspace = true
|
||||
component_preview.workspace = true
|
||||
copilot.workspace = true
|
||||
db.workspace = true
|
||||
diagnostics.workspace = true
|
||||
@@ -69,7 +70,6 @@ language_tools.workspace = true
|
||||
languages = { workspace = true, features = ["load-grammars"] }
|
||||
libc.workspace = true
|
||||
log.workspace = true
|
||||
markdown.workspace = true
|
||||
markdown_preview.workspace = true
|
||||
menu.workspace = true
|
||||
mimalloc = { version = "0.1", optional = true }
|
||||
@@ -127,6 +127,12 @@ welcome.workspace = true
|
||||
workspace.workspace = true
|
||||
zed_actions.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
workspace = { workspace = true, features = ["livekit-macos"] }
|
||||
|
||||
[target.'cfg(not(target_os = "macos"))'.dependencies]
|
||||
workspace = { workspace = true, features = ["livekit-cross-platform"] }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
windows.workspace = true
|
||||
|
||||
|
||||
@@ -461,6 +461,7 @@ fn main() {
|
||||
feedback::init(cx);
|
||||
markdown_preview::init(cx);
|
||||
welcome::init(cx);
|
||||
component_preview::init(cx);
|
||||
settings_ui::init(cx);
|
||||
extensions_ui::init(cx);
|
||||
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
use gpui::{
|
||||
div, AppContext, EventEmitter, FocusHandle, FocusableView, FontWeight, InteractiveElement,
|
||||
IntoElement, ParentElement, PromptHandle, PromptLevel, PromptResponse, Refineable, Render,
|
||||
RenderablePromptHandle, Styled, TextStyleRefinement, View, ViewContext, VisualContext,
|
||||
WindowContext,
|
||||
IntoElement, ParentElement, PromptHandle, PromptLevel, PromptResponse, Render,
|
||||
RenderablePromptHandle, Styled, ViewContext, VisualContext, WindowContext,
|
||||
};
|
||||
use markdown::{Markdown, MarkdownStyle};
|
||||
use settings::Settings;
|
||||
use theme::ThemeSettings;
|
||||
use ui::{
|
||||
h_flex, v_flex, ActiveTheme, ButtonCommon, ButtonStyle, Clickable, ElevationIndex,
|
||||
FluentBuilder, LabelSize, TintColor,
|
||||
h_flex, v_flex, ButtonCommon, ButtonStyle, Clickable, ElevationIndex, FluentBuilder, LabelSize,
|
||||
TintColor,
|
||||
};
|
||||
use workspace::ui::StyledExt;
|
||||
|
||||
@@ -30,27 +28,10 @@ pub fn fallback_prompt_renderer(
|
||||
|cx| FallbackPromptRenderer {
|
||||
_level: level,
|
||||
message: message.to_string(),
|
||||
detail: detail.map(ToString::to_string),
|
||||
actions: actions.iter().map(ToString::to_string).collect(),
|
||||
focus: cx.focus_handle(),
|
||||
active_action_id: 0,
|
||||
detail: detail.filter(|text| !text.is_empty()).map(|text| {
|
||||
cx.new_view(|cx| {
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
let mut base_text_style = cx.text_style();
|
||||
base_text_style.refine(&TextStyleRefinement {
|
||||
font_family: Some(settings.ui_font.family.clone()),
|
||||
font_size: Some(settings.ui_font_size.into()),
|
||||
color: Some(ui::Color::Muted.color(cx)),
|
||||
..Default::default()
|
||||
});
|
||||
let markdown_style = MarkdownStyle {
|
||||
base_text_style,
|
||||
selection_background_color: { cx.theme().players().local().selection },
|
||||
..Default::default()
|
||||
};
|
||||
Markdown::new(text.to_string(), markdown_style, None, None, cx)
|
||||
})
|
||||
}),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -61,10 +42,10 @@ pub fn fallback_prompt_renderer(
|
||||
pub struct FallbackPromptRenderer {
|
||||
_level: PromptLevel,
|
||||
message: String,
|
||||
detail: Option<String>,
|
||||
actions: Vec<String>,
|
||||
focus: FocusHandle,
|
||||
active_action_id: usize,
|
||||
detail: Option<View<Markdown>>,
|
||||
}
|
||||
|
||||
impl FallbackPromptRenderer {
|
||||
@@ -130,11 +111,13 @@ impl Render for FallbackPromptRenderer {
|
||||
.child(self.message.clone())
|
||||
.text_color(ui::Color::Default.color(cx)),
|
||||
)
|
||||
.children(
|
||||
self.detail
|
||||
.clone()
|
||||
.map(|detail| div().w_full().text_xs().child(detail)),
|
||||
)
|
||||
.children(self.detail.clone().map(|detail| {
|
||||
div()
|
||||
.w_full()
|
||||
.text_xs()
|
||||
.text_color(ui::Color::Muted.color(cx))
|
||||
.child(detail)
|
||||
}))
|
||||
.child(h_flex().justify_end().gap_2().children(
|
||||
self.actions.iter().enumerate().rev().map(|(ix, action)| {
|
||||
ui::Button::new(ix, action.clone())
|
||||
|
||||
@@ -192,30 +192,6 @@ The Zed Assistant comes pre-configured to use the latest version for common mode
|
||||
|
||||
You must provide the model's Context Window in the `max_tokens` parameter, this can be found [OpenAI Model Docs](https://platform.openai.com/docs/models). OpenAI `o1` models should set `max_completion_tokens` as well to avoid incurring high reasoning token costs. Custom models will be listed in the model dropdown in the assistant panel.
|
||||
|
||||
### OpenAI API Compatible
|
||||
|
||||
Zed supports using OpenAI compatible APIs by specifying a custom `endpoint` and `available_models` for the OpenAI provider.
|
||||
|
||||
#### X.ai Grok
|
||||
|
||||
Example configuration for using X.ai Grok with Zed:
|
||||
|
||||
```json
|
||||
"language_models": {
|
||||
"openai": {
|
||||
"api_url": "https://api.x.ai/v1",
|
||||
"available_models": [
|
||||
{
|
||||
"name": "grok-beta",
|
||||
"display_name": "X.ai Grok (Beta)",
|
||||
"max_tokens": 131072
|
||||
}
|
||||
],
|
||||
"version": "1"
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced configuration {#advanced-configuration}
|
||||
|
||||
#### Example Configuration
|
||||
|
||||
@@ -139,5 +139,3 @@ New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name
|
||||
```
|
||||
|
||||
For more information on this, please see [win32 docs](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=powershell)
|
||||
|
||||
(note that you will need to restart your system after enabling longpath support)
|
||||
|
||||
@@ -146,15 +146,20 @@ Finally keyboards that support extended Latin alphabets (usually ISO keyboards)
|
||||
|
||||
For example on a German QWERTZ keyboard, the `cmd->` shortcut is moved to `cmd-:` because `cmd->` is the system window switcher and this is where that shortcut is typed on a QWERTY keyboard. `cmd-+` stays the same because + is still typable without option, and as a result, `cmd-[` and `cmd-]` become `cmd-ö` and `cmd-ä`, moving out of the way of the `+` key.
|
||||
|
||||
If you are defining shortcuts in your personal keymap, you can opt into the key equivalent mapping by setting `use_key_equivalents` to `true` in your keymap:
|
||||
If you are defining shortcuts in your personal keymap, you can opt-out of the key equivalent mapping by setting `use_layout_keys` to `true` in your keymap:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl->": "editor::Indent" // parsed as ctrl-: when a German QWERTZ keyboard is active
|
||||
}
|
||||
},
|
||||
{
|
||||
"use_layout_keys": true,
|
||||
"bindings": {
|
||||
"ctrl->": "editor::Indent" // remains ctrl-> when a German QWERTZ keyboard is active
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
@@ -473,15 +473,15 @@ Here's an example of these settings changed:
|
||||
|
||||
Here are a few general Zed settings that can help you fine-tune your Vim experience:
|
||||
|
||||
| Property | Description | Default Value |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
||||
| cursor_blink | If `true`, the cursor blinks. | `true` |
|
||||
| relative_line_numbers | If `true`, line numbers in the left gutter are relative to the cursor. | `false` |
|
||||
| scrollbar | Object that controls the scrollbar display. Set to `{ "show": "never" }` to hide the scroll bar. | `{ "show": "auto" }` |
|
||||
| scroll_beyond_last_line | If set to `"one_page"`, allows scrolling up to one page beyond the last line. Set to `"off"` to prevent this behavior. | `"one_page"` |
|
||||
| vertical_scroll_margin | The number of lines to keep above or below the cursor when scrolling. Set to `0` to allow the cursor to go up to the edges of the screen vertically. | `3` |
|
||||
| gutter.line_numbers | Controls the display of line numbers in the gutter. Set the `"line_numbers"` property to `false` to hide line numbers. | `true` |
|
||||
| command_aliases | Object that defines aliases for commands in the command palette. You can use it to define shortcut names for commands you use often. Read below for examples. | `{}` |
|
||||
| Property | Description | Default Value |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
|
||||
| cursor_blink | If `true`, the cursor blinks. | `true` |
|
||||
| relative_line_numbers | If `true`, line numbers in the left gutter are relative to the cursor. | `false` |
|
||||
| scrollbar | Object that controls the scrollbar display. Set to `{ "show": "never" }` to hide the scroll bar. | `{ "show": "always" }` |
|
||||
| scroll_beyond_last_line | If set to `"one_page"`, allows scrolling up to one page beyond the last line. Set to `"off"` to prevent this behavior. | `"one_page"` |
|
||||
| vertical_scroll_margin | The number of lines to keep above or below the cursor when scrolling. Set to `0` to allow the cursor to go up to the edges of the screen vertically. | `3` |
|
||||
| gutter.line_numbers | Controls the display of line numbers in the gutter. Set the `"line_numbers"` property to `false` to hide line numbers. | `true` |
|
||||
| command_aliases | Object that defines aliases for commands in the command palette. You can use it to define shortcut names for commands you use often. Read below for examples. | `{}` |
|
||||
|
||||
Here's an example of these settings changed:
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
set -exuo pipefail
|
||||
|
||||
git apply script/patches/use-cross-platform-livekit.patch
|
||||
|
||||
# Re-enable error skipping for this check, so that we can unapply the patch
|
||||
set +e
|
||||
|
||||
cargo check -p workspace
|
||||
exit_code=$?
|
||||
|
||||
# Disable error skipping again
|
||||
set -e
|
||||
|
||||
git apply -R script/patches/use-cross-platform-livekit.patch
|
||||
|
||||
exit "$exit_code"
|
||||
@@ -1,59 +0,0 @@
|
||||
diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml
|
||||
index 9ba10e56ba..bb69440691 100644
|
||||
--- a/crates/call/Cargo.toml
|
||||
+++ b/crates/call/Cargo.toml
|
||||
@@ -41,10 +41,10 @@ serde_derive.workspace = true
|
||||
settings.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
-[target.'cfg(target_os = "macos")'.dependencies]
|
||||
+[target.'cfg(any())'.dependencies]
|
||||
livekit_client_macos = { workspace = true }
|
||||
|
||||
-[target.'cfg(not(target_os = "macos"))'.dependencies]
|
||||
+[target.'cfg(all())'.dependencies]
|
||||
livekit_client = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs
|
||||
index 5e212d35b7..a8f9e8f43e 100644
|
||||
--- a/crates/call/src/call.rs
|
||||
+++ b/crates/call/src/call.rs
|
||||
@@ -1,13 +1,13 @@
|
||||
pub mod call_settings;
|
||||
|
||||
-#[cfg(target_os = "macos")]
|
||||
+#[cfg(any())]
|
||||
mod macos;
|
||||
|
||||
-#[cfg(target_os = "macos")]
|
||||
+#[cfg(any())]
|
||||
pub use macos::*;
|
||||
|
||||
-#[cfg(not(target_os = "macos"))]
|
||||
+#[cfg(all())]
|
||||
mod cross_platform;
|
||||
|
||||
-#[cfg(not(target_os = "macos"))]
|
||||
+#[cfg(all())]
|
||||
pub use cross_platform::*;
|
||||
diff --git a/crates/workspace/src/shared_screen.rs b/crates/workspace/src/shared_screen.rs
|
||||
index 1d17cfa145..f845234987 100644
|
||||
--- a/crates/workspace/src/shared_screen.rs
|
||||
+++ b/crates/workspace/src/shared_screen.rs
|
||||
@@ -1,11 +1,11 @@
|
||||
-#[cfg(target_os = "macos")]
|
||||
+#[cfg(any())]
|
||||
mod macos;
|
||||
|
||||
-#[cfg(target_os = "macos")]
|
||||
+#[cfg(any())]
|
||||
pub use macos::*;
|
||||
|
||||
-#[cfg(not(target_os = "macos"))]
|
||||
+#[cfg(all())]
|
||||
mod cross_platform;
|
||||
|
||||
-#[cfg(not(target_os = "macos"))]
|
||||
+#[cfg(all())]
|
||||
pub use cross_platform::*;
|
||||
@@ -43,8 +43,6 @@ extend-exclude = [
|
||||
"docs/theme/css/",
|
||||
# Spellcheck triggers on `|Fixe[sd]|` regex part.
|
||||
"script/danger/dangerfile.ts",
|
||||
# Hashes are not typos
|
||||
"script/patches/use-cross-platform-livekit.patch"
|
||||
]
|
||||
|
||||
[default]
|
||||
|
||||
Reference in New Issue
Block a user