62 Commits
Author SHA1 Message Date
reya c1757bbcae chore: fix web build 2026-11-04 13:05:03 +07:00
reya 74d548e5c6 nip4e: catch up psstpsst (#70)
Reviewed-on: #70
2026-11-04 03:12:52 +00:00
reya 690dc37fc5 chore: fix web build 2026-11-01 16:40:49 +07:00
reya 2f2661ecd2 chore: improve native theme usage 2026-11-01 16:15:28 +07:00
reya d045a7ef3a chore: update the unlock dialog 2026-10-30 20:17:37 +07:00
reya 23d7017f25 feat: add text to speech (#69)
Reviewed-on: #69
2026-10-30 07:16:46 +00:00
reya af2a276b1b chore: update deps 2026-10-28 10:08:47 +07:00
reya 69ca199a7d chore: drop macos x64 from release builds 2026-10-28 09:09:18 +07:00
reya 83a440d893 chore: release version 2.0.0-alpha3 2026-10-28 05:14:03 +07:00
reya 77b2a5f3cd feat: add support for speech to text (#68)
Reviewed-on: #68
2026-10-27 06:25:19 +00:00
reya b0121f8df2 chore: simplify import dialog & sidebar menu 2026-10-26 19:59:48 +07:00
reya bfa34959cf feat: add deploy to cloudflare script 2026-10-26 10:16:14 +07:00
reya 4d0e2f5050 feat: add syncing progress 2026-10-26 09:54:38 +07:00
reya 0824b1825b feat: make the web version fully working (#67)
Reviewed-on: #67
2026-10-25 13:10:51 +00:00
reya 3212951ed4 chore: release version 2.0.0-alpha2 2026-10-24 16:09:59 +07:00
reya ceb69660be chore: improve wot 2026-10-24 11:39:22 +07:00
reya 8f35ae92b7 feat: add user-configurable theme settings 2026-10-24 09:15:12 +07:00
reya 30da985ac1 feat: adopt the native OS theme (#66)
Reviewed-on: #66
2026-10-23 04:07:11 +00:00
reya f44f0704f3 feat: add support for basic web of trust (#65)
Reviewed-on: #65
2026-10-20 13:43:37 +00:00
reya a22aecc694 chore: add public group notice 2026-10-20 08:14:11 +07:00
reya f5afc772b2 chore: improve clarity about public group 2026-10-20 08:13:39 +07:00
reya 03f98f5fa8 fix: align message timestamps in greeter panel 2026-10-19 20:55:37 +07:00
reya deed021332 feat: new greeter panel 2026-10-17 17:04:50 +07:00
reya aa86fef16f chore: release version 2.0.0-alpha 2026-10-15 12:46:25 +07:00
reya bbc69c9861 chore: refactor the login flow 2026-10-13 11:09:37 +07:00
reya a249326c28 feat: add configurable panel background image 2026-10-13 10:55:58 +07:00
reya 61289edb9a chore: update deps and clean up 2026-10-13 08:10:05 +07:00
reya a365a8f584 feat: bind user metadata to per-view directories (#64)
Reviewed-on: #64
2026-10-12 11:14:03 +00:00
reya 7c42466b0a chore: convert NIP-21 references to njump link 2026-10-12 11:02:15 +07:00
reya 291471075b feat: add support for custom emojis (NIP-30) (#63)
Reviewed-on: #63
2026-10-11 13:22:53 +00:00
reya b4ce9368e2 chore: extract shared message components 2026-10-11 15:45:13 +07:00
reya 05f934c93a chore: improve message reply in group chat 2026-10-11 15:02:32 +07:00
reya f8032b2074 chore: update new group dialog 2026-10-11 10:59:12 +07:00
reya f1192492d2 chore: track previous references and preserve foreign tags 2026-10-11 09:02:06 +07:00
reya 440d6dfea3 feat: add invite management, replies and subgroup support 2026-10-11 08:51:03 +07:00
reya 4c279a0686 feat: add group reaction reaction and deletion 2026-10-11 07:57:04 +07:00
reya f83922df2e feat: add relay group creation (#62)
Reviewed-on: #62
2026-10-10 14:11:18 +00:00
reya 4b6f4b3797 chore: refactor middle-click paste 2026-10-10 08:10:57 +07:00
reya 602a91b4f0 feat: add new theme (#61)
Reviewed-on: #61
2026-10-10 00:30:42 +00:00
vbmithrandreya 4ef764984b feat: support middle-click paste in text fields (#60)
Selecting an nsec in another Linux application and middle-clicking Coop's identity import field currently does nothing. The shared input widget handles left and right clicks but never reads the primary selection for a middle click.

Add middle-click paste from PRIMARY on Linux and FreeBSD, inserting at the nearest visible caret to the click. Read the selection before changing focus, preserve disabled/read-only inputs, strip line breaks in single-line fields, and use the input state's atomic insertion path so Undo removes the paste. Also register Shift+Insert for standard clipboard paste on non-macOS platforms. The existing right-click editing menu and Ctrl+V/Cmd+V remain available.

Document the gestures in the README. Enable GPUI test support for the UI crate; the lockfile additions are its test dependencies.

Validation:

- `cargo check -p ui --locked`
- `cargo test -p ui --lib --locked` — all five tests pass, including empty-field paste, Unicode caret placement, primary/clipboard separation, Undo, and disabled/read-only protection
- `git -c core.whitespace=cr-at-eol diff --check` (preserves the existing CRLF Cargo manifest)

Validated with GPUI's simulated input and clipboard platform; no live X11/Wayland compositor test was performed.

Release Notes:

- Fixed middle-click paste in Linux text fields, including identity import, and added Shift+Insert clipboard paste.

Reviewed-on: #60
Co-authored-by: Vincent Bernardoff <vb@luminar.eu.org>
2026-10-10 00:28:07 +00:00
reya 193df36dae feat: add group management to the sidebar 2026-10-08 20:58:58 +07:00
reya 09fb58c5a4 chore: refactor dialogs to render custom footer buttons 2026-10-08 16:27:39 +07:00
reya fff0f6a753 feat: add group discovery and group preview (#59)
Reviewed-on: #59
2026-10-08 00:18:23 +00:00
reya 33785f183d chore: bump gpui-pre to 0.3.8 and clean up unused code 2026-10-06 21:04:20 +07:00
reya 3ada21b728 chore: replace relay panels with relays dialog 2026-10-06 16:40:27 +07:00
reya e1eff61856 chore: move profile panel into the profile dialog 2026-10-06 15:39:09 +07:00
reya 2bce050c3b feat: new search experience (#58)
Reviewed-on: #58
2026-10-06 03:00:21 +00:00
reya 2a404591d8 chore: move "new" menu into sidebar header 2026-10-05 08:10:21 +07:00
reya c544dd4e36 feat: add pinned rooms and groups to the sidebar 2026-10-05 07:19:31 +07:00
reya acb07126c9 feat: replace sidebar tabs with a single collapsible list 2026-10-04 16:19:37 +07:00
reya 2fc1358530 nip4e: fix message encryption and sync (#57)
Reviewed-on: #57
2026-10-04 06:33:32 +00:00
reya e14b815404 chore: replace custom markdown rendering with gpui TextView 2026-10-03 20:41:09 +07:00
reya 88d3840fd2 chore: restrict ui crate visibility and drop unused components 2026-10-03 19:57:56 +07:00
reya 1f350f511e feat: add support for NIP-29 (#56)
Reviewed-on: #56
2026-10-01 04:23:18 +00:00
reya 21f96c7bbe chore: simplify chat send reports (#55)
Reviewed-on: #55
2026-09-29 21:53:50 +00:00
reya be15873e0d feat: add contact-based chat creation and re-add chat screening (#54)
Reviewed-on: #54
2026-09-29 06:28:44 +00:00
reya e4e13b0889 feat: redesign the sidebar and workspace (#53)
Reviewed-on: #53
2026-09-25 02:26:31 +00:00
reya 1032ac3751 feat: add community ui (#52)
Reviewed-on: #52
2026-09-23 01:12:51 +00:00
reya 6ff1eeddbb fix: message list overflow 2026-09-17 19:15:16 +07:00
reya 0c1c23fe9c chore: migrate to gpui-base (#51)
Reviewed-on: #51
2026-09-17 11:50:14 +00:00
reya 874f103752 chore: restructure concord crate (#50)
Reviewed-on: #50
2026-09-17 06:16:01 +00:00
reya fdabfcf026 feat: implement concord protocol (#49)
Only backend, no UI yet.

Reviewed-on: #49
2026-09-17 04:30:12 +00:00
384 changed files with 28373 additions and 36263 deletions
-3
View File
@@ -20,9 +20,6 @@ jobs:
- platform: windows-arm64
os: windows-11-arm
target: aarch64-pc-windows-msvc
- platform: macos-x64
os: macos-15-intel
target: x86_64-apple-darwin
- platform: macos-arm64
os: macos-latest
target: aarch64-apple-darwin
+23
View File
@@ -30,3 +30,26 @@ jobs:
- name: Run tests
run: cargo test --verbose
check-wasm:
name: Check web target
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly-2026-10-31
targets: wasm32-unknown-unknown
components: llvm-tools-preview
- name: Install wasm C toolchain
run: sudo apt-get update && sudo apt-get install -y clang llvm
- name: Check web target
env:
CC_wasm32_unknown_unknown: clang
AR_wasm32_unknown_unknown: llvm-ar
run: cargo +nightly-2026-10-31 check -p coop_web --target wasm32-unknown-unknown
+1
View File
@@ -24,3 +24,4 @@ dist/
vendor/
wasm/
node_modules/
.wrangler/
+5
View File
@@ -2,7 +2,10 @@
* Prioritize code correctness and clarity. Speed and efficiency are secondary priorities unless otherwise specified.
* Do not write organizational or comments that summarize the code. Comments should only be written in order to explain "why" the code is written in some way in the case there is a reason that is tricky / non-obvious.
* Keep doc comments and comments simple, one line, straightforward, and unbloated.
* Prefer implementing functionality in existing files unless it is a new logical component. Avoid creating many small files.
* Prefer `impl` blocks over standalone functions: place functionality as methods or associated functions on the relevant type, extending the type's existing `impl` block instead of creating a duplicate one in another file.
* Bind a global store handle to a local before using it (`let state = GlobalState::global(cx);` then `state.update(...)`) instead of chaining on `GlobalState::global(cx)` inline.
* Avoid using functions that panic like `unwrap()`, instead use mechanisms like `?` to propagate errors.
* Be careful with operations like indexing which may panic if the indexes are out of bounds.
* Never silently discard errors with `let _ =` on fallible operations. Always handle errors appropriately:
@@ -81,6 +84,8 @@ Both `cx.spawn` and `cx.background_spawn` return a `Task<R>`, which is a future
A task which doesn't do anything but provide a value can be created with `Task::ready(value)`.
Prefer keeping a task in a field over `.detach()` when the work belongs to a view or store. A detached task outlives the view that started it (for example, a repository panel the user has closed), while a task stored in a field is cancelled when that struct drops. A finished `Task` held in a container is not reaped by GPUI - it stays alive until its handle is dropped - so if the container can accumulate many runs, either drop the finished handles before pushing a new one, or hold a single `Option<Task<_>>` and replace it.
## Elements
The `Render` trait is used to render some state into an element tree that is laid out using flexbox layout. An `Entity<T>` where `T` implements `Render` is sometimes called a "view".
Generated
+3217 -1350
View File
File diff suppressed because it is too large Load Diff
+78 -68
View File
@@ -1,68 +1,78 @@
[workspace]
resolver = "2"
members = ["crates/*", "desktop", "web"]
default-members = ["desktop"]
[workspace.package]
version = "1.0.2"
edition = "2024"
publish = false
[workspace.dependencies]
# GPUI
gpui = { git = "https://github.com/zed-industries/zed" }
gpui_platform = { git = "https://github.com/zed-industries/zed", features = ["font-kit", "x11", "wayland"] }
gpui_linux = { git = "https://github.com/zed-industries/zed" }
gpui_windows = { git = "https://github.com/zed-industries/zed" }
gpui_macos = { git = "https://github.com/zed-industries/zed" }
gpui_tokio = { git = "https://github.com/zed-industries/zed" }
reqwest_client = { git = "https://github.com/zed-industries/zed" }
# Nostr
nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" }
nostr-memory = { git = "https://github.com/rust-nostr/nostr" }
nostr-blossom = { git = "https://github.com/rust-nostr/nostr" }
nostr-gossip-memory = { git = "https://github.com/rust-nostr/nostr" }
nostr-connect = { git = "https://github.com/rust-nostr/nostr" }
nostr-sdk = { git = "https://github.com/rust-nostr/nostr" }
nostr = { git = "https://github.com/rust-nostr/nostr", features = [ "nip59", "nip49", "nip44" ] }
# Crypto (NIP-17 encrypted file messages)
aes-gcm = "0.10"
sha2 = "0.10"
data-encoding = "2"
# Others
anyhow = "1.0.44"
chrono = { version = "0.4.38", features = ["wasmbind"] }
futures = "0.3"
itertools = "0.13.0"
log = "0.4"
oneshot = "0.1.10"
flume = { version = "0.11.1", default-features = false, features = ["async", "select"] }
rust-embed = { version = "8.5", features = ["include-exclude"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
schemars = "1"
smallvec = "1.14.0"
smol = "2"
webbrowser = "1.0.4"
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
errno = { version = "0.3.14", default-features = false }
instant = "0.1"
ureq = { version = "3", default-features = false, features = ["rustls", "platform-verifier", "json"] }
[patch.crates-io]
# Use stacker's psm version which may have better WASM support
psm = { git = "https://github.com/rust-lang/stacker", branch = "master" }
[profile.release]
strip = true
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
[profile.profiling]
inherits = "release"
debug = true
[workspace]
resolver = "2"
members = ["crates/*", "desktop", "web"]
default-members = ["desktop"]
[workspace.package]
version = "2.0.0-alpha3"
edition = "2024"
publish = false
[workspace.dependencies]
gpui = { package = "gpui-pre", version = "0.3.10" }
gpui_platform = { package = "gpui-pre-platform", version = "0.3.10", features = ["font-kit", "x11", "wayland"] }
gpui_linux = { package = "gpui-pre-linux", version = "0.3.10" }
gpui_windows = { package = "gpui-pre-windows", version = "0.3.10" }
gpui_macos = { package = "gpui-pre-macos", version = "0.3.10" }
gpui_web = { package = "gpui-pre-web", version = "0.3.10" }
gpui_util = { package = "gpui-pre-util", version = "0.3.10" }
reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.10" }
gpui_tokio = { path = "crates/gpui_tokio" }
gpui-base = "0.8.1"
gpui-kit = "0.8.1"
native-theme = { version = "0.7", features = ["native", "watch"] }
native-theme-gpui = { version = "0.7", default-features = false, features = ["system-fonts"] }
# Nostr
nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" }
nostr-memory = { git = "https://github.com/rust-nostr/nostr" }
nostr-blossom = { git = "https://github.com/rust-nostr/nostr" }
nostr-gossip-memory = { git = "https://github.com/rust-nostr/nostr" }
nostr-connect = { git = "https://github.com/rust-nostr/nostr" }
nostr-browser-signer = { git = "https://github.com/rust-nostr/nostr" }
nostr-sdk = { git = "https://github.com/rust-nostr/nostr" }
nostr-sqlite = { git = "https://github.com/rust-nostr/nostr" }
nostr = { git = "https://github.com/rust-nostr/nostr", features = [ "nip59", "nip49", "nip44", "nip98" ] }
# Crypto (NIP-17 encrypted file messages)
aes-gcm = "0.10"
base64 = "0.22"
sha2 = "0.10"
data-encoding = "2"
hkdf = "0.12"
# Pinned to the instances `nostr` already builds: the NIP-44 message-key disclosure
chacha20 = "0.9"
hmac = "0.12"
# Pinned to the instance `nostr-sdk` already builds, so NIP-44 nonces share it
rand = { version = "0.10", default-features = false, features = [ "std", "sys_rng", "thread_rng" ] }
# Others
anyhow = "1.0.44"
tts = { git = "https://github.com/ndarilek/tts-rs" }
chrono = { version = "0.4.38", features = ["wasmbind"] }
futures = "0.3"
itertools = "0.13.0"
log = "0.4"
flume = { version = "0.11.1", default-features = false, features = ["async", "select"] }
rust-embed = { version = "8.5", features = ["include-exclude"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
schemars = "1"
smallvec = "1.14.0"
smol = "2"
webbrowser = "1.0.4"
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
instant = "0.1"
[profile.release]
strip = true
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
[profile.profiling]
inherits = "release"
debug = true
-560
View File
@@ -1,560 +0,0 @@
# Concord support — backend & API plan
Concord is an encrypted community/channel protocol over Nostr: shared-key "Private Streams" (CORD-01), communities with a self-certifying owner id and three planes (CORD-02), public/private channels (CORD-03), an owner-rooted signed roster (CORD-04), invites (CORD-05), rekeys/refoundings (CORD-06) and disappearing messages (CORD-08).
Scope of this plan: **backend + public Rust API**. No views, no widgets, no copy.
## 1. Scope
**In scope**
- New `crates/concord` crate: derivations, stream codec, control/chat/guestbook planes, authority fold, ephemeral state, invites, rekeys, dissolution, storage, sync engine.
- Public API for a future UI: `ConcordRegistry` + `Entity<Community>` / `Entity<Channel>` + events, mirroring the shape of `crates/chat`.
- The minimum surgical edits to existing crates required for coexistence (see §11).
**Out of scope**
- Any UI work.
- CORD-07 audio/video. Reserve `23313`, the `concord/voice-*` labels and the `voice` metadata flag so nothing else claims them, and implement nothing.
- Pins (CORD-04 §7) ship in the last milestone; the design accounts for `vsk 11` early so the fold is not retrofitted.
- Cross-client interop testing (Vector/Armada/Grimoire). Tracked as follow-up work, not blocking.
## 2. Sources of truth
| Doc | What we take from it |
| --- | --- |
| CORD-01 | Stream event shape, seal forms 20013/20014, encoding rules, binding, deletions |
| CORD-02 | `community_id`, `community_root`, `control_root`, epochs, 3 planes, metadata, invites, Community List, dissolution |
| CORD-03 | Channel keying, metadata, message kinds, `channel`/`epoch` binding, threads vs quotes |
| CORD-04 | Editions, `vac`, the roster, permission bits, banlist, the three removals, pins |
| CORD-05 | Bundle, link (naddr + fragment), relay dictionary, Invite List, Registry, Direct Invite |
| CORD-06 | Rekey blobs, chunking, `prevcommit` continuity, Refounding, compaction, races |
| CORD-08 | `message_expiration`, NIP-40 tagging, ingest/purge enforcement, timer notice 1740 |
Appendix A (derivations) and Appendix B (kinds) of CORD-02 are **frozen**: every labeled byte and every kind number is part of the wire format. Treat both as constants with golden-vector tests.
Reference implementations for cross-checking behaviour (not for copying code): Vector (`crates/vector-core/src/community/v2/*`), Armada, Grimoire.
## 3. Reuse map — nostr-sdk APIs we build on
Verified against the pinned revision (`nostr` 0.45.4 / `nostr-sdk` 0.45.2, git `b230cec`).
| Concord need | Existing API |
| --- | --- |
| NIP-44 under a raw conversation key | `nostr::nips::nip44::v2::{ConversationKey, encrypt_to_bytes_with_nonce, decrypt_to_bytes}` |
| NIP-44 conversation key for a keypair | `ConversationKey::derive(&SecretKey, &PublicKey)` (self-ECDH for streams) |
| NIP-44 under a signer | `nip44::{encrypt, decrypt}` (already wrapped by `state::UniversalSigner`) |
| Event id recomputation | `EventId::compute(pubkey, created_at, kind, tags, content)`, `UnsignedEvent::compute_id` |
| Event (de)serialization | `Event::{from_json, as_json, verify}`, `UnsignedEvent::from_json` |
| Building events | `EventBuilder::new(..).tags(..).custom_created_at(..)`, `FinalizeEvent::finalize(&Keys)` |
| Tags | `Tag::{custom, identifier, public_key, expiration}`, `Tags`, `SingleLetterTag` |
| Kinds | `Kind::GiftWrap` (1059), `Kind::Custom(21059|20013|20014|3308|…)`, `Kind::is_ephemeral` |
| Publish | `Client::send_event(&event).to(relays).ack_policy(AckPolicy::none())` |
| Subscribe | `Client::subscribe(target).with_id(..).close_on(..)`, `SubscribeAutoCloseOptions`, `ReqExitPolicy` |
| Backfill | `Client::fetch_events(target)`, `Client::stream_events(target)` |
| Local persistence | `Client::database()` → `query(Filter)`, `save_event(&Event)` |
| Invite link parsing | `Nip19::from_bech32` → `Nip19::Coordinate(Nip19Coordinate)` |
| Signer abstraction | `state::UniversalSigner` (`AsyncSignEvent` + `AsyncNip44`) |
| Relay auth | `nostr_sdk::{Authenticator, SignerAuthenticator}` |
**Not needed.** `secp256k1` (use `nostr::SecretKey::from_slice` + `Keys::new`), `base64` (use `data_encoding::BASE64`, already a workspace dep), `bech32` (NIP-19 is in the SDK), any new storage engine (the client's LMDB database is enough), any new HTTP client.
**Add one dependency:** `hkdf = "0.12"` (already in `Cargo.lock` transitively). Add it to `[workspace.dependencies]` and to the new crate. `sha2` is already a workspace dep.
## 4. Crate layout
New crate `crates/concord`, picked up automatically by the `crates/*` workspace member glob.
```
crates/concord/
Cargo.toml
src/lib.rs init, ConcordRegistry, ConcordEvent, signal bus, subscriptions, ingest pipeline
src/derive.rs frozen HKDF / group_key / locators / commitments + golden vectors
src/stream.rs CORD-01: seal + wrap + open (SealForm, OpenedStream), channel/epoch binding
src/edition.rs CORD-04 §1: canonical signing bytes, edition hash, parse, fold
src/control.rs control plane view, genesis, content types, roster fold, authority checks
src/guestbook.rs CORD-02 §5: join/leave/kick/snapshot, coalesce, complete memberlist
src/chat.rs CORD-03: channel plane — message/edit/delete/reaction builders + message view
src/invite.rs CORD-05: bundle, link, registry, Invite List, Direct Invite
src/rekey.rs CORD-06: blob codec, continuity, refounding, compaction, dissolution
src/store.rs local persistence + opened-rumor cache + history queries
```
`Community` and `Channel` GPUI entities live in `src/lib.rs` next to the registry — they are the public surface, not a separate concern. Ten modules, each with real content; no single-fn files.
Dependencies: `common`, `state`, `person`, `device`, `settings`, `gpui`, `nostr` (for `nip44` features), `nostr-sdk`, `hkdf`, `sha2`, `serde`, `serde_json`, `anyhow`, `flume`, `smallvec`, `itertools`, `futures`, `log`, `instant`. Everything under `cfg(not(target_arch = "wasm32"))` follows the `chat`/`state` split so the crate still builds for web.
## 5. Core types
```rust
pub struct CommunityId([u8; 32]); // sha256 commitment, never on the wire
pub struct ChannelId([u8; 32]);
pub struct Epoch(pub u64);
/// A derived stream: signing keypair + the self-ECDH conversation key that
/// encrypts the wraps. Memoised in a bounded process-wide cache.
pub struct GroupKey { keys: Keys, conversation: ConversationKey }
impl GroupKey {
pub fn pk(&self) -> PublicKey;
pub fn keys(&self) -> &Keys;
pub fn conversation(&self) -> &ConversationKey;
}
pub enum SealForm { Encrypted, Plaintext }
pub struct OpenedStream {
pub rumor_id: EventId,
pub author: PublicKey, // the seal's verified pubkey
pub seal_form: SealForm,
pub seal: Event, // retained: compaction re-wraps plaintext seals verbatim
pub wrapper_id: EventId,
pub at_ms: u64, // created_at * 1000 + ms tag
pub rumor: UnsignedEvent,
}
```
Ordering everywhere uses `at_ms`, never `created_at`, and ties break on the lower inner rumor id.
## 6. Frozen derivations (`derive.rs`)
```rust
fn build_info(label: &str, id: &[u8; 32], epoch: Option<u64>) -> Vec<u8>; // label ‖ 0x00 ‖ id[32] ‖ epoch_be[8]?
fn hkdf32(ikm: &[u8], info: &[u8]) -> [u8; 32]; // HKDF-SHA256, zero-length salt, L = 32
fn hkdf_to_secret_key(ikm: &[u8], info: &[u8]) -> SecretKey; // A.3 scalar_normalize, counter from 0
fn group_key(label: &str, secret: &[u8], id: &[u8; 32], epoch: Option<u64>) -> GroupKey;
pub fn channel_group_key(secret: &[u8; 32], channel: &ChannelId, epoch: Epoch) -> GroupKey;
pub fn control_group_key(root: &[u8; 32], id: &CommunityId, epoch: Epoch) -> GroupKey; // read key
pub fn control_signer_group_key(root: &[u8; 32], id: &CommunityId, epoch: Epoch) -> GroupKey; // write key
pub fn guestbook_group_key(root: &[u8; 32], id: &CommunityId, epoch: Epoch) -> GroupKey;
pub fn channel_rekey_group_key(root: &[u8; 32], channel: &ChannelId, epoch: Epoch) -> GroupKey;
pub fn base_rekey_group_key(root: &[u8; 32], id: &CommunityId, epoch: Epoch) -> GroupKey;
pub fn dissolved_group_key(id: &CommunityId) -> GroupKey; // no epoch field
pub fn community_id_of(owner_xonly: &[u8; 32], owner_salt: &[u8; 32]) -> CommunityId; // plain SHA-256
pub fn epoch_key_commitment(epoch: Epoch, key: &[u8; 32]) -> [u8; 32]; // plain SHA-256
pub fn grant_locator(id: &CommunityId, member: &[u8; 32]) -> [u8; 32];
pub fn banlist_locator(id: &CommunityId) -> [u8; 32];
pub fn pins_locator(id: &CommunityId, channel: &ChannelId) -> [u8; 32];
pub fn invite_links_locator(id: &CommunityId, creator: &[u8; 32]) -> [u8; 32];
pub fn recipient_locator(rotator: &[u8; 32], recipient: &[u8; 32], scope: &[u8; 32], epoch: Epoch) -> [u8; 32];
pub fn invite_bundle_key(token: &[u8; 16]) -> [u8; 32]; // raw hkdf32 output; used as a NIP-44 conversation key
```
Rules that must be enforced by construction, not by convention:
- Hex is lowercase everywhere; pubkeys are x-only hex, never bech32; tag numbers are decimal with no leading zeros (`"4"`, never `04`/`+4`).
- The epoch field is *omitted*, not zeroed, for labels with no epoch (`concord/dissolved`, locators, `concord/community`).
- `scalar_normalize` retries by appending a counter byte to the same `info`, starting at `0`.
- Labels and commitments are append-only. A test asserts every label is unique and that the label table matches Appendix A.6 exactly.
**Golden vectors.** `derive.rs` carries a `#[cfg(test)]` block pinning every derivation output, seeded from the independent Python vectors published by the Vector implementation (channel/control/control-signer/guestbook at epoch 0 and at `0x0102030405060708`, dissolved, all four locators, invite key, community id, epoch commitment). One vector is missing upstream — `pins_locator` — so we mint it from our own implementation and pin it, flagged in the test as self-referential. Changing any pinned value means the wire format changed.
## 7. Stream codec (`stream.rs`)
```rust
pub const KIND_WRAP: u16 = 1059;
pub const KIND_WRAP_EPHEMERAL: u16 = 21059;
pub const KIND_SEAL_ENCRYPTED: u16 = 20013;
pub const KIND_SEAL_PLAINTEXT: u16 = 20014;
pub const NIP44_MAX_PLAINTEXT: usize = 65_535;
pub fn seal_content(rumor: &UnsignedEvent, form: SealForm, group: &GroupKey) -> Result<String, StreamError>;
pub fn build_seal(rumor: &UnsignedEvent, form: SealForm, group: &GroupKey, author: &Keys) -> Result<Event, StreamError>;
pub fn wrap_seal(seal: &Event, group: &GroupKey, wrap_kind: u16, at: Timestamp, extra: &[Tag]) -> Result<(Event, Keys), StreamError>;
pub fn open_wrap(wrap: &Event, group: &GroupKey) -> Result<OpenedStream, StreamError>;
pub fn open_wrap_at(wrap: &Event, address: &PublicKey, conversation: &ConversationKey, verify_wrap_sig: bool) -> Result<OpenedStream, StreamError>;
pub fn channel_binding_tags(channel: &ChannelId, epoch: Epoch) -> Vec<Tag>;
pub fn check_channel_binding(rumor: &UnsignedEvent, channel: &ChannelId, epoch: Epoch) -> Result<(), StreamError>;
pub fn build_rumor(kind: u16, author: PublicKey, content: &str, tags: Vec<Tag>, at_ms: u64) -> UnsignedEvent; // appends ["ms", n]
pub fn resolve_ms_strict(rumor: &UnsignedEvent) -> Result<u64, StreamError>;
```
Design points that are easy to get wrong:
- The wrap is signed by the **stream key** with a random ephemeral `p` tag — NIP-59 reversed. `extra` is how the caller mirrors a NIP-40 expiration onto the wrap.
- The seal is signed by the **real author** and carries `created_at` equal to the rumor's. It is never published bare.
- Control plane **must** use the plaintext seal; chat, guestbook and rekey planes **must** use the encrypted one. Each plane asserts its own form at both ends.
- The control plane is a write-restricted stream: the wrap key derives from `control_root` while the content is encrypted under the `community_root`-derived conversation key. `open_wrap_at` takes the two halves separately for this reason.
- Open order: kind → address match → wrap signature (only when `verify_wrap_sig`) → NIP-44 open → seal kind → seal signature → rumor parse → `rumor.pubkey == seal.pubkey` → recompute the rumor id and reject a mismatch → strict `ms` resolve.
- Enforce the 65 535-byte NIP-44 cap at every nesting layer before publishing.
- Do not verify the wrap signature for ordinary planes: every reader holds the group key, so the signature proves nothing. It matters only for the restricted control plane and for rekeys.
- The ephemeral wrap keypair is returned to the caller so a client may NIP-09-scrub its own wrap later.
## 8. Planes, state and folds
### 8.1 Editions and authority (`edition.rs`, `control.rs`)
```rust
pub const EDITION_LABEL: &[u8] = b"vector-community/v1/edition"; // frozen, cross-client
pub fn edition_hash(entity: &[u8; 32], version: u64, prev: Option<&[u8; 32]>, content: &[u8]) -> [u8; 32];
pub struct ParsedEdition { author: PublicKey, vsk: String, entity: [u8; 32], version: u64,
prev: Option<[u8; 32]>, content: String, self_hash: [u8; 32] };
pub fn parse_edition(rumor: &UnsignedEvent) -> Result<ParsedEdition, EditionError>;
pub struct FoldResult { pub head: Option<usize>, pub gap: bool, pub anchored: bool }
pub fn fold(editions: &[EditionMeta], floor: u64, floor_hash: Option<&[u8; 32]>) -> FoldResult;
pub fn bootstrap_head(editions: &[EditionMeta], floor: u64) -> Option<usize>;
```
- Tag grammar: `["vsk", sub]`, `["eid", hex32]`, `["ev", decimal]`, `["ep", hex32]`, `["vac", eid, version, hash]`. Duplicates of any of the five reject the edition; `ev` must pass a decimal check before parsing.
- Tie-break at equal version is the lower **inner rumor id**, never `created_at`.
- `gap` is a safety signal: a tracking client (already holds the floor) fails closed for that entity and refetches; a bootstrapping client (floor 0) may accept the highest authority-verified head, which is what makes compaction re-wrapping work.
- Entity coordinates are `vsk 0` → `community_id`, `1` → `role_id`, `2` → `channel_id`, `3` → `grant_locator`, `4` → `banlist_locator`, `8` → `invite_links_locator`, `11` → `pins_locator`. All derive from `community_id` only, so a refounding re-wraps heads verbatim.
```rust
pub const P_MANAGE_ROLES: u64 = 1 << 0; // …bit table from CORD-04 §3, frozen; retired bits are burned
pub struct CommunityRoles { roles: BTreeMap<[u8; 32], Role>, grants: BTreeMap<PublicKey, Grant> }
impl CommunityRoles {
pub fn permissions_of(&self, member: &PublicKey) -> u64; // union of role bits
pub fn position_of(&self, member: &PublicKey, owner: &PublicKey) -> u32;
pub fn is_authorized(&self, actor: &PublicKey, owner: &PublicKey, bit: u64) -> bool;
pub fn is_authorized_in(&self, actor: &PublicKey, owner: &PublicKey, channel: &ChannelId, bit: u64) -> bool;
pub fn outranks(&self, actor: &PublicKey, owner: &PublicKey, target_position: u32) -> bool;
pub fn can_act_on(&self, actor: &PublicKey, owner: &PublicKey, target: &PublicKey, bit: u64) -> bool;
pub fn is_staff(&self, member: &PublicKey, owner: &PublicKey) -> bool; // the six control bits, CORD-04 §3
}
```
Authority rules to encode once and test hard:
- The owner is position 0, derived from `community_id`, and is never removable.
- No edition may claim a `position` at or above its own signer's, including the owner: no Role may claim 0.
- The actor must hold the required bit **and strictly outrank** the target. Equal cannot act on equal.
- A `vac` citation is a sync floor, not a verdict: block until the cited Grant version is folded, verify its hash, then judge against the *current* roster.
- A staff-making Grant carries `control_wrap`, a NIP-44 pairwise ciphertext of `epoch_be[8] ‖ control_root[32]`, and is adopted **only if it derives to the `control_pk` the member already holds** for the named epoch.
- Banlist is one replaced entity; mutations carry a re-heal step (re-fold after publish, re-apply if the addition lost the tiebreak).
### 8.2 Communities, channels, metadata
`CommunityMetadata` carries `name` (≤ 64 bytes), `description` (≤ 10 000 bytes), `relays` (truncated on read and write to 5), `icon` and `banner` as encrypted-blob pointers (`{url, key, nonce, hash}`), `message_expiration`, and the optional `custom` object. `ChannelMetadata` carries `name`, `private`, optional `voice`, `deleted`, optional `custom`.
Every content struct uses `#[serde(flatten)] extra: serde_json::Map<String, Value>` and round-trips unknown fields. A name edit by an older client must not wipe another client's `custom` keys. Round-trip discipline gets its own test.
Channel keying follows CORD-03 §1: a public channel derives from `community_root` at the base epoch, a private one from its own random key at its own epoch. Public→private is a rekey at the next channel epoch (monotonic, never reset); private→public derives from the base again and the prior private history stays unreadable to later joiners.
### 8.3 Guestbook and member list (`guestbook.rs`)
```rust
pub enum GuestbookEntry {
Join { member: PublicKey, at_ms: u64, invited_by: Option<(String, String)> },
Leave { member: PublicKey, at_ms: u64 },
Kick { actor: PublicKey, target: PublicKey, at_ms: u64, citation: Option<AuthorityCitation> },
Snapshot { refounder: PublicKey, members: Vec<PublicKey>, snapshot_id: [u8; 32], chunk: (u32, u32), at_ms: u64 },
}
pub fn coalesce(events: &[GuestbookEvent], now_ms: u64, snapshot_authority: Option<&PublicKey>,
can_kick: impl Fn(&PublicKey, &PublicKey, Option<&AuthorityCitation>) -> bool)
-> BTreeMap<PublicKey, MemberState>;
pub fn complete_memberlist(coalesced: &BTreeMap<PublicKey, MemberState>,
observed: &BTreeMap<PublicKey, u64>, // author → newest ms published
banned: &BTreeSet<PublicKey>, banned_at: &BTreeMap<PublicKey, u64>,
refound: Option<&Refound>) -> BTreeSet<PublicKey>;
```
- Entries dated more than an hour ahead of local time are dropped. An `ms` outside `0..999` drops the entry rather than being interpreted.
- Coalesce is per npub, one final state each, by millisecond time, ties on the lower inner rumor id.
- A Kick counts only when its signer holds `KICK` and outranks the target.
- A Snapshot counts only from the npub whose Refounding minted the epoch. There is deliberately no owner fallback.
- The member list is `coalesced Joined ∪ observed authors − banlist`, and observation counts **forward only** (an author re-enters on activity newer than their latest Leave/Kick/Ban). A Grant holder with `ms == 0` is present by construction.
### 8.4 Chat plane (`chat.rs`)
```rust
pub struct ChatMessage {
pub id: EventId, // recomputed rumor id
pub author: PublicKey,
pub channel: ChannelId,
pub epoch: Epoch,
pub kind: Kind, // 9 | 1111 | 3302 | 1740 | 15
pub content: String,
pub media: Vec<SharedUri>,
pub mentions: Vec<Mention>,
pub reply_to: Option<EventId>, // lowercase `e`/`q`
pub thread_root: Option<EventId>, // uppercase `E` for 1111
pub at_ms: u64,
pub expiration: Option<Timestamp>,
pub edited_at: Option<u64>, // folded from 3302
pub deleted: bool, // folded from 5
pub reactions: BTreeMap<PublicKey, String>,
}
```
Sends funnel through one function so the rules cannot drift:
```rust
fn publish_chat(store, client, community, channel, epoch, group, rumor, at_ms, ephemeral) -> Task<Result<Output<EventId, EventSendStatus>, Error>>;
```
It builds the seal + wrap, mirrors the NIP-40 tag onto the wrap for durable kinds, publishes via `send_event(..).to(relays)`, retains the ephemeral wrap key for later NIP-09 scrubbing, and locally echoes its own wrap through the same ingest path so send-then-read works without waiting on a relay round-trip.
Disappearing messages (CORD-08) live here: `message_expiration` is read from the folded metadata, `["expiration", created_at + t]` is attached to every durable Chat rumor and to the wrap, kinds 5 and 1740 are exempt, ingest refuses an already-expired rumor, a periodic sweep purges stored ones, and the kind 1740 timer notice renders only when its author holds `MANAGE_METADATA`.
### 8.5 Invites (`invite.rs`)
```rust
pub struct CommunityInvite { community_id, owner, owner_salt, community_root, root_epoch,
control_pk: Option<String>, channels: Vec<ChannelGrant>,
relays: Vec<String>, name: String, icon: Option<ImageRef>,
expires_at: Option<u64>, creator_npub: Option<String>, label: Option<String>,
extra: Map<String, Value> }
impl CommunityInvite { pub fn validate(&self) -> Result<(), InviteError>; // recompute community_id
pub fn expired(&self, now_ms: u64) -> bool; }
pub fn build_bundle(token: &[u8; 16], link_signer: &Keys, invite: &CommunityInvite) -> Result<Event, InviteError>; // 33301, d = ""
pub fn build_revocation(link_signer: &Keys) -> Result<Event, InviteError>; // vsk 9
pub fn parse_link(input: &str) -> Result<ParsedInviteLink, InviteError>;
pub fn encode_fragment(relays: &[RelayUrl], token: &[u8; 16]) -> String; // version byte 4, flags, ≤ 3 relays, base64url
pub fn decode_fragment(fragment: &str) -> Result<(Vec<RelayUrl>, [u8; 16]), InviteError>;
pub fn build_direct_invite(receiver: &PublicKey, invite: &CommunityInvite, signer: &UniversalSigner) -> Task<Result<Event, Error>>; // 3313 rumor → 13 seal → k-tagged 1059
```
The link rides `naddr` (`Nip19Coordinate` for kind 33301, link signer, empty `d`) in the path and the token + bootstrap relays in the fragment. A fragment is never sent to a server. The bundle is decrypted with `invite_bundle_key(token)`, and the joiner must recompute `community_id` from `owner` + `owner_salt`.
Bounds before allocation: reject a bundle with more than 256 channels, truncate the relay list to 5, refuse an expired one.
### 8.6 Rekeys and refoundings (`rekey.rs`)
```rust
pub enum RekeyScope { Channel(ChannelId), Base }
pub fn encode_blob_plaintext(scope, epoch, new_root, control_pk, control_root) -> Vec<u8>; // 72 | 104 | 136 bytes
pub fn parse_blob_plaintext(bytes: &[u8], scope, epoch) -> Result<KeyDelivery, RekeyError>;
pub fn build_rekey_rumor(rotator, scope, new_epoch, prev_epoch, prev_commit, blobs, chunk) -> UnsignedEvent;
pub fn plan_refounding(fold, removed: &[PublicKey]) -> Result<Refounding, RekeyError>;
pub fn compact(fold, epoch, new_control_root, ...) -> Vec<Event>; // re-wrap heads verbatim, plaintext seals preserved
```
- The subscription for rekeys is precomputed from the *next* epoch's address, per private channel and once for the base.
- A receiver accepts a key only after: locating its blob, decrypting with the rotator↔recipient conversation key, checking the bound `scope` and `epoch` inside the plaintext, and matching `prevcommit` against the key it currently holds.
- Only after holding **all** `n` chunks of one `(rotator, newepoch, prevcommit)` set, with none containing its locator, may a client conclude it was removed.
- Send cap 80 blobs per event, accept cap 120 (Vector's documented erratum: the CORD-01 double envelope pushes 120 blobs past a 64 KB relay limit). Record the reason in a comment so nobody "fixes" it back.
- Compacted control heads are re-wrapped with their original signature intact, which is exactly why the control plane uses the plaintext seal.
- Two concurrent refoundings converge on the lexicographically lowest new base key; the heal is down-only.
- Authority: a channel rekey needs `MANAGE_CHANNELS`, a refounding needs `BAN`, and in both the rotator must strictly outrank every removed target. Holding a key is never authority.
Dissolution (CORD-02 §9) also lives here: a chainless, owner-signed `vsk 10` tombstone at `dissolved_group_key(id)`, plaintext-sealed, and a verifier **must** refuse any tombstone whose `eid` is not the community's own id (including the all-zero placeholder — accepting it lets an owner's genuine tombstone for one community be re-wrapped at another of theirs and kill it permanently). On sight the community is sealed read-only: subscriptions halt, nothing new is honored, existing history stays readable, and a member's delete of their own message is still honored.
## 9. Storage (`store.rs`)
Three layers, no new storage engine:
1. **Raw wraps** (kind 1059) are persisted automatically by the SDK's relay pool when a subscription or fetch matches a filter. Nothing to write.
2. **Opened rumors** are cached locally as NIP-78 `Kind::ApplicationSpecificData` events signed by a session-local keypair, exactly like `chat::set_rumor`. Tags: `["d", rumor_id]` (replace key), `["c", channel_hex]`, `["p", author]`, `["k", kind]`, `["e", wrap_id]`, `["t", "concord"]`. Contents are the rumor JSON.
- The `c`/`t` keys deliberately differ from chat's `r` key so the two message namespaces can never collide in one database.
- The read path dedupes by rumor id and keeps the newest `created_at`, because the local signing key changes per session.
3. **Community state** — one local document per community, `Kind::ApplicationSpecificData` with `["d", "concord/<community_id>"]`:
```rust
pub struct CommunityState {
pub id: CommunityId,
pub owner: PublicKey,
pub owner_salt: [u8; 32],
pub community_root: [u8; 32],
pub root_epoch: Epoch,
pub control_root: Option<[u8; 32]>, // present iff the holder is staff
pub control_pks: BTreeMap<Epoch, PublicKey>,
pub channels: Vec<ChannelKeyRef>, // id, key, epoch, name, private
pub epoch_keys: Vec<([u8; 32], Epoch, [u8; 32])>, // (scope, epoch, key) — the history backfill index
pub relays: Vec<RelayUrl>,
pub heads: BTreeMap<[u8; 32], (u64, [u8; 32], EventId)>, // entity → (version, self_hash, inner id)
pub guestbook: Vec<GuestbookEvent>,
pub observed: BTreeMap<PublicKey, u64>,
pub banned: BTreeSet<PublicKey>,
pub dissolved: bool,
pub added_at_ms: u64,
}
```
Writes are debounced (a fold head changes on every edition); reads load once at init.
**Decision, stated for the record:** this document stores raw community keys unencrypted in a plaintext local database. That matches the existing posture — `chat` already caches decrypted message rumors in the same LMDB. If that posture ever changes, the state document is the one to wrap with NIP-44-to-self, since it is the only local artifact holding keys.
History queries:
```rust
pub async fn query_messages(&self, channel: &ChannelId, until: Option<Timestamp>, limit: usize) -> Result<Vec<Event>, Error>;
pub async fn backfill(&self, plane_authors: &[PublicKey], relays: &[RelayUrl], until: Option<Timestamp>, limit: usize) -> Result<Vec<Event>, Error>;
```
`query_messages` reads the local cache (`Filter::new().kind(ApplicationSpecificData).custom_tag(LOWERCASE_C, channel_hex)`); `backfill` pages relays newest-first with `until`, deduplicating by wrap id and stepping past same-second walls.
## 10. Sync engine and GPUI conventions
`ConcordRegistry` mirrors `ChatRegistry`'s shape exactly: a foreground GPUI entity holding `Entity<Community>` handles, a `flume` signal bus, one background notification listener, one foreground consumer, and task slots that are cleared when the signer changes.
**Subscription.** Community relays come from the folded metadata. `init`/`join` add them to the client (`client.add_relay(url).and_connect()`), then:
```rust
let filter = Filter::new()
.kinds([Kind::GiftWrap, Kind::Custom(KIND_WRAP_EPHEMERAL)])
.authors(plane_authors) // guestbook, control signer, all held channel planes,
// rekey addresses for epoch + 1, dissolved address
.since(Timestamp::from_secs(now - FRESH_WINDOW))
.limit(0); // live tail only; history comes from backfill
client.subscribe(target).with_id(SubscriptionId::new(CONCORD_SUB)).await?;
```
Targeted subscribe against the community relays, with a pool-wide subscribe as the fallback path. Rebuild idempotently whenever a plane's address changes (join, channel added, rekey folded).
**Routing.** `dispatch` matches on the `subscription_id` carried in `RelayMessage::Event`, dedupes by wrap id (both subscriptions and several relays deliver the same wrap), then recognises the plane by **wrap author** against the derived addresses it holds — never by trial decryption. Recognition order: held channel planes → guestbook → control signer → rekey addresses → dissolved.
**Ingest pipeline.** Unwrap, verify and fold all happen inside `cx.background_spawn`, never on the foreground thread: secp256k1 verification per edition and per seal is far too expensive for the UI thread.
```rust
enum Signal {
Chat { community: CommunityId, channel: ChannelId, message: Box<ChatMessage> },
Control { community: CommunityId, heads: Vec<EntityHead>, roster: Box<FoldedRoster> },
Guestbook { community: CommunityId, members: BTreeSet<PublicKey> },
Rekey { community: CommunityId, scope: RekeyScope, epoch: Epoch },
Dissolved(CommunityId),
Eose(SubscriptionId),
Error(ConcordError),
}
```
The consumer is `cx.spawn(async move |this, cx| { while let Ok(signal) = rx.recv_async().await { this.update(cx, |this, cx| this.apply(signal, cx))?; } })`, which updates entities and calls `cx.notify()`.
Rules taken from the project guidelines:
- Crypto, folding, database queries and network I/O only in `cx.background_spawn`.
- Foreground tasks are `cx.spawn` with `this.update(cx, ..)`; any entity update happens there, and the inner `cx` is always used.
- Tasks are stored in fields (`tasks`, `listener`, `consumer`) so they are cancelled on signer change and dropped with the registry. `detach()` only for genuinely fire-and-forget work such as the local state save.
- Long-running paging is bounded by explicit page and step caps, not by unbounded loops.
- Every fallible path returns `Result` and surfaces through `ConcordEvent::Error`; nothing is silently swallowed.
**Registry API.**
```rust
pub fn init(window: &mut Window, cx: &mut App);
pub struct ConcordRegistry { /* … */ }
impl ConcordRegistry {
pub fn global(cx: &App) -> Entity<Self>;
pub fn loading(&self) -> bool;
pub fn communities(&self) -> Vec<Entity<Community>>;
pub fn community(&self, id: &CommunityId, cx: &App) -> Option<WeakEntity<Community>>;
pub fn find(&self, query: &str, cx: &App) -> Vec<Entity<Community>>;
pub fn create(&mut self, params: CommunityParams, cx: &mut Context<Self>) -> Task<Result<CommunityId, Error>>;
pub fn join(&mut self, link: &str, cx: &mut Context<Self>) -> Task<Result<CommunityId, Error>>;
pub fn accept_direct_invite(&mut self, rumor: &UnsignedEvent, cx: &mut Context<Self>) -> Task<Result<CommunityId, Error>>;
pub fn leave(&mut self, id: &CommunityId, cx: &mut Context<Self>);
pub fn discard_invite(&mut self, id: &CommunityId, cx: &mut Context<Self>);
pub fn refresh(&mut self, id: &CommunityId, cx: &mut Context<Self>);
pub fn shutdown(&mut self, cx: &mut Context<Self>); // halt subscriptions, keep our own state
}
```
**Community API** (`Entity<Community>`, `EventEmitter<CommunityEvent>`):
```rust
pub fn id(&self) -> CommunityId;
pub fn owner(&self) -> PublicKey;
pub fn name(&self) -> SharedString; pub fn description(&self) -> Option<SharedString>;
pub fn icon(&self) -> Option<ImageRef>;
pub fn relays(&self) -> Vec<RelayUrl>;
pub fn epoch(&self) -> Epoch;
pub fn dissolved(&self) -> bool;
pub fn channels(&self) -> Vec<Entity<Channel>>;
pub fn channel(&self, id: &ChannelId, cx: &App) -> Option<WeakEntity<Channel>>;
pub fn members(&self) -> BTreeSet<PublicKey>;
pub fn banned(&self) -> BTreeSet<PublicKey>;
pub fn roles(&self) -> &CommunityRoles;
pub fn permissions(&self, member: &PublicKey) -> u64;
pub fn is_staff(&self, member: &PublicKey) -> bool;
pub fn message_expiration(&self) -> Option<u64>;
// authority actions — each returns a publish task and nothing optimistic
pub fn set_metadata(&mut self, meta: CommunityMetadata, cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn create_channel(&mut self, name: &str, private: bool, cx: &mut Context<Self>) -> Task<Result<ChannelId, Error>>;
pub fn edit_channel(&mut self, id: &ChannelId, meta: ChannelMetadata, cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn create_role(&mut self, role: Role, cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn assign_roles(&mut self, member: &PublicKey, roles: &[[u8; 32]], cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn ban(&mut self, members: &[PublicKey], cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn unban(&mut self, members: &[PublicKey], cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn kick(&mut self, member: &PublicKey, cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn rekey_channel(&mut self, id: &ChannelId, removed: &[PublicKey], cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn refound(&mut self, removed: &[PublicKey], cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn dissolve(&mut self, cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn create_invite(&mut self, params: InviteParams, cx: &mut Context<Self>) -> Task<Result<String, Error>>;
pub fn revoke_invite(&mut self, token: &[u8; 16], cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn direct_invite(&mut self, receiver: &PublicKey, cx: &mut Context<Self>) -> Task<Result<(), Error>>;
pub fn save_community_list(&mut self, cx: &mut Context<Self>) -> Task<Result<(), Error>>; // kind 13302, multi-device sync
```
**Channel API** (`Entity<Channel>`): `id`, `name`, `private`, `epoch`, `deleted`, plus
```rust
pub fn messages(&self, until: Option<Timestamp>, limit: usize, cx: &App) -> Task<Result<Vec<ChatMessage>, Error>>;
pub fn send(&self, content: &str, reply_to: Option<EventId>, cx: &App) -> Task<Result<Output<EventId, EventSendStatus>, Error>>;
pub fn send_file(&self, file: FileAttachment, reply_to: Option<EventId>, cx: &App) -> Task<Result<Output<EventId, EventSendStatus>, Error>>;
pub fn edit(&self, id: EventId, content: &str, cx: &App) -> Task<Result<Output<EventId, EventSendStatus>, Error>>;
pub fn delete(&self, id: EventId, cx: &App) -> Task<Result<Output<EventId, EventSendStatus>, Error>>;
pub fn react(&self, id: EventId, emoji: &str, cx: &App) -> Task<Result<Output<EventId, EventSendStatus>, Error>>;
pub fn typing(&self, cx: &App) -> Task<Result<(), Error>>; // kind 23311, ephemeral
pub fn pin(&self, id: EventId, cx: &App) -> Task<Result<(), Error>>; // vsk 11, PIN_MESSAGES
```
`CommunityEvent` and `ChannelEvent` mirror `ChatEvent`: one variant per thing the UI has to react to (`Updated`, `Members`, `Added`, `Removed`, `Dissolved`, `Error`, plus channel-level `Incoming`, `Reload`).
## 11. Integration with existing crates
1. **`crates/chat/src/lib.rs` — required fix.** `handle_notifications` currently treats *every* kind 1059 event as a NIP-17 wrap for the current user and pushes an unwrap failure into the trash. Concord wraps are kind 1059 with an ephemeral `p` tag, so they would flood the trash and leak error toasts. Route by `subscription_id` from `RelayMessage::Event` against `sub_id1`/`sub_id2`, and drop the `if rumor.tags.is_empty()` heuristic once the real recipient check is in place.
2. **`desktop/src/main.rs` and `web/src/lib.rs`** — add `concord::init(window, cx)` after `chat::init(window, cx)`.
3. **`Cargo.toml`** — add `hkdf = "0.12"` to `[workspace.dependencies]`; add the crate to `desktop` and `web` dependencies. No other workspace changes.
4. **No changes** to `state`, `person`, `device`, `settings`, `common`, or `ui`.
## 12. Security invariants to test, not to assume
Each of these has burned a real implementation, or is a documented cross-client trap:
- Recompute every rumor id and reject a claimed mismatch; never trust an embedded `id`.
- Require `rumor.pubkey == seal.pubkey`.
- Require the plaintext seal form on Control and the encrypted form on Chat/Guestbook/Rekey — a strict reader must drop a mis-sealed edition rather than fold a chain a later compaction would fork.
- Check `channel` **and** `epoch` against the plane whose key opened the wrap; reject duplicates of either tag.
- Reject duplicate `vsk`/`eid`/`ev`/`ep`/`vac` tags; require decimal-with-no-leading-zeros on every numeric tag.
- Refuse a tombstone whose `eid` is not this community's id.
- Adopt a `control_root` from a Grant only if it derives to the `control_pk` held for that epoch; adopt a rekey blob only if its bound plaintext matches the scope and epoch and its `prevcommit` matches the key currently held.
- Never conclude removal from a partial rekey chunk set.
- Drop guestbook entries more than an hour in the future; treat an out-of-range `ms` as malformed, not as an interpretation opportunity.
- Never honour a Snapshot from anyone but the refounder of that epoch.
- Refuse to write a Pin List from a list the writer could not read.
- Enforce the NIP-44 65 535-byte cap at every layer before publishing, and the 5-relay / 256-channel / 50-membership / 100-roles / 64-roles-per-member / 500-banlist / 25-pin caps at their ingest and write points.
- Lowercase hex only; x-only pubkeys only; no version tag anywhere.
## 13. Milestones
| # | Deliverable | Done when |
| --- | --- | --- |
| M0 | Crate skeleton, `derive.rs`, golden vectors, workspace wiring | `cargo test -p concord` pins every derivation from an independent vector set; all labels match Appendix A.6 |
| M1 | `stream.rs` + `store.rs` | seal/wrap/open round-trips for both seal forms; malformed inputs rejected in the documented order; local cache reads back after a restart |
| M2 | `edition.rs` + `control.rs` genesis | a community is created and published; its two genesis wraps open at a second client sharing the keys; edition hash matches the cross-client vector |
| M3 | Control fold + roster + metadata/channels | fold tests for chains, gaps, downgrade refusal, fork tiebreak, compaction dangle; metadata and channel edits visible to a second client |
| M4 | Chat plane | send/receive/edit/delete/reaction across two identities; history pages backwards across an epoch boundary; binding checks reject a foreign channel/epoch |
| M5 | Guestbook + member list + moderation | join/leave/kick/ban converge to the same memberlist on both clients; every authority rule from §8.1 has a negative test |
| M6 | Invites + Community List | link mint → fetch → join round-trips; revoked link refuses; direct invite lands in the recipient's giftwrap inbox via the `k` tag; a second device reconstructs membership from 13302 |
| M7 | Rekeys + refounding + dissolution | a removed member stops reading after a rekey; continuity and race rules tested; a tombstone seals the community and a foreign-id tombstone is refused |
| M8 | Pins + disappearing messages + hardening | pins verify from a keyless reader's view; expiry is refused at ingest and purged by the sweep; the audit of §12 is complete with a test per bullet |
Ordering is deliberately dependency-first: each milestone is usable on its own, and nothing in M2+ depends on a later milestone.
## 14. Open questions and risks
1. **Community List kind.** CORD-02 §8 specifies `13302`, replaceable. Vector has retired it in favour of fragmented `33302`, because a replaceable kind holds one event per pubkey and so cannot shard past the NIP-44 size cap. We implement `13302` per spec, enforce the 50-membership cap and pre-publish size check, and treat `33302` as an interop follow-up. Confirm with Armada before writing the multi-device code.
2. **NIP-42 for stream-authored REQs.** Relays that gate kind 1059 by author (for example `ditto-relay`'s `AUTH_KINDS`) need an AUTH event signed by that plane's derived key. `nostr-sdk`'s `Authenticator` is per-client and returns one identity, while a Concord client holds many plane keys — so this cannot be solved by swapping the authenticator. Options: contribute a per-REQ auth hook upstream, or accept that such relays are unsupported and prefer relays without the gate. Decide before M7; the default is "documented limitation" plus a relay-capability check.
3. **`invite_bundle_key`.** Appendix A.6 says the label "yields the public-invite decrypt key" without stating whether that is the raw HKDF output used as a NIP-44 conversation key or the `conv_key` of a normalized keypair. The reference implementation uses the raw output. Pin a vector and verify against Armada early — this one decides whether links open at all.
4. **Missing golden vector for `pins_locator`.** Upstream publishes none. Ours will be self-referential; flag it in the test.
5. **Relay set.** Up to 5 recommended, and both reads and writes fan out across them. Coop's client is a gossip client with `no_background_refresh`, so community relays must be added explicitly and re-added on metadata change.
6. **Local plaintext state.** §9 records the decision. Revisit only if the local database stops being treated as trusted.
7. **Was a `community_id` ever hashed into a tag?** No — it must never appear on the wire. A lint-style test should assert it appears in no published event other than inside an invite bundle and a direct invite.
## 15. Test strategy
- **Unit, pure:** derivations against golden vectors, edition hash, fold, coalesce, memberlist, blob codec, caps and rejection paths. These need no GPUI context and should be exhaustive — they are where cross-client divergence is caught.
- **Integration, GPUI:** `TestAppContext` with two registries sharing an in-memory database, driving wraps through the ingest path; timeouts and delays use `cx.background_executor().timer(..)` per the project guidelines, never `smol::Timer`.
- **Round-trip:** every builder paired with its parser, asserting the parse produces the identical structure, including unknown-field round-tripping on all content types.
- **Negative:** every bullet in §12 gets a test that constructs the hostile input and asserts the drop.
Binary file not shown.

After

Width:  |  Height:  |  Size: 669 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M2.00797 18.6467C2.52996 18.6467 2.82695 18.3947 3.01594 17.8097L3.99691 15.0198H8.96477L9.95475 17.8187C10.1347 18.3947 10.4317 18.6467 10.9717 18.6467C11.5567 18.6467 11.9797 18.2597 11.9797 17.7287C11.9797 17.5577 11.9437 17.3867 11.8537 17.1347L7.8938 6.41604C7.62381 5.69606 7.19182 5.37207 6.48984 5.37207C5.77886 5.37207 5.33788 5.70506 5.07688 6.42504L1.126 17.1347C1.036 17.3777 1 17.5757 1 17.7287C1 18.2777 1.40499 18.6467 2.00797 18.6467ZM4.5099 13.3728L6.44485 7.676H6.49884L8.45179 13.3728H4.5099Z" fill="currentColor"/><path d="M17.1041 18.6647C18.3371 18.6647 19.5431 17.9987 20.0921 16.9457H20.1371V17.7467C20.1551 18.3227 20.524 18.6737 21.064 18.6737C21.613 18.6737 22 18.3047 22 17.6927V11.9779C22 10.0789 20.533 8.86397 18.2381 8.86397C16.5372 8.86397 15.1062 9.54795 14.6112 10.6189C14.4942 10.8349 14.4402 11.0509 14.4402 11.2489C14.4402 11.7349 14.7912 12.0589 15.2772 12.0589C15.6192 12.0589 15.8712 11.9329 16.0692 11.6449C16.6092 10.7809 17.2301 10.4479 18.1661 10.4479C19.3541 10.4479 20.0651 11.0779 20.0651 12.1309V12.8599L17.4641 13.0129C15.2232 13.1388 13.9452 14.1558 13.9452 15.8118C13.9452 17.5127 15.2502 18.6647 17.1041 18.6647ZM17.6351 17.1257C16.6002 17.1257 15.9072 16.5858 15.9072 15.7578C15.9072 14.9478 16.5642 14.4348 17.7251 14.3538L20.0651 14.2098V14.9568C20.0651 16.1898 18.9941 17.1257 17.6351 17.1257Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

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

After

Width:  |  Height:  |  Size: 571 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M18.25 14L12 20.25L5.75 14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 19.5V3.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 320 B

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

After

Width:  |  Height:  |  Size: 319 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12.0029 2.75V21.25M3.99219 7.375L20.0137 16.625M3.99316 16.625L20.0146 7.375" stroke="black" stroke-width="1.5" stroke-linecap="round"/></svg>

After

Width:  |  Height:  |  Size: 224 B

+14
View File
@@ -0,0 +1,14 @@
<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"
>
<circle cx="12" cy="12" r="10" />
<path d="M4.929 4.929 19.07 19.071" />
</svg>

After

Width:  |  Height:  |  Size: 285 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M7.75 5.75H3.75C2.64543 5.75 1.75 6.64543 1.75 7.75V16.25C1.75 17.3546 2.64543 18.25 3.75 18.25H5.25M15.75 5.75H17.25C18.3546 5.75 19.25 6.64543 19.25 7.75V16.25C19.25 17.3546 18.3546 18.25 17.25 18.25H13.25M12.25 5.75L7.5 12H13.5L8.75 18.25M19.25 8.75H21.25C21.8023 8.75 22.25 9.19772 22.25 9.75V14.25C22.25 14.8023 21.8023 15.25 21.25 15.25H19.25V8.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 525 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M6 9.75V14.25M10.5 9.75V14.25M15 9.75V14.25M3.75 18.25H17.25C18.3546 18.25 19.25 17.3546 19.25 16.25V7.75C19.25 6.64543 18.3546 5.75 17.25 5.75H3.75C2.64543 5.75 1.75 6.64543 1.75 7.75V16.25C1.75 17.3546 2.64543 18.25 3.75 18.25ZM19.25 8.75H21.25C21.8023 8.75 22.25 9.19772 22.25 9.75V14.25C22.25 14.8023 21.8023 15.25 21.25 15.25H19.25V8.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 513 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M6 9.75V14.25M3.75 18.25H17.25C18.3546 18.25 19.25 17.3546 19.25 16.25V7.75C19.25 6.64543 18.3546 5.75 17.25 5.75H3.75C2.64543 5.75 1.75 6.64543 1.75 7.75V16.25C1.75 17.3546 2.64543 18.25 3.75 18.25ZM19.25 8.75H21.25C21.8023 8.75 22.25 9.19772 22.25 9.75V14.25C22.25 14.8023 21.8023 15.25 21.25 15.25H19.25V8.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 483 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M6 9.75V14.25M10.5 9.75V14.25M3.75 18.25H17.25C18.3546 18.25 19.25 17.3546 19.25 16.25V7.75C19.25 6.64543 18.3546 5.75 17.25 5.75H3.75C2.64543 5.75 1.75 6.64543 1.75 7.75V16.25C1.75 17.3546 2.64543 18.25 3.75 18.25ZM19.25 8.75H21.25C21.8023 8.75 22.25 9.19772 22.25 9.75V14.25C22.25 14.8023 21.8023 15.25 21.25 15.25H19.25V8.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 499 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12.25 9.75L10 12M10 12L7.75 14.25M10 12L7.75 9.75M10 12L12.25 14.25M3.75 18.25H17.25C18.3546 18.25 19.25 17.3546 19.25 16.25V7.75C19.25 6.64543 18.3546 5.75 17.25 5.75H3.75C2.64543 5.75 1.75 6.64543 1.75 7.75V16.25C1.75 17.3546 2.64543 18.25 3.75 18.25ZM19.25 8.75H21.25C21.8023 8.75 22.25 9.19772 22.25 9.75V14.25C22.25 14.8023 21.8023 15.25 21.25 15.25H19.25V8.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 538 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M1.75 7.75C1.75 6.64543 2.64543 5.75 3.75 5.75H17.25C18.3546 5.75 19.25 6.64543 19.25 7.75V16.25C19.25 17.3546 18.3546 18.25 17.25 18.25H3.75C2.64543 18.25 1.75 17.3546 1.75 16.25V7.75Z" stroke="black" stroke-width="1.5" stroke-linejoin="round"/><path d="M19.25 8.75H21.25C21.8023 8.75 22.25 9.19772 22.25 9.75V14.25C22.25 14.8023 21.8023 15.25 21.25 15.25H19.25V8.75Z" stroke="black" stroke-width="1.5" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 516 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M4.47254 9.40225C4.94336 5.60292 8.17163 2.75 12 2.75C15.8284 2.75 19.0567 5.60291 19.5275 9.40224L20.2217 15.004C20.3695 16.1971 19.439 17.25 18.2369 17.25H5.76317C4.56102 17.25 3.63051 16.1971 3.77835 15.004L4.47254 9.40225Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 17.25C16 19.4591 14.2091 21.25 12 21.25C9.79086 21.25 8 19.4591 8 17.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 565 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M19.25 12V14.75C19.25 15.8546 18.3546 16.75 17.25 16.75H7C5.75736 16.75 4.75 17.7574 4.75 19C4.75 20.2426 5.75736 21.25 7 21.25H10M8.75 7H15.25M8.75 11H12.25M6.75 2.75H17.25C18.3546 2.75 19.25 3.64543 19.25 4.75V19.25C19.25 20.3546 18.3546 21.25 17.25 21.25H6.75C5.64543 21.25 4.75 20.3546 4.75 19.25V4.75C4.75 3.64543 5.64543 2.75 6.75 2.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 513 B

+1 -3
View File
@@ -1,3 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M4.75 20V4.75C4.75 3.64543 5.64543 2.75 6.75 2.75H19.25V18.75H6C5.30964 18.75 4.75 19.3096 4.75 20ZM4.75 20C4.75 20.6904 5.30964 21.25 6 21.25H19.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 12.25C9.75 12.25 9.75 13.75 9.75 13.75H14.25C14.25 13.75 14.25 12.25 12 12.25Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M13 9.25C13 9.80228 12.5523 10.25 12 10.25C11.4477 10.25 11 9.80228 11 9.25C11 8.69772 11.4477 8.25 12 8.25C12.5523 8.25 13 8.69772 13 9.25Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M11.5 9.25H12.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<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: 866 B

After

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

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 2.75V4.75M3 8.75C2.58579 8.75 2.25 9.08579 2.25 9.5V10.5C2.25 10.9142 2.58579 11.25 3 11.25M21 11.25C21.4142 11.25 21.75 10.9142 21.75 10.5V9.5C21.75 9.08579 21.4142 8.75 21 8.75M9.75 14.5H14.25M9.25 10C9.25 10.4142 8.91421 10.75 8.5 10.75C8.08579 10.75 7.75 10.4142 7.75 10C7.75 9.58579 8.08579 9.25 8.5 9.25C8.91421 9.25 9.25 9.58579 9.25 10ZM16.25 10C16.25 10.4142 15.9142 10.75 15.5 10.75C15.0858 10.75 14.75 10.4142 14.75 10C14.75 9.58579 15.0858 9.25 15.5 9.25C15.9142 9.25 16.25 9.58579 16.25 10ZM20.75 16.25V6.75C20.75 5.64543 19.8546 4.75 18.75 4.75H5.25C4.14543 4.75 3.25 5.64543 3.25 6.75V16.25C3.25 17.9069 4.59315 19.25 6.25 19.25H17.75C19.4069 19.25 20.75 17.9069 20.75 16.25Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 865 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M10.25 8.75H7.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M7.75 12.75H10.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M22.25 19.25H1.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M3.75 19.25V5.75C3.75 4.64543 4.64543 3.75 5.75 3.75H12.25C13.3546 3.75 14.25 4.64543 14.25 5.75V19.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M14.25 7.75H18.25C19.3546 7.75 20.25 8.64543 20.25 9.75V19.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 756 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M3.75 6.75C3.75 5.64543 4.64543 4.75 5.75 4.75H18.25C19.3546 4.75 20.25 5.64543 20.25 6.75V18.25C20.25 19.3546 19.3546 20.25 18.25 20.25H5.75C4.64543 20.25 3.75 19.3546 3.75 18.25V6.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M7.75 4.75V2.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M16.25 4.75V2.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 573 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
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M2.00797 18.6467C2.52996 18.6467 2.82695 18.3947 3.01594 17.8097L3.99691 15.0198H8.96477L9.95475 17.8187C10.1347 18.3947 10.4317 18.6467 10.9717 18.6467C11.5567 18.6467 11.9797 18.2597 11.9797 17.7287C11.9797 17.5577 11.9437 17.3867 11.8537 17.1347L7.8938 6.41604C7.62381 5.69606 7.19182 5.37207 6.48984 5.37207C5.77886 5.37207 5.33788 5.70506 5.07688 6.42504L1.126 17.1347C1.036 17.3777 1 17.5757 1 17.7287C1 18.2777 1.40499 18.6467 2.00797 18.6467ZM4.5099 13.3728L6.44485 7.676H6.49884L8.45179 13.3728H4.5099Z" fill="currentColor"/><path d="M17.1041 18.6647C18.3371 18.6647 19.5431 17.9987 20.0921 16.9457H20.1371V17.7467C20.1551 18.3227 20.524 18.6737 21.064 18.6737C21.613 18.6737 22 18.3047 22 17.6927V11.9779C22 10.0789 20.533 8.86397 18.2381 8.86397C16.5372 8.86397 15.1062 9.54795 14.6112 10.6189C14.4942 10.8349 14.4402 11.0509 14.4402 11.2489C14.4402 11.7349 14.7912 12.0589 15.2772 12.0589C15.6192 12.0589 15.8712 11.9329 16.0692 11.6449C16.6092 10.7809 17.2301 10.4479 18.1661 10.4479C19.3541 10.4479 20.0651 11.0779 20.0651 12.1309V12.8599L17.4641 13.0129C15.2232 13.1388 13.9452 14.1558 13.9452 15.8118C13.9452 17.5127 15.2502 18.6647 17.1041 18.6647ZM17.6351 17.1257C16.6002 17.1257 15.9072 16.5858 15.9072 15.7578C15.9072 14.9478 16.5642 14.4348 17.7251 14.3538L20.0651 14.2098V14.9568C20.0651 16.1898 18.9941 17.1257 17.6351 17.1257Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M12 2.75V12L20.7237 15.0833" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="12" cy="12" r="9.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 330 B

+1 -3
View File
@@ -1,3 +1 @@
<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>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M4.75 12.7768L10 19.25L19.25 4.75" stroke="black" stroke-width="1.5" stroke-linecap="round"/></svg>

Before

Width:  |  Height:  |  Size: 215 B

After

Width:  |  Height:  |  Size: 180 B

+1 -1
View File
@@ -1,3 +1,3 @@
<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"/>
<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: 302 B

After

Width:  |  Height:  |  Size: 209 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M15.0002 20L8.4144 13.4142C7.63335 12.6332 7.63335 11.3669 8.4144 10.5858L15.0002 4" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 254 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M9 4L15.5858 10.5858C16.3668 11.3668 16.3668 12.6331 15.5858 13.4142L9 20" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 244 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M4 14.9999L10.5858 8.41416C11.3668 7.63311 12.6331 7.63311 13.4142 8.41415L20 14.9999" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 256 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M8 8.99985L11.2929 5.70696C11.6834 5.31643 12.3166 5.31643 12.7071 5.70696L16 8.99985" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 15L12.7071 18.2929C12.3166 18.6834 11.6834 18.6834 11.2929 18.2929L8 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 441 B

+15
View File
@@ -0,0 +1,15 @@
<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"
>
<circle cx="12" cy="12" r="10" />
<path d="M12 8v8" />
<path d="m8 12 4 4 4-4" />
</svg>

After

Width:  |  Height:  |  Size: 296 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M15 9.5L10.5 15L8.5 13M21.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="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 345 B

Before

Width:  |  Height:  |  Size: 352 B

After

Width:  |  Height:  |  Size: 352 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 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 12C2.75 17.1086 6.89137 21.25 12 21.25Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M14.2485 16.25C12.7476 14.7408 11.9999 13.2338 11.9985 11.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.75 16.25C11.2894 14.758 11.9998 13.2338 11.9984 11.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.75 10.6912C8.75 10.6912 10.7206 10.9559 11.9996 10.9559C13.2787 10.9559 15.25 10.6912 15.25 10.6912" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12.75 8C12.75 8.41421 12.4142 8.75 12 8.75C11.5858 8.75 11.25 8.41421 11.25 8C11.25 7.58579 11.5858 7.25 12 7.25C12.4142 7.25 12.75 7.58579 12.75 8Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -0,0 +1 @@
<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="black" stroke-width="1.5" stroke-linecap="round"/></svg>

After

Width:  |  Height:  |  Size: 319 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
@@ -0,0 +1,3 @@
<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>

After

Width:  |  Height:  |  Size: 377 B

+1 -1
View File
@@ -1,3 +1,3 @@
<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"/>
<path d="M7.75 7.75V5.75C7.75 4.64543 8.64543 3.75 9.75 3.75H18.25C19.3546 3.75 20.25 4.64543 20.25 5.75V14.26C20.25 15.3646 19.3546 16.26 18.25 16.26H16.25M3.75 9.75V18.25C3.75 19.3546 4.64543 20.25 5.75 20.25H14.25C15.3546 20.25 16.25 19.3546 16.25 18.25V9.75C16.25 8.64543 15.3546 7.75 14.25 7.75H5.75C4.64543 7.75 3.75 8.64543 3.75 9.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 472 B

After

Width:  |  Height:  |  Size: 513 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12.0002 4.74693V2.74609M16 4.75V2.75M8.00018 4.75V2.74609M12 21.25V19.25M16 21.25V19.25M8 21.25V19.25M19.25 16H21.25M19.25 8H21.25M19.25 12H21.25M2.75 12H4.75M2.75 16H4.75M2.75 8H4.75M15.2488 11.999C15.2488 13.7939 13.7937 15.249 11.9988 15.249C10.2039 15.249 8.74878 13.7939 8.74878 11.999C8.74878 10.2041 10.2039 8.74902 11.9988 8.74902C13.7937 8.74902 15.2488 10.2041 15.2488 11.999ZM6.75 19.25H17.25C18.3546 19.25 19.25 18.3546 19.25 17.25V6.75C19.25 5.64543 18.3546 4.75 17.25 4.75H6.75C5.64543 4.75 4.75 5.64543 4.75 6.75V17.25C4.75 18.3546 5.64543 19.25 6.75 19.25Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 744 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M5.25 12H18.75" stroke="black" stroke-width="1.5" stroke-linecap="round"/></svg>

After

Width:  |  Height:  |  Size: 161 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M20.25 5.5C20.25 6.88071 16.5563 8 12 8C7.44365 8 3.75 6.88071 3.75 5.5C3.75 4.11929 7.44365 3 12 3C16.5563 3 20.25 4.11929 20.25 5.5Z" stroke="currentColor" stroke-width="1.5"/><path d="M3.75 5.5L4.99636 14.8666C5.22121 16.5564 5.33363 17.4012 5.64516 18.0816C6.23984 19.3804 7.36162 20.3628 8.72748 20.7809C9.44297 21 10.2953 21 12 21C13.7047 21 14.557 21 15.2725 20.7809C16.6384 20.3628 17.7602 19.3804 18.3548 18.0816C18.6664 17.4012 18.7788 16.5564 19.0036 14.8666L20.25 5.5" stroke="currentColor" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 613 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

+17
View File
@@ -0,0 +1,17 @@
<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"
>
<path d="M10 21H2" />
<path d="M10 4a2 2 0 012.36-1.968l5.41.992A1.5 1.5 0 0119 4.5V21l-7.876.992A1 1 0 0110 21z" />
<path d="M10.268 3H7a2 2 0 00-2 2v16" />
<path d="M14 12h.01" />
<path d="M22 21h-3" />
</svg>

After

Width:  |  Height:  |  Size: 423 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 4.75C12.5523 4.75 13 4.30228 13 3.75C13 3.19772 12.5523 2.75 12 2.75C11.4477 2.75 11 3.19772 11 3.75C11 4.30228 11.4477 4.75 12 4.75Z" fill="black"/><path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" fill="black"/><path d="M12 21.25C12.5523 21.25 13 20.8023 13 20.25C13 19.6977 12.5523 19.25 12 19.25C11.4477 19.25 11 19.6977 11 20.25C11 20.8023 11.4477 21.25 12 21.25Z" fill="black"/><path d="M12 4.75C12.5523 4.75 13 4.30228 13 3.75C13 3.19772 12.5523 2.75 12 2.75C11.4477 2.75 11 3.19772 11 3.75C11 4.30228 11.4477 4.75 12 4.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 21.25C12.5523 21.25 13 20.8023 13 20.25C13 19.6977 12.5523 19.25 12 19.25C11.4477 19.25 11 19.6977 11 20.25C11 20.8023 11.4477 21.25 12 21.25Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+1 -3
View File
@@ -1,3 +1 @@
<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>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" fill="black"/><path d="M20.25 13C20.8023 13 21.25 12.5523 21.25 12C21.25 11.4477 20.8023 11 20.25 11C19.6977 11 19.25 11.4477 19.25 12C19.25 12.5523 19.6977 13 20.25 13Z" fill="black"/><path d="M3.75 13C4.30228 13 4.75 12.5523 4.75 12C4.75 11.4477 4.30228 11 3.75 11C3.19772 11 2.75 11.4477 2.75 12C2.75 12.5523 3.19772 13 3.75 13Z" fill="black"/><path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M20.25 13C20.8023 13 21.25 12.5523 21.25 12C21.25 11.4477 20.8023 11 20.25 11C19.6977 11 19.25 11.4477 19.25 12C19.25 12.5523 19.6977 13 20.25 13Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M3.75 13C4.30228 13 4.75 12.5523 4.75 12C4.75 11.4477 4.30228 11 3.75 11C3.19772 11 2.75 11.4477 2.75 12C2.75 12.5523 3.19772 13 3.75 13Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

Before

Width:  |  Height:  |  Size: 632 B

After

Width:  |  Height:  |  Size: 1.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 3.75V15M12 3.75L16.5 8.25M12 3.75L7.5 8.25M20.25 14.75V18.25C20.25 19.3546 19.3546 20.25 18.25 20.25H5.75C4.64543 20.25 3.75 19.3546 3.75 18.25V14.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 324 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M9.38159 5.13889C13.5844 3.87389 18.1952 5.69502 21.2234 10.6022C21.4836 11.0238 21.6136 11.2346 21.6871 11.5612C21.7414 11.8029 21.7414 12.1969 21.6871 12.4386C21.6137 12.7652 21.4839 12.9757 21.2242 13.3966C20.7534 14.1596 20.2444 14.848 19.7046 15.4619" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.5 9.92322C9.03166 10.4864 8.75 11.2103 8.75 12C8.75 13.7949 10.2051 15.25 12 15.25C12.8035 15.25 13.5389 14.9584 14.1061 14.4753" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.22301 6.75C4.94909 7.69766 3.77614 8.98208 2.7759 10.6033C2.51619 11.0242 2.38634 11.2347 2.31292 11.5613C2.25859 11.803 2.25861 12.197 2.31296 12.4387C2.3864 12.7653 2.51649 12.9761 2.77667 13.3977C6.43137 19.3202 12.3913 20.7475 17.1571 17.6795" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.75 2.75L21.25 21.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+1 -3
View File
@@ -1,3 +1 @@
<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>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M21.5298 11.1188C16.6909 2.62714 7.30917 2.62704 2.47032 11.1187C2.15898 11.665 2.15898 12.3348 2.47032 12.8811C7.30917 21.3728 16.6909 21.3729 21.5298 12.8812C21.8411 12.3349 21.8411 11.6652 21.5298 11.1188Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.25 12C15.25 13.7949 13.7949 15.25 12 15.25C10.2051 15.25 8.75 13.7949 8.75 12C8.75 10.2051 10.2051 8.75 12 8.75C13.7949 8.75 15.25 10.2051 15.25 12Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

Before

Width:  |  Height:  |  Size: 800 B

After

Width:  |  Height:  |  Size: 624 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M11.9216 2.75H6.75C5.64543 2.75 4.75 3.64543 4.75 4.75V19.25C4.75 20.3546 5.64543 21.25 6.75 21.25H17.25C18.3546 21.25 19.25 20.3546 19.25 19.25V10.0784C19.25 9.54799 19.0393 9.03929 18.6642 8.66421L13.3358 3.33579C12.9607 2.96071 12.452 2.75 11.9216 2.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12.75 3.25V7.25C12.75 8.35457 13.6454 9.25 14.75 9.25H18.75" stroke="black" stroke-width="1.5" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 557 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M2.75 4.75H21.25" stroke="black" stroke-width="1.5" stroke-linecap="round"/><path d="M8.75 19.25H15.25" stroke="black" stroke-width="1.5" stroke-linecap="round"/><path d="M5.75 12H18.25" stroke="black" stroke-width="1.5" stroke-linecap="round"/></svg>

After

Width:  |  Height:  |  Size: 332 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

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M9.13202 3.75H4.75C3.64543 3.75 2.75 4.64543 2.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.25V7.75C21.25 6.64543 20.3546 5.75 19.25 5.75H12.8124C12.2915 5.75 11.7911 5.54674 11.4177 5.18345L10.5267 4.31655C10.1534 3.95326 9.65297 3.75 9.13202 3.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.75 12.75V11.75C2.75 10.6454 3.64543 9.75 4.75 9.75H19.25C20.3546 9.75 21.25 10.6454 21.25 11.75V12.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 667 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M4.75 3.75H8.92963C9.59834 3.75 10.2228 4.0842 10.5937 4.6406L11.7031 6.3047C11.8886 6.5829 12.2008 6.75 12.5352 6.75H18.25C19.3546 6.75 20.25 7.64543 20.25 8.75V10.75H20.6635C21.3311 10.75 21.8113 11.3917 21.6229 12.0322L19.9223 17.8143C19.6719 18.6655 18.8907 19.25 18.0035 19.25H4.32301C3.45426 19.25 2.75 18.5457 2.75 17.677V5.75C2.75 4.64543 3.64543 3.75 4.75 3.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M20.2493 10.75H9.49573C8.60851 10.75 7.82734 11.3345 7.577 12.1857L5.83136 18.1208C5.63446 18.7903 5.02007 19.25 4.32227 19.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 760 B

+1
View File
@@ -0,0 +1 @@
<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="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 463 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M19.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.25H19.25C20.3546 19.25 21.25 18.3546 21.25 17.25V6.75C21.25 5.64543 20.3546 4.75 19.25 4.75Z" stroke="black" stroke-width="1.5" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 334 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M15.5 3.75H8.5C7.39543 3.75 6.5 4.64543 6.5 5.75V18.25C6.5 19.3546 7.39543 20.25 8.5 20.25H15.5C16.6046 20.25 17.5 19.3546 17.5 18.25V5.75C17.5 4.64543 16.6046 3.75 15.5 3.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M17.5 5.75H19.25C20.3546 5.75 21.25 6.64543 21.25 7.75V16.25C21.25 17.3546 20.3546 18.25 19.25 18.25H17.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.5 18.25H4.75C3.64543 18.25 2.75 17.3546 2.75 16.25V7.75C2.75 6.64543 3.64543 5.75 4.75 5.75H6.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 759 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><circle cx="6.5" cy="6" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="6.5" cy="18" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="17.5" cy="6" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.5 8.25V15.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M17.5 8.25V10C17.5 11.1046 16.6046 12 15.5 12H8.5C7.39543 12 6.5 12.8954 6.5 14V15.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 711 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M2.75 8.75V17.25C2.75 18.3546 3.64543 19.25 4.75 19.25H19.25C20.3546 19.25 21.25 18.3546 21.25 17.25V6.75C21.25 5.64543 20.3546 4.75 19.25 4.75H15.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.75 4.75H7C9.76142 4.75 12 6.98858 12 9.75V15M12 15L15.75 11.25M12 15L8.25 11.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 495 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="5.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M6.5 12H1.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M22.25 12H17.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 357 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M20.255 17.05V10.35C20.255 9.78995 20.255 9.50992 20.146 9.29601C20.0501 9.10785 19.8972 8.95487 19.709 8.85899C19.4951 8.75 19.2151 8.75 18.655 8.75H5.35C4.78995 8.75 4.50992 8.75 4.29601 8.85899C4.10785 8.95487 3.95487 9.10785 3.85899 9.29601C3.75 9.50992 3.75 9.78995 3.75 10.35V17.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.82989 20.25 6.95 20.25H17.055C18.1751 20.25 18.7352 20.25 19.163 20.032C19.5393 19.8403 19.8453 19.5343 20.037 19.158C20.255 18.7302 20.255 18.1701 20.255 17.05Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M5.75 8V3.75C5.75 3.19772 6.19772 2.75 6.75 2.75H9.67157C10.202 2.75 10.7107 2.96071 11.0858 3.33579L12.2071 4.45711C12.3946 4.64464 12.649 4.75 12.9142 4.75H17.25C17.8023 4.75 18.25 5.19772 18.25 5.75V8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M7.75 12.75L11.75 12.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
<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.75C17.1086 2.75 21.25 6.89137 21.25 12Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="3 4"/><path d="M15 9L9 15" stroke="black" stroke-width="1.5" stroke-linecap="round"/><path d="M15 15L9 9" stroke="black" stroke-width="1.5" stroke-linecap="round"/></svg>

After

Width:  |  Height:  |  Size: 504 B

+3
View File
@@ -0,0 +1,3 @@
<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.75C17.1086 2.75 21.25 6.89137 21.25 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="3 4"/>
</svg>

After

Width:  |  Height:  |  Size: 356 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 7.75V12L14.75 14.75M21.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="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 346 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M15 9.5L10.5 15L8.5 13" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><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.75C17.1086 2.75 21.25 6.89137 21.25 12Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="3 4"/></svg>

After

Width:  |  Height:  |  Size: 461 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><circle cx="6.5" cy="6" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="6.5" cy="18" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="17.5" cy="18" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.5 8.25V15.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M17.5 10.75V15.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.5 4L19.5 8M19.5 4L15.5 8" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 765 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><circle cx="6.5" cy="6" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="6.5" cy="18" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="17.5" cy="18" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.5 8.25V15.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M18.25 6C18.25 6.41421 17.9142 6.75 17.5 6.75C17.0858 6.75 16.75 6.41421 16.75 6C16.75 5.58579 17.0858 5.25 17.5 5.25C17.9142 5.25 18.25 5.58579 18.25 6Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M18.25 11.25C18.25 11.6642 17.9142 12 17.5 12C17.0858 12 16.75 11.6642 16.75 11.25C16.75 10.8358 17.0858 10.5 17.5 10.5C17.9142 10.5 18.25 10.8358 18.25 11.25Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><circle cx="6" cy="6" r="2.25" stroke="black" stroke-width="1.5"/><circle cx="6" cy="18" r="2.25" stroke="black" stroke-width="1.5"/><circle cx="18" cy="18" r="2.25" stroke="black" stroke-width="1.5"/><path d="M6 8.25V15.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.5 6H16C17.1046 6 18 6.89543 18 8V15.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 519 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><circle cx="6" cy="6" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="6" cy="18" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="18" cy="18" r="2.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M6 8.25V15.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M13 6H16C17.1046 6 18 6.89543 18 8V15.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M14 3.75L11.75 6L14 8.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 776 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 1.95068C17.525 1.95068 22 6.42568 22 11.9507C21.9995 14.0459 21.3419 16.0883 20.1198 17.7902C18.8977 19.4922 17.1727 20.768 15.1875 21.4382C14.6875 21.5382 14.5 21.2257 14.5 20.9632C14.5 20.6257 14.5125 19.5507 14.5125 18.2132C14.5125 17.2757 14.2 16.6757 13.8375 16.3632C16.0625 16.1132 18.4 15.2632 18.4 11.4257C18.4 10.3257 18.0125 9.43818 17.375 8.73818C17.475 8.48818 17.825 7.46318 17.275 6.08818C17.275 6.08818 16.4375 5.81318 14.525 7.11318C13.725 6.88818 12.875 6.77568 12.025 6.77568C11.175 6.77568 10.325 6.88818 9.525 7.11318C7.6125 5.82568 6.775 6.08818 6.775 6.08818C6.225 7.46318 6.575 8.48818 6.675 8.73818C6.0375 9.43818 5.65 10.3382 5.65 11.4257C5.65 15.2507 7.975 16.1132 10.2 16.3632C9.9125 16.6132 9.65 17.0507 9.5625 17.7007C8.9875 17.9632 7.55 18.3882 6.65 16.8757C6.4625 16.5757 5.9 15.8382 5.1125 15.8507C4.275 15.8632 4.775 16.3257 5.125 16.5132C5.55 16.7507 6.0375 17.6382 6.15 17.9257C6.35 18.4882 7 19.5632 9.5125 19.1007C9.5125 19.9382 9.525 20.7257 9.525 20.9632C9.525 21.2257 9.3375 21.5257 8.8375 21.4382C6.8458 20.7752 5.11342 19.502 3.88611 17.799C2.65881 16.096 1.9989 14.0498 2 11.9507C2 6.42568 6.475 1.95068 12 1.95068Z" fill="black"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+1
View File
@@ -0,0 +1 @@
<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.75C17.1086 2.75 21.25 6.89137 21.25 12Z" stroke="black" stroke-width="1.5" stroke-linecap="square"/><path d="M12 21C9.79086 21 8 16.9706 8 12C8 7.02944 9.79086 3 12 3C14.2091 3 16 7.02944 16 12C16 16.9706 14.2091 21 12 21Z" stroke="black" stroke-width="1.5" stroke-linecap="square"/><path d="M21 12H3" stroke="black" stroke-width="1.5" stroke-linecap="square"/></svg>

After

Width:  |  Height:  |  Size: 561 B

+1
View File
@@ -0,0 +1 @@
<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.75C17.1086 2.75 21.25 6.89137 21.25 12Z" stroke="black" stroke-width="1.5" stroke-linecap="square"/><path d="M12 21C9.79086 21 8 16.9706 8 12C8 7.02944 9.79086 3 12 3C14.2091 3 16 7.02944 16 12C16 16.9706 14.2091 21 12 21Z" stroke="black" stroke-width="1.5" stroke-linecap="square"/><path d="M21 12H3" stroke="black" stroke-width="1.5" stroke-linecap="square"/></svg>

After

Width:  |  Height:  |  Size: 561 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M3.75 5.75C3.75 4.64543 4.64543 3.75 5.75 3.75H8.25C9.35457 3.75 10.25 4.64543 10.25 5.75V8.25C10.25 9.35457 9.35457 10.25 8.25 10.25H5.75C4.64543 10.25 3.75 9.35457 3.75 8.25V5.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M3.75 15.75C3.75 14.6454 4.64543 13.75 5.75 13.75H8.25C9.35457 13.75 10.25 14.6454 10.25 15.75V18.25C10.25 19.3546 9.35457 20.25 8.25 20.25H5.75C4.64543 20.25 3.75 19.3546 3.75 18.25V15.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.75 17C13.75 15.2051 15.2051 13.75 17 13.75C18.7949 13.75 20.25 15.2051 20.25 17C20.25 18.7949 18.7949 20.25 17 20.25C15.2051 20.25 13.75 18.7949 13.75 17Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.75 5.75C13.75 4.64543 14.6454 3.75 15.75 3.75H18.25C19.3546 3.75 20.25 4.64543 20.25 5.75V8.25C20.25 9.35457 19.3546 10.25 18.25 10.25H15.75C14.6454 10.25 13.75 9.35457 13.75 8.25V5.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M4.75 17V16.75C4.75 15.6454 5.64543 14.75 6.75 14.75H17.25C18.3546 14.75 19.25 15.6454 19.25 16.75V17M19.25 17V4.75C19.25 3.64543 18.3546 2.75 17.25 2.75H6.75C5.64543 2.75 4.75 3.64543 4.75 4.75V19.25C4.75 20.3546 5.64543 21.25 6.75 21.25H17.25C18.3546 21.25 19.25 20.3546 19.25 19.25V17Z" stroke="black" stroke-width="1.5" stroke-linejoin="round"/><path d="M13 17.25C13.4142 17.25 13.75 17.5858 13.75 18C13.75 18.4142 13.4142 18.75 13 18.75C12.5858 18.75 12.25 18.4142 12.25 18C12.25 17.5858 12.5858 17.25 13 17.25ZM16 17.25C16.4142 17.25 16.75 17.5858 16.75 18C16.75 18.4142 16.4142 18.75 16 18.75C15.5858 18.75 15.25 18.4142 15.25 18C15.25 17.5858 15.5858 17.25 16 17.25Z" fill="black" stroke="black" stroke-width="0.5" stroke-linecap="square"/></svg>

After

Width:  |  Height:  |  Size: 835 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 5.57193C18.3331 -0.86765 29.1898 11.0916 12 20.75C-5.18982 11.0916 5.66687 -0.867651 12 5.57193ZM12 5.57193C11.1207 6.53558 10.4876 7.78832 10 8.99993L13 11.9999L12 14.9999" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 347 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M21.25 9.9375C21.25 15.8672 12.7708 20.25 12 20.25C11.2292 20.25 2.75 15.8672 2.75 9.9375C2.75 5.8125 5.31944 3.75 7.88889 3.75C10.4583 3.75 12 5.29688 12 5.29688C12 5.29688 13.5417 3.75 16.1111 3.75C18.6806 3.75 21.25 5.8125 21.25 9.9375Z" stroke="black" stroke-width="1.5" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 387 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

+1 -3
View File
@@ -1,3 +1 @@
<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>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M2 12.75H7.42732C8.04847 12.75 8.63437 13.0386 9.01294 13.5311L9.34956 13.9689C9.72814 14.4614 10.314 14.75 10.9352 14.75H13.0648C13.686 14.75 14.2719 14.4614 14.6504 13.9689L14.9871 13.5311C15.3656 13.0386 15.9515 12.75 16.5727 12.75H22M22.0299 12.3205L18.7075 5.83782C18.3653 5.17003 17.678 4.75 16.9276 4.75H7.07236C6.32198 4.75 5.63473 5.17003 5.29249 5.83782L1.97013 12.3205C1.82545 12.6028 1.75 12.9154 1.75 13.2327V17.25C1.75 18.3546 2.64543 19.25 3.75 19.25H20.25C21.3546 19.25 22.25 18.3546 22.25 17.25V13.2327C22.25 12.9154 22.1745 12.6028 22.0299 12.3205Z" stroke="black" stroke-width="1.5" stroke-linecap="round"/></svg>

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 713 B

+1 -3
View File
@@ -1,3 +1 @@
<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>
<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="black" 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="black" stroke="black" stroke-width="0.25"/></svg>

Before

Width:  |  Height:  |  Size: 590 B

After

Width:  |  Height:  |  Size: 566 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M18.5 19.0615C20.6627 18.4544 22.25 16.4502 22.25 14.0714C22.25 11.2114 19.9555 8.89286 17.125 8.89286C16.5661 8.89286 16.0281 8.98326 15.5245 9.15037C14.4289 6.56294 11.8865 4.75 8.925 4.75C4.96236 4.75 1.75 7.99594 1.75 12C1.75 14.7508 3.26609 17.1437 5.5 18.3722M14.5 16.25L12 13.75L9.5 16.25M12 20V14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 488 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M7.25 7.75L9 9.5L7.25 11.25M10.75 11.25H12.75M5.75 20.25H18.25C19.3546 20.25 20.25 19.3546 20.25 18.25V5.75C20.25 4.64543 19.3546 3.75 18.25 3.75H5.75C4.64543 3.75 3.75 4.64543 3.75 5.75V18.25C3.75 19.3546 4.64543 20.25 5.75 20.25Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 412 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M4.84202 20.032L5.18251 19.3638L5.18251 19.3638L4.84202 20.032ZM3.96799 19.158L4.63624 18.8175L4.63624 18.8175L3.96799 19.158ZM20.032 19.158L19.3638 18.8175L19.3638 18.8175L20.032 19.158ZM19.158 20.032L18.8175 19.3638L18.8175 19.3638L19.158 20.032ZM20.032 4.84202L19.3638 5.18251L19.3638 5.18251L20.032 4.84202ZM19.158 3.96799L18.8175 4.63624L18.8175 4.63624L19.158 3.96799ZM3.96799 4.84202L3.29973 4.50153L3.29973 4.50153L3.96799 4.84202ZM4.84202 3.96799L4.50153 3.29973L4.50153 3.29973L4.84202 3.96799ZM19.5 6.95V17.05H21V6.95H19.5ZM17.05 19.5H6.95V21H17.05V19.5ZM4.5 17.05V6.95H3V17.05H4.5ZM6.95 4.5H17.05V3H6.95V4.5ZM6.95 19.5C6.37757 19.5 5.99336 19.4994 5.69748 19.4752C5.41035 19.4518 5.27307 19.4099 5.18251 19.3638L4.50153 20.7003C4.83879 20.8721 5.19545 20.9392 5.57533 20.9703C5.94646 21.0006 6.40232 21 6.95 21V19.5ZM3 17.05C3 17.5977 2.99942 18.0535 3.02974 18.4247C3.06078 18.8046 3.12789 19.1612 3.29973 19.4985L4.63624 18.8175C4.5901 18.7269 4.54822 18.5896 4.52476 18.3025C4.50058 18.0066 4.5 17.6224 4.5 17.05H3ZM5.18251 19.3638C4.94731 19.2439 4.75608 19.0527 4.63624 18.8175L3.29973 19.4985C3.56338 20.0159 3.98408 20.4366 4.50153 20.7003L5.18251 19.3638ZM19.5 17.05C19.5 17.6224 19.4994 18.0066 19.4752 18.3025C19.4518 18.5896 19.4099 18.7269 19.3638 18.8175L20.7003 19.4985C20.8721 19.1612 20.9392 18.8046 20.9703 18.4247C21.0006 18.0535 21 17.5977 21 17.05H19.5ZM17.05 21C17.5977 21 18.0535 21.0006 18.4247 20.9703C18.8046 20.9392 19.1612 20.8721 19.4985 20.7003L18.8175 19.3638C18.7269 19.4099 18.5896 19.4518 18.3025 19.4752C18.0066 19.4994 17.6224 19.5 17.05 19.5V21ZM19.3638 18.8175C19.2439 19.0527 19.0527 19.2439 18.8175 19.3638L19.4985 20.7003C20.0159 20.4366 20.4366 20.0159 20.7003 19.4985L19.3638 18.8175ZM21 6.95C21 6.40232 21.0006 5.94646 20.9703 5.57533C20.9392 5.19545 20.8721 4.83879 20.7003 4.50153L19.3638 5.18251C19.4099 5.27307 19.4518 5.41035 19.4752 5.69748C19.4994 5.99336 19.5 6.37757 19.5 6.95H21ZM17.05 4.5C17.6224 4.5 18.0066 4.50058 18.3025 4.52476C18.5896 4.54822 18.7269 4.5901 18.8175 4.63624L19.4985 3.29973C19.1612 3.12789 18.8046 3.06078 18.4247 3.02974C18.0535 2.99942 17.5977 3 17.05 3V4.5ZM20.7003 4.50153C20.4366 3.98408 20.0159 3.56338 19.4985 3.29973L18.8175 4.63624C19.0527 4.75608 19.2439 4.94731 19.3638 5.18251L20.7003 4.50153ZM4.5 6.95C4.5 6.37757 4.50058 5.99336 4.52476 5.69748C4.54822 5.41035 4.5901 5.27307 4.63624 5.18251L3.29973 4.50153C3.12789 4.83879 3.06078 5.19545 3.02974 5.57533C2.99942 5.94646 3 6.40232 3 6.95H4.5ZM6.95 3C6.40232 3 5.94646 2.99942 5.57533 3.02974C5.19545 3.06078 4.83879 3.12789 4.50153 3.29973L5.18251 4.63624C5.27307 4.5901 5.41035 4.54822 5.69748 4.52476C5.99336 4.50058 6.37757 4.5 6.95 4.5V3ZM4.63624 5.18251C4.75608 4.94731 4.94731 4.75608 5.18251 4.63624L4.50153 3.29973C3.98408 3.56338 3.56338 3.98408 3.29973 4.50153L4.63624 5.18251Z" fill="black"/><path d="M12 13.5H11.25V15H12V13.5ZM20.25 15H21V13.5H20.25V15ZM12 15H20.25V13.5H12V15Z" fill="black"/><path d="M12 10.5H12.75V9H12V10.5ZM3.75 9H3V10.5H3.75V9ZM12 9H3.75V10.5H12V9Z" fill="black"/><path d="M11.25 20.25V21H12.75V20.25H11.25ZM12.75 3.75V3H11.25V3.75H12.75ZM11.25 3.75V20.25H12.75V3.75H11.25Z" fill="black"/></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 2.76027V2.75M12 21.2397V21.25M15.5359 3.4636L15.5398 3.45411M8.46411 20.5364L8.46018 20.5459M18.5335 5.46661L18.5407 5.45935M5.46651 18.5336L5.45924 18.5408M20.5363 8.46431L20.5458 8.46038M3.46354 15.5361L3.45405 15.54M2.76027 12H2.75M21.2397 12H21.25M5.46675 5.46647L5.45949 5.4592M18.5337 18.5334L18.541 18.5407M3.46392 8.4638L3.45443 8.45987M20.5367 15.5356L20.5462 15.5395M8.46409 3.46357L8.46016 3.45408M15.5359 20.5364L15.5398 20.5459" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 615 B

+1 -10
View File
@@ -1,10 +1 @@
<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>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12.0003 2.75L12 6.25M12.0003 17.75V21.25M2.75 12.0007H6.25M17.75 12.0007H21.25M5.45948 5.45905L7.93414 7.93414M16.0661 16.0656L18.541 18.5405M5.45976 18.5412L7.93463 16.0664M16.0664 7.93463L18.5412 5.45976" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

Before

Width:  |  Height:  |  Size: 435 B

After

Width:  |  Height:  |  Size: 377 B

+15
View File
@@ -0,0 +1,15 @@
<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"
>
<path d="m10 17 5-5-5-5" />
<path d="M15 12H3" />
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" />
</svg>

After

Width:  |  Height:  |  Size: 319 B

+15
View File
@@ -0,0 +1,15 @@
<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"
>
<path d="m16 17 5-5-5-5" />
<path d="M21 12H9" />
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
</svg>

After

Width:  |  Height:  |  Size: 317 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M8.75 4.99998V17.5M15.25 6.49998V19M2.75 17.4188V7.58112C2.75 6.93547 3.16315 6.36226 3.77566 6.15809L8.29397 4.65199C8.59056 4.55312 8.91063 4.5494 9.20944 4.64134L14.7906 6.35861C15.0894 6.45055 15.4094 6.44683 15.706 6.34797L19.2757 5.15809C20.247 4.83433 21.25 5.55728 21.25 6.58112V16.4188C21.25 17.0645 20.8369 17.6377 20.2243 17.8419L15.706 19.348C15.4094 19.4468 15.0894 19.4506 14.7906 19.3586L9.20944 17.6413C8.91063 17.5494 8.59056 17.5531 8.29397 17.652L4.72434 18.8419C3.75305 19.1656 2.75 18.4427 2.75 17.4188Z" stroke="black" stroke-width="1.5" stroke-linecap="square" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 696 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M21.25 6.75C21.25 5.64543 20.3546 4.75 19.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.25H19.25C20.3546 19.25 21.25 18.3546 21.25 17.25V6.75Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.75 14.25V9.75L9 12L11.25 9.75V14.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.75 9.75V14.25L14 12.5" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.75 14.25L17.5 12.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 731 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12.75 18.25H16.25C17.3546 18.25 18.25 17.3546 18.25 16.25V12.75" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M5.75 11.25V7.75C5.75 6.64543 6.64543 5.75 7.75 5.75H11.25" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 386 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M15.25 2.75H6.75C5.64543 2.75 4.75 3.64543 4.75 4.75V19.25C4.75 20.3546 5.64543 21.25 6.75 21.25H17.25C18.3546 21.25 19.25 20.3546 19.25 19.25V11.8491C19.25 11.2939 19.0959 10.7497 18.805 10.2768L17.695 8.47317C17.4041 8.00034 17.25 7.45605 17.25 6.90087V4.75C17.25 3.64543 16.3546 2.75 15.25 2.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.25 6.75V9.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12.75 6.75V9.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 710 B

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