Merge branch 'main' into add-installation-id-to-panic-events
This commit is contained in:
commit
8d1cc8815b
125 changed files with 5487 additions and 2149 deletions
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[alias]
|
||||||
|
xtask = "run --package xtask --"
|
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
@ -51,6 +51,7 @@ jobs:
|
||||||
rustup set profile minimal
|
rustup set profile minimal
|
||||||
rustup update stable
|
rustup update stable
|
||||||
rustup target add wasm32-wasi
|
rustup target add wasm32-wasi
|
||||||
|
cargo install cargo-nextest
|
||||||
|
|
||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
|
@ -70,7 +71,7 @@ jobs:
|
||||||
run: cargo check --workspace
|
run: cargo check --workspace
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --workspace --no-fail-fast
|
run: cargo nextest run --workspace --no-fail-fast
|
||||||
|
|
||||||
- name: Build collab
|
- name: Build collab
|
||||||
run: cargo build -p collab
|
run: cargo build -p collab
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -4,6 +4,8 @@
|
||||||
/plugins/bin
|
/plugins/bin
|
||||||
/script/node_modules
|
/script/node_modules
|
||||||
/styles/node_modules
|
/styles/node_modules
|
||||||
|
/styles/src/types/zed.ts
|
||||||
|
/crates/theme/schemas/theme.json
|
||||||
/crates/collab/static/styles.css
|
/crates/collab/static/styles.css
|
||||||
/vendor/bin
|
/vendor/bin
|
||||||
/assets/themes/*.json
|
/assets/themes/*.json
|
||||||
|
|
133
Cargo.lock
generated
133
Cargo.lock
generated
|
@ -109,6 +109,8 @@ dependencies = [
|
||||||
"isahc",
|
"isahc",
|
||||||
"language",
|
"language",
|
||||||
"menu",
|
"menu",
|
||||||
|
"project",
|
||||||
|
"regex",
|
||||||
"schemars",
|
"schemars",
|
||||||
"search",
|
"search",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -190,6 +192,55 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is-terminal 0.4.7",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.71"
|
version = "1.0.71"
|
||||||
|
@ -1102,8 +1153,8 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"clap_derive",
|
"clap_derive 3.2.25",
|
||||||
"clap_lex",
|
"clap_lex 0.2.4",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"strsim",
|
"strsim",
|
||||||
|
@ -1111,6 +1162,30 @@ dependencies = [
|
||||||
"textwrap",
|
"textwrap",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2686c4115cb0810d9a984776e197823d08ec94f176549a89a9efded477c456dc"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive 4.3.2",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e53afce1efce6ed1f633cf0e57612fe51db54a1ee4fd8f8503d078fe02d69ae"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"bitflags",
|
||||||
|
"clap_lex 0.5.0",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "3.2.25"
|
version = "3.2.25"
|
||||||
|
@ -1124,6 +1199,18 @@ dependencies = [
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.4.1",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.18",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
@ -1133,12 +1220,18 @@ dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cli"
|
name = "cli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap 3.2.25",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-services",
|
"core-services",
|
||||||
"dirs 3.0.2",
|
"dirs 3.0.2",
|
||||||
|
@ -1248,7 +1341,7 @@ dependencies = [
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
"call",
|
"call",
|
||||||
"clap",
|
"clap 3.2.25",
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"ctor",
|
"ctor",
|
||||||
|
@ -1343,6 +1436,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "command_palette"
|
name = "command_palette"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -6916,18 +7015,6 @@ dependencies = [
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "theme_testbench"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"gpui",
|
|
||||||
"project",
|
|
||||||
"settings",
|
|
||||||
"smallvec",
|
|
||||||
"theme",
|
|
||||||
"workspace",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.40"
|
version = "1.0.40"
|
||||||
|
@ -8780,6 +8867,17 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xtask"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap 4.3.5",
|
||||||
|
"schemars",
|
||||||
|
"serde_json",
|
||||||
|
"theme",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-rust"
|
name = "yaml-rust"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
@ -8809,7 +8907,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.92.0"
|
version = "0.93.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activity_indicator",
|
"activity_indicator",
|
||||||
"ai",
|
"ai",
|
||||||
|
@ -8888,7 +8986,6 @@ dependencies = [
|
||||||
"text",
|
"text",
|
||||||
"theme",
|
"theme",
|
||||||
"theme_selector",
|
"theme_selector",
|
||||||
"theme_testbench",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tiny_http",
|
"tiny_http",
|
||||||
"toml",
|
"toml",
|
||||||
|
|
|
@ -61,11 +61,11 @@ members = [
|
||||||
"crates/text",
|
"crates/text",
|
||||||
"crates/theme",
|
"crates/theme",
|
||||||
"crates/theme_selector",
|
"crates/theme_selector",
|
||||||
"crates/theme_testbench",
|
|
||||||
"crates/util",
|
"crates/util",
|
||||||
"crates/vim",
|
"crates/vim",
|
||||||
"crates/workspace",
|
"crates/workspace",
|
||||||
"crates/welcome",
|
"crates/welcome",
|
||||||
|
"crates/xtask",
|
||||||
"crates/zed",
|
"crates/zed",
|
||||||
]
|
]
|
||||||
default-members = ["crates/zed"]
|
default-members = ["crates/zed"]
|
||||||
|
@ -118,3 +118,4 @@ split-debuginfo = "unpacked"
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
lto = "thin"
|
lto = "thin"
|
||||||
|
codegen-units = 1
|
||||||
|
|
1
assets/icons/assist_15.svg
Normal file
1
assets/icons/assist_15.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.9 0.499976C13.9 0.279062 13.7209 0.0999756 13.5 0.0999756C13.2791 0.0999756 13.1 0.279062 13.1 0.499976V1.09998H12.5C12.2791 1.09998 12.1 1.27906 12.1 1.49998C12.1 1.72089 12.2791 1.89998 12.5 1.89998H13.1V2.49998C13.1 2.72089 13.2791 2.89998 13.5 2.89998C13.7209 2.89998 13.9 2.72089 13.9 2.49998V1.89998H14.5C14.7209 1.89998 14.9 1.72089 14.9 1.49998C14.9 1.27906 14.7209 1.09998 14.5 1.09998H13.9V0.499976ZM11.8536 3.14642C12.0488 3.34168 12.0488 3.65826 11.8536 3.85353L10.8536 4.85353C10.6583 5.04879 10.3417 5.04879 10.1465 4.85353C9.9512 4.65827 9.9512 4.34169 10.1465 4.14642L11.1464 3.14643C11.3417 2.95116 11.6583 2.95116 11.8536 3.14642ZM9.85357 5.14642C10.0488 5.34168 10.0488 5.65827 9.85357 5.85353L2.85355 12.8535C2.65829 13.0488 2.34171 13.0488 2.14645 12.8535C1.95118 12.6583 1.95118 12.3417 2.14645 12.1464L9.14646 5.14642C9.34172 4.95116 9.65831 4.95116 9.85357 5.14642ZM13.5 5.09998C13.7209 5.09998 13.9 5.27906 13.9 5.49998V6.09998H14.5C14.7209 6.09998 14.9 6.27906 14.9 6.49998C14.9 6.72089 14.7209 6.89998 14.5 6.89998H13.9V7.49998C13.9 7.72089 13.7209 7.89998 13.5 7.89998C13.2791 7.89998 13.1 7.72089 13.1 7.49998V6.89998H12.5C12.2791 6.89998 12.1 6.72089 12.1 6.49998C12.1 6.27906 12.2791 6.09998 12.5 6.09998H13.1V5.49998C13.1 5.27906 13.2791 5.09998 13.5 5.09998ZM8.90002 0.499976C8.90002 0.279062 8.72093 0.0999756 8.50002 0.0999756C8.2791 0.0999756 8.10002 0.279062 8.10002 0.499976V1.09998H7.50002C7.2791 1.09998 7.10002 1.27906 7.10002 1.49998C7.10002 1.72089 7.2791 1.89998 7.50002 1.89998H8.10002V2.49998C8.10002 2.72089 8.2791 2.89998 8.50002 2.89998C8.72093 2.89998 8.90002 2.72089 8.90002 2.49998V1.89998H9.50002C9.72093 1.89998 9.90002 1.72089 9.90002 1.49998C9.90002 1.27906 9.72093 1.09998 9.50002 1.09998H8.90002V0.499976Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
After Width: | Height: | Size: 1.9 KiB |
3
assets/icons/hamburger_15.svg
Normal file
3
assets/icons/hamburger_15.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 3C1.22386 3 1 3.22386 1 3.5C1 3.77614 1.22386 4 1.5 4H13.5C13.7761 4 14 3.77614 14 3.5C14 3.22386 13.7761 3 13.5 3H1.5ZM1 7.5C1 7.22386 1.22386 7 1.5 7H13.5C13.7761 7 14 7.22386 14 7.5C14 7.77614 13.7761 8 13.5 8H1.5C1.22386 8 1 7.77614 1 7.5ZM1 11.5C1 11.2239 1.22386 11 1.5 11H13.5C13.7761 11 14 11.2239 14 11.5C14 11.7761 13.7761 12 13.5 12H1.5C1.22386 12 1 11.7761 1 11.5Z" fill="#CCCAC2"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 552 B |
1
assets/icons/quote_15.svg
Normal file
1
assets/icons/quote_15.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.42503 3.44136C10.0561 3.23654 10.7837 3.2402 11.3792 3.54623C12.7532 4.25224 13.3477 6.07191 12.7946 8C12.5465 8.8649 12.1102 9.70472 11.1861 10.5524C10.262 11.4 8.98034 11.9 8.38571 11.9C8.17269 11.9 8 11.7321 8 11.525C8 11.3179 8.17644 11.15 8.38571 11.15C9.06497 11.15 9.67189 10.7804 10.3906 10.236C10.9406 9.8193 11.3701 9.28633 11.608 8.82191C12.0628 7.93367 12.0782 6.68174 11.3433 6.34901C10.9904 6.73455 10.5295 6.95946 9.97725 6.95946C8.7773 6.95946 8.0701 5.99412 8.10051 5.12009C8.12957 4.28474 8.66032 3.68954 9.42503 3.44136ZM3.42503 3.44136C4.05614 3.23654 4.78366 3.2402 5.37923 3.54623C6.7532 4.25224 7.34766 6.07191 6.79462 8C6.54654 8.8649 6.11019 9.70472 5.1861 10.5524C4.26201 11.4 2.98034 11.9 2.38571 11.9C2.17269 11.9 2 11.7321 2 11.525C2 11.3179 2.17644 11.15 2.38571 11.15C3.06497 11.15 3.67189 10.7804 4.39058 10.236C4.94065 9.8193 5.37014 9.28633 5.60797 8.82191C6.06282 7.93367 6.07821 6.68174 5.3433 6.34901C4.99037 6.73455 4.52948 6.95946 3.97725 6.95946C2.7773 6.95946 2.0701 5.99412 2.10051 5.12009C2.12957 4.28474 2.66032 3.68954 3.42503 3.44136Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
assets/icons/split_message_15.svg
Normal file
1
assets/icons/split_message_15.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.81832 0.68179C7.64258 0.506054 7.35766 0.506054 7.18192 0.68179L5.18192 2.68179C5.00619 2.85753 5.00619 3.14245 5.18192 3.31819C5.35766 3.49392 5.64258 3.49392 5.81832 3.31819L7.05012 2.08638L7.05012 5.50023C7.05012 5.74876 7.25159 5.95023 7.50012 5.95023C7.74865 5.95023 7.95012 5.74876 7.95012 5.50023L7.95012 2.08638L9.18192 3.31819C9.35766 3.49392 9.64258 3.49392 9.81832 3.31819C9.99406 3.14245 9.99406 2.85753 9.81832 2.68179L7.81832 0.68179ZM7.95012 12.9136V9.50023C7.95012 9.2517 7.74865 9.05023 7.50012 9.05023C7.25159 9.05023 7.05012 9.2517 7.05012 9.50023V12.9136L5.81832 11.6818C5.64258 11.5061 5.35766 11.5061 5.18192 11.6818C5.00619 11.8575 5.00619 12.1424 5.18192 12.3182L7.18192 14.3182C7.26632 14.4026 7.38077 14.45 7.50012 14.45C7.61947 14.45 7.73393 14.4026 7.81832 14.3182L9.81832 12.3182C9.99406 12.1424 9.99406 11.8575 9.81832 11.6818C9.64258 11.5061 9.35766 11.5061 9.18192 11.6818L7.95012 12.9136ZM1.49994 7.00017C1.2238 7.00017 0.999939 7.22403 0.999939 7.50017C0.999939 7.77631 1.2238 8.00017 1.49994 8.00017L13.4999 8.00017C13.7761 8.00017 13.9999 7.77631 13.9999 7.50017C13.9999 7.22403 13.7761 7.00017 13.4999 7.00017L1.49994 7.00017Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -40,7 +40,8 @@
|
||||||
"cmd-o": "workspace::Open",
|
"cmd-o": "workspace::Open",
|
||||||
"alt-cmd-o": "projects::OpenRecent",
|
"alt-cmd-o": "projects::OpenRecent",
|
||||||
"ctrl-~": "workspace::NewTerminal",
|
"ctrl-~": "workspace::NewTerminal",
|
||||||
"ctrl-`": "terminal_panel::ToggleFocus"
|
"ctrl-`": "terminal_panel::ToggleFocus",
|
||||||
|
"shift-escape": "workspace::ToggleZoom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -197,9 +198,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "AssistantEditor > Editor",
|
"context": "AssistantPanel",
|
||||||
|
"bindings": {
|
||||||
|
"cmd-g": "search::SelectNextMatch",
|
||||||
|
"cmd-shift-g": "search::SelectPrevMatch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "ConversationEditor > Editor",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-enter": "assistant::Assist",
|
"cmd-enter": "assistant::Assist",
|
||||||
|
"cmd-s": "workspace::Save",
|
||||||
"cmd->": "assistant::QuoteSelection",
|
"cmd->": "assistant::QuoteSelection",
|
||||||
"shift-enter": "assistant::Split",
|
"shift-enter": "assistant::Split",
|
||||||
"ctrl-r": "assistant::CycleMessageRole"
|
"ctrl-r": "assistant::CycleMessageRole"
|
||||||
|
@ -234,8 +243,7 @@
|
||||||
"cmd-shift-g": "search::SelectPrevMatch",
|
"cmd-shift-g": "search::SelectPrevMatch",
|
||||||
"alt-cmd-c": "search::ToggleCaseSensitive",
|
"alt-cmd-c": "search::ToggleCaseSensitive",
|
||||||
"alt-cmd-w": "search::ToggleWholeWord",
|
"alt-cmd-w": "search::ToggleWholeWord",
|
||||||
"alt-cmd-r": "search::ToggleRegex",
|
"alt-cmd-r": "search::ToggleRegex"
|
||||||
"shift-escape": "workspace::ToggleZoom"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Bindings from VS Code
|
// Bindings from VS Code
|
||||||
|
@ -411,6 +419,7 @@
|
||||||
"ctrl-shift-k": "editor::DeleteLine",
|
"ctrl-shift-k": "editor::DeleteLine",
|
||||||
"cmd-shift-d": "editor::DuplicateLine",
|
"cmd-shift-d": "editor::DuplicateLine",
|
||||||
"cmd-shift-l": "editor::SplitSelectionIntoLines",
|
"cmd-shift-l": "editor::SplitSelectionIntoLines",
|
||||||
|
"ctrl-j": "editor::JoinLines",
|
||||||
"ctrl-cmd-up": "editor::MoveLineUp",
|
"ctrl-cmd-up": "editor::MoveLineUp",
|
||||||
"ctrl-cmd-down": "editor::MoveLineDown",
|
"ctrl-cmd-down": "editor::MoveLineDown",
|
||||||
"ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",
|
"ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",
|
||||||
|
|
|
@ -25,11 +25,15 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"h": "vim::Left",
|
"h": "vim::Left",
|
||||||
|
"left": "vim::Left",
|
||||||
"backspace": "vim::Backspace",
|
"backspace": "vim::Backspace",
|
||||||
"j": "vim::Down",
|
"j": "vim::Down",
|
||||||
|
"down": "vim::Down",
|
||||||
"enter": "vim::NextLineStart",
|
"enter": "vim::NextLineStart",
|
||||||
"k": "vim::Up",
|
"k": "vim::Up",
|
||||||
|
"up": "vim::Up",
|
||||||
"l": "vim::Right",
|
"l": "vim::Right",
|
||||||
|
"right": "vim::Right",
|
||||||
"$": "vim::EndOfLine",
|
"$": "vim::EndOfLine",
|
||||||
"shift-g": "vim::EndOfDocument",
|
"shift-g": "vim::EndOfDocument",
|
||||||
"w": "vim::NextWordStart",
|
"w": "vim::NextWordStart",
|
||||||
|
@ -90,6 +94,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ctrl-o": "pane::GoBack",
|
||||||
|
"ctrl-]": "editor::GoToDefinition",
|
||||||
"escape": "editor::Cancel",
|
"escape": "editor::Cancel",
|
||||||
"0": "vim::StartOfLine", // When no number operator present, use start of line motion
|
"0": "vim::StartOfLine", // When no number operator present, use start of line motion
|
||||||
"1": [
|
"1": [
|
||||||
|
@ -131,7 +137,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting",
|
"context": "Editor && vim_mode == normal && (vim_operator == none || vim_operator == n) && !VimWaiting",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"c": [
|
"c": [
|
||||||
"vim::PushOperator",
|
"vim::PushOperator",
|
||||||
|
@ -143,6 +149,7 @@
|
||||||
"Delete"
|
"Delete"
|
||||||
],
|
],
|
||||||
"shift-d": "vim::DeleteToEndOfLine",
|
"shift-d": "vim::DeleteToEndOfLine",
|
||||||
|
"shift-j": "editor::JoinLines",
|
||||||
"y": [
|
"y": [
|
||||||
"vim::PushOperator",
|
"vim::PushOperator",
|
||||||
"Yank"
|
"Yank"
|
||||||
|
@ -184,7 +191,6 @@
|
||||||
"p": "vim::Paste",
|
"p": "vim::Paste",
|
||||||
"u": "editor::Undo",
|
"u": "editor::Undo",
|
||||||
"ctrl-r": "editor::Redo",
|
"ctrl-r": "editor::Redo",
|
||||||
"ctrl-o": "pane::GoBack",
|
|
||||||
"/": [
|
"/": [
|
||||||
"buffer_search::Deploy",
|
"buffer_search::Deploy",
|
||||||
{
|
{
|
||||||
|
@ -214,7 +220,8 @@
|
||||||
"r": [
|
"r": [
|
||||||
"vim::PushOperator",
|
"vim::PushOperator",
|
||||||
"Replace"
|
"Replace"
|
||||||
]
|
],
|
||||||
|
"s": "vim::Substitute"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -301,6 +308,7 @@
|
||||||
"x": "vim::VisualDelete",
|
"x": "vim::VisualDelete",
|
||||||
"y": "vim::VisualYank",
|
"y": "vim::VisualYank",
|
||||||
"p": "vim::VisualPaste",
|
"p": "vim::VisualPaste",
|
||||||
|
"s": "vim::Substitute",
|
||||||
"r": [
|
"r": [
|
||||||
"vim::PushOperator",
|
"vim::PushOperator",
|
||||||
"Replace"
|
"Replace"
|
||||||
|
|
|
@ -57,37 +57,37 @@
|
||||||
"show_whitespaces": "selection",
|
"show_whitespaces": "selection",
|
||||||
// Scrollbar related settings
|
// Scrollbar related settings
|
||||||
"scrollbar": {
|
"scrollbar": {
|
||||||
// When to show the scrollbar in the editor.
|
// When to show the scrollbar in the editor.
|
||||||
// This setting can take four values:
|
// This setting can take four values:
|
||||||
//
|
//
|
||||||
// 1. Show the scrollbar if there's important information or
|
// 1. Show the scrollbar if there's important information or
|
||||||
// follow the system's configured behavior (default):
|
// follow the system's configured behavior (default):
|
||||||
// "auto"
|
// "auto"
|
||||||
// 2. Match the system's configured behavior:
|
// 2. Match the system's configured behavior:
|
||||||
// "system"
|
// "system"
|
||||||
// 3. Always show the scrollbar:
|
// 3. Always show the scrollbar:
|
||||||
// "always"
|
// "always"
|
||||||
// 4. Never show the scrollbar:
|
// 4. Never show the scrollbar:
|
||||||
// "never"
|
// "never"
|
||||||
"show": "auto",
|
"show": "auto",
|
||||||
// Whether to show git diff indicators in the scrollbar.
|
// Whether to show git diff indicators in the scrollbar.
|
||||||
"git_diff": true
|
"git_diff": true
|
||||||
},
|
},
|
||||||
"project_panel": {
|
"project_panel": {
|
||||||
// Whether to show the git status in the project panel.
|
// Whether to show the git status in the project panel.
|
||||||
"git_status": true,
|
"git_status": true,
|
||||||
// Where to dock project panel. Can be 'left' or 'right'.
|
// Where to dock project panel. Can be 'left' or 'right'.
|
||||||
"dock": "left",
|
"dock": "left",
|
||||||
// Default width of the project panel.
|
// Default width of the project panel.
|
||||||
"default_width": 240
|
"default_width": 240
|
||||||
},
|
},
|
||||||
"assistant": {
|
"assistant": {
|
||||||
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
|
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
|
||||||
"dock": "right",
|
"dock": "right",
|
||||||
// Default width when the assistant is docked to the left or right.
|
// Default width when the assistant is docked to the left or right.
|
||||||
"default_width": 450,
|
"default_width": 640,
|
||||||
// Default height when the assistant is docked to the bottom.
|
// Default height when the assistant is docked to the bottom.
|
||||||
"default_height": 320
|
"default_height": 320
|
||||||
},
|
},
|
||||||
// Whether the screen sharing icon is shown in the os status bar.
|
// Whether the screen sharing icon is shown in the os status bar.
|
||||||
"show_call_status_icon": true,
|
"show_call_status_icon": true,
|
||||||
|
|
|
@ -326,7 +326,7 @@ impl View for ActivityIndicator {
|
||||||
let mut element = MouseEventHandler::<Self, _>::new(0, cx, |state, cx| {
|
let mut element = MouseEventHandler::<Self, _>::new(0, cx, |state, cx| {
|
||||||
let theme = &theme::current(cx).workspace.status_bar.lsp_status;
|
let theme = &theme::current(cx).workspace.status_bar.lsp_status;
|
||||||
let style = if state.hovered() && on_click.is_some() {
|
let style = if state.hovered() && on_click.is_some() {
|
||||||
theme.hover.as_ref().unwrap_or(&theme.default)
|
theme.hovered.as_ref().unwrap_or(&theme.default)
|
||||||
} else {
|
} else {
|
||||||
&theme.default
|
&theme.default
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,9 +22,10 @@ util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
chrono = "0.4"
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
isahc.workspace = true
|
isahc.workspace = true
|
||||||
|
regex.workspace = true
|
||||||
schemars.workspace = true
|
schemars.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
@ -33,3 +34,4 @@ tiktoken-rs = "0.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
editor = { path = "../editor", features = ["test-support"] }
|
editor = { path = "../editor", features = ["test-support"] }
|
||||||
|
project = { path = "../project", features = ["test-support"] }
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
pub mod assistant;
|
pub mod assistant;
|
||||||
mod assistant_settings;
|
mod assistant_settings;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
pub use assistant::AssistantPanel;
|
pub use assistant::AssistantPanel;
|
||||||
|
use chrono::{DateTime, Local};
|
||||||
|
use collections::HashMap;
|
||||||
|
use fs::Fs;
|
||||||
|
use futures::StreamExt;
|
||||||
use gpui::AppContext;
|
use gpui::AppContext;
|
||||||
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display};
|
use std::{
|
||||||
|
cmp::Reverse,
|
||||||
|
fmt::{self, Display},
|
||||||
|
path::PathBuf,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
use util::paths::CONVERSATIONS_DIR;
|
||||||
|
|
||||||
// Data types for chat completion requests
|
// Data types for chat completion requests
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
@ -14,6 +26,84 @@ struct OpenAIRequest {
|
||||||
stream: bool,
|
stream: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
|
struct MessageId(usize);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
struct MessageMetadata {
|
||||||
|
role: Role,
|
||||||
|
sent_at: DateTime<Local>,
|
||||||
|
status: MessageStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
enum MessageStatus {
|
||||||
|
Pending,
|
||||||
|
Done,
|
||||||
|
Error(Arc<str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct SavedMessage {
|
||||||
|
id: MessageId,
|
||||||
|
start: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct SavedConversation {
|
||||||
|
zed: String,
|
||||||
|
version: String,
|
||||||
|
text: String,
|
||||||
|
messages: Vec<SavedMessage>,
|
||||||
|
message_metadata: HashMap<MessageId, MessageMetadata>,
|
||||||
|
summary: String,
|
||||||
|
model: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SavedConversation {
|
||||||
|
const VERSION: &'static str = "0.1.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SavedConversationMetadata {
|
||||||
|
title: String,
|
||||||
|
path: PathBuf,
|
||||||
|
mtime: chrono::DateTime<chrono::Local>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SavedConversationMetadata {
|
||||||
|
pub async fn list(fs: Arc<dyn Fs>) -> Result<Vec<Self>> {
|
||||||
|
fs.create_dir(&CONVERSATIONS_DIR).await?;
|
||||||
|
|
||||||
|
let mut paths = fs.read_dir(&CONVERSATIONS_DIR).await?;
|
||||||
|
let mut conversations = Vec::<SavedConversationMetadata>::new();
|
||||||
|
while let Some(path) = paths.next().await {
|
||||||
|
let path = path?;
|
||||||
|
|
||||||
|
let pattern = r" - \d+.zed.json$";
|
||||||
|
let re = Regex::new(pattern).unwrap();
|
||||||
|
|
||||||
|
let metadata = fs.metadata(&path).await?;
|
||||||
|
if let Some((file_name, metadata)) = path
|
||||||
|
.file_name()
|
||||||
|
.and_then(|name| name.to_str())
|
||||||
|
.zip(metadata)
|
||||||
|
{
|
||||||
|
let title = re.replace(file_name, "");
|
||||||
|
conversations.push(Self {
|
||||||
|
title: title.into_owned(),
|
||||||
|
path,
|
||||||
|
mtime: metadata.mtime.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conversations.sort_unstable_by_key(|conversation| Reverse(conversation.mtime));
|
||||||
|
|
||||||
|
Ok(conversations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
struct RequestMessage {
|
struct RequestMessage {
|
||||||
role: Role,
|
role: Role,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -49,7 +49,7 @@ impl View for UpdateNotification {
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
|
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
|
||||||
let style = theme.dismiss_button.style_for(state, false);
|
let style = theme.dismiss_button.style_for(state);
|
||||||
Svg::new("icons/x_mark_8.svg")
|
Svg::new("icons/x_mark_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -74,7 +74,7 @@ impl View for UpdateNotification {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.with_child({
|
.with_child({
|
||||||
let style = theme.action_message.style_for(state, false);
|
let style = theme.action_message.style_for(state);
|
||||||
Text::new("View the release notes", style.text.clone())
|
Text::new("View the release notes", style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
|
|
@ -83,7 +83,7 @@ impl View for Breadcrumbs {
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseEventHandler::<Breadcrumbs, Breadcrumbs>::new(0, cx, |state, _| {
|
MouseEventHandler::<Breadcrumbs, Breadcrumbs>::new(0, cx, |state, _| {
|
||||||
let style = style.style_for(state, false);
|
let style = style.style_for(state);
|
||||||
crumbs.with_style(style.container)
|
crumbs.with_style(style.container)
|
||||||
})
|
})
|
||||||
.on_click(MouseButton::Left, |_, this, cx| {
|
.on_click(MouseButton::Left, |_, this, cx| {
|
||||||
|
|
|
@ -299,7 +299,12 @@ impl CollabTitlebarItem {
|
||||||
pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext<Self>) {
|
pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext<Self>) {
|
||||||
let theme = theme::current(cx).clone();
|
let theme = theme::current(cx).clone();
|
||||||
let avatar_style = theme.workspace.titlebar.leader_avatar.clone();
|
let avatar_style = theme.workspace.titlebar.leader_avatar.clone();
|
||||||
let item_style = theme.context_menu.item.disabled_style().clone();
|
let item_style = theme
|
||||||
|
.context_menu
|
||||||
|
.item
|
||||||
|
.inactive_state()
|
||||||
|
.disabled_style()
|
||||||
|
.clone();
|
||||||
self.user_menu.update(cx, |user_menu, cx| {
|
self.user_menu.update(cx, |user_menu, cx| {
|
||||||
let items = if let Some(user) = self.user_store.read(cx).current_user() {
|
let items = if let Some(user) = self.user_store.read(cx).current_user() {
|
||||||
vec![
|
vec![
|
||||||
|
@ -361,8 +366,20 @@ impl CollabTitlebarItem {
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(titlebar.toggle_contacts_badge)
|
.with_style(titlebar.toggle_contacts_badge)
|
||||||
.contained()
|
.contained()
|
||||||
.with_margin_left(titlebar.toggle_contacts_button.default.icon_width)
|
.with_margin_left(
|
||||||
.with_margin_top(titlebar.toggle_contacts_button.default.icon_width)
|
titlebar
|
||||||
|
.toggle_contacts_button
|
||||||
|
.inactive_state()
|
||||||
|
.default
|
||||||
|
.icon_width,
|
||||||
|
)
|
||||||
|
.with_margin_top(
|
||||||
|
titlebar
|
||||||
|
.toggle_contacts_button
|
||||||
|
.inactive_state()
|
||||||
|
.default
|
||||||
|
.icon_width,
|
||||||
|
)
|
||||||
.aligned(),
|
.aligned(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -372,7 +389,8 @@ impl CollabTitlebarItem {
|
||||||
MouseEventHandler::<ToggleContactsMenu, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<ToggleContactsMenu, Self>::new(0, cx, |state, _| {
|
||||||
let style = titlebar
|
let style = titlebar
|
||||||
.toggle_contacts_button
|
.toggle_contacts_button
|
||||||
.style_for(state, self.contacts_popover.is_some());
|
.in_state(self.contacts_popover.is_some())
|
||||||
|
.style_for(state);
|
||||||
Svg::new("icons/user_plus_16.svg")
|
Svg::new("icons/user_plus_16.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -419,7 +437,7 @@ impl CollabTitlebarItem {
|
||||||
|
|
||||||
let titlebar = &theme.workspace.titlebar;
|
let titlebar = &theme.workspace.titlebar;
|
||||||
MouseEventHandler::<ToggleScreenSharing, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<ToggleScreenSharing, Self>::new(0, cx, |state, _| {
|
||||||
let style = titlebar.call_control.style_for(state, false);
|
let style = titlebar.call_control.style_for(state);
|
||||||
Svg::new(icon)
|
Svg::new(icon)
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -473,7 +491,7 @@ impl CollabTitlebarItem {
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<ShareUnshare, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<ShareUnshare, Self>::new(0, cx, |state, _| {
|
||||||
//TODO: Ensure this button has consistent width for both text variations
|
//TODO: Ensure this button has consistent width for both text variations
|
||||||
let style = titlebar.share_button.style_for(state, false);
|
let style = titlebar.share_button.inactive_state().style_for(state);
|
||||||
Label::new(label, style.text.clone())
|
Label::new(label, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -511,7 +529,7 @@ impl CollabTitlebarItem {
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<ToggleUserMenu, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<ToggleUserMenu, Self>::new(0, cx, |state, _| {
|
||||||
let style = titlebar.call_control.style_for(state, false);
|
let style = titlebar.call_control.style_for(state);
|
||||||
Svg::new("icons/ellipsis_14.svg")
|
Svg::new("icons/ellipsis_14.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -549,7 +567,7 @@ impl CollabTitlebarItem {
|
||||||
fn render_sign_in_button(&self, theme: &Theme, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
fn render_sign_in_button(&self, theme: &Theme, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||||
let titlebar = &theme.workspace.titlebar;
|
let titlebar = &theme.workspace.titlebar;
|
||||||
MouseEventHandler::<SignIn, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<SignIn, Self>::new(0, cx, |state, _| {
|
||||||
let style = titlebar.sign_in_prompt.style_for(state, false);
|
let style = titlebar.sign_in_prompt.inactive_state().style_for(state);
|
||||||
Label::new("Sign In", style.text.clone())
|
Label::new("Sign In", style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
|
|
@ -117,7 +117,8 @@ impl PickerDelegate for ContactFinderDelegate {
|
||||||
.contact_finder
|
.contact_finder
|
||||||
.picker
|
.picker
|
||||||
.item
|
.item
|
||||||
.style_for(mouse_state, selected);
|
.in_state(selected)
|
||||||
|
.style_for(mouse_state);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_children(user.avatar.clone().map(|avatar| {
|
.with_children(user.avatar.clone().map(|avatar| {
|
||||||
Image::from_data(avatar)
|
Image::from_data(avatar)
|
||||||
|
|
|
@ -774,7 +774,8 @@ impl ContactList {
|
||||||
.with_style(
|
.with_style(
|
||||||
*theme
|
*theme
|
||||||
.contact_row
|
.contact_row
|
||||||
.style_for(&mut Default::default(), is_selected),
|
.in_state(is_selected)
|
||||||
|
.style_for(&mut Default::default()),
|
||||||
)
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
|
@ -797,7 +798,7 @@ impl ContactList {
|
||||||
.width
|
.width
|
||||||
.or(theme.contact_avatar.height)
|
.or(theme.contact_avatar.height)
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
let row = &theme.project_row.default;
|
let row = &theme.project_row.inactive_state().default;
|
||||||
let tree_branch = theme.tree_branch;
|
let tree_branch = theme.tree_branch;
|
||||||
let line_height = row.name.text.line_height(font_cache);
|
let line_height = row.name.text.line_height(font_cache);
|
||||||
let cap_height = row.name.text.cap_height(font_cache);
|
let cap_height = row.name.text.cap_height(font_cache);
|
||||||
|
@ -810,8 +811,11 @@ impl ContactList {
|
||||||
};
|
};
|
||||||
|
|
||||||
MouseEventHandler::<JoinProject, Self>::new(project_id as usize, cx, |mouse_state, _| {
|
MouseEventHandler::<JoinProject, Self>::new(project_id as usize, cx, |mouse_state, _| {
|
||||||
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
|
let tree_branch = *tree_branch.in_state(is_selected).style_for(mouse_state);
|
||||||
let row = theme.project_row.style_for(mouse_state, is_selected);
|
let row = theme
|
||||||
|
.project_row
|
||||||
|
.in_state(is_selected)
|
||||||
|
.style_for(mouse_state);
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
@ -893,7 +897,7 @@ impl ContactList {
|
||||||
.width
|
.width
|
||||||
.or(theme.contact_avatar.height)
|
.or(theme.contact_avatar.height)
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
let row = &theme.project_row.default;
|
let row = &theme.project_row.inactive_state().default;
|
||||||
let tree_branch = theme.tree_branch;
|
let tree_branch = theme.tree_branch;
|
||||||
let line_height = row.name.text.line_height(font_cache);
|
let line_height = row.name.text.line_height(font_cache);
|
||||||
let cap_height = row.name.text.cap_height(font_cache);
|
let cap_height = row.name.text.cap_height(font_cache);
|
||||||
|
@ -904,8 +908,11 @@ impl ContactList {
|
||||||
peer_id.as_u64() as usize,
|
peer_id.as_u64() as usize,
|
||||||
cx,
|
cx,
|
||||||
|mouse_state, _| {
|
|mouse_state, _| {
|
||||||
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
|
let tree_branch = *tree_branch.in_state(is_selected).style_for(mouse_state);
|
||||||
let row = theme.project_row.style_for(mouse_state, is_selected);
|
let row = theme
|
||||||
|
.project_row
|
||||||
|
.in_state(is_selected)
|
||||||
|
.style_for(mouse_state);
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
@ -989,7 +996,8 @@ impl ContactList {
|
||||||
|
|
||||||
let header_style = theme
|
let header_style = theme
|
||||||
.header_row
|
.header_row
|
||||||
.style_for(&mut Default::default(), is_selected);
|
.in_state(is_selected)
|
||||||
|
.style_for(&mut Default::default());
|
||||||
let text = match section {
|
let text = match section {
|
||||||
Section::ActiveCall => "Collaborators",
|
Section::ActiveCall => "Collaborators",
|
||||||
Section::Requests => "Contact Requests",
|
Section::Requests => "Contact Requests",
|
||||||
|
@ -999,7 +1007,7 @@ impl ContactList {
|
||||||
let leave_call = if section == Section::ActiveCall {
|
let leave_call = if section == Section::ActiveCall {
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::<LeaveCallContactList, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<LeaveCallContactList, Self>::new(0, cx, |state, _| {
|
||||||
let style = theme.leave_call.style_for(state, false);
|
let style = theme.leave_call.style_for(state);
|
||||||
Label::new("Leave Call", style.text.clone())
|
Label::new("Leave Call", style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -1110,8 +1118,7 @@ impl ContactList {
|
||||||
contact.user.id as usize,
|
contact.user.id as usize,
|
||||||
cx,
|
cx,
|
||||||
|mouse_state, _| {
|
|mouse_state, _| {
|
||||||
let button_style =
|
let button_style = theme.contact_button.style_for(mouse_state);
|
||||||
theme.contact_button.style_for(mouse_state, false);
|
|
||||||
render_icon_button(button_style, "icons/x_mark_8.svg")
|
render_icon_button(button_style, "icons/x_mark_8.svg")
|
||||||
.aligned()
|
.aligned()
|
||||||
.flex_float()
|
.flex_float()
|
||||||
|
@ -1146,7 +1153,8 @@ impl ContactList {
|
||||||
.with_style(
|
.with_style(
|
||||||
*theme
|
*theme
|
||||||
.contact_row
|
.contact_row
|
||||||
.style_for(&mut Default::default(), is_selected),
|
.in_state(is_selected)
|
||||||
|
.style_for(&mut Default::default()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.on_click(MouseButton::Left, move |_, this, cx| {
|
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||||
|
@ -1204,7 +1212,7 @@ impl ContactList {
|
||||||
let button_style = if is_contact_request_pending {
|
let button_style = if is_contact_request_pending {
|
||||||
&theme.disabled_button
|
&theme.disabled_button
|
||||||
} else {
|
} else {
|
||||||
theme.contact_button.style_for(mouse_state, false)
|
theme.contact_button.style_for(mouse_state)
|
||||||
};
|
};
|
||||||
render_icon_button(button_style, "icons/x_mark_8.svg").aligned()
|
render_icon_button(button_style, "icons/x_mark_8.svg").aligned()
|
||||||
})
|
})
|
||||||
|
@ -1227,7 +1235,7 @@ impl ContactList {
|
||||||
let button_style = if is_contact_request_pending {
|
let button_style = if is_contact_request_pending {
|
||||||
&theme.disabled_button
|
&theme.disabled_button
|
||||||
} else {
|
} else {
|
||||||
theme.contact_button.style_for(mouse_state, false)
|
theme.contact_button.style_for(mouse_state)
|
||||||
};
|
};
|
||||||
render_icon_button(button_style, "icons/check_8.svg")
|
render_icon_button(button_style, "icons/check_8.svg")
|
||||||
.aligned()
|
.aligned()
|
||||||
|
@ -1250,7 +1258,7 @@ impl ContactList {
|
||||||
let button_style = if is_contact_request_pending {
|
let button_style = if is_contact_request_pending {
|
||||||
&theme.disabled_button
|
&theme.disabled_button
|
||||||
} else {
|
} else {
|
||||||
theme.contact_button.style_for(mouse_state, false)
|
theme.contact_button.style_for(mouse_state)
|
||||||
};
|
};
|
||||||
render_icon_button(button_style, "icons/x_mark_8.svg")
|
render_icon_button(button_style, "icons/x_mark_8.svg")
|
||||||
.aligned()
|
.aligned()
|
||||||
|
@ -1277,7 +1285,8 @@ impl ContactList {
|
||||||
.with_style(
|
.with_style(
|
||||||
*theme
|
*theme
|
||||||
.contact_row
|
.contact_row
|
||||||
.style_for(&mut Default::default(), is_selected),
|
.in_state(is_selected)
|
||||||
|
.style_for(&mut Default::default()),
|
||||||
)
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ where
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<Dismiss, V>::new(user.id as usize, cx, |state, _| {
|
MouseEventHandler::<Dismiss, V>::new(user.id as usize, cx, |state, _| {
|
||||||
let style = theme.dismiss_button.style_for(state, false);
|
let style = theme.dismiss_button.style_for(state);
|
||||||
Svg::new("icons/x_mark_8.svg")
|
Svg::new("icons/x_mark_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -93,7 +93,7 @@ where
|
||||||
.with_children(buttons.into_iter().enumerate().map(
|
.with_children(buttons.into_iter().enumerate().map(
|
||||||
|(ix, (message, handler))| {
|
|(ix, (message, handler))| {
|
||||||
MouseEventHandler::<Button, V>::new(ix, cx, |state, _| {
|
MouseEventHandler::<Button, V>::new(ix, cx, |state, _| {
|
||||||
let button = theme.button.style_for(state, false);
|
let button = theme.button.style_for(state);
|
||||||
Label::new(message, button.text.clone())
|
Label::new(message, button.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(button.container)
|
.with_style(button.container)
|
||||||
|
|
|
@ -185,8 +185,8 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||||
let mat = &self.matches[ix];
|
let mat = &self.matches[ix];
|
||||||
let command = &self.actions[mat.candidate_id];
|
let command = &self.actions[mat.candidate_id];
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
let key_style = &theme.command_palette.key.style_for(mouse_state, selected);
|
let key_style = &theme.command_palette.key.in_state(selected);
|
||||||
let keystroke_spacing = theme.command_palette.keystroke_spacing;
|
let keystroke_spacing = theme.command_palette.keystroke_spacing;
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
|
|
|
@ -328,10 +328,8 @@ impl ContextMenu {
|
||||||
Flex::column().with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
Flex::column().with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
||||||
match item {
|
match item {
|
||||||
ContextMenuItem::Item { label, .. } => {
|
ContextMenuItem::Item { label, .. } => {
|
||||||
let style = style.item.style_for(
|
let style = style.item.in_state(self.selected_index == Some(ix));
|
||||||
&mut Default::default(),
|
let style = style.style_for(&mut Default::default());
|
||||||
Some(ix) == self.selected_index,
|
|
||||||
);
|
|
||||||
|
|
||||||
match label {
|
match label {
|
||||||
ContextMenuItemLabel::String(label) => {
|
ContextMenuItemLabel::String(label) => {
|
||||||
|
@ -363,10 +361,8 @@ impl ContextMenu {
|
||||||
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
||||||
match item {
|
match item {
|
||||||
ContextMenuItem::Item { action, .. } => {
|
ContextMenuItem::Item { action, .. } => {
|
||||||
let style = style.item.style_for(
|
let style = style.item.in_state(self.selected_index == Some(ix));
|
||||||
&mut Default::default(),
|
let style = style.style_for(&mut Default::default());
|
||||||
Some(ix) == self.selected_index,
|
|
||||||
);
|
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
ContextMenuItemAction::Action(action) => KeystrokeLabel::new(
|
ContextMenuItemAction::Action(action) => KeystrokeLabel::new(
|
||||||
|
@ -412,8 +408,8 @@ impl ContextMenu {
|
||||||
let action = action.clone();
|
let action = action.clone();
|
||||||
let view_id = self.parent_view_id;
|
let view_id = self.parent_view_id;
|
||||||
MouseEventHandler::<MenuItem, ContextMenu>::new(ix, cx, |state, _| {
|
MouseEventHandler::<MenuItem, ContextMenu>::new(ix, cx, |state, _| {
|
||||||
let style =
|
let style = style.item.in_state(self.selected_index == Some(ix));
|
||||||
style.item.style_for(state, Some(ix) == self.selected_index);
|
let style = style.style_for(state);
|
||||||
let keystroke = match &action {
|
let keystroke = match &action {
|
||||||
ContextMenuItemAction::Action(action) => Some(
|
ContextMenuItemAction::Action(action) => Some(
|
||||||
KeystrokeLabel::new(
|
KeystrokeLabel::new(
|
||||||
|
|
|
@ -127,16 +127,16 @@ impl CopilotCodeVerification {
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(
|
Label::new(
|
||||||
if copied { "Copied!" } else { "Copy" },
|
if copied { "Copied!" } else { "Copy" },
|
||||||
device_code_style.cta.style_for(state, false).text.clone(),
|
device_code_style.cta.style_for(state).text.clone(),
|
||||||
)
|
)
|
||||||
.aligned()
|
.aligned()
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(*device_code_style.right_container.style_for(state, false))
|
.with_style(*device_code_style.right_container.style_for(state))
|
||||||
.constrained()
|
.constrained()
|
||||||
.with_width(device_code_style.right),
|
.with_width(device_code_style.right),
|
||||||
)
|
)
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(device_code_style.cta.style_for(state, false).container)
|
.with_style(device_code_style.cta.style_for(state).container)
|
||||||
})
|
})
|
||||||
.on_click(gpui::platform::MouseButton::Left, {
|
.on_click(gpui::platform::MouseButton::Left, {
|
||||||
let user_code = data.user_code.clone();
|
let user_code = data.user_code.clone();
|
||||||
|
|
|
@ -71,7 +71,8 @@ impl View for CopilotButton {
|
||||||
.status_bar
|
.status_bar
|
||||||
.panel_buttons
|
.panel_buttons
|
||||||
.button
|
.button
|
||||||
.style_for(state, active);
|
.in_state(active)
|
||||||
|
.style_for(state);
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
@ -255,7 +256,7 @@ impl CopilotButton {
|
||||||
move |state: &mut MouseState, style: &theme::ContextMenuItem| {
|
move |state: &mut MouseState, style: &theme::ContextMenuItem| {
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(Label::new("Copilot Settings", style.label.clone()))
|
.with_child(Label::new("Copilot Settings", style.label.clone()))
|
||||||
.with_child(theme::ui::icon(icon_style.style_for(state, false)))
|
.with_child(theme::ui::icon(icon_style.style_for(state)))
|
||||||
.align_children_center()
|
.align_children_center()
|
||||||
.into_any()
|
.into_any()
|
||||||
},
|
},
|
||||||
|
|
|
@ -100,7 +100,7 @@ impl View for DiagnosticIndicator {
|
||||||
.workspace
|
.workspace
|
||||||
.status_bar
|
.status_bar
|
||||||
.diagnostic_summary
|
.diagnostic_summary
|
||||||
.style_for(state, false);
|
.style_for(state);
|
||||||
|
|
||||||
let mut summary_row = Flex::row();
|
let mut summary_row = Flex::row();
|
||||||
if self.summary.error_count > 0 {
|
if self.summary.error_count > 0 {
|
||||||
|
@ -198,7 +198,7 @@ impl View for DiagnosticIndicator {
|
||||||
MouseEventHandler::<Message, _>::new(1, cx, |state, _| {
|
MouseEventHandler::<Message, _>::new(1, cx, |state, _| {
|
||||||
Label::new(
|
Label::new(
|
||||||
diagnostic.message.split('\n').next().unwrap().to_string(),
|
diagnostic.message.split('\n').next().unwrap().to_string(),
|
||||||
message_style.style_for(state, false).text.clone(),
|
message_style.style_for(state).text.clone(),
|
||||||
)
|
)
|
||||||
.aligned()
|
.aligned()
|
||||||
.contained()
|
.contained()
|
||||||
|
|
|
@ -206,6 +206,7 @@ actions!(
|
||||||
DuplicateLine,
|
DuplicateLine,
|
||||||
MoveLineUp,
|
MoveLineUp,
|
||||||
MoveLineDown,
|
MoveLineDown,
|
||||||
|
JoinLines,
|
||||||
Transpose,
|
Transpose,
|
||||||
Cut,
|
Cut,
|
||||||
Copy,
|
Copy,
|
||||||
|
@ -321,6 +322,7 @@ pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(Editor::indent);
|
cx.add_action(Editor::indent);
|
||||||
cx.add_action(Editor::outdent);
|
cx.add_action(Editor::outdent);
|
||||||
cx.add_action(Editor::delete_line);
|
cx.add_action(Editor::delete_line);
|
||||||
|
cx.add_action(Editor::join_lines);
|
||||||
cx.add_action(Editor::delete_to_previous_word_start);
|
cx.add_action(Editor::delete_to_previous_word_start);
|
||||||
cx.add_action(Editor::delete_to_previous_subword_start);
|
cx.add_action(Editor::delete_to_previous_subword_start);
|
||||||
cx.add_action(Editor::delete_to_next_word_end);
|
cx.add_action(Editor::delete_to_next_word_end);
|
||||||
|
@ -3320,15 +3322,21 @@ impl Editor {
|
||||||
pub fn render_code_actions_indicator(
|
pub fn render_code_actions_indicator(
|
||||||
&self,
|
&self,
|
||||||
style: &EditorStyle,
|
style: &EditorStyle,
|
||||||
active: bool,
|
is_active: bool,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<AnyElement<Self>> {
|
) -> Option<AnyElement<Self>> {
|
||||||
if self.available_code_actions.is_some() {
|
if self.available_code_actions.is_some() {
|
||||||
enum CodeActions {}
|
enum CodeActions {}
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
|
MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
|
||||||
Svg::new("icons/bolt_8.svg")
|
Svg::new("icons/bolt_8.svg").with_color(
|
||||||
.with_color(style.code_actions.indicator.style_for(state, active).color)
|
style
|
||||||
|
.code_actions
|
||||||
|
.indicator
|
||||||
|
.in_state(is_active)
|
||||||
|
.style_for(state)
|
||||||
|
.color,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
.with_padding(Padding::uniform(3.))
|
.with_padding(Padding::uniform(3.))
|
||||||
|
@ -3378,10 +3386,8 @@ impl Editor {
|
||||||
.with_color(
|
.with_color(
|
||||||
style
|
style
|
||||||
.indicator
|
.indicator
|
||||||
.style_for(
|
.in_state(fold_status == FoldStatus::Folded)
|
||||||
mouse_state,
|
.style_for(mouse_state)
|
||||||
fold_status == FoldStatus::Folded,
|
|
||||||
)
|
|
||||||
.color,
|
.color,
|
||||||
)
|
)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -3952,6 +3958,60 @@ impl Editor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
|
||||||
|
let mut row_ranges = Vec::<Range<u32>>::new();
|
||||||
|
for selection in self.selections.all::<Point>(cx) {
|
||||||
|
let start = selection.start.row;
|
||||||
|
let end = if selection.start.row == selection.end.row {
|
||||||
|
selection.start.row + 1
|
||||||
|
} else {
|
||||||
|
selection.end.row
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(last_row_range) = row_ranges.last_mut() {
|
||||||
|
if start <= last_row_range.end {
|
||||||
|
last_row_range.end = end;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row_ranges.push(start..end);
|
||||||
|
}
|
||||||
|
|
||||||
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
|
let mut cursor_positions = Vec::new();
|
||||||
|
for row_range in &row_ranges {
|
||||||
|
let anchor = snapshot.anchor_before(Point::new(
|
||||||
|
row_range.end - 1,
|
||||||
|
snapshot.line_len(row_range.end - 1),
|
||||||
|
));
|
||||||
|
cursor_positions.push(anchor.clone()..anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.transact(cx, |this, cx| {
|
||||||
|
for row_range in row_ranges.into_iter().rev() {
|
||||||
|
for row in row_range.rev() {
|
||||||
|
let end_of_line = Point::new(row, snapshot.line_len(row));
|
||||||
|
let indent = snapshot.indent_size_for_line(row + 1);
|
||||||
|
let start_of_next_line = Point::new(row + 1, indent.len);
|
||||||
|
|
||||||
|
let replace = if snapshot.line_len(row + 1) > indent.len {
|
||||||
|
" "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
this.buffer.update(cx, |buffer, cx| {
|
||||||
|
buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
|
s.select_anchor_ranges(cursor_positions)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
|
pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
|
||||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
let buffer = &display_map.buffer_snapshot;
|
let buffer = &display_map.buffer_snapshot;
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test::{
|
use crate::{
|
||||||
assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
|
test::{
|
||||||
editor_test_context::EditorTestContext, select_ranges,
|
assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
|
||||||
|
editor_test_context::EditorTestContext, select_ranges,
|
||||||
|
},
|
||||||
|
JoinLines,
|
||||||
};
|
};
|
||||||
use drag_and_drop::DragAndDrop;
|
use drag_and_drop::DragAndDrop;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
@ -2325,6 +2328,137 @@ fn test_delete_line(cx: &mut TestAppContext) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx, |_| {});
|
||||||
|
|
||||||
|
cx.add_window(|cx| {
|
||||||
|
let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
|
||||||
|
let mut editor = build_editor(buffer.clone(), cx);
|
||||||
|
let buffer = buffer.read(cx).as_singleton().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
editor.selections.ranges::<Point>(cx),
|
||||||
|
&[Point::new(0, 0)..Point::new(0, 0)]
|
||||||
|
);
|
||||||
|
|
||||||
|
// When on single line, replace newline at end by space
|
||||||
|
editor.join_lines(&JoinLines, cx);
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
|
||||||
|
assert_eq!(
|
||||||
|
editor.selections.ranges::<Point>(cx),
|
||||||
|
&[Point::new(0, 3)..Point::new(0, 3)]
|
||||||
|
);
|
||||||
|
|
||||||
|
// When multiple lines are selected, remove newlines that are spanned by the selection
|
||||||
|
editor.change_selections(None, cx, |s| {
|
||||||
|
s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
|
||||||
|
});
|
||||||
|
editor.join_lines(&JoinLines, cx);
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
|
||||||
|
assert_eq!(
|
||||||
|
editor.selections.ranges::<Point>(cx),
|
||||||
|
&[Point::new(0, 11)..Point::new(0, 11)]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Undo should be transactional
|
||||||
|
editor.undo(&Undo, cx);
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
|
||||||
|
assert_eq!(
|
||||||
|
editor.selections.ranges::<Point>(cx),
|
||||||
|
&[Point::new(0, 5)..Point::new(2, 2)]
|
||||||
|
);
|
||||||
|
|
||||||
|
// When joining an empty line don't insert a space
|
||||||
|
editor.change_selections(None, cx, |s| {
|
||||||
|
s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
|
||||||
|
});
|
||||||
|
editor.join_lines(&JoinLines, cx);
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
|
||||||
|
assert_eq!(
|
||||||
|
editor.selections.ranges::<Point>(cx),
|
||||||
|
[Point::new(2, 3)..Point::new(2, 3)]
|
||||||
|
);
|
||||||
|
|
||||||
|
// We can remove trailing newlines
|
||||||
|
editor.join_lines(&JoinLines, cx);
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
|
||||||
|
assert_eq!(
|
||||||
|
editor.selections.ranges::<Point>(cx),
|
||||||
|
[Point::new(2, 3)..Point::new(2, 3)]
|
||||||
|
);
|
||||||
|
|
||||||
|
// We don't blow up on the last line
|
||||||
|
editor.join_lines(&JoinLines, cx);
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
|
||||||
|
assert_eq!(
|
||||||
|
editor.selections.ranges::<Point>(cx),
|
||||||
|
[Point::new(2, 3)..Point::new(2, 3)]
|
||||||
|
);
|
||||||
|
|
||||||
|
// reset to test indentation
|
||||||
|
editor.buffer.update(cx, |buffer, cx| {
|
||||||
|
buffer.edit(
|
||||||
|
[
|
||||||
|
(Point::new(1, 0)..Point::new(1, 2), " "),
|
||||||
|
(Point::new(2, 0)..Point::new(2, 3), " \n\td"),
|
||||||
|
],
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// We remove any leading spaces
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb\n c\n \n\td");
|
||||||
|
editor.change_selections(None, cx, |s| {
|
||||||
|
s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
|
||||||
|
});
|
||||||
|
editor.join_lines(&JoinLines, cx);
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb c\n \n\td");
|
||||||
|
|
||||||
|
// We don't insert a space for a line containing only spaces
|
||||||
|
editor.join_lines(&JoinLines, cx);
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
|
||||||
|
|
||||||
|
// We ignore any leading tabs
|
||||||
|
editor.join_lines(&JoinLines, cx);
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
|
||||||
|
|
||||||
|
editor
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx, |_| {});
|
||||||
|
|
||||||
|
cx.add_window(|cx| {
|
||||||
|
let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
|
||||||
|
let mut editor = build_editor(buffer.clone(), cx);
|
||||||
|
let buffer = buffer.read(cx).as_singleton().unwrap();
|
||||||
|
|
||||||
|
editor.change_selections(None, cx, |s| {
|
||||||
|
s.select_ranges([
|
||||||
|
Point::new(0, 2)..Point::new(1, 1),
|
||||||
|
Point::new(1, 2)..Point::new(1, 2),
|
||||||
|
Point::new(3, 1)..Point::new(3, 2),
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.join_lines(&JoinLines, cx);
|
||||||
|
assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
editor.selections.ranges::<Point>(cx),
|
||||||
|
[
|
||||||
|
Point::new(0, 7)..Point::new(0, 7),
|
||||||
|
Point::new(1, 3)..Point::new(1, 3)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
editor
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_duplicate_line(cx: &mut TestAppContext) {
|
fn test_duplicate_line(cx: &mut TestAppContext) {
|
||||||
init_test(cx, |_| {});
|
init_test(cx, |_| {});
|
||||||
|
|
|
@ -1529,7 +1529,7 @@ impl EditorElement {
|
||||||
|
|
||||||
enum JumpIcon {}
|
enum JumpIcon {}
|
||||||
MouseEventHandler::<JumpIcon, _>::new((*id).into(), cx, |state, _| {
|
MouseEventHandler::<JumpIcon, _>::new((*id).into(), cx, |state, _| {
|
||||||
let style = style.jump_icon.style_for(state, false);
|
let style = style.jump_icon.style_for(state);
|
||||||
Svg::new("icons/arrow_up_right_8.svg")
|
Svg::new("icons/arrow_up_right_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -2094,7 +2094,7 @@ impl Element<Editor> for EditorElement {
|
||||||
.folds
|
.folds
|
||||||
.ellipses
|
.ellipses
|
||||||
.background
|
.background
|
||||||
.style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize), false)
|
.style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize))
|
||||||
.color;
|
.color;
|
||||||
|
|
||||||
(id, fold, color)
|
(id, fold, color)
|
||||||
|
|
|
@ -41,7 +41,8 @@ impl View for DeployFeedbackButton {
|
||||||
.status_bar
|
.status_bar
|
||||||
.panel_buttons
|
.panel_buttons
|
||||||
.button
|
.button
|
||||||
.style_for(state, active);
|
.in_state(active)
|
||||||
|
.style_for(state);
|
||||||
|
|
||||||
Svg::new("icons/feedback_16.svg")
|
Svg::new("icons/feedback_16.svg")
|
||||||
.with_color(style.icon_color)
|
.with_color(style.icon_color)
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl View for SubmitFeedbackButton {
|
||||||
let theme = theme::current(cx).clone();
|
let theme = theme::current(cx).clone();
|
||||||
enum SubmitFeedbackButton {}
|
enum SubmitFeedbackButton {}
|
||||||
MouseEventHandler::<SubmitFeedbackButton, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<SubmitFeedbackButton, Self>::new(0, cx, |state, _| {
|
||||||
let style = theme.feedback.submit_button.style_for(state, false);
|
let style = theme.feedback.submit_button.style_for(state);
|
||||||
Label::new("Submit as Markdown", style.text.clone())
|
Label::new("Submit as Markdown", style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
|
|
@ -546,7 +546,7 @@ impl PickerDelegate for FileFinderDelegate {
|
||||||
.get(ix)
|
.get(ix)
|
||||||
.expect("Invalid matches state: no element for index {ix}");
|
.expect("Invalid matches state: no element for index {ix}");
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
let (file_name, file_name_positions, full_path, full_path_positions) =
|
let (file_name, file_name_positions, full_path, full_path_positions) =
|
||||||
self.labels_for_match(path_match, cx, ix);
|
self.labels_for_match(path_match, cx, ix);
|
||||||
Flex::column()
|
Flex::column()
|
||||||
|
|
|
@ -6,15 +6,16 @@ use std::{
|
||||||
|
|
||||||
use crate::json::ToJson;
|
use crate::json::ToJson;
|
||||||
use pathfinder_color::{ColorF, ColorU};
|
use pathfinder_color::{ColorF, ColorU};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{self, Unexpected},
|
de::{self, Unexpected},
|
||||||
Deserialize, Deserializer,
|
Deserialize, Deserializer,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, JsonSchema)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Color(ColorU);
|
pub struct Color(#[schemars(with = "String")] ColorU);
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
pub fn transparent_black() -> Self {
|
pub fn transparent_black() -> Self {
|
||||||
|
|
|
@ -41,13 +41,7 @@ use collections::HashMap;
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use json::ToJson;
|
use json::ToJson;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{any::Any, borrow::Cow, mem, ops::Range};
|
||||||
any::Any,
|
|
||||||
borrow::Cow,
|
|
||||||
marker::PhantomData,
|
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut, Range},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait Element<V: View>: 'static {
|
pub trait Element<V: View>: 'static {
|
||||||
type LayoutState;
|
type LayoutState;
|
||||||
|
@ -567,90 +561,6 @@ impl<V: View> RootElement<V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Component<V: View>: 'static {
|
|
||||||
fn render(&self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ComponentHost<V: View, C: Component<V>> {
|
|
||||||
component: C,
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: View, C: Component<V>> ComponentHost<V, C> {
|
|
||||||
pub fn new(c: C) -> Self {
|
|
||||||
Self {
|
|
||||||
component: c,
|
|
||||||
view_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: View, C: Component<V>> Deref for ComponentHost<V, C> {
|
|
||||||
type Target = C;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.component
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: View, C: Component<V>> DerefMut for ComponentHost<V, C> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.component
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: View, C: Component<V>> Element<V> for ComponentHost<V, C> {
|
|
||||||
type LayoutState = AnyElement<V>;
|
|
||||||
type PaintState = ();
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
constraint: SizeConstraint,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> (Vector2F, AnyElement<V>) {
|
|
||||||
let mut element = self.component.render(view, cx);
|
|
||||||
let size = element.layout(constraint, view, cx);
|
|
||||||
(size, element)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
scene: &mut SceneBuilder,
|
|
||||||
bounds: RectF,
|
|
||||||
visible_bounds: RectF,
|
|
||||||
element: &mut AnyElement<V>,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
) {
|
|
||||||
element.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rect_for_text_range(
|
|
||||||
&self,
|
|
||||||
range_utf16: Range<usize>,
|
|
||||||
_: RectF,
|
|
||||||
_: RectF,
|
|
||||||
element: &AnyElement<V>,
|
|
||||||
_: &(),
|
|
||||||
view: &V,
|
|
||||||
cx: &ViewContext<V>,
|
|
||||||
) -> Option<RectF> {
|
|
||||||
element.rect_for_text_range(range_utf16, view, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug(
|
|
||||||
&self,
|
|
||||||
_: RectF,
|
|
||||||
element: &AnyElement<V>,
|
|
||||||
_: &(),
|
|
||||||
view: &V,
|
|
||||||
cx: &ViewContext<V>,
|
|
||||||
) -> serde_json::Value {
|
|
||||||
element.debug(view, cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AnyRootElement {
|
pub trait AnyRootElement {
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
|
@ -12,10 +12,11 @@ use crate::{
|
||||||
scene::{self, Border, CursorRegion, Quad},
|
scene::{self, Border, CursorRegion, Quad},
|
||||||
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize)]
|
#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)]
|
||||||
pub struct ContainerStyle {
|
pub struct ContainerStyle {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub margin: Margin,
|
pub margin: Margin,
|
||||||
|
@ -332,7 +333,7 @@ impl ToJson for ContainerStyle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default, JsonSchema)]
|
||||||
pub struct Margin {
|
pub struct Margin {
|
||||||
pub top: f32,
|
pub top: f32,
|
||||||
pub left: f32,
|
pub left: f32,
|
||||||
|
@ -359,7 +360,7 @@ impl ToJson for Margin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default, JsonSchema)]
|
||||||
pub struct Padding {
|
pub struct Padding {
|
||||||
pub top: f32,
|
pub top: f32,
|
||||||
pub left: f32,
|
pub left: f32,
|
||||||
|
@ -486,9 +487,10 @@ impl ToJson for Padding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize)]
|
#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)]
|
||||||
pub struct Shadow {
|
pub struct Shadow {
|
||||||
#[serde(default, deserialize_with = "deserialize_vec2f")]
|
#[serde(default, deserialize_with = "deserialize_vec2f")]
|
||||||
|
#[schemars(with = "Vec::<f32>")]
|
||||||
offset: Vector2F,
|
offset: Vector2F,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
blur: f32,
|
blur: f32,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
scene, Border, Element, ImageData, LayoutContext, SceneBuilder, SizeConstraint, View,
|
scene, Border, Element, ImageData, LayoutContext, SceneBuilder, SizeConstraint, View,
|
||||||
ViewContext,
|
ViewContext,
|
||||||
};
|
};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{ops::Range, sync::Arc};
|
use std::{ops::Range, sync::Arc};
|
||||||
|
|
||||||
|
@ -21,7 +22,7 @@ pub struct Image {
|
||||||
style: ImageStyle,
|
style: ImageStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, Deserialize)]
|
#[derive(Copy, Clone, Default, Deserialize, JsonSchema)]
|
||||||
pub struct ImageStyle {
|
pub struct ImageStyle {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub border: Border,
|
pub border: Border,
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::{
|
||||||
text_layout::{Line, RunStyle},
|
text_layout::{Line, RunStyle},
|
||||||
Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
@ -20,7 +21,7 @@ pub struct Label {
|
||||||
highlight_indices: Vec<usize>,
|
highlight_indices: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
|
||||||
pub struct LabelStyle {
|
pub struct LabelStyle {
|
||||||
pub text: TextStyle,
|
pub text: TextStyle,
|
||||||
pub highlight_text: Option<TextStyle>,
|
pub highlight_text: Option<TextStyle>,
|
||||||
|
@ -164,6 +165,7 @@ impl<V: View> Element<V> for Label {
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut ViewContext<V>,
|
||||||
) -> Self::PaintState {
|
) -> Self::PaintState {
|
||||||
|
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||||
line.paint(
|
line.paint(
|
||||||
scene,
|
scene,
|
||||||
bounds.origin(),
|
bounds.origin(),
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use std::{borrow::Cow, ops::Range};
|
use super::constrain_size_preserving_aspect_ratio;
|
||||||
|
use crate::json::ToJson;
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Color,
|
color::Color,
|
||||||
geometry::{
|
geometry::{
|
||||||
|
@ -10,6 +8,10 @@ use crate::{
|
||||||
},
|
},
|
||||||
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
use serde_json::json;
|
||||||
|
use std::{borrow::Cow, ops::Range};
|
||||||
|
|
||||||
pub struct Svg {
|
pub struct Svg {
|
||||||
path: Cow<'static, str>,
|
path: Cow<'static, str>,
|
||||||
|
@ -24,6 +26,14 @@ impl Svg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_style<V: View>(style: SvgStyle) -> impl Element<V> {
|
||||||
|
Self::new(style.asset)
|
||||||
|
.with_color(style.color)
|
||||||
|
.constrained()
|
||||||
|
.with_width(style.dimensions.width)
|
||||||
|
.with_height(style.dimensions.height)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_color(mut self, color: Color) -> Self {
|
pub fn with_color(mut self, color: Color) -> Self {
|
||||||
self.color = color;
|
self.color = color;
|
||||||
self
|
self
|
||||||
|
@ -105,9 +115,24 @@ impl<V: View> Element<V> for Svg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::json::ToJson;
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
|
pub struct SvgStyle {
|
||||||
|
pub color: Color,
|
||||||
|
pub asset: String,
|
||||||
|
pub dimensions: Dimensions,
|
||||||
|
}
|
||||||
|
|
||||||
use super::constrain_size_preserving_aspect_ratio;
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
|
pub struct Dimensions {
|
||||||
|
pub width: f32,
|
||||||
|
pub height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dimensions {
|
||||||
|
pub fn to_vec(&self) -> Vector2F {
|
||||||
|
vec2f(self.width, self.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn from_usvg_rect(rect: usvg::Rect) -> RectF {
|
fn from_usvg_rect(rect: usvg::Rect) -> RectF {
|
||||||
RectF::new(
|
RectF::new(
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::{
|
||||||
Action, Axis, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint, Task, View,
|
Action, Axis, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint, Task, View,
|
||||||
ViewContext,
|
ViewContext,
|
||||||
};
|
};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
|
@ -33,7 +34,7 @@ struct TooltipState {
|
||||||
debounce: RefCell<Option<Task<()>>>,
|
debounce: RefCell<Option<Task<()>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct TooltipStyle {
|
pub struct TooltipStyle {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -42,7 +43,7 @@ pub struct TooltipStyle {
|
||||||
pub max_text_width: Option<f32>,
|
pub max_text_width: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct KeystrokeStyle {
|
pub struct KeystrokeStyle {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
container: ContainerStyle,
|
container: ContainerStyle,
|
||||||
|
|
|
@ -7,13 +7,14 @@ use crate::{
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
|
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, JsonSchema)]
|
||||||
pub struct FamilyId(usize);
|
pub struct FamilyId(usize);
|
||||||
|
|
||||||
struct Family {
|
struct Family {
|
||||||
|
|
|
@ -16,7 +16,7 @@ use serde::{de, Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::{cell::RefCell, sync::Arc};
|
use std::{cell::RefCell, sync::Arc};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, JsonSchema)]
|
||||||
pub struct FontId(pub usize);
|
pub struct FontId(pub usize);
|
||||||
|
|
||||||
pub type GlyphId = u32;
|
pub type GlyphId = u32;
|
||||||
|
@ -59,20 +59,44 @@ pub struct Features {
|
||||||
pub zero: Option<bool>,
|
pub zero: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, JsonSchema)]
|
||||||
pub struct TextStyle {
|
pub struct TextStyle {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub font_family_name: Arc<str>,
|
pub font_family_name: Arc<str>,
|
||||||
pub font_family_id: FamilyId,
|
pub font_family_id: FamilyId,
|
||||||
pub font_id: FontId,
|
pub font_id: FontId,
|
||||||
pub font_size: f32,
|
pub font_size: f32,
|
||||||
|
#[schemars(with = "PropertiesDef")]
|
||||||
pub font_properties: Properties,
|
pub font_properties: Properties,
|
||||||
pub underline: Underline,
|
pub underline: Underline,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
#[derive(JsonSchema)]
|
||||||
|
#[serde(remote = "Properties")]
|
||||||
|
pub struct PropertiesDef {
|
||||||
|
/// The font style, as defined in CSS.
|
||||||
|
pub style: StyleDef,
|
||||||
|
/// The font weight, as defined in CSS.
|
||||||
|
pub weight: f32,
|
||||||
|
/// The font stretchiness, as defined in CSS.
|
||||||
|
pub stretch: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(JsonSchema)]
|
||||||
|
#[schemars(remote = "Style")]
|
||||||
|
pub enum StyleDef {
|
||||||
|
/// A face that is neither italic not obliqued.
|
||||||
|
Normal,
|
||||||
|
/// A form that is generally cursive in nature.
|
||||||
|
Italic,
|
||||||
|
/// A typically-sloped version of the regular face.
|
||||||
|
Oblique,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, JsonSchema)]
|
||||||
pub struct HighlightStyle {
|
pub struct HighlightStyle {
|
||||||
pub color: Option<Color>,
|
pub color: Option<Color>,
|
||||||
|
#[schemars(with = "Option::<f32>")]
|
||||||
pub weight: Option<Weight>,
|
pub weight: Option<Weight>,
|
||||||
pub italic: Option<bool>,
|
pub italic: Option<bool>,
|
||||||
pub underline: Option<Underline>,
|
pub underline: Option<Underline>,
|
||||||
|
@ -81,9 +105,10 @@ pub struct HighlightStyle {
|
||||||
|
|
||||||
impl Eq for HighlightStyle {}
|
impl Eq for HighlightStyle {}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, JsonSchema)]
|
||||||
pub struct Underline {
|
pub struct Underline {
|
||||||
pub color: Option<Color>,
|
pub color: Option<Color>,
|
||||||
|
#[schemars(with = "f32")]
|
||||||
pub thickness: OrderedFloat<f32>,
|
pub thickness: OrderedFloat<f32>,
|
||||||
pub squiggly: bool,
|
pub squiggly: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub mod color;
|
||||||
pub mod json;
|
pub mod json;
|
||||||
pub mod keymap_matcher;
|
pub mod keymap_matcher;
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
pub use gpui_macros::test;
|
pub use gpui_macros::{test, Element};
|
||||||
pub use window::{Axis, SizeConstraint, Vector2FExt, WindowContext};
|
pub use window::{Axis, SizeConstraint, Vector2FExt, WindowContext};
|
||||||
|
|
||||||
pub use anyhow;
|
pub use anyhow;
|
||||||
|
|
|
@ -25,6 +25,7 @@ use anyhow::{anyhow, bail, Result};
|
||||||
use async_task::Runnable;
|
use async_task::Runnable;
|
||||||
pub use event::*;
|
pub use event::*;
|
||||||
use postage::oneshot;
|
use postage::oneshot;
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlez::{
|
use sqlez::{
|
||||||
bindable::{Bind, Column, StaticColumnCount},
|
bindable::{Bind, Column, StaticColumnCount},
|
||||||
|
@ -282,7 +283,7 @@ pub enum PromptLevel {
|
||||||
Critical,
|
Critical,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Deserialize)]
|
#[derive(Copy, Clone, Debug, Deserialize, JsonSchema)]
|
||||||
pub enum CursorStyle {
|
pub enum CursorStyle {
|
||||||
Arrow,
|
Arrow,
|
||||||
ResizeLeftRight,
|
ResizeLeftRight,
|
||||||
|
|
|
@ -3,6 +3,7 @@ mod mouse_region;
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::{borrow::Cow, sync::Arc};
|
use std::{borrow::Cow, sync::Arc};
|
||||||
|
@ -99,7 +100,7 @@ pub struct Icon {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Debug)]
|
#[derive(Clone, Copy, Default, Debug, JsonSchema)]
|
||||||
pub struct Border {
|
pub struct Border {
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
|
|
@ -3,8 +3,8 @@ use proc_macro2::Ident;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, parse_quote, spanned::Spanned as _, AttributeArgs, FnArg, ItemFn, Lit, Meta,
|
parse_macro_input, parse_quote, spanned::Spanned as _, AttributeArgs, DeriveInput, FnArg,
|
||||||
NestedMeta, Type,
|
ItemFn, Lit, Meta, NestedMeta, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
|
@ -275,3 +275,68 @@ fn parse_bool(literal: &Lit) -> Result<bool, TokenStream> {
|
||||||
|
|
||||||
result.map_err(|err| TokenStream::from(err.into_compile_error()))
|
result.map_err(|err| TokenStream::from(err.into_compile_error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(Element)]
|
||||||
|
pub fn element_derive(input: TokenStream) -> TokenStream {
|
||||||
|
// Parse the input tokens into a syntax tree
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
// The name of the struct/enum
|
||||||
|
let name = input.ident;
|
||||||
|
|
||||||
|
let expanded = quote! {
|
||||||
|
impl<V: gpui::View> gpui::elements::Element<V> for #name {
|
||||||
|
type LayoutState = gpui::elements::AnyElement<V>;
|
||||||
|
type PaintState = ();
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
constraint: gpui::SizeConstraint,
|
||||||
|
view: &mut V,
|
||||||
|
cx: &mut gpui::LayoutContext<V>,
|
||||||
|
) -> (gpui::geometry::vector::Vector2F, gpui::elements::AnyElement<V>) {
|
||||||
|
let mut element = self.render(view, cx);
|
||||||
|
let size = element.layout(constraint, view, cx);
|
||||||
|
(size, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(
|
||||||
|
&mut self,
|
||||||
|
scene: &mut gpui::SceneBuilder,
|
||||||
|
bounds: gpui::geometry::rect::RectF,
|
||||||
|
visible_bounds: gpui::geometry::rect::RectF,
|
||||||
|
element: &mut gpui::elements::AnyElement<V>,
|
||||||
|
view: &mut V,
|
||||||
|
cx: &mut gpui::ViewContext<V>,
|
||||||
|
) {
|
||||||
|
element.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rect_for_text_range(
|
||||||
|
&self,
|
||||||
|
range_utf16: std::ops::Range<usize>,
|
||||||
|
_: gpui::geometry::rect::RectF,
|
||||||
|
_: gpui::geometry::rect::RectF,
|
||||||
|
element: &gpui::elements::AnyElement<V>,
|
||||||
|
_: &(),
|
||||||
|
view: &V,
|
||||||
|
cx: &gpui::ViewContext<V>,
|
||||||
|
) -> Option<gpui::geometry::rect::RectF> {
|
||||||
|
element.rect_for_text_range(range_utf16, view, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug(
|
||||||
|
&self,
|
||||||
|
_: gpui::geometry::rect::RectF,
|
||||||
|
element: &gpui::elements::AnyElement<V>,
|
||||||
|
_: &(),
|
||||||
|
view: &V,
|
||||||
|
cx: &gpui::ViewContext<V>,
|
||||||
|
) -> serde_json::Value {
|
||||||
|
element.debug(view, cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Return generated code
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl View for ActiveBufferLanguage {
|
||||||
|
|
||||||
MouseEventHandler::<Self, Self>::new(0, cx, |state, cx| {
|
MouseEventHandler::<Self, Self>::new(0, cx, |state, cx| {
|
||||||
let theme = &theme::current(cx).workspace.status_bar;
|
let theme = &theme::current(cx).workspace.status_bar;
|
||||||
let style = theme.active_language.style_for(state, false);
|
let style = theme.active_language.style_for(state);
|
||||||
Label::new(active_language_text, style.text.clone())
|
Label::new(active_language_text, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
|
|
@ -180,7 +180,7 @@ impl PickerDelegate for LanguageSelectorDelegate {
|
||||||
) -> AnyElement<Picker<Self>> {
|
) -> AnyElement<Picker<Self>> {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let mat = &self.matches[ix];
|
let mat = &self.matches[ix];
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
let buffer_language_name = self.buffer.read(cx).language().map(|l| l.name());
|
let buffer_language_name = self.buffer.read(cx).language().map(|l| l.name());
|
||||||
let mut label = mat.string.clone();
|
let mut label = mat.string.clone();
|
||||||
if buffer_language_name.as_deref() == Some(mat.string.as_str()) {
|
if buffer_language_name.as_deref() == Some(mat.string.as_str()) {
|
||||||
|
|
|
@ -681,7 +681,7 @@ impl LspLogToolbarItemView {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| "No server selected".into());
|
.unwrap_or_else(|| "No server selected".into());
|
||||||
let style = theme.toolbar_dropdown_menu.header.style_for(state, false);
|
let style = theme.toolbar_dropdown_menu.header.style_for(state);
|
||||||
Label::new(label, style.text.clone())
|
Label::new(label, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -722,7 +722,8 @@ impl LspLogToolbarItemView {
|
||||||
let style = theme
|
let style = theme
|
||||||
.toolbar_dropdown_menu
|
.toolbar_dropdown_menu
|
||||||
.item
|
.item
|
||||||
.style_for(state, logs_selected);
|
.in_state(logs_selected)
|
||||||
|
.style_for(state);
|
||||||
Label::new(SERVER_LOGS, style.text.clone())
|
Label::new(SERVER_LOGS, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -739,7 +740,8 @@ impl LspLogToolbarItemView {
|
||||||
let style = theme
|
let style = theme
|
||||||
.toolbar_dropdown_menu
|
.toolbar_dropdown_menu
|
||||||
.item
|
.item
|
||||||
.style_for(state, rpc_trace_selected);
|
.in_state(rpc_trace_selected)
|
||||||
|
.style_for(state);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(RPC_MESSAGES, style.text.clone())
|
Label::new(RPC_MESSAGES, style.text.clone())
|
||||||
|
|
|
@ -565,7 +565,7 @@ impl SyntaxTreeToolbarItemView {
|
||||||
) -> impl Element<Self> {
|
) -> impl Element<Self> {
|
||||||
enum ToggleMenu {}
|
enum ToggleMenu {}
|
||||||
MouseEventHandler::<ToggleMenu, Self>::new(0, cx, move |state, _| {
|
MouseEventHandler::<ToggleMenu, Self>::new(0, cx, move |state, _| {
|
||||||
let style = theme.toolbar_dropdown_menu.header.style_for(state, false);
|
let style = theme.toolbar_dropdown_menu.header.style_for(state);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(active_layer.language.name().to_string(), style.text.clone())
|
Label::new(active_layer.language.name().to_string(), style.text.clone())
|
||||||
|
@ -601,7 +601,8 @@ impl SyntaxTreeToolbarItemView {
|
||||||
let style = theme
|
let style = theme
|
||||||
.toolbar_dropdown_menu
|
.toolbar_dropdown_menu
|
||||||
.item
|
.item
|
||||||
.style_for(state, is_selected);
|
.in_state(is_selected)
|
||||||
|
.style_for(state);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(layer.language.name().to_string(), style.text.clone())
|
Label::new(layer.language.name().to_string(), style.text.clone())
|
||||||
|
|
|
@ -33,7 +33,7 @@ const JSON_RPC_VERSION: &str = "2.0";
|
||||||
const CONTENT_LEN_HEADER: &str = "Content-Length: ";
|
const CONTENT_LEN_HEADER: &str = "Content-Length: ";
|
||||||
|
|
||||||
type NotificationHandler = Box<dyn Send + FnMut(Option<usize>, &str, AsyncAppContext)>;
|
type NotificationHandler = Box<dyn Send + FnMut(Option<usize>, &str, AsyncAppContext)>;
|
||||||
type ResponseHandler = Box<dyn Send + FnOnce(Result<&str, Error>)>;
|
type ResponseHandler = Box<dyn Send + FnOnce(Result<String, Error>)>;
|
||||||
type IoHandler = Box<dyn Send + FnMut(bool, &str)>;
|
type IoHandler = Box<dyn Send + FnMut(bool, &str)>;
|
||||||
|
|
||||||
pub struct LanguageServer {
|
pub struct LanguageServer {
|
||||||
|
@ -302,9 +302,9 @@ impl LanguageServer {
|
||||||
if let Some(error) = error {
|
if let Some(error) = error {
|
||||||
handler(Err(error));
|
handler(Err(error));
|
||||||
} else if let Some(result) = result {
|
} else if let Some(result) = result {
|
||||||
handler(Ok(result.get()));
|
handler(Ok(result.get().into()));
|
||||||
} else {
|
} else {
|
||||||
handler(Ok("null"));
|
handler(Ok("null".into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -457,11 +457,13 @@ impl LanguageServer {
|
||||||
let response_handlers = self.response_handlers.clone();
|
let response_handlers = self.response_handlers.clone();
|
||||||
let next_id = AtomicUsize::new(self.next_id.load(SeqCst));
|
let next_id = AtomicUsize::new(self.next_id.load(SeqCst));
|
||||||
let outbound_tx = self.outbound_tx.clone();
|
let outbound_tx = self.outbound_tx.clone();
|
||||||
|
let executor = self.executor.clone();
|
||||||
let mut output_done = self.output_done_rx.lock().take().unwrap();
|
let mut output_done = self.output_done_rx.lock().take().unwrap();
|
||||||
let shutdown_request = Self::request_internal::<request::Shutdown>(
|
let shutdown_request = Self::request_internal::<request::Shutdown>(
|
||||||
&next_id,
|
&next_id,
|
||||||
&response_handlers,
|
&response_handlers,
|
||||||
&outbound_tx,
|
&outbound_tx,
|
||||||
|
&executor,
|
||||||
(),
|
(),
|
||||||
);
|
);
|
||||||
let exit = Self::notify_internal::<notification::Exit>(&outbound_tx, ());
|
let exit = Self::notify_internal::<notification::Exit>(&outbound_tx, ());
|
||||||
|
@ -658,6 +660,7 @@ impl LanguageServer {
|
||||||
&self.next_id,
|
&self.next_id,
|
||||||
&self.response_handlers,
|
&self.response_handlers,
|
||||||
&self.outbound_tx,
|
&self.outbound_tx,
|
||||||
|
&self.executor,
|
||||||
params,
|
params,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -666,6 +669,7 @@ impl LanguageServer {
|
||||||
next_id: &AtomicUsize,
|
next_id: &AtomicUsize,
|
||||||
response_handlers: &Mutex<Option<HashMap<usize, ResponseHandler>>>,
|
response_handlers: &Mutex<Option<HashMap<usize, ResponseHandler>>>,
|
||||||
outbound_tx: &channel::Sender<String>,
|
outbound_tx: &channel::Sender<String>,
|
||||||
|
executor: &Arc<executor::Background>,
|
||||||
params: T::Params,
|
params: T::Params,
|
||||||
) -> impl 'static + Future<Output = Result<T::Result>>
|
) -> impl 'static + Future<Output = Result<T::Result>>
|
||||||
where
|
where
|
||||||
|
@ -686,15 +690,20 @@ impl LanguageServer {
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or_else(|| anyhow!("server shut down"))
|
.ok_or_else(|| anyhow!("server shut down"))
|
||||||
.map(|handlers| {
|
.map(|handlers| {
|
||||||
|
let executor = executor.clone();
|
||||||
handlers.insert(
|
handlers.insert(
|
||||||
id,
|
id,
|
||||||
Box::new(move |result| {
|
Box::new(move |result| {
|
||||||
let response = match result {
|
executor
|
||||||
Ok(response) => serde_json::from_str(response)
|
.spawn(async move {
|
||||||
.context("failed to deserialize response"),
|
let response = match result {
|
||||||
Err(error) => Err(anyhow!("{}", error.message)),
|
Ok(response) => serde_json::from_str(&response)
|
||||||
};
|
.context("failed to deserialize response"),
|
||||||
let _ = tx.send(response);
|
Err(error) => Err(anyhow!("{}", error.message)),
|
||||||
|
};
|
||||||
|
let _ = tx.send(response);
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -204,7 +204,7 @@ impl PickerDelegate for OutlineViewDelegate {
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> AnyElement<Picker<Self>> {
|
) -> AnyElement<Picker<Self>> {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
let string_match = &self.matches[ix];
|
let string_match = &self.matches[ix];
|
||||||
let outline_item = &self.outline.items[string_match.candidate_id];
|
let outline_item = &self.outline.items[string_match.candidate_id];
|
||||||
|
|
||||||
|
|
|
@ -153,6 +153,7 @@ pub fn init(cx: &mut AppContext) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
OpenedEntry {
|
OpenedEntry {
|
||||||
entry_id: ProjectEntryId,
|
entry_id: ProjectEntryId,
|
||||||
|
@ -1253,7 +1254,10 @@ impl ProjectPanel {
|
||||||
let show_editor = details.is_editing && !details.is_processing;
|
let show_editor = details.is_editing && !details.is_processing;
|
||||||
|
|
||||||
MouseEventHandler::<Self, _>::new(entry_id.to_usize(), cx, |state, cx| {
|
MouseEventHandler::<Self, _>::new(entry_id.to_usize(), cx, |state, cx| {
|
||||||
let mut style = entry_style.style_for(state, details.is_selected).clone();
|
let mut style = entry_style
|
||||||
|
.in_state(details.is_selected)
|
||||||
|
.style_for(state)
|
||||||
|
.clone();
|
||||||
|
|
||||||
if cx
|
if cx
|
||||||
.global::<DragAndDrop<Workspace>>()
|
.global::<DragAndDrop<Workspace>>()
|
||||||
|
@ -1264,7 +1268,7 @@ impl ProjectPanel {
|
||||||
.filter(|destination| details.path.starts_with(destination))
|
.filter(|destination| details.path.starts_with(destination))
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
style = entry_style.active.clone().unwrap();
|
style = entry_style.active_state().default.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let row_container_style = if show_editor {
|
let row_container_style = if show_editor {
|
||||||
|
@ -1405,9 +1409,11 @@ impl View for ProjectPanel {
|
||||||
let button_style = theme.open_project_button.clone();
|
let button_style = theme.open_project_button.clone();
|
||||||
let context_menu_item_style = theme::current(cx).context_menu.item.clone();
|
let context_menu_item_style = theme::current(cx).context_menu.item.clone();
|
||||||
move |state, cx| {
|
move |state, cx| {
|
||||||
let button_style = button_style.style_for(state, false).clone();
|
let button_style = button_style.style_for(state).clone();
|
||||||
let context_menu_item =
|
let context_menu_item = context_menu_item_style
|
||||||
context_menu_item_style.style_for(state, true).clone();
|
.active_state()
|
||||||
|
.style_for(state)
|
||||||
|
.clone();
|
||||||
|
|
||||||
theme::ui::keystroke_label(
|
theme::ui::keystroke_label(
|
||||||
"Open a project",
|
"Open a project",
|
||||||
|
|
|
@ -196,7 +196,7 @@ impl PickerDelegate for ProjectSymbolsDelegate {
|
||||||
) -> AnyElement<Picker<Self>> {
|
) -> AnyElement<Picker<Self>> {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = &theme.picker.item;
|
let style = &theme.picker.item;
|
||||||
let current_style = style.style_for(mouse_state, selected);
|
let current_style = style.in_state(selected).style_for(mouse_state);
|
||||||
|
|
||||||
let string_match = &self.matches[ix];
|
let string_match = &self.matches[ix];
|
||||||
let symbol = &self.symbols[string_match.candidate_id];
|
let symbol = &self.symbols[string_match.candidate_id];
|
||||||
|
@ -229,7 +229,10 @@ impl PickerDelegate for ProjectSymbolsDelegate {
|
||||||
.with_child(
|
.with_child(
|
||||||
// Avoid styling the path differently when it is selected, since
|
// Avoid styling the path differently when it is selected, since
|
||||||
// the symbol's syntax highlighting doesn't change when selected.
|
// the symbol's syntax highlighting doesn't change when selected.
|
||||||
Label::new(path.to_string(), style.default.label.clone()),
|
Label::new(
|
||||||
|
path.to_string(),
|
||||||
|
style.inactive_state().default.label.clone(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(current_style.container)
|
.with_style(current_style.container)
|
||||||
|
|
|
@ -173,7 +173,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||||
cx: &gpui::AppContext,
|
cx: &gpui::AppContext,
|
||||||
) -> AnyElement<Picker<Self>> {
|
) -> AnyElement<Picker<Self>> {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
|
|
||||||
let string_match = &self.matches[ix];
|
let string_match = &self.matches[ix];
|
||||||
|
|
||||||
|
|
|
@ -259,7 +259,11 @@ impl BufferSearchBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
|
pub fn is_dismissed(&self) -> bool {
|
||||||
|
self.dismissed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
|
||||||
self.dismissed = true;
|
self.dismissed = true;
|
||||||
for searchable_item in self.seachable_items_with_matches.keys() {
|
for searchable_item in self.seachable_items_with_matches.keys() {
|
||||||
if let Some(searchable_item) =
|
if let Some(searchable_item) =
|
||||||
|
@ -275,7 +279,7 @@ impl BufferSearchBar {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext<Self>) -> bool {
|
pub fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext<Self>) -> bool {
|
||||||
let searchable_item = if let Some(searchable_item) = &self.active_searchable_item {
|
let searchable_item = if let Some(searchable_item) = &self.active_searchable_item {
|
||||||
SearchableItemHandle::boxed_clone(searchable_item.as_ref())
|
SearchableItemHandle::boxed_clone(searchable_item.as_ref())
|
||||||
} else {
|
} else {
|
||||||
|
@ -328,7 +332,11 @@ impl BufferSearchBar {
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| {
|
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.search.option_button.style_for(state, is_active);
|
let style = theme
|
||||||
|
.search
|
||||||
|
.option_button
|
||||||
|
.in_state(is_active)
|
||||||
|
.style_for(state);
|
||||||
Label::new(icon, style.text.clone())
|
Label::new(icon, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -371,7 +379,7 @@ impl BufferSearchBar {
|
||||||
enum NavButton {}
|
enum NavButton {}
|
||||||
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
|
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.search.option_button.style_for(state, false);
|
let style = theme.search.option_button.inactive_state().style_for(state);
|
||||||
Label::new(icon, style.text.clone())
|
Label::new(icon, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -403,7 +411,7 @@ impl BufferSearchBar {
|
||||||
|
|
||||||
enum CloseButton {}
|
enum CloseButton {}
|
||||||
MouseEventHandler::<CloseButton, _>::new(0, cx, |state, _| {
|
MouseEventHandler::<CloseButton, _>::new(0, cx, |state, _| {
|
||||||
let style = theme.dismiss_button.style_for(state, false);
|
let style = theme.dismiss_button.style_for(state);
|
||||||
Svg::new("icons/x_mark_8.svg")
|
Svg::new("icons/x_mark_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -480,7 +488,7 @@ impl BufferSearchBar {
|
||||||
self.select_match(Direction::Prev, cx);
|
self.select_match(Direction::Prev, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
|
pub fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(index) = self.active_match_index {
|
if let Some(index) = self.active_match_index {
|
||||||
if let Some(searchable_item) = self.active_searchable_item.as_ref() {
|
if let Some(searchable_item) = self.active_searchable_item.as_ref() {
|
||||||
if let Some(matches) = self
|
if let Some(matches) = self
|
||||||
|
|
|
@ -896,7 +896,7 @@ impl ProjectSearchBar {
|
||||||
enum NavButton {}
|
enum NavButton {}
|
||||||
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
|
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.search.option_button.style_for(state, false);
|
let style = theme.search.option_button.inactive_state().style_for(state);
|
||||||
Label::new(icon, style.text.clone())
|
Label::new(icon, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -927,7 +927,11 @@ impl ProjectSearchBar {
|
||||||
let is_active = self.is_option_enabled(option, cx);
|
let is_active = self.is_option_enabled(option, cx);
|
||||||
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| {
|
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.search.option_button.style_for(state, is_active);
|
let style = theme
|
||||||
|
.search
|
||||||
|
.option_button
|
||||||
|
.in_state(is_active)
|
||||||
|
.style_for(state);
|
||||||
Label::new(icon, style.text.clone())
|
Label::new(icon, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
|
|
@ -25,6 +25,7 @@ pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(TerminalPanel::new_terminal);
|
cx.add_action(TerminalPanel::new_terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Close,
|
Close,
|
||||||
DockPositionChanged,
|
DockPositionChanged,
|
||||||
|
|
|
@ -4,15 +4,16 @@ pub mod ui;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
color::Color,
|
||||||
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, TooltipStyle},
|
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, SvgStyle, TooltipStyle},
|
||||||
fonts::{HighlightStyle, TextStyle},
|
fonts::{HighlightStyle, TextStyle},
|
||||||
platform, AppContext, AssetSource, Border, MouseState,
|
platform, AppContext, AssetSource, Border, MouseState,
|
||||||
};
|
};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::{de::DeserializeOwned, Deserialize};
|
use serde::{de::DeserializeOwned, Deserialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle, SvgStyle};
|
use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle};
|
||||||
|
|
||||||
pub use theme_registry::*;
|
pub use theme_registry::*;
|
||||||
pub use theme_settings::*;
|
pub use theme_settings::*;
|
||||||
|
@ -36,7 +37,7 @@ pub fn init(source: impl AssetSource, cx: &mut AppContext) {
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub meta: ThemeMeta,
|
pub meta: ThemeMeta,
|
||||||
|
@ -67,7 +68,7 @@ pub struct Theme {
|
||||||
pub color_scheme: ColorScheme,
|
pub color_scheme: ColorScheme,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone)]
|
#[derive(Deserialize, Default, Clone, JsonSchema)]
|
||||||
pub struct ThemeMeta {
|
pub struct ThemeMeta {
|
||||||
#[serde(skip_deserializing)]
|
#[serde(skip_deserializing)]
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
|
@ -75,7 +76,7 @@ pub struct ThemeMeta {
|
||||||
pub is_light: bool,
|
pub is_light: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct Workspace {
|
pub struct Workspace {
|
||||||
pub background: Color,
|
pub background: Color,
|
||||||
pub blank_pane: BlankPaneStyle,
|
pub blank_pane: BlankPaneStyle,
|
||||||
|
@ -102,7 +103,7 @@ pub struct Workspace {
|
||||||
pub drop_target_overlay_color: Color,
|
pub drop_target_overlay_color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct BlankPaneStyle {
|
pub struct BlankPaneStyle {
|
||||||
pub logo: SvgStyle,
|
pub logo: SvgStyle,
|
||||||
pub logo_shadow: SvgStyle,
|
pub logo_shadow: SvgStyle,
|
||||||
|
@ -112,7 +113,7 @@ pub struct BlankPaneStyle {
|
||||||
pub keyboard_hint_width: f32,
|
pub keyboard_hint_width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Titlebar {
|
pub struct Titlebar {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -128,16 +129,16 @@ pub struct Titlebar {
|
||||||
pub leader_avatar: AvatarStyle,
|
pub leader_avatar: AvatarStyle,
|
||||||
pub follower_avatar: AvatarStyle,
|
pub follower_avatar: AvatarStyle,
|
||||||
pub inactive_avatar_grayscale: bool,
|
pub inactive_avatar_grayscale: bool,
|
||||||
pub sign_in_prompt: Interactive<ContainedText>,
|
pub sign_in_prompt: Toggleable<Interactive<ContainedText>>,
|
||||||
pub outdated_warning: ContainedText,
|
pub outdated_warning: ContainedText,
|
||||||
pub share_button: Interactive<ContainedText>,
|
pub share_button: Toggleable<Interactive<ContainedText>>,
|
||||||
pub call_control: Interactive<IconButton>,
|
pub call_control: Interactive<IconButton>,
|
||||||
pub toggle_contacts_button: Interactive<IconButton>,
|
pub toggle_contacts_button: Toggleable<Interactive<IconButton>>,
|
||||||
pub user_menu_button: Interactive<IconButton>,
|
pub user_menu_button: Toggleable<Interactive<IconButton>>,
|
||||||
pub toggle_contacts_badge: ContainerStyle,
|
pub toggle_contacts_badge: ContainerStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Deserialize, Default)]
|
#[derive(Copy, Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct AvatarStyle {
|
pub struct AvatarStyle {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub image: ImageStyle,
|
pub image: ImageStyle,
|
||||||
|
@ -145,14 +146,14 @@ pub struct AvatarStyle {
|
||||||
pub outer_corner_radius: f32,
|
pub outer_corner_radius: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone)]
|
#[derive(Deserialize, Default, Clone, JsonSchema)]
|
||||||
pub struct Copilot {
|
pub struct Copilot {
|
||||||
pub out_link_icon: Interactive<IconStyle>,
|
pub out_link_icon: Interactive<IconStyle>,
|
||||||
pub modal: ModalStyle,
|
pub modal: ModalStyle,
|
||||||
pub auth: CopilotAuth,
|
pub auth: CopilotAuth,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone)]
|
#[derive(Deserialize, Default, Clone, JsonSchema)]
|
||||||
pub struct CopilotAuth {
|
pub struct CopilotAuth {
|
||||||
pub content_width: f32,
|
pub content_width: f32,
|
||||||
pub prompting: CopilotAuthPrompting,
|
pub prompting: CopilotAuthPrompting,
|
||||||
|
@ -162,14 +163,14 @@ pub struct CopilotAuth {
|
||||||
pub header: IconStyle,
|
pub header: IconStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone)]
|
#[derive(Deserialize, Default, Clone, JsonSchema)]
|
||||||
pub struct CopilotAuthPrompting {
|
pub struct CopilotAuthPrompting {
|
||||||
pub subheading: ContainedText,
|
pub subheading: ContainedText,
|
||||||
pub hint: ContainedText,
|
pub hint: ContainedText,
|
||||||
pub device_code: DeviceCode,
|
pub device_code: DeviceCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone)]
|
#[derive(Deserialize, Default, Clone, JsonSchema)]
|
||||||
pub struct DeviceCode {
|
pub struct DeviceCode {
|
||||||
pub text: TextStyle,
|
pub text: TextStyle,
|
||||||
pub cta: ButtonStyle,
|
pub cta: ButtonStyle,
|
||||||
|
@ -179,19 +180,19 @@ pub struct DeviceCode {
|
||||||
pub right_container: Interactive<ContainerStyle>,
|
pub right_container: Interactive<ContainerStyle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone)]
|
#[derive(Deserialize, Default, Clone, JsonSchema)]
|
||||||
pub struct CopilotAuthNotAuthorized {
|
pub struct CopilotAuthNotAuthorized {
|
||||||
pub subheading: ContainedText,
|
pub subheading: ContainedText,
|
||||||
pub warning: ContainedText,
|
pub warning: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone)]
|
#[derive(Deserialize, Default, Clone, JsonSchema)]
|
||||||
pub struct CopilotAuthAuthorized {
|
pub struct CopilotAuthAuthorized {
|
||||||
pub subheading: ContainedText,
|
pub subheading: ContainedText,
|
||||||
pub hint: ContainedText,
|
pub hint: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct ContactsPopover {
|
pub struct ContactsPopover {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -199,17 +200,17 @@ pub struct ContactsPopover {
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct ContactList {
|
pub struct ContactList {
|
||||||
pub user_query_editor: FieldEditor,
|
pub user_query_editor: FieldEditor,
|
||||||
pub user_query_editor_height: f32,
|
pub user_query_editor_height: f32,
|
||||||
pub add_contact_button: IconButton,
|
pub add_contact_button: IconButton,
|
||||||
pub header_row: Interactive<ContainedText>,
|
pub header_row: Toggleable<Interactive<ContainedText>>,
|
||||||
pub leave_call: Interactive<ContainedText>,
|
pub leave_call: Interactive<ContainedText>,
|
||||||
pub contact_row: Interactive<ContainerStyle>,
|
pub contact_row: Toggleable<Interactive<ContainerStyle>>,
|
||||||
pub row_height: f32,
|
pub row_height: f32,
|
||||||
pub project_row: Interactive<ProjectRow>,
|
pub project_row: Toggleable<Interactive<ProjectRow>>,
|
||||||
pub tree_branch: Interactive<TreeBranch>,
|
pub tree_branch: Toggleable<Interactive<TreeBranch>>,
|
||||||
pub contact_avatar: ImageStyle,
|
pub contact_avatar: ImageStyle,
|
||||||
pub contact_status_free: ContainerStyle,
|
pub contact_status_free: ContainerStyle,
|
||||||
pub contact_status_busy: ContainerStyle,
|
pub contact_status_busy: ContainerStyle,
|
||||||
|
@ -221,7 +222,7 @@ pub struct ContactList {
|
||||||
pub calling_indicator: ContainedText,
|
pub calling_indicator: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct ProjectRow {
|
pub struct ProjectRow {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -229,13 +230,13 @@ pub struct ProjectRow {
|
||||||
pub name: ContainedText,
|
pub name: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone, Copy)]
|
#[derive(Deserialize, Default, Clone, Copy, JsonSchema)]
|
||||||
pub struct TreeBranch {
|
pub struct TreeBranch {
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct ContactFinder {
|
pub struct ContactFinder {
|
||||||
pub picker: Picker,
|
pub picker: Picker,
|
||||||
pub row_height: f32,
|
pub row_height: f32,
|
||||||
|
@ -245,17 +246,17 @@ pub struct ContactFinder {
|
||||||
pub disabled_contact_button: IconButton,
|
pub disabled_contact_button: IconButton,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct DropdownMenu {
|
pub struct DropdownMenu {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub header: Interactive<DropdownMenuItem>,
|
pub header: Interactive<DropdownMenuItem>,
|
||||||
pub section_header: ContainedText,
|
pub section_header: ContainedText,
|
||||||
pub item: Interactive<DropdownMenuItem>,
|
pub item: Toggleable<Interactive<DropdownMenuItem>>,
|
||||||
pub row_height: f32,
|
pub row_height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct DropdownMenuItem {
|
pub struct DropdownMenuItem {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -266,11 +267,11 @@ pub struct DropdownMenuItem {
|
||||||
pub secondary_text_spacing: f32,
|
pub secondary_text_spacing: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct TabBar {
|
pub struct TabBar {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub pane_button: Interactive<IconButton>,
|
pub pane_button: Toggleable<Interactive<IconButton>>,
|
||||||
pub pane_button_container: ContainerStyle,
|
pub pane_button_container: ContainerStyle,
|
||||||
pub active_pane: TabStyles,
|
pub active_pane: TabStyles,
|
||||||
pub inactive_pane: TabStyles,
|
pub inactive_pane: TabStyles,
|
||||||
|
@ -294,13 +295,13 @@ impl TabBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct TabStyles {
|
pub struct TabStyles {
|
||||||
pub active_tab: Tab,
|
pub active_tab: Tab,
|
||||||
pub inactive_tab: Tab,
|
pub inactive_tab: Tab,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct AvatarRibbon {
|
pub struct AvatarRibbon {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -308,7 +309,7 @@ pub struct AvatarRibbon {
|
||||||
pub height: f32,
|
pub height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct OfflineIcon {
|
pub struct OfflineIcon {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -316,7 +317,7 @@ pub struct OfflineIcon {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
pub height: f32,
|
pub height: f32,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -333,7 +334,7 @@ pub struct Tab {
|
||||||
pub icon_conflict: Color,
|
pub icon_conflict: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Toolbar {
|
pub struct Toolbar {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -342,14 +343,14 @@ pub struct Toolbar {
|
||||||
pub nav_button: Interactive<IconButton>,
|
pub nav_button: Interactive<IconButton>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Notifications {
|
pub struct Notifications {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Search {
|
pub struct Search {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -359,14 +360,14 @@ pub struct Search {
|
||||||
pub include_exclude_editor: FindEditor,
|
pub include_exclude_editor: FindEditor,
|
||||||
pub invalid_include_exclude_editor: ContainerStyle,
|
pub invalid_include_exclude_editor: ContainerStyle,
|
||||||
pub include_exclude_inputs: ContainedText,
|
pub include_exclude_inputs: ContainedText,
|
||||||
pub option_button: Interactive<ContainedText>,
|
pub option_button: Toggleable<Interactive<ContainedText>>,
|
||||||
pub match_background: Color,
|
pub match_background: Color,
|
||||||
pub match_index: ContainedText,
|
pub match_index: ContainedText,
|
||||||
pub results_status: TextStyle,
|
pub results_status: TextStyle,
|
||||||
pub dismiss_button: Interactive<IconButton>,
|
pub dismiss_button: Interactive<IconButton>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct FindEditor {
|
pub struct FindEditor {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub input: FieldEditor,
|
pub input: FieldEditor,
|
||||||
|
@ -374,7 +375,7 @@ pub struct FindEditor {
|
||||||
pub max_width: f32,
|
pub max_width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct StatusBar {
|
pub struct StatusBar {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -390,15 +391,15 @@ pub struct StatusBar {
|
||||||
pub diagnostic_message: Interactive<ContainedText>,
|
pub diagnostic_message: Interactive<ContainedText>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct StatusBarPanelButtons {
|
pub struct StatusBarPanelButtons {
|
||||||
pub group_left: ContainerStyle,
|
pub group_left: ContainerStyle,
|
||||||
pub group_bottom: ContainerStyle,
|
pub group_bottom: ContainerStyle,
|
||||||
pub group_right: ContainerStyle,
|
pub group_right: ContainerStyle,
|
||||||
pub button: Interactive<PanelButton>,
|
pub button: Toggleable<Interactive<PanelButton>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct StatusBarDiagnosticSummary {
|
pub struct StatusBarDiagnosticSummary {
|
||||||
pub container_ok: ContainerStyle,
|
pub container_ok: ContainerStyle,
|
||||||
pub container_warning: ContainerStyle,
|
pub container_warning: ContainerStyle,
|
||||||
|
@ -413,7 +414,7 @@ pub struct StatusBarDiagnosticSummary {
|
||||||
pub summary_spacing: f32,
|
pub summary_spacing: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct StatusBarLspStatus {
|
pub struct StatusBarLspStatus {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -424,14 +425,14 @@ pub struct StatusBarLspStatus {
|
||||||
pub message: TextStyle,
|
pub message: TextStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct Dock {
|
pub struct Dock {
|
||||||
pub left: ContainerStyle,
|
pub left: ContainerStyle,
|
||||||
pub bottom: ContainerStyle,
|
pub bottom: ContainerStyle,
|
||||||
pub right: ContainerStyle,
|
pub right: ContainerStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct PanelButton {
|
pub struct PanelButton {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -440,20 +441,20 @@ pub struct PanelButton {
|
||||||
pub label: ContainedText,
|
pub label: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct ProjectPanel {
|
pub struct ProjectPanel {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub entry: Interactive<ProjectPanelEntry>,
|
pub entry: Toggleable<Interactive<ProjectPanelEntry>>,
|
||||||
pub dragged_entry: ProjectPanelEntry,
|
pub dragged_entry: ProjectPanelEntry,
|
||||||
pub ignored_entry: Interactive<ProjectPanelEntry>,
|
pub ignored_entry: Toggleable<Interactive<ProjectPanelEntry>>,
|
||||||
pub cut_entry: Interactive<ProjectPanelEntry>,
|
pub cut_entry: Toggleable<Interactive<ProjectPanelEntry>>,
|
||||||
pub filename_editor: FieldEditor,
|
pub filename_editor: FieldEditor,
|
||||||
pub indent_width: f32,
|
pub indent_width: f32,
|
||||||
pub open_project_button: Interactive<ContainedText>,
|
pub open_project_button: Interactive<ContainedText>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
|
||||||
pub struct ProjectPanelEntry {
|
pub struct ProjectPanelEntry {
|
||||||
pub height: f32,
|
pub height: f32,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -465,28 +466,28 @@ pub struct ProjectPanelEntry {
|
||||||
pub status: EntryStatus,
|
pub status: EntryStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
|
||||||
pub struct EntryStatus {
|
pub struct EntryStatus {
|
||||||
pub git: GitProjectStatus,
|
pub git: GitProjectStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
|
||||||
pub struct GitProjectStatus {
|
pub struct GitProjectStatus {
|
||||||
pub modified: Color,
|
pub modified: Color,
|
||||||
pub inserted: Color,
|
pub inserted: Color,
|
||||||
pub conflict: Color,
|
pub conflict: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
|
||||||
pub struct ContextMenu {
|
pub struct ContextMenu {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub item: Interactive<ContextMenuItem>,
|
pub item: Toggleable<Interactive<ContextMenuItem>>,
|
||||||
pub keystroke_margin: f32,
|
pub keystroke_margin: f32,
|
||||||
pub separator: ContainerStyle,
|
pub separator: ContainerStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
|
||||||
pub struct ContextMenuItem {
|
pub struct ContextMenuItem {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -496,13 +497,13 @@ pub struct ContextMenuItem {
|
||||||
pub icon_spacing: f32,
|
pub icon_spacing: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Default)]
|
#[derive(Debug, Deserialize, Default, JsonSchema)]
|
||||||
pub struct CommandPalette {
|
pub struct CommandPalette {
|
||||||
pub key: Interactive<ContainedLabel>,
|
pub key: Toggleable<ContainedLabel>,
|
||||||
pub keystroke_spacing: f32,
|
pub keystroke_spacing: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct InviteLink {
|
pub struct InviteLink {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -511,7 +512,7 @@ pub struct InviteLink {
|
||||||
pub icon: Icon,
|
pub icon: Icon,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Copy, Default)]
|
#[derive(Deserialize, Clone, Copy, Default, JsonSchema)]
|
||||||
pub struct Icon {
|
pub struct Icon {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -519,7 +520,7 @@ pub struct Icon {
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Copy, Default)]
|
#[derive(Deserialize, Clone, Copy, Default, JsonSchema)]
|
||||||
pub struct IconButton {
|
pub struct IconButton {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -528,7 +529,7 @@ pub struct IconButton {
|
||||||
pub button_width: f32,
|
pub button_width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct ChatMessage {
|
pub struct ChatMessage {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -537,7 +538,7 @@ pub struct ChatMessage {
|
||||||
pub timestamp: ContainedText,
|
pub timestamp: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct ChannelSelect {
|
pub struct ChannelSelect {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -549,7 +550,7 @@ pub struct ChannelSelect {
|
||||||
pub menu: ContainerStyle,
|
pub menu: ContainerStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct ChannelName {
|
pub struct ChannelName {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -557,7 +558,7 @@ pub struct ChannelName {
|
||||||
pub name: TextStyle,
|
pub name: TextStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Picker {
|
pub struct Picker {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -565,10 +566,10 @@ pub struct Picker {
|
||||||
pub input_editor: FieldEditor,
|
pub input_editor: FieldEditor,
|
||||||
pub empty_input_editor: FieldEditor,
|
pub empty_input_editor: FieldEditor,
|
||||||
pub no_matches: ContainedLabel,
|
pub no_matches: ContainedLabel,
|
||||||
pub item: Interactive<ContainedLabel>,
|
pub item: Toggleable<Interactive<ContainedLabel>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
|
||||||
pub struct ContainedText {
|
pub struct ContainedText {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -576,7 +577,7 @@ pub struct ContainedText {
|
||||||
pub text: TextStyle,
|
pub text: TextStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
|
||||||
pub struct ContainedLabel {
|
pub struct ContainedLabel {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -584,7 +585,7 @@ pub struct ContainedLabel {
|
||||||
pub label: LabelStyle,
|
pub label: LabelStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct ProjectDiagnostics {
|
pub struct ProjectDiagnostics {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -594,7 +595,7 @@ pub struct ProjectDiagnostics {
|
||||||
pub tab_summary_spacing: f32,
|
pub tab_summary_spacing: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct ContactNotification {
|
pub struct ContactNotification {
|
||||||
pub header_avatar: ImageStyle,
|
pub header_avatar: ImageStyle,
|
||||||
pub header_message: ContainedText,
|
pub header_message: ContainedText,
|
||||||
|
@ -604,21 +605,21 @@ pub struct ContactNotification {
|
||||||
pub dismiss_button: Interactive<IconButton>,
|
pub dismiss_button: Interactive<IconButton>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct UpdateNotification {
|
pub struct UpdateNotification {
|
||||||
pub message: ContainedText,
|
pub message: ContainedText,
|
||||||
pub action_message: Interactive<ContainedText>,
|
pub action_message: Interactive<ContainedText>,
|
||||||
pub dismiss_button: Interactive<IconButton>,
|
pub dismiss_button: Interactive<IconButton>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct MessageNotification {
|
pub struct MessageNotification {
|
||||||
pub message: ContainedText,
|
pub message: ContainedText,
|
||||||
pub action_message: Interactive<ContainedText>,
|
pub action_message: Interactive<ContainedText>,
|
||||||
pub dismiss_button: Interactive<IconButton>,
|
pub dismiss_button: Interactive<IconButton>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct ProjectSharedNotification {
|
pub struct ProjectSharedNotification {
|
||||||
pub window_height: f32,
|
pub window_height: f32,
|
||||||
pub window_width: f32,
|
pub window_width: f32,
|
||||||
|
@ -635,7 +636,7 @@ pub struct ProjectSharedNotification {
|
||||||
pub dismiss_button: ContainedText,
|
pub dismiss_button: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default, JsonSchema)]
|
||||||
pub struct IncomingCallNotification {
|
pub struct IncomingCallNotification {
|
||||||
pub window_height: f32,
|
pub window_height: f32,
|
||||||
pub window_width: f32,
|
pub window_width: f32,
|
||||||
|
@ -652,7 +653,7 @@ pub struct IncomingCallNotification {
|
||||||
pub decline_button: ContainedText,
|
pub decline_button: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Editor {
|
pub struct Editor {
|
||||||
pub text_color: Color,
|
pub text_color: Color,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -693,7 +694,7 @@ pub struct Editor {
|
||||||
pub whitespace: Color,
|
pub whitespace: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Scrollbar {
|
pub struct Scrollbar {
|
||||||
pub track: ContainerStyle,
|
pub track: ContainerStyle,
|
||||||
pub thumb: ContainerStyle,
|
pub thumb: ContainerStyle,
|
||||||
|
@ -702,14 +703,14 @@ pub struct Scrollbar {
|
||||||
pub git: GitDiffColors,
|
pub git: GitDiffColors,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct GitDiffColors {
|
pub struct GitDiffColors {
|
||||||
pub inserted: Color,
|
pub inserted: Color,
|
||||||
pub modified: Color,
|
pub modified: Color,
|
||||||
pub deleted: Color,
|
pub deleted: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct DiagnosticPathHeader {
|
pub struct DiagnosticPathHeader {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -718,7 +719,7 @@ pub struct DiagnosticPathHeader {
|
||||||
pub text_scale_factor: f32,
|
pub text_scale_factor: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct DiagnosticHeader {
|
pub struct DiagnosticHeader {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -729,7 +730,7 @@ pub struct DiagnosticHeader {
|
||||||
pub icon_width_factor: f32,
|
pub icon_width_factor: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct DiagnosticStyle {
|
pub struct DiagnosticStyle {
|
||||||
pub message: LabelStyle,
|
pub message: LabelStyle,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -737,7 +738,7 @@ pub struct DiagnosticStyle {
|
||||||
pub text_scale_factor: f32,
|
pub text_scale_factor: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct AutocompleteStyle {
|
pub struct AutocompleteStyle {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -747,13 +748,13 @@ pub struct AutocompleteStyle {
|
||||||
pub match_highlight: HighlightStyle,
|
pub match_highlight: HighlightStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Deserialize)]
|
#[derive(Clone, Copy, Default, Deserialize, JsonSchema)]
|
||||||
pub struct SelectionStyle {
|
pub struct SelectionStyle {
|
||||||
pub cursor: Color,
|
pub cursor: Color,
|
||||||
pub selection: Color,
|
pub selection: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct FieldEditor {
|
pub struct FieldEditor {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -763,21 +764,21 @@ pub struct FieldEditor {
|
||||||
pub selection: SelectionStyle,
|
pub selection: SelectionStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct InteractiveColor {
|
pub struct InteractiveColor {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct CodeActions {
|
pub struct CodeActions {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub indicator: Interactive<InteractiveColor>,
|
pub indicator: Toggleable<Interactive<InteractiveColor>>,
|
||||||
pub vertical_scale: f32,
|
pub vertical_scale: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Folds {
|
pub struct Folds {
|
||||||
pub indicator: Interactive<InteractiveColor>,
|
pub indicator: Toggleable<Interactive<InteractiveColor>>,
|
||||||
pub ellipses: FoldEllipses,
|
pub ellipses: FoldEllipses,
|
||||||
pub fold_background: Color,
|
pub fold_background: Color,
|
||||||
pub icon_margin_scale: f32,
|
pub icon_margin_scale: f32,
|
||||||
|
@ -785,14 +786,14 @@ pub struct Folds {
|
||||||
pub foldable_icon: String,
|
pub foldable_icon: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct FoldEllipses {
|
pub struct FoldEllipses {
|
||||||
pub text_color: Color,
|
pub text_color: Color,
|
||||||
pub background: Interactive<InteractiveColor>,
|
pub background: Interactive<InteractiveColor>,
|
||||||
pub corner_radius_factor: f32,
|
pub corner_radius_factor: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct DiffStyle {
|
pub struct DiffStyle {
|
||||||
pub inserted: Color,
|
pub inserted: Color,
|
||||||
pub modified: Color,
|
pub modified: Color,
|
||||||
|
@ -802,41 +803,49 @@ pub struct DiffStyle {
|
||||||
pub corner_radius: f32,
|
pub corner_radius: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy, JsonSchema)]
|
||||||
pub struct Interactive<T> {
|
pub struct Interactive<T> {
|
||||||
pub default: T,
|
pub default: T,
|
||||||
pub hover: Option<T>,
|
pub hovered: Option<T>,
|
||||||
pub hover_and_active: Option<T>,
|
|
||||||
pub clicked: Option<T>,
|
pub clicked: Option<T>,
|
||||||
pub click_and_active: Option<T>,
|
|
||||||
pub active: Option<T>,
|
|
||||||
pub disabled: Option<T>,
|
pub disabled: Option<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Interactive<T> {
|
#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)]
|
||||||
pub fn style_for(&self, state: &mut MouseState, active: bool) -> &T {
|
pub struct Toggleable<T> {
|
||||||
|
active: T,
|
||||||
|
inactive: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Toggleable<T> {
|
||||||
|
pub fn new(active: T, inactive: T) -> Self {
|
||||||
|
Self { active, inactive }
|
||||||
|
}
|
||||||
|
pub fn in_state(&self, active: bool) -> &T {
|
||||||
if active {
|
if active {
|
||||||
if state.hovered() {
|
&self.active
|
||||||
self.hover_and_active
|
} else {
|
||||||
.as_ref()
|
&self.inactive
|
||||||
.unwrap_or(self.active.as_ref().unwrap_or(&self.default))
|
}
|
||||||
} else if state.clicked() == Some(platform::MouseButton::Left) && self.clicked.is_some()
|
}
|
||||||
{
|
pub fn active_state(&self) -> &T {
|
||||||
self.click_and_active
|
self.in_state(true)
|
||||||
.as_ref()
|
}
|
||||||
.unwrap_or(self.active.as_ref().unwrap_or(&self.default))
|
pub fn inactive_state(&self) -> &T {
|
||||||
} else {
|
self.in_state(false)
|
||||||
self.active.as_ref().unwrap_or(&self.default)
|
}
|
||||||
}
|
}
|
||||||
} else if state.clicked() == Some(platform::MouseButton::Left) && self.clicked.is_some() {
|
|
||||||
|
impl<T> Interactive<T> {
|
||||||
|
pub fn style_for(&self, state: &mut MouseState) -> &T {
|
||||||
|
if state.clicked() == Some(platform::MouseButton::Left) && self.clicked.is_some() {
|
||||||
self.clicked.as_ref().unwrap()
|
self.clicked.as_ref().unwrap()
|
||||||
} else if state.hovered() {
|
} else if state.hovered() {
|
||||||
self.hover.as_ref().unwrap_or(&self.default)
|
self.hovered.as_ref().unwrap_or(&self.default)
|
||||||
} else {
|
} else {
|
||||||
&self.default
|
&self.default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disabled_style(&self) -> &T {
|
pub fn disabled_style(&self) -> &T {
|
||||||
self.disabled.as_ref().unwrap_or(&self.default)
|
self.disabled.as_ref().unwrap_or(&self.default)
|
||||||
}
|
}
|
||||||
|
@ -849,13 +858,9 @@ impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
|
||||||
{
|
{
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Helper {
|
struct Helper {
|
||||||
#[serde(flatten)]
|
|
||||||
default: Value,
|
default: Value,
|
||||||
hover: Option<Value>,
|
hovered: Option<Value>,
|
||||||
hover_and_active: Option<Value>,
|
|
||||||
clicked: Option<Value>,
|
clicked: Option<Value>,
|
||||||
click_and_active: Option<Value>,
|
|
||||||
active: Option<Value>,
|
|
||||||
disabled: Option<Value>,
|
disabled: Option<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -880,21 +885,15 @@ impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let hover = deserialize_state(json.hover)?;
|
let hovered = deserialize_state(json.hovered)?;
|
||||||
let hover_and_active = deserialize_state(json.hover_and_active)?;
|
|
||||||
let clicked = deserialize_state(json.clicked)?;
|
let clicked = deserialize_state(json.clicked)?;
|
||||||
let click_and_active = deserialize_state(json.click_and_active)?;
|
|
||||||
let active = deserialize_state(json.active)?;
|
|
||||||
let disabled = deserialize_state(json.disabled)?;
|
let disabled = deserialize_state(json.disabled)?;
|
||||||
let default = serde_json::from_value(json.default).map_err(serde::de::Error::custom)?;
|
let default = serde_json::from_value(json.default).map_err(serde::de::Error::custom)?;
|
||||||
|
|
||||||
Ok(Interactive {
|
Ok(Interactive {
|
||||||
default,
|
default,
|
||||||
hover,
|
hovered,
|
||||||
hover_and_active,
|
|
||||||
clicked,
|
clicked,
|
||||||
click_and_active,
|
|
||||||
active,
|
|
||||||
disabled,
|
disabled,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -911,7 +910,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, JsonSchema)]
|
||||||
pub struct SyntaxTheme {
|
pub struct SyntaxTheme {
|
||||||
pub highlights: Vec<(String, HighlightStyle)>,
|
pub highlights: Vec<(String, HighlightStyle)>,
|
||||||
}
|
}
|
||||||
|
@ -945,7 +944,7 @@ impl<'de> Deserialize<'de> for SyntaxTheme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct HoverPopover {
|
pub struct HoverPopover {
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub info_container: ContainerStyle,
|
pub info_container: ContainerStyle,
|
||||||
|
@ -957,7 +956,7 @@ pub struct HoverPopover {
|
||||||
pub highlight: Color,
|
pub highlight: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct TerminalStyle {
|
pub struct TerminalStyle {
|
||||||
pub black: Color,
|
pub black: Color,
|
||||||
pub red: Color,
|
pub red: Color,
|
||||||
|
@ -991,24 +990,39 @@ pub struct TerminalStyle {
|
||||||
pub dim_foreground: Color,
|
pub dim_foreground: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct AssistantStyle {
|
pub struct AssistantStyle {
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub header: ContainerStyle,
|
pub hamburger_button: Interactive<IconStyle>,
|
||||||
|
pub split_button: Interactive<IconStyle>,
|
||||||
|
pub assist_button: Interactive<IconStyle>,
|
||||||
|
pub quote_button: Interactive<IconStyle>,
|
||||||
|
pub zoom_in_button: Interactive<IconStyle>,
|
||||||
|
pub zoom_out_button: Interactive<IconStyle>,
|
||||||
|
pub plus_button: Interactive<IconStyle>,
|
||||||
|
pub title: ContainedText,
|
||||||
|
pub message_header: ContainerStyle,
|
||||||
pub sent_at: ContainedText,
|
pub sent_at: ContainedText,
|
||||||
pub user_sender: Interactive<ContainedText>,
|
pub user_sender: Interactive<ContainedText>,
|
||||||
pub assistant_sender: Interactive<ContainedText>,
|
pub assistant_sender: Interactive<ContainedText>,
|
||||||
pub system_sender: Interactive<ContainedText>,
|
pub system_sender: Interactive<ContainedText>,
|
||||||
pub model_info_container: ContainerStyle,
|
|
||||||
pub model: Interactive<ContainedText>,
|
pub model: Interactive<ContainedText>,
|
||||||
pub remaining_tokens: ContainedText,
|
pub remaining_tokens: ContainedText,
|
||||||
pub no_remaining_tokens: ContainedText,
|
pub no_remaining_tokens: ContainedText,
|
||||||
pub error_icon: Icon,
|
pub error_icon: Icon,
|
||||||
pub api_key_editor: FieldEditor,
|
pub api_key_editor: FieldEditor,
|
||||||
pub api_key_prompt: ContainedText,
|
pub api_key_prompt: ContainedText,
|
||||||
|
pub saved_conversation: SavedConversation,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
|
pub struct SavedConversation {
|
||||||
|
pub container: Interactive<ContainerStyle>,
|
||||||
|
pub saved_at: ContainedText,
|
||||||
|
pub title: ContainedText,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct FeedbackStyle {
|
pub struct FeedbackStyle {
|
||||||
pub submit_button: Interactive<ContainedText>,
|
pub submit_button: Interactive<ContainedText>,
|
||||||
pub button_margin: f32,
|
pub button_margin: f32,
|
||||||
|
@ -1017,7 +1031,7 @@ pub struct FeedbackStyle {
|
||||||
pub link_text_hover: ContainedText,
|
pub link_text_hover: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct WelcomeStyle {
|
pub struct WelcomeStyle {
|
||||||
pub page_width: f32,
|
pub page_width: f32,
|
||||||
pub logo: SvgStyle,
|
pub logo: SvgStyle,
|
||||||
|
@ -1031,7 +1045,7 @@ pub struct WelcomeStyle {
|
||||||
pub checkbox_group: ContainerStyle,
|
pub checkbox_group: ContainerStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct ColorScheme {
|
pub struct ColorScheme {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub is_light: bool,
|
pub is_light: bool,
|
||||||
|
@ -1046,13 +1060,13 @@ pub struct ColorScheme {
|
||||||
pub players: Vec<Player>,
|
pub players: Vec<Player>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
pub cursor: Color,
|
pub cursor: Color,
|
||||||
pub selection: Color,
|
pub selection: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct RampSet {
|
pub struct RampSet {
|
||||||
pub neutral: Vec<Color>,
|
pub neutral: Vec<Color>,
|
||||||
pub red: Vec<Color>,
|
pub red: Vec<Color>,
|
||||||
|
@ -1065,7 +1079,7 @@ pub struct RampSet {
|
||||||
pub magenta: Vec<Color>,
|
pub magenta: Vec<Color>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Layer {
|
pub struct Layer {
|
||||||
pub base: StyleSet,
|
pub base: StyleSet,
|
||||||
pub variant: StyleSet,
|
pub variant: StyleSet,
|
||||||
|
@ -1076,7 +1090,7 @@ pub struct Layer {
|
||||||
pub negative: StyleSet,
|
pub negative: StyleSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct StyleSet {
|
pub struct StyleSet {
|
||||||
pub default: Style,
|
pub default: Style,
|
||||||
pub active: Style,
|
pub active: Style,
|
||||||
|
@ -1086,7 +1100,7 @@ pub struct StyleSet {
|
||||||
pub inverted: Style,
|
pub inverted: Style,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
pub background: Color,
|
pub background: Color,
|
||||||
pub border: Color,
|
pub border: Color,
|
||||||
|
|
|
@ -14,12 +14,13 @@ use util::ResultExt as _;
|
||||||
|
|
||||||
const MIN_FONT_SIZE: f32 = 6.0;
|
const MIN_FONT_SIZE: f32 = 6.0;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, JsonSchema)]
|
||||||
pub struct ThemeSettings {
|
pub struct ThemeSettings {
|
||||||
pub buffer_font_family_name: String,
|
pub buffer_font_family_name: String,
|
||||||
pub buffer_font_features: fonts::Features,
|
pub buffer_font_features: fonts::Features,
|
||||||
pub buffer_font_family: FamilyId,
|
pub buffer_font_family: FamilyId,
|
||||||
pub(crate) buffer_font_size: f32,
|
pub(crate) buffer_font_size: f32,
|
||||||
|
#[serde(skip)]
|
||||||
pub theme: Arc<Theme>,
|
pub theme: Arc<Theme>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
|
||||||
elements::{
|
elements::{
|
||||||
ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label,
|
ConstrainedBox, Container, ContainerStyle, Dimensions, Empty, Flex, KeystrokeLabel, Label,
|
||||||
MouseEventHandler, ParentElement, Stack, Svg,
|
MouseEventHandler, ParentElement, Stack, Svg, SvgStyle,
|
||||||
},
|
},
|
||||||
fonts::TextStyle,
|
fonts::TextStyle,
|
||||||
geometry::vector::{vec2f, Vector2F},
|
geometry::vector::Vector2F,
|
||||||
platform,
|
platform,
|
||||||
platform::MouseButton,
|
platform::MouseButton,
|
||||||
scene::MouseClick,
|
scene::MouseClick,
|
||||||
Action, Element, EventContext, MouseState, View, ViewContext,
|
Action, Element, EventContext, MouseState, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{ContainedText, Interactive};
|
use crate::{ContainedText, Interactive};
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct CheckboxStyle {
|
pub struct CheckboxStyle {
|
||||||
pub icon: SvgStyle,
|
pub icon: SvgStyle,
|
||||||
pub label: ContainedText,
|
pub label: ContainedText,
|
||||||
|
@ -93,25 +93,6 @@ where
|
||||||
.with_cursor_style(platform::CursorStyle::PointingHand)
|
.with_cursor_style(platform::CursorStyle::PointingHand)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
|
||||||
pub struct SvgStyle {
|
|
||||||
pub color: Color,
|
|
||||||
pub asset: String,
|
|
||||||
pub dimensions: Dimensions,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
|
||||||
pub struct Dimensions {
|
|
||||||
pub width: f32,
|
|
||||||
pub height: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dimensions {
|
|
||||||
pub fn to_vec(&self) -> Vector2F {
|
|
||||||
vec2f(self.width, self.height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
|
pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
|
||||||
Svg::new(style.asset.clone())
|
Svg::new(style.asset.clone())
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
|
@ -120,10 +101,10 @@ pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
|
||||||
.with_height(style.dimensions.height)
|
.with_height(style.dimensions.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct IconStyle {
|
pub struct IconStyle {
|
||||||
icon: SvgStyle,
|
pub icon: SvgStyle,
|
||||||
container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn icon<V: View>(style: &IconStyle) -> Container<V> {
|
pub fn icon<V: View>(style: &IconStyle) -> Container<V> {
|
||||||
|
@ -170,7 +151,7 @@ where
|
||||||
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
|
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
|
||||||
{
|
{
|
||||||
MouseEventHandler::<Tag, V>::new(0, cx, |state, _| {
|
MouseEventHandler::<Tag, V>::new(0, cx, |state, _| {
|
||||||
let style = style.style_for(state, false);
|
let style = style.style_for(state);
|
||||||
Label::new(label, style.text.to_owned())
|
Label::new(label, style.text.to_owned())
|
||||||
.aligned()
|
.aligned()
|
||||||
.contained()
|
.contained()
|
||||||
|
@ -182,7 +163,7 @@ where
|
||||||
.with_cursor_style(platform::CursorStyle::PointingHand)
|
.with_cursor_style(platform::CursorStyle::PointingHand)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct ModalStyle {
|
pub struct ModalStyle {
|
||||||
close_icon: Interactive<IconStyle>,
|
close_icon: Interactive<IconStyle>,
|
||||||
container: ContainerStyle,
|
container: ContainerStyle,
|
||||||
|
@ -220,13 +201,13 @@ where
|
||||||
title,
|
title,
|
||||||
style
|
style
|
||||||
.title_text
|
.title_text
|
||||||
.style_for(&mut MouseState::default(), false)
|
.style_for(&mut MouseState::default())
|
||||||
.clone(),
|
.clone(),
|
||||||
))
|
))
|
||||||
.with_child(
|
.with_child(
|
||||||
// FIXME: Get a better tag type
|
// FIXME: Get a better tag type
|
||||||
MouseEventHandler::<Tag, V>::new(999999, cx, |state, _cx| {
|
MouseEventHandler::<Tag, V>::new(999999, cx, |state, _cx| {
|
||||||
let style = style.close_icon.style_for(state, false);
|
let style = style.close_icon.style_for(state);
|
||||||
icon(style)
|
icon(style)
|
||||||
})
|
})
|
||||||
.on_click(platform::MouseButton::Left, move |_, _, cx| {
|
.on_click(platform::MouseButton::Left, move |_, _, cx| {
|
||||||
|
|
|
@ -208,7 +208,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> AnyElement<Picker<Self>> {
|
) -> AnyElement<Picker<Self>> {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
|
|
||||||
let theme_match = &self.matches[ix];
|
let theme_match = &self.matches[ix];
|
||||||
Label::new(theme_match.string.clone(), style.label.clone())
|
Label::new(theme_match.string.clone(), style.label.clone())
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "theme_testbench"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/theme_testbench.rs"
|
|
||||||
doctest = false
|
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
gpui = { path = "../gpui" }
|
|
||||||
theme = { path = "../theme" }
|
|
||||||
settings = { path = "../settings" }
|
|
||||||
workspace = { path = "../workspace" }
|
|
||||||
project = { path = "../project" }
|
|
||||||
|
|
||||||
smallvec.workspace = true
|
|
|
@ -1,300 +0,0 @@
|
||||||
use gpui::{
|
|
||||||
actions,
|
|
||||||
color::Color,
|
|
||||||
elements::{
|
|
||||||
AnyElement, Canvas, Container, ContainerStyle, Flex, Label, Margin, MouseEventHandler,
|
|
||||||
Padding, ParentElement,
|
|
||||||
},
|
|
||||||
fonts::TextStyle,
|
|
||||||
AppContext, Border, Element, Entity, ModelHandle, Quad, Task, View, ViewContext, ViewHandle,
|
|
||||||
WeakViewHandle,
|
|
||||||
};
|
|
||||||
use project::Project;
|
|
||||||
use theme::{ColorScheme, Layer, Style, StyleSet, ThemeSettings};
|
|
||||||
use workspace::{item::Item, register_deserializable_item, Pane, Workspace};
|
|
||||||
|
|
||||||
actions!(theme, [DeployThemeTestbench]);
|
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
|
||||||
cx.add_action(ThemeTestbench::deploy);
|
|
||||||
|
|
||||||
register_deserializable_item::<ThemeTestbench>(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ThemeTestbench {}
|
|
||||||
|
|
||||||
impl ThemeTestbench {
|
|
||||||
pub fn deploy(
|
|
||||||
workspace: &mut Workspace,
|
|
||||||
_: &DeployThemeTestbench,
|
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) {
|
|
||||||
let view = cx.add_view(|_| ThemeTestbench {});
|
|
||||||
workspace.add_item(Box::new(view), cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_ramps(color_scheme: &ColorScheme) -> Flex<Self> {
|
|
||||||
fn display_ramp(ramp: &Vec<Color>) -> AnyElement<ThemeTestbench> {
|
|
||||||
Flex::row()
|
|
||||||
.with_children(ramp.iter().cloned().map(|color| {
|
|
||||||
Canvas::new(move |scene, bounds, _, _, _| {
|
|
||||||
scene.push_quad(Quad {
|
|
||||||
bounds,
|
|
||||||
background: Some(color),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.flex(1.0, false)
|
|
||||||
}))
|
|
||||||
.flex(1.0, false)
|
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
|
|
||||||
Flex::column()
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.neutral))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.red))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.orange))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.yellow))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.green))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.cyan))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.blue))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.violet))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.magenta))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_layer(
|
|
||||||
layer_index: usize,
|
|
||||||
layer: &Layer,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) -> Container<Self> {
|
|
||||||
Flex::column()
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(0, layer_index, "base", &layer.base, cx).flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(1, layer_index, "variant", &layer.variant, cx)
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(2, layer_index, "on", &layer.on, cx).flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(3, layer_index, "accent", &layer.accent, cx)
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(4, layer_index, "positive", &layer.positive, cx)
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(5, layer_index, "warning", &layer.warning, cx)
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(6, layer_index, "negative", &layer.negative, cx)
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.contained()
|
|
||||||
.with_style(ContainerStyle {
|
|
||||||
margin: Margin {
|
|
||||||
top: 10.,
|
|
||||||
bottom: 10.,
|
|
||||||
left: 10.,
|
|
||||||
right: 10.,
|
|
||||||
},
|
|
||||||
background_color: Some(layer.base.default.background),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_button_set(
|
|
||||||
set_index: usize,
|
|
||||||
layer_index: usize,
|
|
||||||
set_name: &'static str,
|
|
||||||
style_set: &StyleSet,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) -> Flex<Self> {
|
|
||||||
Flex::row()
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6,
|
|
||||||
layer_index,
|
|
||||||
set_name,
|
|
||||||
&style_set,
|
|
||||||
None,
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6 + 1,
|
|
||||||
layer_index,
|
|
||||||
"hovered",
|
|
||||||
&style_set,
|
|
||||||
Some(|style_set| &style_set.hovered),
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6 + 2,
|
|
||||||
layer_index,
|
|
||||||
"pressed",
|
|
||||||
&style_set,
|
|
||||||
Some(|style_set| &style_set.pressed),
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6 + 3,
|
|
||||||
layer_index,
|
|
||||||
"active",
|
|
||||||
&style_set,
|
|
||||||
Some(|style_set| &style_set.active),
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6 + 4,
|
|
||||||
layer_index,
|
|
||||||
"disabled",
|
|
||||||
&style_set,
|
|
||||||
Some(|style_set| &style_set.disabled),
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6 + 5,
|
|
||||||
layer_index,
|
|
||||||
"inverted",
|
|
||||||
&style_set,
|
|
||||||
Some(|style_set| &style_set.inverted),
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_button(
|
|
||||||
button_index: usize,
|
|
||||||
layer_index: usize,
|
|
||||||
text: &'static str,
|
|
||||||
style_set: &StyleSet,
|
|
||||||
style_override: Option<fn(&StyleSet) -> &Style>,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) -> AnyElement<Self> {
|
|
||||||
enum TestBenchButton {}
|
|
||||||
MouseEventHandler::<TestBenchButton, _>::new(layer_index + button_index, cx, |state, cx| {
|
|
||||||
let style = if let Some(style_override) = style_override {
|
|
||||||
style_override(&style_set)
|
|
||||||
} else if state.clicked().is_some() {
|
|
||||||
&style_set.pressed
|
|
||||||
} else if state.hovered() {
|
|
||||||
&style_set.hovered
|
|
||||||
} else {
|
|
||||||
&style_set.default
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::render_label(text.to_string(), style, cx)
|
|
||||||
.contained()
|
|
||||||
.with_style(ContainerStyle {
|
|
||||||
margin: Margin {
|
|
||||||
top: 4.,
|
|
||||||
bottom: 4.,
|
|
||||||
left: 4.,
|
|
||||||
right: 4.,
|
|
||||||
},
|
|
||||||
padding: Padding {
|
|
||||||
top: 4.,
|
|
||||||
bottom: 4.,
|
|
||||||
left: 4.,
|
|
||||||
right: 4.,
|
|
||||||
},
|
|
||||||
background_color: Some(style.background),
|
|
||||||
border: Border {
|
|
||||||
width: 1.,
|
|
||||||
color: style.border,
|
|
||||||
overlay: false,
|
|
||||||
top: true,
|
|
||||||
bottom: true,
|
|
||||||
left: true,
|
|
||||||
right: true,
|
|
||||||
},
|
|
||||||
corner_radius: 2.,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.flex(1., true)
|
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_label(text: String, style: &Style, cx: &mut ViewContext<Self>) -> Label {
|
|
||||||
let settings = settings::get::<ThemeSettings>(cx);
|
|
||||||
let font_cache = cx.font_cache();
|
|
||||||
let family_id = settings.buffer_font_family;
|
|
||||||
let font_size = settings.buffer_font_size(cx);
|
|
||||||
let font_id = font_cache
|
|
||||||
.select_font(family_id, &Default::default())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let text_style = TextStyle {
|
|
||||||
color: style.foreground,
|
|
||||||
font_family_id: family_id,
|
|
||||||
font_family_name: font_cache.family_name(family_id).unwrap(),
|
|
||||||
font_id,
|
|
||||||
font_size,
|
|
||||||
font_properties: Default::default(),
|
|
||||||
underline: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Label::new(text, text_style)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Entity for ThemeTestbench {
|
|
||||||
type Event = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for ThemeTestbench {
|
|
||||||
fn ui_name() -> &'static str {
|
|
||||||
"ThemeTestbench"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> AnyElement<Self> {
|
|
||||||
let color_scheme = &theme::current(cx).clone().color_scheme;
|
|
||||||
|
|
||||||
Flex::row()
|
|
||||||
.with_child(
|
|
||||||
Self::render_ramps(color_scheme)
|
|
||||||
.contained()
|
|
||||||
.with_margin_right(10.)
|
|
||||||
.flex(0.1, false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Flex::column()
|
|
||||||
.with_child(Self::render_layer(100, &color_scheme.lowest, cx).flex(1., true))
|
|
||||||
.with_child(Self::render_layer(200, &color_scheme.middle, cx).flex(1., true))
|
|
||||||
.with_child(Self::render_layer(300, &color_scheme.highest, cx).flex(1., true))
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Item for ThemeTestbench {
|
|
||||||
fn tab_content<T: View>(
|
|
||||||
&self,
|
|
||||||
_: Option<usize>,
|
|
||||||
style: &theme::Tab,
|
|
||||||
_: &AppContext,
|
|
||||||
) -> AnyElement<T> {
|
|
||||||
Label::new("Theme Testbench", style.label.clone())
|
|
||||||
.aligned()
|
|
||||||
.contained()
|
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialized_item_kind() -> Option<&'static str> {
|
|
||||||
Some("ThemeTestBench")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize(
|
|
||||||
_project: ModelHandle<Project>,
|
|
||||||
_workspace: WeakViewHandle<Workspace>,
|
|
||||||
_workspace_id: workspace::WorkspaceId,
|
|
||||||
_item_id: workspace::ItemId,
|
|
||||||
cx: &mut ViewContext<Pane>,
|
|
||||||
) -> Task<gpui::anyhow::Result<ViewHandle<Self>>> {
|
|
||||||
Task::ready(Ok(cx.add_view(|_| Self {})))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory");
|
pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory");
|
||||||
pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed");
|
pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed");
|
||||||
|
pub static ref CONVERSATIONS_DIR: PathBuf = HOME.join(".config/zed/conversations");
|
||||||
pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed");
|
pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed");
|
||||||
pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed");
|
pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed");
|
||||||
pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages");
|
pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages");
|
||||||
|
|
|
@ -209,8 +209,9 @@ impl Motion {
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
point: DisplayPoint,
|
point: DisplayPoint,
|
||||||
goal: SelectionGoal,
|
goal: SelectionGoal,
|
||||||
times: usize,
|
maybe_times: Option<usize>,
|
||||||
) -> Option<(DisplayPoint, SelectionGoal)> {
|
) -> Option<(DisplayPoint, SelectionGoal)> {
|
||||||
|
let times = maybe_times.unwrap_or(1);
|
||||||
use Motion::*;
|
use Motion::*;
|
||||||
let infallible = self.infallible();
|
let infallible = self.infallible();
|
||||||
let (new_point, goal) = match self {
|
let (new_point, goal) = match self {
|
||||||
|
@ -236,7 +237,10 @@ impl Motion {
|
||||||
EndOfLine => (end_of_line(map, point), SelectionGoal::None),
|
EndOfLine => (end_of_line(map, point), SelectionGoal::None),
|
||||||
CurrentLine => (end_of_line(map, point), SelectionGoal::None),
|
CurrentLine => (end_of_line(map, point), SelectionGoal::None),
|
||||||
StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None),
|
StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None),
|
||||||
EndOfDocument => (end_of_document(map, point, times), SelectionGoal::None),
|
EndOfDocument => (
|
||||||
|
end_of_document(map, point, maybe_times),
|
||||||
|
SelectionGoal::None,
|
||||||
|
),
|
||||||
Matching => (matching(map, point), SelectionGoal::None),
|
Matching => (matching(map, point), SelectionGoal::None),
|
||||||
FindForward { before, text } => (
|
FindForward { before, text } => (
|
||||||
find_forward(map, point, *before, text.clone(), times),
|
find_forward(map, point, *before, text.clone(), times),
|
||||||
|
@ -257,7 +261,7 @@ impl Motion {
|
||||||
&self,
|
&self,
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
selection: &mut Selection<DisplayPoint>,
|
selection: &mut Selection<DisplayPoint>,
|
||||||
times: usize,
|
times: Option<usize>,
|
||||||
expand_to_surrounding_newline: bool,
|
expand_to_surrounding_newline: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if let Some((new_head, goal)) =
|
if let Some((new_head, goal)) =
|
||||||
|
@ -473,14 +477,19 @@ fn start_of_document(map: &DisplaySnapshot, point: DisplayPoint, line: usize) ->
|
||||||
map.clip_point(new_point, Bias::Left)
|
map.clip_point(new_point, Bias::Left)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_of_document(map: &DisplaySnapshot, point: DisplayPoint, line: usize) -> DisplayPoint {
|
fn end_of_document(
|
||||||
let mut new_point = if line == 1 {
|
map: &DisplaySnapshot,
|
||||||
map.max_point()
|
point: DisplayPoint,
|
||||||
|
line: Option<usize>,
|
||||||
|
) -> DisplayPoint {
|
||||||
|
let new_row = if let Some(line) = line {
|
||||||
|
(line - 1) as u32
|
||||||
} else {
|
} else {
|
||||||
Point::new((line - 1) as u32, 0).to_display_point(map)
|
map.max_buffer_row()
|
||||||
};
|
};
|
||||||
*new_point.column_mut() = point.column();
|
|
||||||
map.clip_point(new_point, Bias::Left)
|
let new_point = Point::new(new_row, point.column());
|
||||||
|
map.clip_point(new_point.to_display_point(map), Bias::Left)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matching(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
|
fn matching(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mod change;
|
mod change;
|
||||||
mod delete;
|
mod delete;
|
||||||
|
mod substitute;
|
||||||
mod yank;
|
mod yank;
|
||||||
|
|
||||||
use std::{borrow::Cow, cmp::Ordering, sync::Arc};
|
use std::{borrow::Cow, cmp::Ordering, sync::Arc};
|
||||||
|
@ -25,6 +26,7 @@ use workspace::Workspace;
|
||||||
use self::{
|
use self::{
|
||||||
change::{change_motion, change_object},
|
change::{change_motion, change_object},
|
||||||
delete::{delete_motion, delete_object},
|
delete::{delete_motion, delete_object},
|
||||||
|
substitute::substitute,
|
||||||
yank::{yank_motion, yank_object},
|
yank::{yank_motion, yank_object},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,6 +47,7 @@ actions!(
|
||||||
DeleteToEndOfLine,
|
DeleteToEndOfLine,
|
||||||
Paste,
|
Paste,
|
||||||
Yank,
|
Yank,
|
||||||
|
Substitute,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -56,6 +59,12 @@ pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(insert_end_of_line);
|
cx.add_action(insert_end_of_line);
|
||||||
cx.add_action(insert_line_above);
|
cx.add_action(insert_line_above);
|
||||||
cx.add_action(insert_line_below);
|
cx.add_action(insert_line_below);
|
||||||
|
cx.add_action(|_: &mut Workspace, _: &Substitute, cx| {
|
||||||
|
Vim::update(cx, |vim, cx| {
|
||||||
|
let times = vim.pop_number_operator(cx);
|
||||||
|
substitute(vim, times, cx);
|
||||||
|
})
|
||||||
|
});
|
||||||
cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
|
cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
|
||||||
Vim::update(cx, |vim, cx| {
|
Vim::update(cx, |vim, cx| {
|
||||||
let times = vim.pop_number_operator(cx);
|
let times = vim.pop_number_operator(cx);
|
||||||
|
@ -93,7 +102,7 @@ pub fn init(cx: &mut AppContext) {
|
||||||
pub fn normal_motion(
|
pub fn normal_motion(
|
||||||
motion: Motion,
|
motion: Motion,
|
||||||
operator: Option<Operator>,
|
operator: Option<Operator>,
|
||||||
times: usize,
|
times: Option<usize>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
Vim::update(cx, |vim, cx| {
|
Vim::update(cx, |vim, cx| {
|
||||||
|
@ -129,7 +138,7 @@ pub fn normal_object(object: Object, cx: &mut WindowContext) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_cursor(vim: &mut Vim, motion: Motion, times: usize, cx: &mut WindowContext) {
|
fn move_cursor(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.move_cursors_with(|map, cursor, goal| {
|
s.move_cursors_with(|map, cursor, goal| {
|
||||||
|
@ -147,7 +156,7 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspa
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.maybe_move_cursors_with(|map, cursor, goal| {
|
s.maybe_move_cursors_with(|map, cursor, goal| {
|
||||||
Motion::Right.move_point(map, cursor, goal, 1)
|
Motion::Right.move_point(map, cursor, goal, None)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -164,7 +173,7 @@ fn insert_first_non_whitespace(
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.maybe_move_cursors_with(|map, cursor, goal| {
|
s.maybe_move_cursors_with(|map, cursor, goal| {
|
||||||
Motion::FirstNonWhitespace.move_point(map, cursor, goal, 1)
|
Motion::FirstNonWhitespace.move_point(map, cursor, goal, None)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -177,7 +186,7 @@ fn insert_end_of_line(_: &mut Workspace, _: &InsertEndOfLine, cx: &mut ViewConte
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.maybe_move_cursors_with(|map, cursor, goal| {
|
s.maybe_move_cursors_with(|map, cursor, goal| {
|
||||||
Motion::EndOfLine.move_point(map, cursor, goal, 1)
|
Motion::EndOfLine.move_point(map, cursor, goal, None)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -237,7 +246,7 @@ fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContex
|
||||||
});
|
});
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.maybe_move_cursors_with(|map, cursor, goal| {
|
s.maybe_move_cursors_with(|map, cursor, goal| {
|
||||||
Motion::EndOfLine.move_point(map, cursor, goal, 1)
|
Motion::EndOfLine.move_point(map, cursor, goal, None)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.edit_with_autoindent(edits, cx);
|
editor.edit_with_autoindent(edits, cx);
|
||||||
|
|
|
@ -6,7 +6,7 @@ use editor::{
|
||||||
use gpui::WindowContext;
|
use gpui::WindowContext;
|
||||||
use language::Selection;
|
use language::Selection;
|
||||||
|
|
||||||
pub fn change_motion(vim: &mut Vim, motion: Motion, times: usize, cx: &mut WindowContext) {
|
pub fn change_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
|
||||||
// Some motions ignore failure when switching to normal mode
|
// Some motions ignore failure when switching to normal mode
|
||||||
let mut motion_succeeded = matches!(
|
let mut motion_succeeded = matches!(
|
||||||
motion,
|
motion,
|
||||||
|
@ -78,10 +78,10 @@ pub fn change_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Windo
|
||||||
fn expand_changed_word_selection(
|
fn expand_changed_word_selection(
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
selection: &mut Selection<DisplayPoint>,
|
selection: &mut Selection<DisplayPoint>,
|
||||||
times: usize,
|
times: Option<usize>,
|
||||||
ignore_punctuation: bool,
|
ignore_punctuation: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if times == 1 {
|
if times.is_none() || times.unwrap() == 1 {
|
||||||
let in_word = map
|
let in_word = map
|
||||||
.chars_at(selection.head())
|
.chars_at(selection.head())
|
||||||
.next()
|
.next()
|
||||||
|
@ -97,7 +97,8 @@ fn expand_changed_word_selection(
|
||||||
});
|
});
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
Motion::NextWordStart { ignore_punctuation }.expand_selection(map, selection, 1, false)
|
Motion::NextWordStart { ignore_punctuation }
|
||||||
|
.expand_selection(map, selection, None, false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Motion::NextWordStart { ignore_punctuation }.expand_selection(map, selection, times, false)
|
Motion::NextWordStart { ignore_punctuation }.expand_selection(map, selection, times, false)
|
||||||
|
|
|
@ -3,7 +3,7 @@ use collections::{HashMap, HashSet};
|
||||||
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Bias};
|
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Bias};
|
||||||
use gpui::WindowContext;
|
use gpui::WindowContext;
|
||||||
|
|
||||||
pub fn delete_motion(vim: &mut Vim, motion: Motion, times: usize, cx: &mut WindowContext) {
|
pub fn delete_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.transact(cx, |editor, cx| {
|
editor.transact(cx, |editor, cx| {
|
||||||
editor.set_clip_at_line_ends(false, cx);
|
editor.set_clip_at_line_ends(false, cx);
|
||||||
|
|
69
crates/vim/src/normal/substitute.rs
Normal file
69
crates/vim/src/normal/substitute.rs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
use gpui::WindowContext;
|
||||||
|
use language::Point;
|
||||||
|
|
||||||
|
use crate::{motion::Motion, Mode, Vim};
|
||||||
|
|
||||||
|
pub fn substitute(vim: &mut Vim, count: Option<usize>, cx: &mut WindowContext) {
|
||||||
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
|
editor.set_clip_at_line_ends(false, cx);
|
||||||
|
editor.change_selections(None, cx, |s| {
|
||||||
|
s.move_with(|map, selection| {
|
||||||
|
if selection.start == selection.end {
|
||||||
|
Motion::Right.expand_selection(map, selection, count, true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
editor.transact(cx, |editor, cx| {
|
||||||
|
let selections = editor.selections.all::<Point>(cx);
|
||||||
|
for selection in selections.into_iter().rev() {
|
||||||
|
editor.buffer().update(cx, |buffer, cx| {
|
||||||
|
buffer.edit([(selection.start..selection.end, "")], None, cx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
editor.set_clip_at_line_ends(true, cx);
|
||||||
|
});
|
||||||
|
vim.switch_mode(Mode::Insert, true, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::{state::Mode, test::VimTestContext};
|
||||||
|
use indoc::indoc;
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_substitute(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = VimTestContext::new(cx, true).await;
|
||||||
|
|
||||||
|
// supports a single cursor
|
||||||
|
cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
|
||||||
|
cx.simulate_keystrokes(["s", "x"]);
|
||||||
|
cx.assert_editor_state("xˇbc\n");
|
||||||
|
|
||||||
|
// supports a selection
|
||||||
|
cx.set_state(indoc! {"a«bcˇ»\n"}, Mode::Visual { line: false });
|
||||||
|
cx.assert_editor_state("a«bcˇ»\n");
|
||||||
|
cx.simulate_keystrokes(["s", "x"]);
|
||||||
|
cx.assert_editor_state("axˇ\n");
|
||||||
|
|
||||||
|
// supports counts
|
||||||
|
cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
|
||||||
|
cx.simulate_keystrokes(["2", "s", "x"]);
|
||||||
|
cx.assert_editor_state("xˇc\n");
|
||||||
|
|
||||||
|
// supports multiple cursors
|
||||||
|
cx.set_state(indoc! {"a«bcˇ»deˇffg\n"}, Mode::Normal);
|
||||||
|
cx.simulate_keystrokes(["2", "s", "x"]);
|
||||||
|
cx.assert_editor_state("axˇdexˇg\n");
|
||||||
|
|
||||||
|
// does not read beyond end of line
|
||||||
|
cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
|
||||||
|
cx.simulate_keystrokes(["5", "s", "x"]);
|
||||||
|
cx.assert_editor_state("xˇ\n");
|
||||||
|
|
||||||
|
// it handles multibyte characters
|
||||||
|
cx.set_state(indoc! {"ˇcàfé\n"}, Mode::Normal);
|
||||||
|
cx.simulate_keystrokes(["4", "s", "x"]);
|
||||||
|
cx.assert_editor_state("xˇ\n");
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim}
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::WindowContext;
|
use gpui::WindowContext;
|
||||||
|
|
||||||
pub fn yank_motion(vim: &mut Vim, motion: Motion, times: usize, cx: &mut WindowContext) {
|
pub fn yank_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.transact(cx, |editor, cx| {
|
editor.transact(cx, |editor, cx| {
|
||||||
editor.set_clip_at_line_ends(false, cx);
|
editor.set_clip_at_line_ends(false, cx);
|
||||||
|
|
|
@ -98,3 +98,28 @@ async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
|
||||||
assert_eq!(bar.query_editor.read(cx).text(cx), "jumps");
|
assert_eq!(bar.query_editor.read(cx).text(cx), "jumps");
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_count_down(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = VimTestContext::new(cx, true).await;
|
||||||
|
|
||||||
|
cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
|
||||||
|
cx.simulate_keystrokes(["2", "down"]);
|
||||||
|
cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
|
||||||
|
cx.simulate_keystrokes(["9", "down"]);
|
||||||
|
cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = VimTestContext::new(cx, true).await;
|
||||||
|
|
||||||
|
// goes to end by default
|
||||||
|
cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
|
||||||
|
cx.simulate_keystrokes(["shift-g"]);
|
||||||
|
cx.assert_editor_state("aa\nbb\ncˇc");
|
||||||
|
|
||||||
|
// can go to line 1 (https://github.com/zed-industries/community/issues/710)
|
||||||
|
cx.simulate_keystrokes(["1", "shift-g"]);
|
||||||
|
cx.assert_editor_state("aˇa\nbb\ncc");
|
||||||
|
}
|
||||||
|
|
|
@ -238,13 +238,12 @@ impl Vim {
|
||||||
popped_operator
|
popped_operator
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_number_operator(&mut self, cx: &mut WindowContext) -> usize {
|
fn pop_number_operator(&mut self, cx: &mut WindowContext) -> Option<usize> {
|
||||||
let mut times = 1;
|
|
||||||
if let Some(Operator::Number(number)) = self.active_operator() {
|
if let Some(Operator::Number(number)) = self.active_operator() {
|
||||||
times = number;
|
|
||||||
self.pop_operator(cx);
|
self.pop_operator(cx);
|
||||||
|
return Some(number);
|
||||||
}
|
}
|
||||||
times
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_operator(&mut self, cx: &mut WindowContext) {
|
fn clear_operator(&mut self, cx: &mut WindowContext) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(paste);
|
cx.add_action(paste);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visual_motion(motion: Motion, times: usize, cx: &mut WindowContext) {
|
pub fn visual_motion(motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
|
||||||
Vim::update(cx, |vim, cx| {
|
Vim::update(cx, |vim, cx| {
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
|
|
|
@ -141,7 +141,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
|
||||||
) -> gpui::AnyElement<Picker<Self>> {
|
) -> gpui::AnyElement<Picker<Self>> {
|
||||||
let theme = &theme::current(cx);
|
let theme = &theme::current(cx);
|
||||||
let keymap_match = &self.matches[ix];
|
let keymap_match = &self.matches[ix];
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
|
|
||||||
Label::new(keymap_match.string.clone(), style.label.clone())
|
Label::new(keymap_match.string.clone(), style.label.clone())
|
||||||
.with_highlights(keymap_match.positions.clone())
|
.with_highlights(keymap_match.positions.clone())
|
||||||
|
|
|
@ -249,7 +249,7 @@ impl Dock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
|
pub(crate) fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
|
||||||
let subscriptions = [
|
let subscriptions = [
|
||||||
cx.observe(&panel, |_, _, cx| cx.notify()),
|
cx.observe(&panel, |_, _, cx| cx.notify()),
|
||||||
cx.subscribe(&panel, |this, panel, event, cx| {
|
cx.subscribe(&panel, |this, panel, event, cx| {
|
||||||
|
@ -498,7 +498,9 @@ impl View for PanelButtons {
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<Self, _>::new(panel_ix, cx, |state, cx| {
|
MouseEventHandler::<Self, _>::new(panel_ix, cx, |state, cx| {
|
||||||
let style = button_style.style_for(state, is_active);
|
let style = button_style.in_state(is_active);
|
||||||
|
|
||||||
|
let style = style.style_for(state);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Svg::new(view.icon_path(cx))
|
Svg::new(view.icon_path(cx))
|
||||||
|
@ -603,6 +605,7 @@ pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use gpui::{ViewContext, WindowContext};
|
use gpui::{ViewContext, WindowContext};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum TestPanelEvent {
|
pub enum TestPanelEvent {
|
||||||
PositionChanged,
|
PositionChanged,
|
||||||
Activated,
|
Activated,
|
||||||
|
|
|
@ -291,7 +291,7 @@ pub mod simple_message_notification {
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
|
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
|
||||||
let style = theme.dismiss_button.style_for(state, false);
|
let style = theme.dismiss_button.style_for(state);
|
||||||
Svg::new("icons/x_mark_8.svg")
|
Svg::new("icons/x_mark_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -323,7 +323,7 @@ pub mod simple_message_notification {
|
||||||
0,
|
0,
|
||||||
cx,
|
cx,
|
||||||
|state, _| {
|
|state, _| {
|
||||||
let style = theme.action_message.style_for(state, false);
|
let style = theme.action_message.style_for(state);
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
mod dragged_item_receiver;
|
mod dragged_item_receiver;
|
||||||
|
|
||||||
use super::{ItemHandle, SplitDirection};
|
use super::{ItemHandle, SplitDirection};
|
||||||
|
pub use crate::toolbar::Toolbar;
|
||||||
use crate::{
|
use crate::{
|
||||||
item::WeakItemHandle, notify_of_new_dock, toolbar::Toolbar, AutosaveSetting, Item,
|
item::WeakItemHandle, notify_of_new_dock, AutosaveSetting, Item, NewCenterTerminal, NewFile,
|
||||||
NewCenterTerminal, NewFile, NewSearch, ToggleZoom, Workspace, WorkspaceSettings,
|
NewSearch, ToggleZoom, Workspace, WorkspaceSettings,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{HashMap, HashSet, VecDeque};
|
use collections::{HashMap, HashSet, VecDeque};
|
||||||
|
@ -250,7 +251,7 @@ impl Pane {
|
||||||
pane: handle.clone(),
|
pane: handle.clone(),
|
||||||
next_timestamp,
|
next_timestamp,
|
||||||
}))),
|
}))),
|
||||||
toolbar: cx.add_view(|_| Toolbar::new(handle)),
|
toolbar: cx.add_view(|_| Toolbar::new(Some(handle))),
|
||||||
tab_bar_context_menu: TabBarContextMenu {
|
tab_bar_context_menu: TabBarContextMenu {
|
||||||
kind: TabBarContextMenuKind::New,
|
kind: TabBarContextMenuKind::New,
|
||||||
handle: context_menu,
|
handle: context_menu,
|
||||||
|
@ -1112,7 +1113,7 @@ impl Pane {
|
||||||
.get(self.active_item_index)
|
.get(self.active_item_index)
|
||||||
.map(|item| item.as_ref());
|
.map(|item| item.as_ref());
|
||||||
self.toolbar.update(cx, |toolbar, cx| {
|
self.toolbar.update(cx, |toolbar, cx| {
|
||||||
toolbar.set_active_pane_item(active_item, cx);
|
toolbar.set_active_item(active_item, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1410,7 +1411,7 @@ impl Pane {
|
||||||
pub fn render_tab_bar_button<F: 'static + Fn(&mut Pane, &mut EventContext<Pane>)>(
|
pub fn render_tab_bar_button<F: 'static + Fn(&mut Pane, &mut EventContext<Pane>)>(
|
||||||
index: usize,
|
index: usize,
|
||||||
icon: &'static str,
|
icon: &'static str,
|
||||||
active: bool,
|
is_active: bool,
|
||||||
tooltip: Option<(String, Option<Box<dyn Action>>)>,
|
tooltip: Option<(String, Option<Box<dyn Action>>)>,
|
||||||
cx: &mut ViewContext<Pane>,
|
cx: &mut ViewContext<Pane>,
|
||||||
on_click: F,
|
on_click: F,
|
||||||
|
@ -1420,7 +1421,7 @@ impl Pane {
|
||||||
|
|
||||||
let mut button = MouseEventHandler::<TabBarButton, _>::new(index, cx, |mouse_state, cx| {
|
let mut button = MouseEventHandler::<TabBarButton, _>::new(index, cx, |mouse_state, cx| {
|
||||||
let theme = &settings::get::<ThemeSettings>(cx).theme.workspace.tab_bar;
|
let theme = &settings::get::<ThemeSettings>(cx).theme.workspace.tab_bar;
|
||||||
let style = theme.pane_button.style_for(mouse_state, active);
|
let style = theme.pane_button.in_state(is_active).style_for(mouse_state);
|
||||||
Svg::new(icon)
|
Svg::new(icon)
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -1602,7 +1603,7 @@ impl View for Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.toolbar.update(cx, |toolbar, cx| {
|
self.toolbar.update(cx, |toolbar, cx| {
|
||||||
toolbar.pane_focus_update(true, cx);
|
toolbar.focus_changed(true, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(active_item) = self.active_item() {
|
if let Some(active_item) = self.active_item() {
|
||||||
|
@ -1631,7 +1632,7 @@ impl View for Pane {
|
||||||
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||||
self.has_focus = false;
|
self.has_focus = false;
|
||||||
self.toolbar.update(cx, |toolbar, cx| {
|
self.toolbar.update(cx, |toolbar, cx| {
|
||||||
toolbar.pane_focus_update(false, cx);
|
toolbar.focus_changed(false, cx);
|
||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ trait ToolbarItemViewHandle {
|
||||||
active_pane_item: Option<&dyn ItemHandle>,
|
active_pane_item: Option<&dyn ItemHandle>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> ToolbarItemLocation;
|
) -> ToolbarItemLocation;
|
||||||
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut WindowContext);
|
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext);
|
||||||
fn row_count(&self, cx: &WindowContext) -> usize;
|
fn row_count(&self, cx: &WindowContext) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,10 +51,10 @@ pub enum ToolbarItemLocation {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Toolbar {
|
pub struct Toolbar {
|
||||||
active_pane_item: Option<Box<dyn ItemHandle>>,
|
active_item: Option<Box<dyn ItemHandle>>,
|
||||||
hidden: bool,
|
hidden: bool,
|
||||||
can_navigate: bool,
|
can_navigate: bool,
|
||||||
pane: WeakViewHandle<Pane>,
|
pane: Option<WeakViewHandle<Pane>>,
|
||||||
items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
|
items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ impl View for Toolbar {
|
||||||
let pane = self.pane.clone();
|
let pane = self.pane.clone();
|
||||||
let mut enable_go_backward = false;
|
let mut enable_go_backward = false;
|
||||||
let mut enable_go_forward = false;
|
let mut enable_go_forward = false;
|
||||||
if let Some(pane) = pane.upgrade(cx) {
|
if let Some(pane) = pane.and_then(|pane| pane.upgrade(cx)) {
|
||||||
let pane = pane.read(cx);
|
let pane = pane.read(cx);
|
||||||
enable_go_backward = pane.can_navigate_backward();
|
enable_go_backward = pane.can_navigate_backward();
|
||||||
enable_go_forward = pane.can_navigate_forward();
|
enable_go_forward = pane.can_navigate_forward();
|
||||||
|
@ -143,19 +143,17 @@ impl View for Toolbar {
|
||||||
enable_go_backward,
|
enable_go_backward,
|
||||||
spacing,
|
spacing,
|
||||||
{
|
{
|
||||||
let pane = pane.clone();
|
|
||||||
move |toolbar, cx| {
|
move |toolbar, cx| {
|
||||||
if let Some(workspace) = toolbar
|
if let Some(pane) = toolbar.pane.as_ref().and_then(|pane| pane.upgrade(cx))
|
||||||
.pane
|
|
||||||
.upgrade(cx)
|
|
||||||
.and_then(|pane| pane.read(cx).workspace().upgrade(cx))
|
|
||||||
{
|
{
|
||||||
let pane = pane.clone();
|
if let Some(workspace) = pane.read(cx).workspace().upgrade(cx) {
|
||||||
cx.window_context().defer(move |cx| {
|
let pane = pane.downgrade();
|
||||||
workspace.update(cx, |workspace, cx| {
|
cx.window_context().defer(move |cx| {
|
||||||
workspace.go_back(pane.clone(), cx).detach_and_log_err(cx);
|
workspace.update(cx, |workspace, cx| {
|
||||||
});
|
workspace.go_back(pane, cx).detach_and_log_err(cx);
|
||||||
})
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -171,21 +169,17 @@ impl View for Toolbar {
|
||||||
enable_go_forward,
|
enable_go_forward,
|
||||||
spacing,
|
spacing,
|
||||||
{
|
{
|
||||||
let pane = pane.clone();
|
|
||||||
move |toolbar, cx| {
|
move |toolbar, cx| {
|
||||||
if let Some(workspace) = toolbar
|
if let Some(pane) = toolbar.pane.as_ref().and_then(|pane| pane.upgrade(cx))
|
||||||
.pane
|
|
||||||
.upgrade(cx)
|
|
||||||
.and_then(|pane| pane.read(cx).workspace().upgrade(cx))
|
|
||||||
{
|
{
|
||||||
let pane = pane.clone();
|
if let Some(workspace) = pane.read(cx).workspace().upgrade(cx) {
|
||||||
cx.window_context().defer(move |cx| {
|
let pane = pane.downgrade();
|
||||||
workspace.update(cx, |workspace, cx| {
|
cx.window_context().defer(move |cx| {
|
||||||
workspace
|
workspace.update(cx, |workspace, cx| {
|
||||||
.go_forward(pane.clone(), cx)
|
workspace.go_forward(pane, cx).detach_and_log_err(cx);
|
||||||
.detach_and_log_err(cx);
|
});
|
||||||
});
|
})
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -231,7 +225,7 @@ fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>
|
||||||
) -> AnyElement<Toolbar> {
|
) -> AnyElement<Toolbar> {
|
||||||
MouseEventHandler::<A, _>::new(0, cx, |state, _| {
|
MouseEventHandler::<A, _>::new(0, cx, |state, _| {
|
||||||
let style = if enabled {
|
let style = if enabled {
|
||||||
style.style_for(state, false)
|
style.style_for(state)
|
||||||
} else {
|
} else {
|
||||||
style.disabled_style()
|
style.disabled_style()
|
||||||
};
|
};
|
||||||
|
@ -269,9 +263,9 @@ fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Toolbar {
|
impl Toolbar {
|
||||||
pub fn new(pane: WeakViewHandle<Pane>) -> Self {
|
pub fn new(pane: Option<WeakViewHandle<Pane>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
active_pane_item: None,
|
active_item: None,
|
||||||
pane,
|
pane,
|
||||||
items: Default::default(),
|
items: Default::default(),
|
||||||
hidden: false,
|
hidden: false,
|
||||||
|
@ -288,7 +282,7 @@ impl Toolbar {
|
||||||
where
|
where
|
||||||
T: 'static + ToolbarItemView,
|
T: 'static + ToolbarItemView,
|
||||||
{
|
{
|
||||||
let location = item.set_active_pane_item(self.active_pane_item.as_deref(), cx);
|
let location = item.set_active_pane_item(self.active_item.as_deref(), cx);
|
||||||
cx.subscribe(&item, |this, item, event, cx| {
|
cx.subscribe(&item, |this, item, event, cx| {
|
||||||
if let Some((_, current_location)) =
|
if let Some((_, current_location)) =
|
||||||
this.items.iter_mut().find(|(i, _)| i.id() == item.id())
|
this.items.iter_mut().find(|(i, _)| i.id() == item.id())
|
||||||
|
@ -307,20 +301,16 @@ impl Toolbar {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_active_pane_item(
|
pub fn set_active_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
|
||||||
&mut self,
|
self.active_item = item.map(|item| item.boxed_clone());
|
||||||
pane_item: Option<&dyn ItemHandle>,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) {
|
|
||||||
self.active_pane_item = pane_item.map(|item| item.boxed_clone());
|
|
||||||
self.hidden = self
|
self.hidden = self
|
||||||
.active_pane_item
|
.active_item
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|item| !item.show_toolbar(cx))
|
.map(|item| !item.show_toolbar(cx))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
for (toolbar_item, current_location) in self.items.iter_mut() {
|
for (toolbar_item, current_location) in self.items.iter_mut() {
|
||||||
let new_location = toolbar_item.set_active_pane_item(pane_item, cx);
|
let new_location = toolbar_item.set_active_pane_item(item, cx);
|
||||||
if new_location != *current_location {
|
if new_location != *current_location {
|
||||||
*current_location = new_location;
|
*current_location = new_location;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
@ -328,9 +318,9 @@ impl Toolbar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut ViewContext<Self>) {
|
pub fn focus_changed(&mut self, focused: bool, cx: &mut ViewContext<Self>) {
|
||||||
for (toolbar_item, _) in self.items.iter_mut() {
|
for (toolbar_item, _) in self.items.iter_mut() {
|
||||||
toolbar_item.pane_focus_update(pane_focused, cx);
|
toolbar_item.focus_changed(focused, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +354,7 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut WindowContext) {
|
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) {
|
||||||
self.update(cx, |this, cx| {
|
self.update(cx, |this, cx| {
|
||||||
this.pane_focus_update(pane_focused, cx);
|
this.pane_focus_update(pane_focused, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
|
@ -861,7 +861,10 @@ impl Workspace {
|
||||||
&self.right_dock
|
&self.right_dock
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
|
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>)
|
||||||
|
where
|
||||||
|
T::Event: std::fmt::Debug,
|
||||||
|
{
|
||||||
let dock = match panel.position(cx) {
|
let dock = match panel.position(cx) {
|
||||||
DockPosition::Left => &self.left_dock,
|
DockPosition::Left => &self.left_dock,
|
||||||
DockPosition::Bottom => &self.bottom_dock,
|
DockPosition::Bottom => &self.bottom_dock,
|
||||||
|
@ -904,10 +907,11 @@ impl Workspace {
|
||||||
});
|
});
|
||||||
} else if T::should_zoom_in_on_event(event) {
|
} else if T::should_zoom_in_on_event(event) {
|
||||||
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
|
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
|
||||||
if panel.has_focus(cx) {
|
if !panel.has_focus(cx) {
|
||||||
this.zoomed = Some(panel.downgrade().into_any());
|
cx.focus(&panel);
|
||||||
this.zoomed_position = Some(panel.read(cx).position(cx));
|
|
||||||
}
|
}
|
||||||
|
this.zoomed = Some(panel.downgrade().into_any());
|
||||||
|
this.zoomed_position = Some(panel.read(cx).position(cx));
|
||||||
} else if T::should_zoom_out_on_event(event) {
|
} else if T::should_zoom_out_on_event(event) {
|
||||||
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx));
|
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx));
|
||||||
if this.zoomed_position == Some(prev_position) {
|
if this.zoomed_position == Some(prev_position) {
|
||||||
|
@ -1702,6 +1706,11 @@ impl Workspace {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
pub fn zoomed_view(&self, cx: &AppContext) -> Option<AnyViewHandle> {
|
||||||
|
self.zoomed.and_then(|view| view.upgrade(cx))
|
||||||
|
}
|
||||||
|
|
||||||
fn dismiss_zoomed_items_to_reveal(
|
fn dismiss_zoomed_items_to_reveal(
|
||||||
&mut self,
|
&mut self,
|
||||||
dock_to_reveal: Option<DockPosition>,
|
dock_to_reveal: Option<DockPosition>,
|
||||||
|
|
13
crates/xtask/Cargo.toml
Normal file
13
crates/xtask/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "xtask"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
clap = {version = "4.0", features = ["derive"]}
|
||||||
|
theme = {path = "../theme"}
|
||||||
|
serde_json.workspace = true
|
||||||
|
schemars.workspace = true
|
23
crates/xtask/src/cli.rs
Normal file
23
crates/xtask/src/cli.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
/// Common utilities for Zed developers.
|
||||||
|
// For more information, see [matklad's repository README](https://github.com/matklad/cargo-xtask/)
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
#[command(propagate_version = true)]
|
||||||
|
pub struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
pub command: Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Command to run.
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum Commands {
|
||||||
|
/// Builds theme types for interop with Typescript.
|
||||||
|
BuildThemeTypes {
|
||||||
|
#[clap(short, long, default_value = "schemas")]
|
||||||
|
out_dir: PathBuf,
|
||||||
|
#[clap(short, long, default_value = "theme.json")]
|
||||||
|
file_name: PathBuf,
|
||||||
|
},
|
||||||
|
}
|
29
crates/xtask/src/main.rs
Normal file
29
crates/xtask/src/main.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
mod cli;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use schemars::schema_for;
|
||||||
|
use theme::Theme;
|
||||||
|
|
||||||
|
fn build_themes(out_dir: PathBuf, file_name: PathBuf) -> Result<()> {
|
||||||
|
let theme = schema_for!(Theme);
|
||||||
|
let output = serde_json::to_string_pretty(&theme)?;
|
||||||
|
|
||||||
|
std::fs::create_dir(&out_dir)?;
|
||||||
|
|
||||||
|
let mut file_path = out_dir;
|
||||||
|
file_path.push(file_name);
|
||||||
|
|
||||||
|
std::fs::write(file_path, output)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let args = cli::Cli::parse();
|
||||||
|
match args.command {
|
||||||
|
cli::Commands::BuildThemeTypes { out_dir, file_name } => build_themes(out_dir, file_name),
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
||||||
description = "The fast, collaborative code editor."
|
description = "The fast, collaborative code editor."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.92.0"
|
version = "0.93.0"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@ -62,7 +62,6 @@ text = { path = "../text" }
|
||||||
terminal_view = { path = "../terminal_view" }
|
terminal_view = { path = "../terminal_view" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
theme_selector = { path = "../theme_selector" }
|
theme_selector = { path = "../theme_selector" }
|
||||||
theme_testbench = { path = "../theme_testbench" }
|
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
vim = { path = "../vim" }
|
vim = { path = "../vim" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -156,7 +156,6 @@ fn main() {
|
||||||
search::init(cx);
|
search::init(cx);
|
||||||
vim::init(cx);
|
vim::init(cx);
|
||||||
terminal_view::init(cx);
|
terminal_view::init(cx);
|
||||||
theme_testbench::init(cx);
|
|
||||||
copilot::init(http.clone(), node_runtime, cx);
|
copilot::init(http.clone(), node_runtime, cx);
|
||||||
ai::init(cx);
|
ai::init(cx);
|
||||||
|
|
||||||
|
|
1
styles/.gitignore
vendored
1
styles/.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
|
coverage/
|
||||||
|
|
20
styles/.zed/settings.json
Normal file
20
styles/.zed/settings.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// Folder-specific settings
|
||||||
|
//
|
||||||
|
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||||
|
// see the documentation: https://docs.zed.dev/configuration/configuring-zed#folder-specific-settings
|
||||||
|
{
|
||||||
|
"languages": {
|
||||||
|
"TypeScript": {
|
||||||
|
"tab_size": 4
|
||||||
|
},
|
||||||
|
"TSX": {
|
||||||
|
"tab_size": 4
|
||||||
|
},
|
||||||
|
"JavaScript": {
|
||||||
|
"tab_size": 4
|
||||||
|
},
|
||||||
|
"JSON": {
|
||||||
|
"tab_size": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1642
styles/package-lock.json
generated
1642
styles/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,9 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "ts-node ./src/buildThemes.ts",
|
"build": "ts-node ./src/buildThemes.ts",
|
||||||
"build-licenses": "ts-node ./src/buildLicenses.ts",
|
"build-licenses": "ts-node ./src/buildLicenses.ts",
|
||||||
"build-tokens": "ts-node ./src/buildTokens.ts"
|
"build-tokens": "ts-node ./src/buildTokens.ts",
|
||||||
|
"build-types": "cd ../crates/theme && cargo test && cd ../../styles && ts-node ./src/buildTypes.ts",
|
||||||
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
@ -19,13 +21,20 @@
|
||||||
"case-anything": "^2.1.10",
|
"case-anything": "^2.1.10",
|
||||||
"chroma-js": "^2.4.2",
|
"chroma-js": "^2.4.2",
|
||||||
"deepmerge": "^4.3.0",
|
"deepmerge": "^4.3.0",
|
||||||
|
"json-schema-to-typescript": "^13.0.2",
|
||||||
"toml": "^3.0.0",
|
"toml": "^3.0.0",
|
||||||
"ts-node": "^10.9.1"
|
"ts-deepmerge": "^6.0.3",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"utility-types": "^3.10.0",
|
||||||
|
"vitest": "^0.32.0"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"printWidth": 80,
|
"printWidth": 80,
|
||||||
"htmlWhitespaceSensitivity": "strict",
|
"htmlWhitespaceSensitivity": "strict",
|
||||||
"tabWidth": 4
|
"tabWidth": 4
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitest/coverage-v8": "^0.32.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import * as fs from "fs";
|
import * as fs from "fs"
|
||||||
import * as path from "path";
|
import * as path from "path"
|
||||||
import { ColorScheme, createColorScheme } from "./common";
|
import { ColorScheme, createColorScheme } from "./common"
|
||||||
import { themes } from "./themes";
|
import { themes } from "./themes"
|
||||||
import { slugify } from "./utils/slugify";
|
import { slugify } from "./utils/slugify"
|
||||||
import { colorSchemeTokens } from "./theme/tokens/colorScheme";
|
import { colorSchemeTokens } from "./theme/tokens/colorScheme"
|
||||||
|
|
||||||
const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens");
|
const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens")
|
||||||
const TOKENS_FILE = path.join(TOKENS_DIRECTORY, "$themes.json");
|
const TOKENS_FILE = path.join(TOKENS_DIRECTORY, "$themes.json")
|
||||||
const METADATA_FILE = path.join(TOKENS_DIRECTORY, "$metadata.json");
|
const METADATA_FILE = path.join(TOKENS_DIRECTORY, "$metadata.json")
|
||||||
|
|
||||||
function clearTokens(tokensDirectory: string) {
|
function clearTokens(tokensDirectory: string) {
|
||||||
if (!fs.existsSync(tokensDirectory)) {
|
if (!fs.existsSync(tokensDirectory)) {
|
||||||
|
@ -22,64 +22,66 @@ function clearTokens(tokensDirectory: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenSet = {
|
type TokenSet = {
|
||||||
id: string;
|
id: string
|
||||||
name: string;
|
name: string
|
||||||
selectedTokenSets: { [key: string]: "enabled" };
|
selectedTokenSets: { [key: string]: "enabled" }
|
||||||
};
|
}
|
||||||
|
|
||||||
function buildTokenSetOrder(colorSchemes: ColorScheme[]): { tokenSetOrder: string[] } {
|
function buildTokenSetOrder(colorSchemes: ColorScheme[]): {
|
||||||
const tokenSetOrder: string[] = colorSchemes.map(
|
tokenSetOrder: string[]
|
||||||
(scheme) => scheme.name.toLowerCase().replace(/\s+/g, "_")
|
} {
|
||||||
);
|
const tokenSetOrder: string[] = colorSchemes.map((scheme) =>
|
||||||
return { tokenSetOrder };
|
scheme.name.toLowerCase().replace(/\s+/g, "_")
|
||||||
|
)
|
||||||
|
return { tokenSetOrder }
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildThemesIndex(colorSchemes: ColorScheme[]): TokenSet[] {
|
function buildThemesIndex(colorSchemes: ColorScheme[]): TokenSet[] {
|
||||||
const themesIndex: TokenSet[] = colorSchemes.map((scheme, index) => {
|
const themesIndex: TokenSet[] = colorSchemes.map((scheme, index) => {
|
||||||
const id = `${scheme.isLight ? "light" : "dark"}_${scheme.name
|
const id = `${scheme.isLight ? "light" : "dark"}_${scheme.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/\s+/g, "_")}_${index}`;
|
.replace(/\s+/g, "_")}_${index}`
|
||||||
const selectedTokenSets: { [key: string]: "enabled" } = {};
|
const selectedTokenSets: { [key: string]: "enabled" } = {}
|
||||||
const tokenSet = scheme.name.toLowerCase().replace(/\s+/g, "_");
|
const tokenSet = scheme.name.toLowerCase().replace(/\s+/g, "_")
|
||||||
selectedTokenSets[tokenSet] = "enabled";
|
selectedTokenSets[tokenSet] = "enabled"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
name: `${scheme.name} - ${scheme.isLight ? "Light" : "Dark"}`,
|
name: `${scheme.name} - ${scheme.isLight ? "Light" : "Dark"}`,
|
||||||
selectedTokenSets,
|
selectedTokenSets,
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
return themesIndex;
|
return themesIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeTokens(colorSchemes: ColorScheme[], tokensDirectory: string) {
|
function writeTokens(colorSchemes: ColorScheme[], tokensDirectory: string) {
|
||||||
clearTokens(tokensDirectory);
|
clearTokens(tokensDirectory)
|
||||||
|
|
||||||
for (const colorScheme of colorSchemes) {
|
for (const colorScheme of colorSchemes) {
|
||||||
const fileName = slugify(colorScheme.name) + ".json";
|
const fileName = slugify(colorScheme.name) + ".json"
|
||||||
const tokens = colorSchemeTokens(colorScheme);
|
const tokens = colorSchemeTokens(colorScheme)
|
||||||
const tokensJSON = JSON.stringify(tokens, null, 2);
|
const tokensJSON = JSON.stringify(tokens, null, 2)
|
||||||
const outPath = path.join(tokensDirectory, fileName);
|
const outPath = path.join(tokensDirectory, fileName)
|
||||||
fs.writeFileSync(outPath, tokensJSON, { mode: 0o644 });
|
fs.writeFileSync(outPath, tokensJSON, { mode: 0o644 })
|
||||||
console.log(`- ${outPath} created`);
|
console.log(`- ${outPath} created`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const themeIndexData = buildThemesIndex(colorSchemes);
|
const themeIndexData = buildThemesIndex(colorSchemes)
|
||||||
|
|
||||||
const themesJSON = JSON.stringify(themeIndexData, null, 2);
|
const themesJSON = JSON.stringify(themeIndexData, null, 2)
|
||||||
fs.writeFileSync(TOKENS_FILE, themesJSON, { mode: 0o644 });
|
fs.writeFileSync(TOKENS_FILE, themesJSON, { mode: 0o644 })
|
||||||
console.log(`- ${TOKENS_FILE} created`);
|
console.log(`- ${TOKENS_FILE} created`)
|
||||||
|
|
||||||
const tokenSetOrderData = buildTokenSetOrder(colorSchemes);
|
const tokenSetOrderData = buildTokenSetOrder(colorSchemes)
|
||||||
|
|
||||||
const metadataJSON = JSON.stringify(tokenSetOrderData, null, 2);
|
const metadataJSON = JSON.stringify(tokenSetOrderData, null, 2)
|
||||||
fs.writeFileSync(METADATA_FILE, metadataJSON, { mode: 0o644 });
|
fs.writeFileSync(METADATA_FILE, metadataJSON, { mode: 0o644 })
|
||||||
console.log(`- ${METADATA_FILE} created`);
|
console.log(`- ${METADATA_FILE} created`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const colorSchemes: ColorScheme[] = themes.map((theme) =>
|
const colorSchemes: ColorScheme[] = themes.map((theme) =>
|
||||||
createColorScheme(theme)
|
createColorScheme(theme)
|
||||||
);
|
)
|
||||||
|
|
||||||
writeTokens(colorSchemes, TOKENS_DIRECTORY);
|
writeTokens(colorSchemes, TOKENS_DIRECTORY)
|
||||||
|
|
64
styles/src/buildTypes.ts
Normal file
64
styles/src/buildTypes.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import * as fs from "fs/promises"
|
||||||
|
import * as fsSync from "fs"
|
||||||
|
import * as path from "path"
|
||||||
|
import { compile } from "json-schema-to-typescript"
|
||||||
|
|
||||||
|
const BANNER = `/*
|
||||||
|
* This file is autogenerated
|
||||||
|
*/\n\n`
|
||||||
|
const dirname = __dirname
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
let schemasPath = path.join(dirname, "../../", "crates/theme/schemas")
|
||||||
|
let schemaFiles = (await fs.readdir(schemasPath)).filter((x) =>
|
||||||
|
x.endsWith(".json")
|
||||||
|
)
|
||||||
|
|
||||||
|
let compiledTypes = new Set()
|
||||||
|
|
||||||
|
for (let filename of schemaFiles) {
|
||||||
|
let filePath = path.join(schemasPath, filename)
|
||||||
|
const fileContents = await fs.readFile(filePath)
|
||||||
|
let schema = JSON.parse(fileContents.toString())
|
||||||
|
let compiled = await compile(schema, schema.title, {
|
||||||
|
bannerComment: "",
|
||||||
|
})
|
||||||
|
let eachType = compiled.split("export")
|
||||||
|
for (let type of eachType) {
|
||||||
|
if (!type) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
compiledTypes.add("export " + type.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = BANNER + Array.from(compiledTypes).join("\n\n")
|
||||||
|
let outputPath = path.join(dirname, "../../styles/src/types/zed.ts")
|
||||||
|
|
||||||
|
try {
|
||||||
|
let existing = await fs.readFile(outputPath)
|
||||||
|
if (existing.toString() == output) {
|
||||||
|
// Skip writing if it hasn't changed
|
||||||
|
console.log("Schemas are up to date")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// It's fine if there's no output from a previous run.
|
||||||
|
// @ts-ignore
|
||||||
|
if (e.code !== "ENOENT") {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const typesDic = path.dirname(outputPath)
|
||||||
|
if (!fsSync.existsSync(typesDic)) {
|
||||||
|
await fs.mkdir(typesDic)
|
||||||
|
}
|
||||||
|
await fs.writeFile(outputPath, output)
|
||||||
|
console.log(`Wrote Typescript types to ${outputPath}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
4
styles/src/element/index.ts
Normal file
4
styles/src/element/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { interactive } from "./interactive"
|
||||||
|
import { toggleable } from "./toggle"
|
||||||
|
|
||||||
|
export { interactive, toggleable }
|
56
styles/src/element/interactive.test.ts
Normal file
56
styles/src/element/interactive.test.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import {
|
||||||
|
NOT_ENOUGH_STATES_ERROR,
|
||||||
|
NO_DEFAULT_OR_BASE_ERROR,
|
||||||
|
interactive,
|
||||||
|
} from "./interactive"
|
||||||
|
import { describe, it, expect } from "vitest"
|
||||||
|
|
||||||
|
describe("interactive", () => {
|
||||||
|
it("creates an Interactive<Element> with base properties and states", () => {
|
||||||
|
const result = interactive({
|
||||||
|
base: { fontSize: 10, color: "#FFFFFF" },
|
||||||
|
state: {
|
||||||
|
hovered: { color: "#EEEEEE" },
|
||||||
|
clicked: { color: "#CCCCCC" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
default: { color: "#FFFFFF", fontSize: 10 },
|
||||||
|
hovered: { color: "#EEEEEE", fontSize: 10 },
|
||||||
|
clicked: { color: "#CCCCCC", fontSize: 10 },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates an Interactive<Element> with no base properties", () => {
|
||||||
|
const result = interactive({
|
||||||
|
state: {
|
||||||
|
default: { color: "#FFFFFF", fontSize: 10 },
|
||||||
|
hovered: { color: "#EEEEEE" },
|
||||||
|
clicked: { color: "#CCCCCC" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
default: { color: "#FFFFFF", fontSize: 10 },
|
||||||
|
hovered: { color: "#EEEEEE", fontSize: 10 },
|
||||||
|
clicked: { color: "#CCCCCC", fontSize: 10 },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("throws error when both default and base are missing", () => {
|
||||||
|
const state = {
|
||||||
|
hovered: { color: "blue" },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => interactive({ state })).toThrow(NO_DEFAULT_OR_BASE_ERROR)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("throws error when no other state besides default is present", () => {
|
||||||
|
const state = {
|
||||||
|
default: { fontSize: 10 },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => interactive({ state })).toThrow(NOT_ENOUGH_STATES_ERROR)
|
||||||
|
})
|
||||||
|
})
|
97
styles/src/element/interactive.ts
Normal file
97
styles/src/element/interactive.ts
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import merge from "ts-deepmerge"
|
||||||
|
import { DeepPartial } from "utility-types"
|
||||||
|
|
||||||
|
type InteractiveState =
|
||||||
|
| "default"
|
||||||
|
| "hovered"
|
||||||
|
| "clicked"
|
||||||
|
| "selected"
|
||||||
|
| "disabled"
|
||||||
|
|
||||||
|
type Interactive<T> = {
|
||||||
|
default: T
|
||||||
|
hovered?: T
|
||||||
|
clicked?: T
|
||||||
|
selected?: T
|
||||||
|
disabled?: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NO_DEFAULT_OR_BASE_ERROR =
|
||||||
|
"An interactive object must have a default state, or a base property."
|
||||||
|
export const NOT_ENOUGH_STATES_ERROR =
|
||||||
|
"An interactive object must have a default and at least one other state."
|
||||||
|
|
||||||
|
interface InteractiveProps<T> {
|
||||||
|
base?: T
|
||||||
|
state: Partial<Record<InteractiveState, DeepPartial<T>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for creating Interactive<T> objects that works with Toggle<T>-like behavior.
|
||||||
|
* It takes a default object to be used as the value for `default` field and fills out other fields
|
||||||
|
* with fields from either `base` or from the `state` object which contains values for specific states.
|
||||||
|
* Notably, it does not touch `hover`, `clicked`, `selected` and `disabled` states if there are no modifications for them.
|
||||||
|
*
|
||||||
|
* @param defaultObj Object to be used as the value for the `default` field.
|
||||||
|
* @param base Optional object containing base fields to be included in the resulting object.
|
||||||
|
* @param state Object containing optional modified fields to be included in the resulting object for each state.
|
||||||
|
* @returns Interactive<T> object with fields from `base` and `state`.
|
||||||
|
*/
|
||||||
|
export function interactive<T extends Object>({
|
||||||
|
base,
|
||||||
|
state,
|
||||||
|
}: InteractiveProps<T>): Interactive<T> {
|
||||||
|
if (!base && !state.default) throw new Error(NO_DEFAULT_OR_BASE_ERROR)
|
||||||
|
|
||||||
|
let defaultState: T
|
||||||
|
|
||||||
|
if (state.default && base) {
|
||||||
|
defaultState = merge(base, state.default) as T
|
||||||
|
} else {
|
||||||
|
defaultState = base ? base : (state.default as T)
|
||||||
|
}
|
||||||
|
|
||||||
|
let interactiveObj: Interactive<T> = {
|
||||||
|
default: defaultState,
|
||||||
|
}
|
||||||
|
|
||||||
|
let stateCount = 0
|
||||||
|
|
||||||
|
if (state.hovered !== undefined) {
|
||||||
|
interactiveObj.hovered = merge(
|
||||||
|
interactiveObj.default,
|
||||||
|
state.hovered
|
||||||
|
) as T
|
||||||
|
stateCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.clicked !== undefined) {
|
||||||
|
interactiveObj.clicked = merge(
|
||||||
|
interactiveObj.default,
|
||||||
|
state.clicked
|
||||||
|
) as T
|
||||||
|
stateCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.selected !== undefined) {
|
||||||
|
interactiveObj.selected = merge(
|
||||||
|
interactiveObj.default,
|
||||||
|
state.selected
|
||||||
|
) as T
|
||||||
|
stateCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.disabled !== undefined) {
|
||||||
|
interactiveObj.disabled = merge(
|
||||||
|
interactiveObj.default,
|
||||||
|
state.disabled
|
||||||
|
) as T
|
||||||
|
stateCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateCount < 1) {
|
||||||
|
throw new Error(NOT_ENOUGH_STATES_ERROR)
|
||||||
|
}
|
||||||
|
|
||||||
|
return interactiveObj
|
||||||
|
}
|
52
styles/src/element/toggle.test.ts
Normal file
52
styles/src/element/toggle.test.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import {
|
||||||
|
NO_ACTIVE_ERROR,
|
||||||
|
NO_INACTIVE_OR_BASE_ERROR,
|
||||||
|
toggleable,
|
||||||
|
} from "./toggle"
|
||||||
|
import { describe, it, expect } from "vitest"
|
||||||
|
|
||||||
|
describe("toggleable", () => {
|
||||||
|
it("creates a Toggleable<Element> with base properties and states", () => {
|
||||||
|
const result = toggleable({
|
||||||
|
base: { background: "#000000", color: "#CCCCCC" },
|
||||||
|
state: {
|
||||||
|
active: { color: "#FFFFFF" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||||
|
active: { background: "#000000", color: "#FFFFFF" },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates a Toggleable<Element> with no base properties", () => {
|
||||||
|
const result = toggleable({
|
||||||
|
state: {
|
||||||
|
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||||
|
active: { background: "#000000", color: "#FFFFFF" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||||
|
active: { background: "#000000", color: "#FFFFFF" },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("throws error when both inactive and base are missing", () => {
|
||||||
|
const state = {
|
||||||
|
active: { background: "#000000", color: "#FFFFFF" },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => toggleable({ state })).toThrow(NO_INACTIVE_OR_BASE_ERROR)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("throws error when no active state is present", () => {
|
||||||
|
const state = {
|
||||||
|
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => toggleable({ state })).toThrow(NO_ACTIVE_ERROR)
|
||||||
|
})
|
||||||
|
})
|
47
styles/src/element/toggle.ts
Normal file
47
styles/src/element/toggle.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import merge from "ts-deepmerge"
|
||||||
|
import { DeepPartial } from "utility-types"
|
||||||
|
|
||||||
|
type ToggleState = "inactive" | "active"
|
||||||
|
|
||||||
|
type Toggleable<T> = Record<ToggleState, T>
|
||||||
|
|
||||||
|
export const NO_INACTIVE_OR_BASE_ERROR =
|
||||||
|
"A toggleable object must have an inactive state, or a base property."
|
||||||
|
export const NO_ACTIVE_ERROR = "A toggleable object must have an active state."
|
||||||
|
|
||||||
|
interface ToggleableProps<T> {
|
||||||
|
base?: T
|
||||||
|
state: Partial<Record<ToggleState, DeepPartial<T>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for creating Toggleable objects.
|
||||||
|
* @template T The type of the object being toggled.
|
||||||
|
* @param props Object containing the base (inactive) state and state modifications to create the active state.
|
||||||
|
* @returns A Toggleable object containing both the inactive and active states.
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* toggleable({
|
||||||
|
* base: { background: "#000000", text: "#CCCCCC" },
|
||||||
|
* state: { active: { text: "#CCCCCC" } },
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function toggleable<T extends object>(
|
||||||
|
props: ToggleableProps<T>
|
||||||
|
): Toggleable<T> {
|
||||||
|
const { base, state } = props
|
||||||
|
|
||||||
|
if (!base && !state.inactive) throw new Error(NO_INACTIVE_OR_BASE_ERROR)
|
||||||
|
if (!state.active) throw new Error(NO_ACTIVE_ERROR)
|
||||||
|
|
||||||
|
const inactiveState = base
|
||||||
|
? ((state.inactive ? merge(base, state.inactive) : base) as T)
|
||||||
|
: (state.inactive as T)
|
||||||
|
|
||||||
|
const toggleObj: Toggleable<T> = {
|
||||||
|
inactive: inactiveState,
|
||||||
|
active: merge(base ?? {}, state.active) as T,
|
||||||
|
}
|
||||||
|
return toggleObj
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
import { text } from "./components"
|
|
||||||
import contactFinder from "./contactFinder"
|
import contactFinder from "./contactFinder"
|
||||||
import contactsPopover from "./contactsPopover"
|
import contactsPopover from "./contactsPopover"
|
||||||
import commandPalette from "./commandPalette"
|
import commandPalette from "./commandPalette"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { text, border, background, foreground } from "./components"
|
import { text, border, background, foreground } from "./components"
|
||||||
import editor from "./editor"
|
import editor from "./editor"
|
||||||
|
import { interactive } from "../element"
|
||||||
|
|
||||||
export default function assistant(colorScheme: ColorScheme) {
|
export default function assistant(colorScheme: ColorScheme) {
|
||||||
const layer = colorScheme.highest
|
const layer = colorScheme.highest
|
||||||
|
@ -9,50 +10,244 @@ export default function assistant(colorScheme: ColorScheme) {
|
||||||
background: editor(colorScheme).background,
|
background: editor(colorScheme).background,
|
||||||
padding: { left: 12 },
|
padding: { left: 12 },
|
||||||
},
|
},
|
||||||
header: {
|
messageHeader: {
|
||||||
border: border(layer, "default", { bottom: true, top: true }),
|
border: border(layer, "default", { bottom: true, top: true }),
|
||||||
margin: { bottom: 6, top: 6 },
|
margin: { bottom: 6, top: 6 },
|
||||||
background: editor(colorScheme).background,
|
background: editor(colorScheme).background,
|
||||||
},
|
},
|
||||||
|
hamburgerButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/hamburger_15.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 15,
|
||||||
|
height: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { left: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
splitButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/split_message_15.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 15,
|
||||||
|
height: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { left: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
quoteButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/quote_15.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 15,
|
||||||
|
height: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { left: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
assistButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/assist_15.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 15,
|
||||||
|
height: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { left: 12, right: 24 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
zoomInButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/maximize_8.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { right: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
zoomOutButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/minimize_8.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { right: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
plusButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/plus_12.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { right: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
title: {
|
||||||
|
margin: { left: 12 },
|
||||||
|
...text(layer, "sans", "default", { size: "sm" })
|
||||||
|
},
|
||||||
|
savedConversation: {
|
||||||
|
container: interactive({
|
||||||
|
base: {
|
||||||
|
background: background(layer, "on"),
|
||||||
|
padding: { top: 4, bottom: 4 }
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "on", "hovered"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
savedAt: {
|
||||||
|
margin: { left: 8 },
|
||||||
|
...text(layer, "sans", "default", { size: "xs" }),
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
margin: { left: 16 },
|
||||||
|
...text(layer, "sans", "default", { size: "sm", weight: "bold" }),
|
||||||
|
}
|
||||||
|
},
|
||||||
userSender: {
|
userSender: {
|
||||||
...text(layer, "sans", "default", { size: "sm", weight: "bold" }),
|
default: {
|
||||||
|
...text(layer, "sans", "default", {
|
||||||
|
size: "sm",
|
||||||
|
weight: "bold",
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
assistantSender: {
|
assistantSender: {
|
||||||
...text(layer, "sans", "accent", { size: "sm", weight: "bold" }),
|
default: {
|
||||||
|
...text(layer, "sans", "accent", {
|
||||||
|
size: "sm",
|
||||||
|
weight: "bold",
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
systemSender: {
|
systemSender: {
|
||||||
...text(layer, "sans", "variant", { size: "sm", weight: "bold" }),
|
default: {
|
||||||
|
...text(layer, "sans", "variant", {
|
||||||
|
size: "sm",
|
||||||
|
weight: "bold",
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
sentAt: {
|
sentAt: {
|
||||||
margin: { top: 2, left: 8 },
|
margin: { top: 2, left: 8 },
|
||||||
...text(layer, "sans", "default", { size: "2xs" }),
|
...text(layer, "sans", "default", { size: "2xs" }),
|
||||||
},
|
},
|
||||||
modelInfoContainer: {
|
model: interactive({
|
||||||
margin: { right: 16, top: 4 },
|
base: {
|
||||||
},
|
background: background(layer, "on"),
|
||||||
model: {
|
margin: { left: 12, right: 12, top: 12 },
|
||||||
background: background(layer, "on"),
|
padding: 4,
|
||||||
border: border(layer, "on", { overlay: true }),
|
cornerRadius: 4,
|
||||||
padding: 4,
|
...text(layer, "sans", "default", { size: "xs" }),
|
||||||
cornerRadius: 4,
|
|
||||||
...text(layer, "sans", "default", { size: "xs" }),
|
|
||||||
hover: {
|
|
||||||
background: background(layer, "on", "hovered"),
|
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "on", "hovered"),
|
||||||
|
border: border(layer, "on", { overlay: true }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
remainingTokens: {
|
remainingTokens: {
|
||||||
background: background(layer, "on"),
|
background: background(layer, "on"),
|
||||||
border: border(layer, "on", { overlay: true }),
|
margin: { top: 12, right: 12 },
|
||||||
padding: 4,
|
padding: 4,
|
||||||
margin: { left: 4 },
|
|
||||||
cornerRadius: 4,
|
cornerRadius: 4,
|
||||||
...text(layer, "sans", "positive", { size: "xs" }),
|
...text(layer, "sans", "positive", { size: "xs" }),
|
||||||
},
|
},
|
||||||
noRemainingTokens: {
|
noRemainingTokens: {
|
||||||
background: background(layer, "on"),
|
background: background(layer, "on"),
|
||||||
border: border(layer, "on", { overlay: true }),
|
margin: { top: 12, right: 12 },
|
||||||
padding: 4,
|
padding: 4,
|
||||||
margin: { left: 4 },
|
|
||||||
cornerRadius: 4,
|
cornerRadius: 4,
|
||||||
...text(layer, "sans", "negative", { size: "xs" }),
|
...text(layer, "sans", "negative", { size: "xs" }),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { withOpacity } from "../theme/color"
|
import { withOpacity } from "../theme/color"
|
||||||
import { text, background } from "./components"
|
import { text, background } from "./components"
|
||||||
|
import { toggleable } from "../element"
|
||||||
|
|
||||||
export default function commandPalette(colorScheme: ColorScheme) {
|
export default function commandPalette(colorScheme: ColorScheme) {
|
||||||
let layer = colorScheme.highest
|
let layer = colorScheme.highest
|
||||||
return {
|
|
||||||
keystrokeSpacing: 8,
|
const key = toggleable({
|
||||||
key: {
|
base: {
|
||||||
text: text(layer, "mono", "variant", "default", { size: "xs" }),
|
text: text(layer, "mono", "variant", "default", { size: "xs" }),
|
||||||
cornerRadius: 2,
|
cornerRadius: 2,
|
||||||
background: background(layer, "on"),
|
background: background(layer, "on"),
|
||||||
|
@ -21,10 +22,21 @@ export default function commandPalette(colorScheme: ColorScheme) {
|
||||||
bottom: 1,
|
bottom: 1,
|
||||||
left: 2,
|
left: 2,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
state: {
|
||||||
active: {
|
active: {
|
||||||
text: text(layer, "mono", "on", "default", { size: "xs" }),
|
text: text(layer, "mono", "on", "default", { size: "xs" }),
|
||||||
background: withOpacity(background(layer, "on"), 0.2),
|
background: withOpacity(background(layer, "on"), 0.2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
keystrokeSpacing: 8,
|
||||||
|
// TODO: This should be a Toggle<ContainedText> on the rust side so we don't have to do this
|
||||||
|
key: {
|
||||||
|
inactive: { ...key.inactive },
|
||||||
|
active: key.active,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ export function foreground(
|
||||||
return getStyle(layer, styleSetOrStyles, style).foreground
|
return getStyle(layer, styleSetOrStyles, style).foreground
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Text {
|
interface Text extends Object {
|
||||||
family: keyof typeof fontFamilies
|
family: keyof typeof fontFamilies
|
||||||
color: string
|
color: string
|
||||||
size: number
|
size: number
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue