feat: adopt the native OS theme #66

Merged
reya merged 14 commits from feat/native-theme into master 2026-10-23 04:07:12 +00:00
173 changed files with 2775 additions and 10885 deletions
Showing only changes of commit d0ad1a6f57 - Show all commits
Generated
+531 -40
View File
@@ -188,6 +188,23 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "annotate-snippets"
version = "0.12.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a5b0a5ab8561c603f16c3b78c24a386b1f70e7fc1b1b1e8c15738828e4a176"
dependencies = [
"anstyle",
"memchr",
"unicode-width",
]
[[package]]
name = "anstyle"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.104" version = "1.0.104"
@@ -200,6 +217,15 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bc62ac97cc33321f50863d514c3bc38a453947a8f9e781137e47c7401020aed" checksum = "3bc62ac97cc33321f50863d514c3bc38a453947a8f9e781137e47c7401020aed"
[[package]]
name = "arc-swap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380962b3d93b99642e50b9febf72c071e97e8b32d86eae9cf4ef582bb960ae6e"
dependencies = [
"rustversion",
]
[[package]] [[package]]
name = "arg_enum_proc_macro" name = "arg_enum_proc_macro"
version = "0.3.4" version = "0.3.4"
@@ -211,6 +237,12 @@ dependencies = [
"syn 2.0.119", "syn 2.0.119",
] ]
[[package]]
name = "arraydeque"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.9" version = "0.3.9"
@@ -270,6 +302,7 @@ name = "assets"
version = "2.0.0-alpha" version = "2.0.0-alpha"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"gpui-kit",
"gpui-pre", "gpui-pre",
"rust-embed", "rust-embed",
] ]
@@ -607,6 +640,12 @@ dependencies = [
"windows-link 0.2.1", "windows-link 0.2.1",
] ]
[[package]]
name = "base62"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3a647dce9631f152a99ec16cfc09267ae55fe280d5dc288964027d458076323"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.22.1"
@@ -821,6 +860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f"
dependencies = [ dependencies = [
"memchr", "memchr",
"regex-automata",
"serde_core", "serde_core",
] ]
@@ -1020,6 +1060,7 @@ dependencies = [
"anyhow", "anyhow",
"chat", "chat",
"common", "common",
"gpui-kit",
"gpui-pre", "gpui-pre",
"itertools 0.13.0", "itertools 0.13.0",
"nostr-sdk", "nostr-sdk",
@@ -1029,7 +1070,6 @@ dependencies = [
"settings", "settings",
"smallvec", "smallvec",
"state", "state",
"theme",
"ui", "ui",
] ]
@@ -1285,6 +1325,7 @@ dependencies = [
"assets", "assets",
"auto_update", "auto_update",
"chat", "chat",
"gpui-kit",
"gpui-pre", "gpui-pre",
"gpui-pre-linux", "gpui-pre-linux",
"gpui-pre-macos", "gpui-pre-macos",
@@ -1298,7 +1339,6 @@ dependencies = [
"person", "person",
"settings", "settings",
"state", "state",
"theme",
"tracing-subscriber", "tracing-subscriber",
"ui", "ui",
"workspace", "workspace",
@@ -1314,6 +1354,7 @@ dependencies = [
"getrandom 0.2.17", "getrandom 0.2.17",
"getrandom 0.3.4", "getrandom 0.3.4",
"getrandom 0.4.3", "getrandom 0.4.3",
"gpui-kit",
"gpui-pre", "gpui-pre",
"gpui-pre-platform", "gpui-pre-platform",
"gpui-pre-web", "gpui-pre-web",
@@ -1324,7 +1365,6 @@ dependencies = [
"person", "person",
"settings", "settings",
"state", "state",
"theme",
"tracing-wasm", "tracing-wasm",
"ui", "ui",
"universal-time 0.3.1 (git+https://github.com/shadowylab/universal-time)", "universal-time 0.3.1 (git+https://github.com/shadowylab/universal-time)",
@@ -1834,12 +1874,41 @@ dependencies = [
"simdutf8", "simdutf8",
] ]
[[package]]
name = "encoding_rs_io"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fba3fe847045ecff794b9c138293a80db914678c453ad63fbf0c6a9eb6e00b22"
dependencies = [
"encoding_rs",
]
[[package]] [[package]]
name = "endi" name = "endi"
version = "1.1.1" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
[[package]]
name = "enum-iterator"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4549325971814bda7a44061bf3fe7e487d447cba01e4220a4b454d630d7a016"
dependencies = [
"enum-iterator-derive",
]
[[package]]
name = "enum-iterator-derive"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]] [[package]]
name = "enumflags2" name = "enumflags2"
version = "0.7.12" version = "0.7.12"
@@ -2200,6 +2269,15 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "fsevent-sys"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "futf" name = "futf"
version = "0.1.5" version = "0.1.5"
@@ -2391,6 +2469,16 @@ dependencies = [
"polyval", "polyval",
] ]
[[package]]
name = "gif"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b"
dependencies = [
"color_quant",
"weezl",
]
[[package]] [[package]]
name = "gif" name = "gif"
version = "0.14.2" version = "0.14.2"
@@ -2418,6 +2506,12 @@ dependencies = [
"xml-rs", "xml-rs",
] ]
[[package]]
name = "glob"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
[[package]] [[package]]
name = "globset" name = "globset"
version = "0.4.20" version = "0.4.20"
@@ -2431,6 +2525,17 @@ dependencies = [
"regex-syntax", "regex-syntax",
] ]
[[package]]
name = "globwalk"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
dependencies = [
"bitflags 1.3.2",
"ignore",
"walkdir",
]
[[package]] [[package]]
name = "gloo-timers" name = "gloo-timers"
version = "0.3.0" version = "0.3.0"
@@ -2539,6 +2644,95 @@ dependencies = [
"windows 0.58.0", "windows 0.58.0",
] ]
[[package]]
name = "gpui-component"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465a363235cd4062ef5f0953fc096fc764ed6f53dfbd9f409f669f93ea3b1ff9"
dependencies = [
"anyhow",
"chrono",
"core-text",
"enum-iterator",
"gpui-base",
"gpui-component-macros",
"gpui-kit-assets",
"gpui-pre",
"gpui-pre-macros",
"gpui-pre-sum-tree",
"instant",
"itertools 0.13.0",
"log",
"lsp-types",
"markdown",
"notify",
"num-traits",
"objc2 0.6.5",
"objc2-app-kit 0.3.2",
"objc2-foundation 0.3.2",
"once_cell",
"paste",
"raw-window-handle",
"regex",
"resvg 0.45.1",
"ropey",
"rust-i18n",
"schemars",
"serde",
"serde_json",
"serde_repr",
"similar",
"smallvec",
"smol",
"tracing",
"unicode-segmentation",
"unicode-width",
"uuid",
"windows 0.58.0",
]
[[package]]
name = "gpui-component-macros"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c0c9398aaf99e595d450e38eb44ff0c500df8c332b4783d5b72022e69525359"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "gpui-kit"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac401f6b149bf33794da0942510240afd29b7b18556574e4cf17196b647066"
dependencies = [
"gpui-base",
"gpui-component",
"gpui-kit-assets",
"gpui-pre",
"gpui-pre-macros",
"gpui-pre-platform",
"gpui-pre-web",
]
[[package]]
name = "gpui-kit-assets"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7abc281dcbfe51c16a7e73189521a3a771168c31e3ab0db99e21e481d1b1b9a"
dependencies = [
"anyhow",
"gpui-pre",
"gpui-pre-reqwest",
"log",
"rust-embed",
"wasm-bindgen",
"wasm-bindgen-futures",
]
[[package]] [[package]]
name = "gpui-pre" name = "gpui-pre"
version = "0.3.9" version = "0.3.9"
@@ -2584,7 +2778,7 @@ dependencies = [
"rand 0.9.5", "rand 0.9.5",
"raw-window-handle", "raw-window-handle",
"regex", "regex",
"resvg", "resvg 0.46.0",
"schemars", "schemars",
"seahash", "seahash",
"serde", "serde",
@@ -2597,7 +2791,7 @@ dependencies = [
"thiserror 2.0.21", "thiserror 2.0.21",
"ttf-parser", "ttf-parser",
"url", "url",
"usvg", "usvg 0.46.0",
"uuid", "uuid",
"waker-fn", "waker-fn",
"windows 0.62.2", "windows 0.62.2",
@@ -2829,6 +3023,7 @@ checksum = "05be23908e707966824f8c51a609904b7f30739e8d915e120a53c1b995ba964c"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
"encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2",
@@ -2842,6 +3037,7 @@ dependencies = [
"js-sys", "js-sys",
"log", "log",
"mime", "mime",
"mime_guess",
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
@@ -3081,6 +3277,16 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "granit-parser"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e20f99e46474f56bd905c56e817ebddcf377a611f94c53ac4649e4d3fa3c0cd0"
dependencies = [
"arraydeque",
"smallvec",
]
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.4.20" version = "0.4.20"
@@ -3547,6 +3753,22 @@ dependencies = [
"icu_properties", "icu_properties",
] ]
[[package]]
name = "ignore"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f"
dependencies = [
"crossbeam-deque",
"globset",
"log",
"memchr",
"regex-automata",
"same-file",
"walkdir",
"winapi-util",
]
[[package]] [[package]]
name = "image" name = "image"
version = "0.25.10" version = "0.25.10"
@@ -3557,7 +3779,7 @@ dependencies = [
"byteorder-lite", "byteorder-lite",
"color_quant", "color_quant",
"exr", "exr",
"gif", "gif 0.14.2",
"image-webp", "image-webp",
"moxcms", "moxcms",
"num-traits", "num-traits",
@@ -3566,8 +3788,8 @@ dependencies = [
"ravif", "ravif",
"rayon", "rayon",
"tiff", "tiff",
"zune-core", "zune-core 0.5.3",
"zune-jpeg", "zune-jpeg 0.5.15",
] ]
[[package]] [[package]]
@@ -3580,6 +3802,12 @@ dependencies = [
"quick-error", "quick-error",
] ]
[[package]]
name = "imagesize"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
[[package]] [[package]]
name = "imagesize" name = "imagesize"
version = "0.14.0" version = "0.14.0"
@@ -3604,6 +3832,26 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "inotify"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cc00ea907cab49550b7da656f80ebb97be1b997d931fbcd28d39734e17ce592"
dependencies = [
"bitflags 2.13.2",
"inotify-sys",
"libc",
]
[[package]]
name = "inotify-sys"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "inout" name = "inout"
version = "0.1.4" version = "0.1.4"
@@ -3680,6 +3928,15 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.13.0" version = "0.13.0"
@@ -3800,6 +4057,37 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kqueue"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d763e5b24120b4ddf50de6c92308156765aabfbbccebf401da7cff2d70a41ea"
dependencies = [
"kqueue-sys",
"libc",
]
[[package]]
name = "kqueue-sys"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087"
dependencies = [
"bitflags 2.13.2",
"libc",
]
[[package]]
name = "kurbo"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
dependencies = [
"arrayvec",
"euclid",
"smallvec",
]
[[package]] [[package]]
name = "kurbo" name = "kurbo"
version = "0.13.1" version = "0.13.1"
@@ -4195,6 +4483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee"
dependencies = [ dependencies = [
"libc", "libc",
"log",
"wasi", "wasi",
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
@@ -4294,6 +4583,7 @@ dependencies = [
"chat", "chat",
"chat_ui", "chat_ui",
"common", "common",
"gpui-kit",
"gpui-pre", "gpui-pre",
"log", "log",
"nip29", "nip29",
@@ -4303,7 +4593,6 @@ dependencies = [
"settings", "settings",
"smallvec", "smallvec",
"state", "state",
"theme",
"ui", "ui",
] ]
@@ -4313,6 +4602,7 @@ version = "2.0.0-alpha"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"flume 0.11.1", "flume 0.11.1",
"gpui-kit",
"gpui-pre", "gpui-pre",
"log", "log",
"nostr-sdk", "nostr-sdk",
@@ -4321,7 +4611,6 @@ dependencies = [
"smallvec", "smallvec",
"smol", "smol",
"state", "state",
"theme",
"ui", "ui",
] ]
@@ -4334,6 +4623,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "nohash-hasher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]] [[package]]
name = "nom" name = "nom"
version = "8.0.0" version = "8.0.0"
@@ -4349,6 +4644,15 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "normpath"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b11ce00d2594068e8a27c9146fdc5cf9f3ac38eb42c7cd34d05fea618873ac9f"
dependencies = [
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "nostr" name = "nostr"
version = "0.45.5" version = "0.45.5"
@@ -4479,6 +4783,24 @@ dependencies = [
"universal-time 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "universal-time 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "notify"
version = "8.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
dependencies = [
"bitflags 2.13.2",
"fsevent-sys",
"inotify",
"kqueue",
"libc",
"log",
"mio",
"notify-types",
"walkdir",
"windows-sys 0.60.2",
]
[[package]] [[package]]
name = "notify-rust" name = "notify-rust"
version = "4.18.2" version = "4.18.2"
@@ -4493,6 +4815,15 @@ dependencies = [
"zbus", "zbus",
] ]
[[package]]
name = "notify-types"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
dependencies = [
"bitflags 2.13.2",
]
[[package]] [[package]]
name = "ntapi" name = "ntapi"
version = "0.4.3" version = "0.4.3"
@@ -5947,19 +6278,36 @@ dependencies = [
[[package]] [[package]]
name = "resvg" name = "resvg"
version = "0.46.0" version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43"
dependencies = [ dependencies = [
"gif", "gif 0.13.3",
"image-webp", "image-webp",
"log", "log",
"pico-args", "pico-args",
"rgb", "rgb",
"svgtypes", "svgtypes 0.15.3",
"tiny-skia", "tiny-skia",
"usvg", "usvg 0.45.1",
"zune-jpeg", "zune-jpeg 0.4.21",
]
[[package]]
name = "resvg"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419"
dependencies = [
"gif 0.14.2",
"image-webp",
"log",
"pico-args",
"rgb",
"svgtypes 0.16.1",
"tiny-skia",
"usvg 0.46.0",
"zune-jpeg 0.5.15",
] ]
[[package]] [[package]]
@@ -6030,6 +6378,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rust-embed-utils", "rust-embed-utils",
"shellexpand",
"syn 2.0.119", "syn 2.0.119",
"walkdir", "walkdir",
] ]
@@ -6045,6 +6394,54 @@ dependencies = [
"walkdir", "walkdir",
] ]
[[package]]
name = "rust-i18n"
version = "4.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8502e73e6461c738659455c5d2cf95bc132760a6f203c717e66ee72b78906d7f"
dependencies = [
"globwalk",
"regex",
"rust-i18n-macro",
"rust-i18n-support",
"smallvec",
]
[[package]]
name = "rust-i18n-macro"
version = "4.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54a28dd9d46108d56b9f0c85b220908844233e561c03087890325104f90f2c6"
dependencies = [
"glob",
"proc-macro2",
"quote",
"rust-i18n-support",
"serde",
"serde_json",
"syn 2.0.119",
]
[[package]]
name = "rust-i18n-support"
version = "4.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e806c2a9701cbd6043992cd935c30cb1fdb128fa15e7b506f3b46f97045737c0"
dependencies = [
"arc-swap",
"base62",
"foldhash 0.2.0",
"globwalk",
"itertools 0.11.0",
"normpath",
"serde",
"serde-saphyr",
"serde_json",
"siphasher",
"toml 0.8.23",
"triomphe",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.28" version = "0.1.28"
@@ -6377,6 +6774,23 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-saphyr"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8050abb251097357e24aff63ba2c52a6309ecb7d23a5474023df960a02694d8"
dependencies = [
"annotate-snippets",
"base64 0.23.1",
"encoding_rs_io",
"granit-parser",
"nohash-hasher",
"num-traits",
"serde_core",
"smallvec",
"zmij",
]
[[package]] [[package]]
name = "serde_bytes" name = "serde_bytes"
version = "0.11.19" version = "0.11.19"
@@ -6480,6 +6894,7 @@ version = "2.0.0-alpha"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"common", "common",
"gpui-kit",
"gpui-pre", "gpui-pre",
"log", "log",
"nostr-sdk", "nostr-sdk",
@@ -6488,7 +6903,6 @@ dependencies = [
"serde_json", "serde_json",
"smallvec", "smallvec",
"smol", "smol",
"theme",
] ]
[[package]] [[package]]
@@ -6539,6 +6953,15 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shellexpand"
version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8"
dependencies = [
"dirs",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "2.0.1" version = "2.0.1"
@@ -6586,6 +7009,16 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "similar"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
dependencies = [
"bstr",
"unicode-segmentation",
]
[[package]] [[package]]
name = "simplecss" name = "simplecss"
version = "0.2.2" version = "0.2.2"
@@ -6819,13 +7252,23 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb"
[[package]]
name = "svgtypes"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc"
dependencies = [
"kurbo 0.11.3",
"siphasher",
]
[[package]] [[package]]
name = "svgtypes" name = "svgtypes"
version = "0.16.1" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d"
dependencies = [ dependencies = [
"kurbo", "kurbo 0.13.1",
"siphasher", "siphasher",
] ]
@@ -7003,20 +7446,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "theme"
version = "2.0.0-alpha"
dependencies = [
"anyhow",
"gpui-base",
"gpui-pre",
"log",
"schemars",
"serde",
"serde_json",
"tempfile",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"
@@ -7077,7 +7506,7 @@ dependencies = [
"half", "half",
"quick-error", "quick-error",
"weezl", "weezl",
"zune-jpeg", "zune-jpeg 0.5.15",
] ]
[[package]] [[package]]
@@ -7466,6 +7895,17 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "triomphe"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d15f4f5898bcfc5d8a8e45ed30b2f108d710c1a2e5489dbf80e078f707181bc"
dependencies = [
"arc-swap",
"serde",
"stable_deref_trait",
]
[[package]] [[package]]
name = "try-lock" name = "try-lock"
version = "0.2.5" version = "0.2.5"
@@ -7522,12 +7962,12 @@ name = "ui"
version = "2.0.0-alpha" version = "2.0.0-alpha"
dependencies = [ dependencies = [
"gpui-base", "gpui-base",
"gpui-kit",
"gpui-pre", "gpui-pre",
"instant",
"log", "log",
"smallvec", "smallvec",
"theme",
"uuid", "uuid",
"web-time",
] ]
[[package]] [[package]]
@@ -7683,6 +8123,33 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "usvg"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef"
dependencies = [
"base64 0.22.1",
"data-url",
"flate2",
"fontdb",
"imagesize 0.13.0",
"kurbo 0.11.3",
"log",
"pico-args",
"roxmltree 0.20.0",
"rustybuzz",
"simplecss",
"siphasher",
"strict-num",
"svgtypes 0.15.3",
"tiny-skia-path",
"unicode-bidi",
"unicode-script",
"unicode-vo",
"xmlwriter",
]
[[package]] [[package]]
name = "usvg" name = "usvg"
version = "0.46.0" version = "0.46.0"
@@ -7693,8 +8160,8 @@ dependencies = [
"data-url", "data-url",
"flate2", "flate2",
"fontdb", "fontdb",
"imagesize", "imagesize 0.14.0",
"kurbo", "kurbo 0.13.1",
"log", "log",
"pico-args", "pico-args",
"roxmltree 0.21.1", "roxmltree 0.21.1",
@@ -7702,7 +8169,7 @@ dependencies = [
"simplecss", "simplecss",
"siphasher", "siphasher",
"strict-num", "strict-num",
"svgtypes", "svgtypes 0.16.1",
"tiny-skia-path", "tiny-skia-path",
"unicode-bidi", "unicode-bidi",
"unicode-script", "unicode-script",
@@ -8651,6 +9118,15 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.61.2" version = "0.61.2"
@@ -8926,6 +9402,7 @@ dependencies = [
"chat_ui", "chat_ui",
"common", "common",
"gpui-base", "gpui-base",
"gpui-kit",
"gpui-pre", "gpui-pre",
"instant", "instant",
"log", "log",
@@ -8939,7 +9416,6 @@ dependencies = [
"settings", "settings",
"smallvec", "smallvec",
"state", "state",
"theme",
"ui", "ui",
] ]
@@ -9381,6 +9857,12 @@ version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
[[package]]
name = "zune-core"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]] [[package]]
name = "zune-core" name = "zune-core"
version = "0.5.3" version = "0.5.3"
@@ -9396,13 +9878,22 @@ dependencies = [
"simd-adler32", "simd-adler32",
] ]
[[package]]
name = "zune-jpeg"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
dependencies = [
"zune-core 0.4.12",
]
[[package]] [[package]]
name = "zune-jpeg" name = "zune-jpeg"
version = "0.5.15" version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [ dependencies = [
"zune-core", "zune-core 0.5.3",
] ]
[[package]] [[package]]
+1
View File
@@ -19,6 +19,7 @@ gpui_util = { package = "gpui-pre-util", version = "0.3.8" }
reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.8" } reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.8" }
gpui_tokio = { path = "crates/gpui_tokio" } gpui_tokio = { path = "crates/gpui_tokio" }
gpui-base = "0.7.1" gpui-base = "0.7.1"
gpui-kit = "0.7.3"
# Nostr # Nostr
nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" } nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" }
-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1086 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.25 13L12 16.25L8.75 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 7.75V15.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 571 B

-16
View File
@@ -1,16 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path
d="M10 5.75L3.75 12L10 18.25"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M4.5 12H20.25"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>

Before

Width:  |  Height:  |  Size: 418 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M14 5.75L20.25 12L14 18.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M19.5 12H3.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 320 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <path d="M21.25 12C21.25 17.1086 17.1086 21.25 12 21.25C9.44568 21.25 7.13318 20.2147 5.45926 18.5407C3.78534 16.8668 2.75 14.5543 2.75 12C2.75 6.89137 6.89137 2.75 12 2.75C14.5543 2.75 16.8668 3.78534 18.5407 5.45926C20.2147 7.13318 21.25 9.44568 21.25 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="square"/><path d="M18 6L6 18" stroke="currentColor" stroke-width="1.5" stroke-linecap="square"/> </svg>

Before

Width:  |  Height:  |  Size: 493 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <path d="M9.25 9.75C7.317 9.75 5.75 8.183 5.75 6.25C5.75 4.317 7.317 2.75 9.25 2.75C11.183 2.75 12.75 4.317 12.75 6.25C12.75 8.183 11.183 9.75 9.25 9.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M15 2.75C16.933 2.75 18.5 4.317 18.5 6.25C18.5 8.183 16.933 9.75 15 9.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.24806 12.75C5.46395 12.75 2.27489 15.1517 1.30259 18.2496C0.971822 19.3035 1.89349 20.2187 2.99806 20.2188H15.4981C16.6026 20.2188 17.5243 19.3035 17.1935 18.2496C16.2212 15.1517 13.0322 12.75 9.24806 12.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M20.75 20.25H21.25C22.3546 20.25 23.2748 19.3347 22.9405 18.2819C22.2598 16.1389 20.512 14.31 18.25 13.5088" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>

Before

Width:  |  Height:  |  Size: 1014 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M17.25 14C17.25 18.0041 14.0041 21.25 10 21.25C5.99594 21.25 2.75 18.0041 2.75 14C2.75 9.99594 5.99594 6.75 10 6.75C14.0041 6.75 17.25 9.99594 17.25 14Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.5 8.5L17.5 6.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M16.75 1.75V3.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M20.75 7.25H22.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 4L21.25 2.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 800 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M5.75 9.5L12 15.75L18.25 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 209 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M9.5 18.25L15.75 12L9.5 5.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 209 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M5.75 14.5L12 8.25L18.25 14.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 210 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="9.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M7.75 12.9231L10.5625 15.75L15.25 8.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 341 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M6.75 13.0625L9.9 16.25L17.25 7.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 215 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M9.8007 10.25C8.74816 10.25 8.16683 11.4713 8.83056 12.2882L11.0301 14.9953C11.5303 15.611 12.4701 15.611 12.9704 14.9953L15.1699 12.2882C15.8336 11.4713 15.2523 10.25 14.1997 10.25H9.8007Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 302 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12ZM9.53033 8.46967C9.23744 8.17678 8.76256 8.17678 8.46967 8.46967C8.17678 8.76256 8.17678 9.23744 8.46967 9.53033L10.9393 12L8.46967 14.4697C8.17678 14.7626 8.17678 15.2374 8.46967 15.5303C8.76256 15.8232 9.23744 15.8232 9.53033 15.5303L12 13.0607L14.4697 15.5303C14.7626 15.8232 15.2374 15.8232 15.5303 15.5303C15.8232 15.2374 15.8232 14.7626 15.5303 14.4697L13.0607 12L15.5303 9.53033C15.8232 9.23744 15.8232 8.76256 15.5303 8.46967C15.2374 8.17678 14.7626 8.17678 14.4697 8.46967L12 10.9393L9.53033 8.46967Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 774 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M15 9L9 15M15 15L9 9M21.25 12C21.25 17.1086 17.1086 21.25 12 21.25C6.89137 21.25 2.75 17.1086 2.75 12C2.75 6.89137 6.89137 2.75 12 2.75C17.1086 2.75 21.25 6.89137 21.25 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 329 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M6.25 6.25L17.75 17.75M17.75 6.25L6.25 17.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 201 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="9.25" stroke="currentColor" stroke-width="1.5"/><ellipse cx="12" cy="12" rx="3.5" ry="9.25" stroke="currentColor" stroke-width="1.5"/><path d="M3.5 9.25H20.5" stroke="currentColor" stroke-width="1.5"/><path d="M3.5 14.75H20.5" stroke="currentColor" stroke-width="1.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 377 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M8.75 8.75V4C8.75 3.30964 9.30964 2.75 10 2.75H20C20.6904 2.75 21.25 3.30964 21.25 4V14C21.25 14.6904 20.6904 15.25 20 15.25H15.25M14 8.75H4C3.30964 8.75 2.75 9.30964 2.75 10V20C2.75 20.6904 3.30964 21.25 4 21.25H14C14.6904 21.25 15.25 20.6904 15.25 20V10C15.25 9.30964 14.6904 8.75 14 8.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 472 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M14.25 10.75C14.25 9.64543 15.1454 8.75 16.25 8.75H20.25C21.3546 8.75 22.25 9.64543 22.25 10.75V19.25C22.25 20.3546 21.3546 21.25 20.25 21.25H16.25C15.1454 21.25 14.25 20.3546 14.25 19.25V10.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M17.25 18.25H19.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M20.25 8.75V5.75C20.25 4.64543 19.3546 3.75 18.25 3.75H5.75C4.64543 3.75 3.75 4.64543 3.75 5.75V14.75C3.75 15.8546 2.85457 16.75 1.75 16.75V18.25C1.75 19.3546 2.64543 20.25 3.75 20.25H14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M3.75 16.75H14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 898 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M2.75 21.25L21.25 21.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M4.75 21.25V4.75C4.75 3.64543 5.64543 2.75 6.75 2.75H17.25C18.3546 2.75 19.25 3.64543 19.25 4.75V21.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M7.75 12.25H8.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 522 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <path d="M12.75 11.2499V8.24994L17.3358 3.66416C18.1168 2.88311 19.3832 2.88311 20.1642 3.66416L20.3358 3.83573C21.1168 4.61678 21.1168 5.88311 20.3358 6.66416L15.75 11.2499H12.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M11.25 3.75H6.95C5.8299 3.75 5.26984 3.75 4.84202 3.96799C4.46569 4.15973 4.15973 4.46569 3.96799 4.84202C3.75 5.26984 3.75 5.8299 3.75 6.95V17.05C3.75 18.1701 3.75 18.7302 3.96799 19.158C4.15973 19.5343 4.46569 19.8403 4.84202 20.032C5.26984 20.25 5.8299 20.25 6.95 20.25H17.05C18.1701 20.25 18.7302 20.25 19.158 20.032C19.5343 19.8403 19.8403 19.5343 20.032 19.158C20.25 18.7302 20.25 18.1701 20.25 17.05V12.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>

Before

Width:  |  Height:  |  Size: 857 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M3.75 10.25C2.7835 10.25 2 11.0335 2 12C2 12.9665 2.7835 13.75 3.75 13.75C4.7165 13.75 5.5 12.9665 5.5 12C5.5 11.0335 4.7165 10.25 3.75 10.25Z" fill="currentColor"/><path d="M12 10.25C11.0335 10.25 10.25 11.0335 10.25 12C10.25 12.9665 11.0335 13.75 12 13.75C12.9665 13.75 13.75 12.9665 13.75 12C13.75 11.0335 12.9665 10.25 12 10.25Z" fill="currentColor"/><path d="M20.25 10.25C19.2835 10.25 18.5 11.0335 18.5 12C18.5 12.9665 19.2835 13.75 20.25 13.75C21.2165 13.75 22 12.9665 22 12C22 11.0335 21.2165 10.25 20.25 10.25Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 632 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M21.25 12C21.25 17.1086 17.1086 21.25 12 21.25C6.89137 21.25 2.75 17.1086 2.75 12C2.75 6.89137 6.89137 2.75 12 2.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M19 1.75V8.25M15.75 5H22.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M10.75 9.9C10.75 11.0046 10.0784 11.75 9.25 11.75C8.42157 11.75 7.75 11.0046 7.75 9.9C7.75 8.79543 8.42157 8 9.25 8C10.0784 8 10.75 8.79543 10.75 9.9Z" fill="currentColor"/><path d="M16.25 9.9C16.25 11.0046 15.5784 11.75 14.75 11.75C13.9216 11.75 13.25 11.0046 13.25 9.9C13.25 8.79543 13.9216 8 14.75 8C15.5784 8 16.25 8.79543 16.25 9.9Z" fill="currentColor"/><path d="M16.1123 14.8493C16.1942 14.7105 16.2249 14.545 16.192 14.3857C16.1592 14.2263 16.0665 14.0867 15.933 13.9968C15.7996 13.9069 15.6354 13.8733 15.4754 13.9028C15.3154 13.9321 15.1736 14.0226 15.0757 14.1507C15.0008 14.2469 14.9237 14.3367 14.8415 14.4241C14.1096 15.2083 13.061 15.628 12.0035 15.625C10.946 15.6265 9.8972 15.2055 9.16254 14.4222C9.08002 14.3348 9.00261 14.2451 8.92738 14.1491C8.8291 14.0214 8.68699 13.9313 8.52686 13.9024C8.36679 13.8735 8.20268 13.9076 8.06954 13.9979C7.9364 14.0882 7.84406 14.2281 7.81174 14.3875C7.77938 14.547 7.81054 14.7123 7.89293 14.8509C7.97731 14.99 8.06686 15.1223 8.16553 15.2526C9.04297 16.4311 10.5292 17.1343 12.0024 17.125C13.4754 17.1367 14.965 16.4342 15.8405 15.2521C15.939 15.1215 16.0282 14.9888 16.1123 14.8493Z" fill="currentColor"/><path d="M21.25 12C21.25 17.1086 17.1086 21.25 12 21.25C6.89137 21.25 2.75 17.1086 2.75 12C2.75 6.89137 6.89137 2.75 12 2.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M3.75 13.0199C8.54029 18.1132 15.4597 18.1132 20.25 13.0199" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M3.75 7.62257C6.14516 5.07587 9.0726 3.80251 12 3.80249C14.9274 3.80247 17.8549 5.07576 20.25 7.62238" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 17V20.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.25 16.5L6.75 18.9821" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.5 16.5L17.25 18.9821" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 800 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M12.7507 3.75C12.7507 3.33579 12.4149 3 12.0007 3C11.5865 3 11.2507 3.33579 11.2507 3.75V6.25C11.2507 6.66421 11.5865 7 12.0007 7C12.4149 7 12.7507 6.66421 12.7507 6.25V3.75Z" fill="currentColor"/><path d="M7.26594 5.19799C6.99969 4.88068 6.52662 4.8393 6.20932 5.10555C5.89201 5.3718 5.85062 5.84487 6.11687 6.16217L7.72384 8.07728C7.99009 8.39459 8.46316 8.43598 8.78047 8.16973C9.09777 7.90347 9.13916 7.43041 8.87291 7.1131L7.26594 5.19799Z" fill="currentColor"/><path d="M17.8726 6.16227C18.1389 5.84496 18.0975 5.37189 17.7802 5.10564C17.4629 4.83939 16.9898 4.88078 16.7235 5.19809L15.1166 7.1132C14.8503 7.4305 14.8917 7.90357 15.209 8.16982C15.5263 8.43607 15.9994 8.39468 16.2656 8.07738L17.8726 6.16227Z" fill="currentColor"/><path d="M5.22073 9C4.33013 9 3.52687 9.5355 3.18434 10.3576L2.78846 11.3077C2.61378 11.7269 2.20416 12 1.75 12C1.33579 12 1 12.3358 1 12.75V19.25C1 19.6642 1.33579 20 1.75 20H7.38937C9.39779 20 11.0763 18.4715 11.2637 16.4719L11.4255 14.746C11.6391 12.468 9.84697 10.5 7.559 10.5C7.46053 10.5 7.36858 10.4508 7.31396 10.3689L7.0563 9.98237C6.64715 9.36864 5.95834 9 5.22073 9Z" fill="currentColor"/><path d="M18.722 9C17.9844 9 17.2956 9.36864 16.8865 9.98237L16.6288 10.3689C16.5742 10.4508 16.4822 10.5 16.3838 10.5C14.0958 10.5 12.3037 12.468 12.5172 14.746L12.679 16.4719C12.8665 18.4715 14.545 20 16.5534 20H22.1928C22.607 20 22.9428 19.6642 22.9428 19.25V12.75C22.9428 12.3358 22.607 12 22.1928 12C21.7386 12 21.329 11.7269 21.1543 11.3077L20.7584 10.3576C20.4159 9.5355 19.6126 9 18.722 9Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M1.75 19.25H7.38937C9.0107 19.25 10.3657 18.0161 10.517 16.4019L10.6788 14.676C10.8511 12.8379 9.40511 11.25 7.559 11.25C7.20977 11.25 6.88364 11.0755 6.68992 10.7849L6.43226 10.3984C6.16221 9.99331 5.70757 9.75 5.22073 9.75C4.6329 9.75 4.10273 10.1034 3.87664 10.6461L3.48077 11.5962C3.18964 12.2949 2.50694 12.75 1.75 12.75M22.1928 19.25H16.5534C14.9321 19.25 13.5771 18.0161 13.4258 16.4019L13.264 14.676C13.0916 12.8379 14.5377 11.25 16.3838 11.25C16.733 11.25 17.0591 11.0755 17.2528 10.7849L17.5105 10.3984C17.7806 9.99331 18.2352 9.75 18.722 9.75C19.3099 9.75 19.84 10.1034 20.0661 10.6461L20.462 11.5962C20.7531 12.2949 21.4358 12.75 22.1928 12.75M12.0007 3.75V6.25M6.69141 5.68008L8.29838 7.59519M17.2981 5.68018L15.6911 7.59529" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 918 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M2.75 5.75V17.25C2.75 18.3546 3.64543 19.25 4.75 19.25H19.25C20.3546 19.25 21.25 18.3546 21.25 17.25V8.75C21.25 7.64543 20.3546 6.75 19.25 6.75H13.0704C12.4017 6.75 11.7772 6.4158 11.4063 5.8594L10.5937 4.6406C10.2228 4.0842 9.59834 3.75 8.92963 3.75H4.75C3.64543 3.75 2.75 4.64543 2.75 5.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 473 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="8.75" r="3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="4" cy="9.80005" r="2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="20" cy="9.80005" r="2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M7.25 16.625V16.5C7.25 13.8766 9.37665 11.75 12 11.75C14.6234 11.75 16.75 13.8766 16.75 16.5V16.625C16.75 17.5225 16.0225 18.25 15.125 18.25H8.875C7.97754 18.25 7.25 17.5225 7.25 16.625Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M4.25 17.2602H2.75C1.64543 17.2602 0.706551 16.3538 0.919944 15.2701C1.25877 13.5493 2.15049 12.3257 4 11.8301" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M19.75 17.2601H21.25C22.3546 17.2601 23.2935 16.3538 23.08 15.27C22.7412 13.5493 21.8495 12.3257 20 11.8301" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M8.75 3.75L6.75 20.25M17.25 3.75L15.25 20.25M3.75 7.75H20.25M20.25 16.25H3.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 258 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M12 7.75V12L15.5 15.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.75 4.75V8.75H6.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M3.25 15.0833C4.52169 18.676 7.95303 21.25 11.9864 21.25C17.1026 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1026 2.75 11.9864 2.75C8.14808 2.75 4.85497 5.08106 3.44947 8.40278" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 600 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.00001 13.7453L3.00004 5.75C3.00004 4.23122 4.23126 3.00001 5.75004 3L18.25 3C19.7688 3 21 4.23122 21 5.75V13.7466C21 13.7477 21 13.7489 21 13.75L21 18.25C21 19.7688 19.7688 21 18.25 21H5.75C4.32614 21 3.15502 19.9179 3.0142 18.5312C3.00481 18.4387 3 18.3449 3 18.25M5.75004 4.5L18.25 4.5C18.9403 4.5 19.5 5.05964 19.5 5.75V13L15.9298 13C15.5695 13 15.2601 13.2562 15.1929 13.6102C14.9078 15.1135 13.5858 16.25 12 16.25C10.4142 16.25 9.09221 15.1135 8.80706 13.6102C8.73991 13.2562 8.43051 13 8.0702 13H4.50002L4.50004 5.75C4.50004 5.05965 5.05968 4.50001 5.75004 4.5Z" fill="currentColor"/><path d="M3 18.25V13.75C3 13.7484 3 13.7469 3.00001 13.7453" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 805 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M3.75 12.75H8.0702C8.42126 14.6006 10.0472 16 12 16C13.9528 16 15.5787 14.6006 15.9298 12.75L20.25 12.75M18.25 20.25H5.75001C4.64543 20.25 3.75 19.3546 3.75001 18.25L3.75005 5.75C3.75005 4.64543 4.64548 3.75001 5.75005 3.75L18.25 3.75C19.3546 3.75 20.25 4.64543 20.25 5.75V18.25C20.25 19.3546 19.3546 20.25 18.25 20.25Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 501 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M10.75 11H12L12 16.25M21.25 12C21.25 17.1086 17.1086 21.25 12 21.25C6.89137 21.25 2.75 17.1086 2.75 12C2.75 6.89137 6.89137 2.75 12 2.75C17.1086 2.75 21.25 6.89137 21.25 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 7.375C11.6548 7.375 11.375 7.65482 11.375 8C11.375 8.34518 11.6548 8.625 12 8.625C12.3452 8.625 12.625 8.34518 12.625 8C12.625 7.65482 12.3452 7.375 12 7.375Z" fill="currentColor" stroke="currentColor" stroke-width="0.25"/>
</svg>

Before

Width:  |  Height:  |  Size: 590 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M11.75 6.75H19.25C20.3546 6.75 21.25 7.64543 21.25 8.75V15.25C21.25 16.3546 20.3546 17.25 19.25 17.25H11.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M5.75 6.75H4.75C3.64543 6.75 2.75 7.64543 2.75 8.75V15.25C2.75 16.3546 3.64543 17.25 4.75 17.25H5.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.75 3.75V20.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 604 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M4.75 10.9853V4.75C4.75 3.64543 5.64543 2.75 6.75 2.75H17.25C18.3546 2.75 19.25 3.64543 19.25 4.75V10.9853M9.75 7.75H14.25M12.617 13.5499L19.9415 11.1744C20.5875 10.9649 21.25 11.4465 21.25 12.1256V18.25C21.25 19.3546 20.3546 20.25 19.25 20.25H4.75C3.64543 20.25 2.75 19.3546 2.75 18.25V12.1256C2.75 11.4465 3.41249 10.9649 4.0585 11.1744L11.383 13.5499C11.784 13.68 12.216 13.68 12.617 13.5499Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 576 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M9.75027 5.52371L10.7168 4.55722C13.1264 2.14759 17.0332 2.14759 19.4428 4.55722C21.8524 6.96684 21.8524 10.8736 19.4428 13.2832L18.4742 14.2519M5.52886 9.74513L4.55722 10.7168C2.14759 13.1264 2.1476 17.0332 4.55722 19.4428C6.96684 21.8524 10.8736 21.8524 13.2832 19.4428L14.2478 18.4782M9.5 14.5L14.5 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 462 B

-10
View File
@@ -1,10 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader">
<path d="M12 2v4"/>
<path d="m16.2 7.8 2.9-2.9"/>
<path d="M18 12h4"/>
<path d="m16.2 16.2 2.9 2.9"/>
<path d="M12 18v4"/>
<path d="m4.9 19.1 2.9-2.9"/>
<path d="M2 12h4"/>
<path d="m4.9 4.9 2.9 2.9"/>
</svg>

Before

Width:  |  Height:  |  Size: 435 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M12 8.75003C11.1716 8.75003 10.5 9.4216 10.5 10.25C10.5 11.0785 11.1716 11.75 12 11.75C12.8284 11.75 13.5 11.0785 13.5 10.25C13.5 9.4216 12.8284 8.75003 12 8.75003ZM12 8.75003V14.75M20.25 11.9124V6.94155C20.25 6.08069 19.6991 5.31641 18.8825 5.04418L12.6325 2.96085C12.2219 2.824 11.7781 2.824 11.3675 2.96085L5.11754 5.04418C4.30086 5.31641 3.75 6.08069 3.75 6.94155V11.9124C3.75 16.8848 8 19.25 12 21.4079C16 19.25 20.25 16.8848 20.25 11.9124Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 626 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <path d="M14.75 3.75L18.25 3.75C19.3546 3.75 20.25 4.64543 20.25 5.75V18.25C20.25 19.3546 19.3546 20.25 18.25 20.25H14.75M15 12H3.75M15 12L11.5 15.5M15 12L11.5 8.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>

Before

Width:  |  Height:  |  Size: 335 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <path d="M20.25 12L9 12M20.25 12L15.75 16.5M20.25 12L15.75 7.5M11.25 20.25H5.75C4.64543 20.25 3.75 19.3546 3.75 18.25L3.75 5.75C3.75 4.64543 4.64543 3.75 5.75 3.75L11.25 3.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>

Before

Width:  |  Height:  |  Size: 346 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M21.75 12C21.75 6.84375 17.9583 3.75 12 3.75C6.04167 3.75 2.25 6.84375 2.25 12C2.25 13.3368 3.17054 15.6055 3.3145 15.9522C3.32742 15.9833 3.34021 16.0117 3.3518 16.0433C3.45089 16.3136 3.85722 17.7527 2.25 19.8828C4.41667 20.914 6.71766 19.2188 6.71766 19.2188C8.30963 20.0597 10.2038 20.25 12 20.25C17.9583 20.25 21.75 17.1562 21.75 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 520 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M21.2481 11.8112C20.1889 12.56 18.8958 13 17.5 13C13.9101 13 11 10.0899 11 6.5C11 5.10416 11.44 3.81108 12.1888 2.75189C12.126 2.75063 12.0631 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 11.9369 21.2494 11.874 21.2481 11.8112Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 489 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.75 4C3.23122 4 2 5.23122 2 6.75V17.25C2 18.7688 3.23122 20 4.75 20H19.25C20.7688 20 22 18.7688 22 17.25V6.75C22 5.23122 20.7688 4 19.25 4H4.75ZM3.5 6.75C3.5 6.05964 4.05964 5.5 4.75 5.5H10.5V18.5H4.75C4.05964 18.5 3.5 17.9404 3.5 17.25V6.75Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M7 9.5C6.44772 9.5 6 9.05228 6 8.5C6 7.94772 6.44772 7.5 7 7.5C7.55228 7.5 8 7.94772 8 8.5C8 9.05228 7.55228 9.5 7 9.5ZM7 13C6.44772 13 6 12.5523 6 12C6 11.4477 6.44772 11 7 11C7.55228 11 8 11.4477 8 12C8 12.5523 7.55228 13 7 13ZM7 16.5C6.44772 16.5 6 16.0523 6 15.5C6 14.9477 6.44772 14.5 7 14.5C7.55228 14.5 8 14.9477 8 15.5C8 16.0523 7.55228 16.5 7 16.5Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 826 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M11.25 4.75H4.75C3.64543 4.75 2.75 5.64543 2.75 6.75V17.25C2.75 18.3546 3.64543 19.25 4.75 19.25H11.25M11.25 4.75H19.25C20.3546 4.75 21.25 5.64543 21.25 6.75V17.25C21.25 18.3546 20.3546 19.25 19.25 19.25H11.25M11.25 4.75V19.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" stroke-linejoin="round"/><path d="M6.25 8.5C6.25 8.91421 6.58579 9.25 7 9.25C7.41421 9.25 7.75 8.91421 7.75 8.5C7.75 8.08579 7.41421 7.75 7 7.75C6.58579 7.75 6.25 8.08579 6.25 8.5ZM6.25 12C6.25 12.4142 6.58579 12.75 7 12.75C7.41421 12.75 7.75 12.4142 7.75 12C7.75 11.5858 7.41421 11.25 7 11.25C6.58579 11.25 6.25 11.5858 6.25 12ZM6.25 15.5C6.25 15.9142 6.58579 16.25 7 16.25C7.41421 16.25 7.75 15.9142 7.75 15.5C7.75 15.0858 7.41421 14.75 7 14.75C6.58579 14.75 6.25 15.0858 6.25 15.5Z" fill="currentColor" stroke="currentColor" stroke-width="0.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 931 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 5.5V18.5H4.75C4.05964 18.5 3.5 17.9404 3.5 17.25V6.75C3.5 6.05964 4.05964 5.5 4.75 5.5H15ZM22 6.75C22 5.23122 20.7688 4 19.25 4H4.75C3.23122 4 2 5.23122 2 6.75V17.25C2 18.7688 3.23122 20 4.75 20H19.25C20.7688 20 22 18.7688 22 17.25V6.75Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 394 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M15.75 5V19" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M2.75 6.75C2.75 5.64543 3.64543 4.75 4.75 4.75H19.25C20.3546 4.75 21.25 5.64543 21.25 6.75V17.25C21.25 18.3546 20.3546 19.25 19.25 19.25H4.75C3.64543 19.25 2.75 18.3546 2.75 17.25V6.75Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 431 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M2.66936 5.12886C2.12122 3.64104 3.6759 2.24953 5.09409 2.95862L20.0468 10.435C21.3366 11.0799 21.3366 12.9205 20.0468 13.5655L5.09409 21.0418C3.67589 21.7509 2.12122 20.3594 2.66936 18.8715L4.92467 12.75H9.25021C9.66442 12.75 10.0002 12.4142 10.0002 12C10.0002 11.5858 9.66442 11.25 9.25021 11.25H4.92452L2.66936 5.12886Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 435 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <path d="M21.75 12C21.75 6.84375 17.9583 3.75 12 3.75C6.04167 3.75 2.25 6.84375 2.25 12C2.25 13.3368 3.17054 15.6055 3.3145 15.9522C3.32742 15.9833 3.34021 16.0117 3.3518 16.0433C3.45089 16.3136 3.85722 17.7527 2.25 19.8828C4.41667 20.914 6.71766 19.2188 6.71766 19.2188C8.30963 20.0597 10.2038 20.25 12 20.25C17.9583 20.25 21.75 17.1562 21.75 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" stroke-linejoin="round"/><path d="M12 8.75V15.25M8.75 12H15.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> </svg>

Before

Width:  |  Height:  |  Size: 624 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M16.2426 12.0005H7.75736M12 16.2431V7.75781M21.25 12C21.25 17.1086 17.1086 21.25 12 21.25C6.89137 21.25 2.75 17.1086 2.75 12C2.75 6.89137 6.89137 2.75 12 2.75C17.1086 2.75 21.25 6.89137 21.25 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 352 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M12 6.75V12M12 12V17.25M12 12H6.75M12 12H17.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 203 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M17.75 2.75H6.25C5.14543 2.75 4.25 3.64543 4.25 4.75V19.25C4.25 20.3546 5.14543 21.25 6.25 21.25H17.75C18.8546 21.25 19.75 20.3546 19.75 19.25V4.75C19.75 3.64543 18.8546 2.75 17.75 2.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="12" cy="12.25" r="2.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M16 21C16 18.7909 14.2091 17 12 17C9.79086 17 8 18.7909 8 21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M9.75 6.25H14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 696 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M13 21C13.5523 21 14 20.5523 14 20C14 19.4477 13.5523 19 13 19C12.4477 19 12 19.4477 12 20C12 20.5523 12.4477 21 13 21Z" fill="currentColor"/><path d="M21 11C21 10.4477 20.5523 9.99999 20 9.99999C19.4477 9.99999 19 10.4477 19 11C19 11.5523 19.4477 12 20 12C20.5523 12 21 11.5523 21 11Z" fill="currentColor"/><path d="M19.9295 14.2679C20.4078 14.5441 20.5716 15.1557 20.2955 15.634C20.0193 16.1123 19.4078 16.2761 18.9295 16C18.4512 15.7238 18.2873 15.1123 18.5634 14.634C18.8396 14.1557 19.4512 13.9918 19.9295 14.2679Z" fill="currentColor"/><path d="M17.3676 19.2942C17.8459 19.0181 18.0098 18.4065 17.7336 17.9282C17.4575 17.4499 16.8459 17.286 16.3676 17.5621C15.8893 17.8383 15.7254 18.4499 16.0016 18.9282C16.2777 19.4065 16.8893 19.5703 17.3676 19.2942Z" fill="currentColor"/><path d="M18.9269 7.99998C18.4487 8.27612 17.8371 8.11225 17.5609 7.63396C17.2848 7.15566 17.4487 6.54407 17.9269 6.26793C18.4052 5.99179 19.0168 6.15566 19.293 6.63396C19.5691 7.11225 19.4052 7.72384 18.9269 7.99998Z" fill="currentColor"/><path d="M9.25 14.75V20.25H3.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.2493 4.41452C14.2521 3.98683 13.1537 3.75 12 3.75C7.44365 3.75 3.75 7.44365 3.75 12C3.75 15.498 5.92698 18.4875 9 19.6876" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="9.25" r="1.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M7.75 21.25L11.75 9.25H12.25L16.25 21.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.5 17.75H14.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M7.75693 12.7501C6.08102 10.7234 6.08103 7.77679 7.75693 5.75M16.2431 5.75C17.919 7.77679 17.919 10.7234 16.2431 12.7501" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M5.06494 2.7574C1.64285 6.40823 1.64502 12.1018 5.07145 15.75M18.9281 2.75C22.3572 6.40053 22.3573 12.0993 18.9285 15.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 899 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M1.84521 11.4494L9.99071 3.91478C10.471 3.47055 11.25 3.81116 11.25 4.46535V7.99994C11.25 8.27608 11.478 8.49949 11.7541 8.50388C19.8394 8.63247 22 11.9205 22 20.2499C20.5303 17.3105 19.7806 15.5711 11.7551 15.5021C11.4789 15.4997 11.25 15.7238 11.25 15.9999V19.5345C11.25 20.1887 10.471 20.5293 9.99071 20.0851L1.84521 12.5505C1.52425 12.2536 1.52425 11.7463 1.84521 11.4494Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 534 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M13 17.25C16.4518 17.25 19.25 14.4518 19.25 11V10.5C19.25 6.77208 16.2279 3.75 12.5 3.75C8.77208 3.75 5.75 6.77208 5.75 10.5V20" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2 16.5L5.75 20.25L9.5 16.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 435 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M7.25 4.75H4.75C3.64543 4.75 2.75 5.64543 2.75 6.75V9.25M16.75 4.75H19.25C20.3546 4.75 21.25 5.64543 21.25 6.75V9.25M21.25 14.75V17.25C21.25 18.3546 20.3546 19.25 19.25 19.25H16.75M7.25 19.25H4.75C3.64543 19.25 2.75 18.3546 2.75 17.25V14.75M7.75 9.75V14.25M16.25 9.75V14.25M12 9.75V12.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 468 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M20 20L16.1265 16.1265M16.1265 16.1265C17.4385 14.8145 18.25 13.002 18.25 11C18.25 6.99594 15.0041 3.75 11 3.75C6.99594 3.75 3.75 6.99594 3.75 11C3.75 15.0041 6.99594 18.25 11 18.25C13.002 18.25 14.8145 17.4385 16.1265 16.1265Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 408 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M7.878 5.21415L7.17474 5.05186C6.58003 4.91462 5.95657 5.09343 5.525 5.525C5.09343 5.95657 4.91462 6.58003 5.05186 7.17474L5.21415 7.878C5.40122 8.6886 5.06696 9.53036 4.37477 9.99182L3.51965 10.5619C3.03881 10.8825 2.75 11.4221 2.75 12C2.75 12.5779 3.03881 13.1175 3.51965 13.4381L4.37477 14.0082C5.06696 14.4696 5.40122 15.3114 5.21415 16.122L5.05186 16.8253C4.91462 17.42 5.09343 18.0434 5.525 18.475C5.95657 18.9066 6.58003 19.0854 7.17474 18.9481L7.878 18.7858C8.6886 18.5988 9.53036 18.933 9.99182 19.6252L10.5619 20.4804C10.8825 20.9612 11.4221 21.25 12 21.25C12.5779 21.25 13.1175 20.9612 13.4381 20.4804L14.0082 19.6252C14.4696 18.933 15.3114 18.5988 16.122 18.7858L16.8253 18.9481C17.42 19.0854 18.0434 18.9066 18.475 18.475C18.9066 18.0434 19.0854 17.42 18.9481 16.8253L18.7858 16.122C18.5988 15.3114 18.933 14.4696 19.6252 14.0082L20.4804 13.4381C20.9612 13.1175 21.25 12.5779 21.25 12C21.25 11.4221 20.9612 10.8825 20.4804 10.5619L19.6252 9.99182C18.933 9.53036 18.5988 8.6886 18.7858 7.878L18.9481 7.17473C19.0854 6.58003 18.9066 5.95657 18.475 5.525C18.0434 5.09343 17.42 4.91462 16.8253 5.05186L16.122 5.21415C15.3114 5.40122 14.4696 5.06696 14.0082 4.37477L13.4381 3.51965C13.1175 3.03881 12.5779 2.75 12 2.75C11.4221 2.75 10.8825 3.03881 10.5619 3.51965L9.99182 4.37477C9.53036 5.06696 8.6886 5.40122 7.878 5.21415Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M14.75 12C14.75 13.5188 13.5188 14.75 12 14.75C10.4812 14.75 9.25 13.5188 9.25 12C9.25 10.4812 10.4812 9.25 12 9.25C13.5188 9.25 14.75 10.4812 14.75 12Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M13 19.25H5.95C4.82989 19.25 4.26984 19.25 3.84202 19.032C3.46569 18.8403 3.15973 18.5343 2.96799 18.158C2.75 17.7302 2.75 17.1701 2.75 16.05V7.95C2.75 6.82989 2.75 6.26984 2.96799 5.84202C3.15973 5.46569 3.46569 5.15973 3.84202 4.96799C4.26984 4.75 4.8299 4.75 5.95 4.75H18.05C19.1701 4.75 19.7302 4.75 20.158 4.96799C20.5343 5.15973 20.8403 5.46569 21.032 5.84202C21.25 6.26984 21.25 6.8299 21.25 7.95V11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M3 5.63635L10.9761 10.3898C11.6069 10.7657 12.3931 10.7657 13.0239 10.3898L21 5.63635" stroke="currentColor" stroke-width="1.5"/><path d="M21.8148 15.375L21.1669 15.7491M16.1856 18.625L16.8335 18.2509M21.8147 18.625L21.1669 18.251M16.1855 15.375L16.8335 15.7491M19.0002 20.25L19.0002 19.4375M19.0002 13.75V14.5625M21.1669 17C21.1669 16.6053 21.0613 16.2352 20.8769 15.9165C20.5022 15.269 19.8021 14.8333 19.0002 14.8333C18.1983 14.8333 17.4981 15.269 17.1235 15.9165C16.9391 16.2352 16.8335 16.6053 16.8335 17C16.8335 17.3947 16.9391 17.7648 17.1235 18.0835C17.4982 18.731 18.1983 19.1667 19.0002 19.1667C19.8021 19.1667 20.5022 18.731 20.8769 18.0835C21.0613 17.7648 21.1669 17.3947 21.1669 17Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M20.25 6.94155C20.25 6.08069 19.6991 5.31641 18.8825 5.04418L12.6325 2.96085C12.2219 2.824 11.7781 2.824 11.3675 2.96085L5.11754 5.04418C4.30086 5.31641 3.75 6.08069 3.75 6.94155V11.9124C3.75 16.8848 8 19.25 12 21.4079C16 19.25 20.25 16.8848 20.25 11.9124V6.94155Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 446 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M2.75 20.25L6.26353 19.4903M6.26353 19.4903L6.95233 19.3414C7.23089 19.2812 7.51911 19.2812 7.79767 19.3414L11.5773 20.1586C11.8559 20.2188 12.1441 20.2188 12.4227 20.1586L16.2023 19.3414C16.4809 19.2812 16.7691 19.2812 17.0477 19.3414L17.7365 19.4903M6.26353 19.4903C5.08645 17.9188 4.46034 16.5675 4.08992 15.0117C3.8539 14.0205 4.52677 13.0678 5.51689 12.827L11.5273 11.365C11.8379 11.2894 12.1621 11.2894 12.4727 11.365L18.4831 12.827C19.4732 13.0678 20.1461 14.0205 19.9101 15.0117C19.5397 16.5675 18.9136 17.9188 17.7365 19.4903M17.7365 19.4903L21.25 20.25M5.75 12.75V7.75C5.75 7.19772 6.19772 6.75 6.75 6.75H17.25C17.8023 6.75 18.25 7.19772 18.25 7.75V12.75M9.75 6.75V3.75C9.75 3.19772 10.1977 2.75 10.75 2.75H13.25C13.8023 2.75 14.25 3.19772 14.25 3.75V6.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 946 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M11.9982 3.29083V1.76758M5.83985 18.1586L4.76275 19.2357M11.9982 22.2327V20.7094M19.2334 4.76468L18.1562 5.84179M20.707 12.0001H22.2303M18.1562 18.1586L19.2334 19.2357M1.76562 12.0001H3.28888M4.76267 4.76462L5.83977 5.84173M15.7104 8.28781C17.7606 10.3381 17.7606 13.6622 15.7104 15.7124C13.6601 17.7627 10.336 17.7627 8.28574 15.7124C6.23548 13.6622 6.23548 10.3381 8.28574 8.28781C10.336 6.23756 13.6601 6.23756 15.7104 8.28781Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 611 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <path d="M11.25 20.25H5.75C4.64543 20.25 3.75 19.3546 3.75 18.25V5.75C3.75 4.64543 4.64543 3.75 5.75 3.75H18.25C19.3546 3.75 20.25 4.64543 20.25 5.75V11.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M3.75 16.25L6.58579 13.4142C7.36683 12.6332 8.63317 12.6332 9.41421 13.4142L12 16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M14.5 11.5C15.6046 11.5 16.5 10.6046 16.5 9.5C16.5 8.39543 15.6046 7.5 14.5 7.5C13.3954 7.5 12.5 8.39543 12.5 9.5C12.5 10.6046 13.3954 11.5 14.5 11.5Z" stroke="currentColor" stroke-width="1.5"/><path d="M15.75 18.5H21.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M18.5 15.75V21.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> </svg>

Before

Width:  |  Height:  |  Size: 849 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M10 5.75V7.25M14 5.75V7.25M3.75 10.25H20.25V19.25C20.25 20.3546 19.3546 21.25 18.25 21.25H5.75C4.64543 21.25 3.75 20.3546 3.75 19.25V10.25ZM5.75 2.75H18.25V10.25H5.75V2.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 353 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M12.9996 12.8145C12.675 12.7719 12.3415 12.75 11.9996 12.75C8.55174 12.75 5.94978 14.981 4.9305 18.114C4.56744 19.23 5.50919 20.25 6.68275 20.25H13.9996M15.7496 6.5C15.7496 8.57107 14.0706 10.25 11.9996 10.25C9.92851 10.25 8.24958 8.57107 8.24958 6.5C8.24958 4.42893 9.92851 2.75 11.9996 2.75C14.0706 2.75 15.7496 4.42893 15.7496 6.5ZM15.7496 14C15.7496 12.7574 16.7569 11.75 17.9996 11.75C19.2422 11.75 20.2496 12.7574 20.2496 14C20.2496 14.7801 19.8526 15.4675 19.2496 15.8711V17L18.7496 17.9356L19.2496 18.9678V20L17.9996 21L16.7496 20V15.8711C16.1466 15.4675 15.7496 14.7801 15.7496 14Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 771 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="9.25" stroke="currentColor" stroke-width="1.5"/><path d="M11.3121 12.3511L11.0582 7.9983C11.0266 7.45662 11.4574 7 12 7C12.5426 7 12.9734 7.45662 12.9418 7.9983L12.6879 12.3511C12.6666 12.7154 12.365 13 12 13C11.635 13 11.3334 12.7154 11.3121 12.3511Z" fill="currentColor"/><circle cx="11.9999" cy="15.8998" r="1.1" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 445 B

-9
View File
@@ -1,9 +0,0 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-120.000000, 0.000000)" fill="currentColor" fill-rule="nonzero">
<g id="close" transform="translate(120.000000, 0.000000)">
<path d="M19.203125,20 C19.3125,20 19.4166667,19.9791667 19.515625,19.9375 C19.6145833,19.8958333 19.6992188,19.8385417 19.7695312,19.765625 C19.8398438,19.6927083 19.8958333,19.6067708 19.9375,19.5078125 C19.9791667,19.4088542 20,19.3046875 20,19.1953125 C20,18.9765625 19.921875,18.7890625 19.765625,18.6328125 L19.765625,18.6328125 L13.1328125,12 L19.765625,5.3671875 C19.921875,5.2109375 20,5.02083333 20,4.796875 C20,4.6875 19.9791667,4.58463542 19.9375,4.48828125 C19.8958333,4.39192708 19.8385417,4.30729167 19.765625,4.234375 C19.6927083,4.16145833 19.6080729,4.10416667 19.5117188,4.0625 C19.4153646,4.02083333 19.3125,4 19.203125,4 C18.9791667,4 18.7890625,4.078125 18.6328125,4.234375 L18.6328125,4.234375 L12,10.8671875 L5.3671875,4.234375 C5.2109375,4.078125 5.0234375,4 4.8046875,4 C4.6953125,4 4.59114583,4.02083333 4.4921875,4.0625 C4.39322917,4.10416667 4.30729167,4.16015625 4.234375,4.23046875 C4.16145833,4.30078125 4.10416667,4.38541667 4.0625,4.484375 C4.02083333,4.58333333 4,4.6875 4,4.796875 C4,5.02083333 4.078125,5.2109375 4.234375,5.3671875 L4.234375,5.3671875 L10.8671875,12 L4.234375,18.6328125 C4.078125,18.7890625 4,18.9765625 4,19.1953125 C4,19.4244792 4.07682292,19.6158854 4.23046875,19.7695312 C4.38411458,19.9231771 4.57552083,20 4.8046875,20 C5.0234375,20 5.2109375,19.921875 5.3671875,19.765625 L5.3671875,19.765625 L12,13.1328125 L18.6328125,19.765625 C18.7890625,19.921875 18.9791667,20 19.203125,20 Z"></path>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

-9
View File
@@ -1,9 +0,0 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-80.000000, 0.000000)" fill="currentColor" fill-rule="nonzero">
<g id="maximize" transform="translate(80.000000, 0.000000)">
<path d="M17.640625,20 C17.953125,20 18.2526042,19.9361979 18.5390625,19.8085938 C18.8255208,19.6809896 19.0768229,19.5091146 19.2929688,19.2929688 C19.5091146,19.0768229 19.6809896,18.8255208 19.8085938,18.5390625 C19.9361979,18.2526042 20,17.953125 20,17.640625 L20,17.640625 L20,6.359375 C20,6.046875 19.9361979,5.74739583 19.8085938,5.4609375 C19.6809896,5.17447917 19.5091146,4.92317708 19.2929688,4.70703125 C19.0768229,4.49088542 18.8255208,4.31901042 18.5390625,4.19140625 C18.2526042,4.06380208 17.953125,4 17.640625,4 L17.640625,4 L6.359375,4 C6.046875,4 5.74739583,4.06380208 5.4609375,4.19140625 C5.17447917,4.31901042 4.92317708,4.49088542 4.70703125,4.70703125 C4.49088542,4.92317708 4.31901042,5.17447917 4.19140625,5.4609375 C4.06380208,5.74739583 4,6.046875 4,6.359375 L4,6.359375 L4,17.640625 C4,17.953125 4.06380208,18.2526042 4.19140625,18.5390625 C4.31901042,18.8255208 4.49088542,19.0768229 4.70703125,19.2929688 C4.92317708,19.5091146 5.17447917,19.6809896 5.4609375,19.8085938 C5.74739583,19.9361979 6.046875,20 6.359375,20 L6.359375,20 L17.640625,20 Z M17.6015625,18.3984375 L6.3984375,18.3984375 C6.2890625,18.3984375 6.18619792,18.3776042 6.08984375,18.3359375 C5.99348958,18.2942708 5.90885417,18.2369792 5.8359375,18.1640625 C5.76302083,18.0911458 5.70572917,18.0065104 5.6640625,17.9101562 C5.62239583,17.8138021 5.6015625,17.7109375 5.6015625,17.6015625 L5.6015625,17.6015625 L5.6015625,6.3984375 C5.6015625,6.2890625 5.62239583,6.18619792 5.6640625,6.08984375 C5.70572917,5.99348958 5.76302083,5.90885417 5.8359375,5.8359375 C5.90885417,5.76302083 5.99348958,5.70572917 6.08984375,5.6640625 C6.18619792,5.62239583 6.2890625,5.6015625 6.3984375,5.6015625 L6.3984375,5.6015625 L17.6015625,5.6015625 C17.7109375,5.6015625 17.8138021,5.62239583 17.9101562,5.6640625 C18.0065104,5.70572917 18.0911458,5.76302083 18.1640625,5.8359375 C18.2369792,5.90885417 18.2942708,5.99348958 18.3359375,6.08984375 C18.3776042,6.18619792 18.3984375,6.2890625 18.3984375,6.3984375 L18.3984375,6.3984375 L18.3984375,17.6015625 C18.3984375,17.7109375 18.3776042,17.8138021 18.3359375,17.9101562 C18.2942708,18.0065104 18.2369792,18.0911458 18.1640625,18.1640625 C18.0911458,18.2369792 18.0065104,18.2942708 17.9101562,18.3359375 C17.8138021,18.3776042 17.7109375,18.3984375 17.6015625,18.3984375 L17.6015625,18.3984375 Z"></path>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

-9
View File
@@ -1,9 +0,0 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-40.000000, 0.000000)" fill="currentColor" fill-rule="nonzero">
<g id="minimize" transform="translate(40.000000, 0.000000)">
<path d="M19.203125,12 C19.3125,12 19.4153646,11.9791667 19.5117188,11.9375 C19.6080729,11.8958333 19.6927083,11.8385417 19.765625,11.765625 C19.8385417,11.6927083 19.8958333,11.6080729 19.9375,11.5117188 C19.9791667,11.4153646 20,11.3125 20,11.203125 C20,11.09375 19.9791667,10.9908854 19.9375,10.8945312 C19.8958333,10.7981771 19.8385417,10.7122396 19.765625,10.6367188 C19.6927083,10.5611979 19.6080729,10.5026042 19.5117188,10.4609375 C19.4153646,10.4192708 19.3125,10.3984375 19.203125,10.3984375 L19.203125,10.3984375 L4.796875,10.3984375 C4.6875,10.3984375 4.58463542,10.4192708 4.48828125,10.4609375 C4.39192708,10.5026042 4.30729167,10.5611979 4.234375,10.6367188 C4.16145833,10.7122396 4.10416667,10.7981771 4.0625,10.8945312 C4.02083333,10.9908854 4,11.09375 4,11.203125 C4,11.3125 4.02083333,11.4153646 4.0625,11.5117188 C4.10416667,11.6080729 4.16145833,11.6927083 4.234375,11.765625 C4.30729167,11.8385417 4.39192708,11.8958333 4.48828125,11.9375 C4.58463542,11.9791667 4.6875,12 4.796875,12 L4.796875,12 L19.203125,12 Z"></path>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g fill="currentColor" fill-rule="nonzero">
<g id="restore">
<path d="M18.3984375,16.6640625 C18.6380208,16.5807292 18.8567708,16.4635417 19.0546875,16.3125 C19.2526042,16.1614583 19.421875,15.9869792 19.5625,15.7890625 C19.703125,15.5911458 19.8111979,15.3736979 19.8867188,15.1367188 C19.9622396,14.8997396 20,14.6536458 20,14.3984375 L20,14.3984375 L20,8.796875 C20,8.13541667 19.8736979,7.51302083 19.6210938,6.9296875 C19.3684896,6.34635417 19.0247396,5.83854167 18.5898438,5.40625 C18.1549479,4.97395833 17.6458333,4.63151042 17.0625,4.37890625 C16.4791667,4.12630208 15.8567708,4 15.1953125,4 L15.1953125,4 L9.6015625,4 C9.34635417,4 9.10026042,4.03776042 8.86328125,4.11328125 C8.62630208,4.18880208 8.40885417,4.296875 8.2109375,4.4375 C8.01302083,4.578125 7.83854167,4.74739583 7.6875,4.9453125 C7.53645833,5.14322917 7.41927083,5.36197917 7.3359375,5.6015625 L7.3359375,5.6015625 L15.1953125,5.6015625 C15.6223958,5.6015625 16.0299479,5.68359375 16.4179688,5.84765625 C16.8059896,6.01171875 17.1471354,6.23567708 17.4414062,6.51953125 C17.7356771,6.80338542 17.96875,7.13671875 18.140625,7.51953125 C18.3125,7.90234375 18.3984375,8.30989583 18.3984375,8.7421875 L18.3984375,8.7421875 L18.3984375,16.6640625 Z M14.4375,20 C14.7552083,20 15.0559896,19.9361979 15.3398438,19.8085938 C15.6236979,19.6809896 15.8736979,19.5091146 16.0898438,19.2929688 C16.3059896,19.0768229 16.4778646,18.8268229 16.6054688,18.5429688 C16.7330729,18.2591146 16.796875,17.9583333 16.796875,17.640625 L16.796875,17.640625 L16.796875,9.5625 C16.796875,9.24479167 16.7330729,8.94270833 16.6054688,8.65625 C16.4778646,8.36979167 16.3072917,8.11979167 16.09375,7.90625 C15.8802083,7.69270833 15.6302083,7.52213542 15.34375,7.39453125 C15.0572917,7.26692708 14.7552083,7.203125 14.4375,7.203125 L14.4375,7.203125 L6.359375,7.203125 C6.04166667,7.203125 5.74088542,7.26692708 5.45703125,7.39453125 C5.17317708,7.52213542 4.92317708,7.69401042 4.70703125,7.91015625 C4.49088542,8.12630208 4.31901042,8.37630208 4.19140625,8.66015625 C4.06380208,8.94401042 4,9.24479167 4,9.5625 L4,9.5625 L4,17.640625 C4,17.953125 4.06380208,18.2526042 4.19140625,18.5390625 C4.31901042,18.8255208 4.49088542,19.0768229 4.70703125,19.2929688 C4.92317708,19.5091146 5.17447917,19.6809896 5.4609375,19.8085938 C5.74739583,19.9361979 6.046875,20 6.359375,20 L6.359375,20 L14.4375,20 Z M14.3984375,18.3984375 L6.3984375,18.3984375 C6.2890625,18.3984375 6.18619792,18.3776042 6.08984375,18.3359375 C5.99348958,18.2942708 5.90885417,18.2369792 5.8359375,18.1640625 C5.76302083,18.0911458 5.70572917,18.0065104 5.6640625,17.9101562 C5.62239583,17.8138021 5.6015625,17.7109375 5.6015625,17.6015625 L5.6015625,17.6015625 L5.6015625,9.6015625 C5.6015625,9.4921875 5.62239583,9.38932292 5.6640625,9.29296875 C5.70572917,9.19661458 5.76302083,9.11067708 5.8359375,9.03515625 C5.90885417,8.95963542 5.99348958,8.90104167 6.08984375,8.859375 C6.18619792,8.81770833 6.2890625,8.796875 6.3984375,8.796875 L6.3984375,8.796875 L14.3984375,8.796875 C14.5078125,8.796875 14.6119792,8.81770833 14.7109375,8.859375 C14.8098958,8.90104167 14.8958333,8.95833333 14.96875,9.03125 C15.0416667,9.10416667 15.0989583,9.19010417 15.140625,9.2890625 C15.1822917,9.38802083 15.203125,9.4921875 15.203125,9.6015625 L15.203125,9.6015625 L15.203125,17.6015625 C15.203125,17.7109375 15.1822917,17.8138021 15.140625,17.9101562 C15.0989583,18.0065104 15.0403646,18.0911458 14.9648438,18.1640625 C14.8893229,18.2369792 14.8033854,18.2942708 14.7070312,18.3359375 C14.6106771,18.3776042 14.5078125,18.3984375 14.3984375,18.3984375 L14.3984375,18.3984375 Z"></path>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

-58
View File
@@ -1,58 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path
d="M4.75 9.25V4.75H9.25"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M19.25 9.25V4.75H14.75"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M19.25 14.75V19.25H14.75"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M4.75 14.75V19.25H9.25"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5 5L9.5 9.5"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M19 5L14.5 9.5"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M19 19L14.5 14.5"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5 19L9.5 14.5"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

+1
View File
@@ -8,3 +8,4 @@ publish.workspace = true
gpui.workspace = true gpui.workspace = true
anyhow.workspace = true anyhow.workspace = true
rust-embed.workspace = true rust-embed.workspace = true
gpui-kit.workspace = true
+19
View File
@@ -10,6 +10,25 @@ use rust_embed::RustEmbed;
#[exclude = "*.DS_Store"] #[exclude = "*.DS_Store"]
pub struct Assets; pub struct Assets;
/// Coop's bundled assets, with the GPUI Kit component icon bundle (Lucide) as
/// the fallback so `IconName` SVGs resolve alongside coop's own files.
pub struct CombinedAssets;
impl AssetSource for CombinedAssets {
fn load(&self, path: &str) -> Result<Option<std::borrow::Cow<'static, [u8]>>> {
if let Some(data) = Assets.load(path)? {
return Ok(Some(data));
}
gpui_kit::assets::Assets.load(path)
}
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
let mut paths = Assets.list(path)?;
paths.extend(gpui_kit::assets::Assets.list(path)?);
Ok(paths)
}
}
impl AssetSource for Assets { impl AssetSource for Assets {
fn load(&self, path: &str) -> Result<Option<std::borrow::Cow<'static, [u8]>>> { fn load(&self, path: &str) -> Result<Option<std::borrow::Cow<'static, [u8]>>> {
Self::get(path) Self::get(path)
+1 -1
View File
@@ -7,7 +7,7 @@ publish.workspace = true
[dependencies] [dependencies]
state = { path = "../state" } state = { path = "../state" }
ui = { path = "../ui" } ui = { path = "../ui" }
theme = { path = "../theme" } gpui-kit.workspace = true
common = { path = "../common" } common = { path = "../common" }
person = { path = "../person" } person = { path = "../person" }
chat = { path = "../chat" } chat = { path = "../chat" }
+109 -91
View File
@@ -9,12 +9,22 @@ use common::{TimestampExt, emojis_from_tags};
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle, AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle,
Focusable, ImageSource, InteractiveElement, IntoElement, ListAlignment, ListOffset, ListState, Focusable, InteractiveElement, IntoElement, ListAlignment, ListOffset, ListState, MouseButton,
MouseButton, ObjectFit, ParentElement, PathPromptOptions, Render, SharedString, SharedUri, ObjectFit, ParentElement, PathPromptOptions, Render, SharedString, SharedUri,
StatefulInteractiveElement, Styled, StyledImage, Subscription, SystemNotification, StatefulInteractiveElement, Styled, StyledImage, Subscription, SystemNotification,
SystemNotificationAction, Task, WeakEntity, Window, div, img, list, px, red, relative, SystemNotificationAction, Task, WeakEntity, Window, div, img, list, px, red, relative,
retain_all, svg, white, retain_all, svg, white,
}; };
use gpui_kit::assets::IconName;
use gpui_kit::component::avatar::Avatar;
use gpui_kit::component::button::{Button, ButtonVariants};
use gpui_kit::component::dock::{Panel, PanelEvent};
use gpui_kit::component::input::{Input, InputEvent, InputState, Textarea, TextareaState};
use gpui_kit::component::menu::DropdownMenu;
use gpui_kit::component::notification::Notification;
use gpui_kit::component::scroll::Scrollbar;
use gpui_kit::component::tooltip::Tooltip;
use gpui_kit::component::{ActiveTheme, Disableable, Icon, Sizable, WindowExt, h_flex, v_flex};
use itertools::Itertools; use itertools::Itertools;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use person::{Directory, Person}; use person::{Directory, Person};
@@ -24,21 +34,12 @@ use smallvec::{SmallVec, smallvec};
use state::{ use state::{
FileAttachment, NostrRegistry, download_and_decrypt_to_file, upload, upload_encrypted, FileAttachment, NostrRegistry, download_and_decrypt_to_file, upload, upload_encrypted,
}; };
use theme::{ActiveTheme, TextStyle, Typeset as _}; use ui::{TextStyle, Typeset as _};
use ui::avatar::Avatar;
use ui::button::{Button, ButtonVariants};
use ui::dock::{Panel, PanelEvent};
use ui::input::{Input, InputEvent, InputState, Textarea, TextareaState};
use ui::menu::DropdownMenu;
use ui::message::{
MessageMedia, MessageRow, ReactionBar, ReplyPreview, WelcomeMessage, group_reactions, opens_run,
};
use ui::notification::Notification;
use ui::scroll::Scrollbar;
use ui::tooltip::Tooltip;
use ui::{Disableable, Icon, IconName, Sizable, StyledExt, WindowExtension, h_flex, v_flex};
use crate::file::*; use crate::file::*;
use crate::message::{
MessageMedia, MessageRow, ReactionBar, ReplyPreview, WelcomeMessage, group_reactions, opens_run,
};
use crate::text::RenderedText; use crate::text::RenderedText;
const REACTION_EMOJIS: &[&str] = &["👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀"]; const REACTION_EMOJIS: &[&str] = &["👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀"];
@@ -59,6 +60,7 @@ static CUSTOM_EMOJI_RE: LazyLock<Regex> =
mod actions; mod actions;
mod file; mod file;
pub mod message;
pub mod text; pub mod text;
pub fn init(room: WeakEntity<Room>, window: &mut Window, cx: &mut App) -> Entity<ChatPanel> { pub fn init(room: WeakEntity<Room>, window: &mut Window, cx: &mut App) -> Entity<ChatPanel> {
@@ -126,6 +128,11 @@ pub struct ChatPanel {
} }
impl ChatPanel { impl ChatPanel {
/// The room this panel displays, used to dedupe open tabs.
pub fn room_id(&self) -> &SharedString {
&self.id
}
pub fn new(room: WeakEntity<Room>, window: &mut Window, cx: &mut Context<Self>) -> Self { pub fn new(room: WeakEntity<Room>, window: &mut Window, cx: &mut Context<Self>) -> Self {
// Define attachments and replies_to entities // Define attachments and replies_to entities
let attachments = cx.new(|_| vec![]); let attachments = cx.new(|_| vec![]);
@@ -916,7 +923,7 @@ impl ChatPanel {
let seen_on = chat.read(cx).rumor_seen_on(id); let seen_on = chat.read(cx).rumor_seen_on(id);
window.open_dialog(cx, move |this, _window, cx| { window.open_dialog(cx, move |this, _window, cx| {
this.title("Seen on").show_close(true).child( this.title("Seen on").close_button(true).child(
v_flex() v_flex()
.gap_1() .gap_1()
.when_none(&seen_on, |this| { .when_none(&seen_on, |this| {
@@ -925,8 +932,8 @@ impl ChatPanel {
.h_10() .h_10()
.justify_center() .justify_center()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.bg(cx.theme().surface_raised) .bg(cx.theme().secondary)
.rounded(px(theme::panel_radius())) .rounded(px(ui::panel_radius()))
.child("Message isn't traced yet"), .child("Message isn't traced yet"),
) )
}) })
@@ -939,9 +946,9 @@ impl ChatPanel {
h_flex() h_flex()
.h_7() .h_7()
.px_2() .px_2()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.bg(cx.theme().surface_raised) .bg(cx.theme().secondary)
.rounded(px(theme::control_radius())) .rounded(px(ui::control_radius()))
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.child(div().size_1p5().rounded_full().bg(gpui::green())) .child(div().size_1p5().rounded_full().bg(gpui::green()))
.child(SharedString::from(url.to_string())), .child(SharedString::from(url.to_string())),
@@ -962,7 +969,7 @@ impl ChatPanel {
let relays = profile.messaging_relays(); let relays = profile.messaging_relays();
this.title("Messaging Relays") this.title("Messaging Relays")
.show_close(true) .close_button(true)
.child(v_flex().gap_1().children({ .child(v_flex().gap_1().children({
let mut items = vec![]; let mut items = vec![];
@@ -971,9 +978,9 @@ impl ChatPanel {
h_flex() h_flex()
.h_7() .h_7()
.px_2() .px_2()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.bg(cx.theme().surface_raised) .bg(cx.theme().secondary)
.rounded(px(theme::control_radius())) .rounded(px(ui::control_radius()))
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.child(div().size_1p5().rounded_full().bg(gpui::green())) .child(div().size_1p5().rounded_full().bg(gpui::green()))
.child(SharedString::from(url.to_string())), .child(SharedString::from(url.to_string())),
@@ -996,7 +1003,7 @@ impl ChatPanel {
svg() svg()
.path("brand/coop.svg") .path("brand/coop.svg")
.size_12() .size_12()
.text_color(cx.theme().surface_raised), .text_color(cx.theme().secondary),
) )
.message(PRIVATE_NOTICE) .message(PRIVATE_NOTICE)
.into_any_element() .into_any_element()
@@ -1012,13 +1019,13 @@ impl ChatPanel {
.justify_center() .justify_center()
.text_center() .text_center()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.line_height(relative(1.3)) .line_height(relative(1.3))
.child( .child(
svg() svg()
.path("brand/coop.svg") .path("brand/coop.svg")
.size_12() .size_12()
.text_color(cx.theme().element_active), .text_color(cx.theme().list_active),
) )
.child(PRIVATE_NOTICE) .child(PRIVATE_NOTICE)
.into_any_element() .into_any_element()
@@ -1041,9 +1048,9 @@ impl ChatPanel {
.size_8() .size_8()
.justify_center() .justify_center()
.rounded_full() .rounded_full()
.bg(cx.theme().warning_muted) .bg(cx.theme().warning.opacity(0.15))
.text_color(cx.theme().warning) .text_color(cx.theme().warning)
.child(Icon::new(IconName::Warning).small()), .child(Icon::new(IconName::TriangleAlert).small()),
) )
.child( .child(
div() div()
@@ -1146,10 +1153,14 @@ impl ChatPanel {
.show_author(show_author) .show_author(show_author)
.hide_avatar(hide_avatar) .hide_avatar(hide_avatar)
.avatar( .avatar(
Avatar::new(author.avatar()) Button::new("message-avatar")
.seed(author.avatar_seed()) .ghost()
.flex_shrink_0()
.relative() .relative()
.flex_shrink_0()
.child(match author.avatar() {
Some(picture) => Avatar::new().src(picture).into_any_element(),
None => Avatar::new().into_any_element(),
})
.dropdown_menu(move |this, _window, _cx| { .dropdown_menu(move |this, _window, _cx| {
this.menu("Public Key", Box::new(Command::Copy(pk))) this.menu("Public Key", Box::new(Command::Copy(pk)))
.menu("View Relays", Box::new(Command::Relays(pk))) .menu("View Relays", Box::new(Command::Relays(pk)))
@@ -1173,7 +1184,7 @@ impl ChatPanel {
.when(has_reactions, |this| this.child(reactions)) .when(has_reactions, |this| this.child(reactions))
.overlay( .overlay(
div() div()
.group_hover("", |this| this.bg(cx.theme().element_active)) .group_hover("", |this| this.bg(cx.theme().list_active))
.absolute() .absolute()
.left_0() .left_0()
.top_0() .top_0()
@@ -1247,7 +1258,7 @@ impl ChatPanel {
div() div()
.id(SharedString::from(id.to_hex())) .id(SharedString::from(id.to_hex()))
.child(label) .child(label)
.when(failed, |this| this.text_color(cx.theme().danger_muted)) .when(failed, |this| this.text_color(cx.theme().danger))
.when_some(reports, |this, reports| { .when_some(reports, |this, reports| {
this.when(!pending, |this| { this.when(!pending, |this| {
this.on_click(move |_e, window, cx| { this.on_click(move |_e, window, cx| {
@@ -1265,7 +1276,7 @@ impl ChatPanel {
.unwrap_or_default(); .unwrap_or_default();
this.title(SharedString::from("Sent Reports")) this.title(SharedString::from("Sent Reports"))
.show_close(true) .close_button(true)
.child(v_flex().gap_4().children(items)) .child(v_flex().gap_4().children(items))
}); });
}) })
@@ -1279,18 +1290,23 @@ impl ChatPanel {
let avatar = profile.avatar(); let avatar = profile.avatar();
v_flex() v_flex()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.w_full() .w_full()
.child( .child(
h_flex() h_flex()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.child(SharedString::from("Sent to:")) .child(SharedString::from("Sent to:"))
.child( .child(
h_flex() h_flex()
.gap_1() .gap_1()
.font_semibold() .font_weight(gpui::FontWeight::SEMIBOLD)
.child(Avatar::new(avatar).seed(profile.avatar_seed()).small()) .child(match avatar {
Some(picture) => {
Avatar::new().src(picture).small().into_any_element()
}
None => Avatar::new().small().into_any_element(),
})
.child(name.clone()), .child(name.clone()),
), ),
) )
@@ -1303,8 +1319,8 @@ impl ChatPanel {
.h_16() .h_16()
.w_full() .w_full()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.rounded(px(theme::surface_radius())) .rounded(px(ui::surface_radius()))
.bg(cx.theme().warning_muted) .bg(cx.theme().warning.opacity(0.15))
.text_color(cx.theme().warning) .text_color(cx.theme().warning)
.child(div().flex_1().w_full().text_center().child(error)), .child(div().flex_1().w_full().text_center().child(error)),
) )
@@ -1312,7 +1328,7 @@ impl ChatPanel {
.when_some(report.output().cloned(), |this, output| { .when_some(report.output().cloned(), |this, output| {
this.child( this.child(
v_flex() v_flex()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.w_full() .w_full()
.children({ .children({
let mut items = Vec::with_capacity(output.failed.len()); let mut items = Vec::with_capacity(output.failed.len());
@@ -1323,19 +1339,19 @@ impl ChatPanel {
.gap_0p5() .gap_0p5()
.p_1() .p_1()
.w_full() .w_full()
.rounded(px(theme::surface_radius())) .rounded(px(ui::surface_radius()))
.bg(cx.theme().danger.opacity(0.06)) .bg(cx.theme().danger.opacity(0.06))
.child( .child(
div() div()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.font_semibold() .font_weight(gpui::FontWeight::SEMIBOLD)
.line_height(relative(1.25)) .line_height(relative(1.25))
.child(SharedString::from(url.to_string())), .child(SharedString::from(url.to_string())),
) )
.child( .child(
div() div()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().danger_muted.opacity(0.9)) .text_color(cx.theme().danger.opacity(0.9))
.line_height(relative(1.25)) .line_height(relative(1.25))
.child(SharedString::from(msg.to_string())), .child(SharedString::from(msg.to_string())),
), ),
@@ -1353,12 +1369,12 @@ impl ChatPanel {
.gap_0p5() .gap_0p5()
.p_1() .p_1()
.w_full() .w_full()
.rounded(px(theme::surface_radius())) .rounded(px(ui::surface_radius()))
.bg(cx.theme().surface_card) .bg(cx.theme().muted)
.child( .child(
div() div()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.font_semibold() .font_weight(gpui::FontWeight::SEMIBOLD)
.line_height(relative(1.25)) .line_height(relative(1.25))
.child(SharedString::from(url.0.to_string())), .child(SharedString::from(url.0.to_string())),
) )
@@ -1392,10 +1408,10 @@ impl ChatPanel {
.right_4() .right_4()
.top_neg_2() .top_neg_2()
.when(cx.theme().shadow, |this| this.shadow_sm()) .when(cx.theme().shadow, |this| this.shadow_sm())
.rounded(px(theme::surface_radius())) .rounded(px(ui::surface_radius()))
.border_1() .border_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.bg(cx.theme().bg) .bg(cx.theme().background)
.children({ .children({
let mut items = vec![]; let mut items = vec![];
@@ -1472,7 +1488,7 @@ impl ChatPanel {
img(url.as_str()) img(url.as_str())
.size_16() .size_16()
.when(cx.theme().shadow, |this| this.shadow_lg()) .when(cx.theme().shadow, |this| this.shadow_lg())
.rounded(px(theme::control_radius())) .rounded(px(ui::control_radius()))
.object_fit(ObjectFit::Cover), .object_fit(ObjectFit::Cover),
) )
.child( .child(
@@ -1486,7 +1502,7 @@ impl ChatPanel {
.justify_center() .justify_center()
.rounded_full() .rounded_full()
.bg(red()) .bg(red())
.child(Icon::new(IconName::Close).size_2().text_color(white())), .child(Icon::new(IconName::X).size_2().text_color(white())),
) )
.on_click({ .on_click({
let url = url.clone(); let url = url.clone();
@@ -1530,10 +1546,10 @@ impl ChatPanel {
.child( .child(
img(path.clone()) img(path.clone())
.border_1() .border_1()
.border_color(cx.theme().border_faint) .border_color(cx.theme().border)
.h(px(250.)) .h(px(250.))
.object_fit(ObjectFit::Cover) .object_fit(ObjectFit::Cover)
.rounded(px(theme::bubble_radius())), .rounded(px(ui::bubble_radius())),
) )
.into_any_element(); .into_any_element();
} }
@@ -1561,12 +1577,12 @@ impl ChatPanel {
.self_start() .self_start()
.items_start() .items_start()
.min_w_0() .min_w_0()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.p_2() .p_2()
.border_1() .border_1()
.border_color(cx.theme().border_faint) .border_color(cx.theme().border)
.rounded(px(theme::control_radius())) .rounded(px(ui::control_radius()))
.child(Icon::new(IconName::Lock).text_color(cx.theme().accent)) .child(Icon::new(IconName::Lock).text_color(cx.theme().primary))
.child( .child(
v_flex() v_flex()
.min_w_0() .min_w_0()
@@ -1576,7 +1592,7 @@ impl ChatPanel {
.child( .child(
div() div()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.child(label), .child(label),
), ),
) )
@@ -1600,14 +1616,14 @@ impl ChatPanel {
.id(SharedString::from(file.url.to_string())) .id(SharedString::from(file.url.to_string()))
.relative() .relative()
.w_16() .w_16()
.tooltip(move |window, cx| Tooltip::new(label.clone(), window, cx).into()) .tooltip(move |window, cx| Tooltip::new(label.clone()).build(window, cx))
.map(|this| { .map(|this| {
if file.is_image() { if file.is_image() {
this.child( this.child(
img(pending.path.clone()) img(pending.path.clone())
.size_16() .size_16()
.when(cx.theme().shadow, |this| this.shadow_sm()) .when(cx.theme().shadow, |this| this.shadow_sm())
.rounded(px(theme::control_radius())) .rounded(px(ui::control_radius()))
.object_fit(ObjectFit::Cover), .object_fit(ObjectFit::Cover),
) )
} else { } else {
@@ -1617,10 +1633,10 @@ impl ChatPanel {
.flex() .flex()
.items_center() .items_center()
.justify_center() .justify_center()
.rounded(px(theme::control_radius())) .rounded(px(ui::control_radius()))
.border_1() .border_1()
.border_color(cx.theme().border_faint) .border_color(cx.theme().border)
.bg(cx.theme().surface) .bg(cx.theme().sidebar)
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_center() .text_center()
.child("Preview not available"), .child("Preview not available"),
@@ -1637,7 +1653,7 @@ impl ChatPanel {
.justify_center() .justify_center()
.rounded_full() .rounded_full()
.border_1() .border_1()
.border_color(cx.theme().border_faint) .border_color(cx.theme().border)
.bg(gpui::green()) .bg(gpui::green())
.child(Icon::new(IconName::Lock).size_2().text_color(gpui::white())), .child(Icon::new(IconName::Lock).size_2().text_color(gpui::white())),
) )
@@ -1671,7 +1687,7 @@ impl ChatPanel {
.w_full() .w_full()
.pl_2() .pl_2()
.border_l_2() .border_l_2()
.border_color(cx.theme().element_active) .border_color(cx.theme().list_active)
.child( .child(
h_flex() h_flex()
.justify_between() .justify_between()
@@ -1681,13 +1697,13 @@ impl ChatPanel {
.items_baseline() .items_baseline()
.gap_1() .gap_1()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child("Replying to:") .child("Replying to:")
.child(div().text_color(cx.theme().accent).child(profile.name())), .child(div().text_color(cx.theme().primary).child(profile.name())),
) )
.child( .child(
Button::new("remove-reply") Button::new("remove-reply")
.icon(IconName::Close) .icon(IconName::X)
.xsmall() .xsmall()
.ghost() .ghost()
.on_click({ .on_click({
@@ -1784,13 +1800,11 @@ impl ChatPanel {
.unwrap_or_default(); .unwrap_or_default();
Button::new("emoji") Button::new("emoji")
.icon(IconName::Emoji) .icon(IconName::Sticker)
.ghost() .ghost()
.large() .large()
.dropdown_menu_with_anchor(gpui::Anchor::BottomLeft, move |this, _window, _cx| { .dropdown_menu_with_anchor(gpui::Anchor::BottomLeft, move |this, _window, _cx| {
let menu = REACTION_EMOJIS let menu = REACTION_EMOJIS.iter().fold(this, |this, emoji| {
.iter()
.fold(this.horizontal(), |this, emoji| {
this.menu(*emoji, Box::new(Command::Insert(emoji.to_string()))) this.menu(*emoji, Box::new(Command::Insert(emoji.to_string())))
}); });
@@ -1802,22 +1816,26 @@ impl ChatPanel {
} }
} }
impl Panel for ChatPanel { impl gpui_kit::component::dock::BasePanel for ChatPanel {
fn panel_id(&self) -> SharedString { fn panel_name(&self) -> &'static str {
self.id.clone() "ChatPanel"
}
} }
fn title(&self, cx: &App) -> AnyElement { impl Panel for ChatPanel {
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
self.room self.room
.read_with(cx, |this, cx| { .read_with(cx, |this, cx| {
let label = this.display_name(cx); let label = this.display_name(cx);
let picture = this.display_image(cx); let picture = this.display_image(cx);
let seed = this.display_image_seed(cx);
h_flex() h_flex()
.gap_1() .gap_1()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.child(Avatar::new(picture).seed(seed).xsmall()) .child(match picture {
Some(picture) => Avatar::new().src(picture).xsmall().into_any_element(),
None => Avatar::new().xsmall().into_any_element(),
})
.child(label) .child(label)
.into_any_element() .into_any_element()
}) })
@@ -1829,12 +1847,16 @@ impl Panel for ChatPanel {
) )
} }
fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec<Button> { fn toolbar_buttons(
&mut self,
_window: &mut Window,
_cx: &mut Context<Self>,
) -> Option<Vec<Button>> {
let subject_bar = self.subject_bar.clone(); let subject_bar = self.subject_bar.clone();
vec![ Some(vec![
Button::new("subject") Button::new("subject")
.icon(IconName::Input) .icon(IconName::TextCursorInput)
.tooltip("Change subject") .tooltip("Change subject")
.small() .small()
.ghost() .ghost()
@@ -1844,11 +1866,7 @@ impl Panel for ChatPanel {
cx.notify(); cx.notify();
}); });
}), }),
] ])
}
fn background(&self, cx: &App) -> Option<ImageSource> {
AppSettings::panel_background_source(cx)
} }
} }
@@ -1877,13 +1895,13 @@ impl Render for ChatPanel {
.h_12() .h_12()
.w_full() .w_full()
.px_2() .px_2()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.border_b_1() .border_b_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.child(Input::new(&self.subject_input).small()) .child(Input::new(&self.subject_input).small())
.child( .child(
Button::new("change") Button::new("change")
.icon(IconName::CheckCircle) .icon(IconName::CircleCheck)
.label("Change") .label("Change")
.secondary() .secondary()
.disabled(self.uploading) .disabled(self.uploading)
@@ -1962,7 +1980,7 @@ impl Render for ChatPanel {
.child(self.render_config_menu(window, cx)) .child(self.render_config_menu(window, cx))
.child( .child(
Button::new("send") Button::new("send")
.icon(IconName::PaperPlaneFill) .icon(IconName::SendHorizontal)
.disabled(self.uploading) .disabled(self.uploading)
.ghost() .ghost()
.large() .large()
@@ -6,10 +6,11 @@ use gpui::{
MouseDownEvent, ObjectFit, ParentElement as _, RenderOnce, SharedString, SharedUri, MouseDownEvent, ObjectFit, ParentElement as _, RenderOnce, SharedString, SharedUri,
StatefulInteractiveElement as _, StyleRefinement, Styled, StyledImage, Window, div, img, px, StatefulInteractiveElement as _, StyleRefinement, Styled, StyledImage, Window, div, img, px,
}; };
use gpui_kit::component::{
ActiveTheme, InteractiveElementExt as _, StyledExt as _, h_flex, v_flex,
};
use smallvec::SmallVec; use smallvec::SmallVec;
use theme::{ActiveTheme, TextStyle, Typeset as _, bubble_radius, control_radius}; use ui::{TextStyle, Typeset as _, bubble_radius, control_radius};
use crate::{InteractiveElementExt as _, StyledExt as _, h_flex, v_flex};
type MouseDownListener = Box<dyn Fn(&MouseDownEvent, &mut Window, &mut App) + 'static>; type MouseDownListener = Box<dyn Fn(&MouseDownEvent, &mut Window, &mut App) + 'static>;
type ClickListener = Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>; type ClickListener = Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
@@ -205,11 +206,15 @@ impl RenderOnce for MessageRow {
.when(show_author, |this| { .when(show_author, |this| {
this.child( this.child(
h_flex() h_flex()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.text_color(cx.theme().text_dim) .text_color(cx.theme().muted_foreground)
.when_some(author, |this, author| { .when_some(author, |this, author| {
this.child(div().font_semibold().child(author)) this.child(
div()
.font_weight(gpui::FontWeight::SEMIBOLD)
.child(author),
)
}) })
.when_some(timestamp, |this, timestamp| { .when_some(timestamp, |this, timestamp| {
this.child(timestamp) this.child(timestamp)
@@ -227,7 +232,7 @@ impl RenderOnce for MessageRow {
.when_some(on_double_click, |this, listener| { .when_some(on_double_click, |this, listener| {
this.on_double_click(listener) this.on_double_click(listener)
}) })
.hover(|this| this.bg(cx.theme().element_hover)) .hover(|this| this.bg(cx.theme().accent))
.into_any_element() .into_any_element()
} }
} }
@@ -290,12 +295,12 @@ impl RenderOnce for ReplyPreview {
.w_full() .w_full()
.px_2() .px_2()
.border_l_2() .border_l_2()
.border_color(cx.theme().element_active) .border_color(cx.theme().list_active)
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.refine_style(&style) .refine_style(&style)
.child(div().font_semibold().child(author)) .child(div().font_weight(gpui::FontWeight::SEMIBOLD).child(author))
.child(div().w_full().text_ellipsis().line_clamp(1).child(preview)) .child(div().w_full().text_ellipsis().line_clamp(1).child(preview))
.hover(|this| this.bg(cx.theme().surface_raised)) .hover(|this| this.bg(cx.theme().secondary))
.when_some(on_click, |this, on_click| this.on_click(on_click)) .when_some(on_click, |this, on_click| this.on_click(on_click))
.into_any_element() .into_any_element()
} }
@@ -338,7 +343,7 @@ impl RenderOnce for MessageMedia {
.child( .child(
img(only.clone()) img(only.clone())
.border_1() .border_1()
.border_color(cx.theme().border_faint) .border_color(cx.theme().border)
.h(px(250.)) .h(px(250.))
.object_fit(ObjectFit::Cover) .object_fit(ObjectFit::Cover)
.rounded(px(bubble_radius())), .rounded(px(bubble_radius())),
@@ -352,14 +357,14 @@ impl RenderOnce for MessageMedia {
.flex() .flex()
.flex_row() .flex_row()
.flex_wrap() .flex_wrap()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.refine_style(&style) .refine_style(&style)
.children(media.into_iter().enumerate().map(|(ix, item)| { .children(media.into_iter().enumerate().map(|(ix, item)| {
div().id(format!("media-{ix}")).flex_shrink_0().child( div().id(format!("media-{ix}")).flex_shrink_0().child(
img(item) img(item)
.h_32() .h_32()
.border_1() .border_1()
.border_color(cx.theme().border_faint) .border_color(cx.theme().border)
.rounded(px(bubble_radius())), .rounded(px(bubble_radius())),
) )
})) }))
@@ -436,16 +441,16 @@ impl RenderOnce for ReactionBar {
.refine_style(&style) .refine_style(&style)
.children(groups.into_iter().map(|group| { .children(groups.into_iter().map(|group| {
h_flex() h_flex()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.py_0p5() .py_0p5()
.px_1() .px_1()
.rounded(px(control_radius())) .rounded(px(control_radius()))
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.border_1() .border_1()
.when(group.mine, |this| { .when(group.mine, |this| {
this.text_color(cx.theme().text) this.text_color(cx.theme().foreground)
.bg(cx.theme().surface_raised) .bg(cx.theme().secondary)
.border_color(cx.theme().element_active) .border_color(cx.theme().list_active)
}) })
.when(!group.mine, |this| this.border_color(cx.theme().border)) .when(!group.mine, |this| this.border_color(cx.theme().border))
.when(group.image.is_none(), |this| this.child(group.emoji)) .when(group.image.is_none(), |this| this.child(group.emoji))
@@ -520,7 +525,7 @@ impl RenderOnce for WelcomeMessage {
v_flex() v_flex()
.id(id) .id(id)
.w_full() .w_full()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.p_3() .p_3()
.items_center() .items_center()
.justify_center() .justify_center()
@@ -536,7 +541,7 @@ impl RenderOnce for WelcomeMessage {
this.child( this.child(
div() div()
.text_style(TextStyle::Headline) .text_style(TextStyle::Headline)
.text_color(cx.theme().text) .text_color(cx.theme().foreground)
.child(title), .child(title),
) )
}) })
@@ -544,7 +549,7 @@ impl RenderOnce for WelcomeMessage {
this.child( this.child(
div() div()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.child(message), .child(message),
) )
}), }),
+119 -3
View File
@@ -1,9 +1,125 @@
use std::ops::Range;
use chat::{Mention, nevent_refs}; use chat::{Mention, nevent_refs};
use gpui::{App, Entity}; use gpui::{AnyElement, App, ElementId, Entity, IntoElement as _, SharedString, SharedUri, Window};
use gpui_kit::component::text::TextView;
use nostr_sdk::prelude::Url; use nostr_sdk::prelude::Url;
use person::Directory; use person::Directory;
use ui::markdown::InlineReplacement;
pub use ui::markdown::RenderedText; /// A span of the source content, by byte range, that renders as `text` instead.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InlineReplacement {
pub range: Range<usize>,
pub text: SharedString,
/// When set, the span renders as this image (NIP-30 custom emoji)
pub image: Option<SharedUri>,
}
impl InlineReplacement {
pub fn new(range: Range<usize>, text: impl Into<SharedString>) -> Self {
Self {
range,
text: text.into(),
image: None,
}
}
pub fn image(
range: Range<usize>,
alt: impl Into<SharedString>,
url: impl Into<SharedUri>,
) -> Self {
Self {
range,
text: alt.into(),
image: Some(url.into()),
}
}
}
/// Message content prepared for rendering as a GPUI Kit text view.
#[derive(Default)]
pub struct RenderedText {
text: SharedString,
}
impl RenderedText {
pub fn new(content: &str, replacements: &[InlineReplacement]) -> Self {
let mut text = String::with_capacity(content.len());
let mut last = 0;
for replacement in replacements {
let range = &replacement.range;
if range.start < last
|| range.end > content.len()
|| range.start >= range.end
|| !content.is_char_boundary(range.start)
|| !content.is_char_boundary(range.end)
{
continue;
}
text.push_str(&content[last..range.start]);
match &replacement.image {
Some(url) => text.push_str(&format!(
"![{}](<{url}>)",
escape_link_text(&replacement.text)
)),
None => text.push_str(&format!(
"[{}](<{}>)",
escape_link_text(&replacement.text),
&content[range.start..range.end]
)),
}
last = range.end;
}
text.push_str(&content[last..]);
Self {
text: SharedString::from(text),
}
}
/// Render the message as a GPUI Kit text view.
///
/// The framework Root installs a theme-derived base style for text views,
/// so only the Nostr link handling is customized here.
pub fn element(&self, id: ElementId, _window: &Window, _cx: &App) -> AnyElement {
TextView::markdown(id, self.text.clone())
.on_link_click(|url, _, _, cx| {
if is_web_url(url) {
cx.open_url(url);
} else if let Some(bech32) = nevent_bech32(url) {
cx.open_url(&format!("https://njump.me/{bech32}"));
}
})
.into_any_element()
}
}
/// Escapes the characters that would terminate a markdown link label.
fn escape_link_text(text: &str) -> String {
let mut escaped = String::with_capacity(text.len());
for character in text.chars() {
if matches!(character, '[' | ']' | '\\') {
escaped.push('\\');
}
escaped.push(character);
}
escaped
}
/// Matches `http://` and `https://` URLs. Only these are treated as clickable links.
fn is_web_url(url: &str) -> bool {
let url = url.to_ascii_lowercase();
url.starts_with("http://") || url.starts_with("https://")
}
/// Extracts the bech32 from a NIP-19 event reference (`nevent1...` or `nostr:nevent1...`).
fn nevent_bech32(url: &str) -> Option<&str> {
let bech32 = url.strip_prefix("nostr:").unwrap_or(url);
bech32.starts_with("nevent1").then_some(bech32)
}
pub fn rendered_text( pub fn rendered_text(
content: &str, content: &str,
+1 -1
View File
@@ -12,7 +12,7 @@ nip29 = { path = "../nip29" }
person = { path = "../person" } person = { path = "../person" }
settings = { path = "../settings" } settings = { path = "../settings" }
state = { path = "../state" } state = { path = "../state" }
theme = { path = "../theme" } gpui-kit.workspace = true
ui = { path = "../ui" } ui = { path = "../ui" }
gpui.workspace = true gpui.workspace = true
+21 -19
View File
@@ -2,15 +2,15 @@ use gpui::{
AnyElement, App, IntoElement, ParentElement, SharedString, Styled, WeakEntity, Window, div, px, AnyElement, App, IntoElement, ParentElement, SharedString, Styled, WeakEntity, Window, div, px,
relative, relative,
}; };
use gpui_kit::component::avatar::Avatar;
use gpui_kit::component::{ActiveTheme, Sizable, WindowExt, h_flex, v_flex};
use nip29::{Group, GroupMetadata}; use nip29::{Group, GroupMetadata};
use theme::{ActiveTheme, TextStyle, Typeset as _, control_radius}; use ui::{TextStyle, Typeset as _, control_radius};
use ui::avatar::Avatar;
use ui::{Sizable, StyledExt, WindowExtension, h_flex, v_flex};
pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App) { pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App) {
window.open_dialog(cx, move |this, _window, cx| { window.open_dialog(cx, move |this, _window, cx| {
this.max_h(px(600.)) this.max_h(px(600.))
.show_close(true) .close_button(true)
.title("About Group") .title("About Group")
.child(body(&group, cx)) .child(body(&group, cx))
}); });
@@ -22,13 +22,16 @@ fn body(group: &WeakEntity<Group>, cx: &App) -> AnyElement {
}; };
let group = entity.read(cx); let group = entity.read(cx);
let mut body = v_flex().gap_3().text_style(TextStyle::Callout).child(header(group, cx)); let mut body = v_flex()
.gap_3()
.text_style(TextStyle::Callout)
.child(header(group, cx));
if let Some(metadata) = group.metadata() { if let Some(metadata) = group.metadata() {
if let Some(about) = metadata.about.as_ref() { if let Some(about) = metadata.about.as_ref() {
body = body.child( body = body.child(
div() div()
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child(SharedString::from(about.clone())), .child(SharedString::from(about.clone())),
); );
} }
@@ -76,7 +79,7 @@ fn body(group: &WeakEntity<Group>, cx: &App) -> AnyElement {
"Supported kinds", "Supported kinds",
if items.is_empty() { if items.is_empty() {
div() div()
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.child("None") .child("None")
.into_any_element() .into_any_element()
} else { } else {
@@ -107,7 +110,7 @@ fn body(group: &WeakEntity<Group>, cx: &App) -> AnyElement {
.map(|pin| { .map(|pin| {
div() div()
.truncate() .truncate()
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child(crate::util::pin_label(group, pin)) .child(crate::util::pin_label(group, pin))
.into_any_element() .into_any_element()
}) })
@@ -127,25 +130,24 @@ fn header(group: &Group, cx: &App) -> AnyElement {
h_flex() h_flex()
.gap_3() .gap_3()
.items_center() .items_center()
.child( .child(match group.display_image() {
Avatar::from_source(group.display_image()) Some(picture) => Avatar::new().src(picture).large().into_any_element(),
.seed(key.cache_tag()) None => Avatar::new().large().into_any_element(),
.large(), })
)
.child( .child(
v_flex() v_flex()
.min_w_0() .min_w_0()
.child( .child(
div() div()
.text_style(TextStyle::Body) .text_style(TextStyle::Body)
.font_semibold() .font_weight(gpui::FontWeight::SEMIBOLD)
.truncate() .truncate()
.child(group.display_name()), .child(group.display_name()),
) )
.child( .child(
div() div()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.truncate() .truncate()
.child(SharedString::from(format!( .child(SharedString::from(format!(
"{} · {}", "{} · {}",
@@ -163,8 +165,8 @@ fn section(label: &'static str, content: AnyElement, cx: &App) -> AnyElement {
.child( .child(
div() div()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.font_medium() .font_weight(gpui::FontWeight::MEDIUM)
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child(label), .child(label),
) )
.child(content) .child(content)
@@ -184,8 +186,8 @@ fn chip(label: impl Into<SharedString>, cx: &App) -> AnyElement {
.px_2() .px_2()
.py_1() .py_1()
.rounded(px(control_radius())) .rounded(px(control_radius()))
.bg(cx.theme().surface_raised) .bg(cx.theme().secondary)
.text_color(cx.theme().text) .text_color(cx.theme().foreground)
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.line_height(relative(1.)) .line_height(relative(1.))
.child(label.into()) .child(label.into())
+9 -9
View File
@@ -5,13 +5,13 @@ use gpui::{
App, AppContext, ClipboardItem, Entity, IntoElement, ParentElement, SharedString, Styled, App, AppContext, ClipboardItem, Entity, IntoElement, ParentElement, SharedString, Styled,
WeakEntity, Window, div, px, WeakEntity, Window, div, px,
}; };
use gpui_kit::component::button::{Button, ButtonVariants};
use gpui_kit::component::input::InputState;
use gpui_kit::component::notification::Notification;
use gpui_kit::component::{ActiveTheme, Sizable, WindowExt, h_flex, v_flex};
use nip29::{Group, Invite}; use nip29::{Group, Invite};
use nostr_sdk::prelude::SecretKey; use nostr_sdk::prelude::SecretKey;
use theme::{ActiveTheme, TextStyle, Typeset as _}; use ui::{TextStyle, Typeset as _};
use ui::button::{Button, ButtonVariants};
use ui::input::InputState;
use ui::notification::Notification;
use ui::{Sizable, WindowExtension, h_flex, v_flex};
use crate::util::{field, report}; use crate::util::{field, report};
@@ -26,7 +26,7 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
window.open_dialog(cx, move |this, _window, cx| { window.open_dialog(cx, move |this, _window, cx| {
this.max_h(px(600.)) this.max_h(px(600.))
.show_close(true) .close_button(true)
.title("Invites") .title("Invites")
.child(body(group.clone(), input.clone(), cx)) .child(body(group.clone(), input.clone(), cx))
}); });
@@ -51,13 +51,13 @@ fn body(group: WeakEntity<Group>, input: Entity<InputState>, cx: &App) -> impl I
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.child( .child(
div() div()
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child("Anyone with a code can ask to join. Revoking a code stops new joins, it doesn't remove anyone."), .child("Anyone with a code can ask to join. Revoking a code stops new joins, it doesn't remove anyone."),
) )
.when(invites.is_empty(), |this| { .when(invites.is_empty(), |this| {
this.child( this.child(
div() div()
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.child("No active invites"), .child("No active invites"),
) )
}) })
@@ -69,7 +69,7 @@ fn invite_row(group: WeakEntity<Group>, invite: &Invite, cx: &App) -> impl IntoE
let created = div() let created = div()
.flex_shrink_0() .flex_shrink_0()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.child(invite.created_at.to_ago()); .child(invite.created_at.to_ago());
h_flex() h_flex()
+91 -89
View File
@@ -1,14 +1,24 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use anyhow::Result; use anyhow::Result;
use chat_ui::text::rendered_text; use chat_ui::message::WelcomeMessage;
use chat_ui::text::{RenderedText, rendered_text};
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
Action, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable, Action, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
FollowMode, ImageSource, IntoElement, ListAlignment, ListScrollEvent, ListState, ParentElement, FollowMode, IntoElement, ListAlignment, ListScrollEvent, ListState, ParentElement,
PathPromptOptions, Render, SharedString, Styled, Subscription, Task, WeakEntity, Window, div, PathPromptOptions, Render, SharedString, Styled, Subscription, Task, WeakEntity, Window, div,
list, px, list, px,
}; };
use gpui_kit::assets::IconName;
use gpui_kit::component::avatar::Avatar;
use gpui_kit::component::button::{Button, ButtonVariant, ButtonVariants};
use gpui_kit::component::dialog::DialogButtonProps;
use gpui_kit::component::dock::{Panel, PanelEvent};
use gpui_kit::component::input::{InputEvent, Textarea, TextareaState};
use gpui_kit::component::notification::Notification;
use gpui_kit::component::scroll::Scrollbar;
use gpui_kit::component::{ActiveTheme, Disableable, Icon, Sizable, WindowExt, h_flex, v_flex};
use nip29::{Group, GroupEvent, GroupKey, GroupsRegistry, Membership, Pin, Row}; use nip29::{Group, GroupEvent, GroupKey, GroupsRegistry, Membership, Pin, Row};
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use person::Directory; use person::Directory;
@@ -16,17 +26,7 @@ use serde::Deserialize;
use settings::AppSettings; use settings::AppSettings;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use state::{NostrRegistry, upload}; use state::{NostrRegistry, upload};
use theme::{ActiveTheme, TextStyle, Typeset as _, control_radius}; use ui::{TextStyle, Typeset as _, control_radius};
use ui::avatar::Avatar;
use ui::button::{Button, ButtonVariant, ButtonVariants};
use ui::dialog::DialogButtonProps;
use ui::dock::{Panel, PanelEvent};
use ui::input::{InputEvent, Textarea, TextareaState};
use ui::markdown::RenderedText;
use ui::message::WelcomeMessage;
use ui::notification::Notification;
use ui::scroll::Scrollbar;
use ui::{Disableable, Icon, IconName, Sizable, StyledExt, WindowExtension, h_flex, v_flex};
use util::{attachment_name, display_name, opens_run, pin_label, report}; use util::{attachment_name, display_name, opens_run, pin_label, report};
mod details; mod details;
@@ -123,7 +123,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
.when(admin, |this| { .when(admin, |this| {
this.child( this.child(
Button::new("edit") Button::new("edit")
.icon(IconName::Edit) .icon(IconName::SquarePen)
.tooltip("Edit group") .tooltip("Edit group")
.ghost() .ghost()
.small() .small()
@@ -133,7 +133,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
) )
.child( .child(
Button::new("group-invite") Button::new("group-invite")
.icon(IconName::Invite) .icon(IconName::UserRoundPlus)
.tooltip("Invite people") .tooltip("Invite people")
.ghost() .ghost()
.small() .small()
@@ -164,7 +164,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
) )
.child( .child(
Button::new("group-delete") Button::new("group-delete")
.icon(IconName::CloseCircle) .icon(IconName::CircleX)
.tooltip("Delete group") .tooltip("Delete group")
.ghost() .ghost()
.small() .small()
@@ -175,7 +175,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
}) })
.child( .child(
Button::new("leave") Button::new("leave")
.icon(IconName::Logout) .icon(IconName::LogOut)
.tooltip("Leave group") .tooltip("Leave group")
.ghost() .ghost()
.small() .small()
@@ -195,7 +195,7 @@ pub fn confirm_leave(group: WeakEntity<Group>, window: &mut Window, cx: &mut App
return; return;
}; };
window.open_dialog(cx, move |this, _window, _cx| { window.open_alert_dialog(cx, move |this, _window, _cx| {
let key = key.clone(); let key = key.clone();
this.confirm() this.confirm()
@@ -231,7 +231,7 @@ pub fn confirm_delete(group: WeakEntity<Group>, window: &mut Window, cx: &mut Ap
return; return;
}; };
window.open_dialog(cx, move |this, _window, _cx| { window.open_alert_dialog(cx, move |this, _window, _cx| {
let key = key.clone(); let key = key.clone();
this.confirm() this.confirm()
@@ -305,7 +305,6 @@ fn put_user(
} }
pub struct GroupPanel { pub struct GroupPanel {
id: SharedString,
focus_handle: FocusHandle, focus_handle: FocusHandle,
group: WeakEntity<Group>, group: WeakEntity<Group>,
/// Person directory for members, message authors, and activity subjects /// Person directory for members, message authors, and activity subjects
@@ -328,16 +327,11 @@ pub struct GroupPanel {
impl GroupPanel { impl GroupPanel {
pub(crate) fn new(group: Entity<Group>, window: &mut Window, cx: &mut Context<Self>) -> Self { pub(crate) fn new(group: Entity<Group>, window: &mut Window, cx: &mut Context<Self>) -> Self {
let (id, name, count, head) = { let (name, count, head) = {
let group = group.read(cx); let group = group.read(cx);
let rows = group.rows(); let rows = group.rows();
( (display_name(group), rows.len(), rows.first().map(Row::id))
SharedString::from(format!("group-{}", group.key().cache_tag())),
display_name(group),
rows.len(),
rows.first().map(Row::id),
)
}; };
let input = cx.new(|cx| { let input = cx.new(|cx| {
@@ -383,7 +377,6 @@ impl GroupPanel {
}); });
let mut this = Self { let mut this = Self {
id,
focus_handle: cx.focus_handle(), focus_handle: cx.focus_handle(),
group: group.downgrade(), group: group.downgrade(),
directory: Directory::new(cx), directory: Directory::new(cx),
@@ -721,7 +714,7 @@ impl GroupPanel {
h_flex() h_flex()
.flex_shrink_0() .flex_shrink_0()
.w_full() .w_full()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.px_3() .px_3()
.py_2() .py_2()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
@@ -730,7 +723,7 @@ impl GroupPanel {
.child( .child(
Icon::new(IconName::Info) Icon::new(IconName::Info)
.small() .small()
.text_color(cx.theme().text_faint), .text_color(cx.theme().muted_foreground),
) )
.child(div().flex_1().min_w_0().truncate().child(message)) .child(div().flex_1().min_w_0().truncate().child(message))
.child( .child(
@@ -759,11 +752,11 @@ impl GroupPanel {
h_flex() h_flex()
.flex_shrink_0() .flex_shrink_0()
.w_full() .w_full()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.px_3() .px_3()
.py_2() .py_2()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.bg(cx.theme().surface_raised) .bg(cx.theme().secondary)
.child( .child(
Icon::new(IconName::Info) Icon::new(IconName::Info)
.small() .small()
@@ -788,7 +781,7 @@ impl GroupPanel {
) )
.child( .child(
Button::new("dismiss") Button::new("dismiss")
.icon(IconName::Close) .icon(IconName::X)
.small() .small()
.ghost() .ghost()
.tooltip("Close") .tooltip("Close")
@@ -827,9 +820,9 @@ impl GroupPanel {
.px_1p5() .px_1p5()
.py_0p5() .py_0p5()
.rounded(px(control_radius())) .rounded(px(control_radius()))
.bg(cx.theme().solid) .bg(cx.theme().primary)
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child(pin_label(group, pin)) .child(pin_label(group, pin))
})) }))
.into_any_element() .into_any_element()
@@ -839,17 +832,22 @@ impl GroupPanel {
let (name, avatar) = match self.group.upgrade() { let (name, avatar) = match self.group.upgrade() {
Some(group) => { Some(group) => {
let group = group.read(cx); let group = group.read(cx);
let seed = group.key().cache_tag();
let picture = group let picture = group
.metadata() .metadata()
.and_then(|metadata| metadata.picture.clone()); .and_then(|metadata| metadata.picture.clone());
( (
display_name(group), display_name(group),
Avatar::from_source(picture).seed(seed).large(), match picture {
Some(picture) => Avatar::new().src(picture).large().into_any_element(),
None => Avatar::new().large().into_any_element(),
},
) )
} }
None => (SharedString::from("this group"), Avatar::new(None)), None => (
SharedString::from("this group"),
Avatar::new().large().into_any_element(),
),
}; };
WelcomeMessage::new("welcome") WelcomeMessage::new("welcome")
@@ -906,11 +904,11 @@ impl GroupPanel {
.w_full() .w_full()
.justify_center() .justify_center()
.items_center() .items_center()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.px_3() .px_3()
.py_2() .py_2()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.text_color(cx.theme().text_muted); .text_color(cx.theme().muted_foreground);
match notice { match notice {
Notice::Relay(reason) => row Notice::Relay(reason) => row
@@ -941,7 +939,7 @@ impl GroupPanel {
Button::new("join") Button::new("join")
.label("Request to Join") .label("Request to Join")
.small() .small()
.ghost_alt() .ghost()
.on_click(cx.listener(move |this, _event, _window, cx| { .on_click(cx.listener(move |this, _event, _window, cx| {
this.request_join(key.clone(), cx); this.request_join(key.clone(), cx);
})), })),
@@ -1079,7 +1077,7 @@ impl GroupPanel {
.child(Textarea::new(&self.input).appearance(false).flex_1()) .child(Textarea::new(&self.input).appearance(false).flex_1())
.child( .child(
Button::new("send") Button::new("send")
.icon(IconName::PaperPlaneFill) .icon(IconName::SendHorizontal)
.tooltip("Send") .tooltip("Send")
.ghost() .ghost()
.large() .large()
@@ -1120,12 +1118,12 @@ impl GroupPanel {
.px_2() .px_2()
.py_1() .py_1()
.rounded(px(control_radius())) .rounded(px(control_radius()))
.bg(cx.theme().surface_raised) .bg(cx.theme().secondary)
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.child( .child(
div() div()
.flex_shrink_0() .flex_shrink_0()
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child("Replying to"), .child("Replying to"),
) )
.child( .child(
@@ -1133,8 +1131,8 @@ impl GroupPanel {
.flex_shrink_0() .flex_shrink_0()
.max_w(px(140.)) .max_w(px(140.))
.truncate() .truncate()
.font_semibold() .font_weight(gpui::FontWeight::SEMIBOLD)
.text_color(cx.theme().text) .text_color(cx.theme().foreground)
.child(author), .child(author),
) )
.child( .child(
@@ -1142,12 +1140,12 @@ impl GroupPanel {
.min_w_0() .min_w_0()
.flex_1() .flex_1()
.truncate() .truncate()
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.child(snippet), .child(snippet),
) )
.child( .child(
Button::new("cancel-reply") Button::new("cancel-reply")
.icon(IconName::Close) .icon(IconName::X)
.tooltip("Cancel reply") .tooltip("Cancel reply")
.ghost() .ghost()
.small() .small()
@@ -1171,10 +1169,10 @@ impl GroupPanel {
.h_8() .h_8()
.px_2() .px_2()
.border_1() .border_1()
.border_color(cx.theme().border_faint) .border_color(cx.theme().border)
.rounded(px(control_radius())) .rounded(px(control_radius()))
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child(Icon::new(IconName::Link).xsmall()) .child(Icon::new(IconName::Link).xsmall())
.child( .child(
div() div()
@@ -1184,7 +1182,7 @@ impl GroupPanel {
) )
.child( .child(
Button::new(SharedString::from(format!("remove-attachment-{ix}"))) Button::new(SharedString::from(format!("remove-attachment-{ix}")))
.icon(IconName::Close) .icon(IconName::X)
.tooltip("Remove") .tooltip("Remove")
.ghost() .ghost()
.xsmall() .xsmall()
@@ -1198,45 +1196,12 @@ impl GroupPanel {
} }
} }
impl Panel for GroupPanel { impl gpui_kit::component::dock::BasePanel for GroupPanel {
fn panel_id(&self) -> SharedString { fn panel_name(&self) -> &'static str {
self.id.clone() "GroupPanel"
} }
fn title(&self, cx: &App) -> AnyElement { fn set_active(&mut self, active: bool, window: &mut Window, cx: &mut Context<Self>) {
self.group
.upgrade()
.map(|group| {
let group = group.read(cx);
let seed = group.key().cache_tag();
let picture = group
.metadata()
.and_then(|metadata| metadata.picture.clone());
h_flex()
.gap_1()
.text_style(TextStyle::Caption)
.child(Avatar::from_source(picture).seed(seed).xsmall())
.child(display_name(group))
.into_any_element()
})
.unwrap_or_else(|| {
div()
.text_style(TextStyle::Caption)
.child("Unknown")
.into_any_element()
})
}
fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec<Button> {
Vec::new()
}
fn background(&self, cx: &App) -> Option<ImageSource> {
AppSettings::panel_background_source(cx)
}
fn set_active(&self, active: bool, window: &mut Window, cx: &mut App) {
let Some(group) = self.group.upgrade() else { let Some(group) = self.group.upgrade() else {
return; return;
}; };
@@ -1262,6 +1227,43 @@ impl Panel for GroupPanel {
} }
} }
impl Panel for GroupPanel {
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
self.group
.upgrade()
.map(|group| {
let group = group.read(cx);
let picture = group
.metadata()
.and_then(|metadata| metadata.picture.clone());
h_flex()
.gap_1()
.text_style(TextStyle::Caption)
.child(match picture {
Some(picture) => Avatar::new().src(picture).xsmall().into_any_element(),
None => Avatar::new().xsmall().into_any_element(),
})
.child(display_name(group))
.into_any_element()
})
.unwrap_or_else(|| {
div()
.text_style(TextStyle::Caption)
.child("Unknown")
.into_any_element()
})
}
fn toolbar_buttons(
&mut self,
_window: &mut Window,
_cx: &mut Context<Self>,
) -> Option<Vec<Button>> {
None
}
}
impl EventEmitter<PanelEvent> for GroupPanel {} impl EventEmitter<PanelEvent> for GroupPanel {}
impl Focusable for GroupPanel { impl Focusable for GroupPanel {
@@ -1295,7 +1297,7 @@ impl Render for GroupPanel {
.size_full() .size_full()
.justify_center() .justify_center()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.child("No messages yet") .child("No messages yet")
.into_any_element(), .into_any_element(),
}), }),
+38 -33
View File
@@ -1,20 +1,21 @@
use chat::Message; use chat::Message;
use chat_ui::message::{MessageMedia, MessageRow, ReactionBar, ReplyPreview, group_reactions};
use common::TimestampExt; use common::TimestampExt;
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
AnyElement, App, Entity, InteractiveElement, IntoElement, ParentElement, SharedString, Styled, AnyElement, App, Entity, InteractiveElement, IntoElement, ParentElement, SharedString, Styled,
WeakEntity, div, px, WeakEntity, div, px,
}; };
use gpui_kit::assets::IconName;
use gpui_kit::component::avatar::Avatar;
use gpui_kit::component::menu::{ContextMenuExt as _, PopupMenu, PopupMenuItem};
use gpui_kit::component::{ActiveTheme, Icon, Sizable, h_flex};
use nip29::{Activity, Group, Reaction}; use nip29::{Activity, Group, Reaction};
use nostr_sdk::prelude::{EventId, Kind}; use nostr_sdk::prelude::{EventId, Kind};
use person::Directory; use person::Directory;
use settings::AppSettings; use settings::AppSettings;
use state::NostrRegistry; use state::NostrRegistry;
use theme::{ActiveTheme, TextStyle, Typeset as _}; use ui::{TextStyle, Typeset as _};
use ui::avatar::Avatar;
use ui::menu::{ContextMenu, PopupMenu, PopupMenuItem};
use ui::message::{MessageMedia, MessageRow, ReactionBar, ReplyPreview, group_reactions};
use ui::{Icon, IconName, Sizable, StyledExt, h_flex};
use crate::GroupPanel; use crate::GroupPanel;
@@ -57,11 +58,13 @@ pub(crate) fn render(
let row = MessageRow::new(ix) let row = MessageRow::new(ix)
.show_author(show_author) .show_author(show_author)
.hide_avatar(hide_avatar) .hide_avatar(hide_avatar)
.avatar( .avatar(match author.avatar() {
Avatar::new(author.avatar()) Some(picture) => Avatar::new()
.seed(author.avatar_seed()) .src(picture)
.flex_shrink_0(), .flex_shrink_0()
) .into_any_element(),
None => Avatar::new().flex_shrink_0().into_any_element(),
})
.author(author.name()) .author(author.name())
.timestamp(message.created_at.to_human_time()) .timestamp(message.created_at.to_human_time())
.when(!message.replies_to.is_empty(), |this| { .when(!message.replies_to.is_empty(), |this| {
@@ -79,8 +82,7 @@ pub(crate) fn render(
}) })
.when(!reaction_groups.is_empty(), |this| { .when(!reaction_groups.is_empty(), |this| {
this.child(ReactionBar::new(reaction_groups)) this.child(ReactionBar::new(reaction_groups))
}) });
.into_any_element();
let id = message.id; let id = message.id;
let admin = actions.admin; let admin = actions.admin;
@@ -88,10 +90,11 @@ pub(crate) fn render(
let group = actions.group; let group = actions.group;
let panel = actions.panel; let panel = actions.panel;
ContextMenu::new( let row = div()
format!("message-menu-{}", id.to_hex()), .id(SharedString::from(format!("message-menu-{}", id.to_hex())))
row, .w_full()
move |menu, window, cx| { .child(row)
.context_menu(move |menu, window, cx| {
let menu = menu let menu = menu
.item({ .item({
let panel = panel.clone(); let panel = panel.clone();
@@ -136,9 +139,9 @@ pub(crate) fn render(
}); });
}) })
}) })
}, });
)
.into_any_element() row.into_any_element()
} }
fn react_menu( fn react_menu(
@@ -214,25 +217,27 @@ pub(crate) fn render_activity(
h_flex() h_flex()
.id(ix) .id(ix)
.w_full() .w_full()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.py_1() .py_1()
.px_3() .px_3()
.hover(|this| this.bg(cx.theme().element_hover)) .hover(|this| this.bg(cx.theme().primary))
.child(badge(activity, cx)) .child(badge(activity, cx))
.when(!hide_avatar, |this| { .when(!hide_avatar, |this| {
this.child( this.child(match subject.avatar() {
Avatar::new(subject.avatar()) Some(picture) => Avatar::new()
.seed(subject.avatar_seed()) .src(picture)
.small() .small()
.flex_shrink_0(), .flex_shrink_0()
) .into_any_element(),
None => Avatar::new().small().flex_shrink_0().into_any_element(),
})
}) })
.child( .child(
div() div()
.min_w_0() .min_w_0()
.truncate() .truncate()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.font_semibold() .font_weight(gpui::FontWeight::SEMIBOLD)
.child(subject.name()), .child(subject.name()),
) )
.child( .child(
@@ -240,7 +245,7 @@ pub(crate) fn render_activity(
.min_w_0() .min_w_0()
.truncate() .truncate()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.child(SharedString::from(action(activity))), .child(SharedString::from(action(activity))),
) )
.child( .child(
@@ -248,7 +253,7 @@ pub(crate) fn render_activity(
.flex_shrink_0() .flex_shrink_0()
.ml_1() .ml_1()
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.child(activity.created_at.to_ago()), .child(activity.created_at.to_ago()),
) )
.into_any_element() .into_any_element()
@@ -256,11 +261,11 @@ pub(crate) fn render_activity(
fn badge(activity: &Activity, cx: &App) -> AnyElement { fn badge(activity: &Activity, cx: &App) -> AnyElement {
let (icon, color) = match activity.kind { let (icon, color) = match activity.kind {
Kind::GroupJoinRequest => (IconName::Login, cx.theme().accent), Kind::GroupJoinRequest => (IconName::LogIn, cx.theme().primary),
Kind::GroupPutUser => (IconName::Login, cx.theme().accent), Kind::GroupPutUser => (IconName::LogIn, cx.theme().primary),
Kind::GroupLeaveRequest => (IconName::Logout, cx.theme().danger), Kind::GroupLeaveRequest => (IconName::LogOut, cx.theme().danger),
Kind::GroupRemoveUser => (IconName::Ban, cx.theme().danger), Kind::GroupRemoveUser => (IconName::Ban, cx.theme().danger),
_ => (IconName::Info, cx.theme().text_faint), _ => (IconName::Info, cx.theme().muted_foreground),
}; };
h_flex() h_flex()
+45 -20
View File
@@ -6,15 +6,16 @@ use gpui::{
AnyElement, App, AppContext, Entity, IntoElement, ParentElement, SharedString, Styled, AnyElement, App, AppContext, Entity, IntoElement, ParentElement, SharedString, Styled,
WeakEntity, Window, div, px, WeakEntity, Window, div, px,
}; };
use gpui_kit::component::button::{Button, ButtonVariants};
use gpui_kit::component::dialog::DialogButtonProps;
use gpui_kit::component::form::field as form_field;
use gpui_kit::component::input::InputState;
use gpui_kit::component::notification::Notification;
use gpui_kit::component::switch::Switch;
use gpui_kit::component::{ActiveTheme, Sizable, WindowExt, h_flex, v_flex};
use nip29::{Group, GroupId, GroupMetadata}; use nip29::{Group, GroupId, GroupMetadata};
use nostr_sdk::prelude::RelayUrl; use nostr_sdk::prelude::RelayUrl;
use theme::{ActiveTheme, TextStyle, Typeset as _}; use ui::{TextStyle, Typeset as _};
use ui::button::{Button, ButtonVariants};
use ui::dialog::DialogButtonProps;
use ui::input::InputState;
use ui::notification::Notification;
use ui::switch::Switch;
use ui::{Sizable, WindowExtension, h_flex, v_flex};
use crate::util::field; use crate::util::field;
@@ -99,13 +100,22 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
let current = *flags.read(cx); let current = *flags.read(cx);
let supported = *livekit.read(cx) != Some(false); let supported = *livekit.read(cx) != Some(false);
this.confirm() this.close_button(true)
.title("Edit Group") .title("Edit Group")
.button_props( .button_props(
DialogButtonProps::default() DialogButtonProps::default()
.cancel_text("Cancel") .cancel_text("Cancel")
.ok_text("Save"), .ok_text("Save"),
) )
.footer(
gpui_kit::component::dialog::DialogFooter::new().child(
gpui_kit::component::dialog::DialogAction::new().child(
gpui_kit::component::button::Button::new("save")
.label("Save")
.primary(),
),
),
)
.child( .child(
v_flex() v_flex()
.gap_3() .gap_3()
@@ -116,45 +126,59 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
.child(children_section(&children, cx)) .child(children_section(&children, cx))
.child( .child(
v_flex() v_flex()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.child( .child(
Switch::new("private") form_field()
.label("Private") .label("Private")
.description("Only members can read group messages") .description("Only members can read group messages")
.child(
Switch::new("private")
.checked(current.private) .checked(current.private)
.on_click(toggle(&flags, |flags| &mut flags.private)), .on_click(toggle(&flags, |flags| &mut flags.private)),
),
) )
.child( .child(
Switch::new("restricted") form_field()
.label("Restricted") .label("Restricted")
.description("Only members can post messages") .description("Only members can post messages")
.child(
Switch::new("restricted")
.checked(current.restricted) .checked(current.restricted)
.on_click(toggle(&flags, |flags| &mut flags.restricted)), .on_click(toggle(&flags, |flags| {
&mut flags.restricted
})),
),
) )
.child( .child(
Switch::new("hidden") form_field()
.label("Hidden") .label("Hidden")
.description("Hidden from non-members, not discoverable") .description("Hidden from non-members, not discoverable")
.child(
Switch::new("hidden")
.checked(current.hidden) .checked(current.hidden)
.on_click(toggle(&flags, |flags| &mut flags.hidden)), .on_click(toggle(&flags, |flags| &mut flags.hidden)),
),
) )
.child( .child(
Switch::new("closed") form_field()
.label("Closed") .label("Closed")
.description("Join requests are ignored (invite-only)") .description("Join requests are ignored (invite-only)")
.child(
Switch::new("closed")
.checked(current.closed) .checked(current.closed)
.on_click(toggle(&flags, |flags| &mut flags.closed)), .on_click(toggle(&flags, |flags| &mut flags.closed)),
),
) )
.child( .child(
Switch::new("livekit") form_field()
.label("LiveKit") .label("LiveKit")
.description(if supported { .description(if supported {
"Announce audio and video support." "Announce audio and video support."
} else { } else {
"The relay doesn't announce LiveKit support." "The relay doesn't announce LiveKit support."
}) })
.checked(current.livekit) .child(
.on_click({ Switch::new("livekit").checked(current.livekit).on_click({
let flags = flags.clone(); let flags = flags.clone();
let livekit = livekit.clone(); let livekit = livekit.clone();
@@ -170,6 +194,7 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
}), }),
), ),
), ),
),
) )
.on_ok({ .on_ok({
let group = group.clone(); let group = group.clone();
@@ -263,14 +288,14 @@ fn children_section(children: &Entity<Vec<GroupId>>, cx: &App) -> AnyElement {
let mut section = v_flex() let mut section = v_flex()
.gap_1() .gap_1()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child("Children"); .child("Children");
if list.is_empty() { if list.is_empty() {
return section return section
.child( .child(
div() div()
.text_color(cx.theme().text_faint) .text_color(cx.theme().muted_foreground)
.child("No subgroups"), .child("No subgroups"),
) )
.into_any_element(); .into_any_element();
@@ -295,7 +320,7 @@ fn children_section(children: &Entity<Vec<GroupId>>, cx: &App) -> AnyElement {
div() div()
.min_w_0() .min_w_0()
.truncate() .truncate()
.text_color(cx.theme().text) .text_color(cx.theme().foreground)
.child(SharedString::from(child.as_str().to_owned())), .child(SharedString::from(child.as_str().to_owned())),
) )
.child(order), .child(order),
+6 -6
View File
@@ -1,11 +1,11 @@
use anyhow::Result; use anyhow::Result;
use gpui::{App, Div, Entity, ParentElement, SharedString, Styled, Task, Window}; use gpui::{App, Div, Entity, ParentElement, SharedString, Styled, Task, Window};
use gpui_kit::component::input::{Input, InputState};
use gpui_kit::component::notification::Notification;
use gpui_kit::component::{ActiveTheme, WindowExt, v_flex};
use nip29::{Group, Pin, Row}; use nip29::{Group, Pin, Row};
use nostr_sdk::prelude::Url; use nostr_sdk::prelude::Url;
use theme::{ActiveTheme, TextStyle, Typeset as _}; use ui::{TextStyle, Typeset as _};
use ui::input::{Input, InputState};
use ui::notification::Notification;
use ui::{WindowExtension, v_flex};
pub(crate) fn display_name(group: &Group) -> SharedString { pub(crate) fn display_name(group: &Group) -> SharedString {
group group
@@ -36,7 +36,7 @@ pub(crate) fn opens_run(rows: &[Row], index: usize) -> bool {
}; };
match (current, previous) { match (current, previous) {
(Row::Message(current), Row::Message(previous)) => ui::message::opens_run( (Row::Message(current), Row::Message(previous)) => chat_ui::message::opens_run(
Some((previous.author, previous.created_at.as_secs())), Some((previous.author, previous.created_at.as_secs())),
(current.author, current.created_at.as_secs()), (current.author, current.created_at.as_secs()),
), ),
@@ -48,7 +48,7 @@ pub(crate) fn field(label: &'static str, input: &Entity<InputState>, cx: &App) -
v_flex() v_flex()
.gap_1() .gap_1()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child(label) .child(label)
.child(Input::new(input)) .child(Input::new(input))
} }
+1 -1
View File
@@ -8,7 +8,7 @@ publish.workspace = true
state = { path = "../state" } state = { path = "../state" }
person = { path = "../person" } person = { path = "../person" }
ui = { path = "../ui" } ui = { path = "../ui" }
theme = { path = "../theme" } gpui-kit.workspace = true
settings = { path = "../settings" } settings = { path = "../settings" }
gpui.workspace = true gpui.workspace = true
+26 -24
View File
@@ -1,3 +1,4 @@
use gpui_kit::component::{ActiveTheme, Disableable, Sizable, WindowExt, h_flex, v_flex};
use std::cell::Cell; use std::cell::Cell;
use std::collections::HashSet; use std::collections::HashSet;
use std::path::PathBuf; use std::path::PathBuf;
@@ -10,16 +11,15 @@ use gpui::{
App, AppContext, Context, Entity, EventEmitter, Global, IntoElement, ParentElement, App, AppContext, Context, Entity, EventEmitter, Global, IntoElement, ParentElement,
SharedString, Styled, Subscription, Task, Window, div, px, relative, SharedString, Styled, Subscription, Task, Window, div, px, relative,
}; };
use gpui_kit::component::avatar::Avatar;
use gpui_kit::component::button::Button;
use gpui_kit::component::notification::{Notification, NotificationType};
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use person::Directory; use person::Directory;
use settings::AppSettings; use settings::AppSettings;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use state::{Announcement, CLIENT_NAME, NostrRegistry, UniversalSigner}; use state::{Announcement, CLIENT_NAME, NostrRegistry, UniversalSigner};
use theme::{ActiveTheme, TextStyle, Typeset as _, control_radius}; use ui::{TextStyle, Typeset as _, control_radius};
use ui::avatar::Avatar;
use ui::button::Button;
use ui::notification::{Notification, NotificationKind};
use ui::{Disableable, Sizable, StyledExt, WindowExtension, h_flex, v_flex};
const IDENTIFIER: &str = "coop:device"; const IDENTIFIER: &str = "coop:device";
@@ -599,7 +599,6 @@ impl DeviceRegistry {
// Get user's write relays // Get user's write relays
let event = event.clone(); let event = event.clone();
let id: SharedString = event.id.to_hex().into();
let task: Task<Result<(), Error>> = cx.background_spawn(async move { let task: Task<Result<(), Error>> = cx.background_spawn(async move {
// Get device keys // Get device keys
@@ -645,7 +644,7 @@ impl DeviceRegistry {
})?; })?;
cx.update(|window, cx| { cx.update(|window, cx| {
window.clear_notification_by_id::<DeviceNotification>(id, cx); window.remove_notification::<DeviceNotification>(cx);
}) })
.ok(); .ok();
} }
@@ -718,18 +717,17 @@ impl DeviceRegistry {
let entity = cx.entity().downgrade(); let entity = cx.entity().downgrade();
let loading = Rc::new(Cell::new(false)); let loading = Rc::new(Cell::new(false));
let key = SharedString::from(event.id.to_hex());
Ok(Notification::new() Ok(Notification::new()
.type_id::<DeviceNotification>(key) .id::<DeviceNotification>()
.autohide(false) .autohide(false)
.with_kind(NotificationKind::Info) .with_type(NotificationType::Info)
.title("Encryption Key Request") .title("Encryption Key Request")
.content(move |_this, _window, cx| { .content(move |_this, _window, cx| {
let profile = directory.read(cx).person(&requester, cx); let profile = directory.read(cx).person(&requester, cx);
v_flex() v_flex()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.child( .child(
div() div()
@@ -739,16 +737,16 @@ impl DeviceRegistry {
) )
.child( .child(
v_flex() v_flex()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.child( .child(
v_flex() v_flex()
.gap_1() .gap_1()
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.child( .child(
div() div()
.font_semibold() .font_weight(gpui::FontWeight::SEMIBOLD)
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child(SharedString::from("From:")), .child(SharedString::from("From:")),
) )
.child( .child(
@@ -757,15 +755,19 @@ impl DeviceRegistry {
.w_full() .w_full()
.px_2() .px_2()
.rounded(px(control_radius())) .rounded(px(control_radius()))
.bg(cx.theme().surface_card) .bg(cx.theme().muted)
.child( .child(
h_flex() h_flex()
.gap(px(theme::SPACE)) .gap(px(ui::SPACE))
.child( .child(match profile.avatar() {
Avatar::new(profile.avatar()) Some(picture) => Avatar::new()
.seed(profile.avatar_seed()) .src(picture)
.xsmall(), .xsmall()
) .into_any_element(),
None => Avatar::new()
.xsmall()
.into_any_element(),
})
.child(profile.name()), .child(profile.name()),
), ),
), ),
@@ -776,9 +778,9 @@ impl DeviceRegistry {
.text_style(TextStyle::Callout) .text_style(TextStyle::Callout)
.child( .child(
div() div()
.font_semibold() .font_weight(gpui::FontWeight::SEMIBOLD)
.text_style(TextStyle::Caption) .text_style(TextStyle::Caption)
.text_color(cx.theme().text_muted) .text_color(cx.theme().muted_foreground)
.child(SharedString::from("Code:")), .child(SharedString::from("Code:")),
) )
.child( .child(
@@ -787,7 +789,7 @@ impl DeviceRegistry {
.w_full() .w_full()
.px_2() .px_2()
.rounded(px(control_radius())) .rounded(px(control_radius()))
.bg(cx.theme().surface_card) .bg(cx.theme().muted)
.child(code_label.clone()), .child(code_label.clone()),
), ),
), ),
+1 -1
View File
@@ -5,7 +5,7 @@ edition.workspace = true
publish.workspace = true publish.workspace = true
[dependencies] [dependencies]
theme = { path = "../theme" } gpui-kit.workspace = true
common = { path = "../common" } common = { path = "../common" }
nostr-sdk.workspace = true nostr-sdk.workspace = true
+32 -2
View File
@@ -6,7 +6,30 @@ use gpui::{App, AppContext, Context, Entity, Global, ImageSource, Subscription,
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use theme::{AppExt as _, AppearanceMode};
/// The user's appearance preference, persisted with the rest of the settings.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum AppearanceMode {
/// Follow the OS.
System,
Light,
/// Paint dark regardless of the OS.
#[default]
Dark,
}
impl AppearanceMode {
pub const ALL: [Self; 3] = [Self::System, Self::Light, Self::Dark];
/// Menu/label text.
pub fn label(self) -> &'static str {
match self {
Self::System => "System",
Self::Light => "Light",
Self::Dark => "Dark",
}
}
}
pub fn init(cx: &mut App) { pub fn init(cx: &mut App) {
AppSettings::set_global(cx.new(AppSettings::new), cx) AppSettings::set_global(cx.new(AppSettings::new), cx)
@@ -276,8 +299,15 @@ impl AppSettings {
/// Apply the persisted appearance mode to the app /// Apply the persisted appearance mode to the app
pub fn apply_theme(&mut self, cx: &mut Context<Self>) { pub fn apply_theme(&mut self, cx: &mut Context<Self>) {
use gpui_kit::component::Theme;
let mode = self.inner.read(cx).appearance_mode; let mode = self.inner.read(cx).appearance_mode;
cx.set_appearance_mode(mode); match mode {
AppearanceMode::System => Theme::sync_system_appearance(None, cx),
AppearanceMode::Light => Theme::change(gpui_kit::component::ThemeMode::Light, None, cx),
AppearanceMode::Dark => Theme::change(gpui_kit::component::ThemeMode::Dark, None, cx),
}
cx.refresh_windows();
} }
/// Check if decoupling encryption key is enabled /// Check if decoupling encryption key is enabled
-17
View File
@@ -1,17 +0,0 @@
[package]
name = "theme"
version.workspace = true
edition.workspace = true
publish.workspace = true
[dependencies]
gpui.workspace = true
gpui-base.workspace = true
anyhow.workspace = true
log.workspace = true
serde.workspace = true
serde_json.workspace = true
schemars.workspace = true
[dev-dependencies]
tempfile = "3.10"
-151
View File
@@ -1,151 +0,0 @@
//! Light/dark switching, adapted from bezel
//! (`crates/theme/src/appearance.rs`, MIT License, Copyright (c) 2026 bezel
//! contributors): what the user asked for, what the OS reports, and the
//! plumbing that turns a change in either into a repaint.
use gpui::{App, Global, Subscription, Window};
use serde::{Deserialize, Serialize};
use crate::{Theme, ThemeMode};
/// The user's appearance preference. Serde-serializable so callers can persist
/// it wherever their settings live; this crate never touches disk.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum AppearanceMode {
/// Follow the OS — matches every other native app on the machine,
/// including when the OS is set to switch at sunset.
System,
Light,
/// Paint dark regardless of the OS.
#[default]
Dark,
}
impl AppearanceMode {
pub const ALL: [Self; 3] = [Self::System, Self::Light, Self::Dark];
/// Menu/label text.
pub fn label(self) -> &'static str {
match self {
Self::System => "System",
Self::Light => "Light",
Self::Dark => "Dark",
}
}
}
/// Global state behind the current theme: what the user chose, and what the OS
/// last said. Kept separate from [`Theme`] itself so that flipping the OS
/// appearance while the user has pinned Light still records the new system
/// value (and takes effect the moment they switch back to `System`).
pub struct AppearanceState {
pub mode: AppearanceMode,
pub system: ThemeMode,
}
impl Global for AppearanceState {}
/// Combine the user's choice with the OS state.
pub fn resolve(mode: AppearanceMode, system: ThemeMode) -> ThemeMode {
match mode {
AppearanceMode::System => system,
AppearanceMode::Light => ThemeMode::Light,
AppearanceMode::Dark => ThemeMode::Dark,
}
}
/// Install the appearance globals and the matching theme. Call once at boot,
/// before any window opens, so the first frame is already the right palette
/// (installing later produces a visible dark-to-light flash).
pub fn init(mode: AppearanceMode, cx: &mut App) {
let system = ThemeMode::from_window(cx.window_appearance());
cx.set_global(AppearanceState { mode, system });
Theme::change(resolve(mode, system), None, cx);
}
/// The mode currently in effect (defaults to `Dark` before [`init`]).
pub fn mode(cx: &App) -> AppearanceMode {
cx.try_global::<AppearanceState>()
.map(|state| state.mode)
.unwrap_or_default()
}
/// Change the user's preference and repaint if that changed the palette.
/// Persisting the choice is the caller's job.
pub fn set_mode(mode: AppearanceMode, cx: &mut App) {
if !cx.has_global::<AppearanceState>() {
return;
}
let state = cx.global_mut::<AppearanceState>();
if state.mode == mode {
return;
}
state.mode = mode;
// Coming back to `System`, re-read the OS: a pinned mode holds an
// appearance override, so `system` may be stale from before the pin.
if mode == AppearanceMode::System {
let system = ThemeMode::from_window(cx.window_appearance());
cx.global_mut::<AppearanceState>().system = system;
}
apply(cx);
}
/// Subscribe a window to OS appearance changes. The returned [`Subscription`]
/// must outlive the window — callers typically `.detach()` it.
///
/// The notification is *per window*, but the appearance it reports is a system
/// setting, so any one window is enough to learn about the change; re-applying
/// is idempotent when several fire.
pub fn observe_window(window: &mut Window, cx: &mut App) -> Subscription {
// Reconcile against the *window's* appearance before subscribing: `init`
// runs before any window exists and can only ask the app, which on some
// platforms is not reliably populated that early in launch.
record_system(window.appearance().into(), cx);
window.observe_window_appearance(move |window, cx| {
record_system(window.appearance().into(), cx);
})
}
/// Record the OS appearance and re-apply if it moved. `window`, when given,
/// is the more authoritative source for what the OS currently reports.
pub fn record_system_appearance(window: Option<&Window>, cx: &mut App) {
let appearance = match window {
Some(window) => window.appearance(),
None => cx.window_appearance(),
};
record_system(ThemeMode::from_window(appearance), cx);
}
/// Record the OS appearance and re-apply if it moved. Only recorded while the
/// user's mode is `System` — a pinned mode ignores the OS anyway, and
/// recording an override read back would overwrite what the OS said with what
/// we asked for.
fn record_system(system: ThemeMode, cx: &mut App) {
if !cx.has_global::<AppearanceState>() {
return;
}
let state = cx.global_mut::<AppearanceState>();
if state.mode != AppearanceMode::System || state.system == system {
return;
}
state.system = system;
apply(cx);
}
/// Re-resolve the palette and, if it moved, swap the theme and force a full
/// repaint. A no-op when the resolved appearance is unchanged — the OS fires
/// the appearance notification for other changes too, and repainting every
/// window for those would be a visible hitch for nothing.
pub fn apply(cx: &mut App) {
let Some(state) = cx.try_global::<AppearanceState>() else {
return;
};
let wanted = resolve(state.mode, state.system);
let changed = !cx
.try_global::<Theme>()
.is_some_and(|theme| theme.mode == wanted);
if changed {
Theme::change(wanted, None, cx);
cx.refresh_windows();
}
}
-298
View File
@@ -1,298 +0,0 @@
//! Brand, ported from bezel (`crates/theme/src/brand.rs`, MIT License,
//! Copyright (c) 2026 bezel contributors): one hue for the greys, one for the
//! accent, one radius, and the text ink.
//!
//! A brand moves hue alone: every lightness in `palette.rs` stays as the
//! palette set it.
use gpui::{App, Global, Hsla};
use crate::{ThemeColors, ThemeMode, color, layout};
/// A hue and how much of it, in oklch terms. `chroma: 0.0` is the shipped
/// neutral, so [`Brand::default`] reproduces the built-in palette exactly.
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Tint {
/// oklch hue, in degrees.
pub hue: f32,
/// oklch chroma. Neutral ramps live near 0.01–0.05; an accent carries more.
pub chroma: f32,
}
impl Tint {
pub const NONE: Self = Self {
hue: 0.0,
chroma: 0.0,
};
pub const fn new(hue: f32, chroma: f32) -> Self {
Self { hue, chroma }
}
}
/// The greys a UI is built on, as oklch hue and chroma.
///
/// Tailwind's five neutral families at their 500 step (tailwindcss.com/docs/colors)
/// — the same list shadcn offers as its base colour, and the reason these are
/// quoted rather than invented: a neutral that carries hue is a judgement
/// someone else has already made five times.
pub const BASE_COLORS: [(&str, Tint); 5] = [
("Neutral", Tint::NONE),
("Stone", Tint::new(58.071, 0.013)),
("Zinc", Tint::new(285.938, 0.016)),
("Gray", Tint::new(264.364, 0.027)),
("Slate", Tint::new(257.417, 0.046)),
];
/// The alpha of each text role over the appearance's ink — white in dark,
/// black in light.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TextInk {
/// [`ThemeColors::text`].
pub text: f32,
/// [`ThemeColors::text_muted`].
pub muted: f32,
/// [`ThemeColors::text_faint`].
pub faint: f32,
}
impl TextInk {
/// The role at `alpha` over `appearance`'s ink.
pub fn color(appearance: ThemeMode, alpha: f32) -> Hsla {
match appearance {
ThemeMode::Dark => gpui::hsla(0.0, 0.0, 1.0, alpha),
ThemeMode::Light => gpui::hsla(0.0, 0.0, 0.0, alpha),
}
}
/// Writes [`ThemeColors::text`], [`ThemeColors::text_muted`] and
/// [`ThemeColors::text_faint`] for the appearance.
pub fn paint(&self, appearance: ThemeMode, colors: &mut ThemeColors) {
colors.text = Self::color(appearance, self.text);
colors.text_muted = Self::color(appearance, self.muted);
colors.text_faint = Self::color(appearance, self.faint);
}
}
/// Text ink for both appearances.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Ink {
pub dark: TextInk,
pub light: TextInk,
}
impl Ink {
/// Text and muted at 4.5:1 or more and faint at 3:1 or more, on
/// [`ThemeColors::bg`] and [`ThemeColors::surface`] in both appearances.
pub const ACCESSIBLE: Self = Self {
dark: TextInk {
text: 0.85,
muted: 0.52,
faint: 0.40,
},
light: TextInk {
text: 0.85,
muted: 0.56,
faint: 0.43,
},
};
/// AppKit's label, secondary and tertiary label colours. Muted is under
/// 4.5:1 on white, and faint is under 3:1 in both appearances.
pub const APPKIT: Self = Self {
dark: TextInk {
text: 0.85,
muted: 0.55,
faint: 0.25,
},
light: TextInk {
text: 0.85,
muted: 0.50,
faint: 0.26,
},
};
pub fn for_appearance(&self, appearance: ThemeMode) -> TextInk {
match appearance {
ThemeMode::Dark => self.dark,
ThemeMode::Light => self.light,
}
}
}
/// What an app changes about the shipped palette without redesigning it.
///
/// Installed as a gpui [`Global`] via [`crate::AppExt::set_brand`]; every
/// palette install applies it, so it survives a light/dark switch and composes
/// with [`crate::AppExt::set_palette`] rather than competing with it.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Brand {
/// The hue every grey in the palette carries.
pub tint: Tint,
/// The emphasis hue. Left neutral, the accent follows [`Self::tint`] like
/// any other grey — which is what the shipped palette already is.
pub accent: Tint,
/// The base corner radius; every other corner is a ratio of it. See
/// [`crate::BASE_RADIUS`].
pub radius: f32,
/// The text roles' alphas. [`Ink::APPKIT`] is what the palettes ship.
pub ink: Ink,
}
impl Global for Brand {}
impl Default for Brand {
fn default() -> Self {
Self {
tint: Tint::NONE,
accent: Tint::NONE,
radius: layout::BASE_RADIUS,
ink: Ink::APPKIT,
}
}
}
impl Brand {
/// Whether applying this brand would change nothing — the shipped palette
/// already carries these values, so a JSON theme's own text colors must be
/// left alone.
pub fn is_default(&self) -> bool {
*self == Self::default()
}
/// The shipped palette for an appearance, rotated onto this brand.
pub fn branded(&self, appearance: ThemeMode) -> ThemeColors {
let mut colors = ThemeColors::for_appearance(appearance);
// apply() re-asserts this brand's ink, so the default brand — whose
// ink is the shipped one — must skip it to stay a strict no-op.
if !self.is_default() {
self.apply(appearance, &mut colors);
}
colors
}
/// Rotate a palette onto this brand's hues.
pub fn apply(&self, appearance: ThemeMode, colors: &mut ThemeColors) {
self.ink
.for_appearance(appearance)
.paint(appearance, colors);
// Every colour token, with the rule doing the choosing: a token that is
// already grey takes the tint, and one that already carries a hue —
// danger, warning, success — is semantic and keeps it. Translucent ink
// is skipped because it paints over whatever is beneath it, which is
// tinted already.
let tokens: [&mut Hsla; 41] = [
&mut colors.bg,
&mut colors.surface,
&mut colors.surface_raised,
&mut colors.surface_card,
&mut colors.surface_dialog,
&mut colors.surface_overlay,
&mut colors.element_hover,
&mut colors.element_active,
&mut colors.border_faint,
&mut colors.border,
&mut colors.border_strong,
&mut colors.text,
&mut colors.text_muted,
&mut colors.text_faint,
&mut colors.text_dim,
&mut colors.solid,
&mut colors.on_solid,
&mut colors.accent,
&mut colors.accent_strong,
&mut colors.on_accent,
&mut colors.danger,
&mut colors.danger_muted,
&mut colors.warning,
&mut colors.warning_muted,
&mut colors.success,
&mut colors.busy,
&mut colors.success_muted,
&mut colors.surface_raised_hover,
&mut colors.band,
&mut colors.input_bg,
&mut colors.selection,
&mut colors.cursor,
&mut colors.caret,
&mut colors.ring,
&mut colors.drop_target,
&mut colors.danger_strong,
&mut colors.code_text,
&mut colors.code_wash,
&mut colors.diff_add,
&mut colors.diff_del,
&mut colors.diff_hunk_bg,
];
let syntax: [&mut Hsla; 24] = [
&mut colors.syntax.comment,
&mut colors.syntax.keyword,
&mut colors.syntax.string,
&mut colors.syntax.string_special,
&mut colors.syntax.escape,
&mut colors.syntax.number,
&mut colors.syntax.boolean,
&mut colors.syntax.type_name,
&mut colors.syntax.type_builtin,
&mut colors.syntax.constructor,
&mut colors.syntax.function,
&mut colors.syntax.function_builtin,
&mut colors.syntax.macro_name,
&mut colors.syntax.property,
&mut colors.syntax.constant,
&mut colors.syntax.variable,
&mut colors.syntax.variable_special,
&mut colors.syntax.parameter,
&mut colors.syntax.operator,
&mut colors.syntax.punctuation,
&mut colors.syntax.tag,
&mut colors.syntax.attribute,
&mut colors.syntax.label,
&mut colors.syntax.invalid,
];
for slot in tokens.into_iter().chain(syntax) {
if slot.a == 1.0 && slot.s <= f32::EPSILON {
*slot = color::tint(*slot, self.tint.hue, self.tint.chroma);
}
}
if self.accent.chroma > 0.0 {
let (accent_l, plate_l) = if appearance.is_light() {
(ACCENT_L.1, PLATE_L.1)
} else {
(ACCENT_L.0, PLATE_L.0)
};
colors.accent = color::oklch(accent_l, self.accent.chroma, self.accent.hue);
colors.accent_strong = color::oklch(plate_l, self.accent.chroma, self.accent.hue);
// Whichever label the plate can actually hold. The shipped accent is
// the maximum-contrast neutral, where the answer is always the
// inverse; a chromatic plate at a yellow hue is bright enough that
// the inverse would be the unreadable one.
colors.on_accent = label_on(colors.accent_strong, colors);
}
}
}
/// The accent's lightness in each appearance — indigo-400's and indigo-600's,
/// the two steps `palette.rs` picked so an accent clears WCAG AA on its own
/// background rather than glowing on one and vanishing on the other.
const ACCENT_L: (f32, f32) = (0.673, 0.511);
/// The lightness of a *plate* carrying [`ThemeColors::on_accent`], taken from
/// `danger_strong` — the palette's existing chromatic plate, already tuned to
/// hold a label in both appearances.
const PLATE_L: (f32, f32) = (0.58, 0.51);
/// Whichever of the palette's two extremes the plate can actually hold.
fn label_on(plate: Hsla, colors: &ThemeColors) -> Hsla {
let (a, b) = (colors.solid, colors.on_solid);
if color::contrast_ratio(plate, a) >= color::contrast_ratio(plate, b) {
a
} else {
b
}
}
/// Read the installed brand (the default before one is set).
pub fn installed(cx: &App) -> Brand {
cx.try_global::<Brand>().copied().unwrap_or_default()
}
-215
View File
@@ -1,215 +0,0 @@
//! Color math ported from bezel (`crates/theme/src/color.rs`, MIT License,
//! Copyright (c) 2026 bezel contributors): the oklch → sRGB → HSL converters
//! behind the token constructors, WCAG contrast for the palette tests, and
//! small paint helpers.
use gpui::{Hsla, hsla};
/// A neutral (chroma 0) oklch tone as Hsla. Chroma 0 means r == g == b exactly,
/// so this goes straight to an achromatic Hsla (skipping the hue math avoids
/// float-noise saturation).
pub fn neutral(lightness: f32) -> Hsla {
let [v, _, _] = oklch_to_srgb(lightness, 0.0, 0.0);
hsla(0.0, 0.0, v, 1.0)
}
/// An exact achromatic tone from an 8-bit channel value (`grey(13)` ≡ `#0d0d0d`)
/// — for surfaces matched against reference samples.
pub fn grey(value: u8) -> Hsla {
hsla(0.0, 0.0, value as f32 / 255.0, 1.0)
}
/// Convert an oklch color (CSS notation: L 0..1, C, H in degrees) to gpui Hsla.
pub fn oklch(l: f32, c: f32, h_deg: f32) -> Hsla {
let [r, g, b] = oklch_to_srgb(l, c, h_deg);
let (h, s, l) = rgb_to_hsl(r, g, b);
hsla(h, s, l, 1.0)
}
/// oklch → sRGB (each 0..1, clamped/gamut-clipped per channel).
/// Reference: Björn Ottosson's OKLab definition (the same matrices CSS Color 4 uses).
pub fn oklch_to_srgb(l: f32, c: f32, h_deg: f32) -> [f32; 3] {
let [r, g, b] = oklch_to_linear(l, c, h_deg);
[gamma_encode(r), gamma_encode(g), gamma_encode(b)]
}
/// oklch → *linear* sRGB, unclamped: a component outside 0..1 is a color the
/// display cannot make, which is what [`fit_chroma`] tests for.
fn oklch_to_linear(l: f32, c: f32, h_deg: f32) -> [f32; 3] {
let h = h_deg.to_radians();
let a = c * h.cos();
let b = c * h.sin();
// OKLab → LMS (cube roots undone)
let l_ = l + 0.396_337_78 * a + 0.215_803_76 * b;
let m_ = l - 0.105_561_346 * a - 0.063_854_17 * b;
let s_ = l - 0.089_484_18 * a - 1.291_485_5 * b;
let (l3, m3, s3) = (l_ * l_ * l_, m_ * m_ * m_, s_ * s_ * s_);
// LMS → linear sRGB
let r = 4.076_741_7 * l3 - 3.307_711_6 * m3 + 0.230_969_93 * s3;
let g = -1.268_438 * l3 + 2.609_757_4 * m3 - 0.341_319_4 * s3;
let b = -0.004_196_086_3 * l3 - 0.703_418_6 * m3 + 1.707_614_7 * s3;
[r, g, b]
}
fn gamma_encode(x: f32) -> f32 {
let x = x.clamp(0.0, 1.0);
if x <= 0.003_130_8 {
12.92 * x
} else {
1.055 * x.powf(1.0 / 2.4) - 0.055
}
}
fn gamma_decode(x: f32) -> f32 {
let x = x.clamp(0.0, 1.0);
if x <= 0.040_449_936 {
x / 12.92
} else {
((x + 0.055) / 1.055).powf(2.4)
}
}
/// The oklch lightness of an achromatic tone: [`neutral`] inverted.
///
/// Only greys round-trip through this. A grey's three channels are equal, and
/// the three LMS matrix rows each sum to 1, so the whole OKLab transform
/// collapses to one cube root — no inverse matrix needed.
pub fn lightness(color: Hsla) -> f32 {
gamma_decode(color.l).cbrt()
}
/// The most chroma sRGB can hold at this lightness and hue, up to `chroma`.
///
/// Near black and near white the gamut is a needle: asking for a mid-ramp
/// chroma there produces an out-of-range component, and clamping it per channel
/// shifts the hue instead of dropping the saturation.
fn fit_chroma(l: f32, chroma: f32, hue: f32) -> f32 {
let fits = |c: f32| {
oklch_to_linear(l, c, hue)
.iter()
.all(|x| (-1e-4..=1.0 + 1e-4).contains(x))
};
if fits(chroma) {
return chroma;
}
let (mut lo, mut hi) = (0.0, chroma);
for _ in 0..24 {
let mid = 0.5 * (lo + hi);
if fits(mid) { lo = mid } else { hi = mid }
}
lo
}
/// Re-emit an achromatic tone at `hue`, carrying as much `chroma` as its
/// lightness can hold. Alpha rides through untouched.
pub fn tint(color: Hsla, hue: f32, chroma: f32) -> Hsla {
if chroma <= 0.0 {
return color;
}
let l = lightness(color);
let mut out = oklch(l, fit_chroma(l, chroma, hue), hue);
out.a = color.a;
out
}
/// sRGB (0..1 components) → HSL, all components 0..1 (gpui's Hsla convention).
pub fn rgb_to_hsl(r: f32, g: f32, b: f32) -> (f32, f32, f32) {
let max = r.max(g).max(b);
let min = r.min(g).min(b);
let l = (max + min) / 2.0;
let delta = max - min;
if delta < f32::EPSILON {
return (0.0, 0.0, l);
}
let s = if l > 0.5 {
delta / (2.0 - max - min)
} else {
delta / (max + min)
};
let h = if (max - r).abs() < f32::EPSILON {
((g - b) / delta).rem_euclid(6.0)
} else if (max - g).abs() < f32::EPSILON {
(b - r) / delta + 2.0
} else {
(r - g) / delta + 4.0
} / 6.0;
(h, s, l)
}
/// HSL (gpui convention, all 0..1) → sRGB components 0..1.
pub fn hsl_to_rgb(h: f32, s: f32, l: f32) -> [f32; 3] {
if s <= f32::EPSILON {
return [l, l, l];
}
let q = if l < 0.5 {
l * (1.0 + s)
} else {
l + s - l * s
};
let p = 2.0 * l - q;
let hue = |mut t: f32| {
t = t.rem_euclid(1.0);
if t < 1.0 / 6.0 {
p + (q - p) * 6.0 * t
} else if t < 0.5 {
q
} else if t < 2.0 / 3.0 {
p + (q - p) * (2.0 / 3.0 - t) * 6.0
} else {
p
}
};
[hue(h + 1.0 / 3.0), hue(h), hue(h - 1.0 / 3.0)]
}
/// WCAG 2.1 relative luminance of an opaque color.
pub fn relative_luminance(color: Hsla) -> f32 {
let lin = |c: f32| {
if c <= 0.040_45 {
c / 12.92
} else {
((c + 0.055) / 1.055).powf(2.4)
}
};
let [r, g, b] = hsl_to_rgb(color.h, color.s, color.l);
0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b)
}
/// WCAG 2.1 contrast ratio between two opaque colors (1.0 … 21.0).
///
/// Used by the palette tests to prove each light token reproduces the contrast
/// its dark counterpart had, rather than merely looking plausible.
pub fn contrast_ratio(a: Hsla, b: Hsla) -> f32 {
let (la, lb) = (relative_luminance(a), relative_luminance(b));
let (hi, lo) = if la >= lb { (la, lb) } else { (lb, la) };
(hi + 0.05) / (lo + 0.05)
}
/// Composite `fg` (which may be translucent) over an opaque `bg`, returning the
/// opaque result — the color the eye actually receives.
pub fn flatten(fg: Hsla, bg: Hsla) -> Hsla {
let a = fg.a.clamp(0.0, 1.0);
let [fr, fg_, fb] = hsl_to_rgb(fg.h, fg.s, fg.l);
let [br, bg_, bb] = hsl_to_rgb(bg.h, bg.s, bg.l);
let (h, s, l) = rgb_to_hsl(
fr * a + br * (1.0 - a),
fg_ * a + bg_ * (1.0 - a),
fb * a + bb * (1.0 - a),
);
hsla(h, s, l, 1.0)
}
/// Linear per-component mix of two colors.
pub fn mix(a: Hsla, b: Hsla, t: f32) -> Hsla {
let t = t.clamp(0.0, 1.0);
let lerp = |x: f32, y: f32| x + (y - x) * t;
hsla(
lerp(a.h, b.h),
lerp(a.s, b.s),
lerp(a.l, b.l),
lerp(a.a, b.a),
)
}
-118
View File
@@ -1,118 +0,0 @@
use gpui::Hsla;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::syntax::SyntaxPalette;
use crate::{ThemeMode, palette};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
#[serde(default)]
pub struct ThemeColors {
/// The main content plane.
pub bg: Hsla,
/// The shell / sidebar plane.
pub surface: Hsla,
/// Opaque pills and chips raised above the panel.
pub surface_raised: Hsla,
/// An inline card resting on the main plane.
pub surface_card: Hsla,
/// A modal dialog, over the scrim.
pub surface_dialog: Hsla,
/// Popovers and menus — the highest plane.
pub surface_overlay: Hsla,
/// Row hover — translucent ink over whatever is beneath.
pub element_hover: Hsla,
/// Pressed or selected, one rung past [`ThemeColors::element_hover`].
pub element_active: Hsla,
/// The standard hairline border.
pub border: Hsla,
/// Dividers inside cards, nested frames.
pub border_faint: Hsla,
/// Focused or raised edges.
pub border_strong: Hsla,
/// The focus ring.
pub ring: Hsla,
/// Primary text.
pub text: Hsla,
/// Secondary text.
pub text_muted: Hsla,
/// Placeholders and disabled text.
pub text_faint: Hsla,
/// Diff file paths — one notch below [`ThemeColors::text_muted`].
pub text_dim: Hsla,
/// The text cursor.
pub cursor: Hsla,
/// The selected-text wash.
pub selection: Hsla,
/// The primary button plate — the maximum-contrast fill.
pub solid: Hsla,
/// Label ink on [`ThemeColors::solid`].
pub on_solid: Hsla,
/// Emphasis for text and icons; neutral by default.
pub accent: Hsla,
/// Accent fills carrying [`ThemeColors::on_accent`].
pub accent_strong: Hsla,
/// Label ink on [`ThemeColors::accent_strong`].
pub on_accent: Hsla,
/// Errors and destructive actions.
pub danger: Hsla,
/// Inline error copy.
pub danger_muted: Hsla,
/// The destructive plate — white ink in both appearances.
pub danger_strong: Hsla,
/// Warnings.
pub warning: Hsla,
/// The translucent warning wash behind chips and banners.
pub warning_muted: Hsla,
/// Success and online presence.
pub success: Hsla,
/// Text on success-tinted chips.
pub success_muted: Hsla,
/// Working or streaming states.
pub busy: Hsla,
/// Hover over [`ThemeColors::surface_raised`].
pub surface_raised_hover: Hsla,
/// A recessed header / footer strip.
pub band: Hsla,
/// Input backgrounds.
pub input_bg: Hsla,
/// The text caret.
pub caret: Hsla,
/// The line marking where a drag will land.
pub drop_line: Hsla,
/// The fill of the region a dragged pane would dock into.
pub drop_target: Hsla,
/// Inline-code text.
pub code_text: Hsla,
/// The wash behind [`ThemeColors::code_text`].
pub code_wash: Hsla,
/// The shared syntax palette, read at paint time.
pub syntax: SyntaxPalette,
/// Diff additions.
pub diff_add: Hsla,
/// Diff deletions.
pub diff_del: Hsla,
/// The diff hunk-header wash.
pub diff_hunk_bg: Hsla,
}
impl Default for ThemeColors {
fn default() -> Self {
palette::dark()
}
}
impl ThemeColors {
/// Returns the default colors for an appearance.
pub fn for_appearance(appearance: ThemeMode) -> Self {
palette::for_appearance(appearance)
}
}
-63
View File
@@ -1,63 +0,0 @@
use std::fmt::Debug;
use gpui::Axis;
use serde::{Deserialize, Serialize};
/// A enum for defining the side of the element.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Side {
#[serde(rename = "left")]
Left,
#[serde(rename = "right")]
Right,
}
impl Side {
/// Returns true if the side is left.
#[inline]
pub fn is_left(&self) -> bool {
matches!(self, Self::Left)
}
/// Returns true if the side is right.
#[inline]
pub fn is_right(&self) -> bool {
matches!(self, Self::Right)
}
}
/// A trait to extend the [`Axis`] enum with utility methods.
pub trait AxisExt {
#[allow(clippy::wrong_self_convention)]
fn is_horizontal(self) -> bool;
#[allow(clippy::wrong_self_convention)]
fn is_vertical(self) -> bool;
}
impl AxisExt for Axis {
#[inline]
fn is_horizontal(self) -> bool {
self == Axis::Horizontal
}
#[inline]
fn is_vertical(self) -> bool {
self == Axis::Vertical
}
}
/// A struct for defining the edges of an element.
///
/// A extend version of [`gpui::Edges`] to serialize/deserialize.
#[derive(Debug, Clone, Default, Serialize, Deserialize, Eq, PartialEq)]
#[repr(C)]
pub struct Edges<T: Clone + Debug + Default + PartialEq> {
/// The size of the top edge.
pub top: T,
/// The size of the right edge.
pub right: T,
/// The size of the bottom edge.
pub bottom: T,
/// The size of the left edge.
pub left: T,
}
-312
View File
@@ -1,312 +0,0 @@
use std::ops::{Deref, DerefMut};
use gpui::{App, Global, Pixels, SharedString, Window, px};
mod brand;
mod color;
mod colors;
mod geometry;
mod layout;
mod notification;
mod paint;
mod palette;
mod platform_kind;
mod scrollbar_mode;
mod syntax;
mod theme;
mod typography;
pub mod appearance;
pub use appearance::AppearanceMode;
pub use brand::{BASE_COLORS, Brand, Ink, TextInk, Tint};
pub use color::{
contrast_ratio, flatten, grey, hsl_to_rgb, lightness, mix, neutral, oklch, oklch_to_srgb,
relative_luminance, rgb_to_hsl, tint,
};
pub use colors::*;
pub use geometry::*;
pub use layout::{
BASE_RADIUS, BUTTON_HEIGHT, CONTENT_MARGIN, CONTROL_HEIGHT_SMALL, EDGE, SPACE, bubble_radius,
button_radius, control_radius, inset_radius, panel_radius, surface_radius,
};
pub use notification::*;
pub use paint::{
INK_FILL_SCALE, INK_HAIRLINE_SCALE, SCRIM_ALPHA_DARK, band, current_appearance, hairline, ink,
plate_active, plate_hover, scrim, set_current_appearance, surface_shadows, wash,
};
pub use platform_kind::PlatformKind;
pub use scrollbar_mode::*;
pub use syntax::{HighlightKind, SyntaxPalette};
pub use theme::*;
pub use typography::{
DEFAULT_BASE_TEXT_SIZE, Metrics, TextStyle, Typeset, base_text_size, set_base_text_size,
};
/// Which appearance the app is painting. Bezel names this enum `Appearance`;
/// coop's `ThemeMode` predates the port and carries the same two variants.
pub type Appearance = ThemeMode;
/// Defines window border radius for platforms that use client side decorations.
pub const CLIENT_SIDE_DECORATION_ROUNDING: Pixels = px(10.0);
/// Defines window shadow size for platforms that use client side decorations.
pub const CLIENT_SIDE_DECORATION_SHADOW: Pixels = px(10.0);
/// Defines window border size for platforms that use client side decorations.
pub const CLIENT_SIDE_DECORATION_BORDER: Pixels = px(1.0);
/// Defines window titlebar height
pub(crate) const TITLEBAR_HEIGHT: Pixels = px(36.0);
/// Defines workspace tabbar height
pub const TABBAR_HEIGHT: Pixels = px(36.0);
/// Defines default sidebar width
pub const SIDEBAR_WIDTH: Pixels = px(240.);
pub fn init(cx: &mut App) {
appearance::init(AppearanceMode::default(), cx);
Theme::sync_scrollbar_appearance(cx);
}
/// An alternative palette source.
pub type PaletteBuilder = fn(ThemeMode) -> ThemeColors;
struct GlobalPaletteBuilder(Option<PaletteBuilder>);
impl Global for GlobalPaletteBuilder {}
/// Install a brand or an alternative palette on the app.
pub trait AppExt {
fn set_brand(&mut self, brand: Brand);
fn set_palette(&mut self, build: PaletteBuilder);
fn set_appearance_mode(&mut self, mode: AppearanceMode);
fn set_base_text_size(&mut self, points: f32);
}
impl AppExt for App {
fn set_brand(&mut self, brand: Brand) {
self.set_global(brand);
reinstall(self);
}
fn set_palette(&mut self, build: PaletteBuilder) {
self.set_global(GlobalPaletteBuilder(Some(build)));
reinstall(self);
}
fn set_appearance_mode(&mut self, mode: AppearanceMode) {
appearance::set_mode(mode, self);
}
fn set_base_text_size(&mut self, points: f32) {
typography::set_base_text_size(points);
self.refresh_windows();
}
}
/// Re-install the palette for the current appearance.
fn reinstall(cx: &mut App) {
let mode = cx
.try_global::<Theme>()
.map(|theme| theme.mode)
.unwrap_or_default();
Theme::change(mode, None, cx);
cx.refresh_windows();
}
/// Mirror the active coop theme into the `gpui-base` global theme.
pub fn sync_base(cx: &mut App) {
let Some(theme) = cx.try_global::<Theme>() else {
return;
};
let appearance = if theme.mode.is_dark() {
gpui_base::ThemeAppearance::Dark
} else {
gpui_base::ThemeAppearance::Light
};
let scrollbar_mode = match theme.scrollbar_mode {
ScrollbarMode::Scrolling => gpui_base::ScrollbarMode::Scrolling,
ScrollbarMode::Hover => gpui_base::ScrollbarMode::Hover,
ScrollbarMode::Always => gpui_base::ScrollbarMode::Always,
};
let colors = theme.colors;
let font_sans = theme.font_sans.clone();
let base = gpui_base::Theme::global_mut(cx);
base.appearance = appearance;
base.scrollbar = base.scrollbar.clone().with_mode(scrollbar_mode);
base.tokens.typography.sans = font_sans;
let tokens = &mut base.tokens.colors;
tokens.background = colors.bg;
tokens.foreground = colors.text;
tokens.surface = colors.surface;
tokens.surface_foreground = colors.text;
tokens.primary = colors.solid;
tokens.primary_foreground = colors.on_solid;
tokens.secondary = colors.surface_raised;
tokens.secondary_foreground = colors.text;
tokens.muted = colors.surface_card;
tokens.muted_foreground = colors.text_muted;
tokens.accent = colors.element_hover;
tokens.accent_foreground = colors.text;
tokens.destructive = colors.danger_strong;
tokens.destructive_foreground = gpui::white();
tokens.border = colors.border;
tokens.input = colors.border;
tokens.ring = colors.ring;
tokens.selection = colors.selection;
}
pub trait ActiveTheme {
fn theme(&self) -> &Theme;
}
impl ActiveTheme for App {
#[inline(always)]
fn theme(&self) -> &Theme {
Theme::global(self)
}
}
#[derive(Debug, Clone)]
pub struct Theme {
pub colors: ThemeColors,
pub mode: ThemeMode,
pub font_sans: SharedString,
pub font_mono: SharedString,
pub shadow: bool,
pub scrollbar_mode: ScrollbarMode,
pub notification: NotificationSettings,
}
impl Deref for Theme {
type Target = ThemeColors;
fn deref(&self) -> &Self::Target {
&self.colors
}
}
impl DerefMut for Theme {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.colors
}
}
impl Global for Theme {}
impl Theme {
/// Returns the global theme reference
pub fn global(cx: &App) -> &Theme {
cx.global::<Theme>()
}
/// Returns the global theme mutable reference
pub(crate) fn global_mut(cx: &mut App) -> &mut Theme {
cx.global_mut::<Theme>()
}
/// Returns true if the theme is dark.
pub fn is_dark(&self) -> bool {
self.mode.is_dark()
}
/// Sync the theme with the system appearance
pub fn sync_system_appearance(window: Option<&mut Window>, cx: &mut App) {
appearance::record_system_appearance(window.as_deref(), cx);
}
/// Sync the Scrollbar showing behavior with the system
pub fn sync_scrollbar_appearance(cx: &mut App) {
Theme::global_mut(cx).scrollbar_mode = if cx.should_auto_hide_scrollbars() {
ScrollbarMode::Scrolling
} else {
ScrollbarMode::Hover
};
}
/// Change the app's appearance
pub fn change<M>(mode: M, window: Option<&mut Window>, cx: &mut App)
where
M: Into<ThemeMode>,
{
if !cx.has_global::<Theme>() {
cx.set_global(Theme::default());
}
let mode = mode.into();
let builder = cx
.try_global::<GlobalPaletteBuilder>()
.and_then(|builder| builder.0);
let mut colors = match builder {
Some(build) => build(mode),
None => ThemeColors::for_appearance(mode),
};
// A default brand changes nothing; anything else rotates the tokens.
let installed_brand = brand::installed(cx);
if !installed_brand.is_default() {
installed_brand.apply(mode, &mut colors);
}
let theme = Theme::global_mut(cx);
// Set the theme mode
theme.mode = mode;
// Set the theme colors
theme.colors = colors;
// Refresh the window if available
if let Some(window) = window {
window.refresh();
}
// Keep the context-free paint helpers and the radius accessors in step
paint::set_current_appearance(mode);
layout::set_base_radius(installed_brand.radius);
// Keep the base-layer projection in step with the coop palette
sync_base(cx);
}
}
impl Default for Theme {
fn default() -> Self {
let platform = PlatformKind::platform();
let mode = ThemeMode::default();
// The system UI font, by platform.
let font_sans = match platform {
PlatformKind::Linux => "Inter",
_ => ".SystemUIFont",
};
Self {
font_sans: font_sans.into(),
font_mono: system_mono().into(),
shadow: true,
scrollbar_mode: ScrollbarMode::default(),
notification: NotificationSettings::default(),
mode,
colors: ThemeColors::for_appearance(mode),
}
}
}
/// The mono face has no alias of its own, so each backend is named here.
fn system_mono() -> &'static str {
if cfg!(target_family = "wasm") {
".ZedMono"
} else if cfg!(target_os = "macos") {
".AppleSystemUIFontMonospaced"
} else if cfg!(target_os = "windows") {
"Cascadia Mono"
} else {
"DejaVu Sans Mono"
}
}
-30
View File
@@ -1,30 +0,0 @@
use gpui::{Anchor, Pixels, px};
use crate::{Edges, TITLEBAR_HEIGHT};
/// The settings for notifications.
#[derive(Debug, Clone)]
pub struct NotificationSettings {
/// The placement of the notification, default: [`Anchor::TopRight`]
pub placement: Anchor,
/// The margins of the notification with respect to the window edges.
pub margins: Edges<Pixels>,
/// The maximum number of notifications to show at once, default: 10
pub max_items: usize,
}
impl Default for NotificationSettings {
fn default() -> Self {
let offset = px(16.);
Self {
placement: Anchor::TopRight,
margins: Edges {
top: TITLEBAR_HEIGHT + offset, // avoid overlap with title bar
right: offset,
bottom: offset,
left: offset,
},
max_items: 10,
}
}
}
-191
View File
@@ -1,191 +0,0 @@
//! Context-free paint helpers, ported from bezel
//! (`crates/theme/src/paint.rs`, MIT License, Copyright (c) 2026 bezel
//! contributors): the process-wide appearance mirror and the free functions
//! ([`ink`], [`hairline`], [`wash`], …) that element builders call without a
//! `cx` in scope.
use std::sync::atomic::{AtomicU8, Ordering};
use gpui::{BoxShadow, Hsla, hsla, point, px};
use crate::ThemeMode;
/// Process-wide mirror of the installed theme's appearance.
///
/// The paint helpers are free functions called from deep inside element
/// builders that have no `cx` in scope, so they read the appearance from here
/// instead of the gpui global. Appearance is genuinely process-wide — one
/// setting for every window — so a single mirror is sound; [`Theme::change`]
/// is the only writer outside tests.
static CURRENT_APPEARANCE: AtomicU8 = AtomicU8::new(0);
/// The appearance the context-free paint helpers are painting for.
pub fn current_appearance() -> ThemeMode {
match CURRENT_APPEARANCE.load(Ordering::Relaxed) {
1 => ThemeMode::Light,
_ => ThemeMode::Dark,
}
}
/// Point the context-free paint helpers at an appearance. Called by
/// [`Theme::change`]; exposed for tests that build a palette without an `App`.
pub fn set_current_appearance(appearance: ThemeMode) {
let encoded = match appearance {
ThemeMode::Dark => 0,
ThemeMode::Light => 1,
};
CURRENT_APPEARANCE.store(encoded, Ordering::Relaxed);
}
/// Light-mode alpha multiplier for **fills** (hover/active washes, chip and pill
/// backgrounds).
///
/// The established light-UI scales (Primer, Radix) land subtle ≈ 3–4%, hover ≈ 8%,
/// selected ≈ 14% black — which is where the dark palette's white alphas already
/// sit. So the honest multiplier is 1: the same number in both appearances, with
/// only the *tone* flipping. Any per-state correction belongs in that state's
/// token, not in a blanket multiplier.
pub const INK_FILL_SCALE: f32 = 1.0;
/// Light-mode alpha multiplier for **hairlines** (borders, dividers, rings).
/// Opposite of fills: a 1px edge has to hold its own against a bright surround,
/// and the dark palette's white hairlines are deliberately faint. Scaling up
/// keeps separators legible instead of dissolving into the panel.
pub const INK_HAIRLINE_SCALE: f32 = 1.35;
/// Lightness of [`hairline`] ink, dark then light.
const HAIRLINE_TONE: (f32, f32) = (0.75, 0.25);
/// Translucent **fill** ink for interactive states and chip plates: soft-white on
/// dark, soft-black on light at [`INK_FILL_SCALE`] of the alpha.
///
/// Alphas are quoted in *dark-mode terms* at every call site — the dark theme is
/// the tuned one — and the light value is derived. Callers keep one number and
/// both appearances stay in the relationship the dark tuning established.
pub fn ink(alpha: f32) -> Hsla {
ink_for(current_appearance(), alpha)
}
pub(crate) fn ink_for(appearance: ThemeMode, alpha: f32) -> Hsla {
match appearance {
ThemeMode::Dark => hsla(0.0, 0.0, 1.0, alpha),
ThemeMode::Light => hsla(0.0, 0.0, 0.0, alpha * INK_FILL_SCALE),
}
}
/// Translucent **hairline** ink for borders, dividers and rings: a light grey on
/// dark, a dark grey on light at [`INK_HAIRLINE_SCALE`] of the alpha. Never
/// pure white or black.
///
/// Separate from [`ink`] because edges and fills scale in opposite directions
/// when the field brightens — a 1px line needs *more* ink on white, a plate needs
/// less.
pub fn hairline(alpha: f32) -> Hsla {
hairline_for(current_appearance(), alpha)
}
pub(crate) fn hairline_for(appearance: ThemeMode, alpha: f32) -> Hsla {
match appearance {
ThemeMode::Dark => hsla(0.0, 0.0, HAIRLINE_TONE.0, alpha),
ThemeMode::Light => hsla(
0.0,
0.0,
HAIRLINE_TONE.1,
(alpha * INK_HAIRLINE_SCALE).min(0.5),
),
}
}
/// Interactive-state wash: a softened [`ink`] that stops short of pure black or
/// white so hover plates read as tinted glass rather than paint.
pub fn wash(alpha: f32) -> Hsla {
wash_for(current_appearance(), alpha)
}
pub(crate) fn wash_for(appearance: ThemeMode, alpha: f32) -> Hsla {
match appearance {
ThemeMode::Dark => hsla(0.0, 0.0, 0.92, alpha),
ThemeMode::Light => hsla(0.0, 0.0, 0.10, alpha * INK_FILL_SCALE),
}
}
/// Alpha of the standard modal backdrop in dark mode. Call sites that need a
/// heavier or lighter scrim pass their own dark-mode alpha to [`scrim`].
pub const SCRIM_ALPHA_DARK: f32 = 0.60;
/// Modal backdrop at `alpha_dark` (quoted, as everywhere, in dark-mode terms).
///
/// Black in both appearances — a scrim's job is to darken what is behind it, and
/// a "light scrim" of white would wash the modal out rather than seat it. What
/// changes is strength: on a bright field a dark-mode-weight scrim reads as a
/// blackout, so light mode scales to roughly half.
pub fn scrim(alpha_dark: f32) -> Hsla {
scrim_for(current_appearance(), alpha_dark)
}
pub(crate) fn scrim_for(appearance: ThemeMode, alpha_dark: f32) -> Hsla {
match appearance {
ThemeMode::Dark => hsla(0.0, 0.0, 0.0, alpha_dark),
ThemeMode::Light => hsla(0.0, 0.0, 0.0, 0.32 * (alpha_dark / SCRIM_ALPHA_DARK)),
}
}
/// Recessed band behind a picker header or footer strip.
///
/// A free function as well as a token so the chrome that paints it can be built
/// from context-free helpers; both resolve to the same value.
pub fn band() -> Hsla {
band_for(current_appearance())
}
pub(crate) fn band_for(appearance: ThemeMode) -> Hsla {
match appearance {
ThemeMode::Dark => hsla(0.0, 0.0, 0.0, 0.16),
// A recessed strip on white needs far less ink than on near-black; the
// dark 16% would read as a bruise.
ThemeMode::Light => hsla(0.0, 0.0, 0.0, 0.045),
}
}
/// The elevation shadow a floating surface casts. Painted cut to outside the
/// shape, because the same drop shadow under a translucent fill reads as a grey
/// plate.
pub fn surface_shadows() -> Vec<BoxShadow> {
vec![
BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.1),
offset: point(px(0.0), px(10.0)),
blur_radius: px(15.0),
spread_radius: px(-3.0),
inset: false,
},
BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.1),
offset: point(px(0.0), px(4.0)),
blur_radius: px(6.0),
spread_radius: px(-4.0),
inset: false,
},
]
}
/// Hover over an opaque plate: brighten the plate in dark mode, darken it in
/// light — never swap an opaque plate for a translucent wash.
pub(crate) fn plate_hover_for(appearance: ThemeMode, plate: gpui::Hsla) -> gpui::Hsla {
crate::color::flatten(ink_for(appearance, 0.10), plate)
}
/// Pressed over an opaque plate, one rung above [`plate_hover_for`].
pub(crate) fn plate_active_for(appearance: ThemeMode, plate: gpui::Hsla) -> gpui::Hsla {
crate::color::flatten(ink_for(appearance, 0.16), plate)
}
/// Hover over an opaque plate, for call sites outside this crate.
pub fn plate_hover(plate: gpui::Hsla) -> gpui::Hsla {
plate_hover_for(current_appearance(), plate)
}
/// Pressed over an opaque plate, for call sites outside this crate.
pub fn plate_active(plate: gpui::Hsla) -> gpui::Hsla {
plate_active_for(current_appearance(), plate)
}
-137
View File
@@ -1,137 +0,0 @@
use gpui::{hsla, rgb};
use crate::paint::{band_for, hairline_for};
use crate::syntax::SyntaxPalette;
use crate::{Ink, TextInk, ThemeColors, ThemeMode, color};
pub(crate) fn dark() -> ThemeColors {
let warning = color::oklch(0.828, 0.189, 84.429);
ThemeColors {
bg: color::grey(6),
surface: color::grey(13),
surface_raised: color::neutral(0.235),
surface_card: color::grey(0x0e),
surface_dialog: color::grey(0x10),
surface_overlay: color::grey(0x16),
element_hover: hsla(0.0, 0.0, 1.0, 0.08),
element_active: hsla(0.0, 0.0, 1.0, 0.12),
border_faint: hairline_for(ThemeMode::Dark, 0.06),
border: hairline_for(ThemeMode::Dark, 0.08),
border_strong: hairline_for(ThemeMode::Dark, 0.14),
text: TextInk::color(ThemeMode::Dark, Ink::APPKIT.dark.text),
text_muted: TextInk::color(ThemeMode::Dark, Ink::APPKIT.dark.muted),
text_faint: TextInk::color(ThemeMode::Dark, Ink::APPKIT.dark.faint),
text_dim: color::grey(0x98),
// The restored Radix yellow brand: #ffe629 plate, #14120b label.
solid: color::oklch(0.918, 0.184, 100.9),
on_solid: color::grey(0x0e),
// The restored Radix yellow brand: #f5e147 ink, #ffe629 plate.
accent: color::oklch(0.900, 0.166, 101.7),
accent_strong: color::oklch(0.918, 0.184, 100.9),
on_accent: color::grey(0x0e),
danger: color::oklch(0.704, 0.191, 22.216),
danger_muted: color::oklch(0.808, 0.114, 19.571),
danger_strong: color::oklch(0.58, 0.16, 25.0),
success: color::oklch(0.765, 0.177, 163.223),
success_muted: color::oklch(0.845, 0.143, 164.978),
busy: color::oklch(0.718, 0.202, 349.761),
surface_raised_hover: color::neutral(0.29),
band: band_for(ThemeMode::Dark),
input_bg: hsla(0.0, 0.0, 1.0, 0.03),
selection: rgb(0x3f638b).into(),
cursor: color::neutral(0.94),
caret: color::neutral(0.673),
ring: hairline_for(ThemeMode::Dark, 0.35),
drop_line: hairline_for(ThemeMode::Dark, 0.35),
drop_target: hsla(0.0, 0.0, 1.0, 0.10),
code_text: color::neutral(0.94),
code_wash: hsla(0.0, 0.0, 1.0, 0.08),
syntax: SyntaxPalette::dark(
color::neutral(0.922),
color::neutral(0.60),
color::oklch(0.704, 0.191, 22.216),
),
diff_add: color::oklch(0.765, 0.177, 163.223),
diff_del: color::oklch(0.704, 0.191, 22.216),
diff_hunk_bg: hsla(0.6, 0.35, 0.6, 0.05),
warning,
warning_muted: warning.alpha(0.16),
}
}
pub(crate) fn light() -> ThemeColors {
let warning = color::oklch(0.555, 0.163, 48.998);
ThemeColors {
bg: color::grey(0xff),
surface: color::neutral(0.968),
surface_raised: color::neutral(0.940),
surface_card: color::grey(0xff),
surface_dialog: color::grey(0xff),
surface_overlay: color::grey(0xff),
element_hover: hsla(0.0, 0.0, 0.0, 0.04),
element_active: hsla(0.0, 0.0, 0.0, 0.06),
border_faint: hairline_for(ThemeMode::Light, 0.06),
border: hairline_for(ThemeMode::Light, 0.08),
border_strong: hairline_for(ThemeMode::Light, 0.14),
text: TextInk::color(ThemeMode::Light, Ink::APPKIT.light.text),
text_muted: color::neutral(0.45),
text_faint: TextInk::color(ThemeMode::Light, Ink::APPKIT.light.faint),
text_dim: color::neutral(0.50),
// The restored Radix yellow brand: #ffe629 plate, #473b1f label.
solid: color::oklch(0.918, 0.184, 100.9),
on_solid: color::oklch(0.358, 0.046, 86.9),
// The restored Radix yellow brand: #9e6c00 ink, #ffe629 plate.
accent: color::oklch(0.569, 0.119, 76.8),
accent_strong: color::oklch(0.918, 0.184, 100.9),
on_accent: color::neutral(0.205),
danger: color::oklch(0.577, 0.245, 27.325),
danger_muted: color::oklch(0.505, 0.213, 27.518),
danger_strong: color::oklch(0.51, 0.20, 25.0),
warning,
warning_muted: warning.alpha(0.10),
success: color::oklch(0.596, 0.145, 163.225),
success_muted: color::oklch(0.508, 0.118, 165.612),
busy: color::oklch(0.592, 0.249, 0.584),
surface_raised_hover: color::neutral(0.900),
band: band_for(ThemeMode::Light),
input_bg: color::neutral(0.950),
selection: rgb(0xb3d7ff).into(),
cursor: color::neutral(0.205),
caret: color::neutral(0.511),
ring: hairline_for(ThemeMode::Light, 0.35),
drop_line: hairline_for(ThemeMode::Light, 0.35),
drop_target: hsla(0.0, 0.0, 0.0, 0.06),
code_text: color::neutral(0.18),
code_wash: hsla(0.0, 0.0, 0.0, 0.06),
syntax: SyntaxPalette::light(
color::neutral(0.25),
color::neutral(0.48),
color::oklch(0.505, 0.213, 27.518),
),
diff_add: color::oklch(0.596, 0.145, 163.225),
diff_del: color::oklch(0.577, 0.245, 27.325),
diff_hunk_bg: hsla(0.6, 0.35, 0.35, 0.07),
}
}
pub(crate) fn for_appearance(appearance: ThemeMode) -> ThemeColors {
match appearance {
ThemeMode::Dark => dark(),
ThemeMode::Light => light(),
}
}
-21
View File
@@ -1,21 +0,0 @@
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub enum PlatformKind {
Mac,
Linux,
Windows,
Web,
}
impl PlatformKind {
pub const fn platform() -> Self {
if cfg!(any(target_os = "linux", target_os = "freebsd")) {
Self::Linux
} else if cfg!(target_os = "windows") {
Self::Windows
} else if cfg!(target_os = "macos") {
Self::Mac
} else {
Self::Web
}
}
}

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